protected override ActorAction SelectAction(RogueGame game, List <Percept> percepts)
        {
            List <Percept> mapPercepts = FilterSameMap(game, percepts);

            ///////////////////////
            // 1. Follow order
            // 2. Normal behavior.
            ///////////////////////

            // 1. Follow order
            #region
            if (this.Order != null)
            {
                ActorAction orderAction = ExecuteOrder(game, this.Order, mapPercepts);
                if (orderAction == null)
                {
                    SetOrder(null);
                }
                else
                {
                    m_Actor.Activity = Activity.FOLLOWING_ORDER;
                    return(orderAction);
                }
            }
            #endregion

            // 2. Normal behavior.
            #region
            //////////////////////////////////////////////////////////////////////
            // BEHAVIOR
            // - FLAGS
            // "courageous" : has leader, see leader, he is fighting and actor not tired.
            // - RULES
            // 0 run away from primed explosives.
            // 1 throw grenades at enemies.
            // 2 equip weapon/armor
            // 3 fire at nearest (always if has leader, half of the time if not)  - check directives
            // 4 fight or flee, shout
            // 5 use medicine
            // 6 rest if tired
            // 7 charge enemy if courageous
            // 8 eat when hungry (also eat corpses)
            // 9 sleep when almost sleepy and safe.
            // 10 drop light/tracker with no batteries
            // 11 equip light/tracker/scent spray
            // 12 make room for food items if needed.
            // 13 get nearby item/trade (not if seeing enemy) - check directives.
            // 14 if hungry and no food, charge at people for food (option, not follower or law enforcer)
            // 15 use stench killer.
            // 16 close door behind me.
            // 17 use entertainment
            // 18 follow leader.
            // 19 take lead (if leadership)
            // 20 if hungry, tear down barricades & push objects.
            // 21 go revive corpse.
            // 22 use exit.
            // 23 build trap or fortification.
            // 24 tell friend about latest raid.
            // 25 tell friend about latest friendly soldier.
            // 26 tell friend about latest enemy.
            // 27 tell friend about latest items.
            // 28 (law enforcer) watch for murderers.
            // 29 (leader) don't leave followers behind.
            // 30 explore.
            // 31 wander.
            //////////////////////////////////////////////////////////////////////

            // don't run by default.
            m_Actor.IsRunning = false;

            // get data.
            List <Percept> enemies          = FilterEnemies(game, mapPercepts);
            bool           hasEnemies       = enemies != null && enemies.Count > 0;
            bool           checkOurLeader   = m_Actor.HasLeader && !DontFollowLeader;
            bool           seeLeader        = checkOurLeader && m_LOSSensor.FOV.Contains(m_Actor.Leader.Location.Position);
            bool           isLeaderFighting = checkOurLeader && IsAdjacentToEnemy(game, m_Actor.Leader);
            bool           isCourageous     = checkOurLeader && seeLeader && isLeaderFighting && !game.Rules.IsActorTired(m_Actor);

            // safety counter.
            if (hasEnemies)
            {
                m_SafeTurns = 0;
            }
            else
            {
                ++m_SafeTurns;
            }

            // exploration.
            m_Exploration.Update(m_Actor.Location);

            // clear taboo tiles : periodically or when changing maps.
            if (m_Actor.Location.Map.LocalTime.TurnCounter % WorldTime.TURNS_PER_HOUR == 0 ||
                (PrevLocation != null && PrevLocation.Map != m_Actor.Location.Map))
            {
                ClearTabooTiles();
            }
            // clear trades.
            if (m_Actor.Location.Map.LocalTime.TurnCounter % WorldTime.TURNS_PER_DAY == 0)
            {
                ClearTabooTrades();
            }

            // last enemy saw.
            if (hasEnemies)
            {
                m_LastEnemySaw = enemies[game.Rules.Roll(0, enemies.Count)];
            }

            // 0 run away from primed explosives.
            #region
            ActorAction runFromExplosives = BehaviorFleeFromExplosives(game, FilterStacks(game, mapPercepts));
            if (runFromExplosives != null)
            {
                m_Actor.Activity = Activity.FLEEING_FROM_EXPLOSIVE;
                return(runFromExplosives);
            }
            #endregion

            // 1 throw grenades at enemies.
            #region
            // if directive off, unequip.
            if (!this.Directives.CanThrowGrenades)
            {
                // unequip grenade?
                ItemGrenade eqGrenade = m_Actor.GetEquippedWeapon() as ItemGrenade;
                if (eqGrenade != null)
                {
                    ActionUnequipItem unequipGre = new ActionUnequipItem(m_Actor, game, eqGrenade);
                    if (unequipGre.IsLegal())
                    {
                        m_Actor.Activity = Activity.IDLE;
                        return(unequipGre);
                    }
                }
            }
            // throw?
            if (hasEnemies)
            {
                ActorAction throwAction = BehaviorThrowGrenade(game, m_LOSSensor.FOV, enemies);
                if (throwAction != null)
                {
                    return(throwAction);
                }
            }
            #endregion

            // 2 equip weapon/armor
            #region
            ActorAction equipWpnAction = BehaviorEquipWeapon(game);
            if (equipWpnAction != null)
            {
                m_Actor.Activity = Activity.IDLE;
                return(equipWpnAction);
            }
            ActorAction equipArmAction = BehaviorEquipBodyArmor(game);
            if (equipArmAction != null)
            {
                m_Actor.Activity = Activity.IDLE;
                return(equipArmAction);
            }
            #endregion

            // 3 fire at nearest enemy
            #region
            if (hasEnemies && this.Directives.CanFireWeapons && m_Actor.GetEquippedWeapon() is ItemRangedWeapon)
            {
                List <Percept> fireTargets = FilterFireTargets(game, enemies);
                if (fireTargets != null)
                {
                    Percept nearestTarget = FilterNearest(game, fireTargets);
                    Actor   target        = nearestTarget.Percepted as Actor;

                    // flee contact from someone SLOWER with no ranged weapon.
                    if (game.Rules.GridDistance(nearestTarget.Location.Position, m_Actor.Location.Position) == 1 &&
                        !HasEquipedRangedWeapon(target) &&
                        HasSpeedAdvantage(game, m_Actor, target))
                    {
                        // flee!
                        ActorAction fleeAction = BehaviorWalkAwayFrom(game, nearestTarget);
                        if (fleeAction != null)
                        {
                            RunIfPossible(game.Rules);
                            m_Actor.Activity = Activity.FLEEING;
                            return(fleeAction);
                        }
                    }

                    // fire ze missiles!
                    ActorAction fireAction = BehaviorRangedAttack(game, nearestTarget);
                    if (fireAction != null)
                    {
                        m_Actor.Activity    = Activity.FIGHTING;
                        m_Actor.TargetActor = target;
                        return(fireAction);
                    }
                }
            }
            #endregion

            // 4 fight or flee, shout
            #region
            if (hasEnemies)
            {
                // shout?
                if (game.Rules.RollChance(50))
                {
                    List <Percept> friends = FilterNonEnemies(game, mapPercepts);
                    if (friends != null)
                    {
                        ActorAction shoutAction = BehaviorWarnFriends(game, friends, FilterNearest(game, enemies).Percepted as Actor);
                        if (shoutAction != null)
                        {
                            m_Actor.Activity = Activity.IDLE;
                            return(shoutAction);
                        }
                    }
                }
                // fight or flee.
                ActorAction fightOrFlee = BehaviorFightOrFlee(game, enemies, seeLeader, isLeaderFighting, Directives.Courage, m_Emotes);
                if (fightOrFlee != null)
                {
                    return(fightOrFlee);
                }
            }
            #endregion

            // 5 use medicine
            #region
            ActorAction useMedAction = BehaviorUseMedecine(game, 2, 1, 2, 4, 2);
            if (useMedAction != null)
            {
                m_Actor.Activity = Activity.IDLE;
                return(useMedAction);
            }
            #endregion

            // 6 rest if tired
            #region
            ActorAction restAction = BehaviorRestIfTired(game);
            if (restAction != null)
            {
                m_Actor.Activity = Activity.IDLE;
                return(new ActionWait(m_Actor, game));
            }
            #endregion

            // 7 charge enemy if courageous
            #region
            if (hasEnemies && isCourageous)
            {
                Percept     nearestEnemy = FilterNearest(game, enemies);
                ActorAction chargeAction = BehaviorChargeEnemy(game, nearestEnemy);
                if (chargeAction != null)
                {
                    m_Actor.Activity    = Activity.FIGHTING;
                    m_Actor.TargetActor = nearestEnemy.Percepted as Actor;
                    return(chargeAction);
                }
            }
            #endregion

            // 8 eat when hungry (also eat corpses)
            #region
            if (game.Rules.IsActorHungry(m_Actor))
            {
                ActorAction eatAction = BehaviorEat(game);
                if (eatAction != null)
                {
                    m_Actor.Activity = Activity.IDLE;
                    return(eatAction);
                }
                if (game.Rules.IsActorStarving(m_Actor) || game.Rules.IsActorInsane(m_Actor))
                {
                    eatAction = BehaviorGoEatCorpse(game, FilterCorpses(game, mapPercepts));
                    if (eatAction != null)
                    {
                        m_Actor.Activity = Activity.IDLE;
                        return(eatAction);
                    }
                }
            }
            #endregion

            // 9 sleep when almost sleepy and safe.
            #region
            if (m_SafeTurns >= MIN_TURNS_SAFE_TO_SLEEP && this.Directives.CanSleep &&
                WouldLikeToSleep(game, m_Actor) && IsInside(m_Actor) && game.Rules.CanActorSleep(m_Actor))
            {
                // secure sleep.
                ActorAction secureSleepAction = BehaviorSecurePerimeter(game, m_LOSSensor.FOV);
                if (secureSleepAction != null)
                {
                    m_Actor.Activity = Activity.IDLE;
                    return(secureSleepAction);
                }

                // sleep.
                ActorAction sleepAction = BehaviorSleep(game, m_LOSSensor.FOV);
                if (sleepAction != null)
                {
                    if (sleepAction is ActionSleep)
                    {
                        m_Actor.Activity = Activity.SLEEPING;
                    }
                    return(sleepAction);
                }
            }
            #endregion

            // 10 drop useless light/tracker/spray
            #region
            ActorAction dropUseless = BehaviorDropUselessItem(game);
            if (dropUseless != null)
            {
                m_Actor.Activity = Activity.IDLE;
                return(dropUseless);
            }
            #endregion

            // 11 equip light/tracker/spray.
            #region
            // tracker : if has leader or is a leader.
            bool needCellPhone = m_Actor.HasLeader || m_Actor.CountFollowers > 0;
            // then light.
            bool needLight = NeedsLight(game);
            // finally spray.
            bool needSpray = IsGoodStenchKillerSpot(game, m_Actor.Location.Map, m_Actor.Location.Position);
            // if tracker/light/spray useless, unequip it.
            if (!needCellPhone && !needLight && !needSpray)
            {
                ActorAction unequipUselessLeftItem = BehaviorUnequipLeftItem(game);
                if (unequipUselessLeftItem != null)
                {
                    m_Actor.Activity = Activity.IDLE;
                    return(unequipUselessLeftItem);
                }
            }
            // tracker?
            if (needCellPhone)
            {
                ActorAction eqTrackerAction = BehaviorEquipCellPhone(game);
                if (eqTrackerAction != null)
                {
                    m_Actor.Activity = Activity.IDLE;
                    return(eqTrackerAction);
                }
            }
            // ...or light?
            else if (needLight)
            {
                ActorAction eqLightAction = BehaviorEquipLight(game);
                if (eqLightAction != null)
                {
                    m_Actor.Activity = Activity.IDLE;
                    return(eqLightAction);
                }
            }
            // ... scent spray?
            else if (needSpray)
            {
                ActorAction eqScentSpray = BehaviorEquipStenchKiller(game);
                if (eqScentSpray != null)
                {
                    m_Actor.Activity = Activity.IDLE;
                    return(eqScentSpray);
                }
            }
            #endregion

            // 12 make room for food items if needed.
            // &&
            // 13 get nearby item/trade (not if seeing enemy)
            // ignore not currently visible items & blocked items.
            #region
            if (!hasEnemies && this.Directives.CanTakeItems)
            {
                Map map = m_Actor.Location.Map;

                #region Get nearby item
                // list visible items.
                List <Percept> interestingStacks = FilterOut(game, FilterStacks(game, mapPercepts),
                                                             (p) => (p.Turn != map.LocalTime.TurnCounter) ||
                                                             IsOccupiedByOther(map, p.Location.Position) ||
                                                             IsTileTaboo(p.Location.Position) ||
                                                             !HasAnyInterestingItem(game, p.Percepted as Inventory));

                // if some items...
                if (interestingStacks != null)
                {
                    // update last percept saw.
                    Percept nearestStack = FilterNearest(game, interestingStacks);
                    m_LastItemsSaw = nearestStack;

                    // 12: make room for food if needed.
                    ActorAction makeRoomForFood = BehaviorMakeRoomForFood(game, interestingStacks);
                    if (makeRoomForFood != null)
                    {
                        m_Actor.Activity = Activity.IDLE;
                        return(makeRoomForFood);
                    }

                    // 13: try to grab.
                    ActorAction grabAction = BehaviorGrabFromStack(game, nearestStack.Location.Position, nearestStack.Percepted as Inventory);
                    if (grabAction != null)
                    {
                        m_Actor.Activity = Activity.IDLE;
                        return(grabAction);
                    }
                    // we can't grab the item. mark the tile as taboo.
                    MarkTileAsTaboo(nearestStack.Location.Position);
                    // emote
                    game.DoEmote(m_Actor, "Mmmh. Looks like I can't reach what I want.");
                }
                #endregion

                #region Trade
                if (Directives.CanTrade)
                {
                    // get actors we want to trade with.
                    List <Percept> tradingActors = FilterOut(game, FilterNonEnemies(game, mapPercepts),
                                                             (p) =>
                    {
                        if (p.Turn != map.LocalTime.TurnCounter)
                        {
                            return(true);
                        }
                        Actor other = p.Percepted as Actor;
                        // dont bother player or someone we can't trade with or already did trade.
                        if (other.IsPlayer)
                        {
                            return(true);
                        }
                        if (!game.Rules.CanActorInitiateTradeWith(m_Actor, other))
                        {
                            return(true);
                        }
                        if (IsActorTabooTrade(other))
                        {
                            return(true);
                        }
                        // dont bother if no interesting items.
                        if (!HasAnyInterestingItem(game, other.Inventory))
                        {
                            return(true);
                        }
                        if (!((other.Controller as BaseAI).HasAnyInterestingItem(game, m_Actor.Inventory)))
                        {
                            return(true);
                        }
                        // don't reject.
                        return(false);
                    });
                    // trade with nearest.
                    if (tradingActors != null)
                    {
                        Actor tradeTarget = FilterNearest(game, tradingActors).Percepted as Actor;
                        if (game.Rules.IsAdjacent(m_Actor.Location, tradeTarget.Location))
                        {
                            ActorAction tradeAction = new ActionTrade(m_Actor, game, tradeTarget);
                            if (tradeAction.IsLegal())
                            {
                                // remember we tried to trade.
                                MarkActorAsRecentTrade(tradeTarget);
                                // say, so we make sure we spend a turn and won't loop.
                                game.DoSay(m_Actor, tradeTarget, String.Format("Hey {0}, let's make a deal!", tradeTarget.Name), RogueGame.Sayflags.NONE);
                                return(tradeAction);
                            }
                        }
                        else
                        {
                            ActorAction bump = BehaviorIntelligentBumpToward(game, tradeTarget.Location.Position);
                            if (bump != null)
                            {
                                m_Actor.Activity    = Activity.FOLLOWING;
                                m_Actor.TargetActor = tradeTarget;
                                return(bump);
                            }
                        }
                    }
                }
                #endregion
            }
            #endregion

            // 14 if hungry and no food, charge at people for food (option, not follower or law enforcer)
            #region
            if (RogueGame.Options.IsAggressiveHungryCiviliansOn &&
                mapPercepts != null && !m_Actor.HasLeader && !m_Actor.Model.Abilities.IsLawEnforcer &&
                game.Rules.IsActorHungry(m_Actor) && HasNoFoodItems(m_Actor))
            {
                Percept targetForFood = FilterNearest(game, FilterActors(game, mapPercepts,
                                                                         (a) =>
                {
                    // reject self, dead and leader/follower.
                    if (a == m_Actor)
                    {
                        return(false);
                    }
                    if (a.IsDead)
                    {
                        return(false);
                    }
                    if (a.Inventory == null || a.Inventory.IsEmpty)
                    {
                        return(false);
                    }
                    if (a.Leader == m_Actor || m_Actor.Leader == a)
                    {
                        return(false);
                    }

                    // actor has food or is standing on food.
                    if (a.Inventory.HasItemOfType(typeof(ItemFood)))
                    {
                        return(true);
                    }
                    Inventory groundInv = a.Location.Map.GetItemsAt(a.Location.Position);
                    if (groundInv == null || groundInv.IsEmpty)
                    {
                        return(false);
                    }
                    return(groundInv.HasItemOfType(typeof(ItemFood)));
                }));

                if (targetForFood != null)
                {
                    ActorAction chargeAction = BehaviorChargeEnemy(game, targetForFood);
                    if (chargeAction != null)
                    {
                        // randomly emote.
                        if (game.Rules.RollChance(HUNGRY_CHARGE_EMOTE_CHANCE))
                        {
                            game.DoSay(m_Actor, targetForFood.Percepted as Actor, "HEY! YOU! SHARE SOME FOOD!", RogueGame.Sayflags.IS_FREE_ACTION);
                        }

                        // chaaarge!
                        m_Actor.Activity    = Activity.FIGHTING;
                        m_Actor.TargetActor = targetForFood.Percepted as Actor;
                        return(chargeAction);
                    }
                }
            }
            #endregion

            // 15 use stench killer.
            #region
            if (game.Rules.RollChance(USE_STENCH_KILLER_CHANCE))
            {
                ActorAction sprayAction = BehaviorUseStenchKiller(game);
                if (sprayAction != null)
                {
                    m_Actor.Activity = Activity.IDLE;
                    return(sprayAction);
                }
            }
            #endregion

            // 16 close door behind me.
            #region
            ActorAction closeBehindMe = BehaviorCloseDoorBehindMe(game, PrevLocation);
            if (closeBehindMe != null)
            {
                m_Actor.Activity = Activity.IDLE;
                return(closeBehindMe);
            }
            #endregion

            // 17 use entertainment
            #region
            if (m_Actor.Model.Abilities.HasSanity)
            {
                if (m_Actor.Sanity < 0.75f * game.Rules.ActorMaxSanity(m_Actor))
                {
                    ActorAction entAction = BehaviorUseEntertainment(game);
                    if (entAction != null)
                    {
                        m_Actor.Activity = Activity.IDLE;
                        return(entAction);
                    }
                }
                ActorAction dropEnt = BehaviorDropBoringEntertainment(game);
                if (dropEnt != null)
                {
                    m_Actor.Activity = Activity.IDLE;
                    return(dropEnt);
                }
            }
            #endregion

            // 18 follow leader
            #region
            if (checkOurLeader)
            {
                Point       lastKnownLeaderPosition = m_Actor.Leader.Location.Position;
                bool        isLeaderVisible         = m_LOSSensor.FOV.Contains(m_Actor.Leader.Location.Position);
                int         maxDist      = m_Actor.Leader.IsPlayer ? FOLLOW_PLAYERLEADER_MAXDIST : FOLLOW_NPCLEADER_MAXDIST;
                ActorAction followAction = BehaviorFollowActor(game, m_Actor.Leader, lastKnownLeaderPosition, isLeaderVisible, maxDist);
                if (followAction != null)
                {
                    m_Actor.Activity    = Activity.FOLLOWING;
                    m_Actor.TargetActor = m_Actor.Leader;
                    return(followAction);
                }
            }
            #endregion

            // 19 take lead (if leadership)
            #region
            bool hasLeadership = m_Actor.Sheet.SkillTable.GetSkillLevel((int)Skills.IDs.LEADERSHIP) >= 1;
            if (hasLeadership)
            {
                bool canLead = !checkOurLeader && m_Actor.CountFollowers < game.Rules.ActorMaxFollowers(m_Actor);
                if (canLead)
                {
                    Percept nearestFriend = FilterNearest(game, FilterNonEnemies(game, mapPercepts));
                    if (nearestFriend != null)
                    {
                        ActorAction leadAction = BehaviorLeadActor(game, nearestFriend);
                        if (leadAction != null)
                        {
                            m_Actor.Activity    = Activity.IDLE;
                            m_Actor.TargetActor = nearestFriend.Percepted as Actor;
                            return(leadAction);
                        }
                    }
                }
            }
            #endregion

            // 20 if hungry, tear down barricades & push objects.
            #region
            if (game.Rules.IsActorHungry(m_Actor))
            {
                ActorAction attackBarricadeAction = BehaviorAttackBarricade(game);
                if (attackBarricadeAction != null)
                {
                    // emote.
                    game.DoEmote(m_Actor, "Open damn it! I know there is food there!");

                    // go!
                    m_Actor.Activity = Activity.IDLE;
                    return(attackBarricadeAction);
                }
                if (game.Rules.RollChance(HUNGRY_PUSH_OBJECTS_CHANCE))
                {
                    ActorAction pushAction = BehaviorPushNonWalkableObject(game);
                    if (pushAction != null)
                    {
                        // emote.
                        game.DoEmote(m_Actor, "Where is all the damn food?!");

                        // go!
                        m_Actor.Activity = Activity.IDLE;
                        return(pushAction);
                    }
                }
            }
            #endregion

            // 21 go revive corpse.
            ActorAction revive = BehaviorGoReviveCorpse(game, FilterCorpses(game, mapPercepts));
            if (revive != null)
            {
                m_Actor.Activity = Activity.IDLE;
                return(revive);
            }

            // 22 use exit.
            #region
            if (game.Rules.RollChance(USE_EXIT_CHANCE))
            {
                ActorAction useExit = BehaviorUseExit(game, UseExitFlags.DONT_BACKTRACK);
                if (useExit != null)
                {
                    m_Actor.Activity = Activity.IDLE;
                    return(useExit);
                }
            }
            #endregion

            // 23 build trap or fortification.
            #region
            if (game.Rules.RollChance(BUILD_TRAP_CHANCE))
            {
                ActorAction trapAction = BehaviorBuildTrap(game);
                if (trapAction != null)
                {
                    m_Actor.Activity = Activity.IDLE;
                    return(trapAction);
                }
            }
            // large fortification.
            if (game.Rules.RollChance(BUILD_LARGE_FORT_CHANCE))
            {
                ActorAction buildAction = BehaviorBuildLargeFortification(game, START_FORT_LINE_CHANCE);
                if (buildAction != null)
                {
                    m_Actor.Activity = Activity.IDLE;
                    return(buildAction);
                }
            }
            // small fortification.
            if (game.Rules.RollChance(BUILD_SMALL_FORT_CHANCE))
            {
                ActorAction buildAction = BehaviorBuildSmallFortification(game);
                if (buildAction != null)
                {
                    m_Actor.Activity = Activity.IDLE;
                    return(buildAction);
                }
            }
            #endregion

            // 24 tell friend about latest raid.
            #region
            // tell?
            if (m_LastRaidHeard != null && game.Rules.RollChance(TELL_FRIEND_ABOUT_RAID_CHANCE))
            {
                ActorAction tellAction = BehaviorTellFriendAboutPercept(game, m_LastRaidHeard);
                if (tellAction != null)
                {
                    m_Actor.Activity = Activity.IDLE;
                    return(tellAction);
                }
            }
            #endregion

            // 25 tell friend about latest soldier.
            #region
            // update percept.
            Percept seeingSoldier = FilterFirst(game, mapPercepts,
                                                (p) =>
            {
                Actor other = p.Percepted as Actor;
                if (other == null || other == m_Actor)
                {
                    return(false);
                }
                return(IsSoldier(other));
            });
            if (seeingSoldier != null)
            {
                m_LastSoldierSaw = seeingSoldier;
            }
            // tell?
            if (game.Rules.RollChance(TELL_FRIEND_ABOUT_SOLDIER_CHANCE) && m_LastSoldierSaw != null)
            {
                ActorAction tellAction = BehaviorTellFriendAboutPercept(game, m_LastSoldierSaw);
                if (tellAction != null)
                {
                    m_Actor.Activity = Activity.IDLE;
                    return(tellAction);
                }
            }
            #endregion

            // 26 tell friend about latest enemy.
            #region
            if (game.Rules.RollChance(TELL_FRIEND_ABOUT_ENEMY_CHANCE) && m_LastEnemySaw != null)
            {
                ActorAction tellAction = BehaviorTellFriendAboutPercept(game, m_LastEnemySaw);
                if (tellAction != null)
                {
                    m_Actor.Activity = Activity.IDLE;
                    return(tellAction);
                }
            }
            #endregion

            // 27 tell friend about latest items.
            #region
            if (game.Rules.RollChance(TELL_FRIEND_ABOUT_ITEMS_CHANCE) && m_LastItemsSaw != null)
            {
                ActorAction tellAction = BehaviorTellFriendAboutPercept(game, m_LastItemsSaw);
                if (tellAction != null)
                {
                    m_Actor.Activity = Activity.IDLE;
                    return(tellAction);
                }
            }
            #endregion

            // 28 (law enforcer) watch for murderers.
            #region
            if (m_Actor.Model.Abilities.IsLawEnforcer && mapPercepts != null && game.Rules.RollChance(LAW_ENFORCE_CHANCE))
            {
                Actor       target;
                ActorAction lawAction = BehaviorEnforceLaw(game, mapPercepts, out target);
                if (lawAction != null)
                {
                    m_Actor.TargetActor = target;
                    return(lawAction);
                }
            }
            #endregion

            // 29 (leader) don't leave followers behind.
            #region
            if (m_Actor.CountFollowers > 0)
            {
                Actor       target;
                ActorAction stickTogether = BehaviorDontLeaveFollowersBehind(game, 2, out target);
                if (stickTogether != null)
                {
                    // emote?
                    if (game.Rules.RollChance(DONT_LEAVE_BEHIND_EMOTE_CHANCE))
                    {
                        if (target.IsSleeping)
                        {
                            game.DoEmote(m_Actor, String.Format("patiently waits for {0} to wake up.", target.Name));
                        }
                        else
                        {
                            if (m_LOSSensor.FOV.Contains(target.Location.Position))
                            {
                                game.DoEmote(m_Actor, String.Format("Come on {0}! Hurry up!", target.Name));
                            }
                            else
                            {
                                game.DoEmote(m_Actor, String.Format("Where the hell is {0}?", target.Name));
                            }
                        }
                    }

                    // go!
                    m_Actor.Activity = Activity.IDLE;
                    return(stickTogether);
                }
            }
            #endregion

            // 30 explore
            #region
            ActorAction exploreAction = BehaviorExplore(game, m_Exploration);
            if (exploreAction != null)
            {
                m_Actor.Activity = Activity.IDLE;
                return(exploreAction);
            }
            #endregion

            // 31 wander.
            #region
            m_Actor.Activity = Activity.IDLE;
            return(BehaviorWander(game));

            #endregion

            #endregion
        }
