using Invector.vCharacterController.AI.FSMBehaviour; using UnityEngine; namespace ArcherEnemy { /// /// State action that makes archer flee/retreat from player /// Moves archer away from player while trying to maintain shooting distance /// [CreateAssetMenu(menuName = "Invector/FSM/Actions/Archer/Flee From Player")] public class SA_FleeFromPlayer : vStateAction { public override string categoryName => "Archer/Combat"; public override string defaultName => "Flee From Player"; [Header("Flee Configuration")] [Tooltip("Desired safe distance from player")] public float safeDistance = 12f; [Tooltip("How far to look ahead when fleeing")] public float fleeDistance = 5f; [Tooltip("Check for obstacles when fleeing")] public bool avoidObstacles = true; [Tooltip("Layers considered as obstacles")] public LayerMask obstacleMask = -1; [Tooltip("Number of directions to try when finding flee path")] public int directionSamples = 8; [Header("Movement")] [Tooltip("Movement speed multiplier (uses AI's speed)")] [Range(0.5f, 2f)] public float speedMultiplier = 1.2f; [Tooltip("Make archer sprint while fleeing")] public bool useSprint = true; [Header("Rotation")] [Tooltip("Keep facing player while backing away")] public bool facePlayer = true; [Tooltip("Rotation speed when facing player (degrees/sec)")] public float turnSpeed = 180f; [Header("Debug")] [Tooltip("Enable debug logging")] public bool enableDebug = false; [Tooltip("Show flee direction gizmos")] public bool showGizmos = true; private Vector3 currentFleeDirection; private Transform currentTarget; public override void DoAction(vIFSMBehaviourController fsmBehaviour, vFSMComponentExecutionType executionType = vFSMComponentExecutionType.OnStateUpdate) { if (executionType == vFSMComponentExecutionType.OnStateEnter) { OnEnter(fsmBehaviour); } else if (executionType == vFSMComponentExecutionType.OnStateUpdate) { OnUpdate(fsmBehaviour); } else if (executionType == vFSMComponentExecutionType.OnStateExit) { OnExit(fsmBehaviour); } } private void OnEnter(vIFSMBehaviourController fsmBehaviour) { currentTarget = GetTarget(fsmBehaviour); if (currentTarget == null) { if (enableDebug) Debug.LogWarning("[SA_FleeFromPlayer] No target found!"); return; } // Calculate initial flee direction currentFleeDirection = CalculateFleeDirection(fsmBehaviour); // Set AI to sprint if enabled var aiController = fsmBehaviour as Invector.vCharacterController.AI.vIControlAI; if (aiController != null && useSprint) { } if (enableDebug) Debug.Log("[SA_FleeFromPlayer] Started fleeing from player"); } private void OnUpdate(vIFSMBehaviourController fsmBehaviour) { if (currentTarget == null) { currentTarget = GetTarget(fsmBehaviour); if (currentTarget == null) return; } // Recalculate flee direction periodically currentFleeDirection = CalculateFleeDirection(fsmBehaviour); // Move in flee direction MoveInDirection(fsmBehaviour, currentFleeDirection); // Face player while fleeing if enabled if (facePlayer) { RotateTowardsPlayer(fsmBehaviour); } } private void OnExit(vIFSMBehaviourController fsmBehaviour) { // Reset AI speed var aiController = fsmBehaviour as Invector.vCharacterController.AI.vIControlAI; if (aiController != null) { } if (enableDebug) Debug.Log("[SA_FleeFromPlayer] Stopped fleeing"); } /// /// Calculates the best direction to flee /// private Vector3 CalculateFleeDirection(vIFSMBehaviourController fsmBehaviour) { Vector3 archerPos = fsmBehaviour.transform.position; Vector3 playerPos = currentTarget.position; // Basic flee direction: away from player Vector3 awayFromPlayer = (archerPos - playerPos).normalized; // If not avoiding obstacles, return simple direction if (!avoidObstacles) { return awayFromPlayer; } // Try to find clear path Vector3 bestDirection = awayFromPlayer; float bestScore = EvaluateDirection(archerPos, awayFromPlayer, playerPos); // Sample multiple directions around the flee vector for (int i = 0; i < directionSamples; i++) { float angle = (360f / directionSamples) * i; Vector3 testDirection = Quaternion.Euler(0f, angle, 0f) * awayFromPlayer; float score = EvaluateDirection(archerPos, testDirection, playerPos); if (score > bestScore) { bestScore = score; bestDirection = testDirection; } } return bestDirection; } /// /// Evaluates how good a flee direction is (higher = better) /// private float EvaluateDirection(Vector3 from, Vector3 direction, Vector3 playerPos) { float score = 0f; // Check if path is clear Ray ray = new Ray(from + Vector3.up * 0.5f, direction); bool isBlocked = Physics.Raycast(ray, fleeDistance, obstacleMask, QueryTriggerInteraction.Ignore); if (!isBlocked) { score += 10f; // Big bonus for clear path } // Prefer directions that move away from player Vector3 awayFromPlayer = (from - playerPos).normalized; float alignment = Vector3.Dot(direction, awayFromPlayer); score += alignment * 5f; // Check destination height (avoid running off cliffs) Vector3 destination = from + direction * fleeDistance; if (Physics.Raycast(destination + Vector3.up * 2f, Vector3.down, 5f, obstacleMask)) { score += 3f; // Bonus for having ground } return score; } /// /// Moves archer in specified direction /// private void MoveInDirection(vIFSMBehaviourController fsmBehaviour, Vector3 direction) { var aiController = fsmBehaviour as Invector.vCharacterController.AI.vIControlAI; if (aiController == null) return; // Calculate destination point Vector3 destination = fsmBehaviour.transform.position + direction * fleeDistance; // Use Invector's AI movement aiController.MoveTo(destination); // Apply speed multiplier var motor = aiController as Invector.vCharacterController.AI.vSimpleMeleeAI_Controller; if (motor != null) { // This would need to be adapted to your specific Invector version // The goal is to make the archer move faster while fleeing } } /// /// Rotates archer to face player while backing away /// private void RotateTowardsPlayer(vIFSMBehaviourController fsmBehaviour) { if (currentTarget == null) return; Vector3 directionToPlayer = (currentTarget.position - fsmBehaviour.transform.position); directionToPlayer.y = 0f; if (directionToPlayer.sqrMagnitude > 0.001f) { Quaternion targetRotation = Quaternion.LookRotation(directionToPlayer); fsmBehaviour.transform.rotation = Quaternion.RotateTowards( fsmBehaviour.transform.rotation, targetRotation, turnSpeed * Time.deltaTime ); } } private Transform GetTarget(vIFSMBehaviourController fsmBehaviour) { // Try through AI controller var aiController = fsmBehaviour as Invector.vCharacterController.AI.vIControlAI; if (aiController != null && aiController.currentTarget != null) return aiController.currentTarget.transform; // Fallback - find player GameObject player = GameObject.FindGameObjectWithTag("Player"); return player?.transform; } #if UNITY_EDITOR private void OnDrawGizmosSelected() { if (!showGizmos || !Application.isPlaying) return; // Draw current flee direction if (currentFleeDirection != Vector3.zero) { Gizmos.color = Color.red; // This would need the archer's position to draw properly } } #endif } }