├── .editorconfig ├── .gitattributes ├── .gitignore ├── Artifacts ├── .gdignore ├── LeonardoAI │ ├── .gdignore │ ├── Leonardo_Creative_grid_of_bones_and_shadows_representing_dead_zombies_black_an_0.jpg │ ├── Leonardo_Creative_grid_of_bones_and_shadows_representing_dead_zombies_black_an_1.jpg │ ├── Leonardo_Select_grid_of_bones_and_shadows_representing_dead_zombies_black_an_0.jpg │ ├── Leonardo_Select_grid_of_bones_and_shadows_representing_dead_zombies_black_an_1.jpg │ ├── Paper_art_style_a_diorama_of_an_igloo_on_a_snowy_mountain_for_a_presentatio_1.jpg │ ├── Paper_art_style_a_set_of_game_assets_with_different_zombie_top_down_bodies_1.jpg │ ├── Paper_art_style_a_set_of_game_assets_with_different_zombie_top_down_bodies_3.jpg │ ├── Paper_art_style_front_facing_skull_burning_angry_mean_0.jpg │ ├── Paper_art_style_front_facing_skull_burning_angry_mean_2 (1).jpg │ ├── Paper_art_style_front_facing_skull_burning_angry_mean_2.jpg │ ├── Pixel_Art_burning_skull_scale_to_fit_frame_distopian_transparent_backg_0.jpg │ ├── Pixel_Art_burning_skull_scale_to_fit_frame_distopian_transparent_backg_2.jpg │ └── Pixel_Art_burning_skull_scale_to_fit_frame_distopian_transparent_backg_3.jpg ├── Midjourney │ ├── .gdignore │ ├── skull_burning.png │ └── skull_burning64x101.png └── burn-title.png ├── Game ├── Assets │ ├── Backgrounds │ │ ├── Background01.jpg │ │ ├── Background02.jpg │ │ ├── Background03.jpg │ │ ├── Background04.jpg │ │ └── Paper_art_style_a_diorama_of_an_igloo_on_a_snowy_mountain_for_a_presentatio_1.png │ ├── Explosions │ │ ├── ParticleExplosion │ │ │ ├── frame-001.png │ │ │ ├── frame-002.png │ │ │ ├── frame-003.png │ │ │ ├── frame-004.png │ │ │ ├── frame-005.png │ │ │ ├── frame-006.png │ │ │ ├── frame-007.png │ │ │ ├── frame-008.png │ │ │ ├── frame-009.png │ │ │ ├── frame-010.png │ │ │ ├── frame-011.png │ │ │ ├── frame-012.png │ │ │ ├── frame-013.png │ │ │ ├── frame-014.png │ │ │ ├── frame-015.png │ │ │ ├── frame-016.png │ │ │ ├── frame-017.png │ │ │ ├── frame-018.png │ │ │ ├── frame-019.png │ │ │ ├── frame-020.png │ │ │ ├── frame-021.png │ │ │ ├── frame-022.png │ │ │ ├── frame-023.png │ │ │ ├── frame-024.png │ │ │ ├── frame-025.png │ │ │ ├── frame-026.png │ │ │ ├── frame-027.png │ │ │ ├── frame-028.png │ │ │ ├── frame-029.png │ │ │ ├── frame-030.png │ │ │ ├── frame-031.png │ │ │ ├── frame-032.png │ │ │ ├── frame-033.png │ │ │ ├── frame-034.png │ │ │ ├── frame-035.png │ │ │ ├── frame-036.png │ │ │ ├── frame-037.png │ │ │ ├── frame-038.png │ │ │ ├── frame-039.png │ │ │ ├── frame-040.png │ │ │ ├── frame-041.png │ │ │ ├── frame-042.png │ │ │ ├── frame-043.png │ │ │ ├── frame-044.png │ │ │ ├── frame-045.png │ │ │ ├── frame-046.png │ │ │ ├── frame-047.png │ │ │ ├── frame-048.png │ │ │ ├── frame-049.png │ │ │ ├── frame-050.png │ │ │ ├── frame-051.png │ │ │ ├── frame-052.png │ │ │ ├── frame-053.png │ │ │ ├── frame-054.png │ │ │ ├── frame-055.png │ │ │ ├── frame-056.png │ │ │ ├── frame-057.png │ │ │ ├── frame-058.png │ │ │ ├── frame-059.png │ │ │ ├── frame-060.png │ │ │ ├── frame-061.png │ │ │ ├── frame-062.png │ │ │ ├── frame-063.png │ │ │ ├── frame-064.png │ │ │ ├── frame-065.png │ │ │ ├── frame-066.png │ │ │ ├── frame-067.png │ │ │ ├── frame-068.png │ │ │ ├── frame-069.png │ │ │ ├── frame-070.png │ │ │ ├── frame-071.png │ │ │ ├── frame-072.png │ │ │ ├── frame-073.png │ │ │ ├── frame-074.png │ │ │ ├── frame-075.png │ │ │ ├── frame-076.png │ │ │ ├── frame-077.png │ │ │ ├── frame-078.png │ │ │ ├── frame-079.png │ │ │ ├── frame-080.png │ │ │ ├── frame-081.png │ │ │ ├── frame-082.png │ │ │ ├── frame-083.png │ │ │ ├── frame-084.png │ │ │ ├── frame-085.png │ │ │ ├── frame-086.png │ │ │ ├── frame-087.png │ │ │ ├── frame-088.png │ │ │ ├── frame-089.png │ │ │ ├── frame-090.png │ │ │ ├── frame-091.png │ │ │ ├── frame-092.png │ │ │ ├── frame-093.png │ │ │ ├── frame-094.png │ │ │ ├── frame-095.png │ │ │ ├── frame-096.png │ │ │ ├── frame-097.png │ │ │ ├── frame-098.png │ │ │ ├── frame-099.png │ │ │ ├── frame-100.png │ │ │ ├── frame-101.png │ │ │ ├── frame-102.png │ │ │ ├── frame-103.png │ │ │ ├── frame-104.png │ │ │ └── frame-105.png │ │ └── explosion-hd-ss.png │ ├── Fire 2.png │ ├── Fire.png │ ├── Skulls │ │ ├── skull1.png │ │ └── skull2.png │ ├── Sounds │ │ ├── Ambience_Bonfire_Loop.wav │ │ ├── Hit_HardStone_03.wav │ │ ├── Land01.wav │ │ ├── SND36351.wav │ │ ├── chipsHandle3.ogg │ │ └── switch16.ogg │ ├── Zombie │ │ ├── 1.png │ │ └── 2.png │ ├── fireball-logo-5.png │ ├── fireball-logo-7.png │ └── fireball-logo-8.png ├── Autoload │ ├── GameEvents.cs │ ├── GameEvents.tscn │ ├── Global.cs │ ├── Global.tscn │ └── RsPreloader.tscn ├── Component │ ├── BaseComponent.cs │ ├── Element │ │ ├── ElementComponent.cs │ │ ├── FireComponent.cs │ │ └── FireComponent.tscn │ ├── ExplosionComponent.cs │ ├── ExplosionComponent.tscn │ ├── FacingComponent.cs │ ├── FacingComponent.tscn │ ├── Follow │ │ ├── FollowMouseComponent.cs │ │ ├── FollowMouseComponent.tscn │ │ ├── FollowPathComponent.cs │ │ ├── FollowPathComponent.tscn │ │ ├── FollowPlayerComponent.cs │ │ ├── FollowPlayerComponent.tscn │ │ └── IFollowComponent.cs │ ├── HealthComponent.cs │ ├── HealthComponent.tscn │ ├── QueueFreeComponent.cs │ ├── QueueFreeComponent.tscn │ ├── ScoreAttractorComponent.cs │ ├── ScoreAttractorComponent.tscn │ ├── VelocityComponent.cs │ └── VelocityComponent.tscn ├── Entity │ ├── Enemy │ │ ├── BaseEnemy.cs │ │ ├── Zombie.cs │ │ └── Zombie.tscn │ ├── Fireball.cs │ └── Fireball.tscn ├── Extension │ └── NodeExtension.cs ├── Helpers │ └── PointGenerator.cs ├── Main.cs ├── Main.tscn ├── Manager │ ├── FxManager.cs │ ├── FxManager.tscn │ ├── SoundManager.cs │ └── SoundManager.tscn └── UI │ ├── Fps.cs │ ├── Fps.tscn │ ├── RateStats.cs │ ├── Score.cs │ └── ZombieCounter.cs ├── GodotUtilities ├── ChildNodeAttribute.cs ├── Collections │ └── DoubleDictionary.cs ├── Extension │ ├── AudioStreamPlayerExtension.cs │ ├── ControlExtension.cs │ ├── Node2DExtension.cs │ ├── NodeExtension.cs │ ├── PackedSceneExtension.cs │ ├── Particles2DExtension.cs │ ├── Physics2DDirectSpaceStateExtension.cs │ ├── ResourcePreloaderExtension.cs │ ├── SceneTreeExtension.cs │ └── VectorExtension.cs ├── Logic │ ├── DelegateStateMachine.cs │ ├── ImmediateStateMachine.cs │ ├── LootTable.cs │ └── StateMachine.cs ├── ParentNodeAttribute.cs ├── ProjectSettingsExtended.cs └── Util │ ├── FileSystem.cs │ ├── Logger.cs │ ├── MathUtil.cs │ ├── RaycastResult.cs │ └── ShapecastResult.cs ├── LICENSE ├── README.md ├── SampleGodotCSharpProject.csproj ├── SampleGodotCSharpProject.sln ├── Screenshots ├── .gdignore ├── game-screenshot.png ├── godot-editor-layout.png ├── jetbrains-rider-layout.png ├── splash-screen.png └── vscode-formatting-settings.png ├── ScriptTemplates └── .gdignore ├── Videos └── .gdignore ├── default_bus_layout.tres ├── icon.svg ├── nuget.config └── project.godot /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | charset = utf-8 3 | end_of_line = lf 4 | indent_size = 4 5 | indent_style = tab 6 | insert_final_newline = true 7 | max_line_length = 120 8 | tab_width = 4 9 | trim_trailing_whitespace = true 10 | 11 | 12 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Normalize EOL for all files that Git considers text files. 2 | * text=auto eol=lf 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .import/ 2 | export.cfg 3 | export_presets.cfg 4 | 5 | # Mono-specific ignores 6 | .mono/ 7 | data_*/ 8 | 9 | .DS_Store 10 | # Godot 4+ specific ignores 11 | .godot/ 12 | 13 | .vs/ 14 | .idea/ 15 | .vscode/ 16 | 17 | #GodotUtilities - include Firebelley's code into this project for easier consumption. 18 | *.import 19 | *.user 20 | 21 | -------------------------------------------------------------------------------- /Artifacts/.gdignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Artifacts/.gdignore -------------------------------------------------------------------------------- /Artifacts/LeonardoAI/.gdignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Artifacts/LeonardoAI/.gdignore -------------------------------------------------------------------------------- /Artifacts/LeonardoAI/Leonardo_Creative_grid_of_bones_and_shadows_representing_dead_zombies_black_an_0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Artifacts/LeonardoAI/Leonardo_Creative_grid_of_bones_and_shadows_representing_dead_zombies_black_an_0.jpg -------------------------------------------------------------------------------- /Artifacts/LeonardoAI/Leonardo_Creative_grid_of_bones_and_shadows_representing_dead_zombies_black_an_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Artifacts/LeonardoAI/Leonardo_Creative_grid_of_bones_and_shadows_representing_dead_zombies_black_an_1.jpg -------------------------------------------------------------------------------- /Artifacts/LeonardoAI/Leonardo_Select_grid_of_bones_and_shadows_representing_dead_zombies_black_an_0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Artifacts/LeonardoAI/Leonardo_Select_grid_of_bones_and_shadows_representing_dead_zombies_black_an_0.jpg -------------------------------------------------------------------------------- /Artifacts/LeonardoAI/Leonardo_Select_grid_of_bones_and_shadows_representing_dead_zombies_black_an_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Artifacts/LeonardoAI/Leonardo_Select_grid_of_bones_and_shadows_representing_dead_zombies_black_an_1.jpg -------------------------------------------------------------------------------- /Artifacts/LeonardoAI/Paper_art_style_a_diorama_of_an_igloo_on_a_snowy_mountain_for_a_presentatio_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Artifacts/LeonardoAI/Paper_art_style_a_diorama_of_an_igloo_on_a_snowy_mountain_for_a_presentatio_1.jpg -------------------------------------------------------------------------------- /Artifacts/LeonardoAI/Paper_art_style_a_set_of_game_assets_with_different_zombie_top_down_bodies_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Artifacts/LeonardoAI/Paper_art_style_a_set_of_game_assets_with_different_zombie_top_down_bodies_1.jpg -------------------------------------------------------------------------------- /Artifacts/LeonardoAI/Paper_art_style_a_set_of_game_assets_with_different_zombie_top_down_bodies_3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Artifacts/LeonardoAI/Paper_art_style_a_set_of_game_assets_with_different_zombie_top_down_bodies_3.jpg -------------------------------------------------------------------------------- /Artifacts/LeonardoAI/Paper_art_style_front_facing_skull_burning_angry_mean_0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Artifacts/LeonardoAI/Paper_art_style_front_facing_skull_burning_angry_mean_0.jpg -------------------------------------------------------------------------------- /Artifacts/LeonardoAI/Paper_art_style_front_facing_skull_burning_angry_mean_2 (1).jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Artifacts/LeonardoAI/Paper_art_style_front_facing_skull_burning_angry_mean_2 (1).jpg -------------------------------------------------------------------------------- /Artifacts/LeonardoAI/Paper_art_style_front_facing_skull_burning_angry_mean_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Artifacts/LeonardoAI/Paper_art_style_front_facing_skull_burning_angry_mean_2.jpg -------------------------------------------------------------------------------- /Artifacts/LeonardoAI/Pixel_Art_burning_skull_scale_to_fit_frame_distopian_transparent_backg_0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Artifacts/LeonardoAI/Pixel_Art_burning_skull_scale_to_fit_frame_distopian_transparent_backg_0.jpg -------------------------------------------------------------------------------- /Artifacts/LeonardoAI/Pixel_Art_burning_skull_scale_to_fit_frame_distopian_transparent_backg_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Artifacts/LeonardoAI/Pixel_Art_burning_skull_scale_to_fit_frame_distopian_transparent_backg_2.jpg -------------------------------------------------------------------------------- /Artifacts/LeonardoAI/Pixel_Art_burning_skull_scale_to_fit_frame_distopian_transparent_backg_3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Artifacts/LeonardoAI/Pixel_Art_burning_skull_scale_to_fit_frame_distopian_transparent_backg_3.jpg -------------------------------------------------------------------------------- /Artifacts/Midjourney/.gdignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Artifacts/Midjourney/.gdignore -------------------------------------------------------------------------------- /Artifacts/Midjourney/skull_burning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Artifacts/Midjourney/skull_burning.png -------------------------------------------------------------------------------- /Artifacts/Midjourney/skull_burning64x101.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Artifacts/Midjourney/skull_burning64x101.png -------------------------------------------------------------------------------- /Artifacts/burn-title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Artifacts/burn-title.png -------------------------------------------------------------------------------- /Game/Assets/Backgrounds/Background01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Backgrounds/Background01.jpg -------------------------------------------------------------------------------- /Game/Assets/Backgrounds/Background02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Backgrounds/Background02.jpg -------------------------------------------------------------------------------- /Game/Assets/Backgrounds/Background03.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Backgrounds/Background03.jpg -------------------------------------------------------------------------------- /Game/Assets/Backgrounds/Background04.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Backgrounds/Background04.jpg -------------------------------------------------------------------------------- /Game/Assets/Backgrounds/Paper_art_style_a_diorama_of_an_igloo_on_a_snowy_mountain_for_a_presentatio_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Backgrounds/Paper_art_style_a_diorama_of_an_igloo_on_a_snowy_mountain_for_a_presentatio_1.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-001.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-002.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-003.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-003.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-004.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-004.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-005.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-005.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-006.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-006.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-007.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-007.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-008.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-008.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-009.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-009.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-010.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-010.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-011.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-011.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-012.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-012.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-013.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-013.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-014.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-014.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-015.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-015.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-016.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-016.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-017.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-017.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-018.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-018.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-019.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-019.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-020.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-020.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-021.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-021.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-022.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-022.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-023.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-023.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-024.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-025.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-025.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-026.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-026.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-027.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-027.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-028.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-028.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-029.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-029.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-030.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-030.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-031.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-031.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-032.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-032.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-033.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-033.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-034.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-034.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-035.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-035.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-036.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-036.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-037.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-037.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-038.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-038.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-039.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-039.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-040.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-040.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-041.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-041.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-042.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-042.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-043.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-043.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-044.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-044.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-045.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-045.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-046.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-046.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-047.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-047.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-048.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-048.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-049.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-049.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-050.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-050.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-051.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-051.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-052.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-052.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-053.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-053.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-054.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-054.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-055.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-055.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-056.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-056.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-057.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-057.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-058.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-058.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-059.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-059.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-060.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-060.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-061.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-061.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-062.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-062.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-063.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-063.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-064.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-064.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-065.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-065.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-066.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-066.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-067.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-067.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-068.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-068.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-069.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-069.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-070.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-070.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-071.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-071.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-072.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-072.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-073.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-073.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-074.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-074.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-075.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-075.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-076.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-076.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-077.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-077.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-078.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-078.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-079.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-079.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-080.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-080.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-081.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-081.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-082.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-082.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-083.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-083.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-084.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-084.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-085.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-085.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-086.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-086.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-087.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-087.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-088.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-088.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-089.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-089.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-090.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-090.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-091.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-091.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-092.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-092.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-093.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-093.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-094.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-094.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-095.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-095.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-096.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-096.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-097.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-097.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-098.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-098.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-099.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-099.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-100.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-101.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-101.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-102.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-102.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-103.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-103.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-104.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-104.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/ParticleExplosion/frame-105.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/ParticleExplosion/frame-105.png -------------------------------------------------------------------------------- /Game/Assets/Explosions/explosion-hd-ss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Explosions/explosion-hd-ss.png -------------------------------------------------------------------------------- /Game/Assets/Fire 2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Fire 2.png -------------------------------------------------------------------------------- /Game/Assets/Fire.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Fire.png -------------------------------------------------------------------------------- /Game/Assets/Skulls/skull1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Skulls/skull1.png -------------------------------------------------------------------------------- /Game/Assets/Skulls/skull2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Skulls/skull2.png -------------------------------------------------------------------------------- /Game/Assets/Sounds/Ambience_Bonfire_Loop.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Sounds/Ambience_Bonfire_Loop.wav -------------------------------------------------------------------------------- /Game/Assets/Sounds/Hit_HardStone_03.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Sounds/Hit_HardStone_03.wav -------------------------------------------------------------------------------- /Game/Assets/Sounds/Land01.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Sounds/Land01.wav -------------------------------------------------------------------------------- /Game/Assets/Sounds/SND36351.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Sounds/SND36351.wav -------------------------------------------------------------------------------- /Game/Assets/Sounds/chipsHandle3.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Sounds/chipsHandle3.ogg -------------------------------------------------------------------------------- /Game/Assets/Sounds/switch16.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Sounds/switch16.ogg -------------------------------------------------------------------------------- /Game/Assets/Zombie/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Zombie/1.png -------------------------------------------------------------------------------- /Game/Assets/Zombie/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/Zombie/2.png -------------------------------------------------------------------------------- /Game/Assets/fireball-logo-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/fireball-logo-5.png -------------------------------------------------------------------------------- /Game/Assets/fireball-logo-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/fireball-logo-7.png -------------------------------------------------------------------------------- /Game/Assets/fireball-logo-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Game/Assets/fireball-logo-8.png -------------------------------------------------------------------------------- /Game/Autoload/GameEvents.cs: -------------------------------------------------------------------------------- 1 | namespace Game.Autoload; 2 | 3 | using Game.Component.Element; 4 | using Game.Entity.Enemy; 5 | 6 | public partial class GameEvents : Node 7 | { 8 | public static GameEvents Instance { get; private set; } 9 | 10 | [Signal] 11 | public delegate void CollisionEventHandler(KinematicCollision2D collision2D); 12 | 13 | [Signal] 14 | public delegate void ElementIntensityDepletedEventHandler(ElementComponent element); 15 | 16 | [Signal] 17 | public delegate void ElementIntensityMaxedEventHandler(ElementComponent element); 18 | 19 | [Signal] 20 | public delegate void ZombieKilledEventHandler(Zombie zombie); 21 | 22 | [Signal] 23 | public delegate void ZombieSpawnedEventHandler(Zombie zombie); 24 | 25 | [Signal] 26 | public delegate void PlayerHitEventHandler(Node2D player, float angle, Vector2 direction); 27 | 28 | public static void EmitElementIntensityMaxed(ElementComponent element) => 29 | Instance.EmitSignal(SignalName.ElementIntensityMaxed, element); 30 | 31 | public static void EmitElementIntensityDepleted(ElementComponent element) => 32 | Instance.EmitSignal(SignalName.ElementIntensityDepleted, element); 33 | 34 | public static void EmitZombieKilled(Zombie zombie) => 35 | Instance.EmitSignal(SignalName.ZombieKilled, zombie); 36 | 37 | public static void EmitCollision(KinematicCollision2D collision2D) => 38 | Instance.EmitSignal(SignalName.Collision, collision2D); 39 | 40 | public static void EmitSpawnZombie(Zombie zombie) => 41 | Instance.EmitSignal(SignalName.ZombieSpawned, zombie); 42 | 43 | public static void EmitPlayerHit(Node2D player, float angle, Vector2 direction) => 44 | Instance.EmitSignal(SignalName.PlayerHit, player, angle, direction); 45 | 46 | public override void _Notification(int what) 47 | { 48 | if (what != NotificationEnterTree) return; 49 | 50 | Instance = this; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Game/Autoload/GameEvents.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://jfk3t6qfa71w"] 2 | 3 | [ext_resource type="Script" path="res://Game/Autoload/GameEvents.cs" id="1_xc6uy"] 4 | 5 | [node name="GameEvents" type="Node"] 6 | script = ExtResource("1_xc6uy") 7 | -------------------------------------------------------------------------------- /Game/Autoload/Global.cs: -------------------------------------------------------------------------------- 1 | global using Godot; 2 | global using GodotUtilities; 3 | global using GodotUtilities.Logic; 4 | global using System; 5 | 6 | namespace Game.Autoload; 7 | 8 | public partial class Global : Node 9 | { 10 | public Camera2D Camera2D; 11 | public CanvasLayer Hud; 12 | 13 | public static Global Instance { get; private set; } 14 | 15 | public override void _Notification(int what) 16 | { 17 | if (what == NotificationEnterTree) Instance = this; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Game/Autoload/Global.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://3i6xhxajjng5"] 2 | 3 | [ext_resource type="Script" path="res://Game/Autoload/Global.cs" id="1_5nmbi"] 4 | 5 | [node name="Global" type="Node"] 6 | script = ExtResource("1_5nmbi") 7 | -------------------------------------------------------------------------------- /Game/Autoload/RsPreloader.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=9 format=3 uid="uid://bkkwqgne0628c"] 2 | 3 | [ext_resource type="PackedScene" uid="uid://bdyiyimjj56sf" path="res://Game/Component/ExplosionComponent.tscn" id="2_24y3x"] 4 | [ext_resource type="PackedScene" uid="uid://bxqxjytua5jb2" path="res://Game/Component/FacingComponent.tscn" id="2_768rq"] 5 | [ext_resource type="PackedScene" uid="uid://b6bslvhpbudfc" path="res://Game/Component/Element/FireComponent.tscn" id="2_e2rwb"] 6 | [ext_resource type="PackedScene" uid="uid://h6cpojqhxl87" path="res://Game/Entity/Fireball.tscn" id="3_s3qei"] 7 | [ext_resource type="PackedScene" path="res://Game/Component/Follow/FollowMouseComponent.tscn" id="5_hxvkl"] 8 | [ext_resource type="PackedScene" uid="uid://dfcjblk01lnsf" path="res://Game/Component/VelocityComponent.tscn" id="5_jc15n"] 9 | [ext_resource type="PackedScene" uid="uid://cjalmyc66a4bh" path="res://Game/Entity/Enemy/Zombie.tscn" id="6_3vm86"] 10 | [ext_resource type="PackedScene" uid="uid://dgvrlux56u8r" path="res://Game/Component/ScoreAttractorComponent.tscn" id="6_n6wy4"] 11 | 12 | [node name="RsPreloader" type="ResourcePreloader"] 13 | resources = [PackedStringArray("ExplosionComponent", "FacingComponent", "FireComponent", "Fireball", "FollowMouseComponent", "ScoreAttractorComponent", "VelocityComponent", "Zombie"), [ExtResource("2_24y3x"), ExtResource("2_768rq"), ExtResource("2_e2rwb"), ExtResource("3_s3qei"), ExtResource("5_hxvkl"), ExtResource("6_n6wy4"), ExtResource("5_jc15n"), ExtResource("6_3vm86")]] 14 | -------------------------------------------------------------------------------- /Game/Component/BaseComponent.cs: -------------------------------------------------------------------------------- 1 | namespace Game.Component; 2 | 3 | public partial class BaseComponent : Node2D 4 | { 5 | private bool _enabled = true; 6 | 7 | [Export] 8 | public bool Enabled 9 | { 10 | set => SetEnabled(value); 11 | get => _enabled; 12 | } 13 | 14 | protected virtual void _EnabledPostProcess() 15 | { 16 | } 17 | 18 | public void SetEnabled(bool enabled) 19 | { 20 | _enabled = enabled; 21 | _EnabledPostProcess(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Game/Component/Element/ElementComponent.cs: -------------------------------------------------------------------------------- 1 | namespace Game.Component.Element; 2 | 3 | public partial class ElementComponent : BaseComponent 4 | { 5 | [Signal] 6 | public delegate void IntensityDepletedEventHandler(ElementComponent element); 7 | 8 | [Signal] 9 | public delegate void IntensityMaxedEventHandler(ElementComponent element); 10 | 11 | protected float Energy; 12 | 13 | public virtual float AddEnergy(float factor, bool emitSignals = true) 14 | { 15 | return 0.0f; 16 | } 17 | 18 | public virtual float SetEnergy(float energy, bool emitSignals = true) 19 | { 20 | var oldEnergy = Energy; 21 | Energy = Mathf.Clamp(energy, 0f, 1f); 22 | return oldEnergy; 23 | } 24 | 25 | public float GetEnergy() => Energy; 26 | } 27 | -------------------------------------------------------------------------------- /Game/Component/Element/FireComponent.cs: -------------------------------------------------------------------------------- 1 | namespace Game.Component.Element; 2 | 3 | using Game.Autoload; 4 | using Game.Extension; 5 | 6 | public partial class FireComponent : ElementComponent 7 | { 8 | private HealthComponent _healthComponent; 9 | private Node2D _parent; 10 | 11 | private bool _wait; 12 | 13 | [Node] 14 | public Area2D CatchFireZone; 15 | 16 | [Node] 17 | public CollisionShape2D CatchFireZoneCollision; 18 | 19 | [Export] 20 | public float DepletionRate = -0.03f; 21 | 22 | [Node] 23 | public Timer DepletionTimer; 24 | 25 | [Export] 26 | public float EnergyLossPercent = -10.0f; 27 | 28 | [Export] 29 | public float EnergyTransferPercent = 10.0f; 30 | 31 | [Node] 32 | public CpuParticles2D FireParticles; 33 | 34 | [Node] 35 | public Timer HealthDamageTimer; 36 | 37 | [Node] 38 | public Label Label; 39 | 40 | [Export] 41 | public float MaxHealthDamage = 0.05f; 42 | 43 | [Node] 44 | public Node2D Visuals; 45 | 46 | public override void _EnterTree() 47 | { 48 | this.WireNodes(); 49 | } 50 | 51 | public override void _Ready() 52 | { 53 | DepletionTimer.Timeout += () => { AddEnergy(DepletionRate); }; 54 | HealthDamageTimer.Timeout += _InflictDamage; 55 | 56 | CatchFireZone.BodyEntered += _EnteredCatchFireZone; 57 | 58 | _parent = GetParent() as Node2D; 59 | } 60 | 61 | private void _EnteredCatchFireZone(Node2D body) 62 | { 63 | _ApplyFire(body); 64 | } 65 | 66 | private void _ApplyFire(Node body) 67 | { 68 | // _wait here guards against a race condition whereby getting the first node returns 69 | // null even though the fire component has been added through a deferred call. 70 | if (_wait || !Enabled) return; 71 | 72 | var fireComponent = body.GetFirstNodeOfType(); 73 | 74 | if (fireComponent == null) 75 | { 76 | _wait = true; 77 | body.AddResourceDeferredWithAction(fc => 78 | { 79 | fc.SetEnergy(Energy * EnergyTransferPercent / 100f); 80 | _wait = false; 81 | }); 82 | } 83 | else 84 | { 85 | fireComponent.AddEnergy(EnergyTransferPercent / 100f); 86 | AddEnergy(EnergyLossPercent / 100f); 87 | } 88 | } 89 | 90 | public override float AddEnergy(float factor, bool emitSignals = true) 91 | { 92 | if (!Enabled) return Energy; 93 | 94 | var oldEnergy = SetEnergy(Energy + factor, emitSignals); 95 | return oldEnergy; 96 | } 97 | 98 | public override float SetEnergy(float energy, bool emitSignals = true) 99 | { 100 | if (!Enabled) return Energy; 101 | 102 | var result = base.SetEnergy(energy, emitSignals); 103 | 104 | _UpdateVisuals(); 105 | 106 | _CheckAndNotify(emitSignals); 107 | 108 | return result; 109 | } 110 | 111 | private void _UpdateVisuals() 112 | { 113 | if (Label.Visible) 114 | Label.Text = $"{Energy}"; 115 | Visuals.Modulate = new Color(3.0f * Energy, 2.0f * Energy, 2.0f * Energy); 116 | } 117 | 118 | private void _CheckAndNotify(bool emitSignals) 119 | { 120 | if (Energy > 0.0f) 121 | { 122 | if (DepletionTimer.IsInsideTree()) 123 | { 124 | DepletionTimer.Start(); 125 | FireParticles.Emitting = true; 126 | FireParticles.Lifetime = Energy; 127 | } 128 | } 129 | else 130 | { 131 | if (DepletionTimer.IsInsideTree()) 132 | { 133 | DepletionTimer.Stop(); 134 | FireParticles.Emitting = false; 135 | if (emitSignals) 136 | { 137 | EmitSignal(ElementComponent.SignalName.IntensityDepleted, this); 138 | GameEvents.EmitElementIntensityDepleted(this); 139 | } 140 | } 141 | } 142 | 143 | if (!(Energy >= 1.0f)) return; 144 | 145 | if (!emitSignals) return; 146 | EmitSignal(ElementComponent.SignalName.IntensityMaxed, this); 147 | GameEvents.EmitElementIntensityMaxed(this); 148 | } 149 | 150 | protected override void _EnabledPostProcess() 151 | { 152 | base._EnabledPostProcess(); 153 | if (Enabled) DepletionTimer.Start(); 154 | else DepletionTimer.Stop(); 155 | } 156 | 157 | private void _InflictDamage() 158 | { 159 | if (Energy > 0.0f) 160 | { 161 | _healthComponent ??= _parent.GetFirstNodeOfType(); 162 | _healthComponent?.DecreaseHealth(MaxHealthDamage * Energy); 163 | } 164 | } 165 | 166 | public override void _ExitTree() 167 | { 168 | if (Enabled) 169 | { 170 | EmitSignal(ElementComponent.SignalName.IntensityDepleted, null); 171 | GameEvents.EmitElementIntensityDepleted(null); 172 | } 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /Game/Component/Element/FireComponent.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=7 format=3 uid="uid://b6bslvhpbudfc"] 2 | 3 | [ext_resource type="Script" path="res://Game/Component/Element/FireComponent.cs" id="1_8js25"] 4 | [ext_resource type="Texture2D" uid="uid://b36bsoiftptpr" path="res://Game/Assets/Fire.png" id="2_ys0pb"] 5 | 6 | [sub_resource type="CircleShape2D" id="CircleShape2D_r4wc2"] 7 | radius = 103.005 8 | 9 | [sub_resource type="CanvasItemMaterial" id="CanvasItemMaterial_5hltt"] 10 | blend_mode = 1 11 | light_mode = 1 12 | particles_animation = true 13 | particles_anim_h_frames = 5 14 | particles_anim_v_frames = 1 15 | particles_anim_loop = true 16 | 17 | [sub_resource type="Curve" id="Curve_uwwdf"] 18 | _data = [Vector2(0, 0), 0.0, 3.54088, 0, 0, Vector2(0.554841, 1), -0.518227, -0.518227, 0, 0, Vector2(1, 0), -0.150039, 0.0, 0, 0] 19 | point_count = 3 20 | 21 | [sub_resource type="Gradient" id="Gradient_5gb4x"] 22 | offsets = PackedFloat32Array(0, 0.404826, 0.55496, 0.801609, 1) 23 | colors = PackedColorArray(3, 0, 0, 1, 1.4, 0, 0.03, 1, 2.6, 2.8, 0, 1, 0.201299, 0.119869, 0.0404145, 1, 0.2, 0.12, 0.04, 0.17) 24 | 25 | [node name="FireComponent" type="Node2D"] 26 | script = ExtResource("1_8js25") 27 | EnergyLossPercent = -5.0 28 | EnergyTransferPercent = 50.0 29 | 30 | [node name="DepletionTimer" type="Timer" parent="."] 31 | wait_time = 0.25 32 | autostart = true 33 | 34 | [node name="HealthDamageTimer" type="Timer" parent="."] 35 | wait_time = 0.25 36 | autostart = true 37 | 38 | [node name="CatchFireZone" type="Area2D" parent="."] 39 | collision_layer = 8 40 | monitorable = false 41 | 42 | [node name="CatchFireZoneCollision" type="CollisionShape2D" parent="CatchFireZone"] 43 | unique_name_in_owner = true 44 | shape = SubResource("CircleShape2D_r4wc2") 45 | disabled = true 46 | 47 | [node name="Visuals" type="Node2D" parent="."] 48 | 49 | [node name="FireParticles" type="CPUParticles2D" parent="Visuals"] 50 | unique_name_in_owner = true 51 | material = SubResource("CanvasItemMaterial_5hltt") 52 | position = Vector2(0, -32) 53 | amount = 24 54 | randomness = 1.0 55 | lifetime_randomness = 0.5 56 | texture = ExtResource("2_ys0pb") 57 | emission_shape = 3 58 | emission_rect_extents = Vector2(10, 1) 59 | direction = Vector2(0, 1) 60 | spread = 180.0 61 | gravity = Vector2(0, -300) 62 | initial_velocity_max = 5.0 63 | angular_velocity_min = -15.0 64 | angular_velocity_max = 15.0 65 | angle_min = -5.0 66 | angle_max = 5.0 67 | scale_amount_min = 0.0 68 | scale_amount_max = 0.4 69 | scale_amount_curve = SubResource("Curve_uwwdf") 70 | color_ramp = SubResource("Gradient_5gb4x") 71 | anim_speed_min = 1.0 72 | anim_speed_max = 7.0 73 | 74 | [node name="Label" type="Label" parent="Visuals"] 75 | unique_name_in_owner = true 76 | visible = false 77 | anchors_preset = 8 78 | anchor_left = 0.5 79 | anchor_top = 0.5 80 | anchor_right = 0.5 81 | anchor_bottom = 0.5 82 | offset_left = -58.0 83 | offset_top = 47.0 84 | offset_right = 58.0 85 | offset_bottom = 119.0 86 | grow_horizontal = 2 87 | grow_vertical = 2 88 | size_flags_horizontal = 4 89 | theme_override_font_sizes/font_size = 50 90 | text = "1234" 91 | horizontal_alignment = 1 92 | -------------------------------------------------------------------------------- /Game/Component/ExplosionComponent.cs: -------------------------------------------------------------------------------- 1 | namespace Game.Component; 2 | 3 | using Game.Autoload; 4 | using Game.Entity.Enemy; 5 | using Game.Manager; 6 | using Game.Extension; 7 | 8 | public partial class ExplosionComponent : BaseComponent 9 | { 10 | [Node] 11 | public AnimatedSprite2D AnimatedSprite2D; 12 | 13 | [Node] 14 | public Node2D Visuals; 15 | 16 | public override void _EnterTree() 17 | { 18 | this.WireNodes(); 19 | } 20 | 21 | public override void _Ready() 22 | { 23 | AnimatedSprite2D.AnimationFinished += QueueFree; 24 | 25 | Visuals.Rotation = (float)GD.Randfn(Mathf.Tau, Mathf.Pi); 26 | 27 | if (GetParent() is Zombie node) 28 | { 29 | node.ZIndex = 2; 30 | node.Modulate = this.IntensifyColor(Colors.DarkMagenta, 2.3f); 31 | } 32 | 33 | var shakeIntensity = _CalcShakeIntensity(); 34 | if (shakeIntensity <= float.Epsilon) return; 35 | 36 | FxManager.ShakeScreen(1f, 5 * shakeIntensity); 37 | } 38 | 39 | private float _CalcShakeIntensity() 40 | { 41 | var shakeIntensity = Mathf.Remap(GlobalPosition.DistanceTo(Global.Instance.Camera2D.GlobalPosition), 0, 500f, 1.0f, 0.0f); 42 | return shakeIntensity; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Game/Component/ExplosionComponent.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=48 format=3 uid="uid://bdyiyimjj56sf"] 2 | 3 | [ext_resource type="Script" path="res://Game/Component/ExplosionComponent.cs" id="1_bepyg"] 4 | [ext_resource type="Texture2D" uid="uid://b3utwf4qpy1vj" path="res://Game/Assets/Explosions/explosion-hd-ss.png" id="2_fgvcw"] 5 | [ext_resource type="Texture2D" uid="uid://dquuh87t8wwgd" path="res://Game/Assets/Explosions/ParticleExplosion/frame-002.png" id="3_0ea2h"] 6 | [ext_resource type="Texture2D" uid="uid://c3vpuo35d36rc" path="res://Game/Assets/Explosions/ParticleExplosion/frame-003.png" id="4_b0hlu"] 7 | [ext_resource type="Texture2D" uid="uid://p4ukiqsneei1" path="res://Game/Assets/Explosions/ParticleExplosion/frame-004.png" id="5_tc1od"] 8 | [ext_resource type="Texture2D" uid="uid://o334kgdxspd4" path="res://Game/Assets/Explosions/ParticleExplosion/frame-005.png" id="6_jg8f0"] 9 | [ext_resource type="Texture2D" uid="uid://df7p1t01erm61" path="res://Game/Assets/Explosions/ParticleExplosion/frame-006.png" id="7_h0wtn"] 10 | [ext_resource type="Texture2D" uid="uid://b5xejgs7lblec" path="res://Game/Assets/Explosions/ParticleExplosion/frame-007.png" id="8_oxgv5"] 11 | [ext_resource type="Texture2D" uid="uid://qkau37p30sps" path="res://Game/Assets/Explosions/ParticleExplosion/frame-008.png" id="9_ppmil"] 12 | [ext_resource type="Texture2D" uid="uid://uxdk3dsatkgx" path="res://Game/Assets/Explosions/ParticleExplosion/frame-009.png" id="10_tfe7f"] 13 | [ext_resource type="Texture2D" uid="uid://dayal2wb77kd7" path="res://Game/Assets/Explosions/ParticleExplosion/frame-010.png" id="11_d8jvu"] 14 | [ext_resource type="Texture2D" uid="uid://cx33jr44sfcov" path="res://Game/Assets/Explosions/ParticleExplosion/frame-011.png" id="12_ip6m6"] 15 | [ext_resource type="Texture2D" uid="uid://doy0catm3tywp" path="res://Game/Assets/Explosions/ParticleExplosion/frame-012.png" id="13_qohlq"] 16 | [ext_resource type="Texture2D" uid="uid://dluep26r3iuxo" path="res://Game/Assets/Explosions/ParticleExplosion/frame-013.png" id="14_2lkq5"] 17 | [ext_resource type="Texture2D" uid="uid://dkumpobpmv1cu" path="res://Game/Assets/Explosions/ParticleExplosion/frame-014.png" id="15_kauga"] 18 | [ext_resource type="Texture2D" uid="uid://dhkuryhjjibwk" path="res://Game/Assets/Explosions/ParticleExplosion/frame-015.png" id="16_jthjv"] 19 | [ext_resource type="Texture2D" uid="uid://cp6sy045kff4m" path="res://Game/Assets/Explosions/ParticleExplosion/frame-016.png" id="17_quenw"] 20 | [ext_resource type="Texture2D" uid="uid://b1jnj4kgax87p" path="res://Game/Assets/Explosions/ParticleExplosion/frame-017.png" id="18_048fr"] 21 | [ext_resource type="Texture2D" uid="uid://0gmwuy6pl6o1" path="res://Game/Assets/Explosions/ParticleExplosion/frame-018.png" id="19_f55l6"] 22 | [ext_resource type="Texture2D" uid="uid://diejm3nkgje58" path="res://Game/Assets/Explosions/ParticleExplosion/frame-019.png" id="20_xkqps"] 23 | [ext_resource type="Texture2D" uid="uid://b0ljx31l66nt" path="res://Game/Assets/Explosions/ParticleExplosion/frame-020.png" id="21_2ikk6"] 24 | [ext_resource type="Texture2D" uid="uid://bh2k4npssaajt" path="res://Game/Assets/Explosions/ParticleExplosion/frame-021.png" id="22_7txml"] 25 | [ext_resource type="Texture2D" uid="uid://4mfd0061j4n1" path="res://Game/Assets/Explosions/ParticleExplosion/frame-022.png" id="23_guyur"] 26 | [ext_resource type="Texture2D" uid="uid://bx8nbti6rmsp7" path="res://Game/Assets/Explosions/ParticleExplosion/frame-023.png" id="24_apuxg"] 27 | [ext_resource type="Texture2D" uid="uid://bs4ia3car3v2s" path="res://Game/Assets/Explosions/ParticleExplosion/frame-024.png" id="25_sh1le"] 28 | [ext_resource type="Texture2D" uid="uid://bnqlvd2ok3cnn" path="res://Game/Assets/Explosions/ParticleExplosion/frame-025.png" id="26_ku53u"] 29 | [ext_resource type="Texture2D" uid="uid://dxc5icfllvxk6" path="res://Game/Assets/Explosions/ParticleExplosion/frame-026.png" id="27_dyqcu"] 30 | [ext_resource type="Texture2D" uid="uid://dwtsq46g1xq8f" path="res://Game/Assets/Explosions/ParticleExplosion/frame-027.png" id="28_mnuis"] 31 | 32 | [sub_resource type="AtlasTexture" id="AtlasTexture_0k4qh"] 33 | atlas = ExtResource("2_fgvcw") 34 | region = Rect2(128, 0, 64, 64) 35 | 36 | [sub_resource type="AtlasTexture" id="AtlasTexture_1rt3x"] 37 | atlas = ExtResource("2_fgvcw") 38 | region = Rect2(192, 0, 64, 64) 39 | 40 | [sub_resource type="AtlasTexture" id="AtlasTexture_or6av"] 41 | atlas = ExtResource("2_fgvcw") 42 | region = Rect2(256, 0, 64, 64) 43 | 44 | [sub_resource type="AtlasTexture" id="AtlasTexture_pcwt0"] 45 | atlas = ExtResource("2_fgvcw") 46 | region = Rect2(320, 0, 64, 64) 47 | 48 | [sub_resource type="AtlasTexture" id="AtlasTexture_8t6rc"] 49 | atlas = ExtResource("2_fgvcw") 50 | region = Rect2(384, 0, 64, 64) 51 | 52 | [sub_resource type="AtlasTexture" id="AtlasTexture_jqhc3"] 53 | atlas = ExtResource("2_fgvcw") 54 | region = Rect2(448, 0, 64, 64) 55 | 56 | [sub_resource type="AtlasTexture" id="AtlasTexture_f5ll2"] 57 | atlas = ExtResource("2_fgvcw") 58 | region = Rect2(512, 0, 64, 64) 59 | 60 | [sub_resource type="AtlasTexture" id="AtlasTexture_q2sd3"] 61 | atlas = ExtResource("2_fgvcw") 62 | region = Rect2(576, 0, 64, 64) 63 | 64 | [sub_resource type="AtlasTexture" id="AtlasTexture_vdtjr"] 65 | atlas = ExtResource("2_fgvcw") 66 | region = Rect2(0, 64, 64, 64) 67 | 68 | [sub_resource type="AtlasTexture" id="AtlasTexture_wmfxv"] 69 | atlas = ExtResource("2_fgvcw") 70 | region = Rect2(64, 64, 64, 64) 71 | 72 | [sub_resource type="AtlasTexture" id="AtlasTexture_5mvsm"] 73 | atlas = ExtResource("2_fgvcw") 74 | region = Rect2(128, 64, 64, 64) 75 | 76 | [sub_resource type="AtlasTexture" id="AtlasTexture_08ajy"] 77 | atlas = ExtResource("2_fgvcw") 78 | region = Rect2(192, 64, 64, 64) 79 | 80 | [sub_resource type="AtlasTexture" id="AtlasTexture_quju3"] 81 | atlas = ExtResource("2_fgvcw") 82 | region = Rect2(256, 64, 64, 64) 83 | 84 | [sub_resource type="AtlasTexture" id="AtlasTexture_hdi7v"] 85 | atlas = ExtResource("2_fgvcw") 86 | region = Rect2(320, 64, 64, 64) 87 | 88 | [sub_resource type="AtlasTexture" id="AtlasTexture_pya83"] 89 | atlas = ExtResource("2_fgvcw") 90 | region = Rect2(576, 64, 64, 64) 91 | 92 | [sub_resource type="AtlasTexture" id="AtlasTexture_ohvqa"] 93 | atlas = ExtResource("2_fgvcw") 94 | region = Rect2(512, 64, 64, 64) 95 | 96 | [sub_resource type="AtlasTexture" id="AtlasTexture_unl3d"] 97 | atlas = ExtResource("2_fgvcw") 98 | region = Rect2(448, 64, 64, 64) 99 | 100 | [sub_resource type="AtlasTexture" id="AtlasTexture_lyfr8"] 101 | atlas = ExtResource("2_fgvcw") 102 | region = Rect2(384, 64, 64, 64) 103 | 104 | [sub_resource type="SpriteFrames" id="SpriteFrames_iu2w2"] 105 | animations = [{ 106 | "frames": [{ 107 | "duration": 1.0, 108 | "texture": SubResource("AtlasTexture_0k4qh") 109 | }, { 110 | "duration": 1.0, 111 | "texture": SubResource("AtlasTexture_1rt3x") 112 | }, { 113 | "duration": 1.0, 114 | "texture": SubResource("AtlasTexture_or6av") 115 | }, { 116 | "duration": 1.0, 117 | "texture": SubResource("AtlasTexture_pcwt0") 118 | }, { 119 | "duration": 1.0, 120 | "texture": SubResource("AtlasTexture_8t6rc") 121 | }, { 122 | "duration": 1.0, 123 | "texture": SubResource("AtlasTexture_jqhc3") 124 | }, { 125 | "duration": 1.0, 126 | "texture": SubResource("AtlasTexture_f5ll2") 127 | }, { 128 | "duration": 1.0, 129 | "texture": SubResource("AtlasTexture_q2sd3") 130 | }, { 131 | "duration": 1.0, 132 | "texture": SubResource("AtlasTexture_vdtjr") 133 | }, { 134 | "duration": 1.0, 135 | "texture": SubResource("AtlasTexture_wmfxv") 136 | }, { 137 | "duration": 1.0, 138 | "texture": SubResource("AtlasTexture_5mvsm") 139 | }, { 140 | "duration": 1.0, 141 | "texture": SubResource("AtlasTexture_08ajy") 142 | }, { 143 | "duration": 1.0, 144 | "texture": SubResource("AtlasTexture_quju3") 145 | }, { 146 | "duration": 1.0, 147 | "texture": SubResource("AtlasTexture_hdi7v") 148 | }, { 149 | "duration": 1.0, 150 | "texture": SubResource("AtlasTexture_pya83") 151 | }, { 152 | "duration": 1.0, 153 | "texture": SubResource("AtlasTexture_ohvqa") 154 | }, { 155 | "duration": 1.0, 156 | "texture": SubResource("AtlasTexture_unl3d") 157 | }, { 158 | "duration": 1.0, 159 | "texture": SubResource("AtlasTexture_lyfr8") 160 | }], 161 | "loop": false, 162 | "name": &"explode", 163 | "speed": 25.0 164 | }, { 165 | "frames": [{ 166 | "duration": 1.0, 167 | "texture": ExtResource("3_0ea2h") 168 | }, { 169 | "duration": 1.0, 170 | "texture": ExtResource("4_b0hlu") 171 | }, { 172 | "duration": 1.0, 173 | "texture": ExtResource("5_tc1od") 174 | }, { 175 | "duration": 1.0, 176 | "texture": ExtResource("6_jg8f0") 177 | }, { 178 | "duration": 1.0, 179 | "texture": ExtResource("7_h0wtn") 180 | }, { 181 | "duration": 1.0, 182 | "texture": ExtResource("8_oxgv5") 183 | }, { 184 | "duration": 1.0, 185 | "texture": ExtResource("9_ppmil") 186 | }, { 187 | "duration": 1.0, 188 | "texture": ExtResource("10_tfe7f") 189 | }, { 190 | "duration": 1.0, 191 | "texture": ExtResource("11_d8jvu") 192 | }, { 193 | "duration": 1.0, 194 | "texture": ExtResource("12_ip6m6") 195 | }, { 196 | "duration": 1.0, 197 | "texture": ExtResource("13_qohlq") 198 | }, { 199 | "duration": 1.0, 200 | "texture": ExtResource("14_2lkq5") 201 | }, { 202 | "duration": 1.0, 203 | "texture": ExtResource("15_kauga") 204 | }, { 205 | "duration": 1.0, 206 | "texture": ExtResource("16_jthjv") 207 | }, { 208 | "duration": 1.0, 209 | "texture": ExtResource("17_quenw") 210 | }, { 211 | "duration": 1.0, 212 | "texture": ExtResource("18_048fr") 213 | }, { 214 | "duration": 1.0, 215 | "texture": ExtResource("19_f55l6") 216 | }, { 217 | "duration": 1.0, 218 | "texture": ExtResource("20_xkqps") 219 | }, { 220 | "duration": 1.0, 221 | "texture": ExtResource("21_2ikk6") 222 | }, { 223 | "duration": 1.0, 224 | "texture": ExtResource("22_7txml") 225 | }, { 226 | "duration": 1.0, 227 | "texture": ExtResource("23_guyur") 228 | }, { 229 | "duration": 1.0, 230 | "texture": ExtResource("24_apuxg") 231 | }, { 232 | "duration": 1.0, 233 | "texture": ExtResource("25_sh1le") 234 | }, { 235 | "duration": 1.0, 236 | "texture": ExtResource("26_ku53u") 237 | }, { 238 | "duration": 1.0, 239 | "texture": ExtResource("27_dyqcu") 240 | }, { 241 | "duration": 1.0, 242 | "texture": ExtResource("28_mnuis") 243 | }], 244 | "loop": false, 245 | "name": &"particle", 246 | "speed": 30.0 247 | }] 248 | 249 | [node name="ExplosionComponent" type="Node2D"] 250 | z_index = 1 251 | script = ExtResource("1_bepyg") 252 | 253 | [node name="Visuals" type="Node2D" parent="."] 254 | modulate = Color(3, 0, 0, 1) 255 | scale = Vector2(1.5, 1.5) 256 | 257 | [node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="Visuals"] 258 | unique_name_in_owner = true 259 | sprite_frames = SubResource("SpriteFrames_iu2w2") 260 | animation = &"particle" 261 | autoplay = "particle" 262 | frame = 25 263 | frame_progress = 1.0 264 | -------------------------------------------------------------------------------- /Game/Component/FacingComponent.cs: -------------------------------------------------------------------------------- 1 | namespace Game.Component; 2 | 3 | public partial class FacingComponent : BaseComponent 4 | { 5 | [Export] 6 | public Node2D Node2DToRotate; 7 | 8 | [Export] 9 | public VelocityComponent VelocityComponent; 10 | 11 | public override void _PhysicsProcess(double delta) 12 | { 13 | if (!Enabled) return; 14 | 15 | Node2DToRotate.Rotation = VelocityComponent.Velocity.Normalized().Angle(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Game/Component/FacingComponent.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://bxqxjytua5jb2"] 2 | 3 | [ext_resource type="Script" path="res://Game/Component/FacingComponent.cs" id="1_mvcmg"] 4 | 5 | [node name="FacingComponent" type="Node2D"] 6 | script = ExtResource("1_mvcmg") 7 | -------------------------------------------------------------------------------- /Game/Component/Follow/FollowMouseComponent.cs: -------------------------------------------------------------------------------- 1 | namespace Game.Component; 2 | 3 | using Game.Component.Follow; 4 | 5 | public partial class FollowMouseComponent : BaseComponent, IFollowComponent 6 | { 7 | private PhysicsBody2D _parent; 8 | 9 | [Export] 10 | public float Speed = 30.0f; 11 | 12 | [Export] 13 | public VelocityComponent VelocityComponent; 14 | 15 | public override void _EnterTree() 16 | { 17 | this.WireNodes(); 18 | _parent = GetParent(); 19 | } 20 | 21 | public override void _PhysicsProcess(double delta) 22 | { 23 | if (!Enabled) return; 24 | 25 | var mouseDir = this.GetMouseDirection(); 26 | var distance = GlobalPosition.DistanceTo(GetGlobalMousePosition()); 27 | var newVelocity = mouseDir * Speed * distance; 28 | VelocityComponent.Velocity = newVelocity; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Game/Component/Follow/FollowMouseComponent.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3] 2 | 3 | [ext_resource type="Script" path="res://Game/Component/Follow/FollowMouseComponent.cs" id="1_dgwnx"] 4 | 5 | [node name="FollowMouseComponent" type="Node2D"] 6 | script = ExtResource("1_dgwnx") 7 | -------------------------------------------------------------------------------- /Game/Component/Follow/FollowPathComponent.cs: -------------------------------------------------------------------------------- 1 | namespace Game.Component; 2 | 3 | using Game.Component.Follow; 4 | 5 | public partial class FollowPathComponent : BaseComponent, IFollowComponent 6 | { 7 | [Export] 8 | public float FollowSpeed = 0.5f; 9 | 10 | [Export] 11 | public float PathSpeed = 0.1f; 12 | 13 | [Export] 14 | public VelocityComponent VelocityComponent; 15 | 16 | [Node] 17 | public PathFollow2D PathFollow2D; 18 | 19 | private Node2D _parent; 20 | 21 | public override void _EnterTree() 22 | { 23 | this.WireNodes(); 24 | _parent = GetParent(); 25 | } 26 | 27 | public override void _PhysicsProcess(double delta) 28 | { 29 | if (!Enabled) return; 30 | 31 | PathFollow2D.ProgressRatio += PathSpeed * (float)delta; 32 | 33 | var pathDir = _parent.GlobalPosition.DirectionTo(PathFollow2D.GlobalPosition); 34 | var distance = _parent.GlobalPosition.DistanceTo(PathFollow2D.GlobalPosition); 35 | var newVelocity = pathDir * FollowSpeed * distance; 36 | VelocityComponent.Velocity = newVelocity; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Game/Component/Follow/FollowPathComponent.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://blqe5s042oxxj"] 2 | 3 | [ext_resource type="Script" path="res://Game/Component/Follow/FollowPathComponent.cs" id="1_tcbeh"] 4 | 5 | [sub_resource type="Curve2D" id="Curve2D_tqggc"] 6 | 7 | [node name="FollowPathComponent" type="Node2D"] 8 | top_level = true 9 | script = ExtResource("1_tcbeh") 10 | 11 | [node name="Path2D" type="Path2D" parent="."] 12 | curve = SubResource("Curve2D_tqggc") 13 | 14 | [node name="PathFollow2D" type="PathFollow2D" parent="Path2D"] 15 | unique_name_in_owner = true 16 | rotates = false 17 | -------------------------------------------------------------------------------- /Game/Component/Follow/FollowPlayerComponent.cs: -------------------------------------------------------------------------------- 1 | namespace Game.Component; 2 | 3 | using Game.Component.Follow; 4 | 5 | public partial class FollowPlayerComponent : BaseComponent, IFollowComponent 6 | { 7 | private PhysicsBody2D _parent; 8 | private Node2D _player; 9 | 10 | [Export] 11 | public float Speed = 0.5f; 12 | 13 | [Export] 14 | public VelocityComponent VelocityComponent; 15 | 16 | public override void _EnterTree() 17 | { 18 | this.WireNodes(); 19 | _parent = GetParent(); 20 | _player = GetTree().GetFirstNodeInGroup("Player") as Node2D; 21 | } 22 | 23 | public override void _PhysicsProcess(double delta) 24 | { 25 | if (!Enabled) return; 26 | 27 | var playerDir = GlobalPosition.DirectionTo(_player.GlobalPosition); 28 | var distance = GlobalPosition.DistanceTo(_player.GlobalPosition); 29 | var newVelocity = playerDir * Speed * distance; 30 | VelocityComponent.Velocity = newVelocity; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Game/Component/Follow/FollowPlayerComponent.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3] 2 | 3 | [ext_resource type="Script" path="res://Game/Component/Follow/FollowPlayerComponent.cs" id="1_jhe5b"] 4 | 5 | [node name="FollowPlayerComponent" type="Node2D"] 6 | script = ExtResource("1_jhe5b") 7 | -------------------------------------------------------------------------------- /Game/Component/Follow/IFollowComponent.cs: -------------------------------------------------------------------------------- 1 | namespace Game.Component.Follow; 2 | 3 | public interface IFollowComponent 4 | { 5 | } 6 | -------------------------------------------------------------------------------- /Game/Component/HealthComponent.cs: -------------------------------------------------------------------------------- 1 | namespace Game.Component; 2 | 3 | public partial class HealthComponent : BaseComponent 4 | { 5 | [Export] 6 | public float Health; 7 | 8 | [Export] 9 | public Gradient HealthGradient; 10 | 11 | [Node] 12 | public Label Label; 13 | 14 | public override void _EnterTree() 15 | { 16 | this.WireNodes(); 17 | } 18 | 19 | public override void _Ready() 20 | { 21 | _UpdateVisuals(); 22 | } 23 | 24 | private void _UpdateVisuals() 25 | { 26 | if (Label.Visible) 27 | Label.Text = $"{Health}"; 28 | } 29 | 30 | public float DecreaseHealth(float damage) 31 | { 32 | if (!Enabled) return Health; 33 | 34 | var oldHealth = Health; 35 | Health -= damage; 36 | _UpdateVisuals(); 37 | 38 | return oldHealth; 39 | } 40 | 41 | public float IncreaseHealth(float damage) 42 | { 43 | if (!Enabled) return Health; 44 | 45 | var oldHealth = Health; 46 | Health += damage; 47 | _UpdateVisuals(); 48 | 49 | return oldHealth; 50 | } 51 | 52 | public void SetHealth(float health) 53 | { 54 | Health = health; 55 | _UpdateVisuals(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Game/Component/HealthComponent.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://udkfo3veimlm"] 2 | 3 | [ext_resource type="Script" path="res://Game/Component/HealthComponent.cs" id="1_h2daq"] 4 | 5 | [sub_resource type="Gradient" id="Gradient_1s0qp"] 6 | offsets = PackedFloat32Array(0, 0.897638, 1) 7 | colors = PackedColorArray(1, 0.869707, 0.939869, 1, 0.969098, 0, 1, 1, 0.812278, 0, 0.704802, 1) 8 | 9 | [node name="HealthComponent" type="Node2D"] 10 | script = ExtResource("1_h2daq") 11 | HealthGradient = SubResource("Gradient_1s0qp") 12 | 13 | [node name="Visuals" type="Node2D" parent="."] 14 | 15 | [node name="Label" type="Label" parent="Visuals"] 16 | unique_name_in_owner = true 17 | visible = false 18 | anchors_preset = 8 19 | anchor_left = 0.5 20 | anchor_top = 0.5 21 | anchor_right = 0.5 22 | anchor_bottom = 0.5 23 | offset_left = -58.0 24 | offset_top = 47.0 25 | offset_right = 58.0 26 | offset_bottom = 119.0 27 | grow_horizontal = 2 28 | grow_vertical = 2 29 | size_flags_horizontal = 4 30 | theme_override_font_sizes/font_size = 50 31 | text = "HEALTH" 32 | horizontal_alignment = 1 33 | -------------------------------------------------------------------------------- /Game/Component/QueueFreeComponent.cs: -------------------------------------------------------------------------------- 1 | namespace Game.Component; 2 | 3 | public partial class QueueFreeComponent : BaseComponent 4 | { 5 | private int _exitCount; 6 | private Node _parent; 7 | 8 | public override void _Ready() 9 | { 10 | _parent = GetParent(); 11 | } 12 | 13 | public void AddWaitForNodeExit(Node node) 14 | { 15 | if (Enabled) 16 | { 17 | _exitCount++; 18 | node.TreeExited += _NodeExitedTree; 19 | } 20 | } 21 | 22 | public void _NodeExitedTree() 23 | { 24 | if (Enabled) 25 | { 26 | _exitCount--; 27 | if (_exitCount <= 0) 28 | { 29 | _parent.QueueFree(); 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Game/Component/QueueFreeComponent.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://dsfwe0jfckbyj"] 2 | 3 | [ext_resource type="Script" path="res://Game/Component/QueueFreeComponent.cs" id="1_m1emf"] 4 | 5 | [node name="QueueFreeComponent" type="Node2D"] 6 | script = ExtResource("1_m1emf") 7 | -------------------------------------------------------------------------------- /Game/Component/ScoreAttractorComponent.cs: -------------------------------------------------------------------------------- 1 | namespace Game.Component; 2 | 3 | using Game.Autoload; 4 | using Game.Component; 5 | using Game.Extension; 6 | using Vector2 = Godot.Vector2; 7 | 8 | public partial class ScoreAttractorComponent : BaseComponent 9 | { 10 | [Export] 11 | public Node2D AttractorNode; 12 | 13 | [Export] 14 | public float Duration = 2.0f; 15 | 16 | private float Amount { get; set; } 17 | 18 | private Node2D _node; 19 | private Vector2 _nodeInitialGlobalPos; 20 | 21 | public override void _Ready() 22 | { 23 | _node = GetParent(); 24 | _nodeInitialGlobalPos = _node.GlobalPosition; 25 | 26 | _node.EnableComponent(false); 27 | _node.EnableComponent(false); 28 | _node.EnableComponent(false); 29 | 30 | var tween = CreateTween(); 31 | tween.TweenProperty( 32 | this, 33 | nameof(Amount), 34 | 1.0f, 35 | Duration) 36 | .SetTrans(Tween.TransitionType.Back) 37 | .SetEase(Tween.EaseType.In); 38 | 39 | tween.Parallel().TweenProperty( 40 | _node, 41 | Node2D.PropertyName.Rotation.ToString(), 42 | Mathf.Pi * 7.0, 43 | Duration) 44 | .SetTrans(Tween.TransitionType.Expo) 45 | .SetEase(Tween.EaseType.In); 46 | 47 | tween.TweenCallback( 48 | Callable.From(() => 49 | { 50 | var hitDirection = AttractorNode.GlobalPosition.DirectionTo(_node.GlobalPosition); 51 | var hitAngle = AttractorNode.GlobalPosition.AngleToPoint(_node.GlobalPosition); 52 | GameEvents.EmitPlayerHit(AttractorNode, hitAngle, hitDirection); 53 | })); 54 | 55 | tween.TweenCallback(Callable.From(QueueFree)); 56 | } 57 | 58 | public override void _PhysicsProcess(double delta) 59 | { 60 | var fireballPos = AttractorNode.GlobalPosition; 61 | var distance = _nodeInitialGlobalPos.DistanceTo(fireballPos) * Amount; 62 | _node.GlobalPosition = _nodeInitialGlobalPos.MoveToward(fireballPos, distance); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Game/Component/ScoreAttractorComponent.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://dgvrlux56u8r"] 2 | 3 | [ext_resource type="Script" path="res://Game/Component/ScoreAttractorComponent.cs" id="1_topu5"] 4 | 5 | [node name="ScoreAttractorComponent" type="Node2D"] 6 | script = ExtResource("1_topu5") 7 | -------------------------------------------------------------------------------- /Game/Component/VelocityComponent.cs: -------------------------------------------------------------------------------- 1 | namespace Game.Component; 2 | 3 | using Game.Autoload; 4 | 5 | public partial class VelocityComponent : BaseComponent 6 | { 7 | [Signal] 8 | public delegate void CollidedEventHandler(KinematicCollision2D collision2D); 9 | 10 | [Export] 11 | public CollisionShape2D CollisionShape2D; 12 | 13 | [Export] 14 | public PhysicsBody2D ContinuousProcess; 15 | 16 | [Export] 17 | public Vector2 Gravity = new(0f, 0f); 18 | 19 | [Export] 20 | public Vector2 Velocity = Vector2.Zero; 21 | 22 | [Export] 23 | public bool JustMove; 24 | 25 | public bool Falling { get; private set; } 26 | 27 | 28 | public float Speed { get; private set; } 29 | 30 | private Vector2 _lastPosition = Vector2.Zero; 31 | 32 | public override void _EnterTree() 33 | { 34 | this.WireNodes(); 35 | } 36 | 37 | public override void _Ready() 38 | { 39 | SetPhysicsProcess(false); 40 | } 41 | 42 | public void DisableCollisionCheck(bool flag) => 43 | CollisionShape2D?.CallDeferred(CollisionShape2D.MethodName.SetDisabled, flag); 44 | 45 | public KinematicCollision2D MoveAndCollide(PhysicsBody2D node, double delta) 46 | { 47 | if (!Enabled) return null; 48 | 49 | Velocity += Gravity; 50 | 51 | var collision2D = _CalculateSpeed(() => 52 | { 53 | if (JustMove) 54 | { 55 | node.GlobalPosition += Velocity * (float)delta; 56 | return null; 57 | } 58 | return node.MoveAndCollide(Velocity * (float)delta); 59 | }); 60 | if (collision2D == null) return null; 61 | 62 | _EmitCollision(collision2D); 63 | 64 | return collision2D; 65 | } 66 | 67 | public void MoveAndSlide(CharacterBody2D node) 68 | { 69 | if (!Enabled) return; 70 | 71 | Velocity += Gravity; 72 | node.Velocity = Velocity; 73 | var collided = _CalculateSpeed(() => 74 | { 75 | if (JustMove) 76 | { 77 | node.GlobalPosition += Velocity * (float)GetPhysicsProcessDeltaTime(); 78 | return false; 79 | } 80 | return node.MoveAndSlide(); 81 | }); 82 | 83 | if (!collided) return; 84 | 85 | var collision2D = node.GetLastSlideCollision(); 86 | _EmitCollision(collision2D); 87 | } 88 | 89 | public void EnablePhysics(bool flag) 90 | { 91 | SetPhysicsProcess(flag); 92 | if (flag == false) 93 | { 94 | Falling = false; 95 | } 96 | } 97 | 98 | public void ApplyGravity(Node2D node, Vector2 gravity) 99 | { 100 | Velocity = Vector2.Zero; 101 | Gravity = gravity; 102 | ContinuousProcess = node as PhysicsBody2D; 103 | SetPhysicsProcess(true); 104 | } 105 | 106 | private void _EmitCollision(KinematicCollision2D collision2D) 107 | { 108 | EmitSignal(SignalName.Collided, collision2D); 109 | GameEvents.EmitCollision(collision2D); 110 | } 111 | 112 | private void _UpdateSpeed() 113 | { 114 | Speed = (_lastPosition - GlobalPosition).LengthSquared(); 115 | _lastPosition = GlobalPosition; 116 | } 117 | 118 | private KinematicCollision2D _CalculateSpeed(Func action) 119 | { 120 | var result = action.Invoke(); 121 | _UpdateSpeed(); 122 | return result; 123 | } 124 | 125 | private bool _CalculateSpeed(Func action) 126 | { 127 | var result = action.Invoke(); 128 | _UpdateSpeed(); 129 | return result; 130 | } 131 | 132 | public override void _PhysicsProcess(double delta) 133 | { 134 | if (!Enabled) return; 135 | 136 | MoveAndCollide(ContinuousProcess, delta); 137 | Falling = Gravity.LengthSquared() > 0.0f; 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /Game/Component/VelocityComponent.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://dfcjblk01lnsf"] 2 | 3 | [ext_resource type="Script" path="res://Game/Component/VelocityComponent.cs" id="1_dc0hy"] 4 | 5 | [node name="VelocityComponent" type="Node2D"] 6 | script = ExtResource("1_dc0hy") 7 | -------------------------------------------------------------------------------- /Game/Entity/Enemy/BaseEnemy.cs: -------------------------------------------------------------------------------- 1 | namespace Game.Entity.Enemy; 2 | 3 | public partial class BaseEnemy : CharacterBody2D 4 | { 5 | public bool IsDead; 6 | } 7 | -------------------------------------------------------------------------------- /Game/Entity/Enemy/Zombie.cs: -------------------------------------------------------------------------------- 1 | namespace Game.Entity.Enemy; 2 | 3 | using Game.Autoload; 4 | using Game.Component; 5 | using Game.Component.Follow; 6 | using Game.Extension; 7 | 8 | public partial class Zombie : BaseEnemy 9 | { 10 | [Node] 11 | public AnimatedSprite2D AnimatedSprite2D; 12 | 13 | [Node] 14 | public CollisionShape2D CollisionShape2D; 15 | 16 | [Node] 17 | public HealthComponent HealthComponent; 18 | 19 | [Node] 20 | public VelocityComponent VelocityComponent; 21 | 22 | [Node] 23 | public FacingComponent FacingComponent; 24 | 25 | [Node] 26 | public Node2D Visuals; 27 | 28 | private DelegateStateMachine _stateMachine = new(); 29 | private float _oldHealth; 30 | 31 | public override void _EnterTree() 32 | { 33 | this.WireNodes(); 34 | } 35 | 36 | public override void _Ready() 37 | { 38 | // State Machine States 39 | _stateMachine.AddStates(StateIdle, EnterStateIdle); 40 | _stateMachine.AddStates(StateWalk, EnterStateWalk); 41 | _stateMachine.SetInitialState(StateIdle); 42 | 43 | GameEvents.EmitSpawnZombie(this); 44 | } 45 | 46 | public override void _PhysicsProcess(double delta) 47 | { 48 | _stateMachine.Update(); 49 | } 50 | 51 | private void _CheckHealth() 52 | { 53 | if (HealthComponent != null) 54 | { 55 | if (HealthComponent.Health <= 0.0f && !IsDead) 56 | { 57 | IsDead = true; 58 | 59 | this.AddResourceAndQueueFree(); 60 | 61 | // TODO: a little verbose - maybe create an extension method??? 62 | var scoreAttractorComponent = this.InstantiateFromResources(); 63 | scoreAttractorComponent.AttractorNode = GetTree().GetFirstNodeInGroup(); 64 | this.AddNodeToQueueFreeComponent(scoreAttractorComponent); 65 | this.AddChildDeferred(scoreAttractorComponent); 66 | 67 | GameEvents.EmitZombieKilled(this); 68 | } 69 | else if (Math.Abs(HealthComponent.Health - _oldHealth) > 0.001f) 70 | { 71 | _oldHealth = HealthComponent.Health; 72 | Visuals.Modulate = HealthComponent.HealthGradient.Sample(1.0f - _oldHealth); 73 | } 74 | } 75 | } 76 | 77 | private void _CheckSpeed() 78 | { 79 | var speed = VelocityComponent.Speed; 80 | if (speed <= 0.1f) 81 | { 82 | if (_stateMachine.GetCurrentState() != StateIdle) 83 | { 84 | _stateMachine.ChangeState(StateIdle); 85 | } 86 | } 87 | else 88 | { 89 | if (_stateMachine.GetCurrentState() != StateWalk) 90 | { 91 | _stateMachine.ChangeState(StateWalk); 92 | } 93 | } 94 | } 95 | 96 | private void EnterStateIdle() => AnimatedSprite2D.Pause(); 97 | 98 | private void StateIdle() 99 | { 100 | VelocityComponent.MoveAndSlide(this); 101 | 102 | _CheckHealth(); 103 | _CheckSpeed(); 104 | } 105 | 106 | private void EnterStateWalk() => AnimatedSprite2D.Play("walk"); 107 | 108 | private void StateWalk() 109 | { 110 | VelocityComponent.MoveAndSlide(this); 111 | 112 | _CheckHealth(); 113 | _CheckSpeed(); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /Game/Entity/Enemy/Zombie.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=11 format=3 uid="uid://cjalmyc66a4bh"] 2 | 3 | [ext_resource type="PackedScene" uid="uid://dfcjblk01lnsf" path="res://Game/Component/VelocityComponent.tscn" id="1_tkmm1"] 4 | [ext_resource type="Script" path="res://Game/Entity/Enemy/Zombie.cs" id="1_uyh6b"] 5 | [ext_resource type="PackedScene" uid="uid://bxqxjytua5jb2" path="res://Game/Component/FacingComponent.tscn" id="2_31ob2"] 6 | [ext_resource type="PackedScene" uid="uid://udkfo3veimlm" path="res://Game/Component/HealthComponent.tscn" id="2_l5geu"] 7 | [ext_resource type="PackedScene" path="res://Game/Component/Follow/FollowPlayerComponent.tscn" id="3_15m1n"] 8 | [ext_resource type="Texture2D" uid="uid://c258ccybjyo5" path="res://Game/Assets/Zombie/1.png" id="7_6gvi0"] 9 | [ext_resource type="PackedScene" uid="uid://dsfwe0jfckbyj" path="res://Game/Component/QueueFreeComponent.tscn" id="7_wvx8j"] 10 | [ext_resource type="Texture2D" uid="uid://7mob3wh4prxh" path="res://Game/Assets/Zombie/2.png" id="8_e2wem"] 11 | 12 | [sub_resource type="SpriteFrames" id="SpriteFrames_gbfpe"] 13 | animations = [{ 14 | "frames": [{ 15 | "duration": 1.0, 16 | "texture": ExtResource("7_6gvi0") 17 | }, { 18 | "duration": 1.0, 19 | "texture": ExtResource("8_e2wem") 20 | }], 21 | "loop": true, 22 | "name": &"walk", 23 | "speed": 10.0 24 | }] 25 | 26 | [sub_resource type="CircleShape2D" id="CircleShape2D_7l1wo"] 27 | radius = 64.0 28 | 29 | [node name="Zombie" type="CharacterBody2D" groups=["Zombie"]] 30 | collision_mask = 3 31 | motion_mode = 1 32 | safe_margin = 0.001 33 | script = ExtResource("1_uyh6b") 34 | metadata/_edit_group_ = true 35 | 36 | [node name="HealthComponent" parent="." instance=ExtResource("2_l5geu")] 37 | Health = 1.0 38 | 39 | [node name="VelocityComponent" parent="." node_paths=PackedStringArray("CollisionShape2D") instance=ExtResource("1_tkmm1")] 40 | CollisionShape2D = NodePath("../CollisionShape2D") 41 | 42 | [node name="FacingComponent" parent="." node_paths=PackedStringArray("Node2DToRotate", "VelocityComponent") instance=ExtResource("2_31ob2")] 43 | Node2DToRotate = NodePath("../Visuals") 44 | VelocityComponent = NodePath("../VelocityComponent") 45 | 46 | [node name="FollowComponent" parent="." node_paths=PackedStringArray("VelocityComponent") instance=ExtResource("3_15m1n")] 47 | VelocityComponent = NodePath("../VelocityComponent") 48 | 49 | [node name="QueueFreeComponent" parent="." instance=ExtResource("7_wvx8j")] 50 | 51 | [node name="Visuals" type="Node2D" parent="."] 52 | scale = Vector2(0.3, 0.3) 53 | 54 | [node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="Visuals"] 55 | unique_name_in_owner = true 56 | scale = Vector2(5.422, 5.422) 57 | sprite_frames = SubResource("SpriteFrames_gbfpe") 58 | animation = &"walk" 59 | autoplay = "walk" 60 | frame_progress = 0.163318 61 | 62 | [node name="CollisionShape2D" type="CollisionShape2D" parent="."] 63 | position = Vector2(-0.5, 1) 64 | shape = SubResource("CircleShape2D_7l1wo") 65 | -------------------------------------------------------------------------------- /Game/Entity/Fireball.cs: -------------------------------------------------------------------------------- 1 | namespace Game.Entity; 2 | 3 | using Game.Autoload; 4 | using Game.Component; 5 | using Game.Component.Element; 6 | using Game.Extension; 7 | using Godot.Collections; 8 | 9 | public partial class Fireball : StaticBody2D 10 | { 11 | [Export] 12 | public float HeatUpEnergyRate = 0.075f; 13 | 14 | [Export] 15 | public bool HeatUpTouching = true; 16 | 17 | [Node] 18 | public FollowMouseComponent FollowMouseComponent; 19 | 20 | [Node] 21 | public VelocityComponent VelocityComponent; 22 | 23 | [Node] 24 | public Area2D HotZone; 25 | 26 | [Node] 27 | public Sprite2D Skull; 28 | 29 | [Node] 30 | public Timer Timer; 31 | 32 | [Node] 33 | public CpuParticles2D TrailingParticles; 34 | 35 | [Node] 36 | public CpuParticles2D SwirlingParticles; 37 | 38 | [Node] 39 | public CpuParticles2D HitParticles; 40 | 41 | private Dictionary _nodesInsideHotZone = new(); 42 | private Vector2 _screenSize; 43 | private Vector2 _skullScale; 44 | 45 | public override void _EnterTree() 46 | { 47 | this.WireNodes(); 48 | } 49 | 50 | public override async void _Ready() 51 | { 52 | _screenSize = GetViewportRect().Size; 53 | 54 | HotZone.BodyEntered += _EnteredHotZone; 55 | HotZone.BodyExited += _ExitedHotZone; 56 | Timer.Timeout += _HeatUpHotZone; 57 | GameEvents.Instance.PlayerHit += _hit; 58 | 59 | _skullScale = Skull.Scale; 60 | 61 | await ToSignal(GetTree(), "process_frame"); 62 | 63 | Input.WarpMouse(_screenSize / 2.0f); 64 | } 65 | 66 | public override void _PhysicsProcess(double delta) 67 | { 68 | VelocityComponent.MoveAndCollide(this, delta); 69 | } 70 | 71 | private void _EnteredHotZone(Node2D body) 72 | { 73 | var fireComponent = body.GetFirstNodeOfType() ?? body.AddResourceDeferred(); 74 | 75 | _nodesInsideHotZone.Add(body.Name, fireComponent); 76 | } 77 | 78 | private void _ExitedHotZone(Node2D body) 79 | { 80 | _nodesInsideHotZone.Remove(body.Name); 81 | } 82 | 83 | private void _HeatUpHotZone() 84 | { 85 | if (!HeatUpTouching) return; 86 | foreach (var fireComponent in _nodesInsideHotZone.Values) 87 | { 88 | fireComponent.AddEnergy(HeatUpEnergyRate); 89 | } 90 | } 91 | 92 | private void _hit(Node2D player, float angle, Vector2 direction) 93 | { 94 | var tween = CreateTween(); 95 | 96 | if (!HitParticles.Emitting) 97 | { 98 | HitParticles.Emitting = true; 99 | HitParticles.Gravity = Vector2.One * direction * -1000.0f; 100 | } 101 | tween.TweenProperty( 102 | Skull, 103 | CanvasItem.PropertyName.Modulate.ToString(), 104 | Colors.White, 105 | 0.25f) 106 | .From(this.IntensifyColor(Colors.Magenta, 2.3f)) 107 | .SetTrans(Tween.TransitionType.Linear) 108 | .SetEase(Tween.EaseType.In); 109 | 110 | tween.SetParallel(); 111 | tween.TweenProperty( 112 | Skull, 113 | Node2D.PropertyName.Scale.ToString(), 114 | _skullScale, 115 | 0.25f) 116 | .From(_skullScale * 1.3f) 117 | .SetTrans(Tween.TransitionType.Linear) 118 | .SetEase(Tween.EaseType.In); 119 | tween.TweenProperty( 120 | HitParticles, 121 | CpuParticles2D.PropertyName.Direction.ToString(), 122 | direction, 123 | 0.25f); 124 | 125 | tween.SetParallel(false); 126 | tween.TweenCallback(Callable.From(() => HitParticles.Emitting = false)); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /Game/Entity/Fireball.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=11 format=3 uid="uid://h6cpojqhxl87"] 2 | 3 | [ext_resource type="PackedScene" uid="uid://dfcjblk01lnsf" path="res://Game/Component/VelocityComponent.tscn" id="1_athld"] 4 | [ext_resource type="Script" path="res://Game/Entity/Fireball.cs" id="1_wsqe4"] 5 | [ext_resource type="PackedScene" path="res://Game/Component/Follow/FollowMouseComponent.tscn" id="2_iv1x6"] 6 | [ext_resource type="Texture2D" uid="uid://ndeuju5mm1x" path="res://Game/Assets/Skulls/skull1.png" id="4_1otds"] 7 | 8 | [sub_resource type="CircleShape2D" id="CircleShape2D_hc3n0"] 9 | radius = 74.0608 10 | 11 | [sub_resource type="CircleShape2D" id="CircleShape2D_jggh1"] 12 | radius = 111.018 13 | 14 | [sub_resource type="Curve" id="Curve_1ibpw"] 15 | max_value = 5.0 16 | _data = [Vector2(0, 5), 0.0, 0.0, 0, 0, Vector2(1, 0), -10.402, 0.0, 0, 0] 17 | point_count = 2 18 | 19 | [sub_resource type="Gradient" id="Gradient_5rrxe"] 20 | colors = PackedColorArray(0.988974, 0.660255, 0, 1, 0.242031, 0.148812, 0.00243234, 1) 21 | 22 | [sub_resource type="Curve" id="Curve_d61kb"] 23 | max_value = 5.0 24 | _data = [Vector2(0, 5), 0.0, 0.0, 0, 0, Vector2(1, 0), 0.0, 0.0, 0, 0] 25 | point_count = 2 26 | 27 | [sub_resource type="Gradient" id="Gradient_hh4dq"] 28 | colors = PackedColorArray(0.988974, 0, 0, 1, 0.99, 0.95, 0, 1) 29 | 30 | [node name="FireBall" type="StaticBody2D" groups=["Fireball", "Player"]] 31 | collision_layer = 2 32 | collision_mask = 0 33 | script = ExtResource("1_wsqe4") 34 | HeatUpEnergyRate = 0.5 35 | 36 | [node name="RemoteTransform2D" type="RemoteTransform2D" parent="."] 37 | update_rotation = false 38 | update_scale = false 39 | 40 | [node name="VelocityComponent" parent="." instance=ExtResource("1_athld")] 41 | 42 | [node name="FollowMouseComponent" parent="." node_paths=PackedStringArray("VelocityComponent") instance=ExtResource("2_iv1x6")] 43 | Speed = 4.0 44 | VelocityComponent = NodePath("../VelocityComponent") 45 | 46 | [node name="CollisionShape2D" type="CollisionShape2D" parent="."] 47 | shape = SubResource("CircleShape2D_hc3n0") 48 | 49 | [node name="HotZone" type="Area2D" parent="."] 50 | collision_layer = 4 51 | monitorable = false 52 | 53 | [node name="HotZoneCollisionShape" type="CollisionShape2D" parent="HotZone"] 54 | shape = SubResource("CircleShape2D_jggh1") 55 | 56 | [node name="Timer" type="Timer" parent="."] 57 | wait_time = 0.1 58 | autostart = true 59 | 60 | [node name="Visuals" type="Node2D" parent="."] 61 | 62 | [node name="TrailingParticles" type="CPUParticles2D" parent="Visuals"] 63 | unique_name_in_owner = true 64 | amount = 100 65 | randomness = 1.0 66 | lifetime_randomness = 0.5 67 | emission_shape = 2 68 | emission_sphere_radius = 64.0 69 | direction = Vector2(2.08165e-12, 2.08165e-12) 70 | spread = 180.0 71 | gravity = Vector2(0, 0) 72 | initial_velocity_max = 100.0 73 | angular_velocity_min = -720.0 74 | angular_velocity_max = 720.0 75 | scale_amount_max = 3.0 76 | scale_amount_curve = SubResource("Curve_1ibpw") 77 | color_ramp = SubResource("Gradient_5rrxe") 78 | hue_variation_max = 1.0 79 | 80 | [node name="SwirlingParticles" type="CPUParticles2D" parent="Visuals"] 81 | unique_name_in_owner = true 82 | amount = 450 83 | randomness = 1.0 84 | local_coords = true 85 | emission_shape = 2 86 | emission_sphere_radius = 64.0 87 | direction = Vector2(0, 0) 88 | spread = 180.0 89 | gravity = Vector2(0, 0) 90 | initial_velocity_max = 100.0 91 | angular_velocity_min = -720.0 92 | angular_velocity_max = 720.0 93 | scale_amount_max = 3.0 94 | scale_amount_curve = SubResource("Curve_d61kb") 95 | color_ramp = SubResource("Gradient_hh4dq") 96 | hue_variation_min = -0.36 97 | hue_variation_max = 1.0 98 | 99 | [node name="HitParticles" type="CPUParticles2D" parent="Visuals"] 100 | unique_name_in_owner = true 101 | emitting = false 102 | amount = 400 103 | explosiveness = 0.3 104 | randomness = 1.0 105 | lifetime_randomness = 0.3 106 | direction = Vector2(0, 0) 107 | spread = 20.0 108 | gravity = Vector2(0, 0) 109 | initial_velocity_min = 300.0 110 | initial_velocity_max = 800.0 111 | linear_accel_max = 100.0 112 | scale_amount_min = 0.0 113 | scale_amount_max = 10.0 114 | color = Color(2.6, 0, 2.9, 1) 115 | hue_variation_min = -0.25 116 | hue_variation_max = 0.45 117 | 118 | [node name="Skull" type="Sprite2D" parent="Visuals"] 119 | unique_name_in_owner = true 120 | z_index = 3 121 | scale = Vector2(0.346, 0.346) 122 | texture = ExtResource("4_1otds") 123 | -------------------------------------------------------------------------------- /Game/Extension/NodeExtension.cs: -------------------------------------------------------------------------------- 1 | namespace Game.Extension; 2 | 3 | using Game.Component; 4 | 5 | public static class NodeExtension 6 | { 7 | private static ResourcePreloader _preloader; 8 | 9 | public static T InstantiateFromResources(this Node node) where T : Node 10 | { 11 | _preloader ??= node.GetNode("/root/RsPreloader"); 12 | return _preloader.InstanceSceneOrNull(); 13 | } 14 | 15 | public static Color IntensifyColor(this Node node, Color color, float factor) 16 | { 17 | var result = new Color(color); 18 | result *= factor; 19 | result.A = 1.0f; 20 | return result; 21 | } 22 | 23 | public static T AddResourceDeferred(this Node node) where T : Node 24 | { 25 | var child = node.InstantiateFromResources(); 26 | node.CallDeferred("add_child", child); 27 | return child; 28 | } 29 | 30 | public static async void AddResourceDeferredWithAction(this Node node, Action action) where T : Node 31 | { 32 | var child = node.AddResourceDeferred(); 33 | await child.ToSignal(child, Node.SignalName.TreeEntered); 34 | action(child); 35 | } 36 | 37 | public static void AddNodeToQueueFreeComponent(this Node node, Node nodeToAdd) 38 | { 39 | var queueFreeComponent = node.GetFirstNodeOfType(); 40 | if (queueFreeComponent == null) 41 | { 42 | return; 43 | } 44 | 45 | queueFreeComponent.AddWaitForNodeExit(nodeToAdd); 46 | } 47 | 48 | public static void AddResourceAndQueueFree(this Node node) where T : Node => 49 | node.AddResourceDeferredWithAction(node.AddNodeToQueueFreeComponent); 50 | 51 | /// 52 | /// Enables or disables the specified child component type - does nothing if component not found. 53 | /// 54 | /// The type of component to enable or disable. Must be a subclass of BaseComponent. 55 | /// The node to search for its component. 56 | /// Whether to enable or disable the component. 57 | public static void EnableComponent(this Node node, bool enabled) where T : BaseComponent => 58 | node.GetFirstNodeOfType()?.SetEnabled(enabled); 59 | } 60 | -------------------------------------------------------------------------------- /Game/Helpers/PointGenerator.cs: -------------------------------------------------------------------------------- 1 | namespace Game.Helpers; 2 | 3 | internal class PointGenerator 4 | { 5 | private readonly Vector2 _center; 6 | private readonly float _padding; 7 | private readonly float _radius; 8 | private readonly RandomNumberGenerator _random = new(); 9 | 10 | public PointGenerator(Vector2 center, float radius, float padding) 11 | { 12 | _center = center; 13 | _radius = radius; 14 | _padding = padding; 15 | 16 | _random.Seed = 1234L; 17 | } 18 | 19 | public Vector2[] GeneratePoints(int totalPoints) 20 | { 21 | var points = new Vector2[totalPoints]; 22 | 23 | for (var i = 0; i < totalPoints; i++) 24 | { 25 | var point = GeneratePoint(); 26 | points[i] = point; 27 | } 28 | 29 | return points; 30 | } 31 | 32 | public Vector2 GeneratePoint() 33 | { 34 | var radius = _radius + _padding * _random.RandfRange(0.1f, 3.0f); 35 | var angle = _random.RandfRange(0.0f, 720.0f); 36 | var x = _center.X + radius * Mathf.Cos(Mathf.DegToRad(angle)); 37 | var y = _center.Y + radius * Mathf.Sin(Mathf.DegToRad(angle)); 38 | return new Vector2(x, y); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Game/Main.cs: -------------------------------------------------------------------------------- 1 | namespace Game; 2 | 3 | using Game.Autoload; 4 | using Game.Entity.Enemy; 5 | using Game.Helpers; 6 | 7 | public partial class Main : Node2D 8 | { 9 | private int _killedZombies; 10 | private PointGenerator _pointGenerator; 11 | private RandomNumberGenerator _random = new(); 12 | private Rect2 _rect; 13 | private Vector2 _screenSize; 14 | 15 | [Node] 16 | public Camera2D Camera2D; 17 | 18 | [Node] 19 | public Node2D FireBall; 20 | 21 | [Node] 22 | public CanvasLayer Hud; 23 | 24 | [Export] 25 | public int TotalZombies = 500; 26 | 27 | [Node] 28 | public CanvasGroup Entities; 29 | 30 | [Export] 31 | public PackedScene ZombieScene; 32 | 33 | public override void _EnterTree() 34 | { 35 | this.WireNodes(); 36 | } 37 | 38 | public override void _Ready() 39 | { 40 | Global.Instance.Hud = Hud; 41 | Global.Instance.Camera2D = Camera2D; 42 | 43 | _random.Seed = 1234L; 44 | _screenSize = GetViewportRect().Size; 45 | 46 | FireBall.GlobalPosition = _screenSize / 2; 47 | 48 | _pointGenerator = new PointGenerator( 49 | _screenSize / 2.0f, 50 | 500, 51 | 200); 52 | 53 | _CreateZombies(); 54 | 55 | GameEvents.Instance.ZombieKilled += _ => 56 | { 57 | _killedZombies++; 58 | if (_killedZombies % _random.RandiRange(1, 2) == 0) 59 | { 60 | _CreateZombies(_pointGenerator.GeneratePoint()); 61 | } 62 | }; 63 | } 64 | 65 | private void _CreateZombies() 66 | { 67 | var points = _pointGenerator.GeneratePoints(TotalZombies); 68 | foreach (var point in points) 69 | { 70 | _CreateZombies(point); 71 | } 72 | } 73 | 74 | private void _CreateZombies(Vector2 point) 75 | { 76 | var zombie = ZombieScene.Instantiate(); 77 | zombie.GlobalPosition = point; 78 | zombie.Scale *= (float)GD.RandRange(0.15, 0.45); 79 | 80 | Entities.AddChild(zombie); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Game/Main.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=17 format=3 uid="uid://ht3rbkacq8nf"] 2 | 3 | [ext_resource type="Script" path="res://Game/Main.cs" id="1_a1d5q"] 4 | [ext_resource type="PackedScene" uid="uid://cjalmyc66a4bh" path="res://Game/Entity/Enemy/Zombie.tscn" id="2_n8u82"] 5 | [ext_resource type="PackedScene" uid="uid://jihm1oy8rd03" path="res://Game/Manager/FxManager.tscn" id="3_b265l"] 6 | [ext_resource type="PackedScene" uid="uid://dyxakolt14kln" path="res://Game/Manager/SoundManager.tscn" id="4_6v2nd"] 7 | [ext_resource type="PackedScene" uid="uid://h6cpojqhxl87" path="res://Game/Entity/Fireball.tscn" id="5_3oww0"] 8 | [ext_resource type="PackedScene" uid="uid://blqe5s042oxxj" path="res://Game/Component/Follow/FollowPathComponent.tscn" id="6_q02mi"] 9 | [ext_resource type="PackedScene" path="res://Game/UI/Fps.tscn" id="6_xr8q0"] 10 | [ext_resource type="Script" path="res://Game/UI/RateStats.cs" id="7_dovvp"] 11 | [ext_resource type="Script" path="res://Game/UI/Score.cs" id="8_3ms7h"] 12 | [ext_resource type="Script" path="res://Game/UI/ZombieCounter.cs" id="9_vj8vb"] 13 | [ext_resource type="Texture2D" uid="uid://d3r6h3ktg0kka" path="res://Game/Assets/Backgrounds/Paper_art_style_a_diorama_of_an_igloo_on_a_snowy_mountain_for_a_presentatio_1.png" id="10_iumaq"] 14 | 15 | [sub_resource type="Environment" id="Environment_otope"] 16 | background_mode = 3 17 | tonemap_mode = 3 18 | glow_enabled = true 19 | glow_levels/1 = 1.0 20 | glow_levels/5 = 0.45 21 | glow_normalized = true 22 | glow_intensity = 8.0 23 | glow_blend_mode = 0 24 | glow_hdr_threshold = 0.0 25 | 26 | [sub_resource type="Curve2D" id="Curve2D_ufdyh"] 27 | resource_local_to_scene = true 28 | bake_interval = 2.0 29 | _data = { 30 | "points": PackedVector2Array(0, 0, 0, 0, 160, 0, 0, 0, 0, 0, 304, -51, 0, 0, 0, 0, 498, -61, 0, 0, 0, 0, 712, 4, 0, 0, 0, 0, 798, 156, 0, 0, 0, 0, 813, 305, 0, 0, 0, 0, 761, 425, 0, 0, 0, 0, 595, 480, 0, 0, 0, 0, 394, 480, 0, 0, 0, 0, 197, 461, 0, 0, 0, 0, 102, 383, 0, 0, 0, 0, 41, 195, 0, 0, 0, 0, 112, 74, 0, 0, 0, 0, 160, 0) 31 | } 32 | point_count = 14 33 | 34 | [sub_resource type="SystemFont" id="SystemFont_8kl1n"] 35 | 36 | [sub_resource type="LabelSettings" id="LabelSettings_7gb6r"] 37 | font = SubResource("SystemFont_8kl1n") 38 | font_size = 30 39 | outline_size = 4 40 | outline_color = Color(0, 0, 0, 1) 41 | 42 | [sub_resource type="LabelSettings" id="LabelSettings_f5cu4"] 43 | font = SubResource("SystemFont_8kl1n") 44 | font_size = 30 45 | outline_size = 4 46 | outline_color = Color(0, 0, 0, 1) 47 | 48 | [node name="Main" type="Node2D"] 49 | script = ExtResource("1_a1d5q") 50 | ZombieScene = ExtResource("2_n8u82") 51 | 52 | [node name="WorldEnvironment" type="WorldEnvironment" parent="."] 53 | environment = SubResource("Environment_otope") 54 | 55 | [node name="Camera2D" type="Camera2D" parent="." groups=["Camera2D"]] 56 | position = Vector2(576, 324) 57 | limit_smoothed = true 58 | drag_horizontal_enabled = true 59 | drag_vertical_enabled = true 60 | drag_top_margin = 0.3 61 | drag_bottom_margin = 0.3 62 | editor_draw_limits = true 63 | editor_draw_drag_margin = true 64 | 65 | [node name="FxManager" parent="." instance=ExtResource("3_b265l")] 66 | 67 | [node name="SoundManager" parent="." instance=ExtResource("4_6v2nd")] 68 | 69 | [node name="Entities" type="CanvasGroup" parent="."] 70 | y_sort_enabled = true 71 | 72 | [node name="FireBall" parent="Entities" instance=ExtResource("5_3oww0")] 73 | unique_name_in_owner = true 74 | position = Vector2(576, 324) 75 | collision_mask = 4 76 | 77 | [node name="RemoteTransform2D" parent="Entities/FireBall" index="0"] 78 | remote_path = NodePath("../../../Camera2D") 79 | 80 | [node name="ZombieFollowPath" parent="Entities" instance=ExtResource("2_n8u82")] 81 | modulate = Color(0.18, 2.5, 0.22, 1) 82 | position = Vector2(208, 131) 83 | collision_layer = 5 84 | 85 | [node name="HealthComponent" parent="Entities/ZombieFollowPath" index="0"] 86 | Enabled = false 87 | 88 | [node name="VelocityComponent" parent="Entities/ZombieFollowPath" index="1"] 89 | JustMove = true 90 | 91 | [node name="FollowComponent" parent="Entities/ZombieFollowPath" index="3"] 92 | Enabled = false 93 | 94 | [node name="FollowPathComponent" parent="Entities/ZombieFollowPath" node_paths=PackedStringArray("VelocityComponent") instance=ExtResource("6_q02mi")] 95 | position = Vector2(160, 113) 96 | FollowSpeed = 3.0 97 | PathSpeed = 0.3 98 | VelocityComponent = NodePath("../VelocityComponent") 99 | 100 | [node name="Path2D" parent="Entities/ZombieFollowPath/FollowPathComponent" index="0"] 101 | curve = SubResource("Curve2D_ufdyh") 102 | 103 | [node name="PathFollow2D" parent="Entities/ZombieFollowPath/FollowPathComponent/Path2D" index="0"] 104 | position = Vector2(160, 0) 105 | rotation = -0.340382 106 | 107 | [node name="Hud" type="CanvasLayer" parent="."] 108 | unique_name_in_owner = true 109 | layer = 2 110 | 111 | [node name="FPS" parent="Hud" instance=ExtResource("6_xr8q0")] 112 | label_settings = SubResource("LabelSettings_7gb6r") 113 | 114 | [node name="Top Right" type="VBoxContainer" parent="Hud"] 115 | anchors_preset = 1 116 | anchor_left = 1.0 117 | anchor_right = 1.0 118 | offset_left = -156.0 119 | offset_bottom = 141.0 120 | grow_horizontal = 0 121 | theme_override_constants/separation = -13 122 | script = ExtResource("7_dovvp") 123 | 124 | [node name="Timer" type="Timer" parent="Hud/Top Right"] 125 | autostart = true 126 | 127 | [node name="Score" type="Label" parent="Hud/Top Right"] 128 | unique_name_in_owner = true 129 | layout_mode = 2 130 | size_flags_vertical = 2 131 | text = "Score: 0" 132 | label_settings = SubResource("LabelSettings_f5cu4") 133 | horizontal_alignment = 2 134 | script = ExtResource("8_3ms7h") 135 | 136 | [node name="ZombieCounter" type="Label" parent="Hud/Top Right"] 137 | unique_name_in_owner = true 138 | layout_mode = 2 139 | size_flags_vertical = 2 140 | text = "Zombies: 0" 141 | label_settings = SubResource("LabelSettings_f5cu4") 142 | horizontal_alignment = 2 143 | script = ExtResource("9_vj8vb") 144 | 145 | [node name="HitRate" type="Label" parent="Hud/Top Right"] 146 | unique_name_in_owner = true 147 | layout_mode = 2 148 | size_flags_vertical = 2 149 | text = "Hits: 0" 150 | label_settings = SubResource("LabelSettings_f5cu4") 151 | horizontal_alignment = 2 152 | 153 | [node name="MaxRate" type="Label" parent="Hud/Top Right"] 154 | unique_name_in_owner = true 155 | layout_mode = 2 156 | size_flags_vertical = 2 157 | text = "Max: 0" 158 | label_settings = SubResource("LabelSettings_f5cu4") 159 | horizontal_alignment = 2 160 | 161 | [node name="ParallaxBackground" type="ParallaxBackground" parent="."] 162 | 163 | [node name="ParallaxLayer" type="ParallaxLayer" parent="ParallaxBackground"] 164 | motion_mirroring = Vector2(1152, 1152) 165 | 166 | [node name="Sprite2D" type="Sprite2D" parent="ParallaxBackground/ParallaxLayer"] 167 | modulate = Color(0.0235294, 0.176471, 0.25098, 0.858824) 168 | texture = ExtResource("10_iumaq") 169 | centered = false 170 | 171 | [editable path="Entities/FireBall"] 172 | [editable path="Entities/ZombieFollowPath"] 173 | [editable path="Entities/ZombieFollowPath/FollowPathComponent"] 174 | -------------------------------------------------------------------------------- /Game/Manager/FxManager.cs: -------------------------------------------------------------------------------- 1 | namespace Game.Manager; 2 | 3 | public partial class FxManager : Node 4 | { 5 | public static FxManager Instance { get; private set; } 6 | 7 | private readonly Random _random = new(); 8 | private int _currentShakePriority; 9 | 10 | public static void ShakeScreen(double shakeLength, float shakePower) => 11 | Instance._ShakeScreen(shakeLength, shakePower); 12 | 13 | public override void _Notification(int what) 14 | { 15 | if (what != NotificationEnterTree) return; 16 | 17 | Instance = this; 18 | } 19 | 20 | private float _RandRange(float min, float max) => 21 | (float)_random.NextDouble() * (max - min) + min; 22 | 23 | private void _MoveCamera(Vector2 move) 24 | { 25 | var camera = GetTree().GetFirstNodeInGroup(); 26 | camera.Offset = new Vector2(_RandRange(-move.X, move.X), _RandRange(-move.Y, move.Y)); 27 | } 28 | 29 | private void _ShakeScreen(double shakeLength, float shakePower) 30 | { 31 | var tweenShake = CreateTween(); 32 | tweenShake.TweenMethod( 33 | Callable.From(Instance._MoveCamera), 34 | new Vector2(shakePower, shakePower), 35 | new Vector2(0, 0), 36 | shakeLength) 37 | .SetTrans(Tween.TransitionType.Sine) 38 | .SetEase(Tween.EaseType.Out); 39 | } 40 | 41 | private void _resetCurrentShakePriority() => _currentShakePriority = 0; 42 | } 43 | -------------------------------------------------------------------------------- /Game/Manager/FxManager.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://jihm1oy8rd03"] 2 | 3 | [ext_resource type="Script" path="res://Game/Manager/FxManager.cs" id="1_nfwxl"] 4 | 5 | [node name="FxManager" type="Node2D"] 6 | script = ExtResource("1_nfwxl") 7 | -------------------------------------------------------------------------------- /Game/Manager/SoundManager.cs: -------------------------------------------------------------------------------- 1 | namespace Game.Manager; 2 | 3 | using Game.Autoload; 4 | 5 | public partial class SoundManager : Node 6 | { 7 | [Export] 8 | public bool Enabled = true; 9 | 10 | [Node] 11 | public AudioStreamPlayer2D Explosion; 12 | 13 | [Node] 14 | public AudioStreamPlayer2D PlayerHit; 15 | 16 | [Node] 17 | public AudioStreamPlayer Fire; 18 | 19 | private float _fireIntensity; 20 | 21 | public override void _EnterTree() 22 | { 23 | this.WireNodes(); 24 | } 25 | 26 | public override void _Ready() 27 | { 28 | GameEvents.Instance.PlayerHit += (player, _, _) => 29 | { 30 | if (!Enabled) return; 31 | PlayerHit.GlobalPosition = player.GlobalPosition; 32 | PlayerHit.PlayWithPitch((float)GD.RandRange(0.8f, 1.1f)); 33 | }; 34 | 35 | GameEvents.Instance.ZombieKilled += zombie => 36 | { 37 | if (!Enabled) return; 38 | Explosion.GlobalPosition = zombie.GlobalPosition; 39 | Explosion.PlayWithPitch((float)GD.RandRange(0.6f, 1.4f)); 40 | }; 41 | 42 | GameEvents.Instance.ElementIntensityDepleted += _ => 43 | { 44 | if (!Enabled) return; 45 | _fireIntensity = Mathf.Max(_fireIntensity - 1.0f, 0.0f); 46 | if (_fireIntensity <= 0.0f) 47 | { 48 | if (Fire.Playing) Fire.Stop(); 49 | } 50 | }; 51 | 52 | GameEvents.Instance.ElementIntensityMaxed += _ => 53 | { 54 | if (!Enabled) return; 55 | _fireIntensity += 1.0f; 56 | if (!Fire.Playing) Fire.Play(); 57 | }; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Game/Manager/SoundManager.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=5 format=3 uid="uid://dyxakolt14kln"] 2 | 3 | [ext_resource type="Script" path="res://Game/Manager/SoundManager.cs" id="1_i4nu1"] 4 | [ext_resource type="AudioStream" uid="uid://b0q7v0wqeeyyc" path="res://Game/Assets/Sounds/SND36351.wav" id="2_gpll2"] 5 | [ext_resource type="AudioStream" uid="uid://ckd4h7y5t3hih" path="res://Game/Assets/Sounds/chipsHandle3.ogg" id="3_t6mi4"] 6 | [ext_resource type="AudioStream" uid="uid://cfwubuk035v58" path="res://Game/Assets/Sounds/Ambience_Bonfire_Loop.wav" id="4_2bt1m"] 7 | 8 | [node name="SoundManager" type="Node"] 9 | script = ExtResource("1_i4nu1") 10 | 11 | [node name="Explosion" type="AudioStreamPlayer2D" parent="."] 12 | stream = ExtResource("2_gpll2") 13 | volume_db = -10.0 14 | bus = &"Explosion" 15 | 16 | [node name="PlayerHit" type="AudioStreamPlayer2D" parent="."] 17 | stream = ExtResource("3_t6mi4") 18 | volume_db = -5.0 19 | bus = &"PlayerHit" 20 | 21 | [node name="Fire" type="AudioStreamPlayer" parent="."] 22 | stream = ExtResource("4_2bt1m") 23 | volume_db = -10.0 24 | -------------------------------------------------------------------------------- /Game/UI/Fps.cs: -------------------------------------------------------------------------------- 1 | namespace Game.UI; 2 | 3 | public partial class Fps : Label 4 | { 5 | public override void _PhysicsProcess(double delta) 6 | { 7 | Text = $"{Engine.GetFramesPerSecond()}/s"; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Game/UI/Fps.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=4 format=3] 2 | 3 | [ext_resource type="Script" path="res://Game/UI/Fps.cs" id="1_nj1k4"] 4 | 5 | [sub_resource type="SystemFont" id="SystemFont_8kl1n"] 6 | subpixel_positioning = 0 7 | 8 | [sub_resource type="LabelSettings" id="LabelSettings_c4xv5"] 9 | font = SubResource("SystemFont_8kl1n") 10 | font_size = 20 11 | outline_size = 4 12 | outline_color = Color(0, 0, 0, 1) 13 | 14 | [node name="FPS" type="Label"] 15 | offset_right = 40.0 16 | offset_bottom = 23.0 17 | text = "120 FPS" 18 | label_settings = SubResource("LabelSettings_c4xv5") 19 | script = ExtResource("1_nj1k4") 20 | -------------------------------------------------------------------------------- /Game/UI/RateStats.cs: -------------------------------------------------------------------------------- 1 | namespace Game.UI; 2 | 3 | using Game.Autoload; 4 | 5 | public partial class RateStats : Control 6 | { 7 | [Node] 8 | public Timer Timer; 9 | 10 | [Node] 11 | public Label HitRate; 12 | 13 | [Node] 14 | public Label MaxRate; 15 | 16 | private int _counter; 17 | private int _rate; 18 | private int _maxRate; 19 | 20 | public override void _EnterTree() 21 | { 22 | this.WireNodes(); 23 | } 24 | 25 | public override void _Ready() 26 | { 27 | Timer.Timeout += () => 28 | { 29 | _rate = _counter; 30 | _counter = 0; 31 | HitRate.Text = $"Hits: {_rate:D0}/s"; 32 | }; 33 | GameEvents.Instance.PlayerHit += (_, _, _) => 34 | { 35 | _counter += 1; 36 | if (_maxRate <= _counter) 37 | { 38 | _maxRate = _counter; 39 | MaxRate.Text = $"Max: {_maxRate:D0}"; 40 | } 41 | }; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Game/UI/Score.cs: -------------------------------------------------------------------------------- 1 | namespace Game.UI; 2 | 3 | using Game.Autoload; 4 | 5 | public partial class Score : Label 6 | { 7 | private int _totalScore; 8 | private Vector2 _pivot; 9 | 10 | public override void _Ready() 11 | { 12 | _pivot = Size / 2.0f; 13 | 14 | GameEvents.Instance.PlayerHit += (_, _, _) => 15 | { 16 | _totalScore += 1; 17 | Text = $"Score: {_totalScore}"; 18 | 19 | _pivot.X = Size.X; 20 | PivotOffset = _pivot; 21 | 22 | var tween = CreateTween(); 23 | tween.TweenProperty( 24 | this, 25 | Node2D.PropertyName.Scale.ToString(), 26 | Vector2.One, 27 | 0.25f) 28 | .From(Vector2.One * 1.8f); 29 | }; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Game/UI/ZombieCounter.cs: -------------------------------------------------------------------------------- 1 | namespace Game.UI; 2 | 3 | using Game.Autoload; 4 | 5 | public partial class ZombieCounter : Label 6 | { 7 | private int _totalZombies; 8 | 9 | public override void _Ready() 10 | { 11 | GameEvents.Instance.ZombieSpawned += _ => 12 | { 13 | _totalZombies += 1; 14 | Text = $"Zombies: {_totalZombies}"; 15 | }; 16 | 17 | GameEvents.Instance.ZombieKilled += _ => 18 | { 19 | _totalZombies -= 1; 20 | Text = $"Zombies: {_totalZombies}"; 21 | }; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /GodotUtilities/ChildNodeAttribute.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Reflection; 4 | 5 | namespace GodotUtilities; 6 | 7 | [AttributeUsage(AttributeTargets.All, AllowMultiple = false)] 8 | public class NodeAttribute : Attribute 9 | { 10 | public string NodePath { get; } 11 | 12 | public NodeAttribute(string nodePath = null) 13 | { 14 | NodePath = nodePath; 15 | } 16 | } 17 | 18 | public static class ChildNodeAttributeExtension 19 | { 20 | private const BindingFlags BINDING_FLAGS = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; 21 | 22 | public static void WireNodes(this Node n) 23 | { 24 | var lowerCaseChildNameToChild = n.GetChildren().Cast().ToDictionary(x => x.Name.ToString().ToLowerInvariant(), x => x); 25 | var fields = n.GetType().GetFields(BINDING_FLAGS); 26 | foreach (var memberInfo in fields) 27 | { 28 | SetChildNode(n, memberInfo, lowerCaseChildNameToChild); 29 | SetParentNode(n, memberInfo); 30 | } 31 | 32 | var properties = n.GetType().GetProperties(BINDING_FLAGS); 33 | foreach (var memberInfo in properties) 34 | { 35 | SetChildNode(n, memberInfo, lowerCaseChildNameToChild); 36 | SetParentNode(n, memberInfo); 37 | } 38 | } 39 | 40 | private static void SetChildNode(Node node, MemberInfo memberInfo, Dictionary lowerCaseChildNameToChild) 41 | { 42 | var attribute = Attribute.GetCustomAttribute(memberInfo, typeof(NodeAttribute)); 43 | if (attribute is not NodeAttribute childNodeAttribute) 44 | { 45 | return; 46 | } 47 | 48 | Node childNode; 49 | if (childNodeAttribute.NodePath != null) 50 | { 51 | childNode = node.GetNodeOrNull(childNodeAttribute.NodePath); 52 | } 53 | else 54 | { 55 | var memberNameLower = memberInfo.Name.ToLower(); 56 | var lookupSuccess = lowerCaseChildNameToChild.TryGetValue(memberNameLower, out childNode); 57 | if (!lookupSuccess) 58 | { 59 | childNode = TryGetUniqueNode(node, memberInfo); 60 | 61 | if (childNode == null) 62 | { 63 | childNode = lowerCaseChildNameToChild 64 | .Where(x => memberNameLower.Contains(x.Key)) 65 | .OrderByDescending(x => x.Key.Length) 66 | .FirstOrDefault().Value; 67 | 68 | if (childNode != null) 69 | { 70 | GD.PushWarning($"Assigned member {memberInfo.Name} to node {childNode.Name} in {node?.SceneFilePath ?? "the scene"} as a best-guess."); 71 | } 72 | } 73 | } 74 | } 75 | if (childNode == null) 76 | { 77 | var filename = !string.IsNullOrEmpty(node.SceneFilePath) ? node.SceneFilePath : "the scene."; 78 | GD.PrintErr($"Could not match member {memberInfo.Name} to any Node in {filename}."); 79 | } 80 | 81 | try 82 | { 83 | SetMemberValue(node, memberInfo, childNode); 84 | } 85 | catch 86 | { 87 | var filename = !string.IsNullOrEmpty(node.SceneFilePath) ? node.SceneFilePath : "the scene."; 88 | Type memberType = memberInfo.GetUnderlyingType(); 89 | if (!memberType.IsAssignableFrom(childNode.GetType())) 90 | { 91 | GD.PrintErr($"Could not match member {memberInfo.Name} to any Node of type {memberType} in {filename}. Found {childNode.GetType()}."); 92 | } 93 | } 94 | } 95 | 96 | private static void SetParentNode(Node node, MemberInfo memberInfo) 97 | { 98 | var attribute = Attribute.GetCustomAttribute(memberInfo, typeof(ParentAttribute)); 99 | if (attribute is not ParentAttribute) 100 | { 101 | return; 102 | } 103 | 104 | Node parentNode = node.GetParent(); 105 | SetMemberValue(node, memberInfo, parentNode); 106 | } 107 | 108 | private static void SetMemberValue(Node node, MemberInfo memberInfo, Node childNode) 109 | { 110 | if (memberInfo is PropertyInfo propertyInfo) 111 | { 112 | propertyInfo.SetValue(node, childNode); 113 | } 114 | else if (memberInfo is FieldInfo fieldInfo) 115 | { 116 | fieldInfo.SetValue(node, childNode); 117 | } 118 | } 119 | 120 | private static Node TryGetUniqueNode(Node node, MemberInfo memberInfo) 121 | { 122 | var name = memberInfo.Name; 123 | var child = node.GetNodeOrNull($"%{name}"); 124 | if (child == null && name.Length > 1) 125 | { 126 | name = string.Concat(name[0].ToString().ToUpper(), name.AsSpan(1)); 127 | child = node.GetNodeOrNull($"%{name}"); 128 | } 129 | return child; 130 | } 131 | 132 | public static Type GetUnderlyingType(this MemberInfo member) 133 | { 134 | return member.MemberType switch 135 | { 136 | MemberTypes.Event => ((EventInfo)member).EventHandlerType, 137 | MemberTypes.Field => ((FieldInfo)member).FieldType, 138 | MemberTypes.Method => ((MethodInfo)member).ReturnType, 139 | MemberTypes.Property => ((PropertyInfo)member).PropertyType, 140 | _ => throw new ArgumentException("Input MemberInfo must be of type EventInfo, FieldInfo, MethodInfo, or PropertyInfo"), 141 | }; 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /GodotUtilities/Collections/DoubleDictionary.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | 4 | namespace GodotUtilities.Collections; 5 | 6 | public class DoubleDictionary : IDictionary 7 | { 8 | private readonly Dictionary _keyToValue = new(); 9 | private readonly Dictionary _valueToKey = new(); 10 | 11 | public TValue this[TKey key] 12 | { 13 | get => _keyToValue[key]; 14 | set 15 | { 16 | if (_keyToValue.ContainsKey(key)) 17 | { 18 | var oldVal = _keyToValue[key]; 19 | _valueToKey.Remove(oldVal); 20 | } 21 | _keyToValue[key] = value; 22 | _valueToKey[value] = key; 23 | } 24 | } 25 | 26 | public TKey this[TValue val] 27 | { 28 | get => _valueToKey[val]; 29 | set 30 | { 31 | if (_valueToKey.ContainsKey(val)) 32 | { 33 | var oldVal = _valueToKey[val]; 34 | _keyToValue.Remove(oldVal); 35 | } 36 | _valueToKey[val] = value; 37 | _keyToValue[value] = val; 38 | } 39 | } 40 | 41 | public ICollection Keys => _keyToValue.Keys; 42 | 43 | public ICollection Values => _valueToKey.Keys; 44 | 45 | public int Count => _keyToValue.Count; 46 | 47 | public bool IsReadOnly => false; 48 | 49 | public void Add(TKey key, TValue value) 50 | { 51 | _keyToValue[key] = value; 52 | _valueToKey[value] = key; 53 | } 54 | 55 | public void Add(KeyValuePair item) 56 | { 57 | _keyToValue[item.Key] = item.Value; 58 | _valueToKey[item.Value] = item.Key; 59 | } 60 | 61 | public void Clear() 62 | { 63 | _keyToValue.Clear(); 64 | _valueToKey.Clear(); 65 | } 66 | 67 | public bool Contains(KeyValuePair item) 68 | { 69 | return _keyToValue.ContainsKey(item.Key) && _valueToKey.ContainsKey(item.Value); 70 | } 71 | 72 | public bool ContainsKey(TKey key) 73 | { 74 | return _keyToValue.ContainsKey(key); 75 | } 76 | 77 | public bool ContainsKey(TValue value) 78 | { 79 | return _valueToKey.ContainsKey(value); 80 | } 81 | 82 | public void CopyTo(KeyValuePair[] array, int arrayIndex) 83 | { } 84 | 85 | public IEnumerator> GetEnumerator() 86 | { 87 | return _keyToValue.GetEnumerator(); 88 | } 89 | 90 | public bool Remove(TKey key) 91 | { 92 | if (_keyToValue.ContainsKey(key)) 93 | { 94 | var val = _keyToValue[key]; 95 | _keyToValue.Remove(key); 96 | _valueToKey.Remove(val); 97 | return true; 98 | } 99 | return false; 100 | } 101 | 102 | public bool Remove(TValue value) 103 | { 104 | if (_valueToKey.ContainsKey(value)) 105 | { 106 | var key = _valueToKey[value]; 107 | _valueToKey.Remove(value); 108 | _keyToValue.Remove(key); 109 | return true; 110 | } 111 | return false; 112 | } 113 | 114 | public bool Remove(KeyValuePair item) 115 | { 116 | return Remove(item.Key); 117 | } 118 | 119 | public bool TryGetValue(TKey key, out TValue value) 120 | { 121 | if (_keyToValue.ContainsKey(key)) 122 | { 123 | value = _keyToValue[key]; 124 | return true; 125 | } 126 | value = default; 127 | return false; 128 | } 129 | 130 | public bool TryGetValue(TValue value, out TKey key) 131 | { 132 | if (_valueToKey.ContainsKey(value)) 133 | { 134 | key = _valueToKey[value]; 135 | return true; 136 | } 137 | key = default; 138 | return false; 139 | } 140 | 141 | IEnumerator IEnumerable.GetEnumerator() 142 | { 143 | return _keyToValue.GetEnumerator(); 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /GodotUtilities/Extension/AudioStreamPlayerExtension.cs: -------------------------------------------------------------------------------- 1 | namespace GodotUtilities; 2 | 3 | public static class AudioStreamPlayerExtension 4 | { 5 | public static void PlayWithPitchRange(this AudioStreamPlayer audioStreamPlayer, float minPitchScale, float maxPitchScale) 6 | { 7 | audioStreamPlayer.PitchScale = MathUtil.RNG.RandfRange(minPitchScale, maxPitchScale); 8 | audioStreamPlayer.Play(); 9 | } 10 | 11 | public static void PlayWithPitch(this AudioStreamPlayer audioStreamPlayer, float pitchScale) 12 | { 13 | audioStreamPlayer.PitchScale = pitchScale; 14 | audioStreamPlayer.Play(); 15 | } 16 | 17 | public static void PlayWithPitch(this AudioStreamPlayer2D audioStreamPlayer, float pitchScale) 18 | { 19 | audioStreamPlayer.PitchScale = pitchScale; 20 | audioStreamPlayer.Play(); 21 | } 22 | 23 | public static void PlayWithPitchRange(this AudioStreamPlayer2D audioStreamPlayer, float minPitchScale, float maxPitchScale) 24 | { 25 | audioStreamPlayer.PitchScale = MathUtil.RNG.RandfRange(minPitchScale, maxPitchScale); 26 | audioStreamPlayer.Play(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /GodotUtilities/Extension/ControlExtension.cs: -------------------------------------------------------------------------------- 1 | namespace GodotUtilities; 2 | 3 | public static class ControlExtension 4 | { 5 | public static void CenterPivotOffset(this Control control) 6 | { 7 | control.PivotOffset = control.Size / 2f; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /GodotUtilities/Extension/Node2DExtension.cs: -------------------------------------------------------------------------------- 1 | namespace GodotUtilities; 2 | 3 | public static class Node2DExtension 4 | { 5 | public static Vector2 GetMouseDirection(this Node2D node) 6 | { 7 | return (node.GetGlobalMousePosition() - node.GlobalPosition).Normalized(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /GodotUtilities/Extension/NodeExtension.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace GodotUtilities; 5 | 6 | public static class NodeExtension 7 | { 8 | /// 9 | /// Adds the Node to a group with a name equal to the Node's type name. 10 | /// 11 | /// 12 | public static void AddToGroup(this Node node) 13 | { 14 | node.AddToGroup(node.GetType().Name); 15 | } 16 | 17 | public static T GetSibling(this Node node, int idx) where T : Node 18 | { 19 | return (T)node.GetParent().GetChild(idx); 20 | } 21 | 22 | public static T GetNode(this Node node) where T : Node 23 | { 24 | return node.GetNode(typeof(T).Name); 25 | } 26 | 27 | public static List GetChildren(this Node node) where T : class 28 | { 29 | var children = node.GetChildren().Cast(); 30 | return children.Select(x => x as T).ToList(); 31 | } 32 | 33 | public static T GetFirstNodeOfType(this Node node) 34 | { 35 | var children = node.GetChildren(); 36 | foreach (var child in children) 37 | { 38 | if (child is T t) 39 | { 40 | return t; 41 | } 42 | } 43 | return default; 44 | } 45 | 46 | public static List GetNodesOfType(this Node node) 47 | { 48 | var result = new List(); 49 | var children = node.GetChildren(); 50 | foreach (var child in children) 51 | { 52 | if (child is T t) 53 | { 54 | result.Add(t); 55 | } 56 | } 57 | return result; 58 | } 59 | 60 | public static void AddChildDeferred(this Node node, Node child) 61 | { 62 | node.CallDeferred("add_child", child); 63 | } 64 | 65 | public static T GetNullableNodePath(this Node n, NodePath nodePath) where T : Node 66 | { 67 | if (nodePath == null) return null; 68 | return n.GetNodeOrNull(nodePath); 69 | } 70 | 71 | /// 72 | /// Removes the node's children from the scene tree and then queues them for deletion. 73 | /// 74 | /// 75 | /// 76 | public static void RemoveAndQueueFreeChildren(this Node n) 77 | { 78 | foreach (var child in n.GetChildren()) 79 | { 80 | if (child is Node childNode) 81 | { 82 | n.RemoveChild(childNode); 83 | childNode.QueueFree(); 84 | } 85 | } 86 | } 87 | 88 | /// 89 | /// Queues all child nodes for deletion. 90 | /// 91 | /// 92 | /// 93 | public static void QueueFreeChildren(this Node n) 94 | { 95 | foreach (var child in n.GetChildren()) 96 | { 97 | if (child is Node childNode) 98 | { 99 | childNode.QueueFree(); 100 | } 101 | } 102 | } 103 | 104 | public static T GetAncestor(this Node n) where T : Node 105 | { 106 | Node currentNode = n; 107 | while (currentNode != n.GetTree().Root && currentNode is not T) 108 | { 109 | currentNode = currentNode.GetParent(); 110 | } 111 | 112 | return currentNode is T ancestor ? ancestor : null; 113 | } 114 | 115 | public static Node GetLastChild(this Node n) 116 | { 117 | var count = n.GetChildCount(); 118 | if (count == 0) return null; 119 | return n.GetChild(count - 1); 120 | } 121 | 122 | public static void QueueFreeDeferred(this Node n) 123 | { 124 | n.CallDeferred("queue_free"); 125 | } 126 | 127 | public static void QueueFreeAll(this IEnumerable objects) 128 | { 129 | foreach (var obj in objects) 130 | { 131 | obj.QueueFree(); 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /GodotUtilities/Extension/PackedSceneExtension.cs: -------------------------------------------------------------------------------- 1 | namespace GodotUtilities; 2 | 3 | public static class PackedSceneExtension 4 | { 5 | public static T InstanceOrFree(this PackedScene scene) where T : class 6 | { 7 | var node = scene.Instantiate(); 8 | if (node is T t) 9 | { 10 | return t; 11 | } 12 | node.QueueFree(); 13 | GD.PushWarning($"Could not instance PackedScene {scene} as {typeof(T).Name}"); 14 | return null; 15 | } 16 | 17 | /// 18 | /// Instances the scene and passes it as an argument to the supplied action. Allows for "fetching" data from a packed scene without immediately using it. 19 | /// 20 | /// 21 | /// 22 | /// 23 | public static void ExtractData(this PackedScene scene, Action action) where T : Node 24 | { 25 | var node = scene.InstanceOrFree(); 26 | action(node); 27 | node.QueueFree(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /GodotUtilities/Extension/Particles2DExtension.cs: -------------------------------------------------------------------------------- 1 | namespace GodotUtilities; 2 | 3 | public static class Particles2DExtension 4 | { 5 | public static void SetDirection(this GpuParticles2D particles, Vector2 direction) 6 | { 7 | if (particles.ProcessMaterial is ParticleProcessMaterial material) 8 | { 9 | material.Direction = new Vector3(direction.X, direction.Y, 0f); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /GodotUtilities/Extension/Physics2DDirectSpaceStateExtension.cs: -------------------------------------------------------------------------------- 1 | using GodotUtilities.Util; 2 | 3 | namespace GodotUtilities; 4 | 5 | public static class Physics2DDirectSpaceStateExtensions 6 | { 7 | /// 8 | /// Returns a RaycastResult if there is a collision, otherwise returns null. 9 | /// 10 | /// 11 | /// 12 | /// 13 | /// 14 | /// 15 | /// 16 | /// 17 | /// 18 | public static RaycastResult Raycast(this PhysicsDirectSpaceState2D state, PhysicsRayQueryParameters2D query) 19 | { 20 | var raycastDict = state.IntersectRay(query); 21 | if (raycastDict?.Count > 0) 22 | { 23 | return new RaycastResult(query.From, query.To, raycastDict); 24 | } 25 | return null; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /GodotUtilities/Extension/ResourcePreloaderExtension.cs: -------------------------------------------------------------------------------- 1 | using GodotUtilities.Util; 2 | 3 | namespace GodotUtilities; 4 | 5 | public static class ResourcePreloaderExtension 6 | { 7 | /// 8 | /// Instances a scene with the resource name. Returns null if resource was not found or was not a packed scene. 9 | /// 10 | /// 11 | /// 12 | /// 13 | /// 14 | public static T InstanceSceneOrNull(this ResourcePreloader preloader, string name) where T : Node 15 | { 16 | if (!preloader.HasResource(name)) 17 | { 18 | Logger.Error("Preloader did not have a resource with name " + name); 19 | return null; 20 | } 21 | 22 | if (!(preloader.GetResource(name) is PackedScene resource)) 23 | { 24 | Logger.Error("Resource with name " + name + " was not a " + nameof(PackedScene)); 25 | return null; 26 | } 27 | 28 | return resource.InstantiateOrNull(); 29 | } 30 | 31 | public static T InstanceSceneOrNull(this ResourcePreloader preloader) where T : Node 32 | { 33 | return preloader.InstanceSceneOrNull(typeof(T).Name); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /GodotUtilities/Extension/SceneTreeExtension.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | 5 | namespace GodotUtilities; 6 | 7 | public static class SceneTreeExtension 8 | { 9 | /// 10 | /// Gets the first Node as T in the group provided. 11 | /// 12 | /// 13 | /// 14 | /// 15 | /// 16 | public static T GetFirstNodeInGroup(this SceneTree sceneTree, string group) where T : Node 17 | { 18 | var nodes = sceneTree.GetNodesInGroup(group); 19 | return nodes.Count > 0 ? nodes[0] as T : null; 20 | } 21 | 22 | /// 23 | /// Gets the first Node as T using T's typename as the group name. 24 | /// 25 | /// 26 | /// 27 | /// 28 | public static T GetFirstNodeInGroup(this SceneTree sceneTree) where T : Node 29 | { 30 | var name = typeof(T).Name; 31 | return GetFirstNodeInGroup(sceneTree, name); 32 | } 33 | 34 | public static IEnumerable GetNodesInGroup(this SceneTree sceneTree, string group) where T : Node 35 | { 36 | return sceneTree.GetNodesInGroup(group).Cast(); 37 | } 38 | 39 | public static IEnumerable GetNodesInGroup(this SceneTree sceneTree) where T : Node 40 | { 41 | var name = typeof(T).Name; 42 | return GetNodesInGroup(sceneTree, name); 43 | } 44 | 45 | public static async Task NextIdle(this SceneTree sceneTree) 46 | { 47 | await sceneTree.ToSignal(sceneTree, SceneTree.SignalName.ProcessFrame); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /GodotUtilities/Extension/VectorExtension.cs: -------------------------------------------------------------------------------- 1 | namespace GodotUtilities; 2 | 3 | public static class VectorExtension 4 | { 5 | private const float ALPHA = .9604339f; 6 | private const float BETA = .3978247f; 7 | 8 | public static float ApproximateLength(this Vector2 vec) 9 | { 10 | var absVec = vec.Abs(); 11 | var min = Mathf.Min(absVec.X, absVec.Y); 12 | var max = Mathf.Max(absVec.X, absVec.Y); 13 | return (ALPHA * max) + (BETA * min); 14 | } 15 | 16 | public static Vector2 RotatedDegrees(this Vector2 v, float degrees) 17 | { 18 | return v.Rotated(Mathf.DegToRad(degrees)); 19 | } 20 | 21 | public static bool IsWithinDistanceSquared(this Vector2 v1, Vector2 v2, float distance) 22 | { 23 | return v1.DistanceSquaredTo(v2) <= distance * distance; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /GodotUtilities/Logic/DelegateStateMachine.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace GodotUtilities.Logic; 4 | 5 | public partial class DelegateStateMachine : RefCounted 6 | { 7 | public delegate void State(); 8 | 9 | private State currentState; 10 | 11 | private readonly Dictionary states = new(); 12 | 13 | public void AddStates(State normal, State enterState = null, State leaveState = null) 14 | { 15 | var stateFlows = new StateFlows(normal, enterState, leaveState); 16 | states[normal] = stateFlows; 17 | } 18 | 19 | public void ChangeState(State toStateDelegate) 20 | { 21 | states.TryGetValue(toStateDelegate, out var stateDelegates); 22 | Callable.From(() => SetState(stateDelegates)).CallDeferred(); 23 | } 24 | 25 | public void SetInitialState(State stateDelegate) 26 | { 27 | states.TryGetValue(stateDelegate, out var stateFlows); 28 | SetState(stateFlows); 29 | } 30 | 31 | public State GetCurrentState() 32 | { 33 | return currentState; 34 | } 35 | 36 | public void Update() 37 | { 38 | currentState?.Invoke(); 39 | } 40 | 41 | private void SetState(StateFlows stateFlows) 42 | { 43 | if (currentState != null) 44 | { 45 | states.TryGetValue(currentState, out var currentStateDelegates); 46 | currentStateDelegates?.LeaveState?.Invoke(); 47 | } 48 | currentState = stateFlows.Normal; 49 | stateFlows?.EnterState?.Invoke(); 50 | } 51 | 52 | private class StateFlows 53 | { 54 | public State Normal { get; private set; } 55 | public State EnterState { get; private set; } 56 | public State LeaveState { get; private set; } 57 | 58 | public StateFlows(State normal, State enterState = null, State leaveState = null) 59 | { 60 | Normal = normal; 61 | EnterState = enterState; 62 | LeaveState = leaveState; 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /GodotUtilities/Logic/ImmediateStateMachine.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace GodotUtilities.Logic; 4 | 5 | /// 6 | /// A state machine designed for states that don't need to be updated every frame. 7 | /// 8 | /// 9 | public class ImmediateStateMachine 10 | { 11 | public delegate void StateDelegate(); 12 | 13 | public T CurrentState { get; private set; } 14 | 15 | private readonly Dictionary _states = new(); 16 | private readonly Dictionary _delegates = new(); 17 | private readonly Dictionary _leaveStates = new(); 18 | 19 | public void AddState(T state, StateDelegate del) 20 | { 21 | _states.Add(state, del); 22 | _delegates.Add(del, state); 23 | } 24 | 25 | public void AddLeaveState(T stateToLeave, StateDelegate del) 26 | { 27 | _leaveStates.Add(stateToLeave, del); 28 | } 29 | 30 | public void ChangeState(T state) 31 | { 32 | if (_leaveStates.ContainsKey(CurrentState)) 33 | { 34 | _leaveStates[CurrentState](); 35 | } 36 | CurrentState = state; 37 | if (_states.ContainsKey(CurrentState)) 38 | { 39 | _states[CurrentState](); 40 | } 41 | } 42 | 43 | public void ChangeState(StateDelegate stateDelegate) 44 | { 45 | ChangeState(_delegates[stateDelegate]); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /GodotUtilities/Logic/LootTable.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace GodotUtilities.Logic; 5 | 6 | public class LootTable 7 | { 8 | public int WeightSum { get; protected set; } 9 | private readonly List table = new(); 10 | private RandomNumberGenerator random; 11 | 12 | public class TableData 13 | { 14 | public T Obj { get; private set; } 15 | public int Weight { get; private set; } 16 | 17 | public TableData(T o, int w) 18 | { 19 | Obj = o; 20 | Weight = w; 21 | } 22 | } 23 | 24 | public LootTable() 25 | { 26 | random = MathUtil.RNG; 27 | } 28 | 29 | public void SetSeed(ulong seed) 30 | { 31 | random.Seed = seed; 32 | } 33 | 34 | public void SetRandom(RandomNumberGenerator random) 35 | { 36 | this.random = random; 37 | } 38 | 39 | public void AddItem(T obj, int weight) 40 | { 41 | table.Add(new TableData(obj, weight)); 42 | CalculateWeightSum(); 43 | } 44 | 45 | public void AddItem(TableData tableData) 46 | { 47 | table.Add(tableData); 48 | CalculateWeightSum(); 49 | } 50 | 51 | public void AddRange(List range) 52 | { 53 | table.AddRange(range); 54 | CalculateWeightSum(); 55 | } 56 | 57 | public void SetData(List tableData) 58 | { 59 | table.Clear(); 60 | AddRange(tableData); 61 | } 62 | 63 | public T PickItem() 64 | { 65 | return PickItem(table, WeightSum); 66 | } 67 | 68 | public TableData PickTableData() 69 | { 70 | return PickTableData(); 71 | } 72 | 73 | /// 74 | /// Picks an item from the loot table and returns it if returns true. 75 | /// 76 | /// 77 | /// 78 | public T PickItemConditional(Func canPickConditionalFn) 79 | { 80 | var tableCopy = table.ToList(); 81 | var weightSum = WeightSum; 82 | 83 | TableData pickedTableData; 84 | do 85 | { 86 | pickedTableData = PickTableData(tableCopy, weightSum); 87 | if (pickedTableData != null) 88 | { 89 | weightSum -= pickedTableData.Weight; 90 | tableCopy.Remove(pickedTableData); 91 | } 92 | } while (pickedTableData != null && !canPickConditionalFn(pickedTableData)); 93 | 94 | return pickedTableData != null ? pickedTableData.Obj : default; 95 | } 96 | 97 | public T PickRange(int startIdx, int count) 98 | { 99 | var range = table.GetRange(startIdx, count); 100 | var weightSum = range.Sum(x => x.Weight); 101 | return PickItem(range, weightSum); 102 | } 103 | 104 | public List GetLootTableItems() 105 | { 106 | return table.Select(x => x.Obj).ToList(); 107 | } 108 | 109 | public List GetLootTableData() 110 | { 111 | return table; 112 | } 113 | 114 | public int GetCount() 115 | { 116 | return table.Count; 117 | } 118 | 119 | public void CalculateWeightSum() 120 | { 121 | WeightSum = table.Sum(x => x.Weight); 122 | } 123 | 124 | private T PickItem(List table, int weightSum) 125 | { 126 | var tableData = PickTableData(table, weightSum); 127 | return tableData != null ? tableData.Obj : default; 128 | } 129 | 130 | private TableData PickTableData(List table, int weightSum) 131 | { 132 | int sum = 0; 133 | int val = random.RandiRange(1, weightSum); 134 | foreach (var data in table) 135 | { 136 | sum += data.Weight; 137 | if (val <= sum) 138 | { 139 | return data; 140 | } 141 | } 142 | return null; 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /GodotUtilities/Logic/StateMachine.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace GodotUtilities.Logic; 4 | 5 | public partial class StateMachine : RefCounted 6 | { 7 | public delegate void StateDelegate(); 8 | 9 | private T currentState; 10 | 11 | private readonly Dictionary states = new(); 12 | private readonly Dictionary delegates = new(); 13 | private readonly Dictionary leaveStates = new(); 14 | private readonly Dictionary enterStates = new(); 15 | 16 | public void AddState(T state, StateDelegate del) 17 | { 18 | states.Add(state, del); 19 | delegates.Add(del, state); 20 | } 21 | 22 | public void AddLeaveState(T stateToLeave, StateDelegate del) 23 | { 24 | leaveStates.Add(stateToLeave, del); 25 | } 26 | 27 | public void AddEnterState(T enterState, StateDelegate del) 28 | { 29 | enterStates.Add(enterState, del); 30 | } 31 | 32 | public void ChangeState(T state) 33 | { 34 | Callable.From(() => SetState(state)).CallDeferred(); 35 | } 36 | 37 | public void ChangeState(StateDelegate stateDelegate) 38 | { 39 | ChangeState(delegates[stateDelegate]); 40 | } 41 | 42 | public void SetInitialState(T state) 43 | { 44 | SetState(state); 45 | } 46 | 47 | public void SetInitialState(StateDelegate del) 48 | { 49 | SetInitialState(delegates[del]); 50 | } 51 | 52 | public T GetCurrentState() 53 | { 54 | return currentState; 55 | } 56 | 57 | public void Update() 58 | { 59 | if (states.ContainsKey(currentState)) 60 | { 61 | states[currentState](); 62 | } 63 | } 64 | 65 | private void SetState(T state) 66 | { 67 | if (leaveStates.ContainsKey(currentState)) 68 | { 69 | leaveStates[currentState](); 70 | } 71 | currentState = state; 72 | if (enterStates.ContainsKey(currentState)) 73 | { 74 | enterStates[currentState](); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /GodotUtilities/ParentNodeAttribute.cs: -------------------------------------------------------------------------------- 1 | namespace GodotUtilities; 2 | 3 | [AttributeUsage(AttributeTargets.All, AllowMultiple = false)] 4 | public class ParentAttribute : Attribute 5 | { 6 | public ParentAttribute() 7 | { } 8 | } 9 | -------------------------------------------------------------------------------- /GodotUtilities/ProjectSettingsExtended.cs: -------------------------------------------------------------------------------- 1 | namespace GodotUtilities; 2 | 3 | public static class ProjectSettingsExtended 4 | { 5 | // TODO: check if this casting of variant works 6 | public static T GetSettingOrDefault(string name) 7 | { 8 | return ProjectSettings.HasSetting(name) ? (T)(object)ProjectSettings.GetSetting(name) : default; 9 | } 10 | 11 | public static T GetDebugSettingOrDefault(string name) 12 | { 13 | return OS.IsDebugBuild() ? GetSettingOrDefault(name) : default; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /GodotUtilities/Util/FileSystem.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace GodotUtilities.Util; 4 | 5 | public static class FileSystem 6 | { 7 | public static List InstanceScenesInPath(string dirPath) where T : Node 8 | { 9 | if (dirPath[^1] != '/') 10 | { 11 | dirPath += "/"; 12 | } 13 | 14 | var scenes = new List(); 15 | 16 | using var dir = DirAccess.Open(dirPath); 17 | if (dir == null) 18 | { 19 | Logger.Error("Could not open directory " + dirPath); 20 | return scenes; 21 | } 22 | 23 | dir.ListDirBegin(); 24 | while (true) 25 | { 26 | var path = dir.GetNext(); 27 | if (string.IsNullOrEmpty(path)) 28 | { 29 | break; 30 | } 31 | if (!path.Contains(".converted.res") && path.Contains(".tscn")) 32 | { 33 | path = path.Replace(".remap", ""); 34 | var fullPath = dirPath + path; 35 | 36 | if (GD.Load(fullPath) is PackedScene packedScene) 37 | { 38 | var scene = packedScene.Instantiate(); 39 | if (scene is T node) 40 | { 41 | scenes.Add(node); 42 | } 43 | else 44 | { 45 | scene.QueueFree(); 46 | } 47 | } 48 | } 49 | } 50 | dir.ListDirEnd(); 51 | 52 | return scenes; 53 | } 54 | 55 | public static List LoadResourcesInPath(string path) where T : Resource 56 | { 57 | using var dir = DirAccess.Open(path); 58 | var results = new List(); 59 | if (dir != null) 60 | { 61 | dir.ListDirBegin(); 62 | var fileName = dir.GetNext(); 63 | while (fileName != string.Empty) 64 | { 65 | if (!dir.CurrentIsDir()) 66 | { 67 | if (fileName.EndsWith(".converted.res") || fileName.EndsWith(".tres")) 68 | { 69 | fileName = fileName.Replace(".converted.res", string.Empty); 70 | var fullPath = $"{path}/{fileName}"; 71 | var resource = GD.Load(fullPath); 72 | if (resource is not T res) 73 | { 74 | GD.PushWarning($"Could not load resource at {fullPath} with type {typeof(T).Name}"); 75 | continue; 76 | } 77 | results.Add(res); 78 | } 79 | } 80 | fileName = dir.GetNext(); 81 | } 82 | dir.ListDirEnd(); 83 | } 84 | else 85 | { 86 | GD.PushWarning($"Could load resources in path {path}"); 87 | } 88 | return results; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /GodotUtilities/Util/Logger.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | 3 | namespace GodotUtilities.Util; 4 | 5 | public static class Logger 6 | { 7 | public static void Error(params object[] what) 8 | { 9 | GD.PrintErr(new object[] { "[ERROR] " }.Concat(what).ToArray()); 10 | GD.Print(System.Environment.StackTrace); 11 | } 12 | 13 | public static void Info(params object[] what) 14 | { 15 | GD.PrintRaw(new object[] { "[INFO] " }.Concat(what).ToArray()); 16 | GD.PrintRaw("\n"); 17 | } 18 | 19 | public static void Debug(params object[] what) 20 | { 21 | if (OS.IsDebugBuild()) 22 | { 23 | GD.PrintRaw(new object[] { "[DEBUG] " }.Concat(what).ToArray()); 24 | GD.PrintRaw("\n"); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /GodotUtilities/Util/MathUtil.cs: -------------------------------------------------------------------------------- 1 | namespace GodotUtilities; 2 | 3 | public static class MathUtil 4 | { 5 | public static RandomNumberGenerator RNG { get; private set; } = new RandomNumberGenerator(); 6 | 7 | static MathUtil() 8 | { 9 | RNG.Randomize(); 10 | } 11 | 12 | public static float DeltaLerp(float smoothing, float delta) 13 | { 14 | return 1f - Mathf.Pow(smoothing, delta); 15 | } 16 | 17 | public static void SeedRandomNumberGenerator(ulong seed) 18 | { 19 | RNG = new RandomNumberGenerator 20 | { 21 | Seed = seed 22 | }; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /GodotUtilities/Util/RaycastResult.cs: -------------------------------------------------------------------------------- 1 | using Godot.Collections; 2 | 3 | namespace GodotUtilities.Util; 4 | 5 | public class RaycastResult 6 | { 7 | public Vector2 Position { get; set; } 8 | public Vector2 Normal { get; set; } 9 | public GodotObject Collider { get; set; } 10 | public int ColliderId { get; set; } 11 | public Rid Rid { get; set; } 12 | public int Shape { get; set; } 13 | public Vector2 FromPosition { get; set; } 14 | public Vector2 ToPosition { get; set; } 15 | 16 | public RaycastResult(Vector2 from, Vector2 to, Dictionary resultDict) 17 | { 18 | FromPosition = from; 19 | ToPosition = to; 20 | Position = (Vector2)resultDict["position"]; 21 | Normal = (Vector2)resultDict["normal"]; 22 | Collider = (GodotObject)resultDict["collider"]; 23 | ColliderId = (int)resultDict["collider_id"]; 24 | Rid = (Rid)resultDict["rid"]; 25 | Shape = (int)resultDict["shape"]; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /GodotUtilities/Util/ShapecastResult.cs: -------------------------------------------------------------------------------- 1 | namespace GodotUtilities.Util; 2 | 3 | public class ShapecastResult 4 | { 5 | public Vector2 Point { get; set; } 6 | public Vector2 Normal { get; set; } 7 | public GodotObject Collider { get; set; } 8 | public int ColliderId { get; set; } 9 | public Rid Rid { get; set; } 10 | public int Shape { get; set; } 11 | public Vector2 FromPosition { get; set; } 12 | public Vector2 ToPosition { get; set; } 13 | public Vector2 LinearVelocity { get; set; } 14 | 15 | public ShapecastResult(Vector2 from, Vector2 to, Godot.Collections.Dictionary resultDict) 16 | { 17 | FromPosition = from; 18 | ToPosition = to; 19 | Point = (Vector2)resultDict["point"]; 20 | Normal = (Vector2)resultDict["normal"]; 21 | Collider = (GodotObject)resultDict["collider"]; 22 | ColliderId = (int)resultDict["collider_id"]; 23 | Rid = (Rid)resultDict["rid"]; 24 | Shape = (int)resultDict["shape"]; 25 | LinearVelocity = (Vector2)resultDict["linear_velocity"]; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 godotmancer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SampleGodotCSharpProject 2 | 3 | --- 4 | 5 | ![](https://badgen.net/badge/Godot%20Compatible/4.0RC5%2B/cyan) ![](https://badgen.net/badge/license/MIT/blue) 6 | 7 | A work-in-progress example Godot 4 project written in C# (with .Net 6.0), making use of the awesome utilities and game concepts from [@firebelley](https://github.com/firebelley) 8 | 9 | Shout-out to [@valkyrienyanko](https://github.com/Valks-Games) for being the first to contribute and clean-up the C# 10 | code - since this is my first attempt at programming in C#, [@valkyrienyanko](https://github.com/Valks-Games) Github 11 | [Wiki](https://github.com/Valks-Games/sankari/wiki/Code-Style) has been very helpful. 12 | 13 | ## Purpose of this project 14 | 15 | Develop game mechanics and patterns for Godot 4 using C#. 16 | Try to make the development experience as satisfying and consistent as possible. 17 | 18 | * Component based Scene Tree (Composition) 19 | * Use of global signals (Game Events) 20 | * Managers (Sound, Effects, Bonus) - subscribe to Game Events 21 | * Godot Class extensions (add missing helper methods) 22 | 23 | This project will also serve as a 'light' benchmark of the current `master` branch of Godot 4. 24 | 25 | ## How to build/run 26 | 27 | Make sure you are using latest **Godot 4** version (I build **Godot 4** from source nearly every day and will try to keep this repo up-to-date accordingly). 28 | 29 | I will try to keep the `GodotUtilities` folder from @firebelley in-sync with this project. 30 | 31 | Look around, improve and feedback welcome. 32 | 33 | ## Editor formatting and plugins 34 | 35 | In order to keep the source formatting consistent between JetBrains Rider and VSCode please make sure your VSCode OmniSharp settings are configured as shown below: 36 | 37 | ![vscode-formatting-settings.png](Screenshots%2Fvscode-formatting-settings.png) 38 | 39 | 40 | Install the following **VSCode** plugins for a better experience: 41 | 42 | [Godot Tools](https://marketplace.visualstudio.com/items?itemName=geequlim.godot-tools) 43 | 44 | [C# Tools for Godot](https://marketplace.visualstudio.com/items?itemName=neikeq.godot-csharp-vscode) 45 | 46 | [EditorConfig Support](https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig) 47 | 48 | 49 | Install the following **JetBrains Rider** plugins: 50 | 51 | [Godot Support](https://plugins.jetbrains.com/plugin/13882-godot-support) 52 | 53 | [GDScript Support](https://plugins.jetbrains.com/plugin/13107-gdscript) 54 | 55 | ## Godot Editor and JetBrains Rider Screen Layouts 56 | 57 | I have found that the following screen layout works optimally when developing Godot games with C#: 58 | 59 | ### Godot Editor Layout 60 | 61 | All editor sections/tabs are to the right. 62 | 63 | ![godot-editor-layout.png](Screenshots%2Fgodot-editor-layout.png) 64 | 65 | ### JetBrains Rider 66 | 67 | ![jetbrains-rider-layout.png](Screenshots%2Fjetbrains-rider-layout.png) 68 | 69 | 70 | ## Screenshots 71 | 72 | Splash screens generated by Stable Diffusion 😜. 73 | 74 | ![Splash Screen](Game%2FAssets%2Ffireball-logo-8.png) 75 | ![Game Screenshot](Screenshots%2Fgame-screenshot.png) 76 | 77 | ## Versioning 78 | 79 | Will eventually follow semantic versioning. 80 | 81 | ## License 82 | 83 | [MIT License](LICENSE) 84 | -------------------------------------------------------------------------------- /SampleGodotCSharpProject.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net6.0 4 | true 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /SampleGodotCSharpProject.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 12.00 2 | # Visual Studio 2012 3 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleGodotCSharpProject", "SampleGodotCSharpProject.csproj", "{C3FDBDE3-7292-4CBA-888E-CAA5EB4FB4E0}" 4 | EndProject 5 | Global 6 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 7 | Debug|Any CPU = Debug|Any CPU 8 | ExportDebug|Any CPU = ExportDebug|Any CPU 9 | ExportRelease|Any CPU = ExportRelease|Any CPU 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {C3FDBDE3-7292-4CBA-888E-CAA5EB4FB4E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 13 | {C3FDBDE3-7292-4CBA-888E-CAA5EB4FB4E0}.Debug|Any CPU.Build.0 = Debug|Any CPU 14 | {C3FDBDE3-7292-4CBA-888E-CAA5EB4FB4E0}.ExportDebug|Any CPU.ActiveCfg = ExportDebug|Any CPU 15 | {C3FDBDE3-7292-4CBA-888E-CAA5EB4FB4E0}.ExportDebug|Any CPU.Build.0 = ExportDebug|Any CPU 16 | {C3FDBDE3-7292-4CBA-888E-CAA5EB4FB4E0}.ExportRelease|Any CPU.ActiveCfg = ExportRelease|Any CPU 17 | {C3FDBDE3-7292-4CBA-888E-CAA5EB4FB4E0}.ExportRelease|Any CPU.Build.0 = ExportRelease|Any CPU 18 | EndGlobalSection 19 | EndGlobal 20 | -------------------------------------------------------------------------------- /Screenshots/.gdignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Screenshots/.gdignore -------------------------------------------------------------------------------- /Screenshots/game-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Screenshots/game-screenshot.png -------------------------------------------------------------------------------- /Screenshots/godot-editor-layout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Screenshots/godot-editor-layout.png -------------------------------------------------------------------------------- /Screenshots/jetbrains-rider-layout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Screenshots/jetbrains-rider-layout.png -------------------------------------------------------------------------------- /Screenshots/splash-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Screenshots/splash-screen.png -------------------------------------------------------------------------------- /Screenshots/vscode-formatting-settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Screenshots/vscode-formatting-settings.png -------------------------------------------------------------------------------- /ScriptTemplates/.gdignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/ScriptTemplates/.gdignore -------------------------------------------------------------------------------- /Videos/.gdignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godotmancer/SampleGodotCSharpProject/84452a0bb15818e88e5f67a740bf00f05e61b6f5/Videos/.gdignore -------------------------------------------------------------------------------- /default_bus_layout.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="AudioBusLayout" load_steps=4 format=3 uid="uid://bn8raujndxxgs"] 2 | 3 | [sub_resource type="AudioEffectReverb" id="AudioEffectReverb_rrjej"] 4 | resource_name = "Reverb" 5 | 6 | [sub_resource type="AudioEffectReverb" id="AudioEffectReverb_3on17"] 7 | resource_name = "Reverb" 8 | 9 | [sub_resource type="AudioEffectDelay" id="AudioEffectDelay_8bibw"] 10 | resource_name = "Delay" 11 | 12 | [resource] 13 | bus/1/name = &"Explosion" 14 | bus/1/solo = false 15 | bus/1/mute = false 16 | bus/1/bypass_fx = false 17 | bus/1/volume_db = 0.0 18 | bus/1/send = &"EXplosion 2" 19 | bus/1/effect/0/effect = SubResource("AudioEffectReverb_rrjej") 20 | bus/1/effect/0/enabled = true 21 | bus/2/name = &"PlayerHit" 22 | bus/2/solo = false 23 | bus/2/mute = false 24 | bus/2/bypass_fx = false 25 | bus/2/volume_db = 0.0 26 | bus/2/send = &"Explosion 2" 27 | bus/2/effect/0/effect = SubResource("AudioEffectReverb_3on17") 28 | bus/2/effect/0/enabled = false 29 | bus/2/effect/1/effect = SubResource("AudioEffectDelay_8bibw") 30 | bus/2/effect/1/enabled = true 31 | -------------------------------------------------------------------------------- /icon.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /nuget.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /project.godot: -------------------------------------------------------------------------------- 1 | ; Engine configuration file. 2 | ; It's best edited using the editor UI and not directly, 3 | ; since the parameters that go here are not all obvious. 4 | ; 5 | ; Format: 6 | ; [section] ; section goes between [] 7 | ; param=value ; assign values to parameters 8 | 9 | config_version=5 10 | 11 | [application] 12 | 13 | config/name="SampleGodotCSharpProject" 14 | run/main_scene="res://Game/Main.tscn" 15 | config/features=PackedStringArray("4.0", "C#", "Forward Plus") 16 | boot_splash/image="res://Game/Assets/fireball-logo-8.png" 17 | config/icon="res://icon.svg" 18 | boot_splash/minimum_display_time=500 19 | 20 | [autoload] 21 | 22 | RsPreloader="*res://Game/Autoload/RsPreloader.tscn" 23 | GameEvents="*res://Game/Autoload/GameEvents.tscn" 24 | Global="*res://Game/Autoload/Global.tscn" 25 | 26 | [display] 27 | 28 | window/size/viewport_height=640 29 | window/size/always_on_top=true 30 | window/dpi/allow_hidpi=false 31 | window/vsync/vsync_mode=0 32 | window/stretch/mode="viewport" 33 | 34 | [dotnet] 35 | 36 | project/assembly_name="SampleGodotCSharpProject" 37 | 38 | [editor] 39 | 40 | movie_writer/mjpeg_quality=1.0 41 | movie_writer/movie_file="./Movies/sample.avi" 42 | movie_writer/fps=30 43 | script/templates_search_path="res://ScriptTemplates" 44 | naming/scene_name_casing=1 45 | 46 | [physics] 47 | 48 | 2d/run_on_separate_thread=true 49 | common/physics_ticks_per_second=30 50 | common/max_physics_steps_per_frame=4 51 | common/enable_object_picking=false 52 | 53 | [rendering] 54 | 55 | shader_compiler/shader_cache/strip_debug=true 56 | environment/defaults/default_clear_color=Color(0, 0, 0, 1) 57 | 2d/snap/snap_2d_transforms_to_pixel=true 58 | 2d/snap/snap_2d_vertices_to_pixel=true 59 | textures/canvas_textures/default_texture_filter=0 60 | --------------------------------------------------------------------------------