using Cirno.Scripts; using Godot; using System; using System.Diagnostics; using Cirno.Scripts.Components; using Godot.Collections; public partial class Enemy : CharacterBody2D { private InteractionController _cachedPlayer; private EnemyState _currentState = EnemyState.Idle; [Export] public float Health = 4f; [Export] public float WalkSpeed = 2500f; [Export] public float PlayerDisengageRange = 500f; [Export] public Weapon EquippedWeapon; private float _currentHealth = 0f; private bool _isDestroyed = false; private NavigationAgent2D _navigationAgent; private bool IsPlayerInRange => _playerDetection is { IsPlayerInRange: true }; private Vector2? _lastPlayerPosition = null; [Export] private PlayerDetection _playerDetection; [Export] public bool NavigationEnabled { get; set; } = false; // Called when the node enters the scene tree for the first time. public override void _Ready() { _currentHealth = Health; _navigationAgent = GetNode("NavigationAgent2D"); //var asdf = GetNode("PlayerDetection"); //_playerDetection = GetNode("PlayerDetection"); //CallDeferred("Setup"); } // private void Setup() // { // // } // Called every frame. 'delta' is the elapsed time since the previous frame. public override void _Process(double delta) { switch (_currentState) { case EnemyState.Idle: break; case EnemyState.Alert: break; //case EnemyState.Shooting: // Shoot //break; default: break; } } public override void _PhysicsProcess(double delta) { switch (_currentState) { case EnemyState.Idle: if (_playerDetection != null && _playerDetection.IsPlayerInSight(CollisionMask)) { _currentState = EnemyState.Alert; } //HandlePlayerDetection(); break; case EnemyState.Alert: // Update last known player position if it's in range if (IsPlayerInRange) { _lastPlayerPosition = _playerDetection.CachedPlayer.GlobalPosition; } if (_lastPlayerPosition.HasValue) { _navigationAgent.SetTargetPosition(_lastPlayerPosition.Value); } var currentAgentPosition = GlobalPosition; var nextPathPosition = _navigationAgent.GetNextPathPosition(); var newVelocity = currentAgentPosition.DirectionTo(nextPathPosition) * (float)(WalkSpeed * delta); // Navigation is over, can do other things like shooting if (_navigationAgent.IsNavigationFinished()) { // Shoot player Shoot(); // TODO: If player totally left the max range it should stop shooting and go back to idle return; } if (_navigationAgent.AvoidanceEnabled) { _navigationAgent.SetVelocity(newVelocity); } else { _on_navigation_agent_2d_velocity_computed(newVelocity); } MoveAndSlide(); break; case EnemyState.Patrolling: break; default: throw new ArgumentOutOfRangeException(); } } public void _on_navigation_agent_2d_velocity_computed(Vector2 safeVelocity) { this.Velocity = safeVelocity; } // private void HandlePlayerDetection() // { // if (_cachedPlayer == null) // { // return; // } // // if (IsPlayerInSight()) // { // // Update player position only if player is in sight // // if (NavigationEnabled) // // { // // _navigationAgent.SetTargetPosition(_cachedPlayer.GlobalPosition); // // } // //Shoot(); // // _currentState = EnemyState.Alert; // } // } private void Shoot() { if (EquippedWeapon == null || !_lastPlayerPosition.HasValue) return; // Shoot at the player's last known position EquippedWeapon.ShootDirection = (_lastPlayerPosition.Value - this.GlobalPosition).Normalized(); EquippedWeapon.Shoot(); // // SHOOT // var bullet = this.CreateChild(BulletScene); // // var bullet = BulletScene.Instantiate(); // // Owner.AddChild(bullet); // // bullet.Transform = this.GlobalTransform; // // bullet.Position = this.Position; // bullet.SetDirection((_cachedPlayer.GlobalPosition - this.GlobalPosition).Normalized()); // bullet.Speed = BulletSpeed; // // _ammo -= 1; // // if (_ammo <= 0) // { // _ammo = BulletCount; // _cooldownTimer.Start(ReloadTime); // } // else // { // _cooldownTimer.Start(RateOfFire); // } } // 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); // // // If count is 0 then the player is in sight, otherwise there is level geometry in the way // return result.Count == 0; // // // if (result.Count > 0) // // GD.Print("Hit at point: ", result["position"]); // } // private void _on_player_detection_area_entered(Area2D area) // { // // Assume area is player for now // if (area is InteractionController player) // { // Debug.WriteLine("Enemy detection area Entered by interaction controller"); // // _cachedPlayer = player; // // _isPlayerInRange = true; // // if (_currentState is EnemyState.Idle) // // { // // _currentState = EnemyState.Primed; // // } // } // } // // private void _on_player_detection_area_exited(Area2D area) // { // _isPlayerInRange = false; // // // if (_currentState is EnemyState.Primed) // // { // // _currentState = EnemyState.Idle; // // } // } private void _on_damage_hitbox_area_entered(Area2D area) { if (area is not Bullet bullet) return; GD.Print("Enemy Received damage"); this.Hit(bullet.Damage); bullet.QueueFree(); } // Bullets collision private void _on_area_entered(Area2D area) { } private void Explode() { Debug.WriteLine("Ded"); //CreateParticles(); //CreateDebris(); 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, Alert, Patrolling } }