Пример #1
0
 /// <summary>
 /// This uses an overriden location for the creature
 /// </summary>
 /// <param name="creature"></param>
 /// <param name="fov"></param>
 /// <param name="creatureFOVType"></param>
 /// <param name="locationX"></param>
 /// <param name="locationY"></param>
 public CreatureFOV(Creature creature, WrappedFOV fov, CreatureFOVType creatureFOVType, Point overrideLocation)
 {
     this.fov = fov;
     this.type = creatureFOVType;
     this.creature = creature;
     this.overrideLocation = overrideLocation;
 }
Пример #2
0
        internal PathingResult GetPathToCreature(Creature originCreature, Creature destCreature, PathingType type, PathingPermission permission)
        {
            //If on different levels it's an error
            if (originCreature.LocationLevel != destCreature.LocationLevel)
            {
                string msg = originCreature.Representation + " not on the same level as " + destCreature.Representation;
                LogFile.Log.LogEntry(msg);
                throw new ApplicationException(msg);
            }

            return GetPathToPoint(originCreature.LocationLevel, originCreature.LocationMap, destCreature.LocationMap, type, permission);
        }
Пример #3
0
        /// <summary>
        /// Cast a spell. Returns if time passes.
        /// </summary>
        /// <returns></returns>
        private bool SelectAndCastSpell()
        {
            Dungeon dungeon = Game.Dungeon;
            Player player = Game.Dungeon.Player;

            //No casting in town or wilderness
            /*
            if (player.LocationLevel < 2)
            {
                Game.MessageQueue.AddMessage("You want to save your spells for the dungeons.");
                LogFile.Log.LogEntryDebug("Attempted to cast spell outside of dungeon", LogDebugLevel.Low);

                return false;
            }*/

            //Get the user's selection
            Spell toCast = SelectSpell();

            //User exited
            if (toCast == null)
                return false;

            //Get a target if needed

            Point target = new Point();
            bool targettingSuccess = true;

            if (toCast.NeedsTarget())
            {
                //Find spell range
                int range = toCast.GetRange();
                TargettingType targetType = toCast.TargetType();
                //Calculate FOV
                CreatureFOV currentFOV = Game.Dungeon.CalculateCreatureFOV(Game.Dungeon.Player);
                targettingSuccess = TargetAttack(out target, range, targetType, 0, 'z', currentFOV);
            }

            //User exited
            if (!targettingSuccess)
                return false;

            bool success = Game.Dungeon.Player.CastSpell(toCast, target);

            //Store details for a recast

            //If we successfully cast, store the target
            if (success)
            {
                //Only do this for certain spells
                if (toCast.GetType() != typeof(Spells.MagicMissile) && toCast.GetType() != typeof(Spells.FireLance) && toCast.GetType() != typeof(Spells.FireBall) && toCast.GetType() != typeof(Spells.EnergyBlast))
                    return success;

                lastSpell = toCast;

                //Spell target is the creature (monster or PC)

                SquareContents squareContents = dungeon.MapSquareContents(player.LocationLevel, target);

                //Is there a creature here? If so, store
                if (squareContents.monster != null)
                    lastSpellTarget = squareContents.monster;

                if (squareContents.player != null)
                    lastSpellTarget = squareContents.player;
            }
            else
            {
                //Failure store the spell anyway
                lastSpell = toCast;
            }

            //Time only goes past if successfully cast
            return success;
        }
