Merge branch 'dev'

This commit is contained in:
lutepickle 2024-03-17 16:33:09 -07:00
commit daeefe8218
39 changed files with 273 additions and 208 deletions

Binary file not shown.

View File

@ -9,7 +9,8 @@
<GeneDef Name="Menstruation_EggLifetime" Abstract="True"> <GeneDef Name="Menstruation_EggLifetime" Abstract="True">
<displayCategory>Menstruation</displayCategory> <displayCategory>Menstruation</displayCategory>
<exclusionTags> <iconPath>UI/Genes/Placeholder</iconPath>
<exclusionTags>
<li>Menstruation_EggLifetime</li> <li>Menstruation_EggLifetime</li>
</exclusionTags> </exclusionTags>
</GeneDef> </GeneDef>
@ -18,33 +19,49 @@
<defName>Menstruation_ShortEggLifetime</defName> <defName>Menstruation_ShortEggLifetime</defName>
<label>short egg lifetime</label> <label>short egg lifetime</label>
<description>Unfertilized eggs with this gene last three-quarters as long.</description> <description>Unfertilized eggs with this gene last three-quarters as long.</description>
<iconPath>UI/Genes/Placeholder</iconPath> <iconPath>UI/Genes/ShortEggLifetime</iconPath>
<biostatMet>1</biostatMet> <biostatMet>1</biostatMet>
<displayOrderInCategory>10</displayOrderInCategory> <displayOrderInCategory>10</displayOrderInCategory>
<modExtensions>
<li Class ="RJW_Menstruation.MenstruationModExtension">
<eggLifeTimeFactor>0.75</eggLifeTimeFactor>
</li>
</modExtensions>
</GeneDef> </GeneDef>
<GeneDef ParentName="Menstruation_EggLifetime"> <GeneDef ParentName="Menstruation_EggLifetime">
<defName>Menstruation_DoubleEggLifetime</defName> <defName>Menstruation_DoubleEggLifetime</defName>
<label>double egg lifetime</label> <label>double egg lifetime</label>
<description>Unfertilized eggs with this gene last twice as long.</description> <description>Unfertilized eggs with this gene last twice as long.</description>
<iconPath>UI/Genes/Placeholder</iconPath> <iconPath>UI/Genes/DoubleEggLifetime</iconPath>
<biostatMet>-1</biostatMet> <biostatMet>-1</biostatMet>
<displayOrderInCategory>12</displayOrderInCategory> <displayOrderInCategory>12</displayOrderInCategory>
<modExtensions>
<li Class ="RJW_Menstruation.MenstruationModExtension">
<eggLifeTimeFactor>2.0</eggLifeTimeFactor>
</li>
</modExtensions>
</GeneDef> </GeneDef>
<GeneDef ParentName="Menstruation_EggLifetime"> <GeneDef ParentName="Menstruation_EggLifetime">
<defName>Menstruation_QuadEggLifetime</defName> <defName>Menstruation_QuadEggLifetime</defName>
<label>quadrule egg lifetime</label> <label>quadrule egg lifetime</label>
<description>Eggs with this gene last four times as long.</description> <description>Eggs with this gene last four times as long.</description>
<iconPath>UI/Genes/Placeholder</iconPath> <iconPath>UI/Genes/QuadEggLifetime</iconPath>
<biostatMet>-2</biostatMet> <biostatMet>-2</biostatMet>
<biostatCpx>1</biostatCpx> <biostatCpx>1</biostatCpx>
<displayOrderInCategory>16</displayOrderInCategory> <displayOrderInCategory>16</displayOrderInCategory>
<modExtensions>
<li Class ="RJW_Menstruation.MenstruationModExtension">
<eggLifeTimeFactor>4.0</eggLifeTimeFactor>
</li>
</modExtensions>
</GeneDef> </GeneDef>
<GeneDef Name="Menstruation_Estrus" Abstract="True"> <GeneDef Name="Menstruation_Estrus" Abstract="True">
<displayCategory>Menstruation</displayCategory> <displayCategory>Menstruation</displayCategory>
<exclusionTags> <iconPath>UI/Genes/Placeholder</iconPath>
<exclusionTags>
<li>Menstruation_Estrus</li> <li>Menstruation_Estrus</li>
</exclusionTags> </exclusionTags>
</GeneDef> </GeneDef>
@ -53,24 +70,35 @@
<defName>Menstruation_NeverEstrus</defName> <defName>Menstruation_NeverEstrus</defName>
<label>never estrus</label> <label>never estrus</label>
<description>Carriers of this gene will never go into estrus.</description> <description>Carriers of this gene will never go into estrus.</description>
<iconPath>UI/Genes/Placeholder</iconPath> <iconPath>UI/Genes/NeverEstrus</iconPath>
<biostatMet>1</biostatMet> <biostatMet>1</biostatMet>
<displayOrderInCategory>20</displayOrderInCategory> <displayOrderInCategory>20</displayOrderInCategory>
<modExtensions>
<li Class ="RJW_Menstruation.MenstruationModExtension">
<neverEstrus>true</neverEstrus>
</li>
</modExtensions>
</GeneDef> </GeneDef>
<GeneDef ParentName="Menstruation_Estrus"> <GeneDef ParentName="Menstruation_Estrus">
<defName>Menstruation_FullEstrus</defName> <defName>Menstruation_FullEstrus</defName>
<label>full estrus</label> <label>full estrus</label>
<description>Carriers of this gene will enter full estrus every menstrual cycle, regardless of vagina type.</description> <description>Carriers of this gene will enter full estrus every menstrual cycle, regardless of vagina type.</description>
<iconPath>UI/Genes/Placeholder</iconPath> <iconPath>UI/Genes/FullEstrus</iconPath>
<biostatMet>-1</biostatMet> <biostatMet>-1</biostatMet>
<biostatCpx>1</biostatCpx> <biostatCpx>1</biostatCpx>
<displayOrderInCategory>25</displayOrderInCategory> <displayOrderInCategory>25</displayOrderInCategory>
<modExtensions>
<li Class ="RJW_Menstruation.MenstruationModExtension">
<alwaysEstrus>true</alwaysEstrus>
</li>
</modExtensions>
</GeneDef> </GeneDef>
<GeneDef Name="Menstruation_Ovulation" Abstract="True"> <GeneDef Name="Menstruation_Ovulation" Abstract="True">
<displayCategory>Menstruation</displayCategory> <displayCategory>Menstruation</displayCategory>
<exclusionTags> <iconPath>UI/Genes/Placeholder</iconPath>
<exclusionTags>
<li>Menstruation_Ovulation</li> <li>Menstruation_Ovulation</li>
</exclusionTags> </exclusionTags>
</GeneDef> </GeneDef>
@ -79,18 +107,28 @@
<defName>Menstruation_DoubleOvulation</defName> <defName>Menstruation_DoubleOvulation</defName>
<label>double ovulation</label> <label>double ovulation</label>
<description>Carriers of this gene will ovulate twice as many eggs.</description> <description>Carriers of this gene will ovulate twice as many eggs.</description>
<iconPath>UI/Genes/Placeholder</iconPath> <iconPath>UI/Genes/DoubleOvulation</iconPath>
<biostatMet>-1</biostatMet> <biostatMet>-1</biostatMet>
<displayOrderInCategory>30</displayOrderInCategory> <displayOrderInCategory>30</displayOrderInCategory>
<modExtensions>
<li Class ="RJW_Menstruation.MenstruationModExtension">
<ovulationFactor>2</ovulationFactor>
</li>
</modExtensions>
</GeneDef> </GeneDef>
<GeneDef ParentName="Menstruation_Ovulation"> <GeneDef ParentName="Menstruation_Ovulation">
<defName>Menstruation_QuadOvulation</defName> <defName>Menstruation_QuadOvulation</defName>
<label>quadruple ovulation</label> <label>quadruple ovulation</label>
<description>Carriers of this gene will ovulate four times as many eggs.</description> <description>Carriers of this gene will ovulate four times as many eggs.</description>
<iconPath>UI/Genes/Placeholder</iconPath> <iconPath>UI/Genes/QuadOvulation</iconPath>
<biostatMet>-1</biostatMet> <biostatMet>-1</biostatMet>
<displayOrderInCategory>35</displayOrderInCategory> <displayOrderInCategory>35</displayOrderInCategory>
<modExtensions>
<li Class ="RJW_Menstruation.MenstruationModExtension">
<ovulationFactor>4</ovulationFactor>
</li>
</modExtensions>
</GeneDef> </GeneDef>
<GeneDef> <GeneDef>
@ -98,9 +136,14 @@
<label>no bleeding</label> <label>no bleeding</label>
<displayCategory>Menstruation</displayCategory> <displayCategory>Menstruation</displayCategory>
<description>Carriers of this gene will not bleed at the end of their cycle.</description> <description>Carriers of this gene will not bleed at the end of their cycle.</description>
<iconPath>UI/Genes/Placeholder</iconPath> <iconPath>UI/Genes/NoBleeding</iconPath>
<biostatMet>-1</biostatMet> <biostatMet>-1</biostatMet>
<displayOrderInCategory>40</displayOrderInCategory> <displayOrderInCategory>40</displayOrderInCategory>
<modExtensions>
<li Class ="RJW_Menstruation.MenstruationModExtension">
<noBleeding>true</noBleeding>
</li>
</modExtensions>
</GeneDef> </GeneDef>
<!-- Pheromones? --> <!-- Pheromones? -->

