code:
using RimWorld;
using rjw;
using System;
using System.Collections.Generic;
using UnityEngine;
using Verse;

namespace RJW_Menstruation
{
    public class Configurations : ModSettings
    {
        public const float ImplantationChanceDefault = 0.65f;
        public const int ImplantationChanceAdjustDefault = 65;
        public const float FertilizeChanceDefault = 0.15f;
        public const int FertilizeChanceAdjustDefault = 150;
        public const float CumDecayRatioDefault = 0.15f;
        public const int CumDecayRatioAdjustDefault = 150;
        public const float CumFertilityDecayRatioDefault = 0.05f;
        public const int CumFertilityDecayRatioAdjustDefault = 50;
        public const int CycleAccelerationDefault = 6;
        public const int ColonistTickIntervalDefault = GenDate.TicksPerHour;
        public const int NonColonistTickIntervalDefault = GenDate.TicksPerHour;
        public const int AnimalTickIntervalDefault = GenDate.TicksPerHour;
        public const int TickIntervalMinimum = GenTicks.TicksPerRealSecond / 3;
        public const int TickIntervalMaximum = 4 * GenDate.TicksPerHour;
        public const float EnzygoticTwinsChanceDefault = 0.002f;
        public const int EnzygoticTwinsChanceAdjustDefault = 2;
        public const int MaxEnzygoticTwinsDefault = 9;
        public const int BleedingAmountDefault = 50;
        public const float MaxBreastIncrementFactorDefault = 1.0f;
        public const float MaxBreastIncrementFactorMax = 2.5f;
        public const float MaxNippleIncrementFactorDefault = 1.0f;
        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 VaginaMorphPowerDefault = 0.2f;

        public static DetailLevel infoDetail = DetailLevel.All;

        public static float ImplantationChance = ImplantationChanceDefault;
        public static int ImplantationChanceAdjust = ImplantationChanceAdjustDefault;
        public static float FertilizeChance = FertilizeChanceDefault;
        public static int FertilizeChanceAdjust = FertilizeChanceAdjustDefault;
        public static float CumDecayRatio = CumDecayRatioDefault;
        public static int CumDecayRatioAdjust = CumDecayRatioAdjustDefault;
        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;
        public static bool DrawWombStatus = true;
        public static bool DrawVaginaStatus = true;
        public static bool DrawEggOverlay = true;
        public static bool Debug = false;
        public static bool EnableMenopause = true;
        public static bool EnablePheromones = true;
        public static float AnimalPheromoneEffect = 0.0f;
        public static DetailLevel InfoDetail => infoDetail;
        public static bool EstrusOverridesHookupSettings = false;
        public static float EstrusFuckabilityToHookup = RJWHookupSettings.MinimumFuckabilityToHookup;
        public static float EstrusAttractivenessToHookup = RJWHookupSettings.MinimumAttractivenessToHookup;
        public static float EstrusRelationshipToHookup = RJWHookupSettings.MinimumRelationshipToHookup;
        public static PregnancyType PregnancySource = PregnancyType.MultiplePregnancy;
        public static bool EnableBiotechTwins = false;
        public static bool EnableHeteroOvularTwins = true;
        public static bool EnableEnzygoticTwins = true;
        public static float EnzygoticTwinsChance = EnzygoticTwinsChanceDefault;
        public static int EnzygoticTwinsChanceAdjust = EnzygoticTwinsChanceAdjustDefault;
        public static int MaxEnzygoticTwins = MaxEnzygoticTwinsDefault;
        public static int BleedingAmount = BleedingAmountDefault;
        public static bool EnableButtonInHT = false;
        public static PawnFlags ShowFlag = PawnFlags.Colonist | PawnFlags.Prisoner;
        public static bool UseHybridExtention = true;
        public static bool AllowShrinkIcon = false;
        public static float EggLifespanMultiplier = EggLifespanMultiplierDefault;
        public static bool EnableBirthVaginaMorph = false;
        public static float VaginaMorphPower = VaginaMorphPowerDefault;
        public static float MaxBreastIncrementFactor = MaxBreastIncrementFactorDefault;
        public static float MaxNippleIncrementFactor = MaxNippleIncrementFactorDefault;
        public static float PermanentNippleChange = PermanentNippleChangeDefault;
        public static void SetToDefault()
        {
            ImplantationChanceAdjust = ImplantationChanceAdjustDefault;
            FertilizeChanceAdjust = FertilizeChanceAdjustDefault;
            CumDecayRatioAdjust = CumDecayRatioAdjustDefault;
            CumFertilityDecayRatioAdjust = CumFertilityDecayRatioAdjustDefault;
            EnableWombIcon = true;
            EnableDraftedIcon = true;
            EnableAnimalCycle = false;
            EnableMenopause = true;
            EnablePheromones = true;
            AnimalPheromoneEffect = 0.0f;
            CycleAcceleration = CycleAccelerationDefault;
            ColonistTickInterval = ColonistTickIntervalDefault;
            NonColonistTickInterval = NonColonistTickIntervalDefault;
            AnimalTickInterval = AnimalTickIntervalDefault;
            EstrusOverridesHookupSettings = false;
            EstrusFuckabilityToHookup = RJWHookupSettings.MinimumFuckabilityToHookup;
            EstrusAttractivenessToHookup = RJWHookupSettings.MinimumAttractivenessToHookup;
            EstrusRelationshipToHookup = RJWHookupSettings.MinimumRelationshipToHookup;
            EnzygoticTwinsChanceAdjust = EnzygoticTwinsChanceAdjustDefault;
            EnableBiotechTwins = false;
            EnableEnzygoticTwins = true;
            EnableHeteroOvularTwins = true;
            PregnancySource = PregnancyType.MultiplePregnancy;
            MaxEnzygoticTwins = MaxEnzygoticTwinsDefault;
            BleedingAmount = BleedingAmountDefault;
            MaxBreastIncrementFactor = MaxBreastIncrementFactorDefault;
            MaxNippleIncrementFactor= MaxNippleIncrementFactorDefault;
            PermanentNippleChange = PermanentNippleChangeDefault;
            EggLifespanMultiplier = EggLifespanMultiplierDefault;
            VaginaMorphPower = VaginaMorphPowerDefault;
        }

        public static List<HybridInformations> HybridOverride = new List<HybridInformations>();

        public static bool HARActivated = false;
        public static bool AnimalGeneticsActivated = false;

        public enum DetailLevel
        {
            All,
            OnReveal,
            HideFetusInfo,
            Hide
        }

        public static string LevelString(DetailLevel level)
        {
            switch (level)
            {
                case DetailLevel.All:
                    return "All";
                case DetailLevel.OnReveal:
                    return "On reveal";
                case DetailLevel.HideFetusInfo:
                    return "Hide fetus info";
                case DetailLevel.Hide:
                    return "Hide";
                default:
                    return "";
            }

        }
        public static string HybridString(bool b)
        {
            if (b) return Translations.Option23_Label_1;
            else return Translations.Option23_Label_2;
        }

        public static bool IsOverrideExist(ThingDef def)
        {
            List<HybridInformations> removeList = new List<HybridInformations>();
            if (!HybridOverride.NullOrEmpty())
                foreach (HybridInformations o in HybridOverride)
                {
                    if (o.IsNull) removeList.Add(o);
                    if (o.DefName == def.defName) return true;
                }
            foreach (HybridInformations o in removeList)
            {
                HybridOverride.Remove(o);
            }
            return false;
        }

        [Flags]
        public enum PawnFlags
        {
            None = 0,
            Colonist = 1,
            Prisoner = 2,
            Ally = 4,
            Neutral = 8,
            Hostile = 16
        }

        public enum PregnancyType
        {
            BaseRJW,
            MultiplePregnancy,
            Biotech
        }

