diff --git a/1.4/Assemblies/RJW_Menstruation.dll b/1.4/Assemblies/RJW_Menstruation.dll
index bc84c90..4d7a51b 100644
Binary files a/1.4/Assemblies/RJW_Menstruation.dll and b/1.4/Assemblies/RJW_Menstruation.dll differ
diff --git a/1.4/Patches/Biosculpter_Patch.xml b/1.4/Patches/Biosculpter_Patch.xml
index cdee9e3..558f4b7 100644
--- a/1.4/Patches/Biosculpter_Patch.xml
+++ b/1.4/Patches/Biosculpter_Patch.xml
@@ -11,7 +11,7 @@
eggRestoration
Restore one year worth of eggs in each womb.
- Eggs/Egg
+ UI/Icon/EggRegeneration
4
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/CompBiosclupterPod_EggRestorationCycle.cs b/1.4/source/RJW_Menstruation/RJW_Menstruation/CompBiosculpterPod_EggRestorationCycle.cs
similarity index 100%
rename from 1.4/source/RJW_Menstruation/RJW_Menstruation/CompBiosclupterPod_EggRestorationCycle.cs
rename to 1.4/source/RJW_Menstruation/RJW_Menstruation/CompBiosculpterPod_EggRestorationCycle.cs
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 78638ee..32219a6 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
@@ -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;
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..0026e89
--- /dev/null
+++ b/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_PeriodicOvulator.cs
@@ -0,0 +1,103 @@
+using RimWorld;
+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.health.RemoveHediff(hediff);
+ estrusflag = false;
+ GoNextStage(Stage.Anestrus);
+ return;
+ }
+ else
+ {
+ if (curStageHrs < currentIntervalHours / 4) for (int i = 0; i < Configurations.CycleAcceleration; i++) BleedOut();
+ curStageHrs += Configurations.CycleAcceleration;
+ StayCurrentStage();
+ }
+ }
+
+ 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/Patch/Biotech_Patch.cs b/1.4/source/RJW_Menstruation/RJW_Menstruation/Patch/Biotech_Patch.cs
index 708596c..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
@@ -1,5 +1,4 @@
using HarmonyLib;
-using System;
using System.Linq;
using RimWorld;
using Verse;
@@ -178,15 +177,4 @@ namespace RJW_Menstruation
}
}
- [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;
- }
- }
}
\ 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 1a8b5e3..5dd5cdc 100644
--- a/1.4/source/RJW_Menstruation/RJW_Menstruation/RJW_Menstruation.csproj
+++ b/1.4/source/RJW_Menstruation/RJW_Menstruation/RJW_Menstruation.csproj
@@ -68,6 +68,7 @@
+
diff --git a/changelogs.txt b/changelogs.txt
index e67ba4d..146583c 100644
--- a/changelogs.txt
+++ b/changelogs.txt
@@ -1,6 +1,10 @@
Version 1.0.8.5
- - Added biosculptor recipe to restore 1 year's worth of eggs.
+ - 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.
+ - Experimental "periodic ovulator" cycle type, currently not used. See Patches/Hediffs_Private_Parts_Animal.xml.
Version 1.0.8.4
- Fix Biotech xenotype inheritance for single-child pregnancies.