示例#1
0
文件: Dungeon.cs 项目: vrum/roguelike
        /// <summary>
        /// Does the square contain a player or creature?
        /// </summary>
        /// <param name="level"></param>
        /// <param name="location"></param>
        /// <returns></returns>
        public SquareContents MapSquareContents(int level, Point location)
        {
            SquareContents contents = new SquareContents();

            //Check creature that be blocking
            foreach (Monster creature in monsters)
            {
                if (creature.LocationLevel == level &&
                    creature.LocationMap.x == location.x && creature.LocationMap.y == location.y)
                {
                    contents.monster = creature;
                    break;
                }
            }

            //Check for PC blocking
            if (player.LocationMap.x == location.x && player.LocationMap.y == location.y)
            {
                contents.player = player;
            }

            if (contents.monster == null && contents.player == null)
            {
                contents.empty = true;
            }

            return(contents);
        }
示例#2
0
文件: Dungeon.cs 项目: vrum/roguelike
        internal bool PCMove(int x, int y)
        {
            Point newPCLocation = new Point(Player.LocationMap.x + x, Player.LocationMap.y + y);

            if (newPCLocation.x < 0 || newPCLocation.x >= levels[player.LocationLevel].width)
            {
                return(false);
            }

            if (newPCLocation.y < 0 || newPCLocation.y >= levels[player.LocationLevel].height)
            {
                return(false);
            }

            //If this is not a valid square, return false

            if (!MapSquareCanBeEntered(player.LocationLevel, newPCLocation))
            {
                return(false);
            }

            //Check for monsters in the square
            SquareContents contents           = MapSquareContents(player.LocationLevel, newPCLocation);
            bool           okToMoveIntoSquare = false;

            //If it's empty, it's OK
            if (contents.empty)
            {
                okToMoveIntoSquare = true;
            }

            //Monster - attack it
            if (contents.monster != null)
            {
                CombatResults results = player.AttackMonster(contents.monster);
                if (results == CombatResults.DefenderDied)
                {
                    okToMoveIntoSquare = true;
                }
            }

            if (okToMoveIntoSquare)
            {
                player.LocationMap = newPCLocation;
            }
            return(true);
        }
示例#3
0
文件: Dungeon.cs 项目: vrum/roguelike
        public bool AddMonster(Monster creature, int level, Point location)
        {
            //Try to add a creature at the requested location
            //This may fail due to something else being there or being non-walkable
            try
            {
                Map creatureLevel = levels[level];

                //Check square is accessable
                if (!MapSquareCanBeEntered(level, location))
                {
                    LogFile.Log.LogEntryDebug("AddMonster failure: Square not enterable", LogDebugLevel.Low);
                    return(false);
                }

                //Check square has nothing else on it
                SquareContents contents = MapSquareContents(level, location);

                if (contents.monster != null)
                {
                    LogFile.Log.LogEntryDebug("AddMonster failure: Monster at this square", LogDebugLevel.Low);
                    return(false);
                }

                if (contents.player != null)
                {
                    LogFile.Log.LogEntryDebug("AddMonster failure: Player at this square", LogDebugLevel.Low);
                    return(false);
                }

                //Otherwise OK
                creature.LocationLevel = level;
                creature.LocationMap   = location;

                monsters.Add(creature);
                return(true);
            }
            catch (Exception ex)
            {
                LogFile.Log.LogEntry(String.Format("AddCreature: ") + ex.Message);
                return(false);
            }
        }
示例#4
0
        /// <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);
            }
        }
示例#5
0
        /// <summary>
        /// Does the square contain a player or creature?
        /// </summary>
        /// <param name="level"></param>
        /// <param name="location"></param>
        /// <returns></returns>
        public SquareContents MapSquareContents(int level, Point location)
        {
            SquareContents contents = new SquareContents();

            //Check creature that be blocking
            foreach (Monster creature in monsters)
            {
                if (creature.LocationLevel == level &&
                    creature.LocationMap.x == location.x && creature.LocationMap.y == location.y)
                {
                    contents.monster = creature;
                    break;
                }
            }

            //Check for PC blocking
            if (player.LocationMap.x == location.x && player.LocationMap.y == location.y)
            {
                contents.player = player;
            }

            if (contents.monster == null && contents.player == null)
                contents.empty = true;

            return contents;
        }
