Merge branch 'dev'

This commit is contained in:
lutepickle 2022-08-15 18:40:42 -07:00
commit 13359beb3d
22 changed files with 506 additions and 504 deletions

Binary file not shown.

View file

@ -37,7 +37,7 @@
<Dialog_DoCleanWomb_Tooltip>Gather cum into bucket</Dialog_DoCleanWomb_Tooltip> <Dialog_DoCleanWomb_Tooltip>Gather cum into bucket</Dialog_DoCleanWomb_Tooltip>
<Dialog_DontCleanWomb_Tooltip>Store cum in womb</Dialog_DontCleanWomb_Tooltip> <Dialog_DontCleanWomb_Tooltip>Store cum in womb</Dialog_DontCleanWomb_Tooltip>
<Dialog_FatherUnknown>Unknown</Dialog_FatherUnknown> <Dialog_FatherUnknown>Unknown</Dialog_FatherUnknown>
<Option1_Label_1>Enable womb icon</Option1_Label_1> <Option1_Label_1>Enable womb icon</Option1_Label_1>
@ -94,14 +94,12 @@
<Option23_Label>Dominant hybrid extension</Option23_Label> <Option23_Label>Dominant hybrid extension</Option23_Label>
<Option23_Label_1>Mother</Option23_Label_1> <Option23_Label_1>Mother</Option23_Label_1>
<Option23_Label_2>Father</Option23_Label_2> <Option23_Label_2>Father</Option23_Label_2>
<Option24_Label>Transition variance of nipples after pregnancy</Option24_Label> <Option_MaxBreastIncrementFactor_Label>Breast growth during pregnancy</Option_MaxBreastIncrementFactor_Label>
<Option24_Desc>Set how much nipples/areolas should darken/widen on every pregnancy.</Option24_Desc> <Option_MaxBreastIncrementFactor_Desc>Change how much a pregnant pawn's breasts will grow when pregnant. Some pawns will grow more than others.</Option_MaxBreastIncrementFactor_Desc>
<Option25_Label>Permanent transition variance of nipples</Option25_Label> <Option_MaxNippleIncrementFactor_Label>Nipple change during pregnancy</Option_MaxNippleIncrementFactor_Label>
<Option25_Desc>Set how much nipples/areolas should darken/widen permanently on every pregnancy.</Option25_Desc> <Option_MaxNippleIncrementFactor_Desc>Change how much a pregnant pawn's nipples will change during pregnancy.</Option_MaxNippleIncrementFactor_Desc>
<Option26_Label>Maximum transition</Option26_Label> <Option_PermanentNippleChange_Label>Permanent nipple change after pregnancy</Option_PermanentNippleChange_Label>
<Option26_Desc>Nipples/areolas won't be darker/wider than this value.</Option26_Desc> <Option_PermanentNippleChange_Desc>Adjusts approximately how much of a pregnant pawn's nipples will remain changed after the pregnancy ends.</Option_PermanentNippleChange_Desc>
<Option27_Label>Transition speed of nipples</Option27_Label>
<Option27_Desc>Set speed of transition of nipples/areolas.&#10;1 = instant transition</Option27_Desc>
<Option28_Label>Customize Hybrids</Option28_Label> <Option28_Label>Customize Hybrids</Option28_Label>
<Option28_Tooltip>Open custom hybrid editor.&#10;This will overrides hybrid definitions of XML files.</Option28_Tooltip> <Option28_Tooltip>Open custom hybrid editor.&#10;This will overrides hybrid definitions of XML files.</Option28_Tooltip>
<Option29_Label>Allow shrink icon</Option29_Label> <Option29_Label>Allow shrink icon</Option29_Label>

View file

@ -32,8 +32,7 @@ namespace MilkModule
{ {
if (breastcomp != null) if (breastcomp != null)
{ {
breastcomp.AdjustAreolaSize(Rand.Range(0.0f, 0.0001f * Configurations.NipplePermanentTransitionVariance)); breastcomp.AdjustNippleProgress(Rand.Range(0.0f, 0.01f));
breastcomp.AdjustNippleSize(Rand.Range(0.0f, 0.0001f * Configurations.NipplePermanentTransitionVariance));
} }
} }

View file

@ -24,8 +24,7 @@ namespace MilkModule
if (__instance.parent is Pawn pawn) comp = pawn.GetBreastComp(); if (__instance.parent is Pawn pawn) comp = pawn.GetBreastComp();
if (comp != null) if (comp != null)
{ {
comp.AdjustAreolaSize(Rand.Range(0.0f, 0.01f * Configurations.NipplePermanentTransitionVariance)); comp.AdjustNippleProgress(Rand.Range(0.0f, 0.01f));
comp.AdjustNippleSize(Rand.Range(0.0f, 0.01f * Configurations.NipplePermanentTransitionVariance));
} }
} }

View file

@ -21,10 +21,12 @@ namespace RJW_Menstruation
public const int EnzygoticTwinsChanceAdjustDefault = 2; public const int EnzygoticTwinsChanceAdjustDefault = 2;
public const int MaxEnzygoticTwinsDefault = 9; public const int MaxEnzygoticTwinsDefault = 9;
public const int BleedingAmountDefault = 50; public const int BleedingAmountDefault = 50;
public const float NippleTransitionVarianceDefault = 0.2f; public const float MaxBreastIncrementFactorDefault = 1.0f;
public const float NipplePermanentTransitionVarianceDefault = 0.02f; public const float MaxBreastIncrementFactorMax = 2.5f;
public const float NippleMaximumTransitionDefault = 0.4f; public const float MaxNippleIncrementFactorDefault = 1.0f;
public const float NippleTransitionSpeedDefault = 0.035f; public const float MaxNippleIncrementFactorMax = 2.5f;
public const float PermanentNippleChangeDefault = 0.1f;
public const float PermanentNippleChangeMax = 0.25f;
public const float EggLifespanMultiplierDefault = 1.0f; public const float EggLifespanMultiplierDefault = 1.0f;
public const float VaginaMorphPowerDefault = 0.2f; public const float VaginaMorphPowerDefault = 0.2f;
@ -67,19 +69,9 @@ namespace RJW_Menstruation
public static float EggLifespanMultiplier = EggLifespanMultiplierDefault; public static float EggLifespanMultiplier = EggLifespanMultiplierDefault;
public static bool EnableBirthVaginaMorph = false; public static bool EnableBirthVaginaMorph = false;
public static float VaginaMorphPower = VaginaMorphPowerDefault; public static float VaginaMorphPower = VaginaMorphPowerDefault;
public static float MaxBreastIncrementFactor = MaxBreastIncrementFactorDefault;
public static float NippleTransitionVariance = NippleTransitionVarianceDefault; public static float MaxNippleIncrementFactor = MaxNippleIncrementFactorDefault;
public static float NipplePermanentTransitionVariance = NipplePermanentTransitionVarianceDefault; public static float PermanentNippleChange = PermanentNippleChangeDefault;
public static float NippleMaximumTransition = NippleMaximumTransitionDefault;
public static float NippleTransitionSpeed = NippleTransitionSpeedDefault;
public static float NippleTransitionRatio
{
get
{
return NippleTransitionVariance * NippleTransitionSpeed;
}
}
public static void SettoDefault() public static void SettoDefault()
{ {
ImplantationChanceAdjust = ImplantationChanceAdjustDefault; ImplantationChanceAdjust = ImplantationChanceAdjustDefault;
@ -101,10 +93,9 @@ namespace RJW_Menstruation
MaxEnzygoticTwins = MaxEnzygoticTwinsDefault; MaxEnzygoticTwins = MaxEnzygoticTwinsDefault;
BleedingAmount = BleedingAmountDefault; BleedingAmount = BleedingAmountDefault;
MotherFirst = false; MotherFirst = false;
NippleTransitionVariance = NippleTransitionVarianceDefault; MaxBreastIncrementFactor = MaxBreastIncrementFactorDefault;
NipplePermanentTransitionVariance = NipplePermanentTransitionVarianceDefault; MaxNippleIncrementFactor= MaxNippleIncrementFactorDefault;
NippleMaximumTransition = NippleMaximumTransitionDefault; PermanentNippleChange = PermanentNippleChangeDefault;
NippleTransitionSpeed = NippleTransitionSpeedDefault;
EggLifespanMultiplier = EggLifespanMultiplierDefault; EggLifespanMultiplier = EggLifespanMultiplierDefault;
VaginaMorphPower = VaginaMorphPowerDefault; VaginaMorphPower = VaginaMorphPowerDefault;
} }
@ -214,10 +205,9 @@ namespace RJW_Menstruation
Scribe_Values.Look(ref ShowFlag, "ShowFlag", ShowFlag, true); Scribe_Values.Look(ref ShowFlag, "ShowFlag", ShowFlag, true);
Scribe_Values.Look(ref UseHybridExtention, "UseHybridExtention", UseHybridExtention, true); Scribe_Values.Look(ref UseHybridExtention, "UseHybridExtention", UseHybridExtention, true);
Scribe_Values.Look(ref MotherFirst, "MotherFirst", MotherFirst, true); Scribe_Values.Look(ref MotherFirst, "MotherFirst", MotherFirst, true);
Scribe_Values.Look(ref NippleTransitionVariance, "NippleTransitionVariance", NippleTransitionVariance, true); Scribe_Values.Look(ref MaxBreastIncrementFactor, "MaxBreastIncrementFactor", MaxBreastIncrementFactor, true);
Scribe_Values.Look(ref NipplePermanentTransitionVariance, "NipplePermanentTransitionVariance", NipplePermanentTransitionVariance, true); Scribe_Values.Look(ref MaxNippleIncrementFactor, "MaxNippleIncrementFactor", MaxNippleIncrementFactor, true);
Scribe_Values.Look(ref NippleMaximumTransition, "NippleMaximumTransition", NippleMaximumTransition, true); Scribe_Values.Look(ref PermanentNippleChange, "PermanentNippleChange", PermanentNippleChange, true);
Scribe_Values.Look(ref NippleTransitionSpeed, "NippleTransitionSpeed", NippleTransitionSpeed, true);
Scribe_Values.Look(ref AllowShrinkIcon, "AllowShrinkIcon", AllowShrinkIcon, true); Scribe_Values.Look(ref AllowShrinkIcon, "AllowShrinkIcon", AllowShrinkIcon, true);
Scribe_Values.Look(ref EggLifespanMultiplier, "EggLifespanMultiplier", EggLifespanMultiplier, true); Scribe_Values.Look(ref EggLifespanMultiplier, "EggLifespanMultiplier", EggLifespanMultiplier, true);
Scribe_Values.Look(ref EnableBirthVaginaMorph, "EnableBirthVaginaMorph", EnableBirthVaginaMorph, true); Scribe_Values.Look(ref EnableBirthVaginaMorph, "EnableBirthVaginaMorph", EnableBirthVaginaMorph, true);
@ -259,8 +249,8 @@ namespace RJW_Menstruation
public RJW_Menstruation(ModContentPack content) : base(content) public RJW_Menstruation(ModContentPack content) : base(content)
{ {
GetSettings<Configurations>(); GetSettings<Configurations>();
Configurations.HARActivated = ModLister.HasActiveModWithName("Humanoid Alien Races 2.0"); Configurations.HARActivated = ModLister.GetActiveModWithIdentifier("erdelf.HumanoidAlienRaces") != null;
Configurations.LLActivated = ModLister.HasActiveModWithName("RimJobWorld - Licentia Labs"); Configurations.LLActivated = ModLister.GetActiveModWithIdentifier("LustLicentia.RJWLabs") != null;
} }
@ -294,7 +284,7 @@ namespace RJW_Menstruation
Widgets.CheckboxLabeled(middleAndRightCells.RightHalf(), Translations.Option_EnableGatherCumGizmo_Label, ref Configurations.EnableGatherCumGizmo, false, null, null, true); Widgets.CheckboxLabeled(middleAndRightCells.RightHalf(), Translations.Option_EnableGatherCumGizmo_Label, ref Configurations.EnableGatherCumGizmo, false, null, null, true);
if (Configurations.EnableWombIcon || Configurations.EnableButtonInHT) if (Configurations.EnableWombIcon || Configurations.EnableButtonInHT)
{ {
Listing_Standard wombsection = listmain.BeginSection(400); Listing_Standard wombsection = listmain.BeginSection(350);
wombsection.CheckboxLabeled(Translations.Option9_Label, ref Configurations.DrawWombStatus, Translations.Option9_Desc); wombsection.CheckboxLabeled(Translations.Option9_Label, ref Configurations.DrawWombStatus, Translations.Option9_Desc);
if (Configurations.DrawWombStatus) if (Configurations.DrawWombStatus)
{ {
@ -352,25 +342,20 @@ namespace RJW_Menstruation
Configurations.ShowFlag ^= Configurations.PawnFlags.Hostile; Configurations.ShowFlag ^= Configurations.PawnFlags.Hostile;
} }
Adjust = (int)(Configurations.NippleTransitionVariance * 1000); Adjust = (int)(Configurations.MaxBreastIncrementFactor * 1000 / Configurations.MaxBreastIncrementFactorMax);
wombsection.Label(Translations.Option24_Label + " " + Configurations.NippleTransitionVariance * 100 + " / 100", -1, Translations.Option24_Desc); wombsection.Label(Translations.Option_MaxBreastIncrementFactor_Label + " " + Configurations.MaxBreastIncrementFactor * 100 + "%", -1, Translations.Option_MaxBreastIncrementFactor_Desc);
Adjust = (int)wombsection.Slider(Adjust, 0, 1000); Adjust = (int)wombsection.Slider(Adjust, 0, 1000);
Configurations.NippleTransitionVariance = (float)Adjust / 1000; Configurations.MaxBreastIncrementFactor = (float)Adjust / (1000 / Configurations.MaxBreastIncrementFactorMax);
Adjust = (int)(Configurations.NipplePermanentTransitionVariance * 1000); Adjust = (int)(Configurations.MaxNippleIncrementFactor * 1000 / Configurations.MaxNippleIncrementFactorMax);
wombsection.Label(Translations.Option25_Label + " " + Configurations.NipplePermanentTransitionVariance * 100 + " / 100", -1, Translations.Option25_Desc); wombsection.Label(Translations.Option_MaxNippleIncrementFactor_Label + " " + Configurations.MaxNippleIncrementFactor * 100 + "%", -1, Translations.Option_MaxNippleIncrementFactor_Desc);
Adjust = (int)wombsection.Slider(Adjust, 0, 1000); Adjust = (int)wombsection.Slider(Adjust, 0, 1000);
Configurations.NipplePermanentTransitionVariance = (float)Adjust / 1000; Configurations.MaxNippleIncrementFactor = (float)Adjust / (1000 / Configurations.MaxNippleIncrementFactorMax);
Adjust = (int)(Configurations.NippleMaximumTransition * 1000); Adjust = (int)(Configurations.PermanentNippleChange * 1000 / Configurations.PermanentNippleChangeMax);
wombsection.Label(Translations.Option26_Label + " " + Configurations.NippleMaximumTransition * 100 + " / 100", -1, Translations.Option26_Desc); wombsection.Label(Translations.Option_PermanentNippleChange_Label + " " + Configurations.PermanentNippleChange, -1, Translations.Option_PermanentNippleChange_Desc);
Adjust = (int)wombsection.Slider(Adjust, 0, 1000); Adjust = (int)wombsection.Slider(Adjust, 0, 1000);
Configurations.NippleMaximumTransition = (float)Adjust / 1000; Configurations.PermanentNippleChange = (float)Adjust / (1000 / Configurations.PermanentNippleChangeMax);
Adjust = (int)(Configurations.NippleTransitionSpeed * 1000);
wombsection.Label(Translations.Option27_Label + " " + Configurations.NippleTransitionSpeed, -1, Translations.Option27_Desc);
Adjust = (int)wombsection.Slider(Adjust, 0, 1000);
Configurations.NippleTransitionSpeed = (float)Adjust / 1000;
listmain.EndSection(wombsection); listmain.EndSection(wombsection);
} }