        public override void ExposeData()
        {
            Scribe_Values.Look(ref ImplantationChanceAdjust, "ImplantationChanceAdjust", ImplantationChanceAdjustDefault);
            Scribe_Values.Look(ref ImplantationChance, "ImplantationChance", ImplantationChanceDefault);
            Scribe_Values.Look(ref FertilizeChanceAdjust, "FertilizeChanceAdjust", FertilizeChanceAdjustDefault);
            Scribe_Values.Look(ref FertilizeChance, "FertilizeChance", FertilizeChanceDefault);
            Scribe_Values.Look(ref CumDecayRatioAdjust, "CumDecayRatioAdjust", CumDecayRatioAdjustDefault);
            Scribe_Values.Look(ref CumDecayRatio, "CumDecayRatio", CumDecayRatioDefault);
            Scribe_Values.Look(ref CumFertilityDecayRatioAdjust, "CumFertilityDecayRatioAdjust", CumFertilityDecayRatioAdjustDefault);
            Scribe_Values.Look(ref CumFertilityDecayRatio, "CumFertilityDecayRatio", CumFertilityDecayRatioDefault);
            Scribe_Values.Look(ref CycleAcceleration, "CycleAcceleration", CycleAccelerationDefault);
            Scribe_Values.Look(ref ColonistTickInterval, "ColonistTickInterval", ColonistTickIntervalDefault);
            Scribe_Values.Look(ref NonColonistTickInterval, "NonColonistTickInterval", NonColonistTickIntervalDefault);
            Scribe_Values.Look(ref AnimalTickInterval, "AnimalTickInterval", AnimalTickIntervalDefault);
            Scribe_Values.Look(ref EnableWombIcon, "EnableWombIcon", true);
            Scribe_Values.Look(ref EnableDraftedIcon, "EnableDraftedIcon", true);
            Scribe_Values.Look(ref EnableAnimalCycle, "EnableAnimalCycle", false);
            Scribe_Values.Look(ref DrawWombStatus, "DrawWombStatus", true);
            Scribe_Values.Look(ref DrawVaginaStatus, "DrawVaginaStatus", true);
            Scribe_Values.Look(ref DrawEggOverlay, "DrawEggOvray", true);
            Scribe_Values.Look(ref Debug, "Debug", false);
            Scribe_Values.Look(ref infoDetail, "InfoDetail", DetailLevel.All);
            Scribe_Values.Look(ref EnableMenopause, "EnableMenopause", true);
            Scribe_Values.Look(ref EnablePheromones, "EnablePheromones", true);
            Scribe_Values.Look(ref AnimalPheromoneEffect, "AnimalPheromoneEffect", 0.0f);
            Scribe_Values.Look(ref EstrusOverridesHookupSettings, "EstrusOverridesHookupSettings", false);
            Scribe_Values.Look(ref EstrusFuckabilityToHookup, "EstrusFuckabilityToHookup", EstrusFuckabilityToHookup, true);
            Scribe_Values.Look(ref EstrusAttractivenessToHookup, "EstrusAttractivenessToHookup", EstrusAttractivenessToHookup, true);
            Scribe_Values.Look(ref EstrusRelationshipToHookup, "EstrusRelationshipToHookup", EstrusRelationshipToHookup, true);
            Scribe_Values.Look(ref PregnancySource, "PregnancySource", PregnancyType.MultiplePregnancy);
            Scribe_Values.Look(ref EnableBiotechTwins, "EnableBiotechTwins", false);
            Scribe_Values.Look(ref EnableHeteroOvularTwins, "EnableHeteroOvularTwins", true);
            Scribe_Values.Look(ref EnableEnzygoticTwins, "EnableEnzygoticTwins", true);
            Scribe_Values.Look(ref EnzygoticTwinsChance, "EnzygoticTwinsChance", EnzygoticTwinsChanceDefault);
            Scribe_Values.Look(ref EnzygoticTwinsChanceAdjust, "EnzygoticTwinsChanceAdjust", EnzygoticTwinsChanceAdjustDefault);
            Scribe_Values.Look(ref MaxEnzygoticTwins, "MaxEnzygoticTwins", MaxEnzygoticTwinsDefault);
            Scribe_Values.Look(ref BleedingAmount, "BleedingAmount", BleedingAmountDefault);
            Scribe_Values.Look(ref EnableButtonInHT, "EnableButtonInHT", false);
            Scribe_Values.Look(ref ShowFlag, "ShowFlag", PawnFlags.Colonist | PawnFlags.Prisoner);
            Scribe_Values.Look(ref UseHybridExtention, "UseHybridExtention", true);
            Scribe_Values.Look(ref MaxBreastIncrementFactor, "MaxBreastIncrementFactor", MaxBreastIncrementFactorDefault);
            Scribe_Values.Look(ref MaxNippleIncrementFactor, "MaxNippleIncrementFactor", MaxNippleIncrementFactorDefault);
            Scribe_Values.Look(ref PermanentNippleChange, "PermanentNippleChange", PermanentNippleChangeDefault);
            Scribe_Values.Look(ref AllowShrinkIcon, "AllowShrinkIcon", false);
            Scribe_Values.Look(ref EggLifespanMultiplier, "EggLifespanMultiplier", EggLifespanMultiplierDefault);
            Scribe_Values.Look(ref EnableBirthVaginaMorph, "EnableBirthVaginaMorph", false);
            Scribe_Values.Look(ref VaginaMorphPower, "VaginaMorphPower", VaginaMorphPowerDefault);
            Scribe_Collections.Look(ref HybridOverride, saveDestroyedThings: true, label: "HybridOverride", lookMode: LookMode.Deep, ctorArgs: new object[0]);
            base.ExposeData();
        }

    }

    public class RJW_Menstruation : Mod
    {

        private static Vector2 scroll;

        public static float EstimatedBleedingAmount
        {
            get
            {
                int days = VariousDefOf.HumanVaginaCompProperties.bleedingIntervalDays;
                return days * 0.03f * Configurations.BleedingAmount * 6;
            }
        }

        public static float EstimatedBleedingAmountPerHour
        {
            get
            {
                return 0.03f * Configurations.BleedingAmount * Configurations.CycleAcceleration;
            }
        }

        public RJW_Menstruation(ModContentPack content) : base(content)
        {
            GetSettings<Configurations>();
            if (!ModsConfig.BiotechActive && Configurations.PregnancySource == Configurations.PregnancyType.Biotech)
                Configurations.PregnancySource = Configurations.PregnancyType.MultiplePregnancy;
            Configurations.HARActivated = ModsConfig.IsActive("erdelf.HumanoidAlienRaces");
            Configurations.AnimalGeneticsActivated = ModsConfig.IsActive("Mlie.AnimalGenetics");
        }

        public override string SettingsCategory()
        {
            return Translations.Mod_Title;
        }

