@ -0,0 +1,19 @@ | |||||
[remap] | |||||
importer="wavefront_obj" | |||||
type="Mesh" | |||||
path="res://.import/0.obj-5ff3661b08325671ce64079449bc78dd.mesh" | |||||
[deps] | |||||
files=[ "res://.import/0.obj-5ff3661b08325671ce64079449bc78dd.mesh" ] | |||||
source_file="res://assets/heroes/0.obj" | |||||
source_md5="214f8b376199d3c947df1d85a0928dec" | |||||
dest_files=[ "res://.import/0.obj-5ff3661b08325671ce64079449bc78dd.mesh", "res://.import/0.obj-5ff3661b08325671ce64079449bc78dd.mesh" ] | |||||
dest_md5="4306afcb3a4b4978cce778634c97e7d0" | |||||
[params] | |||||
generate_tangents=true |
@ -0,0 +1,19 @@ | |||||
[remap] | |||||
importer="wavefront_obj" | |||||
type="Mesh" | |||||
path="res://.import/0_head.obj-0b002be4b283361f171e2d4dd9a85105.mesh" | |||||
[deps] | |||||
files=[ "res://.import/0_head.obj-0b002be4b283361f171e2d4dd9a85105.mesh" ] | |||||
source_file="res://assets/heroes/0_head.obj" | |||||
source_md5="4ae67462b018895ed3f258a0db4239e4" | |||||
dest_files=[ "res://.import/0_head.obj-0b002be4b283361f171e2d4dd9a85105.mesh", "res://.import/0_head.obj-0b002be4b283361f171e2d4dd9a85105.mesh" ] | |||||
dest_md5="7094164b68aeac1c4dd9bd292282e4b9" | |||||
[params] | |||||
generate_tangents=true |
@ -0,0 +1,19 @@ | |||||
[remap] | |||||
importer="wavefront_obj" | |||||
type="Mesh" | |||||
path="res://.import/1.obj-3a9ae1e4a3d92228bd05741755fdbcdd.mesh" | |||||
[deps] | |||||
files=[ "res://.import/1.obj-3a9ae1e4a3d92228bd05741755fdbcdd.mesh" ] | |||||
source_file="res://assets/heroes/1.obj" | |||||
source_md5="6a83c78e54b84bd01eb44630c48362f2" | |||||
dest_files=[ "res://.import/1.obj-3a9ae1e4a3d92228bd05741755fdbcdd.mesh", "res://.import/1.obj-3a9ae1e4a3d92228bd05741755fdbcdd.mesh" ] | |||||
dest_md5="808804a04eb10327391dd96c30f14435" | |||||
[params] | |||||
generate_tangents=true |
@ -0,0 +1,19 @@ | |||||
[remap] | |||||
importer="wavefront_obj" | |||||
type="Mesh" | |||||
path="res://.import/1_head.obj-661a3a390ef1299857e3de1aba5a1d01.mesh" | |||||
[deps] | |||||
files=[ "res://.import/1_head.obj-661a3a390ef1299857e3de1aba5a1d01.mesh" ] | |||||
source_file="res://assets/heroes/1_head.obj" | |||||
source_md5="78a4e4475d699dbd061ec9a916cbd606" | |||||
dest_files=[ "res://.import/1_head.obj-661a3a390ef1299857e3de1aba5a1d01.mesh", "res://.import/1_head.obj-661a3a390ef1299857e3de1aba5a1d01.mesh" ] | |||||
dest_md5="65c8304300dd9929d0b3b008623845a0" | |||||
[params] | |||||
generate_tangents=true |
@ -0,0 +1,19 @@ | |||||
[remap] | |||||
importer="wavefront_obj" | |||||
type="Mesh" | |||||
path="res://.import/2.obj-85c8b4ac312b9585e4dda4f545679e80.mesh" | |||||
[deps] | |||||
files=[ "res://.import/2.obj-85c8b4ac312b9585e4dda4f545679e80.mesh" ] | |||||
source_file="res://assets/heroes/2.obj" | |||||
source_md5="7b2a64047a6477edd16b10f1c8601e15" | |||||
dest_files=[ "res://.import/2.obj-85c8b4ac312b9585e4dda4f545679e80.mesh", "res://.import/2.obj-85c8b4ac312b9585e4dda4f545679e80.mesh" ] | |||||
dest_md5="cf97b53977d069c0f48e104093031357" | |||||
[params] | |||||
generate_tangents=true |
@ -0,0 +1,19 @@ | |||||
[remap] | |||||
importer="wavefront_obj" | |||||
type="Mesh" | |||||
path="res://.import/2_head.obj-642d7e5721ba3621d4aeef33bf9079f3.mesh" | |||||
[deps] | |||||
files=[ "res://.import/2_head.obj-642d7e5721ba3621d4aeef33bf9079f3.mesh" ] | |||||
source_file="res://assets/heroes/2_head.obj" | |||||
source_md5="3800b7ca51634172c29e2627d0688739" | |||||
dest_files=[ "res://.import/2_head.obj-642d7e5721ba3621d4aeef33bf9079f3.mesh", "res://.import/2_head.obj-642d7e5721ba3621d4aeef33bf9079f3.mesh" ] | |||||
dest_md5="f41bf13cd6278973e335edcb5af07800" | |||||
[params] | |||||
generate_tangents=true |
@ -0,0 +1,19 @@ | |||||
[remap] | |||||
importer="wavefront_obj" | |||||
type="Mesh" | |||||
path="res://.import/3.obj-a2b36e0242832fc3ee6cf8133bec1df6.mesh" | |||||
[deps] | |||||
files=[ "res://.import/3.obj-a2b36e0242832fc3ee6cf8133bec1df6.mesh" ] | |||||
source_file="res://assets/heroes/3.obj" | |||||
source_md5="ae31181eed13485b325d439fd465e246" | |||||
dest_files=[ "res://.import/3.obj-a2b36e0242832fc3ee6cf8133bec1df6.mesh", "res://.import/3.obj-a2b36e0242832fc3ee6cf8133bec1df6.mesh" ] | |||||
dest_md5="3afa20d19f1fa09fcf2fee691c245317" | |||||
[params] | |||||
generate_tangents=true |
@ -0,0 +1,19 @@ | |||||
[remap] | |||||
importer="wavefront_obj" | |||||
type="Mesh" | |||||
path="res://.import/3_head.obj-31149354fd38179ee6897017177772af.mesh" | |||||
[deps] | |||||
files=[ "res://.import/3_head.obj-31149354fd38179ee6897017177772af.mesh" ] | |||||
source_file="res://assets/heroes/3_head.obj" | |||||
source_md5="4f6b31cfbe16a55cce978896cf8b0b12" | |||||
dest_files=[ "res://.import/3_head.obj-31149354fd38179ee6897017177772af.mesh", "res://.import/3_head.obj-31149354fd38179ee6897017177772af.mesh" ] | |||||
dest_md5="8ba28a8f98e213a2382b508bb344084c" | |||||
[params] | |||||
generate_tangents=true |
@ -0,0 +1,19 @@ | |||||
[remap] | |||||
importer="wavefront_obj" | |||||
type="Mesh" | |||||
path="res://.import/4.obj-773905e6e7c2c08ad34fe36bfaacca4e.mesh" | |||||
[deps] | |||||
files=[ "res://.import/4.obj-773905e6e7c2c08ad34fe36bfaacca4e.mesh" ] | |||||
source_file="res://assets/heroes/4.obj" | |||||
source_md5="8a2ea58bae6a5ba0b5a28b7ab6163ed9" | |||||
dest_files=[ "res://.import/4.obj-773905e6e7c2c08ad34fe36bfaacca4e.mesh", "res://.import/4.obj-773905e6e7c2c08ad34fe36bfaacca4e.mesh" ] | |||||
dest_md5="93358c6c816510bd069825e089cee878" | |||||
[params] | |||||
generate_tangents=true |
@ -0,0 +1,19 @@ | |||||
[remap] | |||||
importer="wavefront_obj" | |||||
type="Mesh" | |||||
path="res://.import/4_beam.obj-00b64391bae3ea00b0d3ef163bfa8860.mesh" | |||||
[deps] | |||||
files=[ "res://.import/4_beam.obj-00b64391bae3ea00b0d3ef163bfa8860.mesh" ] | |||||
source_file="res://assets/heroes/4_beam.obj" | |||||
source_md5="8583eb948f98e108f6848c4fc96a2562" | |||||
dest_files=[ "res://.import/4_beam.obj-00b64391bae3ea00b0d3ef163bfa8860.mesh", "res://.import/4_beam.obj-00b64391bae3ea00b0d3ef163bfa8860.mesh" ] | |||||
dest_md5="76cbfe93be8dd02d9519c5284d1e638f" | |||||
[params] | |||||
generate_tangents=true |
@ -0,0 +1,19 @@ | |||||
[remap] | |||||
importer="wavefront_obj" | |||||
type="Mesh" | |||||
path="res://.import/4_head.obj-0f05fd2c5005558302023bc511be8ae6.mesh" | |||||
[deps] | |||||
files=[ "res://.import/4_head.obj-0f05fd2c5005558302023bc511be8ae6.mesh" ] | |||||
source_file="res://assets/heroes/4_head.obj" | |||||
source_md5="2ed548efe830daac45b9e4fa4b5b1b73" | |||||
dest_files=[ "res://.import/4_head.obj-0f05fd2c5005558302023bc511be8ae6.mesh", "res://.import/4_head.obj-0f05fd2c5005558302023bc511be8ae6.mesh" ] | |||||
dest_md5="9a188b6c84c8a296472464a3366650fc" | |||||
[params] | |||||
generate_tangents=true |
@ -0,0 +1,19 @@ | |||||
[remap] | |||||
importer="wavefront_obj" | |||||
type="Mesh" | |||||
path="res://.import/5.obj-aae555f5fa1f112fdc160f94735bf9d5.mesh" | |||||
[deps] | |||||
files=[ "res://.import/5.obj-aae555f5fa1f112fdc160f94735bf9d5.mesh" ] | |||||
source_file="res://assets/heroes/5.obj" | |||||
source_md5="5a020906bcd263a5302ebe5074b43c01" | |||||
dest_files=[ "res://.import/5.obj-aae555f5fa1f112fdc160f94735bf9d5.mesh", "res://.import/5.obj-aae555f5fa1f112fdc160f94735bf9d5.mesh" ] | |||||
dest_md5="bb5b4b3ca681eb185284560d7f4a8aed" | |||||
[params] | |||||
generate_tangents=true |
@ -0,0 +1,19 @@ | |||||
[remap] | |||||
importer="wavefront_obj" | |||||
type="Mesh" | |||||
path="res://.import/5_head.obj-99b3a2eaf8ae9e841b6fdcc8d4884ea7.mesh" | |||||
[deps] | |||||
files=[ "res://.import/5_head.obj-99b3a2eaf8ae9e841b6fdcc8d4884ea7.mesh" ] | |||||
source_file="res://assets/heroes/5_head.obj" | |||||
source_md5="453157b7ed836fe44c392ad6dda3ff67" | |||||
dest_files=[ "res://.import/5_head.obj-99b3a2eaf8ae9e841b6fdcc8d4884ea7.mesh", "res://.import/5_head.obj-99b3a2eaf8ae9e841b6fdcc8d4884ea7.mesh" ] | |||||
dest_md5="a2bc2ff9d1a92c3be2c8477eceb08768" | |||||
[params] | |||||
generate_tangents=true |
@ -0,0 +1,19 @@ | |||||
[remap] | |||||
importer="wavefront_obj" | |||||
type="Mesh" | |||||
path="res://.import/5_portal.obj-a337ed5122c40b180597dd9794ecfe6d.mesh" | |||||
[deps] | |||||
files=[ "res://.import/5_portal.obj-a337ed5122c40b180597dd9794ecfe6d.mesh" ] | |||||
source_file="res://assets/heroes/5_portal.obj" | |||||
source_md5="bc944713e3a053455322914a39987891" | |||||
dest_files=[ "res://.import/5_portal.obj-a337ed5122c40b180597dd9794ecfe6d.mesh", "res://.import/5_portal.obj-a337ed5122c40b180597dd9794ecfe6d.mesh" ] | |||||
dest_md5="b63a25154e6cdd3e7bcb1a3fe4e0e04a" | |||||
[params] | |||||
generate_tangents=true |
@ -0,0 +1,19 @@ | |||||
[remap] | |||||
importer="wavefront_obj" | |||||
type="Mesh" | |||||
path="res://.import/0.obj-75904c05129c42580cc5d5c2ce0f88f4.mesh" | |||||
[deps] | |||||
files=[ "res://.import/0.obj-75904c05129c42580cc5d5c2ce0f88f4.mesh" ] | |||||
source_file="res://assets/levels/0.obj" | |||||
source_md5="c43e5185ee65f03ac94dc947611d85d5" | |||||
dest_files=[ "res://.import/0.obj-75904c05129c42580cc5d5c2ce0f88f4.mesh", "res://.import/0.obj-75904c05129c42580cc5d5c2ce0f88f4.mesh" ] | |||||
dest_md5="23c015582e544d38c3302925e86550ca" | |||||
[params] | |||||
generate_tangents=true |
@ -0,0 +1,19 @@ | |||||
[remap] | |||||
importer="wavefront_obj" | |||||
type="Mesh" | |||||
path="res://.import/1.obj-8c0967187ed7a9d970c8667286d96ec8.mesh" | |||||
[deps] | |||||
files=[ "res://.import/1.obj-8c0967187ed7a9d970c8667286d96ec8.mesh" ] | |||||
source_file="res://assets/levels/1.obj" | |||||
source_md5="c76ce309215e4ab659d00d70f85051c1" | |||||
dest_files=[ "res://.import/1.obj-8c0967187ed7a9d970c8667286d96ec8.mesh", "res://.import/1.obj-8c0967187ed7a9d970c8667286d96ec8.mesh" ] | |||||
dest_md5="772c79044dd30ef6a01e2c1b99e3f78c" | |||||
[params] | |||||
generate_tangents=true |
@ -0,0 +1,19 @@ | |||||
[remap] | |||||
importer="wavefront_obj" | |||||
type="Mesh" | |||||
path="res://.import/2.obj-3a5433501168477746c24182726a90c2.mesh" | |||||
[deps] | |||||
files=[ "res://.import/2.obj-3a5433501168477746c24182726a90c2.mesh" ] | |||||
source_file="res://assets/levels/2.obj" | |||||
source_md5="9c1795f0c017f636969954e503a75157" | |||||
dest_files=[ "res://.import/2.obj-3a5433501168477746c24182726a90c2.mesh", "res://.import/2.obj-3a5433501168477746c24182726a90c2.mesh" ] | |||||
dest_md5="557048f3ebad19044a3c342d9c06e870" | |||||
[params] | |||||
generate_tangents=true |
@ -0,0 +1,19 @@ | |||||
[remap] | |||||
importer="wavefront_obj" | |||||
type="Mesh" | |||||
path="res://.import/maze-high-obj.obj-24db734e0c48b4dd09ff2d9426ff3915.mesh" | |||||
[deps] | |||||
files=[ "res://.import/maze-high-obj.obj-24db734e0c48b4dd09ff2d9426ff3915.mesh" ] | |||||
source_file="res://assets/maze-high-obj.obj" | |||||
source_md5="6957a6487d30265864dc3d40baf411a2" | |||||
dest_files=[ "res://.import/maze-high-obj.obj-24db734e0c48b4dd09ff2d9426ff3915.mesh", "res://.import/maze-high-obj.obj-24db734e0c48b4dd09ff2d9426ff3915.mesh" ] | |||||
dest_md5="ce1017fcf90646333644720dd66b599f" | |||||
[params] | |||||
generate_tangents=true |
@ -0,0 +1,19 @@ | |||||
[remap] | |||||
importer="wavefront_obj" | |||||
type="Mesh" | |||||
path="res://.import/maze.obj-70de96fca66f7b42f2e7948110e529ab.mesh" | |||||
[deps] | |||||
files=[ "res://.import/maze.obj-70de96fca66f7b42f2e7948110e529ab.mesh" ] | |||||
source_file="res://assets/maze.obj" | |||||
source_md5="f35f5a0e67714a456ed725761a30cd8e" | |||||
dest_files=[ "res://.import/maze.obj-70de96fca66f7b42f2e7948110e529ab.mesh", "res://.import/maze.obj-70de96fca66f7b42f2e7948110e529ab.mesh" ] | |||||
dest_md5="611ce2fc213387da9af0b853967a2f25" | |||||
[params] | |||||
generate_tangents=true |
@ -0,0 +1,32 @@ | |||||
[remap] | |||||
importer="texture" | |||||
type="StreamTexture" | |||||
path="res://.import/objective-left.png-587402b851e8944e16eb289e1b058974.stex" | |||||
[deps] | |||||
source_file="res://assets/objective-left.png" | |||||
source_md5="c172ead10f0d4193e46622fe62668f31" | |||||
dest_files=[ "res://.import/objective-left.png-587402b851e8944e16eb289e1b058974.stex" ] | |||||
dest_md5="09403856c115d79b27429585b5a7b0f5" | |||||
[params] | |||||
compress/mode=0 | |||||
compress/lossy_quality=0.7 | |||||
compress/hdr_mode=0 | |||||
compress/normal_map=0 | |||||
flags/repeat=0 | |||||
flags/filter=true | |||||
flags/mipmaps=false | |||||
flags/anisotropic=false | |||||
flags/srgb=2 | |||||
process/fix_alpha_border=true | |||||
process/premult_alpha=false | |||||
process/HDR_as_SRGB=false | |||||
stream=false | |||||
size_limit=0 | |||||
detect_3d=true | |||||
svg/scale=1.0 |
@ -0,0 +1,32 @@ | |||||
[remap] | |||||
importer="texture" | |||||
type="StreamTexture" | |||||
path="res://.import/objective-right.png-d18195ff55e7aff72b594ff190053ce4.stex" | |||||
[deps] | |||||
source_file="res://assets/objective-right.png" | |||||
source_md5="2725f8f335a4f2ceb2562ef44343830d" | |||||
dest_files=[ "res://.import/objective-right.png-d18195ff55e7aff72b594ff190053ce4.stex" ] | |||||
dest_md5="c3fa2e979e2fdea8132655a82686526d" | |||||
[params] | |||||
compress/mode=0 | |||||
compress/lossy_quality=0.7 | |||||
compress/hdr_mode=0 | |||||
compress/normal_map=0 | |||||
flags/repeat=0 | |||||
flags/filter=true | |||||
flags/mipmaps=false | |||||
flags/anisotropic=false | |||||
flags/srgb=2 | |||||
process/fix_alpha_border=true | |||||
process/premult_alpha=false | |||||
process/HDR_as_SRGB=false | |||||
stream=false | |||||
size_limit=0 | |||||
detect_3d=true | |||||
svg/scale=1.0 |
@ -0,0 +1,19 @@ | |||||
[remap] | |||||
importer="wavefront_obj" | |||||
type="Mesh" | |||||
path="res://.import/objective.obj-d3a42ffdae3ada4606dd452051061b51.mesh" | |||||
[deps] | |||||
files=[ "res://.import/objective.obj-d3a42ffdae3ada4606dd452051061b51.mesh" ] | |||||
source_file="res://assets/objective.obj" | |||||
source_md5="a00bec3114f9259f2ae795c28f75e9c9" | |||||
dest_files=[ "res://.import/objective.obj-d3a42ffdae3ada4606dd452051061b51.mesh", "res://.import/objective.obj-d3a42ffdae3ada4606dd452051061b51.mesh" ] | |||||
dest_md5="56ab97d8a864679c68c7c7d9c1d38dd8" | |||||
[params] | |||||
generate_tangents=true |
@ -0,0 +1,19 @@ | |||||
[remap] | |||||
importer="wavefront_obj" | |||||
type="Mesh" | |||||
path="res://.import/player.obj-9552f61e8059d9a4dd660ae96ad6a512.mesh" | |||||
[deps] | |||||
files=[ "res://.import/player.obj-9552f61e8059d9a4dd660ae96ad6a512.mesh" ] | |||||
source_file="res://assets/player.obj" | |||||
source_md5="7074ae8221d3b083f4f95e70e1ee446f" | |||||
dest_files=[ "res://.import/player.obj-9552f61e8059d9a4dd660ae96ad6a512.mesh", "res://.import/player.obj-9552f61e8059d9a4dd660ae96ad6a512.mesh" ] | |||||
dest_md5="0ca70d88338dbaa80a9676388d516d63" | |||||
[params] | |||||
generate_tangents=true |
@ -0,0 +1,16 @@ | |||||
[gd_resource type="Theme" load_steps=3 format=2] | |||||
[ext_resource path="res://assets/DejaVuSansMono.ttf" type="DynamicFontData" id=1] | |||||
[sub_resource type="DynamicFont" id=1] | |||||
size = 18 | |||||
use_mipmaps = false | |||||
use_filter = false | |||||
font_data = ExtResource( 1 ) | |||||
_sections_unfolded = [ "Font", "Settings" ] | |||||
[resource] | |||||
default_font = SubResource( 1 ) | |||||
@ -0,0 +1,32 @@ | |||||
[remap] | |||||
importer="texture" | |||||
type="StreamTexture" | |||||
path="res://.import/2018-02-14-heroes.png-66ce877f0c90a512065fa1c503625ab1.stex" | |||||
[deps] | |||||
source_file="res://docs/2018-02-14-heroes.png" | |||||
source_md5="98144251e8505d9c53c0ba6cf451448e" | |||||
dest_files=[ "res://.import/2018-02-14-heroes.png-66ce877f0c90a512065fa1c503625ab1.stex" ] | |||||
dest_md5="b9c1300d34876a596a8e284f821d7bc9" | |||||
[params] | |||||
compress/mode=0 | |||||
compress/lossy_quality=0.7 | |||||
compress/hdr_mode=0 | |||||
compress/normal_map=0 | |||||
flags/repeat=0 | |||||
flags/filter=true | |||||
flags/mipmaps=false | |||||
flags/anisotropic=false | |||||
flags/srgb=2 | |||||
process/fix_alpha_border=true | |||||
process/premult_alpha=false | |||||
process/HDR_as_SRGB=false | |||||
stream=false | |||||
size_limit=0 | |||||
detect_3d=true | |||||
svg/scale=1.0 |
@ -0,0 +1,32 @@ | |||||
[remap] | |||||
importer="texture" | |||||
type="StreamTexture" | |||||
path="res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" | |||||
[deps] | |||||
source_file="res://icon.png" | |||||
source_md5="7febcf604d750bd224f744362be04a8a" | |||||
dest_files=[ "res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" ] | |||||
dest_md5="e86d0a8edb5d32d9447a249948fdcf57" | |||||
[params] | |||||
compress/mode=0 | |||||
compress/lossy_quality=0.7 | |||||
compress/hdr_mode=0 | |||||
compress/normal_map=0 | |||||
flags/repeat=0 | |||||
flags/filter=true | |||||
flags/mipmaps=false | |||||
flags/anisotropic=false | |||||
flags/srgb=2 | |||||
process/fix_alpha_border=true | |||||
process/premult_alpha=false | |||||
process/HDR_as_SRGB=false | |||||
stream=false | |||||
size_limit=0 | |||||
detect_3d=true | |||||
svg/scale=1.0 |
@ -0,0 +1,167 @@ | |||||
[gd_scene load_steps=4 format=2] | |||||
[ext_resource path="res://scripts/ability_icon.gd" type="Script" id=1] | |||||
[sub_resource type="StyleBoxFlat" id=2] | |||||
content_margin_left = -1.0 | |||||
content_margin_right = -1.0 | |||||
content_margin_top = -1.0 | |||||
content_margin_bottom = -1.0 | |||||
bg_color = Color( 0.042572, 0.351562, 0.315353, 1 ) | |||||
draw_center = true | |||||
border_width_left = 0 | |||||
border_width_top = 0 | |||||
border_width_right = 0 | |||||
border_width_bottom = 0 | |||||
border_color = Color( 0.8, 0.8, 0.8, 1 ) | |||||
border_blend = false | |||||
corner_radius_top_left = 0 | |||||
corner_radius_top_right = 0 | |||||
corner_radius_bottom_right = 0 | |||||
corner_radius_bottom_left = 0 | |||||
corner_detail = 8 | |||||
expand_margin_left = 0.0 | |||||
expand_margin_right = 0.0 | |||||
expand_margin_top = 0.0 | |||||
expand_margin_bottom = 0.0 | |||||
shadow_color = Color( 0, 0, 0, 0.6 ) | |||||
shadow_size = 0 | |||||
anti_aliasing = true | |||||
anti_aliasing_size = 1 | |||||
[sub_resource type="StyleBoxFlat" id=1] | |||||
content_margin_left = -1.0 | |||||
content_margin_right = -1.0 | |||||
content_margin_top = -1.0 | |||||
content_margin_bottom = -1.0 | |||||
bg_color = Color( 0.73822, 0.921875, 0.814265, 0.403333 ) | |||||
draw_center = true | |||||
border_width_left = 0 | |||||
border_width_top = 0 | |||||
border_width_right = 0 | |||||
border_width_bottom = 0 | |||||
border_color = Color( 0.8, 0.8, 0.8, 1 ) | |||||
border_blend = false | |||||
corner_radius_top_left = 0 | |||||
corner_radius_top_right = 0 | |||||
corner_radius_bottom_right = 0 | |||||
corner_radius_bottom_left = 0 | |||||
corner_detail = 8 | |||||
expand_margin_left = 0.0 | |||||
expand_margin_right = 0.0 | |||||
expand_margin_top = 0.0 | |||||
expand_margin_bottom = 0.0 | |||||
shadow_color = Color( 0, 0, 0, 0.6 ) | |||||
shadow_size = 0 | |||||
anti_aliasing = true | |||||
anti_aliasing_size = 1 | |||||
[node name="Ability" type="Control"] | |||||
anchor_left = 0.0 | |||||
anchor_top = 0.0 | |||||
anchor_right = 0.0 | |||||
anchor_bottom = 0.0 | |||||
margin_right = 30.0 | |||||
margin_bottom = 30.0 | |||||
rect_pivot_offset = Vector2( 0, 0 ) | |||||
mouse_filter = 0 | |||||
mouse_default_cursor_shape = 0 | |||||
size_flags_horizontal = 1 | |||||
size_flags_vertical = 0 | |||||
script = ExtResource( 1 ) | |||||
cost = 1 | |||||
ability_name = "Ability" | |||||
display_progress = true | |||||
action = "" | |||||
[node name="Bar" type="ProgressBar" parent="." index="0"] | |||||
anchor_left = 0.0 | |||||
anchor_top = 0.0 | |||||
anchor_right = 0.0 | |||||
anchor_bottom = 0.0 | |||||
margin_left = -25.0 | |||||
margin_top = -25.0 | |||||
margin_right = 25.0 | |||||
margin_bottom = 25.0 | |||||
rect_pivot_offset = Vector2( 0, 0 ) | |||||
mouse_filter = 0 | |||||
mouse_default_cursor_shape = 0 | |||||
size_flags_horizontal = 1 | |||||
size_flags_vertical = 0 | |||||
custom_styles/fg = SubResource( 2 ) | |||||
custom_styles/bg = SubResource( 1 ) | |||||
min_value = 0.0 | |||||
max_value = 100.0 | |||||
step = 1.0 | |||||
page = 0.0 | |||||
value = 0.0 | |||||
exp_edit = false | |||||
rounded = false | |||||
percent_visible = false | |||||
[node name="Available" type="ColorRect" parent="." index="1"] | |||||
anchor_left = 0.0 | |||||
anchor_top = 0.0 | |||||
anchor_right = 0.0 | |||||
anchor_bottom = 0.0 | |||||
margin_left = -30.0 | |||||
margin_top = -30.0 | |||||
margin_right = 20.0 | |||||
margin_bottom = 20.0 | |||||
rect_pivot_offset = Vector2( 0, 0 ) | |||||
mouse_filter = 0 | |||||
mouse_default_cursor_shape = 0 | |||||
size_flags_horizontal = 1 | |||||
size_flags_vertical = 0 | |||||
color = Color( 0.347443, 0.898438, 0.549761, 1 ) | |||||
[node name="Name" type="Label" parent="." index="2"] | |||||
anchor_left = 0.0 | |||||
anchor_top = 0.0 | |||||
anchor_right = 0.0 | |||||
anchor_bottom = 0.0 | |||||
margin_left = -72.0 | |||||
margin_top = -44.0 | |||||
margin_right = 70.0 | |||||
margin_bottom = -30.0 | |||||
rect_pivot_offset = Vector2( 0, 0 ) | |||||
mouse_filter = 2 | |||||
mouse_default_cursor_shape = 0 | |||||
size_flags_horizontal = 1 | |||||
size_flags_vertical = 4 | |||||
custom_colors/font_color = Color( 0.00357056, 0.0703125, 0.04372, 1 ) | |||||
text = "<Name>" | |||||
align = 1 | |||||
percent_visible = 1.0 | |||||
lines_skipped = 0 | |||||
max_lines_visible = -1 | |||||
[node name="Button" type="Label" parent="." index="3"] | |||||
anchor_left = 0.0 | |||||
anchor_top = 0.0 | |||||
anchor_right = 0.0 | |||||
anchor_bottom = 0.0 | |||||
margin_left = -72.0 | |||||
margin_top = 29.0 | |||||
margin_right = 70.0 | |||||
margin_bottom = 43.0 | |||||
rect_pivot_offset = Vector2( 0, 0 ) | |||||
mouse_filter = 2 | |||||
mouse_default_cursor_shape = 0 | |||||
size_flags_horizontal = 1 | |||||
size_flags_vertical = 4 | |||||
custom_colors/font_color = Color( 0.00357056, 0.0703125, 0.04372, 1 ) | |||||
text = "<Button>" | |||||
align = 1 | |||||
percent_visible = 1.0 | |||||
lines_skipped = 0 | |||||
max_lines_visible = -1 | |||||
@ -0,0 +1,232 @@ | |||||
[gd_scene load_steps=5 format=2] | |||||
[ext_resource path="res://assets/theme.tres" type="Theme" id=1] | |||||
[ext_resource path="res://scripts/custom_game.gd" type="Script" id=2] | |||||
[ext_resource path="res://assets/DejaVuSansMono.ttf" type="DynamicFontData" id=3] | |||||
[sub_resource type="DynamicFont" id=1] | |||||
size = 30 | |||||
use_mipmaps = false | |||||
use_filter = false | |||||
font_data = ExtResource( 3 ) | |||||
[node name="CustomGame" type="Control"] | |||||
anchor_left = 0.0 | |||||
anchor_top = 0.0 | |||||
anchor_right = 0.0 | |||||
anchor_bottom = 0.0 | |||||
margin_right = 40.0 | |||||
margin_bottom = 40.0 | |||||
rect_pivot_offset = Vector2( 0, 0 ) | |||||
mouse_filter = 0 | |||||
mouse_default_cursor_shape = 0 | |||||
size_flags_horizontal = 1 | |||||
size_flags_vertical = 1 | |||||
theme = ExtResource( 1 ) | |||||
script = ExtResource( 2 ) | |||||
[node name="VSeparator" type="VSeparator" parent="." index="0"] | |||||
anchor_left = 0.0 | |||||
anchor_top = 0.0 | |||||
anchor_right = 0.0 | |||||
anchor_bottom = 0.0 | |||||
margin_left = 498.0 | |||||
margin_top = 140.0 | |||||
margin_right = 518.0 | |||||
margin_bottom = 481.0 | |||||
rect_pivot_offset = Vector2( 0, 0 ) | |||||
mouse_filter = 0 | |||||
mouse_default_cursor_shape = 0 | |||||
size_flags_horizontal = 1 | |||||
size_flags_vertical = 1 | |||||
[node name="Label" type="Label" parent="." index="1"] | |||||
anchor_left = 0.0 | |||||
anchor_top = 0.0 | |||||
anchor_right = 0.0 | |||||
anchor_bottom = 0.0 | |||||
margin_left = 54.0 | |||||
margin_top = 66.0 | |||||
margin_right = 395.0 | |||||
margin_bottom = 102.0 | |||||
rect_pivot_offset = Vector2( 0, 0 ) | |||||
mouse_filter = 2 | |||||
mouse_default_cursor_shape = 0 | |||||
size_flags_horizontal = 1 | |||||
size_flags_vertical = 4 | |||||
custom_fonts/font = SubResource( 1 ) | |||||
text = "Custom Game" | |||||
percent_visible = 1.0 | |||||
lines_skipped = 0 | |||||
max_lines_visible = -1 | |||||
[node name="Server" type="Button" parent="." index="2"] | |||||
anchor_left = 0.0 | |||||
anchor_top = 0.0 | |||||
anchor_right = 0.0 | |||||
anchor_bottom = 0.0 | |||||
margin_left = 73.0 | |||||
margin_top = 195.0 | |||||
margin_right = 366.0 | |||||
margin_bottom = 252.0 | |||||
rect_pivot_offset = Vector2( 0, 0 ) | |||||
focus_mode = 2 | |||||
mouse_filter = 0 | |||||
mouse_default_cursor_shape = 0 | |||||
size_flags_horizontal = 1 | |||||
size_flags_vertical = 1 | |||||
toggle_mode = false | |||||
enabled_focus_mode = 2 | |||||
shortcut = null | |||||
group = null | |||||
text = "Host Game" | |||||
flat = false | |||||
align = 1 | |||||
[node name="Client" type="Button" parent="." index="3"] | |||||
anchor_left = 0.0 | |||||
anchor_top = 0.0 | |||||
anchor_right = 0.0 | |||||
anchor_bottom = 0.0 | |||||
margin_left = 588.0 | |||||
margin_top = 258.0 | |||||
margin_right = 890.0 | |||||
margin_bottom = 315.0 | |||||
rect_pivot_offset = Vector2( 0, 0 ) | |||||
focus_mode = 2 | |||||
mouse_filter = 0 | |||||
mouse_default_cursor_shape = 0 | |||||
size_flags_horizontal = 1 | |||||
size_flags_vertical = 1 | |||||
toggle_mode = false | |||||
enabled_focus_mode = 2 | |||||
shortcut = null | |||||
group = null | |||||
text = "Join Game" | |||||
flat = false | |||||
align = 1 | |||||
[node name="IPLabel" type="Label" parent="." index="4"] | |||||
anchor_left = 0.0 | |||||
anchor_top = 0.0 | |||||
anchor_right = 0.0 | |||||
anchor_bottom = 0.0 | |||||
margin_left = 592.0 | |||||
margin_top = 205.0 | |||||
margin_right = 625.0 | |||||
margin_bottom = 227.0 | |||||
rect_pivot_offset = Vector2( 0, 0 ) | |||||
mouse_filter = 2 | |||||
mouse_default_cursor_shape = 0 | |||||
size_flags_horizontal = 1 | |||||
size_flags_vertical = 4 | |||||
text = "IP:" | |||||
percent_visible = 1.0 | |||||
lines_skipped = 0 | |||||
max_lines_visible = -1 | |||||
[node name="IP" type="TextEdit" parent="." index="5"] | |||||
anchor_left = 0.0 | |||||
anchor_top = 0.0 | |||||
anchor_right = 0.0 | |||||
anchor_bottom = 0.0 | |||||
margin_left = 632.0 | |||||
margin_top = 203.0 | |||||
margin_right = 891.0 | |||||
margin_bottom = 232.0 | |||||
rect_pivot_offset = Vector2( 0, 0 ) | |||||
focus_mode = 2 | |||||
mouse_filter = 0 | |||||
mouse_default_cursor_shape = 0 | |||||
size_flags_horizontal = 1 | |||||
size_flags_vertical = 1 | |||||
text = "127.0.0.1" | |||||
readonly = false | |||||
highlight_current_line = false | |||||
syntax_highlighting = false | |||||
show_line_numbers = false | |||||
highlight_all_occurrences = false | |||||
override_selected_font_color = false | |||||
context_menu_enabled = true | |||||
smooth_scrolling = false | |||||
v_scroll_speed = 80.0 | |||||
hiding_enabled = 0 | |||||
wrap_lines = false | |||||
caret_block_mode = false | |||||
caret_blink = false | |||||
caret_blink_speed = 0.65 | |||||
caret_moving_by_right_click = true | |||||
[node name="Label2" type="Label" parent="." index="6"] | |||||
anchor_left = 0.0 | |||||
anchor_top = 0.0 | |||||
anchor_right = 0.0 | |||||
anchor_bottom = 0.0 | |||||
margin_left = 72.0 | |||||
margin_top = 145.0 | |||||
margin_right = 171.0 | |||||
margin_bottom = 167.0 | |||||
rect_pivot_offset = Vector2( 0, 0 ) | |||||
mouse_filter = 2 | |||||
mouse_default_cursor_shape = 0 | |||||
size_flags_horizontal = 1 | |||||
size_flags_vertical = 4 | |||||
text = "Host game" | |||||
percent_visible = 1.0 | |||||
lines_skipped = 0 | |||||
max_lines_visible = -1 | |||||
[node name="Label3" type="Label" parent="." index="7"] | |||||
anchor_left = 0.0 | |||||
anchor_top = 0.0 | |||||
anchor_right = 0.0 | |||||
anchor_bottom = 0.0 | |||||
margin_left = 577.0 | |||||
margin_top = 147.0 | |||||
margin_right = 617.0 | |||||
margin_bottom = 169.0 | |||||
rect_pivot_offset = Vector2( 0, 0 ) | |||||
mouse_filter = 2 | |||||
mouse_default_cursor_shape = 0 | |||||
size_flags_horizontal = 1 | |||||
size_flags_vertical = 4 | |||||
text = "Join Game" | |||||
percent_visible = 1.0 | |||||
lines_skipped = 0 | |||||
max_lines_visible = -1 | |||||
[node name="Back" type="Button" parent="." index="8"] | |||||
anchor_left = 0.0 | |||||
anchor_top = 0.0 | |||||
anchor_right = 0.0 | |||||
anchor_bottom = 0.0 | |||||
margin_left = 437.0 | |||||
margin_top = 509.0 | |||||
margin_right = 581.0 | |||||
margin_bottom = 537.0 | |||||
rect_pivot_offset = Vector2( 0, 0 ) | |||||
focus_mode = 2 | |||||
mouse_filter = 0 | |||||
mouse_default_cursor_shape = 0 | |||||
size_flags_horizontal = 1 | |||||
size_flags_vertical = 1 | |||||
toggle_mode = false | |||||
enabled_focus_mode = 2 | |||||
shortcut = null | |||||
group = null | |||||
text = "Back to menu" | |||||
flat = false | |||||
align = 1 | |||||
@ -0,0 +1,164 @@ | |||||
[gd_scene load_steps=5 format=2] | |||||
[ext_resource path="res://assets/theme.tres" type="Theme" id=1] | |||||
[ext_resource path="res://scripts/menu.gd" type="Script" id=2] | |||||
[sub_resource type="DynamicFontData" id=1] | |||||
font_path = "res://assets/DejaVuSansMono.ttf" | |||||
[sub_resource type="DynamicFont" id=2] | |||||
size = 30 | |||||
use_mipmaps = false | |||||
use_filter = false | |||||
font_data = SubResource( 1 ) | |||||
[node name="Menu" type="Control"] | |||||
anchor_left = 0.0 | |||||
anchor_top = 0.0 | |||||
anchor_right = 1.0 | |||||
anchor_bottom = 1.0 | |||||
rect_pivot_offset = Vector2( 0, 0 ) | |||||
mouse_filter = 0 | |||||
mouse_default_cursor_shape = 0 | |||||
size_flags_horizontal = 1 | |||||
size_flags_vertical = 1 | |||||
theme = ExtResource( 1 ) | |||||
script = ExtResource( 2 ) | |||||
[node name="Center" type="Control" parent="." index="0"] | |||||
anchor_left = 0.5 | |||||
anchor_top = 0.5 | |||||
anchor_right = 0.5 | |||||
anchor_bottom = 0.5 | |||||
margin_left = -20.0 | |||||
margin_top = -20.0 | |||||
margin_right = 20.0 | |||||
margin_bottom = 20.0 | |||||
rect_pivot_offset = Vector2( 0, 0 ) | |||||
mouse_filter = 0 | |||||
mouse_default_cursor_shape = 0 | |||||
size_flags_horizontal = 1 | |||||
size_flags_vertical = 1 | |||||
[node name="Play" type="Button" parent="Center" index="0"] | |||||
anchor_left = 0.0 | |||||
anchor_top = 0.0 | |||||
anchor_right = 0.0 | |||||
anchor_bottom = 0.0 | |||||
margin_left = -250.0 | |||||
margin_top = -140.0 | |||||
margin_right = 298.0 | |||||
margin_bottom = -80.0 | |||||
rect_pivot_offset = Vector2( 0, 0 ) | |||||
focus_mode = 2 | |||||
mouse_filter = 0 | |||||
mouse_default_cursor_shape = 0 | |||||
size_flags_horizontal = 1 | |||||
size_flags_vertical = 1 | |||||
toggle_mode = false | |||||
enabled_focus_mode = 2 | |||||
shortcut = null | |||||
group = null | |||||
text = "Quick Play" | |||||
flat = false | |||||
align = 1 | |||||
[node name="CustomGame" type="Button" parent="Center" index="1"] | |||||
anchor_left = 0.0 | |||||
anchor_top = 0.0 | |||||
anchor_right = 0.0 | |||||
anchor_bottom = 0.0 | |||||
margin_left = -252.0 | |||||
margin_top = -56.0 | |||||
margin_right = 300.0 | |||||
margin_bottom = 5.0 | |||||
rect_pivot_offset = Vector2( 0, 0 ) | |||||
focus_mode = 2 | |||||
mouse_filter = 0 | |||||
mouse_default_cursor_shape = 0 | |||||
size_flags_horizontal = 1 | |||||
size_flags_vertical = 1 | |||||
toggle_mode = false | |||||
enabled_focus_mode = 2 | |||||
shortcut = null | |||||
group = null | |||||
text = "Custom Game" | |||||
flat = false | |||||
align = 1 | |||||
[node name="Singleplayer" type="Button" parent="Center" index="2"] | |||||
anchor_left = 0.0 | |||||
anchor_top = 0.0 | |||||
anchor_right = 0.0 | |||||
anchor_bottom = 0.0 | |||||
margin_left = -251.0 | |||||
margin_top = 34.0 | |||||
margin_right = 299.0 | |||||
margin_bottom = 100.0 | |||||
rect_pivot_offset = Vector2( 0, 0 ) | |||||
focus_mode = 2 | |||||
mouse_filter = 0 | |||||
mouse_default_cursor_shape = 0 | |||||
size_flags_horizontal = 1 | |||||
size_flags_vertical = 1 | |||||
toggle_mode = false | |||||
enabled_focus_mode = 2 | |||||
shortcut = null | |||||
group = null | |||||
text = "Practice Range" | |||||
flat = false | |||||
align = 1 | |||||
[node name="Quit" type="Button" parent="Center" index="3"] | |||||
anchor_left = 0.0 | |||||
anchor_top = 0.0 | |||||
anchor_right = 0.0 | |||||
anchor_bottom = 0.0 | |||||
margin_left = -252.0 | |||||
margin_top = 130.0 | |||||
margin_right = 298.0 | |||||
margin_bottom = 196.0 | |||||
rect_pivot_offset = Vector2( 0, 0 ) | |||||
focus_mode = 2 | |||||
mouse_filter = 0 | |||||
mouse_default_cursor_shape = 0 | |||||
size_flags_horizontal = 1 | |||||
size_flags_vertical = 1 | |||||
toggle_mode = false | |||||
enabled_focus_mode = 2 | |||||
shortcut = null | |||||
group = null | |||||
text = "Quit" | |||||
flat = false | |||||
align = 1 | |||||
[node name="Title" type="Label" parent="Center" index="4"] | |||||
anchor_left = 0.0 | |||||
anchor_top = 0.0 | |||||
anchor_right = 0.0 | |||||
anchor_bottom = 0.0 | |||||
margin_left = -72.0 | |||||
margin_top = -230.0 | |||||
margin_right = 116.0 | |||||
margin_bottom = -190.0 | |||||
rect_pivot_offset = Vector2( 0, 0 ) | |||||
mouse_filter = 2 | |||||
mouse_default_cursor_shape = 0 | |||||
size_flags_horizontal = 1 | |||||
size_flags_vertical = 4 | |||||
custom_fonts/font = SubResource( 2 ) | |||||
text = "VANAGLORIA" | |||||
percent_visible = 1.0 | |||||
lines_skipped = 0 | |||||
max_lines_visible = -1 | |||||
@ -0,0 +1,87 @@ | |||||
[gd_scene load_steps=4 format=2] | |||||
[ext_resource path="res://scenes/lobby.tscn" type="PackedScene" id=1] | |||||
[ext_resource path="res://assets/DejaVuSansMono.ttf" type="DynamicFontData" id=2] | |||||
[sub_resource type="DynamicFont" id=1] | |||||
size = 40 | |||||
use_mipmaps = false | |||||
use_filter = false | |||||
font_data = ExtResource( 2 ) | |||||
[node name="Lobby" instance=ExtResource( 1 )] | |||||
[node name="Label" type="Label" parent="." index="0"] | |||||
anchor_left = 0.0 | |||||
anchor_top = 0.0 | |||||
anchor_right = 0.0 | |||||
anchor_bottom = 0.0 | |||||
margin_left = 602.0 | |||||
margin_top = 276.0 | |||||
margin_right = 938.0 | |||||
margin_bottom = 324.0 | |||||
rect_pivot_offset = Vector2( 0, 0 ) | |||||
mouse_filter = 2 | |||||
mouse_default_cursor_shape = 0 | |||||
size_flags_horizontal = 1 | |||||
size_flags_vertical = 4 | |||||
custom_fonts/font = SubResource( 1 ) | |||||
text = "Practice Range" | |||||
percent_visible = 1.0 | |||||
lines_skipped = 0 | |||||
max_lines_visible = -1 | |||||
[node name="HeroSelect" parent="." index="1"] | |||||
margin_top = 62.0 | |||||
margin_bottom = 62.0 | |||||
[node name="Username" parent="." index="2"] | |||||
visible = false | |||||
margin_top = 308.0 | |||||
margin_bottom = 341.0 | |||||
[node name="Spectating" parent="." index="3"] | |||||
visible = false | |||||
[node name="LevelSelect" parent="." index="4"] | |||||
margin_top = 401.0 | |||||
margin_bottom = 442.0 | |||||
items = [ "Platform map", null, false, 0, null, "City-like thing", null, false, 1, null, "Slide", null, false, 2, null ] | |||||
[node name="TeamLabel" parent="." index="5"] | |||||
visible = false | |||||
[node name="Team" parent="." index="6"] | |||||
visible = false | |||||
[node name="PlayerList" parent="." index="7"] | |||||
visible = false | |||||
[node name="Ready" parent="." index="8"] | |||||
visible = false | |||||
[node name="StartGame" parent="." index="9"] | |||||
visible = true | |||||
margin_left = 41.0 | |||||
margin_top = 486.0 | |||||
margin_right = 163.0 | |||||
margin_bottom = 526.0 | |||||
text = "Play!" | |||||
[node name="VSeparator" parent="." index="10"] | |||||
visible = false | |||||
[editable path="HeroSelect"] |
@ -0,0 +1,51 @@ | |||||
extends Control | |||||
onready var hero = get_node("../..") | |||||
onready var bar = get_node("Bar") | |||||
onready var available = get_node("Available") | |||||
export var cost = 1 | |||||
export var ability_name = "Ability" | |||||
export var display_progress = true | |||||
export var action = "" | |||||
# This is intended to be public | |||||
var disabled = false | |||||
func _ready(): | |||||
get_node("Name").text = ability_name | |||||
var description | |||||
if action: | |||||
var primary = InputMap.get_action_list(action)[0] | |||||
if primary is InputEventMouseButton: | |||||
if primary.button_index == BUTTON_LEFT: | |||||
description = "Click" | |||||
elif primary.button_index == BUTTON_RIGHT: | |||||
description = "Right Click" | |||||
else: | |||||
description = "Scroll Click" | |||||
else: | |||||
description = primary.as_text() | |||||
else: | |||||
description = "" | |||||
get_node("Button").text = description | |||||
func is_pressed(): | |||||
return Input.is_action_pressed(action) | |||||
func _process(delta): | |||||
if action and Input.is_action_pressed(action): | |||||
available.rect_position = Vector2(-25, -25) # Centered / not offset | |||||
else: | |||||
available.rect_position = Vector2(-30, -30) | |||||
if disabled: | |||||
available.hide() | |||||
bar.value = 0 | |||||
else: | |||||
if display_progress: | |||||
if cost == 0: | |||||
bar.value = 100 if hero.charge > 0 else 0 | |||||
else: | |||||
bar.value = 100 * hero.charge / cost | |||||
if hero.charge > cost: | |||||
available.show() | |||||
else: | |||||
available.hide() |
@ -0,0 +1,14 @@ | |||||
extends Control | |||||
func _ready(): | |||||
get_node("Server").connect("pressed", self, "_start_server") | |||||
get_node("Client").connect("pressed", self, "_start_client") | |||||
get_node("Back").connect("pressed", get_tree(), "change_scene", ["res://scenes/menu.tscn"]) | |||||
func _start_server(): | |||||
# Custom Game can assume we're playing as well | |||||
networking.start_server() | |||||
func _start_client(): | |||||
var ip = get_node("IP").text | |||||
networking.start_client(ip) |
@ -1,274 +1,114 @@ | |||||
extends "res://scripts/args.gd" | |||||
extends Control | |||||
# class member variables go here, for example: | |||||
# var a = 2 | |||||
# var b = "textvar" | |||||
var SERVER_PORT = 54672 | |||||
var MAX_PLAYERS = 10 | |||||
var player_info = {} | |||||
var my_info = {} | |||||
var begun = false | |||||
var server_playing = true | |||||
var global_server_ip = "nv.cosinegaming.com" | |||||
var players_done = [] | |||||
func setup_options(): | |||||
var opts = Options.new() | |||||
opts.set_banner(('A non-violent MOBA inspired by Overwatch and Zineth')) | |||||
opts.add('-singleplayer', false, 'Whether to run singeplayer, starting immediately') | |||||
opts.add('-server', false, 'Whether to run as server') | |||||
opts.add('-client', false, 'Immediately connect as client') | |||||
opts.add('-silent', false, 'If the server is not playing, merely serving') | |||||
opts.add('-hero', 'r', 'Your choice of hero (index)') | |||||
opts.add('-level', 'r', 'Your choice of level (index) - server only!') | |||||
opts.add('-start-game', false, 'Join as a client and immediately start the game') | |||||
opts.add('-ai', true, 'Run this client as AI') | |||||
opts.add('-no-record', true, "Don't record this play for AI later") | |||||
opts.add('-h', false, "Print help") | |||||
return opts | |||||
func option_sel(button_name, option): | |||||
var button = get_node(button_name) | |||||
if option == "r": | |||||
option = randi() % button.get_item_count() | |||||
else: | |||||
option = int(option) | |||||
button.select(option) | |||||
onready var hero_select = get_node("HeroSelect/Hero") | |||||
onready var level_select = get_node("LevelSelect") | |||||
onready var start_game_button = get_node("StartGame") | |||||
onready var ready_button = get_node("Ready") | |||||
func _ready(): | func _ready(): | ||||
randomize() | |||||
get_node("GameBrowser/Play").connect("pressed", self, "connect_global_server") | |||||
get_node("PlayerSettings/HeroSelect").connect("item_selected", self, "select_hero") | |||||
get_node("PlayerSettings/Username").connect("text_changed", self, "resend_name") | |||||
get_node("JoinedGameLobby/StartGame").connect("pressed", self, "start_game") | |||||
get_node("CustomGame/Server").connect("pressed", self, "_server_init") | |||||
get_node("CustomGame/Client").connect("pressed", self, "_client_init") | |||||
get_node("CustomGame/Singleplayer").connect("pressed", self, "_singleplayer_init") | |||||
get_node("CustomGame/LevelSelect").connect("item_selected", self, "select_level") | |||||
var o = setup_options() | |||||
o.parse() | |||||
if o.get_value("-silent"): | |||||
server_playing = false # TODO: Uncaps :( | |||||
if o.get_value("-hero"): | |||||
var hero = o.get_value("-hero") | |||||
option_sel("PlayerSettings/HeroSelect", hero) | |||||
# For some reason, calling option_sel doesn't trigger the actual selection | |||||
select_hero(get_node("PlayerSettings/HeroSelect").get_selected_id()) | |||||
if o.get_value("-level"): | |||||
option_sel("CustomGame/LevelSelect", o.get_value("-level")) | |||||
if o.get_value("-server"): | |||||
call_deferred("_server_init") | |||||
if o.get_value("-client"): | |||||
call_deferred("_client_init") | |||||
if o.get_value("-start-game"): | |||||
my_info.start_game = true | |||||
call_deferred("_client_init") | |||||
if o.get_value("-singleplayer"): | |||||
call_deferred("_singleplayer_init") | |||||
if o.get_value("-ai"): | |||||
my_info.is_ai = true | |||||
if not o.get_value("-no-record") and not o.get_value("-ai"): | |||||
my_info.record = true | |||||
if o.get_value('-h'): | |||||
o.print_help() | |||||
quit() | |||||
get_tree().connect("network_peer_connected", self, "_player_connected") | |||||
get_tree().connect("network_peer_disconnected", self, "_player_disconnected") | |||||
get_tree().connect("connected_to_server", self, "_connected_ok") | |||||
func connect_global_server(): | |||||
_client_init(global_server_ip) | |||||
func _client_init(ip=null): | |||||
collect_info() | |||||
var peer = NetworkedMultiplayerENet.new() | |||||
if not ip: | |||||
ip = get_node("CustomGame/IP").get_text() | |||||
ip = IP.resolve_hostname(ip) | |||||
peer.create_client(ip, SERVER_PORT) | |||||
get_tree().set_network_peer(peer) | |||||
get_node("CustomGame/Client").set_text("Clienting!") | |||||
func _singleplayer_init(): | |||||
collect_info() | |||||
var peer = NetworkedMultiplayerENet.new() | |||||
peer.create_server(SERVER_PORT, 1) | |||||
get_tree().set_network_peer(peer) | |||||
player_info[1] = my_info | |||||
start_game() | |||||
func _server_init(): | |||||
collect_info() | |||||
var peer = NetworkedMultiplayerENet.new() | |||||
peer.create_server(SERVER_PORT, MAX_PLAYERS) | |||||
get_tree().set_network_peer(peer) | |||||
get_node("CustomGame/Server").set_text("Serving!") | |||||
get_node("JoinedGameLobby").show() | |||||
if server_playing: | |||||
player_info[1] = my_info | |||||
# Connect (to networking) | |||||
get_node("Username").connect("text_changed", self, "_send_name") | |||||
get_node("Spectating").connect("toggled", self, "_set_info_callback", ["spectating"]) | |||||
ready_button.connect("toggled", self, "_set_info_callback", ["ready"]) | |||||
start_game_button.connect("pressed", networking, "start_game") | |||||
# Connect (from networking) | |||||
networking.connect("info_updated", self, "_render_player_list") | |||||
get_tree().connect("connected_to_server", self, "_connected") | |||||
# Connect (static) | |||||
get_node("Back").connect("pressed", self, "_exit_to_menu") | |||||
var spectating = util.args.get_value("-silent") | |||||
get_node("Spectating").pressed = spectating | |||||
# Shown, maybe, in _check_begun | |||||
start_game_button.hide() | |||||
if get_tree().is_network_server(): | |||||
start_game_button.show() | |||||
func _player_connected(id): | |||||
pass | |||||
if get_tree().is_network_server(): | |||||
# We put level in our players dict because it's automatically broadcast to other players | |||||
var level = util.args.get_value("-level") | |||||
if level == "r": | |||||
level = randi() % level_select.get_item_count() | |||||
level = int(level) | |||||
_set_level(level) | |||||
level_select.show() | |||||
level_select.select(level) | |||||
level_select.connect("item_selected", self, "_set_level") | |||||
else: | |||||
level_select.hide() | |||||
func _player_disconnected(id): | |||||
if get_tree().is_network_server(): | if get_tree().is_network_server(): | ||||
rpc("unregister_player", id) | |||||
call_deferred("render_player_list") | |||||
_connected() | |||||
func _connected_ok(): | |||||
rpc("register_player", get_tree().get_network_unique_id(), my_info) | |||||
if "start_game" in my_info and my_info.start_game: | |||||
rpc_id(1, "start_game") | |||||
get_node("JoinedGameLobby").show() | |||||
func _connected(): | |||||
func collect_info(): | |||||
if not "username" in my_info: | |||||
my_info.username = get_node("PlayerSettings/Username").get_text() | |||||
if not "hero" in my_info: | |||||
my_info.hero = get_node("PlayerSettings/HeroSelect").get_selected_id() | |||||
if not "is_right_team" in my_info: | |||||
my_info.is_right_team = false # Server assigns team, wait for that | |||||
my_info.version = util.version | |||||
_send_name() | |||||
networking.set_info_from_server("ready", false) | |||||
networking.set_info_from_server("spectating", util.args.get_value("-silent")) | |||||
networking.set_info_from_server("begun", false) | |||||
if util.args.get_value("-hero") == "r": | |||||
hero_select.random_hero() | |||||
else: | |||||
hero_select.set_hero(int(util.args.get_value("-hero"))) | |||||
remote func register_player(new_peer, info): | |||||
var p_version = info.version.split(".") | |||||
var version_split = util.version.split(".") | |||||
if p_version[0] != version_split[0]: | |||||
# TODO: Fail gracefully | |||||
return | |||||
player_info[new_peer] = info | |||||
render_player_list() | |||||
if (get_tree().is_network_server()): | |||||
var right_team_count = 0 | |||||
# Send current players' info to new player | |||||
for old_peer in player_info: | |||||
# Send new player, old player's info | |||||
rpc_id(new_peer, "register_player", old_peer, player_info[old_peer]) | |||||
if old_peer != new_peer: | |||||
# We need to assign team later, so count current | |||||
if player_info[old_peer].is_right_team: | |||||
right_team_count += 1 | |||||
# You'd think this part could be met with a simple `rpc(`, but actually it can't | |||||
# My best guess is this is because we haven't registered the names yet, but I'm not sure (TODO) | |||||
if old_peer != 1: | |||||
# Send old player, new player's info (not us, no infinite loop) | |||||
rpc_id(old_peer, "register_player", new_peer, info) | |||||
if begun: | |||||
rpc_id(old_peer, "spawn_player", new_peer) | |||||
rpc_id(old_peer, "begin_player_deferred", new_peer) # Spawning is deferred | |||||
if not server_playing: | |||||
# We didn't catch this in player_info | |||||
rpc_id(1, "spawn_player", new_peer) | |||||
rpc_id(1, "begin_player_deferred", new_peer) # Spawning is deferred | |||||
var assign_right_team = right_team_count * 2 < player_info.size() | |||||
rpc("assign_team", new_peer, assign_right_team) | |||||
if not begun and player_info.size() == MAX_PLAYERS: | |||||
start_game() | |||||
if begun: | |||||
rpc_id(new_peer, "pre_configure_game", my_info.level) | |||||
rpc_id(new_peer, "post_configure_game") | |||||
if util.args.get_value("-start-game"): | |||||
networking.start_game() | |||||
sync func unregister_player(peer): | |||||
player_info.erase(peer) | |||||
get_node("/root/Level/Players/%d" % peer).queue_free() | |||||
func _set_level(level): | |||||
networking.level = level | |||||
func select_hero(hero): | |||||
var description = get_node("PlayerSettings/HeroSelect").hero_text[hero] | |||||
get_node("PlayerSettings/HeroDescription").set_text(description) | |||||
rpc("set_hero", get_tree().get_network_unique_id(), hero) | |||||
# Because of the annoying way callbacks work (automatic parameter, optional parameter) | |||||
# We need a utility function for making these kinds of callbacks for set_info | |||||
func _set_info_callback(value, key): | |||||
networking.set_info_from_server(key, value) | |||||
sync func set_hero(peer, hero): | sync func set_hero(peer, hero): | ||||
player_info[peer].hero = hero | |||||
render_player_list() | |||||
func resend_name(): | |||||
var name = get_node("PlayerSettings/Username").get_text() | |||||
rpc("set_name", get_tree().get_network_unique_id(), name) | |||||
sync func set_name(peer, name): | |||||
player_info[peer].username = name | |||||
render_player_list() | |||||
sync func assign_team(peer, is_right_team): | |||||
player_info[peer].is_right_team = is_right_team | |||||
if peer == get_tree().get_network_unique_id(): | |||||
if is_right_team: | |||||
get_node("PlayerSettings/Team").set_text("Right Team") | |||||
else: | |||||
get_node("PlayerSettings/Team").set_text("Left Team") | |||||
render_player_list() | |||||
func render_player_list(): | |||||
if has_node("PlayerSettings"): | |||||
var list = "" | |||||
var hero_names = get_node("PlayerSettings/HeroSelect").hero_names | |||||
for p in player_info: | |||||
list += "%-15s" % player_info[p].username | |||||
list += "%-20s" % hero_names[player_info[p].hero] | |||||
if player_info[p].is_right_team: | |||||
list += "Right Team" | |||||
else: | |||||
list += "Left Team" | |||||
networking.players[peer].hero = hero | |||||
_render_player_list() | |||||
func _send_name(): | |||||
var name = get_node("Username").text | |||||
networking.set_info_from_server("username", name) | |||||
func _check_begun(): | |||||
if networking.players.has(1) and networking.players[1].has("begun"): | |||||
var game_started = networking.players[1].begun | |||||
if game_started: | |||||
start_game_button.show() | |||||
start_game_button.text = "Join game" | |||||
# The "Ready" toggle doesn't really make sense on a started game | |||||
ready_button.hide() | |||||
func _render_player_list(): | |||||
_check_begun() | |||||
var list = "" | |||||
var hero_names = hero_select.hero_names | |||||
for p in networking.players: | |||||
var player = networking.players[p] | |||||
# A spectating server is just a dedicated server, ignore it | |||||
if p and player.has("spectating") and not (player.spectating and p == 1): | |||||
var username = player.username if player.has("username") else "Loading..." | |||||
list += "%-15s " % username | |||||
var hero = hero_names[player.hero] if player.has("hero") else "Loading..." | |||||
list += "%-10s " % hero | |||||
var team = "Loading..." | |||||
if player.has("is_right_team"): | |||||
if player.is_right_team: | |||||
team = "Right Team" | |||||
else: | |||||
team = "Left Team" | |||||
list += "%-11s" % team | |||||
var ready_text = "Ready" if player.has("ready") and player.ready else "" | |||||
list += "%-6s" % ready_text | |||||
if player.has("spectating") and player.spectating: | |||||
list += "Spectating" | |||||
list += "\n" | list += "\n" | ||||
get_node("JoinedGameLobby/PlayerList").set_text(list) | |||||
sync func start_game(): | |||||
my_info.level = get_node("CustomGame/LevelSelect").get_selected_id() | |||||
rpc("pre_configure_game", my_info.level) | |||||
sync func done_preconfiguring(who): | |||||
players_done.append(who) | |||||
if players_done.size() == player_info.size(): | |||||
# We call deferred in case singleplayer has placing the player in queue still | |||||
call_deferred("rpc", "post_configure_game") | |||||
sync func spawn_player(p): | |||||
var hero = player_info[p].hero | |||||
var player = load("res://scenes/heroes/" + str(hero) + ".tscn").instance() | |||||
player.set_name(str(p)) | |||||
player.set_network_master(p) | |||||
player.player_info = player_info[p] | |||||
get_node("/root/Level/Players").call_deferred("add_child", player) | |||||
sync func pre_configure_game(level): | |||||
begun = true | |||||
my_info.level = level # Remember the level for future player registration | |||||
var self_peer_id = get_tree().get_network_unique_id() | |||||
# Remove the interface so as to not fuck with things | |||||
# But we still need the lobby alive to deal with networking! | |||||
for element in get_node("/root/Lobby").get_children(): | |||||
element.queue_free() | |||||
var world = load("res://scenes/levels/%d.tscn" % level).instance() | |||||
get_node("/root").add_child(world) | |||||
# Load all players (including self) | |||||
for p in player_info: | |||||
player_info[p].level = level | |||||
spawn_player(p) | |||||
rpc_id(1, "done_preconfiguring", self_peer_id) | |||||
func begin_player(peer): | |||||
get_node("/root/Level/Players/%d" % peer).begin() | |||||
remote func begin_player_deferred(peer): | |||||
call_deferred("begin_player", peer) | |||||
sync func reset_state(): | |||||
players_done = [] | |||||
get_node("/root/Level").queue_free() | |||||
get_node("PlayerList").set_text(list) | |||||
sync func post_configure_game(): | |||||
# Begin all players (including self) | |||||
for p in player_info: | |||||
begin_player_deferred(p) | |||||
func _exit_to_menu(): | |||||
get_tree().network_peer.close_connection() | |||||
get_tree().change_scene("res://scenes/menu.tscn") | |||||
@ -0,0 +1,105 @@ | |||||
extends Node | |||||
var SERVER_TO_SERVER_PORT = 54671 | |||||
var MATCHMAKING_PORT = 54672 | |||||
var GAME_SIZE = 6 | |||||
# Number of games we can make without blowing up the computer | |||||
var MAX_GAMES = 50 # Based on how many ports I decided to forward | |||||
var next_port = 54673 | |||||
# Filled with queue info which contains | |||||
# { "netid" } | |||||
var skirmishing_players = [] | |||||
var skirmish | |||||
# To avoid the confusion of the gameSERVERS being CLIENTS, | |||||
# we just call them games whenever possible | |||||
# var games = [] | |||||
var game_connections = [] | |||||
var game_streams = [] | |||||
# Matchmaker to game servers | |||||
var matchmaker_to_games | |||||
enum messages { | |||||
ready_to_connect, | |||||
} | |||||
onready var lobby = get_node("..") | |||||
func _ready(): | |||||
# By default, having this node doesn't do naything | |||||
# You must call start_matchmaker to enable it | |||||
# If not called, don't call _process (= don't matchmake) | |||||
set_process(false) | |||||
func start_matchmaker(): | |||||
# Actually run the matchmaker | |||||
set_process(true) | |||||
# Setup skirmish server | |||||
skirmish = spawn_server(true) | |||||
# Set up communication between GAMESERVERS | |||||
# This is necessary for eg, when a player leaves to backfill | |||||
matchmaker_to_games = TCP_Server.new() | |||||
if matchmaker_to_games.listen(SERVER_TO_SERVER_PORT): | |||||
print("Error, could not listen") | |||||
# Use ENet for matchmaker because we can (makes client code cleaner) | |||||
var matchmaker_to_players = NetworkedMultiplayerENet.new() | |||||
print("Starting matchmaker on port " + str(MATCHMAKING_PORT)) | |||||
matchmaker_to_players.create_server(MATCHMAKING_PORT, MAX_GAMES) | |||||
get_tree().set_network_peer(matchmaker_to_players) | |||||
matchmaker_to_players.connect("peer_connected", self, "queue") | |||||
func _process(delta): | |||||
# Manage connection to GAMESERVERS (not clients) | |||||
if matchmaker_to_games.is_connection_available(): # check if a gameserver's trying to connect | |||||
var game = matchmaker_to_games.take_connection() # accept connection | |||||
game_connections.append(game) # store the connection | |||||
var stream = PacketPeerStream.new() | |||||
stream.set_stream_peer(game) # bind peerstream to new client | |||||
game_streams.append(stream) # make new data transfer object for game | |||||
for stream in game_streams: | |||||
if stream.get_available_packet_count(): | |||||
var message = stream.get_var() | |||||
if message == messages.ready_to_connect: | |||||
var port = stream.get_var() | |||||
print("Server " + str(port) + " has requested connection") | |||||
skirmish_to_game(port, GAME_SIZE) | |||||
func queue(netid): | |||||
print("Player " + str(netid) + " connected.") | |||||
add_to_game(netid, skirmish) | |||||
skirmishing_players.append(netid) | |||||
check_queue() | |||||
func add_to_game(netid, port): | |||||
networking.rpc_id(netid, "reconnect", networking.global_server_ip, port) | |||||
func skirmish_to_game(port, count=1): | |||||
for i in range(count): | |||||
if not skirmishing_players.size(): | |||||
return false | |||||
print(skirmishing_players[0]) | |||||
print("to") | |||||
print(port) | |||||
add_to_game(skirmishing_players[0], port) | |||||
return true | |||||
func check_queue(): | |||||
# Prefer making a full game to backfilling | |||||
if skirmishing_players.size() >= GAME_SIZE: | |||||
spawn_server() | |||||
# games.append(port) | |||||
func spawn_server(skirmish=false): | |||||
var args = ['-port='+str(next_port)] | |||||
if skirmish: | |||||
# Begin skirmish immediately, so players "join" instead of "ready" | |||||
args.append("-start-game") | |||||
OS.execute("util/server.sh", args, false) | |||||
next_port += 1 | |||||
return (next_port - 1) # Return original port | |||||
@ -0,0 +1,56 @@ | |||||
extends Control | |||||
func _ready(): | |||||
randomize() | |||||
_gui_setup() | |||||
_arg_actions() | |||||
# GUI | |||||
func _gui_setup(): | |||||
get_node("Center/Play").connect("pressed", self, "_find_game") | |||||
get_node("Center/CustomGame").connect("pressed", self, "_custom_game") | |||||
get_node("Center/Singleplayer").connect("pressed", self, "_singleplayer") | |||||
get_node("Center/Quit").connect("pressed", get_tree(), "quit") | |||||
func _find_game(): | |||||
var ip = networking.global_server_ip | |||||
# var ip = util.args.get_value("-ip") | |||||
var port = networking.matchmaking.MATCHMAKING_PORT | |||||
networking.start_client(ip, port) | |||||
func _custom_game(): | |||||
get_tree().change_scene("res://scenes/custom_game.tscn") | |||||
func _singleplayer(): | |||||
networking.start_server() | |||||
get_tree().change_scene("res://scenes/singleplayer_lobby.tscn") | |||||
# Command line | |||||
func _option_sel(button_name, option): | |||||
var button = get_node(button_name) | |||||
if option == "r": | |||||
option = randi() % button.get_item_count() | |||||
else: | |||||
option = int(option) | |||||
button.select(option) | |||||
func _arg_actions(): | |||||
var o = util.args | |||||
# if o.get_value("-ai"): | |||||
# my_info.is_ai = true | |||||
# if not o.get_value("-no-record") and not o.get_value("-ai"): | |||||
# my_info.record = true | |||||
if o.get_value("-server"): | |||||
networking.start_server() | |||||
if o.get_value("-matchmaker"): | |||||
networking.matchmaking.start_matchmaker() | |||||
if o.get_value("-client"): | |||||
networking.start_client() | |||||
if o.get_value("-singleplayer"): | |||||
_singleplayer() | |||||
if o.get_value('-h'): | |||||
o.print_help() | |||||
get_tree().quit() | |||||
@ -0,0 +1,202 @@ | |||||
extends Node | |||||
# Public variables | |||||
# ================ | |||||
onready var matchmaking = preload("res://scripts/matchmaking.gd").new() | |||||
remote var players = {} | |||||
var global_server_ip = "nv.cosinegaming.com" | |||||
var matchmaker_tcp | |||||
var level | |||||
signal info_updated | |||||
# Public methods | |||||
# ============== | |||||
func start_client(ip="", port=0): | |||||
if not ip: | |||||
ip = util.args.get_value("-ip") | |||||
ip = IP.resolve_hostname(ip) | |||||
if not port: | |||||
port = util.args.get_value("-port") | |||||
var peer = NetworkedMultiplayerENet.new() | |||||
print("Connecting to " + ip + ":" + str(port)) | |||||
peer.create_client(ip, port) | |||||
get_tree().set_network_peer(peer) | |||||
get_tree().change_scene("res://scenes/lobby.tscn") | |||||
remote func reconnect(ip, port): | |||||
# Reset previously known players | |||||
players = {} | |||||
start_client(ip, port) | |||||
func start_server(port=0): | |||||
if not port: | |||||
port = util.args.get_value("-port") | |||||
var peer = NetworkedMultiplayerENet.new() | |||||
print("Starting server on port " + str(port)) | |||||
peer.create_server(port, matchmaking.GAME_SIZE) | |||||
get_tree().set_network_peer(peer) | |||||
# As soon as we're listening, let the matchmaker know | |||||
_connect_to_matchmaker(port) | |||||
_register_player(get_tree().get_network_unique_id()) | |||||
if util.args.get_value("-silent"): | |||||
set_info("spectating", true) | |||||
get_tree().change_scene("res://scenes/lobby.tscn") | |||||
master func set_info(key, value, peer=0): | |||||
if not peer: | |||||
peer = get_tree().get_network_unique_id() | |||||
rpc("_set_info", str(key), value, peer) | |||||
# When connectivity is not yet guaranteed, the only one we know is always | |||||
# connected to everyone is the server. So in initial handshakes, it's better to | |||||
# tell the server what to tell everyone to do | |||||
func set_info_from_server(key, value, peer=0): | |||||
if not peer: | |||||
peer = get_tree().get_network_unique_id() | |||||
rpc_id(1, "set_info", key, value, peer) | |||||
func start_game(): | |||||
rpc_id(1, "_start_game") | |||||
sync func reset_state(): | |||||
for p in players: | |||||
players[p].begun = false | |||||
# TODO: Do I in fact want to unready everyone automatically? | |||||
players[p].ready = false | |||||
get_node("/root/Level").queue_free() | |||||
# Private methods | |||||
# =============== | |||||
func _ready(): | |||||
add_child(matchmaking) | |||||
get_tree().connect("network_peer_disconnected", self, "_unregister_player") | |||||
get_tree().connect("network_peer_connected", self, "_register_player") | |||||
get_tree().connect("connected_to_server", self, "_on_connect") | |||||
connect("info_updated", self, "_check_info") | |||||
remote func _register_player(new_peer): | |||||
if get_tree().is_network_server(): | |||||
# I tell new player about all the existing people | |||||
_send_all_info(new_peer) | |||||
set_info("is_right_team", _right_team_next(), new_peer) | |||||
sync func _unregister_player(peer): | |||||
players.erase(peer) | |||||
var p = util.get_player(peer) | |||||
if p: | |||||
p.queue_free() | |||||
emit_signal("info_updated") | |||||
func _connect_to_matchmaker(game_port): | |||||
var matchmaker_peer = StreamPeerTCP.new() | |||||
matchmaker_peer.connect_to_host("127.0.0.1", matchmaking.SERVER_TO_SERVER_PORT) | |||||
var matchmaker_tcp = PacketPeerStream.new() | |||||
matchmaker_tcp.set_stream_peer(matchmaker_peer) | |||||
matchmaker_tcp.put_var(matchmaking.messages.ready_to_connect) | |||||
matchmaker_tcp.put_var(game_port) | |||||
master func _start_game(): | |||||
rpc("_pre_configure_game", level) | |||||
func _send_all_info(new_peer): | |||||
for p in players: | |||||
if p != new_peer: | |||||
for key in players[p]: | |||||
var val = players[p][key] | |||||
# TODO: This broadcasts every connected peer, | |||||
# which isn't really a problem but it's lazy | |||||
set_info(key, val, p) | |||||
func _right_team_next(): | |||||
var right_team_count = 0 | |||||
for p in players: | |||||
var player = players[p] | |||||
if player.has("is_right_team") and player.is_right_team: | |||||
right_team_count += 1 | |||||
return (right_team_count <= players.size() / 2) | |||||
sync func _set_info(key, value, peer): | |||||
if not players.has(peer): | |||||
players[peer] = {} | |||||
players[peer][key] = value | |||||
emit_signal("info_updated") | |||||
func _on_connect(): | |||||
_register_player(get_tree().get_network_unique_id()) | |||||
emit_signal("info_updated") | |||||
func _check_info(): | |||||
# Check for "everyone is ready" | |||||
# Only have 1 person check this, might as well be server | |||||
if get_tree().is_network_server(): | |||||
var ready = true | |||||
var all_done = true | |||||
for p in players: | |||||
if not players[p].has("spectating") or not players[p].spectating: | |||||
if not players[p].has("ready") or not players[p].ready: | |||||
ready = false | |||||
if not players[p].has("begun") or not players[p].begun: | |||||
all_done = false | |||||
if all_done: | |||||
rpc("_post_configure_game") | |||||
elif ready: | |||||
# If we're all done, then we don't need to even check a start_game | |||||
start_game() | |||||
sync func _spawn_player(p): | |||||
var hero = 0 | |||||
if players[p].has("hero"): | |||||
hero = players[p].hero | |||||
var player = load("res://scenes/heroes/" + str(hero) + ".tscn").instance() | |||||
player.set_name(str(p)) | |||||
player.set_network_master(p) | |||||
player.player_info = players[p] | |||||
get_node("/root/Level/Players").add_child(player) | |||||
func _begin_player(p): | |||||
var player = util.get_player(p) | |||||
player.begin() | |||||
sync func _pre_configure_game(level): | |||||
var self_peer_id = get_tree().get_network_unique_id() | |||||
var self_begun = players[self_peer_id].begun | |||||
if not self_begun: | |||||
get_node("/root/Lobby").hide() | |||||
var world = load("res://scenes/levels/%d.tscn" % level).instance() | |||||
get_node("/root").add_child(world) | |||||
# Load all players (including self) | |||||
for p in players: | |||||
if not players[p].spectating: | |||||
var existing_player = util.get_player(p) | |||||
if not self_begun or not existing_player: | |||||
_spawn_player(p) | |||||
for p in players: | |||||
if not players[p].spectating: | |||||
# Begin requires all players | |||||
_begin_player(p) | |||||
# Why do we check first? Weird error. It's because set_info triggers a | |||||
# start_game if everyone is ready | |||||
# This causes a stack overflow if we call it from here repeatedly | |||||
# So we only change it once, only start_game twice, and avoida segfault | |||||
if not self_begun: | |||||
set_info("begun", true) | |||||
sync func _post_configure_game(): | |||||
# Begin all players (including self) | |||||
# TODO: What do? Maybe, unpause game? | |||||
pass | |||||
@ -0,0 +1,23 @@ | |||||
#!/usr/bin/python3 | |||||
from pathlib import Path | |||||
# Current working directory | |||||
cwd = Path.cwd() | |||||
for path in cwd.glob('**/*.tscn'): | |||||
result = [] | |||||
with path.open() as f: | |||||
for line in f.readlines(): | |||||
if line.startswith('_sections_unfolded'): | |||||
# Skip lines that start with _sections_unfolded | |||||
continue | |||||
elif line.startswith('[node') and 'parent=' not in line: | |||||
# Root node, remove 'index="0"' | |||||
result.append(line.replace(' index="0"', '')) | |||||
else: | |||||
# Add line as is | |||||
result.append(line) | |||||
with path.open('w') as f: | |||||
f.writelines(result) |
@ -0,0 +1 @@ | |||||
godot-server -matchmaker |
@ -1,2 +1,2 @@ | |||||
godot-server -level=2 -silent -server -start-game | |||||
godot-server -level=2 -silent -server "$@" | |||||