예제 #2
0
파일: Trade.cs 프로젝트: AMartyn/RSRevived
        public override bool UrgentAction(out ActorAction?ret)
        {
            ret = null;
            if (_isExpired)
            {
                return(true);
            }
            if (m_Actor.Controller.InCombat)
            {
                return(false);                              // suspend if in combat
            }
            // prior legality checks
            int i = _whom.Count;

            while (0 < i)
            {
                if (_whom[--i].IsDead)
                {
                    _whom.RemoveAt(i);
                    continue;
                }
            }
            if (0 >= _whom.Count)
            {
                _isExpired = true;
                return(true);
            }

            // closely related to OrderableAI::GetTradingTargets.  We'll use the pathfinder if minstep pathing fails.
            var oai = (m_Actor.Controller as ObjectiveAI) !;

            if (oai.IsFocused)
            {
                return(false);
            }
            var TradeableItems = oai.GetTradeableItems();   // may need revision for player....

            if (null == TradeableItems || 0 >= TradeableItems.Count)
            {
                _isExpired = true;
                return(true);
            }

            i = _whom.Count;
            while (0 < i)
            {
                var actor = _whom[--i];
                if (!m_Actor.CanTradeWith(actor))   // XXX \todo CouldTradeWith (less sensitive to current activity) but can't, just continues
                {
                    _whom.RemoveAt(i);
                    continue;
                }
                if (oai.CanSee(actor.Location) && null == m_Actor.MinStepPathTo(m_Actor.Location, actor.Location))
                {
                    _whom.RemoveAt(i);
                    continue;
                }
                var a_oai = (actor.Controller as ObjectiveAI) !;
                if (1 == TradeableItems.Count)
                {
                    var other_TradeableItems = a_oai.GetTradeableItems();
                    if (null == other_TradeableItems)
                    {
                        continue;
                    }
                    if (1 == other_TradeableItems.Count && TradeableItems[0].Model.ID == other_TradeableItems[0].Model.ID)
                    {
                        _whom.RemoveAt(i);
                        continue;
                    }
                }
                if (a_oai is OrderableAI ai && !ai.HasAnyInterestingItem(TradeableItems))
                {
                    _whom.RemoveAt(i);
                    continue;
                }
                if (!oai.HaveTradeOptions(actor))
                {
                    _whom.RemoveAt(i);
                    continue;
                }
            }
            if (0 >= _whom.Count)
            {
                _isExpired = true;
                return(true);
            }
            // \todo post-filter by (rest of) CanTradeWith once we have CouldTradeWith
            var adjacent = _whom.FindAll(act => Rules.IsAdjacent(m_Actor.Location, act.Location));

            if (0 < adjacent.Count)
            {
                var actor = Rules.Get.DiceRoller.Choose(adjacent);
                (oai as OrderableAI)?.MarkActorAsRecentTrade(actor);
                (actor.Controller as OrderableAI)?.MarkActorAsRecentTrade(m_Actor);                                                      // try to reduce trading spam: one trade per pair, not two
                RogueGame.DoSay(m_Actor, actor, string.Format("Hey {0}, let's make a deal!", actor), RogueGame.Sayflags.IS_FREE_ACTION); // formerly paid AP cost here rather than in RogueGame::DoTrade
                ret = new ActionTrade(m_Actor, actor);
                return(true);
            }

            var find_us      = _whom.Select(act => act.Location);
            var find_visible = find_us.Where(loc => oai.CanSee(loc));
            var act          = oai.BehaviorHeadFor(find_visible, false, false);

            if (null != act)
            {
                m_Actor.Activity    = Activity.FOLLOWING;
                m_Actor.TargetActor = Rules.Get.DiceRoller.Choose(find_visible).Actor;   // XXX possibly misleading
                ret = act;
                return(true);
            }
            act = oai.BehaviorPathTo(new HashSet <Location>(find_us));
            if (null != act)
            {
                m_Actor.Activity    = Activity.FOLLOWING;
                m_Actor.TargetActor = Rules.Get.DiceRoller.Choose(_whom);   // XXX possibly misleading
                ret = act;
                return(true);
            }
            return(false);
        }
