using Cirno.Scripts.Components.FSM._3DPlayer; using Godot; namespace Cirno.Scripts.Components.FSM.Enemy._3D; public partial class PlayerDetection3D : Area3D { [Signal] public delegate void PlayerInRangeEventHandler(); [Signal] public delegate void PlayerOutOfRangeEventHandler(); [Export(PropertyHint.Layers3DPhysics)] public uint ObstaclesCollisionMask { get; private set; } public Vector3? LastKnownPlayerPosition { get; set; } public Vector3? LastKnowPlayerVelocity { get; set; } //public bool PlayerInActiveArea { get; private set; } private CollisionShape3D _collisionShape; private bool _initialized = false; // public override void _Ready() // { // CallDeferred(MethodName.Initialize); // } private CharacterBody3D _mainBody; public void Initialize(CharacterBody3D mainBody) { _mainBody = mainBody; _initialized = true; } public void SetRange(float range) { _collisionShape ??= this.GetNode("CollisionShape3D"); if (_collisionShape.Shape is CylinderShape3D shape3D) { shape3D.Radius = range; } } public bool IsPlayerInRange(float range) { if (!_initialized) return false; if (GameController.Instance is null) return false; if (!GameController.Instance.PlayerPosition.HasValue) { return false; } return this.GlobalPosition.DistanceTo(GameController.Instance.PlayerPosition.Value) <= range; } public bool IsPlayerInSight() { if (!_initialized) return false; if (GameController.Instance is null) return false; //if (_cachedPlayer == null) return false; if (!GameController.Instance.PlayerPosition.HasValue) return false; var found = HasLineOfSight(this.GlobalPosition, GameController.Instance.PlayerPosition.Value); // var spaceState = GetWorld2D().DirectSpaceState; // // // It needs to use its own collision mask because it's detecting obstacles rather than the player // var query = PhysicsRayQueryParameters2D.Create(this.GlobalPosition, GameController.Instance.PlayerPosition.Value, ObstaclesCollisionMask, // [GetRid()]); // //query.CollideWithBodies = true; // //query.CollideWithAreas = true; // // var query = PhysicsRayQueryParameters2D.Create(this.GlobalPosition, _cachedPlayer.GlobalPosition, CollisionMask, new 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 // var found = result.Count == 0; if (found) { LastKnownPlayerPosition = GameController.Instance.PlayerPosition; LastKnowPlayerVelocity = GameController.Instance.PlayerVelocity; } return found; } private void _on_body_entered(Node3D area) { if (area is not IsoPlayerFSMProxy player) return; EmitSignal(SignalName.PlayerInRange); //PlayerInActiveArea = true; } private void _on_body_exited(Node3D area) { if (area is not IsoPlayerFSMProxy player) return; EmitSignal(SignalName.PlayerOutOfRange); //PlayerInActiveArea = false; } public bool HasLineOfSight(Vector3 startPos, Vector3 endPos) { var spaceState = GetWorld3D().DirectSpaceState; // It needs to use its own collision mask because it's detecting obstacles rather than the player var query = PhysicsRayQueryParameters3D.Create(startPos, endPos, ObstaclesCollisionMask, [GetRid(), _mainBody.GetRid()]); query.CollideWithBodies = true; //query.CollideWithAreas = true; // var query = PhysicsRayQueryParameters2D.Create(this.GlobalPosition, _cachedPlayer.GlobalPosition, CollisionMask, new 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 var found = result.Count == 0; return found; } }