private void SetSoundToFollow(SoundEffect sound, double interest)
        {
            CurrentSoundID = sound.ID;
            CurrentSoundInterestScore = interest;
            currentSound = sound;

            BlockedOnSoundTurns = 0;
        }
        /// <summary>
        /// Main loop called on each turn when we move
        /// </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]
            //TODO: add forget mode?

            Random rand = Game.Random;

            Point startOfTurnLocation = LocationMap;

            //RESTORE STATE AFTER SAVE
            //Creature references may be circular, and will crash serialization, so an index is used instead

            //Restore currentTarget reference from ID, in case we have reloaded
            if (currentTargetID == -1)
            {
                currentTarget = null;
            }
            else
            {
                currentTarget = Game.Dungeon.GetCreatureByUniqueID(currentTargetID);
            }

            //Restore lastAttackedByFromID
            if (LastAttackedByID == -1)
            {
                LastAttackedBy = null;
            }
            else
            {
                LastAttackedBy = Game.Dungeon.GetCreatureByUniqueID(LastAttackedByID);
            }

            //Restore sound
            if (CurrentSoundID == -1)
            {
                currentSound = null;
            }
            else
            {
                currentSound = Game.Dungeon.GetSoundByID(CurrentSoundID);

                if (currentSound == null)
                {
                    ResetFollowingSound();
                    AIState = SimpleAIStates.Patrol;
                    LogFile.Log.LogEntryDebug("Error restoring sound, resetting", LogDebugLevel.High);
                }
            }

            //Stunned creatures miss turns
            if (StunnedTurns > 0)
            {
                StunnedTurns--;
                LogFile.Log.LogEntryDebug(this.Representation + " is stunned for " + StunnedTurns + " more turns", LogDebugLevel.Low);

                ResetTurnsMoving();
                return;
            }

            //TEST SLEEPING CREATURES
            //Sleeping is a Creature state that is used like an AI state
            //This is OK since we exit immediately

            //Creatures which sleep until seen (i.e. for ease of processing, not game effects)
            if (Sleeping && WakesOnBeingSeen())
            {
                //Check to see if we should wake by looking for woken creatures in POV
                //(when we drop through currentFOV may be unnecessarily recalculated)

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

                //Player sees monster, wake up
                if (currentFOV.CheckTileFOV(LocationMap.x, LocationMap.y))
                {
                    Sleeping = false;
                    AIState = SimpleAIStates.Patrol;
                    LogFile.Log.LogEntryDebug(this.Representation + " spotted by player so wakes", LogDebugLevel.Low);
                }
            }

            //Sleeping creatures don't react until they see a woken creature
            if (Sleeping && WakesOnSight())
            {
                //Check to see if we should wake by looking for woken creatures in POV
                //(when we drop through currentFOV may be unnecessarily recalculated)

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

                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 in FOV
                    //Check if it's awake. If so, wake up and stop
                    if (!monster.Sleeping)
                    {
                        Sleeping = false;
                        AIState = SimpleAIStates.Patrol;
                        LogFile.Log.LogEntryDebug(this.Representation + " spots awake " + monster.Representation + " and wakes", LogDebugLevel.Low);
                        break;
                    }
                }

                //Check if we can see the player
                if (Game.Dungeon.Player.LocationLevel == this.LocationLevel &&
                    currentFOV.CheckTileFOV(Game.Dungeon.Player.LocationMap.x, Game.Dungeon.Player.LocationMap.y) && !Game.Dungeon.Player.isStealthed())
                {
                    //In FOV wake
                    Sleeping = false;
                    AIState = SimpleAIStates.Patrol;
                    LogFile.Log.LogEntryDebug(this.Representation + " spots player and wakes", LogDebugLevel.Low);
                }
            }

            //If we're still sleeping then skip this go
            if (Sleeping)
            {
                ResetTurnsMoving();
                return;
            }
            //RETURNING - used when a charmed creature gets a long way from the PC

            if (AIState == SimpleAIStates.Returning)
            {
                //Don't stop on an attack otherwise charmed creatures will be frozen in front of missile troops

                //We have been attacked by someone new
                //if (LastAttackedBy != null && LastAttackedBy.Alive)
                //{
                    //Reset the AI, will drop through and chase the nearest target
                //    AIState = SimpleAIStates.RandomWalk;
                //}
                //else {

                    //Are we close enough to the PC?
                    double distance = GetDistance(this, Game.Dungeon.Player);

                    if (distance <= recoverDistance)
                    {
                        //Reset AI and fall through
                        AIState = SimpleAIStates.Patrol;
                        LogFile.Log.LogEntryDebug(this.Representation + " close enough to PC", LogDebugLevel.Low);
                    }

                    //Otherwise follow the PC back
                    FollowPC();
                //}
            }

            //PURSUIT MODES - Pursuit [active] and Fleeing [temporarily fleeing, will return to target]

            if (AIState == SimpleAIStates.Fleeing || AIState == SimpleAIStates.Pursuit)
            {
                Monster targetMonster = currentTarget as Monster;

                //Fleeing
                //Check we have a valid target (may not after reload)
                //still required?
                if (currentTarget == null)
                {
                    AIState = SimpleAIStates.Patrol;
                }

                //Is target yet living?
                else if (currentTarget.Alive == false)
                {
                    //If not, go to non-chase state
                    AIState = SimpleAIStates.Patrol;
                }
                //Charmed creatures should not attack other charmed creatures
                else if (Charmed && targetMonster != null && targetMonster.Charmed)
                {
                    //Go to non-chase state
                    AIState = SimpleAIStates.Patrol;
                }
                //Is target on another level (i.e. has escaped down the stairs)
                else if (currentTarget.LocationLevel != this.LocationLevel)
                {
                    AIState = SimpleAIStates.Patrol;
                }
                //Have we just become charmed? Reset AI (stop chasing player)
                else if (currentTarget == Game.Dungeon.Player && Charmed)
                {
                    AIState = SimpleAIStates.Patrol;
                }
                //Have we just become passive? Reset AI (stop chasing player)
                else if (currentTarget == Game.Dungeon.Player && Passive)
                {
                    AIState = SimpleAIStates.Patrol;
                }
                //Has the player stealthed?
                else if (currentTarget == Game.Dungeon.Player && Game.Dungeon.Player.isStealthed())
                {
                    LogFile.Log.LogEntryDebug(this.Representation + " stop chasing. Player went stealthed", LogDebugLevel.Medium);
                    AIState = SimpleAIStates.Patrol;
                }
                //Have we just been attacked by a new enemy?
                else if (LastAttackedBy != null && LastAttackedBy.Alive && LastAttackedBy != currentTarget)
                {
                    //Reset the AI for now
                    AIState = SimpleAIStates.Patrol;
                }
                else
                {
                    //Otherwise continue to pursue or flee
                    ChaseCreature(currentTarget);
                }
            }

            //PATROL STATE OR INVESTIGATE STATE

            //Check states which override patrol or investigate (e.g being attacked, charmed, seeing the PC)

            if(AIState == SimpleAIStates.Patrol || AIState == SimpleAIStates.InvestigateSound) {

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

                //AI branches here depending on if we are charmed or passive

                if (this.Charmed)
                {
                    //Charmed - will fight for the PC
                    //Won't attack passive creatures (otherwise will de-passify them and it would be annoying)

                    //Look for creatures in FOV
                    CreatureFOV currentFOV = Game.Dungeon.CalculateCreatureFOV(this);

                    //List will contain monsters & player
                    List<Monster> monstersInFOV = new List<Monster>();

                    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
                        monstersInFOV.Add(monster);

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

                    //Look for creatures which aren't passive or charmed
                    List<Monster> notPassiveTargets = monstersInFOV.FindAll(x => !x.Passive);
                    List<Monster> notCharmedOrPassiveTargets = notPassiveTargets.FindAll(x => !x.Charmed);

                    //Go chase a not-passive, not-charmed creature
                    if (notCharmedOrPassiveTargets.Count > 0)
                    {
                        //Find the closest creature
                        Monster closestCreature = null;
                        double closestDistance = Double.MaxValue; //a long way

                        foreach (Monster creature in notCharmedOrPassiveTargets)
                        {
                            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 + " charm chases " + closestCreature.Representation, LogDebugLevel.Low);
                        AIState = SimpleAIStates.Pursuit;
                        ChaseCreature(closestCreature);
                    }
                    else
                    {
                        //No creature to chase, go find PC
                        FollowPC();
                    }

                }
                else if(!Passive)
                {
                    //Normal fighting behaviour

                    //Optional: check next move and open any doors if possible. This gives us a chance to shoot lurking PCs
                    //Removed this while closing doors is not possible - avoids repeated abuse

                    /*
                    if (CanOpenDoors() && (AIState == SimpleAIStates.Patrol || AIState == SimpleAIStates.InvestigateSound))
                    {

                        //Very simple version, monsters open any doors they are facing
                        List<Point> directedAhead = DirectionUtil.SurroundingPointsFromDirection(Heading, LocationMap, 3);

                        foreach (Point p in directedAhead)
                        {
                            MapTerrain doorTerrain = Game.Dungeon.GetTerrainAtPoint(this.LocationLevel, p);

                            if (doorTerrain == MapTerrain.ClosedDoor)
                            {
                                LogFile.Log.LogEntryDebug(this.Representation + " : door detected ahead, opening", LogDebugLevel.Medium);
                                Game.Dungeon.OpenDoor(this.LocationLevel, p);
                            }
                        }
                    }*/

                    //Find creatures & PC in FOV
                    CreatureFOV currentFOV = Game.Dungeon.CalculateCreatureFOV(this);

                    List<Creature> monstersInFOV = new List<Creature>();

                    foreach (Creature 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
                        monstersInFOV.Add(monster);

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

                    if (Game.Dungeon.Player.LocationLevel == this.LocationLevel)
                    {
                        if (currentFOV.CheckTileFOV(Game.Dungeon.Player.LocationMap.x, Game.Dungeon.Player.LocationMap.y) && !Game.Dungeon.Player.isStealthed())
                        {
                            monstersInFOV.Add(Game.Dungeon.Player);
                            LogFile.Log.LogEntryDebug(this.Representation + " spots " + Game.Dungeon.Player.Representation, LogDebugLevel.Low);
                        }
                    }

                    //Have we just been attacked by a new enemy? If so, respond to them
                    if (LastAttackedBy != null && LastAttackedBy.Alive && LastAttackedBy != currentTarget)
                    {
                        //Is this target within FOV? If so, attack it
                        if (monstersInFOV.Contains(LastAttackedBy))
                        {

                            LogFile.Log.LogEntryDebug(this.Representation + " changes target to " + LastAttackedBy.Representation, LogDebugLevel.Medium);
                            AIState = SimpleAIStates.Pursuit;
                            ChaseCreature(LastAttackedBy);
                        }
                        else
                        {
                            //Continue chasing whoever it was we were chasing last
                            if (currentTarget != null)
                            {
                                AIState = SimpleAIStates.Pursuit;
                                ChaseCreature(currentTarget);
                            }
                        }
                    }

                    //Check if we can see the PC and pursue them

                    //If we are not currently pursuing anything and we see the PC, pursue if seen
                    //Technically, go into pursuit mode, which may not involve actual movement
                    if ((AIState == SimpleAIStates.Patrol || AIState == SimpleAIStates.InvestigateSound) && monstersInFOV.Contains(Game.Dungeon.Player) && !Game.Dungeon.Player.isStealthed())
                    {
                        Creature closestCreature = Game.Dungeon.Player;
                        //Start chasing this creature
                        LogFile.Log.LogEntryDebug(this.Representation + " chases " + closestCreature.Representation, LogDebugLevel.Medium);
                        AIState = SimpleAIStates.Pursuit;
                        ChaseCreature(closestCreature);
                    }
                }

                //This is so we don't have to instrument each state
                if (LocationMap == startOfTurnLocation)
                {
                    ResetTurnsMoving();
                    AddTurnsInactive();
                }
                else
                {
                    ResetTurnsInactive();
                    AddTurnsMoving();
                }
            }

            //INVESTIGATE SOUNDS

            //If a new sound has happened, calculate interest
            //If high interest, make this our target
            //Continue to investigate the sound
            //If we've reached the target, return to Patrol [sightings are handled above]

            //Monster that don't pursue still have a chance to direct their FOVs at sounds

            bool moveFollowingSound = false;

            if ((AIState == SimpleAIStates.Patrol || AIState == SimpleAIStates.InvestigateSound) && WillInvestigateSounds() )
            {
                double currentSoundInterest;

                if (CurrentSoundID == -1)
                {
                    currentSoundInterest = 0.0;
                }
                else {
                    //Interest in the last interesting sound will decay over time
                    currentSoundInterest = currentSound.DecayedInterest(CurrentSoundInterestScore, Game.Dungeon.WorldClock);
                }

                //Get sounds which have happened since we last looked (and update)
                //TODO: reset this when we leave a pursuit state - could look at very old sounds then??

                List<SoundEffect> newSounds = Game.Dungeon.GetSoundsAfterTime(LastCheckedSounds);
                LastCheckedSounds = Game.Dungeon.WorldClock;

                SoundEffect newSoundToFollow = null;
                int newSoundToFollowID = -1;
                double newSoundInterest = currentSoundInterest;

                foreach(SoundEffect soundEvent in newSounds) {
                    SoundEffect sEffect = soundEvent;

                    double newSoundScore = sEffect.DecayedMagnitude(this.LocationLevel, this.LocationMap);

                    if (newSoundScore > newSoundInterest)
                    {
                        newSoundToFollowID = sEffect.ID;
                        newSoundToFollow = sEffect;
                        newSoundInterest = newSoundScore;
                    }
                }

                //Have we found a new more interesting sound?
                //If so, follow it
                if (newSoundToFollowID != -1)
                {
                    LogFile.Log.LogEntryDebug(this.Representation + " new sound target: " + newSoundToFollow + "[ int: " + newSoundInterest + "] (old: " + currentSound + " [ int: " + currentSoundInterest + "])", LogDebugLevel.Medium);

                    //Change sound

                    //A sound we choose gets a boost in interest to give us a bit of hystersis
                     SetSoundToFollow(newSoundToFollow, newSoundInterest * 1.5);

                    AIState = SimpleAIStates.InvestigateSound;
                }
                else
                {
                    if (currentSoundInterest < 0.01)
                    {
                        //Sound has decayed so much it's not interesting, or we never had an interesting sound
                        ResetFollowingSound();
                        if(AIState == SimpleAIStates.InvestigateSound)
                            LogFile.Log.LogEntryDebug(this.Representation + " sound " + currentSound + " is old, resetting", LogDebugLevel.Low);
                        AIState = SimpleAIStates.Patrol;

                    }
                }

                //For a new or existing sound, pursue it
                if (AIState == SimpleAIStates.InvestigateSound)
                {
                    moveFollowingSound = InvestigateSound();
                }
            }

            //If nothing else happened, do the Patrol action
            //Don't if we moved in response to a sound
            if ((AIState == SimpleAIStates.Patrol && !moveFollowingSound) || (WillAlwaysPatrol() && !headingSetToSound))
            {
                //We haven't got anything to do and we can't see the PC
                //Do normal movement
                DoPatrol();
            }

            //Reset the skip-patrol if we looked at sound flag
            headingSetToSound = false;
        }
 /// <summary>
 /// Reset sound state. Doesn't change AI
 /// </summary>
 private void ResetFollowingSound()
 {
     CurrentSoundID = -1;
     currentSound = null;
     CurrentSoundInterestScore = 0.0;
 }