[Solved]AttackTargetFinder.BestAttackTarget Not returning target.

Started by BrokenValkyrie, January 14, 2018, 01:35:02 PM

Previous topic - Next topic

BrokenValkyrie

I'm currently working on animal range attack mod. I've run into problem that involves embrasure mod. Pawns and turret will get target just fine when enclosed in embrasure but not animals. However if animal can physically reach the target, they shoot through embrasure fine.

What I know is BestAttackTarget() will check if pawn currentEffectiveRange verb is a range one. I have corrected this for animals by patching the check for range verb. How ever I'm still not getting a target.

Here the attempted retrieval of a target. I have checked the parameter over and over and even copied parameter from turret. I'm sure there no mistake made in this regard.

shootable = (Thing)AttackTargetFinder.BestShootTargetFromCurrentPosition(pawn, (Predicate<Thing>)(x =>
              x is Pawn), verb.verbProps.range, verb.verbProps.minRange, TargetScanFlags.NeedThreat);


Best shoot target call BestAttackTarget()

public static IAttackTarget BestShootTargetFromCurrentPosition(IAttackTargetSearcher searcher, Predicate<Thing> validator, float maxDistance, float minDistance, TargetScanFlags flags)
{
return AttackTargetFinder.BestAttackTarget(searcher, flags, validator, minDistance, maxDistance, default(IntVec3), 3.40282347E+38f, false);
}



The code for BestAttackTarget()