View File

@ -0,0 +1,73 @@
<?xml version="1.0" encoding="utf-8"?>
<Patch>
<Operation Class="PatchOperationFindMod">
<mods>
<li>Alpha Genes</li>
</mods>
<match Class="PatchOperationAddModExtension">
<xpath>/Defs/GeneDef[defName="AG_EggLaying"]</xpath>
<value>
<li Class="RJW_Menstruation.MenstruationModExtension">
<disableCycle>true</disableCycle>
</li>
</value>
</match>
</Operation>
<Operation Class="PatchOperationFindMod">
<mods>
<li>Vanilla Races Expanded - Saurid</li>
</mods>
<match Class="PatchOperationAddModExtension">
<xpath>/Defs/GeneDef[defName="VRESaurids_Oviparous"]</xpath>
<value>
<li Class="RJW_Menstruation.MenstruationModExtension">
<disableCycle>true</disableCycle>
</li>
</value>
</match>
</Operation>
<Operation Class="PatchOperationFindMod">
<mods>
<li>Vanilla Races Expanded - Phytokin</li>
</mods>
<match Class="PatchOperationAddModExtension">
<xpath>/Defs/GeneDef[defName="VRE_SaplingBirth"]</xpath>
<value>
<li Class="RJW_Menstruation.MenstruationModExtension">
<disableCycle>true</disableCycle>
</li>
</value>
</match>
</Operation>
<Operation Class="PatchOperationFindMod">
<mods>
<li>Erin's Corvyia</li>
</mods>
<match Class="PatchOperationAddModExtension">
<xpath>/Defs/GeneDef[defName="ERN_EggLayer"]</xpath>
<value>
<li Class="RJW_Menstruation.MenstruationModExtension">
<disableCycle>true</disableCycle>
</li>
</value>
</match>
</Operation>
<Operation Class="PatchOperationFindMod">
<mods>
<li>Outland - Genetics</li>
</mods>
<match Class="PatchOperationAddModExtension">
<xpath>/Defs/GeneDef[defName="Outland_EggLayer"]</xpath>
<value>
<li Class="RJW_Menstruation.MenstruationModExtension">
<disableCycle>true</disableCycle>
</li>
</value>
</match>
</Operation>
</Patch>

View File

@ -144,11 +144,5 @@
</li> </li>
</comps> </comps>
</value> </value>
</Operation> </Operation>
</Patch> </Patch>

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@ -1,5 +1,4 @@
using AlienRace; using AlienRace;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using UnityEngine; using UnityEngine;
using Verse; using Verse;

View File

@ -21,7 +21,7 @@ namespace RJW_Menstruation
public const int ColonistTickIntervalDefault = GenDate.TicksPerHour; public const int ColonistTickIntervalDefault = GenDate.TicksPerHour;
public const int NonColonistTickIntervalDefault = GenDate.TicksPerHour; public const int NonColonistTickIntervalDefault = GenDate.TicksPerHour;
public const int AnimalTickIntervalDefault = GenDate.TicksPerHour; public const int AnimalTickIntervalDefault = GenDate.TicksPerHour;
public const int TickIntervalMinimum = 20; public const int TickIntervalMinimum = GenTicks.TicksPerRealSecond / 3;
public const int TickIntervalMaximum = 4 * GenDate.TicksPerHour; public const int TickIntervalMaximum = 4 * GenDate.TicksPerHour;
public const float EnzygoticTwinsChanceDefault = 0.002f; public const float EnzygoticTwinsChanceDefault = 0.002f;
public const int EnzygoticTwinsChanceAdjustDefault = 2; public const int EnzygoticTwinsChanceAdjustDefault = 2;

View File

@ -1,7 +1,6 @@
using RimWorld; using RimWorld;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Text; using System.Text;
using UnityEngine; using UnityEngine;
using Verse; using Verse;

View File

@ -1,5 +1,4 @@
using RimWorld; using RimWorld;
using rjw;
using System.Collections.Generic; using System.Collections.Generic;
using Verse; using Verse;

View File

@ -109,5 +109,7 @@ namespace RJW_Menstruation
if (curStage == Stage.Luteal && !hadOvulatoryStage) return false; if (curStage == Stage.Luteal && !hadOvulatoryStage) return false;
else return base.ShouldBeInEstrus(); else return base.ShouldBeInEstrus();
} }
protected override float RandomOvulationChance => 0;
} }
} }

View File

