Initial implementation of variable update rate

This commit is contained in:
lutepickle 2023-04-13 20:17:25 -07:00
parent 2696c2b1f0
commit e37940abcf
13 changed files with 267 additions and 163 deletions

Binary file not shown.

View File

@ -56,6 +56,12 @@
<Option6_Desc>Cum will lose fertility by this amount every hour&#10;This value affects fertilization chance indirectly.</Option6_Desc>
<Option7_Label>Cycle acceleration</Option7_Label>
<Option7_Desc>Accelerate menstruation cycle&#10;This can cause early menopause and infertility.&#10;Setting this lower than x12 is recommended.&#10;Rimworld's timescale: x6(default)</Option7_Desc>
<Option_ColonistUpdateInterval_Label>Colonist update interval</Option_ColonistUpdateInterval_Label>
<Option_ColonistUpdateInterval_Desc>How often the womb of each of your colonists, prisoners, and slaves update.&#10;Lowering this will improve accuracy, increasing this can improve performance.</Option_ColonistUpdateInterval_Desc>
<Option_NonColonistUpdateInterval_Label>Non-colonist update interval</Option_NonColonistUpdateInterval_Label>
<Option_NonColonistUpdateInterval_Desc>How often the womb of humans you don't control update.&#10;Lowering this will improve accuracy, increasing this can improve performance.</Option_NonColonistUpdateInterval_Desc>
<Option_AnimalUpdateInterval_Label>Animal update interval</Option_AnimalUpdateInterval_Label>
<Option_AnimalUpdateInterval_Desc>How often the womb of animals update.&#10;Lowering this will improve accuracy, increasing this can improve performance.</Option_AnimalUpdateInterval_Desc>
<Option8_Label>Debug</Option8_Label>
<Option8_Desc>Show debug information.</Option8_Desc>
<Option9_Label>Womb status</Option9_Label>

View File

