127 lines
4.1 KiB
C#
127 lines
4.1 KiB
C#
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 - _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!");
|
|
}
|
|
}
|
|
} |