private Task ChaseStart(State from)
        {
            PatrolState patrolState = (PatrolState)from;

            if (patrolState == null)
            {
                throw new InvalidOperationException("ChaseState can only be entered from PatrolState");
            }

            ChaseState chaseState = stateMachine.GetCurrentState <ChaseState>();

            if (chaseState == null)
            {
                throw new InvalidOperationException("ChaseStart can only be used with ChaseState");
            }


            chaseState.ChaseTarget         = patrolState.ChaseTarget;
            chaseState.ChaseColliderTarget = patrolState.ChaseColliderTarget;

            return(Task.FromResult(0));
        }
        private void ChaseUpdate()
        {
            Drone.Alerted = true;

            ChaseState chaseState = stateMachine.GetCurrentState <ChaseState>();

            if (chaseState == null)
            {
                throw new InvalidOperationException("ChaseUpdate can only be used with ChaseState");
            }

            if (chaseState.ChaseTarget == null)
            {
                // Stop chasing
                stateMachine.SwitchTo(PatrolState.Name);
                return;
            }

            IDestructible destructible = Utils.GetDestructible(chaseState.ChaseTarget);

            if (destructible == null)
            {
                throw new InvalidOperationException("ChaseTarget can only target IDestructibles");
            }
            if (destructible.IsDead)
            {
                // Stop chasing
                stateMachine.SwitchTo(PatrolState.Name);
                return;
            }

            // Check if still overlapping
            bool withinRange = false;

            foreach (var collision in alertZoneTrigger.Collisions)
            {
                var targetCollider = collision.ColliderA.Entity != alertZoneTrigger.Entity
                    ? collision.ColliderA
                    : collision.ColliderB;
                if (targetCollider == chaseState.ChaseColliderTarget)
                {
                    withinRange = true;
                    break;
                }
            }

            if (!withinRange)
            {
                // Stop chasing
                stateMachine.SwitchTo(PatrolState.Name);
                return;
            }

            // Recalculate path to player?
            Vector3 actualTargetPos = chaseState.ChaseTarget.Transform.WorldMatrix.TranslationVector;

            var source = Entity.Transform.WorldMatrix.TranslationVector;
            var target = actualTargetPos;

            source.Y = 1.5f;
            target.Y = 1.5f;
            Vector3 aimDir       = target - source;
            float   distToTarget = aimDir.Length();

            aimDir.Normalize();
            var playerTargeted = Drone.UpdateHeadRotation(aimDir);

            // Process move step
            if (chaseState.MoveOperation != null)
            {
                if (!chaseState.MoveOperation.MoveNext())
                {
                    chaseState.MoveOperation = null;
                }
            }

            bool hasLineOfSight = CheckLineOfSight(Entity, chaseState.ChaseColliderTarget);

            if (hasLineOfSight)
            {
                if (distToTarget < 6.0f)
                {
                    // No longer need to move, player is in line of sight, and drone is pretty close
                    chaseState.MoveOperation = null;
                    Drone.SetMovement(Vector3.Zero);
                }

                if (playerTargeted)
                {
                    // Shoot the player
                    Drone.Weapon?.TryShoot(chaseState.ChaseTarget);
                }
            }

            // Update path towards player when either not moving or
            //  the current path would end up too far from the player
            float targetDistance = (actualTargetPos - chaseState.CurrentChaseTargetPosition).Length();

            if (chaseState.MoveOperation == null || targetDistance > 1.0f)
            {
                chaseState.CurrentChaseTargetPosition = chaseState.ChaseTarget.Transform.WorldMatrix.TranslationVector;
                chaseState.MoveOperation = Move(chaseState.CurrentChaseTargetPosition);
            }
        }