@ -123,11 +123,10 @@ namespace RJW_Menstruation
protected string customwombtex = null; protected string customwombtex = null;
protected string customvagtex = null; protected string customvagtex = null;
protected bool estrusflag = false; protected bool estrusflag = false;
protected float ovulationChanceCache = -1.0f; // Dirtied every simulation protected float? ovulationChanceCache = null; // Dirtied every simulation
protected float implantationChanceCache = -1.0f; protected float? implantationChanceCache = null;
protected int opcache = -1; protected int opcache = -1;
protected float antisperm = 0.0f; protected float antisperm = 0.0f;
protected float? originvagsize = null;
// RJW pregnancy, or Biotech pregnancy/labor/laborpushing // RJW pregnancy, or Biotech pregnancy/labor/laborpushing
protected Hediff pregnancy = null; protected Hediff pregnancy = null;
@ -179,7 +178,8 @@ namespace RJW_Menstruation
public float HoursBetweenSimulations => (float)TickInterval / GenDate.TicksPerHour; public float HoursBetweenSimulations => (float)TickInterval / GenDate.TicksPerHour;
public Hediff Pregnancy { public Hediff Pregnancy
{
get get
{ {
if (pregnancy == null) return null; if (pregnancy == null) return null;
@ -255,11 +255,11 @@ namespace RJW_Menstruation
public float TotalCum public float TotalCum
{ {
get => cums?.Sum(cum => cum.Volume) ?? 0; get => cums?.Sum(cum => cum.Volume) ?? 0;
} }
public float TotalFertCum public float TotalFertCum
{ {
get => cums?.Sum(cum => cum.FertVolume) ?? 0; get => cums?.Where(cum => CumCanFertilize(cum)).Sum(cum => cum.FertVolume) ?? 0;
} }
public float TotalCumPercent public float TotalCumPercent
{ {
@ -289,7 +289,7 @@ namespace RJW_Menstruation
private bool calculatingOvulationChance = false; private bool calculatingOvulationChance = false;
public bool CalculatingOvulationChance { get => calculatingOvulationChance; } public bool CalculatingOvulationChance { get => calculatingOvulationChance; }
protected float CalculatedOvulationChance() private float CalculatedOvulationChance()
{ {
float ovulationChance = 1.0f; float ovulationChance = 1.0f;
if (EggHealth <= 0.0f) return 0.0f; if (EggHealth <= 0.0f) return 0.0f;
@ -315,19 +315,20 @@ namespace RJW_Menstruation
return ovulationChance; return ovulationChance;
} }
protected float CalculatedImplantChance() private float CalculatedImplantChance()
{ {
if (ModsConfig.BiotechActive && xxx.is_human(Pawn)) if (ModsConfig.BiotechActive && xxx.is_human(Pawn))
{ {
// Implant factor will be based solely on pawn age, plus any rollover from ovulation chance // Implant factor will be based solely on pawn age, plus any rollover from ovulation chance
float factor = 1.0f; float factor = 1.0f;
StatDefOf.Fertility.GetStatPart<StatPart_FertilityByGenderAge>()?.TransformValue(StatRequest.For(Pawn), ref factor); StatDefOf.Fertility.GetStatPart<StatPart_FertilityByGenderAge>()?.TransformValue(StatRequest.For(Pawn), ref factor);
if (factor <= 0.0f) return 0.0f;
if (OvulationChance > 1.0f) factor *= OvulationChance; if (OvulationChance > 1.0f) factor *= OvulationChance;
return Props.baseImplantationChanceFactor * FertilityModifier * factor; return Props.baseImplantationChanceFactor * FertilityModifier * factor;
} }
else else
{ {
return Pawn.health.capacities.GetLevel(xxx.reproduction) * Props.baseImplantationChanceFactor * FertilityModifier; return Pawn.health.capacities.GetLevel(xxx.reproduction) * Props.baseImplantationChanceFactor * FertilityModifier * (Pawn.IsBreeder() ? 10.0f : 1.0f);
} }
} }
@ -335,8 +336,8 @@ namespace RJW_Menstruation
{ {
get get
{ {
if (ovulationChanceCache < 0.0f) ovulationChanceCache = CalculatedOvulationChance(); if (ovulationChanceCache == null) ovulationChanceCache = CalculatedOvulationChance();
return ovulationChanceCache; return ovulationChanceCache.Value;
} }
} }
@ -345,8 +346,8 @@ namespace RJW_Menstruation
{ {
get get
{ {
if (implantationChanceCache < 0.0f) implantationChanceCache = CalculatedImplantChance(); if (implantationChanceCache == null) implantationChanceCache = CalculatedImplantChance();
return implantationChanceCache; return implantationChanceCache.Value;
} }
} }
@ -356,7 +357,7 @@ namespace RJW_Menstruation
{ {
if (cums.NullOrEmpty()) yield return Translations.Info_noCum; if (cums.NullOrEmpty()) yield return Translations.Info_noCum;
else foreach (Cum cum in cums) else foreach (Cum cum in cums)
yield return string.Format("{0}: {1:0.##}ml", cum.notcum ? cum.notcumLabel : cum.pawn?.Label, cum.Volume); yield return string.Format("{0}: {1:0.##}ml", cum.notcum ? cum.notcumLabel : cum.pawn?.Label, cum.Volume);
} }
} }
public Color GetCumMixtureColor public Color GetCumMixtureColor
@ -563,19 +564,6 @@ namespace RJW_Menstruation
} }
} }
public float OriginVagSize
{
get
{
if (originvagsize == null)
{
originvagsize = parent.Severity;
}
return originvagsize ?? 0.1f;
}
set => originvagsize = value;
}
public int CurStageIntervalTicks public int CurStageIntervalTicks
{ {
get => currentIntervalTicks; get => currentIntervalTicks;
@ -608,7 +596,7 @@ namespace RJW_Menstruation
else if (Pawn.story?.bodyType == BodyTypeDefOf.Female) discoveryTime = 0.35f; else if (Pawn.story?.bodyType == BodyTypeDefOf.Female) discoveryTime = 0.35f;
// Estimated; there's no way to get the exact value after the fact without writing it into the save // Estimated; there's no way to get the exact value after the fact without writing it into the save
float lutealProgressWhenImplanted = Math.Min(0.5f, maxImplantDelayHours / (Props.lutealIntervalDays * GenDate.HoursPerDay)); float lutealProgressWhenImplanted = Math.Min(0.5f, maxImplantDelayHours / (Props.lutealIntervalDays * GenDate.HoursPerDay));
return GenMath.LerpDouble(0, discoveryTime, lutealProgressWhenImplanted, 1.0f, pregnancy.Severity); return GenMath.LerpDouble(0, discoveryTime, lutealProgressWhenImplanted, 1.0f, pregnancy.Severity);
} }
} }
@ -656,7 +644,6 @@ namespace RJW_Menstruation
Scribe_Values.Look(ref ovarypower, "ovarypower", ovarypower, true); Scribe_Values.Look(ref ovarypower, "ovarypower", ovarypower, true);
Scribe_Values.Look(ref eggstack, "eggstack", 0); Scribe_Values.Look(ref eggstack, "eggstack", 0);
Scribe_Values.Look(ref estrusflag, "estrusflag", false); Scribe_Values.Look(ref estrusflag, "estrusflag", false);
Scribe_Values.Look(ref originvagsize, "originvagsize", originvagsize, true);
Scribe_Values.Look(ref DoCleanWomb, "DoCleanWomb", false); Scribe_Values.Look(ref DoCleanWomb, "DoCleanWomb", false);
Scribe_References.Look(ref pregnancy, "pregnancy"); Scribe_References.Look(ref pregnancy, "pregnancy");
if (Scribe.mode == LoadSaveMode.PostLoadInit) if (Scribe.mode == LoadSaveMode.PostLoadInit)
@ -679,23 +666,18 @@ namespace RJW_Menstruation
ovulationFactor = 1f; ovulationFactor = 1f;
noBleeding = false; noBleeding = false;
opcache = -1; opcache = -1;
if (Pawn.genes == null || !ModsConfig.BiotechActive) return; if (Pawn.genes == null || !ModsConfig.BiotechActive) return;
foreach (MenstruationModExtension extension in Pawn.genes.GenesListForReading.Select(gene => gene.def.GetModExtension<MenstruationModExtension>()).Where(ext => ext != null))
foreach (GeneDef geneDef in Pawn.genes.GenesListForReading.Select(gene => gene.def))
{ {
if (geneDef == VariousDefOf.ShortEggLifetime) eggLifeSpanTicks = eggLifeSpanTicks * 3 / 4; eggLifeSpanTicks = (int)(eggLifeSpanTicks * extension.eggLifeTimeFactor);
else if (geneDef == VariousDefOf.DoubleEggLifetime) eggLifeSpanTicks *= 2; if (extension.alwaysEstrus) estrusLevel = EstrusLevel.Visible;
else if (geneDef == VariousDefOf.QuadEggLifetime) eggLifeSpanTicks *= 4; else if (extension.neverEstrus) estrusLevel = EstrusLevel.None;
ovulationFactor *= extension.ovulationFactor;
else if (geneDef == VariousDefOf.NeverEstrus) estrusLevel = EstrusLevel.None; if (extension.noBleeding) noBleeding = true;
else if (geneDef == VariousDefOf.FullEstrus) estrusLevel = EstrusLevel.Visible;
else if (geneDef == VariousDefOf.DoubleOvulation) ovulationFactor = 2f;
else if (geneDef == VariousDefOf.QuadOvulation) ovulationFactor = 4f;
else if (geneDef == VariousDefOf.NoBleeding) noBleeding = true;
} }
if (eggLifeSpanTicks < 0) eggLifeSpanTicks = 0;
if (ovulationFactor < 0f) ovulationFactor = 0f;
} }
public bool ShouldSimulate() public bool ShouldSimulate()
@ -724,7 +706,7 @@ namespace RJW_Menstruation
{ {
if (Pawn.IsHashIntervalTick(recalculateTickInterval)) TickInterval = -1; // Every so often, force TickInterval to be recalculated in case the pawn's status changed. if (Pawn.IsHashIntervalTick(recalculateTickInterval)) TickInterval = -1; // Every so often, force TickInterval to be recalculated in case the pawn's status changed.
if (!Pawn.IsHashIntervalTick(TickInterval)) return; if (!Pawn.IsHashIntervalTick(TickInterval)) return;
if (!ShouldSimulate()) return; if (!ShouldSimulate()) return;
// Initialize immediately if needed, but if there's an error, then don't spam it every tick // Initialize immediately if needed, but if there's an error, then don't spam it every tick
@ -733,7 +715,7 @@ namespace RJW_Menstruation
Log.Warning($"{Pawn}'s womb is ticking, but was not initialized first"); Log.Warning($"{Pawn}'s womb is ticking, but was not initialized first");
Initialize(); Initialize();
} }
if (initError) Log.Warning($"Attempting to process {Pawn}'s womb uninitialized"); if (initError) Log.Warning($"Attempting to process {Pawn}'s womb uninitialized");
if (Pregnancy != null && curStage != Stage.Pregnant) if (Pregnancy != null && curStage != Stage.Pregnant)
@ -743,7 +725,7 @@ namespace RJW_Menstruation
} }
BeforeSimulator(); BeforeSimulator();
if (ShouldBeInfertile()) GoNextStage(Stage.Infertile); if (ShouldBeInfertile()) GoNextStage(Stage.Infertile);
switch (curStage) switch (curStage)
{ {
@ -820,7 +802,7 @@ namespace RJW_Menstruation
tip.Append(": "); tip.Append(": ");
tip.Append(GetCurStageLabel); tip.Append(GetCurStageLabel);
string fertInfo = GetFertilizingInfo; string fertInfo = GetFertilizingInfo;
if(CurrentVisibleStage == Stage.Luteal && fertInfo.Length > 0) if (CurrentVisibleStage == Stage.Luteal && fertInfo.Length > 0)
{ {
tip.AppendLine(); tip.AppendLine();
tip.Append(fertInfo); tip.Append(fertInfo);
@ -831,7 +813,7 @@ namespace RJW_Menstruation
protected virtual int TicksToNextStage() protected virtual int TicksToNextStage()
{ {
return Math.Max(0,(currentIntervalTicks - curStageTicks) / Configurations.CycleAcceleration); return Math.Max(0, (currentIntervalTicks - curStageTicks) / Configurations.CycleAcceleration);
} }
public override string CompDebugString() public override string CompDebugString()
@ -1176,7 +1158,7 @@ namespace RJW_Menstruation
if (cycleSpeed < 0f) cycleSpeed = Utility.RandGaussianLike(0.8f, 1.2f); if (cycleSpeed < 0f) cycleSpeed = Utility.RandGaussianLike(0.8f, 1.2f);
if (cycleVariability < 0f) cycleVariability = MenstruationUtility.RandomVariabilityPercent(); if (cycleVariability < 0f) cycleVariability = MenstruationUtility.RandomVariabilityPercent();
InitOvary(); InitOvary();
if (currentIntervalTicks < 0) if (currentIntervalTicks < 0)
@ -1284,8 +1266,8 @@ namespace RJW_Menstruation
protected virtual void BeforeSimulator() protected virtual void BeforeSimulator()
{ {
ovulationChanceCache = -1.0f; ovulationChanceCache = null;
implantationChanceCache = -1.0f; implantationChanceCache = null;
CumOut(); CumOut();
} }
@ -1353,10 +1335,18 @@ namespace RJW_Menstruation
} }
} }
public bool CumCanFertilize(Cum cum)
{
return !cum.notcum &&
cum.FertVolume > 0 &&
!(cum.pawn?.Destroyed ?? true) &&
(RJWPregnancySettings.bestial_pregnancy_enabled || xxx.is_animal(Pawn) == xxx.is_animal(cum.pawn));
}
protected Pawn Fertilize() protected Pawn Fertilize()
{ {
if (cums.NullOrEmpty()) return null; if (cums.NullOrEmpty()) return null;
List<Cum> eligibleCum = cums.FindAll(cum => !cum.notcum && cum.FertVolume > 0 && cum.pawn != null && (RJWPregnancySettings.bestial_pregnancy_enabled || xxx.is_animal(Pawn) == xxx.is_animal(cum.pawn))); List<Cum> eligibleCum = cums.FindAll(cum => CumCanFertilize(cum));
if (eligibleCum.Count == 0) return null; if (eligibleCum.Count == 0) return null;
float totalFertPower = eligibleCum.Sum(cum => cum.FertVolume); float totalFertPower = eligibleCum.Sum(cum => cum.FertVolume);
@ -1364,7 +1354,7 @@ namespace RJW_Menstruation
//float fertFailChancePerHour = Mathf.Pow(1.0f - Configurations.FertilizeChance, totalFertPower * Props.basefertilizationChanceFactor); //float fertFailChancePerHour = Mathf.Pow(1.0f - Configurations.FertilizeChance, totalFertPower * Props.basefertilizationChanceFactor);
//float fertFailChancePerInterval = Mathf.Pow(fertFailChancePerHour, (float)TickInterval / GenDate.TicksPerHour); //float fertFailChancePerInterval = Mathf.Pow(fertFailChancePerHour, (float)TickInterval / GenDate.TicksPerHour);
float fertFailChancePerInterval = Mathf.Pow(1.0f - Configurations.FertilizeChance, totalFertPower * Props.basefertilizationChanceFactor * HoursBetweenSimulations); float fertFailChancePerInterval = Mathf.Pow(1.0f - Configurations.FertilizeChance, totalFertPower * Props.basefertilizationChanceFactor * HoursBetweenSimulations);
if (Rand.Chance(fertFailChancePerInterval)) return null; if (Rand.Chance(fertFailChancePerInterval)) return null;
Pawn.records.AddTo(VariousDefOf.AmountofFertilizedEggs, 1); Pawn.records.AddTo(VariousDefOf.AmountofFertilizedEggs, 1);
@ -1494,7 +1484,7 @@ namespace RJW_Menstruation
float interspeciesFactor = InterspeciesImplantFactor(egg.fertilizer); float interspeciesFactor = InterspeciesImplantFactor(egg.fertilizer);
float implantChance = Configurations.ImplantationChance * ImplantChance * interspeciesFactor; float implantChance = Configurations.ImplantationChance * ImplantChance * interspeciesFactor;
Log.Message($"Fertilized egg of {Pawn} failed to implant (chance {implantChance.ToStringPercent()}, " + Log.Message($"Fertilized egg of {Pawn} failed to implant (chance {implantChance.ToStringPercent()}, " +
(interspeciesFactor < 1.0f ? $"interspecies factor {interspeciesFactor.ToStringPercent()}, " : "" ) + (interspeciesFactor < 1.0f ? $"interspecies factor {interspeciesFactor.ToStringPercent()}, " : "") +
$"father {egg.fertilizer})"); $"father {egg.fertilizer})");
} }
deadeggs.Add(egg); deadeggs.Add(egg);
@ -1525,7 +1515,7 @@ namespace RJW_Menstruation
protected void BleedOut() protected void BleedOut()
{ {
// ~1.5 per hour times acceleration // ~1.5 per hour times acceleration
float bledAmount = 0.03f * Configurations.BleedingAmount * Configurations.CycleAcceleration * Rand.Range(0.5f, 1.5f) * HoursBetweenSimulations; float bledAmount = Utility.VariationRange(0.03f * Configurations.BleedingAmount * Configurations.CycleAcceleration * HoursBetweenSimulations, 0.5f);
CumIn(Pawn, bledAmount, Translations.Menstrual_Blood, -5.0f, Pawn.RaceProps?.BloodDef ?? ThingDefOf.Filth_Blood); CumIn(Pawn, bledAmount, Translations.Menstrual_Blood, -5.0f, Pawn.RaceProps?.BloodDef ?? ThingDefOf.Filth_Blood);
Cum blood = GetNotCum(Translations.Menstrual_Blood); Cum blood = GetNotCum(Translations.Menstrual_Blood);
if (blood != null) blood.Color = BloodColor; if (blood != null) blood.Color = BloodColor;
@ -1580,9 +1570,6 @@ namespace RJW_Menstruation
return amount; return amount;
} }
protected void EggDecay() protected void EggDecay()
{ {
HashSet<Egg> deadeggs = new HashSet<Egg>(); HashSet<Egg> deadeggs = new HashSet<Egg>();
@ -1602,7 +1589,7 @@ namespace RJW_Menstruation
protected void AddCrampPain() protected void AddCrampPain()
{ {
Hediff hediff = HediffMaker.MakeHediff(VariousDefOf.Hediff_MenstrualCramp, Pawn); Hediff hediff = HediffMaker.MakeHediff(VariousDefOf.Hediff_MenstrualCramp, Pawn);
hediff.Severity = crampPain * Rand.Range(0.9f, 1.1f); hediff.Severity = Utility.VariationRange(crampPain, 0.1f);
HediffCompProperties_SeverityPerDay Prop = (HediffCompProperties_SeverityPerDay)hediff.TryGetComp<HediffComp_SeverityPerDay>().props; HediffCompProperties_SeverityPerDay Prop = (HediffCompProperties_SeverityPerDay)hediff.TryGetComp<HediffComp_SeverityPerDay>().props;
Prop.severityPerDay = -hediff.Severity / (currentIntervalTicks / GenDate.TicksPerDay) * Configurations.CycleAcceleration; Prop.severityPerDay = -hediff.Severity / (currentIntervalTicks / GenDate.TicksPerDay) * Configurations.CycleAcceleration;
Pawn.health.AddHediff(hediff, parent.Part); Pawn.health.AddHediff(hediff, parent.Part);
@ -1659,7 +1646,7 @@ namespace RJW_Menstruation
eggnum = 1f; eggnum = 1f;
} }
eggnum *= ovulationFactor; eggnum *= ovulationFactor;
int toOvulate = (int)eggnum + eggstack; int toOvulate = Math.Max(1, (int)eggnum + eggstack);
int ovulated = 0; int ovulated = 0;
for (int i = 0; i < toOvulate; i++) for (int i = 0; i < toOvulate; i++)
@ -1670,7 +1657,7 @@ namespace RJW_Menstruation
} }
ovarypower -= ovulated; ovarypower -= ovulated;
eggstack = 0; eggstack = 0;
if (Configurations.Debug && ovulated != toOvulate) if (Configurations.Debug && ovulated < toOvulate)
Log.Message($"{Pawn} ovulated {ovulated}/{toOvulate} eggs ({OvulationChance.ToStringPercent()} chance)"); Log.Message($"{Pawn} ovulated {ovulated}/{toOvulate} eggs ({OvulationChance.ToStringPercent()} chance)");
GoNextStage(Stage.Luteal); GoNextStage(Stage.Luteal);
@ -1817,10 +1804,10 @@ namespace RJW_Menstruation
} }
else pawnMemories.TryGainMemory(VariousDefOf.CameInsideFFetish, cummer); else pawnMemories.TryGainMemory(VariousDefOf.CameInsideFFetish, cummer);
} }
else if (Pawn.relations.OpinionOf(cummer) <= -5) else if (Pawn.relations.OpinionOf(cummer) <= -5)
pawnMemories.TryGainMemory(VariousDefOf.HaterCameInsideF, cummer); pawnMemories.TryGainMemory(VariousDefOf.HaterCameInsideF, cummer);
else if (Pawn.IsInEstrus() && Pawn.relations.OpinionOf(cummer) < RJWHookupSettings.MinimumRelationshipToHookup) else if (Pawn.IsInEstrus() && Pawn.relations.OpinionOf(cummer) < RJWHookupSettings.MinimumRelationshipToHookup)
pawnMemories.TryGainMemory(VariousDefOf.HaterCameInsideFEstrus, cummer); pawnMemories.TryGainMemory(VariousDefOf.HaterCameInsideFEstrus, cummer);
else if (!Pawn.relations.DirectRelationExists(PawnRelationDefOf.Spouse, cummer) && !Pawn.relations.DirectRelationExists(PawnRelationDefOf.Fiance, cummer)) else if (!Pawn.relations.DirectRelationExists(PawnRelationDefOf.Spouse, cummer) && !Pawn.relations.DirectRelationExists(PawnRelationDefOf.Fiance, cummer))
{ {
if (Pawn.health.capacities.GetLevel(xxx.reproduction) < 0.50f) pawnMemories.TryGainMemory(VariousDefOf.CameInsideFLowFert, cummer); if (Pawn.health.capacities.GetLevel(xxx.reproduction) < 0.50f) pawnMemories.TryGainMemory(VariousDefOf.CameInsideFLowFert, cummer);
@ -1881,15 +1868,15 @@ namespace RJW_Menstruation
switch (stage) switch (stage)
{ {
case Stage.Follicular: case Stage.Follicular:
return (int)(Props.follicularIntervalDays * GenDate.TicksPerDay * (1 + Rand.Range(-cycleVariability, cycleVariability) * 1.5f * variabilityFactor) / (1 + (cycleSpeed - 1) * 1.5f)); return (int)(Utility.VariationRange(Props.follicularIntervalDays * GenDate.TicksPerDay, cycleVariability * 1.5f * variabilityFactor) / (1 + (cycleSpeed - 1) * 1.5f));
case Stage.Ovulatory: case Stage.Ovulatory:
return Props.ovulationIntervalHours * GenDate.TicksPerHour; // No variability for now return Props.ovulationIntervalHours * GenDate.TicksPerHour; // No variability for now
case Stage.Luteal: case Stage.Luteal:
return (int)(Props.lutealIntervalDays * GenDate.TicksPerDay * (1 + Rand.Range(-cycleVariability, cycleVariability) * 0.5f * variabilityFactor) / (1 + (cycleSpeed - 1) * 0.5f)); return (int)(Utility.VariationRange(Props.lutealIntervalDays * GenDate.TicksPerDay, cycleVariability * 0.5f * variabilityFactor) / (1 + (cycleSpeed - 1) * 0.5f));
case Stage.Bleeding: case Stage.Bleeding:
return (int)(Props.bleedingIntervalDays * GenDate.TicksPerDay * (1 + Rand.Range(-cycleVariability, cycleVariability) * 0.5f * variabilityFactor) / (1 + (cycleSpeed - 1) * 0.5f)); return (int)(Utility.VariationRange(Props.bleedingIntervalDays * GenDate.TicksPerDay, cycleVariability * 0.5f * variabilityFactor) / (1 + (cycleSpeed - 1) * 0.5f));
case Stage.Recover: case Stage.Recover:
return (int)(Props.recoveryIntervalDays * GenDate.TicksPerDay * Rand.Range(0.95f, 1.05f)); return (int)Utility.VariationRange(Props.recoveryIntervalDays * GenDate.TicksPerDay, 0.05f);
case Stage.Pregnant: case Stage.Pregnant:
return (int)(MenstruationUtility.GestationHours(pregnancy) * GenDate.TicksPerHour); return (int)(MenstruationUtility.GestationHours(pregnancy) * GenDate.TicksPerHour);
default: default:
@ -1917,10 +1904,13 @@ namespace RJW_Menstruation
else return Rand.Range(0.6f, 1.0f); else return Rand.Range(0.6f, 1.0f);
} }
protected virtual float RandomOvulationChance => (float)Props.ovulationIntervalHours / GenDate.HoursPerDay;
protected Stage RandomStage() protected Stage RandomStage()
{ {
Stage stage = Rand.ElementByWeight( Stage stage = Rand.ElementByWeight(
Stage.Follicular, Props.follicularIntervalDays - Props.bleedingIntervalDays, Stage.Follicular, Props.follicularIntervalDays - Props.bleedingIntervalDays,
Stage.Ovulatory, RandomOvulationChance,
Stage.Luteal, Props.lutealIntervalDays, Stage.Luteal, Props.lutealIntervalDays,
Stage.Bleeding, Props.bleedingIntervalDays); Stage.Bleeding, Props.bleedingIntervalDays);
@ -1929,6 +1919,9 @@ namespace RJW_Menstruation
case Stage.Follicular: case Stage.Follicular:
curStageTicks = Rand.Range(0, (Props.follicularIntervalDays - Props.bleedingIntervalDays) * GenDate.TicksPerDay); curStageTicks = Rand.Range(0, (Props.follicularIntervalDays - Props.bleedingIntervalDays) * GenDate.TicksPerDay);
break; break;
case Stage.Ovulatory:
curStageTicks = Rand.Range(0, Props.ovulationIntervalHours * GenDate.TicksPerHour);
break;
case Stage.Luteal: case Stage.Luteal:
curStageTicks = Rand.Range(0, Props.lutealIntervalDays * GenDate.TicksPerDay); curStageTicks = Rand.Range(0, Props.lutealIntervalDays * GenDate.TicksPerDay);
break; break;
@ -2029,28 +2022,6 @@ namespace RJW_Menstruation
public class HediffComp_Anus : HediffComp public class HediffComp_Anus : HediffComp
{ {
protected float? originanussize; public CompProperties_Anus Props => (CompProperties_Anus)props;
public float OriginAnusSize
{
get
{
if (originanussize == null)
{
originanussize = parent.Severity;
}
return originanussize ?? 0.1f;
}
}
public override void CompExposeData()
{
base.CompExposeData();
Scribe_Values.Look(ref originanussize, "originanussize", originanussize, true);
}
public override void CompPostTick(ref float severityAdjustment)
{
}
} }
} }

View File

@ -1,5 +1,4 @@
using RimWorld; using RimWorld;
using System.Linq;
using Verse; using Verse;
namespace RJW_Menstruation namespace RJW_Menstruation
@ -29,14 +28,14 @@ namespace RJW_Menstruation
protected override void InitializeExtraValues() protected override void InitializeExtraValues()
{ {
base.InitializeExtraValues(); base.InitializeExtraValues();
if (averageCycleIntervalTicks < 0) if (averageCycleIntervalTicks < 0)
{ {
averageCycleIntervalTicks = (int)(Props.cycleIntervalDays.RandomInRange * GenDate.TicksPerDay / cycleSpeed); averageCycleIntervalTicks = (int)(Props.cycleIntervalDays.RandomInRange * GenDate.TicksPerDay / cycleSpeed);
if (ticksToNextCycle < -50000) if (ticksToNextCycle < -50000)
ticksToNextCycle = Rand.Range(0, averageCycleIntervalTicks); ticksToNextCycle = Rand.Range(0, averageCycleIntervalTicks);
// Make the cutoff halfway into cycle, just to be sure there isn't a double-cycle the first time // 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) if ((curStage == Stage.Follicular || curStage == Stage.Ovulatory || curStage == Stage.Luteal || curStage == Stage.Bleeding)
&& (averageCycleIntervalTicks - ticksToNextCycle) / 2 >= GenDate.TicksPerDay * (Props.follicularIntervalDays + Props.lutealIntervalDays) / cycleSpeed) && (averageCycleIntervalTicks - ticksToNextCycle) / 2 >= GenDate.TicksPerDay * (Props.follicularIntervalDays + Props.lutealIntervalDays) / cycleSpeed)
GoNextStage(Stage.Anestrus); GoNextStage(Stage.Anestrus);
} }
@ -99,14 +98,14 @@ namespace RJW_Menstruation
base.PregnantAction(); base.PregnantAction();
if (curStage != Stage.Pregnant) if (curStage != Stage.Pregnant)
// Go halfway into the cycle // Go halfway into the cycle
ticksToNextCycle = (int)(averageCycleIntervalTicks * (1 + Rand.Range(-cycleVariability, cycleVariability))) / 2; ticksToNextCycle = (int)Utility.VariationRange(averageCycleIntervalTicks, cycleVariability) / 2;
} }
protected override void AnestrusAction() protected override void AnestrusAction()
{ {
if (ticksToNextCycle <= 0 && IsBreedingSeason()) if (ticksToNextCycle <= 0 && IsBreedingSeason())
{ {
ticksToNextCycle = (int)(averageCycleIntervalTicks * (1 + Rand.Range(-cycleVariability, cycleVariability))); ticksToNextCycle = (int)Utility.VariationRange(averageCycleIntervalTicks, cycleVariability);
GoNextStage(Stage.Follicular); GoNextStage(Stage.Follicular);
} }
} }

