/// <summary> /// the workgiver checks for encumbered, this is purely extra for CE /// </summary> /// <returns></returns> public Toil CheckForOverencumberedForCombatExtended() { Toil toil = new Toil(); if (!ModCompatibilityCheck.CombatExtendedIsActive) { return(toil); } toil.initAction = delegate { Pawn actor = toil.actor; Job curJob = actor.jobs.curJob; Thing nextThing = curJob.targetA.Thing; var ceOverweight = CompatHelper.CeOverweight(pawn); if (!(MassUtility.EncumbrancePercent(actor) <= 0.9f && !ceOverweight)) { Job haul = HaulAIUtility.HaulToStorageJob(actor, nextThing); if (haul?.TryMakePreToilReservations(actor, false) ?? false) { //note that HaulToStorageJob etc doesn't do opportunistic duplicate hauling for items in valid storage. REEEE actor.jobs.jobQueue.EnqueueFirst(haul, JobTag.Misc); EndJobWith(JobCondition.Succeeded); } } }; 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); yield return(CheckForOverencumberedForCombatExtended()); 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}"); if (ModCompatibilityCheck.CombatExtendedIsActive) { countToPickUp = CompatHelper.CanFitInInventory(pawn, thing); } 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); if (ModCompatibilityCheck.CombatExtendedIsActive) { CompatHelper.UpdateInventory(pawn); } } //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 = JobMaker.MakeJob(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); }
/// <summary> /// Find spot, reserve spot, pull thing out of inventory, go to spot, drop stuff, repeat. /// </summary> /// <returns></returns> protected override IEnumerable <Toil> MakeNewToils() { CompHauledToInventory takenToInventory = pawn.TryGetComp <CompHauledToInventory>(); HashSet <Thing> carriedThing = takenToInventory.GetHashSet(); if (ModCompatibilityCheck.ExtendedStorageIsActive) { _unloadDuration = 20; } Toil wait = Toils_General.Wait(_unloadDuration); Toil celebrate = Toils_General.Wait(_unloadDuration); yield return(wait); Toil findSpot = new Toil { initAction = () => { ThingCount unloadableThing = FirstUnloadableThing(pawn); if (unloadableThing.Count == 0 && carriedThing.Count == 0) { EndJobWith(JobCondition.Succeeded); } if (unloadableThing.Count != 0) { //StoragePriority currentPriority = StoreUtility.StoragePriorityAtFor(pawn.Position, unloadableThing.Thing); if (!StoreUtility.TryFindStoreCellNearColonyDesperate(unloadableThing.Thing, pawn, out IntVec3 c)) { pawn.inventory.innerContainer.TryDrop(unloadableThing.Thing, ThingPlaceMode.Near, unloadableThing.Thing.stackCount, out Thing _); EndJobWith(JobCondition.Succeeded); } else { job.SetTarget(TargetIndex.A, unloadableThing.Thing); job.SetTarget(TargetIndex.B, c); _countToDrop = unloadableThing.Thing.stackCount; } } } }; yield return(findSpot); yield return(Toils_Reserve.Reserve(TargetIndex.B)); yield return(new Toil { initAction = delegate { Thing thing = job.GetTarget(TargetIndex.A).Thing; if (thing == null || !pawn.inventory.innerContainer.Contains(thing)) { carriedThing.Remove(thing); pawn.jobs.curDriver.JumpToToil(wait); return; } if (!pawn.health.capacities.CapableOf(PawnCapacityDefOf.Manipulation) || !thing.def.EverStorable(false)) { pawn.inventory.innerContainer.TryDrop(thing, ThingPlaceMode.Near, _countToDrop, out thing); EndJobWith(JobCondition.Succeeded); carriedThing.Remove(thing); } else { pawn.inventory.innerContainer.TryTransferToContainer(thing, pawn.carryTracker.innerContainer, _countToDrop, out thing); job.count = _countToDrop; job.SetTarget(TargetIndex.A, thing); carriedThing.Remove(thing); } if (ModCompatibilityCheck.CombatExtendedIsActive) { CompatHelper.UpdateInventory(pawn); } thing.SetForbidden(false, false); } }); Toil carryToCell = Toils_Haul.CarryHauledThingToCell(TargetIndex.B); yield return(Toils_Goto.GotoCell(TargetIndex.B, PathEndMode.Touch)); yield return(carryToCell); yield return(Toils_Haul.PlaceHauledThingInCell(TargetIndex.B, carryToCell, true)); //If the original cell is full, PlaceHauledThingInCell will set a different TargetIndex resulting in errors on yield return Toils_Reserve.Release. //We still gotta release though, mostly because of Extended Storage. Toil releaseReservation = new Toil { initAction = () => { if (pawn.Map.reservationManager.ReservedBy(job.targetB, pawn, pawn.CurJob) && !ModCompatibilityCheck.HCSKIsActive) { pawn.Map.reservationManager.Release(job.targetB, pawn, pawn.CurJob); } } }; yield return(releaseReservation); yield return(Toils_Jump.Jump(wait)); yield return(celebrate); }