Пример #1
0
        /// <summary>
        ///     Figures out the best location to use this Ability at. Default implementation returns the enemy target, closest ally
        ///     or the caster.
        /// </summary>
        /// <param name="abilityDef">Ability Def for the AI.</param>
        /// <param name="pawn">Pawn to take in account.</param>
        /// <returns>Targeting location or Pawn.</returns>
        public virtual LocalTargetInfo TargetAbilityFor(AbilityAIDef abilityDef, Pawn pawn)
        {
            if (abilityDef.usedOnCaster)
            {
                return(pawn);
            }
            if (abilityDef.canTargetAlly)
            {
                return(GenClosest.ClosestThingReachable(pawn.Position, pawn.Map,
                                                        ThingRequest.ForGroup(ThingRequestGroup.Pawn), PathEndMode.OnCell,
                                                        TraverseParms.For(TraverseMode.NoPassClosedDoors), abilityDef.maxRange,
                                                        thing => AbilityUtility.AreAllies(pawn, thing)));
            }
            if (pawn.mindState.enemyTarget != null && pawn.mindState.enemyTarget is Pawn targetPawn)
            {
                if (!targetPawn.Dead)
                {
                    return(pawn.mindState.enemyTarget);
                }
            }
            else
            {
                if (pawn.mindState.enemyTarget != null && !(pawn.mindState.enemyTarget is Corpse))
                {
                    return(pawn.mindState.enemyTarget);
                }
            }

            return(null);
        }
Пример #2
0
        /// <summary>
        ///     Picks the best candidate Pawn out of up to 10 other.
        /// </summary>
        /// <param name="abilityDef">Ability Def to optionally take in account.</param>
        /// <param name="pawn">Pawn using the Ability.</param>
        /// <returns>Candidate Pawn if found, null if not.</returns>
        public virtual Pawn PickBestClosestPawn(AbilityAIDef abilityDef, Pawn pawn)
        {
            Pawn bestPawn   = null;
            var  bestHealth = 1f;

            var checkedThings = new List <Thing>();

            //Check 10 closest for optimal target.
            for (var i = 0; i < 10; i++)
            {
                var foundThing = GenClosest.ClosestThingReachable(pawn.Position, pawn.Map,
                                                                  ThingRequest.ForGroup(ThingRequestGroup.Pawn), PathEndMode.OnCell,
                                                                  TraverseParms.For(TraverseMode.NoPassClosedDoors), abilityDef.maxRange,
                                                                  thing => pawn != thing && !checkedThings.Contains(thing) && AbilityUtility.AreAllies(pawn, thing));

                //Found no valid candidate.
                if (foundThing == null)
                {
                    break;
                }

                checkedThings.Add(foundThing);

                if (foundThing is Pawn foundPawn)
                {
                    if (foundPawn.health.summaryHealth.SummaryHealthPercent < bestHealth)
                    {
                        bestPawn   = foundPawn;
                        bestHealth = foundPawn.health.summaryHealth.SummaryHealthPercent;
                    }
                }
            }

            return(bestPawn);
        }
        public override LocalTargetInfo TargetAbilityFor(AbilityAIDef abilityDef, Pawn pawn)
        {
            var bestPawn = PickBestClosestPawn(abilityDef, pawn);

            if (bestPawn == null)
            {
                return(base.TargetAbilityFor(abilityDef, pawn));
            }

            return(bestPawn);
        }
        public override float PowerScoreFor(AbilityAIDef abilityDef, Pawn pawn)
        {
            var baseScore = abilityDef.power;

            //Grab enemies \ allies
            var potentionalTargets = new List <Thing>(GrabTargets(abilityDef, pawn,
                                                                  CustomGrabTargetsPredicate(abilityDef, pawn), MaxTargetsToCheck));

            //Add self if can target allies.
            if (abilityDef.canTargetAlly)
            {
                potentionalTargets.Add(pawn);
            }

            //Get the highest intersecting target.
            var targetInfos = new List <LocalTargetInfo>();

            foreach (var target in potentionalTargets)
            {
                targetInfos.Add(new LocalTargetInfo(target));
            }

            var bestTarget = AbilityMaths.PickMostRadialIntersectingTarget(targetInfos, abilityDef.abilityRadius);

            //If we found no valid target, return negative power.
            if (bestTarget == LocalTargetInfo.Invalid)
            {
                return(-abilityDef.power);
            }

            //Calculate final score from best target.
            var finalScore = baseScore;

            foreach (var targetPawn in AbilityUtility.GetPawnsInsideRadius(bestTarget, pawn.Map,
                                                                           abilityDef.abilityRadius,
                                                                           predPawn => abilityDef.abilityRadiusNeedSight &&
                                                                           GenSight.LineOfSight(pawn.Position, predPawn.Position, pawn.Map, true) ||
                                                                           abilityDef.abilityRadiusNeedSight == false))
            {
                if (targetPawn.HostileTo(pawn) || targetPawn.AnimalOrWildMan()
                    ) //Hostile pawns or animals increase score.
                {
                    finalScore += abilityDef.power;
                }
                else //Friendly pawns decrease score.
                {
                    finalScore -= abilityDef.power;
                }
            }

            //Log.Message("AbilityWorker_AreaOfEffect, finalScore=" + finalScore);
            return(finalScore);
        }
        public override bool CanPawnUseThisAbility(AbilityAIDef abilityDef, Pawn pawn, LocalTargetInfo target)
        {
            //If no best pawn was found, then we should not bother using ability.
            var bestPawn = PickBestClosestPawn(abilityDef, pawn);

            if (bestPawn == null)
            {
                return(false);
            }

            return(base.CanPawnUseThisAbility(abilityDef, pawn, target));
        }
