14 Commits

78 changed files with 1437 additions and 358 deletions

4
.gitignore vendored
View File

@@ -3,7 +3,11 @@
/android/
.idea/
*.csproj
*.csproj*
*.csproj.old
*.csproj.old*
*.sln
*.sln.*
.DS_Store
**/.DS_Store
Folder.DotSettings.user

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 Joshua Higgins
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -1 +1,9 @@
# Connect4 Moderator - Observer
The front end for the [server](https://github.com/joshuafhiggins/connect4-moderator-server) made in [Godot](https://godotengine.org/), an open-soruce game engine.
# Downloads
See [releases](https://github.com/joshuafhiggins/connect4-moderator-observer/releases)
# For Future Maintainers
This was made in Godot 4.5. Due to the use of C# for a lot of the scripting, we are unable to export for the web but [progress is being made](https://github.com/godotengine/godot/pull/106125). An icon in Icon Composer was made for macOS builds but can't be used till Godot 4.6. Currently, there is also a bug in Godot's Websocket implementation that causes random disconnects, options are submitting upstream fixes and debugging or wrapping a third party library to use. It would also be nice to rework the bracket screen to show the bracket and be more thematic with the other pixel art pieces. Some scripts are leftovers/incomplete implementations of having this program act as a client, which would be helpful to finish.

BIN
assets/.DS_Store vendored

Binary file not shown.

1
assets/music/.gitattributes vendored Normal file
View File

@@ -0,0 +1 @@
jazz_music.mp3 filter=lfs diff=lfs merge=lfs -text

BIN
assets/music/jazz_music.mp3 LFS Normal file

Binary file not shown.

View File

@@ -0,0 +1,19 @@
[remap]
importer="mp3"
type="AudioStreamMP3"
uid="uid://csy7ltflvsjq5"
path="res://.godot/imported/jazz_music.mp3-51c0488dbe42064eda25eafa56d387ed.mp3str"
[deps]
source_file="res://assets/music/jazz_music.mp3"
dest_files=["res://.godot/imported/jazz_music.mp3-51c0488dbe42064eda25eafa56d387ed.mp3str"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

Binary file not shown.

View File

@@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://dauf0pi1pkd3x"
path="res://.godot/imported/chip_collide_1.ogg-406dd38ea9c3d5652eebd8457cb16d54.oggvorbisstr"
[deps]
source_file="res://assets/sfx/chip_collide_1.ogg"
dest_files=["res://.godot/imported/chip_collide_1.ogg-406dd38ea9c3d5652eebd8457cb16d54.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

Binary file not shown.

View File

@@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://b6b7b7sc038n7"
path="res://.godot/imported/chip_collide_2.ogg-f3e4af1639a40a7e5696a0d76240e083.oggvorbisstr"
[deps]
source_file="res://assets/sfx/chip_collide_2.ogg"
dest_files=["res://.godot/imported/chip_collide_2.ogg-f3e4af1639a40a7e5696a0d76240e083.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

Binary file not shown.

View File

@@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://c6a4wqoopu53j"
path="res://.godot/imported/chip_collide_3.ogg-8dacdc0da1447d7f98373dc814c23342.oggvorbisstr"
[deps]
source_file="res://assets/sfx/chip_collide_3.ogg"
dest_files=["res://.godot/imported/chip_collide_3.ogg-8dacdc0da1447d7f98373dc814c23342.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

Binary file not shown.

View File

@@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://5liedrachob2"
path="res://.godot/imported/chip_collide_4.ogg-a636adca2293a212ad4dd420b3445fd5.oggvorbisstr"
[deps]
source_file="res://assets/sfx/chip_collide_4.ogg"
dest_files=["res://.godot/imported/chip_collide_4.ogg-a636adca2293a212ad4dd420b3445fd5.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

Binary file not shown.

View File

@@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://dsyovynhhbmw8"
path="res://.godot/imported/chip_collide_5.ogg-43d68269cdbbab204cb581d3f2f55acc.oggvorbisstr"
[deps]
source_file="res://assets/sfx/chip_collide_5.ogg"
dest_files=["res://.godot/imported/chip_collide_5.ogg-43d68269cdbbab204cb581d3f2f55acc.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

Binary file not shown.

View File

@@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://c0a5rl5q04noq"
path="res://.godot/imported/chip_collide_6.ogg-277685742efbfc8894a8af489e0a5d40.oggvorbisstr"
[deps]
source_file="res://assets/sfx/chip_collide_6.ogg"
dest_files=["res://.godot/imported/chip_collide_6.ogg-277685742efbfc8894a8af489e0a5d40.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

Binary file not shown.

View File

@@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://b6d73wiiqxles"
path="res://.godot/imported/chip_collide_7.ogg-7f61ae7f863bd72768f0889e7ed11946.oggvorbisstr"
[deps]
source_file="res://assets/sfx/chip_collide_7.ogg"
dest_files=["res://.godot/imported/chip_collide_7.ogg-7f61ae7f863bd72768f0889e7ed11946.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

BIN
assets/sfx/game_end.ogg Normal file

Binary file not shown.

View File

@@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://crxjuk1vyq331"
path="res://.godot/imported/game_end.ogg-8ceab2b7181c15a955c6f20334947ba7.oggvorbisstr"
[deps]
source_file="res://assets/sfx/game_end.ogg"
dest_files=["res://.godot/imported/game_end.ogg-8ceab2b7181c15a955c6f20334947ba7.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

View File

@@ -23,7 +23,7 @@ compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""

View File

@@ -23,7 +23,7 @@ compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""

View File

@@ -23,7 +23,7 @@ compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""

View File

@@ -23,7 +23,7 @@ compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""

BIN
assets/sprites/back.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 B

View File

@@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://2plbtkcttn7o"
path="res://.godot/imported/back.png-73d0d71f353725c6ba9218045b96399b.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/sprites/back.png"
dest_files=["res://.godot/imported/back.png-73d0d71f353725c6ba9218045b96399b.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

BIN
assets/sprites/bracket.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 B

View File

@@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cdhuhbt2ws5sy"
path="res://.godot/imported/bracket.png-31c6864ee533d160bca76e6c85f99c4e.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/sprites/bracket.png"
dest_files=["res://.godot/imported/bracket.png-31c6864ee533d160bca76e6c85f99c4e.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

BIN
assets/sprites/button.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 B

View File

@@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cftuywdnwvop0"
path="res://.godot/imported/button.png-01faf565b773239305f3664038f20e61.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/sprites/button.png"
dest_files=["res://.godot/imported/button.png-01faf565b773239305f3664038f20e61.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

BIN
assets/sprites/cancel.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 229 B

View File

@@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://stk7umv2ppss"
path="res://.godot/imported/cancel.png-dcc5e0579b1b7ac5745b3b3804e100ab.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/sprites/cancel.png"
dest_files=["res://.godot/imported/cancel.png-dcc5e0579b1b7ac5745b3b3804e100ab.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 B

View File

@@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bvind4dms08sd"
path="res://.godot/imported/long_button.png-97ae4bec79a5441e5b3d9a75cbdf673f.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/sprites/long_button.png"
dest_files=["res://.godot/imported/long_button.png-97ae4bec79a5441e5b3d9a75cbdf673f.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

BIN
assets/sprites/observe.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 261 B

View File

@@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://da13ksuf4vkqe"
path="res://.godot/imported/observe.png-5a0a3c79f51f4f13ffdfce42e4ad04da.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/sprites/observe.png"
dest_files=["res://.godot/imported/observe.png-5a0a3c79f51f4f13ffdfce42e4ad04da.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

BIN
assets/sprites/player.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 B

View File

@@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://b6iac21s36dku"
path="res://.godot/imported/player.png-e1eaffe0873063c60a0d0b322e4d87d9.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/sprites/player.png"
dest_files=["res://.godot/imported/player.png-e1eaffe0873063c60a0d0b322e4d87d9.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 262 B

View File

@@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dta0orurmv5qj"
path="res://.godot/imported/player_badge.png-63850d039b551977db5c0c02f6402465.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/sprites/player_badge.png"
dest_files=["res://.godot/imported/player_badge.png-63850d039b551977db5c0c02f6402465.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

BIN
assets/sprites/rpi.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -2,16 +2,16 @@
importer="texture"
type="CompressedTexture2D"
uid="uid://ebf78p6bpq5m"
path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"
uid="uid://uritd4ygetrk"
path="res://.godot/imported/rpi.png-0f0faa9ccfa1d0b656d9c381bb4a7a6d.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://icon.svg"
dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"]
source_file="res://assets/sprites/rpi.png"
dest_files=["res://.godot/imported/rpi.png-0f0faa9ccfa1d0b656d9c381bb4a7a6d.ctex"]
[params]
@@ -38,6 +38,3 @@ process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

6
assets/theme.tres Normal file
View File

@@ -0,0 +1,6 @@
[gd_resource type="Theme" load_steps=2 format=3 uid="uid://bbgxacei1vwba"]
[ext_resource type="FontFile" uid="uid://c3jmev24lo6ci" path="res://assets/fonts/PixelOperator8.ttf" id="1_5x3i2"]
[resource]
default_font = ExtResource("1_5x3i2")

View File

@@ -1,19 +0,0 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2012
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "connect4-moderator-observer", "connect4-moderator-observer.csproj", "{88C189D2-5C9A-411D-8713-291AC0841D8C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
ExportDebug|Any CPU = ExportDebug|Any CPU
ExportRelease|Any CPU = ExportRelease|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{88C189D2-5C9A-411D-8713-291AC0841D8C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{88C189D2-5C9A-411D-8713-291AC0841D8C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{88C189D2-5C9A-411D-8713-291AC0841D8C}.ExportDebug|Any CPU.ActiveCfg = ExportDebug|Any CPU
{88C189D2-5C9A-411D-8713-291AC0841D8C}.ExportDebug|Any CPU.Build.0 = ExportDebug|Any CPU
{88C189D2-5C9A-411D-8713-291AC0841D8C}.ExportRelease|Any CPU.ActiveCfg = ExportRelease|Any CPU
{88C189D2-5C9A-411D-8713-291AC0841D8C}.ExportRelease|Any CPU.Build.0 = ExportRelease|Any CPU
EndGlobalSection
EndGlobal

View File

@@ -3,7 +3,7 @@
name="macOS"
platform="macOS"
runnable=true
advanced_options=false
advanced_options=true
dedicated_server=false
custom_features=""
export_filter="all_resources"
@@ -25,14 +25,14 @@ binary_format/architecture="universal"
custom_template/debug=""
custom_template/release=""
debug/export_console_wrapper=0
application/icon="uid://ckmfi0cjgxgyk"
application/icon_interpolation=4
application/icon="uid://dd7lvnidxr5ss"
application/icon_interpolation=0
application/bundle_identifier="com.abunchofknowitalls.connect4"
application/signature=""
application/app_category="Games"
application/short_version="0.1.1"
application/version="0.1.1"
application/copyright="RPI Minds & Machines"
application/short_version=""
application/version=""
application/copyright="RPI Minds and Machines"
application/copyright_localized={}
application/min_macos_version_x86_64="10.12"
application/min_macos_version_arm64="11.00"
@@ -49,7 +49,7 @@ xcode/xcode_build="14C18"
codesign/codesign=3
codesign/installer_identity=""
codesign/apple_team_id="8S7C654DQ4"
codesign/identity="Developer ID Application: Joshua Higgins (8S7C654DQ4)"
codesign/identity="73BA692FE950ABC209210ACAA8AD412BD9C6C4A3"
codesign/entitlements/custom_file=""
codesign/entitlements/allow_jit_code_execution=false
codesign/entitlements/allow_unsigned_executable_memory=false
@@ -261,13 +261,13 @@ dotnet/embed_build_outputs=false
name="Windows (x86_64)"
platform="Windows Desktop"
runnable=true
advanced_options=false
advanced_options=true
dedicated_server=false
custom_features=""
export_filter="all_resources"
include_filter=""
exclude_filter=""
export_path="../../Downloads/connect4-moderator-observer (win-x86_64) (0.1.0).exe"
export_path="../../Downloads/connect4-moderator-observer (win-x86_64).exe"
patches=PackedStringArray()
encryption_include_filters=""
encryption_exclude_filters=""
@@ -293,7 +293,7 @@ codesign/digest_algorithm=1
codesign/description=""
codesign/custom_options=PackedStringArray()
application/modify_resources=true
application/icon=""
application/icon="uid://dd7lvnidxr5ss"
application/console_wrapper_icon=""
application/icon_interpolation=4
application/file_version=""
@@ -301,7 +301,7 @@ application/product_version=""
application/company_name=""
application/product_name=""
application/file_description=""
application/copyright=""
application/copyright="RPI Minds & Machines"
application/trademarks=""
application/export_angle=0
application/export_d3d12=0
@@ -325,20 +325,20 @@ Unregister-ScheduledTask -TaskName godot_remote_debug -Confirm:$false -ErrorActi
Remove-Item -Recurse -Force '{temp_dir}'"
dotnet/include_scripts_content=false
dotnet/include_debug_symbols=false
dotnet/embed_build_outputs=false
dotnet/embed_build_outputs=true
[preset.2]
name="Windows (arm64)"
platform="Windows Desktop"
runnable=false
advanced_options=false
advanced_options=true
dedicated_server=false
custom_features=""
export_filter="all_resources"
include_filter=""
exclude_filter=""
export_path="../../Downloads/connect4-moderator-observer (win-arm64) (0.1.0).exe"
export_path="../../Downloads/connect4-moderator-observer (win-arm64).exe"
patches=PackedStringArray()
encryption_include_filters=""
encryption_exclude_filters=""
@@ -364,7 +364,7 @@ codesign/digest_algorithm=1
codesign/description=""
codesign/custom_options=PackedStringArray()
application/modify_resources=true
application/icon=""
application/icon="uid://dd7lvnidxr5ss"
application/console_wrapper_icon=""
application/icon_interpolation=4
application/file_version=""
@@ -396,20 +396,20 @@ Unregister-ScheduledTask -TaskName godot_remote_debug -Confirm:$false -ErrorActi
Remove-Item -Recurse -Force '{temp_dir}'"
dotnet/include_scripts_content=false
dotnet/include_debug_symbols=false
dotnet/embed_build_outputs=false
dotnet/embed_build_outputs=true
[preset.3]
name="Linux (x86_64)"
platform="Linux"
runnable=true
advanced_options=false
advanced_options=true
dedicated_server=false
custom_features=""
export_filter="all_resources"
include_filter=""
exclude_filter=""
export_path="../../Downloads/connect4-moderator-observer (linux-x86_64) (0.1.0)"
export_path="../../Downloads/connect4-moderator-observer (linux-x86_64).out"
patches=PackedStringArray()
encryption_include_filters=""
encryption_exclude_filters=""
@@ -426,7 +426,7 @@ debug/export_console_wrapper=0
binary_format/embed_pck=true
texture_format/s3tc_bptc=true
texture_format/etc2_astc=false
shader_baker/enabled=true
shader_baker/enabled=false
binary_format/architecture="x86_64"
ssh_remote_deploy/enabled=false
ssh_remote_deploy/host="user@host_ip"
@@ -442,20 +442,20 @@ kill $(pgrep -x -f \"{temp_dir}/{exe_name} {cmd_args}\")
rm -rf \"{temp_dir}\""
dotnet/include_scripts_content=false
dotnet/include_debug_symbols=false
dotnet/embed_build_outputs=false
dotnet/embed_build_outputs=true
[preset.4]
name="Linux (arm64)"
platform="Linux"
runnable=false
advanced_options=false
advanced_options=true
dedicated_server=false
custom_features=""
export_filter="all_resources"
include_filter=""
exclude_filter=""
export_path="../../Downloads/connect4-moderator-observer (linux-arm64) (0.1.0)"
export_path="../../Downloads/connect4-moderator-observer (linux-arm64).out"
patches=PackedStringArray()
encryption_include_filters=""
encryption_exclude_filters=""
@@ -472,7 +472,7 @@ debug/export_console_wrapper=0
binary_format/embed_pck=true
texture_format/s3tc_bptc=true
texture_format/etc2_astc=false
shader_baker/enabled=true
shader_baker/enabled=false
binary_format/architecture="arm64"
ssh_remote_deploy/enabled=false
ssh_remote_deploy/host="user@host_ip"
@@ -488,4 +488,4 @@ kill $(pgrep -x -f \"{temp_dir}/{exe_name} {cmd_args}\")
rm -rf \"{temp_dir}\""
dotnet/include_scripts_content=false
dotnet/include_debug_symbols=false
dotnet/embed_build_outputs=false
dotnet/embed_build_outputs=true

BIN
icon.icon/Assets/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

View File

@@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dbn4ae2a7ao0t"
path="res://.godot/imported/icon.png-d1b269abce4c61d818c5589654ffbcc2.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://icon.icon/Assets/icon.png"
dest_files=["res://.godot/imported/icon.png-d1b269abce4c61d818c5589654ffbcc2.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

37
icon.icon/icon.json Normal file
View File

@@ -0,0 +1,37 @@
{
"fill" : {
"automatic-gradient" : "extended-srgb:0.00000,0.53333,1.00000,1.00000"
},
"groups" : [
{
"layers" : [
{
"glass" : true,
"image-name" : "icon.png",
"name" : "icon",
"position" : {
"scale" : 0.2,
"translation-in-points" : [
0,
0
]
}
}
],
"shadow" : {
"kind" : "neutral",
"opacity" : 0.5
},
"translucency" : {
"enabled" : true,
"value" : 0.5
}
}
],
"supported-platforms" : {
"circles" : [
"watchOS"
],
"squares" : "shared"
}
}

BIN
icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

40
icon.png.import Normal file
View File

@@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dd7lvnidxr5ss"
path="res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://icon.png"
dest_files=["res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128"><rect width="124" height="124" x="2" y="2" fill="#363d52" stroke="#212532" stroke-width="4" rx="14"/><g fill="#fff" transform="translate(12.322 12.322)scale(.101)"><path d="M105 673v33q407 354 814 0v-33z"/><path fill="#478cbf" d="m105 673 152 14q12 1 15 14l4 67 132 10 8-61q2-11 15-15h162q13 4 15 15l8 61 132-10 4-67q3-13 15-14l152-14V427q30-39 56-81-35-59-83-108-43 20-82 47-40-37-88-64 7-51 8-102-59-28-123-42-26 43-46 89-49-7-98 0-20-46-46-89-64 14-123 42 1 51 8 102-48 27-88 64-39-27-82-47-48 49-83 108 26 42 56 81zm0 33v39c0 276 813 276 814 0v-39l-134 12-5 69q-2 10-14 13l-162 11q-12 0-16-11l-10-65H446l-10 65q-4 11-16 11l-162-11q-12-3-14-13l-5-69z"/><path d="M483 600c0 34 58 34 58 0v-86c0-34-58-34-58 0z"/><circle cx="725" cy="526" r="90"/><circle cx="299" cy="526" r="90"/></g><g fill="#414042" transform="translate(12.322 12.322)scale(.101)"><circle cx="307" cy="532" r="60"/><circle cx="717" cy="532" r="60"/></g></svg>

Before

Width:  |  Height:  |  Size: 995 B

View File

@@ -10,14 +10,25 @@ config_version=5
[application]
config/name="connect4-moderator-observer"
config/name="Connect4 Observer"
config/version="0.2.0"
run/main_scene="uid://dcx5nvs0pa7me"
config/features=PackedStringArray("4.5", "C#", "Forward Plus")
config/icon="uid://ckmfi0cjgxgyk"
boot_splash/image="uid://dd7lvnidxr5ss"
config/icon="uid://dd7lvnidxr5ss"
[autoload]
Connection="*res://scripts/Connection.cs"
BackgroundMusic="*res://scripts/background_music.gd"
[display]
window/size/viewport_width=1280
window/size/viewport_height=720
window/stretch/mode="canvas_items"
window/stretch/aspect="expand"
window/vsync/vsync_mode=2
[dotnet]
@@ -28,3 +39,5 @@ project/assembly_name="connect4-moderator-observer"
textures/canvas_textures/default_texture_filter=0
textures/vram_compression/import_s3tc_bptc=true
textures/vram_compression/import_etc2_astc=true
anti_aliasing/quality/msaa_2d=2
anti_aliasing/quality/msaa_3d=2

View File

@@ -1,13 +1,16 @@
[gd_scene load_steps=14 format=3 uid="uid://m542qwlp7hl7"]
[gd_scene load_steps=17 format=3 uid="uid://m542qwlp7hl7"]
[ext_resource type="Script" uid="uid://dg5jt0o0r0v3r" path="res://scripts/BoardScreen.cs" id="1_b3w8x"]
[ext_resource type="PackedScene" uid="uid://rl33x81cxlh0" path="res://scenes/bracket_view.tscn" id="2_u1oi2"]
[ext_resource type="AudioStream" uid="uid://crxjuk1vyq331" path="res://assets/sfx/game_end.ogg" id="2_kseed"]
[ext_resource type="Texture2D" uid="uid://dlx02qat7j6lf" path="res://assets/sprites/AssetTileset.png" id="3_1tlhv"]
[ext_resource type="Theme" uid="uid://bbgxacei1vwba" path="res://assets/theme.tres" id="3_3louw"]
[ext_resource type="FontFile" uid="uid://c3jmev24lo6ci" path="res://assets/fonts/PixelOperator8.ttf" id="3_rjcmr"]
[ext_resource type="Texture2D" uid="uid://ckmfi0cjgxgyk" path="res://assets/sprites/RedChip.png" id="4_1hrcj"]
[ext_resource type="Texture2D" uid="uid://qy30emdgrk7o" path="res://assets/sprites/YellowChip.png" id="5_i2o8i"]
[ext_resource type="Script" uid="uid://cwfg17tdbk44b" path="res://scripts/thinking.gd" id="5_wjs8a"]
[ext_resource type="Texture2D" uid="uid://8un28mol7qow" path="res://assets/sprites/BoardTileMap.png" id="6_i2o8i"]
[ext_resource type="PackedScene" uid="uid://pdean68jjg80" path="res://scenes/button_small.tscn" id="7_glh1q"]
[ext_resource type="Script" uid="uid://b3q4gq63qmx23" path="res://scripts/BackButton.cs" id="8_u1oi2"]
[sub_resource type="WorldBoundaryShape2D" id="WorldBoundaryShape2D_b3w8x"]
@@ -31,10 +34,10 @@ sources/0 = SubResource("TileSetAtlasSource_i2o8i")
atlas = ExtResource("3_1tlhv")
region = Rect2(112, 32, 16, 16)
[node name="BoardScreen" type="Node2D" node_paths=PackedStringArray("BackButton")]
[node name="BoardScreen" type="Node2D"]
script = ExtResource("1_b3w8x")
BracketScene = ExtResource("2_u1oi2")
BackButton = NodePath("BracketButton")
endingSfx = ExtResource("2_kseed")
theme = ExtResource("3_3louw")
[node name="Floor Collider" type="StaticBody2D" parent="."]
position = Vector2(0, 200)
@@ -77,6 +80,7 @@ position = Vector2(274, 0)
shape = SubResource("SegmentShape2D_i2o8i")
[node name="Player1Card" type="Node2D" parent="."]
position = Vector2(0, 8)
[node name="Left" type="Sprite2D" parent="Player1Card"]
position = Vector2(-556, -300)
@@ -117,12 +121,13 @@ offset_left = -529.0
offset_top = -292.0
offset_right = -427.0
offset_bottom = -284.0
theme_override_colors/font_color = Color(1, 0, 0, 1)
theme_override_fonts/font = ExtResource("3_rjcmr")
theme_override_font_sizes/font_size = 8
text = "NOT READY"
text = "THINKING"
script = ExtResource("5_wjs8a")
[node name="Player2Card" type="Node2D" parent="."]
position = Vector2(989, -64)
[node name="Left" type="Sprite2D" parent="Player2Card"]
position = Vector2(-556, -228)
@@ -163,10 +168,10 @@ offset_left = -530.0
offset_top = -220.0
offset_right = -428.0
offset_bottom = -212.0
theme_override_colors/font_color = Color(1, 0, 0, 1)
theme_override_fonts/font = ExtResource("3_rjcmr")
theme_override_font_sizes/font_size = 8
text = "NOT READY"
text = "THINKING"
script = ExtResource("5_wjs8a")
[node name="TileMap" type="TileMap" parent="."]
position = Vector2(39, 200)
@@ -181,6 +186,7 @@ offset_left = -566.0
offset_top = 281.0
offset_right = -550.0
offset_bottom = 297.0
script = ExtResource("8_u1oi2")
[node name="Sprite2D" type="Sprite2D" parent="BracketButton"]
position = Vector2(8, 8)

View File

@@ -1,10 +1,22 @@
[gd_scene load_steps=5 format=3 uid="uid://rl33x81cxlh0"]
[gd_scene load_steps=8 format=3 uid="uid://rl33x81cxlh0"]
[ext_resource type="Theme" uid="uid://bbgxacei1vwba" path="res://assets/theme.tres" id="1_as653"]
[ext_resource type="Script" uid="uid://dm25u0a2lqk2x" path="res://scripts/BracketScene.cs" id="1_dvj3m"]
[ext_resource type="Texture2D" uid="uid://ckmfi0cjgxgyk" path="res://assets/sprites/RedChip.png" id="2_7c11m"]
[ext_resource type="PackedScene" uid="uid://b4tujjdhmk4h" path="res://scenes/red_chip.tscn" id="3_1511b"]
[ext_resource type="Texture2D" uid="uid://da13ksuf4vkqe" path="res://assets/sprites/observe.png" id="2_mbqc8"]
[ext_resource type="Texture2D" uid="uid://stk7umv2ppss" path="res://assets/sprites/cancel.png" id="3_as653"]
[ext_resource type="Script" uid="uid://1y72woiynf31" path="res://scripts/AdminControls.cs" id="4_mbqc8"]
[sub_resource type="Gradient" id="Gradient_wu84c"]
colors = PackedColorArray(0, 0.07058824, 0.101960786, 1, 0.39215687, 0.39215687, 0.39215687, 1)
[sub_resource type="GradientTexture2D" id="GradientTexture2D_as653"]
gradient = SubResource("Gradient_wu84c")
width = 1024
height = 1024
fill_from = Vector2(0.5, 1)
fill_to = Vector2(0.5, 0)
metadata/_snap_enabled = true
[node name="BracketView" type="Control" node_paths=PackedStringArray("Players", "Matches")]
layout_mode = 3
anchors_preset = 15
@@ -12,11 +24,23 @@ anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
theme = ExtResource("1_as653")
script = ExtResource("1_dvj3m")
Players = NodePath("HBoxContainer/PlayerList")
Matches = NodePath("HBoxContainer/MatchList")
JoinButton = ExtResource("2_7c11m")
BoardScene = ExtResource("3_1511b")
WatchButton = ExtResource("2_mbqc8")
TerminateKickButton = ExtResource("3_as653")
[node name="Background" type="TextureRect" parent="."]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
offset_top = -152.0
offset_bottom = 152.0
grow_horizontal = 2
grow_vertical = 2
texture = SubResource("GradientTexture2D_as653")
[node name="ColorRect" type="ColorRect" parent="."]
custom_minimum_size = Vector2(0, 36)
@@ -26,7 +50,7 @@ anchor_right = 1.0
grow_horizontal = 2
color = Color(0.13319641, 0.13319641, 0.13319638, 1)
[node name="AdminControls" type="HBoxContainer" parent="ColorRect" node_paths=PackedStringArray("BecomeAdmin", "StartTournament")]
[node name="AdminControls" type="HBoxContainer" parent="ColorRect" node_paths=PackedStringArray("BecomeAdmin", "StartTournament", "CancelTournament", "Label", "Timeout")]
custom_minimum_size = Vector2(0, 36)
layout_mode = 1
anchors_preset = 10
@@ -36,6 +60,9 @@ grow_horizontal = 2
script = ExtResource("4_mbqc8")
BecomeAdmin = NodePath("BecomeAdmin")
StartTournament = NodePath("StartTournament")
CancelTournament = NodePath("CancelTournament")
Label = NodePath("Label")
Timeout = NodePath("HSlider")
[node name="BecomeAdmin" type="Button" parent="ColorRect/AdminControls"]
layout_mode = 2
@@ -45,6 +72,25 @@ text = "Become Admin"
layout_mode = 2
text = "Start Tournament"
[node name="CancelTournament" type="Button" parent="ColorRect/AdminControls"]
layout_mode = 2
text = "Cancel Tournament"
[node name="Label" type="Label" parent="ColorRect/AdminControls"]
layout_mode = 2
text = "Wait To Move: 5.0s "
[node name="HSlider" type="HSlider" parent="ColorRect/AdminControls"]
custom_minimum_size = Vector2(256, 0)
layout_mode = 2
size_flags_vertical = 1
min_value = 0.1
max_value = 5.0
step = 0.2
value = 5.0
tick_count = 25
ticks_on_borders = true
[node name="HBoxContainer" type="HBoxContainer" parent="."]
layout_mode = 1
anchors_preset = -1

View File

@@ -1,10 +1,33 @@
[gd_scene load_steps=3 format=3 uid="uid://cr8fi0e4r88s8"]
[gd_scene load_steps=5 format=3 uid="uid://cr8fi0e4r88s8"]
[ext_resource type="PackedScene" uid="uid://cct663hb47yka" path="res://scenes/create_join_room.tscn" id="1_yqjtg"]
[ext_resource type="PackedScene" uid="uid://m542qwlp7hl7" path="res://scenes/board_screen.tscn" id="2_lnu2h"]
[sub_resource type="Gradient" id="Gradient_wu84c"]
colors = PackedColorArray(0, 0.07058824, 0.101960786, 1, 0.39215687, 0.39215687, 0.39215687, 1)
[sub_resource type="GradientTexture2D" id="GradientTexture2D_yqjtg"]
gradient = SubResource("Gradient_wu84c")
width = 1024
height = 1024
fill_from = Vector2(0.5, 1)
fill_to = Vector2(0.5, 0)
metadata/_snap_enabled = true
[node name="Game" type="Node2D"]
[node name="Background" type="TextureRect" parent="."]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
offset_left = -659.0
offset_top = -558.0
offset_right = 619.0
offset_bottom = 466.0
grow_horizontal = 2
grow_vertical = 2
texture = SubResource("GradientTexture2D_yqjtg")
[node name="GameManager" type="Node" parent="."]
[node name="Camera2D" type="Camera2D" parent="."]

View File

@@ -1,7 +1,20 @@
[gd_scene load_steps=3 format=3 uid="uid://dcx5nvs0pa7me"]
[gd_scene load_steps=7 format=3 uid="uid://dcx5nvs0pa7me"]
[ext_resource type="Script" uid="uid://bk22f71oximjk" path="res://scripts/AddressUI.cs" id="1_l6cm7"]
[ext_resource type="Theme" uid="uid://bbgxacei1vwba" path="res://assets/theme.tres" id="1_wu84c"]
[ext_resource type="Script" uid="uid://cpjbiqn26khck" path="res://scripts/ConnectButtonUI.cs" id="2_ekxnf"]
[ext_resource type="Texture2D" uid="uid://uritd4ygetrk" path="res://assets/sprites/rpi.png" id="3_bqqt6"]
[sub_resource type="Gradient" id="Gradient_wu84c"]
colors = PackedColorArray(0, 0.07058824, 0.101960786, 1, 0.39215687, 0.39215687, 0.39215687, 1)
[sub_resource type="GradientTexture2D" id="GradientTexture2D_yqjtg"]
gradient = SubResource("Gradient_wu84c")
width = 1024
height = 1024
fill_from = Vector2(0.5, 1)
fill_to = Vector2(0.5, 0)
metadata/_snap_enabled = true
[node name="Control" type="Control"]
layout_mode = 3
@@ -11,6 +24,63 @@ anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
[node name="Background" type="TextureRect" parent="."]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
texture = SubResource("GradientTexture2D_yqjtg")
[node name="RPI Minds and Machines" type="Label" parent="."]
layout_mode = 1
anchors_preset = 5
anchor_left = 0.5
anchor_right = 0.5
offset_left = -193.5
offset_top = 128.0
offset_right = 193.5
offset_bottom = 152.0
grow_horizontal = 2
theme = ExtResource("1_wu84c")
theme_type_variation = &"HeaderMedium"
text = "RPI Minds & Machines"
horizontal_alignment = 1
vertical_alignment = 1
[node name="Connect" type="Label" parent="."]
layout_mode = 1
anchors_preset = 5
anchor_left = 0.5
anchor_right = 0.5
offset_left = -100.0
offset_top = 200.0
offset_right = 68.00122
offset_bottom = 229.0
grow_horizontal = 2
theme = ExtResource("1_wu84c")
theme_type_variation = &"HeaderLarge"
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
theme_override_constants/outline_size = 16
text = "Connect"
[node name="4" type="Label" parent="."]
layout_mode = 1
anchors_preset = 5
anchor_left = 0.5
anchor_right = 0.5
offset_left = 78.0
offset_top = 200.0
offset_right = 118.00116
offset_bottom = 229.0
grow_horizontal = 2
theme = ExtResource("1_wu84c")
theme_type_variation = &"HeaderLarge"
theme_override_colors/font_color = Color(1, 0, 0, 1)
theme_override_constants/outline_size = 16
text = "4"
[node name="Address" type="TextEdit" parent="."]
layout_mode = 1
anchors_preset = 8
@@ -24,10 +94,13 @@ offset_right = 250.0
offset_bottom = 20.0
grow_horizontal = 2
grow_vertical = 2
theme = ExtResource("1_wu84c")
placeholder_text = "Server Address"
emoji_menu_enabled = false
scroll_smooth = true
script = ExtResource("1_l6cm7")
[node name="Button" type="Button" parent="." node_paths=PackedStringArray("AddressUi", "ErrorLabel")]
[node name="Button" type="Button" parent="." node_paths=PackedStringArray("AddressField", "ErrorLabel")]
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
@@ -40,9 +113,10 @@ offset_right = 250.0
offset_bottom = 67.25
grow_horizontal = 2
grow_vertical = 2
theme = ExtResource("1_wu84c")
text = "Connect"
script = ExtResource("2_ekxnf")
AddressUi = NodePath("../Address")
AddressField = NodePath("../Address")
ErrorLabel = NodePath("../Label")
[node name="Label" type="Label" parent="."]
@@ -58,4 +132,17 @@ offset_right = 68.5
offset_bottom = 98.149994
grow_horizontal = 2
grow_vertical = 2
theme = ExtResource("1_wu84c")
theme_override_colors/font_color = Color(1, 0, 0, 1)
[node name="TextureRect" type="TextureRect" parent="."]
layout_mode = 1
anchors_preset = 2
anchor_top = 1.0
anchor_bottom = 1.0
offset_left = 5.0
offset_top = -69.0
offset_right = 69.0
offset_bottom = -5.0
grow_vertical = 0
texture = ExtResource("3_bqqt6")

View File

@@ -1,13 +1,25 @@
[gd_scene load_steps=4 format=3 uid="uid://b4tujjdhmk4h"]
[gd_scene load_steps=11 format=3 uid="uid://b4tujjdhmk4h"]
[ext_resource type="Texture2D" uid="uid://ckmfi0cjgxgyk" path="res://assets/sprites/RedChip.png" id="1_qsflu"]
[ext_resource type="Script" uid="uid://dd5nu6037qsr0" path="res://scripts/red_chip.gd" id="1_tfypd"]
[ext_resource type="Script" uid="uid://dd5nu6037qsr0" path="res://scripts/chip_sfx.gd" id="1_tfypd"]
[ext_resource type="AudioStream" uid="uid://dauf0pi1pkd3x" path="res://assets/sfx/chip_collide_1.ogg" id="2_g7r6w"]
[ext_resource type="AudioStream" uid="uid://b6b7b7sc038n7" path="res://assets/sfx/chip_collide_2.ogg" id="3_l66m7"]
[ext_resource type="AudioStream" uid="uid://c6a4wqoopu53j" path="res://assets/sfx/chip_collide_3.ogg" id="4_5isma"]
[ext_resource type="AudioStream" uid="uid://5liedrachob2" path="res://assets/sfx/chip_collide_4.ogg" id="5_sa1hx"]
[ext_resource type="AudioStream" uid="uid://dsyovynhhbmw8" path="res://assets/sfx/chip_collide_5.ogg" id="6_752ap"]
[ext_resource type="AudioStream" uid="uid://c0a5rl5q04noq" path="res://assets/sfx/chip_collide_6.ogg" id="7_ky42j"]
[ext_resource type="AudioStream" uid="uid://b6d73wiiqxles" path="res://assets/sfx/chip_collide_7.ogg" id="8_lgcne"]
[sub_resource type="CircleShape2D" id="CircleShape2D_tfypd"]
radius = 13.017083
radius = 12.8
[node name="RedChip" type="RigidBody2D"]
[node name="RedChip" type="RigidBody2D" node_paths=PackedStringArray("audio_stream_player_2d")]
lock_rotation = true
contact_monitor = true
max_contacts_reported = 3
script = ExtResource("1_tfypd")
audio_stream_player_2d = NodePath("AudioStreamPlayer2D")
sounds = Array[AudioStream]([ExtResource("2_g7r6w"), ExtResource("3_l66m7"), ExtResource("4_5isma"), ExtResource("5_sa1hx"), ExtResource("6_752ap"), ExtResource("7_ky42j"), ExtResource("8_lgcne")])
[node name="Sprite2D" type="Sprite2D" parent="."]
scale = Vector2(3, 3)
@@ -16,3 +28,8 @@ texture = ExtResource("1_qsflu")
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
scale = Vector2(3, 3)
shape = SubResource("CircleShape2D_tfypd")
[node name="AudioStreamPlayer2D" type="AudioStreamPlayer2D" parent="."]
volume_db = -5.0
[connection signal="body_entered" from="." to="." method="_on_body_entered"]

View File

@@ -1,13 +1,25 @@
[gd_scene load_steps=4 format=3 uid="uid://lruk652t0xe5"]
[gd_scene load_steps=11 format=3 uid="uid://lruk652t0xe5"]
[ext_resource type="Script" uid="uid://ddg7dv686sbrb" path="res://scripts/yellow_chip.gd" id="1_epi6l"]
[ext_resource type="Script" uid="uid://dd5nu6037qsr0" path="res://scripts/chip_sfx.gd" id="1_epi6l"]
[ext_resource type="Texture2D" uid="uid://qy30emdgrk7o" path="res://assets/sprites/YellowChip.png" id="1_eu0sq"]
[ext_resource type="AudioStream" uid="uid://dauf0pi1pkd3x" path="res://assets/sfx/chip_collide_1.ogg" id="2_ki13g"]
[ext_resource type="AudioStream" uid="uid://b6b7b7sc038n7" path="res://assets/sfx/chip_collide_2.ogg" id="3_kic5w"]
[ext_resource type="AudioStream" uid="uid://c6a4wqoopu53j" path="res://assets/sfx/chip_collide_3.ogg" id="4_nbcpr"]
[ext_resource type="AudioStream" uid="uid://5liedrachob2" path="res://assets/sfx/chip_collide_4.ogg" id="5_wq2sm"]
[ext_resource type="AudioStream" uid="uid://dsyovynhhbmw8" path="res://assets/sfx/chip_collide_5.ogg" id="6_ik045"]
[ext_resource type="AudioStream" uid="uid://c0a5rl5q04noq" path="res://assets/sfx/chip_collide_6.ogg" id="7_evobg"]
[ext_resource type="AudioStream" uid="uid://b6d73wiiqxles" path="res://assets/sfx/chip_collide_7.ogg" id="8_b44ux"]
[sub_resource type="CircleShape2D" id="CircleShape2D_epi6l"]
radius = 13.004272
radius = 12.8
[node name="YellowChip" type="RigidBody2D"]
[node name="YellowChip" type="RigidBody2D" node_paths=PackedStringArray("audio_stream_player_2d")]
lock_rotation = true
contact_monitor = true
max_contacts_reported = 3
script = ExtResource("1_epi6l")
audio_stream_player_2d = NodePath("AudioStreamPlayer2D")
sounds = Array[AudioStream]([ExtResource("2_ki13g"), ExtResource("3_kic5w"), ExtResource("4_nbcpr"), ExtResource("5_wq2sm"), ExtResource("6_ik045"), ExtResource("7_evobg"), ExtResource("8_b44ux")])
[node name="Sprite2D" type="Sprite2D" parent="."]
scale = Vector2(3, 3)
@@ -16,3 +28,8 @@ texture = ExtResource("1_eu0sq")
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
scale = Vector2(3, 3)
shape = SubResource("CircleShape2D_epi6l")
[node name="AudioStreamPlayer2D" type="AudioStreamPlayer2D" parent="."]
volume_db = -5.0
[connection signal="body_entered" from="." to="." method="_on_body_entered"]

View File

@@ -1,46 +1,96 @@
using Godot;
using System;
using System.Collections.Generic;
public partial class AdminControls : HBoxContainer
{
[Export] public Button BecomeAdmin;
[Export] public Button StartTournament;
[Export] public Button CancelTournament;
[Export] public Label Label;
[Export] public Slider Timeout;
public override void _Ready()
{
Connection.Instance.OnBecomeAdmin += OnBecomeAdmin;
Connection.Instance.OnTournamentEnd += OnEndTournament;
Connection.Instance.OnBecomeAdmin += UpdateUI;
Connection.Instance.OnTournamentEnd += UpdateUI;
Connection.Instance.OnStartTournamentAck += UpdateUI;
Connection.Instance.OnCancelTournamentAck += UpdateUI;
Connection.Instance.OnGetDataAcks += UpdateUI;
StartTournament.Pressed += StartTournamentCommand;
if (!Connection.Instance.IsAdmin || Connection.Instance.ActiveTournament)
StartTournament.Pressed += () => Connection.Instance.StartTournament();
CancelTournament.Pressed += () => Connection.Instance.CancelTournament();
UpdateUI();
Timeout.ValueChanged += value =>
{
StartTournament.Hide();
Connection.Instance.SetTournamentWait((float)value);
var time = Connection.Instance.CurrentWaitTimeout.ToString();
if (time.Length > 3)
{
time = time.Substring(0, 3);
}
Label.Text = "Wait To Move: " + time + "s ";
};
BecomeAdmin.Pressed += ShowAuthPopup;
}
public override void _ExitTree()
{
Connection.Instance.OnBecomeAdmin -= OnBecomeAdmin;
Connection.Instance.OnTournamentEnd -= OnEndTournament;
Connection.Instance.OnBecomeAdmin -= UpdateUI;
Connection.Instance.OnTournamentEnd -= UpdateUI;
Connection.Instance.OnStartTournamentAck -= UpdateUI;
Connection.Instance.OnCancelTournamentAck -= UpdateUI;
Connection.Instance.OnGetDataAcks -= UpdateUI;
}
private void StartTournamentCommand()
private void UpdateUI()
{
Connection.Instance.StartTournament();
if (!Connection.Instance.IsAdmin)
{
BecomeAdmin.Show();
StartTournament.Hide();
CancelTournament.Hide();
Label.Hide();
Timeout.Hide();
}
else
{
BecomeAdmin.Hide();
Label.Show();
Timeout.Show();
}
private void OnEndTournament(List<(string, int)> playerScoreboard)
if (Connection.Instance.IsAdmin && Connection.Instance.DemoMode)
{
StartTournament.Hide();
CancelTournament.Hide();
}
else if (Connection.Instance.IsAdmin && Connection.Instance.ActiveTournament)
{
StartTournament.Hide();
CancelTournament.Show();
}
else if (Connection.Instance.IsAdmin)
{
StartTournament.Show();
ShowTournamentScoreboard(playerScoreboard);
CancelTournament.Hide();
}
Timeout.Value = Connection.Instance.CurrentWaitTimeout;
var time = Connection.Instance.CurrentWaitTimeout.ToString();
if (time.Length > 3)
{
time = time.Substring(0, 3);
}
Label.Text = "Wait To Move: " + time + "s ";
}
private void ShowAuthPopup()
{
var authWindow = new Window();
authWindow.Theme = GD.Load<Theme>("res://assets/theme.tres");
authWindow.AlwaysOnTop = true;
authWindow.MaximizeDisabled = true;
authWindow.Unresizable = true;
@@ -48,7 +98,7 @@ public partial class AdminControls : HBoxContainer
authWindow.Size = new Vector2I(256, 128);
authWindow.CloseRequested += () =>
{
GetTree().Root.RemoveChild(authWindow);
GetTree().Root.CallDeferred(Node.MethodName.RemoveChild, authWindow);
};
var vbox = new VBoxContainer();
@@ -63,12 +113,30 @@ public partial class AdminControls : HBoxContainer
passwordBox.PlaceholderText = "Password";
passwordBox.SetCustomMinimumSize(new Vector2(32, 32));
passwordBox.GuiInput += e =>
{
if (passwordBox.HasFocus() && e is InputEventKey inputEventKey && inputEventKey.IsPressed())
{
if (inputEventKey.KeyLabel == Key.Enter)
{
Connection.Instance.AdminAuth(passwordBox.Text);
GetTree().Root.CallDeferred(Node.MethodName.RemoveChild, authWindow);
GetViewport().SetInputAsHandled();
}
if (inputEventKey.KeyLabel == Key.Space)
{
GetViewport().SetInputAsHandled();
}
}
};
var button = new Button();
button.Text = "Login";
button.Pressed += () =>
{
Connection.Instance.AdminAuth(passwordBox.Text);
GetTree().Root.RemoveChild(authWindow);
GetTree().Root.CallDeferred(Node.MethodName.RemoveChild, authWindow);
};
vbox.AddChild(passwordBox);
@@ -78,46 +146,4 @@ public partial class AdminControls : HBoxContainer
GetTree().Root.AddChild(authWindow);
}
private void ShowTournamentScoreboard(List<(string, int)> playerScoreboard)
{
var scoreboardWindow = new Window();
scoreboardWindow.AlwaysOnTop = true;
scoreboardWindow.MaximizeDisabled = true;
scoreboardWindow.Unresizable = true;
scoreboardWindow.InitialPosition = Window.WindowInitialPosition.CenterMainWindowScreen;
scoreboardWindow.Size = new Vector2I(256, 512);
scoreboardWindow.CloseRequested += () =>
{
GetTree().Root.RemoveChild(scoreboardWindow);
};
var tree = new Tree();
tree.HideRoot = true;
tree.Columns = 2;
tree.ColumnTitlesVisible = true;
tree.SetColumnTitle(0, "Player");
tree.SetColumnTitle(1, "Score");
var root = tree.CreateItem();
foreach ((string, int) entry in playerScoreboard)
{
var item = tree.CreateItem(root);
item.SetText(0, entry.Item1);
item.SetText(1, entry.Item2.ToString());
}
scoreboardWindow.AddChild(tree);
GetTree().Root.AddChild(scoreboardWindow);
}
private void OnBecomeAdmin()
{
BecomeAdmin.Hide();
if (!Connection.Instance.ActiveTournament)
{
StartTournament.Show();
}
}
}

18
scripts/BackButton.cs Normal file
View File

@@ -0,0 +1,18 @@
using Godot;
using System;
public partial class BackButton : TextureButton
{
private const string BRACKET_SCENE_PATH = "res://scenes/bracket_view.tscn";
public override void _Pressed()
{
TransitionToBracket();
base._Pressed();
}
private void TransitionToBracket()
{
GetTree().ChangeSceneToFile(BRACKET_SCENE_PATH);
}
}

View File

@@ -0,0 +1 @@
uid://b3q4gq63qmx23

View File

@@ -1,10 +1,10 @@
using Godot;
using System;
using System.Collections.Generic;
using System.Linq;
public partial class BoardScreen : Node2D {
[Export] public BaseButton BackButton;
public partial class BoardScreen : Node2D
{
[Export] private AudioStream endingSfx;
[Export] private Theme theme;
private const string RED_CHIP_PATH = "res://scenes/red_chip.tscn";
private const string YELLOW_CHIP_PATH = "res://scenes/yellow_chip.tscn";
@@ -15,6 +15,9 @@ public partial class BoardScreen : Node2D {
private const int CHIP_X_OFF = -(CHIP_SIZE + CHIP_PADDING) * 7 / 2 + (CHIP_SIZE + CHIP_PADDING) / 2;
private const int Y_OFF = 300;
private const int CARD_CENTER_X_DEFAULT = -536;
private const double MOVE_TIMEOUT_BEFORE_PLACE = 1.0f;
private double currentTimeout = 0.0f;
private PackedScene redChip;
private PackedScene ylwChip;
@@ -25,6 +28,10 @@ public partial class BoardScreen : Node2D {
private RigidBody2D[,] chips = new RigidBody2D[6, 7]; // 6 rows 7 cols | 0, 0 is top left
private bool _lastMove = false;
private float _lastMoveTimer = 2.5f;
private string _winner = "";
// Called when the node enters the scene tree for the first time.
public override void _Ready() {
// Node initialization
@@ -41,145 +48,147 @@ public partial class BoardScreen : Node2D {
player1Card.GetNode<Label>("Name").Text = matchData.player1;
player2Card.GetNode<Label>("Name").Text = matchData.player2;
GetNode<TextureButton>("BracketButton").Pressed += TransitionToBracket;
Connection.Instance.OnObserveWin += ObserveWin;
Connection.Instance.OnObserveDraw += ObserveDraw;
Connection.Instance.OnObserveTerminated += ObserveTerminated;
Connection.Instance.OnObserveMove += ObserveMove;
Connection.Instance.OnTournamentEnd += ShowTournamentScoreboard;
BackButton.Pressed += () =>
if (Connection.Instance.PreviousMoves.Count == 0)
{
TransitionToBracket();
};
player1Card.GetNode<Label>("Status").Show();
player2Card.GetNode<Label>("Status").Hide();
}
else if (Connection.Instance.PreviousMoves.Last().Item1 == matchData.player1)
{
player1Card.GetNode<Label>("Status").Hide();
player2Card.GetNode<Label>("Status").Show();
}
else
{
player1Card.GetNode<Label>("Status").Show();
player2Card.GetNode<Label>("Status").Hide();
}
Connection.Instance.OnObserveWin += OnObserveWin;
Connection.Instance.OnObserveDraw += OnObserveDraw;
Connection.Instance.OnObserveTerminated += OnObserveTerminated;
Connection.Instance.OnObserveMove += ObserveMove;
}
public override void _Process(double delta)
{
if (Connection.Instance.PreviousMoves.Count != 0 && currentTimeout <= 0.0f)
{
var move = Connection.Instance.PreviousMoves[0];
Connection.Instance.PreviousMoves.RemoveAt(0);
if (move.Item1 == matchData.player1)
{
spawnRed(move.Item2);
}
else
{
spawnYellow(move.Item2);
}
currentTimeout = MOVE_TIMEOUT_BEFORE_PLACE;
}
else if (currentTimeout >= 0.0f)
{
currentTimeout -= delta;
}
if (_lastMove)
{
_lastMoveTimer -= (float) delta;
}
if (_lastMoveTimer <= 0.0f)
{
if (_winner == "")
{
PopupMessage("Draw!");
}
else
{
PopupMessage(_winner + " wins!");
}
}
}
public override void _ExitTree()
{
Connection.Instance.OnObserveWin -= ObserveWin;
Connection.Instance.OnObserveDraw -= ObserveDraw;
Connection.Instance.OnObserveTerminated -= ObserveTerminated;
Connection.Instance.OnObserveWin -= OnObserveWin;
Connection.Instance.OnObserveDraw -= OnObserveDraw;
Connection.Instance.OnObserveTerminated -= OnObserveTerminated;
Connection.Instance.OnObserveMove -= ObserveMove;
Connection.Instance.OnTournamentEnd -= ShowTournamentScoreboard;
}
private void OnObserveWin(string winner)
{
_lastMove = true;
_winner = winner;
player1Card.GetNode<Label>("Status").Hide();
player2Card.GetNode<Label>("Status").Hide();
}
private void OnObserveDraw()
{
_lastMove = true;
player1Card.GetNode<Label>("Status").Hide();
player2Card.GetNode<Label>("Status").Hide();
}
private void OnObserveTerminated()
{
PopupMessage("Match Terminated");
player1Card.GetNode<Label>("Status").Hide();
player2Card.GetNode<Label>("Status").Hide();
}
private void ObserveMove(string username, int column)
{
if (username == matchData.player1)
{
spawnRed(column);
if (!_lastMove)
player2Card.GetNode<Label>("Status").Show();
player1Card.GetNode<Label>("Status").Hide();
}
else
{
spawnYellow(column);
if (!_lastMove)
player1Card.GetNode<Label>("Status").Show();
player2Card.GetNode<Label>("Status").Hide();
}
Connection.Instance.PreviousMoves.Add((username, column));
}
private void ObserveWin(string winner)
private void PopupMessage(string message)
{
var popup = new Popup();
popup.AlwaysOnTop = true;
popup.PopupCentered();
popup.Size = new Vector2I(128, 128);
popup.Size = new Vector2I(200, 100);
popup.Theme = GD.Load<Theme>("res://assets/theme.tres");
var text = new Label();
text.Text = winner + " wins!";
text.Text = message;
var sfx = new AudioStreamPlayer();
sfx.Stream = endingSfx;
sfx.VolumeDb = -2;
popup.AddChild(sfx);
popup.AddChild(text);
text.GrowHorizontal = Control.GrowDirection.Both;
text.GrowVertical = Control.GrowDirection.Both;
text.HorizontalAlignment = HorizontalAlignment.Center;
text.VerticalAlignment = VerticalAlignment.Center;
text.AnchorsPreset = (int) Control.LayoutPreset.FullRect;
popup.InitialPosition = Window.WindowInitialPosition.CenterMainWindowScreen;
popup.PopupHide += () => popup.QueueFree();
GetTree().Root.AddChild(popup);
TransitionToBracket();
}
private void ObserveDraw()
{
var popup = new Popup();
popup.AlwaysOnTop = true;
popup.PopupCentered();
popup.Size = new Vector2I(128, 128);
var text = new Label();
text.Text = "Draw!";
popup.AddChild(text);
GetTree().Root.AddChild(popup);
sfx.Play();
popup.Show();
TransitionToBracket();
}
private void ObserveTerminated()
{
var popup = new Popup();
popup.AlwaysOnTop = true;
popup.PopupCentered();
popup.Size = new Vector2I(128, 128);
var text = new Label();
text.Text = "Match Terminated";
popup.AddChild(text);
GetTree().Root.AddChild(popup);
TransitionToBracket();
}
private void ShowTournamentScoreboard(List<(string, int)> playerScoreboard)
{
var scoreboardWindow = new Window();
scoreboardWindow.AlwaysOnTop = true;
scoreboardWindow.MaximizeDisabled = true;
scoreboardWindow.Unresizable = true;
scoreboardWindow.InitialPosition = Window.WindowInitialPosition.CenterMainWindowScreen;
scoreboardWindow.Size = new Vector2I(256, 512);
scoreboardWindow.CloseRequested += () =>
{
GetTree().Root.RemoveChild(scoreboardWindow);
};
var tree = new Tree();
tree.HideRoot = true;
tree.Columns = 2;
tree.ColumnTitlesVisible = true;
tree.SetColumnTitle(0, "Player");
tree.SetColumnTitle(1, "Score");
var root = tree.CreateItem();
foreach ((string, int) entry in playerScoreboard)
{
var item = tree.CreateItem(root);
item.SetText(0, entry.Item1);
item.SetText(1, entry.Item2.ToString());
}
scoreboardWindow.AddChild(tree);
GetTree().Root.AddChild(scoreboardWindow);
}
private void TransitionToBracket()
{
GetTree().ChangeSceneToFile(BRACKET_SCENE_PATH);
}
// Called every frame. 'delta' is the elapsed time since the previous frame.
public override void _Process(double delta) {
/*
if(p1 != null) {
Label username = player1Card.GetNode<Label>("Name");
username.Text = p1.username;
if(p1.isReady) {
Label status = player1Card.GetNode<Label>("Status");
status.Text = "Ready!";
status.AddThemeColorOverride("font_color", Colors.LimeGreen);
}
}
if(p2 != null) {
Label username = player2Card.GetNode<Label>("Name");
username.Text = p2.username;
if(p2.isReady) {
Label status = player2Card.GetNode<Label>("Status");
status.Text = "Ready!";
status.AddThemeColorOverride("font_color", Colors.LimeGreen);
}
}
*/
}
/*
* Determines if the column can have a new chip placed
* Will return -1 if invalid

View File

@@ -1,12 +1,12 @@
using Godot;
using System;
using System.Collections.Generic;
public partial class BracketScene : Control
{
[Export] public Tree Players;
[Export] public Tree Matches;
[Export] public Texture2D JoinButton;
[Export] public Texture2D WatchButton;
[Export] public Texture2D TerminateKickButton;
private const string BOARD_SCENE_PATH = "res://scenes/game.tscn";
private List<PlayerData> _playerList;
@@ -26,6 +26,7 @@ public partial class BracketScene : Control
Matches.SetColumnTitle(2, "Yellow");
Matches.HideRoot = true;
Matches.ButtonClicked += WatchGame;
Matches.ButtonClicked += TerminateGame;
Connection.Instance.OnUpdatedPlayers += UpdatePlayers;
Connection.Instance.OnUpdatedMatches += UpdateMatches;
@@ -43,6 +44,7 @@ public partial class BracketScene : Control
{
Players.Clear();
_playerList = playerList;
_playerList.Sort((a, b) => a.username.CompareTo(b.username));
var root = Players.CreateItem();
for (int i = 0; i < _playerList.Count; i++)
{
@@ -52,7 +54,7 @@ public partial class BracketScene : Control
item.SetText(2, playerList[i].isPlaying ? "Yes" : "No");
if (Connection.Instance.IsAdmin)
{
item.AddButton(0, JoinButton, i, false, "Kick"); // TODO
item.AddButton(0, TerminateKickButton, i, false, "Kick");
}
}
}
@@ -68,18 +70,30 @@ public partial class BracketScene : Control
item.SetText(0, matchList[i].matchId.ToString());
item.SetText(1, matchList[i].player1);
item.SetText(2, matchList[i].player2);
item.AddButton(0, JoinButton, i, false, "Watch");
item.AddButton(0, WatchButton, i, false, "Watch");
if (Connection.Instance.IsAdmin)
{
item.AddButton(0, TerminateKickButton, 128 + i, false, "Terminate");
}
}
}
private void WatchGame(TreeItem item, long column, long id, long mouseButtonIndex)
{
if (mouseButtonIndex == 1 && column == 0)
if (mouseButtonIndex == 1 && column == 0 && id < 128)
{
Connection.Instance.SendWatchGame(_matchList[(int) id].matchId);
}
}
private void TerminateGame(TreeItem item, long column, long id, long mouseButtonIndex)
{
if (mouseButtonIndex == 1 && column == 0 && id - 128 >= 0 && _matchList[(int) id - 128] != null)
{
Connection.Instance.TerminateGame(_matchList[(int) id - 128].matchId);
}
}
private void KickPlayer(TreeItem item, long column, long id, long mouseButtonIndex)
{
if (mouseButtonIndex == 1 && column == 0)

View File

@@ -1,23 +1,62 @@
using Godot;
using System;
public partial class ConnectButtonUI : Button
{
[Export] public TextEdit AddressUi;
[Export] public TextEdit AddressField;
[Export] public Label ErrorLabel;
private const string BRACKET_SCENE_PATH = "res://scenes/bracket_view.tscn";
public override void _Ready()
{
Connection.Instance.OnWsConnectionSuccess += OnConnectionSuccess;
Connection.Instance.OnWsConnectionFailed += OnConnectionFailed;
if (Connection.Instance.LastUsedConnectionAddress.Length > 0)
{
AddressField.Text = Connection.Instance.LastUsedConnectionAddress;
}
if (Connection.Instance.LastError.Length > 0)
{
ErrorLabel.Text = Connection.Instance.LastError;
}
AddressField.GuiInput += e =>
{
if (AddressField.HasFocus() && e is InputEventKey inputEventKey && inputEventKey.IsPressed())
{
if (inputEventKey.KeyLabel == Key.Enter)
{
Connection.Instance.Connect(AddressField.Text);
GetViewport().SetInputAsHandled();
}
if (inputEventKey.KeyLabel == Key.Space)
{
GetViewport().SetInputAsHandled();
}
}
};
}
public override void _ExitTree()
{
Connection.Instance.OnWsConnectionSuccess -= OnConnectionSuccess;
Connection.Instance.OnWsConnectionFailed -= OnConnectionFailed;
}
public override void _Pressed()
{
if (Connection.Instance.Connect(AddressUi.Text))
Connection.Instance.Connect(AddressField.Text);
}
private void OnConnectionSuccess()
{
GD.Print("Success!");
GetTree().ChangeSceneToFile(BRACKET_SCENE_PATH);
}
else
private void OnConnectionFailed()
{
ErrorLabel.Text = "Couldn't connect to server!";
}
base._Pressed();
ErrorLabel.Text = "Couldn't connect to server! " + Connection.Instance.LastError;
}
}

View File

@@ -9,8 +9,7 @@ public partial class Connection : Node
public static Connection Instance { get; private set; }
private WebSocketPeer _webSocket = new WebSocketPeer();
private bool _firstConnect = true;
private WebSocketPeer _webSocket = new ();
private Thread _gameListThread;
private bool _gameListThreadRunning;
@@ -31,60 +30,57 @@ public partial class Connection : Node
public event Action<List<MatchData>> OnUpdatedMatches;
public event Action<List<PlayerData>> OnUpdatedPlayers;
public event Action OnStartTournamentAck;
public event Action<List<(string, int)>> OnTournamentEnd;
public event Action OnTournamentEnd;
public event Action OnCancelTournamentAck;
public event Action OnBecomeAdmin;
public event Action OnGetDataAcks;
public event Action OnWsConnectionSuccess;
public event Action OnWsConnectionFailed;
public event Action OnWsDisconnect;
// Already prints to console
public event Action<string, string> OnError;
public event Action<string> OnError;
public bool IsAdmin { get; private set; }
public bool IsPlayer { get; private set; }
public bool ActiveTournament { get; private set; }
public bool DemoMode { get; private set; }
public List<(string, int)> PreviousMoves { get; private set; } = [];
public double CurrentWaitTimeout { get; private set; } = 5.0;
public MatchData CurrentObservingMatch { get; private set; }
public string LastUsedConnectionAddress { get; private set; } = "";
public string LastError { get; private set; } = "";
private bool IsSocketOpen => _webSocket.GetReadyState() == WebSocketPeer.State.Open;
private bool _connecting = false;
private bool _connected = false;
private List<(string, int)> _lastScoreboard = [];
private bool _shouldShowTournamentResults = false;
public override void _Ready()
{
Instance = this;
_webSocket.SetHeartbeatInterval(5.0);
_webSocket.HeartbeatInterval = 5.0;
Instance.OnWsDisconnect += () => GetTree().ChangeSceneToFile("res://scenes/main_menu.tscn");
}
public bool Connect(string address)
public void Connect(string address)
{
if (_webSocket.GetReadyState() == WebSocketPeer.State.Open)
_connecting = true;
LastUsedConnectionAddress = address;
if (_connected)
{
return false;
return;
}
Error error = _webSocket.ConnectToUrl(address);
if (error != Error.Ok)
{
return false;
LastError = error.ToString();
}
_webSocket.Poll();
while (_webSocket.GetReadyState() == WebSocketPeer.State.Connecting)
{
_webSocket.Poll();
Thread.Sleep(TimeSpan.FromMilliseconds(5));
}
if (_webSocket.GetReadyState() != WebSocketPeer.State.Open)
{
return false;
}
_webSocket.SetHeartbeatInterval(5.0);
_webSocket.HeartbeatInterval = 5.0;
_firstConnect = false;
StartGameListRefreshLoop();
return true;
}
public override void _ExitTree()
@@ -92,24 +88,69 @@ public partial class Connection : Node
StopGameListRefreshLoop();
}
public override void _PhysicsProcess(double delta)
public override void _Process(double delta)
{
_webSocket.Poll();
WebSocketPeer.State state = _webSocket.GetReadyState();
if ((state == WebSocketPeer.State.Closed || state == WebSocketPeer.State.Closing) && !_firstConnect)
if (state == WebSocketPeer.State.Open)
{
StopGameListRefreshLoop();
GD.PrintErr("Connection lost.");
//GetTree().Quit();
if (_connecting)
{
_connecting = false;
_connected = true;
LastError = "";
OnWsConnectionSuccess?.Invoke();
StartGameListRefreshLoop();
}
if (IsSocketOpen)
{
while (_webSocket.GetAvailablePacketCount() > 0)
{
string message = _webSocket.GetPacket().GetStringFromUtf8();
HandleServerMessage(message);
}
if (_shouldShowTournamentResults)
{
var children = GetTree().Root.GetChildren();
foreach (var child in children)
{
if (child.Name.ToString() == "BracketView")
{
_shouldShowTournamentResults = false;
ShowTournamentScoreboard(_lastScoreboard);
}
}
}
}
else if (state == WebSocketPeer.State.Connecting)
{
// Do nothing
}
else if (state == WebSocketPeer.State.Closing)
{
// Do nothing
}
else if (state == WebSocketPeer.State.Closed)
{
if (_connecting)
{
_connecting = false;
OnWsConnectionFailed?.Invoke();
}
else if (_connected)
{
_connected = false;
IsAdmin = false;
CurrentWaitTimeout = 5.0;
ActiveTournament = false;
DemoMode = false;
StopGameListRefreshLoop();
var code = _webSocket.GetCloseCode();
var reason = _webSocket.GetCloseReason();
LastError = "Unexpected Disconnect. Reason: " + reason + ", Code: " + code;
GD.PrintErr("WebSocket closed with code: " + code + ", reason " + reason + ". Clean: " + (code != -1));
OnWsDisconnect?.Invoke();
}
}
}
@@ -204,6 +245,16 @@ public partial class Connection : Node
SendCommand("ADMIN", "AUTH:" + password);
}
public void GetMoveWait()
{
SendCommand("GET", "MOVE_WAIT");
}
public void GetTournamentStatus()
{
SendCommand("GET", "TOURNAMENT_STATUS");
}
// Admin commands
public void KickPlayer(string playerId)
@@ -218,15 +269,22 @@ public partial class Connection : Node
SendCommand("TOURNAMENT", "START");
}
public void TerminateGame()
public void CancelTournament()
{
if (!IsAdmin) return;
SendCommand("GAME", "TERMINATE");
SendCommand("TOURNAMENT", "CANCEL");
}
public void TerminateGame(int matchID)
{
if (!IsAdmin) return;
SendCommand("GAME", "TERMINATE:" + matchID);
}
public void SetTournamentWait(float waitTime)
{
if (!IsAdmin) return;
CurrentWaitTimeout = waitTime;
SendCommand("TOURNAMENT", "WAIT:" + waitTime);
}
@@ -305,6 +363,8 @@ public partial class Connection : Node
if (body == "AUTH:ACK")
{
IsAdmin = true;
GetMoveWait();
GetTournamentStatus();
OnBecomeAdmin?.Invoke();
}
@@ -312,9 +372,29 @@ public partial class Connection : Node
case "TOURNAMENT":
HandleTournamentMessage(body);
break;
case "GET":
if (body.StartsWith("MOVE_WAIT"))
{
CurrentWaitTimeout = double.Parse(body.Split(":")[1]);
}
else if (body.StartsWith("TOURNAMENT_STATUS"))
{
string status = body.Split(":")[1];
if (status != "DEMO")
{
ActiveTournament = bool.Parse(status);
}
else
{
ActiveTournament = false;
DemoMode = true;
}
}
OnGetDataAcks?.Invoke();
break;
case "ERROR":
HandleErrorMessage(body);
GD.PrintErr($"Error: {body}");
GD.PrintErr(message);
OnError?.Invoke(message);
break;
default:
GD.Print($"Unhandled server message: {message}");
@@ -346,7 +426,9 @@ public partial class Connection : Node
playerScoreboard.Add((data[0], int.Parse(data[1])));
}
OnTournamentEnd?.Invoke(playerScoreboard);
_lastScoreboard = playerScoreboard;
_shouldShowTournamentResults = true;
OnTournamentEnd?.Invoke();
break;
}
case "START":
@@ -355,6 +437,12 @@ public partial class Connection : Node
ActiveTournament = true;
break;
}
case "CANCEL":
{
ActiveTournament = false;
OnCancelTournamentAck?.Invoke();
break;
}
}
}
@@ -465,8 +553,24 @@ public partial class Connection : Node
OnUpdatedMatches?.Invoke(matches);
break;
case "WATCH":
string[] game = segments[2].Split(",");
CurrentObservingMatch = new MatchData(int.Parse(game[0]), game[1], game[2]);
CurrentObservingMatch = null;
PreviousMoves.Clear();
string[] activeMatchData = segments[2].Split("|");
if (activeMatchData.IsEmpty())
{
string[] matchData = segments[2].Split(',');
CurrentObservingMatch = new MatchData(int.Parse(matchData[0]), matchData[1], matchData[2]);
}
else
{
string[] matchData = activeMatchData[0].Split(',');
CurrentObservingMatch = new MatchData(int.Parse(matchData[0]), matchData[1], matchData[2]);
for (int i = 1; i < activeMatchData.Length; i++)
{
string[] moveData = activeMatchData[i].Split(',');
PreviousMoves.Add((moveData[0], int.Parse(moveData[1])));
}
}
OnWatchGameAck?.Invoke();
break;
default:
@@ -476,18 +580,41 @@ public partial class Connection : Node
}
}
private void HandleErrorMessage(string body)
public void ShowTournamentScoreboard(List<(string, int)> playerScoreboard)
{
if (string.IsNullOrWhiteSpace(body))
var scoreboardWindow = new Window();
scoreboardWindow.Theme = GD.Load<Theme>("res://assets/theme.tres");
scoreboardWindow.AlwaysOnTop = true;
scoreboardWindow.MaximizeDisabled = true;
scoreboardWindow.Unresizable = true;
scoreboardWindow.InitialPosition = Window.WindowInitialPosition.CenterMainWindowScreen;
scoreboardWindow.Size = new Vector2I(256, 512);
scoreboardWindow.CloseRequested += () =>
{
OnError?.Invoke("UNKNOWN", string.Empty);
return;
GetTree().Root.RemoveChild(scoreboardWindow);
};
var tree = new Tree();
tree.HideRoot = true;
tree.Columns = 2;
tree.ColumnTitlesVisible = true;
tree.Theme = GD.Load<Theme>("res://assets/theme.tres");
tree.GrowHorizontal = Control.GrowDirection.Both;
tree.GrowVertical = Control.GrowDirection.Both;
tree.SetColumnTitle(0, "Player");
tree.SetColumnTitle(1, "Score");
var root = tree.CreateItem();
foreach ((string, int) entry in playerScoreboard)
{
var item = tree.CreateItem(root);
item.SetText(0, entry.Item1);
item.SetText(1, entry.Item2.ToString());
}
string[] segments = body.Split(':');
string code = segments.Length > 0 ? segments[0].Trim().ToUpperInvariant() : "UNKNOWN";
string detail = segments.Length > 1 ? string.Join(":", segments, 1, segments.Length - 1).Trim() : string.Empty;
scoreboardWindow.AddChild(tree);
tree.SetAnchorsPreset(Control.LayoutPreset.FullRect);
OnError?.Invoke(code, detail);
GetTree().Root.AddChild(scoreboardWindow);
}
}

View File

@@ -0,0 +1,8 @@
extends Node
func _ready() -> void:
var music = AudioStreamPlayer.new()
add_child(music)
music.stream = load("res://assets/music/jazz_music.mp3")
music.volume_db = -10
music.play()

View File

@@ -0,0 +1 @@
uid://hh10ct26xx1b

13
scripts/chip_sfx.gd Normal file
View File

@@ -0,0 +1,13 @@
extends RigidBody2D
@export var audio_stream_player_2d: AudioStreamPlayer2D
@export var velocity_for_sfx: float = 2.0
@export var sounds: Array[AudioStream]
func _on_body_entered(body: Node) -> void:
if body.name.begins_with("@RigidBody2D@") || body.name.begins_with("Floor"):
var rng = RandomNumberGenerator.new()
rng.randomize()
var index = rng.randi_range(0, sounds.size() - 1)
audio_stream_player_2d.stream = sounds[index]
audio_stream_player_2d.play();

View File

@@ -10,5 +10,5 @@ func _ready() -> void:
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
func _process(_delta: float) -> void:
pass

View File

@@ -1,12 +0,0 @@
extends RigidBody2D
var color_ = "red";
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
pass # Replace with function body.
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(_delta: float) -> void:
pass

10
scripts/thinking.gd Normal file
View File

@@ -0,0 +1,10 @@
extends Label
@export var hue = 0.0
func _process(delta):
modulate = Color.from_hsv(hue, 1.0, 1.0, 1.0)
if hue < 1.0:
hue += 0.1 * delta
else:
hue = 0.0

1
scripts/thinking.gd.uid Normal file
View File

@@ -0,0 +1 @@
uid://cwfg17tdbk44b

View File

@@ -1,12 +0,0 @@
extends RigidBody2D
var color_ = "yellow";
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
pass # Replace with function body.
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(_delta: float) -> void:
pass

View File

@@ -1 +0,0 @@
uid://ddg7dv686sbrb