/// <summary> /// Copy of vanilla's Recipe_InstallNaturalBodyPart.GetPartsToApplyOn() plus the addition of code that respects the addsHediff node /// in the recipe (vanilla does not). The addsHediff node is currently used in the RBSE compatibility patch to add organ rejection. /// </summary> /// <param name="pawn"></param> /// <param name="part"></param> /// <param name="billDoer"></param> /// <param name="ingredients"></param> /// <param name="bill"></param> public override void ApplyOnPawn(Pawn pawn, BodyPartRecord part, Pawn billDoer, List <Thing> ingredients, Bill bill) { if (billDoer != null && !CheckSurgeryFail(billDoer, pawn, ingredients, part, bill)) { TaleRecorder.RecordTale(TaleDefOf.DidSurgery, billDoer, pawn); MedicalRecipesUtility.RestorePartAndSpawnAllPreviousParts(pawn, part, billDoer.Position, billDoer.Map); } //add any hediffs in the <addsHediff> element of the recipe, after surgery involving natural body parts completes. if (bill.recipe.addsHediff != null && CompatibilityTracker.RBSEActive && RBSECompat.GetOrganRejectionSetting() == true) { if (part.LabelShort == "shoulder") { //add hediff to arm bodypart foreach (BodyPartRecord childPart in part.parts) { if (childPart.def == BodyPartDefOf.Arm) { QEEMod.TryLog("Adding hediff " + bill.recipe.addsHediff.label + " to installed BodyPart " + childPart.Label); pawn.health.AddHediff(bill.recipe.addsHediff, childPart); return; } } QEEMod.TryLog("No arm found in shoulder replacement!"); } else { QEEMod.TryLog("Adding hediff " + bill.recipe.addsHediff.label + " to installed BodyPart " + part.Label); pawn.health.AddHediff(bill.recipe.addsHediff, part); } } }
/// <summary> /// Instantiate any Types that exist in the other mod for later use in the QEE codebase. /// </summary> /// <param name="assemblies">The list of the other mod's loaded assemblies</param> /// <returns>Indicates whether all types and members were loaded successfully from the other mod</returns> public static bool Init(List <Assembly> assemblies) { HediffPsychicAwakenedType = assemblies.Select(assembly => assembly.GetType("RimWorld.HediffPsychicAwakened")).FirstOrDefault(type => type != null); if (HediffPsychicAwakenedType != null) { PsychicPowerDefType = HediffPsychicAwakenedType.Assembly.GetType("RimWorld.PsychicPowerDef"); if (PsychicPowerDefType == null) { QEEMod.TryLog("Psychic Awakened mod detected, but 'PsychicPowerDef' type missing. Psychic Awakening functionality disabled."); return(false); } powersKnownField = HediffPsychicAwakenedType.GetField("powersKnown", BindingFlags.Public | BindingFlags.Instance); if (powersKnownField == null) { QEEMod.TryLog("Psychic Awakened mod detected, but 'powersKnown' field missing. Psychic Awakening functionality disabled."); return(false); } } else { QEEMod.TryLog("Psychic Awakened mod detected, but attempts to get HediffPsychicAwakened type failed. Psychic Awakening functionality disabled."); 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()
static CompatibilityTracker() { foreach (ModMetaData m in ModsConfig.ActiveModsInLoadOrder) { string modName = m.Name; //Fully incompatible mods if (modName == "Questionable Ethics") { Log.Error("Questionable Ethics Enhanced is incompatible with " + modName); continue; } //Partially incompatible mods else if (modName == "Multiplayer") { Log.Warning("Questionable Ethics Enhanced works with the Multiplayer mod, but is incompatible with the Multiplayer game mode."); continue; } //Enhanced compatibility mods else if (modName.Contains("Humanoid Alien Races 2.0")) { alienRacesActiveInt = true; QEEMod.TryLog("Humanoid Alien Races detected"); } } }
public static Building_Bed FindAvailMedicalBed(this Pawn sleeper, Pawn traveler) { bool sleeperWillBePrisoner = sleeper.IsPrisoner; if (sleeper.InBed() && sleeper.CurrentBed().Medical) { QEEMod.TryLog("Sleeper is in medical bed"); return(sleeper.CurrentBed()); } //QEEMod.TryLog("Count of all BedDefs: " + RestUtility.AllBedDefBestToWorst.Count); //for (int i = 0; i < RestUtility.AllBedDefBestToWorst.Count; i++) QEEMod.TryLog("Count of all BedDefs: " + bedDefsBestToWorst_Medical.Count); for (int i = 0; i < bedDefsBestToWorst_Medical.Count; i++) { ThingDef thingDef = bedDefsBestToWorst_Medical[i]; if (RestUtility.CanUseBedEver(sleeper, thingDef)) { Building_Bed building_Bed = (Building_Bed)GenClosest.ClosestThingReachable(sleeper.Position, sleeper.Map, ThingRequest.ForDef(thingDef), PathEndMode.OnCell, TraverseParms.For(traveler, Danger.Deadly, TraverseMode.ByPawn, false), 9999f, delegate(Thing b) { return(IsValidMedicalBed(b, sleeper, traveler, sleeperWillBePrisoner, false)); }, null, 0, -1, false, RegionType.Set_Passable, false); if (building_Bed != null) { QEEMod.TryLog("QE_BedSelected".Translate(building_Bed.def.defName)); return(building_Bed); } } } return(null); }
public override void ExposeData() { base.ExposeData(); //Basic. Scribe_Values.Look(ref sourceName, "sourceName"); Scribe_Defs.Look(ref pawnKindDef, "pawnKindDef"); Scribe_Values.Look(ref gender, "gender"); //Load first humanoid value Scribe_Values.Look(ref crownType, "crownType"); Scribe_Collections.Look(ref hediffInfos, "hediffInfos", LookMode.Deep); //Save/Load rest of the humanoid values. CrownType will be Undefined for animals. if (crownType != CrownType.Undefined) { Scribe_Defs.Look(ref bodyType, "bodyType"); Scribe_Values.Look(ref hairColor, "hairColor"); Scribe_Values.Look(ref skinMelanin, "skinMelanin"); Scribe_Collections.Look(ref traits, "traits", LookMode.Deep); Scribe_Defs.Look(ref hair, "hair"); Scribe_Values.Look(ref headGraphicPath, "headGraphicPath"); //Humanoid values that could be null in save file go here if (Scribe.mode == LoadSaveMode.PostLoadInit) { if (hair == null) { //hair = DefDatabase<HairDef>.AllDefs.RandomElement(); hair = DefDatabase <HairDef> .GetNamed("Shaved"); } if (headGraphicPath == null) { //*slaps roof of car* this bad code can crash your game! //headGraphicPath = GraphicDatabaseHeadRecords.GetHeadRandom(gender, PawnSkinColors.GetSkinColor(skinMelanin), crownType).GraphicPath; headGraphicPath = gender == Gender.Male ? "Things/Pawn/Humanlike/Heads/Male/Male_Average_Normal" : "Things/Pawn/Humanlike/Heads/Female/Female_Narrow_Normal"; } } } if (Scribe.mode == LoadSaveMode.PostLoadInit && hediffInfos != null) { //remove any hediffs where the def is missing. Most commonly occurs when a mod is removed from a save. int removed = hediffInfos.RemoveAll(h => h.def == null); if (removed > 0) { QEEMod.TryLog("Removed " + removed + " null hediffs from hediffInfo list for " + sourceName + "'s genome template "); } } //Alien Compat. Scribe_Values.Look(ref isAlien, "isAlien"); Scribe_Values.Look(ref skinColor, "skinColor"); Scribe_Values.Look(ref skinColorSecond, "skinColorSecond"); Scribe_Values.Look(ref hairColorSecond, "hairColorSecond"); Scribe_Values.Look(ref crownTypeAlien, "crownTypeAlien"); }
public static Thing MakeGenomeSequence(Pawn pawn, ThingDef genomeDef) { Thing genomeThing = ThingMaker.MakeThing(genomeDef); GenomeSequence genomeSequence = genomeThing as GenomeSequence; if (genomeSequence != null) { //Standard. genomeSequence.sourceName = pawn?.Name?.ToStringFull ?? null; if (genomeSequence.sourceName == null) { genomeSequence.sourceName = pawn.def.LabelCap; } genomeSequence.pawnKindDef = pawn.kindDef; genomeSequence.gender = pawn.gender; if (pawn?.health?.hediffSet?.hediffs != null) { List <Hediff> pawnHediffs = pawn.health.hediffSet.hediffs; if (pawnHediffs.Count > 0) { foreach (Hediff h in pawnHediffs) { if (GeneralCompatibility.includedGenomeTemplateHediffs.Any(hediffDef => h.def.defName == hediffDef.defName)) { QEEMod.TryLog("Hediff " + h.def.defName + " will be added to genome template"); genomeSequence.hediffInfos.Add(new HediffInfo(h)); } } } } //Humanoid only. Pawn_StoryTracker story = pawn.story; if (story != null) { genomeSequence.bodyType = story.bodyType; genomeSequence.crownType = story.crownType; genomeSequence.hairColor = story.hairColor; genomeSequence.skinMelanin = story.melanin; genomeSequence.hair = story.hairDef; genomeSequence.headGraphicPath = story.HeadGraphicPath; foreach (Trait trait in story.traits.allTraits) { genomeSequence.traits.Add(new ExposedTraitEntry(trait)); } } //Alien Races compatibility. if (CompatibilityTracker.AlienRacesActive) { AlienRaceCompat.GetFieldsFromAlienComp(pawn, genomeSequence); } } return(genomeThing); }
static void SpawnNaturalPartIfCleanPostfix(ref Thing __result, Pawn pawn, BodyPartRecord part, IntVec3 pos, Map map) { bool isOrganTransplant = false; bool shouldDropTransplantOrgan = false; int badHediffCount = 0; foreach (Hediff currHediff in pawn.health.hediffSet.hediffs) { if (currHediff.Part == part) { QEEMod.TryLog("Hediff on this body part: " + currHediff.def.defName + " isBad: " + currHediff.def.isBad); if (currHediff.def == QEHediffDefOf.QE_OrganRejection) { isOrganTransplant = true; } else if (currHediff.def.isBad) { badHediffCount++; } } } //if the only hediff on bodypart is organ rejection, that organ should spawn //vanilla game would not spawn it, because part hediffs > 0 if (isOrganTransplant && badHediffCount == 0 && part.def.spawnThingOnRemoved != null) { shouldDropTransplantOrgan = true; } QEEMod.TryLog("shouldDropTransplantOrgan: " + shouldDropTransplantOrgan + " [isOrganTransplant: " + isOrganTransplant + " " + part.LabelShort + " bad hediffs: " + badHediffCount + "]"); //spawn a biological arm when a shoulder is removed with a healthy arm attached if (part.LabelShort == "shoulder") { foreach (BodyPartRecord childPart in part.parts) { bool isHealthy = MedicalRecipesUtility.IsClean(pawn, childPart); QEEMod.TryLog("body part: " + childPart.LabelShort + " defName: " + childPart.def.defName + " healthy: " + isHealthy); if (childPart.def == BodyPartDefOf.Arm && isHealthy && (shouldDropTransplantOrgan = true || isOrganTransplant == false)) { QEEMod.TryLog("Spawn natural arm from shoulder replacement"); __result = GenSpawn.Spawn(QEThingDefOf.QE_Organ_Arm, pos, map); } } } else if (shouldDropTransplantOrgan) { __result = GenSpawn.Spawn(part.def.spawnThingOnRemoved, pos, map); } }
public static bool GetOrganRejectionSetting() { //only set rejectionEnabled to true if the mod loads successfully and we can retrieve the value from settings bool rejectionEnabled = false; var rbseModType = GenTypes.GetTypeInAnyAssembly("RBSE.Mod", "RBSE"); if (rbseModType != null) { var rbseMod = LoadedModManager.GetMod(rbseModType); if (rbseMod != null) { //The RBSE.Settings class is not available w/ public modifier. Use Reflection to get the Type var rbseSettingsType = rbseModType.Assembly.GetType("RBSE.Settings"); if (rbseSettingsType != null) { var settings = typeof(Mod)?.GetMethod(nameof(Mod.GetSettings), Type.EmptyTypes)?.MakeGenericMethod(rbseSettingsType)?.Invoke(rbseMod, new object[0]); if (settings != null) { var toggleActiveRejection = settings?.GetType()?.GetField("ToggleActiveRejection", AccessTools.all)?.GetValue(settings); if (toggleActiveRejection != null) { QEEMod.TryLog("toggleActiveRejection value: " + toggleActiveRejection); rejectionEnabled = (bool)toggleActiveRejection; } else { QEEMod.TryLog("Problem retrieving toggleActiveRejection ModSetting value from RBSE mod. Organ rejection hediff will not be added."); } } else { QEEMod.TryLog("Error making generic GetSettings() method for RBSE compatibility. Organ rejection hediff will not be added."); } } else { QEEMod.TryLog("RBSE class 'RBSE.Settings' not found. Organ rejection hediff will not be added."); } } else { QEEMod.TryLog("GetMod() returned null for RBSE. Organ rejection hediff will not be added."); } } else { QEEMod.TryLog("Class 'RBSE.Mod' not found. Organ rejection hediff will not be added."); } return(rejectionEnabled); }
public override void ExposeData() { base.ExposeData(); Scribe_Values.Look(ref sourceName, "sourceName"); Scribe_Defs.Look(ref kindDef, "kindDef"); string childhoodIdentifier = (backStoryChild == null) ? null : backStoryChild.identifier; Scribe_Values.Look(ref childhoodIdentifier, "backStoryChild"); if (Scribe.mode == LoadSaveMode.LoadingVars && !childhoodIdentifier.NullOrEmpty()) { if (!BackstoryDatabase.TryGetWithIdentifier(childhoodIdentifier, out backStoryChild, true)) { Log.Error("Couldn't load child backstory with identifier " + childhoodIdentifier + ". Giving random.", false); backStoryChild = BackstoryDatabase.RandomBackstory(BackstorySlot.Childhood); } } string adulthoodIdentifier = (backStoryAdult == null) ? null : backStoryAdult.identifier; Scribe_Values.Look(ref adulthoodIdentifier, "backStoryAdult"); if (Scribe.mode == LoadSaveMode.LoadingVars && !adulthoodIdentifier.NullOrEmpty()) { if (!BackstoryDatabase.TryGetWithIdentifier(adulthoodIdentifier, out backStoryAdult, true)) { Log.Error("Couldn't load adult backstory with identifier " + adulthoodIdentifier + ". Giving random.", false); backStoryAdult = BackstoryDatabase.RandomBackstory(BackstorySlot.Adulthood); } } Scribe_Collections.Look(ref skills, "skills", LookMode.Deep); Scribe_Values.Look(ref isAnimal, "isAnimal"); Scribe_Deep.Look(ref trainingLearned, "trainingLearned"); Scribe_Deep.Look(ref trainingSteps, "trainingSteps"); Scribe_Collections.Look(ref hediffInfos, "hediffInfos", LookMode.Deep); if (Scribe.mode == LoadSaveMode.LoadingVars && hediffInfos != null) { //remove any hediffs where the def is missing. Most commonly occurs when a mod is removed from a save. int removed = hediffInfos.RemoveAll(h => h.def == null); if (removed > 0) { QEEMod.TryLog("Removed " + removed + " null hediffs from hediffInfo list for " + sourceName + "'s brain template "); } } }
static CompatibilityTracker() { foreach (string s in incompatibleModArr) { if (ModsConfig.ActiveModsInLoadOrder.Any((ModMetaData m) => m.Name == s)) { Log.Error("Questionable Ethics Enhanced is incompatible with " + s); } } //Check for Alien Races Compatiblity if (GenTypes.AllTypes.Any(type => type.FullName == "AlienRace.ThingDef_AlienRace")) { alienRacesActiveInt = true; QEEMod.TryLog("Humanoid Alien Races detected"); } }
static void SpawnNaturalPartIfCleanPostfix(ref Thing __result, Pawn pawn, BodyPartRecord part, IntVec3 pos, Map map) { //spawn a biological arm when a shoulder is removed with a healthy arm attached (e.g. from installing a prosthetic on a healthy arm) if (part.LabelShort == "shoulder") { foreach (BodyPartRecord childPart in part.parts) { bool isHealthy = MedicalRecipesUtility.IsClean(pawn, childPart); QEEMod.TryLog("body part: " + childPart.LabelShort + " IsClean: " + isHealthy); if (childPart.def == BodyPartDefOf.Arm && isHealthy) { QEEMod.TryLog("Spawn natural arm from shoulder replacement"); __result = GenSpawn.Spawn(QEThingDefOf.QE_Organ_Arm, pos, map); } } } }
public HediffInfo(Hediff h) { def = h.def; part = h.Part; if (CompatibilityTracker.PsychicAwakeningActive) { //check if this is a Psychic Awakened hediff if (PsychicAwakeningCompat.HediffPsychicAwakenedType.IsInstanceOfType(h)) { //get the value of the 'powersKnown' field from the Hediff var powersValue = PsychicAwakeningCompat.powersKnownField.GetValue(h); if (powersValue != null) { if (powersValue is System.Collections.IEnumerable) { psychicAwakeningPowersKnownDefNames = new List <string>(); //loop through each object in the 'powersKnown' List foreach (object o in (powersValue as System.Collections.IEnumerable)) { //PsychicPowerDef is derived from class Def, so this cast should work Def theDef = o as Def; if (theDef != null) { QEEMod.TryLog("Adding PsychicPower " + theDef.defName + " to psychicAwakeningPowersKnownDefNames"); psychicAwakeningPowersKnownDefNames.Add(theDef.defName); } } } else { QEEMod.TryLog("'powersKnown' field from Psychic hediff is not System.Collections.IEnumerable"); } } else { QEEMod.TryLog("Unable to retrieve value of 'powersKnown' field from Psychic hediff"); } } } }
static void IsCleanPostfix(ref bool __result, Pawn pawn, BodyPartRecord part) { if (pawn.Dead) { __result = false; return; } BodyPartRecord diseasedPart = null; if (PawnUtility.bodyPartOrChildHasHediffs(pawn, part, out diseasedPart)) { QEEMod.TryLog("IsClean() false for " + part.Label + " because " + diseasedPart.Label + " has bad Hediffs"); __result = false; return; } __result = true; } //end postfix
static CompatibilityTracker() { foreach (ModContentPack m in LoadedModManager.RunningMods) { string modName = m.Name; //Fully incompatible mods if (modName == "Questionable Ethics") { Log.Error("Questionable Ethics Enhanced is incompatible with " + modName); continue; } //Partially incompatible mods else if (modName == "Multiplayer") { Log.Warning("Questionable Ethics Enhanced works with the Multiplayer mod, but is incompatible with the Multiplayer game mode."); continue; } //Enhanced compatibility mods else if (modName.Contains("Humanoid Alien Races 2.0")) { alienRacesActiveInt = true; QEEMod.TryLog("Humanoid Alien Races detected"); } else if (modName.Contains("Psychic Awakening")) { QEEMod.TryLog("Psychic Awakening detected"); psychicAwakeningActiveInt = PsychicAwakeningCompat.Init(m.assemblies.loadedAssemblies); } else if (modName.Contains("Rah's Bionics and Surgery Expansion") || modName.Contains("RBSE Hardcore Edition")) { rbseActiveInt = true; QEEMod.TryLog("RBSE detected"); } } }
} // 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> /// 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
//this is a modified version of vanilla's RestUtility.IsValidBedFor() that adds medical bed checks public static bool IsValidMedicalBed(Thing bedThing, Pawn sleeper, Pawn traveler, bool sleeperWillBePrisoner, bool checkSocialProperness, bool ignoreOtherReservations = false) { Building_Bed building_Bed = bedThing as Building_Bed; if (building_Bed == null) { QEEMod.TryLog("QE_BedInvalid".Translate(bedThing.def.defName)); return(false); } LocalTargetInfo target = building_Bed; PathEndMode peMode = PathEndMode.OnCell; Danger maxDanger = Danger.Some; int sleepingSlotsCount = building_Bed.SleepingSlotsCount; if (!building_Bed.Medical && !sleeper.RaceProps.Animal) { QEEMod.TryLog("QE_BedIsNotMedical".Translate(building_Bed.def.defName)); return(false); } else if (!traveler.CanReserveAndReach(target, peMode, maxDanger, sleepingSlotsCount, -1, null, ignoreOtherReservations)) { QEEMod.TryLog("QE_BedUnreachableBySurgeon".Translate(building_Bed.def.defName, traveler.Named("SURGEON"))); return(false); } else if (building_Bed.Position.GetDangerFor(sleeper, sleeper.Map) > maxDanger) { QEEMod.TryLog("QE_BedTooDangerous".Translate(building_Bed.def.defName, traveler.Named("PATIENT"))); return(false); } else if (traveler.HasReserved <JobDriver_TakeToBed>(building_Bed, sleeper)) { QEEMod.TryLog("QE_BedSurgeonAlreadyReserved".Translate(building_Bed.def.defName, traveler.Named("SURGEON"))); return(false); } else if (!building_Bed.AnyUnoccupiedSleepingSlot && (!sleeper.InBed() || sleeper.CurrentBed() != building_Bed) && !building_Bed.CompAssignableToPawn.AssignedPawns.Contains(sleeper)) { QEEMod.TryLog("QE_BedOccupied".Translate(building_Bed.def.defName)); return(false); } else if (building_Bed.IsForbidden(traveler)) { QEEMod.TryLog("QE_BedForbidden".Translate(building_Bed.def.defName)); return(false); } else if (checkSocialProperness && !building_Bed.IsSociallyProper(sleeper, sleeperWillBePrisoner)) { QEEMod.TryLog("QE_BedFailsSocialChecks".Translate(building_Bed.def.defName)); return(false); } else if (building_Bed.IsBurning()) { QEEMod.TryLog("QE_BedIsBurning".Translate(building_Bed.def.defName)); return(false); } else if (sleeperWillBePrisoner) { if (!building_Bed.ForPrisoners) { QEEMod.TryLog("QE_BedImproperForPrisoner".Translate(building_Bed.def.defName, traveler.Named("PATIENT"))); return(false); } if (!building_Bed.Position.IsInPrisonCell(building_Bed.Map)) { QEEMod.TryLog("QE_BedNotInPrisonCell".Translate(building_Bed.def.defName, traveler.Named("PATIENT"))); return(false); } } else { if (building_Bed.Faction != traveler.Faction && (traveler.HostFaction == null || building_Bed.Faction != traveler.HostFaction)) { QEEMod.TryLog("QE_BedImproperFaction".Translate(building_Bed.def.defName, traveler.Named("SURGEON"))); return(false); } if (building_Bed.ForPrisoners) { QEEMod.TryLog("QE_PrisonerBedForNonPrisoner".Translate(building_Bed.def.defName, traveler.Named("PATIENT"))); return(false); } } //fail if bed is owned if (building_Bed.OwnersForReading.Any() && !building_Bed.OwnersForReading.Contains(sleeper) && !building_Bed.AnyUnownedSleepingSlot) { QEEMod.TryLog("QE_BedHasAnotherOwner".Translate(building_Bed.def.defName)); return(false); } return(true); }
} //end GetBillPawnCanDo() /// <summary> /// Checks if a pawn can be assigned to a bill based on factors including PawnRestrictions, workSkills, etc. /// Does not check if ingredients are nearby. /// </summary> /// <param name="p"></param> /// <param name="theBill"></param> /// <param name="reason"></param> /// <returns></returns> public bool PawnCanDoThisBill(Pawn p, Bill theBill, LocalTargetInfo target, out string reason) { reason = GenericFailReasonTrans; if (Find.TickManager.TicksGame < theBill.lastIngredientSearchFailTicks + (QEESettings.instance.ingredientCheckIntervalSeconds * 60)) { reason = RecentIngSearchTrans; return(false); } if (theBill?.recipe?.requiredGiverWorkType != null && theBill.recipe.requiredGiverWorkType != def.workType) { reason = WorkTypeMismatchTrans; return(false); } bool shouldDo = theBill.ShouldDoNow(); if (!shouldDo) { reason = ShouldDoFalseTrans; return(false); } if (theBill.pawnRestriction != null && theBill.pawnRestriction != p) { reason = string.Format(PawnRestrictionTrans, theBill.pawnRestriction.LabelShort); return(false); } if (theBill.recipe.workSkill != null) { int level = p.skills.GetSkill(theBill.recipe.workSkill).Level; if (level < theBill.allowedSkillRange.min) { reason = string.Format(UnderAllowedSkillTrans, theBill.allowedSkillRange.min); return(false); } if (level > theBill.allowedSkillRange.max) { reason = string.Format(AboveAllowedSkillTrans, theBill.allowedSkillRange.max); return(false); } } if (!p.Map.reservationManager.CanReserve(p, target, 1, -1, null, false)) { Pawn pawn2 = p.Map.reservationManager.FirstRespectedReserver(target, p); if (pawn2 != null) { reason = string.Format(ReservedByTrans, theBill.billStack.billGiver.LabelShort, pawn2.LabelShort); } else { reason = string.Format(ReservedTrans); } return(false); } QEEMod.TryLog(p.LabelShort + " can do bill " + theBill.GetUniqueLoadID()); return(true); } //end PawnCanDoThisBill()
public static Thing MakeBrainScan(Pawn pawn, ThingDef genomeDef) { Thing brainScanThing = ThingMaker.MakeThing(genomeDef); BrainScanTemplate brainScan = brainScanThing as BrainScanTemplate; if (brainScan != null) { //Standard. brainScan.sourceName = pawn?.Name?.ToStringFull ?? null; brainScan.kindDef = pawn?.kindDef ?? null; //Backgrounds Pawn_StoryTracker story = pawn.story; if (story != null) { brainScan.backStoryChild = story.childhood; brainScan.backStoryAdult = story.adulthood; } //Skills Pawn_SkillTracker skillTracker = pawn.skills; if (skillTracker != null) { foreach (SkillRecord skill in skillTracker.skills) { brainScan.skills.Add(new ComparableSkillRecord() { def = skill.def, level = skill.Level, passion = skill.passion }); } } //Animal brainScan.isAnimal = pawn.RaceProps.Animal; //Training Pawn_TrainingTracker trainingTracker = pawn.training; if (trainingTracker != null) { DefMap <TrainableDef, bool> learned = (DefMap <TrainableDef, bool>)AccessTools.Field(typeof(Pawn_TrainingTracker), "learned").GetValue(trainingTracker); DefMap <TrainableDef, int> steps = (DefMap <TrainableDef, int>)AccessTools.Field(typeof(Pawn_TrainingTracker), "steps").GetValue(trainingTracker); //Copy foreach (var item in learned) { brainScan.trainingLearned[item.Key] = item.Value; } foreach (var item in steps) { brainScan.trainingSteps[item.Key] = item.Value; } } //Hediffs if (pawn?.health?.hediffSet?.hediffs != null) { List <Hediff> pawnHediffs = pawn.health.hediffSet.hediffs; if (pawnHediffs.Count > 0) { foreach (Hediff h in pawnHediffs) { if (GeneralCompatibility.includedBrainTemplateHediffs.Any(hediffDef => h.def.defName == hediffDef.defName)) { QEEMod.TryLog("Hediff " + h.def.defName + " will be added to brain template"); brainScan.hediffInfos.Add(new HediffInfo(h)); } } } } } return(brainScanThing); }
public static Pawn MakePawnFromGenomeSequence(GenomeSequence genomeSequence, Thing creator) { //int adultAge = (int)genome.pawnKindDef.RaceProps.lifeStageAges.Last().minAge; QEEMod.TryLog("Generating pawn..."); PawnGenerationRequest request = new PawnGenerationRequest( genomeSequence.pawnKindDef, faction: creator.Faction, forceGenerateNewPawn: true, canGeneratePawnRelations: false, fixedGender: genomeSequence.gender, fixedBiologicalAge: 0, fixedChronologicalAge: 0, allowFood: false); Pawn pawn = PawnGenerator.GeneratePawn(request); //No pregenerated equipment. pawn?.equipment?.DestroyAllEquipment(); pawn?.apparel?.DestroyAll(); pawn?.inventory?.DestroyAll(); //No pregenerated hediffs. pawn.health.hediffSet.Clear(); //Add Hediff marking them as a clone. QEEMod.TryLog("Adding hediffs to generated pawn"); pawn.health.AddHediff(QEHediffDefOf.QE_CloneStatus); if (genomeSequence.hediffInfos != null && genomeSequence.hediffInfos.Count > 0) { //add hediffs to pawn from defs in HediffInfo class foreach (HediffInfo h in genomeSequence.hediffInfos) { pawn.health.AddHediff(h.def, h.part); } } //Set everything else. if (pawn.story is Pawn_StoryTracker storyTracker) { QEEMod.TryLog("Setting Pawn_StoryTracker attributes for generated pawn..."); storyTracker.bodyType = genomeSequence.bodyType; //sanity check to remove possibility of an Undefined crownType if (genomeSequence.crownType == CrownType.Undefined) { storyTracker.crownType = CrownType.Average; } else { storyTracker.crownType = genomeSequence.crownType; } storyTracker.hairColor = genomeSequence.hairColor; storyTracker.hairDef = genomeSequence.hair ?? PawnHairChooser.RandomHairDefFor(pawn, pawn.Faction.def); storyTracker.melanin = genomeSequence.skinMelanin; //headGraphicPath is private, so we need Harmony to set its value if (genomeSequence.headGraphicPath != null) { QEEMod.TryLog("Setting headGraphicPath for generated pawn"); AccessTools.Field(typeof(Pawn_StoryTracker), "headGraphicPath").SetValue(storyTracker, genomeSequence.headGraphicPath); } else { //could use this code to make a random head, instead of the static graphic paths. //AccessTools.Field(typeof(Pawn_StoryTracker), "headGraphicPath").SetValue(storyTracker, //GraphicDatabaseHeadRecords.GetHeadRandom(genomeSequence.gender, PawnSkinColors.GetSkinColor(genomeSequence.skinMelanin), genomeSequence.crownType).GraphicPath); QEEMod.TryLog("No headGraphicPath in genome template, setting to default head"); string path = genomeSequence.gender == Gender.Male ? "Things/Pawn/Humanlike/Heads/Male/Male_Average_Normal" : "Things/Pawn/Humanlike/Heads/Female/Female_Narrow_Normal"; AccessTools.Field(typeof(Pawn_StoryTracker), "headGraphicPath").SetValue(storyTracker, path); } storyTracker.traits.allTraits.Clear(); QEEMod.TryLog("Setting traits for generated pawn"); foreach (ExposedTraitEntry trait in genomeSequence.traits) { //storyTracker.traits.GainTrait(new Trait(trait.def, trait.degree)); storyTracker.traits.allTraits.Add(new Trait(trait.def, trait.degree)); if (pawn.workSettings != null) { pawn.workSettings.Notify_DisabledWorkTypesChanged(); } if (pawn.skills != null) { pawn.skills.Notify_SkillDisablesChanged(); } if (!pawn.Dead && pawn.RaceProps.Humanlike) { pawn.needs.mood.thoughts.situational.Notify_SituationalThoughtsDirty(); } } QEEMod.TryLog("Setting backstory for generated pawn"); //Give random vatgrown backstory. storyTracker.childhood = DefDatabase <BackstoryDef> .GetNamed("Backstory_ColonyVatgrown").GetFromDatabase(); storyTracker.adulthood = null; } if (pawn.skills is Pawn_SkillTracker skillsTracker) { foreach (SkillRecord skill in skillsTracker.skills) { skill.Level = 0; skill.passion = Passion.None; skill.Notify_SkillDisablesChanged(); } List <SkillRecord> skillPassions = new List <SkillRecord>(); int skillsPicked = 0; int iterations = 0; //Pick 4 random skills to give minor passions to. while (skillsPicked < 4 && iterations < 1000) { SkillRecord randomSkill = skillsTracker.skills.RandomElement(); if (!skillPassions.Contains(randomSkill)) { skillPassions.Add(randomSkill); randomSkill.passion = Passion.Minor; skillsPicked++; } iterations++; } skillsPicked = 0; iterations = 0; //Pick 2 random skills to give major passions to. while (skillsPicked < 2 && iterations < 1000) { SkillRecord randomSkill = skillsTracker.skills.RandomElement(); if (!skillPassions.Contains(randomSkill)) { skillPassions.Add(randomSkill); randomSkill.passion = Passion.Major; skillsPicked++; } iterations++; } } if (pawn.workSettings is Pawn_WorkSettings workSettings) { workSettings.EnableAndInitialize(); } //Alien Races compatibility. if (CompatibilityTracker.AlienRacesActive) { AlienRaceCompat.SetFieldsToAlienComp(pawn, genomeSequence); } PortraitsCache.SetDirty(pawn); PortraitsCache.PortraitsCacheUpdate(); return(pawn); }
public static void ApplyBrainScanTemplateOnPawn(Pawn thePawn, BrainScanTemplate brainScan, float efficency = 1f) { if (thePawn.IsValidBrainScanningTarget()) { //Backgrounds Pawn_StoryTracker storyTracker = thePawn.story; if (storyTracker != null) { //story.childhood = brainScan.backStoryChild; storyTracker.adulthood = brainScan.backStoryAdult; } //Skills Pawn_SkillTracker skillTracker = thePawn.skills; if (skillTracker != null) { foreach (ComparableSkillRecord skill in brainScan.skills) { SkillRecord pawnSkill = skillTracker.GetSkill(skill.def); pawnSkill.Level = (int)Math.Floor((float)skill.level * efficency); pawnSkill.passion = skill.passion; pawnSkill.Notify_SkillDisablesChanged(); } } //Training Pawn_TrainingTracker trainingTracker = thePawn.training; if (trainingTracker != null) { DefMap <TrainableDef, bool> learned = (DefMap <TrainableDef, bool>)AccessTools.Field(typeof(Pawn_TrainingTracker), "learned").GetValue(trainingTracker); DefMap <TrainableDef, int> steps = (DefMap <TrainableDef, int>)AccessTools.Field(typeof(Pawn_TrainingTracker), "steps").GetValue(trainingTracker); //Copy foreach (var item in brainScan.trainingLearned) { learned[item.Key] = item.Value; } foreach (var item in brainScan.trainingSteps) { steps[item.Key] = (int)Math.Floor((float)item.Value * efficency); } } //Add Hediffs thePawn.health.AddHediff(QEHediffDefOf.QE_BrainTemplated); if (brainScan.hediffInfos != null && brainScan.hediffInfos?.Count > 0) { //add hediffs to pawn from defs in HediffInfo class foreach (HediffInfo h in brainScan.hediffInfos) { Hediff addedHediff = thePawn.health.AddHediff(h.def, h.part); //Psychic Awakened compatibility if (h.psychicAwakeningPowersKnownDefNames != null && h.psychicAwakeningPowersKnownDefNames?.Count > 0) { //create a List of the type PsychicPowerDef via Reflection. Cast it as IList to interact with it. var listType = typeof(List <>).MakeGenericType(PsychicAwakeningCompat.PsychicPowerDefType); var powers = Activator.CreateInstance(listType); IList powersInterface = (IList)powers; //iterate through the defNames saved in the list inside HediffInfo foreach (string defName in h.psychicAwakeningPowersKnownDefNames) { //look for this PsychicPowerDef in the DefDatabase var psychicPowerDef = GenDefDatabase.GetDef(PsychicAwakeningCompat.PsychicPowerDefType, defName, false); if (psychicPowerDef != null) { //add this to the list powersInterface.Add(psychicPowerDef); } else { QEEMod.TryLog("Psychic Power def " + defName + " not loaded in database of Rimworld Defs. This power will not be applied."); } } if (powersInterface.Count > 0) { QEEMod.TryLog("assigning " + powersInterface.Count + " psychic powers to " + thePawn.LabelCap + " from brain template"); PsychicAwakeningCompat.powersKnownField.SetValue(addedHediff, powers); } } } } Messages.Message("QE_BrainTemplatingComplete".Translate(thePawn.Named("PAWN")), MessageTypeDefOf.PositiveEvent, false); } }
protected override IEnumerable <Toil> MakeNewToils() { //A - Sleeper B - Brain template C - Bed this.FailOnDestroyedNullOrForbidden(TargetIndex.A); this.FailOnDestroyedNullOrForbidden(TargetIndex.B); this.FailOnDestroyedNullOrForbidden(TargetIndex.C); yield return(Toils_Reserve.Reserve(TargetIndex.B)); if (Patient.CurJobDef != JobDefOf.LayDown || Patient.CurrentBed() != Bed) { QEEMod.TryLog("Patient not in bed, carrying them to bed"); //get the patient and carry them to the bed yield return(Toils_Reserve.Reserve(TargetIndex.A)); yield return(Toils_Reserve.Reserve(TargetIndex.C)); yield return(Toils_Goto.GotoThing(TargetIndex.A, PathEndMode.InteractionCell)); yield return(Toils_Haul.StartCarryThing(TargetIndex.A)); yield return(Toils_Goto.GotoThing(TargetIndex.C, PathEndMode.InteractionCell)); yield return(Toils_Haul.PlaceHauledThingInCell(TargetIndex.C, null, false)); yield return(Toils_Reserve.Release(TargetIndex.C)); } //anesthetize the patient Toil anesthetizePatient = new Toil() { initAction = delegate() { Toils_Reserve.Reserve(TargetIndex.A); Patient.health.AddHediff(HediffDefOf.Anesthetic); if (Patient.CurJobDef != JobDefOf.LayDown) { Patient.pather.StopDead(); Patient.jobs.StartJob(new Job(JobDefOf.LayDown, TargetC) { forceSleep = true }); } }, }; yield return(anesthetizePatient); //Surgeon gets the brain template, carries it, then travel to bed QEEMod.TryLog("Carrying brain template to bed"); yield return(Toils_Goto.GotoThing(TargetIndex.B, PathEndMode.OnCell)); yield return(Toils_Haul.StartCarryThing(TargetIndex.B)); yield return(Toils_Goto.GotoThing(TargetIndex.C, PathEndMode.ClosestTouch)); Toil applyBrainTemplateToil = new Toil() { defaultCompleteMode = ToilCompleteMode.Never, tickAction = delegate() { ticksWork -= StatDefOf.ResearchSpeed.Worker.IsDisabledFor(pawn) ? 1f : 1f * pawn.GetStatValue(StatDefOf.ResearchSpeed); if (ticksWork <= 0) { BrainManipUtility.ApplyBrainScanTemplateOnPawn(Patient, BrainTemplate); //Give headache Patient.health.AddHediff(QEHediffDefOf.QE_Headache, Patient.health.hediffSet.GetBrain()); } } }.WithProgressBar(TargetIndex.A, () => (workRequired - ticksWork) / workRequired).WithEffect(EffecterDefOf.Research, TargetIndex.A); applyBrainTemplateToil.AddPreInitAction(delegate() { ticksWork = workRequired; workStarted = true; //Log.Message("preInitAction: ticksWork = " + ticksWork); }); applyBrainTemplateToil.AddEndCondition(delegate() { if (workStarted && ticksWork <= 0) { //Log.Message("Succeeded: ticksWork = " + ticksWork); return(JobCondition.Succeeded); } //Log.Message("Ongoing: ticksWork = " + ticksWork); return(JobCondition.Ongoing); }); applyBrainTemplateToil.AddFailCondition(() => workStarted && Patient.CurJobDef != JobDefOf.LayDown); yield return(applyBrainTemplateToil); }
/// <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
static PostDefFixer() { //Add recipes to valid Genome Sequencing targets. foreach (ThingDef def in DefDatabase <ThingDef> .AllDefs.Where(def => def.category == ThingCategory.Pawn)) { if (def.GetModExtension <RaceExclusionProperties>() is RaceExclusionProperties props) { if (props.excludeThisRace) { GeneralCompatibility.excludedRaces.Add(def); } if (props.excludeTheseHediffs.Count > 0) { GeneralCompatibility.excludedHediffs.AddRange(props.excludeTheseHediffs); } } if (def.IsValidGenomeSequencingTargetDef()) { if (def.recipes == null) { def.recipes = new List <RecipeDef>(); } if (def.recipes.Count > 0) { def.recipes.Insert(0, QERecipeDefOf.QE_GenomeSequencing); } else { def.recipes.Add(QERecipeDefOf.QE_GenomeSequencing); } } if (def.IsValidBrainScanningDef()) { if (def.recipes == null) { def.recipes = new List <RecipeDef>(); } if (def.recipes.Count > 0) { def.recipes.Insert(0, QERecipeDefOf.QE_BrainScanning); } else { def.recipes.Add(QERecipeDefOf.QE_BrainScanning); } } } //Inject our own backstories. foreach (BackstoryDef def in DefDatabase <BackstoryDef> .AllDefs) { Backstory backstory = new Backstory(); backstory.slot = def.slot; backstory.title = def.title; backstory.titleShort = def.titleShort; backstory.titleFemale = def.titleFemale; backstory.titleShortFemale = def.titleShortFemale; backstory.baseDesc = def.baseDesc; AccessTools.Field(typeof(Backstory), "bodyTypeFemale").SetValue(backstory, def.bodyTypeFemale); AccessTools.Field(typeof(Backstory), "bodyTypeMale").SetValue(backstory, def.bodyTypeMale); AccessTools.Field(typeof(Backstory), "bodyTypeGlobal").SetValue(backstory, def.bodyTypeGlobal); backstory.spawnCategories.AddRange(def.spawnCategories); backstory.PostLoad(); backstory.ResolveReferences(); BackstoryDatabase.AddBackstory(backstory); def.identifier = backstory.identifier; //Log.Message("'" + def.defName + "' identifier is '" + backstory.identifier + "'"); } foreach (HediffDef def in DefDatabase <HediffDef> .AllDefs) { if (def.GetModExtension <HediffTemplateProperties>() is HediffTemplateProperties props) { if (props.includeInBrainTemplate) { QEEMod.TryLog(def.defName + " added to list of Hediffs applied to brain templates"); GeneralCompatibility.includedBrainTemplateHediffs.Add(def); } if (props.includeInGenomeTemplate) { QEEMod.TryLog(def.defName + " added to list of Hediffs applied to genome templates"); GeneralCompatibility.includedGenomeTemplateHediffs.Add(def); } } } }
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()
public bool UpdateAvailIngredientsCache() { anyBillIngredientsAvailable = false; ingredientsAvailableNow.Clear(); if (AnyPendingRequests == false) { return(false); } //Thing giver = (observedThingHolder as Thing); //QEEMod.TryLog("Updating available ingredients cache for " + giver.ThingID); List <Bill> bills = new List <Bill>(); if (_activeBill != null) { bills.Add(_activeBill); } else { bills = (observedThingHolder as IBillGiver)?.BillStack.Bills; } Dictionary <string, Thing> ingsFoundOnMap = new Dictionary <string, Thing>(); int checkIntervalTicks = QEESettings.instance.ingredientCheckIntervalSeconds * 60; for (int i = 0; i < bills.Count; i++) { Bill_Production curBill = bills[i] as Bill_Production; if (Find.TickManager.TicksGame < curBill.lastIngredientSearchFailTicks + checkIntervalTicks) { QEEMod.TryLog("checked " + curBill.GetUniqueLoadID() + " for avail. ingredients recently, skipping"); continue; } curBill.lastIngredientSearchFailTicks = 0; if (curBill.ShouldDoNow()) { //loop through ingredients foreach (IngredientCount curIng in curBill.recipe.ingredients) { Thing ing; //if same ingredient was found on map in previous loop iteration, don't search map again ingsFoundOnMap.TryGetValue(curIng.FixedIngredient.defName, out ing); if (ing != null) { ingredientsAvailableNow[curBill.GetUniqueLoadID()] = ing; //QEEMod.TryLog("Already found " + ing.Label + " on previous iteration. Skipping"); break; } //check if this ingredient is on the map ing = IngredientUtility.FindClosestIngToBillGiver(curBill, curIng); if (ing != null) { QEEMod.TryLog("Found " + ing.Label + " on map, adding to ingredient cache"); ingsFoundOnMap[curIng.FixedIngredient.defName] = ing; ingredientsAvailableNow[curBill.GetUniqueLoadID()] = ing; anyBillIngredientsAvailable = true; break; } else { //QEEMod.TryLog("no ingredients available"); } } Thing dummy; if (!ingredientsAvailableNow.TryGetValue(curBill.GetUniqueLoadID(), out dummy)) { curBill.lastIngredientSearchFailTicks = Find.TickManager.TicksGame; } } } return(anyBillIngredientsAvailable); }
}//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()