Пример #6
0
        /// <summary>
        ///     Final check to say whether the Pawn can use this Ability on the target or self. Default implementation returns true
        ///     for co-ordinates and true if the enemy is NOT Downed.
        /// </summary>
        /// <param name="abilityDef">Ability Def for the AI.</param>
        /// <param name="pawn">Pawn to take in account.</param>
        /// <param name="target">Target this ability is aiming at.</param>
        /// <returns>True if we can use this Ability. False if not.</returns>
        public virtual bool CanPawnUseThisAbility(AbilityAIDef abilityDef, Pawn pawn, LocalTargetInfo target)
        {
            if (target.HasThing)
            {
                var targetPawn = target.Thing as Pawn;

                if (!abilityDef.canTargetAlly)
                {
                    return(!targetPawn.Downed);
                }
            }


            return(true);
        }
Пример #7
0
        public override LocalTargetInfo TargetAbilityFor(AbilityAIDef abilityDef, Pawn pawn)
        {
            //Grab enemies \ allies
            List <Thing> potentionalTargets = new List <Thing>(GrabTargets(abilityDef, pawn, CustomGrabTargetsPredicate(abilityDef, pawn), MaxTargetsToCheck));

            //Add self if can target allies.
            if (abilityDef.canTargetAlly)
            {
                potentionalTargets.Add(pawn);
            }

            //Get the highest intersecting target.
            List <LocalTargetInfo> targetInfos = new List <LocalTargetInfo>();

            foreach (Thing target in potentionalTargets)
            {
                targetInfos.Add(new LocalTargetInfo(target));
            }

            LocalTargetInfo bestTarget = AbilityMaths.PickMostRadialIntersectingTarget(targetInfos, abilityDef.abilityRadius);

            return(bestTarget);
        }
