Beispiel #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);
        }
Beispiel #2
0
        public override LocalTargetInfo TargetAbilityFor(AbilityAIDef abilityDef, Pawn pawn)
        {
            var bestPawn = PickBestClosestPawn(abilityDef, pawn);

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

            return(bestPawn);
        }
Beispiel #3
0
        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);
        }
Beispiel #4
0
        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));
        }
Beispiel #5
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);
        }
Beispiel #6
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);

                var foundPawn = foundThing as Pawn;

                if (foundPawn != null)
                {
                    if (foundPawn.health.summaryHealth.SummaryHealthPercent < bestHealth)
                    {
                        bestPawn   = foundPawn;
                        bestHealth = foundPawn.health.summaryHealth.SummaryHealthPercent;
                    }
                }
            }

            return(bestPawn);
        }
Beispiel #7
0
        public override LocalTargetInfo TargetAbilityFor(AbilityAIDef abilityDef, Pawn pawn)
        {
            //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);

            return(bestTarget);
        }
Beispiel #8
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);
 }
Beispiel #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.
            var 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)
                {
                    var thingPawn = thing as Pawn;

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

                    return(false);
                }
            };

            //Max 'pawnsToTest' shall we grab.
            for (var i = 0; i < pawnsToTest; i++)
            {
                var grabResult = GenClosest.ClosestThingReachable(pawn.Position, pawn.Map,
                                                                  ThingRequest.ForGroup(ThingRequestGroup.Pawn), 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);
            }
        }
    }
Beispiel #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);
 }
        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);
        }
Beispiel #12
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);
 }