View File

@ -1,6 +1,7 @@
 
using RimWorld; using RimWorld;
using RimWorld.Planet;
using rjw; using rjw;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -46,6 +47,8 @@ namespace RJW_Menstruation
protected IEnumerable<Pawn> AffectedPawns() protected IEnumerable<Pawn> AffectedPawns()
{ {
if(Pawn.GetCaravan() is Caravan caravan)
foreach (Pawn p in caravan.PawnsListForReading.Where(p => p != Pawn)) yield return p;
Map mapHeld = Pawn.MapHeld; Map mapHeld = Pawn.MapHeld;
if (mapHeld == null) yield break; if (mapHeld == null) yield break;
foreach (Pawn pawn in mapHeld.mapPawns.AllPawnsSpawned) foreach (Pawn pawn in mapHeld.mapPawns.AllPawnsSpawned)

View File

@ -1,5 +1,4 @@
using HarmonyLib; using HarmonyLib;
using Mono.Cecil.Cil;
using RimWorld; using RimWorld;
using rjw; using rjw;
using System; using System;
@ -203,7 +202,7 @@ namespace RJW_Menstruation
if (comp?.HasBaby ?? false) if (comp?.HasBaby ?? false)
{ {
OutcomeChance thisOutcome = outcome; OutcomeChance thisOutcome = outcome;
Precept_Ritual precept_Ritual = (Precept_Ritual)comp.Pawn.Ideo.GetPrecept(PreceptDefOf.ChildBirth); Precept_Ritual precept_Ritual = (Precept_Ritual)comp.Pawn.Ideo.GetPrecept(RimWorld.PreceptDefOf.ChildBirth);
float birthQuality = PregnancyUtility.GetBirthQualityFor(mother); float birthQuality = PregnancyUtility.GetBirthQualityFor(mother);
do do
{ {

View File

@ -1,14 +1,10 @@
using RimWorld; using RimWorld;
using RimWorld.Planet;
using rjw; using rjw;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.Eventing.Reader;
using System.Linq; using System.Linq;
using System.Text;
using UnityEngine; using UnityEngine;
using Verse; using Verse;
using Verse.AI;
namespace RJW_Menstruation namespace RJW_Menstruation
{ {
@ -141,7 +137,7 @@ namespace RJW_Menstruation
else if (gestationProgress < 0.8f) icon = fetustex + "04"; else if (gestationProgress < 0.8f) icon = fetustex + "04";
else icon = fetustex + "05"; else icon = fetustex + "05";
return TryGetTwinsIcon(icon, babycount) ?? ContentFinder<Texture2D>.Get((icon), true); return TryGetTwinsIcon(icon, babycount) ?? ContentFinder<Texture2D>.Get(icon, true);
} }
public static Texture2D TryGetTwinsIcon(string path, int babycount) public static Texture2D TryGetTwinsIcon(string path, int babycount)
@ -160,7 +156,7 @@ namespace RJW_Menstruation
List<Hediff_InsectEgg> insectEggs = new List<Hediff_InsectEgg>(); List<Hediff_InsectEgg> insectEggs = new List<Hediff_InsectEgg>();
comp.Pawn.health.hediffSet.GetHediffs(ref insectEggs); comp.Pawn.health.hediffSet.GetHediffs(ref insectEggs);
if (!insectEggs.NullOrEmpty() && insectEggs.Sum(hediff => hediff.eggssize) > 1.0f) return null; // same logic as "Stuffed" in GetInsectEggedIcon if (insectEggs?.Sum(hediff => hediff.eggssize) > 1.0f) return null; // same logic as "Stuffed" in GetInsectEggedIcon
string icon = comp.WombTex; string icon = comp.WombTex;
float cumpercent = comp.TotalCumPercent; float cumpercent = comp.TotalCumPercent;
@ -183,7 +179,7 @@ namespace RJW_Menstruation
else if (cumpercent < 0.89f) icon += "_Cum_15"; else if (cumpercent < 0.89f) icon += "_Cum_15";
else if (cumpercent < 0.95f) icon += "_Cum_16"; else if (cumpercent < 0.95f) icon += "_Cum_16";
else icon += "_Cum_17"; else icon += "_Cum_17";
Texture2D cumtex = ContentFinder<Texture2D>.Get((icon), true); Texture2D cumtex = ContentFinder<Texture2D>.Get(icon, true);
return cumtex; return cumtex;
} }
public static Texture2D GetInsectEggedIcon(this HediffComp_Menstruation comp) public static Texture2D GetInsectEggedIcon(this HediffComp_Menstruation comp)
@ -306,15 +302,13 @@ namespace RJW_Menstruation
} }
public static Texture2D GetGenitalIcon(this Pawn pawn, HediffComp_Menstruation comp, bool drawOrigin = false) public static Texture2D GetGenitalIcon(this Pawn pawn, HediffComp_Menstruation comp)
{ {
Hediff hediff = comp?.parent; Hediff hediff = comp?.parent;
if (hediff == null) return ContentFinder<Texture2D>.Get("Genitals/Vagina00", true); if (hediff == null) return ContentFinder<Texture2D>.Get("Genitals/Vagina00", true);
//HediffComp_Menstruation comp = hediff.GetMenstruationComp(); //HediffComp_Menstruation comp = hediff.GetMenstruationComp();
string icon; string icon;
float severity; float severity = hediff.Severity;
if (drawOrigin) severity = comp.OriginVagSize;
else severity = hediff.Severity;
if (comp != null) icon = comp.VagTex; if (comp != null) icon = comp.VagTex;
else icon = "Genitals/Vagina"; else icon = "Genitals/Vagina";
@ -331,30 +325,18 @@ namespace RJW_Menstruation
else if (severity < 1.01f) icon += "10"; //cavernous else if (severity < 1.01f) icon += "10"; //cavernous
else icon += "11"; //abyssal else icon += "11"; //abyssal
return ContentFinder<Texture2D>.Get((icon), true); return ContentFinder<Texture2D>.Get(icon, true);
} }
public static Texture2D GetAnalIcon(this Pawn pawn, bool drawOrigin = false) public static Texture2D GetAnalIcon(this Pawn pawn)
{ {
Hediff hediff = pawn.health.hediffSet.hediffs.FirstOrDefault(h => VariousDefOf.AllAnuses.Contains(h.def)) ?? Hediff hediff = pawn.health.hediffSet.hediffs.FirstOrDefault(h => VariousDefOf.AllAnuses.Contains(h.def)) ??
pawn.health.hediffSet.hediffs.FirstOrDefault(h => h.def.defName.ToLower().Contains("anus")); pawn.health.hediffSet.hediffs.FirstOrDefault(h => h.def.defName.ToLower().Contains("anus"));
if (hediff == null) return ContentFinder<Texture2D>.Get(("Genitals/Anal00"), true); if (hediff == null) return ContentFinder<Texture2D>.Get("Genitals/Anal00", true);
string icon = ((CompProperties_Anus)hediff.GetAnusComp()?.props)?.analTex ?? "Genitals/Anal";
float severity = hediff.Severity;
string icon;
float severity;
HediffComp_Anus comp = hediff.GetAnusComp();
if (comp != null)
{
CompProperties_Anus Props = (CompProperties_Anus)comp.props;
icon = Props.analTex ?? "Genitals/Anal";
if (drawOrigin) severity = comp.OriginAnusSize;
else severity = hediff.Severity;
}
else
{
icon = "Genitals/Anal";
severity = hediff.Severity;
}
if (severity < 0.20f) icon += "00"; //micro if (severity < 0.20f) icon += "00"; //micro
else if (severity < 0.40f) icon += "01"; //tight else if (severity < 0.40f) icon += "01"; //tight
else if (severity < 0.60f) icon += "02"; //average else if (severity < 0.60f) icon += "02"; //average
@ -362,7 +344,7 @@ namespace RJW_Menstruation
else if (severity < 1.01f) icon += "04"; //cavernous else if (severity < 1.01f) icon += "04"; //cavernous
else icon += "05"; //abyssal else icon += "05"; //abyssal
return ContentFinder<Texture2D>.Get((icon), true); return ContentFinder<Texture2D>.Get(icon, true);
} }
public static float GestationHours(this Hediff hediff) public static float GestationHours(this Hediff hediff)
@ -398,7 +380,8 @@ namespace RJW_Menstruation
if (!Configurations.EnableAnimalCycle && pawn.IsAnimal()) return false; if (!Configurations.EnableAnimalCycle && pawn.IsAnimal()) return false;
if (pawn.GetComp<CompEggLayer>() != null) return false; if (pawn.GetComp<CompEggLayer>() != null) return false;
if (pawn.RaceHasOviPregnancy()) return false; if (pawn.RaceHasOviPregnancy()) return false;
if (ModsConfig.BiotechActive && pawn.genes != null &&
if (ModsConfig.BiotechActive && pawn.genes != null &&
pawn.genes.GenesListForReading.Select(gene => gene.def).Intersect(VariousDefOf.EggLayerGenes).Any()) return false; pawn.genes.GenesListForReading.Select(gene => gene.def).Intersect(VariousDefOf.EggLayerGenes).Any()) return false;
return true; return true;
@ -452,7 +435,7 @@ namespace RJW_Menstruation
if (precept != null) return true; if (precept != null) return true;
else return pawn.IsBreeder() || else return pawn.IsBreeder() ||
pawn.HasImpregnationFetish(); pawn.HasImpregnationFetish();
} }
public static float DamagePants(this Pawn pawn, float fluidAmount) public static float DamagePants(this Pawn pawn, float fluidAmount)