        public override void DoSettingsWindowContents(Rect inRect)
        {
            Rect outRect = new Rect(0f, 30f, inRect.width, inRect.height - 30f);
            float mainRectHeight = 126f +
                (Configurations.EnableWombIcon || Configurations.EnableButtonInHT ? 400f : 0f) +
                (Configurations.EnableAnimalCycle ? 48f : 0f) +
                (Configurations.EnableAnimalCycle && Configurations.EnablePheromones ? 48f : 0f) +
                (Configurations.EstrusOverridesHookupSettings ? 144f : 0f) +
                (Configurations.PregnancySource == Configurations.PregnancyType.MultiplePregnancy ? (Configurations.EnableEnzygoticTwins ? 175f : 75f) : 0f) +
                (Configurations.PregnancySource == Configurations.PregnancyType.Biotech ? 75f : 0f) +
                (Configurations.PregnancySource == Configurations.PregnancyType.Biotech ? (Configurations.EnableBiotechTwins ? 175f : 75f) : 0f) +
                (Configurations.EnableBirthVaginaMorph ? 48f : 0f);
            Rect mainRect = new Rect(0f, 0f, inRect.width - 30f, Math.Max(inRect.height + mainRectHeight, 1f));
            int Adjust;
            Listing_Standard listmain = new Listing_Standard
            {
                maxOneColumn = true
            };
            Widgets.BeginScrollView(outRect, ref scroll, mainRect);
            listmain.Begin(mainRect);
            listmain.Gap(20f);
            Rect firstLine = listmain.GetRect(30f);
            firstLine.SplitVertically(firstLine.width / 3, out Rect leftCell, out Rect middleAndRightCells);
            Widgets.CheckboxLabeled(leftCell, Translations.Option1_Label_1, ref Configurations.EnableWombIcon, false, null, null, true);
            Widgets.CheckboxLabeled(middleAndRightCells.LeftHalf(), Translations.Option1_Label_2, ref Configurations.EnableButtonInHT, false, null, null, true);
            if (Configurations.EnableWombIcon || Configurations.EnableButtonInHT)
            {
                Listing_Standard wombsection = listmain.BeginSection(380);
                wombsection.CheckboxLabeled(Translations.Option9_Label, ref Configurations.DrawWombStatus, Translations.Option9_Desc);
                if (Configurations.DrawWombStatus)
                {
                    wombsection.CheckboxLabeled(Translations.Option18_Label, ref Configurations.DrawEggOverlay, Translations.Option18_Desc);
                }

                wombsection.CheckboxLabeled(Translations.Option10_Label, ref Configurations.DrawVaginaStatus, Translations.Option10_Desc);
                wombsection.CheckboxLabeled(Translations.Option29_Label, ref Configurations.AllowShrinkIcon, Translations.Option29_Desc);
                wombsection.CheckboxLabeled(Translations.Option_EnableDraftedIcon_Label, ref Configurations.EnableDraftedIcon, Translations.Option_EnableDraftedIcon_Desc);
                if (wombsection.ButtonText(Translations.Option11_Label + ": " + Configurations.LevelString(Configurations.infoDetail)))
                {
                    if (Configurations.infoDetail == Configurations.DetailLevel.Hide) Configurations.infoDetail = Configurations.DetailLevel.All;
                    else Configurations.infoDetail++;
                }
                switch (Configurations.infoDetail)
                {
                    case Configurations.DetailLevel.All:
                        wombsection.Label(Translations.Option11_Desc_1);
                        break;
                    case Configurations.DetailLevel.OnReveal:
                        wombsection.Label(Translations.Option11_Desc_2);
                        break;
                    case Configurations.DetailLevel.HideFetusInfo:
                        wombsection.Label(Translations.Option11_Desc_3);
                        break;
                    case Configurations.DetailLevel.Hide:
                        wombsection.Label(Translations.Option11_Desc_4);
                        break;
                }
                wombsection.Label(Translations.Option21_Label + " " + Configurations.ShowFlag, -1, Translations.Option21_Desc);
                Rect flagrect = wombsection.GetRect(30f);
                Rect[] flagrects = new Rect[5];
                for (int i = 0; i < 5; i++)
                {
                    flagrects[i] = new Rect(flagrect.x + (flagrect.width / 5) * i, flagrect.y, flagrect.width / 5, flagrect.height);
                }

                if (Widgets.ButtonText(flagrects[0], Translations.Option20_Label_1 + ": " + Configurations.ShowFlag.HasFlag(Configurations.PawnFlags.Colonist)))
                {
                    Configurations.ShowFlag ^= Configurations.PawnFlags.Colonist;
                }
                if (Widgets.ButtonText(flagrects[1], Translations.Option20_Label_2 + ": " + Configurations.ShowFlag.HasFlag(Configurations.PawnFlags.Prisoner)))
                {
                    Configurations.ShowFlag ^= Configurations.PawnFlags.Prisoner;
                }
                if (Widgets.ButtonText(flagrects[2], Translations.Option20_Label_3 + ": " + Configurations.ShowFlag.HasFlag(Configurations.PawnFlags.Ally)))
                {
                    Configurations.ShowFlag ^= Configurations.PawnFlags.Ally;
                }
                if (Widgets.ButtonText(flagrects[3], Translations.Option20_Label_4 + ": " + Configurations.ShowFlag.HasFlag(Configurations.PawnFlags.Neutral)))
                {
                    Configurations.ShowFlag ^= Configurations.PawnFlags.Neutral;
                }
                if (Widgets.ButtonText(flagrects[4], Translations.Option20_Label_5 + ": " + Configurations.ShowFlag.HasFlag(Configurations.PawnFlags.Hostile)))
                {
                    Configurations.ShowFlag ^= Configurations.PawnFlags.Hostile;
                }

                Adjust = (int)(Configurations.MaxBreastIncrementFactor * 1000 / Configurations.MaxBreastIncrementFactorMax);
                wombsection.Label(Translations.Option_MaxBreastIncrementFactor_Label + " " + Configurations.MaxBreastIncrementFactor * 100 + "%", -1, Translations.Option_MaxBreastIncrementFactor_Desc);
                Adjust = (int)wombsection.Slider(Adjust, 0, 1000);
                Configurations.MaxBreastIncrementFactor = (float)Adjust / (1000 / Configurations.MaxBreastIncrementFactorMax);

                Adjust = (int)(Configurations.MaxNippleIncrementFactor * 1000 / Configurations.MaxNippleIncrementFactorMax);
                wombsection.Label(Translations.Option_MaxNippleIncrementFactor_Label + " " + Configurations.MaxNippleIncrementFactor * 100 + "%", -1, Translations.Option_MaxNippleIncrementFactor_Desc);
                Adjust = (int)wombsection.Slider(Adjust, 0, 1000);
                Configurations.MaxNippleIncrementFactor = (float)Adjust / (1000 / Configurations.MaxNippleIncrementFactorMax);

                Adjust = (int)(Configurations.PermanentNippleChange * 1000 / Configurations.PermanentNippleChangeMax);
                wombsection.Label(Translations.Option_PermanentNippleChange_Label + " " + Configurations.PermanentNippleChange, -1, Translations.Option_PermanentNippleChange_Desc);
                Adjust = (int)wombsection.Slider(Adjust, 0, 1000);
                Configurations.PermanentNippleChange = (float)Adjust / (1000 / Configurations.PermanentNippleChangeMax);

                listmain.EndSection(wombsection);
            }

            listmain.CheckboxLabeled(Translations.Option2_Label, ref Configurations.EnableAnimalCycle, Translations.Option2_Desc);

            listmain.CheckboxLabeled(Translations.Option12_Label, ref Configurations.EnableMenopause, Translations.Option12_Desc);

            listmain.CheckboxLabeled(Translations.Option_EnablePheromones_Label, ref Configurations.EnablePheromones, Translations.Option_EnablePheromones_Desc);
            if (Configurations.EnablePheromones && Configurations.EnableAnimalCycle)
            {
                Adjust = (int)(Configurations.AnimalPheromoneEffect * 1000);
                listmain.Label(Translations.Option_AnimalPheromoneEffect_Label + " " + Configurations.AnimalPheromoneEffect * 100 + "%", -1, Translations.Option_AnimalPheromoneEffect_Desc);
                Adjust = (int)listmain.Slider(Adjust, 0, 1000);
                Configurations.AnimalPheromoneEffect = (float)Adjust / 1000f;
            }
            else Configurations.AnimalPheromoneEffect = 0.0f;

            listmain.Label(Translations.Option3_Label + " " + Configurations.ImplantationChance * 100 + "%", -1, Translations.Option3_Desc);
            Configurations.ImplantationChanceAdjust = (int)listmain.Slider(Configurations.ImplantationChanceAdjust, 0, 1000);
            Configurations.ImplantationChance = (float)Configurations.ImplantationChanceAdjust / 100;

            string tenMl = String.Format("10 ml: {0:0}%", (1.0f - Mathf.Pow(1.0f - Configurations.FertilizeChance, 10)) * 100f);
            listmain.LabelDouble(Translations.Option4_Label + " " + Configurations.FertilizeChance * 100 + "%", tenMl, Translations.Option4_Desc);
            Configurations.FertilizeChanceAdjust = (int)listmain.Slider(Configurations.FertilizeChanceAdjust, 0, 1000);
            Configurations.FertilizeChance = (float)Configurations.FertilizeChanceAdjust / 1000;

            listmain.Label(Translations.Option5_Label + " " + Configurations.CumDecayRatio * 100 + "%", -1, Translations.Option5_Desc);
            Configurations.CumDecayRatioAdjust = (int)listmain.Slider(Configurations.CumDecayRatioAdjust, 0, 1000);
            Configurations.CumDecayRatio = (float)Configurations.CumDecayRatioAdjust / 1000;

            Adjust = (int)(Configurations.EggLifespanMultiplier * 20);
            float lifespan = (24f / Configurations.CycleAcceleration * Configurations.EggLifespanMultiplier);
            listmain.LabelDouble(Translations.Option30_Label + " x" + Configurations.EggLifespanMultiplier, Translations.EstimatedEggLifespan + String.Format(": {0:0}h", (int)lifespan), Translations.Option30_Desc);
            Adjust = (int)listmain.Slider(Adjust, 20, 1000);
            Configurations.EggLifespanMultiplier = (float)Adjust / 20;

            int semenlifespan = (int)(-5 / ((float)Math.Log10((1 - Configurations.CumFertilityDecayRatio) * (1 - Configurations.CumDecayRatio) * 10) - 1)) + 1;
            string estimatedlifespan;
            if (semenlifespan < 0)
            {
                estimatedlifespan = string.Format(": Infinite", semenlifespan);
            }
            else
            {
                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);
            Configurations.CumFertilityDecayRatio = (float)Configurations.CumFertilityDecayRatioAdjust / 1000;

            listmain.Label(Translations.Option7_Label + " x" + Configurations.CycleAcceleration, -1, Translations.Option7_Desc);
            Configurations.CycleAcceleration = (int)listmain.Slider(Configurations.CycleAcceleration, 1, 50);

            listmain.LabelDouble(Translations.Option_ColonistUpdateInterval_Label, GenDate.ToStringTicksToPeriod(Configurations.ColonistTickInterval), Translations.Option_ColonistUpdateInterval_Desc);
            Configurations.ColonistTickInterval = (int)Mathf.Exp(listmain.Slider(Mathf.Log(Configurations.ColonistTickInterval), Mathf.Log(Configurations.TickIntervalMinimum), Mathf.Log(Configurations.TickIntervalMaximum)));

            listmain.LabelDouble(Translations.Option_NonColonistUpdateInterval_Label, GenDate.ToStringTicksToPeriod(Configurations.NonColonistTickInterval), Translations.Option_NonColonistUpdateInterval_Desc);
            Configurations.NonColonistTickInterval = (int)Mathf.Exp(listmain.Slider(Mathf.Log(Configurations.NonColonistTickInterval), Mathf.Log(Configurations.TickIntervalMinimum), Mathf.Log(Configurations.TickIntervalMaximum)));

            if(Configurations.EnableAnimalCycle)
            {
                listmain.LabelDouble(Translations.Option_AnimalUpdateInterval_Label, GenDate.ToStringTicksToPeriod(Configurations.AnimalTickInterval), Translations.Option_AnimalUpdateInterval_Desc);
                Configurations.AnimalTickInterval = (int)Mathf.Exp(listmain.Slider(Mathf.Log(Configurations.AnimalTickInterval), Mathf.Log(Configurations.TickIntervalMinimum), Mathf.Log(Configurations.TickIntervalMaximum)));
            }

            float var2 = EstimatedBleedingAmountPerHour;
            float var1 = Math.Max(EstimatedBleedingAmount, var2);
            listmain.LabelDouble(Translations.Option19_Label_1, Translations.Option19_Label_2 + ": " + var1 + "ml, " + var2 + "ml/h", Translations.Option19_Desc);
            Configurations.BleedingAmount = (int)listmain.Slider(Configurations.BleedingAmount, 0, 200);

            listmain.CheckboxLabeled(Translations.Option_EstrusOverride_Label, ref Configurations.EstrusOverridesHookupSettings, Translations.Option_EstrusOverride_Desc);
            if (Configurations.EstrusOverridesHookupSettings)
            {
                listmain.Label(Translations.Option_EstrusFuckability_Label + ": " + (int)(Configurations.EstrusFuckabilityToHookup * 100) + "%");
                Configurations.EstrusFuckabilityToHookup = listmain.Slider(Configurations.EstrusFuckabilityToHookup, 0.1f, 1.0f);
                listmain.Label(Translations.Option_EstrusAttractability_Label + ": " + (int)(Configurations.EstrusAttractivenessToHookup * 100) + "%");
                Configurations.EstrusAttractivenessToHookup = listmain.Slider(Configurations.EstrusAttractivenessToHookup, 0.0f, 1.0f);
                listmain.Label(Translations.Option_EstrusRelationship_Label + ": " + Configurations.EstrusRelationshipToHookup);
                Configurations.EstrusRelationshipToHookup = listmain.Slider((int)Configurations.EstrusRelationshipToHookup, -100f, 100f);
            }

            if (listmain.RadioButton(Translations.Option_PregnancyFromBaseRJW_Label, Configurations.PregnancySource == Configurations.PregnancyType.BaseRJW))
                Configurations.PregnancySource = Configurations.PregnancyType.BaseRJW;
            if (listmain.RadioButton(Translations.Option_PregnancyFromMultiplePregnancy_Label, Configurations.PregnancySource == Configurations.PregnancyType.MultiplePregnancy))
                Configurations.PregnancySource = Configurations.PregnancyType.MultiplePregnancy;
            if (ModsConfig.BiotechActive && listmain.RadioButton(Translations.Option_PregnancyFromBiotech_Label, Configurations.PregnancySource == Configurations.PregnancyType.Biotech))
                Configurations.PregnancySource = Configurations.PregnancyType.Biotech;
            if (Configurations.PregnancySource == Configurations.PregnancyType.Biotech)
                listmain.CheckboxLabeled(Translations.Option_EnableBiotechTwins_Label, ref Configurations.EnableBiotechTwins, Translations.Option_EnableBiotechTwins_Desc);
            if (Configurations.PregnancySource == Configurations.PregnancyType.MultiplePregnancy ||
                (Configurations.PregnancySource == Configurations.PregnancyType.Biotech && Configurations.EnableBiotechTwins))
            {
                float sectionheight = 75f;
                if (Configurations.EnableEnzygoticTwins) sectionheight += 100;
                Listing_Standard twinsection = listmain.BeginSection(sectionheight);
                Rect hybridrect = twinsection.GetRect(25);
                Widgets.CheckboxLabeled(hybridrect.LeftHalf(), Translations.Option22_Label, ref Configurations.UseHybridExtention, false, null, null, true);
                if (Widgets.ButtonText(hybridrect.RightHalf(), Translations.Option28_Label))
                {
                    Dialog_HybridCustom.ToggleWindow();
                    //Configurations.MotherFirst = !Configurations.MotherFirst;
                }
                TooltipHandler.TipRegion(hybridrect, Translations.Option28_Tooltip);

                twinsection.CheckboxLabeled(Translations.Option14_Label, ref Configurations.EnableHeteroOvularTwins, Translations.Option14_Desc);
                twinsection.CheckboxLabeled(Translations.Option15_Label, ref Configurations.EnableEnzygoticTwins, Translations.Option15_Desc);
                if (Configurations.EnableEnzygoticTwins)
                {
                    twinsection.Label(Translations.Option16_Label + " " + Configurations.EnzygoticTwinsChance * 100 + "%", -1, Translations.Option16_Desc);
                    Configurations.EnzygoticTwinsChanceAdjust = (int)twinsection.Slider(Configurations.EnzygoticTwinsChanceAdjust, 0, 1000);
                    Configurations.EnzygoticTwinsChance = (float)Configurations.EnzygoticTwinsChanceAdjust / 1000;

                    twinsection.Label(Translations.Option17_Label + " " + Configurations.MaxEnzygoticTwins, -1, Translations.Option17_Desc);
                    Configurations.MaxEnzygoticTwins = (int)twinsection.Slider(Configurations.MaxEnzygoticTwins, 2, 100);
                }
                listmain.EndSection(twinsection);
            }

            listmain.CheckboxLabeled(Translations.Option31_Label, ref Configurations.EnableBirthVaginaMorph, Translations.Option31_Desc);
            if (Configurations.EnableBirthVaginaMorph)
            {
                float sectionheight = 48f;
                Listing_Standard vmsection = listmain.BeginSection(sectionheight);

                LabelwithTextfield(vmsection.GetRect(24f), Translations.Option32_Label, Translations.Option32_Desc, ref Configurations.VaginaMorphPower, 0, 100f);
                Adjust = (int)(Configurations.VaginaMorphPower * 1000);
                Adjust = (int)vmsection.Slider(Adjust, 0, 1000);
                Configurations.VaginaMorphPower = Adjust / 1000f;

                listmain.EndSection(vmsection);
            }

            listmain.CheckboxLabeled(Translations.Option8_Label, ref Configurations.Debug, Translations.Option8_Desc);
            if (listmain.ButtonText(Translations.Button_ResetToDefault))
            {
                Configurations.SetToDefault();
            }

            listmain.End();
            Widgets.EndScrollView();
        }

