diff --git a/1.4/Assemblies/RJW_Menstruation.dll b/1.4/Assemblies/RJW_Menstruation.dll index 32d579b..5f42dbb 100644 Binary files a/1.4/Assemblies/RJW_Menstruation.dll and b/1.4/Assemblies/RJW_Menstruation.dll differ diff --git a/1.4/Languages/English/Keyed/RJW_Menstruation.xml b/1.4/Languages/English/Keyed/RJW_Menstruation.xml index 3df2d06..a1c65b3 100644 --- a/1.4/Languages/English/Keyed/RJW_Menstruation.xml +++ b/1.4/Languages/English/Keyed/RJW_Menstruation.xml @@ -120,6 +120,8 @@ Hookup minimum opinion in estrus Estimated sperm lifespan Estimated egg lifespan + Ovulation {0} + Chance of each egg being released during ovulation. Implantation chance of fertilized eggs. Chance of fertilization this hour: {0}% Use basic RJW pregnancy Use menstruation multiple pregnancy diff --git a/1.4/MilkModule/Assemblies/MilkModule.dll b/1.4/MilkModule/Assemblies/MilkModule.dll index 5c953b2..174e407 100644 Binary files a/1.4/MilkModule/Assemblies/MilkModule.dll and b/1.4/MilkModule/Assemblies/MilkModule.dll differ diff --git a/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_Menstruation.cs b/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_Menstruation.cs index d438f98..5ec30b2 100644 --- a/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_Menstruation.cs +++ b/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_Menstruation.cs @@ -280,16 +280,63 @@ namespace RJW_Menstruation return 1.0f; } } - //effect on implant chance - public float ImplantFactor + + // I hate doing this, but it's the least bad option + public bool calculatingOvulationChance = false; + + public float OvulationChance + { + get + { + float ovulationChance = 1.0f; + if (EggHealth <= 0.0f) return 0.0f; + if (EggHealth < 1.0f / 3.0f) ovulationChance = 0.2f; + if (ModsConfig.BiotechActive && xxx.is_human(Pawn)) + { + if (Pawn.SterileGenes()) return 0.0f; + // Replicate how rjw.PawnCapacityWorker_Fertility.CalculateCapacityLevel does it, but without the age factor + if (!Pawn.RaceHasFertility()) return 0.0f; + if (AndroidsCompatibility.IsAndroid(Pawn) && parent.def != Genital_Helper.archotech_vagina) return 0.0f; + foreach (var part in StatDefOf.Fertility.parts) + { + if(part is StatPart_FertilityByGenderAge fertilityByAge) + { + float factor = 1.0f; + fertilityByAge.TransformValue(StatRequest.For(Pawn), ref factor); + if (factor <= 0.0f) return 0.0f; // Too young or too old + } + else part.TransformValue(StatRequest.For(Pawn), ref ovulationChance); + } + try + { + calculatingOvulationChance = true; + ovulationChance *= PawnCapacityUtility.CalculateCapacityLevel(Pawn.health.hediffSet, xxx.reproduction); + } + finally { calculatingOvulationChance = false; } + } + return ovulationChance; + } + } + public float ImplantChance { get { float factor = 1.0f; if (Pawn.Has(Quirk.Breeder)) factor = 10.0f; - return Pawn.health.capacities.GetLevel(xxx.reproduction) * Props.baseImplantationChanceFactor * FertilityModifier * factor; + + if (ModsConfig.BiotechActive && xxx.is_human(Pawn)) + { + // Implant factor will be based solely on pawn age, plus any rollover from ovulation chance + StatPart_FertilityByGenderAge fertilityStatPart = StatDefOf.Fertility.GetStatPart(); + fertilityStatPart?.TransformValue(StatRequest.For(Pawn), ref factor); + float ovulationOverflow = OvulationChance; + if (ovulationOverflow > 1.0f) factor *= ovulationOverflow; + return Props.baseImplantationChanceFactor * FertilityModifier * factor; + } + else return Pawn.health.capacities.GetLevel(xxx.reproduction) * Props.baseImplantationChanceFactor * FertilityModifier * factor; } } + public IEnumerable GetCumsInfo { get @@ -678,7 +725,7 @@ namespace RJW_Menstruation BeforeSimulator(); - if (pregnancy == null && (Pawn.health.capacities.GetLevel(xxx.reproduction) <= 0 || EggHealth <= 0 || Pawn.SterileGenes())) GoNextStage(Stage.Infertile); + if (pregnancy == null && (ImplantChance <= 0.0f || EggHealth <= 0)) GoNextStage(Stage.Infertile); switch (curStage) { case Stage.Follicular: @@ -1111,9 +1158,12 @@ namespace RJW_Menstruation if (cycleSpeed < 0f) cycleSpeed = Utility.RandGaussianLike(0.8f, 1.2f); if (cycleVariability < 0f) cycleVariability = MenstruationUtility.RandomVariabilityPercent(); + + InitOvary(); + if (currentIntervalHours < 0) { - if (Pawn.health.capacities.GetLevel(xxx.reproduction) <= 0 || Pawn.SterileGenes()) curStage = Stage.Infertile; + if (ImplantChance <= 0.0f || EggHealth <= 0) curStage = Stage.Infertile; else if (!IsBreedingSeason()) curStage = Stage.Anestrus; else curStage = RandomStage(); if (curStage == Stage.Follicular) @@ -1128,8 +1178,6 @@ namespace RJW_Menstruation if (cums == null) cums = new List(); if (eggs == null) eggs = new List(); - - InitOvary(); TakeLoosePregnancy(); //Log.Message(Pawn.Label + " - Initialized menstruation comp"); @@ -1324,7 +1372,7 @@ namespace RJW_Menstruation deadeggs.Add(egg); continue; } - else if (Rand.Range(0.0f, 1.0f) <= Configurations.ImplantationChance * ImplantFactor * InterspeciesImplantFactor(egg.fertilizer)) + else if (Rand.Range(0.0f, 1.0f) <= Configurations.ImplantationChance * ImplantChance * InterspeciesImplantFactor(egg.fertilizer)) { if (Configurations.Debug) Log.Message($"Implanting fertilized egg of {Pawn} into {parent}, father {egg.fertilizer}"); if (pregnancy != null) @@ -1527,7 +1575,7 @@ namespace RJW_Menstruation } else if (curStageHrs >= currentIntervalHours) { - GoOvulatoryStage(); + GoNextStage(Stage.Ovulatory); } else { @@ -1562,8 +1610,10 @@ namespace RJW_Menstruation eggnum *= ovulationFactor; ovulated = (int)eggnum + eggstack; + float ovulationChance = OvulationChance; for (int i = 0; i < ovulated; i++) - eggs.Add(new Egg((int)(EggLifespanHours / CycleFactor))); + if (i < eggstack || Rand.Chance(ovulationChance)) // eggstack comes from drugs and are guaranteed ovulated + eggs.Add(new Egg((int)(EggLifespanHours / CycleFactor))); ovarypower -= ovulated; eggstack = 0; @@ -1781,12 +1831,7 @@ namespace RJW_Menstruation protected virtual void GoOvulatoryStage() { - if (EggHealth < 1.0f / 3.0f && Rand.Range(0.0f, 1.0f) < 0.2f) // Skip ovulation if deep into climacteric - { - estrusflag = false; - GoNextStage(Stage.Luteal); - } - else GoNextStage(Stage.Ovulatory); + GoNextStage(Stage.Ovulatory); } //stage can be interrupted in other reasons diff --git a/1.4/source/RJW_Menstruation/RJW_Menstruation/Patch/RJW_Patch.cs b/1.4/source/RJW_Menstruation/RJW_Menstruation/Patch/RJW_Patch.cs index cc1ff7c..b754557 100644 --- a/1.4/source/RJW_Menstruation/RJW_Menstruation/Patch/RJW_Patch.cs +++ b/1.4/source/RJW_Menstruation/RJW_Menstruation/Patch/RJW_Patch.cs @@ -129,11 +129,11 @@ namespace RJW_Menstruation else if (pawn.GetMenstruationComps().Any()) return false; else return pawn.IsPregnant(); } - private static readonly MethodInfo IsPregnant = AccessTools.Method(typeof(PawnExtensions), nameof(PawnExtensions.IsPregnant), new System.Type[] {typeof(Pawn), typeof(bool)}); + private static readonly MethodInfo IsPregnant = AccessTools.Method(typeof(PawnExtensions), nameof(PawnExtensions.IsPregnant), new System.Type[] { typeof(Pawn), typeof(bool) }); public static IEnumerable Transpiler(IEnumerable instructions) { if (IsPregnant == null || IsPregnant.ReturnType != typeof(bool)) throw new System.InvalidOperationException("IsPregnant not found"); - foreach(CodeInstruction instruction in instructions) + foreach (CodeInstruction instruction in instructions) { if (instruction.Calls(IsPregnant)) yield return CodeInstruction.Call(typeof(CanImpregnate_Patch), nameof(PregnancyBlocksImpregnation)); @@ -371,4 +371,27 @@ namespace RJW_Menstruation } } + + [HarmonyPatch(typeof(PawnCapacityWorker_Fertility), nameof(PawnCapacityWorker_Fertility.CalculateCapacityLevel))] + public static class PawnCapacityWorker_Fertility_Patch + { + private static float GetFertilityStatOrOne(Thing thing, StatDef stat, bool applyPostProcess, int cacheStaleAfterTicks) + { + Pawn pawn = (Pawn)thing; + if (pawn.GetMenstruationComps().Any(comp => comp.calculatingOvulationChance)) + return 1.0f; + else return thing.GetStatValue(stat, applyPostProcess, cacheStaleAfterTicks); + } + private static readonly MethodInfo GetStatValue = AccessTools.Method(typeof(StatExtension), "GetStatValue", new System.Type[] { typeof(Thing), typeof(StatDef), typeof(bool), typeof(int) }); + public static IEnumerable Transpiler(IEnumerable instructions) + { + if (GetStatValue == null || GetStatValue.ReturnType != typeof(float)) throw new System.InvalidOperationException("GetStatValue not found"); + foreach (CodeInstruction instruction in instructions) + { + if (instruction.Calls(GetStatValue)) + yield return CodeInstruction.Call(typeof(PawnCapacityWorker_Fertility_Patch), nameof(GetFertilityStatOrOne)); + else yield return instruction; + } + } + } } diff --git a/1.4/source/RJW_Menstruation/RJW_Menstruation/Translations.cs b/1.4/source/RJW_Menstruation/RJW_Menstruation/Translations.cs index 7b0862d..1a67ebf 100644 --- a/1.4/source/RJW_Menstruation/RJW_Menstruation/Translations.cs +++ b/1.4/source/RJW_Menstruation/RJW_Menstruation/Translations.cs @@ -134,6 +134,8 @@ namespace RJW_Menstruation public static readonly string EstimatedCumLifespan = "EstimatedCumLifespan".Translate(); public static readonly string EstimatedEggLifespan = "EstimatedEggLifespan".Translate(); + public static string OvulationChanceLabel(string value) => "OvulationChanceLabel".Translate(value); + public static readonly string OvulationChanceDesc = "OvulationChanceDesc".Translate(); public static string FertilityDesc(string value) => "FertilityDesc".Translate(value); public static readonly string Gizmo_GatherCum = "Gizmo_GatherCum".Translate(); diff --git a/1.4/source/RJW_Menstruation/RJW_Menstruation/UI/Dialog_WombStatus.cs b/1.4/source/RJW_Menstruation/RJW_Menstruation/UI/Dialog_WombStatus.cs index 4d428a9..ec93cfd 100644 --- a/1.4/source/RJW_Menstruation/RJW_Menstruation/UI/Dialog_WombStatus.cs +++ b/1.4/source/RJW_Menstruation/RJW_Menstruation/UI/Dialog_WombStatus.cs @@ -450,11 +450,18 @@ namespace RJW_Menstruation statvalue = pawn.records.GetValue(xxx.CountOfBirthEgg); FillableBarLabeled(lineRect, " " + xxx.CountOfBirthEgg.LabelCap.CapitalizeFirst() + " " + statvalue, statvalue / 100, TextureCache.RecoverTexture, Texture2D.blackTexture, xxx.CountOfBirthEgg.description); - lineRect.y += height * 4; + lineRect.y += height * 3; - statvalue = Configurations.ImplantationChance * comp.ImplantFactor; + if (ModsConfig.BiotechActive) + { + statvalue = comp.OvulationChance; + FillableBarLabeled(lineRect, " " + Translations.OvulationChanceLabel(statvalue.ToStringPercent()), statvalue, TextureCache.LutealTexture, Texture2D.blackTexture, Translations.OvulationChanceDesc); + } + lineRect.y += height; + + statvalue = Configurations.ImplantationChance * comp.ImplantChance; float fertchance = comp.GetFertilityChance(); - FillableBarLabeled(lineRect, " " + xxx.reproduction.LabelCap.CapitalizeFirst() + " " + statvalue.ToStringPercent(), statvalue, TextureCache.LutealTexture, Texture2D.blackTexture, Translations.FertilityDesc(String.Format("{0:0.##}", fertchance * 100))); + FillableBarLabeled(lineRect, " " + xxx.reproduction.LabelCap.CapitalizeFirst() + " " + statvalue.ToStringPercent(), statvalue, TextureCache.LutealTexture, Texture2D.blackTexture, Translations.FertilityDesc(string.Format("{0:0.##}", fertchance * 100))); Rect overayRect = new Rect(lineRect.x, lineRect.y, lineRect.width * Math.Min(1.0f, fertchance), lineRect.height); GUI.DrawTexture(overayRect, TextureCache.FertChanceTex); lineRect.y += height; diff --git a/About/Manifest.xml b/About/Manifest.xml index 5477ddb..24b2e46 100644 --- a/About/Manifest.xml +++ b/About/Manifest.xml @@ -1,7 +1,7 @@ RJW Menstruation - 1.0.8.7 + 1.0.8.8 diff --git a/changelogs.txt b/changelogs.txt index 160c7f5..1c41775 100644 --- a/changelogs.txt +++ b/changelogs.txt @@ -1,3 +1,11 @@ +Version 1.0.8.8 + - Fix pawns skipping straight to menopause instead of going through climacteric stages. + - Rework ovulation mechanics. A pawn's implantation chance is now dependent on their age. + - All other fertility-altering effects instead change the odds of ovulation occuring, rolled per-egg. This chance appears in the womb dialog. + - If the chance of ovulation goes above 100%, the implantation chance is increased. + - Drugs that increase the number of eggs ovulated are still guaranteed to work. + - If Biotech is disabled or not installed, the old fertility system will apply instead. + Version 1.0.8.7 - Fix missing texture when using Milkable Colonists. - Fix estrus and egg lifespan lasting far longer than intended.