✨ Enemy and nest
This commit is contained in:
@@ -21,6 +21,15 @@
|
||||
"layer": 1,
|
||||
"size": [1, 1]
|
||||
},
|
||||
"enemy_nest": {
|
||||
"scene": "res://Scenes/Tiles/EnemyNest.tscn",
|
||||
"cost": {},
|
||||
"durability": 200,
|
||||
"buildTime": 0.0,
|
||||
"allowedRotations": [0],
|
||||
"layer": 1,
|
||||
"size": [1, 1]
|
||||
},
|
||||
"ground": {
|
||||
"scene": "res://Scenes/Tiles/GroundTile.tscn",
|
||||
"cost": {},
|
||||
|
34
Scenes/Entities/Enemy.jpg.import
Normal file
34
Scenes/Entities/Enemy.jpg.import
Normal file
@@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dlhpiyxtmp707"
|
||||
path="res://.godot/imported/Enemy.jpg-862b79047d7834ee48aa6bbd7e126824.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://Scenes/Entities/Enemy.jpg"
|
||||
dest_files=["res://.godot/imported/Enemy.jpg-862b79047d7834ee48aa6bbd7e126824.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
|
17
Scenes/Entities/Enemy.tscn
Normal file
17
Scenes/Entities/Enemy.tscn
Normal file
@@ -0,0 +1,17 @@
|
||||
[gd_scene load_steps=4 format=3 uid="uid://b3ffcucytwmk"]
|
||||
|
||||
[ext_resource type="Texture2D" uid="uid://dlhpiyxtmp707" path="res://Scenes/Entities/Enemy.jpg" id="1_8q37v"]
|
||||
[ext_resource type="Script" uid="uid://cvsmy820b8dwl" path="res://Scripts/Entities/Enemy.cs" id="1_jajit"]
|
||||
|
||||
[sub_resource type="RectangleShape2D" id="RectangleShape2D_jajit"]
|
||||
size = Vector2(60, 74)
|
||||
|
||||
[node name="Enemy" type="CharacterBody2D"]
|
||||
script = ExtResource("1_jajit")
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
|
||||
shape = SubResource("RectangleShape2D_jajit")
|
||||
|
||||
[node name="Sprite2D" type="Sprite2D" parent="."]
|
||||
scale = Vector2(0.1, 0.1)
|
||||
texture = ExtResource("1_8q37v")
|
34
Scenes/Tiles/EnemyNest.jpg.import
Normal file
34
Scenes/Tiles/EnemyNest.jpg.import
Normal file
@@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://vwfs68ftvjr4"
|
||||
path="res://.godot/imported/EnemyNest.jpg-9a1f582f2843b75fdeff38422d3798b9.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://Scenes/Tiles/EnemyNest.jpg"
|
||||
dest_files=["res://.godot/imported/EnemyNest.jpg-9a1f582f2843b75fdeff38422d3798b9.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
|
19
Scenes/Tiles/EnemyNest.tscn
Normal file
19
Scenes/Tiles/EnemyNest.tscn
Normal file
@@ -0,0 +1,19 @@
|
||||
[gd_scene load_steps=5 format=3 uid="uid://dup2su0s3ybcy"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://26hl5mk4mqur" path="res://Scripts/Tiles/EnemyNestTile.cs" id="1_4g0ff"]
|
||||
[ext_resource type="Texture2D" uid="uid://vwfs68ftvjr4" path="res://Scenes/Tiles/EnemyNest.jpg" id="1_id484"]
|
||||
[ext_resource type="PackedScene" uid="uid://b3ffcucytwmk" path="res://Scenes/Entities/Enemy.tscn" id="2_pka71"]
|
||||
|
||||
[sub_resource type="RectangleShape2D" id="RectangleShape2D_id484"]
|
||||
size = Vector2(54, 54)
|
||||
|
||||
[node name="EnemyNest" type="CharacterBody2D"]
|
||||
script = ExtResource("1_4g0ff")
|
||||
EnemyScene = ExtResource("2_pka71")
|
||||
|
||||
[node name="Sprite2D" type="Sprite2D" parent="."]
|
||||
scale = Vector2(0.056, 0.056)
|
||||
texture = ExtResource("1_id484")
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
|
||||
shape = SubResource("RectangleShape2D_id484")
|
@@ -9,7 +9,7 @@ size = Vector2(54, 54)
|
||||
[node name="OreIronTile" type="StaticBody2D"]
|
||||
collision_layer = 0
|
||||
script = ExtResource("1_exnim")
|
||||
TileId = "stone_iron"
|
||||
TileId = "ore_iron"
|
||||
|
||||
[node name="Sprite2D" type="Sprite2D" parent="."]
|
||||
position = Vector2(1.49012e-08, -9.53674e-07)
|
||||
|
89
Scripts/Entities/Enemy.cs
Normal file
89
Scripts/Entities/Enemy.cs
Normal file
@@ -0,0 +1,89 @@
|
||||
using Godot;
|
||||
|
||||
namespace AceFieldNewHorizon.Scripts.Entities;
|
||||
|
||||
public partial class Enemy : CharacterBody2D
|
||||
{
|
||||
[Export] public float MoveSpeed = 150.0f;
|
||||
[Export] public float DetectionRadius = 300.0f;
|
||||
[Export] public float AttackRange = 50.0f;
|
||||
[Export] public int Damage = 10;
|
||||
[Export] public float AttackCooldown = 1.0f;
|
||||
|
||||
private Player _player;
|
||||
private float _attackTimer = 0;
|
||||
private bool _isPlayerInRange = false;
|
||||
private Area2D _detectionArea;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
// Create detection area
|
||||
_detectionArea = new Area2D();
|
||||
var collisionShape = new CollisionShape2D();
|
||||
var shape = new CircleShape2D();
|
||||
shape.Radius = DetectionRadius;
|
||||
collisionShape.Shape = shape;
|
||||
_detectionArea.AddChild(collisionShape);
|
||||
AddChild(_detectionArea);
|
||||
|
||||
// Connect signals
|
||||
_detectionArea.BodyEntered += OnBodyEnteredDetection;
|
||||
_detectionArea.BodyExited += OnBodyExitedDetection;
|
||||
}
|
||||
|
||||
public override void _Process(double delta)
|
||||
{
|
||||
if (_player != null && _isPlayerInRange)
|
||||
{
|
||||
// Face the player
|
||||
LookAt(_player.GlobalPosition);
|
||||
|
||||
// Move towards player if not in attack range
|
||||
var direction = GlobalPosition.DirectionTo(_player.GlobalPosition);
|
||||
var distance = GlobalPosition.DistanceTo(_player.GlobalPosition);
|
||||
|
||||
if (distance > AttackRange)
|
||||
{
|
||||
Velocity = direction * MoveSpeed;
|
||||
}
|
||||
else
|
||||
{
|
||||
Velocity = Vector2.Zero;
|
||||
TryAttackPlayer(delta);
|
||||
}
|
||||
|
||||
MoveAndSlide();
|
||||
}
|
||||
}
|
||||
|
||||
private void TryAttackPlayer(double delta)
|
||||
{
|
||||
_attackTimer += (float)delta;
|
||||
|
||||
if (_attackTimer >= AttackCooldown)
|
||||
{
|
||||
_attackTimer = 0;
|
||||
// Here you can implement the attack logic
|
||||
// For example: _player.TakeDamage(Damage);
|
||||
GD.Print("Enemy attacks player!");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnBodyEnteredDetection(Node2D body)
|
||||
{
|
||||
if (body is Player player)
|
||||
{
|
||||
_player = player;
|
||||
_isPlayerInRange = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnBodyExitedDetection(Node2D body)
|
||||
{
|
||||
if (body == _player)
|
||||
{
|
||||
_isPlayerInRange = false;
|
||||
Velocity = Vector2.Zero;
|
||||
}
|
||||
}
|
||||
}
|
1
Scripts/Entities/Enemy.cs.uid
Normal file
1
Scripts/Entities/Enemy.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cvsmy820b8dwl
|
@@ -25,6 +25,10 @@ public partial class NaturalResourceGenerator : Node2D
|
||||
[Export] public int MaxIronVeinSize = 3;
|
||||
[Export] public int Seed;
|
||||
|
||||
[Export] public bool SpawnEnemyNest = true;
|
||||
[Export] public int MinDistanceFromOrigin = 20; // Minimum distance from world origin (0,0)
|
||||
[Export] public int MaxDistanceFromOrigin = 50; // Maximum distance from world origin
|
||||
|
||||
private const string LogPrefix = "[NaturalGeneration]";
|
||||
|
||||
private RandomNumberGenerator _rng;
|
||||
@@ -41,6 +45,32 @@ public partial class NaturalResourceGenerator : Node2D
|
||||
{
|
||||
_rng = new RandomNumberGenerator();
|
||||
_rng.Seed = (ulong)(Seed != 0 ? Seed : (int)GD.Randi());
|
||||
|
||||
// Test if building registry is assigned
|
||||
if (Registry == null)
|
||||
{
|
||||
GD.PrintErr($"{LogPrefix} BuildingRegistry is not assigned!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Test if enemy_nest is in the registry
|
||||
var testBuilding = Registry.GetBuilding("enemy_nest");
|
||||
if (testBuilding == null)
|
||||
{
|
||||
GD.PrintErr($"{LogPrefix} 'enemy_nest' is not found in BuildingRegistry!");
|
||||
}
|
||||
else
|
||||
{
|
||||
GD.Print($"{LogPrefix} Found enemy_nest in registry!");
|
||||
}
|
||||
|
||||
GD.Print($"{LogPrefix} NaturalResourceGenerator ready, SpawnEnemyNest = {SpawnEnemyNest}");
|
||||
|
||||
if (SpawnEnemyNest)
|
||||
{
|
||||
GD.Print($"{LogPrefix} Attempting to spawn enemy nest...");
|
||||
SpawnRandomEnemyNest();
|
||||
}
|
||||
}
|
||||
|
||||
public override void _Process(double delta)
|
||||
@@ -361,12 +391,52 @@ public partial class NaturalResourceGenerator : Node2D
|
||||
GD.Print($"{LogPrefix} Finished placing vein - placed {placedCount}/{maxSize} {tileType} tiles");
|
||||
}
|
||||
|
||||
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
|
||||
int attempts = 0;
|
||||
const int maxAttempts = 10;
|
||||
|
||||
while (attempts < maxAttempts)
|
||||
{
|
||||
if (PlaceTile("enemy_nest", nestPosition))
|
||||
{
|
||||
GD.Print($"{LogPrefix} Placed enemy nest at {nestPosition}");
|
||||
return;
|
||||
}
|
||||
|
||||
// Try a different position if placement failed
|
||||
angle = _rng.Randf() * Mathf.Pi * 2;
|
||||
distance = _rng.RandfRange(MinDistanceFromOrigin, MaxDistanceFromOrigin);
|
||||
offset = new Vector2(Mathf.Cos(angle), Mathf.Sin(angle)) * distance;
|
||||
nestPosition = new Vector2I((int)offset.X, (int)offset.Y);
|
||||
attempts++;
|
||||
}
|
||||
|
||||
GD.PrintErr($"{LogPrefix} Failed to place enemy nest after {maxAttempts} attempts");
|
||||
}
|
||||
|
||||
private bool PlaceTile(string tileType, Vector2I cell)
|
||||
{
|
||||
try
|
||||
{
|
||||
// First, remove any existing tile at this position
|
||||
// For enemy nest, we want to place it on top of existing ground
|
||||
if (tileType != "enemy_nest")
|
||||
{
|
||||
// Original behavior for other tile types
|
||||
Grid.FreeArea(cell, Vector2I.One, 0f, GridLayer.Ground);
|
||||
}
|
||||
else
|
||||
{
|
||||
// For enemy nest, check if there's ground below (skip for now)
|
||||
GD.Print($"{LogPrefix} Attempting placing nest at {cell}.");
|
||||
}
|
||||
|
||||
var building = Registry.GetBuilding(tileType);
|
||||
if (building == null)
|
||||
@@ -374,6 +444,7 @@ public partial class NaturalResourceGenerator : Node2D
|
||||
GD.PrintErr($"{LogPrefix} Building type not found in registry: {tileType}");
|
||||
return false;
|
||||
}
|
||||
// GD.Print($"{LogPrefix} Found building in registry: {tileType}");
|
||||
|
||||
var scene = building.Scene;
|
||||
if (scene == null)
|
||||
@@ -381,27 +452,35 @@ public partial class NaturalResourceGenerator : Node2D
|
||||
GD.PrintErr($"{LogPrefix} Scene is null for building type: {tileType}");
|
||||
return false;
|
||||
}
|
||||
// GD.Print($"{LogPrefix} Scene loaded for {tileType}");
|
||||
|
||||
if (scene.Instantiate() is not BaseTile instance)
|
||||
{
|
||||
GD.PrintErr($"{LogPrefix} Failed to instantiate scene for: {tileType}");
|
||||
return false;
|
||||
}
|
||||
// GD.Print($"{LogPrefix} Successfully instantiated {tileType}");
|
||||
|
||||
// Use the same positioning logic as PlacementManager
|
||||
var rotatedSize = building.GetRotatedSize(0f); // 0f for no rotation
|
||||
var offset = GridUtils.GetCenterOffset(rotatedSize, 0f); // 0f for no rotation
|
||||
instance.GlobalPosition = GridUtils.GridToWorld(cell) + offset;
|
||||
var rotatedSize = building.GetRotatedSize(0f);
|
||||
var offset = GridUtils.GetCenterOffset(rotatedSize, 0f);
|
||||
|
||||
instance.ZIndex = (int)building.Layer;
|
||||
instance.GlobalPosition = GridUtils.GridToWorld(cell) + offset;
|
||||
instance.Grid = Grid;
|
||||
AddChild(instance);
|
||||
Grid.OccupyArea(cell, instance, building.Size, 0f, building.Layer);
|
||||
// GD.Print($"{LogPrefix} Successfully placed {tileType} at {cell}");
|
||||
|
||||
// For enemy nest, use Building layer
|
||||
var layer = tileType == "enemy_nest" ? GridLayer.Building : building.Layer;
|
||||
Grid.OccupyArea(cell, instance, building.Size, 0f, layer);
|
||||
|
||||
// GD.Print($"{LogPrefix} Successfully placed {tileType} at {cell} on layer {layer}");
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
GD.PrintErr($"{LogPrefix} Error placing {tileType} at {cell}: {e.Message}");
|
||||
GD.Print($"{LogPrefix} Stack trace: {e.StackTrace}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
35
Scripts/Tiles/EnemyNestTile.cs
Normal file
35
Scripts/Tiles/EnemyNestTile.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using Godot;
|
||||
|
||||
namespace AceFieldNewHorizon.Scripts.Tiles;
|
||||
|
||||
public partial class EnemyNestTile : BaseTile
|
||||
{
|
||||
[Export] public PackedScene EnemyScene;
|
||||
private Timer _spawnTimer;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
base._Ready();
|
||||
|
||||
// Create and configure the timer
|
||||
_spawnTimer = new Timer();
|
||||
AddChild(_spawnTimer);
|
||||
_spawnTimer.Timeout += OnSpawnTimerTimeout;
|
||||
_spawnTimer.Start(1.0f); // Start with 1 second interval
|
||||
}
|
||||
|
||||
private void OnSpawnTimerTimeout()
|
||||
{
|
||||
if (EnemyScene != null)
|
||||
{
|
||||
var enemy = EnemyScene.Instantiate();
|
||||
GetParent().AddChild(enemy);
|
||||
|
||||
// Position the enemy at the nest's position
|
||||
if (enemy is Node2D enemy2D)
|
||||
{
|
||||
enemy2D.GlobalPosition = GlobalPosition;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
1
Scripts/Tiles/EnemyNestTile.cs.uid
Normal file
1
Scripts/Tiles/EnemyNestTile.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://26hl5mk4mqur
|
@@ -77,8 +77,7 @@ switch_tile={
|
||||
}
|
||||
toggle_build={
|
||||
"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]
|
||||
|
Reference in New Issue
Block a user