using Cirno.Scripts.Enums; using Godot; namespace Cirno.Scripts.Components.Actors; public partial class EnemyTurretRotationMovement : MovementHandler { public override Vector2 MovementDirection { get => _parent.MovementDirection; set => _parent.MovementDirection = value; } public override Vector2 FacingDirection { get => _parent.FacingDirection; set => _parent.FacingDirection = value; } [Export] public float PlayerDisengageRange = 500f; [Export(PropertyHint.Layers2DPhysics)] public uint CollisionMask { get; set; } public bool IsDestroyed => _parent.IsDestroyed; [Export] public Weapon EquippedWeapon; [Export] private PlayerDetection _playerDetection; private Vector2? _lastPlayerPosition = null; private ActorAi _actorAi; private bool IsPlayerInRange => _playerDetection is { IsPlayerInRange: true }; private bool IsPlayerInSight => _playerDetection is not null && _playerDetection.IsPlayerInSight(CollisionMask); public override void Init(Actor parent) { base.Init(parent); MovementDirection = Vector2.Zero; FacingDirection = Vector2.Down; _actorAi = parent.GetNode("ActorAi"); } public override void Update(double delta) { } public override void PhysicsUpdate(double delta) { if (IsDestroyed) return; if (_actorAi.Ai is not AiState.Enabled) return; switch (_actorAi.State) { case EnemyState.Idle: if (_playerDetection != null && IsPlayerInRange && IsPlayerInSight) { _actorAi.State = EnemyState.Alert; } break; case EnemyState.Alert: // Update last known player position if it's in range if (IsPlayerInRange) { _lastPlayerPosition = _playerDetection.CachedPlayer.GlobalPosition; } if (IsPlayerInRange && IsPlayerInSight) { Shoot(); } else { _actorAi.State = EnemyState.Idle; } break; } } private void Shoot() { if (EquippedWeapon == null || !_lastPlayerPosition.HasValue) return; var direction = (_lastPlayerPosition.Value - _parent.GlobalPosition).Normalized(); // Shoot at the player's last known position EquippedWeapon.ShootDirection = direction; FacingDirection = direction; EquippedWeapon.Shoot(); } }