private Task PatrolEnter(State from)
        {
            PatrolState patrolState = stateMachine.GetCurrentState <PatrolState>();

            if (patrolState == null)
            {
                throw new InvalidOperationException("PatrolEnter can only be used with PatrolState");
            }

            if (PathToFollow != null)
            {
                // Select a waypoint to patrol
                patrolState.NextWaypoint =
                    PathToFollow.Path.SelectWaypoint(Entity.Transform.WorldMatrix.TranslationVector);
                patrolState.MoveOperation = Move(patrolState.NextWaypoint.Position);
            }
            else
            {
                // Disable warnings for GDC
                //Log.Warning($"Patrolling Drone {Entity} doesn't have a follow path assigned")
                // Return to spawn
                patrolState.MoveOperation = Move(spawnLocation);
            }

            return(Task.FromResult(0));
        }
        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 PatrolUpdate()
        {
            PatrolState patrolState = stateMachine.GetCurrentState <PatrolState>();

            if (patrolState == null)
            {
                throw new InvalidOperationException("PatrolUpdate can only be used with PatrolState");
            }

            // Move the drone on the current path, until the end of the move target is reached
            if (patrolState.MoveOperation != null)
            {
                if (!patrolState.MoveOperation.MoveNext())
                {
                    // Continue on path (if assigned)
                    if (PathToFollow != null)
                    {
                        // Done moving
                        patrolState.NextWaypoint = patrolState.NextWaypoint.Next;
                        if (patrolState.NextWaypoint == null)
                        {
                            patrolState.NextWaypoint = PathToFollow.Path.Waypoints[0]; // Loop back to first waypoint
                        }
                        patrolState.MoveOperation = Move(patrolState.NextWaypoint.Position);
                    }
                    else
                    {
                        // No move moving, this was a single target move
                        patrolState.MoveOperation = null;
                    }
                }
            }
            else
            {
                // Not moving and no path to follow, reset to spawn rotation
                Drone.UpdateBodyRotation(Drone.RotationToWorldDirection(spawnOrientation.Item1));
                Drone.UpdateHeadRotation(spawnOrientation.Item2);
            }

            // Look in moving direction
            Vector3 dir = Drone.CurrentVelocity;

            dir.Normalize();
            if (dir != Vector3.Zero)
            {
                Drone.UpdateHeadRotation(dir);
            }
            Drone.Alerted = false;

            // Check for enemies
            foreach (var collision in alertZoneTrigger.Collisions)
            {
                var targetCollider = collision.ColliderA.Entity != alertZoneTrigger.Entity
                    ? collision.ColliderA
                    : collision.ColliderB;

                if (Drone.Stunned)
                {
                    if (targetCollider.CollisionGroup != CollisionFilterGroups.CustomFilter3)
                    {
                        continue;
                    }
                }
                else
                {
                    if (targetCollider.CollisionGroup != CollisionFilterGroups.CharacterFilter &&
                        targetCollider.CollisionGroup != CollisionFilterGroups.CustomFilter1)
                    {
                        continue;
                    }
                }

                var enemy = Utils.GetDestructible(targetCollider.Entity);
                if (targetCollider.Entity == Entity || enemy == null || enemy.IsDead)
                {
                    continue;
                }

                // Visibility check
                bool hasLineOfSight = CheckLineOfSight(Entity, targetCollider);
                if (hasLineOfSight)
                {
                    // Start chasing state
                    patrolState.ChaseTarget         = targetCollider.Entity;
                    patrolState.ChaseColliderTarget = targetCollider;
                    stateMachine.SwitchTo(ChaseState.Name);
                }

                break;
            }
        }