✨ Turret shooting and damaing
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
using AceFieldNewHorizon.Scripts.Entities;
|
||||
using Godot;
|
||||
|
||||
namespace AceFieldNewHorizon.Scripts.Tiles;
|
||||
@@ -5,31 +6,69 @@ namespace AceFieldNewHorizon.Scripts.Tiles;
|
||||
public partial class EnemyNestTile : 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 = 0;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
base._Ready();
|
||||
|
||||
// Create and configure the timer
|
||||
_spawnTimer = new Timer();
|
||||
_spawnTimer = new Timer
|
||||
{
|
||||
Autostart = true,
|
||||
WaitTime = SpawnDelay
|
||||
};
|
||||
AddChild(_spawnTimer);
|
||||
_spawnTimer.Timeout += OnSpawnTimerTimeout;
|
||||
_spawnTimer.Start(1.0f); // Start with 1 second interval
|
||||
}
|
||||
|
||||
private void OnSpawnTimerTimeout()
|
||||
{
|
||||
if (EnemyScene != null)
|
||||
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)
|
||||
{
|
||||
var enemy = EnemyScene.Instantiate();
|
||||
GetParent().AddChild(enemy);
|
||||
|
||||
// Position the enemy at the nest's position
|
||||
if (enemy is Node2D enemy2D)
|
||||
{
|
||||
enemy2D.GlobalPosition = GlobalPosition;
|
||||
}
|
||||
// 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,22 +1,43 @@
|
||||
using System.Linq;
|
||||
using AceFieldNewHorizon.Scripts.Entities;
|
||||
using Godot;
|
||||
|
||||
namespace AceFieldNewHorizon.Scripts.Tiles;
|
||||
|
||||
public partial class TurretTile : BaseTile
|
||||
{
|
||||
private Sprite2D _spriteBarrel;
|
||||
[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);
|
||||
|
||||
_spriteBarrel = GetNodeOrNull<Sprite2D>("Barrel");
|
||||
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)
|
||||
@@ -26,8 +47,81 @@ public partial class TurretTile : BaseTile
|
||||
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 - _spriteBarrel.GlobalPosition).Normalized();
|
||||
var targetAngle = Mathf.Atan2(direction.Y, direction.X);
|
||||
|
||||
// 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 attacking enemy!");
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user