Compare commits
16 commits
Author | SHA1 | Date | |
---|---|---|---|
Daniel Flanagan | f9f94b446a | ||
Daniel Flanagan | d02fe3256d | ||
Daniel Flanagan | 4849ba9c20 | ||
Daniel Flanagan | 8673160466 | ||
Daniel Flanagan | 30b3e4eb03 | ||
Daniel Flanagan | 384d7bac31 | ||
Daniel Flanagan | 202c44cabf | ||
Daniel Flanagan | 9c41eaa3b4 | ||
Daniel Flanagan | 68a35b25ac | ||
Daniel Flanagan | 438ba9682f | ||
Daniel Flanagan | 2aea604aba | ||
Daniel Flanagan | 41e7769249 | ||
Daniel Flanagan | 5e7735a815 | ||
Daniel Flanagan | 7094ea299c | ||
d2abbb69f2 | |||
Daniel Flanagan | 25b93a9f2b |
|
@ -1,29 +0,0 @@
|
||||||
[remap]
|
|
||||||
|
|
||||||
importer="font_data_dynamic"
|
|
||||||
type="FontData"
|
|
||||||
uid="uid://cklw0gsblqjmg"
|
|
||||||
path="res://.godot/imported/iosevkalyte-regular.ttf-a2a88ad4f5b4e12dda9997dfa2756a6e.fontdata"
|
|
||||||
|
|
||||||
[deps]
|
|
||||||
|
|
||||||
source_file="res://assets/fonts/iosevkalyte/iosevkalyte-regular.ttf"
|
|
||||||
dest_files=["res://.godot/imported/iosevkalyte-regular.ttf-a2a88ad4f5b4e12dda9997dfa2756a6e.fontdata"]
|
|
||||||
|
|
||||||
[params]
|
|
||||||
|
|
||||||
antialiased=true
|
|
||||||
multichannel_signed_distance_field=false
|
|
||||||
msdf_pixel_range=8
|
|
||||||
msdf_size=48
|
|
||||||
force_autohinter=false
|
|
||||||
hinting=1
|
|
||||||
oversampling=0.0
|
|
||||||
compress=true
|
|
||||||
preload/char_ranges=PackedStringArray()
|
|
||||||
preload/glyph_ranges=PackedStringArray()
|
|
||||||
preload/configurations=PackedStringArray()
|
|
||||||
support_overrides/language_enabled=PackedStringArray()
|
|
||||||
support_overrides/language_disabled=PackedStringArray()
|
|
||||||
support_overrides/script_enabled=PackedStringArray()
|
|
||||||
support_overrides/script_disabled=PackedStringArray()
|
|
|
@ -3,4 +3,6 @@
|
||||||
[ext_resource path="res://assets/fonts/iosevkalyte/iosevkalyte-regular.ttf" type="DynamicFontData" id=1]
|
[ext_resource path="res://assets/fonts/iosevkalyte/iosevkalyte-regular.ttf" type="DynamicFontData" id=1]
|
||||||
|
|
||||||
[resource]
|
[resource]
|
||||||
|
outline_size = 2
|
||||||
|
outline_color = Color( 0, 0, 0, 1 )
|
||||||
font_data = ExtResource( 1 )
|
font_data = ExtResource( 1 )
|
||||||
|
|
BIN
assets/img/Parallax80.png
Normal file
BIN
assets/img/Parallax80.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 350 KiB |
35
assets/img/Parallax80.png.import
Normal file
35
assets/img/Parallax80.png.import
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="StreamTexture"
|
||||||
|
path="res://.import/Parallax80.png-fef98d95260a36330dc772bb5a676274.stex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/img/Parallax80.png"
|
||||||
|
dest_files=[ "res://.import/Parallax80.png-fef98d95260a36330dc772bb5a676274.stex" ]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_mode=0
|
||||||
|
compress/bptc_ldr=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
|
||||||
|
process/invert_color=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
stream=false
|
||||||
|
size_limit=0
|
||||||
|
detect_3d=true
|
||||||
|
svg/scale=1.0
|
|
@ -20,7 +20,7 @@ compress/hdr_mode=0
|
||||||
compress/bptc_ldr=0
|
compress/bptc_ldr=0
|
||||||
compress/normal_map=0
|
compress/normal_map=0
|
||||||
flags/repeat=0
|
flags/repeat=0
|
||||||
flags/filter=true
|
flags/filter=false
|
||||||
flags/mipmaps=false
|
flags/mipmaps=false
|
||||||
flags/anisotropic=false
|
flags/anisotropic=false
|
||||||
flags/srgb=2
|
flags/srgb=2
|
||||||
|
|
|
@ -20,7 +20,7 @@ compress/hdr_mode=0
|
||||||
compress/bptc_ldr=0
|
compress/bptc_ldr=0
|
||||||
compress/normal_map=0
|
compress/normal_map=0
|
||||||
flags/repeat=0
|
flags/repeat=0
|
||||||
flags/filter=true
|
flags/filter=false
|
||||||
flags/mipmaps=false
|
flags/mipmaps=false
|
||||||
flags/anisotropic=false
|
flags/anisotropic=false
|
||||||
flags/srgb=2
|
flags/srgb=2
|
||||||
|
|
|
@ -20,11 +20,11 @@ compress/hdr_mode=0
|
||||||
compress/bptc_ldr=0
|
compress/bptc_ldr=0
|
||||||
compress/normal_map=0
|
compress/normal_map=0
|
||||||
flags/repeat=0
|
flags/repeat=0
|
||||||
flags/filter=true
|
flags/filter=false
|
||||||
flags/mipmaps=false
|
flags/mipmaps=false
|
||||||
flags/anisotropic=false
|
flags/anisotropic=false
|
||||||
flags/srgb=2
|
flags/srgb=2
|
||||||
process/fix_alpha_border=true
|
process/fix_alpha_border=false
|
||||||
process/premult_alpha=false
|
process/premult_alpha=false
|
||||||
process/HDR_as_SRGB=false
|
process/HDR_as_SRGB=false
|
||||||
process/invert_color=false
|
process/invert_color=false
|
||||||
|
|
|
@ -1,6 +1,227 @@
|
||||||
[gd_resource type="Theme" load_steps=2 format=2]
|
[gd_resource type="Theme" load_steps=25 format=2]
|
||||||
|
|
||||||
[ext_resource path="res://assets/fonts/iosevkalyte/iosevkalyte.tres" type="DynamicFont" id=1]
|
[ext_resource path="res://assets/fonts/iosevkalyte/iosevkalyte.tres" type="DynamicFont" id=1]
|
||||||
|
[ext_resource path="res://assets/img/panel.png" type="Texture" id=2]
|
||||||
|
[ext_resource path="res://assets/ui/progressbar_stylebox.tres" type="StyleBox" id=3]
|
||||||
|
|
||||||
|
[sub_resource type="Image" id=23]
|
||||||
|
data = {
|
||||||
|
"data": PoolByteArray( 0, 4, 0, 10, 0, 17, 0, 25, 0, 31, 0, 34, 0, 36, 0, 36, 0, 36, 0, 36, 0, 34, 0, 31, 0, 25, 0, 17, 0, 10, 0, 4, 0, 10, 0, 21, 0, 37, 0, 52, 0, 63, 0, 70, 0, 71, 0, 72, 0, 72, 0, 71, 0, 70, 0, 63, 0, 52, 0, 37, 0, 21, 0, 10, 0, 17, 0, 37, 63, 119, 90, 239, 86, 255, 86, 255, 86, 255, 86, 255, 86, 255, 86, 255, 86, 255, 86, 255, 90, 239, 63, 119, 0, 37, 0, 17, 0, 25, 0, 52, 89, 239, 72, 255, 68, 255, 67, 255, 67, 255, 67, 255, 67, 255, 67, 255, 67, 255, 68, 255, 72, 255, 89, 239, 0, 52, 0, 25, 0, 31, 0, 63, 84, 255, 67, 255, 67, 255, 67, 255, 67, 255, 67, 255, 67, 255, 67, 255, 67, 255, 67, 255, 67, 255, 84, 255, 0, 63, 0, 31, 0, 34, 0, 70, 82, 255, 66, 255, 66, 255, 66, 255, 66, 255, 66, 255, 66, 255, 66, 255, 66, 255, 66, 255, 66, 255, 82, 255, 0, 70, 0, 34, 0, 36, 0, 71, 81, 255, 64, 255, 64, 255, 64, 255, 64, 255, 64, 255, 64, 255, 64, 255, 64, 255, 64, 255, 64, 255, 81, 255, 0, 71, 0, 36, 0, 36, 0, 72, 79, 255, 63, 255, 63, 255, 63, 255, 63, 255, 63, 255, 63, 255, 63, 255, 63, 255, 63, 255, 63, 255, 79, 255, 0, 72, 0, 36, 0, 36, 0, 72, 77, 255, 62, 255, 62, 255, 62, 255, 62, 255, 62, 255, 62, 255, 62, 255, 62, 255, 62, 255, 62, 255, 77, 255, 0, 72, 0, 36, 0, 36, 0, 71, 76, 255, 61, 255, 61, 255, 61, 255, 61, 255, 61, 255, 61, 255, 61, 255, 61, 255, 61, 255, 61, 255, 76, 255, 0, 71, 0, 36, 0, 34, 0, 70, 74, 255, 59, 255, 59, 255, 59, 255, 59, 255, 59, 255, 59, 255, 59, 255, 59, 255, 59, 255, 59, 255, 74, 255, 0, 70, 0, 34, 0, 31, 0, 63, 73, 255, 58, 255, 58, 255, 58, 255, 58, 255, 58, 255, 58, 255, 58, 255, 58, 255, 58, 255, 58, 255, 73, 255, 0, 63, 0, 31, 0, 25, 0, 52, 75, 239, 61, 255, 57, 255, 57, 255, 57, 255, 57, 255, 57, 255, 57, 255, 57, 255, 57, 255, 61, 255, 75, 239, 0, 52, 0, 25, 0, 17, 0, 37, 53, 119, 74, 239, 71, 255, 70, 255, 70, 255, 70, 255, 70, 255, 70, 255, 70, 255, 71, 255, 74, 237, 53, 119, 0, 37, 0, 17, 0, 10, 0, 21, 0, 37, 0, 52, 0, 63, 0, 70, 0, 71, 0, 72, 0, 72, 0, 71, 0, 70, 0, 63, 0, 52, 0, 37, 0, 21, 0, 10, 0, 4, 0, 10, 0, 17, 0, 25, 0, 31, 0, 34, 0, 36, 0, 36, 0, 36, 0, 36, 0, 34, 0, 31, 0, 25, 0, 17, 0, 10, 0, 4 ),
|
||||||
|
"format": "LumAlpha8",
|
||||||
|
"height": 16,
|
||||||
|
"mipmaps": false,
|
||||||
|
"width": 16
|
||||||
|
}
|
||||||
|
|
||||||
|
[sub_resource type="ImageTexture" id=8]
|
||||||
|
flags = 4
|
||||||
|
flags = 4
|
||||||
|
image = SubResource( 23 )
|
||||||
|
size = Vector2( 16, 16 )
|
||||||
|
|
||||||
|
[sub_resource type="StyleBoxTexture" id=9]
|
||||||
|
content_margin_left = 6.0
|
||||||
|
content_margin_right = 6.0
|
||||||
|
content_margin_top = 2.0
|
||||||
|
content_margin_bottom = 2.0
|
||||||
|
texture = SubResource( 8 )
|
||||||
|
region_rect = Rect2( 0, 0, 16, 16 )
|
||||||
|
margin_left = 4.0
|
||||||
|
margin_right = 4.0
|
||||||
|
margin_top = 4.0
|
||||||
|
margin_bottom = 4.0
|
||||||
|
expand_margin_left = 2.0
|
||||||
|
expand_margin_right = 2.0
|
||||||
|
expand_margin_top = 2.0
|
||||||
|
expand_margin_bottom = 2.0
|
||||||
|
|
||||||
|
[sub_resource type="Image" id=24]
|
||||||
|
data = {
|
||||||
|
"data": PoolByteArray( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 140, 191, 14, 71, 140, 191, 57, 71, 140, 191, 104, 71, 140, 191, 122, 71, 140, 191, 123, 71, 140, 191, 123, 71, 140, 191, 123, 71, 140, 191, 123, 71, 140, 191, 122, 71, 140, 191, 104, 71, 140, 191, 57, 71, 140, 191, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 140, 191, 58, 71, 140, 191, 116, 71, 140, 191, 16, 71, 140, 191, 8, 71, 140, 191, 8, 71, 140, 191, 8, 71, 140, 191, 8, 71, 140, 191, 8, 71, 140, 191, 8, 71, 140, 191, 16, 71, 140, 191, 116, 71, 140, 191, 58, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 140, 191, 105, 71, 140, 191, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 140, 191, 15, 71, 140, 191, 105, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 140, 191, 123, 71, 140, 191, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 140, 191, 6, 71, 140, 191, 123, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 140, 191, 123, 71, 140, 191, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 140, 191, 8, 71, 140, 191, 123, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 140, 191, 123, 71, 140, 191, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 140, 191, 8, 71, 140, 191, 123, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 140, 191, 123, 71, 140, 191, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 140, 191, 8, 71, 140, 191, 123, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 140, 191, 123, 71, 140, 191, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 140, 191, 8, 71, 140, 191, 123, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 140, 191, 123, 71, 140, 191, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 140, 191, 6, 71, 140, 191, 123, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 140, 191, 105, 71, 140, 191, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 140, 191, 14, 71, 140, 191, 105, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 140, 191, 58, 71, 140, 191, 117, 71, 140, 191, 15, 71, 140, 191, 8, 71, 140, 191, 8, 71, 140, 191, 8, 71, 140, 191, 8, 71, 140, 191, 8, 71, 140, 191, 8, 71, 140, 191, 17, 71, 140, 191, 116, 71, 140, 191, 58, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 140, 191, 14, 71, 140, 191, 58, 71, 140, 191, 104, 71, 140, 191, 122, 71, 140, 191, 123, 71, 140, 191, 123, 71, 140, 191, 123, 71, 140, 191, 123, 71, 140, 191, 122, 71, 140, 191, 104, 71, 140, 191, 57, 71, 140, 191, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ),
|
||||||
|
"format": "RGBA8",
|
||||||
|
"height": 16,
|
||||||
|
"mipmaps": false,
|
||||||
|
"width": 16
|
||||||
|
}
|
||||||
|
|
||||||
|
[sub_resource type="ImageTexture" id=11]
|
||||||
|
flags = 4
|
||||||
|
flags = 4
|
||||||
|
image = SubResource( 24 )
|
||||||
|
size = Vector2( 16, 16 )
|
||||||
|
|
||||||
|
[sub_resource type="StyleBoxTexture" id=12]
|
||||||
|
content_margin_left = 6.0
|
||||||
|
content_margin_right = 6.0
|
||||||
|
content_margin_top = 2.0
|
||||||
|
content_margin_bottom = 2.0
|
||||||
|
texture = SubResource( 11 )
|
||||||
|
region_rect = Rect2( 0, 0, 16, 16 )
|
||||||
|
margin_left = 4.0
|
||||||
|
margin_right = 4.0
|
||||||
|
margin_top = 4.0
|
||||||
|
margin_bottom = 4.0
|
||||||
|
expand_margin_left = 2.0
|
||||||
|
expand_margin_right = 2.0
|
||||||
|
expand_margin_top = 2.0
|
||||||
|
expand_margin_bottom = 2.0
|
||||||
|
|
||||||
|
[sub_resource type="Image" id=25]
|
||||||
|
data = {
|
||||||
|
"data": PoolByteArray( 0, 0, 0, 4, 0, 0, 0, 10, 0, 0, 0, 17, 0, 0, 0, 25, 0, 0, 0, 31, 0, 0, 0, 34, 0, 0, 0, 36, 0, 0, 0, 36, 0, 0, 0, 36, 0, 0, 0, 36, 0, 0, 0, 34, 0, 0, 0, 31, 0, 0, 0, 25, 0, 0, 0, 17, 0, 0, 0, 10, 0, 0, 0, 4, 0, 0, 0, 10, 0, 0, 0, 21, 0, 0, 0, 37, 0, 0, 0, 52, 0, 0, 0, 63, 0, 0, 0, 70, 0, 0, 0, 71, 0, 0, 0, 72, 0, 0, 0, 72, 0, 0, 0, 71, 0, 0, 0, 70, 0, 0, 0, 63, 0, 0, 0, 52, 0, 0, 0, 37, 0, 0, 0, 21, 0, 0, 0, 10, 0, 0, 0, 17, 0, 0, 0, 37, 66, 64, 75, 119, 95, 90, 108, 239, 91, 87, 104, 255, 90, 86, 103, 255, 90, 86, 103, 255, 90, 86, 103, 255, 90, 86, 103, 255, 90, 86, 103, 255, 90, 86, 103, 255, 91, 87, 104, 255, 95, 90, 108, 239, 66, 64, 75, 119, 0, 0, 0, 37, 0, 0, 0, 17, 0, 0, 0, 25, 0, 0, 0, 52, 95, 90, 107, 239, 77, 74, 87, 255, 73, 70, 82, 255, 72, 69, 81, 255, 72, 69, 81, 255, 72, 69, 81, 255, 72, 69, 81, 255, 72, 69, 81, 255, 72, 69, 81, 255, 73, 70, 82, 255, 77, 74, 87, 255, 95, 90, 107, 239, 0, 0, 0, 52, 0, 0, 0, 25, 0, 0, 0, 31, 0, 0, 0, 63, 91, 87, 102, 255, 73, 70, 82, 255, 72, 69, 81, 255, 72, 69, 81, 255, 72, 69, 81, 255, 72, 69, 81, 255, 72, 69, 81, 255, 72, 69, 81, 255, 72, 69, 81, 255, 72, 69, 81, 255, 73, 70, 82, 255, 91, 87, 102, 255, 0, 0, 0, 63, 0, 0, 0, 31, 0, 0, 0, 34, 0, 0, 0, 70, 89, 85, 100, 255, 71, 68, 80, 255, 71, 68, 80, 255, 71, 68, 80, 255, 71, 68, 80, 255, 71, 68, 80, 255, 71, 68, 80, 255, 71, 68, 80, 255, 71, 68, 80, 255, 71, 68, 80, 255, 71, 68, 80, 255, 89, 85, 100, 255, 0, 0, 0, 70, 0, 0, 0, 34, 0, 0, 0, 36, 0, 0, 0, 71, 88, 84, 100, 255, 70, 67, 80, 255, 70, 67, 80, 255, 70, 67, 80, 255, 70, 67, 80, 255, 70, 67, 80, 255, 70, 67, 80, 255, 70, 67, 80, 255, 70, 67, 80, 255, 70, 67, 80, 255, 70, 67, 80, 255, 88, 84, 100, 255, 0, 0, 0, 71, 0, 0, 0, 36, 0, 0, 0, 36, 0, 0, 0, 72, 86, 83, 99, 255, 69, 66, 79, 255, 69, 66, 79, 255, 69, 66, 79, 255, 69, 66, 79, 255, 69, 66, 79, 255, 69, 66, 79, 255, 69, 66, 79, 255, 69, 66, 79, 255, 69, 66, 79, 255, 69, 66, 79, 255, 86, 83, 99, 255, 0, 0, 0, 72, 0, 0, 0, 36, 0, 0, 0, 36, 0, 0, 0, 72, 86, 83, 98, 255, 69, 66, 78, 255, 69, 66, 78, 255, 69, 66, 78, 255, 69, 66, 78, 255, 69, 66, 78, 255, 69, 66, 78, 255, 69, 66, 78, 255, 69, 66, 78, 255, 69, 66, 78, 255, 69, 66, 78, 255, 86, 83, 98, 255, 0, 0, 0, 72, 0, 0, 0, 36, 0, 0, 0, 36, 0, 0, 0, 71, 85, 81, 98, 255, 68, 65, 78, 255, 68, 65, 78, 255, 68, 65, 78, 255, 68, 65, 78, 255, 68, 65, 78, 255, 68, 65, 78, 255, 68, 65, 78, 255, 68, 65, 78, 255, 68, 65, 78, 255, 68, 65, 78, 255, 85, 81, 98, 255, 0, 0, 0, 71, 0, 0, 0, 36, 0, 0, 0, 34, 0, 0, 0, 70, 85, 81, 96, 255, 68, 65, 77, 255, 68, 65, 77, 255, 68, 65, 77, 255, 68, 65, 77, 255, 68, 65, 77, 255, 68, 65, 77, 255, 68, 65, 77, 255, 68, 65, 77, 255, 68, 65, 77, 255, 68, 65, 77, 255, 85, 81, 96, 255, 0, 0, 0, 70, 0, 0, 0, 34, 0, 0, 0, 31, 0, 0, 0, 63, 85, 81, 96, 255, 68, 65, 77, 255, 67, 64, 76, 255, 67, 64, 76, 255, 67, 64, 76, 255, 67, 64, 76, 255, 67, 64, 76, 255, 67, 64, 76, 255, 67, 64, 76, 255, 67, 64, 76, 255, 68, 65, 77, 255, 85, 81, 96, 255, 0, 0, 0, 63, 0, 0, 0, 31, 0, 0, 0, 25, 0, 0, 0, 52, 86, 83, 100, 239, 71, 67, 81, 255, 67, 63, 77, 255, 66, 63, 76, 255, 66, 63, 76, 255, 66, 63, 76, 255, 66, 63, 76, 255, 66, 63, 76, 255, 66, 63, 76, 255, 67, 63, 77, 255, 71, 67, 81, 255, 87, 83, 100, 239, 0, 0, 0, 52, 0, 0, 0, 25, 0, 0, 0, 17, 0, 0, 0, 37, 62, 59, 70, 119, 87, 83, 99, 239, 83, 80, 95, 255, 83, 79, 94, 255, 83, 79, 94, 255, 83, 79, 94, 255, 83, 79, 94, 255, 83, 79, 94, 255, 83, 79, 94, 255, 83, 80, 95, 255, 87, 83, 99, 237, 62, 59, 70, 119, 0, 0, 0, 37, 0, 0, 0, 17, 0, 0, 0, 10, 0, 0, 0, 21, 0, 0, 0, 37, 0, 0, 0, 52, 0, 0, 0, 63, 0, 0, 0, 70, 0, 0, 0, 71, 0, 0, 0, 72, 0, 0, 0, 72, 0, 0, 0, 71, 0, 0, 0, 70, 0, 0, 0, 63, 0, 0, 0, 52, 0, 0, 0, 37, 0, 0, 0, 21, 0, 0, 0, 10, 0, 0, 0, 4, 0, 0, 0, 10, 0, 0, 0, 17, 0, 0, 0, 25, 0, 0, 0, 31, 0, 0, 0, 34, 0, 0, 0, 36, 0, 0, 0, 36, 0, 0, 0, 36, 0, 0, 0, 36, 0, 0, 0, 34, 0, 0, 0, 31, 0, 0, 0, 25, 0, 0, 0, 17, 0, 0, 0, 10, 0, 0, 0, 4 ),
|
||||||
|
"format": "RGBA8",
|
||||||
|
"height": 16,
|
||||||
|
"mipmaps": false,
|
||||||
|
"width": 16
|
||||||
|
}
|
||||||
|
|
||||||
|
[sub_resource type="ImageTexture" id=14]
|
||||||
|
flags = 4
|
||||||
|
flags = 4
|
||||||
|
image = SubResource( 25 )
|
||||||
|
size = Vector2( 16, 16 )
|
||||||
|
|
||||||
|
[sub_resource type="StyleBoxTexture" id=15]
|
||||||
|
content_margin_left = 6.0
|
||||||
|
content_margin_right = 6.0
|
||||||
|
content_margin_top = 2.0
|
||||||
|
content_margin_bottom = 2.0
|
||||||
|
texture = SubResource( 14 )
|
||||||
|
region_rect = Rect2( 0, 0, 16, 16 )
|
||||||
|
margin_left = 4.0
|
||||||
|
margin_right = 4.0
|
||||||
|
margin_top = 4.0
|
||||||
|
margin_bottom = 4.0
|
||||||
|
expand_margin_left = 2.0
|
||||||
|
expand_margin_right = 2.0
|
||||||
|
expand_margin_top = 2.0
|
||||||
|
expand_margin_bottom = 2.0
|
||||||
|
|
||||||
|
[sub_resource type="Image" id=26]
|
||||||
|
data = {
|
||||||
|
"data": PoolByteArray( 0, 0, 0, 4, 0, 0, 0, 10, 0, 0, 0, 17, 0, 0, 0, 25, 0, 0, 0, 31, 0, 0, 0, 34, 0, 0, 0, 36, 0, 0, 0, 36, 0, 0, 0, 36, 0, 0, 0, 36, 0, 0, 0, 34, 0, 0, 0, 31, 0, 0, 0, 25, 0, 0, 0, 17, 0, 0, 0, 10, 0, 0, 0, 4, 0, 0, 0, 10, 0, 0, 0, 21, 0, 0, 0, 37, 0, 0, 0, 52, 0, 0, 0, 63, 0, 0, 0, 70, 0, 0, 0, 71, 0, 0, 0, 72, 0, 0, 0, 72, 0, 0, 0, 71, 0, 0, 0, 70, 0, 0, 0, 63, 0, 0, 0, 52, 0, 0, 0, 37, 0, 0, 0, 21, 0, 0, 0, 10, 0, 0, 0, 17, 0, 0, 0, 37, 60, 58, 68, 119, 86, 83, 97, 239, 82, 80, 93, 255, 81, 79, 93, 255, 81, 79, 93, 255, 81, 79, 93, 255, 81, 79, 93, 255, 81, 79, 93, 255, 81, 79, 93, 255, 82, 80, 93, 255, 86, 83, 97, 239, 60, 58, 68, 119, 0, 0, 0, 37, 0, 0, 0, 17, 0, 0, 0, 25, 0, 0, 0, 52, 86, 82, 96, 239, 70, 66, 78, 255, 66, 62, 74, 255, 65, 62, 73, 255, 65, 62, 73, 255, 65, 62, 73, 255, 65, 62, 73, 255, 65, 62, 73, 255, 65, 62, 73, 255, 66, 62, 74, 255, 70, 66, 78, 255, 86, 82, 96, 239, 0, 0, 0, 52, 0, 0, 0, 25, 0, 0, 0, 31, 0, 0, 0, 63, 81, 78, 91, 255, 65, 62, 73, 255, 64, 62, 72, 255, 64, 62, 72, 255, 64, 62, 72, 255, 64, 62, 72, 255, 64, 62, 72, 255, 64, 62, 72, 255, 64, 62, 72, 255, 64, 62, 72, 255, 65, 62, 73, 255, 81, 78, 91, 255, 0, 0, 0, 63, 0, 0, 0, 31, 0, 0, 0, 34, 0, 0, 0, 70, 79, 76, 89, 255, 63, 61, 71, 255, 63, 61, 71, 255, 63, 61, 71, 255, 63, 61, 71, 255, 63, 61, 71, 255, 63, 61, 71, 255, 63, 61, 71, 255, 63, 61, 71, 255, 63, 61, 71, 255, 63, 61, 71, 255, 79, 76, 89, 255, 0, 0, 0, 70, 0, 0, 0, 34, 0, 0, 0, 36, 0, 0, 0, 71, 78, 74, 88, 255, 62, 59, 70, 255, 62, 59, 70, 255, 62, 59, 70, 255, 62, 59, 70, 255, 62, 59, 70, 255, 62, 59, 70, 255, 62, 59, 70, 255, 62, 59, 70, 255, 62, 59, 70, 255, 62, 59, 70, 255, 78, 74, 88, 255, 0, 0, 0, 71, 0, 0, 0, 36, 0, 0, 0, 36, 0, 0, 0, 72, 75, 73, 85, 255, 60, 58, 68, 255, 60, 58, 68, 255, 60, 58, 68, 255, 60, 58, 68, 255, 60, 58, 68, 255, 60, 58, 68, 255, 60, 58, 68, 255, 60, 58, 68, 255, 60, 58, 68, 255, 60, 58, 68, 255, 75, 73, 85, 255, 0, 0, 0, 72, 0, 0, 0, 36, 0, 0, 0, 36, 0, 0, 0, 72, 74, 71, 84, 255, 59, 57, 67, 255, 59, 57, 67, 255, 59, 57, 67, 255, 59, 57, 67, 255, 59, 57, 67, 255, 59, 57, 67, 255, 59, 57, 67, 255, 59, 57, 67, 255, 59, 57, 67, 255, 59, 57, 67, 255, 74, 71, 84, 255, 0, 0, 0, 72, 0, 0, 0, 36, 0, 0, 0, 36, 0, 0, 0, 71, 73, 70, 83, 255, 58, 56, 66, 255, 58, 56, 66, 255, 58, 56, 66, 255, 58, 56, 66, 255, 58, 56, 66, 255, 58, 56, 66, 255, 58, 56, 66, 255, 58, 56, 66, 255, 58, 56, 66, 255, 58, 56, 66, 255, 73, 70, 83, 255, 0, 0, 0, 71, 0, 0, 0, 36, 0, 0, 0, 34, 0, 0, 0, 70, 71, 69, 80, 255, 57, 55, 64, 255, 57, 55, 64, 255, 57, 55, 64, 255, 57, 55, 64, 255, 57, 55, 64, 255, 57, 55, 64, 255, 57, 55, 64, 255, 57, 55, 64, 255, 57, 55, 64, 255, 57, 55, 64, 255, 71, 69, 80, 255, 0, 0, 0, 70, 0, 0, 0, 34, 0, 0, 0, 31, 0, 0, 0, 63, 71, 67, 80, 255, 56, 53, 63, 255, 56, 53, 63, 255, 56, 53, 63, 255, 56, 53, 63, 255, 56, 53, 63, 255, 56, 53, 63, 255, 56, 53, 63, 255, 56, 53, 63, 255, 56, 53, 63, 255, 56, 53, 63, 255, 71, 67, 80, 255, 0, 0, 0, 63, 0, 0, 0, 31, 0, 0, 0, 25, 0, 0, 0, 52, 71, 68, 82, 239, 58, 56, 66, 255, 54, 52, 62, 255, 54, 52, 62, 255, 54, 52, 62, 255, 54, 52, 62, 255, 54, 52, 62, 255, 54, 52, 62, 255, 54, 52, 62, 255, 54, 52, 62, 255, 58, 56, 66, 255, 71, 68, 82, 239, 0, 0, 0, 52, 0, 0, 0, 25, 0, 0, 0, 17, 0, 0, 0, 37, 51, 49, 57, 119, 71, 68, 80, 239, 68, 66, 77, 255, 68, 65, 76, 255, 68, 65, 76, 255, 68, 65, 76, 255, 68, 65, 76, 255, 68, 65, 76, 255, 68, 65, 76, 255, 68, 66, 77, 255, 71, 68, 81, 237, 51, 49, 57, 119, 0, 0, 0, 37, 0, 0, 0, 17, 0, 0, 0, 10, 0, 0, 0, 21, 0, 0, 0, 37, 0, 0, 0, 52, 0, 0, 0, 63, 0, 0, 0, 70, 0, 0, 0, 71, 0, 0, 0, 72, 0, 0, 0, 72, 0, 0, 0, 71, 0, 0, 0, 70, 0, 0, 0, 63, 0, 0, 0, 52, 0, 0, 0, 37, 0, 0, 0, 21, 0, 0, 0, 10, 0, 0, 0, 4, 0, 0, 0, 10, 0, 0, 0, 17, 0, 0, 0, 25, 0, 0, 0, 31, 0, 0, 0, 34, 0, 0, 0, 36, 0, 0, 0, 36, 0, 0, 0, 36, 0, 0, 0, 36, 0, 0, 0, 34, 0, 0, 0, 31, 0, 0, 0, 25, 0, 0, 0, 17, 0, 0, 0, 10, 0, 0, 0, 4 ),
|
||||||
|
"format": "RGBA8",
|
||||||
|
"height": 16,
|
||||||
|
"mipmaps": false,
|
||||||
|
"width": 16
|
||||||
|
}
|
||||||
|
|
||||||
|
[sub_resource type="ImageTexture" id=17]
|
||||||
|
flags = 4
|
||||||
|
flags = 4
|
||||||
|
image = SubResource( 26 )
|
||||||
|
size = Vector2( 16, 16 )
|
||||||
|
|
||||||
|
[sub_resource type="StyleBoxTexture" id=18]
|
||||||
|
content_margin_left = 6.0
|
||||||
|
content_margin_right = 6.0
|
||||||
|
content_margin_top = 3.0
|
||||||
|
content_margin_bottom = 3.0
|
||||||
|
texture = SubResource( 17 )
|
||||||
|
region_rect = Rect2( 0, 0, 16, 16 )
|
||||||
|
margin_left = 4.0
|
||||||
|
margin_right = 4.0
|
||||||
|
margin_top = 4.0
|
||||||
|
margin_bottom = 4.0
|
||||||
|
expand_margin_left = 2.0
|
||||||
|
expand_margin_right = 2.0
|
||||||
|
expand_margin_top = 2.0
|
||||||
|
expand_margin_bottom = 2.0
|
||||||
|
|
||||||
|
[sub_resource type="Image" id=27]
|
||||||
|
data = {
|
||||||
|
"data": PoolByteArray( 0, 0, 0, 4, 0, 0, 0, 10, 0, 0, 0, 17, 0, 0, 0, 25, 0, 0, 0, 31, 0, 0, 0, 34, 0, 0, 0, 36, 0, 0, 0, 36, 0, 0, 0, 36, 0, 0, 0, 36, 0, 0, 0, 34, 0, 0, 0, 31, 0, 0, 0, 25, 0, 0, 0, 17, 0, 0, 0, 10, 0, 0, 0, 4, 0, 0, 0, 11, 0, 0, 0, 22, 0, 0, 0, 39, 0, 0, 0, 54, 0, 0, 0, 65, 0, 0, 0, 73, 0, 0, 0, 74, 0, 0, 0, 75, 0, 0, 0, 75, 0, 0, 0, 74, 0, 0, 0, 73, 0, 0, 0, 65, 0, 0, 0, 54, 0, 0, 0, 39, 0, 0, 0, 22, 0, 0, 0, 11, 0, 0, 0, 19, 0, 0, 0, 41, 49, 47, 55, 128, 70, 67, 79, 241, 67, 64, 76, 255, 66, 64, 75, 255, 66, 64, 75, 255, 66, 64, 75, 255, 66, 64, 75, 255, 66, 64, 75, 255, 66, 64, 75, 255, 67, 64, 76, 255, 70, 67, 79, 241, 49, 47, 55, 127, 0, 0, 0, 41, 0, 0, 0, 19, 0, 0, 0, 30, 0, 0, 0, 61, 71, 68, 80, 242, 58, 56, 65, 255, 11, 11, 12, 255, 11, 10, 12, 255, 11, 10, 12, 255, 11, 10, 12, 255, 11, 10, 12, 255, 11, 11, 12, 255, 11, 11, 12, 255, 11, 11, 12, 255, 58, 56, 65, 255, 71, 68, 80, 242, 0, 0, 0, 61, 0, 0, 0, 30, 0, 0, 0, 39, 0, 0, 0, 77, 68, 66, 78, 255, 15, 14, 17, 255, 15, 14, 17, 255, 15, 14, 17, 255, 15, 14, 17, 255, 15, 14, 17, 255, 15, 14, 17, 255, 15, 14, 17, 255, 15, 14, 17, 255, 15, 14, 17, 255, 15, 14, 17, 255, 68, 66, 78, 255, 0, 0, 0, 77, 0, 0, 0, 39, 0, 0, 0, 44, 0, 0, 0, 88, 70, 66, 79, 255, 20, 19, 22, 255, 19, 18, 22, 255, 19, 18, 22, 255, 20, 19, 22, 255, 19, 18, 22, 255, 20, 19, 22, 255, 20, 19, 22, 255, 20, 19, 22, 255, 20, 19, 22, 255, 19, 18, 22, 255, 70, 66, 79, 255, 0, 0, 0, 88, 0, 0, 0, 44, 0, 0, 0, 50, 0, 0, 0, 93, 71, 69, 80, 255, 24, 23, 27, 255, 24, 23, 27, 255, 24, 23, 27, 255, 24, 23, 27, 255, 24, 23, 27, 255, 24, 23, 27, 255, 24, 23, 27, 255, 25, 24, 28, 255, 24, 23, 27, 255, 24, 23, 27, 255, 71, 69, 80, 255, 0, 0, 0, 93, 0, 0, 0, 50, 0, 0, 0, 51, 0, 0, 0, 99, 73, 70, 83, 255, 29, 28, 33, 255, 29, 28, 33, 255, 29, 28, 33, 255, 29, 28, 33, 255, 29, 28, 33, 255, 29, 28, 33, 255, 29, 28, 33, 255, 29, 28, 33, 255, 29, 28, 33, 255, 29, 28, 33, 255, 73, 70, 83, 255, 0, 0, 0, 99, 0, 0, 0, 51, 0, 0, 0, 54, 0, 0, 0, 102, 74, 71, 84, 255, 34, 33, 39, 255, 34, 33, 39, 255, 34, 33, 39, 255, 34, 33, 39, 255, 34, 33, 39, 255, 34, 33, 39, 255, 34, 33, 39, 255, 34, 33, 39, 255, 34, 33, 39, 255, 34, 33, 39, 255, 74, 71, 84, 255, 0, 0, 0, 102, 0, 0, 0, 54, 0, 0, 0, 57, 0, 0, 0, 105, 75, 73, 85, 255, 39, 38, 45, 255, 39, 38, 45, 255, 39, 38, 45, 255, 39, 38, 45, 255, 39, 38, 45, 255, 39, 38, 45, 255, 39, 38, 45, 255, 40, 38, 45, 255, 39, 38, 45, 255, 40, 38, 45, 255, 75, 73, 85, 255, 0, 0, 0, 105, 0, 0, 0, 57, 0, 0, 0, 56, 0, 0, 0, 108, 78, 74, 88, 255, 45, 43, 51, 255, 45, 43, 51, 255, 45, 43, 51, 255, 45, 43, 51, 255, 45, 43, 51, 255, 45, 43, 51, 255, 45, 43, 51, 255, 45, 43, 51, 255, 45, 43, 51, 255, 45, 43, 51, 255, 78, 74, 88, 255, 0, 0, 0, 108, 0, 0, 0, 56, 0, 0, 0, 53, 0, 0, 0, 101, 80, 77, 90, 255, 51, 49, 58, 255, 51, 49, 57, 255, 51, 49, 57, 255, 51, 49, 57, 255, 51, 49, 57, 255, 51, 49, 57, 255, 51, 49, 57, 255, 51, 49, 57, 255, 51, 49, 57, 255, 51, 49, 58, 255, 80, 77, 90, 255, 0, 0, 0, 101, 0, 0, 0, 53, 0, 0, 0, 45, 0, 0, 0, 89, 85, 82, 95, 252, 69, 66, 77, 255, 57, 55, 64, 255, 56, 55, 64, 255, 57, 55, 64, 255, 56, 55, 64, 255, 56, 55, 64, 255, 56, 55, 64, 255, 56, 55, 64, 255, 58, 55, 65, 255, 69, 66, 77, 255, 85, 82, 96, 252, 0, 0, 0, 89, 0, 0, 0, 45, 0, 0, 0, 32, 0, 0, 0, 68, 61, 58, 69, 180, 86, 82, 96, 253, 82, 78, 92, 255, 81, 78, 91, 255, 81, 78, 91, 255, 81, 78, 91, 255, 81, 78, 91, 255, 81, 78, 91, 255, 81, 78, 91, 255, 82, 78, 92, 255, 86, 82, 96, 253, 61, 58, 69, 180, 0, 0, 0, 68, 0, 0, 0, 32, 0, 0, 0, 20, 0, 0, 0, 40, 0, 0, 0, 69, 0, 0, 0, 93, 0, 0, 0, 110, 0, 0, 0, 121, 0, 0, 0, 122, 0, 0, 0, 124, 0, 0, 0, 124, 0, 0, 0, 122, 0, 0, 0, 121, 0, 0, 0, 110, 0, 0, 0, 93, 0, 0, 0, 69, 0, 0, 0, 40, 0, 0, 0, 20, 0, 0, 0, 8, 0, 0, 0, 20, 0, 0, 0, 33, 0, 0, 0, 48, 0, 0, 0, 58, 0, 0, 0, 63, 0, 0, 0, 67, 0, 0, 0, 67, 0, 0, 0, 67, 0, 0, 0, 67, 0, 0, 0, 63, 0, 0, 0, 58, 0, 0, 0, 48, 0, 0, 0, 33, 0, 0, 0, 20, 0, 0, 0, 8 ),
|
||||||
|
"format": "RGBA8",
|
||||||
|
"height": 16,
|
||||||
|
"mipmaps": false,
|
||||||
|
"width": 16
|
||||||
|
}
|
||||||
|
|
||||||
|
[sub_resource type="ImageTexture" id=20]
|
||||||
|
flags = 4
|
||||||
|
flags = 4
|
||||||
|
image = SubResource( 27 )
|
||||||
|
size = Vector2( 16, 16 )
|
||||||
|
|
||||||
|
[sub_resource type="StyleBoxTexture" id=21]
|
||||||
|
content_margin_left = 6.0
|
||||||
|
content_margin_right = 6.0
|
||||||
|
content_margin_top = 3.0
|
||||||
|
content_margin_bottom = 3.0
|
||||||
|
texture = SubResource( 20 )
|
||||||
|
region_rect = Rect2( 0, 0, 16, 16 )
|
||||||
|
margin_left = 4.0
|
||||||
|
margin_right = 4.0
|
||||||
|
margin_top = 4.0
|
||||||
|
margin_bottom = 4.0
|
||||||
|
expand_margin_left = 2.0
|
||||||
|
expand_margin_right = 2.0
|
||||||
|
expand_margin_top = 2.0
|
||||||
|
expand_margin_bottom = 2.0
|
||||||
|
|
||||||
|
[sub_resource type="StyleBoxEmpty" id=22]
|
||||||
|
|
||||||
|
[sub_resource type="Image" id=28]
|
||||||
|
data = {
|
||||||
|
"data": PoolByteArray( 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42, 37, 37, 42 ),
|
||||||
|
"format": "RGB8",
|
||||||
|
"height": 8,
|
||||||
|
"mipmaps": false,
|
||||||
|
"width": 8
|
||||||
|
}
|
||||||
|
|
||||||
|
[sub_resource type="ImageTexture" id=2]
|
||||||
|
flags = 4
|
||||||
|
flags = 4
|
||||||
|
image = SubResource( 28 )
|
||||||
|
size = Vector2( 8, 8 )
|
||||||
|
|
||||||
|
[sub_resource type="StyleBoxTexture" id=3]
|
||||||
|
texture = SubResource( 2 )
|
||||||
|
region_rect = Rect2( 0, 0, 8, 8 )
|
||||||
|
|
||||||
|
[sub_resource type="StyleBoxTexture" id=4]
|
||||||
|
content_margin_left = 7.0
|
||||||
|
content_margin_right = 7.0
|
||||||
|
content_margin_top = 7.0
|
||||||
|
content_margin_bottom = 7.0
|
||||||
|
texture = ExtResource( 2 )
|
||||||
|
region_rect = Rect2( 0, 0, 64, 64 )
|
||||||
|
margin_left = 4.0
|
||||||
|
margin_right = 4.0
|
||||||
|
margin_top = 4.0
|
||||||
|
margin_bottom = 4.0
|
||||||
|
|
||||||
|
[sub_resource type="StyleBoxFlat" id=6]
|
||||||
|
corner_radius_top_left = 1
|
||||||
|
corner_radius_top_right = 1
|
||||||
|
corner_radius_bottom_right = 1
|
||||||
|
corner_radius_bottom_left = 1
|
||||||
|
|
||||||
[resource]
|
[resource]
|
||||||
default_font = ExtResource( 1 )
|
default_font = ExtResource( 1 )
|
||||||
|
Button/colors/font_color = Color( 0.88, 0.88, 0.88, 1 )
|
||||||
|
Button/colors/font_color_disabled = Color( 0.533333, 0.533333, 0.533333, 1 )
|
||||||
|
Button/colors/font_color_focus = Color( 0.94, 0.94, 0.94, 1 )
|
||||||
|
Button/colors/font_color_hover = Color( 0.94, 0.94, 0.94, 1 )
|
||||||
|
Button/colors/font_color_pressed = Color( 1, 1, 1, 1 )
|
||||||
|
Button/constants/hseparation = 2
|
||||||
|
Button/fonts/font = ExtResource( 1 )
|
||||||
|
Button/styles/disabled = SubResource( 9 )
|
||||||
|
Button/styles/focus = SubResource( 12 )
|
||||||
|
Button/styles/hover = SubResource( 15 )
|
||||||
|
Button/styles/normal = SubResource( 18 )
|
||||||
|
Button/styles/pressed = SubResource( 21 )
|
||||||
|
Label/colors/font_color = Color( 1, 1, 1, 1 )
|
||||||
|
Label/colors/font_color_shadow = Color( 0, 0, 0, 0 )
|
||||||
|
Label/colors/font_outline_modulate = Color( 1, 1, 1, 1 )
|
||||||
|
Label/constants/line_spacing = 3
|
||||||
|
Label/constants/shadow_as_outline = 0
|
||||||
|
Label/constants/shadow_offset_x = 1
|
||||||
|
Label/constants/shadow_offset_y = 1
|
||||||
|
Label/fonts/font = ExtResource( 1 )
|
||||||
|
Label/styles/normal = SubResource( 22 )
|
||||||
|
Panel/styles/panel = SubResource( 3 )
|
||||||
|
PanelContainer/styles/panel = SubResource( 4 )
|
||||||
|
ProgressBar/styles/bg = ExtResource( 3 )
|
||||||
|
ProgressBar/styles/fg = SubResource( 6 )
|
||||||
|
|
77
assets/tileset.tres
Normal file
77
assets/tileset.tres
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
[gd_resource type="TileSet" load_steps=6 format=2]
|
||||||
|
|
||||||
|
[ext_resource path="res://assets/img/spaced-dungeon-tileset.png" type="Texture" id=1]
|
||||||
|
|
||||||
|
[sub_resource type="NavigationPolygon" id=2]
|
||||||
|
vertices = PoolVector2Array( 16, 16, 0, 16, 0, 0, 16, 0 )
|
||||||
|
polygons = [ PoolIntArray( 0, 1, 2, 3 ) ]
|
||||||
|
|
||||||
|
[sub_resource type="ConvexPolygonShape2D" id=3]
|
||||||
|
points = PoolVector2Array( 16, 16, 0, 16, 0, 0, 16, 0 )
|
||||||
|
|
||||||
|
[sub_resource type="OccluderPolygon2D" id=4]
|
||||||
|
polygon = PoolVector2Array( 16, 16, 0, 16, 0, 0, 16, 0 )
|
||||||
|
|
||||||
|
[sub_resource type="ConvexPolygonShape2D" id=5]
|
||||||
|
points = PoolVector2Array( 16, 16, 0, 16, 0, 0, 16, 0 )
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
resource_name = "dungeon"
|
||||||
|
0/name = "spaced-dungeon-tileset.png 0"
|
||||||
|
0/texture = ExtResource( 1 )
|
||||||
|
0/tex_offset = Vector2( 0, 0 )
|
||||||
|
0/modulate = Color( 1, 1, 1, 1 )
|
||||||
|
0/region = Rect2( 0, 54, 16, 16 )
|
||||||
|
0/tile_mode = 0
|
||||||
|
0/occluder_offset = Vector2( 0, 0 )
|
||||||
|
0/navigation_offset = Vector2( 0, 0 )
|
||||||
|
0/navigation = SubResource( 2 )
|
||||||
|
0/shape_offset = Vector2( 0, 0 )
|
||||||
|
0/shape_transform = Transform2D( 1, 0, 0, 1, 0, 0 )
|
||||||
|
0/shape_one_way = false
|
||||||
|
0/shape_one_way_margin = 0.0
|
||||||
|
0/shapes = [ ]
|
||||||
|
0/z_index = 0
|
||||||
|
1/name = "spaced-dungeon-tileset.png 1"
|
||||||
|
1/texture = ExtResource( 1 )
|
||||||
|
1/tex_offset = Vector2( 0, 0 )
|
||||||
|
1/modulate = Color( 1, 1, 1, 1 )
|
||||||
|
1/region = Rect2( 36, 36, 16, 16 )
|
||||||
|
1/tile_mode = 0
|
||||||
|
1/occluder_offset = Vector2( 0, 0 )
|
||||||
|
1/navigation_offset = Vector2( 0, 0 )
|
||||||
|
1/shape_offset = Vector2( 0, 0 )
|
||||||
|
1/shape_transform = Transform2D( 1, 0, 0, 1, 0, 0 )
|
||||||
|
1/shape = SubResource( 3 )
|
||||||
|
1/shape_one_way = false
|
||||||
|
1/shape_one_way_margin = 1.0
|
||||||
|
1/shapes = [ {
|
||||||
|
"autotile_coord": Vector2( 0, 0 ),
|
||||||
|
"one_way": false,
|
||||||
|
"one_way_margin": 1.0,
|
||||||
|
"shape": SubResource( 3 ),
|
||||||
|
"shape_transform": Transform2D( 1, 0, 0, 1, 0, 0 )
|
||||||
|
} ]
|
||||||
|
1/z_index = 0
|
||||||
|
2/name = "spaced-dungeon-tileset.png 2"
|
||||||
|
2/texture = ExtResource( 1 )
|
||||||
|
2/tex_offset = Vector2( 0, 0 )
|
||||||
|
2/modulate = Color( 1, 1, 1, 1 )
|
||||||
|
2/region = Rect2( 36, 18, 16, 16 )
|
||||||
|
2/tile_mode = 0
|
||||||
|
2/occluder_offset = Vector2( 0, 0 )
|
||||||
|
2/occluder = SubResource( 4 )
|
||||||
|
2/navigation_offset = Vector2( 0, 0 )
|
||||||
|
2/shape_offset = Vector2( 0, 0 )
|
||||||
|
2/shape_transform = Transform2D( 1, 0, 0, 1, 0, 0 )
|
||||||
|
2/shape = SubResource( 5 )
|
||||||
|
2/shape_one_way = false
|
||||||
|
2/shape_one_way_margin = 1.0
|
||||||
|
2/shapes = [ {
|
||||||
|
"autotile_coord": Vector2( 0, 0 ),
|
||||||
|
"one_way": false,
|
||||||
|
"one_way_margin": 1.0,
|
||||||
|
"shape": SubResource( 5 ),
|
||||||
|
"shape_transform": Transform2D( 1, 0, 0, 1, 0, 0 )
|
||||||
|
} ]
|
||||||
|
2/z_index = 0
|
17
assets/ui/hp_bar_bg.tres
Normal file
17
assets/ui/hp_bar_bg.tres
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
[gd_resource type="StyleBoxFlat" format=2]
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
content_margin_left = 0.0
|
||||||
|
content_margin_right = 0.0
|
||||||
|
content_margin_top = 0.0
|
||||||
|
content_margin_bottom = 0.0
|
||||||
|
bg_color = Color( 0.0666667, 0.0666667, 0.0666667, 1 )
|
||||||
|
border_width_left = 1
|
||||||
|
border_width_top = 1
|
||||||
|
border_width_right = 1
|
||||||
|
border_width_bottom = 1
|
||||||
|
border_color = Color( 1, 0.211765, 0, 1 )
|
||||||
|
corner_radius_top_left = 1
|
||||||
|
corner_radius_top_right = 1
|
||||||
|
corner_radius_bottom_right = 1
|
||||||
|
corner_radius_bottom_left = 1
|
17
assets/ui/hp_bar_fg.tres
Normal file
17
assets/ui/hp_bar_fg.tres
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
[gd_resource type="StyleBoxFlat" format=2]
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
content_margin_left = 0.0
|
||||||
|
content_margin_right = 0.0
|
||||||
|
content_margin_top = 0.0
|
||||||
|
content_margin_bottom = 0.0
|
||||||
|
bg_color = Color( 1, 0.211765, 0, 1 )
|
||||||
|
border_width_left = 1
|
||||||
|
border_width_top = 1
|
||||||
|
border_width_right = 1
|
||||||
|
border_width_bottom = 1
|
||||||
|
border_color = Color( 1, 0.211765, 0, 1 )
|
||||||
|
corner_radius_top_left = 1
|
||||||
|
corner_radius_top_right = 1
|
||||||
|
corner_radius_bottom_right = 1
|
||||||
|
corner_radius_bottom_left = 1
|
17
assets/ui/mana_bar_bg.tres
Normal file
17
assets/ui/mana_bar_bg.tres
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
[gd_resource type="StyleBoxFlat" format=2]
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
content_margin_left = 0.0
|
||||||
|
content_margin_right = 0.0
|
||||||
|
content_margin_top = 0.0
|
||||||
|
content_margin_bottom = 0.0
|
||||||
|
bg_color = Color( 0.0666667, 0.0666667, 0.0666667, 1 )
|
||||||
|
border_width_left = 1
|
||||||
|
border_width_top = 1
|
||||||
|
border_width_right = 1
|
||||||
|
border_width_bottom = 1
|
||||||
|
border_color = Color( 0, 0.309804, 1, 1 )
|
||||||
|
corner_radius_top_left = 1
|
||||||
|
corner_radius_top_right = 1
|
||||||
|
corner_radius_bottom_right = 1
|
||||||
|
corner_radius_bottom_left = 1
|
17
assets/ui/mana_bar_fg.tres
Normal file
17
assets/ui/mana_bar_fg.tres
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
[gd_resource type="StyleBoxFlat" format=2]
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
content_margin_left = 0.0
|
||||||
|
content_margin_right = 0.0
|
||||||
|
content_margin_top = 0.0
|
||||||
|
content_margin_bottom = 0.0
|
||||||
|
bg_color = Color( 0, 0.309804, 1, 1 )
|
||||||
|
border_width_left = 1
|
||||||
|
border_width_top = 1
|
||||||
|
border_width_right = 1
|
||||||
|
border_width_bottom = 1
|
||||||
|
border_color = Color( 0, 0.309804, 1, 1 )
|
||||||
|
corner_radius_top_left = 1
|
||||||
|
corner_radius_top_right = 1
|
||||||
|
corner_radius_bottom_right = 1
|
||||||
|
corner_radius_bottom_left = 1
|
12
assets/ui/progressbar_stylebox.tres
Normal file
12
assets/ui/progressbar_stylebox.tres
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
[gd_resource type="StyleBoxFlat" format=2]
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
bg_color = Color( 0.160784, 0.160784, 0.160784, 1 )
|
||||||
|
border_width_left = 1
|
||||||
|
border_width_top = 1
|
||||||
|
border_width_right = 1
|
||||||
|
border_width_bottom = 1
|
||||||
|
corner_radius_top_left = 1
|
||||||
|
corner_radius_top_right = 1
|
||||||
|
corner_radius_bottom_right = 1
|
||||||
|
corner_radius_bottom_left = 1
|
|
@ -1,10 +1,30 @@
|
||||||
[gd_scene format=2]
|
[gd_scene load_steps=2 format=2]
|
||||||
|
|
||||||
|
[ext_resource path="res://scripts/objects/bar.gd" type="Script" id=1]
|
||||||
|
|
||||||
[node name="bar" type="ProgressBar"]
|
[node name="bar" type="ProgressBar"]
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
anchor_bottom = 1.0
|
anchor_bottom = 1.0
|
||||||
value = 50.0
|
value = 50.0
|
||||||
percent_visible = false
|
percent_visible = false
|
||||||
|
script = ExtResource( 1 )
|
||||||
__meta__ = {
|
__meta__ = {
|
||||||
"_edit_use_anchors_": false
|
"_edit_use_anchors_": false
|
||||||
}
|
}
|
||||||
|
label = NodePath("label")
|
||||||
|
|
||||||
|
[node name="label" type="Label" parent="."]
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
margin_right = -1024.0
|
||||||
|
margin_bottom = -586.0
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
size_flags_vertical = 7
|
||||||
|
align = 1
|
||||||
|
valign = 1
|
||||||
|
__meta__ = {
|
||||||
|
"_edit_use_anchors_": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[connection signal="resized" from="." to="." method="_on_bar_resized"]
|
||||||
|
[connection signal="value_changed" from="." to="." method="_on_bar_value_changed"]
|
||||||
|
|
|
@ -14,7 +14,7 @@ __meta__ = {
|
||||||
|
|
||||||
[node name="TextureRect" type="TextureRect" parent="."]
|
[node name="TextureRect" type="TextureRect" parent="."]
|
||||||
margin_right = 24.0
|
margin_right = 24.0
|
||||||
margin_bottom = 600.0
|
margin_bottom = 720.0
|
||||||
rect_min_size = Vector2( 24, 24 )
|
rect_min_size = Vector2( 24, 24 )
|
||||||
size_flags_vertical = 11
|
size_flags_vertical = 11
|
||||||
texture = ExtResource( 2 )
|
texture = ExtResource( 2 )
|
||||||
|
@ -23,9 +23,16 @@ stretch_mode = 6
|
||||||
|
|
||||||
[node name="Label" type="Label" parent="."]
|
[node name="Label" type="Label" parent="."]
|
||||||
margin_left = 28.0
|
margin_left = 28.0
|
||||||
margin_right = 1024.0
|
margin_right = 1280.0
|
||||||
margin_bottom = 600.0
|
margin_bottom = 720.0
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
size_flags_vertical = 11
|
size_flags_vertical = 11
|
||||||
text = "Lobby Name"
|
text = "Lobby Name"
|
||||||
valign = 1
|
valign = 1
|
||||||
|
|
||||||
|
[node name="Button" type="Button" parent="."]
|
||||||
|
visible = false
|
||||||
|
margin_left = 1242.0
|
||||||
|
margin_right = 1280.0
|
||||||
|
margin_bottom = 720.0
|
||||||
|
text = "Kick"
|
||||||
|
|
|
@ -1,54 +1,50 @@
|
||||||
[gd_scene load_steps=16 format=2]
|
[gd_scene load_steps=19 format=2]
|
||||||
|
|
||||||
[ext_resource path="res://assets/img/spaced-dungeon-tileset.png" type="Texture" id=1]
|
[ext_resource path="res://assets/img/spaced-dungeon-tileset.png" type="Texture" id=1]
|
||||||
|
[ext_resource path="res://assets/theme.tres" type="Theme" id=2]
|
||||||
[ext_resource path="res://objects/bar.tscn" type="PackedScene" id=3]
|
[ext_resource path="res://objects/bar.tscn" type="PackedScene" id=3]
|
||||||
|
[ext_resource path="res://assets/ui/hp_bar_bg.tres" type="StyleBox" id=4]
|
||||||
|
[ext_resource path="res://assets/ui/hp_bar_fg.tres" type="StyleBox" id=5]
|
||||||
|
[ext_resource path="res://assets/ui/mana_bar_fg.tres" type="StyleBox" id=6]
|
||||||
|
[ext_resource path="res://assets/ui/mana_bar_bg.tres" type="StyleBox" id=7]
|
||||||
[ext_resource path="res://scripts/objects/player.gd" type="Script" id=10]
|
[ext_resource path="res://scripts/objects/player.gd" type="Script" id=10]
|
||||||
|
|
||||||
[sub_resource type="AtlasTexture" id=1]
|
[sub_resource type="AtlasTexture" id=1]
|
||||||
flags = 4
|
|
||||||
atlas = ExtResource( 1 )
|
atlas = ExtResource( 1 )
|
||||||
region = Rect2( 0, 468, 18, 36 )
|
region = Rect2( 0, 468, 18, 36 )
|
||||||
|
|
||||||
[sub_resource type="AtlasTexture" id=2]
|
[sub_resource type="AtlasTexture" id=2]
|
||||||
flags = 4
|
|
||||||
atlas = ExtResource( 1 )
|
atlas = ExtResource( 1 )
|
||||||
region = Rect2( 18, 468, 18, 36 )
|
region = Rect2( 18, 468, 18, 36 )
|
||||||
|
|
||||||
[sub_resource type="AtlasTexture" id=3]
|
[sub_resource type="AtlasTexture" id=3]
|
||||||
flags = 4
|
|
||||||
atlas = ExtResource( 1 )
|
atlas = ExtResource( 1 )
|
||||||
region = Rect2( 36, 468, 18, 36 )
|
region = Rect2( 36, 468, 18, 36 )
|
||||||
|
|
||||||
[sub_resource type="AtlasTexture" id=4]
|
[sub_resource type="AtlasTexture" id=4]
|
||||||
flags = 4
|
|
||||||
atlas = ExtResource( 1 )
|
atlas = ExtResource( 1 )
|
||||||
region = Rect2( 54, 468, 18, 36 )
|
region = Rect2( 54, 468, 18, 36 )
|
||||||
|
|
||||||
[sub_resource type="AtlasTexture" id=9]
|
|
||||||
flags = 4
|
|
||||||
atlas = ExtResource( 1 )
|
|
||||||
region = Rect2( 144, 468, 18, 36 )
|
|
||||||
|
|
||||||
[sub_resource type="AtlasTexture" id=5]
|
[sub_resource type="AtlasTexture" id=5]
|
||||||
flags = 4
|
|
||||||
atlas = ExtResource( 1 )
|
atlas = ExtResource( 1 )
|
||||||
region = Rect2( 72, 468, 18, 36 )
|
region = Rect2( 72, 468, 18, 36 )
|
||||||
|
|
||||||
[sub_resource type="AtlasTexture" id=6]
|
[sub_resource type="AtlasTexture" id=6]
|
||||||
flags = 4
|
|
||||||
atlas = ExtResource( 1 )
|
atlas = ExtResource( 1 )
|
||||||
region = Rect2( 90, 468, 18, 36 )
|
region = Rect2( 90, 468, 18, 36 )
|
||||||
|
|
||||||
[sub_resource type="AtlasTexture" id=7]
|
[sub_resource type="AtlasTexture" id=7]
|
||||||
flags = 4
|
|
||||||
atlas = ExtResource( 1 )
|
atlas = ExtResource( 1 )
|
||||||
region = Rect2( 108, 468, 18, 36 )
|
region = Rect2( 108, 468, 18, 36 )
|
||||||
|
|
||||||
[sub_resource type="AtlasTexture" id=8]
|
[sub_resource type="AtlasTexture" id=8]
|
||||||
flags = 4
|
|
||||||
atlas = ExtResource( 1 )
|
atlas = ExtResource( 1 )
|
||||||
region = Rect2( 126, 468, 18, 36 )
|
region = Rect2( 126, 468, 18, 36 )
|
||||||
|
|
||||||
|
[sub_resource type="AtlasTexture" id=9]
|
||||||
|
atlas = ExtResource( 1 )
|
||||||
|
region = Rect2( 144, 468, 18, 36 )
|
||||||
|
|
||||||
[sub_resource type="SpriteFrames" id=10]
|
[sub_resource type="SpriteFrames" id=10]
|
||||||
animations = [ {
|
animations = [ {
|
||||||
"frames": [ SubResource( 1 ), SubResource( 2 ), SubResource( 3 ), SubResource( 4 ) ],
|
"frames": [ SubResource( 1 ), SubResource( 2 ), SubResource( 3 ), SubResource( 4 ) ],
|
||||||
|
@ -56,76 +52,121 @@ animations = [ {
|
||||||
"name": "idle",
|
"name": "idle",
|
||||||
"speed": 6.0
|
"speed": 6.0
|
||||||
}, {
|
}, {
|
||||||
"frames": [ SubResource( 9 ) ],
|
|
||||||
"loop": true,
|
|
||||||
"name": "hurt",
|
|
||||||
"speed": 5.0
|
|
||||||
}, {
|
|
||||||
"frames": [ SubResource( 5 ), SubResource( 6 ), SubResource( 7 ), SubResource( 8 ) ],
|
"frames": [ SubResource( 5 ), SubResource( 6 ), SubResource( 7 ), SubResource( 8 ) ],
|
||||||
"loop": true,
|
"loop": true,
|
||||||
"name": "run",
|
"name": "run",
|
||||||
"speed": 12.0
|
"speed": 12.0
|
||||||
|
}, {
|
||||||
|
"frames": [ SubResource( 9 ) ],
|
||||||
|
"loop": true,
|
||||||
|
"name": "hurt",
|
||||||
|
"speed": 5.0
|
||||||
} ]
|
} ]
|
||||||
|
|
||||||
[sub_resource type="ConvexPolygonShape2D" id=11]
|
|
||||||
points = PoolVector2Array( -2, 4, 2, 4, 6, 1, 6, -1, 1, -4, -1, -4, -6, -1, -6, 1 )
|
|
||||||
|
|
||||||
[sub_resource type="StyleBoxFlat" id=12]
|
|
||||||
bg_color = Color( 0, 0.25098, 1, 1 )
|
|
||||||
|
|
||||||
[node name="player" type="KinematicBody2D" groups=["destructable"]]
|
[node name="player" type="KinematicBody2D" groups=["destructable"]]
|
||||||
script = ExtResource( 10 )
|
script = ExtResource( 10 )
|
||||||
|
sprite = NodePath("sprite")
|
||||||
|
collider = NodePath("collider")
|
||||||
|
name_label = NodePath("ui/player_name")
|
||||||
|
health_bar = NodePath("ui/c2/bars/health_bar")
|
||||||
|
mana_bar = NodePath("ui/c2/bars/mana_bar")
|
||||||
|
aim_indicator = NodePath("collider/aim_indicator")
|
||||||
|
tile_indicator = NodePath("collider/tile_indicator")
|
||||||
|
|
||||||
[node name="sprite" type="AnimatedSprite" parent="."]
|
[node name="sprite" type="AnimatedSprite" parent="."]
|
||||||
modulate = Color( 0.384314, 0.796078, 1, 1 )
|
modulate = Color( 0.384314, 0.796078, 1, 1 )
|
||||||
position = Vector2( 0, -18 )
|
position = Vector2( 0, -48 )
|
||||||
|
scale = Vector2( 4, 4 )
|
||||||
frames = SubResource( 10 )
|
frames = SubResource( 10 )
|
||||||
animation = "idle"
|
animation = "idle"
|
||||||
frame = 1
|
frame = 3
|
||||||
playing = true
|
playing = true
|
||||||
|
|
||||||
[node name="collider" type="CollisionShape2D" parent="."]
|
[node name="collider" type="CollisionPolygon2D" parent="."]
|
||||||
position = Vector2( 0, -6.5 )
|
scale = Vector2( 4, 4 )
|
||||||
scale = Vector2( 1, 1.6 )
|
polygon = PoolVector2Array( 6, -1, 6, 1, 1, 6, -1, 6, -6, 1, -6, -1, -1, -6, 1, -6 )
|
||||||
shape = SubResource( 11 )
|
|
||||||
|
|
||||||
[node name="player_name" type="Label" parent="."]
|
[node name="aim_indicator" type="Sprite" parent="collider"]
|
||||||
margin_left = -21.0
|
modulate = Color( 1, 1, 1, 0.25098 )
|
||||||
margin_right = 21.0
|
texture = ExtResource( 1 )
|
||||||
margin_bottom = 25.0
|
offset = Vector2( 0, 16 )
|
||||||
text = "player_name"
|
region_enabled = true
|
||||||
align = 1
|
region_rect = Rect2( 163, 487, 4, 7 )
|
||||||
|
|
||||||
|
[node name="tile_indicator" type="Sprite" parent="collider"]
|
||||||
|
modulate = Color( 1, 1, 1, 0.25098 )
|
||||||
|
scale = Vector2( 2, 2 )
|
||||||
|
texture = ExtResource( 1 )
|
||||||
|
region_enabled = true
|
||||||
|
region_rect = Rect2( 0, 72, 16, 16 )
|
||||||
|
|
||||||
|
[node name="ui" type="VBoxContainer" parent="."]
|
||||||
|
modulate = Color( 1, 1, 1, 0.498039 )
|
||||||
|
anchor_left = 0.5
|
||||||
|
anchor_top = 0.5
|
||||||
|
anchor_right = 0.5
|
||||||
|
anchor_bottom = 0.5
|
||||||
|
margin_left = -59.0
|
||||||
|
margin_top = -116.0
|
||||||
|
margin_right = 61.0
|
||||||
|
margin_bottom = 55.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
__meta__ = {
|
__meta__ = {
|
||||||
"_edit_use_anchors_": false
|
"_edit_use_anchors_": false
|
||||||
}
|
}
|
||||||
|
|
||||||
[node name="bars" type="VBoxContainer" parent="."]
|
[node name="c2" type="CenterContainer" parent="ui"]
|
||||||
margin_left = -18.0
|
margin_right = 120.0
|
||||||
margin_top = -26.0
|
margin_bottom = 44.0
|
||||||
margin_right = 18.0
|
size_flags_vertical = 0
|
||||||
margin_bottom = -20.0
|
|
||||||
size_flags_horizontal = 3
|
[node name="bars" type="VBoxContainer" parent="ui/c2"]
|
||||||
size_flags_vertical = 3
|
margin_right = 120.0
|
||||||
custom_constants/separation = 0
|
margin_bottom = 44.0
|
||||||
|
rect_min_size = Vector2( 120, 44 )
|
||||||
|
size_flags_horizontal = 0
|
||||||
|
size_flags_vertical = 0
|
||||||
|
custom_constants/separation = 4
|
||||||
alignment = 1
|
alignment = 1
|
||||||
__meta__ = {
|
__meta__ = {
|
||||||
"_edit_use_anchors_": false
|
"_edit_use_anchors_": false
|
||||||
}
|
}
|
||||||
|
|
||||||
[node name="health_bar" parent="bars" instance=ExtResource( 3 )]
|
[node name="health_bar" parent="ui/c2/bars" instance=ExtResource( 3 )]
|
||||||
anchor_right = 0.0
|
anchor_right = 0.0
|
||||||
anchor_bottom = 0.0
|
anchor_bottom = 0.0
|
||||||
margin_right = 36.0
|
margin_right = 120.0
|
||||||
margin_bottom = 3.0
|
margin_bottom = 20.0
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
size_flags_vertical = 3
|
size_flags_vertical = 3
|
||||||
|
theme = ExtResource( 2 )
|
||||||
|
custom_styles/fg = ExtResource( 5 )
|
||||||
|
custom_styles/bg = ExtResource( 4 )
|
||||||
|
|
||||||
[node name="mana_bar" parent="bars" instance=ExtResource( 3 )]
|
[node name="mana_bar" parent="ui/c2/bars" instance=ExtResource( 3 )]
|
||||||
anchor_right = 0.0
|
anchor_right = 0.0
|
||||||
anchor_bottom = 0.0
|
anchor_bottom = 0.0
|
||||||
margin_top = 3.0
|
margin_top = 24.0
|
||||||
margin_right = 36.0
|
margin_right = 120.0
|
||||||
margin_bottom = 6.0
|
margin_bottom = 44.0
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
size_flags_vertical = 3
|
size_flags_vertical = 3
|
||||||
custom_styles/fg = SubResource( 12 )
|
theme = ExtResource( 2 )
|
||||||
|
custom_styles/fg = ExtResource( 6 )
|
||||||
|
custom_styles/bg = ExtResource( 7 )
|
||||||
|
max_value = 1000.0
|
||||||
|
value = 600.0
|
||||||
|
|
||||||
|
[node name="player_name" type="Label" parent="ui"]
|
||||||
|
margin_top = 48.0
|
||||||
|
margin_right = 120.0
|
||||||
|
margin_bottom = 171.0
|
||||||
|
size_flags_horizontal = 7
|
||||||
|
size_flags_vertical = 7
|
||||||
|
text = "player_name"
|
||||||
|
align = 1
|
||||||
|
valign = 2
|
||||||
|
__meta__ = {
|
||||||
|
"_edit_use_anchors_": false
|
||||||
|
}
|
||||||
|
|
66
objects/slime.tscn
Normal file
66
objects/slime.tscn
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
[gd_scene load_steps=12 format=2]
|
||||||
|
|
||||||
|
[ext_resource path="res://assets/img/spaced-dungeon-tileset.png" type="Texture" id=1]
|
||||||
|
[ext_resource path="res://scripts/objects/slime.gd" type="Script" id=2]
|
||||||
|
[ext_resource path="res://scripts/objects/destructable.gd" type="Script" id=3]
|
||||||
|
|
||||||
|
[sub_resource type="AtlasTexture" id=5]
|
||||||
|
atlas = ExtResource( 1 )
|
||||||
|
region = Rect2( 0, 432, 16, 36 )
|
||||||
|
|
||||||
|
[sub_resource type="AtlasTexture" id=1]
|
||||||
|
atlas = ExtResource( 1 )
|
||||||
|
region = Rect2( 0, 432, 16, 36 )
|
||||||
|
|
||||||
|
[sub_resource type="AtlasTexture" id=2]
|
||||||
|
atlas = ExtResource( 1 )
|
||||||
|
region = Rect2( 18, 432, 16, 36 )
|
||||||
|
|
||||||
|
[sub_resource type="AtlasTexture" id=3]
|
||||||
|
atlas = ExtResource( 1 )
|
||||||
|
region = Rect2( 36, 432, 16, 36 )
|
||||||
|
|
||||||
|
[sub_resource type="AtlasTexture" id=4]
|
||||||
|
atlas = ExtResource( 1 )
|
||||||
|
region = Rect2( 54, 432, 16, 36 )
|
||||||
|
|
||||||
|
[sub_resource type="AtlasTexture" id=6]
|
||||||
|
atlas = ExtResource( 1 )
|
||||||
|
region = Rect2( 0, 432, 16, 36 )
|
||||||
|
|
||||||
|
[sub_resource type="SpriteFrames" id=7]
|
||||||
|
animations = [ {
|
||||||
|
"frames": [ SubResource( 5 ), SubResource( 1 ), SubResource( 2 ), SubResource( 3 ), SubResource( 4 ), SubResource( 6 ) ],
|
||||||
|
"loop": true,
|
||||||
|
"name": "idle",
|
||||||
|
"speed": 6.0
|
||||||
|
} ]
|
||||||
|
|
||||||
|
[sub_resource type="CapsuleShape2D" id=8]
|
||||||
|
radius = 6.0
|
||||||
|
height = 0.0
|
||||||
|
|
||||||
|
[node name="slime" type="KinematicBody2D"]
|
||||||
|
scale = Vector2( 2, 2 )
|
||||||
|
script = ExtResource( 2 )
|
||||||
|
|
||||||
|
[node name="sprite" type="AnimatedSprite" parent="."]
|
||||||
|
scale = Vector2( 4, 4 )
|
||||||
|
frames = SubResource( 7 )
|
||||||
|
animation = "idle"
|
||||||
|
playing = true
|
||||||
|
offset = Vector2( 1, -11 )
|
||||||
|
|
||||||
|
[node name="hurtbox" type="CollisionShape2D" parent="."]
|
||||||
|
scale = Vector2( 4, 4 )
|
||||||
|
shape = SubResource( 8 )
|
||||||
|
script = ExtResource( 3 )
|
||||||
|
|
||||||
|
[node name="nav_line" type="Line2D" parent="."]
|
||||||
|
points = PoolVector2Array( 0, 30, 30, 30, 0, 0 )
|
||||||
|
|
||||||
|
[node name="NavigationAgent2D" type="NavigationAgent2D" parent="."]
|
||||||
|
target_desired_distance = 60.0
|
||||||
|
radius = 30.0
|
||||||
|
|
||||||
|
[connection signal="die" from="hurtbox" to="." method="_on_hurtbox_die"]
|
|
@ -9,6 +9,11 @@
|
||||||
config_version=4
|
config_version=4
|
||||||
|
|
||||||
_global_script_classes=[ {
|
_global_script_classes=[ {
|
||||||
|
"base": "CollisionShape2D",
|
||||||
|
"class": "Destructable",
|
||||||
|
"language": "GDScript",
|
||||||
|
"path": "res://scripts/objects/destructable.gd"
|
||||||
|
}, {
|
||||||
"base": "HBoxContainer",
|
"base": "HBoxContainer",
|
||||||
"class": "LobbyControl",
|
"class": "LobbyControl",
|
||||||
"language": "GDScript",
|
"language": "GDScript",
|
||||||
|
@ -18,26 +23,77 @@ _global_script_classes=[ {
|
||||||
"class": "PeerControl",
|
"class": "PeerControl",
|
||||||
"language": "GDScript",
|
"language": "GDScript",
|
||||||
"path": "res://scripts/objects/peer.gd"
|
"path": "res://scripts/objects/peer.gd"
|
||||||
|
}, {
|
||||||
|
"base": "KinematicBody2D",
|
||||||
|
"class": "Slime",
|
||||||
|
"language": "GDScript",
|
||||||
|
"path": "res://scripts/objects/slime.gd"
|
||||||
} ]
|
} ]
|
||||||
_global_script_class_icons={
|
_global_script_class_icons={
|
||||||
|
"Destructable": "",
|
||||||
"LobbyControl": "",
|
"LobbyControl": "",
|
||||||
"PeerControl": ""
|
"PeerControl": "",
|
||||||
|
"Slime": ""
|
||||||
}
|
}
|
||||||
|
|
||||||
[application]
|
[application]
|
||||||
|
|
||||||
config/name="kdt"
|
config/name="webrtc_tech_demo"
|
||||||
run/main_scene="res://screens/main_menu.tscn"
|
run/main_scene="res://screens/main_menu.tscn"
|
||||||
|
boot_splash/fullsize=false
|
||||||
|
boot_splash/use_filter=false
|
||||||
|
boot_splash/bg_color=Color( 0, 0, 0, 1 )
|
||||||
config/icon="res://assets/img/icon.png"
|
config/icon="res://assets/img/icon.png"
|
||||||
|
|
||||||
[autoload]
|
[autoload]
|
||||||
|
|
||||||
Global="*res://scripts/global/global.gd"
|
Global="*res://scripts/global/global.gd"
|
||||||
|
|
||||||
|
[display]
|
||||||
|
|
||||||
|
window/size/width=1280
|
||||||
|
window/size/height=720
|
||||||
|
window/size/borderless=true
|
||||||
|
window/dpi/allow_hidpi=true
|
||||||
|
|
||||||
[gdnative]
|
[gdnative]
|
||||||
|
|
||||||
singletons=[ "res://webrtc/webrtc.tres" ]
|
singletons=[ "res://webrtc/webrtc.tres" ]
|
||||||
|
|
||||||
|
[input]
|
||||||
|
|
||||||
|
ui_cancel={
|
||||||
|
"deadzone": 0.5,
|
||||||
|
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777217,"physical_scancode":0,"unicode":0,"echo":false,"script":null)
|
||||||
|
, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":1,"pressure":0.0,"pressed":false,"script":null)
|
||||||
|
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777220,"physical_scancode":0,"unicode":0,"echo":false,"script":null)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
up={
|
||||||
|
"deadzone": 0.5,
|
||||||
|
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":87,"physical_scancode":0,"unicode":0,"echo":false,"script":null)
|
||||||
|
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777232,"physical_scancode":0,"unicode":0,"echo":false,"script":null)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
down={
|
||||||
|
"deadzone": 0.5,
|
||||||
|
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":83,"physical_scancode":0,"unicode":0,"echo":false,"script":null)
|
||||||
|
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777234,"physical_scancode":0,"unicode":0,"echo":false,"script":null)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
left={
|
||||||
|
"deadzone": 0.5,
|
||||||
|
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":65,"physical_scancode":0,"unicode":0,"echo":false,"script":null)
|
||||||
|
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777231,"physical_scancode":0,"unicode":0,"echo":false,"script":null)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
right={
|
||||||
|
"deadzone": 0.5,
|
||||||
|
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":68,"physical_scancode":0,"unicode":0,"echo":false,"script":null)
|
||||||
|
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777233,"physical_scancode":0,"unicode":0,"echo":false,"script":null)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
[physics]
|
[physics]
|
||||||
|
|
||||||
common/enable_pause_aware_picking=true
|
common/enable_pause_aware_picking=true
|
||||||
|
@ -45,6 +101,7 @@ common/enable_pause_aware_picking=true
|
||||||
[rendering]
|
[rendering]
|
||||||
|
|
||||||
quality/driver/driver_name="GLES2"
|
quality/driver/driver_name="GLES2"
|
||||||
|
2d/snapping/use_gpu_pixel_snap=true
|
||||||
vram_compression/import_etc=true
|
vram_compression/import_etc=true
|
||||||
vram_compression/import_etc2=false
|
vram_compression/import_etc2=false
|
||||||
environment/default_clear_color=Color( 0, 0, 0, 1 )
|
environment/default_clear_color=Color( 0, 0, 0, 1 )
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -62,10 +62,10 @@ __meta__ = {
|
||||||
}
|
}
|
||||||
|
|
||||||
[node name="subhead" type="HBoxContainer" parent="v"]
|
[node name="subhead" type="HBoxContainer" parent="v"]
|
||||||
visible = false
|
|
||||||
margin_top = 54.0
|
margin_top = 54.0
|
||||||
margin_right = 984.0
|
margin_right = 984.0
|
||||||
margin_bottom = 85.0
|
margin_bottom = 85.0
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
|
||||||
[node name="label" type="Label" parent="v/subhead"]
|
[node name="label" type="Label" parent="v/subhead"]
|
||||||
margin_right = 830.0
|
margin_right = 830.0
|
||||||
|
@ -83,7 +83,7 @@ rect_min_size = Vector2( 150, 0 )
|
||||||
placeholder_text = "Your Name Here"
|
placeholder_text = "Your Name Here"
|
||||||
|
|
||||||
[node name="body" type="ScrollContainer" parent="v"]
|
[node name="body" type="ScrollContainer" parent="v"]
|
||||||
margin_top = 54.0
|
margin_top = 89.0
|
||||||
margin_right = 984.0
|
margin_right = 984.0
|
||||||
margin_bottom = 560.0
|
margin_bottom = 560.0
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
|
@ -91,7 +91,7 @@ size_flags_vertical = 3
|
||||||
|
|
||||||
[node name="p" type="PanelContainer" parent="v/body"]
|
[node name="p" type="PanelContainer" parent="v/body"]
|
||||||
margin_right = 984.0
|
margin_right = 984.0
|
||||||
margin_bottom = 506.0
|
margin_bottom = 471.0
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
size_flags_vertical = 3
|
size_flags_vertical = 3
|
||||||
|
|
||||||
|
@ -99,21 +99,21 @@ size_flags_vertical = 3
|
||||||
margin_left = 7.0
|
margin_left = 7.0
|
||||||
margin_top = 7.0
|
margin_top = 7.0
|
||||||
margin_right = 977.0
|
margin_right = 977.0
|
||||||
margin_bottom = 499.0
|
margin_bottom = 464.0
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
size_flags_vertical = 3
|
size_flags_vertical = 3
|
||||||
|
|
||||||
[node name="zero_state" type="CenterContainer" parent="v/body/p/lobbies"]
|
[node name="zero_state" type="CenterContainer" parent="v/body/p/lobbies"]
|
||||||
margin_right = 970.0
|
margin_right = 970.0
|
||||||
margin_bottom = 492.0
|
margin_bottom = 457.0
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
size_flags_vertical = 3
|
size_flags_vertical = 3
|
||||||
|
|
||||||
[node name="Label" type="Label" parent="v/body/p/lobbies/zero_state"]
|
[node name="Label" type="Label" parent="v/body/p/lobbies/zero_state"]
|
||||||
margin_left = 229.0
|
margin_left = 177.0
|
||||||
margin_top = 239.0
|
margin_top = 218.0
|
||||||
margin_right = 740.0
|
margin_right = 793.0
|
||||||
margin_bottom = 253.0
|
margin_bottom = 239.0
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
size_flags_vertical = 3
|
size_flags_vertical = 3
|
||||||
text = "Looks like there are no active lobbies at the moment! Why don't you make one?"
|
text = "Looks like there are no active lobbies at the moment! Why don't you make one?"
|
||||||
|
|
|
@ -1,48 +1,51 @@
|
||||||
[gd_scene load_steps=4 format=2]
|
[gd_scene load_steps=5 format=2]
|
||||||
|
|
||||||
[ext_resource path="res://assets/fonts/iosevkalyte/iosevkalyte-regular.ttf" type="DynamicFontData" id=1]
|
[ext_resource path="res://assets/fonts/iosevkalyte/iosevkalyte-regular.ttf" type="DynamicFontData" id=1]
|
||||||
[ext_resource path="res://scripts/screens/main_menu.gd" type="Script" id=3]
|
[ext_resource path="res://scripts/screens/main_menu.gd" type="Script" id=3]
|
||||||
|
[ext_resource path="res://assets/theme.tres" type="Theme" id=4]
|
||||||
|
|
||||||
[sub_resource type="DynamicFont" id=1]
|
[sub_resource type="DynamicFont" id=1]
|
||||||
size = 70
|
size = 70
|
||||||
font_data = ExtResource( 1 )
|
font_data = ExtResource( 1 )
|
||||||
|
|
||||||
[node name="Control" type="MarginContainer"]
|
[node name="main_menu" type="MarginContainer"]
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
anchor_bottom = 1.0
|
anchor_bottom = 1.0
|
||||||
margin_left = 20.0
|
margin_left = 20.0
|
||||||
margin_top = 20.0
|
margin_top = 20.0
|
||||||
margin_right = -20.0
|
margin_right = -20.0
|
||||||
margin_bottom = -20.0
|
margin_bottom = -20.0
|
||||||
|
theme = ExtResource( 4 )
|
||||||
script = ExtResource( 3 )
|
script = ExtResource( 3 )
|
||||||
__meta__ = {
|
__meta__ = {
|
||||||
"_edit_use_anchors_": false
|
"_edit_use_anchors_": false
|
||||||
}
|
}
|
||||||
|
|
||||||
[node name="VBoxContainer" type="VBoxContainer" parent="."]
|
[node name="v" type="VBoxContainer" parent="."]
|
||||||
margin_right = 984.0
|
margin_right = 1240.0
|
||||||
margin_bottom = 560.0
|
margin_bottom = 680.0
|
||||||
|
|
||||||
[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer"]
|
[node name="h" type="HBoxContainer" parent="v"]
|
||||||
margin_right = 984.0
|
margin_right = 1240.0
|
||||||
margin_bottom = 50.0
|
margin_bottom = 50.0
|
||||||
alignment = 1
|
alignment = 1
|
||||||
|
|
||||||
[node name="quit" type="Button" parent="VBoxContainer/HBoxContainer"]
|
[node name="quit" type="Button" parent="v/h"]
|
||||||
margin_right = 325.0
|
margin_right = 410.0
|
||||||
margin_bottom = 50.0
|
margin_bottom = 50.0
|
||||||
rect_min_size = Vector2( 150, 50 )
|
rect_min_size = Vector2( 150, 50 )
|
||||||
hint_tooltip = "Yeah, let's get out of here."
|
hint_tooltip = "Yeah, let's get out of here."
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
size_flags_vertical = 3
|
size_flags_vertical = 3
|
||||||
text = "Quit"
|
text = "Quit"
|
||||||
|
expand_icon = true
|
||||||
__meta__ = {
|
__meta__ = {
|
||||||
"_edit_use_anchors_": false
|
"_edit_use_anchors_": false
|
||||||
}
|
}
|
||||||
|
|
||||||
[node name="multiplayer" type="Button" parent="VBoxContainer/HBoxContainer"]
|
[node name="multiplayer" type="Button" parent="v/h"]
|
||||||
margin_left = 329.0
|
margin_left = 414.0
|
||||||
margin_right = 654.0
|
margin_right = 825.0
|
||||||
margin_bottom = 50.0
|
margin_bottom = 50.0
|
||||||
rect_min_size = Vector2( 150, 50 )
|
rect_min_size = Vector2( 150, 50 )
|
||||||
hint_tooltip = "This is what you came for!"
|
hint_tooltip = "This is what you came for!"
|
||||||
|
@ -50,9 +53,9 @@ size_flags_horizontal = 3
|
||||||
size_flags_vertical = 3
|
size_flags_vertical = 3
|
||||||
text = "Multiplayer"
|
text = "Multiplayer"
|
||||||
|
|
||||||
[node name="Singleplayer" type="Button" parent="VBoxContainer/HBoxContainer"]
|
[node name="singleplayer" type="Button" parent="v/h"]
|
||||||
margin_left = 658.0
|
margin_left = 829.0
|
||||||
margin_right = 984.0
|
margin_right = 1240.0
|
||||||
margin_bottom = 50.0
|
margin_bottom = 50.0
|
||||||
rect_min_size = Vector2( 150, 50 )
|
rect_min_size = Vector2( 150, 50 )
|
||||||
hint_tooltip = "This is a multiplayer tech demo, you dunce.
|
hint_tooltip = "This is a multiplayer tech demo, you dunce.
|
||||||
|
@ -65,20 +68,20 @@ __meta__ = {
|
||||||
"_edit_use_anchors_": false
|
"_edit_use_anchors_": false
|
||||||
}
|
}
|
||||||
|
|
||||||
[node name="Container" type="CenterContainer" parent="VBoxContainer"]
|
[node name="c" type="CenterContainer" parent="v"]
|
||||||
margin_top = 54.0
|
margin_top = 54.0
|
||||||
margin_right = 984.0
|
margin_right = 1240.0
|
||||||
margin_bottom = 560.0
|
margin_bottom = 680.0
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
size_flags_vertical = 3
|
size_flags_vertical = 3
|
||||||
|
|
||||||
[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/Container"]
|
[node name="v" type="VBoxContainer" parent="v/c"]
|
||||||
margin_left = 124.0
|
margin_left = 252.0
|
||||||
margin_top = 153.0
|
margin_top = 210.0
|
||||||
margin_right = 859.0
|
margin_right = 987.0
|
||||||
margin_bottom = 352.0
|
margin_bottom = 416.0
|
||||||
|
|
||||||
[node name="Label" type="Label" parent="VBoxContainer/Container/VBoxContainer"]
|
[node name="title" type="Label" parent="v/c/v"]
|
||||||
margin_right = 735.0
|
margin_right = 735.0
|
||||||
margin_bottom = 181.0
|
margin_bottom = 181.0
|
||||||
custom_fonts/font = SubResource( 1 )
|
custom_fonts/font = SubResource( 1 )
|
||||||
|
@ -87,26 +90,26 @@ Multiplayer Tech Demo"
|
||||||
align = 1
|
align = 1
|
||||||
valign = 1
|
valign = 1
|
||||||
|
|
||||||
[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/Container/VBoxContainer"]
|
[node name="h" type="HBoxContainer" parent="v/c/v"]
|
||||||
margin_top = 185.0
|
margin_top = 185.0
|
||||||
margin_right = 735.0
|
margin_right = 735.0
|
||||||
margin_bottom = 199.0
|
margin_bottom = 206.0
|
||||||
alignment = 1
|
alignment = 1
|
||||||
|
|
||||||
[node name="Label" type="Label" parent="VBoxContainer/Container/VBoxContainer/HBoxContainer"]
|
[node name="made_by" type="Label" parent="v/c/v/h"]
|
||||||
margin_left = 312.0
|
margin_left = 305.0
|
||||||
margin_right = 371.0
|
margin_right = 369.0
|
||||||
margin_bottom = 14.0
|
margin_bottom = 21.0
|
||||||
text = "made by "
|
text = "made by "
|
||||||
|
|
||||||
[node name="LinkButton" type="LinkButton" parent="VBoxContainer/Container/VBoxContainer/HBoxContainer"]
|
[node name="link" type="LinkButton" parent="v/c/v/h"]
|
||||||
margin_left = 375.0
|
margin_left = 373.0
|
||||||
margin_right = 422.0
|
margin_right = 429.0
|
||||||
margin_bottom = 14.0
|
margin_bottom = 21.0
|
||||||
hint_tooltip = "Open https://lyte.dev"
|
hint_tooltip = "Open https://lyte.dev"
|
||||||
text = "lytedev"
|
text = "lytedev"
|
||||||
|
|
||||||
[connection signal="pressed" from="VBoxContainer/HBoxContainer/quit" to="." method="_on_JoinLobbyButton_pressed"]
|
[connection signal="pressed" from="v/h/quit" to="." method="_on_JoinLobbyButton_pressed"]
|
||||||
[connection signal="pressed" from="VBoxContainer/HBoxContainer/multiplayer" to="." method="_on_CreateLobbyButton_pressed"]
|
[connection signal="pressed" from="v/h/multiplayer" to="." method="_on_multiplayer_pressed"]
|
||||||
[connection signal="pressed" from="VBoxContainer/HBoxContainer/Singleplayer" to="." method="_on_Singleplayer_pressed"]
|
[connection signal="pressed" from="v/h/singleplayer" to="." method="_on_Singleplayer_pressed"]
|
||||||
[connection signal="pressed" from="VBoxContainer/Container/VBoxContainer/HBoxContainer/LinkButton" to="." method="_on_LinkButton_pressed"]
|
[connection signal="pressed" from="v/c/v/h/link" to="." method="_on_LinkButton_pressed"]
|
||||||
|
|
|
@ -17,11 +17,11 @@ __meta__ = {
|
||||||
}
|
}
|
||||||
|
|
||||||
[node name="v" type="VBoxContainer" parent="."]
|
[node name="v" type="VBoxContainer" parent="."]
|
||||||
margin_right = 984.0
|
margin_right = 1240.0
|
||||||
margin_bottom = 560.0
|
margin_bottom = 680.0
|
||||||
|
|
||||||
[node name="head" type="HBoxContainer" parent="v"]
|
[node name="head" type="HBoxContainer" parent="v"]
|
||||||
margin_right = 984.0
|
margin_right = 1240.0
|
||||||
margin_bottom = 50.0
|
margin_bottom = 50.0
|
||||||
|
|
||||||
[node name="leave_button" type="Button" parent="v/head"]
|
[node name="leave_button" type="Button" parent="v/head"]
|
||||||
|
@ -37,7 +37,7 @@ __meta__ = {
|
||||||
[node name="lobby_info" type="LineEdit" parent="v/head"]
|
[node name="lobby_info" type="LineEdit" parent="v/head"]
|
||||||
margin_left = 154.0
|
margin_left = 154.0
|
||||||
margin_top = 9.0
|
margin_top = 9.0
|
||||||
margin_right = 356.0
|
margin_right = 616.0
|
||||||
margin_bottom = 40.0
|
margin_bottom = 40.0
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
size_flags_vertical = 4
|
size_flags_vertical = 4
|
||||||
|
@ -45,37 +45,37 @@ text = "Lobby Info"
|
||||||
placeholder_text = "Lobby Name"
|
placeholder_text = "Lobby Name"
|
||||||
|
|
||||||
[node name="l1" type="Label" parent="v/head"]
|
[node name="l1" type="Label" parent="v/head"]
|
||||||
margin_left = 360.0
|
margin_left = 620.0
|
||||||
margin_top = 14.0
|
margin_top = 14.0
|
||||||
margin_right = 464.0
|
margin_right = 724.0
|
||||||
margin_bottom = 35.0
|
margin_bottom = 35.0
|
||||||
text = " Max Players:"
|
text = " Max Players:"
|
||||||
|
|
||||||
[node name="max_players" type="LineEdit" parent="v/head"]
|
[node name="max_players" type="LineEdit" parent="v/head"]
|
||||||
margin_left = 468.0
|
margin_left = 728.0
|
||||||
margin_top = 9.0
|
margin_top = 9.0
|
||||||
margin_right = 574.0
|
margin_right = 834.0
|
||||||
margin_bottom = 40.0
|
margin_bottom = 40.0
|
||||||
size_flags_vertical = 4
|
size_flags_vertical = 4
|
||||||
text = "20"
|
text = "20"
|
||||||
|
|
||||||
[node name="lock" type="CheckButton" parent="v/head"]
|
[node name="lock" type="CheckButton" parent="v/head"]
|
||||||
margin_left = 578.0
|
margin_left = 838.0
|
||||||
margin_right = 706.0
|
margin_right = 964.0
|
||||||
margin_bottom = 50.0
|
margin_bottom = 50.0
|
||||||
hint_tooltip = "Prevent players from joining to minimize \"Ready Up\" trolling"
|
hint_tooltip = "Prevent players from joining to minimize \"Ready Up\" trolling"
|
||||||
text = "Locked"
|
text = "Locked"
|
||||||
|
|
||||||
[node name="start" type="Button" parent="v/head"]
|
[node name="start" type="Button" parent="v/head"]
|
||||||
margin_left = 710.0
|
margin_left = 968.0
|
||||||
margin_right = 860.0
|
margin_right = 1118.0
|
||||||
margin_bottom = 50.0
|
margin_bottom = 50.0
|
||||||
rect_min_size = Vector2( 150, 0 )
|
rect_min_size = Vector2( 150, 0 )
|
||||||
text = "Start Game"
|
text = "Start Game"
|
||||||
|
|
||||||
[node name="ready_up" type="CheckButton" parent="v/head"]
|
[node name="ready_up" type="CheckButton" parent="v/head"]
|
||||||
margin_left = 864.0
|
margin_left = 1122.0
|
||||||
margin_right = 984.0
|
margin_right = 1240.0
|
||||||
margin_bottom = 50.0
|
margin_bottom = 50.0
|
||||||
text = "Ready"
|
text = "Ready"
|
||||||
__meta__ = {
|
__meta__ = {
|
||||||
|
@ -84,14 +84,14 @@ __meta__ = {
|
||||||
|
|
||||||
[node name="body" type="HBoxContainer" parent="v"]
|
[node name="body" type="HBoxContainer" parent="v"]
|
||||||
margin_top = 54.0
|
margin_top = 54.0
|
||||||
margin_right = 984.0
|
margin_right = 1240.0
|
||||||
margin_bottom = 560.0
|
margin_bottom = 680.0
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
size_flags_vertical = 3
|
size_flags_vertical = 3
|
||||||
|
|
||||||
[node name="peers" type="VBoxContainer" parent="v/body"]
|
[node name="peers" type="VBoxContainer" parent="v/body"]
|
||||||
margin_right = 194.0
|
margin_right = 194.0
|
||||||
margin_bottom = 506.0
|
margin_bottom = 626.0
|
||||||
|
|
||||||
[node name="label" type="Label" parent="v/body/peers"]
|
[node name="label" type="Label" parent="v/body/peers"]
|
||||||
margin_right = 194.0
|
margin_right = 194.0
|
||||||
|
@ -105,7 +105,7 @@ __meta__ = {
|
||||||
[node name="p" type="PanelContainer" parent="v/body/peers"]
|
[node name="p" type="PanelContainer" parent="v/body/peers"]
|
||||||
margin_top = 25.0
|
margin_top = 25.0
|
||||||
margin_right = 194.0
|
margin_right = 194.0
|
||||||
margin_bottom = 506.0
|
margin_bottom = 626.0
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
size_flags_vertical = 3
|
size_flags_vertical = 3
|
||||||
|
|
||||||
|
@ -113,44 +113,44 @@ size_flags_vertical = 3
|
||||||
margin_left = 7.0
|
margin_left = 7.0
|
||||||
margin_top = 7.0
|
margin_top = 7.0
|
||||||
margin_right = 187.0
|
margin_right = 187.0
|
||||||
margin_bottom = 474.0
|
margin_bottom = 594.0
|
||||||
rect_min_size = Vector2( 180, 0 )
|
rect_min_size = Vector2( 180, 0 )
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
size_flags_vertical = 3
|
size_flags_vertical = 3
|
||||||
|
|
||||||
[node name="v" type="VBoxContainer" parent="v/body"]
|
[node name="v" type="VBoxContainer" parent="v/body"]
|
||||||
margin_left = 198.0
|
margin_left = 198.0
|
||||||
margin_right = 984.0
|
margin_right = 1240.0
|
||||||
margin_bottom = 506.0
|
margin_bottom = 626.0
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
size_flags_vertical = 3
|
size_flags_vertical = 3
|
||||||
|
|
||||||
[node name="chat_head" type="HBoxContainer" parent="v/body/v"]
|
[node name="chat_head" type="HBoxContainer" parent="v/body/v"]
|
||||||
margin_right = 786.0
|
margin_right = 1042.0
|
||||||
margin_bottom = 40.0
|
margin_bottom = 38.0
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
|
|
||||||
[node name="label" type="Label" parent="v/body/v/chat_head"]
|
[node name="label" type="Label" parent="v/body/v/chat_head"]
|
||||||
margin_right = 614.0
|
margin_right = 872.0
|
||||||
margin_bottom = 40.0
|
margin_bottom = 38.0
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
size_flags_vertical = 3
|
size_flags_vertical = 3
|
||||||
text = "Chat"
|
text = "Chat"
|
||||||
valign = 2
|
valign = 2
|
||||||
|
|
||||||
[node name="auto_scroll" type="CheckButton" parent="v/body/v/chat_head"]
|
[node name="auto_scroll" type="CheckButton" parent="v/body/v/chat_head"]
|
||||||
margin_left = 618.0
|
margin_left = 876.0
|
||||||
margin_right = 786.0
|
margin_right = 1042.0
|
||||||
margin_bottom = 40.0
|
margin_bottom = 38.0
|
||||||
size_flags_horizontal = 12
|
size_flags_horizontal = 12
|
||||||
size_flags_vertical = 12
|
size_flags_vertical = 12
|
||||||
pressed = true
|
pressed = true
|
||||||
text = "Auto Scroll"
|
text = "Auto Scroll"
|
||||||
|
|
||||||
[node name="messages" type="TextEdit" parent="v/body/v"]
|
[node name="messages" type="TextEdit" parent="v/body/v"]
|
||||||
margin_top = 44.0
|
margin_top = 42.0
|
||||||
margin_right = 786.0
|
margin_right = 1042.0
|
||||||
margin_bottom = 471.0
|
margin_bottom = 591.0
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
size_flags_vertical = 3
|
size_flags_vertical = 3
|
||||||
text = "# Connecting..."
|
text = "# Connecting..."
|
||||||
|
@ -161,30 +161,32 @@ virtual_keyboard_enabled = false
|
||||||
caret_block_mode = true
|
caret_block_mode = true
|
||||||
|
|
||||||
[node name="h" type="HBoxContainer" parent="v/body/v"]
|
[node name="h" type="HBoxContainer" parent="v/body/v"]
|
||||||
margin_top = 475.0
|
margin_top = 595.0
|
||||||
margin_right = 786.0
|
margin_right = 1042.0
|
||||||
margin_bottom = 506.0
|
margin_bottom = 626.0
|
||||||
__meta__ = {
|
__meta__ = {
|
||||||
"_edit_use_anchors_": false
|
"_edit_use_anchors_": false
|
||||||
}
|
}
|
||||||
|
|
||||||
[node name="chat" type="LineEdit" parent="v/body/v/h"]
|
[node name="chat" type="LineEdit" parent="v/body/v/h"]
|
||||||
margin_right = 674.0
|
margin_right = 930.0
|
||||||
margin_bottom = 31.0
|
margin_bottom = 31.0
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
size_flags_vertical = 3
|
size_flags_vertical = 3
|
||||||
clear_button_enabled = true
|
clear_button_enabled = true
|
||||||
placeholder_text = "Compose a chat message here, press Enter to send"
|
placeholder_text = "Compose a chat message here, press Enter to send"
|
||||||
|
|
||||||
[node name="Button" type="Button" parent="v/body/v/h"]
|
[node name="send" type="Button" parent="v/body/v/h"]
|
||||||
margin_left = 678.0
|
margin_left = 934.0
|
||||||
margin_right = 786.0
|
margin_right = 1042.0
|
||||||
margin_bottom = 31.0
|
margin_bottom = 31.0
|
||||||
size_flags_vertical = 3
|
size_flags_vertical = 3
|
||||||
text = "Send Message"
|
text = "Send Message"
|
||||||
|
|
||||||
[connection signal="pressed" from="v/head/leave_button" to="." method="_on_leave_button_pressed"]
|
[connection signal="pressed" from="v/head/leave_button" to="." method="_on_leave_button_pressed"]
|
||||||
[connection signal="text_changed" from="v/head/lobby_info" to="." method="_on_lobby_info_text_changed"]
|
[connection signal="text_changed" from="v/head/lobby_info" to="." method="_on_lobby_info_text_changed"]
|
||||||
|
[connection signal="text_changed" from="v/head/max_players" to="." method="_on_max_players_text_changed"]
|
||||||
|
[connection signal="pressed" from="v/head/start" to="." method="_on_start_pressed"]
|
||||||
[connection signal="toggled" from="v/head/ready_up" to="." method="_on_ready_up_toggled"]
|
[connection signal="toggled" from="v/head/ready_up" to="." method="_on_ready_up_toggled"]
|
||||||
[connection signal="text_entered" from="v/body/v/h/chat" to="." method="_on_TextEdit_text_entered"]
|
[connection signal="text_entered" from="v/body/v/h/chat" to="." method="_on_TextEdit_text_entered"]
|
||||||
[connection signal="pressed" from="v/body/v/h/Button" to="." method="_on_Button_pressed"]
|
[connection signal="pressed" from="v/body/v/h/send" to="." method="_on_Button_pressed"]
|
||||||
|
|
|
@ -1,29 +1,26 @@
|
||||||
extends Node
|
extends Node
|
||||||
|
|
||||||
const MultiplayerClient = preload("multiplayer_client.gd")
|
const SignalledWebRTCMultiplayer = preload("./signalled_webrtc_multiplayer.gd")
|
||||||
|
|
||||||
onready var client = MultiplayerClient.new()
|
onready var client = SignalledWebRTCMultiplayer.new()
|
||||||
|
|
||||||
# for command line flags
|
onready var onetime_cmd_flags = {
|
||||||
onready var goto_multiplayer = false
|
"--singleplayer": false,
|
||||||
onready var create_lobby = false
|
"--multiplayer": false,
|
||||||
onready var join_first_available_lobby = false
|
"--create-lobby": false,
|
||||||
|
"--join-first-available-lobby": false,
|
||||||
|
}
|
||||||
|
|
||||||
func _ready():
|
func _ready():
|
||||||
# client.signaller.connect("websocket_connected", self, "signaller_disconnected")
|
|
||||||
add_child(client)
|
add_child(client)
|
||||||
client.signaller.connect("websocket_connected", self, "_signaller_connected")
|
for flag in onetime_cmd_flags.keys():
|
||||||
|
if flag in OS.get_cmdline_args():
|
||||||
|
onetime_cmd_flags[flag] = true
|
||||||
|
|
||||||
for arg in OS.get_cmdline_args():
|
func check_onetime_flag(flag):
|
||||||
match arg:
|
var result = onetime_cmd_flags["--%s" % flag]
|
||||||
"--multiplayer": goto_multiplayer = true
|
onetime_cmd_flags["--%s" % flag] = false
|
||||||
"--create-lobby": create_lobby = true
|
return result
|
||||||
"--join-first-available-lobby": join_first_available_lobby = true
|
|
||||||
var a: print("Unknown command line arg: %s" % a)
|
|
||||||
|
|
||||||
if goto_multiplayer:
|
|
||||||
goto_multiplayer = false
|
|
||||||
lobby_browser()
|
|
||||||
|
|
||||||
func goto_scene(scene_resource_name):
|
func goto_scene(scene_resource_name):
|
||||||
var _result = get_tree().change_scene("res://screens/%s.tscn" % scene_resource_name)
|
var _result = get_tree().change_scene("res://screens/%s.tscn" % scene_resource_name)
|
||||||
|
@ -33,16 +30,14 @@ func main_menu():
|
||||||
goto_scene("main_menu")
|
goto_scene("main_menu")
|
||||||
|
|
||||||
func start_singleplayer_game():
|
func start_singleplayer_game():
|
||||||
client.fake_singleplayer()
|
client.singleplayer()
|
||||||
client.close()
|
goto_game()
|
||||||
|
|
||||||
|
func goto_game():
|
||||||
goto_scene("game")
|
goto_scene("game")
|
||||||
|
|
||||||
func _signaller_connected():
|
|
||||||
goto_scene("lobby_browser")
|
|
||||||
|
|
||||||
func lobby_browser():
|
func lobby_browser():
|
||||||
client.close()
|
goto_scene("lobby_browser")
|
||||||
Global.client.connect_to_signaller()
|
|
||||||
|
|
||||||
func lobby():
|
func lobby():
|
||||||
goto_scene("multiplayer_lobby")
|
goto_scene("multiplayer_lobby")
|
||||||
|
@ -50,3 +45,9 @@ func lobby():
|
||||||
func quit():
|
func quit():
|
||||||
client.close()
|
client.close()
|
||||||
get_tree().quit()
|
get_tree().quit()
|
||||||
|
|
||||||
|
func key_shortcut(code):
|
||||||
|
var sc = ShortCut.new()
|
||||||
|
sc.shortcut = InputEventKey.new()
|
||||||
|
sc.shortcut.scancode = code
|
||||||
|
return sc
|
||||||
|
|
|
@ -1,117 +0,0 @@
|
||||||
extends Node
|
|
||||||
|
|
||||||
"""
|
|
||||||
This module sets up WebRTC peer connections.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# var multiplayer_url = "wss://webrtc-signaller.deno.dev:443"
|
|
||||||
var multiplayer_url = "ws://localhost:8888"
|
|
||||||
# var multiplayer_url = "ws://echo.websocket.org"
|
|
||||||
var webrtc_ice_servers = [
|
|
||||||
{ "urls": ["stun:stun.l.google.com:19302"] },
|
|
||||||
# { "urls": ["stun:localhost:3478"] }
|
|
||||||
]
|
|
||||||
|
|
||||||
const SignallerClient = preload("signaller_client.gd")
|
|
||||||
|
|
||||||
onready var mp = WebRTCMultiplayer.new()
|
|
||||||
onready var signaller = SignallerClient.new()
|
|
||||||
|
|
||||||
func _ready():
|
|
||||||
signaller.connect("offer_received", self, "offer_received")
|
|
||||||
signaller.connect("answer_received", self, "answer_received")
|
|
||||||
signaller.connect("candidate_received", self, "candidate_received")
|
|
||||||
# signaller.connect("websocket_connected", self, "connected")
|
|
||||||
# signaller.connect("websocket_disconnected", self, "disconnected")
|
|
||||||
|
|
||||||
signaller.connect("peer_id_set", self, "_peer_id_set")
|
|
||||||
signaller.connect("peer_joined", self, "_peer_joined")
|
|
||||||
# mp.connect("peer_disconnected", self, "peer_disconnected")
|
|
||||||
# connect("lobby_sealed", self, "lobby_sealed")
|
|
||||||
add_child(signaller)
|
|
||||||
|
|
||||||
func fake_singleplayer():
|
|
||||||
print("Faking network peer for singleplayer...")
|
|
||||||
mp.initialize(1, true)
|
|
||||||
get_tree().network_peer = mp
|
|
||||||
var peer: WebRTCPeerConnection = WebRTCPeerConnection.new()
|
|
||||||
peer.initialize()
|
|
||||||
mp.add_peer(peer, 1)
|
|
||||||
|
|
||||||
func close():
|
|
||||||
mp.close()
|
|
||||||
signaller.close()
|
|
||||||
|
|
||||||
func connect_to_signaller():
|
|
||||||
close()
|
|
||||||
signaller.connect_to_websocket_signaller(multiplayer_url)
|
|
||||||
|
|
||||||
func _create_peer(peer_id):
|
|
||||||
# if peer_id == signaller.peer_id: return
|
|
||||||
var peer: WebRTCPeerConnection = WebRTCPeerConnection.new()
|
|
||||||
print("New Net Peer with peer_id %d" % [peer_id])
|
|
||||||
var _peer_init_results = peer.initialize({"iceServers": webrtc_ice_servers})
|
|
||||||
var _result = peer.connect("session_description_created", self, "_offer_created", [peer_id])
|
|
||||||
_result = peer.connect("ice_candidate_created", self, "_new_ice_candidate", [peer_id])
|
|
||||||
mp.add_peer(peer, peer_id)
|
|
||||||
var uid = mp.get_unique_id()
|
|
||||||
if peer_id > uid:
|
|
||||||
var offer_result = peer.create_offer()
|
|
||||||
print("Create Offer Result: %s" % offer_result)
|
|
||||||
else:
|
|
||||||
print("Did not create offer: %d <= %d" % [peer_id, uid])
|
|
||||||
return peer
|
|
||||||
|
|
||||||
func _new_ice_candidate(mid_name, index_name, sdp_name, peer_id):
|
|
||||||
print("New ICE Candidate - MID: %s, Index: %s, SDP: %s, PeerID: %s" % [mid_name, index_name, sdp_name, peer_id])
|
|
||||||
signaller.send_candidate(peer_id, mid_name, index_name, sdp_name)
|
|
||||||
|
|
||||||
func _offer_created(type, data, peer_id):
|
|
||||||
print("Offer created - Type: %s, PeerID: %s, Data: %s" % [type, peer_id, JSON.print(data)])
|
|
||||||
if not mp.has_peer(peer_id):
|
|
||||||
return
|
|
||||||
mp.get_peer(peer_id).connection.set_local_description(type, data)
|
|
||||||
if type == "offer":
|
|
||||||
signaller.send_offer(peer_id, data)
|
|
||||||
else:
|
|
||||||
signaller.send_answer(peer_id, data)
|
|
||||||
|
|
||||||
func _peer_id_set(peer_id):
|
|
||||||
print("Peer ID received %s - setting up to peer..." % [peer_id])
|
|
||||||
mp.initialize(peer_id, true)
|
|
||||||
get_tree().network_peer = mp
|
|
||||||
|
|
||||||
func _peer_joined(peers):
|
|
||||||
for i in range(len(peers)):
|
|
||||||
var peer = peers[i]
|
|
||||||
if peer.has("peerId"):
|
|
||||||
var peer_id = peer["peerId"]
|
|
||||||
if not mp.has_peer(peer_id):
|
|
||||||
_create_peer(peer_id)
|
|
||||||
|
|
||||||
func peer_disconnected(id):
|
|
||||||
print("Peer %s disconnected" % id)
|
|
||||||
if mp.has_peer(id):
|
|
||||||
mp.remove_peer(id)
|
|
||||||
|
|
||||||
func offer_received(data):
|
|
||||||
var id = data["peerId"]
|
|
||||||
if mp.has_peer(id):
|
|
||||||
print("Setting offer remote description: %s" % JSON.print(data))
|
|
||||||
mp.get_peer(id).connection.set_remote_description("offer", data["offer"])
|
|
||||||
else:
|
|
||||||
print("Received an offer for a peer with ID %s that hasn't been added" % id)
|
|
||||||
|
|
||||||
func answer_received(data):
|
|
||||||
var id = data["peerId"]
|
|
||||||
if mp.has_peer(id):
|
|
||||||
print("Setting answer remote description: %s" % JSON.print(data))
|
|
||||||
mp.get_peer(id).connection.set_remote_description("answer", data["answer"])
|
|
||||||
|
|
||||||
func candidate_received(data):
|
|
||||||
var id = data["peerId"]
|
|
||||||
if mp.has_peer(id):
|
|
||||||
print("Adding ice candidate: %s" % JSON.print(data))
|
|
||||||
mp.get_peer(id).connection.add_ice_candidate(data["mid"], data["index"], data["sdp"])
|
|
||||||
else:
|
|
||||||
print("Received candidate for non-existant peer %s" % id)
|
|
345
scripts/global/signalled_webrtc_multiplayer.gd
Normal file
345
scripts/global/signalled_webrtc_multiplayer.gd
Normal file
|
@ -0,0 +1,345 @@
|
||||||
|
extends Node
|
||||||
|
|
||||||
|
const DISPLAY_NAME_FILE = "user://display_name.txt"
|
||||||
|
const DEFAULT_DISPLAY_NAME = "UnnamedPlayer"
|
||||||
|
const DEFAULT_SIGNALLER_URL = "ws://localhost:8888"
|
||||||
|
# const DEFAULT_SIGNALLER_URL = "wss://webrtc-signaller.deno.dev:443"
|
||||||
|
|
||||||
|
const DEFAULT_ICE_SERVERS = [
|
||||||
|
# first element in this array is for STUN
|
||||||
|
{ "urls": ["stun:localhost:3478", "stun:stun.l.google.com:19302"] },
|
||||||
|
# { "urls": ["stun:stun.l.google.com:19302"] }, # just google
|
||||||
|
# { "urls": ["stun:localhost:3478"] }, # just localhost
|
||||||
|
# {} # TURN servers
|
||||||
|
]
|
||||||
|
|
||||||
|
signal lobby_data(lobbies)
|
||||||
|
signal lobby_begin_join(uuid)
|
||||||
|
signal lobby_joined(lobby_and_peer)
|
||||||
|
signal lobby_left(uuid)
|
||||||
|
signal lobby_delete(uuid)
|
||||||
|
|
||||||
|
signal peer_data(peers)
|
||||||
|
signal peer_created(peer)
|
||||||
|
signal peer_left(id)
|
||||||
|
signal peer_init(id)
|
||||||
|
|
||||||
|
signal candidate_received(cand)
|
||||||
|
signal offer_received(data)
|
||||||
|
signal answer_received(data)
|
||||||
|
|
||||||
|
# webrtc
|
||||||
|
signal webrtc_peer_connected(id)
|
||||||
|
signal webrtc_peer_disconnected(id)
|
||||||
|
signal webrtc_connection_succeeded()
|
||||||
|
|
||||||
|
# websocket
|
||||||
|
signal signaller_connected
|
||||||
|
signal signaller_connection_failure
|
||||||
|
signal signaller_disconnected
|
||||||
|
|
||||||
|
onready var webrtc = null
|
||||||
|
onready var websocket = WebSocketClient.new()
|
||||||
|
|
||||||
|
onready var display_name = DEFAULT_DISPLAY_NAME setget set_display_name
|
||||||
|
onready var current_lobby = null
|
||||||
|
onready var lobbies = {}
|
||||||
|
onready var peers = {}
|
||||||
|
|
||||||
|
onready var signaller_url = DEFAULT_SIGNALLER_URL
|
||||||
|
|
||||||
|
func _init():
|
||||||
|
pass
|
||||||
|
|
||||||
|
func _ready():
|
||||||
|
display_name = _load_display_name()
|
||||||
|
|
||||||
|
websocket.connect("connection_established", self, "_signaller_connected")
|
||||||
|
websocket.connect("data_received", self, "_signaller_data")
|
||||||
|
websocket.connect("server_close_request", self, "_signaller_close_request")
|
||||||
|
websocket.connect("connection_closed", self, "_signaller_closed")
|
||||||
|
websocket.connect("connection_error", self, "_signaller_error", [false])
|
||||||
|
websocket.connect("connection_failed", self, "_signaller_error", [false])
|
||||||
|
|
||||||
|
func is_signaller_connected():
|
||||||
|
return websocket.get_connection_status() in [
|
||||||
|
websocket.CONNECTION_CONNECTED,
|
||||||
|
websocket.CONNECTION_CONNECTED,
|
||||||
|
]
|
||||||
|
|
||||||
|
func is_in_lobby(): return current_lobby != null
|
||||||
|
|
||||||
|
func connect_to_signaller(url = signaller_url):
|
||||||
|
if url == signaller_url and is_signaller_connected(): return
|
||||||
|
signaller_url = url
|
||||||
|
close()
|
||||||
|
print("Attempting to connect to WebSocket signalling server at %s" % signaller_url)
|
||||||
|
var result = websocket.connect_to_url(signaller_url)
|
||||||
|
if result != OK:
|
||||||
|
print("Failed to connect to WebSocket signalling server at %s: %s" % [signaller_url, result])
|
||||||
|
|
||||||
|
func singleplayer():
|
||||||
|
close()
|
||||||
|
webrtc = WebRTCMultiplayer.new()
|
||||||
|
webrtc.initialize(1, false)
|
||||||
|
get_tree().network_peer = webrtc
|
||||||
|
peers[1] = {
|
||||||
|
connected = false,
|
||||||
|
ready = false,
|
||||||
|
name = display_name,
|
||||||
|
}
|
||||||
|
|
||||||
|
func close():
|
||||||
|
if webrtc != null: webrtc.close()
|
||||||
|
websocket.disconnect_from_host()
|
||||||
|
get_tree().network_peer = null
|
||||||
|
|
||||||
|
func set_display_name(new_display_name: String):
|
||||||
|
display_name = new_display_name
|
||||||
|
_send("update_display_name:%s" % display_name)
|
||||||
|
|
||||||
|
func request_lobby_list():
|
||||||
|
return _send("request_lobby_list")
|
||||||
|
|
||||||
|
func join_lobby(id: String):
|
||||||
|
emit_signal("lobby_begin_join", id)
|
||||||
|
call_deferred("_send", "lobby_join:%s" % id)
|
||||||
|
|
||||||
|
func leave_lobby():
|
||||||
|
return _send("lobby_leave")
|
||||||
|
|
||||||
|
func get_lobby_name():
|
||||||
|
return current_lobby.name if current_lobby.has("name") else null
|
||||||
|
|
||||||
|
func create_lobby():
|
||||||
|
_send("lobby_create")
|
||||||
|
|
||||||
|
func is_host(): return is_signaller_connected() and is_in_lobby() and get_tree().get_network_unique_id() == 1
|
||||||
|
|
||||||
|
func set_lobby_name(s: String):
|
||||||
|
if is_host(): _send_json({"name": s}, "update_lobby")
|
||||||
|
|
||||||
|
func lock_lobby():
|
||||||
|
if is_host(): _send_json({"locked": true}, "update_lobby")
|
||||||
|
|
||||||
|
func set_lobby_max_players(n: int):
|
||||||
|
if is_host(): _send_json({"maxPlayers": n}, "update_lobby")
|
||||||
|
|
||||||
|
func request_peer_list(): _send("request_peer_list")
|
||||||
|
|
||||||
|
func send_candidate(id, mid, index, sdp) -> int:
|
||||||
|
return _send_json({"id": id, "mid": mid, "index": index, "sdp": sdp}, "candidate")
|
||||||
|
|
||||||
|
func send_offer(id, offer) -> int:
|
||||||
|
return _send_json({"id": id, "offer": offer }, "offer")
|
||||||
|
|
||||||
|
func send_answer(id, answer) -> int:
|
||||||
|
return _send_json({"id": id, "answer": answer }, "answer")
|
||||||
|
|
||||||
|
func _webrtc_peer_connected(id):
|
||||||
|
peers[id].connected = true
|
||||||
|
emit_signal("webrtc_peer_connected", id)
|
||||||
|
|
||||||
|
func _webrtc_peer_disconnected(id):
|
||||||
|
peers[id].connected = false
|
||||||
|
emit_signal("webrtc_peer_disconnected", id)
|
||||||
|
|
||||||
|
func _webrtc_connection_succeeded():
|
||||||
|
emit_signal("webrtc_connection_succeeded")
|
||||||
|
|
||||||
|
func _signaller_error(err):
|
||||||
|
print("WebSocket error: %s" % err)
|
||||||
|
emit_signal("signaller_connection_failure")
|
||||||
|
_signaller_closed(err)
|
||||||
|
|
||||||
|
func _signaller_closed(code):
|
||||||
|
print("WebSocket closed: %s: " % code)
|
||||||
|
emit_signal("signaller_disconnected")
|
||||||
|
|
||||||
|
func _signaller_close_request(code: int, reason: String):
|
||||||
|
print("Received WebSocket close request from signalling server - Code: %s, Reason: %s" % [code, reason])
|
||||||
|
# TODO: does this fire _closed?
|
||||||
|
|
||||||
|
func _signaller_connected(protocol = ""):
|
||||||
|
print("Signaller connected via WebSocket using protocol %s" % protocol)
|
||||||
|
websocket.get_peer(1).set_write_mode(WebSocketPeer.WRITE_MODE_TEXT)
|
||||||
|
emit_signal("signaller_connected")
|
||||||
|
_send_json({name = display_name}, "init")
|
||||||
|
|
||||||
|
func _process(_delta: float):
|
||||||
|
if webrtc:
|
||||||
|
webrtc.poll()
|
||||||
|
# while webrtc.get_available_packet_count() > 0:
|
||||||
|
# print("WebRTC Packet: %s" % webrtc.get_packet().get_string_from_utf8())
|
||||||
|
var status: int = websocket.get_connection_status()
|
||||||
|
if status in [WebSocketClient.CONNECTION_CONNECTED, WebSocketClient.CONNECTION_CONNECTING]:
|
||||||
|
websocket.poll()
|
||||||
|
|
||||||
|
func _input(event):
|
||||||
|
if event is InputEventKey:
|
||||||
|
if event.pressed and event.scancode == KEY_D and event.alt:
|
||||||
|
print("## DEBUG NET INFO")
|
||||||
|
for p in get_tree().get_network_connected_peers():
|
||||||
|
var d = webrtc.get_peer(p)
|
||||||
|
print("get_peer(%s): %s" % [p, d])
|
||||||
|
for c in d.channels:
|
||||||
|
print("Channel: %s" % c)
|
||||||
|
if c:
|
||||||
|
print("Label: %s, Protocol: %s, Negotiated: %s" % [c.get_label(), c.get_protocol(), "true" if c.is_negotiated() else "false"])
|
||||||
|
else:
|
||||||
|
print("Channel is null?")
|
||||||
|
|
||||||
|
func _signaller_data():
|
||||||
|
var msg: String = websocket.get_peer(1).get_packet().get_string_from_utf8()
|
||||||
|
if msg.begins_with("json:"):
|
||||||
|
var data = JSON.parse(msg.substr(5))
|
||||||
|
if data.error == OK:
|
||||||
|
_process_signaller_message(data.result)
|
||||||
|
var _result = websocket.get_peer(1).put_packet("".to_utf8())
|
||||||
|
else:
|
||||||
|
var d = msg.split(":", false, 2)
|
||||||
|
_process_signaller_message({type = d[0], data = d[1]})
|
||||||
|
|
||||||
|
func _init_webrtc_peer(data):
|
||||||
|
webrtc = WebRTCMultiplayer.new()
|
||||||
|
webrtc.connect("connection_succeeded", self, "_webrtc_connection_succeeded")
|
||||||
|
webrtc.connect("peer_connected", self, "_webrtc_peer_connected")
|
||||||
|
webrtc.connect("peer_disconnected", self, "_webrtc_peer_disconnected")
|
||||||
|
webrtc.initialize(int(data), true)
|
||||||
|
get_tree().network_peer = webrtc
|
||||||
|
|
||||||
|
func _deinit_webrtc_peer():
|
||||||
|
webrtc.close()
|
||||||
|
get_tree().network_peer = null
|
||||||
|
webrtc = null
|
||||||
|
|
||||||
|
func _process_signaller_message(data: Dictionary):
|
||||||
|
match data["type"]:
|
||||||
|
# "init":
|
||||||
|
"init_peer": _init_webrtc_peer(data.data)
|
||||||
|
|
||||||
|
"lobby_data": emit_signal("lobby_data", data.data)
|
||||||
|
"lobby_delete": emit_signal("lobby_delete", data.uuid)
|
||||||
|
"lobby_joined": _lobby_joined(data)
|
||||||
|
"lobby_left": _lobby_left(data.uuid)
|
||||||
|
|
||||||
|
"peer_data": _signaller_peer_data([data] if data.has("id") else data.data)
|
||||||
|
"peer_left": _signaller_peer_left(int(data.id))
|
||||||
|
|
||||||
|
"candidate": _webrtc_candidate_received(data)
|
||||||
|
"offer": _webrtc_offer_received(data)
|
||||||
|
"answer": _webrtc_answer_received(data)
|
||||||
|
"ping": _send("pong")
|
||||||
|
_: print("Unhandled Message - Data: %s" % JSON.print(data))
|
||||||
|
|
||||||
|
func _lobby_joined(lobby_data):
|
||||||
|
current_lobby = lobby_data
|
||||||
|
peers[int(lobby_data.id)] = {
|
||||||
|
connected = true,
|
||||||
|
ready = int(lobby_data.id) == 1,
|
||||||
|
name = display_name,
|
||||||
|
id = int(lobby_data.id),
|
||||||
|
}
|
||||||
|
print("Lobby Joined: %s" % lobby_data)
|
||||||
|
emit_signal("lobby_joined", lobby_data)
|
||||||
|
|
||||||
|
func _lobby_left(uuid):
|
||||||
|
current_lobby = null
|
||||||
|
peers.erase(get_tree().get_network_unique_id())
|
||||||
|
print("Lobby Left: %s" % uuid)
|
||||||
|
emit_signal("lobby_left", uuid)
|
||||||
|
_deinit_webrtc_peer()
|
||||||
|
|
||||||
|
func _create_peer(data: Dictionary):
|
||||||
|
var id = data.id
|
||||||
|
if id == webrtc.get_unique_id(): return
|
||||||
|
var peer: WebRTCPeerConnection = WebRTCPeerConnection.new()
|
||||||
|
print("Creating WebRTC Peer: %s" % [data])
|
||||||
|
peer.connect("session_description_created", self, "_webrtc_offer_created", [id])
|
||||||
|
peer.connect("ice_candidate_created", self, "_new_ice_candidate", [id])
|
||||||
|
peer.initialize({"iceServers": DEFAULT_ICE_SERVERS})
|
||||||
|
webrtc.add_peer(peer, int(id))
|
||||||
|
# this guarantees only one peer sends the offer and that offers are never
|
||||||
|
# sent to ourselves?
|
||||||
|
if int(id) > webrtc.get_unique_id(): peer.create_offer()
|
||||||
|
peers[int(id)] = {
|
||||||
|
connected = false,
|
||||||
|
ready = data.ready if data.has("ready") else false,
|
||||||
|
name = data.name if data.has(name) else DEFAULT_DISPLAY_NAME,
|
||||||
|
}
|
||||||
|
emit_signal("peer_created", data)
|
||||||
|
|
||||||
|
func _delete_peer(id):
|
||||||
|
if webrtc.has_peer(id): webrtc.remove_peer(id)
|
||||||
|
if peers.has(id): peers.erase(id)
|
||||||
|
emit_signal("peer_left", id)
|
||||||
|
|
||||||
|
func _webrtc_offer_created(type, data, id):
|
||||||
|
print("WebRTC %s created for peer %s" % [type, id])
|
||||||
|
if not webrtc.has_peer(int(id)): return
|
||||||
|
print("WebRTC local description set for peer %s" % id)
|
||||||
|
webrtc.get_peer(id).connection.set_local_description(type, data)
|
||||||
|
print("--> Local Description: %s" % JSON.print(data))
|
||||||
|
if type == "offer": send_offer(id, data)
|
||||||
|
else: send_answer(id, data)
|
||||||
|
|
||||||
|
func _new_ice_candidate(mid, index, sdp, id):
|
||||||
|
print("New ICE candidate for peer %s" % id)
|
||||||
|
send_candidate(id, mid, index, sdp)
|
||||||
|
|
||||||
|
func _signaller_peer_left(id):
|
||||||
|
_delete_peer(id)
|
||||||
|
|
||||||
|
func _signaller_peer_data(peers):
|
||||||
|
for peer in peers:
|
||||||
|
if peer.has("id") and not webrtc.has_peer(int(peer.id)):
|
||||||
|
_create_peer(peer)
|
||||||
|
emit_signal("peer_data", peers)
|
||||||
|
|
||||||
|
func _webrtc_offer_received(data):
|
||||||
|
if webrtc.has_peer(int(data.id)):
|
||||||
|
print("Setting offer remote description for peer %s" % data.id)
|
||||||
|
print("--> Offer: %s" % JSON.print(data.offer))
|
||||||
|
webrtc.get_peer(data.id).connection.set_remote_description("offer", data.offer)
|
||||||
|
else:
|
||||||
|
print("Received an offer for a peer with ID %s that hasn't been added" % data.id)
|
||||||
|
|
||||||
|
func _webrtc_answer_received(data):
|
||||||
|
if webrtc.has_peer(data.id):
|
||||||
|
print("Setting answer remote description for peer %s" % data.id)
|
||||||
|
print("--> Answer: %s" % JSON.print(data.answer))
|
||||||
|
webrtc.get_peer(data.id).connection.set_remote_description("answer", data.answer)
|
||||||
|
|
||||||
|
func _webrtc_candidate_received(data):
|
||||||
|
if webrtc.has_peer(data.id):
|
||||||
|
print("Adding ice candidate for peer %s" % data.id)
|
||||||
|
print("--> Candidate: %s" % JSON.print(data))
|
||||||
|
webrtc.get_peer(data.id).connection.add_ice_candidate(data.mid, data.index, data.sdp)
|
||||||
|
else:
|
||||||
|
print("Received candidate for non-existant peer %s" % data.id)
|
||||||
|
|
||||||
|
func _load_display_name():
|
||||||
|
var _display_name = DEFAULT_DISPLAY_NAME
|
||||||
|
var display_name_file = File.new()
|
||||||
|
if display_name_file.open(DISPLAY_NAME_FILE, File.READ) == OK:
|
||||||
|
_display_name = display_name_file.get_as_text()
|
||||||
|
else:
|
||||||
|
print("Failed to open %s for reading display_name, creating default" % DISPLAY_NAME_FILE)
|
||||||
|
_store_display_name()
|
||||||
|
display_name_file.close()
|
||||||
|
return _display_name
|
||||||
|
|
||||||
|
func _store_display_name():
|
||||||
|
var display_name_file = File.new()
|
||||||
|
if display_name_file.open(DISPLAY_NAME_FILE, File.WRITE) == OK:
|
||||||
|
display_name_file.store_string(display_name)
|
||||||
|
else:
|
||||||
|
print("Failed to open %s for writing display_name" % DISPLAY_NAME_FILE)
|
||||||
|
display_name_file.close()
|
||||||
|
|
||||||
|
func _send(s: String):
|
||||||
|
return websocket.get_peer(1).put_packet(s.to_utf8())
|
||||||
|
|
||||||
|
func _send_json(data: Dictionary, type=null):
|
||||||
|
if type != null: data["type"] = type
|
||||||
|
_send("json:%s" % JSON.print(data))
|
|
@ -1,204 +0,0 @@
|
||||||
extends Node
|
|
||||||
|
|
||||||
"""
|
|
||||||
This module is responsible for making a WebSocket connection to the signaller
|
|
||||||
in order to enable establishish WebRTC P2P connections. Another module is
|
|
||||||
expected to fully setup the peer connections. (./multiplayer_client.gd)
|
|
||||||
"""
|
|
||||||
|
|
||||||
signal lobby_new(lobbiesList)
|
|
||||||
signal lobby_delete(id)
|
|
||||||
signal lobby_joined(id, peer_id)
|
|
||||||
signal lobby_left(id)
|
|
||||||
|
|
||||||
signal peer_joined(peers)
|
|
||||||
signal peer_left(id)
|
|
||||||
signal candidate_received(data)
|
|
||||||
signal offer_received(data)
|
|
||||||
signal answer_received(data)
|
|
||||||
signal client_id_set(client_id)
|
|
||||||
signal peer_id_set(peer_id)
|
|
||||||
signal websocket_connected
|
|
||||||
signal websocket_disconnected
|
|
||||||
|
|
||||||
onready var ws: WebSocketClient = WebSocketClient.new()
|
|
||||||
|
|
||||||
onready var client_id = null
|
|
||||||
onready var lobby_id = null
|
|
||||||
onready var peer_id = null
|
|
||||||
onready var display_name = "UnnamedPlayer"
|
|
||||||
onready var lobby_name = null
|
|
||||||
|
|
||||||
func is_host(): return peer_id == 1
|
|
||||||
|
|
||||||
const DISPLAY_NAME_FILE = "user://display_name.txt"
|
|
||||||
|
|
||||||
func _ready():
|
|
||||||
var dnf = File.new()
|
|
||||||
if dnf.open(DISPLAY_NAME_FILE, File.READ) == OK:
|
|
||||||
display_name = dnf.get_as_text()
|
|
||||||
dnf.close()
|
|
||||||
var _result = ws.connect("data_received", self, "_parse_msg")
|
|
||||||
_result = ws.connect("connection_established", self, "_connected")
|
|
||||||
_result = ws.connect("connection_closed", self, "_closed")
|
|
||||||
_result = ws.connect("connection_error", self, "_closed", [false])
|
|
||||||
_result = ws.connect("connection_failed", self, "_closed", [false])
|
|
||||||
_result = ws.connect("connection_succeeded", self, "_succ")
|
|
||||||
_result = ws.connect("server_close_request", self, "_close_request")
|
|
||||||
|
|
||||||
func _succ():
|
|
||||||
print("WebSocket Connection Succeeded")
|
|
||||||
|
|
||||||
func close():
|
|
||||||
ws.disconnect_from_host()
|
|
||||||
|
|
||||||
func connect_to_websocket_signaller(url: String):
|
|
||||||
print("WebSocket: %s" % ws)
|
|
||||||
close()
|
|
||||||
print("Attempting to connect to WebSocket signalling server at %s" % url)
|
|
||||||
var result = ws.connect_to_url(url)
|
|
||||||
if result != OK:
|
|
||||||
print("FAILED TO CONNECT TO %s" % url)
|
|
||||||
print("WebSocket Connect Result: %s" % result)
|
|
||||||
|
|
||||||
func _closed(unknown):
|
|
||||||
print("WebSocket closed: %s: " % unknown)
|
|
||||||
emit_signal("websocket_disconnected")
|
|
||||||
|
|
||||||
func _close_request(code: int, reason: String):
|
|
||||||
print("Received WebSocket close request from signalling server - Code: %s, Reason: %s" % [code, reason])
|
|
||||||
|
|
||||||
func _connected(protocol = ""):
|
|
||||||
print("WebSocket signaller connected via protocol %s" % protocol)
|
|
||||||
ws.get_peer(1).set_write_mode(WebSocketPeer.WRITE_MODE_TEXT)
|
|
||||||
emit_signal("websocket_connected")
|
|
||||||
_send("json:%s" % JSON.print({
|
|
||||||
"type": "init",
|
|
||||||
"data": {
|
|
||||||
"name": display_name
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
|
|
||||||
func _process(_delta: float):
|
|
||||||
var status: int = ws.get_connection_status()
|
|
||||||
if status == WebSocketClient.CONNECTION_CONNECTED or status == WebSocketClient.CONNECTION_CONNECTING:
|
|
||||||
ws.poll()
|
|
||||||
func join_lobby(id: String):
|
|
||||||
return _send("lobby_join:%s" % id)
|
|
||||||
|
|
||||||
func create_lobby():
|
|
||||||
return _send("lobby_create")
|
|
||||||
|
|
||||||
func request_lobby_list():
|
|
||||||
return _send("request_lobby_list")
|
|
||||||
|
|
||||||
func request_peer_list():
|
|
||||||
return _send("request_peer_list")
|
|
||||||
|
|
||||||
func set_display_name(new_display_name: String):
|
|
||||||
display_name = new_display_name
|
|
||||||
_send("update_display_name:%s" % display_name)
|
|
||||||
var dnf = File.new()
|
|
||||||
if dnf.open(DISPLAY_NAME_FILE, File.WRITE) == OK:
|
|
||||||
dnf.store_string(display_name)
|
|
||||||
dnf.close()
|
|
||||||
|
|
||||||
func _send(s: String):
|
|
||||||
return ws.get_peer(1).put_packet(s.to_utf8())
|
|
||||||
|
|
||||||
func set_lobby_name(s: String):
|
|
||||||
if is_host():
|
|
||||||
_send("json:%s" % JSON.print({"type": "update_lobby", "data": {"name": s}}))
|
|
||||||
|
|
||||||
func lock_lobby():
|
|
||||||
if is_host():
|
|
||||||
_send("json:%s" % JSON.print({"type": "update_lobby", "data": {"locked": true}}))
|
|
||||||
|
|
||||||
func set_lobby_max_players(n: int):
|
|
||||||
if is_host():
|
|
||||||
_send("json:%s" % JSON.print({"type": "update_lobby", "data": {"maxPlayers": n}}))
|
|
||||||
|
|
||||||
func set_ready(n: bool):
|
|
||||||
if !is_host():
|
|
||||||
_send("set_ready:%s" % JSON.print(n))
|
|
||||||
|
|
||||||
func _parse_msg():
|
|
||||||
var msg: String = ws.get_peer(1).get_packet().get_string_from_utf8()
|
|
||||||
if msg.begins_with("json:"):
|
|
||||||
var data = JSON.parse(msg.substr(5))
|
|
||||||
if data.error == OK:
|
|
||||||
handle_message(data.result)
|
|
||||||
var _result = ws.get_peer(1).put_packet("".to_utf8())
|
|
||||||
else:
|
|
||||||
print("Unhandled message: %s" % msg)
|
|
||||||
|
|
||||||
func handle_message(data: Dictionary):
|
|
||||||
match data["type"]:
|
|
||||||
"your_id":
|
|
||||||
client_id = data["data"]["id"]
|
|
||||||
emit_signal("client_id_set", client_id)
|
|
||||||
"your_peer_id":
|
|
||||||
peer_id = int(data["data"])
|
|
||||||
emit_signal("peer_id_set", peer_id)
|
|
||||||
|
|
||||||
"lobby_list": emit_signal("lobby_new", data["data"])
|
|
||||||
"lobby_new": emit_signal("lobby_new", [data])
|
|
||||||
"lobby_delete": emit_signal("lobby_delete", data["id"])
|
|
||||||
|
|
||||||
"lobby_joined":
|
|
||||||
lobby_id = data["id"]
|
|
||||||
lobby_name = data["name"]
|
|
||||||
peer_id = data["peerId"]
|
|
||||||
emit_signal("lobby_joined", data["id"], data["peerId"])
|
|
||||||
"lobby_left":
|
|
||||||
lobby_id = null
|
|
||||||
lobby_name = null
|
|
||||||
emit_signal("lobby_left", data["id"])
|
|
||||||
|
|
||||||
"peer_joined":
|
|
||||||
print(typeof(data), data)
|
|
||||||
if data.get("id") != null: emit_signal("peer_joined", [data])
|
|
||||||
else: emit_signal("peer_joined", data["data"])
|
|
||||||
"peer_left":
|
|
||||||
print("Peer Left: %s" % data)
|
|
||||||
emit_signal("peer_left", data["data"])
|
|
||||||
"ready_change":
|
|
||||||
print("Peer Ready Change")
|
|
||||||
emit_signal("peer_joined", [data])
|
|
||||||
|
|
||||||
"candidate":
|
|
||||||
print("Candidate received - Data: %s" % JSON.print(data["data"]))
|
|
||||||
emit_signal("candidate_received", data["data"])
|
|
||||||
"offer":
|
|
||||||
print("Offer received - Data: %s" % JSON.print(data["data"]))
|
|
||||||
emit_signal("offer_received", data["data"])
|
|
||||||
"answer":
|
|
||||||
print("Answer received - Data: %s" % JSON.print(data["data"]))
|
|
||||||
emit_signal("answer_received", data["data"])
|
|
||||||
"ping":
|
|
||||||
# print("Signaller Ping")
|
|
||||||
_send("pong")
|
|
||||||
_: print("Unhandled Message - Data: %s" % JSON.print(data))
|
|
||||||
|
|
||||||
func send_candidate(peerId, mid, index, sdp) -> int:
|
|
||||||
return _send("json:%s" % JSON.print({
|
|
||||||
"type": "candidate",
|
|
||||||
"data": {
|
|
||||||
"peerId": peerId,
|
|
||||||
"mid": mid,
|
|
||||||
"index": index,
|
|
||||||
"sdp": sdp
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
|
|
||||||
func send_offer(peerId, offer) -> int:
|
|
||||||
return _send("json:%s" % JSON.print({ "type": "offer", "data": {"peerId": peerId, "offer": offer }}))
|
|
||||||
|
|
||||||
func send_answer(peerId, answer) -> int:
|
|
||||||
return _send("json:%s" % JSON.print({ "type": "answer", "data": {"peerId": peerId, "answer": answer }}))
|
|
||||||
|
|
||||||
"""
|
|
||||||
elif type.begins_with("S: "):
|
|
||||||
emit_signal("lobby_sealed")
|
|
||||||
return
|
|
||||||
"""
|
|
15
scripts/objects/bar.gd
Normal file
15
scripts/objects/bar.gd
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
extends ProgressBar
|
||||||
|
|
||||||
|
export(NodePath) onready var label = get_node(label) as Label
|
||||||
|
|
||||||
|
onready var font = label.get_theme_default_font()
|
||||||
|
|
||||||
|
func _ready():
|
||||||
|
_on_bar_resized()
|
||||||
|
|
||||||
|
func _on_bar_value_changed(value):
|
||||||
|
label.text = "%d / %d" % [value, max_value]
|
||||||
|
|
||||||
|
func _on_bar_resized():
|
||||||
|
label.visible = rect_size.y > 16
|
||||||
|
label.rect_size = rect_size / label.rect_scale
|
20
scripts/objects/destructable.gd
Normal file
20
scripts/objects/destructable.gd
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
extends CollisionShape2D
|
||||||
|
|
||||||
|
class_name Destructable
|
||||||
|
|
||||||
|
signal hit(damage, collision)
|
||||||
|
signal die(damage, collision)
|
||||||
|
|
||||||
|
export var max_health = 100
|
||||||
|
export var health = 100
|
||||||
|
|
||||||
|
func _ready():
|
||||||
|
pass
|
||||||
|
|
||||||
|
func trigger_hit(damage, collision):
|
||||||
|
emit_signal("hit", damage, collision)
|
||||||
|
health = clamp(health - damage, -max_health, max_health)
|
||||||
|
if health > max_health:
|
||||||
|
health = max_health
|
||||||
|
elif health <= 0:
|
||||||
|
emit_signal("die", damage, collision)
|
|
@ -2,7 +2,7 @@ extends HBoxContainer
|
||||||
|
|
||||||
class_name LobbyControl
|
class_name LobbyControl
|
||||||
|
|
||||||
export(String) var id = "Some UUID"
|
export(String) var uuid = "Some UUID"
|
||||||
export(String) var display_name = "Lobby" setget set_display_name
|
export(String) var display_name = "Lobby" setget set_display_name
|
||||||
export(int) var max_players = 20 setget set_max_players
|
export(int) var max_players = 20 setget set_max_players
|
||||||
export(int) var current_players = 1 setget set_current_players
|
export(int) var current_players = 1 setget set_current_players
|
||||||
|
@ -14,9 +14,11 @@ func _ready():
|
||||||
func _update_text():
|
func _update_text():
|
||||||
$Label.text = "%s (%d/%d players)" % [display_name, current_players, max_players]
|
$Label.text = "%s (%d/%d players)" % [display_name, current_players, max_players]
|
||||||
|
|
||||||
const keys = ["id", "display_name", "max_players", "current_players", "locked"]
|
const keys = ["uuid", "display_name", "max_players", "current_players", "locked"]
|
||||||
func set_with_dict(dict):
|
func set_with_dict(dict):
|
||||||
if dict.has("name"): self.display_name = dict["name"]
|
if dict.has("name"): self.display_name = dict["name"]
|
||||||
|
if dict.has("currentPlayers"): self.current_players = dict["currentPlayers"]
|
||||||
|
if dict.has("maxPlayers"): self.max_players = dict["maxPlayers"]
|
||||||
for k in keys:
|
for k in keys:
|
||||||
if dict.has(k):
|
if dict.has(k):
|
||||||
self[k] = dict[k]
|
self[k] = dict[k]
|
||||||
|
@ -34,4 +36,4 @@ func set_current_players(n):
|
||||||
_update_text()
|
_update_text()
|
||||||
|
|
||||||
func _on_join_pressed():
|
func _on_join_pressed():
|
||||||
Global.client.signaller.join_lobby(id)
|
Global.client.join_lobby(uuid)
|
||||||
|
|
|
@ -6,6 +6,7 @@ onready var ready_tex: Texture = load("res://assets/img/check.png")
|
||||||
onready var not_ready_tex: Texture = load("res://assets/img/cross.png")
|
onready var not_ready_tex: Texture = load("res://assets/img/cross.png")
|
||||||
|
|
||||||
export(String) var id = "Some UUID"
|
export(String) var id = "Some UUID"
|
||||||
|
export(String) var peerId = "Some UUID"
|
||||||
export(String) var display_name = "Lobby" setget set_display_name
|
export(String) var display_name = "Lobby" setget set_display_name
|
||||||
export(bool) var ready = false setget set_ready
|
export(bool) var ready = false setget set_ready
|
||||||
|
|
||||||
|
@ -17,7 +18,7 @@ func _update():
|
||||||
$TextureRect.texture = ready_tex if ready else not_ready_tex
|
$TextureRect.texture = ready_tex if ready else not_ready_tex
|
||||||
$TextureRect.hint_tooltip = "Ready" if ready else "Not Ready"
|
$TextureRect.hint_tooltip = "Ready" if ready else "Not Ready"
|
||||||
|
|
||||||
const keys = ["id", "display_name", "ready"]
|
const keys = ["id", "display_name", "ready", "peerId"]
|
||||||
func set_with_dict(dict):
|
func set_with_dict(dict):
|
||||||
if dict.has("name"): self.display_name = dict["name"]
|
if dict.has("name"): self.display_name = dict["name"]
|
||||||
for k in keys:
|
for k in keys:
|
||||||
|
|
|
@ -1,21 +1,28 @@
|
||||||
extends KinematicBody2D
|
extends KinematicBody2D
|
||||||
|
|
||||||
export var weight = 8
|
export(NodePath) onready var sprite = get_node(sprite) as AnimatedSprite
|
||||||
export var max_speed = 80 # ~5 tiles/sec at top speed
|
export(NodePath) onready var collider = get_node(collider) as CollisionShape2D
|
||||||
export var friction = 500
|
export(NodePath) onready var name_label = get_node(name_label) as Label
|
||||||
export var speed = 20
|
export(NodePath) onready var health_bar = get_node(health_bar) as ProgressBar
|
||||||
export var health = 100 setget set_health
|
export(NodePath) onready var mana_bar = get_node(mana_bar) as ProgressBar
|
||||||
export var max_health = 100 setget set_max_health
|
export(NodePath) onready var aim_indicator = get_node(aim_indicator) as Sprite
|
||||||
export var mana = 50 setget set_mana
|
export(NodePath) onready var tile_indicator = get_node(tile_indicator) as Sprite
|
||||||
export var max_mana = 100 setget set_max_mana
|
|
||||||
|
export var weight := 8
|
||||||
|
export var max_speed := 64 * 5 # ~5 tiles/sec at top speed
|
||||||
|
export var friction := 2000
|
||||||
|
export var speed := 80
|
||||||
|
export var mana_per_sec := 10
|
||||||
|
export var health_per_sec := 1
|
||||||
|
export var health := 100.0 setget set_health
|
||||||
|
export var max_health := 100.0 setget set_max_health
|
||||||
|
export var mana := 50.0 setget set_mana
|
||||||
|
export var max_mana := 1000.0 setget set_max_mana
|
||||||
|
|
||||||
enum PlayerClass { WIZARD }
|
enum PlayerClass { WIZARD }
|
||||||
|
|
||||||
onready var viewport = get_viewport()
|
onready var viewport = get_viewport()
|
||||||
onready var sprite = $sprite
|
onready var display_name = Global.client.DEFAULT_DISPLAY_NAME setget set_display_name
|
||||||
onready var collider = $collider
|
|
||||||
onready var name_displayer = $player_name
|
|
||||||
onready var display_name = name
|
|
||||||
onready var player_class = PlayerClass.WIZARD
|
onready var player_class = PlayerClass.WIZARD
|
||||||
onready var skills = []
|
onready var skills = []
|
||||||
|
|
||||||
|
@ -39,70 +46,89 @@ onready var skills = []
|
||||||
# },
|
# },
|
||||||
#]
|
#]
|
||||||
|
|
||||||
|
remotesync var target := Vector2(0, 0)
|
||||||
remotesync var acceleration = Vector2.ZERO
|
remotesync var acceleration = Vector2.ZERO
|
||||||
remotesync var velocity = Vector2.ZERO
|
remotesync var velocity = Vector2.ZERO
|
||||||
|
|
||||||
func set_health(x):
|
func set_health(x: float):
|
||||||
health = x
|
health = x
|
||||||
$bars/health_bar.value = x
|
update_bars()
|
||||||
maybe_hide_health_bar()
|
|
||||||
|
|
||||||
func set_max_health(x):
|
func set_max_health(x: float):
|
||||||
max_health = x
|
max_health = x
|
||||||
$bars/health_bar.max_value = x
|
update_bars()
|
||||||
maybe_hide_health_bar()
|
|
||||||
|
|
||||||
func set_mana(x):
|
func set_mana(x: float):
|
||||||
mana = x
|
mana = x
|
||||||
$bars/mana_bar.value = x
|
update_bars()
|
||||||
maybe_hide_mana_bar()
|
|
||||||
|
|
||||||
func set_max_mana(x):
|
func set_max_mana(x: float):
|
||||||
max_mana = x
|
max_mana = x
|
||||||
$bars/mana_bar.max_value = x
|
update_bars()
|
||||||
maybe_hide_mana_bar()
|
|
||||||
|
|
||||||
func maybe_hide_mana_bar():
|
func update_bars():
|
||||||
if mana >= max_mana:
|
if mana_bar is ProgressBar:
|
||||||
$bars/mana_bar.hide()
|
mana_bar.max_value = max_mana
|
||||||
else:
|
mana_bar.value = mana
|
||||||
$bars/mana_bar.show()
|
if mana >= max_mana:
|
||||||
|
mana_bar.hide()
|
||||||
|
else:
|
||||||
|
mana_bar.show()
|
||||||
|
if health_bar is ProgressBar:
|
||||||
|
health_bar.max_value = max_health
|
||||||
|
health_bar.value = health
|
||||||
|
if health >= max_health:
|
||||||
|
health_bar.hide()
|
||||||
|
else:
|
||||||
|
health_bar.show()
|
||||||
|
|
||||||
func maybe_hide_health_bar():
|
|
||||||
if health >= max_health:
|
|
||||||
$bars/health_bar.hide()
|
|
||||||
else:
|
|
||||||
$bars/health_bar.show()
|
|
||||||
|
|
||||||
func _ready():
|
func _ready():
|
||||||
# rset_config("acceleration", MultiplayerAPI.RPC_MODE_REMOTESYNC)
|
|
||||||
# rset_config("velocity", MultiplayerAPI.RPC_MODE_REMOTESYNC)
|
|
||||||
rset_config("position", MultiplayerAPI.RPC_MODE_SLAVE)
|
rset_config("position", MultiplayerAPI.RPC_MODE_SLAVE)
|
||||||
set_display_name(name)
|
var ns = get_tree().get_network_connected_peers().size()
|
||||||
|
if ns < 1:
|
||||||
|
print("Hiding Player Name %s..." % ns)
|
||||||
|
name_label.hide()
|
||||||
|
set_display_name(display_name)
|
||||||
|
if not is_network_master():
|
||||||
|
tile_indicator.modulate.a = 0.05
|
||||||
|
aim_indicator.modulate.a = 0.05
|
||||||
|
# if not is_network_master():
|
||||||
|
# mana_bar.hi
|
||||||
|
|
||||||
|
func _input(event):
|
||||||
|
if event is InputEventKey:
|
||||||
|
if event.pressed and event.scancode == KEY_P and event.alt:
|
||||||
|
position.y += 50
|
||||||
|
|
||||||
func set_display_name(i_name):
|
func set_display_name(i_name):
|
||||||
display_name = i_name
|
display_name = i_name
|
||||||
if len(get_tree().get_network_connected_peers()) > 1:
|
if name_label is Label: name_label.text = display_name
|
||||||
name_displayer.text = display_name
|
|
||||||
else:
|
|
||||||
name_displayer.text = ""
|
|
||||||
|
|
||||||
static func int_input_action(action):
|
static func int_input_action(actions) -> int:
|
||||||
return int(Input.is_action_pressed(action))
|
if typeof(actions) == TYPE_ARRAY:
|
||||||
|
var result = false
|
||||||
|
for action in actions:
|
||||||
|
result = result or Input.is_action_pressed(action)
|
||||||
|
return int(result)
|
||||||
|
else:
|
||||||
|
return int(Input.is_action_pressed(actions))
|
||||||
|
|
||||||
func _physics_process(delta):
|
func _physics_process(delta):
|
||||||
self.mana += (10*delta)
|
# if get_viewport().get_camera() != null: name_label.rect_scale = Vector2(get_viewport().get_camera().zoom)
|
||||||
self.health += (1*delta)
|
self.mana += mana_per_sec * delta
|
||||||
|
self.health += health_per_sec * delta
|
||||||
_process_input()
|
_process_input()
|
||||||
_process_animation()
|
_process_animation()
|
||||||
rset("velocity", move_and_slide((velocity + acceleration).clamped(max_speed), Vector2.ZERO, false, 4, 0.785398, false).move_toward(Vector2.ZERO, friction * delta))
|
aim_indicator.rotation = atan2(target.y, target.x) - PI/2
|
||||||
|
tile_indicator.global_position = ((target + position + Vector2(32, 32)) / 64).floor() * 64
|
||||||
|
rset_unreliable("velocity", move_and_slide((velocity + acceleration).clamped(max_speed), Vector2.ZERO, false, 4, 0.785398, false).move_toward(Vector2.ZERO, friction * delta))
|
||||||
for index in get_slide_count():
|
for index in get_slide_count():
|
||||||
var collision = get_slide_collision(index)
|
var collision = get_slide_collision(index)
|
||||||
if collision.collider is RigidBody2D:
|
if collision.collider is RigidBody2D:
|
||||||
collision.collider.apply_central_impulse(-collision.normal * weight)
|
collision.collider.apply_central_impulse(-collision.normal * weight)
|
||||||
|
|
||||||
if is_network_master():
|
# if is_network_master():
|
||||||
rset_unreliable("position", position)
|
# rset_unreliable("position", position)
|
||||||
|
|
||||||
func _process_input():
|
func _process_input():
|
||||||
if is_network_master():
|
if is_network_master():
|
||||||
|
@ -110,11 +136,14 @@ func _process_input():
|
||||||
# probably want to implement a "whichever one was pressed last" so that
|
# probably want to implement a "whichever one was pressed last" so that
|
||||||
# players move as fast as possible without having to worry about switching
|
# players move as fast as possible without having to worry about switching
|
||||||
# key frame perfectly
|
# key frame perfectly
|
||||||
iv.x = int_input_action("ui_right") - int_input_action("ui_left")
|
iv.x = int_input_action(["ui_right", "right"]) - int_input_action(["ui_left", "left"])
|
||||||
iv.y = int_input_action("ui_down") - int_input_action("ui_up")
|
iv.y = int_input_action(["ui_down", "down"]) - int_input_action(["ui_up", "up"])
|
||||||
rset("acceleration", iv.normalized() * speed)
|
rset_unreliable("acceleration", iv.normalized() * speed)
|
||||||
|
rset_unreliable("position", position)
|
||||||
|
rset_unreliable("target", get_local_mouse_position())
|
||||||
|
|
||||||
func _process_animation():
|
func _process_animation():
|
||||||
|
if sprite == null: return
|
||||||
if velocity == Vector2.ZERO:
|
if velocity == Vector2.ZERO:
|
||||||
sprite.animation = "idle"
|
sprite.animation = "idle"
|
||||||
else:
|
else:
|
||||||
|
|
79
scripts/objects/slime.gd
Normal file
79
scripts/objects/slime.gd
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
extends KinematicBody2D
|
||||||
|
|
||||||
|
class_name Slime
|
||||||
|
|
||||||
|
export var weight := 8
|
||||||
|
export var max_speed = 48 * 5 # ~3 tiles/sec at top speed
|
||||||
|
export var friction = 2000
|
||||||
|
export var speed = 80
|
||||||
|
|
||||||
|
var target_player = null
|
||||||
|
var navigation: Navigation2D = null
|
||||||
|
var nav_path: PoolVector2Array = []
|
||||||
|
var target_position = null
|
||||||
|
remotesync var velocity = Vector2.ZERO
|
||||||
|
remotesync var acceleration = Vector2.ZERO
|
||||||
|
|
||||||
|
enum {
|
||||||
|
IDLE,
|
||||||
|
WANDER,
|
||||||
|
CHASE,
|
||||||
|
ATTACK
|
||||||
|
}
|
||||||
|
|
||||||
|
var state = IDLE
|
||||||
|
onready var nav_line = $nav_line
|
||||||
|
|
||||||
|
func _ready():
|
||||||
|
rset_config("position", MultiplayerAPI.RPC_MODE_SLAVE)
|
||||||
|
|
||||||
|
func _draw():
|
||||||
|
pass
|
||||||
|
|
||||||
|
func _physics_process(delta):
|
||||||
|
print("Slime nav %s %s %s" % [navigation, position, target_player.position])
|
||||||
|
nav_path = navigation.get_simple_path(position, target_player.position)
|
||||||
|
if not nav_path.empty() and (position - target_player.position).length() > (128 + 64):
|
||||||
|
if (nav_path[0] - position).length() < 5: target_position = nav_path[1]
|
||||||
|
else: target_position = nav_path[0]
|
||||||
|
else: target_position = null
|
||||||
|
nav_line.points = []
|
||||||
|
for p in nav_path:
|
||||||
|
nav_line.points.append(p - position)
|
||||||
|
# print(position, path, target_position, target_player)
|
||||||
|
match state:
|
||||||
|
IDLE:
|
||||||
|
if target_position != null:
|
||||||
|
state = CHASE
|
||||||
|
else:
|
||||||
|
rset_unreliable("acceleration", Vector2.ZERO)
|
||||||
|
WANDER:
|
||||||
|
pass
|
||||||
|
CHASE:
|
||||||
|
if target_position == null:
|
||||||
|
state = IDLE
|
||||||
|
else:
|
||||||
|
rset_unreliable("acceleration", (target_position - position).normalized() * speed)
|
||||||
|
ATTACK:
|
||||||
|
pass
|
||||||
|
rset("velocity", move_and_slide((velocity + acceleration).clamped(max_speed), Vector2.ZERO, false, 4, 0.785398, false).move_toward(Vector2.ZERO, friction * delta))
|
||||||
|
if velocity.x < 1:
|
||||||
|
$sprite.flip_h = true
|
||||||
|
elif velocity.x > 1:
|
||||||
|
$sprite.flip_h = false
|
||||||
|
|
||||||
|
if is_network_master():
|
||||||
|
rset_unreliable("position", position)
|
||||||
|
|
||||||
|
func seek_player():
|
||||||
|
pass
|
||||||
|
|
||||||
|
func _on_hurtbox_die(_a, _b):
|
||||||
|
print("slime die")
|
||||||
|
rpc("die")
|
||||||
|
|
||||||
|
remotesync func die():
|
||||||
|
queue_free()
|
||||||
|
|
||||||
|
func _on_hurtbox_hit():
|
||||||
|
print("slime hit")
|
|
@ -1,17 +1,67 @@
|
||||||
extends Node2D
|
extends Control
|
||||||
|
|
||||||
onready var player = preload("res://objects/player.tscn")
|
onready var player_obj = preload("res://objects/player.tscn")
|
||||||
|
|
||||||
|
export(NodePath) onready var camera = get_node(camera) as Camera2D
|
||||||
|
export(NodePath) onready var menu = get_node(menu) as PopupDialog
|
||||||
|
export(NodePath) onready var players = get_node(players) as YSort
|
||||||
|
export(NodePath) onready var objects = get_node(objects) as YSort
|
||||||
|
export(NodePath) onready var nav = get_node(nav) as Navigation2D
|
||||||
|
|
||||||
|
onready var zoom_levels = [0.25, 0.5, 1, 1.5, 2, 2.5, 3]
|
||||||
|
onready var current_zoom_level_index = 2
|
||||||
|
onready var player = null
|
||||||
|
|
||||||
func _ready():
|
func _ready():
|
||||||
rpc("add_player", get_tree().get_network_unique_id())
|
if not get_tree().network_peer:
|
||||||
|
Global.client.singleplayer()
|
||||||
|
for c in players.get_children(): c.queue_free()
|
||||||
|
# TODO: probably have to wait for all peers to be ready before we add players
|
||||||
|
# camera.zoom = Vector2(0.25, 0.25)
|
||||||
|
Global.client.connect("webrtc_peer_disconnected", self, "_webrtc_peer_disconnected")
|
||||||
|
if Global.client.is_host():
|
||||||
|
for id in get_tree().get_network_connected_peers():
|
||||||
|
Global.client.peers[id].ready = false
|
||||||
|
ready(1)
|
||||||
|
else:
|
||||||
|
rpc("ready", get_tree().get_network_unique_id())
|
||||||
|
|
||||||
func _process(delta):
|
remotesync func ready(id):
|
||||||
pass
|
print("Ready %d" % id)
|
||||||
|
Global.client.peers[id].ready = true
|
||||||
|
var all_ready = true
|
||||||
|
for id in Global.client.peers: all_ready = all_ready and Global.client.peers[id].ready
|
||||||
|
if all_ready: start()
|
||||||
|
for c in objects.get_children():
|
||||||
|
if c is Slime:
|
||||||
|
c.target_player = Global.client.peers[id].player
|
||||||
|
c.navigation = nav
|
||||||
|
|
||||||
remotesync func add_player(peer_id):
|
func _webrtc_peer_disconnected(id):
|
||||||
var new_player = player.instance()
|
if Global.client.peers[id].has("player"):
|
||||||
new_player.set_network_master(peer_id)
|
Global.client.peers[id].player.queue_free()
|
||||||
add_child(new_player)
|
|
||||||
|
|
||||||
func _on_Button_pressed():
|
func start():
|
||||||
Global.main_menu()
|
print("Starting Game...")
|
||||||
|
# for each peer
|
||||||
|
for id in Global.client.peers:
|
||||||
|
print("Creating player for peer %d (name: %s)" % [id, Global.client.peers[id].name])
|
||||||
|
rpc("add_player", id, Global.client.peers[id].name)
|
||||||
|
|
||||||
|
func _process(_delta):
|
||||||
|
if player: camera.offset = player.position
|
||||||
|
|
||||||
|
remotesync func add_player(id, name):
|
||||||
|
var new_player = player_obj.instance()
|
||||||
|
if id == get_tree().get_network_unique_id(): player = new_player
|
||||||
|
new_player.name = str(id)
|
||||||
|
new_player.set_network_master(id)
|
||||||
|
players.add_child(new_player)
|
||||||
|
new_player.global_position = Vector2(-400 + (id % 800), -400 + (id % 800))
|
||||||
|
print("Added player: %s for peer %s named %s" % [new_player, id, name])
|
||||||
|
Global.client.peers[id].player = new_player
|
||||||
|
new_player.display_name = name
|
||||||
|
|
||||||
|
func _on_leave_pressed(): Global.main_menu()
|
||||||
|
func _on_menu_button_pressed(): menu.popup_centered()
|
||||||
|
func _on_close_pressed(): menu.hide()
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
extends Control
|
extends Control
|
||||||
|
|
||||||
# TODO: rename to server browser
|
|
||||||
|
|
||||||
onready var is_loaded = false
|
onready var is_loaded = false
|
||||||
|
|
||||||
onready var lobbies_grid = $v/body/p/lobbies
|
onready var lobbies_grid = $v/body/p/lobbies
|
||||||
|
@ -14,96 +12,88 @@ onready var lobbies = {}
|
||||||
onready var lobby = preload("res://objects/lobby.tscn")
|
onready var lobby = preload("res://objects/lobby.tscn")
|
||||||
|
|
||||||
func _ready():
|
func _ready():
|
||||||
display_name_edit.text = Global.client.signaller.display_name
|
display_name_edit.text = Global.client.display_name
|
||||||
Global.client.signaller.connect("lobby_new", self, "_lobby_new")
|
Global.client.connect("lobby_data", self, "_lobby_data")
|
||||||
Global.client.signaller.connect("lobby_delete", self, "_lobby_delete")
|
Global.client.connect("lobby_delete", self, "_lobby_delete")
|
||||||
Global.client.signaller.connect("lobby_joined", self, "_lobby_joined")
|
Global.client.connect("lobby_begin_join", self, "_lobby_begin_join")
|
||||||
Global.client.signaller.connect("lobby_left", self, "_lobby_left")
|
Global.client.connect("lobby_left", self, "_lobby_left")
|
||||||
Global.client.signaller.connect("websocket_disconnected", self, "_signaller_disconnected")
|
Global.client.connect("signaller_connected", self, "_signaller_connected")
|
||||||
Global.client.signaller.request_lobby_list()
|
Global.client.connect("signaller_disconnected", self, "_signaller_disconnected")
|
||||||
|
if Global.client.is_signaller_connected():
|
||||||
if Global.create_lobby:
|
_signaller_connected()
|
||||||
Global.create_lobby = false
|
else:
|
||||||
Global.client.signaller.create_lobby()
|
Global.client.connect_to_signaller()
|
||||||
|
|
||||||
"""
|
|
||||||
var ls = []
|
|
||||||
for n in range(200):
|
|
||||||
ls.push_back({id = n, name = n, currentPlayers = n, maxPlayers = n, locked = false})
|
|
||||||
call_deferred("_lobby_new", ls)
|
|
||||||
"""
|
|
||||||
|
|
||||||
# TODO: add search
|
# TODO: add search
|
||||||
func _input(ev):
|
func _input(ev):
|
||||||
if ev is InputEventKey and ev.pressed:
|
pass
|
||||||
if ev.scancode == KEY_T:
|
# if ev is InputEventKey and ev.pressed:
|
||||||
var ls = []
|
# if ev.scancode == KEY_T:
|
||||||
for n in range(5):
|
# var ls = []
|
||||||
var j = n + lobbies.size()
|
# for n in range(5):
|
||||||
ls.push_back({id = j, name = j, currentPlayers = j, maxPlayers = j, locked = false})
|
# var j = n + lobbies.size()
|
||||||
call_deferred("_lobby_new", ls)
|
# ls.push_back({id = j, name = j, currentPlayers = j, maxPlayers = j, locked = false})
|
||||||
|
# call_deferred("_lobby_new", ls)
|
||||||
|
|
||||||
func _signaller_disconnected():
|
func _signaller_disconnected():
|
||||||
Global.main_menu()
|
Global.main_menu()
|
||||||
|
|
||||||
func _lobby_joined(_id, _peerId):
|
func _signaller_connected():
|
||||||
|
print("Lobby Browser: Signaller Connected")
|
||||||
|
if Global.check_onetime_flag("create-lobby"):
|
||||||
|
call_deferred("_on_create_lobby_pressed")
|
||||||
|
else:
|
||||||
|
Global.client.request_lobby_list()
|
||||||
|
|
||||||
|
func _lobby_begin_join(data):
|
||||||
|
print("Joining Lobby: %s" % data)
|
||||||
Global.lobby()
|
Global.lobby()
|
||||||
|
|
||||||
func _lobby_left(_id):
|
func _lobby_left(_uuid):
|
||||||
Global.lobby_browser()
|
Global.lobby_browser()
|
||||||
|
|
||||||
func _on_back_pressed():
|
func _on_back_pressed():
|
||||||
Global.main_menu()
|
Global.main_menu()
|
||||||
|
|
||||||
func _on_create_lobby_pressed():
|
func _on_create_lobby_pressed():
|
||||||
Global.client.signaller.create_lobby()
|
Global.lobby()
|
||||||
|
Global.client.create_lobby()
|
||||||
|
|
||||||
func _on_join_pressed():
|
func _lobby_data(new_lobbies: Array):
|
||||||
var items = lobbies.get_selected_items()
|
for l in new_lobbies:
|
||||||
if len(items) > 0:
|
if lobbies.has(l.uuid): _update_lobby(l.uuid, l)
|
||||||
Global.client.signaller.join_lobby(lobbies.get_item_metadata(items[0])["id"])
|
else: _add_lobby(l.uuid, l)
|
||||||
|
|
||||||
func _lobby_new(new_lobbies: Array):
|
if Global.check_onetime_flag("join-first-available-lobby"):
|
||||||
# TODO: handle scrolling so that the user is never too jarred (like chat)
|
Global.client.join_lobby(l.uuid)
|
||||||
for lobby_index in range(len(new_lobbies)):
|
|
||||||
var lobby_data = new_lobbies[lobby_index]
|
|
||||||
var id = lobby_data["id"]
|
|
||||||
if lobbies.has(id):
|
|
||||||
_update_lobby(id, lobby_data)
|
|
||||||
else:
|
|
||||||
_add_lobby(id, lobby_data)
|
|
||||||
|
|
||||||
if Global.join_first_available_lobby:
|
func _lobby_delete(uuid: String):
|
||||||
Global.join_first_available_lobby = false
|
print("Lobby Deleted: %s" % uuid)
|
||||||
Global.client.signaller.join_lobby(id)
|
_delete_lobby(uuid)
|
||||||
|
|
||||||
func _lobby_delete(id: String):
|
func _add_lobby(uuid, lobby_data):
|
||||||
print("Lobby Deleted: %s" % id)
|
call_deferred("_update_lobbies_text")
|
||||||
_delete_lobby(id)
|
|
||||||
|
|
||||||
func _add_lobby(id, lobby_data):
|
|
||||||
call_deferred("update_lobbies_text")
|
|
||||||
print("New Lobby ", lobby_data)
|
print("New Lobby ", lobby_data)
|
||||||
# if lobby_data.currentPlayers > 0:
|
# if lobby_data.currentPlayers > 0:
|
||||||
var new_lobby = lobby.instance()
|
var new_lobby = lobby.instance()
|
||||||
new_lobby.set_with_dict(lobby_data)
|
new_lobby.set_with_dict(lobby_data)
|
||||||
lobbies_grid.add_child(new_lobby)
|
lobbies_grid.add_child(new_lobby)
|
||||||
lobbies[id] = new_lobby
|
lobbies[uuid] = new_lobby
|
||||||
|
|
||||||
func _update_lobby(id, lobby_data):
|
func _update_lobby(uuid, lobby_data):
|
||||||
call_deferred("update_lobbies_text")
|
call_deferred("_update_lobbies_text")
|
||||||
print("Updated Lobby ", lobby_data)
|
print("Updated Lobby ", lobby_data)
|
||||||
lobbies[id].set_with_dict(lobby_data)
|
lobbies[uuid].set_with_dict(lobby_data)
|
||||||
|
|
||||||
func _delete_lobby(id):
|
func _delete_lobby(uuid):
|
||||||
call_deferred("update_lobbies_text")
|
call_deferred("_update_lobbies_text")
|
||||||
if lobbies.has(id):
|
if lobbies.has(uuid):
|
||||||
print("Removing lobby...")
|
print("Removing lobby...")
|
||||||
lobbies_grid.remove_child(lobbies[id])
|
lobbies_grid.remove_child(lobbies[uuid])
|
||||||
lobbies[id].queue_free()
|
lobbies[uuid].queue_free()
|
||||||
lobbies.erase(id)
|
lobbies.erase(uuid)
|
||||||
|
|
||||||
func update_lobbies_text():
|
func _update_lobbies_text():
|
||||||
var n = lobbies.size()
|
var n = lobbies.size()
|
||||||
lobbies_label.text = "Active Lobbies: %d" % n
|
lobbies_label.text = "Active Lobbies: %d" % n
|
||||||
if n < 1:
|
if n < 1:
|
||||||
|
@ -114,4 +104,4 @@ func update_lobbies_text():
|
||||||
lobbies_grid.remove_child(zero_state)
|
lobbies_grid.remove_child(zero_state)
|
||||||
|
|
||||||
func _on_display_name_text_changed(new_text):
|
func _on_display_name_text_changed(new_text):
|
||||||
Global.client.signaller.set_display_name(new_text)
|
Global.client.display_name = new_text
|
||||||
|
|
|
@ -1,14 +1,22 @@
|
||||||
extends Node
|
extends Node
|
||||||
|
|
||||||
|
func _ready():
|
||||||
|
$v/h/quit.shortcut = Global.key_shortcut(KEY_ESCAPE)
|
||||||
|
$v/h/multiplayer.shortcut = Global.key_shortcut(KEY_M)
|
||||||
|
$v/h/singleplayer.shortcut = Global.key_shortcut(KEY_S)
|
||||||
|
if Global.check_onetime_flag("multiplayer"):
|
||||||
|
_on_multiplayer_pressed()
|
||||||
|
if Global.check_onetime_flag("singleplayer"):
|
||||||
|
_on_Singleplayer_pressed()
|
||||||
|
|
||||||
func _on_Singleplayer_pressed():
|
func _on_Singleplayer_pressed():
|
||||||
Global.start_singleplayer_game()
|
Global.start_singleplayer_game()
|
||||||
|
|
||||||
func _on_CreateLobbyButton_pressed():
|
func _on_multiplayer_pressed():
|
||||||
Global.lobby_browser()
|
Global.lobby_browser()
|
||||||
|
|
||||||
func _on_JoinLobbyButton_pressed():
|
func _on_JoinLobbyButton_pressed():
|
||||||
Global.quit()
|
Global.quit()
|
||||||
|
|
||||||
|
|
||||||
func _on_LinkButton_pressed():
|
func _on_LinkButton_pressed():
|
||||||
OS.shell_open("https://lyte.dev")
|
OS.shell_open("https://lyte.dev")
|
||||||
|
|
|
@ -5,6 +5,7 @@ onready var lobby_name = $v/head/lobby_info
|
||||||
onready var peers_list_label = $v/body/peers/label
|
onready var peers_list_label = $v/body/peers/label
|
||||||
onready var chat = $v/body/v/messages
|
onready var chat = $v/body/v/messages
|
||||||
onready var chat_edit = $v/body/v/h/chat
|
onready var chat_edit = $v/body/v/h/chat
|
||||||
|
onready var send_button = $v/body/v/h/send
|
||||||
onready var auto_scroll_chk = $v/body/v/chat_head/auto_scroll
|
onready var auto_scroll_chk = $v/body/v/chat_head/auto_scroll
|
||||||
onready var max_players = $v/head/max_players
|
onready var max_players = $v/head/max_players
|
||||||
onready var ready_up = $v/head/ready_up
|
onready var ready_up = $v/head/ready_up
|
||||||
|
@ -13,72 +14,84 @@ onready var start = $v/head/start
|
||||||
|
|
||||||
onready var peer = preload("res://objects/peer.tscn")
|
onready var peer = preload("res://objects/peer.tscn")
|
||||||
onready var peers = {}
|
onready var peers = {}
|
||||||
|
onready var peers_id_mappings = {}
|
||||||
onready var is_host = false
|
onready var is_host = false
|
||||||
|
|
||||||
func _ready():
|
func _ready():
|
||||||
Global.client.signaller.connect("peer_joined", self, "_peer_joined")
|
Global.client.connect("peer_created", self, "_peer_created")
|
||||||
Global.client.signaller.connect("peer_left", self, "_peer_left")
|
Global.client.connect("peer_left", self, "_peer_left")
|
||||||
Global.client.signaller.connect("lobby_left", self, "_lobby_left")
|
Global.client.connect("lobby_left", self, "_lobby_left")
|
||||||
Global.client.signaller.connect("lobby_new", self, "_lobby_update")
|
Global.client.connect("lobby_data", self, "_lobby_data")
|
||||||
|
Global.client.connect("lobby_joined", self, "_lobby_joined")
|
||||||
|
|
||||||
Global.client.signaller.connect("websocket_disconnected", self, "_signaller_disconnected")
|
Global.client.connect("signaller_disconnected", self, "_signaller_disconnected")
|
||||||
|
Global.client.connect("webrtc_connection_succeeded", self, "_webrtc_connection_succeeded")
|
||||||
|
Global.client.connect("webrtc_peer_connected", self, "_webrtc_peer_connected")
|
||||||
|
|
||||||
lobby_name.text = "%s" % Global.client.signaller.lobby_name
|
# lobby_name.text = "%s" % Global.client.lobby_name
|
||||||
|
|
||||||
Global.client.signaller.call_deferred("request_peer_list")
|
# call_deferred("add_chat", "# to %s" % Global.client.get_lobby_name())
|
||||||
call_deferred("add_chat", "# Connected to %s" % Global.client.signaller.lobby_name)
|
|
||||||
|
|
||||||
# hide/show controls depending on whether or not we're the host
|
# hide/show controls depending on whether or not we're the host
|
||||||
var is_host = Global.client.signaller.peer_id == 1
|
_update_ui()
|
||||||
if is_host:
|
|
||||||
ready_up.queue_free()
|
|
||||||
else:
|
|
||||||
lobby_name.editable = false
|
|
||||||
max_players.editable = false
|
|
||||||
start.queue_free()
|
|
||||||
lock.queue_free()
|
|
||||||
|
|
||||||
func _lobby_update(l):
|
func _signaller_disconnected():
|
||||||
for u in l:
|
Global.main_menu()
|
||||||
if !is_host:
|
|
||||||
max_players.text = str(u.maxPlayers)
|
|
||||||
lobby_name.text = u.name
|
|
||||||
print(l)
|
|
||||||
break
|
|
||||||
|
|
||||||
func _peer_joined(joined_peers):
|
func _lobby_joined(data):
|
||||||
|
add_chat("# Connected to %s" % data.name)
|
||||||
|
lobby_name.text = data.name
|
||||||
|
_update_ui()
|
||||||
|
_add_peer(data.id, {
|
||||||
|
connected = true,
|
||||||
|
ready = int(data.id) == 1,
|
||||||
|
name = Global.client.display_name,
|
||||||
|
id = int(data.id),
|
||||||
|
})
|
||||||
|
Global.client.request_peer_list()
|
||||||
|
|
||||||
|
func _webrtc_connection_succeeded():
|
||||||
|
if !is_host:
|
||||||
|
ready_up.disabled = false
|
||||||
|
send_button.disabled = false
|
||||||
|
chat_edit.editable = true
|
||||||
|
Global.client.request_peer_list()
|
||||||
|
|
||||||
|
func _peer_created(p):
|
||||||
call_deferred("update_player_count")
|
call_deferred("update_player_count")
|
||||||
if Global.client.signaller.is_host(): call_deferred("update_can_start")
|
if is_host: call_deferred("update_can_start")
|
||||||
for peer_index in range(len(joined_peers)):
|
if peers.has(p.id): _update_peer(p.id, p)
|
||||||
var peer_data = joined_peers[peer_index]
|
else: _add_peer(p.id, p)
|
||||||
var id = peer_data["id"]
|
|
||||||
if peers.has(id):
|
|
||||||
_update_peer(id, peer_data)
|
|
||||||
else:
|
|
||||||
_add_peer(id, peer_data)
|
|
||||||
|
|
||||||
func _add_peer(id, peer_data):
|
func _add_peer(id, p):
|
||||||
call_deferred("update_player_count")
|
call_deferred("update_player_count")
|
||||||
|
|
||||||
var new_peer = peer.instance()
|
var new_peer = peer.instance()
|
||||||
new_peer.set_with_dict(peer_data)
|
if p.id == 1: p.ready = true
|
||||||
|
new_peer.set_with_dict(p)
|
||||||
peers_grid.add_child(new_peer)
|
peers_grid.add_child(new_peer)
|
||||||
peers[id] = new_peer
|
peers[int(id)] = new_peer
|
||||||
add_chat("> %s joined the lobby" % new_peer.display_name)
|
add_chat("> %s joined the lobby" % new_peer.display_name)
|
||||||
|
|
||||||
func _update_peer(id, peer_data):
|
func _update_peer(id, peer_data):
|
||||||
if peer_data.ready != peers[id].ready: add_chat("! %s is %s" % [peers[id].display_name, "now ready" if peer_data.ready else "no longer ready"])
|
if peer_data.ready != peers[id].ready:
|
||||||
|
var update_text = "now ready" if peer_data.ready else "no longer ready"
|
||||||
|
add_chat("! %s is %s" % [peers[id].display_name, update_text])
|
||||||
peers[id].set_with_dict(peer_data)
|
peers[id].set_with_dict(peer_data)
|
||||||
# TODO: announce changes in chat?
|
|
||||||
|
|
||||||
func _peer_left(ids: Array):
|
func _webrtc_peer_connected(id):
|
||||||
|
print("WebRTC Peer Connected: %s" % id)
|
||||||
|
if id != get_tree().get_network_unique_id():
|
||||||
|
print("Sending ready state from %s to %s: %s" % [get_tree().get_network_unique_id(), id, "true" if peers[get_tree().get_network_unique_id()].ready else "false"])
|
||||||
|
call_deferred("rpc_id", int(id), "set_ready", peers[get_tree().get_network_unique_id()].ready)
|
||||||
|
|
||||||
|
func _peer_left(id):
|
||||||
call_deferred("update_player_count")
|
call_deferred("update_player_count")
|
||||||
for data in ids:
|
if peers.has(id):
|
||||||
var id = data["id"]
|
var peer_id = peers[id].peerId
|
||||||
if peers.has(id):
|
call_deferred("add_chat", "< %s left the lobby" % peers[id].display_name)
|
||||||
call_deferred("add_chat", "< %s left the lobby" % peers[id].display_name)
|
peers[id].queue_free()
|
||||||
peers[id].queue_free()
|
peers.erase(id)
|
||||||
peers.erase(id)
|
peers_id_mappings.erase(peer_id)
|
||||||
|
|
||||||
remotesync func add_chat(message):
|
remotesync func add_chat(message):
|
||||||
if auto_scroll_chk.pressed: call_deferred("scroll_chat_to_bottom")
|
if auto_scroll_chk.pressed: call_deferred("scroll_chat_to_bottom")
|
||||||
|
@ -98,7 +111,7 @@ func preserve_chat_scroll(n, tl, fl, tc, fc):
|
||||||
func send_chat_message():
|
func send_chat_message():
|
||||||
var message = chat_edit.text
|
var message = chat_edit.text
|
||||||
if message != "":
|
if message != "":
|
||||||
rpc("add_chat", "%s: %s" % [Global.client.signaller.display_name, message])
|
rpc("add_chat", "%s: %s" % [Global.client.display_name, message])
|
||||||
chat_edit.text = ""
|
chat_edit.text = ""
|
||||||
|
|
||||||
func update_can_start():
|
func update_can_start():
|
||||||
|
@ -108,15 +121,52 @@ func update_can_start():
|
||||||
if !peer.ready:
|
if !peer.ready:
|
||||||
can_start = false
|
can_start = false
|
||||||
break
|
break
|
||||||
print(can_start)
|
|
||||||
if can_start: add_chat("! All players ready - game may now be started")
|
if can_start: add_chat("! All players ready - game may now be started")
|
||||||
start.disabled = !can_start
|
start.disabled = !can_start
|
||||||
|
|
||||||
|
func _on_start_pressed():
|
||||||
|
if is_host: rpc("start_game")
|
||||||
|
|
||||||
|
remotesync func start_game():
|
||||||
|
# TODO: seal lobby
|
||||||
|
get_tree().refuse_new_network_connections = true
|
||||||
|
Global.goto_game()
|
||||||
|
|
||||||
|
remotesync func set_ready(ready):
|
||||||
|
var from = get_tree().get_rpc_sender_id()
|
||||||
|
print("Set Ready: %s %s" % [from, ready])
|
||||||
|
peers[from].ready = ready
|
||||||
|
if is_host: update_can_start()
|
||||||
|
|
||||||
|
func _on_TextEdit_text_entered(_new_text):
|
||||||
|
if !send_button.disabled:
|
||||||
|
send_chat_message()
|
||||||
|
|
||||||
|
remotesync func set_lobby_name(new_lobby_name: String):
|
||||||
|
if is_host: Global.client.set_lobby_name(new_lobby_name)
|
||||||
|
else: lobby_name.text = new_lobby_name
|
||||||
|
|
||||||
|
remotesync func set_max_players(new_max_players: String):
|
||||||
|
if int(new_max_players) <= 0: return
|
||||||
|
if is_host: Global.client.set_lobby_max_players(int(new_max_players))
|
||||||
|
else: max_players.text = str(int(new_max_players))
|
||||||
|
|
||||||
|
func _update_ui():
|
||||||
|
is_host = Global.client.is_host()
|
||||||
|
ready_up.visible = !is_host
|
||||||
|
lobby_name.editable = is_host
|
||||||
|
max_players.editable = is_host
|
||||||
|
start.visible = is_host
|
||||||
|
lock.visible = is_host
|
||||||
|
var is_in_lobby = Global.client.is_in_lobby()
|
||||||
|
ready_up.disabled = !is_in_lobby
|
||||||
|
chat_edit.editable = is_in_lobby
|
||||||
|
send_button.disabled = !is_in_lobby
|
||||||
|
|
||||||
|
func _on_max_players_text_changed(new_text:String): rpc("set_max_players", new_text)
|
||||||
func _lobby_left(_id): Global.lobby_browser()
|
func _lobby_left(_id): Global.lobby_browser()
|
||||||
func _on_leave_button_pressed(): Global.lobby_browser()
|
func _on_leave_button_pressed(): Global.client.leave_lobby()
|
||||||
func _on_TextEdit_text_entered(_new_text): send_chat_message()
|
|
||||||
func update_player_count(): peers_list_label.text = "Players: %d" % peers.size()
|
func update_player_count(): peers_list_label.text = "Players: %d" % peers.size()
|
||||||
func _on_Button_pressed(): send_chat_message()
|
func _on_Button_pressed(): send_chat_message()
|
||||||
func _on_ready_up_toggled(button_pressed: bool): Global.client.signaller.set_ready(button_pressed)
|
func _on_ready_up_toggled(button_pressed: bool): rpc("set_ready", button_pressed)
|
||||||
func _on_lobby_info_text_changed(new_text: String): Global.client.signaller.set_lobby_name(new_text)
|
func _on_lobby_info_text_changed(new_text: String): rpc("set_lobby_name", new_text)
|
||||||
func _signaller_disconnected(): Global.main_menu()
|
|
||||||
|
|
391
server.ts
391
server.ts
|
@ -1,84 +1,78 @@
|
||||||
// import { randomInt } from "./deps.ts";
|
// import { randomInt } from "./deps.ts";
|
||||||
|
|
||||||
const SERVER_VERSION = "0.2.0";
|
const SERVER_VERSION = "1.0.0";
|
||||||
// TODO: version comparison
|
// TODO: version comparison
|
||||||
|
|
||||||
type ID = string;
|
type ID = number;
|
||||||
|
type UUID = string;
|
||||||
|
|
||||||
// app state
|
// app state
|
||||||
const allLobbies = new Map<ID, Lobby>();
|
const allLobbies = new Map<UUID, Lobby>();
|
||||||
const allClients = new Map<ID, Client>();
|
const allClients = new Map<UUID, Client>();
|
||||||
// TODO: client index by id
|
// TODO: client index by id?
|
||||||
|
|
||||||
interface DataMessage {
|
interface DataMessage {
|
||||||
type: string;
|
type: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
type ServerDataObject = Record<
|
type ServerDataObject = Record<
|
||||||
string,
|
"peerId" | string,
|
||||||
string | number | symbol | null | boolean
|
string | number | symbol | null | boolean
|
||||||
>;
|
>;
|
||||||
type ServerData = ServerDataObject[] | ServerDataObject | string | boolean;
|
type ServerData =
|
||||||
|
| ServerDataObject[]
|
||||||
|
| ServerDataObject
|
||||||
|
| string
|
||||||
|
| boolean
|
||||||
|
| number;
|
||||||
type Message = string | DataMessage;
|
type Message = string | DataMessage;
|
||||||
|
|
||||||
const buildMessage = (
|
const buildMessage = (
|
||||||
type: string,
|
type: string,
|
||||||
data: ServerData | ServerData[],
|
data: ServerData | ServerData[],
|
||||||
) =>
|
) => {
|
||||||
Object.assign(
|
if (Array.isArray(data)) return { type, data };
|
||||||
{ type },
|
else if (typeof data === "object") return { type, ...data };
|
||||||
Array.isArray(data)
|
return `${type}:${data}`;
|
||||||
? { data }
|
};
|
||||||
: (typeof data === "object" ? data : { data }),
|
|
||||||
);
|
|
||||||
|
|
||||||
class Client {
|
class Client {
|
||||||
id: ID;
|
id: number | null;
|
||||||
peerId: number | null;
|
uuid: string;
|
||||||
name: string;
|
name: string;
|
||||||
socket: WebSocket;
|
socket: WebSocket;
|
||||||
lobby: Lobby | null;
|
lobby: Lobby | null;
|
||||||
ready: boolean;
|
|
||||||
|
|
||||||
constructor(socket: WebSocket) {
|
constructor(socket: WebSocket) {
|
||||||
this.id = crypto.randomUUID();
|
this.id = null;
|
||||||
|
this.uuid = crypto.randomUUID();
|
||||||
this.socket = socket;
|
this.socket = socket;
|
||||||
this.peerId = null;
|
|
||||||
this.name = "Client";
|
this.name = "Client";
|
||||||
this.lobby = null;
|
this.lobby = null;
|
||||||
this.ready = false;
|
allClients.set(this.uuid, this);
|
||||||
|
|
||||||
allClients.set(this.id, this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
isConnected() {
|
isConnected() {
|
||||||
return this.socket.readyState == WebSocket.OPEN;
|
return this.socket.readyState == WebSocket.OPEN;
|
||||||
}
|
}
|
||||||
|
|
||||||
setReady(ready: boolean) {
|
|
||||||
this.ready = ready;
|
|
||||||
this.lobby?.broadcast(
|
|
||||||
buildMessage("ready_change", { id: this.id, ready: this.ready }),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
remove() {
|
remove() {
|
||||||
|
console.debug(`Removing Client ${this.uuid}`);
|
||||||
this.lobbyLeave();
|
this.lobbyLeave();
|
||||||
if (this.isConnected()) {
|
if (this.isConnected()) {
|
||||||
this.socket.close();
|
this.socket.close();
|
||||||
}
|
}
|
||||||
allClients.delete(this.id);
|
allClients.delete(this.uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
send(message: Message) {
|
send(message: Message) {
|
||||||
try {
|
try {
|
||||||
if (this.isConnected()) {
|
if (this.isConnected()) {
|
||||||
this.socket.send(
|
this.socket.send(
|
||||||
typeof message === "object"
|
typeof message === "object" ? (JSON.stringify(message)) : message,
|
||||||
? ("json:" + JSON.stringify(message))
|
|
||||||
: message,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
// TODO: maybe log when we try to send and we're not connected?
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(
|
console.error(
|
||||||
`Failed to send on socket ${this.socket} to client ${this.id}: ${
|
`Failed to send on socket ${this.socket} to client ${this.id}: ${
|
||||||
|
@ -94,67 +88,59 @@ class Client {
|
||||||
}
|
}
|
||||||
|
|
||||||
peerList() {
|
peerList() {
|
||||||
if (!this.lobby) return;
|
if (!this.lobby) {
|
||||||
|
this.send(
|
||||||
|
buildMessage(
|
||||||
|
"info",
|
||||||
|
"you cannot request a list of peers unless you are in a lobby",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
const peers: {
|
const peers: {
|
||||||
id: ID;
|
id: ID;
|
||||||
name: string;
|
name: string;
|
||||||
peerId: number | null;
|
|
||||||
ready: boolean;
|
|
||||||
}[] = [];
|
}[] = [];
|
||||||
this.lobby.clients.forEach(({ id, name, peerId, ready }) =>
|
this.lobby.clients.forEach(({ id, name }) => {
|
||||||
peers.push({ id, name, peerId, ready })
|
if (typeof id === "number") peers.push({ id, name });
|
||||||
);
|
});
|
||||||
this.send(buildMessage("peer_joined", peers));
|
console.debug(`Sending peer list with ${peers.length} peers...`);
|
||||||
|
this.send(buildMessage("peer_data", peers));
|
||||||
// TODO: chunk async?
|
// TODO: chunk async?
|
||||||
}
|
}
|
||||||
|
|
||||||
lobbyList() {
|
lobbyList() {
|
||||||
const netLobbies: {
|
const netLobbies: {
|
||||||
id: ID;
|
uuid: UUID;
|
||||||
name: string;
|
name: string;
|
||||||
maxPlayers: number;
|
maxPlayers: number;
|
||||||
locked: boolean;
|
locked: boolean;
|
||||||
currentPlayers: number;
|
currentPlayers: number;
|
||||||
}[] = [];
|
}[] = [];
|
||||||
allLobbies.forEach((lobby) => {
|
allLobbies.forEach((lobby) => netLobbies.push(lobby.toData()));
|
||||||
const { id, name, maxPlayers, locked } = lobby;
|
this.send(buildMessage("lobby_data", netLobbies));
|
||||||
netLobbies.push({
|
|
||||||
id,
|
|
||||||
name,
|
|
||||||
maxPlayers,
|
|
||||||
locked,
|
|
||||||
currentPlayers: lobby.clients.size,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
// TODO: chunk async?
|
|
||||||
this.send(buildMessage("lobby_list", netLobbies));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lobbyNew(lobby: Lobby) {
|
lobbyNew(lobby: Lobby) {
|
||||||
// if the client is already in a lobby, only send messages about their lobby
|
// if the client is already in a lobby, do not send them lobby updates
|
||||||
if (this.lobby && this.lobby != lobby) return;
|
if (this.lobby) return;
|
||||||
const { id, name, maxPlayers, locked } = lobby;
|
|
||||||
this.send(
|
this.send(
|
||||||
buildMessage("lobby_new", {
|
buildMessage("lobby_data", [lobby.toData()]),
|
||||||
id,
|
|
||||||
name,
|
|
||||||
maxPlayers,
|
|
||||||
locked,
|
|
||||||
currentPlayers: lobby.clients.size,
|
|
||||||
}),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
lobbyDelete({ id }: Lobby) {
|
lobbyDelete({ uuid }: Lobby) {
|
||||||
if (this.lobby) return;
|
if (this.lobby) return;
|
||||||
this.send(buildMessage("lobby_delete", { id }));
|
this.send(buildMessage("lobby_delete", { uuid }));
|
||||||
}
|
}
|
||||||
|
|
||||||
lobbyCreate(opts?: Partial<Lobby>) {
|
lobbyCreate(opts?: Partial<Lobby>) {
|
||||||
this.ready = true;
|
|
||||||
if (this.lobby) {
|
if (this.lobby) {
|
||||||
this.send(
|
this.send(
|
||||||
`[info] cannot create lobby (already in lobby ${this.lobby.id})`,
|
buildMessage(
|
||||||
|
"info",
|
||||||
|
`cannot create lobby: already in lobby ${this.lobby.uuid}`,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -163,67 +149,83 @@ class Client {
|
||||||
|
|
||||||
lobbyJoin(lobby: Lobby) {
|
lobbyJoin(lobby: Lobby) {
|
||||||
if (this.lobby) {
|
if (this.lobby) {
|
||||||
this.send(`[info] cannot join lobby (already in lobby ${this.lobby.id})`);
|
this.send(
|
||||||
|
buildMessage(
|
||||||
|
`info`,
|
||||||
|
`cannot join lobby: already in lobby ${this.lobby.uuid}`,
|
||||||
|
),
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.lobby = lobby;
|
this.lobby = lobby;
|
||||||
lobby.addClient(this);
|
lobby.addClient(this);
|
||||||
this.send(
|
this.send(
|
||||||
buildMessage("lobby_joined", {
|
buildMessage("lobby_joined", {
|
||||||
id: lobby.id,
|
...this.lobby.toData(),
|
||||||
name: lobby.name,
|
id: this.id,
|
||||||
peerId: this.peerId,
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
lobbyLeave() {
|
lobbyLeave() {
|
||||||
this.ready = false;
|
|
||||||
const leavingLobby = this.lobby;
|
const leavingLobby = this.lobby;
|
||||||
if (!leavingLobby) {
|
if (!leavingLobby) {
|
||||||
this.send(`[info] cannot leave lobby (not in a lobby)`);
|
this.send(`[info] cannot leave lobby (not in a lobby)`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.peerId = null;
|
console.debug(`Client ${this.uuid} leaving Lobby ${this.lobby?.uuid}`);
|
||||||
this.lobby = null;
|
|
||||||
if (this.isConnected()) {
|
|
||||||
this.send(buildMessage("lobby_left", { id: leavingLobby.id }));
|
|
||||||
}
|
|
||||||
leavingLobby.removeClient(this);
|
leavingLobby.removeClient(this);
|
||||||
|
this.id = null;
|
||||||
|
this.lobby = null;
|
||||||
|
this.send(buildMessage("lobby_left", { uuid: leavingLobby.uuid }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Lobby {
|
class Lobby {
|
||||||
id: ID;
|
uuid: UUID;
|
||||||
name: string;
|
name: string;
|
||||||
clients: Map<ID, Client>;
|
clients: Map<ID, Client>;
|
||||||
hostClientId: ID;
|
hostClientUuid: UUID;
|
||||||
maxPlayers: number;
|
maxPlayers: number;
|
||||||
locked: boolean;
|
locked: boolean;
|
||||||
|
|
||||||
_shouldNotify: boolean;
|
_shouldNotify: boolean;
|
||||||
|
|
||||||
constructor(host: Client, name?: string, maxPlayers = 20) {
|
constructor(host: Client, name?: string, maxPlayers = 20) {
|
||||||
this.id = crypto.randomUUID();
|
this.uuid = crypto.randomUUID();
|
||||||
this.hostClientId = host.id;
|
this.hostClientUuid = host.uuid;
|
||||||
this.clients = new Map<ID, Client>();
|
this.clients = new Map<ID, Client>();
|
||||||
this.name = name || this.id;
|
this.name = name || this.uuid;
|
||||||
this.maxPlayers = maxPlayers;
|
this.maxPlayers = maxPlayers;
|
||||||
this.locked = false;
|
this.locked = false;
|
||||||
|
|
||||||
this._shouldNotify = true;
|
this._shouldNotify = true;
|
||||||
|
|
||||||
allLobbies.set(this.id, this);
|
console.debug(
|
||||||
host.peerId = 1;
|
`Lobby ${this.uuid} created by Client ${this.hostClientUuid}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
allLobbies.set(this.uuid, this);
|
||||||
|
host.id = 1;
|
||||||
host.lobbyJoin(this);
|
host.lobbyJoin(this);
|
||||||
this.notify();
|
this.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toData() {
|
||||||
|
return {
|
||||||
|
uuid: this.uuid,
|
||||||
|
name: this.name,
|
||||||
|
maxPlayers: this.maxPlayers,
|
||||||
|
locked: this.locked,
|
||||||
|
currentPlayers: this.clients.size,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
update(
|
update(
|
||||||
requestor: Client,
|
requestor: Client,
|
||||||
newValues: { name?: string; maxPlayers?: number; locked?: boolean },
|
newValues: { name?: string; maxPlayers?: number; locked?: boolean },
|
||||||
) {
|
) {
|
||||||
if (requestor.peerId === 1) {
|
if (requestor.uuid === this.hostClientUuid) {
|
||||||
for (const k in newValues) {
|
for (const k in newValues) {
|
||||||
switch (k) {
|
switch (k) {
|
||||||
case "name":
|
case "name":
|
||||||
|
@ -256,7 +258,8 @@ class Lobby {
|
||||||
|
|
||||||
remove() {
|
remove() {
|
||||||
this._shouldNotify = false;
|
this._shouldNotify = false;
|
||||||
allLobbies.delete(this.id);
|
console.debug(`Removing Lobby ${this.uuid}`);
|
||||||
|
allLobbies.delete(this.uuid);
|
||||||
allClients.forEach((client) => client.lobbyDelete(this));
|
allClients.forEach((client) => client.lobbyDelete(this));
|
||||||
this.clients.forEach((client) => {
|
this.clients.forEach((client) => {
|
||||||
client.lobbyLeave();
|
client.lobbyLeave();
|
||||||
|
@ -268,25 +271,26 @@ class Lobby {
|
||||||
}
|
}
|
||||||
|
|
||||||
addClient(client: Client) {
|
addClient(client: Client) {
|
||||||
if (!client.peerId) {
|
if (!client.id) {
|
||||||
const arr = new Int32Array(1);
|
const arr = new Int32Array(1);
|
||||||
crypto.getRandomValues(arr);
|
crypto.getRandomValues(arr);
|
||||||
client.peerId = Math.abs(arr[0]);
|
client.id = Math.abs(arr[0]);
|
||||||
}
|
}
|
||||||
client.send(buildMessage("your_peer_id", client.peerId.toString()));
|
console.debug(
|
||||||
|
`Adding Client ${client.uuid} Lobby ${this.uuid} as peer ${client.id}`,
|
||||||
|
);
|
||||||
|
client.send(buildMessage("init_peer", client.id));
|
||||||
this.broadcast(
|
this.broadcast(
|
||||||
buildMessage("peer_joined", [{
|
buildMessage("peer_data", [{
|
||||||
id: client.id,
|
id: client.id,
|
||||||
name: client.name,
|
name: client.name,
|
||||||
peerId: client.peerId,
|
|
||||||
}]),
|
}]),
|
||||||
);
|
);
|
||||||
console.log("Sending peer_joined...");
|
|
||||||
client.send(
|
client.send(
|
||||||
buildMessage(
|
buildMessage(
|
||||||
"peer_joined",
|
"peer_data",
|
||||||
Array.from(this.clients.values()).map(
|
Array.from(this.clients.values()).map(
|
||||||
({ id, name, peerId }) => ({ id, name, peerId }),
|
({ id, name }) => ({ id, name }),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -294,15 +298,24 @@ class Lobby {
|
||||||
this.notify();
|
this.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
removeClient({ id }: Client) {
|
removeClient({ id, uuid }: Client) {
|
||||||
|
if (typeof id !== "number") return;
|
||||||
this.clients.delete(id);
|
this.clients.delete(id);
|
||||||
this.broadcast(buildMessage("peer_left", [{ id }]));
|
this.broadcast(buildMessage("peer_left", { id }));
|
||||||
if (id === this.hostClientId) {
|
console.debug(`Removing Client ${this.uuid} from Lobby ${this.uuid}`);
|
||||||
console.warn("Host left!");
|
if (uuid === this.hostClientUuid) {
|
||||||
|
console.warn(`Host left, removing Lobby ${this.uuid}`);
|
||||||
this.remove();
|
this.remove();
|
||||||
}
|
}
|
||||||
this.notify();
|
this.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getPeer(id: number): Client | null {
|
||||||
|
for (const [_id, client] of this.clients) {
|
||||||
|
if (client.id == id) return client;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ClientMessage {
|
interface ClientMessage {
|
||||||
|
@ -316,91 +329,108 @@ interface ClientMessage {
|
||||||
data: ServerDataObject;
|
data: ServerDataObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
// events
|
function parseMessage(message: string): { type: string; data?: ServerData } {
|
||||||
function onMessage(client: Client, ev: MessageEvent) {
|
const trimmedMessage = message.trim();
|
||||||
// TODO: log who from?
|
if (trimmedMessage.startsWith("{")) {
|
||||||
const msg = ev.data.trim();
|
const { type, ...data } = JSON.parse(trimmedMessage);
|
||||||
if (msg === "pong") return;
|
return { type, data };
|
||||||
console.log("Client Message Received", msg);
|
} else {
|
||||||
if (msg === "init") {
|
const splitAt = trimmedMessage.indexOf(":");
|
||||||
client.send(
|
if (splitAt > 0) {
|
||||||
buildMessage("init", {
|
return {
|
||||||
id: client.id,
|
type: trimmedMessage.substring(0, splitAt),
|
||||||
name: client.name,
|
data: trimmedMessage.substring(splitAt + 1),
|
||||||
serverVersion: SERVER_VERSION,
|
};
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (msg === "lobby_create") client.lobbyCreate();
|
|
||||||
if (msg.startsWith("set_ready:")) {
|
|
||||||
client.setReady(JSON.parse(msg.split(":", 2)[1]));
|
|
||||||
}
|
|
||||||
if (msg === "lobby_leave") client.lobbyLeave();
|
|
||||||
if (msg === "request_lobby_list") client.lobbyList();
|
|
||||||
if (msg === "request_peer_list") {
|
|
||||||
if (client.lobby == null) {
|
|
||||||
client.send(`[info] not in a lobby`);
|
|
||||||
} else {
|
} else {
|
||||||
client.peerList();
|
return { type: trimmedMessage };
|
||||||
}
|
|
||||||
}
|
|
||||||
if (msg.startsWith("update_display_name:")) {
|
|
||||||
const displayName = msg.substr("update_display_name:".indexOf(":") + 1);
|
|
||||||
client.name = displayName;
|
|
||||||
}
|
|
||||||
if (msg.startsWith("lobby_join:")) {
|
|
||||||
const id = msg.substr(11);
|
|
||||||
const lobby = allLobbies.get(id);
|
|
||||||
if (lobby) {
|
|
||||||
client.lobbyJoin(lobby);
|
|
||||||
// client.peerList();
|
|
||||||
} else client.send(`[info] could not find lobby ${id}`);
|
|
||||||
}
|
|
||||||
if (msg.startsWith("json:")) {
|
|
||||||
const data: ClientMessage = JSON.parse(msg.substr(5));
|
|
||||||
if (data.type === "init") {
|
|
||||||
if (data.data.name) {
|
|
||||||
client.name = data.data.name.toString();
|
|
||||||
}
|
|
||||||
client.send(buildMessage("init", { id: client.id, name: client.name }));
|
|
||||||
} else if (data.type === "update_lobby") {
|
|
||||||
if (client.lobby) {
|
|
||||||
client.lobby.update(client, data["data"]);
|
|
||||||
} else {
|
|
||||||
client.send("[info] cannot update lobby (you're not even in one!)");
|
|
||||||
}
|
|
||||||
} else if (data.type === "lobby_create") {
|
|
||||||
client.lobbyCreate({ name: data.data?.name?.toString() });
|
|
||||||
if (data.data.name) {
|
|
||||||
client.name = data.data.name.toString();
|
|
||||||
}
|
|
||||||
client.send(buildMessage("init", { id: client.id, name: client.name }));
|
|
||||||
} else if (["candidate", "answer", "offer"].includes(data.type)) {
|
|
||||||
console.log("Received WebRTC Negotiation Message...");
|
|
||||||
if (typeof data.data === "object") {
|
|
||||||
const subdata = data.data;
|
|
||||||
if (typeof subdata["peerId"] === "number") {
|
|
||||||
const destPeerId: number = subdata["peerId"];
|
|
||||||
for (const iClient of client.lobby?.clients.values() || []) {
|
|
||||||
if (iClient.peerId == destPeerId) {
|
|
||||||
const payload = Object.assign({}, data);
|
|
||||||
const srcPeerId = client.peerId;
|
|
||||||
payload.data.peerId = srcPeerId;
|
|
||||||
console.log(
|
|
||||||
`Forwarding WebRTC Negotiation Message from peer ${srcPeerId} to peer ${destPeerId}...`,
|
|
||||||
);
|
|
||||||
iClient.send(payload);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// events
|
||||||
|
function onMessage(client: Client, ev: MessageEvent) {
|
||||||
|
// TODO: log who from? IPs etc.?
|
||||||
|
const msg = parseMessage(ev.data);
|
||||||
|
if (msg.type === "pong") return;
|
||||||
|
console.debug("Client Message Received", msg);
|
||||||
|
switch (msg.type) {
|
||||||
|
case "init":
|
||||||
|
if (msg.data && (msg.data as { name: string })["name"]) {
|
||||||
|
client.name = (msg.data as { name: string }).name.toString();
|
||||||
|
}
|
||||||
|
client.send(buildMessage("init", {
|
||||||
|
id: client.id,
|
||||||
|
name: client.name,
|
||||||
|
serverVersion: SERVER_VERSION,
|
||||||
|
}));
|
||||||
|
break;
|
||||||
|
case "lobby_create":
|
||||||
|
client.lobbyCreate(msg.data as Partial<Lobby>);
|
||||||
|
break;
|
||||||
|
case "lobby_leave":
|
||||||
|
client.lobbyLeave();
|
||||||
|
break;
|
||||||
|
case "request_lobby_list":
|
||||||
|
client.lobbyList();
|
||||||
|
break;
|
||||||
|
case "request_peer_list":
|
||||||
|
client.peerList();
|
||||||
|
break;
|
||||||
|
case "update_display_name":
|
||||||
|
if (msg.data) client.name = msg.data.toString();
|
||||||
|
break;
|
||||||
|
case "lobby_join":
|
||||||
|
if (msg.data) {
|
||||||
|
const lobby = allLobbies.get(msg.data.toString());
|
||||||
|
if (lobby) {
|
||||||
|
client.lobbyJoin(lobby);
|
||||||
|
} else {
|
||||||
|
client.send(buildMessage("info", `count not find lobby ${msg.data}`));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "update_lobby":
|
||||||
|
if (client.lobby) {
|
||||||
|
if (typeof (msg.data) === "object") {
|
||||||
|
client.lobby.update(client, msg.data as Partial<Lobby>);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
client.send(
|
||||||
|
buildMessage(
|
||||||
|
"info",
|
||||||
|
"failed to update lobby info: you are not in a lobby",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "candidate":
|
||||||
|
/* falls through */
|
||||||
|
case "answer":
|
||||||
|
/* falls through */
|
||||||
|
case "offer": {
|
||||||
|
if (!client.lobby) return;
|
||||||
|
if (typeof msg.data !== "object") return;
|
||||||
|
const webrtcMessage = (msg.data as { id?: number });
|
||||||
|
if (!webrtcMessage.id || typeof webrtcMessage.id !== "number") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log(
|
||||||
|
`Received WebRTC Negotiation Message (type: ${msg.type}, from: ${client.id}, to: ${webrtcMessage.id})...`,
|
||||||
|
);
|
||||||
|
const destClient = client.lobby.getPeer(webrtcMessage.id);
|
||||||
|
if (!destClient || !destClient.id) return;
|
||||||
|
webrtcMessage.id = client.id as number;
|
||||||
|
destClient.send(buildMessage(msg.type, webrtcMessage));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
console.debug("Unknown message: ", msg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function onSocketOpen(client: Client, _ev: Event) {
|
function onSocketOpen(client: Client, _ev: Event) {
|
||||||
console.log("New Client", client.id);
|
console.log("New Client", client.uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onClientLeave(client: Client) {
|
function onClientLeave(client: Client) {
|
||||||
|
@ -408,12 +438,12 @@ function onClientLeave(client: Client) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function onSocketClose(client: Client, _ev: Event) {
|
function onSocketClose(client: Client, _ev: Event) {
|
||||||
console.log("Client Close");
|
console.log("Client Socket Close");
|
||||||
onClientLeave(client);
|
onClientLeave(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onSocketError(client: Client, _ev: Event) {
|
function onSocketError(client: Client, _ev: Event) {
|
||||||
console.log("Client Error");
|
console.log("Client Socket Error");
|
||||||
onClientLeave(client);
|
onClientLeave(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -428,13 +458,10 @@ const PORT = parseInt(Deno.env.get("PORT") || "80");
|
||||||
console.log("Listening on port", PORT);
|
console.log("Listening on port", PORT);
|
||||||
const listener = Deno.listen({ port: PORT });
|
const listener = Deno.listen({ port: PORT });
|
||||||
for await (const conn of listener) {
|
for await (const conn of listener) {
|
||||||
// console.debug("Connection received:", conn);
|
|
||||||
(async () => {
|
(async () => {
|
||||||
const server = Deno.serveHttp(conn);
|
const server = Deno.serveHttp(conn);
|
||||||
for await (const { respondWith, request } of server) {
|
for await (const { respondWith, request } of server) {
|
||||||
// console.debug("HTTP Request Received", request);
|
|
||||||
try {
|
try {
|
||||||
// console.warn(JSON.stringify([allClients, allLobbies]));
|
|
||||||
const { socket, response } = Deno.upgradeWebSocket(request);
|
const { socket, response } = Deno.upgradeWebSocket(request);
|
||||||
const client = new Client(socket);
|
const client = new Client(socket);
|
||||||
socket.onmessage = (ev) => onMessage(client, ev);
|
socket.onmessage = (ev) => onMessage(client, ev);
|
||||||
|
|
Loading…
Reference in a new issue