Merge branch 'dev'

This commit is contained in:
lutepickle 2022-09-05 10:41:10 -07:00
commit 4b558ed1e7
26 changed files with 273 additions and 203 deletions

Binary file not shown.

View file

@ -174,7 +174,7 @@
<HediffDef Name="Hediff_Estrus"> <HediffDef Name="Hediff_Estrus">
<hediffClass>HediffWithComps</hediffClass> <hediffClass>RJW_Menstruation.Hediff_Estrus</hediffClass>
<defName>Hediff_Estrus</defName> <defName>Hediff_Estrus</defName>
<label>Estrus</label> <label>Estrus</label>
<labelNoun>estrus</labelNoun> <labelNoun>estrus</labelNoun>
@ -197,15 +197,10 @@
</statFactors> </statFactors>
</li> </li>
</stages> </stages>
<comps>
<li Class="HediffCompProperties_SeverityPerDay">
<severityPerDay>-1.0</severityPerDay>
</li>
</comps>
</HediffDef> </HediffDef>
<HediffDef Name="Hediff_Estrus_Concealed"> <HediffDef Name="Hediff_Estrus_Concealed">
<hediffClass>HediffWithComps</hediffClass> <hediffClass>RJW_Menstruation.Hediff_Estrus</hediffClass>
<defName>Hediff_Estrus_Concealed</defName> <defName>Hediff_Estrus_Concealed</defName>
<label>Estrus (concealed)</label> <label>Estrus (concealed)</label>
<labelNoun>estrus</labelNoun> <labelNoun>estrus</labelNoun>
@ -229,11 +224,6 @@
</statFactors> </statFactors>
</li> </li>
</stages> </stages>
<comps>
<li Class="HediffCompProperties_SeverityPerDay">
<severityPerDay>-1.0</severityPerDay>
</li>
</comps>
</HediffDef> </HediffDef>

View file

@ -78,20 +78,6 @@
</value> </value>
</Operation> </Operation>
<Operation Class="PatchOperationAddModExtension">
<xpath>/Defs/ThingDef[defName="YorkshireTerrier"]</xpath>
<value>
<li Class="RJW_Menstruation.PawnDNAModExtension">
<fetusTexPath>Fetus/Canines/Fetus_Dog</fetusTexPath>
<cumColor>(255,255,255,255)</cumColor>
<cumThickness>0.05</cumThickness>
<hybridExtension>
</hybridExtension>
</li>
</value>
</Operation>
<Operation Class="PatchOperationAddModExtension"> <Operation Class="PatchOperationAddModExtension">
<xpath>/Defs/ThingDef[defName="Husky"]</xpath> <xpath>/Defs/ThingDef[defName="Husky"]</xpath>
<value> <value>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 6 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View file

@ -30,10 +30,7 @@ namespace MilkModule
protected override void PostTickAction() protected override void PostTickAction()
{ {
if (breastcomp != null) breastcomp?.AdjustNippleProgress(Rand.Range(0.0f, 0.000005f) * Configurations.MaxNippleIncrementFactor);
{
breastcomp.AdjustNippleProgress(Rand.Range(0.0f, 0.01f));
}
} }

View file

@ -15,18 +15,13 @@ namespace MilkModule
} }
} }
[HarmonyPatch(typeof(HumanCompHasGatherableBodyResource), "Gathered")] [HarmonyPatch(typeof(HumanCompHasGatherableBodyResource), nameof(HumanCompHasGatherableBodyResource.Gathered))]
public static class Milk_Patch public static class Milk_Patch
{ {
public static void Postfix(HumanCompHasGatherableBodyResource __instance) public static void Postfix(HumanCompHasGatherableBodyResource __instance)
{ {
HediffComp_Breast comp = null; if (__instance.parent is Pawn pawn)
if (__instance.parent is Pawn pawn) comp = pawn.GetBreastComp(); pawn.GetBreastComp()?.AdjustNippleProgress(Rand.Range(0.0f, 0.005f) * Configurations.MaxNippleIncrementFactor);
if (comp != null)
{
comp.AdjustNippleProgress(Rand.Range(0.0f, 0.01f));
}
} }
} }

View file

@ -148,14 +148,10 @@ namespace RJW_Menstruation
if (o.IsNull) removeList.Add(o); if (o.IsNull) removeList.Add(o);
if (o.DefName == def.defName) return true; if (o.DefName == def.defName) return true;
} }
if (!removeList.NullOrEmpty())
{
foreach (HybridInformations o in removeList) foreach (HybridInformations o in removeList)
{ {
HybridOverride.Remove(o); HybridOverride.Remove(o);
} }
}
removeList.Clear();
return false; return false;
} }

View file

