protected override IEnumerable <Toil> MakeNewToils()
        {
            this.FailOnDespawnedNullOrForbidden <JobDriver_StabilizeHere>(TargetIndex.A);
            this.FailOnAggroMentalState <JobDriver_StabilizeHere>(TargetIndex.A);
            this.AddEndCondition(() => {
                if (HealthAIUtility.ShouldBeTendedNowByPlayer(this.Patient) || this.Patient.health.HasHediffsNeedingTend(false))
                {
                    return(JobCondition.Ongoing);
                }
                return(JobCondition.Succeeded);
            });

            Toil toil1 = Toils_Goto.GotoThing(TargetIndex.A, PathEndMode.InteractionCell);

            yield return(toil1);

            Toil toil2 = Toils_General.Wait((int)(1f / this.Doctor.GetStatValue(StatDefOf.MedicalTendSpeed, true) * 600f), TargetIndex.None).FailOnCannotTouch <Toil>(TargetIndex.A, PathEndMode.InteractionCell).WithProgressBarToilDelay(TargetIndex.A, false, -0.5f).PlaySustainerOrSound(SoundDefOf.Interact_Tend);

            toil2.activeSkill = () => SkillDefOf.Medicine;
            yield return(toil2);

            yield return(Toils_Tend.FinalizeTend(this.Patient));

            yield return(Toils_Jump.Jump(toil1));
        }
Ejemplo n.º 2
0
        public static InjurySeverity GetTendSeverity(this Pawn patient)
        {
            if (!HealthAIUtility.ShouldBeTendedNowByPlayer(patient))     //    .ShouldBeTendedNow( patient ) )
            {
                return(InjurySeverity.Minor);
            }

            var hediffs = patient.health.hediffSet.hediffs;
            var ticksToDeathDueToBloodLoss = HealthUtility.TicksUntilDeathDueToBloodLoss(patient);

            // going to die in <6 hours, or any tendable is life threathening
            if (ticksToDeathDueToBloodLoss <= GenDate.TicksPerHour * 6 ||
                hediffs.Any(h => h.CurStage?.lifeThreatening ?? false) ||
                hediffs.Any(NearLethalDisease))
            {
                return(InjurySeverity.LifeThreathening);
            }

            // going to die in <12 hours, or any immunity < severity and can be fatal, or death by a thousand cuts imminent
            if (ticksToDeathDueToBloodLoss <= GenDate.TicksPerHour * 12 ||
                hediffs.Any(PotentiallyLethalDisease) ||
                DeathByAThousandCuts(patient))
            {
                return(InjurySeverity.Major);
            }

            // otherwise
            return(InjurySeverity.Minor);
        }