Пример #4
0
        /// <summary>
        /// Returns the direction to go in (+-xy) for the next step towards the target
        /// If there's no route at all, return -1, -1. Right now we throw an exception for this, since it shouldn't happen in a connected dungeon
        /// If there's a route but its blocked by a creature return the originCreature's coords
        /// </summary>
        /// <param name="originCreature"></param>
        /// <param name="destCreature"></param>
        /// <returns></returns>
        internal Point GetPathTo(Creature originCreature, Creature destCreature)
        {
            //If on different levels it's an error
            if (originCreature.LocationLevel != destCreature.LocationLevel)
            {
                string msg = originCreature.Representation + " not on the same level as " + destCreature.Representation;
                LogFile.Log.LogEntry(msg);
                throw new ApplicationException(msg);
            }

            //Destination square needs to be walkable for the path finding algorithm. However it isn't walkable at the moment since there is the target creature on it
            //Temporarily make it walkable, keeping transparency the same
            //levelTCODMaps[destCreature.LocationLevel].SetCell(destCreature.LocationMap.x, destCreature.LocationMap.y,
              //  !levels[destCreature.LocationLevel].mapSquares[destCreature.LocationMap.x, destCreature.LocationMap.y].BlocksLight, true);

            //Try to walk the path
            //If we fail, check if this square occupied by a creature
            //If so, make that square temporarily unwalkable and try to re-route

            List<Point> blockedSquares = new List<Point>();
            bool goodPath = false;
            bool pathBlockedByCreature = false;
            Point nextStep = new Point(-1, -1);

            do
            {
                //Generate path object
                TCODPathFinding path = new TCODPathFinding(levelTCODMaps[originCreature.LocationLevel], 1.0);
                path.ComputePath(originCreature.LocationMap.x, originCreature.LocationMap.y, destCreature.LocationMap.x, destCreature.LocationMap.y);

                //Find the first step. We need to load x and y with the origin of the path
                int x, y;
                int xOrigin, yOrigin;

                path.GetPathOrigin(out x, out y);
                xOrigin = x; yOrigin = y;

                path.WalkPath(ref x, ref y, false);

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

                if (x == xOrigin && y == yOrigin)
                {
                    //If there was no blocking creature then there is no possible route (hopefully impossible in a fully connected dungeon)
                    if (!pathBlockedByCreature)
                    {
                        throw new ApplicationException("Path blocked in connected dungeon!");

                        /*
                        nextStep = new Point(x, y);
                        bool trans;
                        bool walkable;
                        levelTCODMaps[0].GetCell(originCreature.LocationMap.x, originCreature.LocationMap.y, out trans, out walkable);
                        levelTCODMaps[0].GetCell(destCreature.LocationMap.x, destCreature.LocationMap.y, out trans, out walkable);
                        */

                        //Uncomment this if you want to return -1, -1

                        //nextStep = new Point(-1, -1);
                        //goodPath = true;
                        //continue;
                    }
                    else
                    {
                        //Blocking creature but no path
                        nextStep = new Point(x, y);
                        goodPath = true;
                        continue;
                    }
                }

                //Check if that square is occupied
                Creature blockingCreature = null;

                foreach (Monster creature in monsters)
                {
                    if (creature.LocationLevel != originCreature.LocationLevel)
                        continue;

                    //Is it the source creature itself?
                    if (creature == originCreature)
                        continue;

                    //Is it the target creature?
                    if (creature == destCreature)
                        continue;

                    //Another creature is blocking
                    if (creature.LocationMap.x == x && creature.LocationMap.y == y)
                    {
                        blockingCreature = creature;
                    }
                }
                //Do the same for the player (if the creature is chasing another creature around the player)

                if (destCreature != Player)
                {
                    if (Player.LocationLevel == originCreature.LocationLevel &&
                        Player.LocationMap.x == x && Player.LocationMap.y == y)
                    {
                        blockingCreature = Player;
                    }
                }

                //If no blocking creature, the path is good
                if (blockingCreature == null)
                {
                    goodPath = true;
                    nextStep = new Point(x, y);
                    path.Dispose();
                }
                else
                {
                    //Otherwise, there's a blocking creature. Make his square unwalkable temporarily and try to reroute
                    pathBlockedByCreature = true;

                    int blockingLevel = blockingCreature.LocationLevel;
                    int blockingX = blockingCreature.LocationMap.x;
                    int blockingY = blockingCreature.LocationMap.y;

                    levelTCODMaps[blockingLevel].SetCell(blockingX, blockingY, !levels[blockingLevel].mapSquares[blockingX, blockingY].BlocksLight, false);

                    //Add this square to a list of squares to put back
                    blockedSquares.Add(new Point(blockingX, blockingY));

                    //Dispose the old path
                    path.Dispose();

                    //We will try again
                }
            } while (!goodPath);

            //Put back any squares we made unwalkable
            foreach (Point sq in blockedSquares)
            {
                levelTCODMaps[originCreature.LocationLevel].SetCell(sq.x, sq.y, !levels[originCreature.LocationLevel].mapSquares[sq.x, sq.y].BlocksLight, true);
            }

            //path.WalkPath(ref x, ref y, false);

            //path.GetPointOnPath(0, out x, out y); //crashes for some reason

            //Dispose of path (bit wasteful seeming!)
            //path.Dispose();

            //Set the destination square as unwalkable again
            //levelTCODMaps[destCreature.LocationLevel].SetCell(destCreature.LocationMap.x, destCreature.LocationMap.y,
              //  !levels[destCreature.LocationLevel].mapSquares[destCreature.LocationMap.x, destCreature.LocationMap.y].BlocksLight, false);

            //Point nextStep = new Point(x, y);

            return nextStep;
        }
