diff --git a/Source/ProjectRimFactory/Common/HarmonyPatches/Patch_CompFoodPoisonable_Notify_RecipeProduced.cs b/Source/ProjectRimFactory/Common/HarmonyPatches/Patch_CompFoodPoisonable_Notify_RecipeProduced.cs new file mode 100644 index 00000000..392718e0 --- /dev/null +++ b/Source/ProjectRimFactory/Common/HarmonyPatches/Patch_CompFoodPoisonable_Notify_RecipeProduced.cs @@ -0,0 +1,35 @@ +using HarmonyLib; +using ProjectRimFactory.SAL3.Things.Assemblers; +using RimWorld; +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Reflection.Emit; // for OpCodes in Harmony Transpiler +using System.Runtime.CompilerServices; +using Verse; + +namespace ProjectRimFactory.Common.HarmonyPatches +{ + + // Building_SimpleAssembler assembler -> Use the Room of The output cell instead of the Pawn + // .def.defName == "PRF_SelfCookerIII" -> Skip + [HarmonyPatch(typeof(CompFoodPoisonable), "Notify_RecipeProduced")] + class Patch_CompFoodPoisonable_Notify_RecipeProduced + { + public static bool Prefix(CompFoodPoisonable __instance, Pawn pawn) + { + if (AssemblerRefrence is null) return true; + if (AssemblerRefrence.def.defName == "PRF_SelfCookerIII") return false; + if (AssemblerRefrence is not Building_SimpleAssembler) return true; + if (Rand.Chance(RegionAndRoomQuery.RoomAt(AssemblerRefrence.OutputCell(), + AssemblerRefrence.Map, RegionType.Set_Passable)?. + GetStat(RoomStatDefOf.FoodPoisonChance) ?? RoomStatDefOf.FoodPoisonChance.roomlessScore)) + { + __instance.SetPoisoned(FoodPoisonCause.FilthyKitchen); + } + return false; + } + + public static Building_ProgrammableAssembler AssemblerRefrence = null; + } +} diff --git a/Source/ProjectRimFactory/Common/HarmonyPatches/Patch_GenRecipe_MakeRecipeProducts_foodPoisoning.cs b/Source/ProjectRimFactory/Common/HarmonyPatches/Patch_GenRecipe_MakeRecipeProducts_foodPoisoning.cs deleted file mode 100644 index 93333f76..00000000 --- a/Source/ProjectRimFactory/Common/HarmonyPatches/Patch_GenRecipe_MakeRecipeProducts_foodPoisoning.cs +++ /dev/null @@ -1,141 +0,0 @@ -using HarmonyLib; -using ProjectRimFactory.SAL3.Things.Assemblers; -using RimWorld; -using System; -using System.Collections.Generic; -using System.Reflection; -using System.Reflection.Emit; // for OpCodes in Harmony Transpiler -using System.Runtime.CompilerServices; -using Verse; - -namespace ProjectRimFactory.Common.HarmonyPatches -{ - /* Patch Verse.GenRecipe.cs's MakeRecipeProducts to remove chance of food - * poisoning for T3 Assemblers: - * We change - * CompFoodPoisonable compFoodPoisonable = thing.TryGetComp(); - * if (compFoodPoisonable != null) {......} - * to - * CompFoodPoisonable compFoodPoisonable = thing.TryGetComp(); - * if (compFoodPoisonable != null && !UsingSpaceCooker(this.billGiver)) {......} - * #DeepMagic - */ - /* Technical notes: - * The toils are returned inside a hidden "inner" iterator class, - * and are returned inside the MoveNext() method of that class. - * So to patch the method, we first have to find it, then we - * use Transpiler to modify the code. - */ - [HarmonyPatch] - public static class Patch_GenRecipe_MakeRecipeProducts_foodPoisoning - { - /// - /// Doing this should find the inner iterator class no matter how the compiler calls it. - /// - private static readonly Type hiddenClass = AccessTools.FirstInner( - typeof(GenRecipe), - type => type.HasAttribute() && type.Name.Contains(nameof(GenRecipe.MakeRecipeProducts))); - public static MethodBase TargetMethod() - { - // Decompiler showed the hidden inner class is "d__0" - if (hiddenClass == null) - { - Log.Error("Couldn't find iterator class -- This should never be reached."); - return null; - } - // and we want the iterator's MoveNext: - MethodBase iteratorMethod = HarmonyLib.AccessTools.Method(hiddenClass, "MoveNext"); - if (iteratorMethod == null) Log.Error("Couldn't find MoveNext"); - return iteratorMethod; - } - public static IEnumerable Transpiler(IEnumerable instructions) - { - var codeHasStoredCompPoisonable = false; - foreach (var instruction in instructions) - { - // =========== T3 cooker patch ========== - // Roughly patches - // if (compFoodPoisonable != null) - // to - // if (compFoodPoisonable != null && !UsingSpaceCooker(billGiver)) - if (instruction.opcode == OpCodes.Stloc_S && ((LocalBuilder)instruction.operand).LocalIndex == 7) - { - // CompFoodPoisonable stored, the next condition will be the food poison check. - codeHasStoredCompPoisonable = true; - } - if (instruction.opcode == OpCodes.Brfalse_S && codeHasStoredCompPoisonable) - { - // For this branch, we emit the original instruction first. - // If we don't have a CompFoodPoisonable, we still want to skip. - - yield return instruction; - // Load billGiver onto the stack - yield return new CodeInstruction(OpCodes.Ldarg_0); - yield return new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(hiddenClass, "billGiver")); - // Call our UsingSpaceCooker method - yield return new CodeInstruction(OpCodes.Call, AccessTools.Method( - typeof(Patch_GenRecipe_MakeRecipeProducts_foodPoisoning), - nameof(Patch_GenRecipe_MakeRecipeProducts_foodPoisoning.UsingSpaceCooker))); - // If that returned true, skip past the original condition - yield return new CodeInstruction(OpCodes.Brtrue_S, (Label)instruction.operand); - codeHasStoredCompPoisonable = false; - - // Emitted original instruction first for this branch. - continue; - } - - // =========== T1 cooker patch ========= - // Roughly patches - // worker.GetRoom() - // to Patch_GenRecipe_foodPoisoning.GetRoomOfPawnOrGiver(worker, RegionType.Set_Passable, billGiver) - - if (instruction.opcode == OpCodes.Call && (instruction.operand as MethodInfo) == AccessTools.Method(typeof(RegionAndRoomQuery), nameof(RegionAndRoomQuery.GetRoom))) - { - // By the time we reach here, worker and the number 6 (signifying RegionType.Set_Passable) - // Load billGiver onto the stack. - yield return new CodeInstruction(OpCodes.Ldarg_0); - yield return new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(hiddenClass, "billGiver")); - yield return new CodeInstruction(OpCodes.Call, AccessTools.Method( - typeof(Patch_GenRecipe_MakeRecipeProducts_foodPoisoning), - nameof(Patch_GenRecipe_MakeRecipeProducts_foodPoisoning.GetRoomOfPawnOrGiver))); - - continue; // Don't emit the original instruction. - } - yield return instruction; - } - } - public static bool UsingSpaceCooker(IBillGiver billGiver) - { - // NOTE: This could be made more efficient by having a Def already loaded - // either a static Def loaded from defdatabase directly, or loaded via - // [DefOf] notation. - // However, string comparison in C# is already super fast, so it's - // probably all fine. - if ((billGiver as Building)?.def.defName == "PRF_SelfCookerIII") - { - // Log.Message("Using Spacer Cooker - skipping poison test"); - return true; - } - // Log.Message("Not using space cooker for this recipe"); - return false; - } - - /// - /// If the billGiver is our SimpleAssembler, get the room of the output tile. - /// Else, retain origin behavior. - /// - /// - /// - /// - /// - public static Room GetRoomOfPawnOrGiver(Pawn pawn, RegionType allowedRegionTypes, IBillGiver billGiver) - { - if (pawn.kindDef == PRFDefOf.PRFSlavePawn && billGiver is Building_SimpleAssembler assembler) - { - return RegionAndRoomQuery.RoomAt(assembler.OutputCell(), billGiver.Map, RegionType.Set_Passable); - } - return pawn.GetRoom(allowedRegionTypes); - } - } - -} diff --git a/Source/ProjectRimFactory/SAL3/Things/Assemblers/Building_ProgrammableAssembler.cs b/Source/ProjectRimFactory/SAL3/Things/Assemblers/Building_ProgrammableAssembler.cs index 05bb9073..6ec919b0 100644 --- a/Source/ProjectRimFactory/SAL3/Things/Assemblers/Building_ProgrammableAssembler.cs +++ b/Source/ProjectRimFactory/SAL3/Things/Assemblers/Building_ProgrammableAssembler.cs @@ -438,43 +438,57 @@ protected virtual void ProduceItems() } // GenRecipe handles creating any bonus products if (this.def == PRFDefOf.PRF_Recycler) Patch_Thing_SmeltProducts.RecyclerProducingItems = true; + Patch_CompFoodPoisonable_Notify_RecipeProduced.AssemblerRefrence = this; IEnumerable products = GenRecipe.MakeRecipeProducts(currentBillReport.bill.recipe, buildingPawn, currentBillReport.selected, ProjectSAL_Utilities.CalculateDominantIngredient(currentBillReport.bill.recipe, currentBillReport.selected), this); foreach (Thing thing in products) { PostProcessRecipeProduct(thing); thingQueue.Add(thing); } + Patch_CompFoodPoisonable_Notify_RecipeProduced.AssemblerRefrence = null; Patch_Thing_SmeltProducts.RecyclerProducingItems = false; + + // Consume the Input for (int i = 0; i < currentBillReport.selected.Count; i++) { - if (currentBillReport.selected[i] is Corpse c) - { - if (c.InnerPawn?.apparel != null) - { - List apparel = new List(c.InnerPawn.apparel.WornApparel); - for (int j = 0; j < apparel.Count; j++) - { - thingQueue.Add(apparel[j]); - c.InnerPawn.apparel.Remove(apparel[j]); - } - } - if (c.InnerPawn.inventory?.innerContainer != null) - { - List things = c.InnerPawn.inventory.innerContainer.ToList(); - for (int j = 0; j < things.Count; j++) - { - thingQueue.Add(things[j]); - c.InnerPawn.inventory.innerContainer.Remove(things[j]); - } - } - - } - currentBillReport.bill.recipe.Worker.ConsumeIngredient(currentBillReport.selected[i], currentBillReport.bill.recipe, Map); - + var selected = currentBillReport.selected[i]; + TryGetCorpseItems(selected); + currentBillReport.bill.recipe.Worker.ConsumeIngredient(selected, currentBillReport.bill.recipe, Map); } thingQueue.AddRange(from Thing t in currentBillReport.selected where t.Spawned select t); } + + /// + /// Checks if is a + /// If so adds all attached Things to the + /// + /// a processed Item + private void TryGetCorpseItems(Thing selected) + { + if (selected is not Corpse c) return; + var innerPawn = c.InnerPawn; + if (innerPawn is null) return; + + if (innerPawn.apparel != null) + { + List apparel = new List(innerPawn.apparel.WornApparel); + for (int j = 0; j < apparel.Count; j++) + { + thingQueue.Add(apparel[j]); + innerPawn.apparel.Remove(apparel[j]); + } + } + if (innerPawn.inventory?.innerContainer != null) + { + List things = innerPawn.inventory.innerContainer.ToList(); + for (int j = 0; j < things.Count; j++) + { + thingQueue.Add(things[j]); + innerPawn.inventory.innerContainer.Remove(things[j]); + } + } + } public override string GetInspectString() {