示例#6
0
        protected override bool UseSpecialAbility()
        {
            //Check if they are going to use their special at all
            if (Game.Random.Next(100) > GetUseSpecialChance())
            {
                return(false);
            }

            if (GetSpecialAIType() == SpecialAIType.Healer)
            {
                //Look for injured creatures within range
                List <Monster> targetsInRange = new List <Monster>();

                foreach (Monster monster in Game.Dungeon.Monsters)
                {
                    if (this.LocationLevel != monster.LocationLevel)
                    {
                        continue;
                    }

                    //Can't heal yourself
                    if (monster == this)
                    {
                        continue;
                    }

                    //Don't healed charmed monsters either
                    if (Utility.GetDistanceBetween(this, monster) < GetMissileRange() + 0.005 &&
                        !monster.Charmed)
                    {
                        targetsInRange.Add(monster);
                    }
                }

                //See if any of them are injured
                List <Monster> injuredTargets = targetsInRange.FindAll(x => x.Hitpoints < x.MaxHitpoints);

                if (injuredTargets.Count == 0)
                {
                    return(false);
                }

                //Pick a random monster
                Monster actualTarget = injuredTargets[Game.Random.Next(injuredTargets.Count)];

                //Heal this monster
                int oldHP = actualTarget.Hitpoints;
                actualTarget.Hitpoints += (int)(Game.Random.Next(actualTarget.MaxHitpoints - actualTarget.Hitpoints) / 3.0);

                //Update msg
                Game.MessageQueue.AddMessage("The " + this.SingleDescription + " heals the " + actualTarget.SingleDescription);
                LogFile.Log.LogEntryDebug(actualTarget.SingleDescription + " hp: " + oldHP + " -> " + actualTarget.Hitpoints, LogDebugLevel.Medium);

                //We used this ability
                return(true);
            }

            else if (GetSpecialAIType() == SpecialAIType.Raiser)
            {
                //Look for a nearby corpse
                //Look for injured creatures within range
                List <Feature> corpseInRange = new List <Feature>();

                foreach (Feature feature in Game.Dungeon.Features)
                {
                    if (this.LocationLevel != feature.LocationLevel)
                    {
                        continue;
                    }

                    if (Utility.GetDistanceBetween(this, feature) < GetMissileRange() + 0.005)
                    {
                        if (feature is Features.Corpse)
                        {
                            corpseInRange.Add(feature);
                        }
                    }
                }

                if (corpseInRange.Count == 0)
                {
                    return(false);
                }

                //Pick a corpse at random
                Feature actualCorpse = corpseInRange[Game.Random.Next(corpseInRange.Count)];

                //Check this square is empty
                int   corpseLevel = actualCorpse.LocationLevel;
                Point corpseMap   = actualCorpse.LocationMap;

                SquareContents contents = Game.Dungeon.MapSquareContents(corpseLevel, corpseMap);

                if (!contents.empty)
                {
                    return(false);
                }

                //Raise a creature here

                //For now just raise skeletons I think we might need to make a separate AI for each raisey creature
                Game.Dungeon.Features.Remove(actualCorpse); //should have a helper for this really

                //Spawn a skelly
                bool raisedSuccess = RaiseCorpse(actualCorpse.LocationLevel, actualCorpse.LocationMap);

                if (raisedSuccess)
                {
                    Game.MessageQueue.AddMessage("The " + this.SingleDescription + " tries to raise a corpse!");
                    LogFile.Log.LogEntryDebug(this.SingleDescription + " raises corpse", LogDebugLevel.Medium);
                }
                return(raisedSuccess);
            }
            //Effect on player. Know we are in range if this was called
            else if (GetSpecialAIType() == SpecialAIType.PlayerEffecter)
            {
                //Shouldn't happen if charmed

                LogFile.Log.LogEntryDebug(this.SingleDescription + " attempting player effect attack", LogDebugLevel.Medium);

                //Player already has this effect
                Player player = Game.Dungeon.Player;

                PlayerEffect effectToUse = GetSpecialAIEffect();

                if (effectToUse == null)
                {
                    LogFile.Log.LogEntryDebug(this.SingleDescription + " error getting effect", LogDebugLevel.High);
                    return(false);
                }

                //Don't do it twice
                if (player.IsEffectActive(effectToUse.GetType()))
                {
                    return(false);
                }

                string attackStr = EffectAttackString();

                Game.MessageQueue.AddMessage("The " + this.SingleDescription + " tries to " + attackStr + " you!");

                //Player resistance
                bool playerResistance = DoPlayerResistance();

                if (playerResistance == true)
                {
                    Game.MessageQueue.AddMessage("You resist the attack.");
                    return(true);
                }

                //If failed, we add our effect
                player.AddEffect(effectToUse);

                return(true);
            }
            //Spellkon player. Know we are in range if this was called
            else if (GetSpecialAIType() == SpecialAIType.PlayerCaster)
            {
                //Shouldn't happen if charmed

                LogFile.Log.LogEntryDebug(this.SingleDescription + " attempting player spell attack", LogDebugLevel.Medium);

                //Player already has this effect
                Player player = Game.Dungeon.Player;

                Spell effectToUse = GetSpecialAISpell();

                if (effectToUse == null)
                {
                    LogFile.Log.LogEntryDebug(this.SingleDescription + " error getting spell", LogDebugLevel.High);
                    return(false);
                }

                string attackStr = EffectAttackString();

                Game.MessageQueue.AddMessage("The " + this.SingleDescription + " tries to " + attackStr + " you!");

                //Player resistance
                bool playerResistance = DoPlayerResistance();

                if (playerResistance == true)
                {
                    Game.MessageQueue.AddMessage("You resist the attack.");
                    return(true);
                }

                //If failed, we add our effect
                effectToUse.DoSpell(player.LocationMap);

                return(true);
            }


            //Dragon can do a variety of things
            else if (GetSpecialAIType() == SpecialAIType.Dragon)
            {
                Player player = Game.Dungeon.Player;

                //Are we injured? If so, try to heal ourselves

                if (this.Hitpoints < (int)Math.Floor(this.MaxHitpoints / 2.0))
                {
                    int oldHP = this.Hitpoints;
                    this.Hitpoints += (int)(Game.Random.Next(this.MaxHitpoints - this.Hitpoints) / 5.0);

                    //Update msg
                    Game.MessageQueue.AddMessage("The Dragon heals itself!");
                    LogFile.Log.LogEntryDebug(this.SingleDescription + " hp: " + oldHP + " -> " + this.Hitpoints, LogDebugLevel.Medium);

                    return(true);
                }

                //If not, screw around with the player a bit

                //50% chance we will just attack

                if (Game.Random.Next(100) < 50)
                {
                    return(false);
                }

                //Otherwise decide what we're going to do

                int taskNo = Game.Random.Next(3);

                if (taskNo == 0)
                {
                    //Player already has this effect

                    /*
                     * Spell effectToUse = new Spells.Blink();
                     *
                     * if (effectToUse == null)
                     * {
                     *  LogFile.Log.LogEntryDebug(this.SingleDescription + " error getting spell", LogDebugLevel.High);
                     *  return false;
                     * }
                     *
                     * Game.MessageQueue.AddMessage("The Dragon tries to teleport you!");
                     *
                     * //Player resistance
                     * bool playerResistance = DoPlayerResistance();
                     *
                     * if (playerResistance == true)
                     * {
                     *  Game.MessageQueue.AddMessage("You resist the attack.");
                     *  return true;
                     * }
                     *
                     * //If failed, we add our effect
                     * effectToUse.DoSpell(player.LocationMap);
                     *
                     * return true;*/

                    return(false);
                }
                else if (taskNo == 1)
                {
                    int duration = 2 * Creature.turnTicks + Game.Random.Next(5 * Creature.turnTicks);

                    PlayerEffects.SpeedDown speedDownEff = new RogueBasin.PlayerEffects.SpeedDown(duration, 30);

                    if (speedDownEff == null)
                    {
                        LogFile.Log.LogEntryDebug(this.SingleDescription + " error getting effect", LogDebugLevel.High);
                        return(false);
                    }

                    //Don't do it twice
                    if (player.IsEffectActive(typeof(PlayerEffects.SpeedDown)))
                    {
                        return(false);
                    }
                    Game.MessageQueue.AddMessage("The Dragon tries to slow you!");

                    //Player resistance
                    bool playerResistance = DoPlayerResistance();

                    if (playerResistance == true)
                    {
                        Game.MessageQueue.AddMessage("You resist the attack.");
                        return(true);
                    }

                    //If failed, we add our effect
                    player.AddEffect(speedDownEff);
                }
                else
                {
                    int duration    = 3 * Creature.turnTicks + Game.Random.Next(5 * Creature.turnTicks);
                    int playerSight = Game.Dungeon.Player.SightRadius;
                    int sightDown   = playerSight - 1;

                    PlayerEffects.SightRadiusDown sightDownEff = new RogueBasin.PlayerEffects.SightRadiusDown(duration, sightDown);

                    if (sightDownEff == null)
                    {
                        LogFile.Log.LogEntryDebug(this.SingleDescription + " error getting effect", LogDebugLevel.High);
                        return(false);
                    }

                    //Don't do it twice
                    if (player.IsEffectActive(typeof(PlayerEffects.SightRadiusDown)))
                    {
                        return(false);
                    }

                    Game.MessageQueue.AddMessage("The Dragon tries to blind you!");

                    //Player resistance
                    bool playerResistance = DoPlayerResistance();

                    if (playerResistance == true)
                    {
                        Game.MessageQueue.AddMessage("You resist the attack.");
                        return(true);
                    }

                    //If failed, we add our effect
                    player.AddEffect(sightDownEff);
                }

                return(true);
            }
            else
            {
                //Summoner not implemented yet
                return(false);
            }
        }