        public void LabelwithTextfield(Rect rect, string label, string tooltip, ref float value, float min, float max)
        {
            Rect textfieldRect = new Rect(rect.xMax - 100f, rect.y, 100f, rect.height);
            string valuestr = value.ToString();
            Widgets.Label(rect, label);
            Widgets.TextFieldNumeric(textfieldRect, ref value, ref valuestr, min, max);
            Widgets.DrawHighlightIfMouseover(rect);
            TooltipHandler.TipRegion(rect, tooltip);
        }

    }

}


code:
using RimWorld;
using System;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
using Verse;

namespace RJW_Menstruation
{
    public class Cum : IExposable
    {
        public Pawn pawn;

        protected float volume; // ml
        public float fertility = 1.0f;
        public bool notcum = false; // for other fluids
        public string notcumLabel = "";
        protected bool useCustomColor = false;
        protected float notcumthickness = 0;
        protected float cumthickness = 1.0f;
        protected Thing internalThing;

        public float Volume
        {
            get
            {
                return volume;
            }
        }

        public float FertVolume
        {
            get
            {
                return volume * fertility;
            }
        }

        public float CumThickness
        {
            get
            {
                return cumthickness;
            }
        }

        public float DecayResist
        {
            get
            {
                if (!notcum) return DNA.cumThickness;
                else return notcumthickness;
            }
            set
            {
                notcumthickness = value;
            }
        }
        protected Color customColor;