Пример #5
0
        /// <summary>
        /// Calculates the FOV for a creature
        /// </summary>
        /// <param name="creature"></param>
        public TCODFov CalculateCreatureFOV(Creature creature)
        {
            Map currentMap = levels[creature.LocationLevel];
            TCODFov tcodFOV = levelTCODMaps[creature.LocationLevel];

            //Update FOV
            tcodFOV.CalculateFOV(creature.LocationMap.x, creature.LocationMap.y, creature.SightRadius);

            return tcodFOV;
        }
Пример #6
0
 public virtual bool OnDrop(Creature droppingCreature)
 {
     return false;
 }
 public override bool OnPickup(Creature pickupCreature)
 {
     return Use(pickupCreature);
 }
Пример #8
0
        /// <summary>
        /// Add cohort monsters close to a unique. Spawns new copies of the cohort monster type passed in (does not use the passed object)
        /// </summary>
        /// <param name="monsterType"></param>
        /// <param name="noMonsters"></param>
        /// <param name="masterMonser"></param>
        /// <param name="minDistance"></param>
        /// <param name="levelNo"></param>
        /// <returns></returns>
        private bool AddMonstersCloseToMaster(Monster monsterType, int noMonsters, Creature masterMonser, int minDistance, int levelNo)
        {
            Point location;

             int outerLoopCount = 0;

             for (int i = 0; i < noMonsters; i++)
             {
                 do
                 {
                     int loopCount = 0;
                     do
                     {
                         location = Game.Dungeon.RandomWalkablePointInLevel(i);
                         loopCount++;
                     } while (Utility.GetDistanceBetween(masterMonser, location) > minDistance && loopCount < maxLoopCount);
                     outerLoopCount++;
                 } while (!Game.Dungeon.AddMonster(NewMonsterOfType(monsterType), levelNo, location) && outerLoopCount < 50);
             }

             //Failed to add monster
             if (outerLoopCount == 50)
             {
                 LogFile.Log.LogEntryDebug("Failed to place a monster near master", LogDebugLevel.Medium);
                 return false;
             }
             return true;
        }
Пример #9
0
 /// <summary>
 /// We have been hit by creature
 /// </summary>
 /// <param name="creature"></param>
 public abstract void NotifyHitByCreature(Creature creature, int damage);
Пример #10
0
 /// <summary>
 /// On death, null our currentTarget. This is a good idea anyway, but looks like it was added for an important reason 'circular references'
 /// </summary>
 public override void NotifyMonsterDeath()
 {
     currentTarget = null;
     currentTargetID = -1;
 }
Пример #11
0
 /// <summary>
 /// We've been hit. Have a chance of recovering if we are fleeing
 /// </summary>
 /// <param name="creature"></param>
 /// <param name="damage"></param>
 public override void NotifyHitByCreature(Creature creature, int damage)
 {
     RecoverOnBeingHit();
 }
Пример #12
0
 /// <summary>
 /// A creature has attacked us (possibly from out of our view range). Don't just sit there passively
 /// </summary>
 public override void NotifyAttackByCreature(Creature creature)
 {
     AIState = SimpleAIStates.Pursuit;
     currentTarget = creature;
     currentTargetID = creature.UniqueID;
 }
Пример #13
0
        double GetDistance(Creature creature1, Creature creature2)
        {
            double distanceSq = Math.Pow(creature1.LocationMap.x - creature2.LocationMap.x, 2) +
                                    Math.Pow(creature1.LocationMap.y - creature2.LocationMap.y, 2);

            double distance = Math.Sqrt(distanceSq);

            return distance;
        }
