using Cirno.Scripts; using Godot; using System; using System.Diagnostics; public partial class Enemy : Area2D, IDestructible { private InteractionController _cachedPlayer; private EnemyState _currentState = EnemyState.Idle; [Export] public PackedScene BulletScene { get; set; } [Export] public float Health = 4f; [Export] public double RateOfFire = 0.4f; // Time between shots within a burst [Export] public float BulletSpeed = 100f; [Export] public int BurstCount = 3; // Number of shots per burst [Export] public float BurstCooldown = 1f; // Time between bursts [Export] public bool AimAtPlayer = true; // Whether to aim at player or shoot straight private float _currentHealth = 0f; private bool _isDestroyed = false; private Timer _burstTimer; private Timer _shotTimer; private int _shotsFired = 0; public override void _Ready() { _currentHealth = Health; // Initialize timers _burstTimer = new Timer(); _shotTimer = new Timer(); AddChild(_burstTimer); AddChild(_shotTimer); _burstTimer.WaitTime = BurstCooldown; _burstTimer.OneShot = true; _shotTimer.WaitTime = RateOfFire; _shotTimer.OneShot = true; _shotTimer.Timeout += OnShotTimerTimeout; _burstTimer.Timeout += OnBurstTimerTimeout; // Debug DebugStats.Instance.AddProperty(this, "_currentHealth", ""); } public override void _Process(double delta) { switch (_currentState) { case EnemyState.Idle: break; case EnemyState.Primed: break; default: break; } } public override void _PhysicsProcess(double delta) { HandlePlayerDetection(); } private void HandlePlayerDetection() { if (_cachedPlayer == null || _currentState is not EnemyState.Primed) { return; } if (_burstTimer.IsStopped() && !_shotTimer.IsStopped() && IsPlayerInSight()) { StartBurst(); } } private void StartBurst() { _shotsFired = 0; _shotTimer.Start(); } private void OnShotTimerTimeout() { if (_shotsFired < BurstCount) { Shoot(); _shotsFired++; if (_shotsFired < BurstCount) { _shotTimer.Start(); // Restart for the next shot in the burst } } else { _burstTimer.Start(); // Start cooldown between bursts } } private void OnBurstTimerTimeout() { // Cooldown complete, can start new burst when conditions are met } private void Shoot() { var bullet = this.CreateChild(BulletScene); if (AimAtPlayer) { bullet.SetDirection((_cachedPlayer.GlobalPosition - this.GlobalPosition).Normalized()); } else { bullet.SetDirection(Vector2.Right.Rotated(this.Rotation)); } bullet.Speed = BulletSpeed; } private bool IsPlayerInSight() { var spaceState = GetWorld2D().DirectSpaceState; var query = PhysicsRayQueryParameters2D.Create(this.GlobalPosition, _cachedPlayer.GlobalPosition, CollisionMask, new Godot.Collections.Array { GetRid() }); var result = spaceState.IntersectRay(query); return result.Count == 0; // True if no obstacles between enemy and player } private void _on_player_detection_area_entered(Area2D area) { if (area is InteractionController player) { Debug.WriteLine("Enemy detection area Entered by interaction controller"); _cachedPlayer = player; if (_currentState is EnemyState.Idle) { _currentState = EnemyState.Primed; } } } private void _on_player_detection_area_exited(Area2D area) { if (_currentState is EnemyState.Primed) { _currentState = EnemyState.Idle; } } private void _on_area_entered(Area2D area) { // Collision handling } private void Explode() { Debug.WriteLine("Ded"); QueueFree(); } public void Hit(float damage) { if (_isDestroyed) return; _currentHealth -= damage; if (!(_currentHealth <= 0)) return; _isDestroyed = true; Explode(); } public bool IsDestroyed() { return _isDestroyed; } private enum EnemyState { Idle, Primed } }