diff --git a/1.4/Assemblies/RJW_Menstruation.dll b/1.4/Assemblies/RJW_Menstruation.dll index 66a2a57..de4e12b 100644 Binary files a/1.4/Assemblies/RJW_Menstruation.dll and b/1.4/Assemblies/RJW_Menstruation.dll differ diff --git a/1.4/Defs/GeneDefs/GeneDefs_Menstruation.xml b/1.4/Defs/GeneDefs/GeneDefs_Menstruation.xml index d430d3f..9f20c9c 100644 --- a/1.4/Defs/GeneDefs/GeneDefs_Menstruation.xml +++ b/1.4/Defs/GeneDefs/GeneDefs_Menstruation.xml @@ -99,7 +99,7 @@ Menstruation Carriers of this gene will not bleed at the end of their cycle. UI/Genes/Placeholder - 1 + -1 40 diff --git a/1.4/Defs/ThingDefs/Apparel_Absorbers.xml b/1.4/Defs/ThingDefs/Apparel_Absorbers.xml index c27e718..87fa765 100644 --- a/1.4/Defs/ThingDefs/Apparel_Absorbers.xml +++ b/1.4/Defs/ThingDefs/Apparel_Absorbers.xml @@ -59,9 +59,16 @@
  • Absorber
  • + +
  • Worker
  • +
  • Soldier
  • +
  • Nudist
  • +
  • Slave
  • +
  • + 0.2 false true 10 @@ -116,6 +123,7 @@
  • + 0.2 false true 8 @@ -190,9 +198,15 @@
  • Absorber
  • + +
  • Worker
  • +
  • Soldier
  • +
  • Slave
  • +
  • + 0.04 true false Absorber_Pad_Dirty @@ -254,6 +268,7 @@
  • + 0.04 true false Absorber_Pad_Dirty diff --git a/1.4/Languages/ChineseTraditional/DefInjected/HediffDef/Hediffs_Menstruation.xml b/1.4/Languages/ChineseTraditional/DefInjected/HediffDef/Hediffs_Menstruation.xml index df23159..fef8db8 100644 --- a/1.4/Languages/ChineseTraditional/DefInjected/HediffDef/Hediffs_Menstruation.xml +++ b/1.4/Languages/ChineseTraditional/DefInjected/HediffDef/Hediffs_Menstruation.xml @@ -13,4 +13,10 @@ 緩解源自於痛經及此類神經痛的苦楚。 環孢素 由於免疫抑製劑的作用,身體抵抗感染和疾病的能力受扼制。 + + 受費洛蒙影響 + 受附近某人處於發情期之故,此人的性需求及娛樂需求正急遽上升。 + + + diff --git a/1.4/Languages/ChineseTraditional/DefInjected/ThoughtDefs/Thoughts_sex.xml b/1.4/Languages/ChineseTraditional/DefInjected/ThoughtDefs/Thoughts_sex.xml index b1eeb56..86e27de 100644 --- a/1.4/Languages/ChineseTraditional/DefInjected/ThoughtDefs/Thoughts_sex.xml +++ b/1.4/Languages/ChineseTraditional/DefInjected/ThoughtDefs/Thoughts_sex.xml @@ -11,4 +11,13 @@ 卵母細胞再生術 我可以繼續繁衍一小段時間了! + + 內射了{0} + 讓她懷孕正是我的職責所在。 + {0}內射了我 + 我希望這次能懷上,讓我盡到責任。 + {0}內射了我 + 我知道我有生育的義務,但是難道非他不可嗎? + 服用避孕藥 + 我的信仰要求我能夠懷孕。 diff --git a/1.4/Languages/ChineseTraditional/Keyed/RJW_Menstruation.xml b/1.4/Languages/ChineseTraditional/Keyed/RJW_Menstruation.xml index 9e5a55e..d190877 100644 --- a/1.4/Languages/ChineseTraditional/Keyed/RJW_Menstruation.xml +++ b/1.4/Languages/ChineseTraditional/Keyed/RJW_Menstruation.xml @@ -154,4 +154,18 @@ (測試中!) 允許在「生機」(Biotech)追加的懷孕機制中出現多胞胎。 啟用這個選項將允許同卵或異卵雙胞胎出現在「生機」(Biotech)追加的懷孕機制中。 一併將啟用雜交系統;然而兩個類人(humanlikes)生物絕不可能生出動物。 + + 玩家小人刷新間隔 + 殖民者、囚犯和奴隸的子宮狀態應該每隔多久刷新一次? +較小的刷新間隔可以更精確地模擬真實子宮;較大的刷新間隔可以提升遊戲流暢度 + 非玩家小人刷新間隔 + 非玩家勢力控制下的小人的子宮應該每隔多久刷新一次? +較小的刷新間隔可以更精確地模擬真實子宮;較大的刷新間隔可以提升遊戲流暢度 + 動物子宮刷新間隔 + 動物們的子宮應該多久刷新一次? +較小的刷新間隔可以更精確地模擬真實子宮;較大的刷新間隔可以提升遊戲流暢度 + 啟用費洛蒙機制 + 擁有發情期的類人生物在發情期將刺激附近的雄性類人生物,使其性慾大增。 + 動物費洛蒙效果 + 選定擁有發情期的動物在發情期內對類人生物的影響。 diff --git a/1.4/Languages/English/Keyed/RJW_Menstruation.xml b/1.4/Languages/English/Keyed/RJW_Menstruation.xml index 0c796b0..7aa650e 100644 --- a/1.4/Languages/English/Keyed/RJW_Menstruation.xml +++ b/1.4/Languages/English/Keyed/RJW_Menstruation.xml @@ -37,7 +37,7 @@ Unknown - + Absorbed Enable womb icon diff --git a/1.4/MilkModule/Assemblies/MilkModule.dll b/1.4/MilkModule/Assemblies/MilkModule.dll deleted file mode 100644 index 95909f5..0000000 Binary files a/1.4/MilkModule/Assemblies/MilkModule.dll and /dev/null differ diff --git a/1.4/MilkModule/Defs/JobDefs/Jobs_MilkSelf_MC.xml b/1.4/MilkModule/Defs/JobDefs/Jobs_MilkSelf_MC.xml deleted file mode 100644 index 69cdc4d..0000000 --- a/1.4/MilkModule/Defs/JobDefs/Jobs_MilkSelf_MC.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - LactateSelf_MC - MilkModule.JobDriver_MilkSelf_MC - lactating self - true - - diff --git a/1.4/source/RJW_Menstruation/RJW_Menstruation.sln b/1.4/source/RJW_Menstruation/RJW_Menstruation.sln index 701c76e..37d1010 100644 --- a/1.4/source/RJW_Menstruation/RJW_Menstruation.sln +++ b/1.4/source/RJW_Menstruation/RJW_Menstruation.sln @@ -5,8 +5,6 @@ VisualStudioVersion = 17.3.32929.385 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RJW_Menstruation", "RJW_Menstruation\RJW_Menstruation.csproj", "{EED2F3B9-8C20-4194-919E-8D151B29F70B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MilkModule", "MilkModule\MilkModule.csproj", "{3591B3C1-EB57-44BF-AB69-A613E097A7F8}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -17,10 +15,6 @@ Global {EED2F3B9-8C20-4194-919E-8D151B29F70B}.Debug|Any CPU.Build.0 = Debug|Any CPU {EED2F3B9-8C20-4194-919E-8D151B29F70B}.Release|Any CPU.ActiveCfg = Release|Any CPU {EED2F3B9-8C20-4194-919E-8D151B29F70B}.Release|Any CPU.Build.0 = Release|Any CPU - {3591B3C1-EB57-44BF-AB69-A613E097A7F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3591B3C1-EB57-44BF-AB69-A613E097A7F8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3591B3C1-EB57-44BF-AB69-A613E097A7F8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3591B3C1-EB57-44BF-AB69-A613E097A7F8}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_Breast.cs b/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_Breast.cs index cbee5b3..eacab7d 100644 --- a/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_Breast.cs +++ b/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_Breast.cs @@ -150,7 +150,7 @@ namespace RJW_Menstruation public bool ShouldSimulate() { - if (!Configurations.EnableAnimalCycle && Pawn.IsAnimal()) return false; + if (!Pawn.ShouldCycle()) return false; if (Pawn.SpawnedOrAnyParentSpawned || Pawn.IsCaravanMember() || PawnUtility.IsTravelingInTransportPodWorldObject(Pawn)) return true; return false; diff --git a/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_InducedOvulator.cs b/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_InducedOvulator.cs index a32969c..08dcf50 100644 --- a/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_InducedOvulator.cs +++ b/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_InducedOvulator.cs @@ -30,7 +30,7 @@ namespace RJW_Menstruation switch (CurrentVisibleStage) { case Stage.Follicular: - return Translations.Stage_Follicular_Induced_Desc + (EggHealth < 1f ? Translations.Stage_Climacteric_Desc : ""); + return Translations.Stage_Follicular_Induced_Desc + (EggHealth < 1f ? " " + Translations.Stage_Climacteric_Desc : ""); default: return base.GetCurStageDesc; } @@ -106,19 +106,8 @@ namespace RJW_Menstruation protected override bool ShouldBeInEstrus() { - if (!loaded) - Initialize(); - switch (curStage) - { - case Stage.Follicular: - return curStageTicks > currentIntervalTicks - Props.estrusDaysBeforeOvulation * GenDate.TicksPerDay; - case Stage.Ovulatory: - return true; - case Stage.Luteal: - return hadOvulatoryStage && curStageTicks < EggLifespanTicks; - default: - return false; - } + if (curStage == Stage.Luteal && !hadOvulatoryStage) return false; + else return base.ShouldBeInEstrus(); } } } diff --git a/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_Menstruation.cs b/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_Menstruation.cs index 7019a44..f4fd872 100644 --- a/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_Menstruation.cs +++ b/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_Menstruation.cs @@ -290,7 +290,7 @@ namespace RJW_Menstruation { get { - if (QuirkUtility.HasQuirk(Pawn, QuirkUtility.Quirks.Breeder)) return 0.5f; + if (Pawn.IsBreeder()) return 0.5f; return 1.0f; } @@ -300,7 +300,7 @@ namespace RJW_Menstruation private bool calculatingOvulationChance = false; public bool CalculatingOvulationChance { get => calculatingOvulationChance; } - protected float CalcuatedOvulationChance() + protected float CalculatedOvulationChance() { float ovulationChance = 1.0f; if (EggHealth <= 0.0f) return 0.0f; @@ -308,20 +308,14 @@ namespace RJW_Menstruation if (ModsConfig.BiotechActive && xxx.is_human(Pawn)) { if (Pawn.SterileGenes()) return 0.0f; - // Replicate how rjw.PawnCapacityWorker_Fertility.CalculateCapacityLevel does it, but without the age factor if (!Pawn.RaceHasFertility()) return 0.0f; if (AndroidsCompatibility.IsAndroid(Pawn) && parent.def != Genital_Helper.archotech_vagina) return 0.0f; - foreach (var part in StatDefOf.Fertility.parts) - { - if(part is StatPart_FertilityByGenderAge fertilityByAge) - { - float factor = 1.0f; - fertilityByAge.TransformValue(StatRequest.For(Pawn), ref factor); - if (factor <= 0.0f) return 0.0f; // Too young or too old - } - else part.TransformValue(StatRequest.For(Pawn), ref ovulationChance); - } - if (Pawn.HasQuirk(QuirkUtility.Quirks.Breeder)) ovulationChance *= 10.0f; + + float ageFactor = 1.0f; + StatDefOf.Fertility.GetStatPart()?.TransformValue(StatRequest.For(Pawn), ref ageFactor); + if (ageFactor <= 0.0f) return 0.0f; // Too young or too old + + if (Pawn.IsBreeder()) ovulationChance *= 10.0f; try { calculatingOvulationChance = true; @@ -332,20 +326,19 @@ namespace RJW_Menstruation return ovulationChance; } - protected float CalcuatedImplantChance() + protected float CalculatedImplantChance() { - float factor = 1.0f; if (ModsConfig.BiotechActive && xxx.is_human(Pawn)) { // Implant factor will be based solely on pawn age, plus any rollover from ovulation chance - StatPart_FertilityByGenderAge fertilityStatPart = StatDefOf.Fertility.GetStatPart(); - fertilityStatPart?.TransformValue(StatRequest.For(Pawn), ref factor); + float factor = 1.0f; + StatDefOf.Fertility.GetStatPart()?.TransformValue(StatRequest.For(Pawn), ref factor); if (OvulationChance > 1.0f) factor *= OvulationChance; return Props.baseImplantationChanceFactor * FertilityModifier * factor; } else { - return Pawn.health.capacities.GetLevel(xxx.reproduction) * Props.baseImplantationChanceFactor * FertilityModifier * factor; + return Pawn.health.capacities.GetLevel(xxx.reproduction) * Props.baseImplantationChanceFactor * FertilityModifier; } } @@ -353,16 +346,17 @@ namespace RJW_Menstruation { get { - if (ovulationChanceCache < 0.0f) ovulationChanceCache = CalcuatedOvulationChance(); + if (ovulationChanceCache < 0.0f) ovulationChanceCache = CalculatedOvulationChance(); return ovulationChanceCache; } } + // Before configuration setting public float ImplantChance { get { - if (implantationChanceCache < 0.0f) implantationChanceCache = CalcuatedImplantChance(); + if (implantationChanceCache < 0.0f) implantationChanceCache = CalculatedImplantChance(); return implantationChanceCache; } } @@ -373,7 +367,7 @@ namespace RJW_Menstruation { if (cums.NullOrEmpty()) yield return Translations.Info_noCum; else foreach (Cum cum in cums) - yield return string.Format("{0}: {1:0.##}ml", cum.notcum ? cum.notcumLabel : cum.pawn?.Label, cum.Volume); + yield return string.Format("{0}: {1:0.##}ml", cum.notcum ? cum.notcumLabel : cum.pawn?.Label, cum.Volume); } } public Color GetCumMixtureColor @@ -698,22 +692,25 @@ namespace RJW_Menstruation if (Pawn.genes == null || !ModsConfig.BiotechActive) return; - if (Pawn.genes.HasGene(VariousDefOf.ShortEggLifetime)) eggLifeSpanTicks = eggLifeSpanTicks * 3 / 4; - else if (Pawn.genes.HasGene(VariousDefOf.DoubleEggLifetime)) eggLifeSpanTicks *= 2; - else if (Pawn.genes.HasGene(VariousDefOf.QuadEggLifetime)) eggLifeSpanTicks *= 4; + foreach (GeneDef geneDef in Pawn.genes.GenesListForReading.Select(gene => gene.def)) + { + if (geneDef == VariousDefOf.ShortEggLifetime) eggLifeSpanTicks = eggLifeSpanTicks * 3 / 4; + else if (geneDef == VariousDefOf.DoubleEggLifetime) eggLifeSpanTicks *= 2; + else if (geneDef == VariousDefOf.QuadEggLifetime) eggLifeSpanTicks *= 4; - if (Pawn.genes.HasGene(VariousDefOf.NeverEstrus)) estrusLevel = EstrusLevel.None; - else if (Pawn.genes.HasGene(VariousDefOf.FullEstrus)) estrusLevel = EstrusLevel.Visible; + else if (geneDef == VariousDefOf.NeverEstrus) estrusLevel = EstrusLevel.None; + else if (geneDef == VariousDefOf.FullEstrus) estrusLevel = EstrusLevel.Visible; - if (Pawn.genes.HasGene(VariousDefOf.DoubleOvulation)) ovulationFactor = 2f; - else if (Pawn.genes.HasGene(VariousDefOf.QuadOvulation)) ovulationFactor = 4f; + else if (geneDef == VariousDefOf.DoubleOvulation) ovulationFactor = 2f; + else if (geneDef == VariousDefOf.QuadOvulation) ovulationFactor = 4f; - noBleeding = Pawn.genes.HasGene(VariousDefOf.NoBleeding); + else if (geneDef == VariousDefOf.NoBleeding) noBleeding = true; + } } public bool ShouldSimulate() { - if (!Configurations.EnableAnimalCycle && Pawn.IsAnimal()) return false; + if (!Pawn.ShouldCycle()) return false; if (Pawn.SpawnedOrAnyParentSpawned || Pawn.IsCaravanMember() || PawnUtility.IsTravelingInTransportPodWorldObject(Pawn)) return true; return false; @@ -735,6 +732,9 @@ namespace RJW_Menstruation // If an exception makes it out, RW will remove the hediff, so catch it here try { + if (Pawn.IsHashIntervalTick(recalculateTickInterval)) TickInterval = -1; // Every so often, force TickInterval to be recalculated in case the pawn's status changed. + if (!Pawn.IsHashIntervalTick(TickInterval)) return; + if (!ShouldSimulate()) return; // Initialize immediately if needed, but if there's an error, then don't spam it every tick @@ -743,10 +743,7 @@ namespace RJW_Menstruation Log.Warning($"{Pawn}'s womb is ticking, but was not initialized first"); Initialize(); } - - if (Pawn.IsHashIntervalTick(recalculateTickInterval)) TickInterval = -1; // Every so often, force TickInterval to be recalculated in case the pawn's status changed. - if (!Pawn.IsHashIntervalTick(TickInterval)) return; - + if (initError) Log.Warning($"Attempting to process {Pawn}'s womb uninitialized"); if (Pregnancy != null && curStage != Stage.Pregnant) @@ -889,8 +886,8 @@ namespace RJW_Menstruation if (!precum && fertility > 0 && IsDangerDay && cummer.relations.GetPregnancyApproachForPartner(Pawn) == PregnancyApproach.AvoidPregnancy) { float successChance = pulloutSuccessRate; - if (cummer.HasQuirk(QuirkUtility.Quirks.ImpregnationFetish)) successChance *= fetishPulloutSuccessModifier; - if (Pawn.HasQuirk(QuirkUtility.Quirks.ImpregnationFetish)) successChance *= fetishPulloutSuccessModifier; + if (cummer.HasImpregnationFetish()) successChance *= fetishPulloutSuccessModifier; + if (Pawn.HasImpregnationFetish()) successChance *= fetishPulloutSuccessModifier; if (Rand.Chance(successChance)) return; } if (Pawn.HasIUD()) fertility /= 100f; @@ -1414,97 +1411,116 @@ namespace RJW_Menstruation } else if (Rand.Chance(Configurations.ImplantationChance * ImplantChance * InterspeciesImplantFactor(egg.fertilizer))) { - if (Configurations.Debug) Log.Message($"Implanting fertilized egg of {Pawn} into {parent}, father {egg.fertilizer}"); - if (pregnancy != null) + try { - if (Configurations.PregnancySource == Configurations.PregnancyType.Biotech && Configurations.EnableBiotechTwins && Configurations.EnableHeteroOvularTwins) + if (Configurations.Debug) Log.Message($"Implanting fertilized egg of {Pawn} into {parent}, father {egg.fertilizer}"); + if (pregnancy == null) { - if (Configurations.Debug) Log.Message($"Adding to existing Biotech pregnancy {pregnancy}"); - HediffComp_PregeneratedBabies comp = pregnancy.TryGetComp(); - if (comp == null) Log.Warning($"Trying to add Biotech egg to {Pawn}'s pregnancy without a pregenerated baby comp: {pregnancy}"); - else + Configurations.PregnancyType usePregnancy = xxx.is_human(Pawn) ? Configurations.PregnancySource : Configurations.PregnancyType.MultiplePregnancy; + switch (usePregnancy) { - comp.AddNewBaby(Pawn, egg.fertilizer); - pregnant = true; - deadeggs.Add(egg); + case Configurations.PregnancyType.BaseRJW: + + if (Configurations.Debug) Log.Message($"Creating new base RJW pregnancy"); + PregnancyHelper.AddPregnancyHediff(Pawn, egg.fertilizer); + // I hate having to do this, but it gets the newest pregnancy + List pregnancies = new List(); + Pawn.health.hediffSet.GetHediffs(ref pregnancies); + pregnancy = pregnancies.MaxBy(hediff => hediff.loadID); + pregnant = true; + break; + + case Configurations.PregnancyType.MultiplePregnancy: + if (Configurations.Debug) Log.Message($"Creating new menstruation pregnancy"); + pregnancy = Hediff_BasePregnancy.Create(Pawn, egg.fertilizer); + pregnant = true; + deadeggs.Add(egg); + break; + + case Configurations.PregnancyType.Biotech: + if (Configurations.Debug) Log.Message($"Creating new biotech pregnancy"); + pregnancy = HediffMaker.MakeHediff(HediffDefOf.PregnantHuman, Pawn); + if (Configurations.EnableBiotechTwins) + pregnancy.TryGetComp().AddNewBaby(Pawn, egg.fertilizer); + ((Hediff_Pregnant)pregnancy).SetParents(Pawn, egg.fertilizer, PregnancyUtility.GetInheritedGeneSet(egg.fertilizer, Pawn)); + Pawn.health.AddHediff(pregnancy); + pregnant = true; + deadeggs.Add(egg); + break; } - } - else if (Configurations.PregnancySource == Configurations.PregnancyType.MultiplePregnancy && Configurations.EnableHeteroOvularTwins) - { - if (pregnancy is Hediff_MultiplePregnancy h) + if (pregnancy is Hediff_BasePregnancy rjw_preg) { - if (Configurations.Debug) Log.Message($"Adding to existing pregnancy {h}"); - h.AddNewBaby(Pawn, egg.fertilizer); + // TODO: advance biotech pregnancy + rjw_preg.p_start_tick -= egg.ticksSinceFertilization / Configurations.CycleAcceleration; + rjw_preg.p_end_tick -= egg.ticksSinceFertilization / Configurations.CycleAcceleration; } - pregnant = true; - deadeggs.Add(egg); } else { - pregnant = true; - break; + switch (pregnancy) + { + case Hediff_Pregnant vanillaPreg: // Not going to do the labor ones + if (!Configurations.EnableBiotechTwins || !Configurations.EnableHeteroOvularTwins) goto default; + if (Configurations.Debug) Log.Message($"Adding to existing Biotech pregnancy {vanillaPreg.GetUniqueLoadID()}"); + HediffComp_PregeneratedBabies comp = vanillaPreg.TryGetComp(); + if (comp == null) Log.Warning($"Trying to add Biotech egg to {Pawn}'s pregnancy without a pregenerated baby comp: {vanillaPreg.GetUniqueLoadID()}"); + else + comp.AddNewBaby(Pawn, egg.fertilizer); + pregnant = true; + deadeggs.Add(egg); + break; + case Hediff_MultiplePregnancy multiPreg: + if (!Configurations.EnableHeteroOvularTwins) goto default; + if (Configurations.Debug) Log.Message($"Adding to existing pregnancy {multiPreg.GetUniqueLoadID()}"); + multiPreg.AddNewBaby(Pawn, egg.fertilizer); + pregnant = true; + deadeggs.Add(egg); + break; + case Hediff_BasePregnancy _: + default: + pregnant = true; + deadeggs.Add(egg); + break; + } } } - else + catch (Exception ex) { - Configurations.PregnancyType usePregnancy = xxx.is_human(Pawn) ? Configurations.PregnancySource : Configurations.PregnancyType.MultiplePregnancy; - switch (usePregnancy) - { - case Configurations.PregnancyType.BaseRJW: - - if (Configurations.Debug) Log.Message($"Creating new base RJW pregnancy"); - PregnancyHelper.AddPregnancyHediff(Pawn, egg.fertilizer); - // I hate having to do this, but it gets the newest pregnancy - List pregnancies = new List(); - Pawn.health.hediffSet.GetHediffs(ref pregnancies); - pregnancy = pregnancies.MaxBy(hediff => hediff.loadID); - pregnant = true; - break; - - case Configurations.PregnancyType.MultiplePregnancy: - if (Configurations.Debug) Log.Message($"Creating new menstruation pregnancy"); - pregnancy = Hediff_BasePregnancy.Create(Pawn, egg.fertilizer); - pregnant = true; - deadeggs.Add(egg); - break; - - case Configurations.PregnancyType.Biotech: - if (Configurations.Debug) Log.Message($"Creating new biotech pregnancy"); - pregnancy = HediffMaker.MakeHediff(HediffDefOf.PregnantHuman, Pawn); - if(Configurations.EnableBiotechTwins) - pregnancy.TryGetComp().AddNewBaby(Pawn, egg.fertilizer); - ((Hediff_Pregnant)pregnancy).SetParents(Pawn, egg.fertilizer, PregnancyUtility.GetInheritedGeneSet(egg.fertilizer, Pawn)); - Pawn.health.AddHediff(pregnancy); - pregnant = true; - deadeggs.Add(egg); - break; - } - if (pregnancy is Hediff_BasePregnancy rjw_preg) - { - // TODO: advance biotech pregnancy - rjw_preg.p_start_tick -= egg.ticksSinceFertilization / Configurations.CycleAcceleration; - rjw_preg.p_end_tick -= egg.ticksSinceFertilization / Configurations.CycleAcceleration; - } + Log.Error($"Error creating pregnancy in {Pawn}'s womb, father {egg.fertilizer}: {ex}"); + TakeLoosePregnancy(); + deadeggs.Add(egg); } } else - { + { if (Configurations.Debug) { - float implantChance = Configurations.ImplantationChance * ImplantChance * InterspeciesImplantFactor(egg.fertilizer); - Log.Message($"Fertilized egg of {Pawn} failed to implant (chance {implantChance.ToStringPercent()}, father {egg.fertilizer})"); + float interspeciesFactor = InterspeciesImplantFactor(egg.fertilizer); + float implantChance = Configurations.ImplantationChance * ImplantChance * interspeciesFactor; + Log.Message($"Fertilized egg of {Pawn} failed to implant (chance {implantChance.ToStringPercent()}, " + + (interspeciesFactor < 1.0f ? $"interspecies factor {interspeciesFactor.ToStringPercent()}, " : "" ) + + $"father {egg.fertilizer})"); } deadeggs.Add(egg); } } - if (pregnant && - (Configurations.PregnancySource != Configurations.PregnancyType.MultiplePregnancy || !Configurations.EnableHeteroOvularTwins) && - (Configurations.PregnancySource != Configurations.PregnancyType.Biotech || !Configurations.EnableBiotechTwins || !Configurations.EnableHeteroOvularTwins)) + bool clearAllEggs = true; + switch (Configurations.PregnancySource) { - eggs.Clear(); - return true; + case Configurations.PregnancyType.BaseRJW: + clearAllEggs = true; + break; + case Configurations.PregnancyType.MultiplePregnancy: + clearAllEggs = !Configurations.EnableHeteroOvularTwins; + break; + case Configurations.PregnancyType.Biotech: + clearAllEggs = !(Configurations.EnableBiotechTwins && Configurations.EnableHeteroOvularTwins); + break; } + + if (pregnant && clearAllEggs) + eggs.Clear(); else eggs.RemoveAll(egg => deadeggs.Contains(egg)); return pregnant; @@ -1554,12 +1570,7 @@ namespace RJW_Menstruation return amount; } absorber.absorbedfluids += amount; - if (absorber.absorbedfluids > absorbable && !Pawn.apparel.IsLocked(absorber)) - { - absorber.def = absorber.DirtyDef; - //absorber.fluidColor = GetCumMixtureColor; - absorber.dirty = true; - } + absorber.CheckDirty(); return 0; } @@ -1601,6 +1612,11 @@ namespace RJW_Menstruation Pawn.health.AddHediff(hediff, parent.Part); } + protected void AdvanceStageTime() + { + curStageTicks += TickInterval * Configurations.CycleAcceleration; + } + protected virtual void FollicularAction() { if (!IsBreedingSeason()) @@ -1615,13 +1631,12 @@ namespace RJW_Menstruation } else { - curStageTicks += TickInterval * Configurations.CycleAcceleration; + AdvanceStageTime(); if (!estrusflag && curStageTicks > currentIntervalTicks - Props.estrusDaysBeforeOvulation * GenDate.TicksPerDay) { estrusflag = true; SetEstrus(); } - StayCurrentStage(); } } @@ -1629,7 +1644,7 @@ namespace RJW_Menstruation { if (curStageTicks < currentIntervalTicks) { - curStageTicks += TickInterval * Configurations.CycleAcceleration; + AdvanceStageTime(); return; } estrusflag = false; @@ -1644,7 +1659,7 @@ namespace RJW_Menstruation } catch (ArgumentException e) { - Log.Warning($"Invalid litterSizeCurve for {Pawn.def}: {e}"); + Log.WarningOnce($"Invalid litterSizeCurve for {Pawn.def}: {e}", 642201874 + Pawn.thingIDNumber); eggnum = 1f; } eggnum *= ovulationFactor; @@ -1689,21 +1704,19 @@ namespace RJW_Menstruation } else { - curStageTicks += TickInterval * Configurations.CycleAcceleration; - StayCurrentStage(); + AdvanceStageTime(); } } else { - curStageTicks += TickInterval * Configurations.CycleAcceleration; - StayCurrentStage(); + AdvanceStageTime(); } } protected virtual void BleedingAction() { - if (curStageTicks >= currentIntervalTicks) + if (curStageTicks >= currentIntervalTicks || noBleeding) { Hediff hediff = Pawn.health.hediffSet.GetFirstHediffOfDef(VariousDefOf.Hediff_MenstrualCramp); if (hediff != null && !Pawn.GetMenstruationComps().Any(comp => comp != this && comp.curStage == Stage.Bleeding)) Pawn.health.RemoveHediff(hediff); @@ -1712,15 +1725,13 @@ namespace RJW_Menstruation GoOvulatoryStage(); else { - currentIntervalTicks = totalFollicularTicks - currentIntervalTicks; // I.e., the remaining follicular time equals the total minus the bleeding time elapsed - GoNextStage(Stage.Follicular, false); + GoNextStage(Stage.Follicular, totalFollicularTicks - currentIntervalTicks); // I.e., the remaining follicular time equals the total minus the bleeding time elapsed } } else { if (curStageTicks < currentIntervalTicks / 4) BleedOut(); - curStageTicks += TickInterval * Configurations.CycleAcceleration; - StayCurrentStage(); + AdvanceStageTime(); } } @@ -1733,16 +1744,10 @@ namespace RJW_Menstruation Implant(); } - if (pregnancy != null && Pawn.health.hediffSet.hediffs.Contains(pregnancy)) - { + if (Pregnancy != null) curStageTicks += TickInterval; - StayCurrentStageConst(Stage.Pregnant); - } else - { - if (pregnancy != null) pregnancy = null; GoNextStage(Stage.Recover); - } } protected virtual void RecoverAction() @@ -1764,22 +1769,18 @@ namespace RJW_Menstruation } else { - curStageTicks += TickInterval * Configurations.CycleAcceleration; - StayCurrentStage(); + AdvanceStageTime(); } } protected virtual void InfertileAction() { if (ShouldBeInfertile()) - { - StayCurrentStageConst(Stage.Infertile); - } + return; + else if (IsBreedingSeason()) + GoNextStage(Stage.Follicular); else - { - bool breedingSeason = IsBreedingSeason(); - GoNextStage(breedingSeason ? Stage.Follicular : Stage.Anestrus, breedingSeason); - } + GoNextStage(Stage.Anestrus); } protected virtual void AnestrusAction() @@ -1788,10 +1789,6 @@ namespace RJW_Menstruation { GoFollicularOrBleeding(); } - else - { - StayCurrentStage(); - } } protected virtual void ThoughtCumInside(Pawn cummer) @@ -1801,7 +1798,7 @@ namespace RJW_Menstruation MemoryThoughtHandler cummerMemories = cummer.needs.mood.thoughts.memories; MemoryThoughtHandler pawnMemories = Pawn.needs.mood.thoughts.memories; - if (cummer.IsProPregnancy(out Precept preceptm) || (cummer.HasQuirk(QuirkUtility.Quirks.Teratophile) != (Pawn.GetStatValue(StatDefOf.PawnBeauty) >= 0))) + if (cummer.IsProPregnancy(out Precept preceptm) || (cummer.IsTeratophile() != (Pawn.GetStatValue(StatDefOf.PawnBeauty) >= 0))) { if (cummer.relations.OpinionOf(Pawn) <= -5) cummerMemories.TryGainMemory(VariousDefOf.HaterCameInsideM, Pawn); @@ -1853,10 +1850,10 @@ namespace RJW_Menstruation TaleRecorder.RecordTale(VariousDefOf.TaleCameInside, new object[] { cummer, Pawn }); } - public void GoNextStage(Stage nextstage, bool calculateHours = true) + public void GoNextStage(Stage nextstage, int? stageTicks = null) { curStageTicks = 0; - if (calculateHours) currentIntervalTicks = PeriodRandomizer(nextstage); + currentIntervalTicks = stageTicks ?? PeriodRandomizer(nextstage); curStage = nextstage; } @@ -1865,16 +1862,6 @@ namespace RJW_Menstruation GoNextStage(Stage.Ovulatory); } - //stage can be interrupted in other reasons - protected void StayCurrentStage() - { - } - - //stage never changes - protected void StayCurrentStageConst(Stage curstage) - { - } - protected void GoFollicularOrBleeding() { if (Props.bleedingIntervalDays == 0 || noBleeding) diff --git a/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_PeriodicOvulator.cs b/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_PeriodicOvulator.cs index ab5b2e2..eb344c3 100644 --- a/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_PeriodicOvulator.cs +++ b/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_PeriodicOvulator.cs @@ -73,7 +73,7 @@ namespace RJW_Menstruation protected override int TicksToNextStage() { - if (curStage == Stage.Anestrus && ticksToNextCycle > 0) return ticksToNextCycle / Configurations.CycleAcceleration; + if (curStage == Stage.Anestrus && ticksToNextCycle > 0) return ticksToNextCycle / Configurations.CycleAcceleration; else return base.TicksToNextStage(); } @@ -104,13 +104,11 @@ namespace RJW_Menstruation protected override void AnestrusAction() { - if (ticksToNextCycle <= 0) + if (ticksToNextCycle <= 0 && IsBreedingSeason()) { ticksToNextCycle = (int)(averageCycleIntervalTicks * (1 + Rand.Range(-cycleVariability, cycleVariability))); - if (IsBreedingSeason()) GoNextStage(Stage.Follicular); - return; + GoNextStage(Stage.Follicular); } - StayCurrentStage(); } public override void CopyCycleProperties(HediffComp_Menstruation original) diff --git a/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_Pheromones.cs b/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_Pheromones.cs index 8462b19..42b2aac 100644 --- a/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_Pheromones.cs +++ b/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_Pheromones.cs @@ -40,7 +40,7 @@ namespace RJW_Menstruation } catch (Exception ex) { - Log.Error($"Error when trying to emit pheromones: {ex}"); + Log.Error($"Error when trying to emit pheromones from pawn {Pawn}: {ex}"); } } diff --git a/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_PregeneratedBabies.cs b/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_PregeneratedBabies.cs index ce0efe9..79c13a9 100644 --- a/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_PregeneratedBabies.cs +++ b/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_PregeneratedBabies.cs @@ -179,7 +179,7 @@ namespace RJW_Menstruation public static IEnumerable Transpiler(IEnumerable instructions) { if (birtherThing < 0) throw new InvalidOperationException("Could not locate index of birtherThing"); - if (GeneratePawn == null || GeneratePawn.ReturnType != typeof(Pawn)) throw new InvalidOperationException("GeneratePawn not found"); + if (GeneratePawn?.ReturnType != typeof(Pawn)) throw new InvalidOperationException("GeneratePawn not found"); foreach (CodeInstruction instruction in instructions) { if (instruction.Calls(GeneratePawn)) @@ -233,7 +233,7 @@ namespace RJW_Menstruation new Type[] {typeof(OutcomeChance), typeof(float), typeof(Precept_Ritual), typeof(List), typeof(Pawn), typeof(Thing), typeof(Pawn), typeof(Pawn), typeof(LordJob_Ritual), typeof(RitualRoleAssignments)}); public static IEnumerable Transpiler(IEnumerable instructions) { - if (ApplyBirthOutcome == null || ApplyBirthOutcome.ReturnType != typeof(Thing)) throw new InvalidOperationException("ApplyBirthOutcome not found"); + if (ApplyBirthOutcome?.ReturnType != typeof(Thing)) throw new InvalidOperationException("ApplyBirthOutcome not found"); foreach (CodeInstruction instruction in instructions) { if (instruction.Calls(ApplyBirthOutcome)) @@ -283,8 +283,8 @@ namespace RJW_Menstruation new Type[] { typeof(OutcomeChance), typeof(float), typeof(Precept_Ritual), typeof(List), typeof(Pawn), typeof(Thing), typeof(Pawn), typeof(Pawn), typeof(LordJob_Ritual), typeof(RitualRoleAssignments) }); public static IEnumerable Transpiler(IEnumerable instructions) { - if (ApplyBirthOutcome == null || ApplyBirthOutcome.ReturnType != typeof(Thing)) throw new InvalidOperationException("ApplyBirthOutcome not found"); - foreach (var instruction in instructions) + if (ApplyBirthOutcome?.ReturnType != typeof(Thing)) throw new InvalidOperationException("ApplyBirthOutcome not found"); + foreach (CodeInstruction instruction in instructions) { if (instruction.Calls(ApplyBirthOutcome)) yield return CodeInstruction.Call(typeof(Ritual_ChildBirth_Apply_Patch), nameof(Ritual_ChildBirth_Apply_Patch.ApplyBirthLoop)); diff --git a/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/MenstruationUtility.cs b/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/MenstruationUtility.cs index ffd37aa..8221acd 100644 --- a/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/MenstruationUtility.cs +++ b/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/MenstruationUtility.cs @@ -22,7 +22,7 @@ namespace RJW_Menstruation public static IEnumerable GetMenstruationComps(this Pawn pawn) { - List hedifflist = Genital_Helper.get_PartsHediffList(pawn, Genital_Helper.get_genitalsBPR(pawn))?.FindAll(h => VariousDefOf.AllVaginas.Contains(h.def)); + List hedifflist = pawn.health.hediffSet.hediffs.FindAll(h => VariousDefOf.AllVaginas.Contains(h.def)); if (hedifflist == null) yield break; foreach (Hediff hediff in hedifflist) { @@ -63,17 +63,7 @@ namespace RJW_Menstruation public static HediffComp_Anus GetAnusComp(this Pawn pawn) { - List hedifflist = Genital_Helper.get_PartsHediffList(pawn, Genital_Helper.get_genitalsBPR(pawn))?.FindAll((Hediff h) => h.def.defName.ToLower().Contains("anus")); - HediffComp_Anus result; - if (!hedifflist.NullOrEmpty()) - { - foreach (Hediff h in hedifflist) - { - result = h.TryGetComp(); - if (result != null) return result; - } - } - return null; + return pawn.health.hediffSet.hediffs.FirstOrDefault((Hediff h) => VariousDefOf.AllAnuses.Contains(h.def))?.TryGetComp(); } [Obsolete("This method is obsolete and can cause ambiguity. Use GetMenstruationCompFromVagina or GetMenstruationCompFromPregnancy instead.", true)] @@ -346,8 +336,8 @@ namespace RJW_Menstruation public static Texture2D GetAnalIcon(this Pawn pawn, bool drawOrigin = false) { - Hediff hediff = Genital_Helper.get_PartsHediffList(pawn, Genital_Helper.get_anusBPR(pawn)).FirstOrDefault(h => VariousDefOf.AllAnuses.Contains(h.def)) ?? - Genital_Helper.get_PartsHediffList(pawn, Genital_Helper.get_anusBPR(pawn)).FirstOrDefault(h => h.def.defName.ToLower().Contains("anus")); + Hediff hediff = pawn.health.hediffSet.hediffs.FirstOrDefault(h => VariousDefOf.AllAnuses.Contains(h.def)) ?? + pawn.health.hediffSet.hediffs.FirstOrDefault(h => h.def.defName.ToLower().Contains("anus")); if (hediff == null) return ContentFinder.Get(("Genitals/Anal00"), true); string icon; @@ -403,6 +393,19 @@ namespace RJW_Menstruation else return variability; } + public static bool ShouldCycle(this Pawn pawn) + { + if (!Configurations.EnableAnimalCycle && pawn.IsAnimal()) return false; + if (pawn.RaceHasOviPregnancy()) return false; + if (ModsConfig.BiotechActive && pawn.genes != null) + { + foreach (Gene gene in pawn.genes.GenesListForReading) + if (VariousDefOf.EggLayerGenes.Contains(gene.def)) return false; + } + + return true; + } + public static bool IsInEstrus(this Pawn pawn, bool visible = true) { if (pawn.Dead) return false; @@ -450,8 +453,8 @@ namespace RJW_Menstruation } if (precept != null) return true; - else return pawn.HasQuirk(QuirkUtility.Quirks.Breeder) || - pawn.HasQuirk(QuirkUtility.Quirks.ImpregnationFetish); + else return pawn.IsBreeder() || + pawn.HasImpregnationFetish(); } public static float DamagePants(this Pawn pawn, float fluidAmount) diff --git a/1.4/source/RJW_Menstruation/RJW_Menstruation/Hediff_MultiplePregnancy.cs b/1.4/source/RJW_Menstruation/RJW_Menstruation/Hediff_MultiplePregnancy.cs index 2cc6646..61ce541 100644 --- a/1.4/source/RJW_Menstruation/RJW_Menstruation/Hediff_MultiplePregnancy.cs +++ b/1.4/source/RJW_Menstruation/RJW_Menstruation/Hediff_MultiplePregnancy.cs @@ -23,7 +23,7 @@ namespace RJW_Menstruation { if (is_discovered || !xxx.is_human(pawn) || - pawn.HasQuirk(QuirkUtility.Quirks.Breeder) || (pawn.Ideo?.HasPrecept(VariousDefOf.Pregnancy_Required) ?? false) || + pawn.IsBreeder() || (pawn.Ideo?.HasPrecept(VariousDefOf.Pregnancy_Required) ?? false) || (pawn.relations?.DirectRelations?.Find(x => x.def.Equals(PawnRelationDefOf.Spouse) || x.def.Equals(PawnRelationDefOf.Fiance))) != null) return; diff --git a/1.4/source/RJW_Menstruation/RJW_Menstruation/Patch/Biotech_Patch.cs b/1.4/source/RJW_Menstruation/RJW_Menstruation/Patch/Biotech_Patch.cs index 4bd3cef..aaf24de 100644 --- a/1.4/source/RJW_Menstruation/RJW_Menstruation/Patch/Biotech_Patch.cs +++ b/1.4/source/RJW_Menstruation/RJW_Menstruation/Patch/Biotech_Patch.cs @@ -155,7 +155,7 @@ namespace RJW_Menstruation public static IEnumerable Transpiler(IEnumerable instructions) { - if (GetPregnancyHediff == null || GetPregnancyHediff.ReturnType != typeof(Hediff)) throw new InvalidOperationException("GetPregnancyHediff not found"); + if (GetPregnancyHediff?.ReturnType != typeof(Hediff)) throw new InvalidOperationException("GetPregnancyHediff not found"); if (Get_Attitude == null || Nullable.GetUnderlyingType(Get_Attitude.ReturnType) != typeof(PregnancyAttitude)) throw new InvalidOperationException("get_Attitude not found"); foreach (CodeInstruction instruction in instructions) { @@ -219,6 +219,17 @@ namespace RJW_Menstruation } } + [HarmonyPatch(typeof(StatPart_FertilityByGenderAge), "AgeFactor")] + public class AgeFactor_Patch + { + public static void Postfix(ref float __result, Pawn pawn) + { + if (__result <= 0.0f) return; + if (pawn.GetMenstruationComps().Any(comp => comp.CalculatingOvulationChance)) + __result = 1.0f; + } + } + //[HarmonyPatch(typeof(ChildcareUtility), nameof(ChildcareUtility.SuckleFromLactatingPawn))] //public class GreedyConsume_Patch //{ diff --git a/1.4/source/RJW_Menstruation/RJW_Menstruation/Patch/Pawn_Patch.cs b/1.4/source/RJW_Menstruation/RJW_Menstruation/Patch/Pawn_Patch.cs index 616f591..98e5997 100644 --- a/1.4/source/RJW_Menstruation/RJW_Menstruation/Patch/Pawn_Patch.cs +++ b/1.4/source/RJW_Menstruation/RJW_Menstruation/Patch/Pawn_Patch.cs @@ -1,5 +1,6 @@ using HarmonyLib; using RimWorld; +using rjw; using System; using System.Collections.Generic; using System.Linq; @@ -36,10 +37,6 @@ namespace RJW_Menstruation if (t.Pawn == pawn && pawn.HasMenstruationComp()) opts.AddDistinct(MakeSelfMenu(pawn, t)); break; } - - - - } public static FloatMenuOption MakeSelfMenu(Pawn pawn, LocalTargetInfo target) @@ -53,60 +50,28 @@ namespace RJW_Menstruation } } - //[HarmonyPatch(typeof(HealthCardUtility), "DrawHediffListing")] - //public class DrawHediffListing_Patch - //{ - // public const float buttonWidth = 80f; - // public const float buttonHeight = 20f; - // - // public static void Postfix(Rect rect, Pawn pawn, bool showBloodLoss) - // { - // if (Configurations.EnableButtonInHT && pawn.HasMenstruationComp()) - // { - // Rect buttonrect = new Rect(rect.xMax - buttonWidth, rect.yMax - buttonHeight, buttonWidth, buttonHeight); - // if (Widgets.ButtonText(buttonrect, "Status")) - // { - // Dialog_WombStatus.ToggleWindow(pawn,pawn.GetMenstruationComp()); - // } - // } - // - // - // } - //} - - [HarmonyPatch(typeof(HealthCardUtility), "DrawHediffRow")] - public class DrawHediffRow_Patch + [HarmonyPatch(typeof(HealthCardUtility), "DrawOverviewTab")] + public class DrawOverviewTab_Patch { public const float buttonWidth = 50f; public const float buttonHeight = 20f; - private static HediffComp_Menstruation GetFirstMenstruation(IEnumerable diffs) + public static void Prefix(Rect leftRect, Pawn pawn, float curY) { - foreach (Hediff diff in diffs) + if (Configurations.EnableButtonInHT && pawn.ShowStatus() && pawn.ShouldCycle()) { - HediffComp_Menstruation comp = diff.GetMenstruationCompFromVagina(); - if (comp != null) return comp; - } - return null; - } - - public static void Prefix(Rect rect, Pawn pawn, IEnumerable diffs, ref float curY) - { - if (Configurations.EnableButtonInHT && pawn.ShowStatus()) - { - HediffComp_Menstruation comp = GetFirstMenstruation(diffs); + HediffComp_Menstruation comp = pawn.GetFirstMenstruationComp(); if (comp != null) { - Rect buttonrect = new Rect((rect.xMax) / 2 - 5f, curY + 2f, buttonWidth, buttonHeight); - if (Widgets.ButtonText(buttonrect, Translations.Button_HealthTab)) + Text.Font = GameFont.Tiny; + Rect buttonRect = new Rect(leftRect.xMax - buttonWidth - 8f, curY + 4f, buttonWidth, buttonHeight); + if (Widgets.ButtonText(buttonRect, Translations.Button_HealthTab)) { Dialog_WombStatus.ToggleWindow(pawn, comp); } } } - } - } [HarmonyPatch(typeof(CompBiosculpterPod), nameof(CompBiosculpterPod.CannotUseNowPawnCycleReason), new Type[] { typeof(Pawn), typeof(Pawn), typeof(CompBiosculpterPod_Cycle), typeof(bool) })] @@ -120,4 +85,28 @@ namespace RJW_Menstruation __result = Translations.CannotNoWomb; } } + + // Doesn't cover everything, but at least it'll get the auto equip + [HarmonyPatch(typeof(Apparel), nameof(Apparel.PawnCanWear))] + public class PawnCanWear_Patch + { + public static void Postfix(ref bool __result, Apparel __instance, Pawn pawn) + { + if (__result && __instance is Absorber) + { + __result = pawn.ShouldCycle() && pawn.GetMenstruationComps().Any(); + } + } + } + + // Might cause issues when it comes to caravans + //[HarmonyPatch(typeof(JobGiver_OptimizeApparel), nameof(JobGiver_OptimizeApparel.ApparelScoreRaw))] + //public class ApparelScoreRaw_Patch + //{ + // public static void Postfix(ref float __result, Pawn pawn, Apparel ap) + // { + // if (__result > 0f && ap is Absorber && !pawn.GetMenstruationComps().Any(comp => comp.TotalCum > 0)) + // __result = -10f; + // } + //} } diff --git a/1.4/source/RJW_Menstruation/RJW_Menstruation/Patch/RJW_Patch.cs b/1.4/source/RJW_Menstruation/RJW_Menstruation/Patch/RJW_Patch.cs index e36e5fa..eb776e3 100644 --- a/1.4/source/RJW_Menstruation/RJW_Menstruation/Patch/RJW_Patch.cs +++ b/1.4/source/RJW_Menstruation/RJW_Menstruation/Patch/RJW_Patch.cs @@ -24,14 +24,14 @@ namespace RJW_Menstruation if (sextype != xxx.rjwSextype.Vaginal && sextype != xxx.rjwSextype.DoublePenetration) return true; - if (partner.IsAnimal() && !Configurations.EnableAnimalCycle) return true; + if (!partner.ShouldCycle()) return true; if (!InteractionCanCausePregnancy(props)) return false; List pawnparts = Genital_Helper.get_PartsHediffList(pawn, Genital_Helper.get_genitalsBPR(pawn)); HediffComp_Menstruation comp; - if (pawn.HasQuirk(QuirkUtility.Quirks.ImpregnationFetish) || partner.HasQuirk(QuirkUtility.Quirks.ImpregnationFetish) || partner.IsInEstrus()) + if (pawn.HasImpregnationFetish() || partner.HasImpregnationFetish() || partner.IsInEstrus()) comp = partner.GetFertileMenstruationComp(); else comp = partner.GetRandomMenstruationComp(); if (comp == null) return true; @@ -100,9 +100,9 @@ namespace RJW_Menstruation { public static bool Prefix(Pawn pawn, Pawn partner) // partner has vagina { - if (partner.IsAnimal() && !Configurations.EnableAnimalCycle) return true; + if (!partner.ShouldCycle()) return true; HediffComp_Menstruation comp; - if (pawn.HasQuirk(QuirkUtility.Quirks.ImpregnationFetish) || partner.HasQuirk(QuirkUtility.Quirks.ImpregnationFetish) || partner.IsInEstrus()) + if (pawn.HasImpregnationFetish() || partner.HasImpregnationFetish() || partner.IsInEstrus()) comp = partner.GetFertileMenstruationComp(); else comp = partner.GetRandomMenstruationComp(); if (comp == null) @@ -125,14 +125,14 @@ namespace RJW_Menstruation { private static bool PregnancyBlocksImpregnation(this Pawn pawn, bool _) { - if (!Configurations.EnableAnimalCycle && pawn.IsAnimal()) return pawn.IsPregnant(); + if (!pawn.ShouldCycle()) return pawn.IsPregnant(); else if (pawn.GetMenstruationComps().Any()) return false; else return pawn.IsPregnant(); } private static readonly MethodInfo IsPregnant = AccessTools.Method(typeof(PawnExtensions), nameof(PawnExtensions.IsPregnant), new System.Type[] { typeof(Pawn), typeof(bool) }); public static IEnumerable Transpiler(IEnumerable instructions) { - if (IsPregnant == null || IsPregnant.ReturnType != typeof(bool)) throw new System.InvalidOperationException("IsPregnant not found"); + if (IsPregnant?.ReturnType != typeof(bool)) throw new System.InvalidOperationException("IsPregnant not found"); foreach (CodeInstruction instruction in instructions) { if (instruction.Calls(IsPregnant)) @@ -179,7 +179,7 @@ namespace RJW_Menstruation { // Awkward, but it'll have to do Pawn pawn = props.pawn; - if (__result == 0 || !pawn.HasQuirk(QuirkUtility.Quirks.ImpregnationFetish) || !props.hasPartner()) return; + if (__result == 0 || !pawn.HasImpregnationFetish() || !props.hasPartner()) return; // Check if the existing code would have added the count Pawn partner = props.partner; @@ -238,7 +238,7 @@ namespace RJW_Menstruation private static readonly FieldInfo MinimumFuckabilityToHookup = AccessTools.Field(typeof(RJWHookupSettings), nameof(RJWHookupSettings.MinimumFuckabilityToHookup)); public static IEnumerable Transpiler(IEnumerable instructions) { - if (MinimumFuckabilityToHookup == null || MinimumFuckabilityToHookup.FieldType != typeof(float)) throw new System.InvalidOperationException("MinimumFuckabilityToHookup not found"); + if (MinimumFuckabilityToHookup?.FieldType != typeof(float)) throw new System.InvalidOperationException("MinimumFuckabilityToHookup not found"); bool first_fuckability = true; foreach (CodeInstruction instruction in instructions) { @@ -274,8 +274,8 @@ namespace RJW_Menstruation private static readonly FieldInfo MinimumRelationshipToHookup = AccessTools.Field(typeof(RJWHookupSettings), nameof(RJWHookupSettings.MinimumRelationshipToHookup)); public static IEnumerable Transpiler(IEnumerable instructions) { - if (MinimumAttractivenessToHookup == null || MinimumAttractivenessToHookup.FieldType != typeof(float)) throw new System.InvalidOperationException("MinimumAttractivenessToHookup not found"); - if (MinimumRelationshipToHookup == null || MinimumRelationshipToHookup.FieldType != typeof(float)) throw new System.InvalidOperationException("MinimumRelationshipToHookup not found"); + if (MinimumAttractivenessToHookup?.FieldType != typeof(float)) throw new System.InvalidOperationException("MinimumAttractivenessToHookup not found"); + if (MinimumRelationshipToHookup?.FieldType != typeof(float)) throw new System.InvalidOperationException("MinimumRelationshipToHookup not found"); LocalBuilder pawn_index = null; // Like in the last one, we switch the arguments around for the second load bool first_attractiveness = true; @@ -347,7 +347,7 @@ namespace RJW_Menstruation if (__instance.Sexprops.usedCondom) return; if (AndroidsCompatibility.IsAndroid(pawn)) return; if (!Impregnate_Patch.InteractionCanCausePregnancy(__instance.Sexprops)) return; - if (!Configurations.EnableAnimalCycle && xxx.is_animal(partner)) return; + if (!partner.ShouldCycle()) return; // Archotech penises have more control. Or something. CompHediffBodyPart penisComp = pawn.GetGenitalsList()?.Find(genital => (genital as Hediff_PartBaseNatural)?.def.defName.ToLower().Contains("penis") ?? false)?.TryGetComp(); @@ -374,26 +374,14 @@ namespace RJW_Menstruation } } - [HarmonyPatch(typeof(PawnCapacityWorker_Fertility), nameof(PawnCapacityWorker_Fertility.CalculateCapacityLevel))] - public static class PawnCapacityWorker_Fertility_Patch + [HarmonyPatch(typeof(PawnCapacityWorker_Fertility), "CalculateAgeImpact")] + public static class PawnCapacityWorker_Fertility_Age_Patch { - private static float GetFertilityStatOrOne(Thing thing, StatDef stat, bool applyPostProcess, int cacheStaleAfterTicks) + public static void Postfix(ref float __result, Pawn pawn) { - Pawn pawn = (Pawn)thing; + if (__result <= 0.0f) return; if (pawn.GetMenstruationComps().Any(comp => comp.CalculatingOvulationChance)) - return 1.0f; - else return thing.GetStatValue(stat, applyPostProcess, cacheStaleAfterTicks); - } - private static readonly MethodInfo GetStatValue = AccessTools.Method(typeof(StatExtension), "GetStatValue", new System.Type[] { typeof(Thing), typeof(StatDef), typeof(bool), typeof(int) }); - public static IEnumerable Transpiler(IEnumerable instructions) - { - if (GetStatValue == null || GetStatValue.ReturnType != typeof(float)) throw new System.InvalidOperationException("GetStatValue not found"); - foreach (CodeInstruction instruction in instructions) - { - if (instruction.Calls(GetStatValue)) - yield return CodeInstruction.Call(typeof(PawnCapacityWorker_Fertility_Patch), nameof(GetFertilityStatOrOne)); - else yield return instruction; - } + __result = 1.0f; } } } diff --git a/1.4/source/RJW_Menstruation/RJW_Menstruation/QuirkUtility.cs b/1.4/source/RJW_Menstruation/RJW_Menstruation/QuirkUtility.cs index c8c2352..409d3d7 100644 --- a/1.4/source/RJW_Menstruation/RJW_Menstruation/QuirkUtility.cs +++ b/1.4/source/RJW_Menstruation/RJW_Menstruation/QuirkUtility.cs @@ -6,14 +6,14 @@ namespace RJW_Menstruation public static class QuirkUtility { // All quirks used in Menstruation - public enum Quirks + private enum Quirks { Breeder, ImpregnationFetish, Messy, Teratophile, } - public static bool HasQuirk(this Pawn pawn, Quirks quirk) + private static bool HasQuirk(Pawn pawn, Quirks quirk) { switch (quirk) { @@ -29,5 +29,9 @@ namespace RJW_Menstruation return false; } } + public static bool IsBreeder(this Pawn pawn) => HasQuirk(pawn, Quirks.Breeder); + public static bool HasImpregnationFetish(this Pawn pawn) => HasQuirk(pawn, Quirks.ImpregnationFetish); + public static bool IsMessy(this Pawn pawn) => HasQuirk(pawn, Quirks.Messy); + public static bool IsTeratophile(this Pawn pawn) => HasQuirk(pawn, Quirks.Teratophile); } } diff --git a/1.4/source/RJW_Menstruation/RJW_Menstruation/Things.cs b/1.4/source/RJW_Menstruation/RJW_Menstruation/Things.cs index 5e4dd91..6417fb4 100644 --- a/1.4/source/RJW_Menstruation/RJW_Menstruation/Things.cs +++ b/1.4/source/RJW_Menstruation/RJW_Menstruation/Things.cs @@ -1,6 +1,7 @@ using RimWorld; using rjw; using System.Collections.Generic; +using System.Text; using System.Xml; using UnityEngine; using Verse; @@ -186,6 +187,7 @@ namespace RJW_Menstruation } public class AbsorberModExtension : DefModExtension { + public float passiveAbsorptionPerHour = 0.1f; public bool leakAfterDirty = false; public bool effectsAfterDirty = false; public ThingDef dirtyDef = null; @@ -198,7 +200,7 @@ namespace RJW_Menstruation public float absorbedfluids = 0; public bool dirty = false; public int wearTicks = 0; - protected virtual float PassiveAbsorptionPerHour => 0.1f; + public virtual float PassiveAbsorptionPerHour => def.GetModExtension().passiveAbsorptionPerHour; public virtual bool LeakAfterDirty => def.GetModExtension().leakAfterDirty; public virtual bool EffectAfterDirty => def.GetModExtension().effectsAfterDirty; public virtual ThingDef DirtyDef => def.GetModExtension().dirtyDef; @@ -211,9 +213,25 @@ namespace RJW_Menstruation public virtual void WearEffect(int tickInterval) { absorbedfluids += PassiveAbsorptionPerHour * tickInterval / GenDate.TicksPerHour; + CheckDirty(); if (dirty) wearTicks += tickInterval; } + public void CheckDirty() + { + if (absorbedfluids > this.GetStatValue(VariousDefOf.MaxAbsorbable) && (Wearer?.apparel?.IsLocked(this) ?? false)) + { + def = DirtyDef; + dirty = true; + OutfitForcedHandler forcedHandler = Wearer.outfits?.forcedHandler; + if (forcedHandler?.IsForced(this) ?? false) + forcedHandler.SetForced(this, false); + if (!def.equippedStatOffsets.NullOrEmpty()) + Wearer.health.capacities.Notify_CapacityLevelsDirty(); + Wearer.apparel.Notify_ApparelChanged(); + } + } + public override Color DrawColorTwo => fluidColor; public override void ExposeData() @@ -231,11 +249,26 @@ namespace RJW_Menstruation Scribe_Values.Look(ref fluidColor, "fluidColor", Color.white); } + public override string DescriptionDetailed + { + get + { + StringBuilder text = new StringBuilder(base.DescriptionDetailed); + text.AppendLine(); + text.Append(Translations.Description_Absorbed); + text.Append(": "); + text.Append(absorbedfluids.ToStringDecimalIfSmall()); + text.Append("/"); + text.Append(this.GetStatValue(VariousDefOf.MaxAbsorbable).ToStringDecimalIfSmall()); + text.Append("ml"); + + return text.ToString(); + } + } } public class Absorber_Tampon : Absorber { - protected override float PassiveAbsorptionPerHour => 0.5f; public override void DirtyEffect(int tickInterval) { diff --git a/1.4/source/RJW_Menstruation/RJW_Menstruation/Translations.cs b/1.4/source/RJW_Menstruation/RJW_Menstruation/Translations.cs index 0da2b3c..6c511cc 100644 --- a/1.4/source/RJW_Menstruation/RJW_Menstruation/Translations.cs +++ b/1.4/source/RJW_Menstruation/RJW_Menstruation/Translations.cs @@ -44,6 +44,7 @@ namespace RJW_Menstruation public static readonly string Dialog_WombInfo08 = "Dialog_WombInfo08".Translate(); public static readonly string Dialog_WombInfo09 = "Dialog_WombInfo09".Translate(); public static readonly string Dialog_WombInfo10 = "Dialog_WombInfo10".Translate(); + public static readonly string Description_Absorbed = "Description_Absorbed".Translate(); public static readonly string Option1_Label_1 = "Option1_Label_1".Translate(); diff --git a/1.4/source/RJW_Menstruation/RJW_Menstruation/Utility.cs b/1.4/source/RJW_Menstruation/RJW_Menstruation/Utility.cs index db91411..ae2cb4a 100644 --- a/1.4/source/RJW_Menstruation/RJW_Menstruation/Utility.cs +++ b/1.4/source/RJW_Menstruation/RJW_Menstruation/Utility.cs @@ -86,7 +86,7 @@ namespace RJW_Menstruation { res = 0.0f; } - if (pawn.HasQuirk(QuirkUtility.Quirks.Messy)) res *= Rand.Range(4.0f, 8.0f); + if (pawn.IsMessy()) res *= Rand.Range(4.0f, 8.0f); return res; } @@ -95,16 +95,7 @@ namespace RJW_Menstruation public static HediffComp_Breast GetBreastComp(this Pawn pawn) { - List hedifflist = Genital_Helper.get_PartsHediffList(pawn, Genital_Helper.get_breastsBPR(pawn))?.FindAll(h => VariousDefOf.AllBreasts.Contains(h.def)); - if (hedifflist.NullOrEmpty()) hedifflist = Genital_Helper.get_PartsHediffList(pawn, Genital_Helper.get_uddersBPR(pawn))?.FindAll(h => VariousDefOf.AllBreasts.Contains(h.def)); - if (hedifflist.NullOrEmpty()) return null; - HediffComp_Breast result; - foreach (Hediff h in hedifflist) - { - result = h.TryGetComp(); - if (result != null) return result; - } - return null; + return pawn.health.hediffSet.hediffs.FirstOrDefault((Hediff h) => VariousDefOf.AllBreasts.Contains(h.def))?.TryGetComp(); } public static HediffComp_Breast GetBreastComp(this Hediff hediff) @@ -192,8 +183,7 @@ namespace RJW_Menstruation public static void DrawBreastIcon(this Pawn pawn, Rect rect) { - Hediff hediff = Genital_Helper.get_PartsHediffList(pawn, Genital_Helper.get_breastsBPR(pawn)).FirstOrDefault(h => VariousDefOf.AllBreasts.Contains(h.def)) ?? - Genital_Helper.get_PartsHediffList(pawn, Genital_Helper.get_uddersBPR(pawn)).FirstOrDefault(h => VariousDefOf.AllBreasts.Contains(h.def)); + Hediff hediff = pawn.health.hediffSet.hediffs.FirstOrDefault(h => VariousDefOf.AllBreasts.Contains(h.def)); Texture2D breast, nipple, areola; if (hediff != null) { @@ -351,20 +341,19 @@ namespace RJW_Menstruation public static string GetVaginaLabel(this Pawn pawn) { - Hediff hediff = Genital_Helper.get_PartsHediffList(pawn, Genital_Helper.get_genitalsBPR(pawn)).Find(h => VariousDefOf.AllVaginas.Contains(h.def)); + Hediff hediff = pawn.health.hediffSet.hediffs.Find(h => VariousDefOf.AllVaginas.Contains(h.def)); return hediff.LabelBase.CapitalizeFirst() + "\n(" + hediff.LabelInBrackets + ")" + "\n" + xxx.CountOfSex.LabelCap.CapitalizeFirst() + ": " + pawn.records.GetAsInt(xxx.CountOfSex); } public static string GetAnusLabel(this Pawn pawn) { - Hediff hediff = Genital_Helper.get_PartsHediffList(pawn, Genital_Helper.get_anusBPR(pawn)).FirstOrDefault(h => VariousDefOf.AllAnuses.Contains(h.def)) ?? + Hediff hediff = pawn.health.hediffSet.hediffs.FirstOrDefault(h => VariousDefOf.AllAnuses.Contains(h.def)) ?? Genital_Helper.get_PartsHediffList(pawn, Genital_Helper.get_anusBPR(pawn)).FirstOrDefault(h => h.def.defName.ToLower().Contains("anus")); if (hediff != null) return hediff.LabelBase.CapitalizeFirst() + "\n(" + hediff.LabelInBrackets + ")"; else return ""; } public static string GetBreastLabel(this Pawn pawn) { - Hediff hediff = Genital_Helper.get_PartsHediffList(pawn, Genital_Helper.get_breastsBPR(pawn)).FirstOrDefault(h => VariousDefOf.AllBreasts.Contains(h.def)) ?? - Genital_Helper.get_PartsHediffList(pawn, Genital_Helper.get_uddersBPR(pawn)).FirstOrDefault(h => VariousDefOf.AllBreasts.Contains(h.def)); + Hediff hediff = pawn.health.hediffSet.hediffs.FirstOrDefault(h => VariousDefOf.AllBreasts.Contains(h.def)); if (hediff != null) return hediff.LabelBase.CapitalizeFirst() + "\n(" + hediff.LabelInBrackets + ")"; else return ""; } @@ -416,7 +405,7 @@ namespace RJW_Menstruation { Pawn res = pawn.GetFather(); if (res != null) return res; - else res = pawn.relations?.GetFirstDirectRelationPawn(PawnRelationDefOf.Parent, x => x != mother) ?? null; + else res = pawn.relations?.GetFirstDirectRelationPawn(PawnRelationDefOf.Parent, x => x != mother); return res; } @@ -448,7 +437,7 @@ namespace RJW_Menstruation { if (!Configurations.EnableWombIcon) return false; if (pawn.Drafted && !Configurations.EnableDraftedIcon) return false; - if (pawn.IsAnimal() && !Configurations.EnableAnimalCycle) return false; + if (!pawn.ShouldCycle()) return false; return true; } diff --git a/1.4/source/RJW_Menstruation/RJW_Menstruation/VariousDefOf.cs b/1.4/source/RJW_Menstruation/RJW_Menstruation/VariousDefOf.cs index cd4bb56..920bd78 100644 --- a/1.4/source/RJW_Menstruation/RJW_Menstruation/VariousDefOf.cs +++ b/1.4/source/RJW_Menstruation/RJW_Menstruation/VariousDefOf.cs @@ -40,7 +40,7 @@ namespace RJW_Menstruation public static readonly ThoughtDef TookContraceptivePill = DefDatabase.GetNamed("TookContraceptivePill"); public static readonly ThoughtDef HateTookContraceptivePill = DefDatabase.GetNamed("HateTookContraceptivePill"); public static readonly ThoughtDef EggRestorationReceived = DefDatabase.GetNamed("EggRestorationReceived"); - public static readonly CompProperties_Menstruation HumanVaginaCompProperties = (CompProperties_Menstruation)Genital_Helper.average_vagina.comps.FirstOrDefault(x => x is CompProperties_Menstruation); + public static readonly CompProperties_Menstruation HumanVaginaCompProperties = Genital_Helper.average_vagina.CompProps(); public static readonly KeyBindingDef OpenStatusWindowKey = DefDatabase.GetNamed("OpenStatusWindow"); public static readonly RecordDef AmountofCreampied = DefDatabase.GetNamed("AmountofCreampied"); public static readonly RecordDef AmountofFertilizedEggs = DefDatabase.GetNamed("AmountofFertilizedEggs"); @@ -69,6 +69,7 @@ namespace RJW_Menstruation private static HashSet allvaginas = null; private static HashSet allanuses = null; private static HashSet allbreasts = null; + private static HashSet egglayergenes = null; public static List AllRaces { @@ -161,6 +162,22 @@ namespace RJW_Menstruation return allbreasts; } } + public static HashSet EggLayerGenes + { + get + { + if (egglayergenes != null) return egglayergenes; + egglayergenes = new HashSet + { + DefDatabase.GetNamedSilentFail("AG_EggLaying"), // Alpha Genes + DefDatabase.GetNamedSilentFail("VRESaurids_Oviparous"), // VE Saurid + DefDatabase.GetNamedSilentFail("VRE_SaplingBirth"), // VE Phytokin + }; + egglayergenes.Remove(null); + + return egglayergenes; + } + } // Defs from Milkable Colonists public static readonly HediffDef Hediff_Lactating_Drug = DefDatabase.GetNamedSilentFail("Lactating_Drug"); diff --git a/About/Manifest.xml b/About/Manifest.xml index 028cc7b..eb861f5 100644 --- a/About/Manifest.xml +++ b/About/Manifest.xml @@ -1,7 +1,7 @@ RJW Menstruation - 1.0.9.1 + 1.0.9.2 diff --git a/LoadFolders.xml b/LoadFolders.xml index 1707748..f711d5c 100644 --- a/LoadFolders.xml +++ b/LoadFolders.xml @@ -14,6 +14,5 @@
  • 1.4
  • 1.4/RJW Menstruation Race Support
  • - \ No newline at end of file diff --git a/changelogs.txt b/changelogs.txt index 6db36eb..2000239 100644 --- a/changelogs.txt +++ b/changelogs.txt @@ -1,3 +1,20 @@ +Version 1.0.9.2 + - Updated Traditional Chinese translation by Hydrogen. + - Fixed the no bleeding gene having positive metabolic efficiency instead of negative. + - Fixed ovulation chance not accounting for genes or precepts. + - Better handle wombs that are not in the genitals body part. + - Handle errors more gracefully when starting a pregnancy. + - Fix implanting multiple eggs in an animal when configured to use Biotech pregnancies. + - An equipped tampon or pad will now show how much fluid it has absorbed in its tooltip. + - Passive absorption will now make a tampon or pad dirty after enough time, even if no fluid was added. + - It will take a cloth tampon about 2 days to become dirty passively. A cloth pad will take about 10 days. + - An absorber that was force worn will no longer be forced once it becomes dirty. + - Pawns without a menstrual cycle will no longer equip absorbers. + - Egglaying races no longer have a menstrual cycle, regardless of vagina type. + - Pawns with the egglaying genes from Alpha Genes, VE Saurids, or Phytokin no longer have a menstrual cycle. + - The womb status button will now appear in a pawn's health tab when using Compact Hediffs, with thanks to Fern. + - Tampons and pads are now allowed by default in the worker, soldier, and slave starting outfits. + Version 1.0.9.1 - Japanese translation for most text by Lokuzt. - New womb, cum, and fetus graphics by Euldrop.