示例#1
0
        public override GameAction Think(Character controlledChar, bool preThink, IRandom rand)
        {
            if (controlledChar.CantWalk)
            {
                GameAction attack = TryAttackChoice(rand, controlledChar, AttackPattern);
                if (attack.Type != GameAction.ActionType.Wait)
                {
                    return(attack);
                }
                return(null);
            }

            //if we have another move we can make, take this turn to reposition
            int extraTurns = controlledChar.MovementSpeed - controlledChar.TiersUsed;

            if (extraTurns <= 0)
            {
                //attempt to use a move
                GameAction attack = TryAttackChoice(rand, controlledChar, AttackPattern);
                if (attack.Type != GameAction.ActionType.Wait)
                {
                    return(attack);
                }
            }

            //past this point, using moves won't work, so try to find a path

            List <Character> seenCharacters = controlledChar.GetSeenCharacters(GetAcceptableTargets());

            bool playerSense = (IQ & AIFlags.PlayerSense) != AIFlags.None;

            if (playerSense)
            {
                //check for statuses that may make them ineligible targets
                for (int ii = seenCharacters.Count - 1; ii >= 0; ii--)
                {
                    if (!playerSensibleToAttack(seenCharacters[ii]))
                    {
                        seenCharacters.RemoveAt(ii);
                    }
                }
            }


            //path to the closest enemy
            List <Loc> path       = null;
            Character  targetChar = null;
            Dictionary <Loc, RangeTarget> endHash = new Dictionary <Loc, RangeTarget>();

            Loc[] ends           = null;
            bool  hasSelfEnd     = false; //the controlledChar's destination is included among ends
            bool  aimForDistance = false; // determines if we are pathing directly to the target or to a tile we can hit the target from

            PositionChoice positioning = PositionPattern;

            if (extraTurns > 0)
            {
                positioning = PositionChoice.Avoid;
            }

            if (controlledChar.AttackOnly)
            {
                positioning = PositionChoice.Approach;
            }

            if (!playerSense)
            {
                //for dumb NPCs, if they have a status where they can't attack, treat it as a regular attack pattern so that they walk up to the player
                //only cringe does this right now...
                StatusEffect flinchStatus = controlledChar.GetStatusEffect(8); //NOTE: specialized AI code!
                if (flinchStatus != null)
                {
                    positioning = PositionChoice.Approach;
                }
            }

            // If the Positionchoice is Avoid, take attack ranges into consideration
            // the end points should be all locations where one can attack the target
            // for projectiles, it should be the farthest point where they can attack:
            if (positioning != PositionChoice.Approach)
            {
                //get all move ranges and use all their ranges to denote destination tiles.
                FillRangeTargets(controlledChar, seenCharacters, endHash, positioning != PositionChoice.Avoid);
                List <Loc> endList = new List <Loc>();
                foreach (Loc endLoc in endHash.Keys)
                {
                    bool addLoc = false;
                    if (aimForDistance)
                    {
                        if (endHash[endLoc].Weight > 0)
                        {
                            addLoc = true;
                        }
                    }
                    else
                    {
                        if (endHash[endLoc].Weight > 0)
                        {
                            aimForDistance = true;
                            endList.Clear();
                        }
                        addLoc = true;
                    }
                    if (addLoc)
                    {
                        if (endLoc != controlledChar.CharLoc)//destination cannot be the current location (unless we have turns to spare)
                        {
                            endList.Add(endLoc);
                        }
                        else
                        {
                            if (extraTurns > 0)
                            {
                                endList.Add(endLoc);
                                hasSelfEnd = true;
                            }
                        }
                    }
                }
                ends = endList.ToArray();
            }
            else
            {
                ends = new Loc[seenCharacters.Count];
                for (int ii = 0; ii < seenCharacters.Count; ii++)
                {
                    endHash[seenCharacters[ii].CharLoc] = new RangeTarget(seenCharacters[ii], 0);
                    ends[ii] = seenCharacters[ii].CharLoc;
                }
            }

            //now actually decide the path to get there
            if (ends.Length > 0)
            {
                List <Loc>[] closestPaths = GetPaths(controlledChar, ends, !aimForDistance, !preThink, hasSelfEnd ? 2 : 1);
                int          closestIdx   = -1;
                for (int ii = 0; ii < ends.Length; ii++)
                {
                    if (closestPaths[ii] == null)//no path was found
                    {
                        continue;
                    }
                    if (closestPaths[ii][0] != ends[ii])                  //an incomplete path was found
                    {
                        if (endHash[ends[ii]].Origin.CharLoc != ends[ii]) // but only for pathing that goes to a tile to hit the target from
                        {
                            continue;
                        }
                    }

                    if (closestIdx == -1)
                    {
                        closestIdx = ii;
                    }
                    else
                    {
                        int cmp = comparePathValues(positioning, endHash[ends[ii]], endHash[ends[closestIdx]]);
                        if (cmp > 0)
                        {
                            closestIdx = ii;
                        }
                        else if (cmp == 0)
                        {
                            // among ties, the tile closest to the target wins
                            int curDiff = (ends[closestIdx] - endHash[ends[closestIdx]].Origin.CharLoc).DistSquared();
                            int newDiff = (ends[ii] - endHash[ends[ii]].Origin.CharLoc).DistSquared();
                            if (newDiff < curDiff)
                            {
                                closestIdx = ii;
                            }
                        }
                    }
                }

                if (closestIdx > -1)
                {
                    path       = closestPaths[closestIdx];
                    targetChar = endHash[ends[closestIdx]].Origin;
                }
            }

            //update last-seen target location if we have a target, otherwise leave it alone
            if (targetChar != null)
            {
                targetLoc = targetChar.CharLoc;
            }

            if (path != null)
            {
                //pursue the enemy if one is located
                if (path[0] == targetChar.CharLoc)
                {
                    path.RemoveAt(0);
                }

                GameAction attack = null;
                if (path.Count > 3)//if it takes more than 2 steps to get into position (list includes the loc for start position, for a total of 3), try a local attack
                {
                    Loc diff = targetChar.CharLoc - controlledChar.CharLoc;
                    if (diff.Dist8() == 1)
                    {
                        attack = TryAttackChoice(rand, controlledChar, AttackPattern, true);
                        if (attack.Type != GameAction.ActionType.Wait)
                        {
                            return(attack);
                        }
                    }
                    attack = TryAttackChoice(rand, controlledChar, AttackChoice.StandardAttack, true);
                    if (attack.Type != GameAction.ActionType.Wait)
                    {
                        return(attack);
                    }
                }
                //move if the destination can be reached
                if (path.Count > 1)
                {
                    return(SelectChoiceFromPath(controlledChar, path));
                }
                //lastly, try normal attack
                if (attack == null)
                {
                    attack = TryAttackChoice(rand, controlledChar, AttackChoice.StandardAttack, true);
                }
                if (attack.Type != GameAction.ActionType.Wait)
                {
                    return(attack);
                }
                return(new GameAction(GameAction.ActionType.Wait, Dir8.None));
            }
            else if (!playerSense && targetLoc.HasValue && targetLoc.Value != controlledChar.CharLoc)
            {
                //if no enemy is located, path to the location of the last seen enemy
                List <Loc>[] paths = GetPaths(controlledChar, new Loc[1] {
                    targetLoc.Value
                }, false, !preThink);
                path = paths[0];
                if (path.Count > 1)
                {
                    return(SelectChoiceFromPath(controlledChar, path));
                }
                else
                {
                    targetLoc = null;
                }
            }

            return(null);
        }