@ -222,14 +222,10 @@ namespace RJW_Menstruation
public void CumEffects(Pawn pawn) public void CumEffects(Pawn pawn)
{ {
if (notcum || DNA == null || volume < 1.0f) return; if (notcum || DNA?.ingestionOutcomeDoers == null || volume < 1.0f) return;
List<IngestionOutcomeDoer> doers = DNA.ingestionOutcomeDoers; foreach (IngestionOutcomeDoer doer in DNA.ingestionOutcomeDoers)
doer.DoIngestionOutcome(pawn, CumThing);
if (!doers.NullOrEmpty()) for (int i = 0; i < doers.Count; i++)
{
doers[i].DoIngestionOutcome(pawn, CumThing);
}
} }
protected void CutMinor() protected void CutMinor()
@ -238,7 +234,7 @@ namespace RJW_Menstruation
} }
} }
public class CumMixture : Cum, IDisposable public class CumMixture : Cum
{ {
protected List<string> cums; protected List<string> cums;
public bool ispurecum = true; public bool ispurecum = true;
@ -260,12 +256,6 @@ namespace RJW_Menstruation
ispurecum = pure; ispurecum = pure;
} }
public void Dispose()
{
cums.Clear();
}
public override void ExposeData() public override void ExposeData()
{ {
base.ExposeData(); base.ExposeData();

View file

@ -1,4 +1,5 @@
using RimWorld; using RimWorld;
using RimWorld.Planet;
using rjw; using rjw;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -158,6 +159,14 @@ namespace RJW_Menstruation
Scribe_Values.Look(ref baseNipple, "baseNipple", baseNipple, true); Scribe_Values.Look(ref baseNipple, "baseNipple", baseNipple, true);
} }
public bool ShouldSimulate()
{
if (!Configurations.EnableAnimalCycle && Pawn.IsAnimal()) return false;
if (Pawn.Spawned || Pawn.IsCaravanMember() || PawnUtility.IsTravelingInTransportPodWorldObject(Pawn)) return true;
return false;
}
public override void CompPostTick(ref float severityAdjustment) public override void CompPostTick(ref float severityAdjustment)
{ {
base.CompPostTick(ref severityAdjustment); base.CompPostTick(ref severityAdjustment);
@ -166,7 +175,7 @@ namespace RJW_Menstruation
{ {
if ( if (
!Pawn.IsHashIntervalTick(tickInterval) || !Pawn.IsHashIntervalTick(tickInterval) ||
!Pawn.Spawned // TODO: Add option to simulate off-map pawns !ShouldSimulate()
) )
{ {
return; return;

View file

@ -1,4 +1,5 @@
using RimWorld; using RimWorld;
using UnityEngine;
using Verse; using Verse;
namespace RJW_Menstruation namespace RJW_Menstruation
@ -30,27 +31,21 @@ namespace RJW_Menstruation
} }
} }
// The maximum theoretical rate of ovulation is inducing the moment it goes follicular and no pregnancies // In an induced ovulator, about 65% of eggs ovulated will become pregnancies, so the expected lifetime supply
// There will be far more eggs than will ever actually be produced, but it fits the induced ovulator philosophy // is the number of pregnancies they could have, with some extra to be sure.
// An IUD or a poor fertility mate could run this down quicker. Oh well.
protected override float RaceCyclesPerYear() protected override float RaceCyclesPerYear()
{ {
int breedingSeasons = 0; // Don't bother with breeding season, since so much time is planned to be spent pregnant.
if (Props.breedingSeason == SeasonalBreed.Always) breedingSeasons = 4; float pregnanciesPerYear = GenDate.DaysPerYear / Mathf.Max(1, Pawn.def.race.gestationPeriodDays);
else
{ return 2 * pregnanciesPerYear / Configurations.ImplantationChanceDefault;
if ((Props.breedingSeason & SeasonalBreed.Spring) != 0) breedingSeasons++;
if ((Props.breedingSeason & SeasonalBreed.Summer) != 0) breedingSeasons++;
if ((Props.breedingSeason & SeasonalBreed.Fall) != 0) breedingSeasons++;
if ((Props.breedingSeason & SeasonalBreed.Winter) != 0) breedingSeasons++;
}
float breedingRatio = breedingSeasons / 4.0f;
return breedingRatio * GenDate.DaysPerYear / ((float)(Props.lutealIntervalDays + Props.bleedingIntervalDays) / Configurations.CycleAccelerationDefault);
} }
// There's really no good way to estimate the number of times it's been induced, so this is all we can do // There's really no good way to estimate the number of times it's been induced, so this is all we can do
protected override int PawnEggsUsed(float pawnCyclesElapsed, float avglittersize) protected override int PawnEggsUsed(float pawnCyclesElapsed, float avglittersize)
{ {
return 0; return Mathf.CeilToInt((Pawn.relations?.ChildrenCount ?? 0) / Configurations.ImplantationChanceDefault);
} }
protected override void GoOvulatoryStage(bool climacteric) protected override void GoOvulatoryStage(bool climacteric)
@ -92,14 +87,21 @@ namespace RJW_Menstruation
} }
} }
protected override bool ShouldBeInEstrus()
{
switch (curStage)
{
case Stage.Follicular:
case Stage.ClimactericFollicular:
return curStageHrs > currentIntervalHours - Props.estrusDaysBeforeOvulation * 24;
case Stage.Ovulatory:
return true;
case Stage.Luteal:
case Stage.ClimactericLuteal:
return IsEggExist && curStageHrs < Props.eggLifespanDays * 24;
default:
return false;
}
}
} }
} }

