Compare commits
	
		
			17 Commits
		
	
	
		
			8073ed23c0
			...
			master
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 2c5e0459ad | |||
| f2c243ecf6 | |||
| b424aafeab | |||
| 09511b37c9 | |||
| c72353716f | |||
| 1cc941d893 | |||
| 88647b1c41 | |||
| 7438ba407a | |||
| 1c6c03cd41 | |||
| 32f96d488d | |||
| 630dbf0800 | |||
| ac1d8cfab9 | |||
| 60b6d6f989 | |||
| 483773f042 | |||
| 56cd4c2db2 | |||
| 7720e74a3d | |||
| 885d2c0075 | 
| @@ -4,4 +4,7 @@ | ||||
|     <EnableDynamicLoading>true</EnableDynamicLoading> | ||||
|     <RootNamespace>AceFieldNewHorizon</RootNamespace> | ||||
|   </PropertyGroup> | ||||
|   <ItemGroup> | ||||
|     <PackageReference Include="SimpleInjector" Version="5.5.0" /> | ||||
|   </ItemGroup> | ||||
| </Project> | ||||
| @@ -19,8 +19,38 @@ | ||||
| 	"buildTime": 3.0, | ||||
| 	"allowedRotations": [0], | ||||
| 	"layer": 1, | ||||
| 	"size": [1, 1] | ||||
|   }, | ||||
|   "turret": { | ||||
| 	"scene": "res://Scenes/Tiles/TurretTile.tscn", | ||||
| 	"cost": { | ||||
| 	  "stone": 10, | ||||
| 	  "ore_iron": 5 | ||||
| 	}, | ||||
| 	"durability": 200, | ||||
| 	"buildTime": 5.0, | ||||
| 	"allowedRotations": [0], | ||||
| 	"layer": 1, | ||||
| 	"size": [1, 1] | ||||
|   }, | ||||
|   "reactor": { | ||||
| 	"scene": "res://Scenes/Tiles/ReactorTile.tscn", | ||||
| 	"cost": {}, | ||||
| 	"durability": 1000, | ||||
| 	"buildTime": 5.0, | ||||
| 	"allowedRotations": [0], | ||||
| 	"layer": 1, | ||||
| 	"size": [3, 3] | ||||
|   }, | ||||
|   "enemy_portal": { | ||||
| 	"scene": "res://Scenes/Tiles/EnemyPortalTile.tscn", | ||||
| 	"cost": {}, | ||||
| 	"durability": 200, | ||||
| 	"buildTime": 0.0, | ||||
| 	"allowedRotations": [0], | ||||
| 	"layer": 1, | ||||
| 	"size": [1, 2] | ||||
|   }, | ||||
|   "ground": { | ||||
| 	"scene": "res://Scenes/Tiles/GroundTile.tscn", | ||||
| 	"cost": {}, | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								Scenes/Entities/Bullet.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Scenes/Entities/Bullet.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 659 KiB | 
							
								
								
									
										34
									
								
								Scenes/Entities/Bullet.png.import
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								Scenes/Entities/Bullet.png.import
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| [remap] | ||||
|  | ||||
| importer="texture" | ||||
| type="CompressedTexture2D" | ||||
| uid="uid://b5mb8tu15rc2p" | ||||
| path="res://.godot/imported/Bullet.png-db4c50cb16094f39ca6a1b9de30a2fe2.ctex" | ||||
| metadata={ | ||||
| "vram_texture": false | ||||
| } | ||||
|  | ||||
| [deps] | ||||
|  | ||||
| source_file="res://Scenes/Entities/Bullet.png" | ||||
| dest_files=["res://.godot/imported/Bullet.png-db4c50cb16094f39ca6a1b9de30a2fe2.ctex"] | ||||
|  | ||||
| [params] | ||||
|  | ||||
| compress/mode=0 | ||||
| compress/high_quality=false | ||||
| compress/lossy_quality=0.7 | ||||
| compress/hdr_compression=1 | ||||
| compress/normal_map=0 | ||||
| compress/channel_pack=0 | ||||
| mipmaps/generate=false | ||||
| mipmaps/limit=-1 | ||||
| roughness/mode=0 | ||||
| roughness/src_normal="" | ||||
| process/fix_alpha_border=true | ||||
| process/premult_alpha=false | ||||
| process/normal_map_invert_y=false | ||||
| process/hdr_as_srgb=false | ||||
| process/hdr_clamp_exposure=false | ||||
| process/size_limit=0 | ||||
| detect_3d/compress_to=1 | ||||
							
								
								
									
										18
									
								
								Scenes/Entities/Bullet.tscn
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								Scenes/Entities/Bullet.tscn
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| [gd_scene load_steps=4 format=3 uid="uid://erqawdsydh6a"] | ||||
|  | ||||
| [ext_resource type="Texture2D" uid="uid://b5mb8tu15rc2p" path="res://Scenes/Entities/Bullet.png" id="1_fi8au"] | ||||
| [ext_resource type="Script" uid="uid://vgx2a8gm7l8b" path="res://Scripts/Entities/Bullet.cs" id="1_k5b1m"] | ||||
|  | ||||
| [sub_resource type="RectangleShape2D" id="RectangleShape2D_fi8au"] | ||||
| size = Vector2(44, 12) | ||||
|  | ||||
| [node name="Bullet" type="Area2D"] | ||||
| collision_mask = 2 | ||||
| script = ExtResource("1_k5b1m") | ||||
|  | ||||
| [node name="Sprite2D" type="Sprite2D" parent="."] | ||||
| scale = Vector2(0.03, 0.03) | ||||
| texture = ExtResource("1_fi8au") | ||||
|  | ||||
| [node name="CollisionShape2D" type="CollisionShape2D" parent="."] | ||||
| shape = SubResource("RectangleShape2D_fi8au") | ||||
							
								
								
									
										
											BIN
										
									
								
								Scenes/Entities/Enemy.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Scenes/Entities/Enemy.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 807 KiB | 
							
								
								
									
										34
									
								
								Scenes/Entities/Enemy.png.import
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								Scenes/Entities/Enemy.png.import
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| [remap] | ||||
|  | ||||
| importer="texture" | ||||
| type="CompressedTexture2D" | ||||
| uid="uid://x4u6oatvsm8y" | ||||
| path="res://.godot/imported/Enemy.png-7a121c0bc2e7a40a7fe012e488d00452.ctex" | ||||
| metadata={ | ||||
| "vram_texture": false | ||||
| } | ||||
|  | ||||
| [deps] | ||||
|  | ||||
| source_file="res://Scenes/Entities/Enemy.png" | ||||
| dest_files=["res://.godot/imported/Enemy.png-7a121c0bc2e7a40a7fe012e488d00452.ctex"] | ||||
|  | ||||
| [params] | ||||
|  | ||||
| compress/mode=0 | ||||
| compress/high_quality=false | ||||
| compress/lossy_quality=0.7 | ||||
| compress/hdr_compression=1 | ||||
| compress/normal_map=0 | ||||
| compress/channel_pack=0 | ||||
| mipmaps/generate=false | ||||
| mipmaps/limit=-1 | ||||
| roughness/mode=0 | ||||
| roughness/src_normal="" | ||||
| process/fix_alpha_border=true | ||||
| process/premult_alpha=false | ||||
| process/normal_map_invert_y=false | ||||
| process/hdr_as_srgb=false | ||||
| process/hdr_clamp_exposure=false | ||||
| process/size_limit=0 | ||||
| detect_3d/compress_to=1 | ||||
							
								
								
									
										21
									
								
								Scenes/Entities/Enemy.tscn
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								Scenes/Entities/Enemy.tscn
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| [gd_scene load_steps=3 format=3 uid="uid://b3ffcucytwmk"] | ||||
|  | ||||
| [ext_resource type="Script" uid="uid://cvsmy820b8dwl" path="res://Scripts/Entities/Enemy.cs" id="1_jajit"] | ||||
| [ext_resource type="Texture2D" uid="uid://x4u6oatvsm8y" path="res://Scenes/Entities/Enemy.png" id="2_jajit"] | ||||
|  | ||||
| [node name="Enemy" type="CharacterBody2D"] | ||||
| collision_layer = 2 | ||||
| collision_mask = 3 | ||||
| script = ExtResource("1_jajit") | ||||
|  | ||||
| [node name="CollisionShape2D" type="CollisionPolygon2D" parent="."] | ||||
| polygon = PackedVector2Array(-2, -21, 3, -21, 24, 6, 24, 10, 20, 14, -21, 14, -24, 11, -24, 7) | ||||
|  | ||||
| [node name="Sprite2D" type="Sprite2D" parent="."] | ||||
| scale = Vector2(0.05, 0.05) | ||||
| texture = ExtResource("2_jajit") | ||||
|  | ||||
| [node name="AttackArea" type="Area2D" parent="."] | ||||
|  | ||||
| [node name="CollisionShape2D" type="CollisionPolygon2D" parent="AttackArea"] | ||||
| polygon = PackedVector2Array(-3, -24, 4, -24, 27, 5, 27, 14, 20, 19, -21, 19, -27, 14, -27, 5) | ||||
| @@ -4,6 +4,7 @@ | ||||
| [ext_resource type="Texture2D" uid="uid://jye6c2ehuxtg" path="res://Scenes/Entities/Player.png" id="1_ucweq"] | ||||
|  | ||||
| [node name="Player" type="CharacterBody2D"] | ||||
| collision_mask = 3 | ||||
| script = ExtResource("1_08t41") | ||||
| MinZoom = 0.1 | ||||
| MaxZoom = 5.0 | ||||
|   | ||||
| @@ -1,47 +1,30 @@ | ||||
| [gd_scene load_steps=8 format=3 uid="uid://c22aprj452aha"] | ||||
| [gd_scene load_steps=6 format=3 uid="uid://c22aprj452aha"] | ||||
|  | ||||
| [ext_resource type="Script" uid="uid://cudpc3w17mbsw" path="res://Scripts/System/GridManager.cs" id="1_knkkn"] | ||||
| [ext_resource type="Script" uid="uid://dfi2snip78eq6" path="res://Scripts/System/ResourceManager.cs" id="1_pl8e4"] | ||||
| [ext_resource type="Script" uid="uid://cfbj72nm0eovg" path="res://Scripts/System/BuildingRegistry.cs" id="1_sxhdm"] | ||||
| [ext_resource type="Script" uid="uid://cugfbvw70clgd" path="res://Scripts/System/NaturalResourceGenerator.cs" id="2_oss8w"] | ||||
| [ext_resource type="Script" uid="uid://bx1wj7gn6vrqe" path="res://Scripts/System/PlacementManager.cs" id="2_sxhdm"] | ||||
| [ext_resource type="PackedScene" uid="uid://doxy60afddg1m" path="res://Scenes/Entities/Player.tscn" id="3_oss8w"] | ||||
| [ext_resource type="PackedScene" uid="uid://xwkplaxmye3v" path="res://Scenes/System/ItemPickup.tscn" id="7_is6ib"] | ||||
| [ext_resource type="PackedScene" uid="uid://byv2vu0k2drdd" path="res://Scenes/System/HUD.tscn" id="8_hud_scene"] | ||||
|  | ||||
| [node name="Root" type="Node2D"] | ||||
|  | ||||
| [node name="ResourceSystem" type="Node" parent="."] | ||||
| script = ExtResource("1_pl8e4") | ||||
|  | ||||
| [node name="BuildingRegistry" type="Node" parent="."] | ||||
| script = ExtResource("1_sxhdm") | ||||
|  | ||||
| [node name="NaturalResourceGenerator" type="Node2D" parent="." node_paths=PackedStringArray("Grid", "Registry")] | ||||
| [node name="NaturalResourceGenerator" type="Node2D" parent="."] | ||||
| script = ExtResource("2_oss8w") | ||||
| Grid = NodePath("../GridSystem") | ||||
| Registry = NodePath("../BuildingRegistry") | ||||
|  | ||||
| [node name="GridSystem" type="Node2D" parent="."] | ||||
| script = ExtResource("1_knkkn") | ||||
|  | ||||
| [node name="PlacementSystem" type="Node2D" parent="." node_paths=PackedStringArray("Grid", "Inventory", "Registry")] | ||||
| [node name="PlacementSystem" type="Node2D" parent="."] | ||||
| script = ExtResource("2_sxhdm") | ||||
| Grid = NodePath("../GridSystem") | ||||
| Inventory = NodePath("../ResourceSystem") | ||||
| Registry = NodePath("../BuildingRegistry") | ||||
|  | ||||
| [node name="Player" parent="." node_paths=PackedStringArray("Inventory") instance=ExtResource("3_oss8w")] | ||||
| [node name="Player" parent="." instance=ExtResource("3_oss8w")] | ||||
| scale = Vector2(0.35, 0.35) | ||||
| Inventory = NodePath("../ResourceSystem") | ||||
|  | ||||
| [node name="HUD" parent="." instance=ExtResource("8_hud_scene")] | ||||
|  | ||||
| [node name="ItemPickup" parent="." instance=ExtResource("7_is6ib")] | ||||
| position = Vector2(-496, -245) | ||||
| ItemId = "stone" | ||||
| Quantity = 64 | ||||
| Infinite = true | ||||
|  | ||||
| [node name="ItemPickup2" parent="." instance=ExtResource("7_is6ib")] | ||||
| position = Vector2(-495, 5) | ||||
| ItemId = "ore_iron" | ||||
| Quantity = 16 | ||||
| Infinite = true | ||||
|   | ||||
							
								
								
									
										13
									
								
								Scenes/System/HUD.tscn
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								Scenes/System/HUD.tscn
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| [gd_scene load_steps=2 format=3 uid="uid://byv2vu0k2drdd"] | ||||
|  | ||||
| [ext_resource type="Script" uid="uid://ddoqqcg77f60v" path="res://Scripts/System/Hud.cs" id="1_yw4ou"] | ||||
|  | ||||
| [node name="HUD" type="CanvasLayer"] | ||||
| script = ExtResource("1_yw4ou") | ||||
|  | ||||
| [node name="ResourceDisplay" type="VBoxContainer" parent="."] | ||||
| layout_mode = 0 | ||||
| offset_left = 10.0 | ||||
| offset_top = 10.0 | ||||
| offset_right = 100.0 | ||||
| offset_bottom = 100.0 | ||||
| @@ -4,18 +4,17 @@ | ||||
| [ext_resource type="Script" uid="uid://qgcue2doj2lf" path="res://Scripts/System/ItemPickup.cs" id="1_ps3kh"] | ||||
|  | ||||
| [sub_resource type="RectangleShape2D" id="RectangleShape2D_4weev"] | ||||
| size = Vector2(160, 160) | ||||
| size = Vector2(50, 50) | ||||
|  | ||||
| [node name="ItemPickup" type="Node2D"] | ||||
| [node name="ItemPickup" type="Area2D"] | ||||
| script = ExtResource("1_ps3kh") | ||||
| MagnetRange = 96.0 | ||||
|  | ||||
| [node name="Sprite2D" type="Sprite2D" parent="."] | ||||
| scale = Vector2(0.05, 0.05) | ||||
| texture = ExtResource("1_4weev") | ||||
|  | ||||
| [node name="Area2D" type="Area2D" parent="."] | ||||
|  | ||||
| [node name="CollisionShape2D" type="CollisionShape2D" parent="Area2D"] | ||||
| [node name="CollisionShape2D" type="CollisionShape2D" parent="."] | ||||
| shape = SubResource("RectangleShape2D_4weev") | ||||
|  | ||||
| [node name="Label" type="Label" parent="."] | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								Scenes/Tiles/EnemyPortalTile.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Scenes/Tiles/EnemyPortalTile.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 431 KiB | 
							
								
								
									
										34
									
								
								Scenes/Tiles/EnemyPortalTile.png.import
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								Scenes/Tiles/EnemyPortalTile.png.import
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| [remap] | ||||
|  | ||||
| importer="texture" | ||||
| type="CompressedTexture2D" | ||||
| uid="uid://dv2xwfyshxdtp" | ||||
| path="res://.godot/imported/EnemyPortalTile.png-3904776a211e67c58254b1bdc9aba071.ctex" | ||||
| metadata={ | ||||
| "vram_texture": false | ||||
| } | ||||
|  | ||||
| [deps] | ||||
|  | ||||
| source_file="res://Scenes/Tiles/EnemyPortalTile.png" | ||||
| dest_files=["res://.godot/imported/EnemyPortalTile.png-3904776a211e67c58254b1bdc9aba071.ctex"] | ||||
|  | ||||
| [params] | ||||
|  | ||||
| compress/mode=0 | ||||
| compress/high_quality=false | ||||
| compress/lossy_quality=0.7 | ||||
| compress/hdr_compression=1 | ||||
| compress/normal_map=0 | ||||
| compress/channel_pack=0 | ||||
| mipmaps/generate=false | ||||
| mipmaps/limit=-1 | ||||
| roughness/mode=0 | ||||
| roughness/src_normal="" | ||||
| process/fix_alpha_border=true | ||||
| process/premult_alpha=false | ||||
| process/normal_map_invert_y=false | ||||
| process/hdr_as_srgb=false | ||||
| process/hdr_clamp_exposure=false | ||||
| process/size_limit=0 | ||||
| detect_3d/compress_to=1 | ||||
							
								
								
									
										23
									
								
								Scenes/Tiles/EnemyPortalTile.tscn
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								Scenes/Tiles/EnemyPortalTile.tscn
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| [gd_scene load_steps=5 format=3 uid="uid://dup2su0s3ybcy"] | ||||
|  | ||||
| [ext_resource type="Script" uid="uid://26hl5mk4mqur" path="res://Scripts/Tiles/EnemyPortalTile.cs" id="1_o543x"] | ||||
| [ext_resource type="PackedScene" uid="uid://b3ffcucytwmk" path="res://Scenes/Entities/Enemy.tscn" id="2_nh7ff"] | ||||
| [ext_resource type="Texture2D" uid="uid://dv2xwfyshxdtp" path="res://Scenes/Tiles/EnemyPortalTile.png" id="3_i4us4"] | ||||
|  | ||||
| [sub_resource type="RectangleShape2D" id="RectangleShape2D_id484"] | ||||
| size = Vector2(54, 79) | ||||
|  | ||||
| [node name="EnemyPortal" type="StaticBody2D"] | ||||
| collision_layer = 0 | ||||
| collision_mask = 0 | ||||
| script = ExtResource("1_o543x") | ||||
| EnemyScene = ExtResource("2_nh7ff") | ||||
| TileId = "enemy_portal" | ||||
|  | ||||
| [node name="Sprite2D" type="Sprite2D" parent="."] | ||||
| scale = Vector2(0.1, 0.1) | ||||
| texture = ExtResource("3_i4us4") | ||||
|  | ||||
| [node name="CollisionShape2D" type="CollisionShape2D" parent="."] | ||||
| position = Vector2(0, -0.5) | ||||
| shape = SubResource("RectangleShape2D_id484") | ||||
| @@ -8,6 +8,7 @@ size = Vector2(54, 54) | ||||
|  | ||||
| [node name="GroundTile" type="StaticBody2D"] | ||||
| collision_layer = 0 | ||||
| collision_mask = 0 | ||||
| script = ExtResource("1_mqsaf") | ||||
| TileId = "ground" | ||||
|  | ||||
|   | ||||
| @@ -1,14 +1,15 @@ | ||||
| [gd_scene load_steps=4 format=3 uid="uid://cbu81slklwq3u"] | ||||
| [gd_scene load_steps=5 format=3 uid="uid://cbu81slklwq3u"] | ||||
|  | ||||
| [ext_resource type="Script" uid="uid://dyubkyqtpcg3a" path="res://Scripts/Tiles/MinerTile.cs" id="1_mecoy"] | ||||
| [ext_resource type="Texture2D" uid="uid://bt6xmcgrbb078" path="res://Scenes/Tiles/MinerTile.png" id="2_mecoy"] | ||||
| [ext_resource type="PackedScene" uid="uid://xwkplaxmye3v" path="res://Scenes/System/ItemPickup.tscn" id="2_xhk0k"] | ||||
|  | ||||
| [sub_resource type="RectangleShape2D" id="RectangleShape2D_8o613"] | ||||
| size = Vector2(54, 54) | ||||
|  | ||||
| [node name="MinerTile" type="StaticBody2D"] | ||||
| scale = Vector2(3, 3) | ||||
| script = ExtResource("1_mecoy") | ||||
| ItemPickup = ExtResource("2_xhk0k") | ||||
| TileId = "miner" | ||||
|  | ||||
| [node name="Sprite2D" type="Sprite2D" parent="."] | ||||
|   | ||||
| @@ -8,8 +8,9 @@ size = Vector2(54, 54) | ||||
|  | ||||
| [node name="OreIronTile" type="StaticBody2D"] | ||||
| collision_layer = 0 | ||||
| collision_mask = 0 | ||||
| script = ExtResource("1_exnim") | ||||
| TileId = "stone_iron" | ||||
| TileId = "ore_iron" | ||||
|  | ||||
| [node name="Sprite2D" type="Sprite2D" parent="."] | ||||
| position = Vector2(1.49012e-08, -9.53674e-07) | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								Scenes/Tiles/ReactorTile.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Scenes/Tiles/ReactorTile.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 544 KiB | 
							
								
								
									
										34
									
								
								Scenes/Tiles/ReactorTile.png.import
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								Scenes/Tiles/ReactorTile.png.import
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| [remap] | ||||
|  | ||||
| importer="texture" | ||||
| type="CompressedTexture2D" | ||||
| uid="uid://fg03qxqphp7n" | ||||
| path="res://.godot/imported/ReactorTile.png-f6f5bfaa813b044011d6b0a5736b9bc6.ctex" | ||||
| metadata={ | ||||
| "vram_texture": false | ||||
| } | ||||
|  | ||||
| [deps] | ||||
|  | ||||
| source_file="res://Scenes/Tiles/ReactorTile.png" | ||||
| dest_files=["res://.godot/imported/ReactorTile.png-f6f5bfaa813b044011d6b0a5736b9bc6.ctex"] | ||||
|  | ||||
| [params] | ||||
|  | ||||
| compress/mode=0 | ||||
| compress/high_quality=false | ||||
| compress/lossy_quality=0.7 | ||||
| compress/hdr_compression=1 | ||||
| compress/normal_map=0 | ||||
| compress/channel_pack=0 | ||||
| mipmaps/generate=false | ||||
| mipmaps/limit=-1 | ||||
| roughness/mode=0 | ||||
| roughness/src_normal="" | ||||
| process/fix_alpha_border=true | ||||
| process/premult_alpha=false | ||||
| process/normal_map_invert_y=false | ||||
| process/hdr_as_srgb=false | ||||
| process/hdr_clamp_exposure=false | ||||
| process/size_limit=0 | ||||
| detect_3d/compress_to=1 | ||||
							
								
								
									
										26
									
								
								Scenes/Tiles/ReactorTile.tscn
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								Scenes/Tiles/ReactorTile.tscn
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| [gd_scene load_steps=4 format=3 uid="uid://w6ni678js7cu"] | ||||
|  | ||||
| [ext_resource type="Script" uid="uid://c4k3ottt7j3b1" path="res://Scripts/Tiles/ReactorTile.cs" id="1_yldg2"] | ||||
| [ext_resource type="Texture2D" uid="uid://fg03qxqphp7n" path="res://Scenes/Tiles/ReactorTile.png" id="3_fk1vt"] | ||||
|  | ||||
| [sub_resource type="RectangleShape2D" id="RectangleShape2D_8o613"] | ||||
| size = Vector2(54, 54) | ||||
|  | ||||
| [node name="ReactorTile" type="StaticBody2D"] | ||||
| script = ExtResource("1_yldg2") | ||||
| TileId = "reactor" | ||||
|  | ||||
| [node name="Sprite2D" type="Sprite2D" parent="."] | ||||
| scale = Vector2(0.3, 0.3) | ||||
| texture = ExtResource("3_fk1vt") | ||||
|  | ||||
| [node name="CollisionShape2D" type="CollisionShape2D" parent="."] | ||||
| scale = Vector2(3, 3) | ||||
| shape = SubResource("RectangleShape2D_8o613") | ||||
|  | ||||
| [node name="ProgressOverlay" type="ColorRect" parent="."] | ||||
| offset_left = -81.0 | ||||
| offset_top = -81.0 | ||||
| offset_right = -27.0 | ||||
| offset_bottom = -27.0 | ||||
| scale = Vector2(3, 3) | ||||
| @@ -8,6 +8,7 @@ size = Vector2(54, 54) | ||||
|  | ||||
| [node name="StoneTile" type="StaticBody2D"] | ||||
| collision_layer = 0 | ||||
| collision_mask = 0 | ||||
| script = ExtResource("1_rndy8") | ||||
| TileId = "stone" | ||||
|  | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								Scenes/Tiles/TurretTile.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Scenes/Tiles/TurretTile.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 448 KiB | 
							
								
								
									
										34
									
								
								Scenes/Tiles/TurretTile.png.import
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								Scenes/Tiles/TurretTile.png.import
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| [remap] | ||||
|  | ||||
| importer="texture" | ||||
| type="CompressedTexture2D" | ||||
| uid="uid://ckssi7soymu7g" | ||||
| path="res://.godot/imported/TurretTile.png-d55543f854deaa0fedf248ba979d1cb4.ctex" | ||||
| metadata={ | ||||
| "vram_texture": false | ||||
| } | ||||
|  | ||||
| [deps] | ||||
|  | ||||
| source_file="res://Scenes/Tiles/TurretTile.png" | ||||
| dest_files=["res://.godot/imported/TurretTile.png-d55543f854deaa0fedf248ba979d1cb4.ctex"] | ||||
|  | ||||
| [params] | ||||
|  | ||||
| compress/mode=0 | ||||
| compress/high_quality=false | ||||
| compress/lossy_quality=0.7 | ||||
| compress/hdr_compression=1 | ||||
| compress/normal_map=0 | ||||
| compress/channel_pack=0 | ||||
| mipmaps/generate=false | ||||
| mipmaps/limit=-1 | ||||
| roughness/mode=0 | ||||
| roughness/src_normal="" | ||||
| process/fix_alpha_border=true | ||||
| process/premult_alpha=false | ||||
| process/normal_map_invert_y=false | ||||
| process/hdr_as_srgb=false | ||||
| process/hdr_clamp_exposure=false | ||||
| process/size_limit=0 | ||||
| detect_3d/compress_to=1 | ||||
							
								
								
									
										39
									
								
								Scenes/Tiles/TurretTile.tscn
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								Scenes/Tiles/TurretTile.tscn
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | ||||
| [gd_scene load_steps=6 format=3 uid="uid://dbup2pvjl8het"] | ||||
|  | ||||
| [ext_resource type="Script" uid="uid://n5g6i0uovxfk" path="res://Scripts/Tiles/TurretTile.cs" id="1_j3157"] | ||||
| [ext_resource type="Texture2D" uid="uid://ckssi7soymu7g" path="res://Scenes/Tiles/TurretTile.png" id="2_7ljeh"] | ||||
| [ext_resource type="PackedScene" uid="uid://erqawdsydh6a" path="res://Scenes/Entities/Bullet.tscn" id="2_gfad6"] | ||||
| [ext_resource type="Texture2D" uid="uid://dmbbkwgff7dej" path="res://Scenes/Tiles/TurretTileBarrel.png" id="3_gfad6"] | ||||
|  | ||||
| [sub_resource type="RectangleShape2D" id="RectangleShape2D_pndcb"] | ||||
| size = Vector2(54, 54) | ||||
|  | ||||
| [node name="Turret" type="StaticBody2D"] | ||||
| script = ExtResource("1_j3157") | ||||
| BulletScene = ExtResource("2_gfad6") | ||||
| BarrelTipPath = NodePath("Barrel/Marker2D") | ||||
| TileId = "turret" | ||||
|  | ||||
| [node name="Sprite2D" type="Sprite2D" parent="."] | ||||
| scale = Vector2(0.1, 0.1) | ||||
| texture = ExtResource("2_7ljeh") | ||||
|  | ||||
| [node name="Barrel" type="Sprite2D" parent="."] | ||||
| position = Vector2(0, -3) | ||||
| scale = Vector2(0.08, 0.08) | ||||
| texture = ExtResource("3_gfad6") | ||||
| offset = Vector2(0, -54) | ||||
|  | ||||
| [node name="Marker2D" type="Marker2D" parent="Barrel"] | ||||
| position = Vector2(0, -225) | ||||
| scale = Vector2(12.5, 12.5) | ||||
|  | ||||
| [node name="CollisionShape2D" type="CollisionShape2D" parent="."] | ||||
| shape = SubResource("RectangleShape2D_pndcb") | ||||
|  | ||||
| [node name="ProgressOverlay" type="ColorRect" parent="."] | ||||
| visible = false | ||||
| offset_left = -27.0 | ||||
| offset_top = -27.0 | ||||
| offset_right = 27.0 | ||||
| offset_bottom = 27.0 | ||||
							
								
								
									
										
											BIN
										
									
								
								Scenes/Tiles/TurretTileBarrel.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Scenes/Tiles/TurretTileBarrel.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 185 KiB | 
							
								
								
									
										34
									
								
								Scenes/Tiles/TurretTileBarrel.png.import
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								Scenes/Tiles/TurretTileBarrel.png.import
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| [remap] | ||||
|  | ||||
| importer="texture" | ||||
| type="CompressedTexture2D" | ||||
| uid="uid://dmbbkwgff7dej" | ||||
| path="res://.godot/imported/TurretTileBarrel.png-a9f6a29579c44d5d6ed4cc8f6c09fd72.ctex" | ||||
| metadata={ | ||||
| "vram_texture": false | ||||
| } | ||||
|  | ||||
| [deps] | ||||
|  | ||||
| source_file="res://Scenes/Tiles/TurretTileBarrel.png" | ||||
| dest_files=["res://.godot/imported/TurretTileBarrel.png-a9f6a29579c44d5d6ed4cc8f6c09fd72.ctex"] | ||||
|  | ||||
| [params] | ||||
|  | ||||
| compress/mode=0 | ||||
| compress/high_quality=false | ||||
| compress/lossy_quality=0.7 | ||||
| compress/hdr_compression=1 | ||||
| compress/normal_map=0 | ||||
| compress/channel_pack=0 | ||||
| mipmaps/generate=false | ||||
| mipmaps/limit=-1 | ||||
| roughness/mode=0 | ||||
| roughness/src_normal="" | ||||
| process/fix_alpha_border=true | ||||
| process/premult_alpha=false | ||||
| process/normal_map_invert_y=false | ||||
| process/hdr_as_srgb=false | ||||
| process/hdr_clamp_exposure=false | ||||
| process/size_limit=0 | ||||
| detect_3d/compress_to=1 | ||||
							
								
								
									
										14
									
								
								Scripts/AutoLoad/DIInitializer.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								Scripts/AutoLoad/DIInitializer.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| using Godot; | ||||
| using AceFieldNewHorizon.Scripts.System; | ||||
|  | ||||
| namespace AceFieldNewHorizon.Scripts.AutoLoad; | ||||
|  | ||||
| public partial class DIInitializer : Node | ||||
| { | ||||
|     public override void _Ready() | ||||
|     { | ||||
|         // Initialize the Simple Injector container as early as possible | ||||
|         DependencyInjection.Initialize(); | ||||
|         GD.Print("[DIInitializer] Dependency Injection container initialized via AutoLoad."); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										1
									
								
								Scripts/AutoLoad/DIInitializer.cs.uid
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								Scripts/AutoLoad/DIInitializer.cs.uid
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| uid://cr2a8w6ur4uei | ||||
							
								
								
									
										97
									
								
								Scripts/Entities/Bullet.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								Scripts/Entities/Bullet.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,97 @@ | ||||
| using Godot; | ||||
|  | ||||
| namespace AceFieldNewHorizon.Scripts.Entities; | ||||
|  | ||||
| public partial class Bullet : Area2D | ||||
| { | ||||
|     [Export] public float Speed = 400.0f; | ||||
|     [Export] public int Damage = 10; | ||||
|     [Export] public float MaxDistance = 1000.0f; | ||||
|      | ||||
|     private Vector2 _direction = Vector2.Right; | ||||
|     private Vector2 _startPosition; | ||||
|     private float _distanceTraveled = 0f; | ||||
|     private bool _hasHit = false; | ||||
|     private uint _ignoreCollisionLayer = 0; // Layer to ignore (will be set by turret) | ||||
|  | ||||
|     public void Initialize(Vector2 direction, Vector2 position, float rotation, uint ignoreLayer = 0) | ||||
|     { | ||||
|         _direction = direction.Normalized(); | ||||
|         Position = position; | ||||
|         Rotation = rotation; | ||||
|         _startPosition = position; | ||||
|         _ignoreCollisionLayer = ignoreLayer; | ||||
|          | ||||
|         // Connect the area entered signal | ||||
|         BodyEntered += OnBodyEntered; | ||||
|         AreaEntered += OnAreaEntered; | ||||
|     } | ||||
|  | ||||
|     public override void _PhysicsProcess(double delta) | ||||
|     { | ||||
|         if (_hasHit) return; | ||||
|          | ||||
|         // Move the bullet | ||||
|         var movement = _direction * Speed * (float)delta; | ||||
|         Position += movement; | ||||
|         _distanceTraveled += movement.Length(); | ||||
|          | ||||
|         // Check if bullet has traveled max distance | ||||
|         if (_distanceTraveled >= MaxDistance) | ||||
|         { | ||||
|             QueueFree(); | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     private void OnBodyEntered(Node2D body) | ||||
|     { | ||||
|         HandleCollision(body); | ||||
|     } | ||||
|      | ||||
|     private void OnAreaEntered(Area2D area) | ||||
|     { | ||||
|         HandleCollision(area); | ||||
|     } | ||||
|      | ||||
|     private void HandleCollision(Node2D node) | ||||
|     { | ||||
|         if (_hasHit) return; | ||||
|          | ||||
|         // Skip collision if it's on the ignore layer | ||||
|         if (node is PhysicsBody2D physicsBody &&  | ||||
|             (physicsBody.CollisionLayer & _ignoreCollisionLayer) != 0) | ||||
|         { | ||||
|             return; | ||||
|         } | ||||
|          | ||||
|         _hasHit = true; | ||||
|          | ||||
|         // If we hit an enemy, deal damage | ||||
|         if (node is Enemy enemy) | ||||
|         { | ||||
|             // Get the global position where the bullet hit | ||||
|             var hitPosition = GlobalPosition; | ||||
|             enemy.TakeDamage(Damage, hitPosition); | ||||
|         } | ||||
|          | ||||
|         // Optional: Add hit effect here | ||||
|         // CreateHitEffect(); | ||||
|          | ||||
|         // Remove the bullet | ||||
|         QueueFree(); | ||||
|     } | ||||
|      | ||||
|     private void CreateHitEffect() | ||||
|     { | ||||
|         // You can add a hit effect here if desired | ||||
|         // For example, a small explosion or impact sprite | ||||
|     } | ||||
|      | ||||
|     public override void _ExitTree() | ||||
|     { | ||||
|         // Clean up signal connections | ||||
|         BodyEntered -= OnBodyEntered; | ||||
|         AreaEntered -= OnAreaEntered; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										1
									
								
								Scripts/Entities/Bullet.cs.uid
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								Scripts/Entities/Bullet.cs.uid
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| uid://vgx2a8gm7l8b | ||||
							
								
								
									
										13
									
								
								Scripts/Entities/Enemy.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								Scripts/Entities/Enemy.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| using System.Collections.Generic; | ||||
| using AceFieldNewHorizon.Scripts.Tiles; | ||||
| using Godot; | ||||
| using AceFieldNewHorizon.Scripts.System; | ||||
|  | ||||
| namespace AceFieldNewHorizon.Scripts.Entities; | ||||
|  | ||||
| public partial class Enemy : BaseEnemy | ||||
| { | ||||
| 	// All the base functionality is now in BaseEnemy | ||||
| 	// This class is kept for backward compatibility and can be used to add | ||||
| 	// specific behaviors for the basic enemy type if needed | ||||
| } | ||||
							
								
								
									
										1
									
								
								Scripts/Entities/Enemy.cs.uid
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								Scripts/Entities/Enemy.cs.uid
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| uid://cvsmy820b8dwl | ||||
							
								
								
									
										283
									
								
								Scripts/Entities/EnemyBase.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										283
									
								
								Scripts/Entities/EnemyBase.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,283 @@ | ||||
| using System.Collections.Generic; | ||||
| using AceFieldNewHorizon.Scripts.System; | ||||
| using AceFieldNewHorizon.Scripts.Tiles; | ||||
| using Godot; | ||||
|  | ||||
| namespace AceFieldNewHorizon.Scripts.Entities; | ||||
|  | ||||
| public abstract partial class BaseEnemy : CharacterBody2D | ||||
| { | ||||
|     public const string EnemyGroupName = "Enemy"; | ||||
|  | ||||
|     [Export] public float MoveSpeed = 150.0f; | ||||
|     [Export] public float DetectionRadius = 300.0f; | ||||
|     [Export] public float AttackRadius = 80.0f; | ||||
|     [Export] public int Damage = 10; | ||||
|     [Export] public float AttackCooldown = 3.0f; | ||||
|     [Export] public int MaxHealth = 100; | ||||
|     [Export] public bool ShowDamageNumbers = true; | ||||
|  | ||||
|     protected BaseTile TargetTile; | ||||
|     protected ReactorTile Reactor; | ||||
|     protected float AttackTimer = 0; | ||||
|     protected Area2D DetectionArea; | ||||
|     protected Area2D AttackArea; | ||||
|     protected int CurrentHealthValue; | ||||
|     protected ProgressBar HealthBar; | ||||
|     protected GridManager Grid; | ||||
|  | ||||
|     // Track collisions with potential targets | ||||
|     protected readonly HashSet<BaseTile> CollidingTiles = []; | ||||
|  | ||||
|     public int CurrentHealth | ||||
|     { | ||||
|         get => CurrentHealthValue; | ||||
|         protected set | ||||
|         { | ||||
|             CurrentHealthValue = Mathf.Clamp(value, 0, MaxHealth); | ||||
|             UpdateHealthBar(); | ||||
|  | ||||
|             if (CurrentHealthValue <= 0) | ||||
|             { | ||||
|                 Die(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public bool IsDead => CurrentHealthValue <= 0; | ||||
|  | ||||
|     public override void _Ready() | ||||
|     { | ||||
|         CurrentHealthValue = MaxHealth; | ||||
|         Grid = DependencyInjection.Container.GetInstance<GridManager>(); | ||||
|         Reactor = GetTree().GetFirstNodeInGroup(ReactorTile.ReactorGroupName) as ReactorTile; | ||||
|  | ||||
|         InitializeHealthBar(); | ||||
|         InitializeDetectionArea(); | ||||
|         InitializeAttackArea(); | ||||
|          | ||||
|         AddToGroup(EnemyGroupName); | ||||
|     } | ||||
|  | ||||
|     protected virtual void InitializeHealthBar() | ||||
|     { | ||||
|         HealthBar = new ProgressBar | ||||
|         { | ||||
|             MaxValue = MaxHealth, | ||||
|             Value = CurrentHealthValue, | ||||
|             Size = new Vector2(40, 4), | ||||
|             ShowPercentage = false, | ||||
|             Visible = false | ||||
|         }; | ||||
|  | ||||
|         var healthBarContainer = new Control(); | ||||
|         healthBarContainer.AddChild(HealthBar); | ||||
|         AddChild(healthBarContainer); | ||||
|         healthBarContainer.Position = new Vector2(-20, -20); | ||||
|     } | ||||
|  | ||||
|     protected virtual void InitializeDetectionArea() | ||||
|     { | ||||
|         DetectionArea = new Area2D(); | ||||
|         var collisionShape = new CollisionShape2D(); | ||||
|         var shape = new CircleShape2D(); | ||||
|         shape.Radius = DetectionRadius; | ||||
|         collisionShape.Shape = shape; | ||||
|         DetectionArea.AddChild(collisionShape); | ||||
|         AddChild(DetectionArea); | ||||
|  | ||||
|         DetectionArea.BodyEntered += OnBodyEnteredDetection; | ||||
|         DetectionArea.BodyExited += OnBodyExitedDetection; | ||||
|     } | ||||
|  | ||||
|     protected virtual void InitializeAttackArea() | ||||
|     { | ||||
|         AttackArea = GetNodeOrNull<Area2D>("AttackArea"); | ||||
|         if (AttackArea != null) | ||||
|         { | ||||
|             AttackArea.BodyEntered += OnBodyEntered; | ||||
|             AttackArea.BodyExited += OnBodyExited; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public override void _Process(double delta) | ||||
|     { | ||||
|         if (IsDead) return; | ||||
|  | ||||
|         if (TargetTile == null || TargetTile.IsDestroyed || !TargetTile.IsInsideTree()) | ||||
|         { | ||||
|             UpdateTarget(); | ||||
|         } | ||||
|  | ||||
|         MoveTowardsTarget(); | ||||
|         HandleAttacks(delta); | ||||
|     } | ||||
|  | ||||
|     protected virtual void MoveTowardsTarget() | ||||
|     { | ||||
|         if (TargetTile != null) | ||||
|         { | ||||
|             var direction = GlobalPosition.DirectionTo(TargetTile.GlobalPosition); | ||||
|             Velocity = direction * MoveSpeed; | ||||
|             LookAt(TargetTile.GlobalPosition); | ||||
|             MoveAndSlide(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     protected virtual void HandleAttacks(double delta) | ||||
|     { | ||||
|         if (AttackTimer > 0) | ||||
|         { | ||||
|             AttackTimer -= (float)delta; | ||||
|         } | ||||
|         else if (CollidingTiles.Count > 0) | ||||
|         { | ||||
|             foreach (var tile in CollidingTiles) | ||||
|             { | ||||
|                 if (tile != null && !tile.IsDestroyed && tile.IsInsideTree()) | ||||
|                 { | ||||
|                     TryAttackTile(tile); | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     protected virtual void UpdateTarget() | ||||
|     { | ||||
|         // If we have a valid target in collision, use that | ||||
|         foreach (var tile in CollidingTiles) | ||||
|         { | ||||
|             if (tile != null && !tile.IsDestroyed && tile.IsInsideTree()) | ||||
|             { | ||||
|                 TargetTile = tile; | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Otherwise find the reactor | ||||
|         if (Reactor != null && !Reactor.IsDestroyed && Reactor.IsInsideTree()) | ||||
|         { | ||||
|             TargetTile = Reactor; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             TargetTile = null; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     protected virtual void TryAttackTile(BaseTile tile) | ||||
|     { | ||||
|         if (IsDead || tile == null || tile.IsDestroyed || !tile.IsInsideTree()) | ||||
|             return; | ||||
|  | ||||
|         AttackTimer = AttackCooldown; | ||||
|         bool wasDestroyed = tile.TakeDamage(Damage); | ||||
|         GD.Print($"Attacking {tile.Name} for {Damage} damage. Was destroyed: {wasDestroyed}"); | ||||
|  | ||||
|         if (wasDestroyed) | ||||
|         { | ||||
|             CollidingTiles.Remove(tile); | ||||
|             if (TargetTile == tile) | ||||
|             { | ||||
|                 TargetTile = null; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     protected virtual void OnBodyEntered(Node2D body) | ||||
|     { | ||||
|         if (body is BaseTile { IsDestroyed: false } tile && !body.IsInGroup("Hostile")) | ||||
|         { | ||||
|             GD.Print($"[Enemy] {body.Name} Entered attack range"); | ||||
|             CollidingTiles.Add(tile); | ||||
|             if (TargetTile == null || TargetTile.IsDestroyed || !TargetTile.IsInsideTree()) | ||||
|             { | ||||
|                 TargetTile = tile; | ||||
|             } | ||||
|  | ||||
|             // Attack immediately on collision | ||||
|             TryAttackTile(tile); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     protected virtual void OnBodyExited(Node2D body) | ||||
|     { | ||||
|         if (body.GetParent() is BaseTile tile) | ||||
|         { | ||||
|             CollidingTiles.Remove(tile); | ||||
|             if (TargetTile == tile) | ||||
|             { | ||||
|                 TargetTile = null; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     protected virtual void OnBodyEnteredDetection(Node2D body) | ||||
|     { | ||||
|         // Can be overridden by derived classes | ||||
|     } | ||||
|  | ||||
|     protected virtual void OnBodyExitedDetection(Node2D body) | ||||
|     { | ||||
|         // Can be overridden by derived classes | ||||
|     } | ||||
|  | ||||
|     public virtual void TakeDamage(int damage, Vector2? hitPosition = null) | ||||
|     { | ||||
|         if (IsDead) return; | ||||
|  | ||||
|         CurrentHealth -= damage; | ||||
|  | ||||
|         // Show damage number (optional) | ||||
|         if (ShowDamageNumbers) | ||||
|         { | ||||
|             var damageLabel = new Label | ||||
|             { | ||||
|                 Text = $"-{damage}", | ||||
|                 Position = hitPosition ?? GlobalPosition, | ||||
|                 ZIndex = 1000 | ||||
|             }; | ||||
|  | ||||
|             GetTree().CurrentScene.AddChild(damageLabel); | ||||
|  | ||||
|             // Animate and remove damage number | ||||
|             var tween = CreateTween(); | ||||
|             tween.TweenProperty(damageLabel, "position:y", damageLabel.Position.Y - 30, 0.5f); | ||||
|             tween.TweenCallback(Callable.From(() => damageLabel.QueueFree())).SetDelay(0.5f); | ||||
|         } | ||||
|  | ||||
|         // Visual feedback | ||||
|         var originalModulate = Modulate; | ||||
|         Modulate = new Color(1, 0.5f, 0.5f); // Flash red | ||||
|  | ||||
|         var tweenFlash = CreateTween(); | ||||
|         tweenFlash.TweenProperty(this, "modulate", originalModulate, 0.2f); | ||||
|     } | ||||
|  | ||||
|     protected virtual void UpdateHealthBar() | ||||
|     { | ||||
|         if (HealthBar != null) | ||||
|         { | ||||
|             HealthBar.Value = CurrentHealthValue; | ||||
|             HealthBar.Visible = CurrentHealthValue < MaxHealth; // Only show when damaged | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     protected virtual void Die() | ||||
|     { | ||||
|         // Play death animation/sound | ||||
|         // You can add a death animation here | ||||
|  | ||||
|         // Disable collisions and hide | ||||
|         SetProcess(false); | ||||
|         SetPhysicsProcess(false); | ||||
|         Hide(); | ||||
|  | ||||
|         // Queue free after a delay (for any death animation/sound to play) | ||||
|         var timer = new Timer(); | ||||
|         AddChild(timer); | ||||
|         timer.Timeout += () => QueueFree(); | ||||
|         timer.Start(0.5f); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										1
									
								
								Scripts/Entities/EnemyBase.cs.uid
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								Scripts/Entities/EnemyBase.cs.uid
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| uid://6oduws4kbdlf | ||||
| @@ -1,4 +1,3 @@ | ||||
| using System; | ||||
| using AceFieldNewHorizon.Scripts.System; | ||||
| using Godot; | ||||
|  | ||||
| @@ -20,7 +19,7 @@ public partial class Player : CharacterBody2D | ||||
|     [Export] public float ZoomDecay = 0.9f; | ||||
|     [Export] public float ZoomSmoothing = 10.0f; | ||||
|  | ||||
|     [Export] public ResourceManager Inventory; | ||||
|     public ResourceManager Inventory { get; private set; } | ||||
|          | ||||
|     private Camera2D _camera; | ||||
|     private Vector2 _cameraTargetZoom = Vector2.One; | ||||
| @@ -33,6 +32,8 @@ public partial class Player : CharacterBody2D | ||||
|         _camera = GetNode<Camera2D>("Camera2D"); | ||||
|         _cameraTargetZoom = _camera.Zoom; | ||||
|          | ||||
|         Inventory = DependencyInjection.Container.GetInstance<ResourceManager>(); | ||||
|          | ||||
|         AddToGroup(ItemPickup.PickupGroupName); | ||||
|         AddToGroup(NaturalResourceGenerator.ChunkTrackerGroupName); | ||||
|     } | ||||
| @@ -60,13 +61,9 @@ public partial class Player : CharacterBody2D | ||||
|          | ||||
|         // If same direction as last time, accelerate | ||||
|         if (direction == _lastZoomDirection && (currentTime - _lastZoomTime) < 300) | ||||
|         { | ||||
|             _currentZoomSpeed = Mathf.Min(_currentZoomSpeed + ZoomAcceleration, MaxZoomSpeed); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             _currentZoomSpeed = BaseZoomSpeed; | ||||
|         } | ||||
|          | ||||
|         _lastZoomDirection = direction; | ||||
|         _lastZoomTime = currentTime; | ||||
|   | ||||
							
								
								
									
										27
									
								
								Scripts/Root.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								Scripts/Root.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| using Godot; | ||||
| using AceFieldNewHorizon.Scripts.System; | ||||
| using SimpleInjector; | ||||
|  | ||||
| namespace AceFieldNewHorizon.Scripts; | ||||
|  | ||||
| public partial class Root : Node | ||||
| { | ||||
|     public override void _Ready() | ||||
|     { | ||||
|         // Dependency Injection container is now initialized via AutoLoad (DIInitializer.cs). | ||||
|  | ||||
|         // Get references to the main system nodes from the scene tree | ||||
|         // and inject their dependencies. | ||||
|         // This assumes these nodes are direct children or easily accessible. | ||||
|         // You might need to adjust paths based on your scene setup. | ||||
|  | ||||
|         // Example: | ||||
|         // var resourceManager = GetNode<ResourceManager>("ResourceSystem"); // Assuming ResourceManager is a child of Root | ||||
|         // DependencyInjection.Container.InjectProperties(resourceManager); // If ResourceManager had properties to inject | ||||
|  | ||||
|         // For now, we'll manually resolve and assign for the main system nodes. | ||||
|         // The actual injection will happen in the _Ready methods of the system nodes themselves, | ||||
|         // by resolving from the static container. | ||||
|         // This is a common pattern when Godot instantiates the nodes. | ||||
|     } | ||||
| } | ||||
							
								
								
									
										1
									
								
								Scripts/Root.cs.uid
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								Scripts/Root.cs.uid
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| uid://dmint8ii0oj5g | ||||
| @@ -40,15 +40,15 @@ public record BuildingData( | ||||
| 	} | ||||
| } | ||||
|  | ||||
| public partial class BuildingRegistry : Node | ||||
| public class BuildingRegistry | ||||
| { | ||||
| 	private Dictionary<string, BuildingData> _registry = new(); | ||||
|  | ||||
| 	[Export] public string JsonPath { get; set; } = "res://Data/Buildings.json"; | ||||
| 	 | ||||
| 	public override void _Ready() | ||||
|  | ||||
| 	public BuildingRegistry(string jsonPath) | ||||
| 	{ | ||||
| 		LoadFromJson(JsonPath); | ||||
| 		LoadFromJson(jsonPath); | ||||
| 	} | ||||
|  | ||||
| 	public void LoadFromJson(string path) | ||||
|   | ||||
							
								
								
									
										23
									
								
								Scripts/System/DependencyInjection.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								Scripts/System/DependencyInjection.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| using Godot; | ||||
| using Container = SimpleInjector.Container; | ||||
|  | ||||
| namespace AceFieldNewHorizon.Scripts.System; | ||||
|  | ||||
| public static class DependencyInjection | ||||
| { | ||||
|     public static Container Container { get; private set; } | ||||
|  | ||||
|     public static void Initialize() | ||||
|     { | ||||
|         Container = new Container(); | ||||
|  | ||||
|         // Register your system services here | ||||
|         // As singletons, since they are typically unique global managers | ||||
|         Container.RegisterSingleton<ResourceManager>(); | ||||
|         Container.RegisterSingleton<GridManager>(); | ||||
|         Container.RegisterSingleton(() => new BuildingRegistry("res://Data/Buildings.json")); | ||||
|  | ||||
|         Container.Verify(); | ||||
|         GD.Print("[DI] Simple Injector container initialized and verified."); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										1
									
								
								Scripts/System/DependencyInjection.cs.uid
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								Scripts/System/DependencyInjection.cs.uid
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| uid://hppsxnesg0ys | ||||
| @@ -43,15 +43,33 @@ public partial class GridManager : Node | ||||
|  | ||||
|     public void FreeArea(Vector2I topLeft, Vector2I size, float rotation, GridLayer layer = GridLayer.Building) | ||||
|     { | ||||
|         // Get all cells that should be occupied by this building | ||||
|         var occupiedCells = GridUtils.GetOccupiedCells(topLeft, size, rotation); | ||||
|         foreach (var cell in occupiedCells) | ||||
|      | ||||
|         // Create a list to store cells that should be removed | ||||
|         var cellsToRemove = new List<Vector2I>(); | ||||
|      | ||||
|         // First, find all cells that match this building's position and size | ||||
|         foreach (var cell in _layers[layer].Keys.ToList()) | ||||
|         { | ||||
|             var (building, buildingSize, buildingRotation) = _layers[layer][cell]; | ||||
|             var buildingCells = GridUtils.GetOccupiedCells(cell, buildingSize, buildingRotation); | ||||
|          | ||||
|             // If any of the building's cells match our target area, mark all of its cells for removal | ||||
|             if (buildingCells.Any(c => occupiedCells.Contains(c))) | ||||
|             { | ||||
|                 cellsToRemove.AddRange(buildingCells); | ||||
|             } | ||||
|         } | ||||
|      | ||||
|         // Remove all marked cells | ||||
|         foreach (var cell in cellsToRemove.Distinct()) | ||||
|         { | ||||
|             if (_layers[layer].ContainsKey(cell)) | ||||
|             _layers[layer].Remove(cell); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public Node2D? GetBuildingAtCell(Vector2I cell, GridLayer layer = GridLayer.Building) | ||||
|     public Node2D? GetTileAtCell(Vector2I cell, GridLayer layer = GridLayer.Building) | ||||
|     { | ||||
|         return _layers[layer].TryGetValue(cell, out var data) ? data.Building : null; | ||||
|     } | ||||
|   | ||||
							
								
								
									
										102
									
								
								Scripts/System/Hud.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								Scripts/System/Hud.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,102 @@ | ||||
| using Godot; | ||||
| using System.Collections.Generic; | ||||
|  | ||||
| namespace AceFieldNewHorizon.Scripts.System; | ||||
|  | ||||
| public partial class Hud : CanvasLayer | ||||
| { | ||||
| 	private ResourceManager _resourceManager; | ||||
| 	private VBoxContainer _resourceDisplay; | ||||
| 	private readonly Dictionary<string, Label> _resourceLabels = new(); | ||||
| 	private VBoxContainer _pickupLogContainer; | ||||
|  | ||||
| 	public override void _Ready() | ||||
| 	{ | ||||
| 		_resourceDisplay = GetNode<VBoxContainer>("ResourceDisplay"); | ||||
| 		 | ||||
| 		_pickupLogContainer = new VBoxContainer(); | ||||
| 		_pickupLogContainer.Name = "PickupLogContainer"; | ||||
| 		_pickupLogContainer.SetAnchorsPreset(Control.LayoutPreset.BottomLeft); | ||||
| 		_pickupLogContainer.OffsetLeft = 10; | ||||
| 		_pickupLogContainer.OffsetBottom = -10; // Negative offset from bottom anchor | ||||
| 		_pickupLogContainer.GrowVertical = Control.GrowDirection.Begin; // Make it grow upwards | ||||
| 		AddChild(_pickupLogContainer); | ||||
| 		 | ||||
| 		_resourceManager = DependencyInjection.Container.GetInstance<ResourceManager>();  | ||||
| 		if (_resourceManager == null) | ||||
| 		{ | ||||
| 			GD.PushError("ResourceSystem not found in the scene tree!"); | ||||
| 			return; | ||||
| 		} | ||||
|  | ||||
| 		_resourceManager.OnResourceChanged += UpdateResourceDisplay; | ||||
| 		_resourceManager.OnResourcePickedUp += OnResourcePickedUp; | ||||
| 		 | ||||
| 		// Initialize display with current resources | ||||
| 		foreach (var entry in _resourceManager.GetAllResources()) | ||||
| 		{ | ||||
| 			UpdateResourceDisplay(entry.Key, entry.Value); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	private void UpdateResourceDisplay(string resourceId, int newAmount) | ||||
| 	{ | ||||
| 		if (!_resourceLabels.TryGetValue(resourceId, out Label label)) | ||||
| 		{ | ||||
| 			label = new Label(); | ||||
| 			_resourceDisplay.AddChild(label); | ||||
| 			_resourceLabels.Add(resourceId, label); | ||||
| 		} | ||||
|  | ||||
| 		if (newAmount <= 0) | ||||
| 		{ | ||||
| 			// Remove label if resource amount is zero or less | ||||
| 			if (label.GetParent() != null) | ||||
| 			{ | ||||
| 				_resourceDisplay.RemoveChild(label); | ||||
| 			} | ||||
| 			_resourceLabels.Remove(resourceId); | ||||
| 			label.QueueFree(); // Free the label from memory | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			label.Text = $"{resourceId}: {newAmount}"; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	private void OnResourcePickedUp(string resourceId, int amount) | ||||
| 	{ | ||||
| 		AddPickupLogEntry($"Picked up {amount} {resourceId}!"); | ||||
| 	} | ||||
|  | ||||
| 	private void AddPickupLogEntry(string message) | ||||
| 	{ | ||||
| 		var logLabel = new Label(); | ||||
| 		logLabel.Text = message; | ||||
| 		_pickupLogContainer.AddChild(logLabel); | ||||
|  | ||||
| 		var timer = new Timer(); | ||||
| 		timer.WaitTime = 3.0f; | ||||
| 		timer.OneShot = true; | ||||
| 		timer.Autostart = true; | ||||
| 		logLabel.AddChild(timer); // Add timer as child of the label | ||||
| 		timer.Timeout += () => OnLogEntryTimeout(logLabel); | ||||
| 	} | ||||
|  | ||||
| 	private void OnLogEntryTimeout(Label logLabel) | ||||
| 	{ | ||||
| 		// Start fading out the label | ||||
| 		var tween = logLabel.CreateTween(); | ||||
| 		tween.TweenProperty(logLabel, "modulate", new Color(1, 1, 1, 0), 0.5f); // Fade out over 0.5 seconds | ||||
| 		tween.TweenCallback(Callable.From(() => logLabel.QueueFree())); // Free after fading | ||||
| 	} | ||||
|  | ||||
| 	public override void _ExitTree() | ||||
| 	{ | ||||
| 		if (_resourceManager != null) | ||||
| 		{ | ||||
| 			_resourceManager.OnResourceChanged -= UpdateResourceDisplay; | ||||
| 			_resourceManager.OnResourcePickedUp -= OnResourcePickedUp; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										1
									
								
								Scripts/System/Hud.cs.uid
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								Scripts/System/Hud.cs.uid
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| uid://ddoqqcg77f60v | ||||
| @@ -1,9 +1,14 @@ | ||||
| using System.Collections; | ||||
| using AceFieldNewHorizon.Scripts.Entities; | ||||
| using Godot; | ||||
|  | ||||
| namespace AceFieldNewHorizon.Scripts.System; | ||||
|  | ||||
| public partial class ItemPickup : Node2D | ||||
| public partial class ItemPickup : Area2D | ||||
| { | ||||
|     [Signal] | ||||
|     public delegate void StackMergedEventHandler(string itemId, int totalQuantity); | ||||
|  | ||||
|     public const string PickupGroupName = "ItemPickupTarget"; | ||||
|  | ||||
|     [Export] public string ItemId { get; set; } = ""; | ||||
| @@ -11,6 +16,50 @@ public partial class ItemPickup : Node2D | ||||
|     [Export] public bool Infinite { get; set; } = false; | ||||
|     [Export] public float MagnetRange { get; set; } = 64f; | ||||
|  | ||||
|     public int GetItemQuantity(string itemId) | ||||
|     { | ||||
|         if (itemId == ItemId) | ||||
|             return Quantity; | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     public void SetItemQuantity(string itemId, int newQuantity) | ||||
|     { | ||||
|         if (itemId == ItemId) | ||||
|         { | ||||
|             Quantity = newQuantity; | ||||
|             // Update the quantity label if it exists | ||||
|             if (_quantityLabel != null) | ||||
|             { | ||||
|                 if (Quantity > 1) | ||||
|                     _quantityLabel.Text = Quantity.ToString(); | ||||
|                 else | ||||
|                     _quantityLabel.Text = string.Empty; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public bool HasItem(string itemId) | ||||
|     { | ||||
|         return itemId == ItemId; | ||||
|     } | ||||
|  | ||||
|     public void AddItem(string itemId, int amount) | ||||
|     { | ||||
|         if (itemId == ItemId) | ||||
|         { | ||||
|             Quantity += amount; | ||||
|             // Update the quantity label if it exists | ||||
|             if (_quantityLabel != null) | ||||
|             { | ||||
|                 if (Quantity > 1) | ||||
|                     _quantityLabel.Text = Quantity.ToString(); | ||||
|                 else | ||||
|                     _quantityLabel.Text = string.Empty; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private Sprite2D _sprite; | ||||
|     private Label _quantityLabel; | ||||
|     private Sprite2D _shadowSprite; | ||||
| @@ -19,8 +68,8 @@ public partial class ItemPickup : Node2D | ||||
|     // Called when the node enters the scene tree | ||||
|     public override void _Ready() | ||||
|     { | ||||
|         var area = GetNode<Area2D>("Area2D"); | ||||
|         area.BodyEntered += OnBodyEntered; | ||||
|         BodyEntered += OnEntered; | ||||
|         AreaEntered += OnEntered; | ||||
|  | ||||
|         _sprite = GetNode<Sprite2D>("Sprite2D"); | ||||
|         UpdateTexture(); | ||||
| @@ -66,13 +115,11 @@ public partial class ItemPickup : Node2D | ||||
|         if (_playerTarget != null) | ||||
|         { | ||||
|             var distance = Position.DistanceTo(_playerTarget.Position); | ||||
|             const float speed = 10f; | ||||
|             if (distance <= MagnetRange) | ||||
|             { | ||||
|                 float speed = 10f; | ||||
|                 Position = Position.Lerp(_playerTarget.Position, (float)delta * speed); | ||||
|         } | ||||
|     } | ||||
|     } | ||||
|  | ||||
|     private void UpdateTexture() | ||||
|     { | ||||
| @@ -109,15 +156,32 @@ public partial class ItemPickup : Node2D | ||||
|         _sprite.Texture = texture; | ||||
|     } | ||||
|  | ||||
|     private void OnBodyEntered(Node body) | ||||
|     private void OnEntered(Node body) | ||||
|     { | ||||
|         if (body.IsInGroup(PickupGroupName)) | ||||
|         if (body is ItemPickup itemStack) | ||||
|         { | ||||
|             if (body.HasMethod("AddItem")) | ||||
|                 body.Call("AddItem", ItemId, Quantity); | ||||
|             // Only process the merge for the item with the lower instance ID to prevent double merging | ||||
|             if (itemStack.ItemId != ItemId || GetInstanceId() > body.GetInstanceId())  | ||||
|                 return; | ||||
|              | ||||
|             // Get current quantity and add to it | ||||
|             var currentQuantity = itemStack.GetItemQuantity(ItemId); | ||||
|             var newQuantity = currentQuantity + Quantity; | ||||
|             itemStack.SetItemQuantity(ItemId, newQuantity); | ||||
|  | ||||
|             // Emit signal for stack merge | ||||
|             EmitSignal(nameof(StackMerged), ItemId, newQuantity); | ||||
|             QueueFree(); | ||||
|         } | ||||
|         else if (body.IsInGroup(PickupGroupName)) | ||||
|         { | ||||
|             if (body is Player player) | ||||
|             { | ||||
|                 player.AddItem(ItemId, Quantity); | ||||
|             } | ||||
|  | ||||
|             if (!Infinite) | ||||
|                 QueueFree(); // remove the pickup from the world | ||||
|                 QueueFree(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -4,6 +4,7 @@ using Godot; | ||||
| using System.Collections.Generic; | ||||
| using System.Threading.Tasks; | ||||
| using AceFieldNewHorizon.Scripts.Entities; | ||||
| using AceFieldNewHorizon.Scripts.Tiles; | ||||
|  | ||||
| namespace AceFieldNewHorizon.Scripts.System; | ||||
|  | ||||
| @@ -11,8 +12,8 @@ public partial class NaturalResourceGenerator : Node2D | ||||
| { | ||||
|     public const string ChunkTrackerGroupName = "NrgTrackingTarget"; | ||||
|  | ||||
|     [Export] public GridManager Grid { get; set; } | ||||
|     [Export] public BuildingRegistry Registry { get; set; } | ||||
|     public GridManager Grid { get; private set; } | ||||
|     public BuildingRegistry Registry { get; private set; } | ||||
|  | ||||
|     [Export] public int ChunkSize = 16; | ||||
|     [Export] public int LoadDistance = 2; // Number of chunks to load in each direction | ||||
| @@ -24,6 +25,10 @@ public partial class NaturalResourceGenerator : Node2D | ||||
|     [Export] public int MaxIronVeinSize = 3; | ||||
|     [Export] public int Seed; | ||||
|  | ||||
|     [Export] public bool SpawnEnemyNest = true; | ||||
|     [Export] public int MinDistanceFromOrigin = 20; // Minimum distance from world origin (0,0) | ||||
|     [Export] public int MaxDistanceFromOrigin = 50; // Maximum distance from world origin | ||||
|  | ||||
|     private const string LogPrefix = "[NaturalGeneration]"; | ||||
|  | ||||
|     private RandomNumberGenerator _rng; | ||||
| @@ -38,8 +43,39 @@ public partial class NaturalResourceGenerator : Node2D | ||||
|  | ||||
|     public override void _Ready() | ||||
|     { | ||||
|         Grid = DependencyInjection.Container.GetInstance<GridManager>(); | ||||
|         Registry = DependencyInjection.Container.GetInstance<BuildingRegistry>(); | ||||
|         _rng = new RandomNumberGenerator(); | ||||
|         _rng.Seed = (ulong)(Seed != 0 ? Seed : (int)GD.Randi()); | ||||
|      | ||||
|         // Test if building registry is assigned | ||||
|         if (Registry == null) | ||||
|         { | ||||
|             GD.PrintErr($"{LogPrefix} BuildingRegistry is not assigned!"); | ||||
|             return; | ||||
|         } | ||||
|      | ||||
|         // Test if enemy_portal is in the registry | ||||
|         var testBuilding = Registry.GetBuilding("enemy_portal"); | ||||
|         if (testBuilding == null) | ||||
|         { | ||||
|             GD.PrintErr($"{LogPrefix} 'enemy_portal' is not found in BuildingRegistry!"); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             GD.Print($"{LogPrefix} Found enemy_portal in registry!"); | ||||
|         } | ||||
|      | ||||
|         GD.Print($"{LogPrefix} NaturalResourceGenerator ready, SpawnEnemyNest = {SpawnEnemyNest}"); | ||||
|          | ||||
|         GD.Print($"{LogPrefix} Spawning the core reactor..."); | ||||
|         SpawnCoreReactor(); | ||||
|      | ||||
|         if (SpawnEnemyNest) | ||||
|         { | ||||
|             GD.Print($"{LogPrefix} Attempting to spawn enemy nest..."); | ||||
|             SpawnRandomEnemyNest(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public override void _Process(double delta) | ||||
| @@ -120,9 +156,9 @@ public partial class NaturalResourceGenerator : Node2D | ||||
|         } | ||||
|  | ||||
|         // Generate stone veins | ||||
|         for (int x = 0; x < ChunkSize; x++) | ||||
|         for (var x = 0; x < ChunkSize; x++) | ||||
|         { | ||||
|             for (int y = 0; y < ChunkSize; y++) | ||||
|             for (var y = 0; y < ChunkSize; y++) | ||||
|             { | ||||
|                 var cell = new Vector2I(chunkWorldPos.X + x, chunkWorldPos.Y + y); | ||||
|                 if (_rng.Randf() < StoneDensity) | ||||
| @@ -130,6 +166,11 @@ public partial class NaturalResourceGenerator : Node2D | ||||
|                     var veinSize = _rng.RandiRange(MinStoneVeinSize, MaxStoneVeinSize); | ||||
|                     PlaceVeinInBackground(cell, "stone", veinSize, chunkData.StoneTiles); | ||||
|                 } | ||||
|                 else if (_rng.Randf() < IronDensity) | ||||
|                 { | ||||
|                     var veinSize = _rng.RandiRange(MinIronVeinSize, MaxIronVeinSize); | ||||
|                     PlaceVeinInBackground(cell, "ore_iron", veinSize, chunkData.IronTiles); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
| @@ -246,9 +287,9 @@ public partial class NaturalResourceGenerator : Node2D | ||||
|  | ||||
|         // Remove all tiles in this chunk | ||||
|         var chunkWorldPos = ChunkToWorldCoords(chunkPos); | ||||
|         for (int x = 0; x < ChunkSize; x++) | ||||
|         for (var x = 0; x < ChunkSize; x++) | ||||
|         { | ||||
|             for (int y = 0; y < ChunkSize; y++) | ||||
|             for (var y = 0; y < ChunkSize; y++) | ||||
|             { | ||||
|                 var cell = new Vector2I(chunkWorldPos.X + x, chunkWorldPos.Y + y); | ||||
|                 // Free a 1x1 area for each cell | ||||
| @@ -355,13 +396,47 @@ public partial class NaturalResourceGenerator : Node2D | ||||
|         GD.Print($"{LogPrefix} Finished placing vein - placed {placedCount}/{maxSize} {tileType} tiles"); | ||||
|     } | ||||
|  | ||||
|     private void SpawnCoreReactor() | ||||
|     { | ||||
|         // Place the reactor tile at the center of the map | ||||
|         PlaceTile("reactor", new Vector2I(0, 0)); | ||||
|     } | ||||
|  | ||||
|     private void SpawnRandomEnemyNest() | ||||
|     { | ||||
|         // Generate a random position within the specified distance from origin | ||||
|         var angle = _rng.Randf() * Mathf.Pi * 2; | ||||
|         var distance = _rng.RandfRange(MinDistanceFromOrigin, MaxDistanceFromOrigin); | ||||
|         var offset = new Vector2(Mathf.Cos(angle), Mathf.Sin(angle)) * distance; | ||||
|         var nestPosition = new Vector2I((int)offset.X, (int)offset.Y); | ||||
|          | ||||
|         // Try to find a valid position for the nest | ||||
|         var attempts = 0; | ||||
|         const int maxAttempts = 10; | ||||
|          | ||||
|         while (attempts < maxAttempts) | ||||
|         { | ||||
|             if (PlaceTile("enemy_portal", nestPosition)) | ||||
|             { | ||||
|                 GD.Print($"{LogPrefix} Placed enemy nest at {nestPosition}"); | ||||
|                 return; | ||||
|             } | ||||
|              | ||||
|             // Try a different position if placement failed | ||||
|             angle = _rng.Randf() * Mathf.Pi * 2; | ||||
|             distance = _rng.RandfRange(MinDistanceFromOrigin, MaxDistanceFromOrigin); | ||||
|             offset = new Vector2(Mathf.Cos(angle), Mathf.Sin(angle)) * distance; | ||||
|             nestPosition = new Vector2I((int)offset.X, (int)offset.Y); | ||||
|             attempts++; | ||||
|         } | ||||
|          | ||||
|         GD.PrintErr($"{LogPrefix} Failed to place enemy nest after {maxAttempts} attempts"); | ||||
|     } | ||||
|  | ||||
|     private bool PlaceTile(string tileType, Vector2I cell) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             // First, remove any existing tile at this position | ||||
|             Grid.FreeArea(cell, Vector2I.One, 0f, GridLayer.Ground); | ||||
|  | ||||
|             var building = Registry.GetBuilding(tileType); | ||||
|             if (building == null) | ||||
|             { | ||||
| @@ -369,6 +444,12 @@ public partial class NaturalResourceGenerator : Node2D | ||||
|                 return false; | ||||
|             } | ||||
|  | ||||
|             // Free area for ground layer if needed | ||||
|             if (building.Layer == GridLayer.Ground) | ||||
|                 Grid.FreeArea(cell, building.Size, 0f, GridLayer.Ground); | ||||
|             else | ||||
|                 GD.Print($"{LogPrefix} Attempting placing building tile {tileType} at {cell}."); | ||||
|  | ||||
|             var scene = building.Scene; | ||||
|             if (scene == null) | ||||
|             { | ||||
| @@ -376,23 +457,29 @@ public partial class NaturalResourceGenerator : Node2D | ||||
|                 return false; | ||||
|             } | ||||
|  | ||||
|             var instance = scene.Instantiate() as Node2D; | ||||
|             if (instance == null) | ||||
|             if (scene.Instantiate() is not BaseTile instance) | ||||
|             { | ||||
|                 GD.PrintErr($"{LogPrefix} Failed to instantiate scene for: {tileType}"); | ||||
|                 return false; | ||||
|             } | ||||
|  | ||||
|             instance.GlobalPosition = GridUtils.GridToWorld(cell); | ||||
|             // Calculate position with proper offset based on building size | ||||
|             var rotatedSize = building.GetRotatedSize(0f); | ||||
|             var offset = GridUtils.GetCenterOffset(rotatedSize, 0f); | ||||
|              | ||||
|             instance.ZIndex = (int)building.Layer; | ||||
|             instance.GlobalPosition = GridUtils.GridToWorld(cell) + offset; | ||||
|             AddChild(instance); | ||||
|              | ||||
|             // Occupy the appropriate area based on building size | ||||
|             Grid.OccupyArea(cell, instance, building.Size, 0f, building.Layer); | ||||
|             // GD.Print($"{LogPrefix} Successfully placed {tileType} at {cell}"); | ||||
|              | ||||
|             return true; | ||||
|         } | ||||
|         catch (Exception e) | ||||
|         { | ||||
|             GD.PrintErr($"{LogPrefix} Error placing {tileType} at {cell}: {e.Message}"); | ||||
|             GD.Print($"{LogPrefix} Stack trace: {e.StackTrace}"); | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -8,27 +8,52 @@ namespace AceFieldNewHorizon.Scripts.System; | ||||
|  | ||||
| public partial class PlacementManager : Node2D | ||||
| { | ||||
|     [Export] public GridManager Grid { get; set; } | ||||
|     [Export] public ResourceManager Inventory { get; set; } | ||||
|     [Export] public BuildingRegistry Registry { get; set; } | ||||
|     [Export] public int MaxConcurrentBuilds { get; set; } = 6; // Make it adjustable in editor | ||||
|     public GridManager Grid { get; private set; } | ||||
|     public ResourceManager Inventory { get; private set; } | ||||
|     public BuildingRegistry Registry { get; private set; } | ||||
|  | ||||
|     private static readonly List<string> BuildableTiles = ["wall", "miner"]; | ||||
|     [Export] public int MaxConcurrentBuilds { get; set; } = 6; // Make it adjustable in editor | ||||
|     [Export] public bool Enabled { get; set; } = true; | ||||
|     [Export] public StringName ToggleBuildAction { get; set; } = "toggle_build"; | ||||
|  | ||||
|     private static readonly List<string> BuildableTiles = ["wall", "miner", "turret"]; | ||||
|     private readonly Dictionary<Node2D, BuildTask> _buildTasks = new(); | ||||
|     private AudioStreamPlayer _completionSound; | ||||
|     private AudioStreamPlayer _buildingSound; | ||||
|     private AudioStreamPlayer _canceledSound; | ||||
|     private AudioStreamPlayer _cannotDeploySound; | ||||
|     private AudioStreamPlayer _notReadySound; | ||||
|     private AudioStreamPlayer _insufficientFundsSound; | ||||
|     private Node2D _currentGhost; // Keep track of the current ghost building | ||||
|  | ||||
|     public override void _Ready() | ||||
|     { | ||||
|         base._Ready(); | ||||
|  | ||||
|         Grid = DependencyInjection.Container.GetInstance<GridManager>(); | ||||
|         Inventory = DependencyInjection.Container.GetInstance<ResourceManager>(); | ||||
|         Registry = DependencyInjection.Container.GetInstance<BuildingRegistry>(); | ||||
|  | ||||
|         // Setup completion sound | ||||
|         _completionSound = new AudioStreamPlayer(); | ||||
|         AddChild(_completionSound); | ||||
|         var sound = GD.Load<AudioStream>("res://Sounds/Events/ConstructionComplete.wav"); | ||||
|         _completionSound = CreateAudioPlayer("res://Sounds/Events/ConstructionComplete.wav"); | ||||
|         _buildingSound = CreateAudioPlayer("res://Sounds/Events/Building.wav"); | ||||
|         _canceledSound = CreateAudioPlayer("res://Sounds/Events/Canceled.wav"); | ||||
|         _cannotDeploySound = CreateAudioPlayer("res://Sounds/Events/CannotDeployHere.wav"); | ||||
|         _notReadySound = CreateAudioPlayer("res://Sounds/Events/NotReady.wav"); | ||||
|         _insufficientFundsSound = CreateAudioPlayer("res://Sounds/Events/InsufficientFunds.wav"); | ||||
|     } | ||||
|  | ||||
|     private AudioStreamPlayer CreateAudioPlayer(string path) | ||||
|     { | ||||
|         var player = new AudioStreamPlayer(); | ||||
|         AddChild(player); | ||||
|         var sound = GD.Load<AudioStream>(path); | ||||
|         if (sound != null) | ||||
|         { | ||||
|             _completionSound.Stream = sound; | ||||
|             player.Stream = sound; | ||||
|         } | ||||
|  | ||||
|         return player; | ||||
|     } | ||||
|  | ||||
|     private void OnBuildCompleted() | ||||
| @@ -153,6 +178,8 @@ public partial class PlacementManager : Node2D | ||||
|  | ||||
|     public override void _Process(double delta) | ||||
|     { | ||||
|         if (!Enabled) return; | ||||
|  | ||||
|         // Snap mouse to grid | ||||
|         var mousePos = GetGlobalMousePosition(); | ||||
|         var newHoveredCell = GridUtils.WorldToGrid(mousePos); | ||||
| @@ -188,24 +215,39 @@ public partial class PlacementManager : Node2D | ||||
|         _ghostBuilding.SetGhostMode(canPlace); | ||||
|  | ||||
|         // Left click to place | ||||
|         if (Input.IsActionPressed("build_tile") && canPlace) | ||||
|         if (Input.IsActionPressed("build_tile")) | ||||
|         { | ||||
|             var building = Registry.GetBuilding(_currentBuildingId); | ||||
|             if (building == null) return; | ||||
|  | ||||
|             if (!CanStartNewBuild()) | ||||
|             { | ||||
|                 // Optionally show feedback to player that build queue is full | ||||
|                 _notReadySound.Play(); | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             // First check if area is free | ||||
|             if (!IsAreaFree(_hoveredCell, building.Size, _currentRotation, building.Layer)) | ||||
|             { | ||||
|                 // Check if the area is occupied by under-construction tiles | ||||
|                 var occupiedCells = GridUtils.GetOccupiedCells(_hoveredCell, building.Size, _currentRotation); | ||||
|                 var isUnderConstruction = occupiedCells.Any(cell => | ||||
|                     Grid.GetTileAtCell(cell, building.Layer) is BaseTile { IsConstructing: true }); | ||||
|  | ||||
|                 if (!isUnderConstruction) | ||||
|                     _cannotDeploySound.Play(); | ||||
|  | ||||
|                 return; | ||||
|             } | ||||
|              | ||||
|             // Consume resources first | ||||
|             if (!ConsumeBuildingResources(_currentBuildingId)) | ||||
|             { | ||||
|                 // Optionally show feedback to player that they can't afford this building | ||||
|                 _insufficientFundsSound.Play(); | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             // Create the building instance | ||||
|             var scene = building.Scene; | ||||
|             var buildingInstance = (BaseTile)scene.Instantiate(); | ||||
|             buildingInstance.RotationDegrees = _currentRotation; | ||||
| @@ -213,23 +255,21 @@ public partial class PlacementManager : Node2D | ||||
|             buildingInstance.Position = _ghostBuilding.Position; | ||||
|             AddChild(buildingInstance); | ||||
|  | ||||
|             // Check if area is free before placing | ||||
|             if (!IsAreaFree(_hoveredCell, building.Size, _currentRotation, building.Layer)) | ||||
|             { | ||||
|                 RefundBuildingResources(_currentBuildingId); | ||||
|                 buildingInstance.QueueFree(); | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             // Occupy the area | ||||
|             // If we get here, area is free, so we can safely occupy it | ||||
|             Grid.OccupyArea(_hoveredCell, buildingInstance, building.Size, _currentRotation, building.Layer); | ||||
|  | ||||
|             if (building.BuildTime > 0f) | ||||
|             { | ||||
|                 var wasQueueEmpty = _buildTasks.Count == 0; | ||||
|                 var buildTask = new BuildTask(OnBuildCompleted); | ||||
|                 _buildTasks[buildingInstance] = buildTask; | ||||
|  | ||||
|                 buildingInstance.StartConstruction(building.BuildTime, () => { | ||||
|                 // Play building sound only when adding to an empty queue | ||||
|                 if (wasQueueEmpty) | ||||
|                     _buildingSound.Play(); | ||||
|  | ||||
|                 buildingInstance.StartConstruction(building.BuildTime, () => | ||||
|                 { | ||||
|                     // On construction complete | ||||
|                     if (_buildTasks.TryGetValue(buildingInstance, out var task)) | ||||
|                     { | ||||
| @@ -239,6 +279,7 @@ public partial class PlacementManager : Node2D | ||||
|                             Grid.FreeArea(_hoveredCell, building.Size, _currentRotation, building.Layer); | ||||
|                             buildingInstance.QueueFree(); | ||||
|                         } | ||||
|  | ||||
|                         task.Complete(); | ||||
|                         _buildTasks.Remove(buildingInstance); | ||||
|                     } | ||||
| @@ -250,11 +291,30 @@ public partial class PlacementManager : Node2D | ||||
|             !Grid.IsAreaFree(_hoveredCell, Vector2I.One, 0f)) | ||||
|         { | ||||
|             // Right click to destroy from current layer | ||||
|             var building = Grid.GetBuildingAtCell(_hoveredCell); | ||||
|             var building = Grid.GetTileAtCell(_hoveredCell); | ||||
|             if (building == null) return; | ||||
|  | ||||
|             // Find all cells occupied by this building | ||||
|             var buildingInfo = Grid.GetBuildingInfoAtCell(_hoveredCell, GridLayer.Building); | ||||
|             if (buildingInfo == null) return; | ||||
|  | ||||
|             // Check if this building is in the build tasks (under construction) | ||||
|             if (_buildTasks.TryGetValue(building, out var buildTask)) | ||||
|             { | ||||
|                 var buildingTile = building as BaseTile; | ||||
|                 // Cancel the build task | ||||
|                 buildTask.Complete(true); // Mark as cancelled | ||||
|                 _buildTasks.Remove(building); | ||||
|                 _canceledSound.Play(); | ||||
|                 if (buildingTile == null) return; | ||||
|  | ||||
|                 // Refund resources for canceled build | ||||
|                 var buildingData = Registry.GetBuilding(buildingTile.TileId); | ||||
|                 if (buildingData != null) | ||||
|                     RefundBuildingResources(buildingTile.TileId); | ||||
|             } | ||||
|  | ||||
|             // Clean up the building and grid | ||||
|             building.QueueFree(); | ||||
|             Grid.FreeArea(buildingInfo.Value.Position, buildingInfo.Value.Size, buildingInfo.Value.Rotation); | ||||
|         } | ||||
| @@ -267,6 +327,21 @@ public partial class PlacementManager : Node2D | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public override void _Input(InputEvent @event) | ||||
|     { | ||||
|         if (@event.IsActionPressed(ToggleBuildAction)) | ||||
|         { | ||||
|             Enabled = !Enabled; | ||||
|  | ||||
|             // Hide ghost building when disabling | ||||
|             if (!Enabled && _ghostBuilding != null && _ghostBuilding.IsInsideTree()) | ||||
|             { | ||||
|                 _ghostBuilding.QueueFree(); | ||||
|                 _ghostBuilding = null; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public override void _ExitTree() | ||||
|     { | ||||
|         base._ExitTree(); | ||||
| @@ -353,34 +428,30 @@ public static class GridManagerExtensions | ||||
|     public static (Vector2I Position, Vector2I Size, float Rotation)? GetBuildingInfoAtCell(this GridManager grid, | ||||
|         Vector2I cell, GridLayer layer) | ||||
|     { | ||||
|         if (grid.GetBuildingAtCell(cell, layer) is { } building) | ||||
|         { | ||||
|         if (grid.GetTileAtCell(cell, layer) is not { } building) return null; | ||||
|         // Find the top-left position of the building | ||||
|             for (int x = 0; x < 100; x++) // Arbitrary max size | ||||
|         for (var x = 0; x < 100; x++) // Arbitrary max size | ||||
|         { | ||||
|                 for (int y = 0; y < 100; y++) | ||||
|             for (var y = 0; y < 100; y++) | ||||
|             { | ||||
|                 var checkCell = new Vector2I(cell.X - x, cell.Y - y); | ||||
|                     if (grid.GetBuildingAtCell(checkCell, layer) == building) | ||||
|                     { | ||||
|                 if (grid.GetTileAtCell(checkCell, layer) != building) continue; | ||||
|                 // Found the top-left corner, now find the size | ||||
|                 var size = Vector2I.One; | ||||
|                 // Search right | ||||
|                         while (grid.GetBuildingAtCell(new Vector2I(checkCell.X + size.X, checkCell.Y), layer) == | ||||
|                 while (grid.GetTileAtCell(new Vector2I(checkCell.X + size.X, checkCell.Y), layer) == | ||||
|                        building) | ||||
|                     size.X++; | ||||
|                 // Search down | ||||
|                         while (grid.GetBuildingAtCell(new Vector2I(checkCell.X, checkCell.Y + size.Y), layer) == | ||||
|                 while (grid.GetTileAtCell(new Vector2I(checkCell.X, checkCell.Y + size.Y), layer) == | ||||
|                        building) | ||||
|                     size.Y++; | ||||
|  | ||||
|                 // Get rotation from the first cell | ||||
|                         var rotation = 0f; // You'll need to store rotation in GridManager to make this work | ||||
|                 var rotation = 0f; // You'll need to store rotation in Grid to make this work | ||||
|                 return (checkCell, size, rotation); | ||||
|             } | ||||
|         } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return null; | ||||
|     } | ||||
|   | ||||
| @@ -14,6 +14,10 @@ public partial class ResourceManager : Node | ||||
|     [Signal] | ||||
|     public delegate void OnResourceChangedEventHandler(string resourceId, int newAmount); | ||||
|      | ||||
|     // Event for when resources are picked up (added) | ||||
|     [Signal] | ||||
|     public delegate void OnResourcePickedUpEventHandler(string resourceId, int amount); | ||||
|  | ||||
|     public override void _Ready() | ||||
|     { | ||||
|         base._Ready(); | ||||
| @@ -43,6 +47,7 @@ public partial class ResourceManager : Node | ||||
|         } | ||||
|  | ||||
|         EmitSignal(nameof(OnResourceChanged), resourceId, _resources[resourceId]); | ||||
|         EmitSignal(nameof(OnResourcePickedUp), resourceId, amount); | ||||
|     } | ||||
|  | ||||
|     // Remove resources of a specific type | ||||
|   | ||||
| @@ -10,22 +10,45 @@ public partial class BaseTile : Node2D | ||||
| { | ||||
|     [Export] public string TileId { get; set; } | ||||
|  | ||||
|     protected GridManager Grid { get; set; } | ||||
|     protected BuildingRegistry Registry { get; set; } | ||||
|  | ||||
|     public int MaxDurability { get; private set; } | ||||
|     public int CurrentDurability { get; private set; } | ||||
|     public bool IsDestroyed { get; private set; } | ||||
|  | ||||
|     private CollisionShape2D _collisionShape; | ||||
|     private Sprite2D _sprite; | ||||
|     private ColorRect _progressOverlay; | ||||
|     private Action _onConstructionComplete; | ||||
|     private Tween _damageTween; | ||||
|  | ||||
|     public bool IsConstructing; | ||||
|     public bool IsConstructed; | ||||
|  | ||||
|     public override void _Ready() | ||||
|     { | ||||
|         Grid = DependencyInjection.Container.GetInstance<GridManager>(); | ||||
|         Registry = DependencyInjection.Container.GetInstance<BuildingRegistry>(); | ||||
|  | ||||
|         _collisionShape = GetNodeOrNull<CollisionShape2D>("CollisionShape2D"); | ||||
|         _sprite = GetNodeOrNull<Sprite2D>("Sprite2D"); | ||||
|         _progressOverlay = GetNodeOrNull<ColorRect>("ProgressOverlay"); | ||||
|         if (_progressOverlay != null) | ||||
|             _progressOverlay.Visible = false; | ||||
|  | ||||
|         // Get durability from BuildingRegistry | ||||
|         var buildingData = Registry?.GetBuilding(TileId); | ||||
|         MaxDurability = buildingData?.Durability ?? 100; // Default to 100 if not found | ||||
|         CurrentDurability = MaxDurability; | ||||
|         IsDestroyed = false; | ||||
|     } | ||||
|  | ||||
|     public void SetGhostMode(bool canPlace) | ||||
|     public virtual void SetGhostMode(bool canPlace) | ||||
|     { | ||||
|         // Don't modify collision for constructing buildings | ||||
|         if (IsConstructing) return; | ||||
|  | ||||
|         if (_collisionShape != null) | ||||
|             _collisionShape.Disabled = true; | ||||
|  | ||||
| @@ -35,7 +58,7 @@ public partial class BaseTile : Node2D | ||||
|                 : new Color(1, 0, 0, 0.5f); | ||||
|     } | ||||
|  | ||||
|     public void FinalizePlacement() | ||||
|     public virtual void FinalizePlacement() | ||||
|     { | ||||
|         if (_collisionShape != null) | ||||
|             _collisionShape.Disabled = false; | ||||
| @@ -43,11 +66,91 @@ public partial class BaseTile : Node2D | ||||
|             _sprite.Modulate = Colors.White; | ||||
|     } | ||||
|  | ||||
|     public virtual bool TakeDamage(int damage) | ||||
|     { | ||||
|         if (IsDestroyed || IsConstructing) return false; | ||||
|  | ||||
|         GD.Print($"[Tile] {TileId} {GetInstanceId()} took {damage} damage"); | ||||
|  | ||||
|         CurrentDurability = Mathf.Max(0, CurrentDurability - damage); | ||||
|  | ||||
|         // Visual feedback for taking damage | ||||
|         ShowDamageEffect(); | ||||
|  | ||||
|         if (CurrentDurability <= 0) | ||||
|         { | ||||
|             Destroy(); | ||||
|             return true; // Tile was destroyed | ||||
|         } | ||||
|  | ||||
|         return false; // Tile is still alive | ||||
|     } | ||||
|  | ||||
|     private void ShowDamageEffect() | ||||
|     { | ||||
|         if (_sprite == null) return; | ||||
|  | ||||
|         // Cancel any existing tween | ||||
|         _damageTween?.Kill(); | ||||
|         _damageTween = CreateTween(); | ||||
|  | ||||
|         // Flash red briefly | ||||
|         _damageTween.TweenProperty(_sprite, "modulate", Colors.Red, 0.1f); | ||||
|         _damageTween.TweenProperty(_sprite, "modulate", Colors.White, 0.1f); | ||||
|  | ||||
|         // Shake effect | ||||
|         var originalPosition = _sprite.Position; | ||||
|         _damageTween.TweenMethod( | ||||
|             Callable.From<Vector2>(pos => _sprite.Position = pos), | ||||
|             originalPosition + new Vector2(-2, -2), | ||||
|             originalPosition + new Vector2(2, 2), | ||||
|             0.05f | ||||
|         ).SetTrans(Tween.TransitionType.Bounce).SetEase(Tween.EaseType.InOut); | ||||
|  | ||||
|         _damageTween.TweenMethod( | ||||
|             Callable.From<Vector2>(pos => _sprite.Position = pos), | ||||
|             originalPosition + new Vector2(2, 2), | ||||
|             originalPosition, | ||||
|             0.05f | ||||
|         ).SetTrans(Tween.TransitionType.Bounce).SetEase(Tween.EaseType.InOut); | ||||
|     } | ||||
|  | ||||
|     public virtual void Destroy() | ||||
|     { | ||||
|         if (IsDestroyed) return; | ||||
|  | ||||
|         IsDestroyed = true; | ||||
|  | ||||
|         // Disable collision using SetDeferred to avoid physics update conflicts | ||||
|         _collisionShape?.SetDeferred("disabled", true); | ||||
|  | ||||
|         // Fade out and remove | ||||
|         var tween = CreateTween(); | ||||
|         tween.TweenProperty(this, "modulate:a", 0f, 0.3f); | ||||
|         tween.TweenCallback(Callable.From(() => CallDeferred("queue_free"))); | ||||
|  | ||||
|         // Emit signal or call method when tile is destroyed | ||||
|         CallDeferred(nameof(OnTileDestroyed)); | ||||
|     } | ||||
|  | ||||
|     protected virtual void OnTileDestroyed() | ||||
|     { | ||||
|         // Can be overridden by derived classes for custom destruction behavior | ||||
|         var cell = GridUtils.WorldToGrid(Position); | ||||
|         var buildingInfo = Registry.GetBuilding(TileId); | ||||
|         Grid.FreeArea(cell, buildingInfo.Size, Rotation, buildingInfo.Layer); | ||||
|     } | ||||
|  | ||||
|     // Building progress visualization | ||||
|     public void StartConstruction(float buildTime, Action onComplete = null) | ||||
|     { | ||||
|         IsConstructing = true; | ||||
|         if (_collisionShape != null) | ||||
|             _collisionShape.Disabled = true; | ||||
|  | ||||
|         if (_progressOverlay == null || _sprite?.Texture == null) | ||||
|         { | ||||
|             IsConstructing = false; | ||||
|             onComplete?.Invoke(); | ||||
|             return; | ||||
|         } | ||||
| @@ -55,6 +158,10 @@ public partial class BaseTile : Node2D | ||||
|         _onConstructionComplete = onComplete; | ||||
|         var texSize = new Vector2(GridUtils.TileSize, GridUtils.TileSize); | ||||
|  | ||||
|         // Set initial transparency for construction | ||||
|         if (_sprite != null) | ||||
|             _sprite.Modulate = new Color(1, 1, 1, 0.8f); | ||||
|  | ||||
|         _progressOverlay.Visible = true; | ||||
|         _progressOverlay.Modulate = Colors.White; | ||||
|         _progressOverlay.Color = new Color(0, 0, 1, 0.4f); // semi-transparent blue | ||||
| @@ -80,8 +187,16 @@ public partial class BaseTile : Node2D | ||||
|             // Fade out the overlay | ||||
|             await FadeOutOverlay(0.5f); | ||||
|  | ||||
|             // Notify completion | ||||
|             // Construction complete - restore full opacity and enable collision | ||||
|             if (_sprite != null) | ||||
|                 _sprite.Modulate = Colors.White; | ||||
|  | ||||
|             IsConstructing = false; | ||||
|             if (_collisionShape != null) | ||||
|                 _collisionShape.Disabled = false; | ||||
|  | ||||
|             _onConstructionComplete?.Invoke(); | ||||
|             IsConstructed = true; | ||||
|         } | ||||
|  | ||||
|         RunProgress(); | ||||
|   | ||||
							
								
								
									
										129
									
								
								Scripts/Tiles/EnemyPortalTile.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								Scripts/Tiles/EnemyPortalTile.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,129 @@ | ||||
| using AceFieldNewHorizon.Scripts.Entities; | ||||
| using Godot; | ||||
|  | ||||
| namespace AceFieldNewHorizon.Scripts.Tiles; | ||||
|  | ||||
| public partial class EnemyPortalTile : BaseTile | ||||
| { | ||||
| 	[Export] public PackedScene EnemyScene; | ||||
| 	[Export] public int MaxEnemies = 5; | ||||
| 	[Export] public float SpawnDelay = 5.0f; // Time between spawn attempts | ||||
| 	[Export] public float SpawnRadius = 50.0f; // Radius around the nest where enemies can spawn | ||||
| 	[Export] public bool Active = true; | ||||
| 	 | ||||
| 	private Timer _spawnTimer; | ||||
| 	private int _currentEnemyCount; | ||||
|  | ||||
| 	public override void _Ready() | ||||
| 	{ | ||||
| 		base._Ready(); | ||||
| 		 | ||||
| 		// Add to Hostile group to prevent enemies from attacking their own nest | ||||
| 		AddToGroup("Hostile"); | ||||
| 		 | ||||
| 		// Create and configure the timer | ||||
| 		_spawnTimer = new Timer | ||||
| 		{ | ||||
| 			Autostart = true, | ||||
| 			WaitTime = SpawnDelay | ||||
| 		}; | ||||
| 		AddChild(_spawnTimer); | ||||
| 		_spawnTimer.Timeout += OnSpawnTimerTimeout; | ||||
|  | ||||
| 		var sprite = GetNode<Sprite2D>("Sprite2D"); | ||||
| 		 | ||||
| 		// Create and configure the shadow sprite | ||||
| 		var shadow = new Sprite2D | ||||
| 		{ | ||||
| 			Texture = sprite.Texture, | ||||
| 			Scale = sprite.Scale * 1.05f, // Slightly larger than the original | ||||
| 			Modulate = new Color(0, 0, 0, 0.5f), // Slightly more transparent | ||||
| 			Position = new Vector2(0, 6), // Closer to the sprite (reduced from 30) | ||||
| 			ZIndex = -1 | ||||
| 		}; | ||||
| 		AddChild(shadow); | ||||
| 		 | ||||
| 		// Create floating animation | ||||
| 		const float floatOffset = 5.0f; | ||||
| 		const float floatDuration = 2.0f; | ||||
| 		 | ||||
| 		var tween = CreateTween().SetLoops(); | ||||
| 		tween.TweenProperty(sprite, "position:y", -floatOffset, floatDuration) | ||||
| 			.SetEase(Tween.EaseType.InOut) | ||||
| 			.SetTrans(Tween.TransitionType.Sine); | ||||
| 		tween.TweenProperty(sprite, "position:y", floatOffset, floatDuration * 2) | ||||
| 			.SetEase(Tween.EaseType.InOut) | ||||
| 			.SetTrans(Tween.TransitionType.Sine); | ||||
| 		tween.TweenProperty(sprite, "position:y", 0, floatDuration) | ||||
| 			.SetEase(Tween.EaseType.InOut) | ||||
| 			.SetTrans(Tween.TransitionType.Sine); | ||||
| 		 | ||||
| 		// Animate shadow | ||||
| 		tween.Parallel().TweenProperty(shadow, "position:y", 12 - (floatOffset * 0.3f), floatDuration) | ||||
| 			.SetEase(Tween.EaseType.InOut) | ||||
| 			.SetTrans(Tween.TransitionType.Sine); | ||||
| 		tween.Parallel().TweenProperty(shadow, "scale", sprite.Scale * 1.02f, floatDuration) | ||||
| 			.SetEase(Tween.EaseType.InOut) | ||||
| 			.SetTrans(Tween.TransitionType.Sine); | ||||
| 		tween.Parallel().TweenProperty(shadow, "modulate:a", 0.6f, floatDuration) | ||||
| 			.SetEase(Tween.EaseType.InOut) | ||||
| 			.SetTrans(Tween.TransitionType.Sine); | ||||
| 		 | ||||
| 		tween.Parallel().TweenProperty(shadow, "position:y", 12 + (floatOffset * 0.4f), floatDuration * 2) | ||||
| 			.SetEase(Tween.EaseType.InOut) | ||||
| 			.SetTrans(Tween.TransitionType.Sine) | ||||
| 			.SetDelay(floatDuration); | ||||
| 		tween.Parallel().TweenProperty(shadow, "scale", sprite.Scale * 1.08f, floatDuration * 2) | ||||
| 			.SetEase(Tween.EaseType.InOut) | ||||
| 			.SetTrans(Tween.TransitionType.Sine) | ||||
| 			.SetDelay(floatDuration); | ||||
| 		tween.Parallel().TweenProperty(shadow, "modulate:a", 0.4f, floatDuration * 2) | ||||
| 			.SetEase(Tween.EaseType.InOut) | ||||
| 			.SetTrans(Tween.TransitionType.Sine) | ||||
| 			.SetDelay(floatDuration); | ||||
| 	} | ||||
|  | ||||
| 	private void OnSpawnTimerTimeout() | ||||
| 	{ | ||||
| 		if (!Active || EnemyScene == null || _currentEnemyCount >= MaxEnemies) | ||||
| 			return; | ||||
|  | ||||
| 		// Check if we can spawn more enemies | ||||
| 		var enemies = GetTree().GetNodesInGroup("Enemy"); | ||||
| 		_currentEnemyCount = enemies.Count; | ||||
| 		 | ||||
| 		if (_currentEnemyCount >= MaxEnemies) | ||||
| 			return; | ||||
|  | ||||
| 		// Spawn a new enemy | ||||
| 		var enemy = EnemyScene.Instantiate<Enemy>(); | ||||
| 		if (enemy != null) | ||||
| 		{ | ||||
| 			GetParent().AddChild(enemy); | ||||
| 			 | ||||
| 			// Calculate a random position within the spawn radius | ||||
| 			var randomAngle = GD.Randf() * Mathf.Pi * 2; | ||||
| 			var randomOffset = new Vector2( | ||||
| 				Mathf.Cos(randomAngle) * SpawnRadius, | ||||
| 				Mathf.Sin(randomAngle) * SpawnRadius | ||||
| 			); | ||||
| 			 | ||||
| 			enemy.GlobalPosition = GlobalPosition + randomOffset; | ||||
| 			_currentEnemyCount++; | ||||
| 			 | ||||
| 			// Connect to the enemy's death signal if available | ||||
| 			enemy.TreeExiting += () => OnEnemyDied(); | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	private void OnEnemyDied() | ||||
| 	{ | ||||
| 		_currentEnemyCount = Mathf.Max(0, _currentEnemyCount - 1); | ||||
| 	} | ||||
| 	 | ||||
| 	public void SetActive(bool active) | ||||
| 	{ | ||||
| 		Active = active; | ||||
| 		_spawnTimer.Paused = !active; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										1
									
								
								Scripts/Tiles/EnemyPortalTile.cs.uid
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								Scripts/Tiles/EnemyPortalTile.cs.uid
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| uid://26hl5mk4mqur | ||||
| @@ -8,6 +8,7 @@ public partial class GroundTile : BaseTile | ||||
| 	{ | ||||
| 		var sprite = GetNode<Sprite2D>("Sprite2D"); | ||||
| 		sprite.Modulate = new Color(0.75f, 0.75f, 0.75f); // Makes the sprite 25% darker	 | ||||
| 		sprite.ZIndex = -10; | ||||
| 		 | ||||
| 		base._Ready(); | ||||
| 	} | ||||
|   | ||||
| @@ -1,8 +1,83 @@ | ||||
| using AceFieldNewHorizon.Scripts.System; | ||||
| using Godot; | ||||
|  | ||||
| namespace AceFieldNewHorizon.Scripts.Tiles; | ||||
|  | ||||
| public partial class MinerTile : BaseTile | ||||
| { | ||||
|     [Export] public PackedScene ItemPickup { get; set; } | ||||
|     [Export] public string ItemToMine { get; set; } | ||||
|     [Export] public int MiningRate = 1; // Items per second | ||||
|  | ||||
|     private Vector2I _gridPosition; | ||||
|     private float _timeSinceLastMine; | ||||
|  | ||||
|     public override void _Ready() | ||||
|     { | ||||
|         base._Ready(); | ||||
|         _gridPosition = GridUtils.WorldToGrid(Position); | ||||
|  | ||||
|         var ground = Grid.GetTileAtCell(_gridPosition, GridLayer.Ground) as BaseTile; | ||||
|         if (ground == null) | ||||
|         { | ||||
|             GD.Print($"[Miner] Miner {GetInstanceId()} not found available resource..."); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         ItemToMine = (ground.TileId) switch | ||||
|         { | ||||
|             "stone" => "stone", | ||||
|             "ore_iron" => "ore_iron", | ||||
|             _ => null | ||||
|         }; | ||||
|         if (ItemToMine == null) | ||||
|             GD.Print($"[Miner] Miner {GetInstanceId()} not found available resource..."); | ||||
|     } | ||||
|  | ||||
|     public override void _Process(double delta) | ||||
|     { | ||||
|         if (ItemToMine == null) | ||||
|             return; | ||||
|          | ||||
|         // Don't mine if building is not completed | ||||
|         if (!IsConstructed || ItemPickup == null) | ||||
|             return; | ||||
|  | ||||
|         _timeSinceLastMine += (float)delta; | ||||
|  | ||||
|         if (!(_timeSinceLastMine >= 1f / MiningRate)) return; | ||||
|         _timeSinceLastMine = 0f; | ||||
|         SpawnItem(); | ||||
|     } | ||||
|  | ||||
|     private void SpawnItem() | ||||
|     { | ||||
|         var itemPickup = ItemPickup?.Instantiate<ItemPickup>(); | ||||
|         if (itemPickup == null) return; | ||||
|  | ||||
|         itemPickup.ItemId = ItemToMine; | ||||
|         itemPickup.Quantity = 1; | ||||
|  | ||||
|         // Initial position (slightly below the spawn point) | ||||
|         const int halfTileSize = GridUtils.TileSize / 2; | ||||
|         var spawnPosition = GridUtils.GridToWorld(_gridPosition); | ||||
|         var targetY = spawnPosition.Y - halfTileSize; // Target Y position | ||||
|         var targetX = spawnPosition.X + halfTileSize + (GD.Randf() * 10f - 5f); | ||||
|         itemPickup.Position = | ||||
|             new Vector2(spawnPosition.X + halfTileSize, spawnPosition.Y + 16); // Start below | ||||
|         itemPickup.Scale = Vector2.Zero; // Start invisible | ||||
|  | ||||
|         // Add to the scene | ||||
|         GetTree().CurrentScene.AddChild(itemPickup); | ||||
|  | ||||
|         // Create the pop-up animation | ||||
|         var tween = CreateTween().SetTrans(Tween.TransitionType.Elastic).SetEase(Tween.EaseType.Out); | ||||
|  | ||||
|         // Animate the pop-up effect | ||||
|         tween.TweenProperty(itemPickup, "position:y", targetY, 0.6f); | ||||
|         tween.Parallel().TweenProperty(itemPickup, "scale", Vector2.One, 0.6f); | ||||
|  | ||||
|         // Optional: Add a slight horizontal wobble | ||||
|         tween.Parallel().TweenProperty(itemPickup, "position:x", targetX, 0.6f); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										13
									
								
								Scripts/Tiles/ReactorTile.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								Scripts/Tiles/ReactorTile.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| namespace AceFieldNewHorizon.Scripts.Tiles; | ||||
|  | ||||
| public partial class ReactorTile : BaseTile | ||||
| { | ||||
|     public static readonly string ReactorGroupName = "Reactor"; | ||||
|  | ||||
|     public override void _Ready() | ||||
|     { | ||||
|         base._Ready(); | ||||
|          | ||||
|         AddToGroup(ReactorGroupName); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										1
									
								
								Scripts/Tiles/ReactorTile.cs.uid
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								Scripts/Tiles/ReactorTile.cs.uid
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| uid://c4k3ottt7j3b1 | ||||
							
								
								
									
										127
									
								
								Scripts/Tiles/TurretTile.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								Scripts/Tiles/TurretTile.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,127 @@ | ||||
| using System.Linq; | ||||
| using AceFieldNewHorizon.Scripts.Entities; | ||||
| using Godot; | ||||
|  | ||||
| namespace AceFieldNewHorizon.Scripts.Tiles; | ||||
|  | ||||
| public partial class TurretTile : BaseTile | ||||
| { | ||||
|     [Export] public PackedScene BulletScene; | ||||
|      | ||||
|     [Export] public float RotationSpeed = 2.0f; // Radians per second | ||||
|     [Export] public float AttackRange = 300.0f; | ||||
|     [Export] public float AttackCooldown = 0.5f; | ||||
|     [Export] public int Damage = 10; | ||||
|     [Export] public float BulletSpeed = 400.0f; | ||||
|     [Export] public NodePath BarrelTipPath; | ||||
|      | ||||
|     private Node2D _spriteBarrel; | ||||
|     private Node2D _barrelTip; | ||||
|     private float _attackTimer = 0; | ||||
|     private bool _hasTarget = false; | ||||
|  | ||||
|     public override void _Ready() | ||||
|     { | ||||
|         base._Ready(); | ||||
|  | ||||
|         _spriteBarrel = GetNodeOrNull<Node2D>("Barrel"); | ||||
|         _barrelTip = GetNodeOrNull<Node2D>(BarrelTipPath); | ||||
|          | ||||
|         if (_barrelTip == null && _spriteBarrel != null) | ||||
|         { | ||||
|             // If no barrel tip is specified, use the end of the barrel sprite | ||||
|             _barrelTip = _spriteBarrel.GetNodeOrNull<Node2D>("BarrelTip"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public override void SetGhostMode(bool canPlace) | ||||
|     { | ||||
|         base.SetGhostMode(canPlace); | ||||
|  | ||||
|         if (_spriteBarrel != null) | ||||
|             _spriteBarrel.Modulate = canPlace | ||||
|                 ? new Color(0, 1, 0, 0.5f) | ||||
|                 : new Color(1, 0, 0, 0.5f); | ||||
|     } | ||||
|  | ||||
|     public override void FinalizePlacement() | ||||
|     { | ||||
|         base.FinalizePlacement(); | ||||
|  | ||||
|         if (_spriteBarrel != null) | ||||
|             _spriteBarrel.Modulate = Colors.White; | ||||
|     } | ||||
|  | ||||
|     public override void _Process(double delta) | ||||
|     { | ||||
|         if (!IsConstructed) return; | ||||
|  | ||||
|         // Update attack cooldown | ||||
|         if (_attackTimer > 0) | ||||
|         { | ||||
|             _attackTimer -= (float)delta; | ||||
|         } | ||||
|  | ||||
|         // Find the nearest enemy | ||||
|         var enemies = GetTree().GetNodesInGroup(Enemy.EnemyGroupName) | ||||
|             .OfType<Enemy>() | ||||
|             .Where(e => e.GlobalPosition.DistanceTo(GlobalPosition) <= AttackRange) | ||||
|             .OrderBy(e => e.GlobalPosition.DistanceTo(GlobalPosition)) | ||||
|             .ToList(); | ||||
|  | ||||
|         if (enemies.Count > 0) | ||||
|         { | ||||
|             var nearestEnemy = enemies[0]; | ||||
|             _hasTarget = true; | ||||
|              | ||||
|             // Calculate target angle | ||||
|             var direction = (nearestEnemy.GlobalPosition - _barrelTip.GlobalPosition).Normalized(); | ||||
|             var targetAngle = direction.Angle() + 90f; | ||||
|  | ||||
|             // Smoothly rotate towards target | ||||
|             _spriteBarrel.Rotation = Mathf.LerpAngle(_spriteBarrel.Rotation, targetAngle, (float)delta * RotationSpeed); | ||||
|  | ||||
|             // Check if we're facing the target and can attack | ||||
|             if (Mathf.Abs(Mathf.Wrap(targetAngle - _spriteBarrel.Rotation, -Mathf.Pi, Mathf.Pi)) < 0.1f) | ||||
|                 TryAttack(nearestEnemy.GlobalPosition); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             _hasTarget = false; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void TryAttack(Vector2 targetPosition) | ||||
|     { | ||||
|         if (_attackTimer <= 0 && BulletScene != null && _barrelTip != null) | ||||
|         { | ||||
|             // Create bullet instance | ||||
|             var bullet = BulletScene.Instantiate<Bullet>(); | ||||
|              | ||||
|             // Calculate direction and rotation | ||||
|             var direction = (targetPosition - _barrelTip.GlobalPosition).Normalized(); | ||||
|             var bulletRotation = direction.Angle(); | ||||
|              | ||||
|             // Set bullet position and rotation | ||||
|             GetTree().CurrentScene.AddChild(bullet); | ||||
|             bullet.GlobalPosition = _barrelTip.GlobalPosition; | ||||
|             bullet.Rotation = bulletRotation; // Use the calculated rotation | ||||
|              | ||||
|             // Initialize bullet with direction and damage | ||||
|             bullet.Initialize( | ||||
|                 direction,  | ||||
|                 bullet.GlobalPosition,  | ||||
|                 bulletRotation, // Pass the calculated rotation | ||||
|                 1 // Pass the turret's collision layer to ignore | ||||
|             ); | ||||
|             bullet.Damage = Damage; | ||||
|             bullet.Speed = BulletSpeed; | ||||
|             bullet.MaxDistance = AttackRange * 1.5f; // Bullets can travel slightly further than attack range | ||||
|              | ||||
|             // Reset attack cooldown | ||||
|             _attackTimer = AttackCooldown; | ||||
|              | ||||
|             GD.Print("[Turret] Turret firing!"); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										1
									
								
								Scripts/Tiles/TurretTile.cs.uid
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								Scripts/Tiles/TurretTile.cs.uid
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| uid://n5g6i0uovxfk | ||||
							
								
								
									
										
											BIN
										
									
								
								Sounds/Events/Building.wav
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Sounds/Events/Building.wav
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										24
									
								
								Sounds/Events/Building.wav.import
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								Sounds/Events/Building.wav.import
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| [remap] | ||||
|  | ||||
| importer="wav" | ||||
| type="AudioStreamWAV" | ||||
| uid="uid://d1trbqrntmuij" | ||||
| path="res://.godot/imported/Building.wav-b8766581fd25a206c63f47b13bd2e2f5.sample" | ||||
|  | ||||
| [deps] | ||||
|  | ||||
| source_file="res://Sounds/Events/Building.wav" | ||||
| dest_files=["res://.godot/imported/Building.wav-b8766581fd25a206c63f47b13bd2e2f5.sample"] | ||||
|  | ||||
| [params] | ||||
|  | ||||
| force/8_bit=false | ||||
| force/mono=false | ||||
| force/max_rate=false | ||||
| force/max_rate_hz=44100 | ||||
| edit/trim=false | ||||
| edit/normalize=false | ||||
| edit/loop_mode=0 | ||||
| edit/loop_begin=0 | ||||
| edit/loop_end=-1 | ||||
| compress/mode=2 | ||||
							
								
								
									
										
											BIN
										
									
								
								Sounds/Events/Canceled.wav
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Sounds/Events/Canceled.wav
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										24
									
								
								Sounds/Events/Canceled.wav.import
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								Sounds/Events/Canceled.wav.import
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| [remap] | ||||
|  | ||||
| importer="wav" | ||||
| type="AudioStreamWAV" | ||||
| uid="uid://chn8ux4two1kd" | ||||
| path="res://.godot/imported/Canceled.wav-6d441d9a898b5cd9be4c0664a1f489ed.sample" | ||||
|  | ||||
| [deps] | ||||
|  | ||||
| source_file="res://Sounds/Events/Canceled.wav" | ||||
| dest_files=["res://.godot/imported/Canceled.wav-6d441d9a898b5cd9be4c0664a1f489ed.sample"] | ||||
|  | ||||
| [params] | ||||
|  | ||||
| force/8_bit=false | ||||
| force/mono=false | ||||
| force/max_rate=false | ||||
| force/max_rate_hz=44100 | ||||
| edit/trim=false | ||||
| edit/normalize=false | ||||
| edit/loop_mode=0 | ||||
| edit/loop_begin=0 | ||||
| edit/loop_end=-1 | ||||
| compress/mode=2 | ||||
							
								
								
									
										
											BIN
										
									
								
								Sounds/Events/CannotDeployHere.wav
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Sounds/Events/CannotDeployHere.wav
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										24
									
								
								Sounds/Events/CannotDeployHere.wav.import
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								Sounds/Events/CannotDeployHere.wav.import
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| [remap] | ||||
|  | ||||
| importer="wav" | ||||
| type="AudioStreamWAV" | ||||
| uid="uid://7u1gw7lt5xd1" | ||||
| path="res://.godot/imported/CannotDeployHere.wav-a9ec27508654f74d03ac7b8c8037059c.sample" | ||||
|  | ||||
| [deps] | ||||
|  | ||||
| source_file="res://Sounds/Events/CannotDeployHere.wav" | ||||
| dest_files=["res://.godot/imported/CannotDeployHere.wav-a9ec27508654f74d03ac7b8c8037059c.sample"] | ||||
|  | ||||
| [params] | ||||
|  | ||||
| force/8_bit=false | ||||
| force/mono=false | ||||
| force/max_rate=false | ||||
| force/max_rate_hz=44100 | ||||
| edit/trim=false | ||||
| edit/normalize=false | ||||
| edit/loop_mode=0 | ||||
| edit/loop_begin=0 | ||||
| edit/loop_end=-1 | ||||
| compress/mode=2 | ||||
							
								
								
									
										
											BIN
										
									
								
								Sounds/Events/InsufficientFunds.wav
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Sounds/Events/InsufficientFunds.wav
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										24
									
								
								Sounds/Events/InsufficientFunds.wav.import
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								Sounds/Events/InsufficientFunds.wav.import
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| [remap] | ||||
|  | ||||
| importer="wav" | ||||
| type="AudioStreamWAV" | ||||
| uid="uid://bemxvqcettqgp" | ||||
| path="res://.godot/imported/InsufficientFunds.wav-7aba215cb1cd04a5285a5e9908999906.sample" | ||||
|  | ||||
| [deps] | ||||
|  | ||||
| source_file="res://Sounds/Events/InsufficientFunds.wav" | ||||
| dest_files=["res://.godot/imported/InsufficientFunds.wav-7aba215cb1cd04a5285a5e9908999906.sample"] | ||||
|  | ||||
| [params] | ||||
|  | ||||
| force/8_bit=false | ||||
| force/mono=false | ||||
| force/max_rate=false | ||||
| force/max_rate_hz=44100 | ||||
| edit/trim=false | ||||
| edit/normalize=false | ||||
| edit/loop_mode=0 | ||||
| edit/loop_begin=0 | ||||
| edit/loop_end=-1 | ||||
| compress/mode=2 | ||||
							
								
								
									
										
											BIN
										
									
								
								Sounds/Events/NotReady.wav
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Sounds/Events/NotReady.wav
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										24
									
								
								Sounds/Events/NotReady.wav.import
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								Sounds/Events/NotReady.wav.import
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| [remap] | ||||
|  | ||||
| importer="wav" | ||||
| type="AudioStreamWAV" | ||||
| uid="uid://dogbl6tfealwg" | ||||
| path="res://.godot/imported/NotReady.wav-2cfa5f22feed110ca411d6478060fdea.sample" | ||||
|  | ||||
| [deps] | ||||
|  | ||||
| source_file="res://Sounds/Events/NotReady.wav" | ||||
| dest_files=["res://.godot/imported/NotReady.wav-2cfa5f22feed110ca411d6478060fdea.sample"] | ||||
|  | ||||
| [params] | ||||
|  | ||||
| force/8_bit=false | ||||
| force/mono=false | ||||
| force/max_rate=false | ||||
| force/max_rate_hz=44100 | ||||
| edit/trim=false | ||||
| edit/normalize=false | ||||
| edit/loop_mode=0 | ||||
| edit/loop_begin=0 | ||||
| edit/loop_end=-1 | ||||
| compress/mode=2 | ||||
| @@ -15,8 +15,15 @@ run/main_scene="uid://c22aprj452aha" | ||||
| config/features=PackedStringArray("4.4", "C#", "GL Compatibility") | ||||
| config/icon="res://icon.svg" | ||||
|  | ||||
| [autoload] | ||||
|  | ||||
| ResourceManager="res://Scripts/System/ResourceManager.cs" | ||||
| DiInitializer="*res://Scripts/AutoLoad/DIInitializer.cs" | ||||
|  | ||||
| [display] | ||||
|  | ||||
| window/size/viewport_width=1920 | ||||
| window/size/viewport_height=1080 | ||||
| window/stretch/mode="viewport" | ||||
|  | ||||
| [dotnet] | ||||
| @@ -75,8 +82,14 @@ switch_tile={ | ||||
| "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":93,"key_label":0,"unicode":93,"location":0,"echo":false,"script":null) | ||||
| ] | ||||
| } | ||||
| toggle_build={ | ||||
| "deadzone": 0.2, | ||||
| "events": [] | ||||
| } | ||||
|  | ||||
| [rendering] | ||||
|  | ||||
| anti_aliasing/quality/msaa_2d=3 | ||||
| anti_aliasing/quality/msaa_3d=3 | ||||
| anti_aliasing/quality/screen_space_aa=1 | ||||
| anti_aliasing/quality/use_taa=true | ||||
|   | ||||
		Reference in New Issue
	
	Block a user