Files
AceField-New-Horizon/Scripts/Tiles/TurretTile.cs
2025-08-30 13:05:50 +08:00

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!");
}
}
}