View file

@ -91,6 +91,12 @@ namespace RJW_Menstruation
Anestrus Anestrus
} }
public enum EstrusLevel
{
None,
Concealed,
Visible
}
public static readonly Dictionary<Stage, Texture2D> StageTexture = new Dictionary<Stage, Texture2D>() public static readonly Dictionary<Stage, Texture2D> StageTexture = new Dictionary<Stage, Texture2D>()
{ {
@ -115,7 +121,6 @@ namespace RJW_Menstruation
protected string customvagtex = null; protected string customvagtex = null;
protected bool estrusflag = false; protected bool estrusflag = false;
protected int opcache = -1; protected int opcache = -1;
protected HediffComp_Breast breastcache = null;
protected float antisperm = 0.0f; protected float antisperm = 0.0f;
protected float? originvagsize = null; protected float? originvagsize = null;
protected Hediff_BasePregnancy pregnancy = null; protected Hediff_BasePregnancy pregnancy = null;
@ -139,7 +144,6 @@ namespace RJW_Menstruation
get get
{ {
if (opcache >= 0) return opcache; if (opcache >= 0) return opcache;
// Climacteric will set in 6 (human) years before egg exhaustion
float avglittersize; float avglittersize;
try try
{ {
@ -150,9 +154,10 @@ namespace RJW_Menstruation
// Any exceptions in that will have been reported elsewhere in the code by now // Any exceptions in that will have been reported elsewhere in the code by now
avglittersize = 1.0f; avglittersize = 1.0f;
}; };
const float yearsBeforeMenopause = 6.0f;
opcache = (int)(RaceCyclesPerYear() * opcache = (int)(RaceCyclesPerYear() *
avglittersize * avglittersize *
6f * yearsBeforeMenopause *
(Pawn.def.race.lifeExpectancy / ThingDefOf.Human.race.lifeExpectancy)); (Pawn.def.race.lifeExpectancy / ThingDefOf.Human.race.lifeExpectancy));
return opcache; return opcache;
} }
@ -162,24 +167,21 @@ namespace RJW_Menstruation
{ {
get get
{ {
if (cums.NullOrEmpty()) return 0; return cums?.Sum(cum => cum.Volume) ?? 0;
return cums.Sum(cum => cum.Volume);
} }
} }
public float TotalFertCum public float TotalFertCum
{ {
get get
{ {
if (cums.NullOrEmpty()) return 0; return cums?.Sum(cum => cum.FertVolume) ?? 0;
return cums.Sum(cum => cum.FertVolume);
} }
} }
public float TotalCumPercent public float TotalCumPercent
{ {
get get
{ {
if (cums.NullOrEmpty()) return 0; return cums?.Sum(cum => cum.Volume) / Props.maxCumCapacity ?? 0;
return cums.Sum(cum => cum.Volume) / Props.maxCumCapacity;
} }
} }
public float CumCapacity public float CumCapacity
@ -333,8 +335,7 @@ namespace RJW_Menstruation
{ {
get get
{ {
if (customwombtex == null) return Props.wombTex; return customwombtex ?? Props.wombTex;
else return customwombtex;
} }
set set
{ {
@ -345,8 +346,7 @@ namespace RJW_Menstruation
{ {
get get
{ {
if (customvagtex == null) return Props.vagTex; return customvagtex ?? Props.vagTex;
else return customvagtex;
} }
set set
{ {
@ -382,8 +382,8 @@ namespace RJW_Menstruation
{ {
get get
{ {
if (eggs.NullOrEmpty() || cums.NullOrEmpty()) return false; if (eggs.NullOrEmpty()) return false;
return cums.Any(cum => cum.FertVolume > 0); return cums?.Any(cum => cum.FertVolume > 0) ?? false;
} }
} }
/// <summary> /// <summary>
@ -393,7 +393,7 @@ namespace RJW_Menstruation
{ {
get get
{ {
if (eggs.NullOrEmpty()) return -1; if (eggs?.All(egg => !egg.fertilized) ?? true) return -1;
return eggs.Max(egg => egg.fertstage); return eggs.Max(egg => egg.fertstage);
} }
} }
@ -434,12 +434,11 @@ namespace RJW_Menstruation
} }
} }
} }
public int GetNumofEggs public int GetNumOfEggs
{ {
get get
{ {
if (eggs.NullOrEmpty()) return 0; return eggs?.Count ?? 0;
else return eggs.Count;
} }
} }
public Color BloodColor public Color BloodColor
@ -458,17 +457,6 @@ namespace RJW_Menstruation
} }
} }
public HediffComp_Breast Breast
{
get
{
if (breastcache == null)
{
breastcache = Pawn.GetBreastComp();
}
return breastcache;
}
}
public float OriginVagSize public float OriginVagSize
{ {
@ -549,6 +537,14 @@ namespace RJW_Menstruation
} }
} }
public bool ShouldSimulate()
{
if (!Configurations.EnableAnimalCycle && Pawn.IsAnimal()) return false;
if (Pawn.Spawned || Pawn.IsCaravanMember() || PawnUtility.IsTravelingInTransportPodWorldObject(Pawn)) return true;
return false;
}
public override void CompPostTick(ref float severityAdjustment) public override void CompPostTick(ref float severityAdjustment)
{ {
base.CompPostTick(ref severityAdjustment); base.CompPostTick(ref severityAdjustment);
@ -557,8 +553,7 @@ namespace RJW_Menstruation
{ {
if ( if (
!Pawn.IsHashIntervalTick(tickInterval) || !Pawn.IsHashIntervalTick(tickInterval) ||
!Pawn.Spawned || // TODO: Add option to simulate off-map pawns !ShouldSimulate()
(Pawn.IsAnimal() && !Configurations.EnableAnimalCycle)
) )
{ {
return; return;
@ -656,8 +651,7 @@ namespace RJW_Menstruation
/// <returns></returns> /// <returns></returns>
public Cum GetCum(Pawn pawn) public Cum GetCum(Pawn pawn)
{ {
if (cums.NullOrEmpty()) return null; return cums?.Find(cum => !cum.notcum && cum.pawn == pawn);
return cums.Find(cum => !cum.notcum && cum.pawn == pawn);
} }
/// <summary> /// <summary>
@ -845,7 +839,6 @@ namespace RJW_Menstruation
{ {
cums.Remove(cum); cums.Remove(cum);
} }
removecums.Clear();
cumd = TotalCumPercent - cumd; cumd = TotalCumPercent - cumd;
if (totalleak >= 1.0f) AfterCumOut(); if (totalleak >= 1.0f) AfterCumOut();
AfterFluidOut(cumd); AfterFluidOut(cumd);
@ -882,7 +875,6 @@ namespace RJW_Menstruation
{ {
cums.Remove(cum); cums.Remove(cum);
} }
removecums.Clear();
cumd = TotalCumPercent - cumd; cumd = TotalCumPercent - cumd;
AfterFluidOut(cumd); AfterFluidOut(cumd);
return outcum; return outcum;
@ -915,7 +907,6 @@ namespace RJW_Menstruation
{ {
cums.Remove(cum); cums.Remove(cum);
} }
removecums.Clear();
return new CumMixture(Pawn, totalleak, cumlabels, color, mixtureDef, pure); return new CumMixture(Pawn, totalleak, cumlabels, color, mixtureDef, pure);
} }
@ -978,21 +969,7 @@ namespace RJW_Menstruation
InitOvary(); InitOvary();
TakeLoosePregnancy();
if (pregnancy == null)
{
// If this womb isn't marked pregnant, search for pregnancies that have no womb and claim one
foreach (Hediff_BasePregnancy preg in Pawn.health.hediffSet.GetHediffs<Hediff_BasePregnancy>())
{
if (preg.GetMenstruationComp() == null)
{
currentIntervalHours = (int)preg.GestationHours();
curStage = Stage.Pregnant;
pregnancy = preg;
break;
}
}
}
//Log.Message(Pawn.Label + " - Initialized menstruation comp"); //Log.Message(Pawn.Label + " - Initialized menstruation comp");
loaded = true; loaded = true;
@ -1092,32 +1069,34 @@ namespace RJW_Menstruation
} }
} }
protected virtual bool ShouldBeInEstrus()
{
switch (curStage)
{
case Stage.Follicular:
case Stage.ClimactericFollicular:
return curStageHrs > currentIntervalHours - Props.estrusDaysBeforeOvulation * 24;
case Stage.Ovulatory:
return true;
case Stage.Luteal:
case Stage.ClimactericLuteal:
return curStageHrs < Props.eggLifespanDays * 24;
default:
return false;
}
}
public EstrusLevel GetEstrusLevel()
{
if (!ShouldBeInEstrus()) return EstrusLevel.None;
else return Props.concealedEstrus ? EstrusLevel.Concealed : EstrusLevel.Visible;
}
public void SetEstrus(int days) public void SetEstrus(int days)
{ {
HediffDef estrusdef = Props.concealedEstrus ? VariousDefOf.Hediff_Estrus_Concealed : VariousDefOf.Hediff_Estrus; Hediff hediff = HediffMaker.MakeHediff(Props.concealedEstrus ? VariousDefOf.Hediff_Estrus_Concealed : VariousDefOf.Hediff_Estrus, Pawn);
Hediff hediff = Pawn.health.hediffSet.GetFirstHediffOfDef(estrusdef);
if (Props.concealedEstrus)
{
if (Pawn.health.hediffSet.HasHediff(VariousDefOf.Hediff_Estrus)) return;
}
else
{
Hediff concealedHediff = Pawn.health.hediffSet.GetFirstHediffOfDef(VariousDefOf.Hediff_Estrus_Concealed);
if (concealedHediff != null) Pawn.health.RemoveHediff(concealedHediff);
}
if (hediff != null)
{
hediff.Severity = (float)days / Configurations.CycleAcceleration + 0.2f;
}
else
{
hediff = HediffMaker.MakeHediff(estrusdef, Pawn);
hediff.Severity = (float)days / Configurations.CycleAcceleration + 0.2f;
Pawn.health.AddHediff(hediff); Pawn.health.AddHediff(hediff);
} }
}
public bool IsBreedingSeason() public bool IsBreedingSeason()
{ {
@ -1194,14 +1173,14 @@ namespace RJW_Menstruation
} }
else if (Rand.Range(0.0f, 1.0f) <= Configurations.ImplantationChance * ImplantFactor * InterspeciesImplantFactor(egg.fertilizer)) else if (Rand.Range(0.0f, 1.0f) <= Configurations.ImplantationChance * ImplantFactor * InterspeciesImplantFactor(egg.fertilizer))
{ {
if (Configurations.Debug) Log.Message($"Implanting fertilized egg of {Pawn}, father {egg.fertilizer}"); if (Configurations.Debug) Log.Message($"Implanting fertilized egg of {Pawn} into {parent}, father {egg.fertilizer}");
if (pregnancy != null) if (pregnancy != null)
{ {
if (Configurations.UseMultiplePregnancy && Configurations.EnableHeteroOvularTwins) if (Configurations.UseMultiplePregnancy && Configurations.EnableHeteroOvularTwins)
{ {
if (pregnancy is Hediff_MultiplePregnancy h) if (pregnancy is Hediff_MultiplePregnancy h)
{ {
if (Configurations.Debug) Log.Message($"Adding to existing pregnancy"); if (Configurations.Debug) Log.Message($"Adding to existing pregnancy {h}");
h.AddNewBaby(Pawn, egg.fertilizer); h.AddNewBaby(Pawn, egg.fertilizer);
} }
pregnant = true; pregnant = true;
@ -1244,16 +1223,14 @@ namespace RJW_Menstruation
if (pregnant && (!Configurations.UseMultiplePregnancy || !Configurations.EnableHeteroOvularTwins)) if (pregnant && (!Configurations.UseMultiplePregnancy || !Configurations.EnableHeteroOvularTwins))
{ {
eggs.Clear(); eggs.Clear();
deadeggs.Clear();
return true; return true;
} }
else if (!deadeggs.NullOrEmpty()) else
{ {
foreach (Egg egg in deadeggs) foreach (Egg egg in deadeggs)
{ {
eggs.Remove(egg); eggs.Remove(egg);
} }
deadeggs.Clear();
} }
return pregnant; return pregnant;
} }
@ -1335,14 +1312,10 @@ namespace RJW_Menstruation
if (egg.lifespanhrs < 0) deadeggs.Add(egg); if (egg.lifespanhrs < 0) deadeggs.Add(egg);
} }
} }
if (!deadeggs.NullOrEmpty())
{
foreach (Egg egg in deadeggs) foreach (Egg egg in deadeggs)
{ {
eggs.Remove(egg); eggs.Remove(egg);
} }
deadeggs.Clear();
}
} }
protected void AddCrampPain() protected void AddCrampPain()
@ -1525,7 +1498,7 @@ namespace RJW_Menstruation
Implant(); Implant();
} }
if (Pawn.health.hediffSet.hediffs.Contains(pregnancy)) if (pregnancy != null && Pawn.health.hediffSet.hediffs.Contains(pregnancy))
{ {
curStageHrs += 1; curStageHrs += 1;
StayCurrentStageConst(Stage.Pregnant); StayCurrentStageConst(Stage.Pregnant);
@ -1541,14 +1514,18 @@ namespace RJW_Menstruation
{ {
if (curStageHrs >= currentIntervalHours) if (curStageHrs >= currentIntervalHours)
{ {
if (Configurations.EnableMenopause && ovarypower < OvaryPowerThreshold) if (Pawn.health.capacities.GetLevel(xxx.reproduction) == 0)
{
GoNextStage(Stage.ClimactericFollicular);
}
else if (Pawn.health.capacities.GetLevel(xxx.reproduction) == 0)
{ {
GoNextStage(Stage.Young); GoNextStage(Stage.Young);
} }
else if (!IsBreedingSeason())
{
GoNextStage(Stage.Anestrus);
}
else if (Configurations.EnableMenopause && ovarypower < OvaryPowerThreshold)
{
GoNextStage(Stage.ClimactericFollicular);
}
else else
{ {
GoNextStage(Stage.Follicular); GoNextStage(Stage.Follicular);
@ -1716,7 +1693,7 @@ namespace RJW_Menstruation
case Stage.ClimactericBleeding: case Stage.ClimactericBleeding:
return (int)(Props.bleedingIntervalDays * 24 * (1 + Rand.Range(-cycleVariability, cycleVariability) * 0.5f * variabilityFactor) / (1 + (cycleSpeed - 1) * 0.5f)); return (int)(Props.bleedingIntervalDays * 24 * (1 + Rand.Range(-cycleVariability, cycleVariability) * 0.5f * variabilityFactor) / (1 + (cycleSpeed - 1) * 0.5f));
case Stage.Recover: case Stage.Recover:
return (int)(Props.recoveryIntervalDays * 24 * Rand.Range(-0.05f, 0.05f)); return (int)(Props.recoveryIntervalDays * 24 * Rand.Range(0.95f, 1.05f));
case Stage.Pregnant: case Stage.Pregnant:
return (int)MenstruationUtility.GestationHours(pregnancy); return (int)MenstruationUtility.GestationHours(pregnancy);
default: // Often unused default: // Often unused
@ -1766,6 +1743,19 @@ namespace RJW_Menstruation
return stage; return stage;
} }
// Searches for an RJW pregnancy unclaimed by any womb and put it in this one
public void TakeLoosePregnancy()
{
if (pregnancy != null) return;
pregnancy =
Pawn.health.hediffSet.GetHediffs<Hediff_BasePregnancy>().Except(
Pawn.GetMenstruationComps().Select(comp => comp.pregnancy).Where(preg => preg != null)
).FirstOrDefault();
if (pregnancy != null)
GoNextStage(Stage.Pregnant);
}
public void CopyCycleProperties(HediffComp_Menstruation original) public void CopyCycleProperties(HediffComp_Menstruation original)
{ {
cycleSpeed = original.cycleSpeed; cycleSpeed = original.cycleSpeed;

View file

@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using UnityEngine; using UnityEngine;
using Verse; using Verse;
using Verse.AI;
namespace RJW_Menstruation namespace RJW_Menstruation
{ {
@ -45,7 +46,7 @@ namespace RJW_Menstruation
public static HediffComp_Menstruation GetMenstruationComp(this Hediff vagina) public static HediffComp_Menstruation GetMenstruationComp(this Hediff vagina)
{ {
if (vagina is Hediff_PartBaseNatural || vagina is Hediff_PartBaseArtifical) if (VariousDefOf.AllVaginas.Contains(vagina?.def))
{ {
return vagina.TryGetComp<HediffComp_Menstruation>(); return vagina.TryGetComp<HediffComp_Menstruation>();
} }
@ -330,5 +331,26 @@ namespace RJW_Menstruation
if (pawn.Dead) return false; if (pawn.Dead) return false;
return pawn.health?.hediffSet?.HasHediff(visible ? VariousDefOf.Hediff_Estrus : VariousDefOf.Hediff_Estrus_Concealed) ?? false; return pawn.health?.hediffSet?.HasHediff(visible ? VariousDefOf.Hediff_Estrus : VariousDefOf.Hediff_Estrus_Concealed) ?? false;
} }
public static HediffComp_Menstruation.EstrusLevel HighestEstrus(this Pawn pawn)
{
HediffComp_Menstruation.EstrusLevel res = HediffComp_Menstruation.EstrusLevel.None;
foreach(HediffComp_Menstruation comp in pawn.GetMenstruationComps())
{
switch (comp.GetEstrusLevel())
{
case HediffComp_Menstruation.EstrusLevel.None:
break;
case HediffComp_Menstruation.EstrusLevel.Concealed:
res = HediffComp_Menstruation.EstrusLevel.Concealed;
break;
case HediffComp_Menstruation.EstrusLevel.Visible:
return HediffComp_Menstruation.EstrusLevel.Visible;
}
}
return res;
}
} }
} }

View file

@ -0,0 +1,77 @@
using RimWorld;
using System.Linq;
using Verse;
namespace RJW_Menstruation
{
public class Hediff_Estrus : HediffWithComps
{
const int checkInterval = GenTicks.TickRareInterval;
private bool shouldRemove = false;
public override bool ShouldRemove
{
get
{
return shouldRemove || base.ShouldRemove;
}
}
protected virtual bool IsVisible
{
get
{
return def == VariousDefOf.Hediff_Estrus;
}
}
public override void PostAdd(DamageInfo? dinfo)
{
base.PostAdd(dinfo);
if (IsVisible)
{
foreach (Hediff concealedEstrus in pawn.health.hediffSet.hediffs.Where(hediff => hediff.def == VariousDefOf.Hediff_Estrus_Concealed))
{
pawn.health.RemoveHediff(concealedEstrus);
}
}
}
public override void PostTick()
{
base.PostTick();
if (!pawn.IsHashIntervalTick(checkInterval)) return;
if (IsVisible)
{
switch (pawn.HighestEstrus())
{
case HediffComp_Menstruation.EstrusLevel.None:
shouldRemove = true;
break;
case HediffComp_Menstruation.EstrusLevel.Concealed:
shouldRemove = true;
pawn.health.AddHediff(VariousDefOf.Hediff_Estrus_Concealed);
break;
case HediffComp_Menstruation.EstrusLevel.Visible:
break;
}
}
else
{
// Adding a visible will remove this one, so we don't have to check it here
if (pawn.HighestEstrus() == HediffComp_Menstruation.EstrusLevel.None) shouldRemove = true;
}
}
public override bool TryMergeWith(Hediff other)
{
if (!(other is Hediff_Estrus otherEstrus)) return false;
if (this.def == otherEstrus.def) return true;
else if (!this.IsVisible && otherEstrus.IsVisible && !otherEstrus.shouldRemove) return true; // A concealed estrus won't be added if there's a visible
// This is a visible estrus overwriting a concealed.
else return false; // Since this is being added while hediffs are being looped through, it's not safe to remove the concealed yet. Do it in PostAdd.
}
}
}

View file

@ -411,7 +411,7 @@ namespace RJW_Menstruation
fixedLastName: lastname, fixedLastName: lastname,
kind: BabyPawnKindDecider(mother, father), kind: BabyPawnKindDecider(mother, father),
//fixedIdeo: mother.Ideo, //fixedIdeo: mother.Ideo,
//forbidAnyTitle: true, forbidAnyTitle: true,
forceNoBackstory: true forceNoBackstory: true
); );
@ -734,9 +734,7 @@ namespace RJW_Menstruation
public override bool TryMergeWith(Hediff other) public override bool TryMergeWith(Hediff other)
{ {
if (other is Hediff_MultiplePregnancy preg) return false;
return this.GetMenstruationComp() == preg.GetMenstruationComp();
else return base.TryMergeWith(other);
} }
} }

View file

@ -48,7 +48,7 @@ namespace RJW_Menstruation
(comp.Pregnancy is Hediff_MultiplePregnancy preg ? "due: " + preg.DueDate() + "\n" : "") + (comp.Pregnancy is Hediff_MultiplePregnancy preg ? "due: " + preg.DueDate() + "\n" : "") +
"fertcums: " + comp.TotalFertCum + "\n" + "fertcums: " + comp.TotalFertCum + "\n" +
"ovarypower: " + comp.ovarypower + "\n" + "ovarypower: " + comp.ovarypower + "\n" +
"eggs: " + comp.GetNumofEggs + "\n"; "eggs: " + comp.GetNumOfEggs + "\n";
} }
else description += comp.GetCurStageLabel + "\n"; else description += comp.GetCurStageLabel + "\n";
if (pawn.IsRJWPregnant()) if (pawn.IsRJWPregnant())
@ -75,14 +75,14 @@ namespace RJW_Menstruation
Hediff hediff = pawn.health.hediffSet.GetHediffs<Hediff_InsectEgg>().FirstOrDefault(); Hediff hediff = pawn.health.hediffSet.GetHediffs<Hediff_InsectEgg>().FirstOrDefault();
if (hediff != null) if (hediff != null)
{ {
icon = ContentFinder<Texture2D>.Get(("Womb/Womb_Egged"), true); icon = MenstruationUtility.GetInsectEggedIcon(comp);
icon_overay = ContentFinder<Texture2D>.Get(("Womb/Empty"), true);
} }
else else
{ {
icon = comp.GetWombIcon(); icon = comp.GetWombIcon();
icon_overay = comp.GetCumIcon();
} }
icon_overay = comp.GetCumIcon();
} }
foreach (string s in comp.GetCumsInfo) description += s + "\n"; foreach (string s in comp.GetCumsInfo) description += s + "\n";

