diff --git a/1.4/Assemblies/RJW_Menstruation.dll b/1.4/Assemblies/RJW_Menstruation.dll index 936deef..325db98 100644 Binary files a/1.4/Assemblies/RJW_Menstruation.dll and b/1.4/Assemblies/RJW_Menstruation.dll differ diff --git a/1.4/Defs/ThoughtDefs/Thoughts_sex.xml b/1.4/Defs/ThoughtDefs/Thoughts_sex.xml index d66d16b..e35d3e8 100644 --- a/1.4/Defs/ThoughtDefs/Thoughts_sex.xml +++ b/1.4/Defs/ThoughtDefs/Thoughts_sex.xml @@ -225,7 +225,20 @@ - - + + + EggRestorationReceived + Thought_Memory + 4 + 1 + +
  • + + I can breed for a little longer now. + 2 +
  • +
    +
    + diff --git a/1.4/Languages/English/Keyed/RJW_Menstruation.xml b/1.4/Languages/English/Keyed/RJW_Menstruation.xml index 5be5d11..f28203c 100644 --- a/1.4/Languages/English/Keyed/RJW_Menstruation.xml +++ b/1.4/Languages/English/Keyed/RJW_Menstruation.xml @@ -57,7 +57,7 @@ Cycle acceleration Accelerate menstruation cycle This can cause early menopause and infertility. Setting this lower than x12 is recommended. Rimworld's timescale: x6(default) Debug - Show debug information. Also overrides "Fetus information level" to All. + Show debug information. Womb status Draw womb icon in status window. Vagina and breast status @@ -68,7 +68,7 @@ Show only image of a fetus after discovered pregnancy. Do not show any information about a fetus. Enable menopause - Enable menopause effect that makes pawn infertile in time progress If you have problems with long life races, turn off this option. + Enable menopause effect that makes pawn infertile when they run out of eggs If you have problems with long lived races, turn off this option. Use multiple pregnancy Use multiple pregnancy instead RJW's default pregnancy Disable this option if you are in trouble with impregnation RJW pregnancy should be turned on. Enable hetero ovular twins @@ -136,5 +136,7 @@ When {0} breed with {1}, {2} will be born at {3} chance. If both races have hybrid definitions for each other, the father's definition will be used. No eggs + Must have a womb + {PAWN_labelShort} has completed {PAWN_possessive} egg restoration cycle. \ No newline at end of file diff --git a/1.4/MilkModule/Assemblies/MilkModule.dll b/1.4/MilkModule/Assemblies/MilkModule.dll index 7320b77..5961d35 100644 Binary files a/1.4/MilkModule/Assemblies/MilkModule.dll and b/1.4/MilkModule/Assemblies/MilkModule.dll differ diff --git a/1.4/Patches/Biosculpter_Patch.xml b/1.4/Patches/Biosculpter_Patch.xml new file mode 100644 index 0000000..558f4b7 --- /dev/null +++ b/1.4/Patches/Biosculpter_Patch.xml @@ -0,0 +1,24 @@ + + + + +
  • Ideology
  • +
    + + /Defs/ThingDef[defName="BiosculpterPod"]/comps + +
  • + eggRestoration + + Restore one year worth of eggs in each womb. + UI/Icon/EggRegeneration + 4 + + + (0.867, 0.373, 0.396) + 1.0 +
  • +
    +
    +
    +
    \ No newline at end of file diff --git a/1.4/Patches/Hediffs_PrivateParts_Animal.xml b/1.4/Patches/Hediffs_PrivateParts_Animal.xml index 723eafc..fc60760 100644 --- a/1.4/Patches/Hediffs_PrivateParts_Animal.xml +++ b/1.4/Patches/Hediffs_PrivateParts_Animal.xml @@ -30,6 +30,28 @@ + + /Defs/rjw.HediffDef_PartBase[defName="CatVagina"] diff --git a/1.4/Textures/UI/Icon/EggRegeneration.png b/1.4/Textures/UI/Icon/EggRegeneration.png new file mode 100644 index 0000000..3c16583 Binary files /dev/null and b/1.4/Textures/UI/Icon/EggRegeneration.png differ diff --git a/1.4/source/RJW_Menstruation/RJW_Menstruation/CompBiosculpterPod_EggRestorationCycle.cs b/1.4/source/RJW_Menstruation/RJW_Menstruation/CompBiosculpterPod_EggRestorationCycle.cs new file mode 100644 index 0000000..537ef53 --- /dev/null +++ b/1.4/source/RJW_Menstruation/RJW_Menstruation/CompBiosculpterPod_EggRestorationCycle.cs @@ -0,0 +1,27 @@ +using RimWorld; +using Verse; + +namespace RJW_Menstruation +{ + public class CompProperties_BiosculpterPod_EggRestorationCycle : CompProperties_BiosculpterPod_BaseCycle + { + public CompProperties_BiosculpterPod_EggRestorationCycle() + { + compClass = typeof(CompBiosculpterPod_EggRestorationCycle); + } + + public float yearsToRestore; + } + + public class CompBiosculpterPod_EggRestorationCycle : CompBiosculpterPod_Cycle + { + + public override void CycleCompleted(Pawn occupant) + { + foreach (HediffComp_Menstruation comp in occupant.GetMenstruationComps()) + comp.RestoreEggs(((CompProperties_BiosculpterPod_EggRestorationCycle)Props).yearsToRestore); + + Messages.Message(Translations.EggRestorationCompleted(occupant.Named("PAWN")), occupant, MessageTypeDefOf.PositiveEvent); + } + } +} diff --git a/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_Breast.cs b/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_Breast.cs index b0d8b79..1d5eb60 100644 --- a/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_Breast.cs +++ b/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_Breast.cs @@ -246,8 +246,14 @@ namespace RJW_Menstruation protected void CalculateBreastSize() { - // Scenario A: the youngest child is less than halfway into babyhood: Full size - if (ageOfLastBirth + BabyHalfAge * GenDate.TicksPerYear > Pawn.ageTracker.AgeBiologicalTicks) + // Ageless pawns can't depend on the chrono age, so just disable their growth entirely + if (Pawn.ageTracker.BiologicalTicksPerTick <= 0f && breastSizeIncreased > 0) + { + ShrinkBreasts(); + debugGrowthStatus = "Base size (ageless)"; + } + // The youngest child is less than halfway into babyhood: Full size + else if (ageOfLastBirth + BabyHalfAge * GenDate.TicksPerYear > Pawn.ageTracker.AgeBiologicalTicks) { debugGrowthStatus = "Full size due to young child"; if (breastSizeIncreased < MaxBreastIncrement) @@ -256,7 +262,7 @@ namespace RJW_Menstruation breastSizeIncreased = MaxBreastIncrement; } } - // Scenario B: Pregnant, grow in the second half of first trimester + // Pregnant, grow in the second half of first trimester else if (Pawn.IsRJWPregnant() || Pawn.IsBiotechPregnant()) { float pregnancySize = Mathf.InverseLerp(breastGrowthStart, breastGrowthEnd, Pawn.GetFarthestPregnancyProgress()) * MaxBreastIncrement; @@ -279,7 +285,7 @@ namespace RJW_Menstruation } else debugGrowthStatus = "Pregnant and full size"; } - // Scenario C: Not (or very early) pregnant and youngest child nonexistent or more than halfway into babyhood, time to shrink + // Not (or very early) pregnant and youngest child nonexistent or more than halfway into babyhood, time to shrink else if (breastSizeIncreased > 0) { debugGrowthStatus = "Shrinking due to no pregnancy nor young child"; @@ -291,7 +297,9 @@ namespace RJW_Menstruation protected void CalculateNipples() { float newNippleProgress; - if (ageOfLastBirth + BabyHalfAge * GenDate.TicksPerYear > Pawn.ageTracker.AgeBiologicalTicks) + if (Pawn.ageTracker.BiologicalTicksPerTick <= 0f) + newNippleProgress = 0f; + else if (ageOfLastBirth + BabyHalfAge * GenDate.TicksPerYear > Pawn.ageTracker.AgeBiologicalTicks) newNippleProgress = 1f; else if (Pawn.IsRJWPregnant() || Pawn.IsBiotechPregnant()) newNippleProgress = nippleTransitions.Evaluate(Pawn.GetFarthestPregnancyProgress()); 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 dc97bbf..5064dc2 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 @@ -68,6 +68,8 @@ namespace RJW_Menstruation const int tickInterval = GenDate.TicksPerHour; const int maxImplantDelayHours = 30 * 24; const int minImplantAgeHours = 3 * 24; + const float pulloutSuccessRate = 0.8f; + const float fetishPulloutSuccessModifier = 0.25f; public CompProperties_Menstruation Props; public Stage curStage = Stage.Follicular; @@ -637,7 +639,7 @@ namespace RJW_Menstruation curStage = Stage.Pregnant; } - CumOut(); + BeforeSimulator(); if (pregnancy == null && (Pawn.health.capacities.GetLevel(xxx.reproduction) <= 0 || EggHealth <= 0 || Pawn.SterileGenes())) curStage = Stage.Infertile; switch (curStage) @@ -735,10 +737,17 @@ namespace RJW_Menstruation /// /// /// - /// - public void CumIn(Pawn pawn, float volume, float fertility = 1.0f, ThingDef filthdef = null) + /// + public void CumIn(Pawn pawn, float volume, float fertility = 1.0f, bool precum = false) { if (volume <= 0) return; + if (!precum && fertility > 0 && IsDangerDay && pawn.relations.GetPregnancyApproachForPartner(Pawn) == PregnancyApproach.AvoidPregnancy) + { + float successChance = pulloutSuccessRate; + if (pawn.Has(Quirk.ImpregnationFetish)) successChance *= fetishPulloutSuccessModifier; + if (Pawn.Has(Quirk.ImpregnationFetish)) successChance *= fetishPulloutSuccessModifier; + if (Rand.Chance(successChance)) return; + } if (Pawn.HasIUD()) fertility /= 100f; float cumd = TotalCumPercent; float tmp = TotalCum + volume; @@ -750,12 +759,12 @@ namespace RJW_Menstruation { if (cum.pawn.Equals(pawn)) { - cum.MergeWithCum(volume, fertility, filthdef); + cum.MergeWithCum(volume, fertility); merged = true; } cum.DismishForce(cumoutrate); } - if (!merged) cums.Add(new Cum(pawn, volume * (1 - cumoutrate), fertility, filthdef)); + if (!merged) cums.Add(new Cum(pawn, volume * (1 - cumoutrate), fertility)); } else { @@ -765,17 +774,20 @@ namespace RJW_Menstruation { if (cum.pawn.Equals(pawn)) { - cum.MergeWithCum(volume, fertility, filthdef); + cum.MergeWithCum(volume, fertility); merged = true; } } - if (!merged) cums.Add(new Cum(pawn, volume, fertility, filthdef)); + if (!merged) cums.Add(new Cum(pawn, volume, fertility)); } cumd = TotalCumPercent - cumd; - Pawn.records.AddTo(VariousDefOf.AmountofCreampied, volume); - AfterCumIn(pawn); - AfterFluidIn(cumd); + if (!precum) + { + Pawn.records.AddTo(VariousDefOf.AmountofCreampied, volume); + AfterCumIn(pawn); + AfterFluidIn(cumd); + } } /// @@ -1041,6 +1053,7 @@ namespace RJW_Menstruation else if (currentIntervalHours < curStageHrs) curStageHrs = currentIntervalHours; } if (crampPain < 0) crampPain = PainRandomizer(); + InitializeExtraValues(); if (cums == null) cums = new List(); if (eggs == null) eggs = new List(); @@ -1053,6 +1066,10 @@ namespace RJW_Menstruation initError = false; } + protected virtual void InitializeExtraValues() + { + } + protected virtual float RaceCyclesPerYear() { int breedingSeasons = 0; @@ -1118,8 +1135,12 @@ namespace RJW_Menstruation ovarypower = Math.Max(0, (int)(ovarypower * multiply)); } + protected virtual void BeforeSimulator() + { + CumOut(); + } - protected void AfterSimulator() + protected virtual void AfterSimulator() { if (EggHealth < 1f) { @@ -1468,7 +1489,7 @@ namespace RJW_Menstruation protected virtual void LutealAction() { - if (curStageHrs > currentIntervalHours) + if (curStageHrs >= currentIntervalHours) { eggs.Clear(); if (EggHealth < 1f / 4f || (EggHealth < 1f / 3f && Rand.Range(0.0f, 1.0f) < 0.3f)) //skips bleeding @@ -1507,7 +1528,7 @@ namespace RJW_Menstruation if (curStageHrs >= currentIntervalHours) { Hediff hediff = Pawn.health.hediffSet.GetFirstHediffOfDef(VariousDefOf.Hediff_MenstrualCramp); - if (hediff != null) Pawn.health.RemoveHediff(hediff); + if (hediff != null && !Pawn.GetMenstruationComps().Any(comp => comp != this && comp.curStage == Stage.Bleeding)) Pawn.health.RemoveHediff(hediff); int totalFollicularHours = PeriodRandomizer(Stage.Follicular); // The total amount of time for both bleeding and follicular if (totalFollicularHours <= currentIntervalHours) // We've bled for so long that we completely missed the follicular phase GoOvulatoryStage(); @@ -1784,7 +1805,7 @@ namespace RJW_Menstruation GoNextStage(Stage.Pregnant); } - public void CopyCycleProperties(HediffComp_Menstruation original) + public virtual void CopyCycleProperties(HediffComp_Menstruation original) { cycleSpeed = original.cycleSpeed; cycleVariability = original.cycleVariability; @@ -1792,6 +1813,16 @@ namespace RJW_Menstruation crampPain = original.crampPain; } + public int EggsRestoredPerBiosculptor(float yearsWorth) + { + return Math.Max(1, (int)((float)RaceCyclesPerYear() * yearsWorth)); + } + + public void RestoreEggs(float yearsWorth) + { + ovarypower += EggsRestoredPerBiosculptor(yearsWorth); + } + public class Egg : IExposable { public bool fertilized; diff --git a/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_PeriodicOvulator.cs b/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_PeriodicOvulator.cs new file mode 100644 index 0000000..11509de --- /dev/null +++ b/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_PeriodicOvulator.cs @@ -0,0 +1,99 @@ +using RimWorld; +using System.Linq; +using Verse; + +namespace RJW_Menstruation +{ + public class CompProperties_PeriodicOvulator : CompProperties_Menstruation + { + public FloatRange cycleIntervalDays; // From the start of one cycle to the start of the next + + public CompProperties_PeriodicOvulator() + { + compClass = typeof(HediffComp_PeriodicOvulator); + } + } + + public class HediffComp_PeriodicOvulator : HediffComp_Menstruation + { + public int hoursToNextCycle = -100000; + public int averageCycleIntervalHours = -1; + + public new CompProperties_PeriodicOvulator Props; + + protected override void InitializeExtraValues() + { + base.InitializeExtraValues(); + Props = (CompProperties_PeriodicOvulator)props; + if (averageCycleIntervalHours < 0) + { + averageCycleIntervalHours = (int)(24f * Props.cycleIntervalDays.RandomInRange / cycleSpeed); + if (hoursToNextCycle < -50000) + hoursToNextCycle = Rand.Range(0, averageCycleIntervalHours); + // Make the cutoff halfway into cycle, just to be sure there isn't a double-cycle the first time + if ((curStage == Stage.Follicular || curStage == Stage.Luteal || curStage == Stage.Bleeding) + && (averageCycleIntervalHours - hoursToNextCycle) / 2 >= 24 * (Props.follicularIntervalDays + Props.lutealIntervalDays) / cycleSpeed) + curStage = Stage.Anestrus; + } + } + + protected override float RaceCyclesPerYear() + { + // Don't bother trying to work seasonal breeding into the math + // Due to the enormous variation in possible cycle gaps, cheat and base it off the individual + return averageCycleIntervalHours * cycleSpeed / (24 * 360); // cancel out their cycleSpeed from initialization to get their "normal" speed + } + + protected override void BeforeSimulator() + { + base.BeforeSimulator(); + if (hoursToNextCycle > 0) hoursToNextCycle -= Configurations.CycleAcceleration; + } + + public override void CompExposeData() + { + base.CompExposeData(); + Scribe_Values.Look(ref hoursToNextCycle, "hoursToNextCycle", hoursToNextCycle, true); + Scribe_Values.Look(ref averageCycleIntervalHours, "averageCycleIntervalHours", averageCycleIntervalHours, true); + } + + protected override void BleedingAction() + { + if (curStageHrs >= currentIntervalHours) + { + Hediff hediff = Pawn.health.hediffSet.GetFirstHediffOfDef(VariousDefOf.Hediff_MenstrualCramp); + if (hediff != null && !Pawn.GetMenstruationComps().Any(comp => comp != this && comp.curStage == Stage.Bleeding)) Pawn.health.RemoveHediff(hediff); + estrusflag = false; + GoNextStage(Stage.Anestrus); + return; + } + else base.BleedingAction(); + } + + protected override void PregnantAction() + { + base.PregnantAction(); + if (curStage != Stage.Pregnant) + // Go halfway into the cycle + hoursToNextCycle = (int)(averageCycleIntervalHours * Rand.Range(-cycleVariability, cycleVariability)) / 2; + } + + protected override void AnestrusAction() + { + if (hoursToNextCycle <= 0) + { + hoursToNextCycle = (int)(averageCycleIntervalHours * Rand.Range(-cycleVariability, cycleVariability)); + if (IsBreedingSeason()) GoNextStage(Stage.Follicular); + return; + } + StayCurrentStage(); + } + + public override void CopyCycleProperties(HediffComp_Menstruation original) + { + base.CopyCycleProperties(original); + if (original is HediffComp_PeriodicOvulator comp) + averageCycleIntervalHours = comp.averageCycleIntervalHours; + } + } +} diff --git a/1.4/source/RJW_Menstruation/RJW_Menstruation/Hediff_MultiplePregnancy.cs b/1.4/source/RJW_Menstruation/RJW_Menstruation/Hediff_MultiplePregnancy.cs index 0641e24..c1d995a 100644 --- a/1.4/source/RJW_Menstruation/RJW_Menstruation/Hediff_MultiplePregnancy.cs +++ b/1.4/source/RJW_Menstruation/RJW_Menstruation/Hediff_MultiplePregnancy.cs @@ -1,9 +1,10 @@ using RimWorld; using rjw; +using System; using System.Collections.Generic; using System.Linq; +using System.Reflection; using System.Text; -using UnityEngine; using Verse; namespace RJW_Menstruation @@ -12,6 +13,9 @@ namespace RJW_Menstruation { protected Dictionary enzygoticSiblings = new Dictionary(); // Each pawn and who they split from + protected readonly MethodInfo TryGetInheritedXenotype = typeof(PregnancyUtility).GetMethod("TryGetInheritedXenotype", BindingFlags.Static | BindingFlags.NonPublic, null, new Type[] { typeof(Pawn), typeof(Pawn), typeof(XenotypeDef).MakeByRefType() }, null ); + protected readonly MethodInfo ShouldByHybrid = typeof(PregnancyUtility).GetMethod("ShouldByHybrid", BindingFlags.Static | BindingFlags.NonPublic, null, new Type[] { typeof(Pawn), typeof(Pawn) }, null); + public override void DiscoverPregnancy() { PregnancyThought(); @@ -160,6 +164,21 @@ namespace RJW_Menstruation if (xxx.is_human(mother)) TaleRecorder.RecordTale(TaleDefOf.GaveBirth, new object[] { mother, baby }); + if (ModsConfig.BiotechActive) + { + // Ugly, but it'll have to do + OutcomeChance bestOutcome = RitualOutcomeEffectDefOf.ChildBirth.outcomeChances.Find(chance => chance.positivityIndex == 1); + + string label = bestOutcome.label; + string description = bestOutcome.description.Formatted(mother.Named("MOTHER")); + + ChoiceLetter_BabyBirth choiceLetter_BabyBirth = (ChoiceLetter_BabyBirth)LetterMaker.MakeLetter( + label, description, LetterDefOf.BabyBirth, baby + ); + choiceLetter_BabyBirth.Start(); + Find.LetterStack.ReceiveLetter(choiceLetter_BabyBirth); + } + PostBirth(mother, father, baby); } @@ -416,9 +435,13 @@ namespace RJW_Menstruation baby.genes.xenotypeName = mother.genes.xenotypeName; baby.genes.iconDef = mother.genes.iconDef; } - XenotypeDef xenoTypeDef = BabyXenoTypeDecider(mother, father, out bool hybridBaby); - if (xenoTypeDef != null) baby.genes.SetXenotypeDirect(xenoTypeDef); - if (hybridBaby) + + object[] args = new object[] { mother, father, null }; + if ((bool)TryGetInheritedXenotype.Invoke(null, args)) + { + baby.genes.SetXenotypeDirect((XenotypeDef)args[2]); + } + else if((bool)ShouldByHybrid.Invoke(null, new object[] { mother, father })) { baby.genes.hybrid = true; baby.genes.xenotypeName = "Hybrid".Translate(); @@ -623,39 +646,6 @@ namespace RJW_Menstruation } - public XenotypeDef BabyXenoTypeDecider(Pawn mother, Pawn father, out bool hybrid) - { - hybrid = false; - bool hybridMother = mother?.genes?.hybrid ?? false; - bool hybridFather = father?.genes?.hybrid ?? false; - if (hybridMother && hybridFather) - { - hybrid = true; - return null; - } - XenotypeDef motherInheritableXenotype = mother?.genes?.Xenotype; - XenotypeDef fatherInheritableXenotype = father?.genes?.Xenotype; - if (!(motherInheritableXenotype?.inheritable ?? false)) motherInheritableXenotype = null; - if (!(fatherInheritableXenotype?.inheritable ?? false)) fatherInheritableXenotype = null; - - // If they're the same (or both null) - if (motherInheritableXenotype == fatherInheritableXenotype) - { - // Both null, but one's a hybrid - if (motherInheritableXenotype == null && (hybridMother || hybridFather)) - hybrid = true; - - return motherInheritableXenotype; - } - - // If one is null and the other isn't - if ((motherInheritableXenotype == null) != (fatherInheritableXenotype == null)) return motherInheritableXenotype ?? fatherInheritableXenotype; - - // So two different inheritable ones - hybrid = true; - return null; - } - public PawnKindDef GetHybrid(Pawn first, Pawn second) { PawnKindDef res = null; diff --git a/1.4/source/RJW_Menstruation/RJW_Menstruation/Patch/Biotech_Patch.cs b/1.4/source/RJW_Menstruation/RJW_Menstruation/Patch/Biotech_Patch.cs index a6d34e2..4de0dfa 100644 --- a/1.4/source/RJW_Menstruation/RJW_Menstruation/Patch/Biotech_Patch.cs +++ b/1.4/source/RJW_Menstruation/RJW_Menstruation/Patch/Biotech_Patch.cs @@ -132,7 +132,7 @@ namespace RJW_Menstruation if (pregnancy == null) continue; if (Earliest_Pregnancy == null || Earliest_Pregnancy.Severity > pregnancy.Severity) Earliest_Pregnancy = pregnancy; } - + return Earliest_Pregnancy; } @@ -176,4 +176,5 @@ namespace RJW_Menstruation return TryTerminatePregnancy_Patch.Transpiler(instructions); } } + } \ No newline at end of file diff --git a/1.4/source/RJW_Menstruation/RJW_Menstruation/Patch/Pawn_Patch.cs b/1.4/source/RJW_Menstruation/RJW_Menstruation/Patch/Pawn_Patch.cs index 97cb9c4..616f591 100644 --- a/1.4/source/RJW_Menstruation/RJW_Menstruation/Patch/Pawn_Patch.cs +++ b/1.4/source/RJW_Menstruation/RJW_Menstruation/Patch/Pawn_Patch.cs @@ -1,6 +1,8 @@ using HarmonyLib; using RimWorld; +using System; using System.Collections.Generic; +using System.Linq; using UnityEngine; using Verse; @@ -18,11 +20,7 @@ namespace RJW_Menstruation comp.Initialize(); } - HediffComp_Breast bcomp = __instance.GetBreastComp(); - if (bcomp != null) - { - bcomp.Initialize(); - } + __instance.GetBreastComp()?.Initialize(); } } @@ -111,56 +109,15 @@ namespace RJW_Menstruation } - - //Merged to RJW - //[HarmonyPatch(typeof(PawnColumnWorker_Pregnant), "GetIconFor")] - //public class PawnColumnWorker_Patch_Icon - //{ - // public static void Postfix(Pawn pawn, ref Texture2D __result) - // { - // if (pawn.IsVisiblyPregnant()) __result = ContentFinder.Get("UI/Icons/Animal/Pregnant", true); - // } - // - //} - // - //[HarmonyPatch(typeof(PawnColumnWorker_Pregnant), "GetTooltipText")] - //public class PawnColumnWorker_Patch_Tooltip - //{ - // public static bool Prefix(Pawn pawn, ref string __result) - // { - // float gestationProgress = PregnancyHelper.GetPregnancy(pawn).Severity; - // int num = (int)(pawn.RaceProps.gestationPeriodDays * 60000f); - // int numTicks = (int)(gestationProgress * (float)num); - // __result = "PregnantIconDesc".Translate(numTicks.ToStringTicksToDays("F0"), num.ToStringTicksToDays("F0")); - // return false; - // } - // - //} - // - //[HarmonyPatch(typeof(TransferableUIUtility), "DoExtraAnimalIcons")] - //public class TransferableUIUtility_Patch_Icon - //{ - // //private static readonly Texture2D PregnantIcon = ContentFinder.Get("UI/Icons/Animal/Pregnant", true); - // - // - // - // public static void Postfix(Transferable trad, Rect rect, ref float curX, Texture2D ___PregnantIcon) - // { - // Pawn pawn = trad.AnyThing as Pawn; - // if (pawn?.health?.hediffSet != null && pawn.IsVisiblyPregnant()) - // { - // Rect rect3 = new Rect(curX - 24f, (rect.height - 24f) / 2f, 24f, 24f); - // curX -= 24f; - // if (Mouse.IsOver(rect3)) - // { - // TooltipHandler.TipRegion(rect3, PawnColumnWorker_Pregnant.GetTooltipText(pawn)); - // } - // GUI.DrawTexture(rect3, ___PregnantIcon); - // } - // } - //} - - - - + [HarmonyPatch(typeof(CompBiosculpterPod), nameof(CompBiosculpterPod.CannotUseNowPawnCycleReason), new Type[] { typeof(Pawn), typeof(Pawn), typeof(CompBiosculpterPod_Cycle), typeof(bool) })] + public class CannotUseNowPawnCycleReason_Patch + { + private const string eggRestorationKey = "eggRestoration"; + public static void Postfix(ref string __result, Pawn biosculptee, CompBiosculpterPod_Cycle cycle) + { + if (__result != null) return; + if (cycle.Props.key == eggRestorationKey && !biosculptee.GetMenstruationComps().Any()) + __result = Translations.CannotNoWomb; + } + } } 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 9032766..a32877c 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 @@ -43,7 +43,7 @@ namespace RJW_Menstruation } else if (Genital_Helper.has_ovipositorM(pawn, pawnparts)) { - comp.CumIn(pawn, Rand.Range(0.75f, 4.5f) * pawn.BodySize, partner.SterileGenes() ? 0.0f : 1.0f); + comp.CumIn(pawn, Rand.Range(0.75f, 4.5f) * pawn.BodySize, pawn.SterileGenes() ? 0.0f : 1.0f); } else comp.CumIn(pawn, pawn.GetCumVolume(pawnparts), 0); @@ -70,7 +70,7 @@ namespace RJW_Menstruation /// /// /// Interaction can result in pregnancy - private static bool InteractionCanCausePregnancy(SexProps props) + public static bool InteractionCanCausePregnancy(SexProps props) { InteractionWithExtension interaction = rjw.Modules.Interactions.Helpers.InteractionHelper.GetWithExtension(props.dictionaryKey); @@ -331,6 +331,34 @@ namespace RJW_Menstruation } } + [HarmonyPatch(typeof(JobDriver_Sex), nameof(JobDriver_Sex.SexTick))] + public static class SexTick_Patch + { + private const float fertilePrecummersPercentage = 0.33f; + private const float precumRatio = 0.1f; // Relative to ejaculation volume + private const float precumFertility = 0.5f; + private const float expectedDurationTicks = 2000f * (0.9f - 0.5f) / 2; + + public static void Postfix(JobDriver_Sex __instance, Pawn pawn, Thing target) + { + if (!pawn.IsHashIntervalTick(__instance.ticks_between_thrusts)) return; + xxx.rjwSextype sextype = __instance.Sexprops.sexType; + if (!(target is Pawn partner) || pawn == partner) return; + if (sextype != xxx.rjwSextype.Vaginal && sextype != xxx.rjwSextype.DoublePenetration) return; + if (__instance.Sexprops.usedCondom) return; + if (!Impregnate_Patch.InteractionCanCausePregnancy(__instance.Sexprops)) return; + + // Archotech penises have more control. Or something. + CompHediffBodyPart penisComp = pawn.GetGenitalsList()?.Find(genital => (genital as Hediff_PartBaseNatural)?.def.defName.ToLower().Contains("penis") ?? false)?.TryGetComp(); + if (penisComp == null || Rand.ChanceSeeded(1.0f - fertilePrecummersPercentage, Gen.HashOffset(penisComp.parent.loadID))) return; + HediffComp_Menstruation vaginaComp = partner.GetRandomMenstruationComp(); + if (vaginaComp == null) return; + + float precumAmount = pawn.GetCumVolume(penisComp) * precumRatio * __instance.ticks_between_thrusts / expectedDurationTicks; + vaginaComp.CumIn(pawn, precumAmount, pawn.SterileGenes() ? 0.0f : precumFertility * pawn.health.capacities.GetLevel(xxx.reproduction), true); + } + } + [HarmonyPatch(typeof(CompHediffBodyPart), nameof(CompHediffBodyPart.updatesize))] public static class Updatesize_Patch { diff --git a/1.4/source/RJW_Menstruation/RJW_Menstruation/RJW_Menstruation.csproj b/1.4/source/RJW_Menstruation/RJW_Menstruation/RJW_Menstruation.csproj index 2de20d8..1a35c67 100644 --- a/1.4/source/RJW_Menstruation/RJW_Menstruation/RJW_Menstruation.csproj +++ b/1.4/source/RJW_Menstruation/RJW_Menstruation/RJW_Menstruation.csproj @@ -61,12 +61,14 @@ + + diff --git a/1.4/source/RJW_Menstruation/RJW_Menstruation/Translations.cs b/1.4/source/RJW_Menstruation/RJW_Menstruation/Translations.cs index 426991d..8534281 100644 --- a/1.4/source/RJW_Menstruation/RJW_Menstruation/Translations.cs +++ b/1.4/source/RJW_Menstruation/RJW_Menstruation/Translations.cs @@ -139,6 +139,9 @@ namespace RJW_Menstruation public static readonly string CannotNoEggs = "CannotNoEggs".Translate(); + public static readonly string CannotNoWomb = "CannotNoWomb".Translate(); + public static string EggRestorationCompleted(NamedArgument name) { return "EggRestorationCompleted".Translate(name); } + public static readonly string CustomHybrid_List_Title = "CustomHybrid_List_Title".Translate(); static public string CustomHybrid_Title(string label) { return TranslatorFormattedStringExtensions.Translate("CustomHybrid_Title", label); } static public string CustomHybrid_Tooltip(string label, string breedee, string baby, string chance) { return TranslatorFormattedStringExtensions.Translate("CustomHybrid_Tooltip", label, breedee, baby, chance); } diff --git a/About/Manifest.xml b/About/Manifest.xml index 72e60db..13e6eed 100644 --- a/About/Manifest.xml +++ b/About/Manifest.xml @@ -1,7 +1,7 @@ RJW Menstruation - 1.0.8.4 + 1.0.8.5 diff --git a/changelogs.txt b/changelogs.txt index ddd0654..c14cc2c 100644 --- a/changelogs.txt +++ b/changelogs.txt @@ -1,3 +1,13 @@ +Version 1.0.8.5 + - Added biosculpter recipe to restore 1 year's worth of eggs, with icon by DestinyPlayer. + - Vaginal sex with the "avoid pregnancy" relation will (usually) pull out prevent cum from entering the womb if there's a risk of pregnancy. + - Impregnation fetishists are significantly less likely to pull out. + - Some males will release small amounts of semen into a womb during vaginal sex before their actual ejaculation. + - Babies born from multiple pregnancy will properly produce the prompt to name them. + - Hopefully improve compatibility with xenotype inhertiance-altering mods for multiple pregnancy. + - Pawns that have stopped aging will no longer have larger breasts during and after pregnancy. + - Experimental "periodic ovulator" cycle type added, currently not used. See Patches/Hediffs_Private_Parts_Animal.xml for an example. + Version 1.0.8.4 - Fix Biotech xenotype inheritance for single-child pregnancies. - Fix error in Traditional Chinese translation.