/// <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); }
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); }
/// <summary> /// Can the caster use this ability at all? /// </summary> /// <param name="caster">Caster wanting to use ability.</param> /// <param name="target">Target if any to use ability on.</param> /// <returns>True if they can, false if not.</returns> public bool CanPawnUseThisAbility(Pawn caster, LocalTargetInfo target) { //if (!appliedHediffs.NullOrEmpty()) // return false; if (appliedHediffs.Count > 0 && !appliedHediffs.Any(hediffDef => caster.health.hediffSet.HasHediff(hediffDef))) { return(false); } if (!Worker.CanPawnUseThisAbility(this, caster, target)) { return(false); } if (!needEnemyTarget) { return(true); } if (!usedOnCaster && target.IsValid) { var distance = Math.Abs(caster.Position.DistanceTo(target.Cell)); //Log.Message("CanPawnUseThisAbility.distance=" + distance); if (distance < minRange || distance > maxRange) { return(false); } //if (needSeeingTarget && !GenSight.LineOfSight(caster.Position, target.Cell, caster.Map)) if (needSeeingTarget && !AbilityUtility.LineOfSightLocalTarget(caster, target, true)) { return(false); } } //Valid ability to use. return(true); }
/// <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); } } }
/// <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); }