Пример #8
0
        /// <summary>
        /// Recursively travels through this decision node sub nodes. Not stack friendly.
        /// </summary>
        /// <param name="caster">Caster to recurse for.</param>
        /// <returns>Ability if found. Null if none is found.</returns>
        public virtual AbilityAIDef RecursivelyGetAbility(Pawn caster)
        {
            AbilityAIDef result = TryPickAbility(caster);

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

            if (CanContinueTraversing(caster))
            {
                foreach (AbilityDecisionNode node in subNodes)
                {
                    result = node.RecursivelyGetAbility(caster);

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

            return(null);
        }
Пример #9
0
        /// <summary>
        /// Grab all viable target candidates.
        /// </summary>
        /// <param name="abilityDef">Ability Def to take in account.</param>
        /// <param name="pawn">Caster Pawn.</param>
        /// <param name="customPredicate">If set it overrides the default predicate.</param>
        /// <param name="pawnsToTest">How many pawns to test at max before stopping. Default is 30.</param>
        /// <returns>Things that are viable.</returns>
        public virtual IEnumerable <Thing> GrabTargets(AbilityAIDef abilityDef, Pawn pawn, Predicate <Thing> customPredicate = null, int pawnsToTest = 30)
        {
            //Make a list of candidates.
            List <Thing>      potentionalTargets = new List <Thing>();
            Predicate <Thing> pawnPredicate      = null;

            if (customPredicate != null)
            {
                pawnPredicate = customPredicate;
            }
            else
            if (abilityDef.canTargetAlly)
            {
                pawnPredicate = delegate(Thing thing)
                {
                    //Count own faction and faction whose goodwill they got above 50% as allies.
                    if (AbilityUtility.AreAllies(pawn, thing))
                    {
                        return(true);
                    }
                    return(false);
                }
            }
            ;
            else
            {
                pawnPredicate = delegate(Thing thing)
                {
                    Pawn thingPawn = thing as Pawn;

                    //Count anything hostile as a target.
                    if (thingPawn != null)
                    {
                        if (!thingPawn.Downed && GenHostility.HostileTo(thing, pawn))
                        {
                            return(true);
                        }
                        else
                        if (GenHostility.HostileTo(thing, pawn))
                        {
                            return(true);
                        }
                    }

                    return(false);
                }
            };

            //Max 'pawnsToTest' shall we grab.
            for (int i = 0; i < pawnsToTest; i++)
            {
                Thing grabResult = GenClosest.ClosestThingReachable(pawn.Position, pawn.Map, ThingRequest.ForGroup(ThingRequestGroup.Pawn), Verse.AI.PathEndMode.OnCell, TraverseParms.For(TraverseMode.NoPassClosedDoors),
                                                                    abilityDef.maxRange, thing => pawn != thing && !potentionalTargets.Contains(thing) && (thing.Position - pawn.Position).LengthHorizontal >= abilityDef.minRange && pawnPredicate(thing));

                //If found nothing, then break.
                if (grabResult == null)
                {
                    break;
                }

                potentionalTargets.Add(grabResult);

                yield return(grabResult);
            }
        }
    }
Пример #10
0
 /// <summary>
 /// Custom overridable Predicate for classes expanding on this algorithm.
 /// </summary>
 /// <param name="abilityDef">Ability Def to take in account.</param>
 /// <param name="pawn">Caster Pawn.</param>
 /// <returns>Optional predicate.</returns>
 public virtual Predicate <Thing> CustomGrabTargetsPredicate(AbilityAIDef abilityDef, Pawn pawn)
 {
     return(null);
 }
Пример #11
0
        protected override Job TryGiveJob(Pawn pawn)
        {
            //Do we have at least one elegible profile?
            var profiles = pawn.EligibleAIProfiles();

            /*StringBuilder builder = new StringBuilder("profiles = ");
             *
             * foreach(AbilityUserAIProfileDef profile in profiles)
             * {
             *  builder.Append(profile.defName + ", ");
             * }
             *
             * Log.Message(builder.ToString());*/

            if (profiles != null && profiles.Count() > 0)
            {
                foreach (var profile in profiles)
                {
                    if (profile != null)
                    {
                        //Traverse the decision tree.
                        //List<AbilityDecisionNode> currentNodes = new List<AbilityDecisionNode>();
                        //List<AbilityDecisionNode> nextNodes = new List<AbilityDecisionNode>();

                        //Seed root.
                        //nextNodes.Add(profile.decisionTree);

                        //Result AbilityAIDef to use.
                        AbilityAIDef useThisAbility = null;

                        if (profile.decisionTree != null)
                        {
                            useThisAbility = profile.decisionTree.RecursivelyGetAbility(pawn);
                        }

                        //Debug

                        /*int nodesTraversed = 0;
                         * AbilityDecisionNode lastNode = null;
                         *
                         * //Flat recursive iteration
                         * do
                         * {
                         *  //Add from next list to current list.
                         *  currentNodes.AddRange(nextNodes);
                         *  nextNodes.Clear();
                         *
                         *  //Check if we can continue traversing on the current level.
                         *  foreach (AbilityDecisionNode currentNode in currentNodes)
                         *  {
                         *      nodesTraversed++;
                         *
                         *      if (currentNode.CanContinueTraversing(pawn))
                         *          nextNodes.AddRange(currentNode.subNodes);
                         *
                         *      //Try picking an ability.
                         *      useThisAbility = currentNode.TryPickAbility(pawn);
                         *
                         *      //Found ability to use.
                         *      if (useThisAbility != null)
                         *      {
                         *          lastNode = currentNode;
                         *          break;
                         *      }
                         *  }
                         *
                         *  //Found ability to use.
                         *  if (useThisAbility != null)
                         *      break;
                         *
                         *  //Clear current set.
                         *  currentNodes.Clear();
                         * } while (nextNodes.Count > 0);*/

                        //Debug
                        //if (useThisAbility != null)
                        //    Log.Message("JobGiver_AIAbilityUser.TryGiveJob for '" + pawn.ThingID + "' with ability: " + useThisAbility.defName + ", while traversing " + nodesTraversed + " nodes.");
                        //else
                        //    Log.Message("JobGiver_AIAbilityUser.TryGiveJob for '" + pawn.ThingID + "' with ability: No ability, while traversing " + nodesTraversed + " nodes.");

                        if (useThisAbility != null)
                        {
                            //Debug

                            /*Log.Message("Ability '" + useThisAbility.defName + "' picked for AI.\n" +
                             *  "lastNode=" + lastNode.GetType().Name + "\n" +
                             *  "lastNode.parent=" + lastNode?.parent?.GetType()?.Name);*/

                            //Get CompAbilityUser
                            var thingComp       = pawn.AllComps.First(comp => comp.GetType() == profile.compAbilityUserClass);
                            var compAbilityUser = thingComp as CompAbilityUser;

                            if (compAbilityUser != null)
                            {
                                //Get Ability from Pawn.
                                var useAbility =
                                    compAbilityUser.AbilityData.AllPowers.First(ability =>
                                                                                ability.Def == useThisAbility.ability);

                                var reason = "";
                                //Give job.
                                if (useAbility.CanCastPowerCheck(AbilityContext.AI, out reason))
                                {
                                    var target = useThisAbility.Worker.TargetAbilityFor(useThisAbility, pawn);
                                    if (target.IsValid)
                                    {
                                        return(useAbility.UseAbility(AbilityContext.AI, target));
                                    }
                                }
                            }
                        }
                    }
                }
            }

            //No Job to give.
            //Report.
            //Log.Message("JobGiver_AIAbilityUser.TryGiveJob for '" + pawn.ThingID + "' is Invalid.");
            return(null);
        }
Пример #12
0
 /// <summary>
 /// First check on whether a Ability can be used or not. Default implementation have no special criterias.
 /// </summary>
 /// <param name="profileDef">Profile Def to check for.</param>
 /// <param name="pawn">Pawn to check for.</param>
 /// <param name="abilityDef">Ability Def to check for.</param>
 /// <returns>True if Ability can be used. False if not.</returns>
 public virtual bool CanUseAbility(AbilityUserAIProfileDef profileDef, Pawn pawn, AbilityAIDef abilityDef)
 {
     return(true);
 }
Пример #13
0
 /// <summary>
 ///     Calculates the final power score for this Ability taking the condition of the Pawn in account. Default
 ///     implementation just returns the power score.
 /// </summary>
 /// <param name="abilityDef">Ability Def for the AI.</param>
 /// <param name="pawn">Pawn to take in account.</param>
 /// <returns>Final calculated score.</returns>
 public virtual float PowerScoreFor(AbilityAIDef abilityDef, Pawn pawn)
 {
     return(abilityDef.power);
 }