public static bool PreFix(ref Toil __result, TargetIndex targetInd, Action hitAction) { Toil followAndAttack = new Toil(); followAndAttack.tickAction = delegate() { Pawn actor = followAndAttack.actor; Job curJob = actor.jobs.curJob; JobDriver curDriver = actor.jobs.curDriver; Thing thing = curJob.GetTarget(targetInd).Thing; Pawn pawn = thing as Pawn; if (thing.Spawned && !ReachabilityImmediate.CanReachImmediate(actor, pawn, PathEndMode.Touch)) { bool flag; if (actor == null) { flag = (null != null); } else { Pawn_MindState mindState = actor.mindState; flag = ((mindState?.meleeThreat) != null); } if (!flag && pawn != null && pawn != null && CamoUtility.IsTargetHidden(pawn, actor)) { CamoAIUtility.CorrectJob(actor, pawn); } } if (!thing.Spawned) { curDriver.ReadyForNextToil(); return; } if (thing != actor.pather.Destination.Thing || (!actor.pather.Moving && !ReachabilityImmediate.CanReachImmediate(actor, thing, PathEndMode.Touch))) { actor.pather.StartPath(thing, PathEndMode.Touch); return; } if (ReachabilityImmediate.CanReachImmediate(actor, thing, PathEndMode.Touch)) { if (pawn != null && pawn.Downed && !curJob.killIncappedTarget) { curDriver.ReadyForNextToil(); return; } hitAction(); } }; followAndAttack.defaultCompleteMode = ToilCompleteMode.Never; __result = followAndAttack; return(false); }
public static Toil UninstallIfMinifiable(TargetIndex thingInd) { Toil uninstallIfMinifiable = new Toil().FailOnDestroyedNullOrForbidden(thingInd); uninstallIfMinifiable.initAction = delegate { Pawn actor = uninstallIfMinifiable.actor; JobDriver curDriver = actor.jobs.curDriver; Thing thing = actor.CurJob.GetTarget(thingInd).Thing; if (thing.def.Minifiable) { curDriver.uninstallWorkLeft = thing.def.building.uninstallWork; } else { curDriver.ReadyForNextToil(); } }; uninstallIfMinifiable.tickAction = delegate { Pawn actor = uninstallIfMinifiable.actor; JobDriver curDriver = actor.jobs.curDriver; Job curJob = actor.CurJob; curDriver.uninstallWorkLeft -= actor.GetStatValue(StatDefOf.ConstructionSpeed, true); if (curDriver.uninstallWorkLeft <= 0f) { Thing thing = curJob.GetTarget(thingInd).Thing; MinifiedThing minifiedThing = thing.MakeMinified(); GenSpawn.Spawn(minifiedThing, thing.Position, uninstallIfMinifiable.actor.Map, WipeMode.Vanish); curJob.SetTarget(thingInd, minifiedThing); actor.jobs.curDriver.ReadyForNextToil(); return; } }; uninstallIfMinifiable.defaultCompleteMode = ToilCompleteMode.Never; uninstallIfMinifiable.WithProgressBar(thingInd, () => 1f - uninstallIfMinifiable.actor.jobs.curDriver.uninstallWorkLeft / uninstallIfMinifiable.actor.CurJob.targetA.Thing.def.building.uninstallWork, false, -0.5f); return(uninstallIfMinifiable); }
public static IEnumerable <Toil> MakeFeedToils(JobDriver thisDriver, Pawn actor, LocalTargetInfo TargetA, float workLeft, Action effect, Func <Pawn, Pawn, bool> stopCondition) { yield return(Toils_Reserve.Reserve(TargetIndex.A)); Toil gotoToil = Toils_Goto.GotoThing(TargetIndex.A, PathEndMode.Touch); yield return(gotoToil); Toil grappleToil = new Toil() { initAction = delegate { MoteMaker.MakeColonistActionOverlay(actor, ThingDefOf.Mote_ColonistAttacking); workLeft = JobDriver_Feed.BaseFeedTime; Pawn victim = (Pawn)TargetA.Thing; if (victim != null) { if (victim.InAggroMentalState || victim.Faction != actor.Faction) { if (!JecsTools.GrappleUtility.CanGrapple(actor, victim)) { thisDriver.EndJobWith(JobCondition.Incompletable); } } GenClamor.DoClamor(actor, 10f, ClamorDefOf.Harm); if (!AllowFeeding(actor, victim)) { actor.jobs.EndCurrentJob(JobCondition.Incompletable); } if (actor?.VampComp()?.Bloodline?.bloodlineHediff?.CompProps <HediffCompProperties_VerbGiver>()?.verbs is List <VerbProperties> verbProps) { if (actor?.VerbTracker?.AllVerbs is List <Verb> verbs) { if (verbs.Find(x => verbProps.Contains(x.verbProps)) is Verb_MeleeAttack v) { victim.TakeDamage(new DamageInfo(v.verbProps.meleeDamageDef, v.verbProps.meleeDamageBaseAmount, 0, -1, actor)); } } } victim.stances.stunner.StunFor((int)BaseFeedTime); } } }; yield return(grappleToil); Toil feedToil = new Toil() { tickAction = delegate { Pawn victim = (Pawn)TargetA.Thing; if (victim == null || !victim.Spawned || victim.Dead) { thisDriver.ReadyForNextToil(); } workLeft--; if (workLeft <= 0f) { if (actor?.VampComp() is CompVampire v && v.IsVampire && actor.Faction == Faction.OfPlayer) { MoteMaker.ThrowText(actor.DrawPos, actor.Map, "XP +" + 15); v.XP += 15; } workLeft = BaseFeedTime; MoteMaker.MakeColonistActionOverlay(actor, ThingDefOf.Mote_ColonistAttacking); effect(); if ((victim.HostileTo(actor.Faction) || victim.IsPrisoner) && !JecsTools.GrappleUtility.CanGrapple(actor, victim)) { thisDriver.EndJobWith(JobCondition.Incompletable); } if (!stopCondition(actor, victim)) { thisDriver.ReadyForNextToil(); } else { if (victim != null && !victim.Dead) { victim.stances.stunner.StunFor((int)BaseFeedTime); PawnUtility.ForceWait((Pawn)TargetA.Thing, (int)BaseFeedTime, actor); } } } },
public static bool DriverTick(JobDriver __instance) { try { __instance.ticksLeftThisToil--; __instance.debugTicksSpentThisToil++; if (get_CurToil2(__instance) == null) { if (!__instance.pawn.stances.FullBodyBusy || get_CanStartNextToilInBusyStance2(__instance)) { __instance.ReadyForNextToil(); } } else { if (CheckCurrentToilEndOrFail2(__instance)) { return(false); } if (curToilCompleteMode(__instance) == ToilCompleteMode.Delay) { if (__instance.ticksLeftThisToil > 0) { goto IL_0099; } __instance.ReadyForNextToil(); } else { if (curToilCompleteMode(__instance) != ToilCompleteMode.FinishedBusy || __instance.pawn.stances.FullBodyBusy) { goto IL_0099; } __instance.ReadyForNextToil(); } return(false); } goto end_IL_0000; IL_01b8: if (__instance.job.mote != null) { __instance.job.mote.Maintain(); } goto end_IL_0000; IL_0099: if (wantBeginNextToil(__instance)) { TryActuallyStartNextToil(__instance); return(false); } if (curToilCompleteMode(__instance) == ToilCompleteMode.Instant && __instance.debugTicksSpentThisToil > 300) { Log.Error(string.Concat(__instance.pawn, " had to be broken from frozen state. He was doing job ", __instance.job, ", toilindex=", curToilIndex)); __instance.ReadyForNextToil(); return(false); } Job startingJob = __instance.pawn.CurJob; int startingJobId = startingJob.loadID; if (get_CurToil2(__instance) != null && get_CurToil2(__instance).preTickActions != null) { Toil curToil = get_CurToil2(__instance); for (int i = 0; i < curToil.preTickActions.Count; i++) { curToil.preTickActions[i](); if (JobChanged() || get_CurToil2(__instance) != curToil || wantBeginNextToil(__instance)) { return(false); } } } Toil gct = get_CurToil2(__instance); if (gct.tickAction == null) { goto IL_01b8; } gct.tickAction(); if (!JobChanged()) { goto IL_01b8; } end_IL_0000: bool JobChanged() { if (__instance.pawn.CurJob == startingJob) { return(__instance.pawn.CurJob.loadID != startingJobId); } return(true); } } catch (Exception exception) { JobUtility.TryStartErrorRecoverJob(__instance.pawn, "Exception in JobDriver tick for pawn " + __instance.pawn.ToStringSafe(), exception, __instance); } return(false); }
public static bool TryActuallyStartNextToil(JobDriver __instance) { if (!__instance.pawn.Spawned || (__instance.pawn.stances.FullBodyBusy && !get_CanStartNextToilInBusyStance2(__instance)) || __instance.job == null || __instance.pawn.CurJob != __instance.job) { return(false); } /* * if (get_HaveCurToil2(__instance)) * { * get_CurToil2(__instance).Cleanup(curToilIndex(__instance), __instance); * } */ if (curToilIndex(__instance) >= 0 && curToilIndex(__instance) < toils(__instance).Count&& __instance.job != null) { if (__instance.pawn.CurJob == __instance.job) { Toil curToil2 = toils(__instance)[curToilIndex(__instance)]; if (curToil2 != null) { curToil2.Cleanup(curToilIndex(__instance), __instance); } } } if (nextToilIndex(__instance) >= 0) { curToilIndex(__instance) = nextToilIndex(__instance); nextToilIndex(__instance) = -1; } else { curToilIndex(__instance)++; } wantBeginNextToil(__instance) = false; if (!get_HaveCurToil2(__instance)) { if (__instance.pawn.stances != null && __instance.pawn.stances.curStance.StanceBusy) { Log.ErrorOnce(__instance.pawn.ToStringSafe() + " ended job " + __instance.job.ToStringSafe() + " due to running out of toils during a busy stance.", 6453432); } __instance.EndJobWith(JobCondition.Succeeded); return(false); } __instance.debugTicksSpentThisToil = 0; Toil curToil = get_CurToil2(__instance); if (curToil != null) { __instance.ticksLeftThisToil = curToil.defaultDuration; curToilCompleteMode(__instance) = curToil.defaultCompleteMode; } if (CheckCurrentToilEndOrFail2(__instance)) { return(false); } curToil = get_CurToil2(__instance); Toil gct = get_CurToil2(__instance); if (gct != null && gct.preInitActions != null) { List <Action> preInitActions = gct.preInitActions; for (int i = 0; i < preInitActions.Count; i++) { try { gct = get_CurToil2(__instance); if (gct != null) { preInitActions = gct.preInitActions; } else { break; } preInitActions[i](); } catch (Exception exception) { JobUtility.TryStartErrorRecoverJob(__instance.pawn, "JobDriver threw exception in preInitActions[" + i + "] for pawn " + __instance.pawn.ToStringSafe(), exception, __instance); return(false); } if (get_CurToil2(__instance) != curToil) { break; } gct = get_CurToil2(__instance); if (gct != null) { preInitActions = gct.preInitActions; } else { break; } } } Toil gct2 = get_CurToil2(__instance); if (gct2 == curToil) { if (gct2 != null) { if (gct2.initAction != null) { try { gct2.initAction(); } catch (Exception exception2) { JobUtility.TryStartErrorRecoverJob(__instance.pawn, "JobDriver threw exception in initAction for pawn " + __instance.pawn.ToStringSafe(), exception2, __instance); return(false); } } if (!__instance.ended && curToilCompleteMode(__instance) == ToilCompleteMode.Instant && get_CurToil2(__instance) == curToil) { __instance.ReadyForNextToil(); } } } return(false); }
public static bool StartJob(Pawn_JobTracker __instance, Job newJob, JobCondition lastJobEndCondition = JobCondition.None, ThinkNode jobGiver = null, bool resumeCurJobAfterwards = false, bool cancelBusyStances = true, ThinkTreeDef thinkTree = null, JobTag?tag = null, bool fromQueue = false, bool canReturnCurJobToPool = false) { __instance.startingNewJob = true; try { if (!fromQueue && (!Find.TickManager.Paused || __instance.lastJobGivenAtFrame == RealTime.frameCount)) { ++__instance.jobsGivenThisTick; if (Prefs.DevMode) { __instance.jobsGivenThisTickTextual = __instance.jobsGivenThisTickTextual + "(" + newJob.ToString() + ") "; } } __instance.lastJobGivenAtFrame = RealTime.frameCount; if (__instance.jobsGivenThisTick > 10) { string givenThisTickTextual = __instance.jobsGivenThisTickTextual; __instance.jobsGivenThisTick = 0; __instance.jobsGivenThisTickTextual = ""; __instance.startingNewJob = false; __instance.pawn.ClearReservationsForJob(newJob); JobUtility.TryStartErrorRecoverJob(__instance.pawn, __instance.pawn.ToStringSafe <Pawn>() + " started 10 jobs in one tick. newJob=" + newJob.ToStringSafe <Job>() + " jobGiver=" + jobGiver.ToStringSafe <ThinkNode>() + " jobList=" + givenThisTickTextual); } else { if (__instance.debugLog) { __instance.DebugLogEvent("StartJob [" + (object)newJob + "] lastJobEndCondition=" + (object)lastJobEndCondition + ", jobGiver=" + (object)jobGiver + ", cancelBusyStances=" + cancelBusyStances.ToString()); } Pawn_StanceTracker stances = __instance.pawn.stances; //changed if (cancelBusyStances && stances != null && stances.FullBodyBusy) //changed { stances.CancelBusyStanceHard(); } if (__instance.curJob != null) { if (lastJobEndCondition == JobCondition.None) { Log.Warning(__instance.pawn.ToString() + " starting job " + (object)newJob + " from JobGiver " + (object)newJob.jobGiver + " while already having job " + (object)__instance.curJob + " without a specific job end condition."); lastJobEndCondition = JobCondition.InterruptForced; } if (resumeCurJobAfterwards && __instance.curJob.def.suspendable) { __instance.jobQueue.EnqueueFirst(__instance.curJob); if (__instance.debugLog) { __instance.DebugLogEvent(" JobQueue EnqueueFirst curJob: " + (object)__instance.curJob); } __instance.CleanupCurrentJob(lastJobEndCondition, false, cancelBusyStances); } else { __instance.CleanupCurrentJob(lastJobEndCondition, true, cancelBusyStances, canReturnCurJobToPool); } } if (newJob == null) { Log.Warning(__instance.pawn.ToString() + " tried to start doing a null job."); } else { newJob.startTick = Find.TickManager.TicksGame; if (__instance.pawn.Drafted || newJob.playerForced) { newJob.ignoreForbidden = true; newJob.ignoreDesignations = true; } __instance.curJob = newJob; __instance.curJob.jobGiverThinkTree = thinkTree; __instance.curJob.jobGiver = jobGiver; JobDriver cDriver = __instance.curJob.MakeDriver(__instance.pawn); //changed __instance.curDriver = cDriver; //changed bool flag = fromQueue; if (__instance.curDriver.TryMakePreToilReservations(!flag)) { Job newJob1 = __instance.TryOpportunisticJob(newJob); if (newJob1 != null) { __instance.jobQueue.EnqueueFirst(newJob); __instance.curJob = (Job)null; __instance.curDriver = (JobDriver)null; __instance.StartJob(newJob1); } else { if (tag.HasValue) { __instance.pawn.mindState.lastJobTag = tag.Value; } cDriver.SetInitialPosture(); //changed cDriver.Notify_Starting(); //changed cDriver.SetupToils(); //changed cDriver.ReadyForNextToil(); //changed } } else if (flag) { __instance.EndCurrentJob(JobCondition.QueuedNoLongerValid); } else { Log.Warning("TryMakePreToilReservations() returned false for a non-queued job right after StartJob(). This should have been checked before. curJob=" + __instance.curJob.ToStringSafe <Job>()); __instance.EndCurrentJob(JobCondition.Errored); } } } } finally { __instance.startingNewJob = false; } return(false); }
public static IEnumerable <Toil> MakeFeedToils(JobDef job, JobDriver thisDriver, Pawn actor, LocalTargetInfo TargetA, ThoughtDef victimThoughtDef, ThoughtDef actorThoughtDef, float workLeft, Action effect, Func <Pawn, Pawn, bool> stopCondition, bool needsGrapple = true, bool cleansWound = true, bool neverGiveUp = false) { yield return(Toils_Reserve.Reserve(TargetIndex.A)); Toil gotoToil = actor?.Faction == TargetA.Thing?.Faction && (!actor.InAggroMentalState && !((Pawn)TargetA.Thing).InAggroMentalState) ? Toils_Goto.GotoThing(TargetIndex.A, PathEndMode.ClosestTouch) : Toils_Goto.GotoThing(TargetIndex.A, PathEndMode.Touch); yield return(gotoToil); Toil grappleToil = new Toil() { initAction = delegate { MoteMaker.MakeColonistActionOverlay(actor, ThingDefOf.Mote_ColonistAttacking); workLeft = BaseFeedTime; Pawn victim = TargetA.Thing as Pawn; if (victim != null) { // if (!AllowFeeding(actor, victim)) // { // actor.jobs.EndCurrentJob(JobCondition.Incompletable); // } if (actor.InAggroMentalState || victim.InAggroMentalState || victim.Faction != actor.Faction) { if (needsGrapple) { int grappleBonus = actor is PawnTemporary ? 100 : 0; if (!JecsTools.GrappleUtility.TryGrapple(actor, victim, grappleBonus)) { thisDriver.EndJobWith(JobCondition.Incompletable); PawnUtility.ForceWait(actor, (int)(BaseFeedTime * 0.15f)); return; } } } if (actor.IsVampire()) { VampireBiteUtility.MakeNew(actor, victim); } victim.stances.stunner.StunFor((int)BaseFeedTime, actor); } } }; yield return(grappleToil); Toil feedToil = new Toil() { tickAction = delegate { //try //{ if (TargetA.Thing is Pawn victim && victim.Spawned && !victim.Dead) { workLeft--; VampireWitnessUtility.HandleWitnessesOf(job, actor, victim); if (victim?.needs?.mood?.thoughts?.memories != null) { Thought_Memory victimThought = null; if (victimThoughtDef != null) { victimThought = (Thought_Memory)ThoughtMaker.MakeThought(victimThoughtDef); } if (victimThought != null) { victim.needs.mood.thoughts.memories.TryGainMemory(victimThought); } } if (actor?.needs?.mood?.thoughts?.memories != null) { Thought_Memory actorThought = null; if (actorThoughtDef != null) { actorThought = (Thought_Memory)ThoughtMaker.MakeThought(actorThoughtDef); } if (actorThought != null) { actor.needs.mood.thoughts.memories.TryGainMemory(actorThought); } } if (workLeft <= 0f) { if (actor?.VampComp() is CompVampire v && v.IsVampire && actor.Faction == Faction.OfPlayer) { MoteMaker.ThrowText(actor.DrawPos, actor.Map, "XP +" + 15); v.XP += 15; workLeft = BaseFeedTime; MoteMaker.MakeColonistActionOverlay(actor, ThingDefOf.Mote_ColonistAttacking); } effect(); if (victim != null && !victim.Dead && needsGrapple) { int victimBonus = (victim.VampComp() is CompVampire victimVampComp) ? -victimVampComp.Generation + 14 : 0; int actorBonus = 0; if (actor?.VampComp() is CompVampire v2 && v2.IsVampire) { actorBonus = -v2.Generation + 14; } if (!JecsTools.GrappleUtility.TryGrapple(actor, victim, actorBonus, victimBonus)) { thisDriver.EndJobWith(JobCondition.Incompletable); } } if (!stopCondition(actor, victim)) { thisDriver.ReadyForNextToil(); if (actor.IsVampire() && cleansWound) { VampireBiteUtility.CleanBite(actor, victim); } } else { if (victim != null && !victim.Dead) { victim.stances.stunner.StunFor((int)BaseFeedTime, actor); PawnUtility.ForceWait((Pawn)TargetA.Thing, (int)BaseFeedTime, actor); } } } }