Пример #14
0
        private void ChaseCreature(Creature newTarget)
        {
            //Confirm this as current target
            currentTarget = newTarget;
            currentTargetID = newTarget.UniqueID;

            //Go into pursuit mode
            //AIState = SimpleAIStates.Pursuit;

            //If the creature is badly damaged they may flee
            int maxHitPointsWillFlee = GetMaxHPWillFlee();
            int chanceToRecover = GetChanceToRecover(); // out of 100
            int chanceToFlee = GetChanceToFlee(); // out of 100

            //Are we fleeing already
            if (AIState == SimpleAIStates.Fleeing)
            {
                //Do we recover?
                if (Game.Random.Next(100) < chanceToRecover)
                {
                    AIState = SimpleAIStates.Pursuit;
                    LogFile.Log.LogEntryDebug(this.Representation + " recovered", LogDebugLevel.Medium);
                }
            }
            else
            {
                //Only not-charmed creatures will flee

                if (!Charmed)
                {
                    //Check if we want to flee. Only recheck after we've been injured again
                    if (Hitpoints <= maxHitPointsWillFlee && Hitpoints < lastHitpoints)
                    {
                        if (Game.Random.Next(100) < chanceToFlee)
                        {
                            AIState = SimpleAIStates.Fleeing;
                            LogFile.Log.LogEntryDebug(this.Representation + " fleeing", LogDebugLevel.Medium);
                        }
                    }
                }
            }

            lastHitpoints = Hitpoints;

            if (AIState == SimpleAIStates.Fleeing)
            {
                //Flee code, same as ThrowAndRunAI
                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;

                Pathing.PathingResult pathingResult;
                Point nextStep = LocationMap;

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

                do
                {
                    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;

                    //Check these are in the direction away from the attacker
                    int deltaFleeX = fleeX - this.LocationMap.x;
                    int deltaFleeY = fleeY - this.LocationMap.y;

                    if (!relaxDirection)
                    {
                        if (deltaFleeX > 0 && deltaX > 0)
                        {
                            counter++;
                            continue;
                        }
                        if (deltaFleeX < 0 && deltaX < 0)
                        {
                            counter++;
                            continue;
                        }
                        if (deltaFleeY > 0 && deltaY > 0)
                        {
                            counter++;
                            continue;
                        }
                        if (deltaFleeY < 0 && deltaY < 0)
                        {
                            counter++;
                            continue;
                        }
                    }

                    //Check the square is empty
                    bool isEnterable = Game.Dungeon.MapSquareIsWalkable(this.LocationLevel, new Point(fleeX, fleeY));
                    if (!isEnterable)
                    {
                        counter++;
                        continue;
                    }

                    //Check the square is empty of creatures
                    SquareContents contents = Game.Dungeon.MapSquareContents(this.LocationLevel, new Point(fleeX, fleeY));
                    if (contents.monster != null)
                    {
                        counter++;
                        continue;
                    }

                    //Check the square is pathable to
                    Pathing.PathingPermission permission = Pathing.PathingPermission.Normal;

                    if(CanOpenDoors())
                        permission = Pathing.PathingPermission.IgnoreDoors;

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

                    if (pathingResult.TerminallyBlocked)
                    {
                        counter++;
                        continue;
                    }

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

                //If we found a good path, walk it
                if (goodPath)
                {
                    SetHeadingToMapSquare(nextStep);
                    MoveIntoSquare(nextStep);
                }
                else
                {
                    //No good place to flee, attack instead
                    FollowAndAttack(newTarget);
                }

            }
            //Not fleeing
            else
            {
                //If charmed creatures get too far away chasing they will come back
                if (Charmed)
                {
                    //Calculate distance between PC and creature
                    if (Game.Dungeon.Player.LocationLevel == this.LocationLevel)
                    {

                        double distanceSq = Math.Pow(Game.Dungeon.Player.LocationMap.x - this.LocationMap.x, 2) +
                                    Math.Pow(Game.Dungeon.Player.LocationMap.y - this.LocationMap.y, 2);
                        double distance = Math.Sqrt(distanceSq);

                        if (distance > maxChaseDistance)
                        {
                            LogFile.Log.LogEntryDebug(this.SingleDescription + " returns to PC", LogDebugLevel.Low);
                            AIState = SimpleAIStates.Returning;
                            FollowPC();
                        }
                    }
                }

                //Not charmed - pursue and attack
                FollowAndAttack(newTarget);
            }
        }
Пример #15
0
 /// <summary>
 /// Call after updating LocationMap. Aim at the target
 /// </summary>
 protected void SetHeadingToTarget(Creature newTarget)
 {
     Heading = DirectionUtil.AngleFromOriginToTarget(this.LocationMap, newTarget.LocationMap);
 }
Пример #16
0
        /// <summary>
        /// Fire weapon. Returns if time passes.
        /// </summary>
        /// <returns></returns>
        private bool FireWeapon()
        {
            Dungeon dungeon = Game.Dungeon;
            Player player = Game.Dungeon.Player;

            //Check we have a fireable weapon
            IEquippableItem weapon = player.GetEquippedWeapon();
            Item weaponI = player.GetEquippedWeaponAsItem();

            if (weapon == null || !weapon.HasFireAction())
            {
                Game.MessageQueue.AddMessage("Need a weapon that can fire.");
                return false;
            }

            Point target = new Point();
            bool targettingSuccess = true;

            //Find weapon range
            int range = weapon.RangeFire();
            TargettingType targetType = weapon.TargetTypeFire();
            double spreadAngle = weapon.ShotgunSpreadAngle();

            //Calculate FOV
            CreatureFOV currentFOV = Game.Dungeon.CalculateCreatureFOV(Game.Dungeon.Player);

            targettingSuccess = TargetAttack(out target, range, targetType, spreadAngle, 'f', currentFOV);

            //User exited
            if (!targettingSuccess)
                return false;

            if (target.x == player.LocationMap.x && target.y == player.LocationMap.y)
            {
                Game.MessageQueue.AddMessage("Can't target self with " + weaponI.SingleItemDescription + ".");
                LogFile.Log.LogEntryDebug("Can't target self with " + weaponI.SingleItemDescription, LogDebugLevel.Medium);
                return false;
            }

            //Check ammo
            if (weapon.RemainingAmmo() < 1)
            {
                Game.MessageQueue.AddMessage("Not enough ammo for " + weaponI.SingleItemDescription);
                LogFile.Log.LogEntryDebug("Not enough ammo for " + weaponI.SingleItemDescription, LogDebugLevel.Medium);

                return false;
            }

            //Check we are in range of target (not done above)
            if (!Utility.TestRangeFOVForWeapon(Game.Dungeon.Player, target, range, currentFOV))
            {
                Game.MessageQueue.AddMessage("Out of range!");
                LogFile.Log.LogEntryDebug("Out of range for " + weaponI.SingleItemDescription, LogDebugLevel.Medium);

                return false;
            }

            //Actually do firing action
            bool success = weapon.FireItem(target);

            if (success)
            {
                RemoveEffectsDueToFiringWeapon(player);
            }

            //Store details for a recast

            //If we successfully cast, store the target
            if (success)
            {
                //Spell target is the creature (monster or PC)

                SquareContents squareContents = dungeon.MapSquareContents(player.LocationLevel, target);

                //Is there a creature here? If so, store
                if (squareContents.monster != null)
                    lastSpellTarget = squareContents.monster;

                if (squareContents.player != null)
                    lastSpellTarget = squareContents.player;
            }

            //Time only goes past if successfully cast
            return success;
        }
Пример #17
0
 /// <summary>
 /// We have be attacked (but not necessarily damaged by) creature
 /// </summary>
 /// <param name="creature"></param>
 public abstract void NotifyAttackByCreature(Creature creature);
Пример #18
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;
        }