        public PawnDNAModExtension DNA
        {
            get
            {
                if (DNAcache != null) return DNAcache;
                try
                {
                    DNAcache = pawn.def.GetModExtension<PawnDNAModExtension>();
                }
                catch (NullReferenceException)
                {
                    DNAcache = ThingDefOf.Human.GetModExtension<PawnDNAModExtension>();
                }
                if (DNAcache == null)
                {
                    DNAcache = ThingDefOf.Human.GetModExtension<PawnDNAModExtension>();
                }
                return DNAcache;
            }
        }
        protected PawnDNAModExtension DNAcache = null;
        public ThingDef FilthDef
        {
            get
            {
                if (filthDef == null) return VariousDefOf.CumFilth;
                else return filthDef;
            }
            set
            {
                filthDef = value;
            }
        }
        protected ThingDef filthDef = null;
        public Color Color
        {
            get
            {
                if (!useCustomColor) return DNA.CumColor;
                else return customColor;
            }

            set
            {
                useCustomColor = true;
                customColor = value;
            }
        }

        public Thing CumThing
        {
            get
            {
                if (internalThing == null)
                {
                    internalThing = ThingMaker.MakeThing(VariousDefOf.CumFilth);
                    internalThing.stackCount = (int)volume;
                }
                internalThing.stackCount = (int)volume;
                return internalThing;
            }
        }

        public Cum() { }

        public Cum(Pawn pawn)
        {
            this.pawn = pawn;
            volume = 1.0f;
            fertility = 1.0f;
        }

        /// <summary>
        /// Not Cum
        /// </summary>
        /// <param name="pawn"></param>
        /// <param name="volume"></param>
        /// <param name="notcumlabel"></param>
        /// <param name="decayresist"></param>
        /// <param name="filthDef"></param>
        public Cum(Pawn pawn, float volume, string notcumlabel, float decayresist = 0, ThingDef filthDef = null)
        {
            this.pawn = pawn;
            this.volume = volume;
            this.fertility = 0f;
            this.notcum = true;
            this.notcumLabel = notcumlabel;
            this.notcumthickness = decayresist;
            this.filthDef = filthDef;
        }

        public Cum(Pawn pawn, float volume, float fertility, ThingDef filthDef = null)
        {
            this.pawn = pawn;
            this.volume = volume;
            this.fertility = fertility;
            this.filthDef = filthDef;
        }

        public virtual void ExposeData()
        {
            Scribe_References.Look(ref pawn, "pawn", true);
            Scribe_Values.Look(ref volume, "volume", volume, true);
            Scribe_Values.Look(ref fertility, "fertility", fertility, true);
            Scribe_Values.Look(ref notcumthickness, "notcumthickness", 0);
            Scribe_Values.Look(ref notcum, "notcum", false);
            Scribe_Values.Look(ref notcumLabel, "notcumLabel", "");
            Scribe_Values.Look(ref useCustomColor, "useCustomColor", false);
            Scribe_Values.Look(ref customColor, "customColor", default);
            Scribe_Defs.Look(ref filthDef, "filthDef");
        }

        public void MakeThinner(float speed)
        {
            cumthickness = cumthickness.LerpMultiple(DecayResist, 0.3f, speed);
        }

        public void MergeWithCum(float volumein, float fertility, ThingDef updatefilthDef = null)
        {
            if (updatefilthDef != null) filthDef = updatefilthDef;
            volume += volumein;
            this.fertility = (this.volume * this.fertility + volumein * fertility) / (this.volume + volumein);
            cumthickness = Mathf.Lerp(cumthickness, 1.0f, volumein / volume);
        }

        public void MergeWithFluid(float volumein, float thickness, ThingDef updatefilthDef = null)
        {
            if (updatefilthDef != null) filthDef = updatefilthDef;
            volume += volumein;
            fertility = volume * fertility / (volume + volumein);
            notcumthickness = Mathf.Lerp(notcumthickness, thickness, volumein / volume);
        }

        public bool ShouldRemove()
        {
            if ((notcum || FertVolume < 0.001f) && volume < 0.01f) return true;
            return false;
        }

        public float DismishNatural(float leakfactor, HediffComp_Menstruation comp, float antisperm = 0.0f)
        {
            // comp is used for Hydrogen's RJW Muscle Injury
            float totalleak = volume;
            float decayPerInterval = 1 - Mathf.Pow(1 - Configurations.CumDecayRatio, comp.HoursBetweenSimulations);
            float fertilityDecayPerInterval = 1 - Mathf.Pow(1 - Configurations.CumFertilityDecayRatio, comp.HoursBetweenSimulations);
            antisperm *= comp.HoursBetweenSimulations;
            volume *= Math.Max(0, 1 - decayPerInterval * (1 - DecayResist) * leakfactor);
            fertility *= Math.Max(0, 1 - (fertilityDecayPerInterval * (1 - DecayResist) + antisperm));
            CutMinor();
            totalleak -= volume;
            return totalleak;
        }

        public float DismishForce(float portion, float leakfactor = 1.0f)
        {
            float totalleak = volume;
            volume *= Math.Max(0, 1 - (portion * (1 - DecayResist / 10)) * leakfactor);
            CutMinor();
            totalleak -= volume;
            return totalleak;
        }

        public void CumEffects(Pawn pawn)
        {
            if (notcum || DNA?.ingestionOutcomeDoers == null || volume < 1.0f) return;

            foreach (IngestionOutcomeDoer doer in DNA.ingestionOutcomeDoers)
                doer.DoIngestionOutcome(pawn, CumThing);
        }

        protected void CutMinor()
        {
            if (volume < 0.01f) volume = 0f;
        }
    }

    public class CumMixture : Cum
    {
        protected List<string> cums;
        public bool ispurecum = true;
        public List<string> Getingredients => cums;

        public CumMixture()
        {
            notcum = true;
            cums = new List<string>();
        }

        public CumMixture(Pawn pawn, float volume, List<string> cums, Color color, ThingDef mixtureDef, bool pure)
        {
            this.pawn = pawn;
            this.volume = volume;
            this.cums = cums;
            this.customColor = color;
            this.useCustomColor = true;
            ispurecum = pure;
        }

        public override void ExposeData()
        {
            base.ExposeData();
            Scribe_Collections.Look(ref cums, "cumslabel", LookMode.Value, new object[0]);
        }

        public string GetIngredients()
        {
            StringBuilder res = new StringBuilder();

            if (!cums.NullOrEmpty()) for (int i = 0; i < cums.Count; i++)
                {
                    res.Append(cums[i]);
                    if (i < cums.Count - 1) res.Append(", ");
                }
            return res.ToString();
        }
    }
}

license: unknown


code:
using RimWorld;
using Verse;