View File

@ -1,5 +1,4 @@
using RimWorld; using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using Verse; using Verse;

View File

@ -1,10 +1,7 @@
using RimWorld; using RimWorld;
using rjw; using rjw;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection;
using System.Text;
using Verse; using Verse;
namespace RJW_Menstruation namespace RJW_Menstruation

View File

@ -0,0 +1,15 @@
using Verse;
namespace RJW_Menstruation
{
public class MenstruationModExtension : DefModExtension
{
public float eggLifeTimeFactor = 1.0f;
public bool neverEstrus = false;
public bool alwaysEstrus = false;
public float ovulationFactor = 1.0f;
public bool noBleeding = false;
public bool disableCycle = false;
}
}

View File

@ -1,11 +1,11 @@
using HarmonyLib; using HarmonyLib;
using System.Linq;
using RimWorld; using RimWorld;
using Verse;
using System.Collections.Generic;
using System.Reflection;
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit; using System.Reflection.Emit;
using Verse;
namespace RJW_Menstruation namespace RJW_Menstruation
{ {
@ -195,8 +195,9 @@ namespace RJW_Menstruation
[HarmonyPatch(typeof(Pawn_GeneTracker), "Notify_GenesChanged")] [HarmonyPatch(typeof(Pawn_GeneTracker), "Notify_GenesChanged")]
public class Notify_GenesChanged_Patch public class Notify_GenesChanged_Patch
{ {
public static void Postfix(Pawn_GeneTracker __instance) public static void Postfix(Pawn_GeneTracker __instance, GeneDef addedOrRemovedGene)
{ {
if (!addedOrRemovedGene.HasModExtension<MenstruationModExtension>()) return;
foreach (HediffComp_Menstruation comp in __instance.pawn.GetMenstruationComps()) foreach (HediffComp_Menstruation comp in __instance.pawn.GetMenstruationComps())
comp.Notify_UpdatedGenes(); comp.Notify_UpdatedGenes();
} }

View File

@ -2,7 +2,6 @@
using RimWorld; using RimWorld;
using rjw; using rjw;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text; using System.Text;
using UnityEngine; using UnityEngine;
using Verse; using Verse;

View File

@ -1,5 +1,4 @@
using AlienRace; using HarmonyLib;
using HarmonyLib;
using rjw; using rjw;
using rjw.Modules.Interactions.Internals.Implementation; using rjw.Modules.Interactions.Internals.Implementation;
using rjw.Modules.Interactions.Rules.PartKindUsageRules; using rjw.Modules.Interactions.Rules.PartKindUsageRules;

View File

@ -1,6 +1,5 @@
using HarmonyLib; using HarmonyLib;
using RimWorld; using RimWorld;
using rjw;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;

View File

@ -20,9 +20,8 @@ namespace RJW_Menstruation
StringBuilder res = new StringBuilder(); StringBuilder res = new StringBuilder();
IEnumerable<Pawn> babiesdistinct = babies.Distinct(new RaceComparer());
int iteration = 0; int iteration = 0;
foreach (Pawn baby in babiesdistinct) foreach (Pawn baby in babies.Distinct(new RaceComparer()))
{ {
int num = babies.Where(x => x.def.Equals(baby.def)).Count(); int num = babies.Where(x => x.def.Equals(baby.def)).Count();
if (iteration > 0) res.Append(", "); if (iteration > 0) res.Append(", ");
@ -43,9 +42,8 @@ namespace RJW_Menstruation
if (!is_parent_known && Configurations.InfoDetail != Configurations.DetailLevel.All) if (!is_parent_known && Configurations.InfoDetail != Configurations.DetailLevel.All)
return res.Append(Translations.Dialog_FatherUnknown).ToString(); return res.Append(Translations.Dialog_FatherUnknown).ToString();
IEnumerable<Pawn> babiesdistinct = babies.Distinct(new FatherComparer(mother));
int iteration = 0; int iteration = 0;
foreach (Pawn baby in babiesdistinct) foreach (Pawn baby in babies.Distinct(new FatherComparer(mother)))
{ {
if (iteration > 0) res.Append(", "); if (iteration > 0) res.Append(", ");
res.Append(Utility.GetFather(baby, mother)?.LabelShort ?? Translations.Dialog_FatherUnknown); res.Append(Utility.GetFather(baby, mother)?.LabelShort ?? Translations.Dialog_FatherUnknown);

View File

@ -74,6 +74,7 @@
<Compile Include="HediffComps\MenstruationUtility.cs" /> <Compile Include="HediffComps\MenstruationUtility.cs" />
<Compile Include="Hediff_Estrus.cs" /> <Compile Include="Hediff_Estrus.cs" />
<Compile Include="IngestionOutcomeDoers.cs" /> <Compile Include="IngestionOutcomeDoers.cs" />
<Compile Include="MenstruationModExtension.cs" />
<Compile Include="Patch\Biotech_Patch.cs" /> <Compile Include="Patch\Biotech_Patch.cs" />
<Compile Include="Patch\GC_Patch.cs" /> <Compile Include="Patch\GC_Patch.cs" />
<Compile Include="Patch\Gizmo_Patch.cs" /> <Compile Include="Patch\Gizmo_Patch.cs" />
@ -108,10 +109,6 @@
<HintPath>..\..\..\..\..\..\..\..\workshop\content\294100\2830943477\1.4\Assemblies\AnimalGenetics.dll</HintPath> <HintPath>..\..\..\..\..\..\..\..\workshop\content\294100\2830943477\1.4\Assemblies\AnimalGenetics.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
<Reference Include="Assembly-CSharp">
<HintPath>..\..\..\..\..\..\RimWorldWin64_Data\Managed\Assembly-CSharp.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="RJW"> <Reference Include="RJW">
<HintPath>..\..\..\..\..\rjw\1.4\Assemblies\RJW.dll</HintPath> <HintPath>..\..\..\..\..\rjw\1.4\Assemblies\RJW.dll</HintPath>
<Private>False</Private> <Private>False</Private>
@ -172,6 +169,9 @@
</BootstrapperPackage> </BootstrapperPackage>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Krafs.Rimworld.Ref">
<Version>1.4.3901</Version>
</PackageReference>
<PackageReference Include="Lib.Harmony"> <PackageReference Include="Lib.Harmony">
<Version>2.2.2</Version> <Version>2.2.2</Version>
<ExcludeAssets>runtime</ExcludeAssets> <ExcludeAssets>runtime</ExcludeAssets>

View File

@ -219,14 +219,14 @@ namespace RJW_Menstruation
public void CheckDirty() public void CheckDirty()
{ {
if (absorbedfluids > this.GetStatValue(VariousDefOf.MaxAbsorbable) && !(Wearer?.apparel?.IsLocked(this) ?? false)) if (absorbedfluids > this.GetStatValue(VariousDefOf.MaxAbsorbable) && !(Wearer?.apparel?.IsLocked(this) ?? false) && DirtyDef != def && DirtyDef != null)
{ {
bool oldHasStats = !def.equippedStatOffsets.NullOrEmpty();
bool newHasStats = !DirtyDef.equippedStatOffsets.NullOrEmpty();
def = DirtyDef; def = DirtyDef;
dirty = true; dirty = true;
OutfitForcedHandler forcedHandler = Wearer.outfits?.forcedHandler; Wearer.outfits?.forcedHandler?.SetForced(this, false);
if (forcedHandler?.IsForced(this) ?? false) if (oldHasStats || newHasStats)
forcedHandler.SetForced(this, false);
if (!def.equippedStatOffsets.NullOrEmpty())
Wearer.health.capacities.Notify_CapacityLevelsDirty(); Wearer.health.capacities.Notify_CapacityLevelsDirty();
Wearer.apparel.Notify_ApparelChanged(); Wearer.apparel.Notify_ApparelChanged();
} }
@ -272,7 +272,7 @@ namespace RJW_Menstruation
public override void DirtyEffect(int tickInterval) public override void DirtyEffect(int tickInterval)
{ {
if (wearTicks > MinHrstoDirtyEffect * GenDate.TicksPerHour && Rand.MTBEventOccurs(100.0f, GenDate.TicksPerHour, tickInterval) && !Wearer.apparel.IsLocked(this)) if (wearTicks > MinHrstoDirtyEffect * GenDate.TicksPerHour && Rand.MTBEventOccurs(100.0f, GenDate.TicksPerHour, tickInterval) && !(Wearer.apparel?.IsLocked(this) ?? false))
{ {
Wearer.health.AddHediff(HediffDefOf.WoundInfection, Genital_Helper.get_genitalsBPR(Wearer)); Wearer.health.AddHediff(HediffDefOf.WoundInfection, Genital_Helper.get_genitalsBPR(Wearer));
} }

View File

@ -227,7 +227,7 @@ namespace RJW_Menstruation
string feinfo = PregnancyCommon.GetBabyInfo(babiescomp?.babies); string feinfo = PregnancyCommon.GetBabyInfo(babiescomp?.babies);
string fainfo = PregnancyCommon.GetFatherInfo(babiescomp?.babies, babiescomp.Pawn, true) + " "; // Keep all parents known, for now string fainfo = PregnancyCommon.GetFatherInfo(babiescomp?.babies, babiescomp.Pawn, true) + " "; // Keep all parents known, for now
if (feinfo == "Null") feinfo = "1 " + p.Mother.def.label + " " + Translations.Dialog_WombInfo02; if (feinfo == "Null") feinfo = "1 " + (p.Mother ?? pawn).def.label + " " + Translations.Dialog_WombInfo02;
if (fainfo == "Null ") if (fainfo == "Null ")
{ {
string father = p.Father?.LabelShort ?? Translations.Dialog_FatherUnknown; string father = p.Father?.LabelShort ?? Translations.Dialog_FatherUnknown;
@ -388,10 +388,9 @@ namespace RJW_Menstruation
Rect genitalIconRect = new Rect(rect.x, rect.y + fontheight, genitalRectWidth, genitalRectHeight); Rect genitalIconRect = new Rect(rect.x, rect.y + fontheight, genitalRectWidth, genitalRectHeight);
Rect genitalVaginaLabelRect = new Rect(rect.x, rect.y + 10f, genitalRectWidth, fontheight); Rect genitalVaginaLabelRect = new Rect(rect.x, rect.y + 10f, genitalRectWidth, fontheight);
Rect genitalAnusLabelRect = new Rect(rect.x, rect.y + fontheight + genitalRectHeight, genitalRectWidth, fontheight); Rect genitalAnusLabelRect = new Rect(rect.x, rect.y + fontheight + genitalRectHeight, genitalRectWidth, fontheight);
bool showOrigin = Mouse.IsOver(genitalIconRect) && Input.GetMouseButton(0);
vagina = pawn.GetGenitalIcon(comp, showOrigin); vagina = pawn.GetGenitalIcon(comp);
anal = pawn.GetAnalIcon(showOrigin); anal = pawn.GetAnalIcon();
GUI.color = new Color(1.00f, 0.47f, 0.47f, 1); GUI.color = new Color(1.00f, 0.47f, 0.47f, 1);
GUI.Box(rect, "", boxstyle); GUI.Box(rect, "", boxstyle);
GUI.color = Utility.SafeSkinColor(pawn); GUI.color = Utility.SafeSkinColor(pawn);

View File

@ -80,7 +80,7 @@ namespace RJW_Menstruation
try try
{ {
res = part.FluidAmmount * part.FluidModifier * pawn.BodySize / pawn.RaceProps.baseBodySize * Rand.Range(0.8f, 1.2f); res = VariationRange(part.FluidAmmount * part.FluidModifier * pawn.BodySize / pawn.RaceProps.baseBodySize, 0.2f);
} }
catch (NullReferenceException) catch (NullReferenceException)
{ {

View File

@ -45,14 +45,6 @@ namespace RJW_Menstruation
public static readonly RecordDef AmountofCreampied = DefDatabase<RecordDef>.GetNamed("AmountofCreampied"); public static readonly RecordDef AmountofCreampied = DefDatabase<RecordDef>.GetNamed("AmountofCreampied");
public static readonly RecordDef AmountofFertilizedEggs = DefDatabase<RecordDef>.GetNamed("AmountofFertilizedEggs"); public static readonly RecordDef AmountofFertilizedEggs = DefDatabase<RecordDef>.GetNamed("AmountofFertilizedEggs");
public static readonly TaleDef TaleCameInside = DefDatabase<TaleDef>.GetNamed("CameInside"); public static readonly TaleDef TaleCameInside = DefDatabase<TaleDef>.GetNamed("CameInside");
public static readonly GeneDef ShortEggLifetime = DefDatabase<GeneDef>.GetNamed("Menstruation_ShortEggLifetime");
public static readonly GeneDef DoubleEggLifetime = DefDatabase<GeneDef>.GetNamed("Menstruation_DoubleEggLifetime");
public static readonly GeneDef QuadEggLifetime = DefDatabase<GeneDef>.GetNamed("Menstruation_QuadEggLifetime");
public static readonly GeneDef NeverEstrus = DefDatabase<GeneDef>.GetNamed("Menstruation_NeverEstrus");
public static readonly GeneDef FullEstrus = DefDatabase<GeneDef>.GetNamed("Menstruation_FullEstrus");
public static readonly GeneDef DoubleOvulation = DefDatabase<GeneDef>.GetNamed("Menstruation_DoubleOvulation");
public static readonly GeneDef QuadOvulation = DefDatabase<GeneDef>.GetNamed("Menstruation_QuadOvulation");
public static readonly GeneDef NoBleeding = DefDatabase<GeneDef>.GetNamed("Menstruation_NoBleeding");
private static List<ThingDef> allraces = null; private static List<ThingDef> allraces = null;
private static List<PawnKindDef> allkinds = null; private static List<PawnKindDef> allkinds = null;
@ -66,9 +58,8 @@ namespace RJW_Menstruation
get get
{ {
if (allraces != null) return allraces; if (allraces != null) return allraces;
allraces = DefDatabase<ThingDef>.AllDefsListForReading.Where(thingdef => thingdef.race?.IsFlesh ?? false).ToList();
List<ThingDef> allThings = DefDatabase<ThingDef>.AllDefsListForReading;
allraces = allThings.FindAll(x => x.race != null && x.race.IsFlesh);
return allraces; return allraces;
} }
} }
@ -77,9 +68,8 @@ namespace RJW_Menstruation
get get
{ {
if (allkinds != null) return allkinds; if (allkinds != null) return allkinds;
allkinds = DefDatabase<PawnKindDef>.AllDefsListForReading.Where(pawnkinddef => pawnkinddef.race != null).ToList();
List<PawnKindDef> allKinds = DefDatabase<PawnKindDef>.AllDefsListForReading;
allkinds = allKinds.FindAll(x => x.race != null);
return allkinds; return allkinds;
} }
} }
@ -157,13 +147,7 @@ namespace RJW_Menstruation
get get
{ {
if (egglayergenes != null) return egglayergenes; if (egglayergenes != null) return egglayergenes;
egglayergenes = new HashSet<GeneDef> egglayergenes = DefDatabase<GeneDef>.AllDefsListForReading.Where(geneDef => geneDef.GetModExtension<MenstruationModExtension>()?.disableCycle ?? false).ToHashSet();
{
DefDatabase<GeneDef>.GetNamedSilentFail("AG_EggLaying"), // Alpha Genes
DefDatabase<GeneDef>.GetNamedSilentFail("VRESaurids_Oviparous"), // VE Saurid
DefDatabase<GeneDef>.GetNamedSilentFail("VRE_SaplingBirth"), // VE Phytokin
};
egglayergenes.Remove(null);
return egglayergenes; return egglayergenes;
} }

View File

@ -1,7 +1,9 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<ModMetaData> <ModMetaData>
<packageId>rjw.menstruation</packageId>
<name>RJW Menstruation Cycle</name> <name>RJW Menstruation Cycle</name>
<author>lutepickle</author> <author>lutepickle</author>
<url>https://gitgud.io/lutepickle/rjw_menstruation/</url>
<supportedVersions> <supportedVersions>
<li>1.2</li> <li>1.2</li>
<li>1.3</li> <li>1.3</li>
@ -28,13 +30,17 @@
<li>Abraxas.RJW.RaceSupport</li> <li>Abraxas.RJW.RaceSupport</li>
<li>rjw.milk.humanoid</li> <li>rjw.milk.humanoid</li>
</loadAfter> </loadAfter>
<packageId>rjw.menstruation</packageId> <incompatibleWithByVersion>
<v1.4>
<li>conit.thebirdsandthebees</li> <!--Breaks fertility calculations-->
</v1.4>
</incompatibleWithByVersion>
<description>Adds menstruation mechanics to vaginas: <description>Adds menstruation mechanics to vaginas:
Wombs cycle between follicular, luteal, and bleeding phases Wombs cycle between follicular, luteal, and bleeding phases
Tracks eggs ovulated and cum in wombs to determine pregnancy Tracks eggs ovulated and cum in wombs to determine pregnancy
Womb icon and status window Womb icon and status window
Estrus effects Estrus and pheromone effects
Race-specific fetus images for many vanilla animals Race-specific fetus images for many vanilla animals
Pregnancies from multiple eggs and different fathers Pregnancies from multiple eggs and different fathers
Identical siblings Identical siblings

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.9.3</version> <version>1.0.9.4</version>
<dependencies> <dependencies>
</dependencies> </dependencies>
<incompatibleWith /> <incompatibleWith />

View File

@ -1,3 +1,8 @@
Version 1.0.9.4
- Added graphics for the menstruation genes with thanks to Alpenglow.
- Pawns in estrus now give their pheromones to their caravan-mates.
- Pawns with the egglaying genes from Erin's Corvyia and Outland - Genetics no longer have a menstrual cycle.
Version 1.0.9.3 Version 1.0.9.3
- The biosculptor egg restoration cycle will now give more eggs to races that ovulate more than one egg at a time. - The biosculptor egg restoration cycle will now give more eggs to races that ovulate more than one egg at a time.
- All pawns can now use all menstruation genes, regardless of gender or having a womb. - All pawns can now use all menstruation genes, regardless of gender or having a womb.