Beispiel #1
0
 public void EnqueueFirst(Job j, JobTag?tag = default(JobTag?))
 {
     jobs.Insert(0, new QueuedJob(j, tag));
 }
 public QueuedJob(Job job, JobTag?tag)
 {
     this.job = job;
     this.tag = tag;
 }
 public QueuedCaravanJob(CaravanJob job, JobTag?tag)
 {
     this.job = job;
     this.tag = tag;
 }
Beispiel #4
0
 public void EnqueueLast(CaravanJob j, JobTag?tag = null)
 {
     this.jobs.Add(new QueuedCaravanJob(j, tag));
 }
 public void EnqueueLast(Job j, JobTag?tag = null)
 {
     jobs.Add(new QueuedJob(j, tag));
 }
 public void StartJob(Job newJob, JobCondition lastJobEndCondition = JobCondition.None, ThinkNode jobGiver = null, bool resumeCurJobAfterwards = false, bool cancelBusyStances = true, ThinkTreeDef thinkTree = null, JobTag?tag = default(JobTag?), bool fromQueue = false)
 {
     this.startingNewJob = true;
     try
     {
         if (!fromQueue && (!Find.TickManager.Paused || this.lastJobGivenAtFrame == RealTime.frameCount))
         {
             this.jobsGivenThisTick++;
             this.jobsGivenThisTickTextual = this.jobsGivenThisTickTextual + "(" + newJob.ToString() + ") ";
         }
         this.lastJobGivenAtFrame = RealTime.frameCount;
         if (this.jobsGivenThisTick > 10)
         {
             string text = this.jobsGivenThisTickTextual;
             this.jobsGivenThisTick        = 0;
             this.jobsGivenThisTickTextual = string.Empty;
             this.startingNewJob           = false;
             this.pawn.ClearReservationsForJob(newJob);
             this.StartErrorRecoverJob(this.pawn + " started 10 jobs in one tick. newJob=" + newJob + " jobGiver=" + jobGiver + " jobList=" + text);
         }
         else
         {
             PawnPosture     posture    = this.pawn.GetPosture();
             LayingDownState layingDown = (this.pawn.jobs != null && this.pawn.jobs.curDriver != null) ? this.pawn.jobs.curDriver.layingDown : LayingDownState.NotLaying;
             if (this.debugLog)
             {
                 this.DebugLogEvent("StartJob [" + newJob + "] lastJobEndCondition=" + lastJobEndCondition + ", jobGiver=" + jobGiver + ", cancelBusyStances=" + cancelBusyStances);
             }
             if (cancelBusyStances && this.pawn.stances.FullBodyBusy)
             {
                 this.pawn.stances.CancelBusyStanceHard();
             }
             if (this.curJob != null)
             {
                 if (lastJobEndCondition == JobCondition.None)
                 {
                     Log.Warning(this.pawn + " starting job " + newJob + " from JobGiver " + this.pawn.mindState.lastJobGiver + " while already having job " + this.curJob + " without a specific job end condition.");
                     lastJobEndCondition = JobCondition.InterruptForced;
                 }
                 if (resumeCurJobAfterwards && this.curJob.def.suspendable)
                 {
                     this.jobQueue.EnqueueFirst(this.curJob, null);
                     if (this.debugLog)
                     {
                         this.DebugLogEvent("   JobQueue EnqueueFirst curJob: " + this.curJob);
                     }
                     this.CleanupCurrentJob(lastJobEndCondition, false, cancelBusyStances);
                 }
                 else
                 {
                     this.CleanupCurrentJob(lastJobEndCondition, true, cancelBusyStances);
                 }
             }
             if (newJob == null)
             {
                 Log.Warning(this.pawn + " tried to start doing a null job.");
             }
             else
             {
                 newJob.startTick = Find.TickManager.TicksGame;
                 if (this.pawn.Drafted || newJob.playerForced)
                 {
                     newJob.ignoreForbidden    = true;
                     newJob.ignoreDesignations = true;
                 }
                 this.curJob = newJob;
                 this.pawn.mindState.lastJobGiver          = jobGiver;
                 this.pawn.mindState.lastJobGiverThinkTree = thinkTree;
                 this.curDriver = this.curJob.MakeDriver(this.pawn);
                 if (this.curDriver.TryMakePreToilReservations())
                 {
                     if (tag.HasValue)
                     {
                         this.pawn.mindState.lastJobTag = tag.Value;
                     }
                     this.curDriver.Notify_Starting();
                     this.curDriver.Notify_LastPosture(posture, layingDown);
                     this.curDriver.SetupToils();
                     this.curDriver.ReadyForNextToil();
                 }
                 else if (fromQueue)
                 {
                     this.EndCurrentJob(JobCondition.QueuedNoLongerValid, true);
                 }
                 else
                 {
                     Log.Warning("TryMakePreToilReservations() returned false for a non-queued job right after StartJob(). This should have been checked before. curJob=" + this.curJob.ToStringSafe());
                     this.EndCurrentJob(JobCondition.Errored, true);
                 }
             }
         }
     }
     finally
     {
         this.startingNewJob = false;
     }
 }
