Exemplo n.º 1
0
        static IEnumerable <Toil> DoMakeToils(JobDriver_DoBill_Access __instance)
        {
            //normal scenario
            __instance.AddEndCondition(delegate
            {
                Thing thing = __instance.GetActor().jobs.curJob.GetTarget(TargetIndex.A).Thing;
                if (thing is Building && !thing.Spawned)
                {
                    return(JobCondition.Incompletable);
                }
                return(JobCondition.Ongoing);
            });
            __instance.FailOnBurningImmobile(TargetIndex.A);
            __instance.FailOn(delegate()
            {
                if (__instance.job.GetTarget(TargetIndex.A).Thing is Filth)
                {
                    return(false);
                }

                IBillGiver billGiver = __instance.job.GetTarget(TargetIndex.A).Thing as IBillGiver;
                if (billGiver != null)
                {
                    if (__instance.job.bill.DeletedOrDereferenced)
                    {
                        return(true);
                    }
                    if (!billGiver.CurrentlyUsableForBills())
                    {
                        return(true);
                    }
                }
                return(false);
            });

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

            yield return(new Toil
            {
                initAction = delegate()
                {
                    if (__instance.job.targetQueueB != null && __instance.job.targetQueueB.Count == 1)
                    {
                        UnfinishedThing unfinishedThing = __instance.job.targetQueueB[0].Thing as UnfinishedThing;
                        if (unfinishedThing != null)
                        {
                            unfinishedThing.BoundBill = (Bill_ProductionWithUft)__instance.job.bill;
                        }
                    }
                }
            });

            yield return(Toils_Jump.JumpIf(gotoBillGiver, () => __instance.job.GetTargetQueue(TargetIndex.B).NullOrEmpty()));

            //hauling patch
            if (Settings.adv_haul_all_ings && __instance.pawn.Faction == Faction.OfPlayer)
            {
                Toil checklist = new Toil();
                checklist.initAction = delegate()
                {
                    Pawn actor  = checklist.actor;
                    Job  curJob = actor.jobs.curJob;
                    List <LocalTargetInfo> targetQueue = curJob.GetTargetQueue(TargetIndex.B);
                    if (targetQueue.NullOrEmpty())
                    {
                        actor.jobs.curDriver.EndJobWith(JobCondition.Incompletable);
                    }
                    else
                    {
                        foreach (var target in (targetQueue))
                        {
                            if (target == null || target.Thing.DestroyedOrNull())
                            {
                                actor.jobs.curDriver.EndJobWith(JobCondition.Incompletable);
                                break;
                            }
                        }
                    }
                };

                yield return(checklist);

                Toil extract = new Toil();
                extract.initAction = delegate()
                {
                    Pawn actor  = extract.actor;
                    Job  curJob = actor.jobs.curJob;
                    List <LocalTargetInfo> targetQueue = curJob.GetTargetQueue(TargetIndex.B);
                    if (!curJob.countQueue.NullOrEmpty())
                    {
                        if (curJob.countQueue[0] > targetQueue[0].Thing.stackCount)
                        {
                            actor.jobs.curDriver.EndJobWith(JobCondition.Incompletable);
                        }
                        else
                        {
                            curJob.SetTarget(TargetIndex.B, targetQueue[0]);
                            targetQueue.RemoveAt(0);
                            curJob.count = curJob.countQueue[0];
                            curJob.countQueue.RemoveAt(0);
                        }
                    }
                };

                Toil PickUpThing;
                List <LocalTargetInfo> L = __instance.job.GetTargetQueue(TargetIndex.B);
                if (L.Count < 2 && (L.Count == 0 || L[0].Thing.def.stackLimit < 2))
                {
                    PickUpThing = Toils_Haul.StartCarryThing(TargetIndex.B, true, false, true);
                }
                else
                {
                    PickUpThing            = new Toil();
                    PickUpThing.initAction = delegate()
                    {
                        Pawn  actor  = PickUpThing.actor;
                        Job   curJob = actor.jobs.curJob;
                        Thing thing  = curJob.GetTarget(TargetIndex.B).Thing;
                        List <LocalTargetInfo> targetQueue = curJob.GetTargetQueue(TargetIndex.B);
                        bool InventorySpawned = thing.ParentHolder == actor.inventory;
                        if (InventorySpawned || !Toils_Haul.ErrorCheckForCarry(actor, thing))
                        {
                            if (thing.stackCount < curJob.count)
                            {
                                actor.jobs.curDriver.EndJobWith(JobCondition.Incompletable);
                            }
                            else
                            {
                                Thing splitThing = thing.SplitOff(curJob.count);
                                if (splitThing.ParentHolder != actor.inventory && !actor.inventory.GetDirectlyHeldThings().TryAdd(splitThing, false))
                                {
                                    actor.jobs.curDriver.EndJobWith(JobCondition.Incompletable);
                                }


                                if (!splitThing.Destroyed && splitThing.stackCount != 0)
                                {
                                    targetQueue.Add(splitThing);

                                    if (!InventorySpawned)
                                    {
                                        CompUnloadChecker CUC = splitThing.TryGetComp <CompUnloadChecker>();
                                        if (CUC != null)
                                        {
                                            CUC.ShouldUnload = true;
                                        }
                                    }
                                }

                                if (splitThing != thing && actor.Map.reservationManager.ReservedBy(thing, actor, curJob))
                                {
                                    actor.Map.reservationManager.Release(thing, actor, curJob);
                                }
                            }
                        }
                    };
                }

                Toil TakeToHands = new Toil();
                TakeToHands.initAction = delegate()
                {
                    Pawn actor  = TakeToHands.actor;
                    Job  curJob = actor.jobs.curJob;
                    List <LocalTargetInfo> targetQueue = curJob.GetTargetQueue(TargetIndex.B);
                    if (!targetQueue.NullOrEmpty() && targetQueue[0].Thing.ParentHolder != actor.carryTracker)
                    {
                        actor.inventory.innerContainer.TryTransferToContainer(targetQueue[0].Thing, actor.carryTracker.innerContainer);
                        actor.Reserve(targetQueue[0], curJob);
                        curJob.SetTarget(TargetIndex.B, targetQueue[0]);
                        targetQueue.RemoveAt(0);
                    }
                };

                yield return(extract);

                Toil getToHaulTarget = Toils_Goto.GotoThing(TargetIndex.B, PathEndMode.ClosestTouch).FailOnDespawnedNullOrForbidden(TargetIndex.B).FailOnSomeonePhysicallyInteracting(TargetIndex.B);
                yield return(Toils_Jump.JumpIf(PickUpThing, () => __instance.job.GetTarget(TargetIndex.B).Thing.ParentHolder == __instance.pawn.inventory));

                yield return(getToHaulTarget);

                yield return(PickUpThing);

                yield return(Toils_Jump.JumpIf(extract, () => !__instance.job.countQueue.NullOrEmpty()));

                yield return(TakeToHands);

                yield return(Toils_Goto.GotoThing(TargetIndex.A, PathEndMode.InteractionCell).FailOnDestroyedOrNull(TargetIndex.B));

                Toil findPlaceTarget = Toils_JobTransforms.SetTargetToIngredientPlaceCell(TargetIndex.A, TargetIndex.B, TargetIndex.C);
                yield return(findPlaceTarget);

                yield return(Toils_Haul.PlaceHauledThingInCell(TargetIndex.C, findPlaceTarget, false));

                yield return(Toils_Jump.JumpIfHaveTargetInQueue(TargetIndex.B, TakeToHands));
            }
            else
            {
                Toil extract = Toils_JobTransforms.ExtractNextTargetFromQueue(TargetIndex.B, true);
                yield return(extract);

                Toil getToHaulTarget = Toils_Goto.GotoThing(TargetIndex.B, PathEndMode.ClosestTouch).FailOnDespawnedNullOrForbidden(TargetIndex.B).FailOnSomeonePhysicallyInteracting(TargetIndex.B);
                yield return(getToHaulTarget);

                yield return(Toils_Haul.StartCarryThing(TargetIndex.B, true, false, true));

                yield return(JobDriver_DoBill_Access.JumpToCollectNextIntoHandsForBillCrutch(getToHaulTarget, TargetIndex.B));

                yield return(Toils_Goto.GotoThing(TargetIndex.A, PathEndMode.InteractionCell).FailOnDestroyedOrNull(TargetIndex.B));

                Toil findPlaceTarget = Toils_JobTransforms.SetTargetToIngredientPlaceCell(TargetIndex.A, TargetIndex.B, TargetIndex.C);
                yield return(findPlaceTarget);

                yield return(Toils_Haul.PlaceHauledThingInCell(TargetIndex.C, findPlaceTarget, false));

                yield return(Toils_Jump.JumpIfHaveTargetInQueue(TargetIndex.B, extract));
            }

            yield return(gotoBillGiver); //one line from normal scenario

            //cleaning patch
            if (Settings.adv_cleaning && !Utility.IncapableOfCleaning(__instance.pawn))
            {
                Toil returnToBillGiver = Toils_Goto.GotoThing(TargetIndex.A, PathEndMode.InteractionCell);
                Toil FilthList         = new Toil();
                FilthList.initAction = delegate()
                {
                    Job curJob = FilthList.actor.jobs.curJob;
                    if (curJob.GetTargetQueue(TargetIndex.A).NullOrEmpty())
                    {
                        LocalTargetInfo A = curJob.GetTarget(TargetIndex.A);
                        DoCleanComp     comp;
                        if (!Settings.clean_gizmo || (comp = A.Thing?.TryGetComp <DoCleanComp>()) == null || comp.Active)
                        {
                            IEnumerable <Filth> l = Utility.SelectAllFilth(FilthList.actor, A, Settings.adv_clean_num);
                            Utility.AddFilthToQueue(curJob, TargetIndex.A, l, FilthList.actor);
                            FilthList.actor.ReserveAsManyAsPossible(curJob.GetTargetQueue(TargetIndex.A), curJob);
                        }
                        curJob.targetQueueA.Add(A);
                    }
                };
                yield return(FilthList);

                yield return(Toils_Jump.JumpIf(returnToBillGiver, () => __instance.job.GetTargetQueue(TargetIndex.A).NullOrEmpty()));

                Toil CleanFilthList = Toils_JobTransforms.ClearDespawnedNullOrForbiddenQueuedTargets(TargetIndex.A, null);
                yield return(CleanFilthList);

                yield return(Toils_JobTransforms.ExtractNextTargetFromQueue(TargetIndex.A, true));

                yield return(Toils_Jump.JumpIf(returnToBillGiver, () => __instance.job.GetTargetQueue(TargetIndex.A).NullOrEmpty()));

                yield return(Toils_Goto.GotoThing(TargetIndex.A, PathEndMode.Touch).JumpIfDespawnedOrNullOrForbidden(TargetIndex.A, CleanFilthList).JumpIfOutsideHomeArea(TargetIndex.A, CleanFilthList));

                Toil clean = new Toil();
                clean.initAction = delegate()
                {
                    Filth filth = clean.actor.jobs.curJob.GetTarget(TargetIndex.A).Thing as Filth;
                    __instance.billStartTick             = 0;
                    __instance.ticksSpentDoingRecipeWork = 0;
                    __instance.workLeft = filth.def.filth.cleaningWorkToReduceThickness * filth.thickness;
                };
                clean.tickAction = delegate()
                {
                    Filth filth = clean.actor.jobs.curJob.GetTarget(TargetIndex.A).Thing as Filth;
                    __instance.billStartTick             += 1;
                    __instance.ticksSpentDoingRecipeWork += 1;
                    if (__instance.billStartTick > filth.def.filth.cleaningWorkToReduceThickness)
                    {
                        filth.ThinFilth();
                        __instance.billStartTick = 0;
                        if (filth.Destroyed)
                        {
                            clean.actor.records.Increment(RecordDefOf.MessesCleaned);
                            __instance.ReadyForNextToil();
                            return;
                        }
                    }
                };
                clean.defaultCompleteMode = ToilCompleteMode.Never;
                clean.WithEffect(EffecterDefOf.Clean, TargetIndex.A);
                clean.WithProgressBar(TargetIndex.A, () => __instance.ticksSpentDoingRecipeWork / __instance.workLeft, true, -0.5f);
                clean.PlaySustainerOrSound(() => SoundDefOf.Interact_CleanFilth);
                clean.JumpIfDespawnedOrNullOrForbidden(TargetIndex.A, CleanFilthList);
                clean.JumpIfOutsideHomeArea(TargetIndex.A, CleanFilthList);
                yield return(clean);

                yield return(Toils_Jump.Jump(CleanFilthList));

                yield return(returnToBillGiver);
            }

            //continuation of normal scenario
            yield return(Toils_Recipe.MakeUnfinishedThingIfNeeded());

            yield return(Toils_Recipe.DoRecipeWork().FailOnDespawnedNullOrForbiddenPlacedThings().FailOnCannotTouch(TargetIndex.A, PathEndMode.InteractionCell));

            yield return(Toils_Recipe.FinishRecipeAndStartStoringProduct());

            if (!__instance.job.RecipeDef.products.NullOrEmpty() || !__instance.job.RecipeDef.specialProducts.NullOrEmpty())
            {
                yield return(Toils_Reserve.Reserve(TargetIndex.B, 1, -1, null));

                Toil carryToCell = Toils_Haul.CarryHauledThingToCell(TargetIndex.B);
                yield return(carryToCell);

                yield return(Toils_Haul.PlaceHauledThingInCell(TargetIndex.B, carryToCell, true));

                Toil recount = new Toil();
                recount.initAction = delegate()
                {
                    Bill_Production bill_Production = recount.actor.jobs.curJob.bill as Bill_Production;
                    if (bill_Production != null && bill_Production.repeatMode == BillRepeatModeDefOf.TargetCount)
                    {
                        __instance.MapCrutch().resourceCounter.UpdateResourceCounts();
                    }
                };
                yield return(recount);
            }
            yield break;
        }
