public override bool HasJobOnThing(Pawn pawn, Thing t, bool forced = false) { Building_GrowerBase_WorkTable grower = t as Building_GrowerBase_WorkTable; if (grower == null) { return(false); } if (!grower.GrowerProps.productRequireManualExtraction) { return(false); } if (t.IsForbidden(pawn)) { return(false); } if (!pawn.CanReserve(t)) { return(false); } if (grower.status != CrafterStatus.Finished) { return(false); } return(true); }
}//end JobOnThing() /// <summary> /// Checks if a pawn can be assigned to a bill based on factors including PawnRestrictions, workSkills, etc. /// If a bill passes all the checks, it's assigned as the ActiveBill in the billProcessor. /// </summary> /// <param name="p"></param> /// <param name="grower"></param> /// <param name="reason"></param> /// <returns></returns> public Bill GetBillPawnCanDo(Pawn p, Building_GrowerBase_WorkTable grower, out string reason) { reason = GenericFailReasonTrans; BillStack bills = grower.billStack; LocalTargetInfo target = grower; //loop through bills for (int i = 0; i < bills.Count; i++) { Bill_Production theBill = bills[i] as Bill_Production; if (!PawnCanDoThisBill(p, theBill, target, out reason)) { continue; } //check if cached ingredients search found ingredients for this bill Thing cachedThing; grower.billProc.ingredientsAvailableNow.TryGetValue(theBill.GetUniqueLoadID(), out cachedThing); if (cachedThing == null) { QEEMod.TryLog("GetBillPawnCanDo - no ingredients available"); reason = NoIngredientsTrans; continue; } grower.billProc.ActiveBill = theBill; QEEMod.TryLog(p.LabelShort + " can do bill " + theBill.GetUniqueLoadID()); return(theBill); } return(null); } //end GetBillPawnCanDo()
public override Job JobOnThing(Pawn pawn, Thing t, bool forced = false) { Building_GrowerBase_WorkTable grower = t as Building_GrowerBase_WorkTable; Job job = new Job(QEJobDefOf.QE_ExtractProductFromGrowerJob, t); return(job); }
public override bool HasJobOnThing(Pawn pawn, Thing t, bool forced = false) { Building_GrowerBase_WorkTable grower = t as Building_GrowerBase_WorkTable; if (grower == null) { return(false); } IMaintainableGrower maintainable = grower as IMaintainableGrower; if (maintainable == null) { return(false); } if (t.IsForbidden(pawn)) { return(false); } if (!pawn.CanReserve(t)) { return(false); } if (grower.status != CrafterStatus.Crafting) { return(false); } bool shouldMaintainScience = true; if (maintainable.ScientistMaintenance > QEESettings.instance.maintWorkThresholdFloat) { if (maintainable.ScientistMaintenance > 0.90f) { shouldMaintainScience = false; } shouldMaintainScience = forced; } bool shouldMaintainDoctor = true; if (maintainable.DoctorMaintenance > QEESettings.instance.maintWorkThresholdFloat) { if (maintainable.DoctorMaintenance > 0.90f) { shouldMaintainDoctor = false; } shouldMaintainDoctor = forced; } bool maintainAtAll = shouldMaintainScience || shouldMaintainDoctor; return(maintainAtAll); }
/// <summary> /// Finds the closest Thing on the map to a pawn that matches the ThingDef needed by the Bill. /// The available ingredients are cached in the BillProcessor variable in Building_GrowerBase_WorkTable. /// If the cached ThingDef is no longer available, it will look for another ThingDef that's needed. /// </summary> /// <param name="theBill"></param> /// <param name="finder"></param> /// <param name="countForVat"></param> /// <returns></returns> public static Thing ThingPawnShouldRetrieveForBill(Bill theBill, Pawn finder, ref int countForVat) { Building_GrowerBase_WorkTable vat = theBill.billStack.billGiver as Building_GrowerBase_WorkTable; ThingOwner vatStoredIngredients = vat.GetDirectlyHeldThings(); Thing cachedThing = null; bool ingAreAvailable = vat.billProc.ingredientsAvailableNow.TryGetValue(theBill.GetUniqueLoadID(), out cachedThing); if (cachedThing == null) { QEEMod.TryLog("ThingPawnShouldRetrieveForBill() returning null. Reason - cachedThing is null"); return(null); } if (ingAreAvailable == false) { QEEMod.TryLog("ThingPawnShouldRetrieveForBill() returning null. Reason - ingAreAvailable is false"); return(null); } ThingRequest tRequest; ThingOrderRequest desiredRequest; vat.billProc.desiredRequests.TryGetValue(cachedThing.def.defName, out desiredRequest); //check that the vat still needs the cached ingredient before searching the map for the same ThingDef if (desiredRequest == null || desiredRequest.amount <= 0) { QEEMod.TryLog("Cached ingredient " + cachedThing.LabelShort + " is already fulfilled in vat. Looking for next ingredient in recipe"); //this ingredient isn't in desiredIngredients or the vat has the full amount. Refresh desiredIngredients and try once more vat.billProc.UpdateDesiredRequests(); //now get a random item in the dictionary of desired requests foreach (ThingOrderRequest value in vat.billProc.desiredRequests.Values) { desiredRequest = value; } //return if there's no thingDef or desiredRequest is null if (desiredRequest == null) { QEEMod.TryLog("ThingPawnShouldRetrieveForBill() returning null. Reason - Desired ThingOrderRequest is null"); return(null); } //return if the amount for this request is 0 if (desiredRequest.amount <= 0) { QEEMod.TryLog("ThingPawnShouldRetrieveForBill() returning null. Reason - Desired Thing " + desiredRequest.Label + " has 0 amount"); return(null); } } tRequest = desiredRequest.GetThingRequest(); if (tRequest.IsUndefined) { QEEMod.TryLog("ThingPawnShouldRetrieveForBill() returning null. Reason - ThingRequest for " + desiredRequest.Label + " returned undefined ThingRequest"); return(null); } countForVat = desiredRequest.amount; QEEMod.TryLog("Searching map for closest " + desiredRequest.Label + " to " + finder.LabelShort); //search the map for the closest Thing to the pawn that matches the ThingDef in 'tRequest' Thing result = GenClosest.ClosestThingReachable(finder.Position, finder.Map, tRequest, PathEndMode.OnCell, TraverseParms.For(finder), validator : delegate(Thing testThing) { if (tRequest.Accepts(testThing)) { if (testThing.IsForbidden(finder)) { return(false); } if (!finder.CanReserve(testThing)) { return(false); } return(true); } return(false); }); if (result != null) { QEEMod.TryLog(finder.LabelShort + " should retrieve: " + result.Label + " | stackCount: " + result.stackCount + " | countForVat: " + countForVat); } return(result); } //end FindClosestIngForBill
protected override IEnumerable <Toil> MakeNewToils() { this.FailOnBurningImmobile(BillGiverInd); this.FailOnDestroyedNullOrForbidden(BillGiverInd); this.FailOnForbidden(IngredientInd); this.FailOn(delegate { Thing building = job.GetTarget(BillGiverInd).Thing; IBillGiver billGiver = building as IBillGiver; if (building == null || billGiver == null || job.bill == null) { QEEMod.TryLog("something is null, failing job"); return(true); } Building_GrowerBase_WorkTable grower = job.GetTarget(BillGiverInd).Thing as Building_GrowerBase_WorkTable; if (job.bill.DeletedOrDereferenced) { QEEMod.TryLog("Bill deleted, failing job"); //refund ingredients if player cancels bill during Filling phase if (grower != null && grower.status == CrafterStatus.Filling) { QEEMod.TryLog("Bill cancelled, refunding ingredients"); grower.StopCrafting(true); } return(true); } if (grower.status != CrafterStatus.Filling) { QEEMod.TryLog("Crafter is not 'filling', ending job"); return(true); } return(false); }); Toil logToil = new Toil() { initAction = delegate() { QEEMod.TryLog("Pawn " + GetActor().LabelShort + " | Toil: " + logCounter + " | Job: " + job.GetUniqueLoadID()); logCounter++; } }; ////yield return logToil; //travel to ingredient and carry it Toil reserveIng = Toils_Reserve.Reserve(IngredientInd); yield return(reserveIng); //yield return logToil; Toil travelToil = Toils_Goto.GotoThing(IngredientInd, PathEndMode.OnCell); yield return(travelToil); //yield return logToil; Toil carryThing = Toils_Haul.StartCarryThing(IngredientInd, subtractNumTakenFromJobCount: true); yield return(carryThing); //yield return logToil; //Opportunistically haul a nearby ingredient of same ThingDef. Checks 8 square radius. yield return(Toils_Haul.CheckForGetOpportunityDuplicate(reserveIng, IngredientInd, TargetIndex.None, takeFromValidStorage: true)); //yield return logToil; //head back to grower yield return(Toils_Goto.GotoThing(BillGiverInd, PathEndMode.InteractionCell).FailOnDestroyedOrNull(IngredientInd)); //yield return logToil; //deposit into grower Toil depositIntoGrower = new Toil() { initAction = delegate() { Building_GrowerBase_WorkTable grower = TargetThingA as Building_GrowerBase_WorkTable; if (grower != null) { grower.FillThing(GetActor().carryTracker.CarriedThing); } } }; yield return(depositIntoGrower); Toil tryStartCrafting = new Toil() { initAction = delegate() { Building_GrowerBase_WorkTable grower = job.GetTarget(BillGiverInd).Thing as Building_GrowerBase_WorkTable; Pawn actor = GetActor(); //if all ingredients have been loaded, start crafting if (grower?.billProc != null) { if (grower.billProc.AnyPendingRequests == false) { grower.Notify_CraftingStarted(); } } actor.jobs.EndCurrentJob(JobCondition.Succeeded); } }; yield return(tryStartCrafting); } //end function MakeNewToils()
} // end TryDropProductOnFloor /// <summary> /// Generates the recipe product, then looks for a valid cell in the Bill's output stockpile. /// If a valid cell is found, puts the product into the pawn's arms for transport in a future Toil. /// Uses code from the Toils_Recipe.FinishRecipeAndStartStoringProduct() toil in vanilla. /// </summary> /// <param name="vat"></param> /// <returns></returns> public static Toil StartCarryProductToStockpile(Building_GrowerBase_WorkTable vat) { Toil toil = new Toil(); toil.initAction = delegate { Pawn actor = toil.actor; Job curJob = actor.jobs.curJob; //Skip null checking, as it was done in previous toil Thing product = ThingMaker.MakeThing(vat.activeRecipe.products[0].thingDef); product.stackCount = vat.activeRecipe.products[0].count; Bill_Production activeBill = vat.billProc.ActiveBill; //decrement billstack count if (activeBill.repeatMode == BillRepeatModeDefOf.RepeatCount) { if (activeBill.repeatCount > 0) { activeBill.repeatCount--; } } IntVec3 foundCell = IntVec3.Invalid; //find the best cell to put the product in if (activeBill.GetStoreMode() == BillStoreModeDefOf.BestStockpile) { StoreUtility.TryFindBestBetterStoreCellFor(product, actor, actor.Map, StoragePriority.Unstored, actor.Faction, out foundCell); } else if (activeBill.GetStoreMode() == BillStoreModeDefOf.SpecificStockpile) { StoreUtility.TryFindBestBetterStoreCellForIn(product, actor, actor.Map, StoragePriority.Unstored, actor.Faction, activeBill.GetStoreZone().slotGroup, out foundCell); } else { Log.ErrorOnce("Unknown bill store mode", 9158246); } //if a cell was found in a stockpile, start a hauling job to move the product from the ground to that cell if (foundCell.IsValid) { bool tryCarrySuccess = actor.carryTracker.TryStartCarry(product); QEEMod.TryLog("Valid stockpile found - haul product to cell " + foundCell + ". TryStartCarry() result for " + actor.LabelShort + ": " + tryCarrySuccess); curJob.targetB = foundCell; curJob.targetA = product; curJob.count = 99999; vat.Notify_ProductExtracted(actor); //the next toil in the JobDriver will now haul the carried object to the stockpile } else { QEEMod.TryLog("No stockpile found to haul " + product.Label + " to. Dropping product on ground."); if (GenPlace.TryPlaceThing(product, actor.Position, actor.Map, ThingPlaceMode.Near)) { vat.Notify_ProductExtracted(actor); } else { QEEMod.TryLog(actor + " could not drop recipe product " + product + " near " + actor.Position + ". Ending extract job."); } actor.jobs.EndCurrentJob(JobCondition.Succeeded); } }; return(toil); } // end StartCarryProductToStockpile
/// <summary> /// Checks if Bill specifies to drop product on ground, and if so, creates the product and drops it. /// The current grower's active Bill is retrieved programmatically via grower members. /// Heavily modified version of the Toils_Recipe.FinishRecipeAndStartStoringProduct() toil in vanilla. /// </summary> /// <returns></returns> public static Toil TryDropProductOnFloor(Building_GrowerBase_WorkTable vat) { Toil toil = new Toil(); toil.initAction = delegate { Pawn actor = toil.actor; Bill_Production activeBill = vat.billProc.ActiveBill; if (vat?.activeRecipe?.products[0]?.thingDef == null) { if (vat?.activeRecipe?.label != null) { QEEMod.TryLog("No product found in recipe " + vat.activeRecipe.label + ".Ending extract Job."); } else { QEEMod.TryLog("No product found in recipe. Ending extract Job."); } //decrement billstack count if (activeBill.repeatMode == BillRepeatModeDefOf.RepeatCount) { if (activeBill.repeatCount > 0) { activeBill.repeatCount--; } } actor.jobs.EndCurrentJob(JobCondition.Succeeded); } else if (activeBill == null || activeBill.GetStoreMode() == BillStoreModeDefOf.DropOnFloor) { //true if no output stockpile specified in bill options Thing product = ThingMaker.MakeThing(vat.activeRecipe.products[0].thingDef); product.stackCount = vat.activeRecipe.products[0].count; if (activeBill?.GetUniqueLoadID() != null) { QEEMod.TryLog("activeBill: " + activeBill?.GetUniqueLoadID() + " specifies dropping " + product.Label + " on floor."); } else { QEEMod.TryLog("activeBill is null, dropping " + product.Label + " on floor."); } //put the product on the ground on the same cell as the pawn if (GenPlace.TryPlaceThing(product, actor.Position, actor.Map, ThingPlaceMode.Near)) { vat.Notify_ProductExtracted(actor); } else { QEEMod.TryLog(actor + " could not drop recipe product " + product + " near " + actor.Position + ". Ending extract job."); } //decrement billstack count if (activeBill.repeatMode == BillRepeatModeDefOf.RepeatCount) { if (activeBill.repeatCount > 0) { activeBill.repeatCount--; } } actor.jobs.EndCurrentJob(JobCondition.Succeeded); } }; return(toil); } // end TryDropProductOnFloor
/* return true if all conditions are met: * a) any bills outstanding * b) status is 'Idle' * c) the usual reservation and forbidden checks pass */ public override bool HasJobOnThing(Pawn pawn, Thing aThing, bool forced = false) { Building_GrowerBase_WorkTable grower = aThing as Building_GrowerBase_WorkTable; IBillGiver billGiver = aThing as IBillGiver; BillProcessor processor = grower.billProc; LocalTargetInfo target = aThing; if (grower == null || billGiver == null) { return(false); } if (grower.status == CrafterStatus.Crafting || grower.status == CrafterStatus.Finished) { JobFailReason.Is(GrowerBusyTrans); return(false); } if (!processor.AnyPendingRequests) { JobFailReason.Is(NoBillsQueuedTrans); return(false); } //check if the grower's cached ingredients search found ingredients for any bill if (grower.billProc.anyBillIngredientsAvailable == false) { JobFailReason.Is(NoIngredientsTrans); return(false); } if (aThing.IsBurning()) { JobFailReason.Is(IsBurningTrans); return(false); } if (aThing.IsForbidden(pawn)) { return(false); } if (!ThingIsUsableBillGiver(aThing)) { return(false); } string reason = ""; //if the grower isn't growing anything, loop through the billstack and check for any bill the pawn can do if (grower.billProc.ActiveBill == null) { if (GetBillPawnCanDo(pawn, grower, out reason) == null) { JobFailReason.Is(reason); return(false); } } else if (!PawnCanDoThisBill(pawn, grower.billProc.ActiveBill, target, out reason)) { JobFailReason.Is(reason); return(false); } return(true); }//end HasJobOnThing()
}//end HasJobOnThing() public override Job JobOnThing(Pawn p, Thing t, bool forced = false) { IBillGiver billGiver = t as IBillGiver; Building_GrowerBase_WorkTable grower = t as Building_GrowerBase_WorkTable; BillProcessor processor = grower.billProc; //if null is returned from this function, the game will throw an error. Instead, //return this simple wait job to avoid unnecessary errors. Job returnJobOnFailure = new Job(JobDefOf.Wait, 5); //check if there are any pending requests (unfulfilled bills) if (!processor.AnyPendingRequests) { QEEMod.TryLog("AnyPendingRequests is false inside JobOnThing()!"); return(returnJobOnFailure); } //check if there are ingredients available for those bills if (processor.ingredientsAvailableNow.Count <= 0) { QEEMod.TryLog("ingredientsAvailableNow.Count is 0 inside JobOnThing()!"); return(returnJobOnFailure); } //get the reference to the currently active bill Bill theBill = processor.ActiveBill; if (theBill == null) { QEEMod.TryLog("Attempt to get ActiveBill failed inside JobOnThing()!"); return(returnJobOnFailure); } //get the cached ingredient that's available. This Thing is not always going to be used in the Job, //but we know there's at least one stack of this Thing available on the map Thing cachedThing; grower.billProc.ingredientsAvailableNow.TryGetValue(theBill.GetUniqueLoadID(), out cachedThing); if (cachedThing == null) { QEEMod.TryLog("Attempt to retrieve cached ingredients failed for " + theBill.GetUniqueLoadID()); return(returnJobOnFailure); } //get the nearest Thing to the Pawn with the same ThingDef int countForVat = 0; Thing thingToFill = IngredientUtility.ThingPawnShouldRetrieveForBill(theBill, p, ref countForVat); if (thingToFill == null) { grower.billProc.anyBillIngredientsAvailable = false; QEEMod.TryLog("ThingPawnShouldRetrieveForBill() is null for " + theBill.GetUniqueLoadID()); return(returnJobOnFailure); } //all checks have passed! Return the Job and notify the grower that it's time to start Filling grower.Notify_FillingStarted(theBill); Job returnJob = new Job(QEJobDefOf.QE_LoadGrowerJob, t, thingToFill); returnJob.count = countForVat; returnJob.bill = theBill; return(returnJob); }//end JobOnThing()