View file

@ -8,6 +8,7 @@ using System.Reflection;
using System.Reflection.Emit; using System.Reflection.Emit;
using UnityEngine; using UnityEngine;
using Verse; using Verse;
using static RJW_Menstruation.HediffComp_Menstruation;
namespace RJW_Menstruation namespace RJW_Menstruation
{ {
@ -15,15 +16,12 @@ namespace RJW_Menstruation
[HarmonyPatch(typeof(PregnancyHelper), nameof(PregnancyHelper.impregnate))] [HarmonyPatch(typeof(PregnancyHelper), nameof(PregnancyHelper.impregnate))]
public static class Impregnate_Patch public static class Impregnate_Patch
{ {
public static bool Prefix(SexProps props, out HediffComp_Menstruation __state) public static bool Prefix(SexProps props)
{ {
xxx.rjwSextype sextype = props.sexType; xxx.rjwSextype sextype = props.sexType;
Pawn pawn = props.pawn; Pawn pawn = props.pawn;
Pawn partner = props.partner; Pawn partner = props.partner;
if (PregnancyHelper.GetPregnancy(partner) is Hediff_BasePregnancy oldestPregnancy) __state = oldestPregnancy.GetMenstruationComp();
else __state = null;
if (sextype != xxx.rjwSextype.Vaginal && sextype != xxx.rjwSextype.DoublePenetration) return true; if (sextype != xxx.rjwSextype.Vaginal && sextype != xxx.rjwSextype.DoublePenetration) return true;
if (partner.IsAnimal() && !Configurations.EnableAnimalCycle) return true; if (partner.IsAnimal() && !Configurations.EnableAnimalCycle) return true;
@ -52,15 +50,18 @@ namespace RJW_Menstruation
return true; return true;
} }
public static void Postfix(SexProps props, HediffComp_Menstruation __state) public static void Postfix(SexProps props)
{ {
if (__state == null || __state.Pregnancy != null) return;
// It was pregnant, but not anymore. This probably means the pregnancy was destroyed by e.g. a mech implant
Pawn pawn = props.partner; Pawn pawn = props.partner;
Hediff_BasePregnancy newestPregnancy = pawn.health.hediffSet.GetHediffs<Hediff_BasePregnancy>().MaxByWithFallback(hediff => hediff.loadID);
if (newestPregnancy == null || pawn.GetMenstruationComps().Any(comp => comp.Pregnancy == newestPregnancy)) return; // One of the wombs did get it if (props.sexType != xxx.rjwSextype.MechImplant && !pawn.health.hediffSet.GetHediffs<Hediff_InsectEgg>().Any()) return;
else __state.Pregnancy = newestPregnancy;
// The existing pregnancies might have been destroyed, so go through see if any new mech pregnancies need to be picked up
foreach (HediffComp_Menstruation comp in pawn.GetMenstruationComps())
{
_ = comp.Pregnancy; // get_Pregnancy will do any removals
comp.TakeLoosePregnancy();
}
} }
/// <summary> /// <summary>

View file

@ -65,6 +65,7 @@
<Compile Include="EstrusPartKindUsageRule.cs" /> <Compile Include="EstrusPartKindUsageRule.cs" />
<Compile Include="HediffComps\HediffComp_InducedOvulator.cs" /> <Compile Include="HediffComps\HediffComp_InducedOvulator.cs" />
<Compile Include="HediffComps\MenstruationUtility.cs" /> <Compile Include="HediffComps\MenstruationUtility.cs" />
<Compile Include="Hediff_Estrus.cs" />
<Compile Include="IngestionOutcomeDoers.cs" /> <Compile Include="IngestionOutcomeDoers.cs" />
<Compile Include="Patch\GC_Patch.cs" /> <Compile Include="Patch\GC_Patch.cs" />
<Compile Include="Recipe_Surgery.cs" /> <Compile Include="Recipe_Surgery.cs" />
@ -162,5 +163,10 @@
<ExcludeAssets>runtime</ExcludeAssets> <ExcludeAssets>runtime</ExcludeAssets>
</PackageReference> </PackageReference>
</ItemGroup> </ItemGroup>
<ItemGroup>
<Content Include="..\..\..\..\changelogs.txt">
<Link>changelogs.txt</Link>
</Content>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project> </Project>

View file

@ -404,10 +404,8 @@ namespace RJW_Menstruation
DoSubRow(sublist.GetRect(rowH), key, extension, removeelements); DoSubRow(sublist.GetRect(rowH), key, extension, removeelements);
} }
} }
if (!removeelements.NullOrEmpty()) foreach (string key in removeelements) foreach (string key in removeelements)
{
extension.hybridInfo.Remove(key); extension.hybridInfo.Remove(key);
}
sublist.End(); sublist.End();
Widgets.DrawHighlightIfMouseover(rect); Widgets.DrawHighlightIfMouseover(rect);