// Verse.AI.AttackTargetFinder
public static IAttackTarget BestAttackTarget(IAttackTargetSearcher searcher, TargetScanFlags flags, Predicate<Thing> validator = null, float minDist = 0f, float maxDist = 9999f, IntVec3 locus = default(IntVec3), float maxTravelRadiusFromLocus = 3.40282347E+38f, bool canBash = false)
{
Thing searcherThing = searcher.Thing;
Pawn searcherPawn = searcher as Pawn;
Verb verb = searcher.CurrentEffectiveVerb;
if (verb == null)
{
Log.Error("BestAttackTarget with " + searcher + " who has no attack verb.");
return null;
}
bool onlyTargetMachines = verb != null && verb.IsEMP();
float minDistanceSquared = minDist * minDist;
float num = maxTravelRadiusFromLocus + verb.verbProps.range;
float maxLocusDistSquared = num * num;
Func<IntVec3, bool> losValidator = null;
if ((byte)(flags & TargetScanFlags.LOSBlockableByGas) != 0)
{
losValidator = delegate(IntVec3 vec3)
{
Gas gas = vec3.GetGas(searcherThing.Map);
return gas == null || !gas.def.gas.blockTurretTracking;
};
}
Predicate<IAttackTarget> innerValidator = delegate(IAttackTarget t)
{
Thing thing = t.Thing;
if (t == searcher)
{
return false;
}
if (minDistanceSquared > 0f && (float)(searcherThing.Position - thing.Position).LengthHorizontalSquared < minDistanceSquared)
{
return false;
}
if (maxTravelRadiusFromLocus < 9999f && (float)(thing.Position - locus).LengthHorizontalSquared > maxLocusDistSquared)
{
return false;
}
if (!searcherThing.HostileTo(thing))
{
return false;
}
if (validator != null && !validator(thing))
{
return false;
}
if ((byte)(flags & TargetScanFlags.NeedLOSToAll) != 0 && !searcherThing.CanSee(thing, losValidator))
{
if (t is Pawn)
{
if ((byte)(flags & TargetScanFlags.NeedLOSToPawns) != 0)
{
return false;
}
}
else if ((byte)(flags & TargetScanFlags.NeedLOSToNonPawns) != 0)
{
return false;
}
}
if ((byte)(flags & TargetScanFlags.NeedThreat) != 0 && t.ThreatDisabled())
{
return false;
}
Pawn pawn = t as Pawn;
if (onlyTargetMachines && pawn != null && pawn.RaceProps.IsFlesh)
{
return false;
}
if ((byte)(flags & TargetScanFlags.NeedNonBurning) != 0 && thing.IsBurning())
{
return false;
}
if (searcherThing.def.race != null && searcherThing.def.race.intelligence >= Intelligence.Humanlike)
{
CompExplosive compExplosive = thing.TryGetComp<CompExplosive>();
if (compExplosive != null && compExplosive.wickStarted)
{
return false;
}
}
if (thing.def.size.x == 1 && thing.def.size.z == 1)
{
if (thing.Position.Fogged(thing.Map))
{
return false;
}
}
else
{
bool flag2 = false;
CellRect.CellRectIterator iterator = thing.OccupiedRect().GetIterator();
while (!iterator.Done())
{
if (!iterator.Current.Fogged(thing.Map))
{
flag2 = true;
break;
}
iterator.MoveNext();
}
if (!flag2)
{
return false;
}
}
return true;
};
if (AttackTargetFinder.HasRangedAttack(searcher))
{
AttackTargetFinder.tmpTargets.Clear();
AttackTargetFinder.tmpTargets.AddRange(searcherThing.Map.attackTargetsCache.GetPotentialTargetsFor(searcher));
if ((byte)(flags & TargetScanFlags.NeedReachable) != 0)
{
Predicate<IAttackTarget> oldValidator = innerValidator;
innerValidator = ((IAttackTarget t) => oldValidator(t) && AttackTargetFinder.CanReach(searcherThing, t.Thing, canBash));
}
bool flag = false;
if (searcherThing.Faction != Faction.OfPlayer)
{
for (int i = 0; i < AttackTargetFinder.tmpTargets.Count; i++)
{
IAttackTarget attackTarget = AttackTargetFinder.tmpTargets[i];
if (attackTarget.Thing.Position.InHorDistOf(searcherThing.Position, maxDist) && innerValidator(attackTarget) && AttackTargetFinder.CanShootAtFromCurrentPosition(attackTarget, searcher, verb))
{
flag = true;
break;
}
}
}
IAttackTarget result;
if (flag)
{
AttackTargetFinder.tmpTargets.RemoveAll((IAttackTarget x) => !x.Thing.Position.InHorDistOf(searcherThing.Position, maxDist) || !innerValidator(x));
result = AttackTargetFinder.GetRandomShootingTargetByScore(AttackTargetFinder.tmpTargets, searcher, verb);
}
else
{
Predicate<Thing> validator2;
if ((byte)(flags & TargetScanFlags.NeedReachableIfCantHitFromMyPos) != 0 && (byte)(flags & TargetScanFlags.NeedReachable) == 0)
{
validator2 = ((Thing t) => innerValidator((IAttackTarget)t) && (AttackTargetFinder.CanReach(searcherThing, t, canBash) || AttackTargetFinder.CanShootAtFromCurrentPosition((IAttackTarget)t, searcher, verb)));
}
else
{
validator2 = ((Thing t) => innerValidator((IAttackTarget)t));
}
result = (IAttackTarget)GenClosest.ClosestThing_Global(searcherThing.Position, AttackTargetFinder.tmpTargets, maxDist, validator2, null);
}
AttackTargetFinder.tmpTargets.Clear();
return result;
}
if (searcherPawn != null && searcherPawn.mindState.duty != null && searcherPawn.mindState.duty.radius > 0f && !searcherPawn.InMentalState)
{
Predicate<IAttackTarget> oldValidator = innerValidator;
innerValidator = ((IAttackTarget t) => oldValidator(t) && t.Thing.Position.InHorDistOf(searcherPawn.mindState.duty.focus.Cell, searcherPawn.mindState.duty.radius));
}
IntVec3 position = searcherThing.Position;
Map map = searcherThing.Map;
ThingRequest thingReq = ThingRequest.ForGroup(ThingRequestGroup.AttackTarget);
PathEndMode peMode = PathEndMode.Touch;
Pawn searcherPawn2 = searcherPawn;
Danger maxDanger = Danger.Deadly;
bool canBash2 = canBash;
TraverseParms traverseParams = TraverseParms.For(searcherPawn2, maxDanger, TraverseMode.ByPawn, canBash2);
float maxDist2 = maxDist;
Predicate<Thing> validator3 = (Thing x) => innerValidator((IAttackTarget)x);
int searchRegionsMax = (maxDist <= 800f) ? 40 : -1;
IAttackTarget attackTarget2 = (IAttackTarget)GenClosest.ClosestThingReachable(position, map, thingReq, peMode, traverseParams, maxDist2, validator3, null, 0, searchRegionsMax, false, RegionType.Set_Passable, false);
if (attackTarget2 != null && PawnUtility.ShouldCollideWithPawns(searcherPawn))
{
IAttackTarget attackTarget3 = AttackTargetFinder.FindBestReachableMeleeTarget(innerValidator, searcherPawn, maxDist, canBash);
if (attackTarget3 != null)
{
float lengthHorizontal = (searcherPawn.Position - attackTarget2.Thing.Position).LengthHorizontal;
float lengthHorizontal2 = (searcherPawn.Position - attackTarget3.Thing.Position).LengthHorizontal;
if (Mathf.Abs(lengthHorizontal - lengthHorizontal2) < 50f)
{
attackTarget2 = attackTarget3;
}
}
}
return attackTarget2;
}