Beispiel #7
0
 public void EnqueueFirst(CaravanJob j, JobTag?tag = null)
 {
     this.jobs.Insert(0, new QueuedCaravanJob(j, tag));
 }
Beispiel #8
0
 private static void Postfix(Pawn_JobTracker __instance, Pawn ___pawn, Job newJob, JobTag?tag)
 {
     if (___pawn is Wendigo)
     {
         Log.Message(___pawn + " is starting " + newJob);
     }
 }
Beispiel #9
0
        static void Prefix(Pawn_JobTracker __instance, Job newJob, JobCondition lastJobEndCondition, ThinkNode jobGiver, bool resumeCurJobAfterwards, bool cancelBusyStances, ThinkTreeDef thinkTree, JobTag?tag, bool fromQueue)
        {
            if (newJob != null && newJob.targetB != null && newJob.targetB.Thing is StoredAmmo sa)
            {
                Pawn pawn = __instance.GetType().GetField("pawn", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(__instance) as Pawn;

                Building_TurretGunCE turret = newJob.targetA.Thing as Building_TurretGunCE;

                FieldInfo fi = sa.root.GetType().GetField("AllowAdds", BindingFlags.Instance | BindingFlags.Public);

                try
                {
                    fi.SetValue(sa.root, false);
                    IntVec3  pos;
                    Building building = sa.root as Building;
                    if (sa.forced || sa.root == null)
                    {
                        pos = pawn.Position;
                    }
                    else
                    {
                        pos = building.InteractionCell;
                    }

                    if (!CombatExtendedUtil.TryDropAmmo(sa.ammoDef, sa.ammoCount, pos, sa.root.Map, out Thing t))
                    {
                        Log.Error("Could not get ammo");
                    }
                    else
                    {
                        newJob.targetB = t;
                        newJob.count   = t.stackCount;
                    }
                }
                finally
                {
                    fi.SetValue(sa.root, true);
                }
            }
        }
Beispiel #10
0
        public bool TryTakeOrderedJob_NewTemp(Job job, JobTag?tag = JobTag.Misc, bool requestQueueing = false)
        {
            if (debugLog)
            {
                DebugLogEvent("TryTakeOrderedJob " + job);
            }
            job.playerForced = true;
            if (curJob != null && curJob.JobIsSameAs(job))
            {
                return(true);
            }
            bool num         = pawn.jobs.IsCurrentJobPlayerInterruptible();
            bool flag        = pawn.mindState.IsIdle || pawn.CurJob == null || pawn.CurJob.def.isIdle;
            bool isDownEvent = KeyBindingDefOf.QueueOrder.IsDownEvent;

            if (isDownEvent)
            {
                PlayerKnowledgeDatabase.KnowledgeDemonstrated(ConceptDefOf.QueueOrders, KnowledgeAmount.NoteTaught);
            }
            isDownEvent = isDownEvent || requestQueueing;
            if (num && (!isDownEvent || flag))
            {
                pawn.stances.CancelBusyStanceSoft();
                if (debugLog)
                {
                    DebugLogEvent("    Queueing job");
                }
                ClearQueuedJobs();
                if (job.TryMakePreToilReservations(pawn, errorOnFailed: true))
                {
                    jobQueue.EnqueueFirst(job, tag);
                    if (curJob != null)
                    {
                        curDriver.EndJobWith(JobCondition.InterruptForced);
                    }
                    else
                    {
                        CheckForJobOverride();
                    }
                    return(true);
                }
                Log.Warning("TryMakePreToilReservations() returned false right after TryTakeOrderedJob(). This should have been checked before. job=" + job.ToStringSafe());
                pawn.ClearReservationsForJob(job);
                return(false);
            }
            if (isDownEvent)
            {
                if (job.TryMakePreToilReservations(pawn, errorOnFailed: true))
                {
                    jobQueue.EnqueueLast(job, tag);
                    return(true);
                }
                Log.Warning("TryMakePreToilReservations() returned false right after TryTakeOrderedJob(). This should have been checked before. job=" + job.ToStringSafe());
                pawn.ClearReservationsForJob(job);
                return(false);
            }
            ClearQueuedJobs();
            if (job.TryMakePreToilReservations(pawn, errorOnFailed: true))
            {
                jobQueue.EnqueueLast(job, tag);
                return(true);
            }
            Log.Warning("TryMakePreToilReservations() returned false right after TryTakeOrderedJob(). This should have been checked before. job=" + job.ToStringSafe());
            pawn.ClearReservationsForJob(job);
            return(false);
        }
Beispiel #11
0
 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);
 }