View file

@ -81,13 +81,12 @@ namespace RJW_Menstruation
if (allvaginas != null) return allvaginas; if (allvaginas != null) return allvaginas;
allvaginas = new HashSet<HediffDef>(); allvaginas = new HashSet<HediffDef>();
List<HediffDef> allHediffs = DefDatabase<HediffDef>.AllDefsListForReading; foreach(HediffDef hediffDef in DefDatabase<HediffDef>.AllDefsListForReading)
foreach(HediffDef hediffDef in allHediffs)
{ {
if (hediffDef.comps.NullOrEmpty()) continue; if (hediffDef.comps.NullOrEmpty()) continue;
foreach (HediffCompProperties comp in hediffDef.comps) foreach (HediffCompProperties comp in hediffDef.comps)
{ {
if (comp.compClass == typeof(HediffComp_Menstruation) || comp.compClass.IsSubclassOf(typeof(HediffComp_Menstruation))) if (comp.compClass == typeof(HediffComp_Menstruation) || (comp.compClass?.IsSubclassOf(typeof(HediffComp_Menstruation)) ?? false))
{ {
allvaginas.Add(hediffDef); allvaginas.Add(hediffDef);
break; break;

View file

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Manifest> <Manifest>
<identifier>RJW Menstruation</identifier> <identifier>RJW Menstruation</identifier>
<version>1.0.7.3</version> <version>1.0.7.4</version>
<dependencies> <dependencies>
</dependencies> </dependencies>
<incompatibleWith /> <incompatibleWith />

View file

@ -1,3 +1,17 @@
Version 1.0.7.4
- Fix errors when using other mods with bad HediffCompProperties.
- Fix egg appearing to be fertilized in womb display when it is not.
- Fix post-birth recovery being instant or very short.
- Fix womb gizmo error when insect egged.
- Induced ovulators will start with a lower number of eggs, but still enough for a long breeding life. IUDs or sex with poor fertility partners may result in early menopause.
- Pawns on caravans and in transport pods will have their wombs simulated.
- Updated max size areola images by wruf.
- Substantially reduce the speed of nipple variance during milking and respect the max nipple increment setting.
- Possible titles for newborns removed to match RJW 5.1.0.
- Improve compatibility with RJW 5.1.0 for multiple wombs.
- Better handling of estrus for multiple wombs.
- Induced ovulators will end estrus immediately upon reaching luteal if not induced. One that is induced will have their estrus only last as long as an unfertilized egg would.
Version 1.0.7.3 Version 1.0.7.3
- Fix null reference error upon birth of female pawns. - Fix null reference error upon birth of female pawns.
- Properly display multiple icons for pawns with multiple wombs. - Properly display multiple icons for pawns with multiple wombs.