Ejemplo n.º 3
0
        // Token: 0x06000026 RID: 38 RVA: 0x000026A3 File Offset: 0x000008A3
        protected override IEnumerable <Toil> MakeNewToils()
        {
            base.AddEndCondition(delegate()
            {
                if (this.pawn.Faction == Faction.OfPlayer && HealthAIUtility.ShouldBeTendedNowByPlayer(this.pawn))
                {
                    return(JobCondition.Ongoing);
                }
                if (this.pawn.Faction != Faction.OfPlayer && this.pawn.health.HasHediffsNeedingTend(false))
                {
                    return(JobCondition.Ongoing);
                }
                return(JobCondition.Succeeded);
            });
            int ticks = (int)(1f / StatExtension.GetStatValue(this.pawn, StatDefOf.MedicalTendSpeed, true) * 600f);

            yield return(ToilEffects.PlaySustainerOrSound(ToilEffects.WithProgressBarToilDelay(Toils_General.Wait(ticks, 0), (TargetIndex)1, false, -0.5f), SoundDefOf.Interact_Tend));

            Toil toil = new Toil();

            toil.initAction = delegate()
            {
                Pawn     actor  = toil.actor;
                Cloakgen medkit = actor.apparel.WornApparel.OfType <Cloakgen>().FirstOrDefault <Cloakgen>();
                float    num    = (!actor.RaceProps.Animal) ? 500f : 175f;
                float    num2   = (medkit != null) ? medkit.kitComp.Props.medicine.MedicineTendXpGainFactor : 0.5f;
                actor.skills.Learn(SkillDefOf.Medicine, num * num2, false);
                HealthShardTendUtility.DoTend(actor, actor, medkit);
            };
            toil.defaultCompleteMode = (ToilCompleteMode)1;
            yield return(toil);

            yield break;
        }
        // Token: 0x0600002D RID: 45 RVA: 0x00002758 File Offset: 0x00000958
        public override ThinkResult TryIssueJobPackage(Pawn pawn, JobIssueParams jobParm)
        {
            bool        flag = !HealthAIUtility.ShouldSeekMedicalRest(pawn);
            ThinkResult result;

            if (flag)
            {
                result = ThinkResult.NoJob;
            }
            else
            {
                bool flag2 = !HealthAIUtility.ShouldBeTendedNowByPlayer(pawn);
                if (flag2)
                {
                    result = ThinkResult.NoJob;
                }
                else
                {
                    bool flag3 = !GenCollection.Any <Apparel>(pawn.apparel.WornApparel, (Apparel x) => x.def.defName.Contains("RRY_Equipment_HunterGauntlet"));
                    if (flag3)
                    {
                        result = ThinkResult.NoJob;
                    }
                    else
                    {
                        Thing thing = RestUtility.FindPatientBedFor(pawn);
                        bool  flag4 = thing == null;
                        if (flag4)
                        {
                            result = ThinkResult.NoJob;
                        }
                        else
                        {
                            Thing thing2 = null;
                            bool  flag5  = Medicine.GetMedicineCountToFullyHeal(pawn) > 0;
                            if (flag5)
                            {
                                thing2 = HealthAIUtility.FindBestMedicine(pawn, pawn);
                            }
                            bool flag6 = thing2 != null;
                            Job  job;
                            if (flag6)
                            {
                                job = new Job(YautjaDefOf.RRY_Yautja_TendSelf, thing, thing2);
                            }
                            else
                            {
                                job = new Job(YautjaDefOf.RRY_Yautja_TendSelf, thing);
                            }
                            result = new ThinkResult(job, this, null, false);
                        }
                    }
                }
            }
            return(result);
        }
Ejemplo n.º 5
0
        private bool KeepRoomUnlockedForTending(Pawn patient)
        {
            if ((Settings.KeepUnlockedForUrgentTending && HealthAIUtility.ShouldBeTendedNowByPlayerUrgent(patient)) ||
                (Settings.KeepUnlockedForSurgery && HealthAIUtility.ShouldHaveSurgeryDoneNow(patient)) ||
                (Settings.KeepUnlockedForAnyTending && HealthAIUtility.ShouldBeTendedNowByPlayer(patient)))
            {
                return(true);
            }

            return(false);
        }
        public bool HasJobOnThing(Pawn pawn, Thing t, bool forced = false)
        {
            Pawn pawn2 = t as Pawn;

            if (pawn2 == null || pawn.WorkTypeIsDisabled(WorkTypeDefOf.Doctor) || !GoodLayingStatusForTend(pawn2, pawn) ||
                !HealthAIUtility.ShouldBeTendedNowByPlayer(pawn2) || !pawn.CanReserve(pawn2, 1, -1, null, forced))
            {
                return(false);
            }
            return(true);
        }
Ejemplo n.º 7
0
        public static bool Skip(Pawn pawn)
        {
            Log.Message($"Skip need tend?");
            if (pawn.Map.mapPawns.AllPawnsSpawned.Any(p => HealthAIUtility.ShouldBeTendedNowByPlayer(p) && pawn.CanReserveAndReach(p, PathEndMode.ClosestTouch, Danger.Deadly, ignoreOtherReservations: true)))
            {
                return(true);
            }

            if (pawn.Map.mapPawns.AllPawnsSpawned.Any(p => p is IBillGiver billGiver && billGiver.BillStack.AnyShouldDoNow && pawn.CanReserveAndReach(p, PathEndMode.ClosestTouch, Danger.Deadly, ignoreOtherReservations: true)))
            {
                return(true);
            }

            return(false);
        }
