using System; using Unity.BossRoom.Gameplay.GameplayObjects.Character; using Unity.Netcode; using UnityEngine; namespace Unity.BossRoom.Gameplay.Actions { [CreateAssetMenu(menuName = "BossRoom/Actions/Chase Action")] public class ChaseAction : Action { private NetworkObject m_Target; Transform m_TargetTransform; /// /// Called when the Action starts actually playing (which may be after it is created, because of queueing). /// /// false if the action decided it doesn't want to run after all, true otherwise. public override bool OnStart(ServerCharacter serverCharacter) { if (!HasValidTarget()) { Debug.Log("Failed to start ChaseAction. The target entity wasn't submitted or doesn't exist anymore"); return ActionConclusion.Stop; } m_Target = NetworkManager.Singleton.SpawnManager.SpawnedObjects[m_Data.TargetIds[0]]; if (PhysicsWrapper.TryGetPhysicsWrapper(m_Data.TargetIds[0], out var physicsWrapper)) { m_TargetTransform = physicsWrapper.Transform; } else { m_TargetTransform = m_Target.transform; } Vector3 currentTargetPos = m_TargetTransform.position; if (StopIfDone(serverCharacter)) { serverCharacter.physicsWrapper.Transform.LookAt(currentTargetPos); //even if we didn't move, snap to face the target! return ActionConclusion.Stop; } if (!serverCharacter.Movement.IsPerformingForcedMovement()) { serverCharacter.Movement.FollowTransform(m_TargetTransform); } return ActionConclusion.Continue; } public override void Reset() { base.Reset(); m_Target = null; m_TargetTransform = null; } /// /// Returns true if our ActionRequestData came with a valid target. For the ChaseAction, this is pretty liberal (could be friend or foe, could be /// dead or alive--just needs to be present). /// private bool HasValidTarget() { return m_Data.TargetIds != null && m_Data.TargetIds.Length > 0 && NetworkManager.Singleton.SpawnManager.SpawnedObjects.ContainsKey(m_Data.TargetIds[0]); } /// /// Tests to see if we've reached our target. Returns true if we've reached our target, false otherwise (in which case it also stops our movement). /// private bool StopIfDone(ServerCharacter parent) { if (m_TargetTransform == null) { //if the target disappeared on us, then just stop. Cancel(parent); return true; } float distToTarget2 = (parent.physicsWrapper.Transform.position - m_TargetTransform.position).sqrMagnitude; if ((m_Data.Amount * m_Data.Amount) > distToTarget2) { //we made it! we're done. Cancel(parent); return true; } return false; } /// /// Called each frame while the action is running. /// /// true to keep running, false to stop. The Action will stop by default when its duration expires, if it has a duration set. public override bool OnUpdate(ServerCharacter clientCharacter) { if (StopIfDone(clientCharacter)) { return ActionConclusion.Stop; } // Keep re-assigning our chase target whenever possible. // This way, if we get Knocked Back mid-chase, we pick right back up and continue the chase. if (!clientCharacter.Movement.IsPerformingForcedMovement()) { clientCharacter.Movement.FollowTransform(m_TargetTransform); } return ActionConclusion.Continue; } public override void Cancel(ServerCharacter serverCharacter) { if (serverCharacter.Movement && !serverCharacter.Movement.IsPerformingForcedMovement()) { serverCharacter.Movement.CancelMove(); } } public override bool OnUpdateClient(ClientCharacter clientCharacter) { return ActionConclusion.Continue; } } }