Beispiel #12
0
 public void StartJob(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)
 {
     startingNewJob = true;
     try
     {
         if (!fromQueue && (!Find.TickManager.Paused || lastJobGivenAtFrame == RealTime.frameCount))
         {
             jobsGivenThisTick++;
             if (Prefs.DevMode)
             {
                 jobsGivenThisTickTextual = jobsGivenThisTickTextual + "(" + newJob.ToString() + ") ";
             }
         }
         lastJobGivenAtFrame = RealTime.frameCount;
         if (jobsGivenThisTick > 10)
         {
             string text = jobsGivenThisTickTextual;
             jobsGivenThisTick        = 0;
             jobsGivenThisTickTextual = "";
             startingNewJob           = false;
             pawn.ClearReservationsForJob(newJob);
             JobUtility.TryStartErrorRecoverJob(pawn, pawn.ToStringSafe() + " started 10 jobs in one tick. newJob=" + newJob.ToStringSafe() + " jobGiver=" + jobGiver.ToStringSafe() + " jobList=" + text);
             return;
         }
         if (debugLog)
         {
             DebugLogEvent(string.Concat("StartJob [", newJob, "] lastJobEndCondition=", lastJobEndCondition, ", jobGiver=", jobGiver, ", cancelBusyStances=", cancelBusyStances.ToString()));
         }
         if (cancelBusyStances && pawn.stances.FullBodyBusy)
         {
             pawn.stances.CancelBusyStanceHard();
         }
         if (curJob != null)
         {
             if (lastJobEndCondition == JobCondition.None)
             {
                 Log.Warning(string.Concat(pawn, " starting job ", newJob, " from JobGiver ", newJob.jobGiver, " while already having job ", curJob, " without a specific job end condition."));
                 lastJobEndCondition = JobCondition.InterruptForced;
             }
             if (resumeCurJobAfterwards && curJob.def.suspendable)
             {
                 jobQueue.EnqueueFirst(curJob);
                 if (debugLog)
                 {
                     DebugLogEvent("   JobQueue EnqueueFirst curJob: " + curJob);
                 }
                 CleanupCurrentJob(lastJobEndCondition, releaseReservations: false, cancelBusyStances);
             }
             else
             {
                 CleanupCurrentJob(lastJobEndCondition, releaseReservations: true, cancelBusyStances, canReturnCurJobToPool);
             }
         }
         if (newJob == null)
         {
             Log.Warning(string.Concat(pawn, " tried to start doing a null job."));
             return;
         }
         newJob.startTick = Find.TickManager.TicksGame;
         if (pawn.Drafted || newJob.playerForced)
         {
             newJob.ignoreForbidden    = true;
             newJob.ignoreDesignations = true;
         }
         curJob = newJob;
         curJob.jobGiverThinkTree = thinkTree;
         curJob.jobGiver          = jobGiver;
         curDriver = curJob.MakeDriver(pawn);
         bool flag = fromQueue;
         if (curDriver.TryMakePreToilReservations(!flag))
         {
             Job job = TryOpportunisticJob(newJob);
             if (job != null)
             {
                 jobQueue.EnqueueFirst(newJob);
                 curJob    = null;
                 curDriver = null;
                 StartJob(job);
                 return;
             }
             if (tag.HasValue)
             {
                 pawn.mindState.lastJobTag = tag.Value;
             }
             curDriver.SetInitialPosture();
             curDriver.Notify_Starting();
             curDriver.SetupToils();
             curDriver.ReadyForNextToil();
         }
         else if (flag)
         {
             EndCurrentJob(JobCondition.QueuedNoLongerValid);
         }
         else
         {
             Log.Warning("TryMakePreToilReservations() returned false for a non-queued job right after StartJob(). This should have been checked before. curJob=" + curJob.ToStringSafe());
             EndCurrentJob(JobCondition.Errored);
         }
     }
     finally
     {
         startingNewJob = false;
     }
 }
            static bool Prefix(ref Pawn_JobTracker_Crutch __instance, Job newJob, JobCondition lastJobEndCondition, ThinkNode jobGiver, bool resumeCurJobAfterwards, bool cancelBusyStances, ThinkTreeDef thinkTree, JobTag?tag, bool fromQueue)
            {
                if (__instance == null || __instance._pawn == null || newJob == null || newJob.def == null)
                {
                    return(true);
                }

                if (Settings.fun_police && __instance._pawn.needs.joy != null && __instance._pawn.needs.joy.CurLevel < 0.8f)
                {
                    CompJoyToppedOff c = __instance._pawn.TryGetComp <CompJoyToppedOff>();
                    if (c != null)
                    {
                        c.JoyToppedOff = false;
                    }
                }

                if (!Settings.clean_before_work && !Settings.hauling_over_bills)
                {
                    return(true);
                }

                if (!newJob.def.allowOpportunisticPrefix)
                {
                    return(true);
                }

                Job job = null;

                if (newJob.def == JobDefOf.DoBill)
                {
                    if (Settings.hauling_over_bills)
                    {
                        job = Hauling_Opportunity(newJob, __instance._pawn);
                    }
                }
                else if (!newJob.playerForced && newJob.targetA != null && newJob.targetA.Cell != null)
                {
                    IntVec3 cell = newJob.targetA.Cell;

                    if (!cell.IsValid || cell.IsForbidden(__instance._pawn) || __instance._pawn.Downed)
                    {
                        return(true);
                    }

                    if (Settings.clean_before_work && (newJob.targetA.Thing != null && newJob.targetA.Thing.GetType().IsSubclassOf(typeof(Building)) || newJob.def.joyKind != null))
                    {
                        job = Cleaning_Opportunity(newJob, cell, __instance._pawn, 20);
                    }
                }

                if (job != null)
                {
                    if (Settings.add_to_que)
                    {
                        __instance.jobQueue.EnqueueFirst(newJob);
                    }
                    __instance.jobQueue.EnqueueFirst(job);
                    __instance.curJob    = null;
                    __instance.curDriver = null;
                    return(false);
                }
                return(true);
            }
