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