public List <Point> pathNodes(int level, Point origin, Point dest, Pathing.PathingPermission permission)
        {
            List <Point> returnNodes = new List <Point>();

            TCODFov mapToUse;

            switch (permission)
            {
            case Pathing.PathingPermission.Normal:
                mapToUse = levelTCODMaps[level];
                break;

            case Pathing.PathingPermission.IgnoreDoors:
                mapToUse = levelTCODMapsIgnoringClosedDoors[level];
                break;

            case Pathing.PathingPermission.IgnoreDoorsAndLocks:
                mapToUse = levelTCODMapsIgnoringClosedDoorsAndLocks[level];
                break;

            default:
                mapToUse = levelTCODMaps[level];
                break;
            }

            //Try to walk the path
            TCODPathFinding path = new TCODPathFinding(mapToUse, 1.0);

            path.ComputePath(origin.x, origin.y, dest.x, dest.y);

            returnNodes.Add(origin);

            int x = origin.x;
            int y = origin.y;

            path.WalkPath(ref x, ref y, false);
            returnNodes.Add(new Point(x, y));

            //If the x and y of the next step it means the path is blocked

            if (x == origin.x && y == origin.y)
            {
                //Return 1 node list
                return(returnNodes.GetRange(0, 1));
            }

            do
            {
                path.WalkPath(ref x, ref y, false);
                returnNodes.Add(new Point(x, y));
                if (x == dest.x && y == dest.y)
                {
                    return(returnNodes);
                }
            } while (true);
        }
        private void ContinueChasing(Creature newTarget)
        {
            //Chase the player
            //They are either out of range or out of FOV
            //For now, pursuing creatures know how to move their FOV to get the player

            //Return if we can't move or won't pursue
            if (!CanMove() || !WillPursue())
            {
                //Return to patrol mode. This allows creatures to go back to patrolling if the PC moves out of range
                AIState = SimpleAIStates.Patrol;
                LogFile.Log.LogEntryDebug("MonsterThrowAndRunAI: Out of range but can't pursue, returning to patrol ", LogDebugLevel.Medium);
                return;
            }

            //Find location of next step on the path towards them
            Pathing.PathingResult pathingResult;

            Pathing.PathingPermission permission = Pathing.PathingPermission.Normal;
            if (CanOpenDoors())
            {
                permission = Pathing.PathingPermission.IgnoreDoors;
            }

            pathingResult = Game.Dungeon.Pathing.GetPathToCreature(this, newTarget, PathingType(), permission);

            //If this is the same as the target creature's location, we are adjacent. TODO: change our FOV instead of moving.
            //We are allowed to attack in this case. TODO: more fun if not?
            if (pathingResult.MoveIsInteractionWithTarget)
            {
                LogFile.Log.LogEntryDebug("MonsterThrowAndRunAI: Adjacent to target so changing FOV only ", LogDebugLevel.Low);

                //This does appear to happen now due to the unusual shaped FOVs (i.e. adjacent but out of FOV)
                SetHeadingToTarget(newTarget);

                if (WillAttack())
                {
                    //Fire at the player
                    CombatResults result;

                    if (newTarget == Game.Dungeon.Player)
                    {
                        result = AttackPlayer(newTarget as Player);
                    }
                    else
                    {
                        //It's a normal creature
                        result = AttackMonster(newTarget as Monster);
                    }

                    //Missile animation
                    Screen.Instance.DrawMissileAttack(this, newTarget, result, GetWeaponColor());
                }
            }

            //If we are permanently blocked, return to patrol state
            if (pathingResult.TerminallyBlocked)
            {
                LogFile.Log.LogEntryDebug(this.Representation + " permanently blocked (door), returning to patrol ", LogDebugLevel.Medium);
                AIState = SimpleAIStates.Patrol;
                return;
            }

            //Update position
            MoveIntoSquare(pathingResult.MonsterFinalLocation);
            SetHeadingToTarget(newTarget);
        }
        /// <summary>
        /// Override the following code from the simple throwing AI to include backing away
        /// </summary>
        /// <param name="newTarget"></param>
        protected override void FollowAndAttack(Creature newTarget)
        {
            double range = Utility.GetDistanceBetween(this, newTarget);

            CreatureFOV currentFOV = Game.Dungeon.CalculateCreatureFOV(this);
            CreatureFOV playerFOV  = Game.Dungeon.CalculateCreatureFOV(Game.Dungeon.Player);

            bool backAwayFromTarget = false;

            //Back away if we are too close & can see the target
            //If we can't see the target, don't back away
            if (range < GetMissileRange() / 2.0 && currentFOV.CheckTileFOV(newTarget.LocationMap.x, newTarget.LocationMap.y))
            {
                //Enforce a symmetry FOV
                if (newTarget != Game.Dungeon.Player ||
                    (newTarget == Game.Dungeon.Player && playerFOV.CheckTileFOV(this.LocationMap)))
                {
                    //Check our chance to back away. For a hard/clever creature this is 100. For a stupid creature it is 0.
                    if (Game.Random.Next(100) < GetChanceToBackAway())
                    {
                        backAwayFromTarget = true;
                    }
                }
            }

            if (backAwayFromTarget && CanMove() && WillPursue())
            {
                //Target is too close, so back away before firing

                int deltaX = newTarget.LocationMap.x - this.LocationMap.x;
                int deltaY = newTarget.LocationMap.y - this.LocationMap.y;

                //Find a point in the dungeon to flee to
                int fleeX = 0;
                int fleeY = 0;

                int counter = 0;

                bool relaxDirection = false;
                bool goodPath       = false;

                Point nextStep = new Point(0, 0);

                int totalFleeLoops   = GetTotalFleeLoops();
                int relaxDirectionAt = RelaxDirectionAt();

                do
                {
                    //This performs badly when there are few escape options and you are close to the edge of the map
                    fleeX = Game.Random.Next(Game.Dungeon.Levels[this.LocationLevel].width);
                    fleeY = Game.Random.Next(Game.Dungeon.Levels[this.LocationLevel].height);

                    //Relax conditions if we are having a hard time
                    if (counter > relaxDirectionAt)
                    {
                        relaxDirection = true;
                    }

                    //Find the square to move to
                    Pathing.PathingPermission permission = Pathing.PathingPermission.Normal;
                    if (CanOpenDoors())
                    {
                        permission = Pathing.PathingPermission.IgnoreDoors;
                    }

                    nextStep = Game.Dungeon.Pathing.GetPathToPoint(this.LocationLevel, this.LocationMap, new Point(fleeX, fleeY), PathingType(), permission).MonsterFinalLocation;

                    //Check the square is pathable to
                    if (nextStep.x == LocationMap.x && nextStep.y == LocationMap.y)
                    {
                        counter++;
                        //LogFile.Log.LogEntryDebug("MonsterThrowAndRunAI: Back away fail unpathable", LogDebugLevel.Low);
                        continue;
                    }

                    //Check that the next square is in a direction away from the attacker
                    int deltaFleeX = nextStep.x - this.LocationMap.x;
                    int deltaFleeY = nextStep.y - this.LocationMap.y;

                    if (!relaxDirection)
                    {
                        if (deltaFleeX > 0 && deltaX > 0)
                        {
                            counter++;
                            //LogFile.Log.LogEntryDebug("MonsterThrowAndRunAI: Back away fail direction", LogDebugLevel.Low);
                            continue;
                        }
                        if (deltaFleeX < 0 && deltaX < 0)
                        {
                            counter++;
                            //LogFile.Log.LogEntryDebug("MonsterThrowAndRunAI: Back away fail direction", LogDebugLevel.Low);
                            continue;
                        }
                        if (deltaFleeY > 0 && deltaY > 0)
                        {
                            counter++;
                            //LogFile.Log.LogEntryDebug("MonsterThrowAndRunAI: Back away fail direction", LogDebugLevel.Low);
                            continue;
                        }
                        if (deltaFleeY < 0 && deltaY < 0)
                        {
                            counter++;
                            //LogFile.Log.LogEntryDebug("MonsterThrowAndRunAI: Back away fail direction", LogDebugLevel.Low);
                            continue;
                        }
                    }

                    //Check the square is empty
                    bool isEnterable = Game.Dungeon.MapSquareIsWalkable(this.LocationLevel, new Point(fleeX, fleeY));
                    if (!isEnterable)
                    {
                        counter++;
                        //LogFile.Log.LogEntryDebug("MonsterThrowAndRunAI: Back away fail enterable", LogDebugLevel.Low);
                        continue;
                    }

                    //Check the square is empty of creatures
                    SquareContents contents = Game.Dungeon.MapSquareContents(this.LocationLevel, new Point(fleeX, fleeY));
                    if (contents.monster != null)
                    {
                        counter++;
                        //LogFile.Log.LogEntryDebug("MonsterThrowAndRunAI: Back away fail blocked", LogDebugLevel.Low);
                        continue;
                    }


                    //Check that the target is visible from the square we are fleeing to
                    //This may prove to be too expensive

                    CreatureFOV projectedFOV = Game.Dungeon.CalculateCreatureFOV(this, nextStep);

                    if (!projectedFOV.CheckTileFOV(newTarget.LocationMap.x, newTarget.LocationMap.y))
                    {
                        counter++;
                        //LogFile.Log.LogEntryDebug("MonsterThrowAndRunAI: Back away fail fov", LogDebugLevel.Low);
                        continue;
                    }

                    //Otherwise we found it
                    goodPath = true;
                    break;
                } while (counter < totalFleeLoops);

                LogFile.Log.LogEntryDebug("Back away results. Count: " + counter + " Direction at: " + relaxDirectionAt + " Total: " + totalFleeLoops, LogDebugLevel.Low);

                //If we found a good path, walk it
                if (goodPath)
                {
                    MoveIntoSquare(nextStep);
                    SetHeadingToTarget(newTarget);
                }
                else if (WillAttack())
                {
                    //If not, don't back away and attack
                    //(target in FOV)
                    CombatResults result;

                    //Set heading to target
                    SetHeadingToTarget(newTarget);

                    if (newTarget == Game.Dungeon.Player)
                    {
                        result = AttackPlayer(newTarget as Player);
                    }
                    else
                    {
                        //It's a normal creature
                        result = AttackMonster(newTarget as Monster);
                    }

                    //Missile animation
                    Screen.Instance.DrawMissileAttack(this, newTarget, result, GetWeaponColor());
                }
            }

            //Close enough to fire. Not backing away (either far enough away or chose not to)
            else if (Utility.TestRange(this, newTarget, GetMissileRange()) && WillAttack())
            {
                //In range

                //Check FOV. If not in FOV, chase the player.
                if (!currentFOV.CheckTileFOV(newTarget.LocationMap.x, newTarget.LocationMap.y))
                {
                    ContinueChasing(newTarget);
                    return;
                }

                //Enforce a symmetry FOV
                if (newTarget == Game.Dungeon.Player && !playerFOV.CheckTileFOV(this.LocationMap))
                {
                    ContinueChasing(newTarget);
                    return;
                }

                //In preference they will use their special ability rather than fighting
                //If they don't have a special ability or choose not to use it will return false

                //Special abilities are defined in derived classes

                bool usingSpecial = UseSpecialAbility();

                if (!usingSpecial)
                {
                    //In FOV - fire at the player
                    CombatResults result;

                    //Set heading to target (only if we are a Pursuing creature, capable of adapting our heading)
                    if (WillPursue())
                    {
                        SetHeadingToTarget(newTarget);
                    }

                    if (newTarget == Game.Dungeon.Player)
                    {
                        result = AttackPlayer(newTarget as Player);
                    }
                    else
                    {
                        //It's a normal creature
                        result = AttackMonster(newTarget as Monster);
                    }

                    //Missile animation
                    Screen.Instance.DrawMissileAttack(this, newTarget, result, GetWeaponColor());
                }
            }

            //Not in range, chase the target
            else
            {
                ContinueChasing(newTarget);
            }
        }
 public bool arePointsConnected(int level, Point origin, Point dest, Pathing.PathingPermission permission)
 {
     return(pathNodes(level, origin, dest, permission).Count > 1);
 }