Here how it checks if range verb is present

private static bool HasRangedAttack(IAttackTargetSearcher t)
{
Verb currentEffectiveVerb = t.CurrentEffectiveVerb;
return currentEffectiveVerb != null && !currentEffectiveVerb.verbProps.MeleeRange;
}

MeleeRange just check if the verb range is less than 1.1 and return true if so . I tested MeleeRange check and it works fine.

I am all out of idea why I'm not getting target back and I am hoping I could get some idea.

I am planning to copy the whole code to harmony and just use log warning on each line of interest. If I'm not getting answer from log warning, I might do this instead https://ludeon.com/forums/index.php?topic=25603.0 .

BrokenValkyrie

#1
Just an update, Animal is successfully detected to have range. From testing I found out this code is returning 0 potential target for animal. This is before any further filter is applied. At the moment I don't know it does not return any target.

AttackTargetFinder.tmpTargets.AddRange(searcherThing.Map.attackTargetsCache.GetPotentialTargetsFor(searcher));


*Edit
Found out animals are faction less, which is one reason it didn't return any result.

This code should read all hostile faction in AttackTargetCache. Still not returning anything.

foreach (Pawn current2 in this.pawnsInAggroMentalState)
{
if (thing.HostileTo(current2))
{
AttackTargetsCache.targets.Add(current2);
}
}


*Edit 2
I actually know why now, above code only checks for target in aggro state. Manhunter animal can't look for hostile faction and can only look for hostile pawns in aggro state, with testing they will target player pawn only if they're in state like manhunter. I'll need to write a custom class to return a proper list.

BrokenValkyrie

Just for the sake of closure and for the small chance some one else is having the same issue, here how I solved the problem. I created another static AttackFinderClass and trimmed it down for my purpose. Because harmony can accept static class, I was able to call method from the class without issue.

This snippet of code has been transformed from

AttackTargetFinder.tmpTargets.AddRange(searcherThing.Map.attackTargetsCache.GetPotentialTargetsFor(searcher));


into this. This code fetch all targetable Thing. There no need to validate it since a very detailed predicate already exist to filter the list.

ThingRequest thingReq = ThingRequest.ForGroup(ThingRequestGroup.AttackTarget);
IEnumerable<Thing> searchSet = searcherThing.Map.listerThings.ThingsMatching(thingReq);

foreach (IAttackTarget iTarget in searchSet)
{
ARA_AttackTargetFinder.tmpTargets.Add(iTarget);
}


As a note, getting AttackTargetFinder to recognize range attack from animal messed with the search algorithm. Now the core code only get result under hasRangeAttack, but it was never intended to support manhunter animal so no result come back. It is now fixed since I created a custom AttackTargetFinder class to use for range animal only.