Пример #19
0
        protected virtual int AttackCreatureWithModifiers(Creature creature, int hitMod, int damBase, int damMod, int ACmod)
        {
            //Just do damage base
            return damageBase;

            /*
            int attackToHit = hitModifier + hitMod;
            int attackDamageMod = damageModifier + damMod;

            int attackDamageBase;

            if (damBase > damageBase)
                attackDamageBase = damBase;
            else
                attackDamageBase = damageBase;

            int targetAC = creature.ArmourClass() + ACmod;
            toHitRoll = Utility.d20() + attackToHit;

            if (toHitRoll >= targetAC)
            {
                //Hit - calculate damage
                int totalDamage = Utility.DamageRoll(attackDamageBase) + attackDamageMod;

                return totalDamage;
            }

            //Miss
            return 0;*/
        }
Пример #20
0
        public MonsterFightAndRunAI()
        {
            AIState = SimpleAIStates.Patrol;
            currentTarget = null;

            CurrentSoundID = -1;

            lastHitpoints = MaxHitpoints;
        }
Пример #21
0
        protected virtual void FollowAndAttack(Creature newTarget)
        {
            //Find location of next step on the path towards target

            Pathing.PathingResult pathingResult;
            Pathing.PathingPermission permission = Pathing.PathingPermission.Normal;

            if(CanOpenDoors())
                permission = Pathing.PathingPermission.IgnoreDoors;

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

            Point nextStep = pathingResult.MonsterFinalLocation;

            //We are adjacent and can attack
            if (pathingResult.MoveIsInteractionWithTarget)
            {
                //If we can attack, attack the monster or PC
                if(WillAttack()) {

                    CombatResults result;

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

                    Screen.Instance.DrawMeleeAttack(this, newTarget, result);

                    //If we killed it, move into its square
                    if (result == CombatResults.DefenderDied &&
                        !(newTarget == Game.Dungeon.Player && Game.Dungeon.PlayerImmortal))
                    {
                        nextStep = currentTarget.LocationMap;
                    }
                }
            }

            //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;

            }

            //Otherwise (or if the creature died), move towards it (or its corpse)
            if(WillPursue()) {

                //If we want to pursue, move towards the creature
                if (CanMove())
                {
                    MoveIntoSquare(nextStep);
                    SetHeadingToTarget(newTarget);
                }
            }
            else {
                //If we don't we continue our normal Patrol route
                //(we are set to Pursuit in the AI though)
                DoPatrol();
            }
        }