Ejemplo n.º 8
0
 private static void Postfix(ref bool __result, ref WorkGiver_Tend __instance, Pawn pawn, Thing t, bool forced)
 {
     if (__result || t == pawn && !(__instance is WorkGiver_TendSelf))
     {
         return;
     }
     if (t is Pawn target && target.IsCaptiveOf(pawn.Faction) &&
         !pawn.WorkTypeIsDisabled(WorkTypeDefOf.Doctor) &&
         (!__instance.def.tendToHumanlikesOnly || target.RaceProps.Humanlike && !target.IsWildMan()) &&
         (!__instance.def.tendToAnimalsOnly || target.AnimalOrWildMan()) &&
         target.GetPosture() != PawnPosture.Standing && HealthAIUtility.ShouldBeTendedNowByPlayer(target) &&
         pawn.CanReserve(target, 1, -1, null, forced))
     {
         __result = true;
     }
 }
        public override bool HasJobOnThing(Pawn pawn, Thing t, bool forced = false)
        {
            if (!pawn.story.traits.HasTrait(WizardryDefOf.LotRW_Istari))
            {
                return(false);
            }

            var comp        = pawn.GetComp <CompWizardry>();
            var pawnAbility =
                comp.AbilityData.Powers.FirstOrDefault(x => x.Def == WizardryDefOf.LotRW_Nienna_HealingTouch);

            if (!(t is Pawn pawn2))
            {
                return(false);
            }

            if (def.tendToHumanlikesOnly && !pawn2.RaceProps.Humanlike)
            {
                return(false);
            }

            if (def.tendToAnimalsOnly && !pawn2.RaceProps.Animal)
            {
                return(false);
            }

            if (!GoodLayingStatusForTend(pawn2, pawn))
            {
                return(false);
            }

            if (!HealthAIUtility.ShouldBeTendedNowByPlayer(pawn2))
            {
                return(false);
            }

            if (!HasHediffInjuries(pawn2))
            {
                return(false);
            }

            if (pawn == pawn2)
            {
                return(false);
            }

            if (pawnAbility is { CooldownTicksLeft : > 0 })
Ejemplo n.º 10
0
        //public class Alert_NeedDoctor : Alert
        public static bool get_Patients_Vamp(ref IEnumerable <Pawn> __result)
        {
            List <Map> maps = Find.Maps;

            for (int i = 0; i < maps.Count; i++)
            {
                if (maps[i].IsPlayerHome)
                {
                    HashSet <Pawn> pawns    = new HashSet <Pawn>(maps[i].mapPawns.FreeColonistsSpawned);
                    List <Pawn>    Patients = new List <Pawn>();
                    if (pawns != null && pawns.Count > 0 && pawns.FirstOrDefault(x => x.IsVampire()) != null)
                    {
                        if (pawns.FirstOrDefault(x =>
                                                 !x.Downed && x.workSettings != null &&
                                                 x.workSettings.WorkIsActive(WorkTypeDefOf.Doctor)) != null)
                        {
                            foreach (Pawn p2 in pawns)
                            {
                                if (p2.IsVampire())
                                {
                                    if (HealthAIUtility.ShouldBeTendedNowByPlayer(p2))
                                    {
                                        Patients.Add(p2);
                                    }
                                }
                                else
                                {
                                    if (p2.Downed &&
                                        (p2?.needs?.food?.CurCategory ?? HungerCategory.Fed) < HungerCategory.Fed &&
                                        p2.InBed() || HealthAIUtility.ShouldBeTendedNowByPlayer(p2))
                                    {
                                        Patients.Add(p2);
                                    }
                                }
                            }
                        }

                        __result = null;
                        __result = Patients;
                        return(false);
                    }
                }
            }

            return(true);
        }
Ejemplo n.º 11
0
        public override ThinkResult TryIssueJobPackage(Pawn pawn, JobIssueParams jobParams)
        {
            var need = pawn.needs.TryGetNeed <Need_Motivation>();

            if (pawn.timetable == null)
            {
                WorkSettings.InitWorkSettings(pawn);
            }
            if (HealthAIUtility.ShouldHaveSurgeryDoneNow(pawn))
            {
                return(ThinkResult.NoJob);
            }

            if (PrisonLaborPrefs.EnableFullHealRest && (HealthAIUtility.ShouldBeTendedNowByPlayer(pawn) || HealthAIUtility.ShouldSeekMedicalRest(pawn)))
            {
                return(ThinkResult.NoJob);
            }
            //Check medical assistance, fed, and rest if not override
            if (!PrisonLaborUtility.WorkTime(pawn))
            {
                Other.Tutorials.Timetable();
                if (need != null)
                {
                    need.IsPrisonerWorking = false;
                }
                return(ThinkResult.NoJob);
            }
            //Check motivation
            if (PrisonLaborPrefs.EnableMotivationMechanics && (need == null || need.IsLazy))
            {
                return(ThinkResult.NoJob);
            }
            //Work prisoners will do
            WorkSettings.InitWorkSettings(pawn);
            var workList = pawn.workSettings.WorkGiversInOrderNormal;

            //TODO check this
            //workList.RemoveAll(workGiver => workGiver.def.defName == "GrowerSow");
            if (need != null)
            {
                need.IsPrisonerWorking = false;
            }

            var num        = -999;
            var targetInfo = TargetInfo.Invalid;
            WorkGiver_Scanner workGiver_Scanner = null;

            for (var j = 0; j < workList.Count; j++)
            {
                var workGiver = workList[j];
                if (workGiver.def.priorityInType != num && targetInfo.IsValid)
                {
                    break;
                }
                if (PawnCanUseWorkGiver(pawn, workGiver))
                {
                    try
                    {
                        var job2 = workGiver.NonScanJob(pawn);
                        if (job2 != null)
                        {
                            if (need != null)
                            {
                                need.IsPrisonerWorking = true;
                            }
                            return(new ThinkResult(job2, this, workList[j].def.tagToGive));
                        }
                        var scanner = workGiver as WorkGiver_Scanner;
                        if (scanner != null)
                        {
                            if (workGiver.def.scanThings)
                            {
                                Predicate <Thing> predicate = t =>
                                                              !t.IsForbidden(pawn) && scanner.HasJobOnThing(pawn, t, false);
                                var   enumerable = scanner.PotentialWorkThingsGlobal(pawn);
                                Thing thing;
                                if (scanner.Prioritized)
                                {
                                    var enumerable2 = enumerable;
                                    if (enumerable2 == null)
                                    {
                                        enumerable2 =
                                            pawn.Map.listerThings.ThingsMatching(scanner.PotentialWorkThingRequest);
                                    }
                                    var validator = predicate;
                                    thing = GenClosest.ClosestThing_Global_Reachable(pawn.Position, pawn.Map,
                                                                                     enumerable2, scanner.PathEndMode,
                                                                                     TraverseParms.For(pawn, Danger.Deadly, TraverseMode.ByPawn, false), 9999f,
                                                                                     validator, x => scanner.GetPriority(pawn, x));
                                }
                                else
                                {
                                    var validator         = predicate;
                                    var forceGlobalSearch = enumerable != null;
                                    thing = GenClosest.ClosestThingReachable(pawn.Position, pawn.Map,
                                                                             scanner.PotentialWorkThingRequest, scanner.PathEndMode,
                                                                             TraverseParms.For(pawn, Danger.Deadly, TraverseMode.ByPawn, false), 9999f,
                                                                             validator, enumerable, 0, scanner.MaxRegionsToScanBeforeGlobalSearch, forceGlobalSearch,
                                                                             RegionType.Set_Passable, false);
                                }
                                if (thing != null)
                                {
                                    targetInfo        = thing;
                                    workGiver_Scanner = scanner;
                                }
                            }
                            if (workGiver.def.scanCells)
                            {
                                var position    = pawn.Position;
                                var num2        = 99999f;
                                var num3        = -3.40282347E+38f;
                                var prioritized = scanner.Prioritized;
                                foreach (var current in scanner.PotentialWorkCellsGlobal(pawn))
                                {
                                    var   flag = false;
                                    float num4 = (current - position).LengthHorizontalSquared;
                                    if (prioritized)
                                    {
                                        if (!current.IsForbidden(pawn) && scanner.HasJobOnCell(pawn, current))
                                        {
                                            var priority = scanner.GetPriority(pawn, current);
                                            if (priority > num3 || priority == num3 && num4 < num2)
                                            {
                                                flag = true;
                                                num3 = priority;
                                            }
                                        }
                                    }
                                    else if (num4 < num2 && !current.IsForbidden(pawn) &&
                                             scanner.HasJobOnCell(pawn, current))
                                    {
                                        flag = true;
                                    }
                                    if (flag)
                                    {
                                        targetInfo        = new TargetInfo(current, pawn.Map, false);
                                        workGiver_Scanner = scanner;
                                        num2 = num4;
                                    }
                                }
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        Log.Error(string.Concat(pawn, " threw exception in WorkGiver ", workGiver.def.defName, ": ",
                                                ex.ToString()));
                    }
                    finally
                    {
                    }
                    if (targetInfo.IsValid)
                    {
                        Job job3;
                        if (targetInfo.HasThing)
                        {
                            job3 = workGiver_Scanner.JobOnThing(pawn, targetInfo.Thing, false);
                        }
                        else
                        {
                            job3 = workGiver_Scanner.JobOnCell(pawn, targetInfo.Cell);
                        }
                        if (job3 != null)
                        {
                            job3.workGiverDef = workGiver.def;
                            if (need != null)
                            {
                                need.IsPrisonerWorking = true;
                            }
                            return(new ThinkResult(job3, this, workList[j].def.tagToGive));
                        }
                        Log.ErrorOnce(
                            string.Concat(workGiver_Scanner, " provided target ", targetInfo,
                                          " but yielded no actual job for pawn ", pawn,
                                          ". The CanGiveJob and JobOnX methods may not be synchronized."), 6112651);
                    }
                    num = workGiver.def.priorityInType;
                }
            }
            return(ThinkResult.NoJob);
        }
        protected override IEnumerable <Toil> MakeNewToils()
        {
            //Reserve the takee
            yield return(Toils_Reserve.Reserve(TargetIndex.A, 1));

            //Reserve the bed
            yield return(Toils_Reserve.Reserve(TargetIndex.B, 1));

            //Claim the bed for the takee
            Toil claimBed = new Toil();

            claimBed.initAction = () =>
            {
                if (Takee.ownership != null && Takee.ownership.OwnedBed != DropBed && !DropBed.Medical)
                {
                    Takee.ownership.ClaimBedIfNonMedical((Building_Bed)DropBed);
                }
            };
            yield return(claimBed);

            Func <bool> ownershipFail = () =>
            {
                if (DropBed.Medical)
                {
                    if (DropBed.AnyUnownedSleepingSlot &&
                        DropBed.CurOccupants != Takee)
                    {
                        return(true);
                    }
                }
                else if (DropBed.OwnersForReading != null && !DropBed.OwnersForReading.Contains(Takee))
                {
                    return(true);
                }
                return(false);
            };

            //Goto takee
            yield return(Toils_Goto.GotoThing(TargetIndex.A, PathEndMode.ClosestTouch)
                         .FailOnDestroyedNullOrForbidden(TargetIndex.A)
                         .FailOnDestroyedNullOrForbidden(TargetIndex.B)
                         .FailOn(ownershipFail)                                                              //Abandon if takee loses bed ownership
                         .FailOn(() => this.job.def == JobDefOf.Arrest && !Takee.CanBeArrestedBy(this.pawn)) //Abandon arrest if takee is not of a team who is willing to be arrested
                         .FailOn(() => !pawn.CanReach(DropBed, PathEndMode.OnCell, Danger.Deadly)));

            //.FailOn(()=>!pawn.CanReach( DropBed, PathEndMode.OnCell, Danger.Deadly ) ); // From Alpha 8

            //Make unconscious if needed
            Toil makeUnconscious = new Toil();

            makeUnconscious.initAction = () =>
            {
                //Log.Error("Applying Anesthetic");
                //Takee.healthTracker.ApplyAnesthetic(); Does not work with AIPawn

                Takee.health.forceIncap = true;
                Takee.health.AddHediff(HediffDefOf.Anesthetic, null, null);
                Takee.health.forceIncap = false;
            };
            yield return(makeUnconscious);

            //Start carrying the takee
            yield return(Toils_Haul.StartCarryThing(TargetIndex.A));

            //Change takee to prisoner if necessary
            Toil makePrisoner = new Toil();

            makePrisoner.initAction = () =>
            {
                if (this.job.def == JobDefOf.Arrest || this.job.def == JobDefOf.Capture || this.job.def == JobDefOf.TakeWoundedPrisonerToBed)
                {
                    if (Takee.HostFaction != Faction.OfPlayer)
                    {
                        Takee.guest.SetGuestStatus(Faction.OfPlayer, true);
                    }
                }
            };
            yield return(makePrisoner);

            yield return(Toils_Goto.GotoThing(TargetIndex.B, PathEndMode.Touch)
                         .FailOnDestroyedNullOrForbidden(TargetIndex.A));

            //Note no failure conditions here
            //Because otherwise it's easy to get wardens to drop prisoners in arbitrary places.
            //I'd rather they just go to wherever they were going.
            // if( DropBed.owner != Takee )
            //	return true;
            //return !Toils.CanInteractStandard(DropBed);


            //Unreserve bed so takee can use it
            yield return(Toils_Reserve.Release(TargetIndex.B));


            //Drop in or near bed
            Toil tuckIntoBed = new Toil();

            tuckIntoBed.initAction = () =>
            {
                //Note: We don't stop the task if the bed is destroyed or changes ownership
                //because then the wardens drop prisoners at random points and they escape
                //So we have to handle some ugly cases here

                //note this may use the position of a destroyed bed
                IntVec3 dropPos = DropBed.Position;

                Thing unused;
                pawn.carryTracker.TryDropCarriedThing(dropPos, ThingPlaceMode.Direct, out unused);

                //Should we tuck them into bed?
                if ((Takee.Downed || Takee.health.HasHediffsNeedingTend(false) ||
                     ((HealthAIUtility.ShouldSeekMedicalRest(Takee) || HealthAIUtility.ShouldBeTendedNowByPlayer(Takee) || HealthAIUtility.ShouldBeTendedNowByPlayerUrgent(Takee)) &&
                      DropBed.Medical)) &&
                    !DropBed.Destroyed &&
                    (DropBed.OwnersForReading.Contains(Takee) || (DropBed.Medical && DropBed.AnyUnoccupiedSleepingSlot))      //They could have lost ownership and the last toil would continue
                    )
                {
                    Takee.jobs.Notify_TuckedIntoBed(DropBed);
                }

                if (Takee.IsPrisonerOfColony)
                {
                    LessonAutoActivator.TeachOpportunity(ConceptDefOf.PrisonerTab, Takee, OpportunityType.GoodToKnow);
                }
            };
            tuckIntoBed.defaultCompleteMode = ToilCompleteMode.Instant;
            yield return(tuckIntoBed);
        }
Ejemplo n.º 13
0
            static bool Prefix(ref Pawn_JobTracker_Crutch __instance, Job newJob, 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 && newJob.def != JobDefOf.SpectateCeremony)
                    {
                        return(true);
                    }

                    Job job = null;

                    if (newJob.def == JobDefOf.DoBill)
                    {
                        if (Settings.hauling_over_bills)
                        {
                            job = Hauling_Opportunity(newJob, __instance._pawn);
                        }
                    }
                    else if (!fromQueue && !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.def == JobDefOf.PrisonerAttemptRecruit ||
                                newJob.def == JobDefOf.PrisonerConvert ||
                                newJob.def == JobDefOf.PrisonerEnslave ||
                                newJob.def == JobDefOf.SpectateCeremony ||
                                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);
                        }
                    }

                    if (job != null)
                    {
                        if (Settings.add_to_que)
                        {
                            __instance.jobQueue.EnqueueFirst(newJob);
                        }
                        __instance.jobQueue.EnqueueFirst(job);
                        return(false);
                    }
                }
                catch (Exception e)
                {
                    Log.Warning($"CommonSense: opportunistic task skipped due to error ({e.Message}) ({__instance._pawn}, {newJob})");
                }
                return(true);
            }
            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 || !__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.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);
                }
                return(true);
            }