View file

@ -11,7 +11,7 @@ namespace RJW_Menstruation
{ {
foreach (HediffComp_Menstruation comp in p.GetMenstruationComps()) foreach (HediffComp_Menstruation comp in p.GetMenstruationComps())
comp.curStage = HediffComp_Menstruation.Stage.Follicular; comp.curStage = HediffComp_Menstruation.Stage.Follicular;
Messages.Message($"{p} is now follicular", MessageTypeDefOf.NeutralEvent, false); Messages.Message($"{p} is now follicular", p, MessageTypeDefOf.NeutralEvent, false);
} }
[DebugAction("RJW Menstruation", "Set pawn's state to ovulatory", false, false, actionType = DebugActionType.ToolMapForPawns, allowedGameStates = AllowedGameStates.Playing)] [DebugAction("RJW Menstruation", "Set pawn's state to ovulatory", false, false, actionType = DebugActionType.ToolMapForPawns, allowedGameStates = AllowedGameStates.Playing)]
@ -19,7 +19,7 @@ namespace RJW_Menstruation
{ {
foreach (HediffComp_Menstruation comp in p.GetMenstruationComps()) foreach (HediffComp_Menstruation comp in p.GetMenstruationComps())
comp.curStage = HediffComp_Menstruation.Stage.Ovulatory; comp.curStage = HediffComp_Menstruation.Stage.Ovulatory;
Messages.Message($"{p} is now ovulatory", MessageTypeDefOf.NeutralEvent, false); Messages.Message($"{p} is now ovulatory", p, MessageTypeDefOf.NeutralEvent, false);
} }
[DebugAction("RJW Menstruation", "Set pawn's state to luteal", false, false, actionType = DebugActionType.ToolMapForPawns, allowedGameStates = AllowedGameStates.Playing)] [DebugAction("RJW Menstruation", "Set pawn's state to luteal", false, false, actionType = DebugActionType.ToolMapForPawns, allowedGameStates = AllowedGameStates.Playing)]
@ -27,7 +27,7 @@ namespace RJW_Menstruation
{ {
foreach (HediffComp_Menstruation comp in p.GetMenstruationComps()) foreach (HediffComp_Menstruation comp in p.GetMenstruationComps())
comp.curStage = HediffComp_Menstruation.Stage.Luteal; comp.curStage = HediffComp_Menstruation.Stage.Luteal;
Messages.Message($"{p} is now luteal", MessageTypeDefOf.NeutralEvent, false); Messages.Message($"{p} is now luteal", p, MessageTypeDefOf.NeutralEvent, false);
} }
[DebugAction("RJW Menstruation", "Set pawn's state to bleeding", false, false, actionType = DebugActionType.ToolMapForPawns, allowedGameStates = AllowedGameStates.Playing)] [DebugAction("RJW Menstruation", "Set pawn's state to bleeding", false, false, actionType = DebugActionType.ToolMapForPawns, allowedGameStates = AllowedGameStates.Playing)]
@ -35,7 +35,7 @@ namespace RJW_Menstruation
{ {
foreach (HediffComp_Menstruation comp in p.GetMenstruationComps()) foreach (HediffComp_Menstruation comp in p.GetMenstruationComps())
comp.curStage = HediffComp_Menstruation.Stage.Bleeding; comp.curStage = HediffComp_Menstruation.Stage.Bleeding;
Messages.Message($"{p} is now bleeding", MessageTypeDefOf.NeutralEvent, false); Messages.Message($"{p} is now bleeding", p, MessageTypeDefOf.NeutralEvent, false);
} }
/* /*
[DebugAction("RJW Menstruation", "Set pawn's state to recovering", false, false, actionType = DebugActionType.ToolMapForPawns, allowedGameStates = AllowedGameStates.Playing)] [DebugAction("RJW Menstruation", "Set pawn's state to recovering", false, false, actionType = DebugActionType.ToolMapForPawns, allowedGameStates = AllowedGameStates.Playing)]
@ -55,14 +55,14 @@ namespace RJW_Menstruation
{ {
foreach (HediffComp_Menstruation comp in p.GetMenstruationComps()) foreach (HediffComp_Menstruation comp in p.GetMenstruationComps())
comp.RemoveAllCums(); comp.RemoveAllCums();
Messages.Message($"All cum removed from {p}'s womb", MessageTypeDefOf.NeutralEvent, false); Messages.Message($"All cum removed from {p}'s womb", p, MessageTypeDefOf.NeutralEvent, false);
} }
[DebugAction("RJW Menstruation", "Add egg to pawn's next ovulation", false, false, actionType = DebugActionType.ToolMapForPawns, allowedGameStates = AllowedGameStates.Playing)] [DebugAction("RJW Menstruation", "Add egg to pawn's next ovulation", false, false, actionType = DebugActionType.ToolMapForPawns, allowedGameStates = AllowedGameStates.Playing)]
private static void AddEgg(Pawn p) private static void AddEgg(Pawn p)
{ {
foreach (HediffComp_Menstruation comp in p.GetMenstruationComps()) foreach (HediffComp_Menstruation comp in p.GetMenstruationComps())
comp.eggstack++; comp.eggstack++;
Messages.Message($"1 egg added to {p}'s next ovulation ({p.GetFirstMenstruationComp().eggstack})", MessageTypeDefOf.NeutralEvent, false); Messages.Message($"1 egg added to {p}'s next ovulation ({p.GetFirstMenstruationComp().eggstack})", p, MessageTypeDefOf.NeutralEvent, false);
} }
[DebugAction("RJW Menstruation", "Recalculate pawn's ovary power", false, false, actionType = DebugActionType.ToolMapForPawns, allowedGameStates = AllowedGameStates.Playing)] [DebugAction("RJW Menstruation", "Recalculate pawn's ovary power", false, false, actionType = DebugActionType.ToolMapForPawns, allowedGameStates = AllowedGameStates.Playing)]
@ -70,7 +70,7 @@ namespace RJW_Menstruation
{ {
foreach (HediffComp_Menstruation comp in p.GetMenstruationComps()) foreach (HediffComp_Menstruation comp in p.GetMenstruationComps())
comp.ovarypower = comp.GetOvaryPowerByAge(); comp.ovarypower = comp.GetOvaryPowerByAge();
Messages.Message($"{p}'s ovarypower recalculated ({p.GetFirstMenstruationComp().ovarypower})", MessageTypeDefOf.NeutralEvent, false); Messages.Message($"{p}'s ovarypower recalculated ({p.GetFirstMenstruationComp().ovarypower})", p, MessageTypeDefOf.NeutralEvent, false);
} }
} }
} }

View file

@ -1,4 +1,6 @@
using System.Linq; using AlienRace;
using System.Collections.Generic;
using UnityEngine;
using Verse; using Verse;
namespace RJW_Menstruation namespace RJW_Menstruation
@ -8,24 +10,38 @@ namespace RJW_Menstruation
public static bool IsHAR(this Pawn pawn) public static bool IsHAR(this Pawn pawn)
{ {
return pawn.def.GetType().ToString().StartsWith("AlienRace"); if (!Configurations.HARActivated) return false;
return pawn?.def is ThingDef_AlienRace;
} }
public static ThingComp GetHARComp(this Pawn pawn) public static AlienPartGenerator.AlienComp GetHARComp(this Pawn pawn)
{ {
return pawn?.GetComps<ThingComp>()?.First(x => x.GetType().Namespace.EqualsIgnoreCase("AlienRace") && x.GetType().Name.EndsWith("AlienComp")); return pawn?.TryGetComp<AlienPartGenerator.AlienComp>();
} }
public static string GetHARCrown(this Pawn pawn) public static void CopyHARProperties(Pawn baby, Pawn original)
{ {
return (string)pawn.GetHARComp().GetMemberValue("crownType"); AlienPartGenerator.AlienComp babyHARComp = baby.GetHARComp();
AlienPartGenerator.AlienComp originalHARComp = original.GetHARComp();
if (babyHARComp == null || originalHARComp == null) return;
babyHARComp.crownType = originalHARComp.crownType;
foreach(KeyValuePair<string, AlienPartGenerator.ExposableValueTuple<Color, Color>> channel in originalHARComp.ColorChannels)
{
babyHARComp.OverwriteColorChannel(channel.Key, channel.Value.first, channel.Value.second);
}
babyHARComp.headMaskVariant = originalHARComp.headMaskVariant;
babyHARComp.bodyMaskVariant = originalHARComp.bodyMaskVariant;
} }
public static void SetHARCrown(this Pawn pawn, string crown) // HAR doesn't populate variants until the graphics are called for, so this has to happen late
public static void CopyHARPropertiesPostBirth(Pawn baby, Pawn original)
{ {
pawn.GetHARComp().SetMemberValue("crownType", crown); AlienPartGenerator.AlienComp babyHARComp = baby.GetHARComp();
AlienPartGenerator.AlienComp originalHARComp = original.GetHARComp();
if (babyHARComp == null || originalHARComp == null) return;
if (originalHARComp.addonVariants != null) // Testing has shown that the addons are valid by this point, but it's better to be safe
babyHARComp.addonVariants = new List<int>(originalHARComp.addonVariants);
} }
} }
} }

View file

@ -1,8 +1,8 @@
using HugsLib; using RimWorld;
using RimWorld;
using rjw; using rjw;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using UnityEngine; using UnityEngine;
using Verse; using Verse;
@ -10,6 +10,7 @@ namespace RJW_Menstruation
{ {
public class CompProperties_Breast : HediffCompProperties public class CompProperties_Breast : HediffCompProperties
{ {
public static readonly ColorInt DefaultBlacknippleColor = new ColorInt(55, 20, 0);
public string BreastTex = "Breasts/Breast"; public string BreastTex = "Breasts/Breast";
public ColorInt BlacknippleColor = new ColorInt(55, 20, 0); public ColorInt BlacknippleColor = new ColorInt(55, 20, 0);
@ -31,117 +32,98 @@ namespace RJW_Menstruation
public class HediffComp_Breast : HediffComp public class HediffComp_Breast : HediffComp
{ {
public const float DEFAULTALPHA = -1; public const int tickInterval = GenDate.TicksPerHour * 3 / 2;
public const float DEFAULTAREOLA = -1; public const float breastGrowthStart = 1f / 6f;
public const float DEFAULTNIPPLE = -1; public const float breastGrowthEnd = 1f / 3f;
public const float VARIANT = 0.2f; public static readonly SimpleCurve nippleTransitions = new SimpleCurve()
public const int TICKINTERVAL = 3750; {
public const float MAX_BREAST_INCREMENT = 0.10f; new CurvePoint(0f,0f),
public const float BREAST_GROWTH_START = 1f / 6f; new CurvePoint(0.1f,0f),
public const float BREAST_GROWTH_END = 1f / 3f; new CurvePoint(0.333f,0.167f),
new CurvePoint(0.667f,0.833f),
new CurvePoint(1.0f,1.0f)
};
public const float nippleChange = 0.2f;
public CompProperties_Breast Props; public CompProperties_Breast Props;
protected float alphaPermanent = -1;
protected float alphaCurrent = -1;
protected float alpha = -1;
protected float areolaSizePermanent = -1f;
protected float areolaSizeCurrent = -1f;
protected float areolaSize = -1f;
protected float nippleSizePermanent = -1f;
protected float nippleSizeCurrent = -1f;
protected float nippleSize = -1f;
protected long ageOfLastBirth = 0; protected long ageOfLastBirth = 0;
protected float maxBreastIncrement = -1f;
protected float breastSizeIncreased = 0f; protected float breastSizeIncreased = 0f;
protected string debugGrowthStatus = "(Growth/shrink not yet calculated; run for 1.5h to update)"; protected string debugGrowthStatus = "(Growth/shrink not yet calculated; run for 1.5h to update)";
protected float originalpha = -1f; protected float nippleProgress = 0f;
protected float originareola = -1f; protected float baseAlpha = -1f; // Will grow in response to pregnancy
protected float originnipple = -1f; protected float baseAreola = -1f;
protected Color cachedcolor; protected float baseNipple = -1f;
protected bool loaded = false; protected float cachedAlpha = -1f; // Calculated dynamically instead of saved
protected bool pregnant = false; protected float cachedAreola = -1f; // Actual size = these * breast size
public Action action; protected float cachedNipple = -1f;
protected float babyHalfAge = -1f;
protected Color cachedColor;
protected bool loaded = false;
protected float BabyHalfAge protected float BabyHalfAge
{ {
get get
{ {
float res = 0; if (babyHalfAge > 0f) return babyHalfAge;
List<LifeStageAge> ages = parent.pawn.RaceProps.lifeStageAges; List<LifeStageAge> ages = parent.pawn.def.race.lifeStageAges;
if (ages?.Count > 1) if (ages?.Count > 1)
res = ages[1].minAge / 2; babyHalfAge = ages[1].minAge / 2;
if (res <= 0) res = 1.2f / 2; // Default to human if (babyHalfAge <= 0) babyHalfAge = 1.2f / 2; // Default to human
if (RJWPregnancySettings.phantasy_pregnancy) if (RJWPregnancySettings.phantasy_pregnancy)
res /= GenDate.DaysPerYear; babyHalfAge /= GenDate.DaysPerYear;
return res; return babyHalfAge;
} }
} }
protected void ShrinkBreasts() protected void ShrinkBreasts()
{ {
// The natural rate will take them from full to empty during the second half of their child's babyhood // The natural rate will take them from full to empty during the second half of their child's babyhood
float shrinkRate = TICKINTERVAL * MAX_BREAST_INCREMENT / (BabyHalfAge * GenDate.TicksPerYear); float shrinkRate = tickInterval * MaxBreastIncrement / (BabyHalfAge * GenDate.TicksPerYear);
float shrinkAmount = Mathf.Min(shrinkRate, breastSizeIncreased); float shrinkAmount = Mathf.Min(shrinkRate, breastSizeIncreased);
breastSizeIncreased -= shrinkAmount; breastSizeIncreased -= shrinkAmount;
parent.Severity -= shrinkAmount; parent.Severity -= shrinkAmount;
} }
public float MaxAlpha protected float MaxBreastIncrement
{ {
get get
{ {
return originalpha + Configurations.NippleMaximumTransition; return maxBreastIncrement * Configurations.MaxBreastIncrementFactor;
} }
} }
public float MaxAreola
{
get
{
return originareola + Configurations.NippleMaximumTransition;
}
}
public float MaxNipple
{
get
{
return originnipple + Configurations.NippleMaximumTransition;
}
}
public float OriginAlpha => originalpha;
public float OriginNipple => originnipple;
public float OriginAreola => originareola;
public Color OriginColor => Colors.CMYKLerp(parent?.pawn?.story?.SkinColor ?? Color.white, Props.BlackNippleColor, originalpha);
public Color NippleColor public Color NippleColor
{ {
get get
{ {
return cachedcolor; return cachedColor;
} }
} }
public float Alpha public float Alpha
{ {
get get
{ {
return alphaCurrent; return cachedAlpha;
} }
} }
public float NippleSize public float NippleSize
{ {
get get
{ {
return nippleSizeCurrent; return cachedNipple * parent.Severity;
} }
} }
public float AreolaSize public float AreolaSize
{ {
get get
{ {
return areolaSizeCurrent; return cachedAreola * parent.Severity;
} }
} }
@ -156,25 +138,50 @@ namespace RJW_Menstruation
public override void CompExposeData() public override void CompExposeData()
{ {
base.CompExposeData(); base.CompExposeData();
Scribe_Values.Look(ref alphaPermanent, "alphaPermanent", DEFAULTALPHA, true);
Scribe_Values.Look(ref alphaCurrent, "alphaCurrent", DEFAULTALPHA, true);
Scribe_Values.Look(ref alpha, "alpha", DEFAULTALPHA, true);
Scribe_Values.Look(ref areolaSizePermanent, "areolaSizePermanent", DEFAULTAREOLA, true);
Scribe_Values.Look(ref areolaSizeCurrent, "areolaSizeCurrent", DEFAULTAREOLA, true);
Scribe_Values.Look(ref areolaSize, "areolaSize", DEFAULTAREOLA, true);
Scribe_Values.Look(ref nippleSizePermanent, "nippleSizePermanent", DEFAULTNIPPLE, true);
Scribe_Values.Look(ref nippleSizeCurrent, "nippleSizeCurrent", DEFAULTNIPPLE, true);
Scribe_Values.Look(ref nippleSize, "nippleSize", DEFAULTNIPPLE, true);
Scribe_Values.Look(ref ageOfLastBirth, "ageOfLastBirth", ageOfLastBirth, true);
Scribe_Values.Look(ref breastSizeIncreased, "breastSizeIncreased", breastSizeIncreased, true);
Scribe_Values.Look(ref originalpha, "originalpha", originalpha, true);
Scribe_Values.Look(ref originareola, "originareola", originareola, true);
Scribe_Values.Look(ref originnipple, "originnipple", originnipple, true);
Scribe_Values.Look(ref pregnant, "pregnant", pregnant, true);
if (Scribe.mode == LoadSaveMode.LoadingVars)
{ // For compatibility
Scribe_Values.Look(ref baseAlpha, "alphaPermanent", baseAlpha / 2, false);
Scribe_Values.Look(ref baseAreola, "areolaSizePermanent", baseAreola / 2, false);
Scribe_Values.Look(ref baseNipple, "nippleSizePermanent", baseNipple / 2, false);
baseAlpha *= 2;
baseAreola *= 2;
baseNipple *= 2;
}
Scribe_Values.Look(ref ageOfLastBirth, "ageOfLastBirth", ageOfLastBirth, true);
Scribe_Values.Look(ref maxBreastIncrement, "maxBreastIncrement", maxBreastIncrement, true);
Scribe_Values.Look(ref breastSizeIncreased, "breastSizeIncreased", breastSizeIncreased, true);
Scribe_Values.Look(ref nippleProgress, "nippleProgress", nippleProgress, true);
Scribe_Values.Look(ref baseAlpha, "baseAlpha", baseAlpha, true);
Scribe_Values.Look(ref baseAreola, "baseAreola", baseAreola, true);
Scribe_Values.Look(ref baseNipple, "baseNipple", baseNipple, true);
} }
public override void CompPostTick(ref float severityAdjustment) { } public override void CompPostTick(ref float severityAdjustment)
{
base.CompPostTick(ref severityAdjustment);
// If an exception makes it out, RW will remove the hediff, so catch it here
try
{
if (
!parent.pawn.IsHashIntervalTick(tickInterval) ||
!parent.pawn.Spawned || // TODO: Add option to simulate off-map pawns
parent.pawn.health.Dead
)
{
return;
}
CalculateBreastSize();
CalculateNipples();
UpdateNipples();
}
catch (Exception ex)
{
Log.Error($"Error processing breasts of {parent.pawn}: {ex}");
}
}
public override void CompPostPostAdd(DamageInfo? dinfo) public override void CompPostPostAdd(DamageInfo? dinfo)
{ {
@ -189,8 +196,6 @@ namespace RJW_Menstruation
Log.Warning($"Attempted to remove breast comp from wrong pawn ({parent.pawn})."); Log.Warning($"Attempted to remove breast comp from wrong pawn ({parent.pawn}).");
return; return;
} }
HugsLibController.Instance.TickDelayScheduler.TryUnscheduleCallback(action);
if (Configurations.Debug) Log.Message(parent.pawn.Label + " breast tick scheduler removed");
base.CompPostPostRemoved(); base.CompPostPostRemoved();
} }
@ -202,15 +207,12 @@ namespace RJW_Menstruation
foreach (Pawn child in parent.pawn.relations.Children) foreach (Pawn child in parent.pawn.relations.Children)
{ {
bool isFetus = false; bool isFetus = false;
foreach (Hediff_BasePregnancy preg in parent.pawn.health.hediffSet.GetHediffs<Hediff_BasePregnancy>()) if (parent.pawn.health.hediffSet.GetHediffs<Hediff_BasePregnancy>().Any(preg => preg.babies.Contains(child)))
{ {
if (preg.babies.Contains(child)) isFetus = true;
{ break;
isFetus = true;
break;
}
} }
if ( if (
parent.pawn.ageTracker.BirthAbsTicks - child.ageTracker.BirthAbsTicks > ageOfLastBirth && parent.pawn.ageTracker.BirthAbsTicks - child.ageTracker.BirthAbsTicks > ageOfLastBirth &&
!isFetus && !isFetus &&
@ -225,70 +227,54 @@ namespace RJW_Menstruation
public void Initialize() public void Initialize()
{ {
Props = (CompProperties_Breast)props; Props = (CompProperties_Breast)props;
action = Transition;
if (maxBreastIncrement <= 0f)
{
maxBreastIncrement = Utility.RandGaussianLike(0.088f, 0.202f);
}
if (ageOfLastBirth == 0) if (ageOfLastBirth == 0)
{ {
ageOfLastBirth = CalculateLastBirth(); ageOfLastBirth = CalculateLastBirth();
} }
if (baseAlpha <= 0f)
if (alphaPermanent < 0f)
{ {
alphaPermanent = (Utility.RandGaussianLike(0.0f, 0.3f) + Rand.Range(0.0f, 0.5f)) / 2; baseAlpha = Utility.RandGaussianLike(0.0f, 0.3f) + Rand.Range(0.0f, 0.5f);
originalpha = alphaPermanent;
alpha = alphaPermanent;
alphaCurrent = alphaPermanent;
} }
if (areolaSizePermanent < 0f) if (baseAreola <= 0f)
{ {
areolaSizePermanent = Utility.RandGaussianLike(0f, parent.Severity); baseAreola = Utility.RandGaussianLike(0.0f, 1.0f);
originareola = areolaSizePermanent;
areolaSize = areolaSizePermanent;
areolaSizeCurrent = areolaSizePermanent;
} }
if (nippleSizePermanent < 0f) if (baseNipple <= 0f)
{ {
nippleSizePermanent = Utility.RandGaussianLike(0f, parent.Severity); baseNipple = Utility.RandGaussianLike(0.0f, 1.0f);
originnipple = nippleSizePermanent;
nippleSize = nippleSizePermanent;
nippleSizeCurrent = nippleSizePermanent;
} }
UpdateColor(); UpdateNipples();
loaded = true; loaded = true;
HugsLibController.Instance.TickDelayScheduler.ScheduleCallback(action, TICKINTERVAL, parent.pawn);
} }
protected void CalculateBreastSize()
public void Transition()
{ {
alphaCurrent = Mathf.Lerp(alphaCurrent, alpha, Configurations.NippleTransitionRatio);
areolaSizeCurrent = Mathf.Lerp(areolaSizeCurrent, areolaSize, Configurations.NippleTransitionRatio);
nippleSizeCurrent = Mathf.Lerp(nippleSizeCurrent, nippleSize, Configurations.NippleTransitionRatio);
UpdateColor();
HugsLibController.Instance.TickDelayScheduler.ScheduleCallback(action, TICKINTERVAL, parent.pawn);
// Scenario A: the youngest child is less than halfway into babyhood: Full size // Scenario A: the youngest child is less than halfway into babyhood: Full size
if (ageOfLastBirth + BabyHalfAge * GenDate.TicksPerYear > parent.pawn.ageTracker.AgeBiologicalTicks) if (ageOfLastBirth + BabyHalfAge * GenDate.TicksPerYear > parent.pawn.ageTracker.AgeBiologicalTicks)
{ {
debugGrowthStatus = "Full size due to young child"; debugGrowthStatus = "Full size due to young child";
if (breastSizeIncreased < MAX_BREAST_INCREMENT) if (breastSizeIncreased < MaxBreastIncrement)
{ {
parent.Severity += (MAX_BREAST_INCREMENT - breastSizeIncreased); parent.Severity += (MaxBreastIncrement - breastSizeIncreased);
breastSizeIncreased = MAX_BREAST_INCREMENT; breastSizeIncreased = MaxBreastIncrement;
} }
} }
// Scenario B: Pregnant, grow in the second half of first trimester // Scenario B: Pregnant, grow in the second half of first trimester
else if (parent.pawn.IsPregnant()) else if (parent.pawn.IsPregnant())
{ {
float pregnancySize = Mathf.InverseLerp(BREAST_GROWTH_START, BREAST_GROWTH_END, parent.pawn.GetFarthestPregnancyProgress()) * MAX_BREAST_INCREMENT; float pregnancySize = Mathf.InverseLerp(breastGrowthStart, breastGrowthEnd, parent.pawn.GetFarthestPregnancyProgress()) * MaxBreastIncrement;
if (breastSizeIncreased > pregnancySize) if (breastSizeIncreased > pregnancySize)
{ {
debugGrowthStatus = "Shrinking due to being oversize for pregnancy"; debugGrowthStatus = "Shrinking due to being oversize for pregnancy";
// Breasts still large from the last kid // Breasts still large from the last kid
ShrinkBreasts(); ShrinkBreasts();
} }
else if (breastSizeIncreased < MAX_BREAST_INCREMENT) else if (breastSizeIncreased < MaxBreastIncrement)
{ {
// Time to grow // Time to grow
float growAmount = pregnancySize - breastSizeIncreased; float growAmount = pregnancySize - breastSizeIncreased;
@ -310,106 +296,88 @@ namespace RJW_Menstruation
else debugGrowthStatus = "Base size"; else debugGrowthStatus = "Base size";
} }
public void ChangeColorFermanant(float alpha) protected void CalculateNipples()
{ {
alphaPermanent = alpha; float newNippleProgress;
if (ageOfLastBirth + BabyHalfAge * GenDate.TicksPerYear > parent.pawn.ageTracker.AgeBiologicalTicks)
newNippleProgress = 1f;
else if (parent.pawn.IsPregnant())
newNippleProgress = nippleTransitions.Evaluate(parent.pawn.GetFarthestPregnancyProgress());
else
newNippleProgress = 0f;
if (newNippleProgress == nippleProgress) return; // Nothing to change
else if (newNippleProgress > nippleProgress)
{
float progressDifference = newNippleProgress - nippleProgress;
// All nipple growth has a slight effect on the base
// Not mathematically precise in hitting the goal at the end of the term, but close enough
baseAlpha *= 1.0f + progressDifference * Configurations.PermanentNippleChange;
if (baseAlpha > 1.0f) baseAlpha = 1.0f;
baseAreola *= 1.0f + progressDifference * Configurations.PermanentNippleChange;
if (baseAreola > 1.0f) baseAreola = 1.0f;
baseNipple *= 1.0f + progressDifference * Configurations.PermanentNippleChange;
if (baseNipple > 1.0f) baseNipple = 1.0f;
nippleProgress = newNippleProgress;
}
else
{
nippleProgress -= tickInterval / (BabyHalfAge * GenDate.TicksPerYear);
if (nippleProgress < newNippleProgress) nippleProgress = newNippleProgress;
}
} }
public void ChangeColor(float alpha) public void AdjustNippleProgress(float amount)
{ {
this.alpha = alpha; nippleProgress = Mathf.Clamp01(nippleProgress + amount);
UpdateNipples();
} }
public void PregnancyTransition() public void AdjustNippleSizeImmediately(float amount)
{ {
alphaPermanent = Math.Min(MaxAlpha, alphaPermanent + Configurations.NipplePermanentTransitionVariance.VariationRange(VARIANT)); baseNipple = Mathf.Clamp01(baseNipple + amount);
areolaSizePermanent = Math.Min(MaxAreola, areolaSizePermanent + Configurations.NipplePermanentTransitionVariance.VariationRange(VARIANT)); UpdateNipples();
nippleSizePermanent = Math.Min(MaxNipple, nippleSizePermanent + Configurations.NipplePermanentTransitionVariance.VariationRange(VARIANT));
alpha = Math.Min(MaxAlpha, alpha + Configurations.NippleTransitionVariance.VariationRange(VARIANT));
areolaSize = Math.Min(MaxAreola, areolaSize + Configurations.NippleTransitionVariance.VariationRange(VARIANT));
nippleSize = Math.Min(MaxNipple, nippleSize + Configurations.NippleTransitionVariance.VariationRange(VARIANT));
pregnant = true;
} }
public void BirthTransition() public void AdjustAreolaSizeImmediately(float amount)
{ {
alpha = alphaPermanent; baseAreola = Mathf.Clamp01(baseAreola + amount);
areolaSize = areolaSizePermanent; UpdateNipples();
nippleSize = nippleSizePermanent;
pregnant = false;
ageOfLastBirth = parent.pawn.ageTracker.AgeBiologicalTicks;
} }
public void UpdateNipples()
public void AdjustBreastSize(float amount)
{ {
parent.Severity += amount; cachedAlpha = baseAlpha + nippleProgress * nippleChange;
breastSizeIncreased += amount; cachedAreola = baseAreola + nippleProgress * nippleChange;
cachedNipple = baseNipple + nippleProgress * nippleChange;
// For some reason, Props can go null when RJW relocates the chest (e.g. some animals), so catch that
cachedColor = Colors.CMYKLerp(parent.pawn.story?.SkinColor ?? Color.white, (Props?.BlackNippleColor ?? CompProperties_Breast.DefaultBlacknippleColor.ToColor), Alpha);
} }
public void AdjustNippleSize(float amount) public void CopyBreastProperties(HediffComp_Breast original)
{ {
nippleSizePermanent = Math.Min(MaxNipple, nippleSizePermanent + amount); maxBreastIncrement = original.maxBreastIncrement;
nippleSize = Math.Min(MaxNipple, nippleSize + amount); baseAlpha = original.baseAlpha;
} baseAreola = original.baseAreola;
baseNipple = original.baseNipple;
public void AdjustAreolaSize(float amount) UpdateNipples();
{
areolaSizePermanent = Math.Min(MaxAreola, areolaSizePermanent + amount);
areolaSize = Math.Min(MaxAreola, areolaSize + amount);
}
public void RestoreBreastSize(float ratio)
{
float variance = breastSizeIncreased * Math.Min(ratio, 1.0f);
breastSizeIncreased -= variance;
parent.Severity -= variance;
}
public void AdjustNippleSizeImmidiately(float amount)
{
originnipple = Math.Max(0, originnipple + amount);
nippleSizePermanent = Math.Min(MaxNipple, nippleSizePermanent + amount);
nippleSize = Math.Min(MaxNipple, nippleSize + amount);
nippleSizeCurrent = nippleSize;
}
public void AdjustAreolaSizeImmidiately(float amount)
{
originareola = Math.Max(0, originareola + amount);
areolaSizePermanent = Math.Min(MaxAreola, areolaSizePermanent + amount);
areolaSize = Math.Min(MaxAreola, areolaSize + amount);
areolaSizeCurrent = areolaSize;
}
public void UpdateColor()
{
cachedcolor = Colors.CMYKLerp(parent?.pawn?.story?.SkinColor ?? Color.white, Props.BlackNippleColor, Alpha);
} }
public string DebugInfo() public string DebugInfo()
{ {
return "Increase: " + breastSizeIncreased + return "Size: " + parent.Severity +
"\nIncrease: " + breastSizeIncreased +
"\n" + debugGrowthStatus + "\n" + debugGrowthStatus +
"\nAlpha: " + alpha + "\nNipple progress: " + nippleProgress +
"\nNippleSize: " + nippleSize + "\nBase alpha: " + baseAlpha +
"\nAreolaSize: " + areolaSize + "\nAlpha: " + cachedAlpha +
"\nAlphaCurrent: " + alphaCurrent + "\nBase areola: " + baseAreola +
"\nNippleSizeCurrent: " + nippleSizeCurrent + "\nAreola: " + cachedAreola +
"\nAreolaSizeCurrent: " + areolaSizeCurrent + "\nDisplayed areola: " + AreolaSize +
"\nAlphaOrigin: " + originalpha + "\nBase nipple: " + baseNipple +
"\nNippleSizeOrigin: " + originnipple + "\nNipple: " + cachedNipple +
"\nAreolaSizeOrigin: " + originareola + "\nDisplayed nipple: " + NippleSize;
"\nAlphaMax: " + MaxAlpha +
"\nNippleSizeMax: " + MaxNipple +
"\nAreolaSizeMax: " + MaxAreola +
"\nPermanentAlpha:" + alphaPermanent +
"\nPermanentNipple:" + nippleSizePermanent +
"\nPermanentAreola:" + areolaSizePermanent;
} }
} }
} }

View file

@ -1,5 +1,4 @@
using HugsLib; using RimWorld;
using RimWorld;
using rjw; using rjw;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -70,7 +69,6 @@ namespace RJW_Menstruation
public CompProperties_Menstruation Props; public CompProperties_Menstruation Props;
public Stage curStage = Stage.Follicular; public Stage curStage = Stage.Follicular;
public int curStageHrs = 0; public int curStageHrs = 0;
public Action actionref;
public bool loaded = false; public bool loaded = false;
public int ovarypower = -100000; public int ovarypower = -100000;
public int eggstack = 0; public int eggstack = 0;
@ -139,7 +137,22 @@ namespace RJW_Menstruation
{ {
get get
{ {
if (opcache < 0) opcache = (int)(72f * parent.pawn.def.race.lifeExpectancy / ThingDefOf.Human.race.lifeExpectancy); if (opcache >= 0) return opcache;
// Climacteric will set in 6 (human) years before egg exhaustion
float avglittersize;
try
{
avglittersize = Mathf.Max(Rand.ByCurveAverage(parent.pawn.def.race.litterSizeCurve), 1.0f);
}
catch
{
// Any exceptions in that will have been reported elsewhere in the code by now
avglittersize = 1.0f;
};
opcache = (int)(RaceCyclesPerYear() *
avglittersize *
6f *
(parent.pawn.def.race.lifeExpectancy / ThingDefOf.Human.race.lifeExpectancy));
return opcache; return opcache;
} }
} }
@ -487,7 +500,17 @@ namespace RJW_Menstruation
{ {
get get
{ {
return Mathf.Clamp01(curStageHrs / CurStageIntervalHours); if (pregnancy == null) return Mathf.Clamp01(curStageHrs / CurStageIntervalHours);
if (pregnancy.is_discovered || Configurations.infoDetail == Configurations.DetailLevel.All) return pregnancy.Severity;
// Luteal will appear to progress, hitting the end of the phase when the pregnancy is discovered
float discoveryTime = 0.5f;
if (parent.pawn.story?.bodyType == BodyTypeDefOf.Thin) discoveryTime = 0.25f;
else if (parent.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));
return GenMath.LerpDouble(0, discoveryTime, lutealProgressWhenImplanted, 1.0f, pregnancy.Severity);
} }
} }
@ -530,11 +553,69 @@ namespace RJW_Menstruation
public override void CompPostTick(ref float severityAdjustment) public override void CompPostTick(ref float severityAdjustment)
{ {
//initializer moved to SpawnSetup base.CompPostTick(ref severityAdjustment);
//if (!loaded) // If an exception makes it out, RW will remove the hediff, so catch it here
//{ try
// Initialize(); {
//} if (
!parent.pawn.IsHashIntervalTick(tickInterval) ||
!parent.pawn.Spawned || // TODO: Add option to simulate off-map pawns
parent.pawn.health.Dead ||
(parent.pawn.IsAnimal() && !Configurations.EnableAnimalCycle)
)
{
return;
}
CumOut();
if (pregnancy == null && parent.pawn.health.capacities.GetLevel(xxx.reproduction) <= 0) curStage = Stage.Young;
switch (curStage)
{
case Stage.Follicular:
FollicularAction(false);
break;
case Stage.Ovulatory:
OvulatoryAction();
break;
case Stage.Luteal:
LutealAction(false);
break;
case Stage.Bleeding:
BleedingAction(false);
break;
case Stage.Pregnant:
PregnantAction();
break;
case Stage.Recover:
RecoverAction();
break;
case Stage.None:
break;
case Stage.Young:
YoungAction();
break;
case Stage.ClimactericFollicular:
FollicularAction(true);
break;
case Stage.ClimactericLuteal:
LutealAction(true);
break;
case Stage.ClimactericBleeding:
BleedingAction(true);
break;
case Stage.Anestrus:
AnestrusAction();
break;
default:
GoNextStage(Stage.Follicular);
break;
}
AfterSimulator();
}
catch (Exception ex)
{
Log.Error($"Error processing womb of {parent.pawn}: {ex}");
}
} }
public override void CompPostPostRemoved() public override void CompPostPostRemoved()
@ -546,8 +627,6 @@ namespace RJW_Menstruation
Log.Warning($"Attempted to remove menstruation comp from wrong pawn ({parent.pawn})."); Log.Warning($"Attempted to remove menstruation comp from wrong pawn ({parent.pawn}).");
return; return;
} }
HugsLibController.Instance.TickDelayScheduler.TryUnscheduleCallback(actionref);
if (Configurations.Debug) Log.Message(parent.pawn.Label + " menstruation tick scheduler removed");
pregnancy?.Miscarry(); pregnancy?.Miscarry();
base.CompPostPostRemoved(); base.CompPostPostRemoved();
} }
@ -871,7 +950,6 @@ namespace RJW_Menstruation
{ {
if (cums == null) cums = new List<Cum>(); if (cums == null) cums = new List<Cum>();
curStage = Stage.None; curStage = Stage.None;
HugsLibController.Instance.TickDelayScheduler.ScheduleCallback(PeriodSimulator(curStage), GetNextUpdate(), parent.pawn, false);
loaded = true; loaded = true;
return; return;
} }
@ -912,18 +990,6 @@ namespace RJW_Menstruation
} }
} }
if (parent.pawn.IsAnimal())
{
if (Configurations.EnableAnimalCycle)
{
HugsLibController.Instance.TickDelayScheduler.ScheduleCallback(PeriodSimulator(curStage), GetNextUpdate(), parent.pawn, false);
}
}
else
{
if (pregnancy == null && parent.pawn.health.capacities.GetLevel(xxx.reproduction) <= 0) HugsLibController.Instance.TickDelayScheduler.ScheduleCallback(PeriodSimulator(Stage.Young), GetNextUpdate(), parent.pawn, false);
else HugsLibController.Instance.TickDelayScheduler.ScheduleCallback(PeriodSimulator(curStage), GetNextUpdate(), parent.pawn, false);
}
//Log.Message(parent.pawn.Label + " - Initialized menstruation comp"); //Log.Message(parent.pawn.Label + " - Initialized menstruation comp");
loaded = true; loaded = true;
} }
@ -995,7 +1061,7 @@ namespace RJW_Menstruation
else if (ovarypower < OvaryPowerThreshold) else if (ovarypower < OvaryPowerThreshold)
{ {
Hediff hediff = HediffMaker.MakeHediff(VariousDefOf.Hediff_Climacteric, parent.pawn); Hediff hediff = HediffMaker.MakeHediff(VariousDefOf.Hediff_Climacteric, parent.pawn);
hediff.Severity = 0.008f * (OvaryPowerThreshold - ovarypower); hediff.Severity = Mathf.InverseLerp(OvaryPowerThreshold, 0, ovarypower);
parent.pawn.health.AddHediff(hediff, Genital_Helper.get_genitalsBPR(parent.pawn)); parent.pawn.health.AddHediff(hediff, Genital_Helper.get_genitalsBPR(parent.pawn));
} }
} }
@ -1146,14 +1212,12 @@ namespace RJW_Menstruation
PregnancyHelper.PregnancyDecider(parent.pawn, egg.fertilizer); PregnancyHelper.PregnancyDecider(parent.pawn, egg.fertilizer);
// I hate having to do this, but it gets the newest pregnancy // I hate having to do this, but it gets the newest pregnancy
pregnancy = parent.pawn.health.hediffSet.GetHediffs<Hediff_BasePregnancy>().MaxBy(hediff => hediff.loadID); pregnancy = parent.pawn.health.hediffSet.GetHediffs<Hediff_BasePregnancy>().MaxBy(hediff => hediff.loadID);
currentIntervalHours = (int)pregnancy?.GestationHours();
pregnant = true; pregnant = true;
break; break;
} }
else else
{ {
pregnancy = Hediff_BasePregnancy.Create<Hediff_MultiplePregnancy>(parent.pawn, egg.fertilizer); pregnancy = Hediff_BasePregnancy.Create<Hediff_MultiplePregnancy>(parent.pawn, egg.fertilizer);
currentIntervalHours = (int)pregnancy?.GestationHours();
pregnant = true; pregnant = true;
deadeggs.Add(egg); deadeggs.Add(egg);
} }
@ -1317,7 +1381,7 @@ namespace RJW_Menstruation
float eggnum; float eggnum;
try try
{ {
eggnum = Rand.ByCurve(parent.pawn.RaceProps.litterSizeCurve); eggnum = Rand.ByCurve(parent.pawn.def.race.litterSizeCurve);
} }
catch (NullReferenceException) catch (NullReferenceException)
{ {
@ -1325,7 +1389,7 @@ namespace RJW_Menstruation
} }
catch (ArgumentException e) catch (ArgumentException e)
{ {
Log.Warning($"Invalid litterSizeCurve for {parent.pawn.RaceProps}: {e}"); Log.Warning($"Invalid litterSizeCurve for {parent.pawn.def}: {e}");
eggnum = 1; eggnum = 1;
} }
eggnum += eggstack; eggnum += eggstack;
@ -1351,7 +1415,7 @@ namespace RJW_Menstruation
else if (Configurations.EnableMenopause && ovarypower < OvaryPowerThreshold) else if (Configurations.EnableMenopause && ovarypower < OvaryPowerThreshold)
{ {
Hediff hediff = HediffMaker.MakeHediff(VariousDefOf.Hediff_Climacteric, parent.pawn); Hediff hediff = HediffMaker.MakeHediff(VariousDefOf.Hediff_Climacteric, parent.pawn);
hediff.Severity = 0.008f * i; hediff.Severity = Mathf.InverseLerp(OvaryPowerThreshold, 0, ovarypower);
parent.pawn.health.AddHediff(hediff, Genital_Helper.get_genitalsBPR(parent.pawn)); parent.pawn.health.AddHediff(hediff, Genital_Helper.get_genitalsBPR(parent.pawn));
GoNextStage(Stage.ClimactericLuteal); GoNextStage(Stage.ClimactericLuteal);
} }
@ -1394,10 +1458,6 @@ namespace RJW_Menstruation
EggDecay(); EggDecay();
if (Implant()) if (Implant())
{ {
if (Breast != null)
{
Breast.PregnancyTransition();
}
GoNextStage(Stage.Pregnant); GoNextStage(Stage.Pregnant);
} }
else else
@ -1459,10 +1519,6 @@ namespace RJW_Menstruation
else else
{ {
if (pregnancy != null) pregnancy = null; if (pregnancy != null) pregnancy = null;
if (Breast != null)
{
Breast.BirthTransition();
}
GoNextStage(Stage.Recover); GoNextStage(Stage.Recover);
} }
} }
@ -1584,108 +1640,12 @@ namespace RJW_Menstruation
TaleRecorder.RecordTale(VariousDefOf.TaleCameInside, new object[] { cummer, parent.pawn }); TaleRecorder.RecordTale(VariousDefOf.TaleCameInside, new object[] { cummer, parent.pawn });
} }
private Action PeriodSimulator(Stage targetstage)
{
Action action = null;
switch (targetstage)
{
case Stage.Follicular:
action = delegate
{
FollicularAction(false);
};
break;
case Stage.Ovulatory:
action = OvulatoryAction;
break;
case Stage.Luteal:
action = delegate
{
LutealAction(false);
};
break;
case Stage.Bleeding:
action = delegate
{
BleedingAction(false);
};
break;
case Stage.Pregnant:
action = PregnantAction;
break;
case Stage.Recover:
action = RecoverAction;
break;
case Stage.None:
action = delegate
{
StayCurrentStageConst(Stage.None);
};
break;
case Stage.Young:
action = YoungAction;
break;
case Stage.ClimactericFollicular:
action = delegate
{
FollicularAction(true);
};
break;
case Stage.ClimactericLuteal:
action = delegate
{
LutealAction(true);
};
break;
case Stage.ClimactericBleeding:
action = delegate
{
BleedingAction(true);
};
break;
case Stage.Anestrus:
action = AnestrusAction;
break;
default:
curStage = Stage.Follicular;
curStageHrs = 0;
if (currentIntervalHours < 0) currentIntervalHours = PeriodRandomizer(curStage);
HugsLibController.Instance.TickDelayScheduler.ScheduleCallback(PeriodSimulator(Stage.Follicular), GetNextUpdate(), parent.pawn, false);
break;
}
action += delegate
{
if (parent.pawn.health.capacities.GetLevel(xxx.reproduction) <= 0) curStage = Stage.Young;
//CumOut();
AfterSimulator();
};
action = CumOut + action;
actionref = action;
return actionref;
}
protected int GetNextUpdate()
{
// Ticks past the hour. Will be equal except for game start or load
int currentOffset = Find.TickManager.TicksGame % tickInterval;
int nextOffset = (parent.pawn.HashOffset() % tickInterval + tickInterval) % tickInterval; // Messy, but HashOffset is negative a lot
// The -1/+1 to ensure that equality works out to 1 hour and not 0 ticks
return ((nextOffset - currentOffset + tickInterval - 1) % tickInterval) + 1;
}
protected void GoNextStage(Stage nextstage, bool calculateHours = true) protected void GoNextStage(Stage nextstage, bool calculateHours = true)
{ {
curStageHrs = 0; curStageHrs = 0;
float variabilityFactor = nextstage == Stage.ClimactericFollicular || nextstage == Stage.ClimactericLuteal || nextstage == Stage.ClimactericBleeding ? 6.0f : 1.0f; float variabilityFactor = nextstage == Stage.ClimactericFollicular || nextstage == Stage.ClimactericLuteal || nextstage == Stage.ClimactericBleeding ? 6.0f : 1.0f;
if (calculateHours) currentIntervalHours = PeriodRandomizer(nextstage, variabilityFactor); if (calculateHours) currentIntervalHours = PeriodRandomizer(nextstage, variabilityFactor);
curStage = nextstage; curStage = nextstage;
HugsLibController.Instance.TickDelayScheduler.ScheduleCallback(PeriodSimulator(nextstage), GetNextUpdate(), parent.pawn, false);
} }
protected virtual void GoOvulatoryStage(bool climacteric) protected virtual void GoOvulatoryStage(bool climacteric)
@ -1696,13 +1656,11 @@ namespace RJW_Menstruation
//stage can be interrupted in other reasons //stage can be interrupted in other reasons
protected void StayCurrentStage() protected void StayCurrentStage()
{ {
HugsLibController.Instance.TickDelayScheduler.ScheduleCallback(PeriodSimulator(curStage), GetNextUpdate(), parent.pawn, false);
} }
//stage never changes //stage never changes
protected void StayCurrentStageConst(Stage curstage) protected void StayCurrentStageConst(Stage curstage)
{ {
HugsLibController.Instance.TickDelayScheduler.ScheduleCallback(PeriodSimulator(curstage), GetNextUpdate(), parent.pawn, false);
} }
protected void GoFollicularOrBleeding() protected void GoFollicularOrBleeding()
@ -1745,6 +1703,8 @@ namespace RJW_Menstruation
return (int)(Props.bleedingIntervalDays * 24 * (1 + Rand.Range(-cycleVariability, cycleVariability) * 0.5f * variabilityFactor) / (1 + (cycleSpeed - 1) * 0.5f)); return (int)(Props.bleedingIntervalDays * 24 * (1 + Rand.Range(-cycleVariability, cycleVariability) * 0.5f * variabilityFactor) / (1 + (cycleSpeed - 1) * 0.5f));
case Stage.Recover: case Stage.Recover:
return (int)(Props.recoveryIntervalDays * 24 * Rand.Range(-0.05f, 0.05f)); return (int)(Props.recoveryIntervalDays * 24 * Rand.Range(-0.05f, 0.05f));
case Stage.Pregnant:
return (int)MenstruationUtility.GestationHours(pregnancy);
default: // Often unused default: // Often unused
return 1; return 1;
} }
@ -1772,24 +1732,32 @@ namespace RJW_Menstruation
protected Stage RandomStage() protected Stage RandomStage()
{ {
int rand = Rand.ElementByWeight( Stage stage = Rand.ElementByWeight(
0, Props.follicularIntervalDays - Props.bleedingIntervalDays, Stage.Follicular, Props.follicularIntervalDays - Props.bleedingIntervalDays,
1, Props.lutealIntervalDays, Stage.Luteal, Props.lutealIntervalDays,
2, Props.bleedingIntervalDays); Stage.Bleeding, Props.bleedingIntervalDays);
switch (rand) switch (stage)
{ {
case 0: case Stage.Follicular:
curStageHrs = Rand.Range(0, (Props.follicularIntervalDays - Props.bleedingIntervalDays) * 24); curStageHrs = Rand.Range(0, (Props.follicularIntervalDays - Props.bleedingIntervalDays) * 24);
return Stage.Follicular; break;
case 1: case Stage.Luteal:
curStageHrs = Rand.Range(0, Props.lutealIntervalDays * 24); curStageHrs = Rand.Range(0, Props.lutealIntervalDays * 24);
return Stage.Luteal; break;
case 2: case Stage.Bleeding:
curStageHrs = Rand.Range(0, Props.bleedingIntervalDays * 24); curStageHrs = Rand.Range(0, Props.bleedingIntervalDays * 24);
return Stage.Bleeding; break;
default: return Stage.Follicular;
} }
return stage;
}
public void CopyCycleProperties(HediffComp_Menstruation original)
{
cycleSpeed = original.cycleSpeed;
cycleVariability = original.cycleVariability;
ovarypower = original.ovarypower;
crampPain = original.crampPain;
} }
public class Egg : IExposable public class Egg : IExposable

View file

@ -9,6 +9,8 @@ namespace RJW_Menstruation
{ {
public class Hediff_MultiplePregnancy : Hediff_BasePregnancy public class Hediff_MultiplePregnancy : Hediff_BasePregnancy
{ {
protected Dictionary<Pawn, Pawn> enzygoticSiblings; // Each pawn and who they split from
public override void DiscoverPregnancy() public override void DiscoverPregnancy()
{ {
PregnancyThought(); PregnancyThought();
@ -185,6 +187,68 @@ namespace RJW_Menstruation
//baby.story.birthLastName = last_name; //baby.story.birthLastName = last_name;
} }
protected void CopyBodyPartProperties(Hediff part, Hediff originalPart)
{
CompHediffBodyPart comp = part.TryGetComp<CompHediffBodyPart>();
CompHediffBodyPart originalComp = originalPart.TryGetComp<CompHediffBodyPart>();
if (comp != null && originalComp != null)
{
// the string properties should be the same between both pawns anyways, besides the name of the owner
part.Severity = originalPart.Severity;
comp.SizeBase = originalComp.SizeBase;
comp.SizeOwner = originalComp.SizeOwner;
comp.EffSize = originalComp.EffSize;
comp.FluidAmmount = originalComp.FluidAmmount;
comp.FluidModifier = originalComp.FluidModifier;
}
HediffComp_Menstruation originalMenstruationComp = originalPart.GetMenstruationComp();
if (originalMenstruationComp != null)
{
part.GetMenstruationComp()?.CopyCycleProperties(originalMenstruationComp);
}
HediffComp_Breast originalBreastComp = originalPart.GetBreastComp();
if (originalBreastComp != null)
{
part.GetBreastComp()?.CopyBreastProperties(originalBreastComp);
}
}
protected void CopyBodyPartRecord(Pawn baby, Pawn original, BodyPartRecord babyBPR, BodyPartRecord originalBPR)
{
if (babyBPR == null || originalBPR == null) return;
RemoveBabyParts(baby, Genital_Helper.get_PartsHediffList(baby, babyBPR));
foreach (Hediff originalPart in Genital_Helper.get_PartsHediffList(original, originalBPR))
{
Hediff part = SexPartAdder.MakePart(originalPart.def, baby, babyBPR);
CopyBodyPartProperties(part, originalPart);
baby.health.AddHediff(part, babyBPR);
}
}
// Baby is the sibling to be changed, original is the first of the set and the one to copy to the rest.
public virtual void ProcessIdenticalSibling(Pawn baby, Pawn original)
{
// They'll be the same pawnkind, which lets us make a lot of useful assumptions
// However, some RNG might still be involved in genital generation (e.g. futas), so the easiest method is to clear out and re-generate
// A bit wasteful since Hediff_BasePregnancy.PostBirth already redid the genitals
CopyBodyPartRecord(baby, original, Genital_Helper.get_genitalsBPR(baby), Genital_Helper.get_genitalsBPR(original));
CopyBodyPartRecord(baby, original, Genital_Helper.get_breastsBPR(baby), Genital_Helper.get_breastsBPR(original));
CopyBodyPartRecord(baby, original, Genital_Helper.get_anusBPR(baby), Genital_Helper.get_anusBPR(original));
if (baby.IsHAR())
HARCompatibility.CopyHARPropertiesPostBirth(baby, original);
}
public override void PostBirth(Pawn mother, Pawn father, Pawn baby)
{
base.PostBirth(mother, father, baby);
// Has to happen on birth since RJW redoes the genitals at birth
if (!enzygoticSiblings.NullOrEmpty() && enzygoticSiblings.TryGetValue(baby, out Pawn original) && baby != original)
ProcessIdenticalSibling(baby, original);
}
// From RJW's trait code // From RJW's trait code
protected List<Trait> GetInheritableTraits(Pawn mother, Pawn father) protected List<Trait> GetInheritableTraits(Pawn mother, Pawn father)
{ {
@ -279,13 +343,17 @@ namespace RJW_Menstruation
return traitpool; return traitpool;
} }
public override void ExposeData()
{
base.ExposeData();
Scribe_Collections.Look(ref enzygoticSiblings, "enzygoticSiblings", keyLookMode: LookMode.Reference, valueLookMode: LookMode.Reference);
}
protected override void GenerateBabies() protected override void GenerateBabies()
{ {
AddNewBaby(pawn, father); AddNewBaby(pawn, father);
} }
protected void Train(Pawn baby, Pawn mother) protected void Train(Pawn baby, Pawn mother)
{ {
if (xxx.is_human(baby) || baby.Faction != Faction.OfPlayer) return; if (xxx.is_human(baby) || baby.Faction != Faction.OfPlayer) return;
@ -348,27 +416,25 @@ namespace RJW_Menstruation
); );
int division = 1; int division = 1;
HairDef firsthair = null; Pawn firstbaby = null;
Color firsthaircolor = Color.white;
BodyTypeDef firstbody = null;
CrownType firstcrown = CrownType.Undefined;
string firstheadpath = null; string firstheadpath = null;
string firstHARcrown = null;
int traitSeed = Rand.Int; int traitSeed = Rand.Int;
List<Trait> parentTraits = GetInheritableTraits(mother, father); List<Trait> parentTraits = GetInheritableTraits(mother, father);
while (Rand.Chance(Configurations.EnzygoticTwinsChance) && division < Configurations.MaxEnzygoticTwins) division++; while (Rand.Chance(Configurations.EnzygoticTwinsChance) && division < Configurations.MaxEnzygoticTwins) division++;
for (int i = 0; i < division; i++) for (int i = 0; i < division; i++)
{ {
Pawn baby = GenerateBaby(request, mother, father, parentTraits, traitSeed); Pawn baby = GenerateBaby(request, mother, father, parentTraits, traitSeed);
if (baby == null) break;
if (division > 1) if (division > 1)
{ {
if (i == 0 && baby.story != null) if (i == 0) firstbaby = baby;
else enzygoticSiblings?.Add(baby, firstbaby);
if (baby.story == null) continue;
if (i == 0)
{ {
firsthair = baby.story.hairDef;
firsthaircolor = baby.story.hairColor;
request.FixedGender = baby.gender; request.FixedGender = baby.gender;
firstbody = baby.story.bodyType;
firstcrown = baby.story.crownType;
firstheadpath = (string)baby.story.GetMemberValue("headGraphicPath"); firstheadpath = (string)baby.story.GetMemberValue("headGraphicPath");
if (firstheadpath == null) if (firstheadpath == null)
{ {
@ -376,36 +442,25 @@ namespace RJW_Menstruation
if (head != null) baby.story.SetMemberValue("headGraphicPath", head.GraphicPath); if (head != null) baby.story.SetMemberValue("headGraphicPath", head.GraphicPath);
firstheadpath = (string)baby.story.GetMemberValue("headGraphicPath"); firstheadpath = (string)baby.story.GetMemberValue("headGraphicPath");
} }
if (Configurations.HARActivated && baby.IsHAR())
{
firstHARcrown = baby.GetHARCrown();
}
} }
else else
{ {
if (baby.story != null) baby.story.hairDef = firstbaby.story.hairDef;
{ baby.story.hairColor = firstbaby.story.hairColor;
baby.story.hairDef = firsthair; baby.story.bodyType = firstbaby.story.bodyType;
baby.story.hairColor = firsthaircolor; baby.story.crownType = firstbaby.story.crownType;
baby.story.bodyType = firstbody; baby.story.SetMemberValue("headGraphicPath", firstheadpath);
baby.story.crownType = firstcrown;
baby.story.SetMemberValue("headGraphicPath", firstheadpath);
if (Configurations.HARActivated && baby.IsHAR()) if (baby.IsHAR())
{ {
baby.SetHARCrown(firstHARcrown); HARCompatibility.CopyHARProperties(baby, firstbaby);
}
} }
} }
} }
babies.Add(baby);
if (baby != null) babies.Add(baby);
} }
return true; return true;
} }
@ -665,8 +720,17 @@ namespace RJW_Menstruation
pawn.story.traits.allTraits = selectedTraits; pawn.story.traits.allTraits = selectedTraits;
} }
public override void Initialize(Pawn mother, Pawn dad)
{
enzygoticSiblings = new Dictionary<Pawn, Pawn>();
base.Initialize(mother, dad);
}
public string DueDate()
{
if (pawn.Map == null) return "";
return GenDate.DateFullStringWithHourAt(GenDate.TickGameToAbs((int)p_end_tick), Find.WorldGrid.LongLatOf(pawn.Map.Tile));
}
} }
/// <summary> /// <summary>

View file

@ -43,12 +43,18 @@ namespace RJW_Menstruation
{ {
Texture2D icon, icon_overay; Texture2D icon, icon_overay;
string description = ""; string description = "";
if (Configurations.Debug) description += comp.curStage + ": " + comp.curStageHrs + "\n" + "fertcums: " + comp.TotalFertCum + "\n" + "ovarypower: " + comp.ovarypower + "\n" + "eggs: " + comp.GetNumofEggs + "\n"; if (Configurations.Debug) {
description += comp.curStage + ": " + comp.curStageHrs + "\n" +
(comp.Pregnancy is Hediff_MultiplePregnancy preg ? "due: " + preg.DueDate() + "\n" : "") +
"fertcums: " + comp.TotalFertCum + "\n" +
"ovarypower: " + comp.ovarypower + "\n" +
"eggs: " + comp.GetNumofEggs + "\n";
}
else description += comp.GetCurStageLabel + "\n"; else description += comp.GetCurStageLabel + "\n";
if (pawn.IsPregnant()) if (pawn.IsPregnant())
{ {
Hediff_BasePregnancy hediff = comp.Pregnancy; Hediff_BasePregnancy hediff = comp.Pregnancy;
if (Utility.ShowFetusImage(hediff)) if (hediff != null && Utility.ShowFetusImage(hediff))
{ {
icon = comp.GetPregnancyIcon(hediff); icon = comp.GetPregnancyIcon(hediff);
if (hediff is Hediff_BasePregnancy h) if (hediff is Hediff_BasePregnancy h)

View file

@ -1,5 +1,4 @@
using HarmonyLib; using HarmonyLib;
using HugsLib;
using RimWorld; using RimWorld;
using System.Collections.Generic; using System.Collections.Generic;
using UnityEngine; using UnityEngine;
@ -16,14 +15,12 @@ namespace RJW_Menstruation
//Log.Message("Initialize on spawnsetup"); //Log.Message("Initialize on spawnsetup");
foreach (HediffComp_Menstruation comp in __instance.GetMenstruationComps()) foreach (HediffComp_Menstruation comp in __instance.GetMenstruationComps())
{ {
HugsLibController.Instance.TickDelayScheduler.TryUnscheduleCallback(comp.actionref);
comp.Initialize(); comp.Initialize();
} }
HediffComp_Breast bcomp = __instance.GetBreastComp(); HediffComp_Breast bcomp = __instance.GetBreastComp();
if (bcomp != null) if (bcomp != null)
{ {
HugsLibController.Instance.TickDelayScheduler.TryUnscheduleCallback(bcomp.action);
bcomp.Initialize(); bcomp.Initialize();
} }
} }

View file

@ -89,12 +89,12 @@
<Compile Include="VariousDefOf.cs" /> <Compile Include="VariousDefOf.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Assembly-CSharp"> <Reference Include="AlienRace">
<HintPath>..\..\..\..\..\..\RimWorldWin64_Data\Managed\Assembly-CSharp.dll</HintPath> <HintPath>..\..\..\..\..\..\..\..\workshop\content\294100\839005762\1.3\Assemblies\AlienRace.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
<Reference Include="HugsLib"> <Reference Include="Assembly-CSharp">
<HintPath>..\..\..\..\..\..\..\..\workshop\content\294100\818773962\v1.3\Assemblies\HugsLib.dll</HintPath> <HintPath>..\..\..\..\..\..\RimWorldWin64_Data\Managed\Assembly-CSharp.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
<Reference Include="RJW"> <Reference Include="RJW">

View file

@ -48,7 +48,7 @@ namespace RJW_Menstruation
{ {
protected override void SurgeryResult(HediffComp_Breast breast) protected override void SurgeryResult(HediffComp_Breast breast)
{ {
breast.AdjustAreolaSizeImmidiately(0.1f); breast.AdjustAreolaSizeImmediately(0.2f);
} }
} }
@ -56,7 +56,7 @@ namespace RJW_Menstruation
{ {
protected override void SurgeryResult(HediffComp_Breast breast) protected override void SurgeryResult(HediffComp_Breast breast)
{ {
breast.AdjustAreolaSizeImmidiately(-0.1f); breast.AdjustAreolaSizeImmediately(-0.2f);
} }
} }
@ -64,7 +64,7 @@ namespace RJW_Menstruation
{ {
protected override void SurgeryResult(HediffComp_Breast breast) protected override void SurgeryResult(HediffComp_Breast breast)
{ {
breast.AdjustNippleSizeImmidiately(0.1f); breast.AdjustNippleSizeImmediately(0.2f);
} }
} }
@ -72,7 +72,7 @@ namespace RJW_Menstruation
{ {
protected override void SurgeryResult(HediffComp_Breast breast) protected override void SurgeryResult(HediffComp_Breast breast)
{ {
breast.AdjustNippleSizeImmidiately(-0.1f); breast.AdjustNippleSizeImmediately(-0.2f);
} }
} }
} }

View file

@ -98,14 +98,6 @@ namespace RJW_Menstruation
public static readonly string Option23_Label = "Option23_Label".Translate(); public static readonly string Option23_Label = "Option23_Label".Translate();
public static readonly string Option23_Label_1 = "Option23_Label_1".Translate(); public static readonly string Option23_Label_1 = "Option23_Label_1".Translate();
public static readonly string Option23_Label_2 = "Option23_Label_2".Translate(); public static readonly string Option23_Label_2 = "Option23_Label_2".Translate();
public static readonly string Option24_Label = "Option24_Label".Translate();
public static readonly string Option24_Desc = "Option24_Desc".Translate();
public static readonly string Option25_Label = "Option25_Label".Translate();
public static readonly string Option25_Desc = "Option25_Desc".Translate();
public static readonly string Option26_Label = "Option26_Label".Translate();
public static readonly string Option26_Desc = "Option26_Desc".Translate();
public static readonly string Option27_Label = "Option27_Label".Translate();
public static readonly string Option27_Desc = "Option27_Desc".Translate();
public static readonly string Option28_Label = "Option28_Label".Translate(); public static readonly string Option28_Label = "Option28_Label".Translate();
public static readonly string Option28_Tooltip = "Option28_Tooltip".Translate(); public static readonly string Option28_Tooltip = "Option28_Tooltip".Translate();
public static readonly string Option29_Label = "Option29_Label".Translate(); public static readonly string Option29_Label = "Option29_Label".Translate();
@ -116,6 +108,12 @@ namespace RJW_Menstruation
public static readonly string Option31_Desc = "Option31_Desc".Translate(); public static readonly string Option31_Desc = "Option31_Desc".Translate();
public static readonly string Option32_Label = "Option32_Label".Translate(); public static readonly string Option32_Label = "Option32_Label".Translate();
public static readonly string Option32_Desc = "Option32_Desc".Translate(); public static readonly string Option32_Desc = "Option32_Desc".Translate();
public static readonly string Option_MaxBreastIncrementFactor_Label = "Option_MaxBreastIncrementFactor_Label".Translate();
public static readonly string Option_MaxBreastIncrementFactor_Desc = "Option_MaxBreastIncrementFactor_Desc".Translate();
public static readonly string Option_MaxNippleIncrementFactor_Label = "Option_MaxNippleIncrementFactor_Label".Translate();
public static readonly string Option_MaxNippleIncrementFactor_Desc = "Option_MaxNippleIncrementFactor_Desc".Translate();
public static readonly string Option_PermanentNippleChange_Label = "Option_PermanentNippleChange_Label".Translate();
public static readonly string Option_PermanentNippleChange_Desc = "Option_PermanentNippleChange_Desc".Translate();
public static readonly string Option_EnableGatherCumGizmo_Label = "Option_EnableGatherCumGizmo_Label".Translate(); public static readonly string Option_EnableGatherCumGizmo_Label = "Option_EnableGatherCumGizmo_Label".Translate();
public static readonly string Option_EstrusOverride_Label = "Option_EstrusOverride_Label".Translate(); public static readonly string Option_EstrusOverride_Label = "Option_EstrusOverride_Label".Translate();
public static readonly string Option_EstrusOverride_Desc = "Option_EstrusOverride_Desc".Translate(); public static readonly string Option_EstrusOverride_Desc = "Option_EstrusOverride_Desc".Translate();

View file

@ -32,11 +32,11 @@ namespace RJW_Menstruation
private Texture2D anal; private Texture2D anal;
private Color cumcolor; private Color cumcolor;
private readonly GUIStyle fontstylecenter = new GUIStyle() { alignment = TextAnchor.MiddleCenter }; private static GUIStyle fontstylecenter = null;
private readonly GUIStyle fontstyleright = new GUIStyle() { alignment = TextAnchor.MiddleRight }; private static GUIStyle fontstyleright = null;
private readonly GUIStyle fontstyleleft = new GUIStyle() { alignment = TextAnchor.MiddleLeft }; private static GUIStyle fontstyleleft = null;
private readonly GUIStyle boxstyle = new GUIStyle(GUI.skin.textArea); private static GUIStyle boxstyle = null;
private readonly GUIStyle buttonstyle = new GUIStyle(GUI.skin.button); private static GUIStyle buttonstyle = null;
public Pawn Pawn public Pawn Pawn
{ {
@ -137,6 +137,16 @@ namespace RJW_Menstruation
} }
public override void PreOpen()
{
base.PreOpen();
if (fontstylecenter == null) fontstylecenter = new GUIStyle() { alignment = TextAnchor.MiddleCenter };
if (fontstyleright == null) fontstyleright = new GUIStyle() { alignment = TextAnchor.MiddleRight };
if (fontstyleleft == null) fontstyleleft = new GUIStyle() { alignment = TextAnchor.MiddleLeft };
if (boxstyle == null) boxstyle = new GUIStyle(GUI.skin.textArea);
if (buttonstyle == null) buttonstyle = new GUIStyle(GUI.skin.button);
}
private void MainContents(Rect mainRect) private void MainContents(Rect mainRect)
{ {
@ -370,7 +380,7 @@ namespace RJW_Menstruation
pawn.DrawBreastIcon(BreastIconRect, Mouse.IsOver(BreastIconRect) && Input.GetMouseButton(0)); pawn.DrawBreastIcon(BreastIconRect);
GUI.color = Color.white; GUI.color = Color.white;

View file

@ -159,7 +159,7 @@ namespace RJW_Menstruation
} }
} }
public static void DrawBreastIcon(this Pawn pawn, Rect rect, bool drawOrigin = false) public static void DrawBreastIcon(this Pawn pawn, Rect rect)
{ {
Hediff hediff = Genital_Helper.get_PartsHediffList(pawn, Genital_Helper.get_breastsBPR(pawn)).FirstOrDefault((Hediff h) => h.def.defName.ToLower().Contains("breast")); Hediff hediff = Genital_Helper.get_PartsHediffList(pawn, Genital_Helper.get_breastsBPR(pawn)).FirstOrDefault((Hediff h) => h.def.defName.ToLower().Contains("breast"));
Texture2D breast, nipple, areola; Texture2D breast, nipple, areola;
@ -167,7 +167,7 @@ namespace RJW_Menstruation
{ {
HediffComp_Breast comp = hediff.TryGetComp<HediffComp_Breast>(); HediffComp_Breast comp = hediff.TryGetComp<HediffComp_Breast>();
string icon; string icon;
if (comp != null) icon = comp.Props.BreastTex ?? "Breasts/Breast_Breast"; if (comp != null) icon = comp.Props?.BreastTex ?? "Breasts/Breast_Breast";
else else
{ {
breast = ContentFinder<Texture2D>.Get("Breasts/Breast_Breast00", false); breast = ContentFinder<Texture2D>.Get("Breasts/Breast_Breast00", false);
@ -191,16 +191,8 @@ namespace RJW_Menstruation
string nippleicon, areolaicon; string nippleicon, areolaicon;
float nipplesize, areolasize; float nipplesize, areolasize;
if (drawOrigin) nipplesize = comp.NippleSize;
{ areolasize = comp.AreolaSize;
nipplesize = comp.OriginNipple;
areolasize = comp.OriginAreola;
}
else
{
nipplesize = comp.NippleSize;
areolasize = comp.AreolaSize;
}
nippleicon = icon + "_Nipple0" + GetNippleIndex(nipplesize); nippleicon = icon + "_Nipple0" + GetNippleIndex(nipplesize);
areolaicon = icon + "_Areola0" + GetAreolaIndex(areolasize); areolaicon = icon + "_Areola0" + GetAreolaIndex(areolasize);
@ -212,14 +204,8 @@ namespace RJW_Menstruation
GUI.color = pawn.story.SkinColor; GUI.color = pawn.story.SkinColor;
GUI.DrawTexture(rect, breast, ScaleMode.ScaleToFit); GUI.DrawTexture(rect, breast, ScaleMode.ScaleToFit);
if (drawOrigin) GUI.color = comp.NippleColor;
{
GUI.color = comp.OriginColor;
}
else
{
GUI.color = comp.NippleColor;
}
GUI.DrawTexture(rect, areola, ScaleMode.ScaleToFit); GUI.DrawTexture(rect, areola, ScaleMode.ScaleToFit);
GUI.DrawTexture(rect, nipple, ScaleMode.ScaleToFit); GUI.DrawTexture(rect, nipple, ScaleMode.ScaleToFit);

View file

@ -13,12 +13,6 @@
<steamWorkshopUrl>steam://url/CommunityFilePage/2009463077</steamWorkshopUrl> <steamWorkshopUrl>steam://url/CommunityFilePage/2009463077</steamWorkshopUrl>
<downloadUrl>https://github.com/pardeike/HarmonyRimWorld/releases/latest</downloadUrl> <downloadUrl>https://github.com/pardeike/HarmonyRimWorld/releases/latest</downloadUrl>
</li> </li>
<li>
<packageId>UnlimitedHugs.HugsLib</packageId>
<displayName>HugsLib</displayName>
<downloadUrl>https://github.com/UnlimitedHugs/RimworldHugsLib/releases/latest</downloadUrl>
<steamWorkshopUrl>steam://url/CommunityFilePage/818773962</steamWorkshopUrl>
</li>
<li> <li>
<packageId>rim.job.world</packageId> <packageId>rim.job.world</packageId>
<displayName>RimJobWorld</displayName> <displayName>RimJobWorld</displayName>
@ -26,12 +20,13 @@
</li> </li>
</modDependencies> </modDependencies>
<loadAfter> <loadAfter>
<li>UnlimitedHugs.HugsLib</li>
<li>brrainz.harmony</li> <li>brrainz.harmony</li>
<li>erdelf.HumanoidAlienRaces</li>
<li>rim.job.world</li> <li>rim.job.world</li>
<li>Abraxas.RJW.RaceSupport</li> <li>Abraxas.RJW.RaceSupport</li>
<li>rjw.milk.humanoid</li> <li>rjw.milk.humanoid</li>
<li>rjw.sexperience</li> <li>rjw.sexperience</li>
<li>rjw.cum</li>
</loadAfter> </loadAfter>
<packageId>rjw.menstruation</packageId> <packageId>rjw.menstruation</packageId>
<description>Adds menstruation mechanics to vaginas <description>Adds menstruation mechanics to vaginas

View file

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Manifest> <Manifest>
<identifier>RJW Menstruation</identifier> <identifier>RJW Menstruation</identifier>
<version>1.0.7.0</version> <version>1.0.7.1</version>
<dependencies> <dependencies>
</dependencies> </dependencies>
<incompatibleWith /> <incompatibleWith />
@ -12,6 +12,7 @@
<li>Abraxas.RJW.RaceSupport</li> <li>Abraxas.RJW.RaceSupport</li>
<li>rjw.milk.humanoid</li> <li>rjw.milk.humanoid</li>
<li>rjw.sexperience</li> <li>rjw.sexperience</li>
<li>rjw.cum</li>
</loadAfter> </loadAfter>
<suggests> <suggests>
</suggests> </suggests>

View file

@ -1,3 +1,15 @@
Version 1.0.7.1
- Null reference error fix for multiple wombs when one is pregnant.
- Fix the progress bar on pregnancy again. Also make undiscovered pregnancies a little more subtle.
- Nipple size/transition system rewritten to be simpler under the hood. Should work with existing saves, but you might find sizes to be different, especially for very large or very small breasts.
- Replaced HugsLib-based scheduler with normal ticking. This should reduce some 'phantom cycle' bugs.
- Redone calculation to determine low eggs remaining. This should cause climacteric to be applied at a more appropriate time in the pawn's life, especially for those with very long cycles.
- Identical twins conceived after this update will have identical sex part sizes, properties, etc. upon being born.
- Identical twins of HAR races will have identical coloration, part variations, and masking.
- For modders:
- The function Hediff_MultiplePregnancy.ProcessIdenticalSibling is called on every identical sibling when born except the first. Any race-specfic genetic properties can be patched in there.
- Any mods that add comps to RJW parts should copy what they need to on a postfix to Hediff_MultiplePregnancy.CopyBodyPartProperties, e.g. how menstruation itself does in that function.
Version 1.0.7.0 Version 1.0.7.0
- Not save compatible with previous versions. Expect glitches and many red errors if you try. However, things should stabilize eventually. - Not save compatible with previous versions. Expect glitches and many red errors if you try. However, things should stabilize eventually.
- Designed for RJW 5.0.0, but should work with previous versions. - Designed for RJW 5.0.0, but should work with previous versions.