Пример #22
0
 /// <summary>
 /// Called every click. If the event duration is over, call OnEnd() and mark as ended
 /// </summary>
 public abstract void IncrementTime(Creature target);
 /// <summary>
 /// Applies the effect of the object
 /// </summary>
 /// <param name="user"></param>
 /// <returns>True if the object could be used</returns>
 public abstract bool Use(Creature user);
Пример #24
0
 /// <summary>
 /// Carries out the end effects on the target
 /// </summary>
 public abstract void OnEnd(Creature target);
Пример #25
0
 public virtual bool OnPickup(Creature pickupCreature)
 {
     return false;
 }
Пример #26
0
 /// <summary>
 /// Carries out the start effects on the target.
 /// </summary>
 public abstract void OnStart(Creature target);
Пример #27
0
        /// <summary>
        /// Displays the creature FOV on the map. Note that this clobbers the FOV map
        /// </summary>
        /// <param name="creature"></param>
        public void ShowCreatureFOVOnMap(Creature creature)
        {
            //Only do this if the creature is on a visible level
            if(creature.LocationLevel != Player.LocationLevel)
                return;

            Map currentMap = levels[creature.LocationLevel];
            TCODFov tcodFOV = levelTCODMaps[creature.LocationLevel];

            //Calculate FOV
            tcodFOV.CalculateFOV(creature.LocationMap.x, creature.LocationMap.y, creature.SightRadius);

            //Only check sightRadius around the creature

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

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

            //If sight is infinite, check all the map
            if (creature.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;

            for (int i = xl; i <= xr; i++)
            {
                for (int j = yt; j <= yb; j++)
                {
                    MapSquare thisSquare = currentMap.mapSquares[i, j];
                    bool inFOV = tcodFOV.CheckTileFOV(i, j);
                    if(inFOV)
                        thisSquare.InMonsterFOV = true;
                }
            }
        }
Пример #28
0
 public CreatureFOV(Creature creature, WrappedFOV fov, CreatureFOVType creatureFOVType)
 {
     this.fov = fov;
     this.type = creatureFOVType;
     this.creature = creature;
 }
Пример #29
0
 public MonsterSimpleAI()
 {
     AIState = SimpleAIStates.Patrol;
     currentTarget = null;
 }
Пример #30
0
        private bool RecastSpellCastAtCreature(Spell spell, Creature target)
        {
            //Convert the stored Creature last target into a square
            Point spellTargetSq = new Point(target.LocationMap.x, target.LocationMap.y);

            return Game.Dungeon.Player.CastSpell(spell, spellTargetSq);
        }