#pragma warning disable IDE0051 // Remove unused private members
namespace RJW_Menstruation
{
    public static class DebugToolsMenstruation
    {
        [DebugAction("RJW Menstruation", "Set pawn's state to follicular", false, false, actionType = DebugActionType.ToolMapForPawns, allowedGameStates = AllowedGameStates.Playing)]
        private static void SetFollicular(Pawn p)
        {
            foreach (HediffComp_Menstruation comp in p.GetMenstruationComps())
            {
                comp.GoNextStage(HediffComp_Menstruation.Stage.Follicular);
                comp.RemoveAllEggs();
            }
            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)]
        private static void SetOvulatory(Pawn p)
        {
            foreach (HediffComp_Menstruation comp in p.GetMenstruationComps())
                comp.GoNextStage(HediffComp_Menstruation.Stage.Ovulatory);
            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)]
        private static void SetLuteal(Pawn p)
        {
            foreach (HediffComp_Menstruation comp in p.GetMenstruationComps())
                comp.GoNextStage(HediffComp_Menstruation.Stage.Luteal);
            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)]
        private static void SetBleeding(Pawn p)
        {
            foreach (HediffComp_Menstruation comp in p.GetMenstruationComps())
            {
                comp.GoNextStage(HediffComp_Menstruation.Stage.Bleeding);
                comp.RemoveAllEggs();
            }
            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)]
        private static void setRecover(Pawn p)
        {
            p.GetMenstruationComp().curStage = HediffComp_Menstruation.Stage.Recover;
        }

        [DebugAction("RJW Menstruation", "Set pawn's state to young", false, false, actionType = DebugActionType.ToolMapForPawns, allowedGameStates = AllowedGameStates.Playing)]
        private static void setYoung(Pawn p)
        {
            p.GetMenstruationComp().curStage = HediffComp_Menstruation.Stage.Young;
        }
        */
        [DebugAction("RJW Menstruation", "Remove all cum from pawn's womb", false, false, actionType = DebugActionType.ToolMapForPawns, allowedGameStates = AllowedGameStates.Playing)]
        private static void RemoveCums(Pawn p)
        {
            foreach (HediffComp_Menstruation comp in p.GetMenstruationComps())
                comp.RemoveAllCums();
            Messages.Message($"All cum removed from {p}'s womb", p, MessageTypeDefOf.NeutralEvent, false);
        }
        [DebugAction("RJW Menstruation", "Remove all eggs from pawn's womb", false, false, actionType = DebugActionType.ToolMapForPawns, allowedGameStates = AllowedGameStates.Playing)]
        private static void RemoveEggs(Pawn p)
        {
            foreach (HediffComp_Menstruation comp in p.GetMenstruationComps())
                comp.RemoveAllEggs();
            Messages.Message($"All eggs 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)]
        private static void AddEgg(Pawn p)
        {
            foreach (HediffComp_Menstruation comp in p.GetMenstruationComps())
                comp.eggstack++;
            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)]
        private static void RecalculateOvaryPower(Pawn p)
        {
            foreach (HediffComp_Menstruation comp in p.GetMenstruationComps())
                comp.ovarypower = comp.GetOvaryPowerByAge();
            Messages.Message($"{p}'s ovarypower recalculated ({p.GetFirstMenstruationComp().ovarypower})", p, MessageTypeDefOf.NeutralEvent, false);
        }
    }
}
#pragma warning restore IDE0051 // Remove unused private members


code:
using RimWorld;
using System.Collections.Generic;
using Verse;

namespace RJW_Menstruation
{
    public class FertPillOutcomeDoer : IngestionOutcomeDoer
    {
        protected override void DoIngestionOutcomeSpecial(Pawn pawn, Thing ingested)
        {
            foreach (HediffComp_Menstruation comp in pawn.GetMenstruationComps())
                if (comp.curStage.Equals(HediffComp_Menstruation.Stage.Follicular)
                || comp.curStage.Equals(HediffComp_Menstruation.Stage.Luteal)
                || comp.curStage.Equals(HediffComp_Menstruation.Stage.Anestrus)
                )
                {
                    comp.SetEstrus();
                    comp.GoNextStage(HediffComp_Menstruation.Stage.Ovulatory);
                    comp.ovarypower--;
                }
        }
    }

    public class InduceOvulationOutcomeDoer : IngestionOutcomeDoer
    {
        protected override void DoIngestionOutcomeSpecial(Pawn pawn, Thing ingested)
        {
            foreach (HediffComp_Menstruation comp in pawn.GetMenstruationComps())
                if (comp.curStage.Equals(HediffComp_Menstruation.Stage.Follicular)
                    || comp.curStage.Equals(HediffComp_Menstruation.Stage.Anestrus)
                    )
                {
                    comp.SetEstrus();
                    comp.GoNextStage(HediffComp_Menstruation.Stage.Ovulatory);
                    comp.eggstack += ingested.stackCount - 1;
                }
        }
    }

    public class IngestionOutcomeDoer_AdjustSeverity : IngestionOutcomeDoer
    {
        public HediffDef hediffDef;
        public float severity;

        protected override void DoIngestionOutcomeSpecial(Pawn pawn, Thing ingested)
        {
            Hediff hediff = pawn.health.hediffSet.GetFirstHediffOfDef(hediffDef);
            if (hediff != null) hediff.Severity += severity;
        }

    }

    public class OvaryPillOutcomeDoer : IngestionOutcomeDoer
    {
        public float effectOffset;

        protected override void DoIngestionOutcomeSpecial(Pawn pawn, Thing ingested)
        {
            foreach (HediffComp_Menstruation comp in pawn.GetMenstruationComps())
                if (Configurations.EnableMenopause) comp.RecoverOvary(1 + effectOffset);
        }
    }

    public class SuperOvulationOutcomeDoer : IngestionOutcomeDoer
    {
        protected override void DoIngestionOutcomeSpecial(Pawn pawn, Thing ingested)
        {
            foreach (HediffComp_Menstruation comp in pawn.GetMenstruationComps())
                comp.eggstack += Rand.Range(1, 4);
        }
    }

    public class ContraceptiveOutcomeDoer : IngestionOutcomeDoer
    {
        protected override void DoIngestionOutcomeSpecial(Pawn pawn, Thing ingested)
        {

            List<Thought_Memory> memories = pawn.needs?.mood?.thoughts?.memories?.Memories.FindAll(
                x =>
                x.def == VariousDefOf.CameInsideF
             || x.def == VariousDefOf.CameInsideFFetish
             || x.def == VariousDefOf.HaterCameInsideF);
            if (memories.NullOrEmpty()) return;
            foreach (Thought_Memory m in memories)
            {
                if (m.def == VariousDefOf.HaterCameInsideF) m.moodPowerFactor = 0.5f;
                else m.moodPowerFactor = 0.3f;

            }
            if (pawn.IsProPregnancy(out _)) pawn.needs.mood.thoughts.memories.TryGainMemoryFast(VariousDefOf.HateTookContraceptivePill);
            else pawn.needs.mood.thoughts.memories.TryGainMemoryFast(VariousDefOf.TookContraceptivePill);
        }
    }
}


code:
using rjw;
using rjw.Modules.Interactions.Contexts;
using rjw.Modules.Interactions.Enums;
using rjw.Modules.Interactions.Rules.PartKindUsageRules;
using rjw.Modules.Shared;
using System.Collections.Generic;
using Verse;

namespace RJW_Menstruation.Interactions
{
    public class EstrusPartKindUsageRule : IPartPreferenceRule
    {
        private const float visible_estrus_multiplier = Multipliers.Doubled;
        private const float concealed_estrus_multiplier = 1.1f;

        private bool WillingAndAble(Pawn fucker, Pawn fucked, bool visible)
        {
            return
                fucked.IsInEstrus(visible)
                &&
                PregnancyHelper.CanImpregnate(fucker, fucked);
        }

        public IEnumerable<Weighted<LewdablePartKind>> ModifiersForDominant(InteractionContext context)
        {
            if (WillingAndAble(context.Internals.Submissive.Pawn, context.Internals.Dominant.Pawn, true))
                yield return new Weighted<LewdablePartKind>(visible_estrus_multiplier, LewdablePartKind.Vagina);
            else if (WillingAndAble(context.Internals.Submissive.Pawn, context.Internals.Dominant.Pawn, false))
                yield return new Weighted<LewdablePartKind>(concealed_estrus_multiplier, LewdablePartKind.Vagina);
        }

        public IEnumerable<Weighted<LewdablePartKind>> ModifiersForSubmissive(InteractionContext context)
        {
            if (WillingAndAble(context.Internals.Dominant.Pawn, context.Internals.Submissive.Pawn, true))
                yield return new Weighted<LewdablePartKind>(visible_estrus_multiplier, LewdablePartKind.Vagina);
            else if (WillingAndAble(context.Internals.Dominant.Pawn, context.Internals.Submissive.Pawn, false))
                yield return new Weighted<LewdablePartKind>(concealed_estrus_multiplier, LewdablePartKind.Vagina);
        }
    }
}


code:
using RimWorld;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using Verse;

namespace RJW_Menstruation
{
    public class FilthMaker_Colored
    {

