using Chickensoft.AutoInject; using Chickensoft.Introspection; using Godot; using Godot.Collections; using System.Linq; using Zennysoft.Game.Abstractions.Entity; namespace Zennysoft.Game.Ma; [Meta(typeof(IAutoNode))] public partial class PatrolBehavior : Node3D, IBehavior { public override void _Notification(int what) => this.Notify(what); [Export] private double _patrolSpeed { get; set; } = 100f; [Export] private double _thinkTime { get; set; } = 0.8f; [Export] private float _patrolRange { get; set; } = 5f; [Export] private float _patrolTime { get; set; } = 10f; private Timer _patrolTimer { get; set; } = default!; private NavigationAgent3D _navigationAgent; private Vector3 _homePosition; public Vector3 HomePosition { get => _homePosition; set { _homePosition = value; _navigationAgent.TargetPosition = value; } } [Signal] public delegate void OnVelocityComputedEventHandler(Vector3 safeVelocity); public void OnReady() { _patrolTimer = new Timer() { WaitTime = _patrolTime }; _patrolTimer.Timeout += PatrolTimer_Timeout; AddChild(_patrolTimer); SetPhysicsProcess(false); } public void Init(NavigationAgent3D navigationAgent) { _navigationAgent = navigationAgent; _navigationAgent.WaypointReached += NavigationAgent_WaypointReached; } private async void NavigationAgent_WaypointReached(Dictionary details) { await ToSignal(GetTree().CreateTimer(_thinkTime), "timeout"); } public void StartPatrol() { SetPhysicsProcess(true); _patrolTimer?.Start(); } public void StopPatrol() { SetPhysicsProcess(false); _patrolTimer?.Stop(); } public void OnPhysicsProcess(double delta) { var nextVelocity = _navigationAgent.GetNextPathPosition(); var parent = GetParent() as Node3D; var velocity = parent.GlobalPosition.DirectionTo(nextVelocity) * (float)_patrolSpeed * (float)delta; EmitSignal(SignalName.OnVelocityComputed, velocity); } public void SetPatrolTarget() { var rng = new RandomNumberGenerator(); rng.Randomize(); var randomPointX = rng.RandfRange(-_patrolRange, _patrolRange); var randomPointZ = rng.RandfRange(-_patrolRange, _patrolRange); var patrolPoint = HomePosition + new Vector3(randomPointX, 0, randomPointZ); var rid = NavigationServer3D.GetMaps().Single(); _navigationAgent.TargetPosition = NavigationServer3D.MapGetClosestPoint(rid, patrolPoint); } private void PatrolTimer_Timeout() => SetPatrolTarget(); public void OnExitTree() { _patrolTimer.Stop(); _patrolTimer.Timeout -= PatrolTimer_Timeout; _navigationAgent.WaypointReached -= NavigationAgent_WaypointReached; } }