Compare commits
	
		
			12 Commits
		
	
	
		
			60b6d6f989
			...
			master
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 2c5e0459ad | |||
| f2c243ecf6 | |||
| b424aafeab | |||
| 09511b37c9 | |||
| c72353716f | |||
| 1cc941d893 | |||
| 88647b1c41 | |||
| 7438ba407a | |||
| 1c6c03cd41 | |||
| 32f96d488d | |||
| 630dbf0800 | |||
| ac1d8cfab9 | 
| @@ -4,4 +4,7 @@ | |||||||
|     <EnableDynamicLoading>true</EnableDynamicLoading> |     <EnableDynamicLoading>true</EnableDynamicLoading> | ||||||
|     <RootNamespace>AceFieldNewHorizon</RootNamespace> |     <RootNamespace>AceFieldNewHorizon</RootNamespace> | ||||||
|   </PropertyGroup> |   </PropertyGroup> | ||||||
|  |   <ItemGroup> | ||||||
|  |     <PackageReference Include="SimpleInjector" Version="5.5.0" /> | ||||||
|  |   </ItemGroup> | ||||||
| </Project> | </Project> | ||||||
| @@ -21,6 +21,36 @@ | |||||||
| 	"layer": 1, | 	"layer": 1, | ||||||
| 	"size": [1, 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": { |   "ground": { | ||||||
| 	"scene": "res://Scenes/Tiles/GroundTile.tscn", | 	"scene": "res://Scenes/Tiles/GroundTile.tscn", | ||||||
| 	"cost": {}, | 	"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"] | [ext_resource type="Texture2D" uid="uid://jye6c2ehuxtg" path="res://Scenes/Entities/Player.png" id="1_ucweq"] | ||||||
|  |  | ||||||
| [node name="Player" type="CharacterBody2D"] | [node name="Player" type="CharacterBody2D"] | ||||||
|  | collision_mask = 3 | ||||||
| script = ExtResource("1_08t41") | script = ExtResource("1_08t41") | ||||||
| MinZoom = 0.1 | MinZoom = 0.1 | ||||||
| MaxZoom = 5.0 | MaxZoom = 5.0 | ||||||
|   | |||||||
| @@ -1,38 +1,23 @@ | |||||||
| [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://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="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://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://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="Root" type="Node2D"] | ||||||
|  |  | ||||||
| [node name="ResourceSystem" type="Node" parent="."] | [node name="NaturalResourceGenerator" type="Node2D" 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")] |  | ||||||
| script = ExtResource("2_oss8w") | script = ExtResource("2_oss8w") | ||||||
| Grid = NodePath("../GridSystem") |  | ||||||
| Registry = NodePath("../BuildingRegistry") |  | ||||||
|  |  | ||||||
| [node name="GridSystem" type="Node2D" parent="."] | [node name="PlacementSystem" type="Node2D" parent="."] | ||||||
| script = ExtResource("1_knkkn") |  | ||||||
|  |  | ||||||
| [node name="PlacementSystem" type="Node2D" parent="." node_paths=PackedStringArray("Grid", "Inventory", "Registry")] |  | ||||||
| script = ExtResource("2_sxhdm") | 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) | 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")] | [node name="ItemPickup" parent="." instance=ExtResource("7_is6ib")] | ||||||
| position = Vector2(-496, -245) | position = Vector2(-496, -245) | ||||||
|   | |||||||
							
								
								
									
										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 | ||||||
							
								
								
									
										
											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"] | [node name="GroundTile" type="StaticBody2D"] | ||||||
| collision_layer = 0 | collision_layer = 0 | ||||||
|  | collision_mask = 0 | ||||||
| script = ExtResource("1_mqsaf") | script = ExtResource("1_mqsaf") | ||||||
| TileId = "ground" | TileId = "ground" | ||||||
|  |  | ||||||
|   | |||||||
| @@ -8,8 +8,9 @@ size = Vector2(54, 54) | |||||||
|  |  | ||||||
| [node name="OreIronTile" type="StaticBody2D"] | [node name="OreIronTile" type="StaticBody2D"] | ||||||
| collision_layer = 0 | collision_layer = 0 | ||||||
|  | collision_mask = 0 | ||||||
| script = ExtResource("1_exnim") | script = ExtResource("1_exnim") | ||||||
| TileId = "stone_iron" | TileId = "ore_iron" | ||||||
|  |  | ||||||
| [node name="Sprite2D" type="Sprite2D" parent="."] | [node name="Sprite2D" type="Sprite2D" parent="."] | ||||||
| position = Vector2(1.49012e-08, -9.53674e-07) | 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"] | [node name="StoneTile" type="StaticBody2D"] | ||||||
| collision_layer = 0 | collision_layer = 0 | ||||||
|  | collision_mask = 0 | ||||||
| script = ExtResource("1_rndy8") | script = ExtResource("1_rndy8") | ||||||
| TileId = "stone" | 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 AceFieldNewHorizon.Scripts.System; | ||||||
| using Godot; | using Godot; | ||||||
|  |  | ||||||
| @@ -20,7 +19,7 @@ public partial class Player : CharacterBody2D | |||||||
|     [Export] public float ZoomDecay = 0.9f; |     [Export] public float ZoomDecay = 0.9f; | ||||||
|     [Export] public float ZoomSmoothing = 10.0f; |     [Export] public float ZoomSmoothing = 10.0f; | ||||||
|  |  | ||||||
|     [Export] public ResourceManager Inventory; |     public ResourceManager Inventory { get; private set; } | ||||||
|          |          | ||||||
|     private Camera2D _camera; |     private Camera2D _camera; | ||||||
|     private Vector2 _cameraTargetZoom = Vector2.One; |     private Vector2 _cameraTargetZoom = Vector2.One; | ||||||
| @@ -33,6 +32,8 @@ public partial class Player : CharacterBody2D | |||||||
|         _camera = GetNode<Camera2D>("Camera2D"); |         _camera = GetNode<Camera2D>("Camera2D"); | ||||||
|         _cameraTargetZoom = _camera.Zoom; |         _cameraTargetZoom = _camera.Zoom; | ||||||
|          |          | ||||||
|  |         Inventory = DependencyInjection.Container.GetInstance<ResourceManager>(); | ||||||
|  |          | ||||||
|         AddToGroup(ItemPickup.PickupGroupName); |         AddToGroup(ItemPickup.PickupGroupName); | ||||||
|         AddToGroup(NaturalResourceGenerator.ChunkTrackerGroupName); |         AddToGroup(NaturalResourceGenerator.ChunkTrackerGroupName); | ||||||
|     } |     } | ||||||
| @@ -60,13 +61,9 @@ public partial class Player : CharacterBody2D | |||||||
|          |          | ||||||
|         // If same direction as last time, accelerate |         // If same direction as last time, accelerate | ||||||
|         if (direction == _lastZoomDirection && (currentTime - _lastZoomTime) < 300) |         if (direction == _lastZoomDirection && (currentTime - _lastZoomTime) < 300) | ||||||
|         { |  | ||||||
|             _currentZoomSpeed = Mathf.Min(_currentZoomSpeed + ZoomAcceleration, MaxZoomSpeed); |             _currentZoomSpeed = Mathf.Min(_currentZoomSpeed + ZoomAcceleration, MaxZoomSpeed); | ||||||
|         } |  | ||||||
|         else |         else | ||||||
|         { |  | ||||||
|             _currentZoomSpeed = BaseZoomSpeed; |             _currentZoomSpeed = BaseZoomSpeed; | ||||||
|         } |  | ||||||
|          |          | ||||||
|         _lastZoomDirection = direction; |         _lastZoomDirection = direction; | ||||||
|         _lastZoomTime = currentTime; |         _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(); | 	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) | 	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) |     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); |         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); |             _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; |         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 | ||||||
| @@ -12,8 +12,8 @@ public partial class NaturalResourceGenerator : Node2D | |||||||
| { | { | ||||||
|     public const string ChunkTrackerGroupName = "NrgTrackingTarget"; |     public const string ChunkTrackerGroupName = "NrgTrackingTarget"; | ||||||
|  |  | ||||||
|     [Export] public GridManager Grid { get; set; } |     public GridManager Grid { get; private set; } | ||||||
|     [Export] public BuildingRegistry Registry { get; set; } |     public BuildingRegistry Registry { get; private set; } | ||||||
|  |  | ||||||
|     [Export] public int ChunkSize = 16; |     [Export] public int ChunkSize = 16; | ||||||
|     [Export] public int LoadDistance = 2; // Number of chunks to load in each direction |     [Export] public int LoadDistance = 2; // Number of chunks to load in each direction | ||||||
| @@ -25,6 +25,10 @@ public partial class NaturalResourceGenerator : Node2D | |||||||
|     [Export] public int MaxIronVeinSize = 3; |     [Export] public int MaxIronVeinSize = 3; | ||||||
|     [Export] public int Seed; |     [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 const string LogPrefix = "[NaturalGeneration]"; | ||||||
|  |  | ||||||
|     private RandomNumberGenerator _rng; |     private RandomNumberGenerator _rng; | ||||||
| @@ -39,8 +43,39 @@ public partial class NaturalResourceGenerator : Node2D | |||||||
|  |  | ||||||
|     public override void _Ready() |     public override void _Ready() | ||||||
|     { |     { | ||||||
|  |         Grid = DependencyInjection.Container.GetInstance<GridManager>(); | ||||||
|  |         Registry = DependencyInjection.Container.GetInstance<BuildingRegistry>(); | ||||||
|         _rng = new RandomNumberGenerator(); |         _rng = new RandomNumberGenerator(); | ||||||
|         _rng.Seed = (ulong)(Seed != 0 ? Seed : (int)GD.Randi()); |         _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) |     public override void _Process(double delta) | ||||||
| @@ -121,9 +156,9 @@ public partial class NaturalResourceGenerator : Node2D | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         // Generate stone veins |         // 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); |                 var cell = new Vector2I(chunkWorldPos.X + x, chunkWorldPos.Y + y); | ||||||
|                 if (_rng.Randf() < StoneDensity) |                 if (_rng.Randf() < StoneDensity) | ||||||
| @@ -131,6 +166,11 @@ public partial class NaturalResourceGenerator : Node2D | |||||||
|                     var veinSize = _rng.RandiRange(MinStoneVeinSize, MaxStoneVeinSize); |                     var veinSize = _rng.RandiRange(MinStoneVeinSize, MaxStoneVeinSize); | ||||||
|                     PlaceVeinInBackground(cell, "stone", veinSize, chunkData.StoneTiles); |                     PlaceVeinInBackground(cell, "stone", veinSize, chunkData.StoneTiles); | ||||||
|                 } |                 } | ||||||
|  |                 else if (_rng.Randf() < IronDensity) | ||||||
|  |                 { | ||||||
|  |                     var veinSize = _rng.RandiRange(MinIronVeinSize, MaxIronVeinSize); | ||||||
|  |                     PlaceVeinInBackground(cell, "ore_iron", veinSize, chunkData.IronTiles); | ||||||
|  |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -247,9 +287,9 @@ public partial class NaturalResourceGenerator : Node2D | |||||||
|  |  | ||||||
|         // Remove all tiles in this chunk |         // Remove all tiles in this chunk | ||||||
|         var chunkWorldPos = ChunkToWorldCoords(chunkPos); |         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); |                 var cell = new Vector2I(chunkWorldPos.X + x, chunkWorldPos.Y + y); | ||||||
|                 // Free a 1x1 area for each cell |                 // Free a 1x1 area for each cell | ||||||
| @@ -356,13 +396,47 @@ public partial class NaturalResourceGenerator : Node2D | |||||||
|         GD.Print($"{LogPrefix} Finished placing vein - placed {placedCount}/{maxSize} {tileType} tiles"); |         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) |     private bool PlaceTile(string tileType, Vector2I cell) | ||||||
|     { |     { | ||||||
|         try |         try | ||||||
|         { |         { | ||||||
|             // First, remove any existing tile at this position |  | ||||||
|             Grid.FreeArea(cell, Vector2I.One, 0f, GridLayer.Ground); |  | ||||||
|  |  | ||||||
|             var building = Registry.GetBuilding(tileType); |             var building = Registry.GetBuilding(tileType); | ||||||
|             if (building == null) |             if (building == null) | ||||||
|             { |             { | ||||||
| @@ -370,6 +444,12 @@ public partial class NaturalResourceGenerator : Node2D | |||||||
|                 return false; |                 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; |             var scene = building.Scene; | ||||||
|             if (scene == null) |             if (scene == null) | ||||||
|             { |             { | ||||||
| @@ -383,20 +463,23 @@ public partial class NaturalResourceGenerator : Node2D | |||||||
|                 return false; |                 return false; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             // Use the same positioning logic as PlacementManager |             // Calculate position with proper offset based on building size | ||||||
|             var rotatedSize = building.GetRotatedSize(0f); // 0f for no rotation |             var rotatedSize = building.GetRotatedSize(0f); | ||||||
|             var offset = GridUtils.GetCenterOffset(rotatedSize, 0f); // 0f for no rotation |             var offset = GridUtils.GetCenterOffset(rotatedSize, 0f); | ||||||
|             instance.GlobalPosition = GridUtils.GridToWorld(cell) + offset; |              | ||||||
|             instance.ZIndex = (int)building.Layer; |             instance.ZIndex = (int)building.Layer; | ||||||
|             instance.Grid = Grid; |             instance.GlobalPosition = GridUtils.GridToWorld(cell) + offset; | ||||||
|             AddChild(instance); |             AddChild(instance); | ||||||
|  |              | ||||||
|  |             // Occupy the appropriate area based on building size | ||||||
|             Grid.OccupyArea(cell, instance, building.Size, 0f, building.Layer); |             Grid.OccupyArea(cell, instance, building.Size, 0f, building.Layer); | ||||||
|             // GD.Print($"{LogPrefix} Successfully placed {tileType} at {cell}"); |              | ||||||
|             return true; |             return true; | ||||||
|         } |         } | ||||||
|         catch (Exception e) |         catch (Exception e) | ||||||
|         { |         { | ||||||
|             GD.PrintErr($"{LogPrefix} Error placing {tileType} at {cell}: {e.Message}"); |             GD.PrintErr($"{LogPrefix} Error placing {tileType} at {cell}: {e.Message}"); | ||||||
|  |             GD.Print($"{LogPrefix} Stack trace: {e.StackTrace}"); | ||||||
|             return false; |             return false; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -8,15 +8,15 @@ namespace AceFieldNewHorizon.Scripts.System; | |||||||
|  |  | ||||||
| public partial class PlacementManager : Node2D | public partial class PlacementManager : Node2D | ||||||
| { | { | ||||||
|     [Export] public GridManager Grid { get; set; } |     public GridManager Grid { get; private set; } | ||||||
|     [Export] public ResourceManager Inventory { get; set; } |     public ResourceManager Inventory { get; private set; } | ||||||
|     [Export] public BuildingRegistry Registry { get; set; } |     public BuildingRegistry Registry { get; private set; } | ||||||
|  |  | ||||||
|     [Export] public int MaxConcurrentBuilds { get; set; } = 6; // Make it adjustable in editor |     [Export] public int MaxConcurrentBuilds { get; set; } = 6; // Make it adjustable in editor | ||||||
|     [Export] public bool Enabled { get; set; } = true; |     [Export] public bool Enabled { get; set; } = true; | ||||||
|     [Export] public StringName ToggleBuildAction { get; set; } = "toggle_build"; |     [Export] public StringName ToggleBuildAction { get; set; } = "toggle_build"; | ||||||
|  |  | ||||||
|     private static readonly List<string> BuildableTiles = ["wall", "miner"]; |     private static readonly List<string> BuildableTiles = ["wall", "miner", "turret"]; | ||||||
|     private readonly Dictionary<Node2D, BuildTask> _buildTasks = new(); |     private readonly Dictionary<Node2D, BuildTask> _buildTasks = new(); | ||||||
|     private AudioStreamPlayer _completionSound; |     private AudioStreamPlayer _completionSound; | ||||||
|     private AudioStreamPlayer _buildingSound; |     private AudioStreamPlayer _buildingSound; | ||||||
| @@ -30,6 +30,10 @@ public partial class PlacementManager : Node2D | |||||||
|     { |     { | ||||||
|         base._Ready(); |         base._Ready(); | ||||||
|  |  | ||||||
|  |         Grid = DependencyInjection.Container.GetInstance<GridManager>(); | ||||||
|  |         Inventory = DependencyInjection.Container.GetInstance<ResourceManager>(); | ||||||
|  |         Registry = DependencyInjection.Container.GetInstance<BuildingRegistry>(); | ||||||
|  |  | ||||||
|         // Setup completion sound |         // Setup completion sound | ||||||
|         _completionSound = CreateAudioPlayer("res://Sounds/Events/ConstructionComplete.wav"); |         _completionSound = CreateAudioPlayer("res://Sounds/Events/ConstructionComplete.wav"); | ||||||
|         _buildingSound = CreateAudioPlayer("res://Sounds/Events/Building.wav"); |         _buildingSound = CreateAudioPlayer("res://Sounds/Events/Building.wav"); | ||||||
| @@ -199,7 +203,6 @@ public partial class PlacementManager : Node2D | |||||||
|             var scene = building.Scene; |             var scene = building.Scene; | ||||||
|  |  | ||||||
|             _ghostBuilding = (BaseTile)scene.Instantiate(); |             _ghostBuilding = (BaseTile)scene.Instantiate(); | ||||||
|             _ghostBuilding.Grid = Grid; |  | ||||||
|             _ghostBuilding.SetGhostMode(true); |             _ghostBuilding.SetGhostMode(true); | ||||||
|             _ghostBuilding.RotationDegrees = _currentRotation; |             _ghostBuilding.RotationDegrees = _currentRotation; | ||||||
|             _ghostBuilding.ZAsRelative = false; |             _ghostBuilding.ZAsRelative = false; | ||||||
| @@ -229,7 +232,7 @@ public partial class PlacementManager : Node2D | |||||||
|                 // Check if the area is occupied by under-construction tiles |                 // Check if the area is occupied by under-construction tiles | ||||||
|                 var occupiedCells = GridUtils.GetOccupiedCells(_hoveredCell, building.Size, _currentRotation); |                 var occupiedCells = GridUtils.GetOccupiedCells(_hoveredCell, building.Size, _currentRotation); | ||||||
|                 var isUnderConstruction = occupiedCells.Any(cell => |                 var isUnderConstruction = occupiedCells.Any(cell => | ||||||
|                     Grid.GetBuildingAtCell(cell, building.Layer) is BaseTile { IsConstructing: true }); |                     Grid.GetTileAtCell(cell, building.Layer) is BaseTile { IsConstructing: true }); | ||||||
|  |  | ||||||
|                 if (!isUnderConstruction) |                 if (!isUnderConstruction) | ||||||
|                     _cannotDeploySound.Play(); |                     _cannotDeploySound.Play(); | ||||||
| @@ -247,7 +250,6 @@ public partial class PlacementManager : Node2D | |||||||
|             // Create the building instance |             // Create the building instance | ||||||
|             var scene = building.Scene; |             var scene = building.Scene; | ||||||
|             var buildingInstance = (BaseTile)scene.Instantiate(); |             var buildingInstance = (BaseTile)scene.Instantiate(); | ||||||
|             buildingInstance.Grid = Grid; |  | ||||||
|             buildingInstance.RotationDegrees = _currentRotation; |             buildingInstance.RotationDegrees = _currentRotation; | ||||||
|             buildingInstance.ZIndex = (int)building.Layer; |             buildingInstance.ZIndex = (int)building.Layer; | ||||||
|             buildingInstance.Position = _ghostBuilding.Position; |             buildingInstance.Position = _ghostBuilding.Position; | ||||||
| @@ -289,7 +291,7 @@ public partial class PlacementManager : Node2D | |||||||
|             !Grid.IsAreaFree(_hoveredCell, Vector2I.One, 0f)) |             !Grid.IsAreaFree(_hoveredCell, Vector2I.One, 0f)) | ||||||
|         { |         { | ||||||
|             // Right click to destroy from current layer |             // Right click to destroy from current layer | ||||||
|             var building = Grid.GetBuildingAtCell(_hoveredCell); |             var building = Grid.GetTileAtCell(_hoveredCell); | ||||||
|             if (building == null) return; |             if (building == null) return; | ||||||
|  |  | ||||||
|             // Find all cells occupied by this building |             // Find all cells occupied by this building | ||||||
| @@ -426,34 +428,30 @@ public static class GridManagerExtensions | |||||||
|     public static (Vector2I Position, Vector2I Size, float Rotation)? GetBuildingInfoAtCell(this GridManager grid, |     public static (Vector2I Position, Vector2I Size, float Rotation)? GetBuildingInfoAtCell(this GridManager grid, | ||||||
|         Vector2I cell, GridLayer layer) |         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 |         // 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); |                 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 |                 // Found the top-left corner, now find the size | ||||||
|                 var size = Vector2I.One; |                 var size = Vector2I.One; | ||||||
|                 // Search right |                 // 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) |                        building) | ||||||
|                     size.X++; |                     size.X++; | ||||||
|                 // Search down |                 // 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) |                        building) | ||||||
|                     size.Y++; |                     size.Y++; | ||||||
|  |  | ||||||
|                 // Get rotation from the first cell |                 // 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 (checkCell, size, rotation); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         return null; |         return null; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -14,6 +14,10 @@ public partial class ResourceManager : Node | |||||||
|     [Signal] |     [Signal] | ||||||
|     public delegate void OnResourceChangedEventHandler(string resourceId, int newAmount); |     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() |     public override void _Ready() | ||||||
|     { |     { | ||||||
|         base._Ready(); |         base._Ready(); | ||||||
| @@ -43,6 +47,7 @@ public partial class ResourceManager : Node | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         EmitSignal(nameof(OnResourceChanged), resourceId, _resources[resourceId]); |         EmitSignal(nameof(OnResourceChanged), resourceId, _resources[resourceId]); | ||||||
|  |         EmitSignal(nameof(OnResourcePickedUp), resourceId, amount); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // Remove resources of a specific type |     // Remove resources of a specific type | ||||||
|   | |||||||
| @@ -9,26 +9,42 @@ namespace AceFieldNewHorizon.Scripts.Tiles; | |||||||
| public partial class BaseTile : Node2D | public partial class BaseTile : Node2D | ||||||
| { | { | ||||||
|     [Export] public string TileId { get; set; } |     [Export] public string TileId { get; set; } | ||||||
|     [Export] public GridManager Grid { 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 CollisionShape2D _collisionShape; | ||||||
|     private Sprite2D _sprite; |     private Sprite2D _sprite; | ||||||
|     private ColorRect _progressOverlay; |     private ColorRect _progressOverlay; | ||||||
|     private Action _onConstructionComplete; |     private Action _onConstructionComplete; | ||||||
|  |     private Tween _damageTween; | ||||||
|  |  | ||||||
|     public bool IsConstructing; |     public bool IsConstructing; | ||||||
|     public bool IsConstructed; |     public bool IsConstructed; | ||||||
|  |  | ||||||
|     public override void _Ready() |     public override void _Ready() | ||||||
|     { |     { | ||||||
|  |         Grid = DependencyInjection.Container.GetInstance<GridManager>(); | ||||||
|  |         Registry = DependencyInjection.Container.GetInstance<BuildingRegistry>(); | ||||||
|  |  | ||||||
|         _collisionShape = GetNodeOrNull<CollisionShape2D>("CollisionShape2D"); |         _collisionShape = GetNodeOrNull<CollisionShape2D>("CollisionShape2D"); | ||||||
|         _sprite = GetNodeOrNull<Sprite2D>("Sprite2D"); |         _sprite = GetNodeOrNull<Sprite2D>("Sprite2D"); | ||||||
|         _progressOverlay = GetNodeOrNull<ColorRect>("ProgressOverlay"); |         _progressOverlay = GetNodeOrNull<ColorRect>("ProgressOverlay"); | ||||||
|         if (_progressOverlay != null) |         if (_progressOverlay != null) | ||||||
|             _progressOverlay.Visible = false; |             _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 |         // Don't modify collision for constructing buildings | ||||||
|         if (IsConstructing) return; |         if (IsConstructing) return; | ||||||
| @@ -42,7 +58,7 @@ public partial class BaseTile : Node2D | |||||||
|                 : new Color(1, 0, 0, 0.5f); |                 : new Color(1, 0, 0, 0.5f); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public void FinalizePlacement() |     public virtual void FinalizePlacement() | ||||||
|     { |     { | ||||||
|         if (_collisionShape != null) |         if (_collisionShape != null) | ||||||
|             _collisionShape.Disabled = false; |             _collisionShape.Disabled = false; | ||||||
| @@ -50,6 +66,81 @@ public partial class BaseTile : Node2D | |||||||
|             _sprite.Modulate = Colors.White; |             _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 |     // Building progress visualization | ||||||
|     public void StartConstruction(float buildTime, Action onComplete = null) |     public void StartConstruction(float buildTime, Action onComplete = null) | ||||||
|     { |     { | ||||||
|   | |||||||
							
								
								
									
										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"); | 		var sprite = GetNode<Sprite2D>("Sprite2D"); | ||||||
| 		sprite.Modulate = new Color(0.75f, 0.75f, 0.75f); // Makes the sprite 25% darker	 | 		sprite.Modulate = new Color(0.75f, 0.75f, 0.75f); // Makes the sprite 25% darker	 | ||||||
|  | 		sprite.ZIndex = -10; | ||||||
| 		 | 		 | ||||||
| 		base._Ready(); | 		base._Ready(); | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -6,7 +6,7 @@ namespace AceFieldNewHorizon.Scripts.Tiles; | |||||||
| public partial class MinerTile : BaseTile | public partial class MinerTile : BaseTile | ||||||
| { | { | ||||||
|     [Export] public PackedScene ItemPickup { get; set; } |     [Export] public PackedScene ItemPickup { get; set; } | ||||||
|     [Export] public string ItemToMine { get; set; } = "OreIron"; |     [Export] public string ItemToMine { get; set; } | ||||||
|     [Export] public int MiningRate = 1; // Items per second |     [Export] public int MiningRate = 1; // Items per second | ||||||
|  |  | ||||||
|     private Vector2I _gridPosition; |     private Vector2I _gridPosition; | ||||||
| @@ -16,10 +16,29 @@ public partial class MinerTile : BaseTile | |||||||
|     { |     { | ||||||
|         base._Ready(); |         base._Ready(); | ||||||
|         _gridPosition = GridUtils.WorldToGrid(Position); |         _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) |     public override void _Process(double delta) | ||||||
|     { |     { | ||||||
|  |         if (ItemToMine == null) | ||||||
|  |             return; | ||||||
|  |          | ||||||
|         // Don't mine if building is not completed |         // Don't mine if building is not completed | ||||||
|         if (!IsConstructed || ItemPickup == null) |         if (!IsConstructed || ItemPickup == null) | ||||||
|             return; |             return; | ||||||
|   | |||||||
							
								
								
									
										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 | ||||||
| @@ -15,8 +15,15 @@ run/main_scene="uid://c22aprj452aha" | |||||||
| config/features=PackedStringArray("4.4", "C#", "GL Compatibility") | config/features=PackedStringArray("4.4", "C#", "GL Compatibility") | ||||||
| config/icon="res://icon.svg" | config/icon="res://icon.svg" | ||||||
|  |  | ||||||
|  | [autoload] | ||||||
|  |  | ||||||
|  | ResourceManager="res://Scripts/System/ResourceManager.cs" | ||||||
|  | DiInitializer="*res://Scripts/AutoLoad/DIInitializer.cs" | ||||||
|  |  | ||||||
| [display] | [display] | ||||||
|  |  | ||||||
|  | window/size/viewport_width=1920 | ||||||
|  | window/size/viewport_height=1080 | ||||||
| window/stretch/mode="viewport" | window/stretch/mode="viewport" | ||||||
|  |  | ||||||
| [dotnet] | [dotnet] | ||||||
| @@ -77,11 +84,12 @@ switch_tile={ | |||||||
| } | } | ||||||
| toggle_build={ | toggle_build={ | ||||||
| "deadzone": 0.2, | "deadzone": 0.2, | ||||||
| "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":66,"key_label":0,"unicode":98,"location":0,"echo":false,"script":null) | "events": [] | ||||||
| ] |  | ||||||
| } | } | ||||||
|  |  | ||||||
| [rendering] | [rendering] | ||||||
|  |  | ||||||
| anti_aliasing/quality/msaa_2d=3 | 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 | anti_aliasing/quality/use_taa=true | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user