        public static bool TryMakeFilth(IntVec3 c, Map map, ThingDef filthDef, IEnumerable<string> sources, Color color, bool shouldPropagate, FilthSourceFlags additionalFlags = FilthSourceFlags.None)
        {
            Filth_Colored filth = (Filth_Colored)(from t in c.GetThingList(map)
                                                  where t.def == filthDef
                                                  select t).FirstOrDefault<Thing>();
            if (!c.Walkable(map) || (filth != null && !filth.CanBeThickened))
            {
                if (shouldPropagate)
                {
                    List<IntVec3> list = GenAdj.AdjacentCells8WayRandomized();
                    for (int i = 0; i < 8; i++)
                    {
                        IntVec3 c2 = c + list[i];
                        if (c2.InBounds(map) && TryMakeFilth(c2, map, filthDef, sources, color, false, FilthSourceFlags.None))
                        {
                            return true;
                        }
                    }
                }
                filth?.AddSources(sources);
                return false;
            }
            if (filth != null)
            {
                filth.ThickenFilth();
                filth.AddSources(sources);
            }
            else
            {
                if (!FilthMaker.CanMakeFilth(c, map, filthDef, additionalFlags))
                {
                    return false;
                }
                Filth_Colored filth2 = (Filth_Colored)ThingMaker.MakeThing(filthDef, null);
                filth2.DrawColor = color;
                filth2.AddSources(sources);
                GenSpawn.Spawn(filth2, c, map, WipeMode.Vanish);
            }
            //FilthMonitor.Notify_FilthSpawned();
            return true;
        }

    }
}


code:
using RimWorld;
using RimWorld.Planet;
using rjw;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using Verse;

namespace RJW_Menstruation
{
    public class CompProperties_Breast : HediffCompProperties
    {
        public static readonly ColorInt DefaultBlacknippleColor = new ColorInt(55, 20, 0);
        public string BreastTex = "Breasts/Breast";
        public ColorInt BlacknippleColor = new ColorInt(55, 20, 0);

        public Color BlackNippleColor
        {
            get
            {
                return BlacknippleColor.ToColor;
            }
        }

        public CompProperties_Breast()
        {
            compClass = typeof(HediffComp_Breast);
        }
    }

    public class HediffComp_Breast : HediffComp
    {
        public const int tickInterval = GenDate.TicksPerHour * 3 / 2;
        public const float breastGrowthStart = 1f / 6f;
        public const float breastGrowthEnd = 1f / 3f;
        public static readonly SimpleCurve nippleTransitions = new SimpleCurve()
        {
            new CurvePoint(0f,0f),
            new CurvePoint(0.1f,0f),
            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;

        protected long ageOfLastBirth = 0;
        protected float maxBreastIncrement = -1f;
        protected float breastSizeIncreased = 0f;
        protected string debugGrowthStatus = "(Growth/shrink not yet calculated; run for 1.5h to update)";
        protected float nippleProgress = 0f;
        protected float baseAlpha = -1f;    // Will grow in response to pregnancy
        protected float baseAreola = -1f;
        protected float baseNipple = -1f;
        protected float cachedAlpha = -1f;  // Calculated dynamically instead of saved
        protected float cachedAreola = -1f; // Actual size = these * breast size
        protected float cachedNipple = -1f;
        protected float babyHalfAge = -1f;

        protected Color cachedColor;
        protected bool loaded = false;

        protected float BabyHalfAge
        {
            get
            {
                if (babyHalfAge > 0f) return babyHalfAge;
                List<LifeStageAge> ages = Pawn.RaceProps.lifeStageAges;
                if (ages?.Count > 1)
                    babyHalfAge = ages[1].minAge / 2;

                if (babyHalfAge <= 0) babyHalfAge = 1.2f / 2; // Default to human

                return babyHalfAge;
            }
        }

        protected void ShrinkBreasts()
        {
            // The natural rate will take them from full to empty during the second half of their child's babyhood
            float shrinkRate = tickInterval * MaxBreastIncrement / (BabyHalfAge * GenDate.TicksPerYear);
            float shrinkAmount = Mathf.Min(shrinkRate, breastSizeIncreased);
            breastSizeIncreased -= shrinkAmount;
            parent.Severity -= shrinkAmount;
        }

        protected float MaxBreastIncrement
        {
            get
            {
                return maxBreastIncrement * Configurations.MaxBreastIncrementFactor;
            }
        }

        public Color NippleColor
        {
            get
            {
                return cachedColor;
            }
        }
        public float Alpha
        {
            get
            {
                return cachedAlpha;
            }
        }
        public float NippleSize
        {
            get
            {
                return cachedNipple * parent.Severity;
            }
        }
        public float AreolaSize
        {
            get
            {
                return cachedAreola * parent.Severity;
            }
        }

        public float BreastSizeIncreased
        {
            get
            {
                return breastSizeIncreased;
            }
        }

        public override void CompExposeData()
        {
            base.CompExposeData();

            Scribe_Values.Look(ref ageOfLastBirth, "ageOfLastBirth", -1);
            Scribe_Values.Look(ref maxBreastIncrement, "maxBreastIncrement", maxBreastIncrement, true);
            Scribe_Values.Look(ref breastSizeIncreased, "breastSizeIncreased", 0.0f);
            Scribe_Values.Look(ref nippleProgress, "nippleProgress", 0.0f);
            Scribe_Values.Look(ref baseAlpha, "baseAlpha", baseAlpha, true);
            Scribe_Values.Look(ref baseAreola, "baseAreola", baseAreola, true);
            Scribe_Values.Look(ref baseNipple, "baseNipple", baseNipple, true);
            if (Scribe.mode == LoadSaveMode.PostLoadInit)
                Initialize();
        }

        public bool ShouldSimulate()
        {
            if (!Pawn.ShouldCycle()) return false;
            if (Pawn.SpawnedOrAnyParentSpawned || Pawn.IsCaravanMember() || PawnUtility.IsTravelingInTransportPodWorldObject(Pawn)) return true;

            return false;
        }

        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 (
                    !Pawn.IsHashIntervalTick(tickInterval) ||
                    !ShouldSimulate()
                    )
                {
                    return;
                }
                CalculateBreastSize();
                CalculateNipples();
                UpdateNipples();
            }
            catch (Exception ex)
            {
                Log.Error($"Error processing breasts of {Pawn}: {ex}");
            }

        }

        public override void CompPostPostAdd(DamageInfo? dinfo)
        {
            if (!loaded) Initialize();
            if (ageOfLastBirth > Pawn.ageTracker.AgeChronologicalTicks) ageOfLastBirth = CalculateLastBirth();   // catch transplant issues
        }

        public override void CompPostPostRemoved()
        {
            if (Pawn.health.hediffSet.hediffs.Contains(parent))
            {
                Log.Warning($"Attempted to remove breast comp from wrong pawn ({Pawn}).");
                return;
            }
            base.CompPostPostRemoved();
        }

        protected long CalculateLastBirth()
        {
            long youngestAge = -1;

            if ((Pawn.relations == null)) return youngestAge;

            List<Hediff_BasePregnancy> rjwPregnancies = new List<Hediff_BasePregnancy>();
            Pawn.health.hediffSet.GetHediffs(ref rjwPregnancies);

            bool hasChild = Pawn.relations.Children.
                Where(child => !rjwPregnancies.Any(preg => preg.babies.Contains(child))). // no fetuses
                Where(child => child.GetMother() == Pawn).  // not Dad
                TryMinBy(child => child.ageTracker.AgeBiologicalTicks, out Pawn youngest);

            if (hasChild) youngestAge = Math.Max(Pawn.ageTracker.AgeBiologicalTicks - youngest.ageTracker.AgeBiologicalTicks, -1);

            return youngestAge;
        }

        public void Initialize()
        {
            Props = (CompProperties_Breast)props;

            if (maxBreastIncrement <= 0f)
            {
                maxBreastIncrement = Utility.RandGaussianLike(0.088f, 0.202f);
            }
            if (ageOfLastBirth == 0 || ageOfLastBirth < -1)
            {
                ageOfLastBirth = CalculateLastBirth();
            }
            if (baseAlpha <= 0f)
            {
                baseAlpha = Utility.RandGaussianLike(0.0f, 0.3f) + Rand.Range(0.0f, 0.5f);
            }
            if (baseAreola <= 0f)
            {
                baseAreola = Utility.RandGaussianLike(0.0f, 1.0f);
            }
            if (baseNipple <= 0f)
            {
                baseNipple = Utility.RandGaussianLike(0.0f, 1.0f);
            }
            UpdateNipples();
            loaded = true;
        }