Beispiel #14
0
 public void StartJob(CaravanJob newJob, JobCondition lastJobEndCondition = JobCondition.None, ThinkNode jobGiver = null, bool resumeCurJobAfterwards = false, bool cancelBusyStances = true, ThinkTreeDef thinkTree = null, JobTag?tag = null)
 {
     Log.Message("JecsTools :: CaravanJobTracker :: JobTracker StartJob :: " + newJob.def.label);
     if (!Find.TickManager.Paused || this.lastJobGivenAtFrame == RealTime.frameCount)
     {
         this.jobsGivenThisTick++;
         this.jobsGivenThisTickTextual = this.jobsGivenThisTickTextual + "(" + newJob.ToString() + ") ";
     }
     this.lastJobGivenAtFrame = RealTime.frameCount;
     if (this.jobsGivenThisTick > 10)
     {
         string text = this.jobsGivenThisTickTextual;
         this.jobsGivenThisTick        = 0;
         this.jobsGivenThisTickTextual = string.Empty;
         this.StartErrorRecoverJob(string.Concat(new object[]
         {
             this.caravan,
             " started 10 jobs in one tick. newJob=",
             newJob,
             " jobGiver=",
             jobGiver,
             " jobList=",
             text
         }));
         return;
     }
     if (this.debugLog)
     {
         this.DebugLogEvent(string.Concat(new object[]
         {
             "StartJob [",
             newJob,
             "] lastJobEndCondition=",
             lastJobEndCondition,
             ", jobGiver=",
             jobGiver,
             ", cancelBusyStances=",
             cancelBusyStances
         }));
     }
     if (this.curJob != null)
     {
         if (lastJobEndCondition == JobCondition.None)
         {
             Log.Warning(string.Concat(new object[]
             {
                 this.caravan,
                 " starting job ",
                 newJob,
                 " while already having job ",
                 this.curJob,
                 " without a specific job end condition."
             }));
             lastJobEndCondition = JobCondition.InterruptForced;
         }
         if (resumeCurJobAfterwards && this.curJob.def.suspendable)
         {
             this.jobQueue.EnqueueFirst(this.curJob, null);
             if (this.debugLog)
             {
                 this.DebugLogEvent("   JobQueue EnqueueFirst curJob: " + this.curJob);
             }
         }
         this.CleanupCurrentJob(lastJobEndCondition, !resumeCurJobAfterwards, cancelBusyStances);
     }
     if (newJob == null)
     {
         Log.Warning(this.caravan + " tried to start doing a null job.");
         return;
     }
     newJob.startTick = Find.TickManager.TicksGame;
     if (newJob.playerForced)
     {
         newJob.ignoreForbidden    = true;
         newJob.ignoreDesignations = true;
     }
     this.curJob    = newJob;
     this.curDriver = this.curJob.MakeDriver(this.caravan);
     this.curDriver.Notify_Starting();
     this.curDriver.SetupToils();
     this.curDriver.ReadyForNextToil();
 }