예제 #3
0
        protected override ActorAction SelectAction(RogueGame game, List <Percept> percepts)
        {
            List <Percept> mapPercepts = FilterSameMap(game, percepts);

            // DEBUG BOT
#if DEBUG
            bool botBreakpoint           = false;
            bool verboseBotExploreWander = false;
            if (m_Actor.IsBotPlayer)
            {
                botBreakpoint           = false; // true;
                verboseBotExploreWander = false; // true;
            }
#endif
            // END DEBUG BOT

            ///////////////////////
            // 0. Equip best item.  // alpha10
            // 1. Follow order
            // 2. Normal behavior.
            ///////////////////////

            // alpha10
            // don't run by default.
            m_Actor.IsRunning = false;

            // 0. Equip best item
            ActorAction bestEquip = BehaviorEquipBestItems(game, true, true);
            if (bestEquip != null)
            {
                return(bestEquip);
            }
            // end alpha10

            // 1. Follow order
            if (this.Order != null)
            {
                ActorAction orderAction = ExecuteOrder(game, this.Order, mapPercepts, m_Exploration);
                if (orderAction == null)
                {
                    SetOrder(null);
                }
                else
                {
                    m_Actor.Activity = Activity.FOLLOWING_ORDER;
                    return(orderAction);
                }
            }

            // 2. Normal behavior.
            //////////////////////////////////////////////////////////////////////
            // BEHAVIOR
            // - FLAGS
            // "courageous" : has leader, see leader, he is fighting and actor not tired.
            // - RULES
            // 0 run away from primed explosives.
            // 1 throw grenades at enemies.
            // alpha10 OBSOLETE 2 equip weapon/armor
            // 3 fire at nearest (always if has leader, half of the time if not)  - check directives
            // 4 fight or flee, shout
            // 5 use medicine
            // 6 rest if tired
            // alpha10 obsolete and redundant with rule 4! 7 charge enemy if courageous
            // 8 eat when hungry (also eat corpses)
            // 9 sleep when almost sleepy and safe.
            // 10 drop light/tracker with no batteries
            // alpha10 OBSOLETE 11 equip light/tracker/scent spray
            // 12 make room for food items if needed.
            // 13 get nearby item/trade (not if seeing enemy) - check directives.
            // 14 if hungry and no food, charge at people for food (option, not follower or law enforcer)
            // 15 use stench killer.
            // 16 close door behind me.
            // 17 use entertainment
            // 18 follow leader.
            // 19 take lead (if leadership)
            // 20 if hungry, tear down barricades & push objects.
            // 21 go revive corpse.
            // 22 use exit.
            // 23 build trap or fortification.
            // 24 tell friend about latest raid.
            // 25 tell friend about latest friendly soldier.
            // 26 tell friend about latest enemy.
            // 27 tell friend about latest items.
            // 28 (law enforcer) watch for murderers.
            // 29 (leader) don't leave followers behind.
            // 30 explore.
            // 31 wander.
            //////////////////////////////////////////////////////////////////////

            // get data.
            List <Percept> enemies          = FilterEnemies(game, mapPercepts);
            bool           hasEnemies       = enemies != null && enemies.Count > 0;
            bool           checkOurLeader   = m_Actor.HasLeader && !DontFollowLeader;
            bool           seeLeader        = checkOurLeader && m_LOSSensor.FOV.Contains(m_Actor.Leader.Location.Position);
            bool           isLeaderFighting = checkOurLeader && IsAdjacentToEnemy(game, m_Actor.Leader);
            bool           isCourageous     = checkOurLeader && seeLeader && isLeaderFighting && !game.Rules.IsActorTired(m_Actor);

            // safety counter.
            if (hasEnemies)
            {
                m_SafeTurns = 0;
            }
            else
            {
                ++m_SafeTurns;
            }

            // exploration.
            m_Exploration.Update(m_Actor.Location);

            // clear taboo tiles : periodically or when changing maps.
            if (m_Actor.Location.Map.LocalTime.TurnCounter % WorldTime.TURNS_PER_HOUR == 0 ||
                (PrevLocation != null && PrevLocation.Map != m_Actor.Location.Map))
            {
                ClearTabooTiles();
            }
            // clear trades.
            if (m_Actor.Location.Map.LocalTime.TurnCounter % WorldTime.TURNS_PER_DAY == 0)
            {
                ClearTabooTrades();
            }

            // last enemy saw.
            if (hasEnemies)
            {
                m_LastEnemySaw = enemies[game.Rules.Roll(0, enemies.Count)];
            }

            // 0 run away from primed explosives.
            ActorAction runFromExplosives = BehaviorFleeFromExplosives(game, FilterStacks(game, mapPercepts));
            if (runFromExplosives != null)
            {
                m_Actor.Activity = Activity.FLEEING_FROM_EXPLOSIVE;
                return(runFromExplosives);
            }

            // 1 throw grenades at enemies.
            // if directive off, unequip.
            if (!this.Directives.CanThrowGrenades)
            {
                // unequip grenade?
                ItemGrenade eqGrenade = m_Actor.GetEquippedWeapon() as ItemGrenade;
                if (eqGrenade != null)
                {
                    ActionUnequipItem unequipGre = new ActionUnequipItem(m_Actor, game, eqGrenade);
                    if (unequipGre.IsLegal())
                    {
                        m_Actor.Activity = Activity.IDLE;
                        return(unequipGre);
                    }
                }
            }
            // throw?
            if (hasEnemies)
            {
                ActorAction throwAction = BehaviorThrowGrenade(game, m_LOSSensor.FOV, enemies);
                if (throwAction != null)
                {
                    return(throwAction);
                }
            }

            // 3 fire at nearest enemy
            if (hasEnemies && this.Directives.CanFireWeapons && m_Actor.GetEquippedWeapon() is ItemRangedWeapon)
            {
                List <Percept> fireTargets = FilterFireTargets(game, enemies);
                if (fireTargets != null)
                {
                    Percept nearestTarget = FilterNearest(game, fireTargets);
                    Actor   target        = nearestTarget.Percepted as Actor;

                    // flee contact from someone SLOWER with no ranged weapon.
                    if (game.Rules.GridDistance(nearestTarget.Location.Position, m_Actor.Location.Position) == 1 &&
                        !HasEquipedRangedWeapon(target) &&
                        HasSpeedAdvantage(game, m_Actor, target))
                    {
                        // flee!
                        ActorAction fleeAction = BehaviorWalkAwayFrom(game, nearestTarget);
                        if (fleeAction != null)
                        {
                            RunIfPossible(game.Rules);
                            m_Actor.Activity = Activity.FLEEING;
                            return(fleeAction);
                        }
                    }

                    // fire ze missiles!
                    ActorAction fireAction = BehaviorRangedAttack(game, nearestTarget);
                    if (fireAction != null)
                    {
                        m_Actor.Activity    = Activity.FIGHTING;
                        m_Actor.TargetActor = target;
                        return(fireAction);
                    }
                }
            }

            // 4 fight or flee, shout
            if (hasEnemies)
            {
                // shout?
                if (game.Rules.RollChance(50))
                {
                    List <Percept> friends = FilterNonEnemies(game, mapPercepts);
                    if (friends != null)
                    {
                        ActorAction shoutAction = BehaviorWarnFriends(game, friends, FilterNearest(game, enemies).Percepted as Actor);
                        if (shoutAction != null)
                        {
                            m_Actor.Activity = Activity.IDLE;
                            return(shoutAction);
                        }
                    }
                }
                // fight or flee.
                RouteFinder.SpecialActions allowedChargeActions = RouteFinder.SpecialActions.JUMP | RouteFinder.SpecialActions.DOORS; // alpha10
                ActorAction fightOrFlee = BehaviorFightOrFlee(game, enemies, seeLeader, isLeaderFighting, Directives.Courage, m_Emotes, allowedChargeActions);
                if (fightOrFlee != null)
                {
                    return(fightOrFlee);
                }
            }

            // 5 use medicine
            ActorAction useMedAction = BehaviorUseMedecine(game, 2, 1, 2, 4, 2);
            if (useMedAction != null)
            {
                m_Actor.Activity = Activity.IDLE;
                return(useMedAction);
            }

            // 6 rest if tired
            ActorAction restAction = BehaviorRestIfTired(game);
            if (restAction != null)
            {
                m_Actor.Activity = Activity.IDLE;
                return(new ActionWait(m_Actor, game));
            }

            // 8 eat when hungry (also eat corpses)
            if (game.Rules.IsActorHungry(m_Actor))
            {
                ActorAction eatAction = BehaviorEat(game);
                if (eatAction != null)
                {
                    m_Actor.Activity = Activity.IDLE;
                    return(eatAction);
                }
                if (game.Rules.IsActorStarving(m_Actor) || game.Rules.IsActorInsane(m_Actor))
                {
                    eatAction = BehaviorGoEatCorpse(game, FilterCorpses(game, mapPercepts));
                    if (eatAction != null)
                    {
                        m_Actor.Activity = Activity.IDLE;
                        return(eatAction);
                    }
                }
            }

            // 9 sleep when almost sleepy and safe.
            if (m_SafeTurns >= MIN_TURNS_SAFE_TO_SLEEP && this.Directives.CanSleep &&
                WouldLikeToSleep(game, m_Actor) && IsInside(m_Actor) && game.Rules.CanActorSleep(m_Actor))
            {
                // secure sleep.
                ActorAction secureSleepAction = BehaviorSecurePerimeter(game, m_LOSSensor.FOV);
                if (secureSleepAction != null)
                {
                    m_Actor.Activity = Activity.IDLE;
                    return(secureSleepAction);
                }

                // sleep.
                ActorAction sleepAction = BehaviorSleep(game, m_LOSSensor.FOV);
                if (sleepAction != null)
                {
                    if (sleepAction is ActionSleep)
                    {
                        m_Actor.Activity = Activity.SLEEPING;
                    }
                    return(sleepAction);
                }
            }

            // 10 drop useless light/tracker/spray
            ActorAction dropUseless = BehaviorDropUselessItem(game);
            if (dropUseless != null)
            {
                m_Actor.Activity = Activity.IDLE;
                return(dropUseless);
            }

            // 12 make room for food items if needed.
            // &&
            // 13 get nearby item/trade (not if seeing enemy)
            // ignore not currently visible items & blocked items.
            if (!hasEnemies && this.Directives.CanTakeItems)
            {
                Map map = m_Actor.Location.Map;

                // alpha10 new common behaviour code, also used by GangAI
                ActorAction getItemAction = BehaviorGoGetInterestingItems(game, mapPercepts,
                                                                          false, false, CANT_GET_ITEM_EMOTE, true, ref m_LastItemsSaw);

                if (getItemAction != null)
                {
                    return(getItemAction);
                }

                if (Directives.CanTrade)
                {
                    // get actors we want to trade with.
                    List <Percept> tradingActors = FilterOut(game, FilterNonEnemies(game, mapPercepts),
                                                             (p) =>
                    {
                        if (p.Turn != map.LocalTime.TurnCounter)
                        {
                            return(true);
                        }
                        Actor other = p.Percepted as Actor;
                        // dont bother player or someone we can't trade with or already did trade.
                        if (other.IsPlayer)
                        {
                            return(true);
                        }
                        if (!game.Rules.CanActorInitiateTradeWith(m_Actor, other))
                        {
                            return(true);
                        }
                        if (IsActorTabooTrade(other))
                        {
                            return(true);
                        }
                        // alpha10 dont bother someone who is fighting or fleeing
                        if (other.Activity == Activity.CHASING || other.Activity == Activity.FIGHTING || other.Activity == Activity.FLEEING || other.Activity == Activity.FLEEING_FROM_EXPLOSIVE)
                        {
                            return(true);
                        }
                        // dont bother if no interesting items.
                        if (!HasAnyInterestingItem(game, other.Inventory, ItemSource.ANOTHER_ACTOR))
                        {
                            return(true);
                        }
                        if (!((other.Controller as BaseAI).HasAnyInterestingItem(game, m_Actor.Inventory, ItemSource.ANOTHER_ACTOR)))
                        {
                            return(true);
                        }
                        // alpha10 reject if unreachable by baseai simple behaviours
                        if (!CanReachSimple(game, other.Location.Position, Tools.RouteFinder.SpecialActions.DOORS | Tools.RouteFinder.SpecialActions.JUMP))
                        {
                            return(true);
                        }
                        // don't reject.
                        return(false);
                    });
                    // trade with nearest.
                    if (tradingActors != null)
                    {
                        Actor tradeTarget = FilterNearest(game, tradingActors).Percepted as Actor;
                        if (game.Rules.IsAdjacent(m_Actor.Location, tradeTarget.Location))
                        {
                            ActorAction tradeAction = new ActionTrade(m_Actor, game, tradeTarget);
                            if (tradeAction.IsLegal())
                            {
                                // remember we tried to trade.
                                MarkActorAsRecentTrade(tradeTarget);
                                // say, so we make sure we spend a turn and won't loop.
                                game.DoSay(m_Actor, tradeTarget, String.Format("Hey {0}, let's make a deal!", tradeTarget.Name), RogueGame.Sayflags.NONE);
                                return(tradeAction);
                            }
                        }
                        else
                        {
                            ActorAction bump = BehaviorIntelligentBumpToward(game, tradeTarget.Location.Position, false, false);
                            if (bump != null)
                            {
                                // alpha10 announce it to make it clear to the player whats happening but dont spend AP (free action)
                                // might spam for a few turns, but its better than not understanding whats going on.
                                game.DoSay(m_Actor, tradeTarget, String.Format("Hey {0}, let's make a deal!", tradeTarget.Name), RogueGame.Sayflags.IS_FREE_ACTION);

                                m_Actor.Activity    = Activity.FOLLOWING;
                                m_Actor.TargetActor = tradeTarget;
                                return(bump);
                            }
                        }
                    }
                }
            }

            // 14 if hungry and no food, charge at people for food (option, not follower or law enforcer)
            if (RogueGame.Options.IsAggressiveHungryCiviliansOn &&
                mapPercepts != null && !m_Actor.HasLeader && !m_Actor.Model.Abilities.IsLawEnforcer &&
                game.Rules.IsActorHungry(m_Actor) && HasNoFoodItems(m_Actor))
            {
                Percept targetForFood = FilterNearest(game, FilterActors(game, mapPercepts,
                                                                         (a) =>
                {
                    // reject self, dead and leader/follower.
                    if (a == m_Actor)
                    {
                        return(false);
                    }
                    if (a.IsDead)
                    {
                        return(false);
                    }
                    if (a.Inventory == null || a.Inventory.IsEmpty)
                    {
                        return(false);
                    }
                    if (a.Leader == m_Actor || m_Actor.Leader == a)
                    {
                        return(false);
                    }

                    // actor has food or is standing on food.
                    if (a.Inventory.HasItemOfType(typeof(ItemFood)))
                    {
                        return(true);
                    }
                    Inventory groundInv = a.Location.Map.GetItemsAt(a.Location.Position);
                    if (groundInv == null || groundInv.IsEmpty)
                    {
                        return(false);
                    }
                    return(groundInv.HasItemOfType(typeof(ItemFood)));
                }));

                if (targetForFood != null)
                {
                    // alpha10 hungry civs can break and push
                    ActorAction chargeAction = BehaviorChargeEnemy(game, targetForFood, true, true);
                    if (chargeAction != null)
                    {
                        // randomly emote.
                        if (game.Rules.RollChance(HUNGRY_CHARGE_EMOTE_CHANCE))
                        {
                            game.DoSay(m_Actor, targetForFood.Percepted as Actor, "HEY! YOU! SHARE SOME FOOD!", RogueGame.Sayflags.IS_FREE_ACTION | RogueGame.Sayflags.IS_DANGER);
                        }

                        // chaaarge!
                        m_Actor.Activity    = Activity.FIGHTING;
                        m_Actor.TargetActor = targetForFood.Percepted as Actor;
                        return(chargeAction);
                    }
                }
            }

            // 15 use stench killer.
            if (game.Rules.RollChance(USE_STENCH_KILLER_CHANCE))
            {
                ActorAction sprayAction = BehaviorUseStenchKiller(game);
                if (sprayAction != null)
                {
                    m_Actor.Activity = Activity.IDLE;
                    return(sprayAction);
                }
            }

            // 16 close door behind me.
            ActorAction closeBehindMe = BehaviorCloseDoorBehindMe(game, PrevLocation);
            if (closeBehindMe != null)
            {
                m_Actor.Activity = Activity.IDLE;
                return(closeBehindMe);
            }

            // 17 use entertainment
            if (m_Actor.Model.Abilities.HasSanity)
            {
                if (m_Actor.Sanity < 0.75f * game.Rules.ActorMaxSanity(m_Actor))
                {
                    ActorAction entAction = BehaviorUseEntertainment(game);
                    if (entAction != null)
                    {
                        m_Actor.Activity = Activity.IDLE;
                        return(entAction);
                    }
                }
                // TODO -- consider moving this to DropUselessItems()
                ActorAction dropEnt = BehaviorDropBoringEntertainment(game);
                if (dropEnt != null)
                {
                    m_Actor.Activity = Activity.IDLE;
                    return(dropEnt);
                }
            }

            // 18 build trap or fortification.
            // alpha10.1 moved trap/fortification rule before following leader rule so they will do it much more often
            if (game.Rules.RollChance(BUILD_TRAP_CHANCE))
            {
                ActorAction trapAction = BehaviorBuildTrap(game);
                if (trapAction != null)
                {
                    m_Actor.Activity = Activity.IDLE;
                    return(trapAction);
                }
            }
            // large fortification.
            if (game.Rules.RollChance(BUILD_LARGE_FORT_CHANCE))
            {
                ActorAction buildAction = BehaviorBuildLargeFortification(game, START_FORT_LINE_CHANCE);
                if (buildAction != null)
                {
                    m_Actor.Activity = Activity.IDLE;
                    return(buildAction);
                }
            }
            // small fortification.
            if (game.Rules.RollChance(BUILD_SMALL_FORT_CHANCE))
            {
                ActorAction buildAction = BehaviorBuildSmallFortification(game);
                if (buildAction != null)
                {
                    m_Actor.Activity = Activity.IDLE;
                    return(buildAction);
                }
            }

            // 19 follow leader
            if (checkOurLeader)
            {
                Point       lastKnownLeaderPosition = m_Actor.Leader.Location.Position;
                bool        isLeaderVisible         = m_LOSSensor.FOV.Contains(m_Actor.Leader.Location.Position);
                int         maxDist      = m_Actor.Leader.IsPlayer ? FOLLOW_PLAYERLEADER_MAXDIST : FOLLOW_NPCLEADER_MAXDIST;
                ActorAction followAction = BehaviorFollowActor(game, m_Actor.Leader, lastKnownLeaderPosition, isLeaderVisible, maxDist);
                if (followAction != null)
                {
                    m_Actor.Activity    = Activity.FOLLOWING;
                    m_Actor.TargetActor = m_Actor.Leader;
                    return(followAction);
                }
            }

            // 20 take lead (if leadership)
            bool hasLeadership = m_Actor.Sheet.SkillTable.GetSkillLevel((int)Skills.IDs.LEADERSHIP) >= 1;
            if (hasLeadership)
            {
                bool canLead = !checkOurLeader && m_Actor.CountFollowers < game.Rules.ActorMaxFollowers(m_Actor);
                if (canLead)
                {
                    Percept nearestFriend = FilterNearest(game, FilterNonEnemies(game, mapPercepts));
                    if (nearestFriend != null)
                    {
                        // alpha10 only if unreachable by baseai simple behaviours
                        if (CanReachSimple(game, nearestFriend.Location.Position, Tools.RouteFinder.SpecialActions.DOORS | Tools.RouteFinder.SpecialActions.JUMP))
                        {
                            ActorAction leadAction = BehaviorLeadActor(game, nearestFriend);
                            if (leadAction != null)
                            {
                                m_Actor.Activity    = Activity.IDLE;
                                m_Actor.TargetActor = nearestFriend.Percepted as Actor;
                                return(leadAction);
                            }
                        }
                    }
                }
            }

            // 21 if hungry, tear down barricades & push objects.
            if (game.Rules.IsActorHungry(m_Actor))
            {
                ActorAction attackBarricadeAction = BehaviorAttackBarricade(game);
                if (attackBarricadeAction != null)
                {
                    // emote.
                    game.DoEmote(m_Actor, "Open damn it! I know there is food there!", true);

                    // go!
                    m_Actor.Activity = Activity.IDLE;
                    return(attackBarricadeAction);
                }
                if (game.Rules.RollChance(HUNGRY_PUSH_OBJECTS_CHANCE))
                {
                    // alpha10.1 do that only inside where food is more likely to be hidden, pushing cars outside is stupid -_-
                    if (m_Actor.Location.Map.GetTileAt(m_Actor.Location.Position).IsInside)
                    {
                        ActorAction pushAction = BehaviorPushNonWalkableObject(game);
                        if (pushAction != null)
                        {
                            // emote.
                            game.DoEmote(m_Actor, "Where is all the damn food?!", true);

                            // go!
                            m_Actor.Activity = Activity.IDLE;
                            return(pushAction);
                        }
                    }
                }
            }

            // 22 go revive corpse.
            ActorAction revive = BehaviorGoReviveCorpse(game, FilterCorpses(game, mapPercepts));
            if (revive != null)
            {
                m_Actor.Activity = Activity.IDLE;
                return(revive);
            }

            // 23 use exit.
            if (game.Rules.RollChance(USE_EXIT_CHANCE))
            {
                ActorAction useExit = BehaviorUseExit(game, UseExitFlags.DONT_BACKTRACK);
                if (useExit != null)
                {
                    m_Actor.Activity = Activity.IDLE;
                    return(useExit);
                }
            }

            // 24 tell friend about latest raid.
            // tell?
            if (m_LastRaidHeard != null && game.Rules.RollChance(TELL_FRIEND_ABOUT_RAID_CHANCE))
            {
                ActorAction tellAction = BehaviorTellFriendAboutPercept(game, m_LastRaidHeard);
                if (tellAction != null)
                {
                    m_Actor.Activity = Activity.IDLE;
                    return(tellAction);
                }
            }

            // 25 tell friend about latest soldier.
            // update percept.
            Percept seeingSoldier = FilterFirst(game, mapPercepts,
                                                (p) =>
            {
                Actor other = p.Percepted as Actor;
                if (other == null || other == m_Actor)
                {
                    return(false);
                }
                return(IsSoldier(other));
            });
            if (seeingSoldier != null)
            {
                m_LastSoldierSaw = seeingSoldier;
            }
            // tell?
            if (game.Rules.RollChance(TELL_FRIEND_ABOUT_SOLDIER_CHANCE) && m_LastSoldierSaw != null)
            {
                ActorAction tellAction = BehaviorTellFriendAboutPercept(game, m_LastSoldierSaw);
                if (tellAction != null)
                {
                    m_Actor.Activity = Activity.IDLE;
                    return(tellAction);
                }
            }

            // 26 tell friend about latest enemy.
            if (game.Rules.RollChance(TELL_FRIEND_ABOUT_ENEMY_CHANCE) && m_LastEnemySaw != null)
            {
                ActorAction tellAction = BehaviorTellFriendAboutPercept(game, m_LastEnemySaw);
                if (tellAction != null)
                {
                    m_Actor.Activity = Activity.IDLE;
                    return(tellAction);
                }
            }

            // 27 tell friend about latest items.
            if (game.Rules.RollChance(TELL_FRIEND_ABOUT_ITEMS_CHANCE) && m_LastItemsSaw != null)
            {
                ActorAction tellAction = BehaviorTellFriendAboutPercept(game, m_LastItemsSaw);
                if (tellAction != null)
                {
                    m_Actor.Activity = Activity.IDLE;
                    return(tellAction);
                }
            }

            // 28 (law enforcer) watch for murderers.
            if (m_Actor.Model.Abilities.IsLawEnforcer && mapPercepts != null && game.Rules.RollChance(LAW_ENFORCE_CHANCE))
            {
                Actor       target;
                ActorAction lawAction = BehaviorEnforceLaw(game, mapPercepts, out target);
                if (lawAction != null)
                {
                    m_Actor.TargetActor = target;
                    return(lawAction);
                }
            }

            // 29 (leader) don't leave followers behind.
            if (m_Actor.CountFollowers > 0)
            {
                Actor       target;
                ActorAction stickTogether = BehaviorDontLeaveFollowersBehind(game, 2, out target);
                if (stickTogether != null)
                {
                    // emote?
                    if (game.Rules.RollChance(DONT_LEAVE_BEHIND_EMOTE_CHANCE))
                    {
                        if (target.IsSleeping)
                        {
                            game.DoEmote(m_Actor, String.Format("patiently waits for {0} to wake up.", target.Name));
                        }
                        else
                        {
                            if (m_LOSSensor.FOV.Contains(target.Location.Position))
                            {
                                game.DoEmote(m_Actor, String.Format("Come on {0}! Hurry up!", target.Name));
                            }
                            else
                            {
                                game.DoEmote(m_Actor, String.Format("Where the hell is {0}?", target.Name));
                            }
                        }
                    }

                    // go!
                    m_Actor.Activity = Activity.IDLE;
                    return(stickTogether);
                }
            }

            // 30 explore

            // DEBUG BOT
#if DEBUG
            if (botBreakpoint)
            {
                Console.Out.WriteLine("test bot exploration breakpoint");
            }
#endif
            // END DEBUG BOT

            ActorAction exploreAction = BehaviorExplore(game, m_Exploration);
            if (exploreAction != null)
            {
                // VERBOSE BOT
#if DEBUG
                if (verboseBotExploreWander)
                {
                    game.AddMessage(new Message(">> Bot is Exploring", m_Actor.Location.Map.LocalTime.TurnCounter));
                }
#endif
                // END VERBOSE BOT

                m_Actor.Activity = Activity.IDLE;
                return(exploreAction);
            }

            // 31 wander.
            // VERBOSE BOT
#if DEBUG
            if (verboseBotExploreWander)
            {
                game.AddMessage(new Message(">> Bot is Wandering", m_Actor.Location.Map.LocalTime.TurnCounter));
            }
#endif
            // END VERBOSE BOT

            m_Actor.Activity = Activity.IDLE;
            return(BehaviorWander(game, m_Exploration));
        }