        protected void CalculateBreastSize()
        {
            // Ageless pawns can't depend on the chrono age, so just disable their growth entirely
            if (Pawn.ageTracker.BiologicalTicksPerTick <= 0f && breastSizeIncreased > 0)
            {
                ShrinkBreasts();
                debugGrowthStatus = "Base size (ageless)";
            }
            // The youngest child is less than halfway into babyhood: Full size
            else if (ageOfLastBirth > 0 && ageOfLastBirth + BabyHalfAge * GenDate.TicksPerYear > Pawn.ageTracker.AgeBiologicalTicks)
            {
                debugGrowthStatus = "Full size due to young child";
                if (breastSizeIncreased < MaxBreastIncrement)
                {
                    parent.Severity += (MaxBreastIncrement - breastSizeIncreased);
                    breastSizeIncreased = MaxBreastIncrement;
                }
            }
            // Pregnant, grow in the second half of first trimester
            else if (Pawn.IsRJWPregnant() || Pawn.IsBiotechPregnant())
            {
                float pregnancySize = Mathf.InverseLerp(breastGrowthStart, breastGrowthEnd, Pawn.GetFarthestPregnancyProgress()) * MaxBreastIncrement;
                if (breastSizeIncreased > pregnancySize)
                {
                    debugGrowthStatus = "Shrinking due to being oversize for pregnancy";
                    // Breasts still large from the last kid
                    ShrinkBreasts();
                }
                else if (breastSizeIncreased < MaxBreastIncrement)
                {
                    // Time to grow
                    float growAmount = pregnancySize - breastSizeIncreased;
                    if (growAmount != 0)
                        debugGrowthStatus = "Growing due to pregnancy";
                    else
                        debugGrowthStatus = "Pregnant, but not time to grow";
                    breastSizeIncreased += growAmount;
                    parent.Severity += growAmount;
                }
                else debugGrowthStatus = "Pregnant and full size";
            }
            // Not (or very early) pregnant and youngest child nonexistent or more than halfway into babyhood, time to shrink
            else if (breastSizeIncreased > 0)
            {
                debugGrowthStatus = "Shrinking due to no pregnancy nor young child";
                ShrinkBreasts();
            }
            else debugGrowthStatus = "Base size";
        }

        protected void CalculateNipples()
        {
            float newNippleProgress;
            if (Pawn.ageTracker.BiologicalTicksPerTick <= 0f)
                newNippleProgress = 0f;
            else if (ageOfLastBirth > 0 && ageOfLastBirth + BabyHalfAge * GenDate.TicksPerYear > Pawn.ageTracker.AgeBiologicalTicks)
                newNippleProgress = 1f;
            else if (Pawn.IsRJWPregnant() || Pawn.IsBiotechPregnant())
                newNippleProgress = nippleTransitions.Evaluate(Pawn.GetFarthestPregnancyProgress());
            else
                newNippleProgress = 0f;

            if (newNippleProgress < 0) newNippleProgress = 0;

            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 GaveBirth()
        {
            ageOfLastBirth = Pawn.ageTracker.AgeBiologicalTicks;
        }

        public void AdjustNippleProgress(float amount)
        {
            nippleProgress = Mathf.Clamp01(nippleProgress + amount);
            UpdateNipples();
        }

        public void AdjustNippleSizeImmediately(float amount)
        {
            baseNipple = Mathf.Clamp01(baseNipple + amount);
            UpdateNipples();
        }

        public void AdjustNippleColorImmediately(float amount)
        {
            baseAlpha = Mathf.Clamp01(baseAlpha + amount);
            UpdateNipples();
        }

        public void AdjustAreolaSizeImmediately(float amount)
        {
            baseAreola = Mathf.Clamp01(baseAreola + amount);
            UpdateNipples();
        }

        public void UpdateNipples()
        {
            cachedAlpha = baseAlpha + nippleProgress * nippleChange;
            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(Utility.SafeSkinColor(Pawn), (Props?.BlackNippleColor ?? CompProperties_Breast.DefaultBlacknippleColor.ToColor), Alpha);
        }

        public void CopyBreastProperties(HediffComp_Breast original)
        {
            maxBreastIncrement = original.maxBreastIncrement;
            baseAlpha = original.baseAlpha;
            baseAreola = original.baseAreola;
            baseNipple = original.baseNipple;
            UpdateNipples();
        }

        public string DebugInfo()
        {
            return "Size: " + parent.Severity +
                "\nIncrease: " + breastSizeIncreased +
                "\n" + debugGrowthStatus +
                "\nNipple progress: " + nippleProgress +
                "\nBase alpha: " + baseAlpha +
                "\nAlpha: " + cachedAlpha +
                "\nBase areola: " + baseAreola +
                "\nAreola: " + cachedAreola +
                "\nDisplayed areola: " + AreolaSize +
                "\nBase nipple: " + baseNipple +
                "\nNipple: " + cachedNipple +
                "\nDisplayed nipple: " + NippleSize;
        }
    }
}


code:
using RimWorld;
using UnityEngine;
using Verse;

namespace RJW_Menstruation
{
    public class CompProperties_InducedOvulator : CompProperties_Menstruation
    {
        public CompProperties_InducedOvulator()
        {
            compClass = typeof(HediffComp_InducedOvulator);
        }

    }

    public class HediffComp_InducedOvulator : HediffComp_Menstruation
    {
        protected bool hadOvulatoryStage = false;   // Regardless of whether an egg was actually produced

        protected override void InitializeExtraValues()
        {
            base.InitializeExtraValues();
            hadOvulatoryStage |= IsEggExist;
        }

        public override string GetCurStageDesc
        {
            get
            {
                switch (CurrentVisibleStage)
                {
                    case Stage.Follicular:
                        return Translations.Stage_Follicular_Induced_Desc + (EggHealth < 1f ? " " + Translations.Stage_Climacteric_Desc : "");
                    default:
                        return base.GetCurStageDesc;
                }
            }
        }

        // In an induced ovulator, about 65% of eggs ovulated will become pregnancies, so the expected lifetime supply
        // is the number of pregnancies they could have, with some extra to be sure.
        // An IUD or a poor fertility mate could run this down quicker. Oh well.
        protected override float RaceCyclesPerYear()
        {
            // Don't bother with breeding season, since so much time is planned to be spent pregnant.
            float pregnanciesPerYear = GenDate.DaysPerYear / Mathf.Max(1, Pawn.RaceProps.gestationPeriodDays);

            return 2 * pregnanciesPerYear / Configurations.ImplantationChanceDefault;
        }

        // There's really no good way to estimate the number of times it's been induced, so this is all we can do
        protected override int PawnEggsUsed(float pawnCyclesElapsed, float avglittersize)
        {
            return Mathf.CeilToInt((Pawn.relations?.ChildrenCount ?? 0) / Configurations.ImplantationChanceDefault);
        }

        public override void CompExposeData()
        {
            base.CompExposeData();
            Scribe_Values.Look(ref hadOvulatoryStage, "hadOvulatoryStage", false);
        }

        protected override void GoOvulatoryStage()
        {
            estrusflag = false;
            hadOvulatoryStage = false;
            GoNextStage(Stage.Luteal);
        }

        protected override void OvulatoryAction()
        {
            base.OvulatoryAction();
            hadOvulatoryStage = true;
        }

        protected override void LutealAction()
        {
            base.LutealAction();
            if (curStage != Stage.Luteal) hadOvulatoryStage = false;
        }

        protected override void AfterCumIn(Pawn cummer)
        {
            base.AfterCumIn(cummer);
            if (curStage == Stage.Follicular) GoNextStage(Stage.Ovulatory);
        }

        public override bool IsDangerDay
        {
            get
            {
                if (Pawn.HasIUD()) return false;

                switch (curStage)
                {
                    case Stage.Follicular:
                    case Stage.Ovulatory:
                        return true;
                    case Stage.Luteal:
                        return hadOvulatoryStage && curStageTicks < EggLifespanTicks;
                    default:
                        return false;
                }
            }
        }

        protected override bool ShouldBeInEstrus()
        {
            if (curStage == Stage.Luteal && !hadOvulatoryStage) return false;
            else return base.ShouldBeInEstrus();
        }

        protected override float RandomOvulationChance => 0;
    }
}