Exemplo n.º 2
0
        public static Toil PickUpThing(TargetIndex haulxItemInd, Toil nextToilIfBeingOpportunistic)
        {
            Toil toil = new Toil();

            toil.initAction = delegate
            {
                Pawn  actor  = toil.actor;
                Job   job    = actor.CurJob;
                Thing target = job.GetTarget(haulxItemInd).Thing;
                if (Toils_Haul.ErrorCheckForCarry(actor, target))
                {
                    return;
                }

                Thing carriedItem             = actor.carryTracker.CarriedThing;
                int   targetInitialStackcount = target.stackCount;
                int   countToPickUp           = Mathf.Min(
                    job.count - (carriedItem?.stackCount ?? 0),
                    actor.carryTracker.AvailableStackSpace(target.def),
                    targetInitialStackcount);
                if (countToPickUp <= 0)
                {
                    throw new Exception("PickUpThing countToPickUp = " + countToPickUp);
                }

                //pick up
                int countPickedUp = actor.carryTracker.TryStartCarry(target, countToPickUp);
                if (countPickedUp < targetInitialStackcount)
                {
                    actor.Map.reservationManager.Release(target, actor, job);
                }
                carriedItem = actor.carryTracker.CarriedThing;
                job.SetTarget(haulxItemInd, carriedItem);
                actor.records.Increment(RecordDefOf.ThingsHauled);

                //register the carried item (into the HaulExplicitly job)
                if (carriedItem.IsAHaulableSetToHaulable())
                {
                    carriedItem.ToggleHaulDesignation();
                }
                var driver = (JobDriver_HaulExplicitly)actor.jobs.curDriver;
                driver.posting.TryAddItemSplinter(carriedItem);

                //pick up next available item in job?
                if (actor.CurJob.haulOpportunisticDuplicates)
                {
                    Thing prospect  = null;
                    int   best_dist = 999;
                    foreach (Thing item in driver.record.items.Where(
                                 i => i.Spawned && WorkGiver_HaulExplicitly.CanGetThing(actor, i, false)))
                    {
                        IntVec3 offset = item.Position - actor.Position;
                        int     dist   = Math.Abs(offset.x) + Math.Abs(offset.z);
                        if (dist < best_dist && dist < 7)
                        {
                            prospect  = item;
                            best_dist = dist;
                        }
                    }
                    if (prospect == null)
                    {
                        return;
                    }
                    int space_request = WorkGiver_HaulExplicitly
                                        .AmountPawnWantsToPickUp(actor, prospect, driver.posting);
                    if (space_request == 0)
                    {
                        return;
                    }
                    var            destInfo = DeliverableDestinations.For(prospect, actor, driver.posting);
                    List <IntVec3> dests    = destInfo.RequestSpaceForItemAmount(
                        Math.Max(0, space_request - driver.dest_space_available));
                    int new_dest_space = destInfo.FreeSpaceInCells(dests);
                    var count          = Math.Min(space_request, driver.dest_space_available + new_dest_space);
                    if (count < 1)
                    {
                        return;
                    }

                    //commit to it
                    actor.Reserve(prospect, job);
                    job.SetTarget(haulxItemInd, prospect);
                    job.SetTarget(TargetIndex.C, prospect.Position);
                    foreach (var dest in dests)
                    {
                        actor.Reserve(dest, job);
                        job.targetQueueB.Add(dest);
                    }
                    job.count += count;
                    driver.JumpToToil(nextToilIfBeingOpportunistic);
                }
            };
            return(toil);
        }
        //get next, goto, take, check for more. Branches off to "all over the place"
        protected override IEnumerable <Toil> MakeNewToils()
        {
            CompHauledToInventory takenToInventory = pawn.TryGetComp <CompHauledToInventory>();

            Toil wait = Toils_General.Wait(2);

            Toil nextTarget = Toils_JobTransforms.ExtractNextTargetFromQueue(TargetIndex.A); //also does count

            yield return(nextTarget);

            //honestly the workgiver checks for encumbered, so until CE checks are in this is unnecessary
            //yield return CheckForOverencumbered();//Probably redundant without CE checks

            Toil gotoThing = new Toil
            {
                initAction = () =>
                {
                    pawn.pather.StartPath(TargetThingA, PathEndMode.ClosestTouch);
                },
                defaultCompleteMode = ToilCompleteMode.PatherArrival
            };

            gotoThing.FailOnDespawnedNullOrForbidden(TargetIndex.A);
            yield return(gotoThing);

            Toil takeThing = new Toil
            {
                initAction = () =>
                {
                    Pawn  actor = pawn;
                    Thing thing = actor.CurJob.GetTarget(TargetIndex.A).Thing;
                    Toils_Haul.ErrorCheckForCarry(actor, thing);

                    //get max we can pick up
                    int countToPickUp = Mathf.Min(job.count, MassUtility.CountToPickUpUntilOverEncumbered(actor, thing));
                    Log.Message($"{actor} is hauling to inventory {thing}:{countToPickUp}");

                    // yo dawg, I heard you like delegates so I put delegates in your delegate, so you can delegate your delegates.
                    // because compilers don't respect IF statements in delegates and toils are fully iterated over as soon as the job starts.
                    try
                    {
                        ((Action)(() =>
                        {
                            if (ModCompatibilityCheck.CombatExtendedIsActive)
                            {
                                //CombatExtended.CompInventory ceCompInventory = actor.GetComp<CombatExtended.CompInventory>();
                                //ceCompInventory.CanFitInInventory(thing, out countToPickUp);
                            }
                        }))();
                    }
                    catch (TypeLoadException) { }

                    if (countToPickUp > 0)
                    {
                        Thing splitThing  = thing.SplitOff(countToPickUp);
                        bool  shouldMerge = takenToInventory.GetHashSet().Any(x => x.def == thing.def);
                        actor.inventory.GetDirectlyHeldThings().TryAdd(splitThing, shouldMerge);
                        takenToInventory.RegisterHauledItem(splitThing);

                        try
                        {
                            ((Action)(() =>
                            {
                                if (ModCompatibilityCheck.CombatExtendedIsActive)
                                {
                                    //CombatExtended.CompInventory ceCompInventory = actor.GetComp<CombatExtended.CompInventory>();
                                    //ceCompInventory.UpdateInventory();
                                }
                            }))();
                        }
                        catch (TypeLoadException)
                        {
                        }
                    }

                    //thing still remains, so queue up hauling if we can + end the current job (smooth/instant transition)
                    //This will technically release the reservations in the queue, but what can you do
                    if (thing.Spawned)
                    {
                        Job haul = HaulAIUtility.HaulToStorageJob(actor, thing);
                        if (haul?.TryMakePreToilReservations(actor, false) ?? false)
                        {
                            actor.jobs.jobQueue.EnqueueFirst(haul, JobTag.Misc);
                        }
                        actor.jobs.curDriver.JumpToToil(wait);
                    }
                }
            };

            yield return(takeThing);

            yield return(Toils_Jump.JumpIf(nextTarget, () => !job.targetQueueA.NullOrEmpty()));

            //Find more to haul, in case things spawned while this was in progess
            yield return(new Toil
            {
                initAction = () =>
                {
                    List <Thing> haulables = pawn.Map.listerHaulables.ThingsPotentiallyNeedingHauling();
                    WorkGiver_HaulToInventory haulMoreWork = DefDatabase <WorkGiverDef> .AllDefsListForReading.First(wg => wg.Worker is WorkGiver_HaulToInventory).Worker as WorkGiver_HaulToInventory;

                    Thing haulMoreThing = GenClosest.ClosestThing_Global(pawn.Position, haulables, 12, t => haulMoreWork.HasJobOnThing(pawn, t));

                    //WorkGiver_HaulToInventory found more work nearby
                    if (haulMoreThing != null)
                    {
                        Log.Message($"{pawn} hauling again : {haulMoreThing}");
                        Job haulMoreJob = haulMoreWork.JobOnThing(pawn, haulMoreThing);

                        if (haulMoreJob.TryMakePreToilReservations(pawn, false))
                        {
                            pawn.jobs.jobQueue.EnqueueFirst(haulMoreJob, JobTag.Misc);
                            EndJobWith(JobCondition.Succeeded);
                        }
                    }
                }
            });

            //maintain cell reservations on the trip back
            //TODO: do that when we carry things
            //I guess that means TODO: implement carrying the rest of the items in this job instead of falling back on HaulToStorageJob
            yield return(Toils_Goto.GotoCell(TargetIndex.B, PathEndMode.ClosestTouch));

            yield return(new Toil //Queue next job
            {
                initAction = () =>
                {
                    Pawn actor = pawn;
                    Job curJob = actor.jobs.curJob;
                    LocalTargetInfo storeCell = curJob.targetB;

                    Job unloadJob = new Job(PickUpAndHaulJobDefOf.UnloadYourHauledInventory, storeCell);
                    if (unloadJob.TryMakePreToilReservations(actor, false))
                    {
                        actor.jobs.jobQueue.EnqueueFirst(unloadJob, JobTag.Misc);
                        EndJobWith(JobCondition.Succeeded);
                        //This will technically release the cell reservations in the queue, but what can you do
                    }
                }
            });

            yield return(wait);
        }
        //reserve, goto, take, check for more. Branches off to "all over the place"
        protected override IEnumerable <Toil> MakeNewToils()
        {
            CompHauledToInventory takenToInventory        = pawn.TryGetComp <CompHauledToInventory>();
            HashSet <Thing>       carriedThings           = takenToInventory.GetHashSet();
            DesignationDef        HaulUrgentlyDesignation = DefDatabase <DesignationDef> .GetNamed("HaulUrgentlyDesignation", false);

            //Thanks to AlexTD for the more dynamic search range
            float searchForOthersRangeFraction = 0.5f;
            float distanceToOthers             = 0f;

            Toil wait           = Toils_General.Wait(2);
            Toil reserveTargetA = Toils_Reserve.Reserve(TargetIndex.A, 1, -1, null);

            Toil calculateExtraDistanceToGo = new Toil
            {
                initAction = () =>
                {
                    if (StoreUtility.TryFindStoreCellNearColonyDesperate(this.job.targetA.Thing, this.pawn, out IntVec3 storeLoc))
                    {
                        distanceToOthers = (storeLoc - job.targetA.Thing.Position).LengthHorizontal * searchForOthersRangeFraction;
                    }
                }
            };

            yield return(calculateExtraDistanceToGo);

            Toil checkForOtherItemsToHaulToInventory         = CheckForOtherItemsToHaulToInventory(reserveTargetA, TargetIndex.A, distanceToOthers, null);
            Toil checkForOtherItemsToUrgentlyHaulToInventory = CheckForOtherItemsToHaulToInventory(reserveTargetA, TargetIndex.A, distanceToOthers, (Thing x) => pawn.Map.designationManager.DesignationOn(x)?.def == HaulUrgentlyDesignation);

            yield return(reserveTargetA);

            Toil gotoThing = new Toil
            {
                initAction = () =>
                {
                    this.pawn.pather.StartPath(this.TargetThingA, PathEndMode.ClosestTouch);
                },
                defaultCompleteMode = ToilCompleteMode.PatherArrival,
            };

            gotoThing.FailOnDespawnedNullOrForbidden(TargetIndex.A);
            yield return(gotoThing);

            Toil takeThing = new Toil
            {
                initAction = () =>
                {
                    Pawn  actor = this.pawn;
                    Thing thing = actor.CurJob.GetTarget(TargetIndex.A).Thing;
                    Toils_Haul.ErrorCheckForCarry(actor, thing);

                    //get max we can pick up
                    int num = Mathf.Min(thing.stackCount, MassUtility.CountToPickUpUntilOverEncumbered(actor, thing));

                    // yo dawg, I heard you like delegates so I put delegates in your delegate, so you can delegate your delegates.
                    // because compilers don't respect IF statements in delegates and toils are fully iterated over as soon as the job starts.
                    try
                    {
                        ((Action)(() =>
                        {
                            if (ModCompatibilityCheck.CombatExtendedIsActive)
                            {
                                CombatExtended.CompInventory ceCompInventory = actor.GetComp <CombatExtended.CompInventory>();
                                ceCompInventory.CanFitInInventory(thing, out num, false, false);
                            }
                        }))();
                    }
                    catch (TypeLoadException) { }

                    //can't store more, so queue up hauling if we can + end the current job (smooth/instant transition)
                    if (num <= 0)
                    {
                        Job haul = HaulAIUtility.HaulToStorageJob(actor, thing);
                        if (haul?.TryMakePreToilReservations(actor) ?? false)
                        {
                            actor.jobs.jobQueue.EnqueueFirst(haul, new JobTag?(JobTag.Misc));
                        }
                        actor.jobs.curDriver.JumpToToil(wait);
                    }
                    else
                    {
                        bool isUrgent = false;
                        if (ModCompatibilityCheck.AllowToolIsActive)
                        {
                            //check BEFORE absorbing the thing, designation disappears when it's in inventory :^)
                            if (pawn.Map.designationManager.DesignationOn(thing)?.def == HaulUrgentlyDesignation)
                            {
                                isUrgent = true;
                            }
                        }

                        actor.inventory.GetDirectlyHeldThings().TryAdd(thing.SplitOff(num), true);
                        takenToInventory.RegisterHauledItem(thing);

                        try
                        {
                            ((Action)(() =>
                            {
                                if (ModCompatibilityCheck.CombatExtendedIsActive)
                                {
                                    CombatExtended.CompInventory ceCompInventory = actor.GetComp <CombatExtended.CompInventory>();
                                    ceCompInventory.UpdateInventory();
                                }
                            }))();
                        }
                        catch (TypeLoadException) { }

                        if (isUrgent)
                        {
                            actor.jobs.curDriver.JumpToToil(checkForOtherItemsToUrgentlyHaulToInventory);
                        }
                    }
                }
            };

            yield return(takeThing);

            yield return(checkForOtherItemsToHaulToInventory); //we end the job in there, so only one of the checks for duplicates gets called.

            yield return(checkForOtherItemsToUrgentlyHaulToInventory);

            yield return(wait);
        }