@ -1,4 +1,5 @@
using rjw;
using RimWorld;
using rjw;
using System;
using System.Collections.Generic;
using UnityEngine;
@ -17,6 +18,9 @@ namespace RJW_Menstruation
public const float CumFertilityDecayRatioDefault = 0.05f;
public const int CumFertilityDecayRatioAdjustDefault = 50;
public const int CycleAccelerationDefault = 6;
public const int ColonistTickIntervalDefault = 2500; // 1 hour
public const int NonColonistTickIntervalDefault = 2500;
public const int AnimalTickIntervalDefault = 2500;
public const float EnzygoticTwinsChanceDefault = 0.002f;
public const int EnzygoticTwinsChanceAdjustDefault = 2;
public const int MaxEnzygoticTwinsDefault = 9;
@ -41,6 +45,9 @@ namespace RJW_Menstruation
public static float CumFertilityDecayRatio = CumFertilityDecayRatioDefault;
public static int CumFertilityDecayRatioAdjust = CumFertilityDecayRatioAdjustDefault;
public static int CycleAcceleration = CycleAccelerationDefault;
public static int ColonistTickInterval = ColonistTickIntervalDefault;
public static int NonColonistTickInterval = NonColonistTickIntervalDefault;
public static int AnimalTickInterval = AnimalTickIntervalDefault;
public static bool EnableWombIcon = true;
public static bool EnableDraftedIcon = true;
public static bool EnableAnimalCycle = false;
@ -85,6 +92,9 @@ namespace RJW_Menstruation
EnableGatherCumGizmo = true;
EnableAnimalCycle = false;
CycleAcceleration = CycleAccelerationDefault;
ColonistTickInterval = ColonistTickIntervalDefault;
NonColonistTickInterval = NonColonistTickIntervalDefault;
AnimalTickInterval = AnimalTickIntervalDefault;
EstrusOverridesHookupSettings = false;
EstrusFuckabilityToHookup = RJWHookupSettings.MinimumFuckabilityToHookup;
EstrusAttractivenessToHookup = RJWHookupSettings.MinimumAttractivenessToHookup;
@ -188,6 +198,9 @@ namespace RJW_Menstruation
Scribe_Values.Look(ref CumFertilityDecayRatioAdjust, "CumFertilityDecayRatioAdjust", CumFertilityDecayRatioAdjust, true);
Scribe_Values.Look(ref CumFertilityDecayRatio, "CumFertilityDecayRatio", CumFertilityDecayRatio, true);
Scribe_Values.Look(ref CycleAcceleration, "CycleAcceleration", CycleAcceleration, true);
Scribe_Values.Look(ref ColonistTickInterval, "ColonistTickInterval", ColonistTickInterval, true);
Scribe_Values.Look(ref NonColonistTickInterval, "NonColonistTickInterval", NonColonistTickInterval, true);
Scribe_Values.Look(ref AnimalTickInterval, "AnimalTickInterval", AnimalTickInterval, true);
Scribe_Values.Look(ref EnableWombIcon, "EnableWombIcon", EnableWombIcon, true);
Scribe_Values.Look(ref EnableDraftedIcon, "EnableDraftedIcon", EnableDraftedIcon, true);
Scribe_Values.Look(ref EnableAnimalCycle, "EnableAnimalCycle", EnableAnimalCycle, true);
@ -274,8 +287,9 @@ namespace RJW_Menstruation
public override void DoSettingsWindowContents(Rect inRect)
{
Rect outRect = new Rect(0f, 30f, inRect.width, inRect.height - 30f);
float mainRectHeight = 30f +
float mainRectHeight = 126f +
(Configurations.EnableWombIcon || Configurations.EnableButtonInHT ? 400f : 0f) +
(Configurations.EnableAnimalCycle ? 48f : 0f) +
(Configurations.EstrusOverridesHookupSettings ? 144f : 0f) +
(Configurations.PregnancySource == Configurations.PregnancyType.MultiplePregnancy ? (Configurations.EnableEnzygoticTwins ? 175f : 75f) : 0f) +
(Configurations.PregnancySource == Configurations.PregnancyType.Biotech ? 75f : 0f) +
@ -403,11 +417,11 @@ namespace RJW_Menstruation
string estimatedlifespan;
if (semenlifespan < 0)
{
estimatedlifespan = String.Format(": Infinite", semenlifespan);
estimatedlifespan = string.Format(": Infinite", semenlifespan);
}
else
{
estimatedlifespan = String.Format(": {0:0}h", semenlifespan);
estimatedlifespan = string.Format(": {0:0}h", semenlifespan);
}
listmain.LabelDouble(Translations.Option6_Label + " " + Configurations.CumFertilityDecayRatio * 100 + "%", Translations.EstimatedCumLifespan + estimatedlifespan, Translations.Option6_Desc);
Configurations.CumFertilityDecayRatioAdjust = (int)listmain.Slider(Configurations.CumFertilityDecayRatioAdjust, 0, 1000);
@ -416,6 +430,20 @@ namespace RJW_Menstruation
listmain.Label(Translations.Option7_Label + " x" + Configurations.CycleAcceleration, -1, Translations.Option7_Desc);
Configurations.CycleAcceleration = (int)listmain.Slider(Configurations.CycleAcceleration, 1, 50);
Adjust = Configurations.ColonistTickInterval / 25;
listmain.LabelDouble(Translations.Option_ColonistUpdateInterval_Label, GenDate.ToStringTicksToPeriod(Configurations.ColonistTickInterval), Translations.Option_ColonistUpdateInterval_Desc);
Configurations.ColonistTickInterval = (int)listmain.Slider(Adjust, 0, 400) * 25;
Adjust = Configurations.NonColonistTickInterval / 25;
listmain.LabelDouble(Translations.Option_NonColonistUpdateInterval_Label, GenDate.ToStringTicksToPeriod(Configurations.NonColonistTickInterval), Translations.Option_NonColonistUpdateInterval_Desc);
Configurations.NonColonistTickInterval = (int)listmain.Slider(Adjust, 0, 400) * 25;
if(Configurations.EnableAnimalCycle)
{
Adjust = Configurations.AnimalTickInterval / 25;
listmain.LabelDouble(Translations.Option_AnimalUpdateInterval_Label, GenDate.ToStringTicksToPeriod(Configurations.AnimalTickInterval), Translations.Option_AnimalUpdateInterval_Desc);
Configurations.AnimalTickInterval = (int)listmain.Slider(Adjust, 0, 400) * 25;
}
float var2 = EstimatedBleedingAmountPerHour;
float var1 = Math.Max(EstimatedBleedingAmount, var2);

View File

@ -205,8 +205,10 @@ namespace RJW_Menstruation
{
// comp is used for Hydrogen's RJW Muscle Injury
float totalleak = volume;
volume *= Math.Max(0, (1 - (Configurations.CumDecayRatio * (1 - DecayResist)) * leakfactor));
fertility *= Math.Max(0, 1 - (Configurations.CumFertilityDecayRatio * (1 - DecayResist) + antisperm));
float decayPerInterval = 1 - Mathf.Pow(1 - Configurations.CumDecayRatio, comp.TickInterval / GenDate.TicksPerHour);
float fertilityDecayPerInterval = 1 - Mathf.Pow(Configurations.CumFertilityDecayRatio, comp.TickInterval / GenDate.TicksPerHour);
volume *= Math.Max(0, 1 - decayPerInterval * (1 - DecayResist) * leakfactor);
fertility *= Math.Max(0, 1 - (fertilityDecayPerInterval * (1 - DecayResist) + antisperm));
CutMinor();
totalleak -= volume;
return totalleak;

View File

@ -70,7 +70,7 @@ namespace RJW_Menstruation
case Stage.Ovulatory:
return true;
case Stage.Luteal:
return IsEggExist && curStageHrs < EggLifespanHours;
return IsEggExist && curStageTicks < EggLifespanTicks;
default:
return false;
}
@ -84,11 +84,11 @@ namespace RJW_Menstruation
switch (curStage)
{
case Stage.Follicular:
return curStageHrs > currentIntervalHours - Props.estrusDaysBeforeOvulation * 24;
return curStageTicks > currentIntervalTicks - Props.estrusDaysBeforeOvulation * GenDate.TicksPerDay;
case Stage.Ovulatory:
return true;
case Stage.Luteal:
return IsEggExist && curStageHrs < EggLifespanHours;
return IsEggExist && curStageTicks < EggLifespanTicks;
default:
return false;
}

View File

@ -29,6 +29,7 @@ namespace RJW_Menstruation
public float baseImplantationChanceFactor;
public float basefertilizationChanceFactor;
public int follicularIntervalDays = 14; //before ovulation including beginning of bleeding
public int ovulationIntervalHours = 12; //between the end of follicular and the egg emerging
public int lutealIntervalDays = 14; //after ovulation until bleeding
public int bleedingIntervalDays = 6; //must be less than folicularIntervalDays
public int recoveryIntervalDays = 10; //additional infertile days after gave birth
@ -63,17 +64,16 @@ namespace RJW_Menstruation
public class HediffComp_Menstruation : HediffComp
{
const float minmakefilthvalue = 1.0f;
//const int ovarypowerthreshold = 72;
const int tickInterval = GenDate.TicksPerHour;
const int maxImplantDelayHours = 30 * 24;
const int minImplantAgeHours = 3 * 24;
const int maxImplantDelayHours = 30 * GenDate.HoursPerDay;
const int minImplantAgeHours = 3 * GenDate.HoursPerDay;
const float pulloutSuccessRate = 0.8f;
const float fetishPulloutSuccessModifier = 0.25f;
public CompProperties_Menstruation Props;
public Stage curStage = Stage.Follicular;
public int curStageHrs = 0;
public int curStageTicks = 0; // Actual number of ticks equals this / cycleAcceleration
private int tickInterval = -1;
const int recalculateTickInterval = GenDate.TicksPerDay;
public bool loaded = false;
public bool initError = false;
public int ovarypower = -100000;
@ -114,7 +114,7 @@ namespace RJW_Menstruation
protected List<Egg> eggs;
protected float cycleSpeed = -1;
protected float cycleVariability = -1;
protected int currentIntervalHours = -1;
protected int currentIntervalTicks = -1; // Actual number of ticks equals this / cycleAcceleration
protected float crampPain = -1;
protected Need sexNeed = null;
protected string customwombtex = null;
@ -126,7 +126,7 @@ namespace RJW_Menstruation
// RJW pregnancy, or Biotech pregnancy/labor/laborpushing
protected Hediff pregnancy = null;
protected int eggLifeSpanHours = 48;
protected int eggLifeSpanTicks = 2 * GenDate.TicksPerDay;
protected EstrusLevel estrusLevel = EstrusLevel.Visible;
protected float ovulationFactor = 1f;
protected bool noBleeding = false;
@ -155,6 +155,21 @@ namespace RJW_Menstruation
new CurvePoint(1.0f,0.5f)
};
public int TickInterval
{
get
{
if (tickInterval <= 0)
{
if (Pawn.IsAnimal()) tickInterval = Configurations.AnimalTickInterval;
else if (Pawn.IsColonist || Pawn.IsPrisonerOfColony || Pawn.IsSlaveOfColony) tickInterval = Configurations.ColonistTickInterval;
else tickInterval = Configurations.NonColonistTickInterval;
if (tickInterval <= 0) tickInterval = 1;
}
return tickInterval;
}
}
public Hediff Pregnancy {
get
{
@ -480,12 +495,12 @@ namespace RJW_Menstruation
/// <summary>
/// returns fertstage. if not fertilized returns -1
/// </summary>
public int IsFertilized
public int EggFertilizedTime
{
get
{
if (eggs?.All(egg => !egg.fertilized) ?? true) return -1;
return eggs.Max(egg => egg.fertstage);
return eggs.Max(egg => egg.ticksSinceFertilization);
}
}
public IEnumerable<Pawn> GetCummersAndFertilizers()
@ -502,9 +517,9 @@ namespace RJW_Menstruation
get => !eggs.NullOrEmpty();
}
public int EggLifespanHours
public int EggLifespanTicks
{
get => eggLifeSpanHours;
get => eggLifeSpanTicks;
}
public virtual bool IsDangerDay
@ -516,11 +531,11 @@ namespace RJW_Menstruation
switch (curStage)
{
case Stage.Follicular:
return curStageHrs > 0.7f * currentIntervalHours;
return curStageTicks > 0.7f * currentIntervalTicks;
case Stage.Ovulatory:
return true;
case Stage.Luteal:
return curStageHrs < EggLifespanHours;
return curStageTicks < EggLifespanTicks;
default:
return false;
}
@ -559,16 +574,16 @@ namespace RJW_Menstruation
set => originvagsize = value;
}
public float CurStageIntervalHours
public int CurStageIntervalTicks
{
get => currentIntervalHours;
get => currentIntervalTicks;
}
public float StageProgress
{
get
{
if (pregnancy == null) return Mathf.Clamp01(curStageHrs / CurStageIntervalHours);
if (pregnancy == null) return Mathf.Clamp01((float)curStageTicks / currentIntervalTicks);
bool is_discovered = false;
switch (pregnancy)
{
@ -590,7 +605,7 @@ namespace RJW_Menstruation
if (Pawn.story?.bodyType == BodyTypeDefOf.Thin) discoveryTime = 0.25f;
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
float lutealProgressWhenImplanted = Math.Min(0.5f, maxImplantDelayHours / (Props.lutealIntervalDays * 24));
float lutealProgressWhenImplanted = Math.Min(0.5f, maxImplantDelayHours / (Props.lutealIntervalDays * GenDate.HoursPerDay));
return GenMath.LerpDouble(0, discoveryTime, lutealProgressWhenImplanted, 1.0f, pregnancy.Severity);
}
@ -608,13 +623,23 @@ namespace RJW_Menstruation
public override void CompExposeData()
{
base.CompExposeData();
if (Scribe.mode == LoadSaveMode.LoadingVars)
{
int curStageHrs = -1;
int currentIntervalHours = -1;
Scribe_Values.Look(ref curStageHrs, "curStageHrs", curStageHrs, true);
Scribe_Values.Look(ref currentIntervalHours, "currentIntervalHours", currentIntervalHours, true);
if (curStageHrs >= 0) curStageTicks = curStageHrs * GenDate.TicksPerHour;
if (currentIntervalHours >= 0) currentIntervalTicks = currentIntervalHours * GenDate.TicksPerHour;
}
Scribe_Collections.Look(ref cums, saveDestroyedThings: true, label: "cums", lookMode: LookMode.Deep, ctorArgs: new object[0]);
Scribe_Collections.Look(ref eggs, saveDestroyedThings: true, label: "eggs", lookMode: LookMode.Deep, ctorArgs: new object[0]);
Scribe_Values.Look(ref curStage, "curStage", curStage, true);
Scribe_Values.Look(ref curStageHrs, "curStageHrs", curStageHrs, true);
Scribe_Values.Look(ref curStageTicks, "curStageTicks", curStageTicks, true);
Scribe_Values.Look(ref cycleSpeed, "cycleSpeed", cycleSpeed, true);
Scribe_Values.Look(ref cycleVariability, "cycleVariability", cycleVariability, true);
Scribe_Values.Look(ref currentIntervalHours, "currentIntervalHours", currentIntervalHours, true);
Scribe_Values.Look(ref currentIntervalTicks, "currentIntervalTicks", currentIntervalTicks, true);
Scribe_Values.Look(ref crampPain, "crampPain", crampPain, true);
Scribe_Values.Look(ref ovarypower, "ovarypower", ovarypower, true);
Scribe_Values.Look(ref eggstack, "eggstack", eggstack, true);
@ -635,16 +660,16 @@ namespace RJW_Menstruation
public void Notify_UpdatedGenes()
{
eggLifeSpanHours = Props.eggLifespanDays * 24;
eggLifeSpanTicks = Props.eggLifespanDays * GenDate.TicksPerDay;
estrusLevel = Props.concealedEstrus ? EstrusLevel.Concealed : EstrusLevel.Visible;
ovulationFactor = 1f;
noBleeding = false;
if (Pawn.genes == null || !ModsConfig.BiotechActive) return;
if (Pawn.genes.HasGene(VariousDefOf.ShortEggLifetime)) eggLifeSpanHours = eggLifeSpanHours * 3 / 4;
else if (Pawn.genes.HasGene(VariousDefOf.DoubleEggLifetime)) eggLifeSpanHours *= 2;
else if (Pawn.genes.HasGene(VariousDefOf.QuadEggLifetime)) eggLifeSpanHours *= 4;
if (Pawn.genes.HasGene(VariousDefOf.ShortEggLifetime)) eggLifeSpanTicks = eggLifeSpanTicks * 3 / 4;
else if (Pawn.genes.HasGene(VariousDefOf.DoubleEggLifetime)) eggLifeSpanTicks *= 2;
else if (Pawn.genes.HasGene(VariousDefOf.QuadEggLifetime)) eggLifeSpanTicks *= 4;
if (Pawn.genes.HasGene(VariousDefOf.NeverEstrus)) estrusLevel = EstrusLevel.None;
else if (Pawn.genes.HasGene(VariousDefOf.FullEstrus)) estrusLevel = EstrusLevel.Visible;
@ -688,7 +713,8 @@ namespace RJW_Menstruation
Initialize();
}
if (!Pawn.IsHashIntervalTick(tickInterval)) return;
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 (initError) Log.Warning($"Attempting to process {Pawn}'s womb uninitialized");
@ -784,9 +810,9 @@ namespace RJW_Menstruation
}
}
protected virtual int HoursToNextStage()
protected virtual int TicksToNextStage()
{
return Math.Max(0,(currentIntervalHours - curStageHrs) / Configurations.CycleAcceleration);
return Math.Max(0,(currentIntervalTicks - curStageTicks) / Configurations.CycleAcceleration);
}
public override string CompDebugString()
@ -794,7 +820,7 @@ namespace RJW_Menstruation
if (curStage == Stage.None || curStage == Stage.Infertile || curStage == Stage.Pregnant) return base.CompDebugString();
StringBuilder debugString = new StringBuilder();
debugString.Append($"Time to next state: ");
debugString.Append(GenDate.ToStringTicksToPeriod(HoursToNextStage() * GenDate.TicksPerHour));
debugString.Append(GenDate.ToStringTicksToPeriod(TicksToNextStage()));
return debugString.ToString();
}
@ -805,11 +831,7 @@ namespace RJW_Menstruation
/// <returns></returns>
public Cum GetNotCum(string notcumlabel)
{
if (!cums.NullOrEmpty()) foreach (Cum cum in cums)
{
if (cum.notcum && cum.notcumLabel.Equals(notcumlabel)) return cum;
}
return null;
return cums?.Find(cum => cum.notcum && cum.notcumLabel.Equals(notcumlabel));
}
/// <summary>
@ -825,17 +847,17 @@ namespace RJW_Menstruation
/// <summary>
/// Inject pawn's cum into womb
/// </summary>
/// <param name="pawn"></param>
/// <param name="cummer"></param>
/// <param name="volume"></param>
/// <param name="fertility"></param>
/// <param name="precum"></param>
public void CumIn(Pawn pawn, float volume, float fertility = 1.0f, bool precum = false)
public void CumIn(Pawn cummer, float volume, float fertility = 1.0f, bool precum = false)
{
if (volume <= 0) return;
if (!precum && fertility > 0 && IsDangerDay && pawn.relations.GetPregnancyApproachForPartner(Pawn) == PregnancyApproach.AvoidPregnancy)
if (!precum && fertility > 0 && IsDangerDay && cummer.relations.GetPregnancyApproachForPartner(Pawn) == PregnancyApproach.AvoidPregnancy)
{
float successChance = pulloutSuccessRate;
if (pawn.HasQuirk(QuirkUtility.Quirks.ImpregnationFetish)) successChance *= fetishPulloutSuccessModifier;
if (cummer.HasQuirk(QuirkUtility.Quirks.ImpregnationFetish)) successChance *= fetishPulloutSuccessModifier;
if (Pawn.HasQuirk(QuirkUtility.Quirks.ImpregnationFetish)) successChance *= fetishPulloutSuccessModifier;
if (Rand.Chance(successChance)) return;
}
@ -848,14 +870,14 @@ namespace RJW_Menstruation
bool merged = false;
if (!cums.NullOrEmpty()) foreach (Cum cum in cums)
{
if (cum.pawn.Equals(pawn))
if (cum.pawn.Equals(cummer))
{
cum.MergeWithCum(volume, fertility);
merged = true;
}
cum.DismishForce(cumoutrate);
}
if (!merged) cums.Add(new Cum(pawn, volume * (1 - cumoutrate), fertility));
if (!merged) cums.Add(new Cum(cummer, volume * (1 - cumoutrate), fertility));
}
else
{
@ -863,20 +885,20 @@ namespace RJW_Menstruation
bool merged = false;
if (!cums.NullOrEmpty()) foreach (Cum cum in cums)
{
if (cum.pawn.Equals(pawn))
if (cum.pawn.Equals(cummer))
{
cum.MergeWithCum(volume, fertility);
merged = true;
}
}
if (!merged) cums.Add(new Cum(pawn, volume, fertility));
if (!merged) cums.Add(new Cum(cummer, volume, fertility));
}
cumd = TotalCumPercent - cumd;
if (!precum)
{
Pawn.records.AddTo(VariousDefOf.AmountofCreampied, volume);
AfterCumIn(pawn);
AfterCumIn(cummer);
AfterFluidIn(cumd);
}
}
@ -884,12 +906,12 @@ namespace RJW_Menstruation
/// <summary>
/// Inject pawn's fluid into womb
/// </summary>
/// <param name="pawn"></param>
/// <param name="cummer"></param>
/// <param name="volume"></param>
/// <param name="notcumlabel"></param>
/// <param name="decayresist"></param>
/// <param name="filthdef"></param>
public void CumIn(Pawn pawn, float volume, string notcumlabel, float decayresist = 0, ThingDef filthdef = null)
public void CumIn(Pawn cummer, float volume, string notcumlabel, float decayresist = 0, ThingDef filthdef = null)
{
if (volume <= 0) return;
float tmp = TotalCum + volume;
@ -900,14 +922,14 @@ namespace RJW_Menstruation
bool merged = false;
if (!cums.NullOrEmpty()) foreach (Cum cum in cums)
{
if (cum.notcum && cum.pawn.Equals(pawn) && cum.notcumLabel.Equals(notcumlabel))
if (cum.notcum && cum.pawn.Equals(cummer) && cum.notcumLabel.Equals(notcumlabel))
{
cum.MergeWithFluid(volume, decayresist, filthdef);
merged = true;
}
cum.DismishForce(cumoutrate);
}
if (!merged) cums.Add(new Cum(pawn, volume * (1 - cumoutrate), notcumlabel, decayresist, filthdef));
if (!merged) cums.Add(new Cum(cummer, volume * (1 - cumoutrate), notcumlabel, decayresist, filthdef));
}
else
{
@ -915,13 +937,13 @@ namespace RJW_Menstruation
bool merged = false;
if (!cums.NullOrEmpty()) foreach (Cum cum in cums)
{
if (cum.notcum && cum.pawn.Equals(pawn) && cum.notcumLabel.Equals(notcumlabel))
if (cum.notcum && cum.pawn.Equals(cummer) && cum.notcumLabel.Equals(notcumlabel))
{
cum.MergeWithFluid(volume, decayresist, filthdef);
merged = true;
}
}
if (!merged) cums.Add(new Cum(pawn, volume, notcumlabel, decayresist, filthdef));
if (!merged) cums.Add(new Cum(cummer, volume, notcumlabel, decayresist, filthdef));
}
cumd = TotalCumPercent - cumd;
AfterNotCumIn();
@ -961,8 +983,8 @@ namespace RJW_Menstruation
absorber = (Absorber)Pawn.apparel?.WornApparel?.Find(x => x is Absorber);
if (absorber != null)
{
absorber.WearEffect();
if (absorber.dirty && absorber.EffectAfterDirty) absorber.DirtyEffect();
absorber.WearEffect(TickInterval);
if (absorber.dirty && absorber.EffectAfterDirty) absorber.DirtyEffect(TickInterval);
}
}
@ -994,13 +1016,13 @@ namespace RJW_Menstruation
float leakfactor = 1.0f;
float totalleak = 0f;
float cumd = TotalCumPercent;
int preCumAmount = Mathf.CeilToInt(TotalCum);
List<string> filthlabels = new List<string>();
BeforeCumOut(out Absorber absorber);
if (cums.NullOrEmpty()) return;
if (TotalCum > Props.maxCumCapacity * Pawn.BodySize) leakfactor = Math.Min(1 + (TotalCum - Props.maxCumCapacity * Pawn.BodySize) / 10, 2f);
if (absorber != null && absorber.dirty && !absorber.LeakAfterDirty) leakfactor = 0f;
if (Pawn.CurJobDef == xxx.knotted) leakfactor = 0f;
HashSet<Cum> removecums = new HashSet<Cum>();
foreach (Cum cum in cums)
{
cum.CumEffects(Pawn);
@ -1009,11 +1031,14 @@ namespace RJW_Menstruation
totalleak += AbsorbCum(vd, absorber);
string tmp = "FilthLabelWithSource".Translate(cum.FilthDef.label, cum.pawn?.LabelShort ?? "Unknown", 1.ToString());
filthlabels.Add(tmp.Replace(" x1", ""));
if (cum.ShouldRemove()) removecums.Add(cum);
}
if (cums.Count > 1) MakeCumFilthMixture(totalleak, filthlabels);
else if (cums.Count == 1) MakeCumFilth(cums.First(), totalleak);
cums.RemoveAll(cum => removecums.Contains(cum));
int postCumAmount = Mathf.CeilToInt(TotalCum);
for (int i = 0; i < postCumAmount - preCumAmount; i++) // Emit a filth every time the integer cum amount drops
{
if (cums.Count > 1) MakeCumFilthMixture(totalleak, filthlabels);
else if (cums.Count == 1) MakeCumFilth(cums.First(), totalleak);
}
cums.RemoveAll(cum => cum.ShouldRemove());
cumd = TotalCumPercent - cumd;
if (totalleak >= 1.0f) AfterCumOut();
AfterFluidOut(cumd);
@ -1127,17 +1152,17 @@ namespace RJW_Menstruation
InitOvary();
if (currentIntervalHours < 0)
if (currentIntervalTicks < 0)
{
if (ShouldBeInfertile()) curStage = Stage.Infertile;
else if (!IsBreedingSeason()) curStage = Stage.Anestrus;
else curStage = RandomStage();
if (curStage == Stage.Follicular)
currentIntervalHours = PeriodRandomizer(Stage.Follicular) - PeriodRandomizer(Stage.Bleeding);
currentIntervalTicks = PeriodRandomizer(Stage.Follicular) - PeriodRandomizer(Stage.Bleeding);
else
currentIntervalHours = PeriodRandomizer(curStage);
if (currentIntervalHours <= 0) currentIntervalHours = 1;
else if (currentIntervalHours < curStageHrs) curStageHrs = currentIntervalHours;
currentIntervalTicks = PeriodRandomizer(curStage);
if (currentIntervalTicks < 0) currentIntervalTicks = 0;
else if (currentIntervalTicks < curStageTicks) curStageTicks = currentIntervalTicks;
}
if (crampPain < 0) crampPain = PainRandomizer();
InitializeExtraValues();
@ -1242,11 +1267,11 @@ namespace RJW_Menstruation
switch (curStage)
{
case Stage.Follicular:
return curStageHrs > currentIntervalHours - Props.estrusDaysBeforeOvulation * 24;
return curStageTicks > currentIntervalTicks - Props.estrusDaysBeforeOvulation * GenDate.TicksPerDay;
case Stage.Ovulatory:
return true;
case Stage.Luteal:
return curStageHrs < EggLifespanHours;
return curStageTicks < EggLifespanTicks;
default:
return false;
}
@ -1296,9 +1321,11 @@ namespace RJW_Menstruation
if (eligibleCum.Count == 0) return null;
float totalFertPower = eligibleCum.Sum(cum => cum.FertVolume);
float fertFailChancePerHour = Mathf.Pow(1.0f - Configurations.FertilizeChance, totalFertPower * Props.basefertilizationChanceFactor);
float fertFailChancePerInterval = Mathf.Pow(fertFailChancePerHour, (float)TickInterval / GenDate.TicksPerHour);
if (Rand.Chance(Mathf.Pow(1.0f - Configurations.FertilizeChance, totalFertPower * Props.basefertilizationChanceFactor)))
return null;
if (Rand.Chance(fertFailChancePerInterval)) return null;
Pawn.records.AddTo(VariousDefOf.AmountofFertilizedEggs, 1);
float selection = Rand.Range(0.0f, totalFertPower);
@ -1323,8 +1350,8 @@ namespace RJW_Menstruation
foreach (Egg egg in eggs)
{
if (!egg.fertilized ||
egg.fertstage < minImplantAgeHours ||
egg.position < Math.Min(Props.lutealIntervalDays * 24 / 2, maxImplantDelayHours))
egg.ticksSinceFertilization < minImplantAgeHours * GenDate.TicksPerHour ||
egg.ageTicks < Math.Min(Props.lutealIntervalDays * GenDate.TicksPerDay / 2, maxImplantDelayHours * GenDate.TicksPerHour))
continue;
else if (egg.fertilizer == null)
{
@ -1408,8 +1435,8 @@ namespace RJW_Menstruation
if (pregnancy is Hediff_BasePregnancy rjw_preg)
{
// TODO: advance biotech pregnancy
rjw_preg.p_start_tick -= egg.fertstage / Configurations.CycleAcceleration * GenDate.TicksPerHour;
rjw_preg.p_end_tick -= egg.fertstage / Configurations.CycleAcceleration * GenDate.TicksPerHour;
rjw_preg.p_start_tick -= egg.ticksSinceFertilization / Configurations.CycleAcceleration;
rjw_preg.p_end_tick -= egg.ticksSinceFertilization / Configurations.CycleAcceleration;
}
}
}
@ -1434,7 +1461,9 @@ namespace RJW_Menstruation
protected void BleedOut()
{
CumIn(Pawn, Rand.Range(0.02f * Configurations.BleedingAmount, 0.04f * Configurations.BleedingAmount), Translations.Menstrual_Blood, -5.0f, Pawn.def.race?.BloodDef ?? ThingDefOf.Filth_Blood);
// ~1.5 per hour times acceleration
float bledAmount = 0.03f * Configurations.BleedingAmount * Configurations.CycleAcceleration * Rand.Range(0.5f, 1.5f) * TickInterval / GenDate.TicksPerHour;
CumIn(Pawn, bledAmount, Translations.Menstrual_Blood, -5.0f, Pawn.def.race?.BloodDef ?? ThingDefOf.Filth_Blood);
Cum blood = GetNotCum(Translations.Menstrual_Blood);
if (blood != null) blood.Color = BloodColor;
}
@ -1501,12 +1530,12 @@ namespace RJW_Menstruation
HashSet<Egg> deadeggs = new HashSet<Egg>();
foreach (Egg egg in eggs)
{
egg.position += Configurations.CycleAcceleration;
if (egg.fertilized) egg.fertstage += Configurations.CycleAcceleration;
egg.ageTicks += TickInterval * Configurations.CycleAcceleration;
if (egg.fertilized) egg.ticksSinceFertilization += TickInterval * Configurations.CycleAcceleration;
else
{
egg.lifespanhrs -= Configurations.CycleAcceleration;
if (egg.lifespanhrs < 0) deadeggs.Add(egg);
egg.lifeSpanTicks -= TickInterval * Configurations.CycleAcceleration;
if (egg.lifeSpanTicks < 0) deadeggs.Add(egg);
}
}
eggs.RemoveAll(egg => deadeggs.Contains(egg));
@ -1517,7 +1546,7 @@ namespace RJW_Menstruation
Hediff hediff = HediffMaker.MakeHediff(VariousDefOf.Hediff_MenstrualCramp, Pawn);
hediff.Severity = crampPain * Rand.Range(0.9f, 1.1f);
HediffCompProperties_SeverityPerDay Prop = (HediffCompProperties_SeverityPerDay)hediff.TryGetComp<HediffComp_SeverityPerDay>().props;
Prop.severityPerDay = -hediff.Severity / (currentIntervalHours / 24) * Configurations.CycleAcceleration;
Prop.severityPerDay = -hediff.Severity / (currentIntervalTicks / GenDate.TicksPerDay) * Configurations.CycleAcceleration;
Pawn.health.AddHediff(hediff, Genital_Helper.get_genitalsBPR(Pawn));
}
@ -1529,14 +1558,14 @@ namespace RJW_Menstruation
GoNextStage(Stage.Anestrus);
return;
}
else if (curStageHrs >= currentIntervalHours)
else if (curStageTicks >= currentIntervalTicks)
{
GoOvulatoryStage();
}
else
{
curStageHrs += Configurations.CycleAcceleration;
if (!estrusflag && curStageHrs > currentIntervalHours - Props.estrusDaysBeforeOvulation * 24)
curStageTicks += TickInterval * Configurations.CycleAcceleration;
if (!estrusflag && curStageTicks > currentIntervalTicks - Props.estrusDaysBeforeOvulation * GenDate.TicksPerDay)
{
estrusflag = true;
SetEstrus();
@ -1547,6 +1576,11 @@ namespace RJW_Menstruation
protected virtual void OvulatoryAction()
{
if (curStageTicks < currentIntervalTicks)
{
curStageTicks += TickInterval * Configurations.CycleAcceleration;
return;
}
estrusflag = false;
float eggnum;
try
@ -1570,7 +1604,7 @@ namespace RJW_Menstruation
for (int i = 0; i < toOvulate; i++)
if (i < eggstack || Rand.Chance(ovulationChance)) // eggstack comes from drugs and are guaranteed ovulated
{
eggs.Add(new Egg((int)(EggLifespanHours / CycleFactor)));
eggs.Add(new Egg((int)(EggLifespanTicks / CycleFactor)));
++ovulated;
}
ovarypower -= ovulated;
@ -1583,7 +1617,7 @@ namespace RJW_Menstruation
protected virtual void LutealAction()
{
if (curStageHrs >= currentIntervalHours)
if (curStageTicks >= currentIntervalTicks)
{
eggs.Clear();
if (EggHealth < 1f / 4f || (EggHealth < 1f / 3f && Rand.Chance(0.3f))) //skips bleeding
@ -1605,13 +1639,13 @@ namespace RJW_Menstruation
}
else
{
curStageHrs += Configurations.CycleAcceleration;
curStageTicks += TickInterval * Configurations.CycleAcceleration;
StayCurrentStage();
}
}
else
{
curStageHrs += Configurations.CycleAcceleration;
curStageTicks += TickInterval * Configurations.CycleAcceleration;
StayCurrentStage();
}
@ -1619,23 +1653,23 @@ namespace RJW_Menstruation
protected virtual void BleedingAction()
{
if (curStageHrs >= currentIntervalHours)
if (curStageTicks >= currentIntervalTicks)
{
Hediff hediff = Pawn.health.hediffSet.GetFirstHediffOfDef(VariousDefOf.Hediff_MenstrualCramp);
if (hediff != null && !Pawn.GetMenstruationComps().Any(comp => comp != this && comp.curStage == Stage.Bleeding)) Pawn.health.RemoveHediff(hediff);
int totalFollicularHours = PeriodRandomizer(Stage.Follicular); // The total amount of time for both bleeding and follicular
if (totalFollicularHours <= currentIntervalHours) // We've bled for so long that we completely missed the follicular phase
int totalFollicularTicks = PeriodRandomizer(Stage.Follicular); // The total amount of time for both bleeding and follicular
if (totalFollicularTicks <= currentIntervalTicks) // We've bled for so long that we completely missed the follicular phase
GoOvulatoryStage();
else
{
currentIntervalHours = totalFollicularHours - currentIntervalHours; // I.e., the remaining follicular hours equals the total minus the bleeding hours elapsed
currentIntervalTicks = totalFollicularTicks - currentIntervalTicks; // I.e., the remaining follicular time equals the total minus the bleeding time elapsed
GoNextStage(Stage.Follicular, false);
}
}
else
{
if (curStageHrs < currentIntervalHours / 4) for (int i = 0; i < Configurations.CycleAcceleration; i++) BleedOut();
curStageHrs += Configurations.CycleAcceleration;
if (curStageTicks < currentIntervalTicks / 4) BleedOut();
curStageTicks += TickInterval * Configurations.CycleAcceleration;
StayCurrentStage();
}
}
@ -1651,7 +1685,7 @@ namespace RJW_Menstruation
if (pregnancy != null && Pawn.health.hediffSet.hediffs.Contains(pregnancy))
{
curStageHrs += 1;
curStageTicks += TickInterval;
StayCurrentStageConst(Stage.Pregnant);
}
else
@ -1663,7 +1697,7 @@ namespace RJW_Menstruation
protected virtual void RecoverAction()
{
if (curStageHrs >= currentIntervalHours)
if (curStageTicks >= currentIntervalTicks)
{
if (ShouldBeInfertile())
{
@ -1680,7 +1714,7 @@ namespace RJW_Menstruation
}
else
{
curStageHrs += Configurations.CycleAcceleration;
curStageTicks += TickInterval * Configurations.CycleAcceleration;
StayCurrentStage();
}
}
@ -1776,8 +1810,8 @@ namespace RJW_Menstruation
public void GoNextStage(Stage nextstage, bool calculateHours = true)
{
curStageHrs = 0;
if (calculateHours) currentIntervalHours = PeriodRandomizer(nextstage);
curStageTicks = 0;
if (calculateHours) currentIntervalTicks = PeriodRandomizer(nextstage);
curStage = nextstage;
}
@ -1819,15 +1853,17 @@ namespace RJW_Menstruation
switch (stage)
{
case Stage.Follicular:
return (int)(Props.follicularIntervalDays * 24 * (1 + Rand.Range(-cycleVariability, cycleVariability) * 1.5f * variabilityFactor) / (1 + (cycleSpeed - 1) * 1.5f));
return (int)(Props.follicularIntervalDays * GenDate.TicksPerDay * (1 + Rand.Range(-cycleVariability, cycleVariability) * 1.5f * variabilityFactor) / (1 + (cycleSpeed - 1) * 1.5f));
case Stage.Ovulatory:
return Props.ovulationIntervalHours * GenDate.TicksPerHour; // No variability for now
case Stage.Luteal:
return (int)(Props.lutealIntervalDays * 24 * (1 + Rand.Range(-cycleVariability, cycleVariability) * 0.5f * variabilityFactor) / (1 + (cycleSpeed - 1) * 0.5f));
return (int)(Props.lutealIntervalDays * GenDate.TicksPerDay * (1 + Rand.Range(-cycleVariability, cycleVariability) * 0.5f * variabilityFactor) / (1 + (cycleSpeed - 1) * 0.5f));
case Stage.Bleeding:
return (int)(Props.bleedingIntervalDays * 24 * (1 + Rand.Range(-cycleVariability, cycleVariability) * 0.5f * variabilityFactor) / (1 + (cycleSpeed - 1) * 0.5f));
return (int)(Props.bleedingIntervalDays * GenDate.TicksPerDay * (1 + Rand.Range(-cycleVariability, cycleVariability) * 0.5f * variabilityFactor) / (1 + (cycleSpeed - 1) * 0.5f));
case Stage.Recover:
return (int)(Props.recoveryIntervalDays * 24 * Rand.Range(0.95f, 1.05f));
return (int)(Props.recoveryIntervalDays * GenDate.TicksPerDay * Rand.Range(0.95f, 1.05f));
case Stage.Pregnant:
return (int)MenstruationUtility.GestationHours(pregnancy);
return (int)(MenstruationUtility.GestationHours(pregnancy) * GenDate.TicksPerHour);
default:
return 1;
}
@ -1863,13 +1899,13 @@ namespace RJW_Menstruation
switch (stage)
{
case Stage.Follicular:
curStageHrs = Rand.Range(0, (Props.follicularIntervalDays - Props.bleedingIntervalDays) * 24);
curStageTicks = Rand.Range(0, (Props.follicularIntervalDays - Props.bleedingIntervalDays) * GenDate.TicksPerDay);
break;
case Stage.Luteal:
curStageHrs = Rand.Range(0, Props.lutealIntervalDays * 24);
curStageTicks = Rand.Range(0, Props.lutealIntervalDays * GenDate.TicksPerDay);
break;
case Stage.Bleeding:
curStageHrs = Rand.Range(0, Props.bleedingIntervalDays * 24);
curStageTicks = Rand.Range(0, Props.bleedingIntervalDays * GenDate.TicksPerDay);
break;
}
return stage;
@ -1915,34 +1951,46 @@ namespace RJW_Menstruation
public class Egg : IExposable
{
public bool fertilized;
public int lifespanhrs;
public int lifeSpanTicks;
public Pawn fertilizer;
public int position;
public int fertstage = 0;
public int ageTicks;
public int ticksSinceFertilization = 0;
public Egg()
{
fertilized = false;
lifespanhrs = (int)(96 * Configurations.EggLifespanMultiplier);
lifeSpanTicks = (int)(96 * GenDate.TicksPerHour * Configurations.EggLifespanMultiplier);
fertilizer = null;
position = 0;
ageTicks = 0;
}
public Egg(int lifespanhrs)
{
fertilized = false;
this.lifespanhrs = (int)(lifespanhrs * Configurations.EggLifespanMultiplier);
lifeSpanTicks = (int)(lifespanhrs * GenDate.TicksPerHour * Configurations.EggLifespanMultiplier);
fertilizer = null;
position = 0;
ageTicks = 0;
}
public void ExposeData()
{
if (Scribe.mode == LoadSaveMode.LoadingVars)
{
int lifespanhrs = -1;
int position = -1;
int fertstage = -1;
Scribe_Values.Look(ref lifespanhrs, "lifespanhrs", lifespanhrs, true);
Scribe_Values.Look(ref position, "position", position, true);
Scribe_Values.Look(ref fertstage, "fertstage", fertstage, true);
if (lifespanhrs >= 0) lifeSpanTicks = lifespanhrs * GenDate.TicksPerHour;
if (position >= 0) ageTicks = position * GenDate.TicksPerHour;
if (fertstage >= 0) ticksSinceFertilization = fertstage * GenDate.TicksPerHour;
}
Scribe_References.Look(ref fertilizer, "fertilizer", true);
Scribe_Values.Look(ref fertilized, "fertilized", fertilized, true);
Scribe_Values.Look(ref lifespanhrs, "lifespanhrs", lifespanhrs, true);
Scribe_Values.Look(ref position, "position", position, true);
Scribe_Values.Look(ref fertstage, "fertstage", fertstage, true);
Scribe_Values.Look(ref lifeSpanTicks, "lifeSpanTicks", lifeSpanTicks, true);
Scribe_Values.Look(ref ageTicks, "ageTicks", ageTicks, true);
Scribe_Values.Look(ref ticksSinceFertilization, "ticksSinceFertilization", ticksSinceFertilization, true);
}
}

View File

@ -16,8 +16,8 @@ namespace RJW_Menstruation
public class HediffComp_PeriodicOvulator : HediffComp_Menstruation
{
public int hoursToNextCycle = -100000;
public int averageCycleIntervalHours = -1;
public int ticksToNextCycle = -100000;
public int averageCycleIntervalTicks = -1;
public new CompProperties_PeriodicOvulator Props;
@ -25,14 +25,14 @@ namespace RJW_Menstruation
{
base.InitializeExtraValues();
Props = (CompProperties_PeriodicOvulator)props;
if (averageCycleIntervalHours < 0)
if (averageCycleIntervalTicks < 0)
{
averageCycleIntervalHours = (int)(24f * Props.cycleIntervalDays.RandomInRange / cycleSpeed);
if (hoursToNextCycle < -50000)
hoursToNextCycle = Rand.Range(0, averageCycleIntervalHours);
averageCycleIntervalTicks = (int)(Props.cycleIntervalDays.RandomInRange * GenDate.TicksPerDay / cycleSpeed);
if (ticksToNextCycle < -50000)
ticksToNextCycle = Rand.Range(0, averageCycleIntervalTicks);
// Make the cutoff halfway into cycle, just to be sure there isn't a double-cycle the first time
if ((curStage == Stage.Follicular || curStage == Stage.Luteal || curStage == Stage.Bleeding)
&& (averageCycleIntervalHours - hoursToNextCycle) / 2 >= 24 * (Props.follicularIntervalDays + Props.lutealIntervalDays) / cycleSpeed)
&& (averageCycleIntervalTicks - ticksToNextCycle) / 2 >= GenDate.TicksPerDay * (Props.follicularIntervalDays + Props.lutealIntervalDays) / cycleSpeed)
GoNextStage(Stage.Anestrus);
}
}
@ -41,31 +41,40 @@ namespace RJW_Menstruation
{
// Don't bother trying to work seasonal breeding into the math
// Due to the enormous variation in possible cycle gaps, cheat and base it off the individual
return averageCycleIntervalHours * cycleSpeed / (24 * 360); // cancel out their cycleSpeed from initialization to get their "normal" speed
return averageCycleIntervalTicks * cycleSpeed / GenDate.TicksPerYear; // cancel out their cycleSpeed from initialization to get their "normal" speed
}
protected override void BeforeSimulator()
{
base.BeforeSimulator();
if (hoursToNextCycle > 0) hoursToNextCycle -= Configurations.CycleAcceleration;
if (ticksToNextCycle > 0) ticksToNextCycle -= TickInterval * Configurations.CycleAcceleration;
}
public override void CompExposeData()
{
base.CompExposeData();
Scribe_Values.Look(ref hoursToNextCycle, "hoursToNextCycle", hoursToNextCycle, true);
Scribe_Values.Look(ref averageCycleIntervalHours, "averageCycleIntervalHours", averageCycleIntervalHours, true);
if (Scribe.mode == LoadSaveMode.LoadingVars)
{
int hoursToNextCycle = -1;
int averageCycleIntervalHours = -1;
Scribe_Values.Look(ref hoursToNextCycle, "hoursToNextCycle", hoursToNextCycle, true);
Scribe_Values.Look(ref averageCycleIntervalHours, "averageCycleIntervalHours", averageCycleIntervalHours, true);
if (hoursToNextCycle >= 0) ticksToNextCycle = hoursToNextCycle * GenDate.TicksPerHour;
if (averageCycleIntervalHours >= 0) averageCycleIntervalTicks = averageCycleIntervalHours * GenDate.TicksPerHour;
}
Scribe_Values.Look(ref ticksToNextCycle, "ticksToNextCycle", ticksToNextCycle, true);
Scribe_Values.Look(ref averageCycleIntervalTicks, "averageCycleIntervalTicks", averageCycleIntervalTicks, true);
}
protected override int HoursToNextStage()
protected override int TicksToNextStage()
{
if (curStage == Stage.Anestrus && hoursToNextCycle > 0) return hoursToNextCycle / Configurations.CycleAcceleration;
else return base.HoursToNextStage();
if (curStage == Stage.Anestrus && ticksToNextCycle > 0) return ticksToNextCycle / Configurations.CycleAcceleration;
else return base.TicksToNextStage();
}
protected override void BleedingAction()
{
if (curStageHrs >= currentIntervalHours)
if (curStageTicks >= currentIntervalTicks)
{
Hediff hediff = Pawn.health.hediffSet.GetFirstHediffOfDef(VariousDefOf.Hediff_MenstrualCramp);
if (hediff != null && !Pawn.GetMenstruationComps().Any(comp => comp != this && comp.curStage == Stage.Bleeding)) Pawn.health.RemoveHediff(hediff);
@ -81,14 +90,14 @@ namespace RJW_Menstruation
base.PregnantAction();
if (curStage != Stage.Pregnant)
// Go halfway into the cycle
hoursToNextCycle = (int)(averageCycleIntervalHours * (1 + Rand.Range(-cycleVariability, cycleVariability))) / 2;
ticksToNextCycle = (int)(averageCycleIntervalTicks * (1 + Rand.Range(-cycleVariability, cycleVariability))) / 2;
}
protected override void AnestrusAction()
{
if (hoursToNextCycle <= 0)
if (ticksToNextCycle <= 0)
{
hoursToNextCycle = (int)(averageCycleIntervalHours * (1 + Rand.Range(-cycleVariability, cycleVariability)));
ticksToNextCycle = (int)(averageCycleIntervalTicks * (1 + Rand.Range(-cycleVariability, cycleVariability)));
if (IsBreedingSeason()) GoNextStage(Stage.Follicular);
return;
}
@ -99,7 +108,7 @@ namespace RJW_Menstruation
{
base.CopyCycleProperties(original);
if (original is HediffComp_PeriodicOvulator comp)
averageCycleIntervalHours = comp.averageCycleIntervalHours;
averageCycleIntervalTicks = comp.averageCycleIntervalTicks;
}
}
}

View File

@ -219,6 +219,7 @@ namespace RJW_Menstruation
}
public static Texture2D GetEggIcon(this HediffComp_Menstruation comp, bool includeOvary)
{
float ovulationChance = comp.OvulationChance;
const float ovaryChanceToShow_01 = 0.4f;
const float ovaryChanceToShow_02 = 1.0f;
switch (comp.CurrentVisibleStage)
@ -231,28 +232,28 @@ namespace RJW_Menstruation
job.Sexprops != null &&
!job.Sexprops.usedCondom &&
(job.Sexprops.sexType == xxx.rjwSextype.Vaginal || job.Sexprops.sexType == xxx.rjwSextype.DoublePenetration))
return ContentFinder<Texture2D>.Get((comp.OvulationChance >= ovaryChanceToShow_01) ? "Ovaries/Ovary_01" : "Ovaries/Ovary_00", true);
return ContentFinder<Texture2D>.Get((ovulationChance >= ovaryChanceToShow_01) ? "Ovaries/Ovary_01" : "Ovaries/Ovary_00", true);
else break;
}
if (comp.curStageHrs > comp.CurStageIntervalHours - 30) // Approximate time for ovulation to occur
return ContentFinder<Texture2D>.Get((comp.OvulationChance >= ovaryChanceToShow_01) ? "Ovaries/Ovary_01" : "Ovaries/Ovary_00", true);
if (comp.curStageTicks > comp.CurStageIntervalTicks - 30 * GenDate.TicksPerHour) // Approximate time for ovulation to occur
return ContentFinder<Texture2D>.Get((ovulationChance >= ovaryChanceToShow_01) ? "Ovaries/Ovary_01" : "Ovaries/Ovary_00", true);
else break;
case HediffComp_Menstruation.Stage.Ovulatory:
if (!includeOvary) break;
if (comp.OvulationChance >= ovaryChanceToShow_02)
if (ovulationChance >= ovaryChanceToShow_02)
return ContentFinder<Texture2D>.Get("Ovaries/Ovary_02", true);
else if (comp.OvulationChance >= ovaryChanceToShow_01)
else if (ovulationChance >= ovaryChanceToShow_01)
return ContentFinder<Texture2D>.Get("Ovaries/Ovary_01", true);
else
return ContentFinder<Texture2D>.Get("Ovaries/Ovary_00", true);
case HediffComp_Menstruation.Stage.Luteal:
if (!comp.IsEggExist) break;
int fertstage = comp.IsFertilized;
if (fertstage >= 0)
int fertTime = comp.EggFertilizedTime;
if (fertTime >= 0)
{
if (fertstage <= Configurations.CycleAcceleration) return ContentFinder<Texture2D>.Get("Eggs/Egg_Fertilizing02", true);
else if (fertstage <= 18) return ContentFinder<Texture2D>.Get("Eggs/Egg_Fertilized00", true);
else if (fertstage <= 54) return ContentFinder<Texture2D>.Get("Eggs/Egg_Fertilized01", true);
if (fertTime <= GenDate.TicksPerHour * Configurations.CycleAcceleration) return ContentFinder<Texture2D>.Get("Eggs/Egg_Fertilizing02", true);
else if (fertTime <= 18 * GenDate.TicksPerHour) return ContentFinder<Texture2D>.Get("Eggs/Egg_Fertilized00", true);
else if (fertTime <= 54 * GenDate.TicksPerHour) return ContentFinder<Texture2D>.Get("Eggs/Egg_Fertilized01", true);
else return ContentFinder<Texture2D>.Get("Eggs/Egg_Fertilized02", true);
}
else if (comp.IsEggFertilizing)

View File

@ -1,4 +1,5 @@
using HarmonyLib;
using RimWorld;
using rjw;
using System.Collections.Generic;
using System.Linq;
@ -47,7 +48,7 @@ namespace RJW_Menstruation
if (Configurations.Debug)
{
description
.AppendFormat("{0}: {1}\n", comp.curStage, comp.curStageHrs);
.AppendFormat("{0}: {1}\n", comp.curStage, comp.curStageTicks / GenDate.TicksPerHour);
if (comp.Pregnancy is Hediff_BasePregnancy rjwpreg) description
.AppendFormat("due: {0}\n", rjwpreg.DueDate());
else if (comp.Pregnancy is Hediff_Pregnant biopreg) description

View File

@ -197,7 +197,8 @@ namespace RJW_Menstruation
public float absorbedfluids = 0;
public bool dirty = false;
public int wearhours = 0;
public int wearTicks = 0;
protected virtual float PassiveAbsorptionPerHour => 0.1f;
public virtual bool LeakAfterDirty => def.GetModExtension<AbsorberModExtension>().leakAfterDirty;
public virtual bool EffectAfterDirty => def.GetModExtension<AbsorberModExtension>().effectsAfterDirty;
public virtual ThingDef DirtyDef => def.GetModExtension<AbsorberModExtension>().dirtyDef;
@ -205,12 +206,12 @@ namespace RJW_Menstruation
public Color fluidColor = Color.white;
public virtual void DirtyEffect() { }
public virtual void DirtyEffect(int tickInterval) { }
public virtual void WearEffect()
public virtual void WearEffect(int tickInterval)
{
absorbedfluids += 0.1f;
if (dirty) wearhours++;
absorbedfluids += PassiveAbsorptionPerHour * tickInterval / GenDate.TicksPerHour;
if (dirty) wearTicks += tickInterval;
}
public override Color DrawColorTwo => fluidColor;
@ -218,9 +219,15 @@ namespace RJW_Menstruation
public override void ExposeData()
{
base.ExposeData();
if(Scribe.mode == LoadSaveMode.LoadingVars)
{
int wearhours = -1;
Scribe_Values.Look(ref wearhours, "wearhours", wearhours, true);
if (wearhours >= 0) wearTicks = wearhours * GenDate.TicksPerHour;
}
Scribe_Values.Look(ref absorbedfluids, "absorbedfluids", absorbedfluids, true);
Scribe_Values.Look(ref dirty, "dirty", dirty, true);
Scribe_Values.Look(ref wearhours, "wearhours", wearhours, true);
Scribe_Values.Look(ref wearTicks, "wearTicks", wearTicks, true);
Scribe_Values.Look(ref fluidColor, "fluidColor", fluidColor, true);
}
@ -228,16 +235,11 @@ namespace RJW_Menstruation
public class Absorber_Tampon : Absorber
{
public override void WearEffect()
protected override float PassiveAbsorptionPerHour => 0.5f;
public override void DirtyEffect(int tickInterval)
{
if (dirty) wearhours++;
absorbedfluids += 0.5f;
}
public override void DirtyEffect()
{
if (wearhours > MinHrstoDirtyEffect && Rand.Chance(0.01f) && !Wearer.apparel.IsLocked(this))
if (wearTicks > MinHrstoDirtyEffect * GenDate.TicksPerHour && Rand.MTBEventOccurs(100.0f, GenDate.TicksPerHour, tickInterval) && !Wearer.apparel.IsLocked(this))
{
Wearer.health.AddHediff(HediffDefOf.WoundInfection, Genital_Helper.get_genitalsBPR(Wearer));
}

View File

@ -60,6 +60,12 @@ namespace RJW_Menstruation
public static readonly string Option6_Desc = "Option6_Desc".Translate();
public static readonly string Option7_Label = "Option7_Label".Translate();
public static readonly string Option7_Desc = "Option7_Desc".Translate();
public static readonly string Option_ColonistUpdateInterval_Label = "Option_ColonistUpdateInterval_Label".Translate();
public static readonly string Option_ColonistUpdateInterval_Desc = "Option_ColonistUpdateInterval_Desc".Translate();
public static readonly string Option_NonColonistUpdateInterval_Label = "Option_NonColonistUpdateInterval_Label".Translate();
public static readonly string Option_NonColonistUpdateInterval_Desc = "Option_NonColonistUpdateInterval_Desc".Translate();
public static readonly string Option_AnimalUpdateInterval_Label = "Option_AnimalUpdateInterval_Label".Translate();
public static readonly string Option_AnimalUpdateInterval_Desc = "Option_AnimalUpdateInterval_Desc".Translate();
public static readonly string Option8_Label = "Option8_Label".Translate();
public static readonly string Option8_Desc = "Option8_Desc".Translate();
public static readonly string Option9_Label = "Option9_Label".Translate();

View File

@ -1,6 +1,7 @@
Version 1.0.9.0
- Fix errors when opening the womb dialog of some low fertility pawns.
- Updated Traditional Chinese translation by Hydrogen.
- New options to update wombs more or less often, defaulting to every hour.
- Menstruation-related genes will now stay on females during initial pawn setup.
Version 1.0.8.9