示例#7
0
        /// <summary>
        /// Run the Simple AI actions
        /// </summary>
        public override void ProcessTurn()
        {
            //If in pursuit state, continue to pursue enemy until it is dead (or creature itself is killed) [no FOV used after initial target selected]

            //If in randomWalk state, look for new enemies in FOV.
            //Closest enemy becomes new target

            //If no targets, move randomly

            Random rand = Game.Random;


            if (AIState == SimpleAIStates.Pursuit)
            {
                //Pursuit state, continue chasing and attacking target

                //Is target yet living?
                if (currentTarget.Alive == false)
                {
                    //If not, go to non-chase state
                    AIState = SimpleAIStates.RandomWalk;
                }
                //Is target on another level (i.e. has escaped down the stairs)
                else if (currentTarget.LocationLevel != this.LocationLevel)
                {
                    AIState = SimpleAIStates.RandomWalk;
                }
                else
                {
                    //Otherwise continue to chase

                    ChaseCreature(currentTarget);
                }
            }

            if (AIState == SimpleAIStates.RandomWalk)
            {
                //RandomWalk state

                //Search an area of sightRadius on either side for creatures and check they are in the FOV

                Map currentMap = Game.Dungeon.Levels[LocationLevel];

                //Get the FOV from Dungeon (this also updates the map creature FOV state)
                TCODFov currentFOV = Game.Dungeon.CalculateCreatureFOV(this);
                //currentFOV.CalculateFOV(LocationMap.x, LocationMap.y, SightRadius);

                //Check for other creatures within this creature's FOV

                int xl = LocationMap.x - SightRadius;
                int xr = LocationMap.x + SightRadius;

                int yt = LocationMap.y - SightRadius;
                int yb = LocationMap.y + SightRadius;

                //If sight is infinite, check all the map
                if (SightRadius == 0)
                {
                    xl = 0;
                    xr = currentMap.width;
                    yt = 0;
                    yb = currentMap.height;
                }

                if (xl < 0)
                {
                    xl = 0;
                }
                if (xr >= currentMap.width)
                {
                    xr = currentMap.width - 1;
                }
                if (yt < 0)
                {
                    yt = 0;
                }
                if (yb >= currentMap.height)
                {
                    yb = currentMap.height - 1;
                }

                //List will contain monsters & player
                List <Creature> creaturesInFOV = new List <Creature>();

                foreach (Monster monster in Game.Dungeon.Monsters)
                {
                    //Same monster
                    if (monster == this)
                    {
                        continue;
                    }

                    //Not on the same level
                    if (monster.LocationLevel != this.LocationLevel)
                    {
                        continue;
                    }

                    //Not in FOV
                    if (!currentFOV.CheckTileFOV(monster.LocationMap.x, monster.LocationMap.y))
                    {
                        continue;
                    }

                    //Otherwise add to list of possible targets
                    creaturesInFOV.Add(monster);

                    LogFile.Log.LogEntryDebug(this.Representation + " spots " + monster.Representation, LogDebugLevel.Medium);
                }

                //Check PC
                if (Game.Dungeon.Player.LocationLevel == this.LocationLevel)
                {
                    if (currentFOV.CheckTileFOV(Game.Dungeon.Player.LocationMap.x, Game.Dungeon.Player.LocationMap.y))
                    {
                        creaturesInFOV.Add(Game.Dungeon.Player);
                        LogFile.Log.LogEntryDebug(this.Representation + " spots " + Game.Dungeon.Player.Representation, LogDebugLevel.Medium);
                    }
                }

                //If there are possible targets, find the closest and chase it
                //Otherwise continue to move randomly

                if (creaturesInFOV.Count > 0)
                {
                    //Find the closest creature
                    Creature closestCreature = null;
                    double   closestDistance = Double.MaxValue; //a long way

                    foreach (Creature creature in creaturesInFOV)
                    {
                        double distanceSq = Math.Pow(creature.LocationMap.x - this.LocationMap.x, 2) +
                                            Math.Pow(creature.LocationMap.y - this.LocationMap.y, 2);

                        double distance = Math.Sqrt(distanceSq);

                        if (distance < closestDistance)
                        {
                            closestDistance = distance;
                            closestCreature = creature;
                        }
                    }

                    //Start chasing this creature
                    LogFile.Log.LogEntryDebug(this.Representation + " chases " + closestCreature.Representation, LogDebugLevel.Medium);
                    ChaseCreature(closestCreature);
                }
                else
                {
                    //Move randomly. If we walk into something attack it, but it does not become a new target

                    int direction = rand.Next(9);

                    int moveX = 0;
                    int moveY = 0;

                    moveX = direction / 3 - 1;
                    moveY = direction % 3 - 1;

                    //If we're not moving quit at this point, otherwise the target square will be the one we're in
                    if (moveX == 0 && moveY == 0)
                    {
                        return;
                    }

                    //Check this is a valid move
                    bool  validMove   = false;
                    Point newLocation = new Point(LocationMap.x + moveX, LocationMap.y + moveY);

                    validMove = Game.Dungeon.MapSquareCanBeEntered(LocationLevel, newLocation);

                    //Give up if this is not a valid move
                    if (!validMove)
                    {
                        return;
                    }

                    //Check if the square is occupied by a PC or monster
                    SquareContents contents           = Game.Dungeon.MapSquareContents(LocationLevel, newLocation);
                    bool           okToMoveIntoSquare = false;

                    if (contents.empty)
                    {
                        okToMoveIntoSquare = true;
                    }

                    if (contents.player != null)
                    {
                        //Attack the player
                        CombatResults result = AttackPlayer(contents.player);

                        if (result == CombatResults.DefenderDied)
                        {
                            //Bad news for the player here!
                            okToMoveIntoSquare = true;
                        }
                    }

                    if (contents.monster != null)
                    {
                        //Attack the monster
                        CombatResults result = AttackMonster(contents.monster);

                        if (result == CombatResults.DefenderDied)
                        {
                            okToMoveIntoSquare = true;
                        }
                    }

                    //Move if allowed
                    if (okToMoveIntoSquare)
                    {
                        LocationMap = newLocation;
                    }
                }
            }
        }