Beispiel #15
0
 public void EnqueueLast(Job j, JobTag?tag = default(JobTag?))
 {
     jobs.Add(new QueuedJob(j, tag));
 }
Beispiel #16
0
 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)
 {
     return(false);
 }
 public void StartJob(Job newJob, JobCondition lastJobEndCondition = JobCondition.None, ThinkNode jobGiver = null, bool resumeCurJobAfterwards = false, bool cancelBusyStances = true, ThinkTreeDef thinkTree = null, JobTag?tag = null, bool fromQueue = false)
 {
     this.startingNewJob = true;
     try
     {
         if (!fromQueue && (!Find.TickManager.Paused || this.lastJobGivenAtFrame == RealTime.frameCount))
         {
             this.jobsGivenThisTick++;
             this.jobsGivenThisTickTextual = this.jobsGivenThisTickTextual + "(" + newJob.ToString() + ") ";
         }
         this.lastJobGivenAtFrame = RealTime.frameCount;
         if (this.jobsGivenThisTick > 10)
         {
             string text = this.jobsGivenThisTickTextual;
             this.jobsGivenThisTick        = 0;
             this.jobsGivenThisTickTextual = "";
             this.startingNewJob           = false;
             this.pawn.ClearReservationsForJob(newJob);
             JobUtility.TryStartErrorRecoverJob(this.pawn, string.Concat(new string[]
             {
                 this.pawn.ToStringSafe <Pawn>(),
                 " started 10 jobs in one tick. newJob=",
                 newJob.ToStringSafe <Job>(),
                 " jobGiver=",
                 jobGiver.ToStringSafe <ThinkNode>(),
                 " jobList=",
                 text
             }), null, null);
         }
         else
         {
             if (this.debugLog)
             {
                 this.DebugLogEvent(string.Concat(new object[]
                 {
                     "StartJob [",
                     newJob,
                     "] lastJobEndCondition=",
                     lastJobEndCondition,
                     ", jobGiver=",
                     jobGiver,
                     ", cancelBusyStances=",
                     cancelBusyStances
                 }));
             }
             if (cancelBusyStances && this.pawn.stances.FullBodyBusy)
             {
                 this.pawn.stances.CancelBusyStanceHard();
             }
             if (this.curJob != null)
             {
                 if (lastJobEndCondition == JobCondition.None)
                 {
                     Log.Warning(string.Concat(new object[]
                     {
                         this.pawn,
                         " starting job ",
                         newJob,
                         " from JobGiver ",
                         this.pawn.mindState.lastJobGiver,
                         " while already having job ",
                         this.curJob,
                         " without a specific job end condition."
                     }), false);
                     lastJobEndCondition = JobCondition.InterruptForced;
                 }
                 if (resumeCurJobAfterwards && this.curJob.def.suspendable)
                 {
                     this.jobQueue.EnqueueFirst(this.curJob, null);
                     if (this.debugLog)
                     {
                         this.DebugLogEvent("   JobQueue EnqueueFirst curJob: " + this.curJob);
                     }
                     this.CleanupCurrentJob(lastJobEndCondition, false, cancelBusyStances);
                 }
                 else
                 {
                     this.CleanupCurrentJob(lastJobEndCondition, true, cancelBusyStances);
                 }
             }
             if (newJob == null)
             {
                 Log.Warning(this.pawn + " tried to start doing a null job.", false);
             }
             else
             {
                 newJob.startTick = Find.TickManager.TicksGame;
                 if (this.pawn.Drafted || newJob.playerForced)
                 {
                     newJob.ignoreForbidden    = true;
                     newJob.ignoreDesignations = true;
                 }
                 this.curJob = newJob;
                 this.pawn.mindState.lastJobGiver          = jobGiver;
                 this.pawn.mindState.lastJobGiverThinkTree = thinkTree;
                 this.curDriver = this.curJob.MakeDriver(this.pawn);
                 if (this.curDriver.TryMakePreToilReservations())
                 {
                     Job job = this.TryOpportunisticJob(newJob);
                     if (job != null)
                     {
                         this.jobQueue.EnqueueFirst(newJob, null);
                         this.curJob    = null;
                         this.curDriver = null;
                         this.StartJob(job, JobCondition.None, null, false, true, null, null, false);
                     }
                     else
                     {
                         if (tag != null)
                         {
                             this.pawn.mindState.lastJobTag = tag.Value;
                         }
                         this.curDriver.SetInitialPosture();
                         this.curDriver.Notify_Starting();
                         this.curDriver.SetupToils();
                         this.curDriver.ReadyForNextToil();
                     }
                 }
                 else if (fromQueue)
                 {
                     this.EndCurrentJob(JobCondition.QueuedNoLongerValid, true);
                 }
                 else
                 {
                     Log.Warning("TryMakePreToilReservations() returned false for a non-queued job right after StartJob(). This should have been checked before. curJob=" + this.curJob.ToStringSafe <Job>(), false);
                     this.EndCurrentJob(JobCondition.Errored, true);
                 }
             }
         }
     }
     finally
     {
         this.startingNewJob = false;
     }
 }
            static bool Prefix(ref Pawn_JobTracker_Crutch __instance, Job newJob, JobCondition lastJobEndCondition, ThinkNode jobGiver, bool resumeCurJobAfterwards, bool cancelBusyStances, ThinkTreeDef thinkTree, JobTag?tag, bool fromQueue)
            {
                try
                {
                    if (__instance == null || __instance._pawn == null || !__instance._pawn.IsColonistPlayerControlled || newJob == null || newJob.def == null)
                    {
                        return(true);
                    }

                    if (Settings.fun_police && __instance._pawn.needs.joy != null && __instance._pawn.needs.joy.CurLevel < 0.8f)
                    {
                        CompJoyToppedOff c = __instance._pawn.TryGetComp <CompJoyToppedOff>();
                        if (c != null)
                        {
                            c.JoyToppedOff = false;
                        }
                    }

                    if (!Settings.clean_before_work && !Settings.hauling_over_bills)
                    {
                        return(true);
                    }

                    if (!newJob.def.allowOpportunisticPrefix)
                    {
                        return(true);
                    }

                    Job job = null;

                    if (newJob.def == JobDefOf.DoBill)
                    {
                        if (Settings.hauling_over_bills)
                        {
                            job = Hauling_Opportunity(newJob, __instance._pawn);
                        }
                    }
                    else if (!newJob.playerForced && newJob.targetA != null && newJob.targetA.Cell != null)
                    {
                        IntVec3 cell = newJob.targetA.Cell;

                        if (!cell.IsValid || cell.IsForbidden(__instance._pawn) || __instance._pawn.Downed)
                        {
                            return(true);
                        }

                        if (Settings.clean_before_work && (newJob.targetA.Thing != null &&
                                                           newJob.targetA.Thing.GetType().IsSubclassOf(typeof(Building)) && newJob.def != JobDefOf.PlaceNoCostFrame && newJob.def != JobDefOf.FinishFrame ||
                                                           newJob.def.joyKind != null) &&
                            !HealthAIUtility.ShouldBeTendedNowByPlayer(__instance._pawn))
                        {
                            job = Cleaning_Opportunity(newJob, cell, __instance._pawn, Settings.op_clean_num);
                        }
                    }

                    //Log.Message($"pawn={__instance._pawn},job={newJob},enque={job}, limit = {Settings.op_clean_num}");
                    if (job != null)
                    {
                        if (Settings.add_to_que)
                        {
                            newJob.playerForced = true;
                            __instance.jobQueue.EnqueueFirst(newJob);
                        }
                        __instance.jobQueue.EnqueueFirst(job);
                        //__instance.curJob = null;
                        //__instance.curDriver = null;
                        return(false);
                    }
                }
                catch (Exception e)
                {
                    Log.Error($"CommonSense: opportunistic task skipped due to error ({e.Message})");
                }
                return(true);
            }