using HarmonyLib; using RimWorld; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Reflection.Emit; using Verse; namespace RJW_Menstruation { [HarmonyPatch(typeof(Hediff_Pregnant), "Miscarry")] public class Miscarry_Patch { public static void Postfix(Hediff_Pregnant __instance) { HediffComp_Menstruation comp = __instance.GetMenstruationCompFromPregnancy(); if (comp == null) return; comp.Pregnancy = null; } } [HarmonyPatch(typeof(Hediff_Pregnant), nameof(Hediff_Pregnant.StartLabor))] public class StartLabor_Patch { public static void Postfix(Hediff_Pregnant __instance) { HediffComp_Menstruation comp = __instance.GetMenstruationCompFromPregnancy(); if (comp == null) return; comp.Pregnancy = __instance.pawn.health.hediffSet.GetFirstHediffOfDef(HediffDefOf.PregnancyLabor); } } [HarmonyPatch(typeof(Hediff_Labor), nameof(Hediff_Labor.PreRemoved))] public class Labor_PreRemoved_Patch { public static void Postfix(Hediff_Labor __instance) { HediffComp_Menstruation comp = __instance.GetMenstruationCompFromPregnancy(); if (comp == null) return; comp.Pregnancy = __instance.pawn.health.hediffSet.GetFirstHediffOfDef(HediffDefOf.PregnancyLaborPushing); } } [HarmonyPatch(typeof(Hediff_LaborPushing), nameof(Hediff_LaborPushing.PreRemoved))] public class LaborPushing_PreRemoved_Patch { public static void Postfix(Hediff_LaborPushing __instance) { HediffComp_Menstruation comp = __instance.GetMenstruationCompFromPregnancy(); if (comp == null) return; comp.Pregnancy = null; } } // Prevents a pregnancy from going into labor if another pregnancy already is [HarmonyPatch(typeof(Hediff_Pregnant), nameof(Hediff_Pregnant.GestationProgress), MethodType.Getter)] public class Hediff_Pregnant_GestationProgess_Patch { public static void Postfix(Hediff_Pregnant __instance, ref float __result) { if (__result < 1f) return; Pawn pawn = __instance.pawn; if (pawn.health.hediffSet.hediffs.Any(hediff => hediff.def == HediffDefOf.PregnancyLabor || hediff.def == HediffDefOf.PregnancyLaborPushing)) __result = 0.999f; } } [HarmonyPatch(typeof(Recipe_ExtractOvum), nameof(Recipe_ExtractOvum.AvailableReport))] public class ExtractOvum_AvailableReport_Patch { public static void Postfix(Thing thing, ref AcceptanceReport __result) { if (!__result.Accepted) return; Pawn pawn = (Pawn)thing; if (pawn.IsRJWPregnant()) { __result = "CannotPregnant".Translate(); return; } List comps = pawn.GetMenstruationComps().ToList(); if (!comps.Any()) return; if (comps.All(comp => comp.ovarypower <= 0)) { __result = Translations.CannotNoEggs; return; } return; } } [HarmonyPatch(typeof(Recipe_ExtractOvum), "OnSurgerySuccess")] public class ExtractOvum_OnSurgerySuccess_Patch { public static void Postfix(Pawn pawn) { List comps = pawn.GetMenstruationComps().ToList(); if (!comps.Any()) return; HediffComp_Menstruation mostEggs = comps.MaxBy(comp => comp.ovarypower); if (mostEggs.ovarypower <= 0) return; // Shouldn't happen mostEggs.ovarypower--; } } [HarmonyPatch(typeof(Recipe_ImplantEmbryo), nameof(Recipe_ImplantEmbryo.ApplyOnPawn))] public class ImplantEmbryo_ApplyOnPawn_Patch { public static void Postfix(Pawn pawn) { foreach (HediffComp_Menstruation comp in pawn.GetMenstruationComps()) comp.TakeLoosePregnancy(); } } [HarmonyPatch(typeof(PregnancyUtility), nameof(PregnancyUtility.ApplyBirthOutcome))] public class ApplyBirthOutcome_Breast_Patch { public static void Postfix(Thing birtherThing) { if (birtherThing is Pawn pawn && !pawn.Dead) pawn.GetBreastComp()?.GaveBirth(); } } [HarmonyPatch] public class TerminatePregnancy_Patch { public static IEnumerable TargetMethods() { yield return AccessTools.Method(typeof(PregnancyUtility), nameof(PregnancyUtility.TryTerminatePregnancy)); yield return AccessTools.Method(typeof(Recipe_TerminatePregnancy), nameof(Recipe_TerminatePregnancy.ApplyOnPawn)); } private static PregnancyAttitude? GetAttitude(Hediff pregnancy) { if (pregnancy is Hediff_Pregnant preg) return preg.Attitude; else return null; } private static Hediff GetEarliestPregnancy(Pawn pawn) { Hediff Earliest_Pregnancy = PregnancyUtility.GetPregnancyHediff(pawn); foreach (HediffComp_Menstruation comp in pawn.GetMenstruationComps()) { Hediff pregnancy = comp.Pregnancy; if (pregnancy == null) continue; if (Earliest_Pregnancy == null || Earliest_Pregnancy.Severity > pregnancy.Severity) Earliest_Pregnancy = pregnancy; } return Earliest_Pregnancy; } private static readonly MethodInfo GetPregnancyHediff = AccessTools.Method(typeof(PregnancyUtility), nameof(PregnancyUtility.GetPregnancyHediff), new Type[] { typeof(Pawn) }); private static readonly MethodInfo Get_Attitude = AccessTools.DeclaredPropertyGetter(typeof(Hediff_Pregnant), nameof(Hediff_Pregnant.Attitude)); public static IEnumerable Transpiler(IEnumerable instructions) { if (GetPregnancyHediff?.ReturnType != typeof(Hediff)) throw new InvalidOperationException("GetPregnancyHediff not found"); if (Get_Attitude == null || Nullable.GetUnderlyingType(Get_Attitude.ReturnType) != typeof(PregnancyAttitude)) throw new InvalidOperationException("get_Attitude not found"); foreach (CodeInstruction instruction in instructions) { if (instruction.Calls(GetPregnancyHediff)) yield return CodeInstruction.Call(typeof(TerminatePregnancy_Patch), nameof(TerminatePregnancy_Patch.GetEarliestPregnancy)); // Menstruation pregnancies don't have an attitude, so skip the cast to Hediff_Pregnant and call a version that handles it else if (instruction.opcode == OpCodes.Castclass && (Type)instruction.operand == typeof(Hediff_Pregnant)) yield return new CodeInstruction(OpCodes.Nop); else if (instruction.Calls(Get_Attitude)) yield return CodeInstruction.Call(typeof(TerminatePregnancy_Patch), nameof(TerminatePregnancy_Patch.GetAttitude)); else yield return instruction; } } } [HarmonyPatch(typeof(PregnancyUtility), nameof(PregnancyUtility.TryTerminatePregnancy))] public class PregnancyUtility_TryTerminatePregnancy_Patch { public static void Postfix(bool __result, Pawn pawn) { if (__result) foreach (HediffComp_Menstruation comp in pawn.GetMenstruationComps()) _ = comp.Pregnancy; // get_Pregnancy will remove the hediff attached to the comp that doesn't have it anymore } } [HarmonyPatch(typeof(Recipe_TerminatePregnancy), nameof(Recipe_TerminatePregnancy.AvailableOnNow))] public class TerminatePregnancy_AvailableOnNow_Patch { public static void Postfix(ref bool __result, Thing thing) { if (!ModsConfig.BiotechActive || !(thing is Pawn pawn)) return; __result |= pawn.GetMenstruationComps().Any(comp => comp.Pregnancy != null); } } [HarmonyPatch(typeof(Pawn_GeneTracker), "Notify_GenesChanged")] public class Notify_GenesChanged_Patch { public static void Postfix(Pawn_GeneTracker __instance, GeneDef addedOrRemovedGene) { if (!addedOrRemovedGene.HasModExtension()) return; foreach (HediffComp_Menstruation comp in __instance.pawn.GetMenstruationComps()) comp.Notify_UpdatedGenes(); } } [HarmonyPatch(typeof(StatPart_FertilityByGenderAge), "AgeFactor")] public class AgeFactor_Patch { public static void Postfix(ref float __result, Pawn pawn) { if (__result <= 0.0f) return; if (pawn.GetMenstruationComps().Any(comp => comp.CalculatingOvulationChance)) __result = 1.0f; } } //[HarmonyPatch(typeof(ChildcareUtility), nameof(ChildcareUtility.SuckleFromLactatingPawn))] //public class GreedyConsume_Patch //{ // private static float ConsumeAndAdjustNipples(HediffComp_Chargeable instance, float desiredCharge) // { // // Pulling breast comp every tick might be too much for performance. // const float averageNippleChangePerTick = 0.0025f / 50000f; // instance.Pawn.GetBreastComp()?.AdjustNippleProgress(Rand.Range(0.0f, averageNippleChangePerTick * 2) * Configurations.MaxBreastIncrementFactor); // return instance.GreedyConsume(desiredCharge); // } // private static readonly MethodInfo GreedyConsume = AccessTools.Method(typeof(HediffComp_Chargeable), nameof(HediffComp_Chargeable.GreedyConsume), new Type[] { typeof(HediffComp_Chargeable), typeof(float) }); // public static IEnumerable Transpiler(IEnumerable instructions) // { // if(GreedyConsume == null) throw new InvalidOperationException("GreedyConsume not found"); // foreach (var instruction in instructions) // { // if (instruction.Calls(GreedyConsume)) // yield return CodeInstruction.Call(typeof(GreedyConsume_Patch), nameof(GreedyConsume_Patch.ConsumeAndAdjustNipples)); // yield return instruction; // } // } //} }