Compare commits

..

40 commits

Author SHA1 Message Date
lutepickle
b254273935 Merge branch 'dev' 2023-03-02 21:34:10 -08:00
lutepickle
e4583fe9d7 Add placeholder graphic for genes 2023-03-02 18:55:29 -08:00
lutepickle
5abdf52dbe Use GoNextStage more to properly reset curStageHrs 2023-03-02 14:42:49 -08:00
lutepickle
69934d06f5 Add patch to darken nipples during Biotech lactation, but commented out 2023-03-01 17:00:58 -08:00
lutepickle
09f9438bcc Reset the baby name deadline at birth 2023-03-01 16:29:58 -08:00
lutepickle
91142d640f Copy hair color for identical twins 2023-03-01 10:21:59 -08:00
lutepickle
b28beb8eaa Add some debug logging around the pregnancy to labor transition 2023-03-01 09:55:54 -08:00
lutepickle
ca895ef296 Show the debug due date on the gizmo for Biotech pregnancies 2023-02-28 18:02:20 -08:00
lutepickle
5c27575446 Save the Biotech twins setting 2023-02-27 21:36:31 -08:00
lutepickle
92ace30022 Properly show multiple fetuses with pregenerated babies 2023-02-27 21:33:14 -08:00
lutepickle
672e6f6ecc Remove unneeded milk bottle graphics 2023-02-13 10:22:23 -08:00
lutepickle
2b0d5dd0e6 Some mods break SkinColor to the point that even trying to access it throws a null ref, so go work around that 2023-01-25 07:11:00 -08:00
lutepickle
aba80efe58 Don't allow adding genes to pawns without wombs 2023-01-22 09:44:28 -08:00
lutepickle
9f20f78aab Remove a HarmonyDebug 2023-01-11 07:34:58 -08:00
lutepickle
71575c671e Fix error when trying to terminate a menstruation pregnancy 2023-01-11 07:32:50 -08:00
lutepickle
da7284b909 Make the HAR disabler conditional only on the configuration. There's too many birtherThing/geneticMother edge cases to make puzzling it out practical 2023-01-11 06:24:14 -08:00
lutepickle
3132992aa3 A bunch of type-safety around HAR. 2023-01-10 16:09:37 -08:00
lutepickle
2cb26ea016 HAR transpiles ApplyBirthOutcome to produce multiple children per littersize, but the pregenerated babies already handle that. So patch HAR to only produce one if our system is running 2023-01-10 12:41:11 -08:00
lutepickle
b9bfad10bb Resolve graphics on all newly generated babies, not just the first of a set 2023-01-10 08:20:22 -08:00
lutepickle
7d8228e03a Ensure that Biotech babies are born at zero ticks of age 2023-01-10 07:05:13 -08:00
lutepickle
93566b5fa3 Call ResolveAllGraphics unconditionally for babies. Also copy skinColorOverride for twins 2023-01-10 05:46:16 -08:00
lutepickle
d34c96315a Have the babies track their fathers during pregnancy 2023-01-09 08:50:50 -08:00
lutepickle
638b9f4611 Make the mod options window a bit longer just to make all the buttons appear again 2023-01-08 12:53:26 -08:00
lutepickle
08a50e47c3 Add Biotech twin option to configuration settings, disabled by default 2023-01-08 12:33:06 -08:00
lutepickle
c3176fb0e5 Integration of pregenerated babies into the womb dialog 2023-01-08 12:19:47 -08:00
lutepickle
f5f8eba037 Save option for enabling biotech twins 2023-01-08 12:02:18 -08:00
lutepickle
fcfb7cdefb Add support for pregenerated babies into Implant 2023-01-08 12:01:45 -08:00
lutepickle
89f26b2601 Add the two transpilers to allow looping through babies 2023-01-08 11:29:21 -08:00
lutepickle
e42bbdb3d4 Add the ApplyBirthOutcome transpiler 2023-01-08 10:11:58 -08:00
lutepickle
7bf9f80742 Add a missing using directive to PregnancyCommon 2023-01-07 21:23:08 -08:00
lutepickle
8aff96e5a4 Prevent two humanlikes from creating an animal for Biotech pregnancies 2023-01-07 20:58:28 -08:00
lutepickle
12c760d16c Fix periodic ovulator cycle variability 2023-01-07 20:26:13 -08:00
lutepickle
045e9548f6 Framework for pregenerated babies 2023-01-07 20:18:17 -08:00
lutepickle
3f21b760b2 Make the MethodInfos in MultiplePregnancy static. 2023-01-07 12:00:48 -08:00
lutepickle
21e471ebf3 Use the builtin BestOutcome for the childbirth ritual instead of searching our own. 2023-01-07 11:50:37 -08:00
lutepickle
da9903aaf9 Updated Traditional Chinese by Hydrogen 2023-01-07 06:46:15 -08:00
lutepickle
22b2aca312 Change the terminology of the gene descriptions a bit 2023-01-07 06:28:16 -08:00
lutepickle
b4aa55c159 Re-add GetMenstruationComp(Hediff) marked as obsolete. There's code out there that calls it. 2023-01-07 06:08:42 -08:00
lutepickle
68f05dc272 The genes aren't inheritable, so don't bother saying "born with" in the egg multiplier descriptions. Leave support in the code, though. 2023-01-07 05:32:19 -08:00
lutepickle
f3e14e67b0 Initial implementation of genes 2023-01-03 22:48:06 -08:00
32 changed files with 1099 additions and 371 deletions

Binary file not shown.

View file

@ -0,0 +1,107 @@
<?xml version="1.0" encoding="utf-8"?>
<Defs>
<GeneCategoryDef>
<defName>Menstruation</defName>
<label>menstruation</label>
<displayPriorityInXenotype>402</displayPriorityInXenotype>
</GeneCategoryDef>
<GeneDef Name="Menstruation_EggLifetime" Abstract="True">
<displayCategory>Menstruation</displayCategory>
<exclusionTags>
<li>Menstruation_EggLifetime</li>
</exclusionTags>
</GeneDef>
<GeneDef ParentName="Menstruation_EggLifetime">
<defName>Menstruation_ShortEggLifetime</defName>
<label>short egg lifetime</label>
<description>Unfertilized eggs with this gene last three-quarters as long.</description>
<iconPath>UI/Genes/Placeholder</iconPath>
<biostatMet>1</biostatMet>
<displayOrderInCategory>10</displayOrderInCategory>
</GeneDef>
<GeneDef ParentName="Menstruation_EggLifetime">
<defName>Menstruation_DoubleEggLifetime</defName>
<label>double egg lifetime</label>
<description>Unfertilized eggs with this gene last twice as long.</description>
<iconPath>UI/Genes/Placeholder</iconPath>
<biostatMet>-1</biostatMet>
<displayOrderInCategory>12</displayOrderInCategory>
</GeneDef>
<GeneDef ParentName="Menstruation_EggLifetime">
<defName>Menstruation_QuadEggLifetime</defName>
<label>quadrule egg lifetime</label>
<description>Eggs with this gene last four times as long.</description>
<iconPath>UI/Genes/Placeholder</iconPath>
<biostatMet>-2</biostatMet>
<biostatCpx>1</biostatCpx>
<displayOrderInCategory>16</displayOrderInCategory>
</GeneDef>
<GeneDef Name="Menstruation_Estrus" Abstract="True">
<displayCategory>Menstruation</displayCategory>
<exclusionTags>
<li>Menstruation_Estrus</li>
</exclusionTags>
</GeneDef>
<GeneDef ParentName="Menstruation_Estrus">
<defName>Menstruation_NeverEstrus</defName>
<label>never estrus</label>
<description>Carriers of this gene will never go into estrus.</description>
<iconPath>UI/Genes/Placeholder</iconPath>
<biostatMet>1</biostatMet>
<displayOrderInCategory>20</displayOrderInCategory>
</GeneDef>
<GeneDef ParentName="Menstruation_Estrus">
<defName>Menstruation_FullEstrus</defName>
<label>full estrus</label>
<description>Carriers of this gene will enter full estrus every menstrual cycle, regardless of vagina type.</description>
<iconPath>UI/Genes/Placeholder</iconPath>
<biostatMet>-1</biostatMet>
<biostatCpx>1</biostatCpx>
<displayOrderInCategory>25</displayOrderInCategory>
</GeneDef>
<GeneDef Name="Menstruation_Ovulation" Abstract="True">
<displayCategory>Menstruation</displayCategory>
<exclusionTags>
<li>Menstruation_Ovulation</li>
</exclusionTags>
</GeneDef>
<GeneDef ParentName="Menstruation_Ovulation">
<defName>Menstruation_DoubleOvulation</defName>
<label>double ovulation</label>
<description>Carriers of this gene will ovulate twice as many eggs.</description>
<iconPath>UI/Genes/Placeholder</iconPath>
<biostatMet>-1</biostatMet>
<displayOrderInCategory>30</displayOrderInCategory>
</GeneDef>
<GeneDef ParentName="Menstruation_Ovulation">
<defName>Menstruation_QuadOvulation</defName>
<label>quadruple ovulation</label>
<description>Carriers of this gene will ovulate four times as many eggs.</description>
<iconPath>UI/Genes/Placeholder</iconPath>
<biostatMet>-1</biostatMet>
<displayOrderInCategory>35</displayOrderInCategory>
</GeneDef>
<GeneDef>
<defName>Menstruation_NoBleeding</defName>
<label>no bleeding</label>
<displayCategory>Menstruation</displayCategory>
<description>Carriers of this gene will not bleed at the end of their cycle.</description>
<iconPath>UI/Genes/Placeholder</iconPath>
<biostatMet>1</biostatMet>
<displayOrderInCategory>40</displayOrderInCategory>
</GeneDef>
<!-- Pheromones? -->
</Defs>

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<LanguageData>
<Menstruation.label>月經週期</Menstruation.label>
<Menstruation_ShortEggLifetime.label>較短卵細胞壽命</Menstruation_ShortEggLifetime.label>
<Menstruation_ShortEggLifetime.description>未受精卵細胞存活時長僅有原先的3/4</Menstruation_ShortEggLifetime.description>
<Menstruation_DoubleEggLifetime.label>雙倍卵細胞壽命</Menstruation_DoubleEggLifetime.label>
<Menstruation_DoubleEggLifetime.description>未受精卵細胞可存活至原先的2倍之久</Menstruation_DoubleEggLifetime.description>
<Menstruation_QuadEggLifetime.label>四倍卵細胞壽命</Menstruation_QuadEggLifetime.label>
<Menstruation_QuadEggLifetime.description>未受精卵細胞可存活至原先的4倍之久</Menstruation_QuadEggLifetime.description>
<Menstruation_NeverEstrus.label>永不發情</Menstruation_NeverEstrus.label>
<Menstruation_NeverEstrus.description>基因攜帶者永遠不會進入發情期。</Menstruation_NeverEstrus.description>
<Menstruation_FullEstrus.label>始終發情</Menstruation_FullEstrus.label>
<Menstruation_FullEstrus.description>基因攜帶者的每一個月經週期均具備發情期,無論陰道類型為何。</Menstruation_FullEstrus.description>
<Menstruation_DoubleOvulation.label>雙倍排卵</Menstruation_DoubleOvulation.label>
<Menstruation_DoubleOvulation.description>基因攜帶者的子宮可以在每個排卵週期產生雙倍的卵子。</Menstruation_DoubleOvulation.description>
<Menstruation_QuadOvulation.label>四倍排卵</Menstruation_QuadOvulation.label>
<Menstruation_QuadOvulation.description>基因攜帶者的子宮可以在每個排卵週期產生四倍的卵子。</Menstruation_QuadOvulation.description>
<Menstruation_NoBleeding.label>無經血</Menstruation_NoBleeding.label>
<Menstruation_NoBleeding.description>基因攜帶者的子宮內膜不會脫落出血。</Menstruation_NoBleeding.description>
</LanguageData>

View file

@ -8,4 +8,7 @@
<TookContraceptivePill.stages.0.description>總算把這事了結了。</TookContraceptivePill.stages.0.description> <TookContraceptivePill.stages.0.description>總算把這事了結了。</TookContraceptivePill.stages.0.description>
<HateTookContraceptivePill.stages.0.label>吃了避孕藥</HateTookContraceptivePill.stages.0.label> <HateTookContraceptivePill.stages.0.label>吃了避孕藥</HateTookContraceptivePill.stages.0.label>
<HateTookContraceptivePill.stages.0.description>我想要小孩!</HateTookContraceptivePill.stages.0.description> <HateTookContraceptivePill.stages.0.description>我想要小孩!</HateTookContraceptivePill.stages.0.description>
<!--Auto generated: Thu Jan 5 22:13:15 2023-->
<EggRestorationReceived.stages.0.label>卵母細胞再生術</EggRestorationReceived.stages.0.label>
<EggRestorationReceived.stages.0.description>我可以繼續繁衍一小段時間了!</EggRestorationReceived.stages.0.description>
</LanguageData> </LanguageData>

View file

@ -37,7 +37,7 @@
<Option7_Label>月經加速</Option7_Label> <Option7_Label>月經加速</Option7_Label>
<Option7_Desc>加快月經週期</Option7_Desc> <Option7_Desc>加快月經週期</Option7_Desc>
<Option8_Label>除錯</Option8_Label> <Option8_Label>除錯</Option8_Label>
<Option8_Desc>顯示除錯資訊</Option8_Desc> <Option8_Desc>顯示除錯資訊&#10;啟用時會令「胎兒信息級別」選項調至「全部細節」。</Option8_Desc>
<Option9_Label>子宮狀態</Option9_Label> <Option9_Label>子宮狀態</Option9_Label>
<Option9_Desc>在狀態窗口中繪製子宮圖標</Option9_Desc> <Option9_Desc>在狀態窗口中繪製子宮圖標</Option9_Desc>
<Option10_Label>陰道狀態</Option10_Label> <Option10_Label>陰道狀態</Option10_Label>
@ -62,7 +62,6 @@
<Option17_Label>最大雙胞胎數量</Option17_Label> <Option17_Label>最大雙胞胎數量</Option17_Label>
<Option17_Desc>設置最大雙胞胎數量</Option17_Desc> <Option17_Desc>設置最大雙胞胎數量</Option17_Desc>
<FloatMenu_CleanSelf>清洗陰道</FloatMenu_CleanSelf> <FloatMenu_CleanSelf>清洗陰道</FloatMenu_CleanSelf>
<!--Auto generated: Mon Oct 3 21:49:26 2022--> <!--Auto generated: Mon Oct 3 21:49:26 2022-->
<Stage_Menopause>絕經</Stage_Menopause> <Stage_Menopause>絕經</Stage_Menopause>
<Stage_Anestrus>乏情期</Stage_Anestrus> <Stage_Anestrus>乏情期</Stage_Anestrus>
@ -98,7 +97,7 @@
<Option21_Label>本模組作用於:</Option21_Label> <Option21_Label>本模組作用於:</Option21_Label>
<Option21_Desc>這些小人的「RJW月經週期」工具欄對玩家可見。</Option21_Desc> <Option21_Desc>這些小人的「RJW月經週期」工具欄對玩家可見。</Option21_Desc>
<Option22_Label>使用進階雜交定義</Option22_Label> <Option22_Label>使用進階雜交定義</Option22_Label>
<Option22_Desc>寫RJW和RaceSupport插件的雜交定義。Overrides RJW and RaceSupport's hybrid definition. <Option22_Desc>寫RJW和RaceSupport插件的雜交定義。Overrides RJW and RaceSupport's hybrid definition.
主導混合擴展決定了首先使用誰的定義。不建議更改此設置。Dominant hybrid extension determines whose definition used first. Not recommended to change this.</Option22_Desc> 主導混合擴展決定了首先使用誰的定義。不建議更改此設置。Dominant hybrid extension determines whose definition used first. Not recommended to change this.</Option22_Desc>
<Option23_Label>主導混合擴展Dominant hybrid extension</Option23_Label> <Option23_Label>主導混合擴展Dominant hybrid extension</Option23_Label>
<Option23_Label_1>母本</Option23_Label_1> <Option23_Label_1>母本</Option23_Label_1>
@ -111,7 +110,7 @@
<Option_PermanentNippleChange_Desc>乳頭在孕期間的改變會在孕期結束後保留多少?</Option_PermanentNippleChange_Desc> <Option_PermanentNippleChange_Desc>乳頭在孕期間的改變會在孕期結束後保留多少?</Option_PermanentNippleChange_Desc>
<Option28_Label>客製化雜交細節</Option28_Label> <Option28_Label>客製化雜交細節</Option28_Label>
<Option28_Tooltip>開啟雜交編輯器 <Option28_Tooltip>開啟雜交編輯器
該選項會寫XML文件中的雜交定義。</Option28_Tooltip> 該選項會寫XML文件中的雜交定義。</Option28_Tooltip>
<Option29_Label>允許圖標縮小</Option29_Label> <Option29_Label>允許圖標縮小</Option29_Label>
<Option29_Desc>允許圖標在特殊場合縮小。</Option29_Desc> <Option29_Desc>允許圖標在特殊場合縮小。</Option29_Desc>
<Option30_Label>卵細胞生命期乘數</Option30_Label> <Option30_Label>卵細胞生命期乘數</Option30_Label>
@ -123,7 +122,7 @@
<Option32_Label>擴張力度</Option32_Label> <Option32_Label>擴張力度</Option32_Label>
<Option32_Desc>調節擴張力度。</Option32_Desc> <Option32_Desc>調節擴張力度。</Option32_Desc>
<Option_EnableGatherCumGizmo_Label>啟用「擠出精液」按鈕</Option_EnableGatherCumGizmo_Label> <Option_EnableGatherCumGizmo_Label>啟用「擠出精液」按鈕</Option_EnableGatherCumGizmo_Label>
<Option_EstrusOverride_Label>令「發情期」機制寫RJW的濫交選項</Option_EstrusOverride_Label> <Option_EstrusOverride_Label>令「發情期」機制寫RJW的濫交選項</Option_EstrusOverride_Label>
<Option_EstrusOverride_Desc>啟用時處於顯式發情期的小人將會使用以下選項來選定床伴。RJW的原始設定會被忽略。 <Option_EstrusOverride_Desc>啟用時處於顯式發情期的小人將會使用以下選項來選定床伴。RJW的原始設定會被忽略。
所有數值與RJW的對應選項相同。</Option_EstrusOverride_Desc> 所有數值與RJW的對應選項相同。</Option_EstrusOverride_Desc>
<Option_EstrusFuckability_Label>發情期床伴最低fuckability</Option_EstrusFuckability_Label> <Option_EstrusFuckability_Label>發情期床伴最低fuckability</Option_EstrusFuckability_Label>
@ -139,4 +138,13 @@
<CustomHybrid_Title>{0}之雜交</CustomHybrid_Title> <CustomHybrid_Title>{0}之雜交</CustomHybrid_Title>
<CustomHybrid_Tooltip>當{0}與{1}產生後代,有{3}的概率生出{2}。 <CustomHybrid_Tooltip>當{0}與{1}產生後代,有{3}的概率生出{2}。
若兩個種族相互間皆存在雜交定義,則以父本的定義為準。</CustomHybrid_Tooltip> 若兩個種族相互間皆存在雜交定義,則以父本的定義為準。</CustomHybrid_Tooltip>
<!--Auto generated: Thu Jan 5 22:13:15 2023-->
<Option_PregnancyFromBaseRJW_Label>使用RJW的簡單懷孕系統</Option_PregnancyFromBaseRJW_Label>
<Option_PregnancyFromMultiplePregnancy_Label>使用本模組的多重懷孕</Option_PregnancyFromMultiplePregnancy_Label>
<Option_PregnancyFromBiotech_Label>使用「生機」(Biotech)追加的懷孕機制</Option_PregnancyFromBiotech_Label>
<Option_EnableDraftedIcon_Label>於「徵召」狀態下仍顯示子宮狀態</Option_EnableDraftedIcon_Label>
<Option_EnableDraftedIcon_Desc>角色處於被徵召狀態時,子宮圖標不予隱藏。</Option_EnableDraftedIcon_Desc>
<CannotNoEggs>沒有卵細胞</CannotNoEggs>
<CannotNoWomb>必須擁有子宮</CannotNoWomb>
<EggRestorationCompleted>{PAWN_labelShort}完成了{PAWN_possessive}卵母細胞再生術(cycle)</EggRestorationCompleted>
</LanguageData> </LanguageData>

View file

@ -124,6 +124,8 @@
<Option_PregnancyFromBaseRJW_Label>Use basic RJW pregnancy</Option_PregnancyFromBaseRJW_Label> <Option_PregnancyFromBaseRJW_Label>Use basic RJW pregnancy</Option_PregnancyFromBaseRJW_Label>
<Option_PregnancyFromMultiplePregnancy_Label>Use menstruation multiple pregnancy</Option_PregnancyFromMultiplePregnancy_Label> <Option_PregnancyFromMultiplePregnancy_Label>Use menstruation multiple pregnancy</Option_PregnancyFromMultiplePregnancy_Label>
<Option_PregnancyFromBiotech_Label>Use Biotech pregnancy</Option_PregnancyFromBiotech_Label> <Option_PregnancyFromBiotech_Label>Use Biotech pregnancy</Option_PregnancyFromBiotech_Label>
<Option_EnableBiotechTwins_Label>(EXPERIMENTAL) Enable multiple babies/twins in a single Biotech pregnancy.</Option_EnableBiotechTwins_Label>
<Option_EnableBiotechTwins_Desc>Enabling this option will allow identical and hetero ovular twins with Biotech.&#10;Also allows the hybrid system, but two humanlikes cannot produce an animal.</Option_EnableBiotechTwins_Desc>
<Option_EnableDraftedIcon_Label>Show womb status when drafted</Option_EnableDraftedIcon_Label> <Option_EnableDraftedIcon_Label>Show womb status when drafted</Option_EnableDraftedIcon_Label>
<Option_EnableDraftedIcon_Desc>Draw womb icon for drafted pawns</Option_EnableDraftedIcon_Desc> <Option_EnableDraftedIcon_Desc>Draw womb icon for drafted pawns</Option_EnableDraftedIcon_Desc>
<Button_ResetToDefault>Reset to default</Button_ResetToDefault> <Button_ResetToDefault>Reset to default</Button_ResetToDefault>

View file

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<Patch>
<Operation Class="PatchOperationFindMod">
<mods>
<li>Biotech</li>
</mods>
<match Class="PatchOperationAdd">
<xpath>/Defs/HediffDef[defName="PregnantHuman" or defName="PregnancyLabor" or defName="PregnancyLaborPushing"]/comps</xpath>
<value>
<li>
<compClass>RJW_Menstruation.HediffComp_PregeneratedBabies</compClass>
</li>
</value>
</match>
</Operation>
</Patch>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

View file

@ -1,4 +1,5 @@
using AlienRace; using AlienRace;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using UnityEngine; using UnityEngine;
using Verse; using Verse;
@ -11,7 +12,7 @@ namespace RJW_Menstruation
public static bool IsHAR(this Pawn pawn) public static bool IsHAR(this Pawn pawn)
{ {
if (!Configurations.HARActivated) return false; if (!Configurations.HARActivated) return false;
return pawn?.def is ThingDef_AlienRace; return GenTypes.GetTypeInAnyAssembly("AlienRace.ThingDef_AlienRace").IsInstanceOfType(pawn?.def);
} }
public static void CopyHARProperties(Pawn baby, Pawn original) public static void CopyHARProperties(Pawn baby, Pawn original)

View file

@ -55,6 +55,7 @@ namespace RJW_Menstruation
public static float EstrusAttractivenessToHookup = RJWHookupSettings.MinimumAttractivenessToHookup; public static float EstrusAttractivenessToHookup = RJWHookupSettings.MinimumAttractivenessToHookup;
public static float EstrusRelationshipToHookup = RJWHookupSettings.MinimumRelationshipToHookup; public static float EstrusRelationshipToHookup = RJWHookupSettings.MinimumRelationshipToHookup;
public static PregnancyType PregnancySource = PregnancyType.MultiplePregnancy; public static PregnancyType PregnancySource = PregnancyType.MultiplePregnancy;
public static bool EnableBiotechTwins = false;
public static bool EnableHeteroOvularTwins = true; public static bool EnableHeteroOvularTwins = true;
public static bool EnableEnzygoticTwins = true; public static bool EnableEnzygoticTwins = true;
public static float EnzygoticTwinsChance = EnzygoticTwinsChanceDefault; public static float EnzygoticTwinsChance = EnzygoticTwinsChanceDefault;
@ -89,6 +90,7 @@ namespace RJW_Menstruation
EstrusAttractivenessToHookup = RJWHookupSettings.MinimumAttractivenessToHookup; EstrusAttractivenessToHookup = RJWHookupSettings.MinimumAttractivenessToHookup;
EstrusRelationshipToHookup = RJWHookupSettings.MinimumRelationshipToHookup; EstrusRelationshipToHookup = RJWHookupSettings.MinimumRelationshipToHookup;
EnzygoticTwinsChanceAdjust = EnzygoticTwinsChanceAdjustDefault; EnzygoticTwinsChanceAdjust = EnzygoticTwinsChanceAdjustDefault;
EnableBiotechTwins = false;
EnableEnzygoticTwins = true; EnableEnzygoticTwins = true;
EnableHeteroOvularTwins = true; EnableHeteroOvularTwins = true;
PregnancySource = PregnancyType.MultiplePregnancy; PregnancySource = PregnancyType.MultiplePregnancy;
@ -200,6 +202,7 @@ namespace RJW_Menstruation
Scribe_Values.Look(ref EstrusAttractivenessToHookup, "EstrusAttractivenessToHookup", EstrusAttractivenessToHookup, true); Scribe_Values.Look(ref EstrusAttractivenessToHookup, "EstrusAttractivenessToHookup", EstrusAttractivenessToHookup, true);
Scribe_Values.Look(ref EstrusRelationshipToHookup, "EstrusRelationshipToHookup", EstrusRelationshipToHookup, true); Scribe_Values.Look(ref EstrusRelationshipToHookup, "EstrusRelationshipToHookup", EstrusRelationshipToHookup, true);
Scribe_Values.Look(ref PregnancySource, "PregnancySource", PregnancySource, true); Scribe_Values.Look(ref PregnancySource, "PregnancySource", PregnancySource, true);
Scribe_Values.Look(ref EnableBiotechTwins, "EnableBiotechTwins", EnableBiotechTwins, true);
Scribe_Values.Look(ref EnableHeteroOvularTwins, "EnableHeteroOvularTwins", EnableHeteroOvularTwins, true); Scribe_Values.Look(ref EnableHeteroOvularTwins, "EnableHeteroOvularTwins", EnableHeteroOvularTwins, true);
Scribe_Values.Look(ref EnableEnzygoticTwins, "EnableEnzygoticTwins", EnableEnzygoticTwins, true); Scribe_Values.Look(ref EnableEnzygoticTwins, "EnableEnzygoticTwins", EnableEnzygoticTwins, true);
Scribe_Values.Look(ref EnzygoticTwinsChance, "EnzygoticTwinsChance", EnzygoticTwinsChance, true); Scribe_Values.Look(ref EnzygoticTwinsChance, "EnzygoticTwinsChance", EnzygoticTwinsChance, true);
@ -271,11 +274,12 @@ namespace RJW_Menstruation
public override void DoSettingsWindowContents(Rect inRect) public override void DoSettingsWindowContents(Rect inRect)
{ {
Rect outRect = new Rect(0f, 30f, inRect.width, inRect.height - 30f); Rect outRect = new Rect(0f, 30f, inRect.width, inRect.height - 30f);
float mainRectHeight = -3f + float mainRectHeight = 30f +
(Configurations.EnableWombIcon || Configurations.EnableButtonInHT ? 400f : 0f) + (Configurations.EnableWombIcon || Configurations.EnableButtonInHT ? 400f : 0f) +
(Configurations.EstrusOverridesHookupSettings ? 144f : 0f) + (Configurations.EstrusOverridesHookupSettings ? 144f : 0f) +
// TODO: Also for modified Biotech pregnancies
(Configurations.PregnancySource == Configurations.PregnancyType.MultiplePregnancy ? (Configurations.EnableEnzygoticTwins ? 175f : 75f) : 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); (Configurations.EnableBirthVaginaMorph ? 48f : 0f);
Rect mainRect = new Rect(0f, 0f, inRect.width - 30f, Math.Max(inRect.height + mainRectHeight, 1f)); Rect mainRect = new Rect(0f, 0f, inRect.width - 30f, Math.Max(inRect.height + mainRectHeight, 1f));
int Adjust; int Adjust;
@ -435,8 +439,10 @@ namespace RJW_Menstruation
Configurations.PregnancySource = Configurations.PregnancyType.MultiplePregnancy; Configurations.PregnancySource = Configurations.PregnancyType.MultiplePregnancy;
if (ModsConfig.BiotechActive && listmain.RadioButton(Translations.Option_PregnancyFromBiotech_Label, Configurations.PregnancySource == Configurations.PregnancyType.Biotech)) if (ModsConfig.BiotechActive && listmain.RadioButton(Translations.Option_PregnancyFromBiotech_Label, Configurations.PregnancySource == Configurations.PregnancyType.Biotech))
Configurations.PregnancySource = Configurations.PregnancyType.Biotech; Configurations.PregnancySource = Configurations.PregnancyType.Biotech;
// TODO: Also for modified Biotech pregnancy if (Configurations.PregnancySource == Configurations.PregnancyType.Biotech)
if (Configurations.PregnancySource == Configurations.PregnancyType.MultiplePregnancy) 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; float sectionheight = 75f;
if (Configurations.EnableEnzygoticTwins) sectionheight += 100; if (Configurations.EnableEnzygoticTwins) sectionheight += 100;

View file

@ -365,7 +365,7 @@ namespace RJW_Menstruation
cachedNipple = baseNipple + 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 // For some reason, Props can go null when RJW relocates the chest (e.g. some animals), so catch that
cachedColor = Colors.CMYKLerp(Pawn.story?.SkinColor ?? Color.white, (Props?.BlackNippleColor ?? CompProperties_Breast.DefaultBlacknippleColor.ToColor), Alpha); cachedColor = Colors.CMYKLerp(Utility.SafeSkinColor(Pawn), (Props?.BlackNippleColor ?? CompProperties_Breast.DefaultBlacknippleColor.ToColor), Alpha);
} }
public void CopyBreastProperties(HediffComp_Breast original) public void CopyBreastProperties(HediffComp_Breast original)

View file

@ -75,7 +75,7 @@ namespace RJW_Menstruation
case Stage.Ovulatory: case Stage.Ovulatory:
return true; return true;
case Stage.Luteal: case Stage.Luteal:
return IsEggExist && curStageHrs < Props.eggLifespanDays * 24; return IsEggExist && curStageHrs < EggLifespanHours * 24;
default: default:
return false; return false;
} }
@ -93,7 +93,7 @@ namespace RJW_Menstruation
case Stage.Ovulatory: case Stage.Ovulatory:
return true; return true;
case Stage.Luteal: case Stage.Luteal:
return IsEggExist && curStageHrs < Props.eggLifespanDays * 24; return IsEggExist && curStageHrs < EggLifespanHours * 24;
default: default:
return false; return false;
} }

View file

@ -126,6 +126,11 @@ namespace RJW_Menstruation
// RJW pregnancy, or Biotech pregnancy/labor/laborpushing // RJW pregnancy, or Biotech pregnancy/labor/laborpushing
protected Hediff pregnancy = null; protected Hediff pregnancy = null;
protected int eggLifeSpanHours = 48;
protected EstrusLevel estrusLevel = EstrusLevel.Visible;
protected float ovulationFactor = 1f;
protected bool noBleeding = false;
private static readonly SimpleCurve SexFrequencyCurve = new SimpleCurve() private static readonly SimpleCurve SexFrequencyCurve = new SimpleCurve()
{ {
new CurvePoint(0.4f,0.05f), new CurvePoint(0.4f,0.05f),
@ -179,6 +184,7 @@ namespace RJW_Menstruation
// Any exceptions in that will have been reported elsewhere in the code by now // Any exceptions in that will have been reported elsewhere in the code by now
avglittersize = 1.0f; avglittersize = 1.0f;
}; };
avglittersize *= ovulationFactor;
const float yearsBeforeMenopause = 6.0f; const float yearsBeforeMenopause = 6.0f;
opcache = (int)(RaceCyclesPerYear() * opcache = (int)(RaceCyclesPerYear() *
avglittersize * avglittersize *
@ -470,6 +476,15 @@ namespace RJW_Menstruation
return !eggs.NullOrEmpty(); return !eggs.NullOrEmpty();
} }
} }
public int EggLifespanHours
{
get
{
return eggLifeSpanHours;
}
}
public virtual bool IsDangerDay public virtual bool IsDangerDay
{ {
get get
@ -483,7 +498,7 @@ namespace RJW_Menstruation
case Stage.Ovulatory: case Stage.Ovulatory:
return true; return true;
case Stage.Luteal: case Stage.Luteal:
return curStageHrs < Props.eggLifespanDays * 24; return curStageHrs < EggLifespanHours * 24;
default: default:
return false; return false;
} }
@ -606,6 +621,28 @@ namespace RJW_Menstruation
} }
} }
public void Notify_UpdatedGenes()
{
eggLifeSpanHours = Props.eggLifespanDays * 24;
estrusLevel = Props.concealedEstrus ? EstrusLevel.Concealed : EstrusLevel.Visible;
ovulationFactor = 1f;
noBleeding = false;
if (Pawn.genes == null || !ModsConfig.BiotechActive) return;
if (Pawn.genes.HasGene(VariousDefOf.ShortEggLifetime)) eggLifeSpanHours = eggLifeSpanHours * 3 / 4;
else if (Pawn.genes.HasGene(VariousDefOf.DoubleEggLifetime)) eggLifeSpanHours *= 2;
else if (Pawn.genes.HasGene(VariousDefOf.QuadEggLifetime)) eggLifeSpanHours *= 4;
if (Pawn.genes.HasGene(VariousDefOf.NeverEstrus)) estrusLevel = EstrusLevel.None;
else if (Pawn.genes.HasGene(VariousDefOf.FullEstrus)) estrusLevel = EstrusLevel.Visible;
if (Pawn.genes.HasGene(VariousDefOf.DoubleOvulation)) ovulationFactor = 2f;
else if (Pawn.genes.HasGene(VariousDefOf.QuadOvulation)) ovulationFactor = 4f;
noBleeding = Pawn.genes.HasGene(VariousDefOf.NoBleeding);
}
public bool ShouldSimulate() public bool ShouldSimulate()
{ {
if (!Configurations.EnableAnimalCycle && Pawn.IsAnimal()) return false; if (!Configurations.EnableAnimalCycle && Pawn.IsAnimal()) return false;
@ -636,12 +673,12 @@ namespace RJW_Menstruation
if (Pregnancy != null && curStage != Stage.Pregnant) if (Pregnancy != null && curStage != Stage.Pregnant)
{ {
Log.Warning($"{Pawn}'s womb has a pregnancy, but was not in the pregnant stage"); Log.Warning($"{Pawn}'s womb has a pregnancy, but was not in the pregnant stage");
curStage = Stage.Pregnant; GoNextStage(Stage.Pregnant);
} }
BeforeSimulator(); BeforeSimulator();
if (pregnancy == null && (Pawn.health.capacities.GetLevel(xxx.reproduction) <= 0 || EggHealth <= 0 || Pawn.SterileGenes())) curStage = Stage.Infertile; if (pregnancy == null && (Pawn.health.capacities.GetLevel(xxx.reproduction) <= 0 || EggHealth <= 0 || Pawn.SterileGenes())) GoNextStage(Stage.Infertile);
switch (curStage) switch (curStage)
{ {
case Stage.Follicular: case Stage.Follicular:
@ -1029,6 +1066,8 @@ namespace RJW_Menstruation
initError = true; initError = true;
Props = (CompProperties_Menstruation)props; Props = (CompProperties_Menstruation)props;
Notify_UpdatedGenes();
if (Props.infertile) if (Props.infertile)
{ {
if (cums == null) cums = new List<Cum>(); if (cums == null) cums = new List<Cum>();
@ -1101,6 +1140,7 @@ namespace RJW_Menstruation
{ {
avglittersize = 1.0f; avglittersize = 1.0f;
} }
avglittersize *= ovulationFactor;
float fertStartAge = Pawn.RaceProps.lifeStageAges?.Find(stage => stage.def.reproductive)?.minAge ?? 0.0f; float fertStartAge = Pawn.RaceProps.lifeStageAges?.Find(stage => stage.def.reproductive)?.minAge ?? 0.0f;
float fertEndAge = Pawn.RaceProps.lifeExpectancy * (Pawn.IsAnimal() ? RJWPregnancySettings.fertility_endage_female_animal : RJWPregnancySettings.fertility_endage_female_humanlike); float fertEndAge = Pawn.RaceProps.lifeExpectancy * (Pawn.IsAnimal() ? RJWPregnancySettings.fertility_endage_female_animal : RJWPregnancySettings.fertility_endage_female_humanlike);
@ -1160,7 +1200,7 @@ namespace RJW_Menstruation
case Stage.Ovulatory: case Stage.Ovulatory:
return true; return true;
case Stage.Luteal: case Stage.Luteal:
return curStageHrs < Props.eggLifespanDays * 24; return curStageHrs < EggLifespanHours * 24;
default: default:
return false; return false;
} }
@ -1169,12 +1209,13 @@ namespace RJW_Menstruation
public EstrusLevel GetEstrusLevel() public EstrusLevel GetEstrusLevel()
{ {
if (!ShouldBeInEstrus()) return EstrusLevel.None; if (!ShouldBeInEstrus()) return EstrusLevel.None;
else return Props.concealedEstrus ? EstrusLevel.Concealed : EstrusLevel.Visible; else return estrusLevel;
} }
public void SetEstrus() public void SetEstrus()
{ {
Hediff hediff = HediffMaker.MakeHediff(Props.concealedEstrus ? VariousDefOf.Hediff_Estrus_Concealed : VariousDefOf.Hediff_Estrus, Pawn); if (estrusLevel == EstrusLevel.None) return;
Hediff hediff = HediffMaker.MakeHediff(estrusLevel == EstrusLevel.Concealed ? VariousDefOf.Hediff_Estrus_Concealed : VariousDefOf.Hediff_Estrus, Pawn);
Pawn.health.AddHediff(hediff); Pawn.health.AddHediff(hediff);
} }
@ -1256,8 +1297,19 @@ namespace RJW_Menstruation
if (Configurations.Debug) Log.Message($"Implanting fertilized egg of {Pawn} into {parent}, father {egg.fertilizer}"); if (Configurations.Debug) Log.Message($"Implanting fertilized egg of {Pawn} into {parent}, father {egg.fertilizer}");
if (pregnancy != null) if (pregnancy != null)
{ {
// TODO: Modified Biotech pregnancy if (Configurations.PregnancySource == Configurations.PregnancyType.Biotech && Configurations.EnableBiotechTwins && Configurations.EnableHeteroOvularTwins)
if (Configurations.PregnancySource == Configurations.PregnancyType.MultiplePregnancy && Configurations.EnableHeteroOvularTwins) {
if (Configurations.Debug) Log.Message($"Adding to existing Biotech pregnancy {pregnancy}");
HediffComp_PregeneratedBabies comp = pregnancy.TryGetComp<HediffComp_PregeneratedBabies>();
if (comp == null) Log.Warning($"Trying to add Biotech egg to {Pawn}'s pregnancy without a pregenerated baby comp: {pregnancy}");
else
{
comp.AddNewBaby(Pawn, egg.fertilizer);
pregnant = true;
deadeggs.Add(egg);
}
}
else if (Configurations.PregnancySource == Configurations.PregnancyType.MultiplePregnancy && Configurations.EnableHeteroOvularTwins)
{ {
if (pregnancy is Hediff_MultiplePregnancy h) if (pregnancy is Hediff_MultiplePregnancy h)
{ {
@ -1299,8 +1351,12 @@ namespace RJW_Menstruation
case Configurations.PregnancyType.Biotech: case Configurations.PregnancyType.Biotech:
if (Configurations.Debug) Log.Message($"Creating new biotech pregnancy"); if (Configurations.Debug) Log.Message($"Creating new biotech pregnancy");
pregnancy = HediffMaker.MakeHediff(HediffDefOf.PregnantHuman, Pawn); pregnancy = HediffMaker.MakeHediff(HediffDefOf.PregnantHuman, Pawn);
if(Configurations.EnableBiotechTwins)
pregnancy.TryGetComp<HediffComp_PregeneratedBabies>().AddNewBaby(Pawn, egg.fertilizer);
((Hediff_Pregnant)pregnancy).SetParents(Pawn, egg.fertilizer, PregnancyUtility.GetInheritedGeneSet(egg.fertilizer, Pawn)); ((Hediff_Pregnant)pregnancy).SetParents(Pawn, egg.fertilizer, PregnancyUtility.GetInheritedGeneSet(egg.fertilizer, Pawn));
Pawn.health.AddHediff(pregnancy); Pawn.health.AddHediff(pregnancy);
pregnant = true;
deadeggs.Add(egg);
break; break;
} }
if (pregnancy is Hediff_BasePregnancy rjw_preg) if (pregnancy is Hediff_BasePregnancy rjw_preg)
@ -1320,7 +1376,9 @@ namespace RJW_Menstruation
} }
} }
if (pregnant && (Configurations.PregnancySource != Configurations.PregnancyType.MultiplePregnancy || !Configurations.EnableHeteroOvularTwins)) if (pregnant &&
(Configurations.PregnancySource != Configurations.PregnancyType.MultiplePregnancy || !Configurations.EnableHeteroOvularTwins) &&
(Configurations.PregnancySource != Configurations.PregnancyType.Biotech || !Configurations.EnableBiotechTwins || !Configurations.EnableHeteroOvularTwins))
{ {
eggs.Clear(); eggs.Clear();
return true; return true;
@ -1454,25 +1512,27 @@ namespace RJW_Menstruation
protected virtual void OvulatoryAction() protected virtual void OvulatoryAction()
{ {
estrusflag = false; estrusflag = false;
int eggnum; float eggnum;
int ovulated;
try try
{ {
eggnum = Math.Max((int)Rand.ByCurve(Pawn.def.race.litterSizeCurve), 1); eggnum = Math.Max(Rand.ByCurve(Pawn.def.race.litterSizeCurve), 1f);
} }
catch (NullReferenceException) catch (NullReferenceException)
{ {
eggnum = 1; eggnum = 1f;
} }
catch (ArgumentException e) catch (ArgumentException e)
{ {
Log.Warning($"Invalid litterSizeCurve for {Pawn.def}: {e}"); Log.Warning($"Invalid litterSizeCurve for {Pawn.def}: {e}");
eggnum = 1; eggnum = 1f;
} }
eggnum += eggstack; eggnum *= ovulationFactor;
ovulated = (int)eggnum + eggstack;
for (int i = 0; i < eggnum; i++) for (int i = 0; i < ovulated; i++)
eggs.Add(new Egg((int)(Props.eggLifespanDays * 24 / CycleFactor))); eggs.Add(new Egg((int)(EggLifespanHours * 24 / CycleFactor)));
ovarypower -= eggnum; ovarypower -= ovulated;
eggstack = 0; eggstack = 0;
if (EggHealth <= 0) if (EggHealth <= 0)
@ -1709,7 +1769,7 @@ namespace RJW_Menstruation
protected void GoFollicularOrBleeding() protected void GoFollicularOrBleeding()
{ {
if (Props.bleedingIntervalDays == 0) if (Props.bleedingIntervalDays == 0 || noBleeding)
{ {
GoNextStage(Stage.Follicular); GoNextStage(Stage.Follicular);
} }
@ -1739,7 +1799,7 @@ namespace RJW_Menstruation
return (int)(Props.recoveryIntervalDays * 24 * Rand.Range(0.95f, 1.05f)); return (int)(Props.recoveryIntervalDays * 24 * Rand.Range(0.95f, 1.05f));
case Stage.Pregnant: case Stage.Pregnant:
return (int)MenstruationUtility.GestationHours(pregnancy); return (int)MenstruationUtility.GestationHours(pregnancy);
default: // Often unused default:
return 1; return 1;
} }
} }

View file

@ -33,7 +33,7 @@ namespace RJW_Menstruation
// Make the cutoff halfway into cycle, just to be sure there isn't a double-cycle the first time // Make the cutoff halfway into cycle, just to be sure there isn't a double-cycle the first time
if ((curStage == Stage.Follicular || curStage == Stage.Luteal || curStage == Stage.Bleeding) if ((curStage == Stage.Follicular || curStage == Stage.Luteal || curStage == Stage.Bleeding)
&& (averageCycleIntervalHours - hoursToNextCycle) / 2 >= 24 * (Props.follicularIntervalDays + Props.lutealIntervalDays) / cycleSpeed) && (averageCycleIntervalHours - hoursToNextCycle) / 2 >= 24 * (Props.follicularIntervalDays + Props.lutealIntervalDays) / cycleSpeed)
curStage = Stage.Anestrus; GoNextStage(Stage.Anestrus);
} }
} }
@ -75,14 +75,14 @@ namespace RJW_Menstruation
base.PregnantAction(); base.PregnantAction();
if (curStage != Stage.Pregnant) if (curStage != Stage.Pregnant)
// Go halfway into the cycle // Go halfway into the cycle
hoursToNextCycle = (int)(averageCycleIntervalHours * Rand.Range(-cycleVariability, cycleVariability)) / 2; hoursToNextCycle = (int)(averageCycleIntervalHours * (1 + Rand.Range(-cycleVariability, cycleVariability))) / 2;
} }
protected override void AnestrusAction() protected override void AnestrusAction()
{ {
if (hoursToNextCycle <= 0) if (hoursToNextCycle <= 0)
{ {
hoursToNextCycle = (int)(averageCycleIntervalHours * Rand.Range(-cycleVariability, cycleVariability)); hoursToNextCycle = (int)(averageCycleIntervalHours * (1 + Rand.Range(-cycleVariability, cycleVariability)));
if (IsBreedingSeason()) GoNextStage(Stage.Follicular); if (IsBreedingSeason()) GoNextStage(Stage.Follicular);
return; return;
} }

View file

@ -0,0 +1,300 @@
using HarmonyLib;
using Mono.Cecil.Cil;
using RimWorld;
using rjw;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using Verse;
namespace RJW_Menstruation
{
public class HediffComp_PregeneratedBabies : HediffComp
{
public List<Pawn> babies;
// Unused, but can't hurt to track
protected Dictionary<Pawn, Pawn> enzygoticSiblings;
protected static readonly MethodInfo RandomLastName = typeof(PregnancyUtility).GetMethod("RandomLastName", BindingFlags.Static | BindingFlags.NonPublic, null, new Type[] { typeof(Pawn), typeof(Pawn), typeof(Pawn) }, null);
public bool HasBaby
{
get => !babies.NullOrEmpty();
}
public Pawn PopBaby()
{
if (babies.NullOrEmpty()) return null;
Pawn firstBaby = babies.First();
babies.Remove(firstBaby);
return firstBaby;
}
public override void CompPostPostRemoved()
{
// At this point in the hediff removal process, the new hediff is already on the pawn
// But it is possible that there is no new hediff (be it a birth, miscarrage, or dev edit)
base.CompPostPostRemoved();
// Send the babies from this comp to the new one
switch (parent)
{
case Hediff_Pregnant hediff_Pregnant:
Hediff_Labor labor = (Hediff_Labor)Pawn.health.hediffSet.hediffs.Where(hediff => hediff is Hediff_Labor).MaxByWithFallback(hediff => hediff.loadID);
HediffComp_PregeneratedBabies laborcomp = labor?.TryGetComp<HediffComp_PregeneratedBabies>();
if (laborcomp == null) return;
laborcomp.babies = this.babies;
laborcomp.enzygoticSiblings = this.enzygoticSiblings;
break;
case Hediff_Labor hediff_Labor:
Hediff_LaborPushing pushing = (Hediff_LaborPushing)Pawn.health.hediffSet.hediffs.Where(hediff => hediff is Hediff_LaborPushing).MaxByWithFallback(hediff => hediff.loadID);
HediffComp_PregeneratedBabies pushingcomp = pushing?.TryGetComp<HediffComp_PregeneratedBabies>();
if (pushingcomp == null) return;
pushingcomp.babies = this.babies;
pushingcomp.enzygoticSiblings = this.enzygoticSiblings;
break;
case Hediff_LaborPushing hediff_LaborPushing:
// Nothing to do, the laborpushing transpiler will pick it up
break;
}
}
public override void CompExposeData()
{
base.CompExposeData();
Scribe_Collections.Look(ref babies, "babies", LookMode.Deep);
Scribe_Collections.Look(ref enzygoticSiblings, "enzygoticSiblings", keyLookMode: LookMode.Reference, valueLookMode: LookMode.Reference);
}
public void AddNewBaby(Pawn mother, Pawn father)
{
if (babies == null) babies = new List<Pawn>();
PawnKindDef babyPawnKind = PregnancyCommon.BabyPawnKindDecider(mother, father, true);
PawnGenerationRequest request = new PawnGenerationRequest
(
kind: babyPawnKind,
faction: mother.Faction,
allowDowned: true,
fixedLastName: (string)RandomLastName.Invoke(null, new object[] { mother, mother, xxx.is_human(father) ? father : null }),
forceNoIdeo: true,
forcedEndogenes: PregnancyUtility.GetInheritedGenes(father, mother),
forcedXenotype: XenotypeDefOf.Baseliner,
developmentalStages: DevelopmentalStage.Newborn
);
int division = 1;
Pawn firstbaby = null;
while (Rand.Chance(Configurations.EnzygoticTwinsChance) && division < Configurations.MaxEnzygoticTwins) division++;
if (division > 1 && enzygoticSiblings == null) enzygoticSiblings = new Dictionary<Pawn, Pawn>();
for (int i = 0; i < division; i++)
{
Pawn baby = PawnGenerator.GeneratePawn(request);
if (baby == null) break;
PregnancyCommon.SetupBabyXenotype(mother, father, baby); // Probably redundant with Biotech post-birth xenotyping
baby.Drawer.renderer.graphics.ResolveAllGraphics();
if (division > 1)
{
if (i == 0)
{
firstbaby = baby;
request.FixedGender = baby.gender;
request.ForcedEndogenes = baby.genes?.Endogenes.Select(gene => gene.def).ToList();
}
else
{
enzygoticSiblings.Add(baby, firstbaby);
if (baby.story != null)
{
baby.story.headType = firstbaby.story.headType;
baby.story.hairDef = firstbaby.story.hairDef;
baby.story.HairColor = firstbaby.story.HairColor;
baby.story.bodyType = firstbaby.story.bodyType;
baby.story.furDef = firstbaby.story.furDef;
baby.story.skinColorOverride = firstbaby.story.skinColorOverride;
}
if (baby.genes != null)
{
baby.genes.SetXenotypeDirect(firstbaby.genes.Xenotype);
baby.genes.xenotypeName = firstbaby.genes.xenotypeName;
baby.genes.iconDef = firstbaby.genes.iconDef;
baby.genes.hybrid = firstbaby.genes.hybrid;
}
if (baby.IsHAR())
HARCompatibility.CopyHARProperties(baby, firstbaby);
// MultiplePregnancy calls this post-birth because RJW resets private parts
// So xenotype things shouldn't be shared
PregnancyCommon.ProcessIdenticalSibling(baby, firstbaby);
}
}
babies.Add(baby);
// These get cleared out later, but setting the relations now will help keep track of things.
baby.SetMother(mother);
if (mother != father)
{
if (father.gender != Gender.Female) baby.SetFather(father);
else baby.relations.AddDirectRelation(PawnRelationDefOf.Parent, father);
}
}
}
}
[HarmonyPatch(typeof(PregnancyUtility), nameof(PregnancyUtility.ApplyBirthOutcome))]
public static class ApplyBirthOutcome_PregeneratedBabies_Patch
{
private static Pawn GetPregeneratedBaby(PawnGenerationRequest request, Thing birtherThing)
{
// Don't test for the config set here. We can do it at the functions that call ApplyBirthOutcome
// Easier to work out twins that way
// From e.g. a vat
if (!(birtherThing is Pawn mother) || !xxx.is_human(mother))
return PawnGenerator.GeneratePawn(request);
// No babies found. Could be an unmodified pregnancy
HediffComp_PregeneratedBabies comp = mother.health.hediffSet.GetFirstHediff<Hediff_LaborPushing>()?.TryGetComp<HediffComp_PregeneratedBabies>();
if (comp == null || !comp.HasBaby)
return PawnGenerator.GeneratePawn(request);
Pawn baby = comp.PopBaby();
if (baby == null) return PawnGenerator.GeneratePawn(request); // Shouldn't happen
baby.ageTracker.AgeBiologicalTicks = 0;
baby.ageTracker.AgeChronologicalTicks = 0;
baby.babyNamingDeadline = Find.TickManager.TicksGame + GenDate.TicksPerDay;
if (request.ForceDead) baby.Kill(null, null);
return baby;
}
private static readonly MethodInfo ApplyBirthOutcome = typeof(PregnancyUtility).GetMethod(nameof(PregnancyUtility.ApplyBirthOutcome));
private static readonly int birtherThing = ApplyBirthOutcome.GetParameters().FirstIndexOf(parameter => parameter.Name == "birtherThing" && parameter.ParameterType == typeof(Thing));
private static readonly MethodInfo GeneratePawn = typeof(PawnGenerator).GetMethod(nameof(PawnGenerator.GeneratePawn), new Type[] {typeof (PawnGenerationRequest)});
public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> 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");
foreach (CodeInstruction instruction in instructions)
{
if (instruction.Calls(GeneratePawn))
{
yield return new CodeInstruction(OpCodes.Ldarg, birtherThing);
yield return CodeInstruction.Call(typeof(ApplyBirthOutcome_PregeneratedBabies_Patch), nameof(GetPregeneratedBaby));
}
else yield return instruction;
}
}
}
[HarmonyPatch(typeof(Hediff_LaborPushing), nameof(Hediff_LaborPushing.PreRemoved))]
public static class Hediff_LaborPushing_PreRemoved_Patch
{
private static Thing ApplyBirthLoop(OutcomeChance outcome, float quality, Precept_Ritual ritual, List<GeneDef> genes, Pawn geneticMother, Thing birtherThing, Pawn father, Pawn doctor, LordJob_Ritual lordJobRitual, RitualRoleAssignments assignments)
{
if (birtherThing is Pawn mother)
{
HediffComp_PregeneratedBabies comp = mother.health.hediffSet.GetFirstHediff<Hediff_LaborPushing>().TryGetComp<HediffComp_PregeneratedBabies>();
if (comp?.HasBaby ?? false)
{
OutcomeChance thisOutcome = outcome;
Precept_Ritual precept_Ritual = (Precept_Ritual)comp.Pawn.Ideo.GetPrecept(PreceptDefOf.ChildBirth);
float birthQuality = PregnancyUtility.GetBirthQualityFor(mother);
do
{
Pawn baby = comp.babies[0];
Pawn thisFather = baby.GetFather();
if (thisFather == null) thisFather = father;
baby.relations.ClearAllRelations(); // To keep ApplyBirthOutcome from erroring when it tries to set up relations
PregnancyUtility.ApplyBirthOutcome(thisOutcome, quality, ritual, genes, geneticMother, birtherThing, thisFather, doctor, lordJobRitual, assignments);
// No more babies if mom dies halfway through. Unrealistic maybe, but saves a lot of headache in ApplyBirthOutcome
if (mother.health.Dead) break;
thisOutcome = ((RitualOutcomeEffectWorker_ChildBirth)precept_Ritual.outcomeEffect).GetOutcome(birthQuality, null);
} while (comp.HasBaby);
// PreRemoved doesn't use the return value
return null;
}
}
return PregnancyUtility.ApplyBirthOutcome(outcome, quality, ritual, genes, geneticMother, birtherThing, father, doctor, lordJobRitual, assignments);
}
private static readonly MethodInfo ApplyBirthOutcome = typeof(PregnancyUtility).GetMethod(nameof(PregnancyUtility.ApplyBirthOutcome),
new Type[] {typeof(OutcomeChance), typeof(float), typeof(Precept_Ritual), typeof(List<GeneDef>), typeof(Pawn), typeof(Thing), typeof(Pawn), typeof(Pawn), typeof(LordJob_Ritual), typeof(RitualRoleAssignments)});
public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{
if (ApplyBirthOutcome == null || ApplyBirthOutcome.ReturnType != typeof(Thing)) throw new InvalidOperationException("ApplyBirthOutcome not found");
foreach (CodeInstruction instruction in instructions)
{
if (instruction.Calls(ApplyBirthOutcome))
yield return CodeInstruction.Call(typeof(Hediff_LaborPushing_PreRemoved_Patch), nameof(Hediff_LaborPushing_PreRemoved_Patch.ApplyBirthLoop));
else yield return instruction;
}
}
}
[HarmonyPatch(typeof(RitualOutcomeEffectWorker_ChildBirth), nameof (RitualOutcomeEffectWorker_ChildBirth.Apply))]
public static class Ritual_ChildBirth_Apply_Patch
{
private static Thing ApplyBirthLoop(OutcomeChance outcome, float quality, Precept_Ritual ritual, List<GeneDef> genes, Pawn geneticMother, Thing birtherThing, Pawn father, Pawn doctor, LordJob_Ritual lordJobRitual, RitualRoleAssignments assignments)
{
if (birtherThing is Pawn mother)
{
HediffComp_PregeneratedBabies comp = mother.health.hediffSet.GetFirstHediff<Hediff_LaborPushing>().TryGetComp<HediffComp_PregeneratedBabies>();
if (comp?.HasBaby ?? false)
{
// Much the same as the other one
// Don't reroll the outcome every time, I think
// This is all one ritual, so every baby has the same ritual outcome
// I don't think this will add the ritual memory every time?
// Though even if it does, that's probably okay. More babies more memories after all
do
{
Pawn baby = comp.babies[0];
Pawn thisFather = baby.GetFather();
if (thisFather == null) thisFather = father;
baby.relations.ClearAllRelations();
PregnancyUtility.ApplyBirthOutcome(outcome, quality, ritual, genes, geneticMother, birtherThing, thisFather, doctor, lordJobRitual, assignments);
if (mother.health.Dead) break;
} while (comp.HasBaby);
// The ritual version doesn't use the return value, either
return null;
}
}
return PregnancyUtility.ApplyBirthOutcome(outcome, quality, ritual, genes, geneticMother, birtherThing, father, doctor, lordJobRitual, assignments);
}
private static readonly MethodInfo ApplyBirthOutcome = typeof(PregnancyUtility).GetMethod(nameof(PregnancyUtility.ApplyBirthOutcome),
new Type[] { typeof(OutcomeChance), typeof(float), typeof(Precept_Ritual), typeof(List<GeneDef>), typeof(Pawn), typeof(Thing), typeof(Pawn), typeof(Pawn), typeof(LordJob_Ritual), typeof(RitualRoleAssignments) });
public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{
if (ApplyBirthOutcome == null || ApplyBirthOutcome.ReturnType != typeof(Thing)) throw new InvalidOperationException("ApplyBirthOutcome not found");
foreach (var instruction in instructions)
{
if (instruction.Calls(ApplyBirthOutcome))
yield return CodeInstruction.Call(typeof(Ritual_ChildBirth_Apply_Patch), nameof(Ritual_ChildBirth_Apply_Patch.ApplyBirthLoop));
else yield return instruction;
}
}
}
// HAR patches ApplyBirthOutcome to produce multiple babies based on the mother's littersize. But the pregenerated babies system already makes multiple babies
// So make it always consider the mother to have one baby
public static class HAR_LitterSize_Undo
{
public static void Postfix(ref int __result, Pawn mother)
{
if (Configurations.PregnancySource != Configurations.PregnancyType.Biotech || !Configurations.EnableBiotechTwins) return;
__result = 0;
return;
}
}
}

View file

@ -73,6 +73,29 @@ namespace RJW_Menstruation
return null; return null;
} }
[Obsolete("This method is obsolete and can cause ambiguity. Use GetMenstruationCompFromVagina or GetMenstruationCompFromPregnancy instead.", true)]
public static HediffComp_Menstruation GetMenstruationComp(Hediff hediff)
{
switch (hediff)
{
case Hediff_BasePregnancy rjwPreg:
return rjwPreg.GetMenstruationCompFromPregnancy();
case Hediff_Pregnant vanillaPreg:
return vanillaPreg.GetMenstruationCompFromPregnancy();
case Hediff_Labor vanillaLabor:
return vanillaLabor.GetMenstruationCompFromPregnancy();
case Hediff_LaborPushing vanillaLaborPushing:
return vanillaLaborPushing.GetMenstruationCompFromPregnancy();
case Hediff_PartBaseNatural rjwNatrual:
return rjwNatrual.GetMenstruationCompFromVagina();
case Hediff_PartBaseArtifical rjwArtificial:
return rjwArtificial.GetMenstruationCompFromVagina();
default:
Log.Warning("Obsolete GetMenstruationComp called with unknown hediff. Ensure your submods are up to date.");
return null;
}
}
public static HediffComp_Anus GetAnusComp(this Hediff hediff) public static HediffComp_Anus GetAnusComp(this Hediff hediff)
{ {
if (hediff is Hediff_PartBaseNatural || hediff is Hediff_PartBaseArtifical) if (hediff is Hediff_PartBaseNatural || hediff is Hediff_PartBaseArtifical)
@ -92,13 +115,25 @@ namespace RJW_Menstruation
if (hediff is Hediff_MechanoidPregnancy) if (hediff is Hediff_MechanoidPregnancy)
return ContentFinder<Texture2D>.Get(("Womb/Mechanoid_Fluid"), true); return ContentFinder<Texture2D>.Get(("Womb/Mechanoid_Fluid"), true);
ThingDef babydef = comp.Pawn.def; // TODO: Pregenerated babies
float gestationProgress = comp.StageProgress; float gestationProgress = comp.StageProgress;
int babycount = hediff is Hediff_BasePregnancy preg ? preg.babies.Count : 1;
if (hediff is Hediff_BasePregnancy h) ThingDef babydef;
int babycount;
HediffComp_PregeneratedBabies babiescomp = hediff?.TryGetComp<HediffComp_PregeneratedBabies>();
if (hediff is Hediff_BasePregnancy preg)
{ {
babydef = h.babies?.FirstOrDefault()?.def ?? ThingDefOf.Human; babydef = preg.babies?.FirstOrDefault()?.def ?? ThingDefOf.Human;
babycount = preg.babies?.Count ?? 1;
}
else if (babiescomp?.HasBaby ?? false)
{
babydef = babiescomp.babies.First().def;
babycount = babiescomp.babies.Count;
}
else
{
babydef = comp.Pawn.def;
babycount = 1;
} }
string fetustex = babydef.GetModExtension<PawnDNAModExtension>()?.fetusTexPath ?? "Fetus/Fetus_Default"; string fetustex = babydef.GetModExtension<PawnDNAModExtension>()?.fetusTexPath ?? "Fetus/Fetus_Default";

View file

@ -13,9 +13,6 @@ namespace RJW_Menstruation
{ {
protected Dictionary<Pawn, Pawn> enzygoticSiblings = new Dictionary<Pawn, Pawn>(); // Each pawn and who they split from protected Dictionary<Pawn, Pawn> enzygoticSiblings = new Dictionary<Pawn, Pawn>(); // Each pawn and who they split from
protected readonly MethodInfo TryGetInheritedXenotype = typeof(PregnancyUtility).GetMethod("TryGetInheritedXenotype", BindingFlags.Static | BindingFlags.NonPublic, null, new Type[] { typeof(Pawn), typeof(Pawn), typeof(XenotypeDef).MakeByRefType() }, null );
protected readonly MethodInfo ShouldByHybrid = typeof(PregnancyUtility).GetMethod("ShouldByHybrid", BindingFlags.Static | BindingFlags.NonPublic, null, new Type[] { typeof(Pawn), typeof(Pawn) }, null);
public override void DiscoverPregnancy() public override void DiscoverPregnancy()
{ {
PregnancyThought(); PregnancyThought();
@ -82,48 +79,6 @@ namespace RJW_Menstruation
breastcomp?.GaveBirth(); breastcomp?.GaveBirth();
} }
public string GetBabyInfo()
{
if (babies.NullOrEmpty())
return "Null";
StringBuilder res = new StringBuilder();
IEnumerable<Pawn> babiesdistinct = babies.Distinct(new RaceComparer());
int iteration = 0;
foreach (Pawn baby in babiesdistinct)
{
int num = babies.Where(x => x.def.Equals(baby.def)).Count();
if (iteration > 0) res.Append(", ");
res.AppendFormat("{0} {1}", num, baby.def.label);
iteration++;
}
res.AppendFormat(" {0}", Translations.Dialog_WombInfo02);
return res.ToString();
}
public string GetFatherInfo()
{
if (babies.NullOrEmpty())
return "Null";
StringBuilder res = new StringBuilder();
res.AppendFormat("{0}: ", Translations.Dialog_WombInfo03);
if (!is_parent_known && Configurations.InfoDetail != Configurations.DetailLevel.All)
return res.Append(Translations.Dialog_FatherUnknown).ToString();
IEnumerable<Pawn> babiesdistinct = babies.Distinct(new FatherComparer(pawn));
int iteration = 0;
foreach (Pawn baby in babiesdistinct)
{
if (iteration > 0) res.Append(", ");
res.Append(Utility.GetFather(baby, pawn)?.LabelShort ?? Translations.Dialog_FatherUnknown);
iteration++;
}
return res.ToString();
}
private void HumanlikeBirth(Pawn baby) private void HumanlikeBirth(Pawn baby)
{ {
Pawn mother = pawn; Pawn father = Utility.GetFather(baby, pawn); Pawn mother = pawn; Pawn father = Utility.GetFather(baby, pawn);
@ -167,11 +122,12 @@ namespace RJW_Menstruation
if (ModsConfig.BiotechActive) if (ModsConfig.BiotechActive)
{ {
// Ugly, but it'll have to do // Ugly, but it'll have to do
OutcomeChance bestOutcome = RitualOutcomeEffectDefOf.ChildBirth.outcomeChances.Find(chance => chance.positivityIndex == 1); OutcomeChance bestOutcome = RitualOutcomeEffectDefOf.ChildBirth.BestOutcome;
string label = bestOutcome.label; string label = bestOutcome.label;
string description = bestOutcome.description.Formatted(mother.Named("MOTHER")); string description = bestOutcome.description.Formatted(mother.Named("MOTHER"));
baby.babyNamingDeadline = Find.TickManager.TicksGame + GenDate.TicksPerDay;
ChoiceLetter_BabyBirth choiceLetter_BabyBirth = (ChoiceLetter_BabyBirth)LetterMaker.MakeLetter( ChoiceLetter_BabyBirth choiceLetter_BabyBirth = (ChoiceLetter_BabyBirth)LetterMaker.MakeLetter(
label, description, LetterDefOf.BabyBirth, baby label, description, LetterDefOf.BabyBirth, baby
); );
@ -212,65 +168,12 @@ namespace RJW_Menstruation
//baby.story.birthLastName = last_name; //baby.story.birthLastName = last_name;
} }
protected void CopyBodyPartProperties(Hediff part, Hediff originalPart)
{
CompHediffBodyPart comp = part.TryGetComp<CompHediffBodyPart>();
CompHediffBodyPart originalComp = originalPart.TryGetComp<CompHediffBodyPart>();
if (comp != null && originalComp != null)
{
// the string properties should be the same between both pawns anyways, besides the name of the owner
part.Severity = originalPart.Severity;
comp.SizeBase = originalComp.SizeBase;
comp.SizeOwner = originalComp.SizeOwner;
comp.EffSize = originalComp.EffSize;
comp.FluidAmmount = originalComp.FluidAmmount;
comp.FluidModifier = originalComp.FluidModifier;
}
HediffComp_Menstruation originalMenstruationComp = originalPart.GetMenstruationCompFromVagina();
if (originalMenstruationComp != null)
{
part.GetMenstruationCompFromVagina()?.CopyCycleProperties(originalMenstruationComp);
}
HediffComp_Breast originalBreastComp = originalPart.GetBreastComp();
if (originalBreastComp != null)
{
part.GetBreastComp()?.CopyBreastProperties(originalBreastComp);
}
}
protected void CopyBodyPartRecord(Pawn baby, Pawn original, BodyPartRecord babyBPR, BodyPartRecord originalBPR)
{
if (babyBPR == null || originalBPR == null) return;
RemoveBabyParts(baby, Genital_Helper.get_PartsHediffList(baby, babyBPR));
foreach (Hediff originalPart in Genital_Helper.get_PartsHediffList(original, originalBPR))
{
Hediff part = SexPartAdder.MakePart(originalPart.def, baby, babyBPR);
CopyBodyPartProperties(part, originalPart);
baby.health.AddHediff(part, babyBPR);
}
}
// Baby is the sibling to be changed, original is the first of the set and the one to copy to the rest.
public virtual void ProcessIdenticalSibling(Pawn baby, Pawn original)
{
// They'll be the same pawnkind, which lets us make a lot of useful assumptions
// However, some RNG might still be involved in genital generation (e.g. futas), so the easiest method is to clear out and re-generate
// A bit wasteful since Hediff_BasePregnancy.PostBirth already redid the genitals
CopyBodyPartRecord(baby, original, Genital_Helper.get_genitalsBPR(baby), Genital_Helper.get_genitalsBPR(original));
CopyBodyPartRecord(baby, original, Genital_Helper.get_breastsBPR(baby), Genital_Helper.get_breastsBPR(original));
CopyBodyPartRecord(baby, original, Genital_Helper.get_uddersBPR(baby), Genital_Helper.get_uddersBPR(original));
CopyBodyPartRecord(baby, original, Genital_Helper.get_anusBPR(baby), Genital_Helper.get_anusBPR(original));
}
public override void PostBirth(Pawn mother, Pawn father, Pawn baby) public override void PostBirth(Pawn mother, Pawn father, Pawn baby)
{ {
base.PostBirth(mother, father, baby); base.PostBirth(mother, father, baby);
// Has to happen on birth since RJW redoes the genitals at birth // Has to happen on birth since RJW redoes the genitals at birth
if (!enzygoticSiblings.NullOrEmpty() && enzygoticSiblings.TryGetValue(baby, out Pawn original) && baby != original) if (!enzygoticSiblings.NullOrEmpty() && enzygoticSiblings.TryGetValue(baby, out Pawn original) && baby != original)
ProcessIdenticalSibling(baby, original); PregnancyCommon.ProcessIdenticalSibling(baby, original);
} }
// From RJW's trait code // From RJW's trait code
@ -411,7 +314,7 @@ namespace RJW_Menstruation
allowAddictions: false, allowAddictions: false,
relationWithExtraPawnChanceFactor: 0, relationWithExtraPawnChanceFactor: 0,
fixedLastName: lastname, fixedLastName: lastname,
kind: BabyPawnKindDecider(mother, father), kind: PregnancyCommon.BabyPawnKindDecider(mother, father, false),
//fixedIdeo: mother.Ideo, //fixedIdeo: mother.Ideo,
forbidAnyTitle: true, forbidAnyTitle: true,
forceNoBackstory: true, forceNoBackstory: true,
@ -428,31 +331,15 @@ namespace RJW_Menstruation
{ {
Pawn baby = GenerateBaby(request, mother, father, parentTraits, traitSeed); Pawn baby = GenerateBaby(request, mother, father, parentTraits, traitSeed);
if (baby == null) break; if (baby == null) break;
if (baby.genes != null && ModsConfig.BiotechActive) PregnancyCommon.SetupBabyXenotype(mother, father, baby);
{ // HAR and some xenotype mods don't randomize graphics until it's rendered
if (GeneUtility.SameHeritableXenotype(mother, father) && mother.genes.UniqueXenotype) // So poke it early
{ baby.Drawer.renderer.graphics.ResolveAllGraphics();
baby.genes.xenotypeName = mother.genes.xenotypeName;
baby.genes.iconDef = mother.genes.iconDef;
}
object[] args = new object[] { mother, father, null };
if ((bool)TryGetInheritedXenotype.Invoke(null, args))
{
baby.genes.SetXenotypeDirect((XenotypeDef)args[2]);
}
else if((bool)ShouldByHybrid.Invoke(null, new object[] { mother, father }))
{
baby.genes.hybrid = true;
baby.genes.xenotypeName = "Hybrid".Translate();
}
}
if (division > 1) if (division > 1)
{ {
if (i == 0) if (i == 0)
{ {
if (baby.IsHAR()) // Have HAR determine the first baby's properties
baby.Drawer.renderer.graphics.ResolveAllGraphics();
firstbaby = baby; firstbaby = baby;
request.FixedGender = baby.gender; request.FixedGender = baby.gender;
request.ForcedEndogenes = baby.genes?.Endogenes.Select(gene => gene.def).ToList(); request.ForcedEndogenes = baby.genes?.Endogenes.Select(gene => gene.def).ToList();
@ -465,7 +352,10 @@ namespace RJW_Menstruation
{ {
baby.story.headType = firstbaby.story.headType; baby.story.headType = firstbaby.story.headType;
baby.story.hairDef = firstbaby.story.hairDef; baby.story.hairDef = firstbaby.story.hairDef;
baby.story.HairColor = firstbaby.story.HairColor;
baby.story.bodyType = firstbaby.story.bodyType; baby.story.bodyType = firstbaby.story.bodyType;
baby.story.furDef = firstbaby.story.furDef;
baby.story.skinColorOverride = firstbaby.story.skinColorOverride;
} }
if (baby.genes != null && ModsConfig.BiotechActive) if (baby.genes != null && ModsConfig.BiotechActive)
@ -519,174 +409,6 @@ namespace RJW_Menstruation
return baby; return baby;
} }
/// <summary>
/// Decide pawnkind from mother and father <para/>
/// Come from RJW
/// </summary>
/// <param name="mother"></param>
/// <param name="father"></param>
/// <returns></returns>
public PawnKindDef BabyPawnKindDecider(Pawn mother, Pawn father)
{
PawnKindDef motherKindDef = Utility.GetRacesPawnKind(mother);
PawnKindDef fatherKindDef = Utility.GetRacesPawnKind(father);
PawnKindDef spawn_kind_def = motherKindDef;
int flag = 0;
if (xxx.is_human(mother)) flag += 2;
if (xxx.is_human(father)) flag += 1;
//Mother - Father = Flag
//Human - Human = 3
//Human - Animal = 2
//Animal - Human = 1
//Animal - Animal = 0
switch (flag)
{
case 3:
if (!Rand.Chance(RJWPregnancySettings.humanlike_DNA_from_mother)) spawn_kind_def = fatherKindDef;
break;
case 2:
if (RJWPregnancySettings.bestiality_DNA_inheritance == 0f) spawn_kind_def = fatherKindDef;
else if (!Rand.Chance(RJWPregnancySettings.bestial_DNA_from_mother)) spawn_kind_def = fatherKindDef;
break;
case 1:
if (RJWPregnancySettings.bestiality_DNA_inheritance == 1f) spawn_kind_def = fatherKindDef;
else if (!Rand.Chance(RJWPregnancySettings.bestial_DNA_from_mother)) spawn_kind_def = fatherKindDef;
break;
case 0:
if (!Rand.Chance(RJWPregnancySettings.bestial_DNA_from_mother)) spawn_kind_def = fatherKindDef;
break;
}
bool IsAndroidmother = AndroidsCompatibility.IsAndroid(mother);
bool IsAndroidfather = AndroidsCompatibility.IsAndroid(father);
if (IsAndroidmother && !IsAndroidfather)
{
spawn_kind_def = fatherKindDef;
}
else if (!IsAndroidmother && IsAndroidfather)
{
spawn_kind_def = motherKindDef;
}
string MotherRaceName = "";
string FatherRaceName = "";
MotherRaceName = motherKindDef?.race?.defName;
PawnKindDef non_hybrid_kind_def = spawn_kind_def;
if (father != null)
FatherRaceName = fatherKindDef?.race?.defName;
if (FatherRaceName != "" && Configurations.UseHybridExtention)
{
spawn_kind_def = GetHybrid(father, mother);
//Log.Message("pawnkind: " + spawn_kind_def?.defName);
}
if (MotherRaceName != FatherRaceName && FatherRaceName != "")
{
if (!Configurations.UseHybridExtention || spawn_kind_def == null)
{
spawn_kind_def = non_hybrid_kind_def;
IEnumerable<RaceGroupDef> groups = DefDatabase<RaceGroupDef>.AllDefs.Where(x => !(x.hybridRaceParents.NullOrEmpty() || x.hybridChildKindDef.NullOrEmpty()));
//ModLog.Message(" found custom RaceGroupDefs " + groups.Count());
foreach (RaceGroupDef t in groups)
{
if ((t.hybridRaceParents.Contains(MotherRaceName) && t.hybridRaceParents.Contains(FatherRaceName))
|| (t.hybridRaceParents.Contains("Any") && (t.hybridRaceParents.Contains(MotherRaceName) || t.hybridRaceParents.Contains(FatherRaceName))))
{
//ModLog.Message(" has hybridRaceParents");
if (t.hybridChildKindDef.Contains("MotherKindDef"))
spawn_kind_def = motherKindDef;
else if (t.hybridChildKindDef.Contains("FatherKindDef") && father != null)
spawn_kind_def = fatherKindDef;
else
{
//ModLog.Message(" trying hybridChildKindDef " + t.defName);
List<PawnKindDef> child_kind_def_list = new List<PawnKindDef>();
child_kind_def_list.AddRange(DefDatabase<PawnKindDef>.AllDefs.Where(x => t.hybridChildKindDef.Contains(x.defName)));
//ModLog.Message(" found custom hybridChildKindDefs " + t.hybridChildKindDef.Count);
if (!child_kind_def_list.NullOrEmpty())
spawn_kind_def = child_kind_def_list.RandomElement();
}
}
}
}
}
else if (!Configurations.UseHybridExtention || spawn_kind_def == null)
{
spawn_kind_def = mother.RaceProps?.AnyPawnKind ?? motherKindDef;
}
if (spawn_kind_def.defName.Contains("Nymph"))
{
//child is nymph, try to find other PawnKindDef
List<PawnKindDef> spawn_kind_def_list = new List<PawnKindDef>();
spawn_kind_def_list.AddRange(DefDatabase<PawnKindDef>.AllDefs.Where(x => x.race == spawn_kind_def.race && !x.defName.Contains("Nymph")));
//no other PawnKindDef found try mother
if (spawn_kind_def_list.NullOrEmpty())
spawn_kind_def_list.AddRange(DefDatabase<PawnKindDef>.AllDefs.Where(x => x.race == motherKindDef.race && !x.defName.Contains("Nymph")));
//no other PawnKindDef found try father
if (spawn_kind_def_list.NullOrEmpty() && father != null)
spawn_kind_def_list.AddRange(DefDatabase<PawnKindDef>.AllDefs.Where(x => x.race == fatherKindDef.race && !x.defName.Contains("Nymph")));
//no other PawnKindDef found fallback to generic colonist
if (spawn_kind_def_list.NullOrEmpty())
spawn_kind_def = PawnKindDefOf.Colonist;
if (!spawn_kind_def_list.NullOrEmpty()) spawn_kind_def = spawn_kind_def_list.RandomElement();
}
return spawn_kind_def;
}
public PawnKindDef GetHybrid(Pawn first, Pawn second)
{
PawnKindDef res = null;
Pawn opposite = second;
HybridInformations info = null;
if (!Configurations.HybridOverride.NullOrEmpty())
{
info = Configurations.HybridOverride.FirstOrDefault(x => x.DefName == first.def?.defName && (x.hybridExtension?.Exists(y => y.DefName == second.def?.defName) ?? false));
if (info == null)
{
info = Configurations.HybridOverride.FirstOrDefault(x => x.DefName == second.def?.defName && (x.hybridExtension?.Exists(y => y.DefName == first.def?.defName) ?? false));
opposite = first;
}
}
if (info != null)
{
res = info.GetHybridWith(opposite.def.defName) ?? null;
}
if (res != null) return res;
PawnDNAModExtension dna;
dna = first.def.GetModExtension<PawnDNAModExtension>();
if (dna != null)
{
res = dna.GetHybridWith(second.def.defName) ?? null;
}
else
{
dna = second.def.GetModExtension<PawnDNAModExtension>();
if (dna != null)
{
res = dna.GetHybridWith(first.def.defName) ?? null;
}
}
return res;
}
/// <summary> /// <summary>
/// Copy from RJW /// Copy from RJW
/// </summary> /// </summary>
@ -746,12 +468,6 @@ namespace RJW_Menstruation
pawn.story.traits.allTraits = selectedTraits; pawn.story.traits.allTraits = selectedTraits;
} }
public string DueDate()
{
if (pawn.Tile == -1) return "";
return GenDate.DateFullStringWithHourAt(GenDate.TickGameToAbs((int)p_end_tick), Find.WorldGrid.LongLatOf(pawn.Tile));
}
public override bool TryMergeWith(Hediff other) public override bool TryMergeWith(Hediff other)
{ {
return false; return false;

View file

@ -4,6 +4,8 @@ using RimWorld;
using Verse; using Verse;
using System.Collections.Generic; using System.Collections.Generic;
using System.Reflection; using System.Reflection;
using System;
using System.Reflection.Emit;
namespace RJW_Menstruation namespace RJW_Menstruation
{ {
@ -24,8 +26,10 @@ namespace RJW_Menstruation
public static void Postfix(Hediff_Pregnant __instance) public static void Postfix(Hediff_Pregnant __instance)
{ {
HediffComp_Menstruation comp = __instance.GetMenstruationCompFromPregnancy(); HediffComp_Menstruation comp = __instance.GetMenstruationCompFromPregnancy();
if (Configurations.Debug) Log.Message($"{comp.Pawn}'s labor starting, menstruation comp is {comp}");
if (comp == null) return; if (comp == null) return;
comp.Pregnancy = __instance.pawn.health.hediffSet.GetFirstHediffOfDef(HediffDefOf.PregnancyLabor); comp.Pregnancy = __instance.pawn.health.hediffSet.GetFirstHediffOfDef(HediffDefOf.PregnancyLabor);
if (Configurations.Debug) Log.Message($"New pregnancy Hediff is {comp.Pregnancy}");
} }
} }
@ -35,8 +39,10 @@ namespace RJW_Menstruation
public static void PostFix(Hediff_Labor __instance) public static void PostFix(Hediff_Labor __instance)
{ {
HediffComp_Menstruation comp = __instance.GetMenstruationCompFromPregnancy(); HediffComp_Menstruation comp = __instance.GetMenstruationCompFromPregnancy();
if (Configurations.Debug) Log.Message($"{comp.Pawn}'s initial labor ending, menstruation comp is {comp}");
if (comp == null) return; if (comp == null) return;
comp.Pregnancy = __instance.pawn.health.hediffSet.GetFirstHediffOfDef(HediffDefOf.PregnancyLaborPushing); comp.Pregnancy = __instance.pawn.health.hediffSet.GetFirstHediffOfDef(HediffDefOf.PregnancyLaborPushing);
if (Configurations.Debug) Log.Message($"New pregnancy Hediff is {comp.Pregnancy}");
} }
} }
@ -46,6 +52,7 @@ namespace RJW_Menstruation
public static void PostFix(Hediff_LaborPushing __instance) public static void PostFix(Hediff_LaborPushing __instance)
{ {
HediffComp_Menstruation comp = __instance.GetMenstruationCompFromPregnancy(); HediffComp_Menstruation comp = __instance.GetMenstruationCompFromPregnancy();
if (Configurations.Debug) Log.Message($"{comp.Pawn}'s labor pushing ending, menstruation comp is {comp}");
if (comp == null) return; if (comp == null) return;
comp.Pregnancy = null; comp.Pregnancy = null;
} }
@ -111,7 +118,7 @@ namespace RJW_Menstruation
} }
[HarmonyPatch(typeof(PregnancyUtility), nameof(PregnancyUtility.ApplyBirthOutcome))] [HarmonyPatch(typeof(PregnancyUtility), nameof(PregnancyUtility.ApplyBirthOutcome))]
public class ApplyBirthOutcome_Patch public class ApplyBirthOutcome_Breast_Patch
{ {
public static void PostFix(Thing birtherThing) public static void PostFix(Thing birtherThing)
{ {
@ -120,9 +127,21 @@ namespace RJW_Menstruation
} }
} }
[HarmonyPatch(typeof(PregnancyUtility), nameof(PregnancyUtility.TryTerminatePregnancy))] [HarmonyPatch]
public class TryTerminatePregnancy_Patch public class TerminatePregnancy_Patch
{ {
public static IEnumerable<MethodBase> TargetMethods()
{
yield return AccessTools.Method(typeof(PregnancyUtility), nameof(PregnancyUtility.TryTerminatePregnancy));
yield return AccessTools.Method(typeof(Recipe_TerminatePregnancy), nameof(Recipe_TerminatePregnancy.ApplyOnPawn));
}
private static PregnancyAttitude? GetAttitude(Hediff pregnancy)
{
if (pregnancy is Hediff_Pregnant preg) return preg.Attitude;
else return null;
}
private static Hediff GetEarliestPregnancy(Pawn pawn) private static Hediff GetEarliestPregnancy(Pawn pawn)
{ {
Hediff Earliest_Pregnancy = PregnancyUtility.GetPregnancyHediff(pawn); Hediff Earliest_Pregnancy = PregnancyUtility.GetPregnancyHediff(pawn);
@ -136,20 +155,30 @@ namespace RJW_Menstruation
return Earliest_Pregnancy; return Earliest_Pregnancy;
} }
private static readonly MethodInfo GetPregnancyHediff = AccessTools.Method(typeof(PregnancyUtility), nameof(PregnancyUtility.GetPregnancyHediff), new System.Type[] { typeof(Pawn) }); private static readonly MethodInfo GetPregnancyHediff = AccessTools.Method(typeof(PregnancyUtility), nameof(PregnancyUtility.GetPregnancyHediff), new Type[] { typeof(Pawn) });
private static readonly MethodInfo Get_Attitude = AccessTools.DeclaredPropertyGetter(typeof(Hediff_Pregnant), nameof(Hediff_Pregnant.Attitude));
// Also called for Recipe_TerminatePregnancy.ApplyOnPawn
public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{ {
if (GetPregnancyHediff == null || GetPregnancyHediff.ReturnType != typeof(Hediff)) throw new System.InvalidOperationException("GetPregnancyHediff not found"); if (GetPregnancyHediff == null || 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) foreach (CodeInstruction instruction in instructions)
{ {
if (instruction.Calls(GetPregnancyHediff)) if (instruction.Calls(GetPregnancyHediff))
yield return CodeInstruction.Call(typeof(TryTerminatePregnancy_Patch), nameof(TryTerminatePregnancy_Patch.GetEarliestPregnancy)); yield return CodeInstruction.Call(typeof(TerminatePregnancy_Patch), nameof(TerminatePregnancy_Patch.GetEarliestPregnancy));
// Menstruation pregnancies don't have an attitude, so skip the cast to Hediff_Pregnant and call a version that handles it
else if (instruction.opcode == OpCodes.Castclass && (Type)instruction.operand == typeof(Hediff_Pregnant))
yield return new CodeInstruction(OpCodes.Nop);
else if (instruction.Calls(Get_Attitude))
yield return CodeInstruction.Call(typeof(TerminatePregnancy_Patch), nameof(TerminatePregnancy_Patch.GetAttitude));
else yield return instruction; else yield return instruction;
} }
} }
}
[HarmonyPatch(typeof(PregnancyUtility), nameof(PregnancyUtility.TryTerminatePregnancy))]
public class PregnancyUtility_TryTerminatePregnancy_Patch
{
public static void Postfix(bool __result, Pawn pawn) public static void Postfix(bool __result, Pawn pawn)
{ {
if (__result) if (__result)
@ -168,13 +197,51 @@ namespace RJW_Menstruation
} }
} }
[HarmonyPatch(typeof(Recipe_TerminatePregnancy), nameof(Recipe_TerminatePregnancy.ApplyOnPawn))] [HarmonyPatch(typeof(Pawn_GeneTracker), "AddGene", new Type[] {typeof(Gene), typeof(bool)})]
public class TerminatePregnancy_ApplyOnPawn_Patch public class AddGene_Patch
{ {
public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) public static bool Prefix(ref Gene __result, Gene gene, Pawn ___pawn)
{ {
return TryTerminatePregnancy_Patch.Transpiler(instructions); if(VariousDefOf.WombGenes.Contains(gene.def) && !___pawn.GetMenstruationComps().Any())
{
__result = null;
return false;
}
else return true;
} }
} }
[HarmonyPatch(typeof(Pawn_GeneTracker), "Notify_GenesChanged")]
public class Notify_GenesChanged_Patch
{
public static void Postfix(Pawn_GeneTracker __instance)
{
foreach (HediffComp_Menstruation comp in __instance.pawn.GetMenstruationComps())
comp.Notify_UpdatedGenes();
}
}
//[HarmonyPatch(typeof(ChildcareUtility), nameof(ChildcareUtility.SuckleFromLactatingPawn))]
//public class GreedyConsume_Patch
//{
// private static float ConsumeAndAdjustNipples(HediffComp_Chargeable instance, float desiredCharge)
// {
// // Pulling breast comp every tick might be too much for performance.
// const float averageNippleChangePerTick = 0.0025f / 50000f;
// instance.Pawn.GetBreastComp()?.AdjustNippleProgress(Rand.Range(0.0f, averageNippleChangePerTick * 2) * Configurations.MaxBreastIncrementFactor);
// return instance.GreedyConsume(desiredCharge);
// }
// private static readonly MethodInfo GreedyConsume = AccessTools.Method(typeof(HediffComp_Chargeable), nameof(HediffComp_Chargeable.GreedyConsume), new Type[] { typeof(HediffComp_Chargeable), typeof(float) });
// public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
// {
// if(GreedyConsume == null) throw new InvalidOperationException("GreedyConsume not found");
// foreach (var instruction in instructions)
// {
// if (instruction.Calls(GreedyConsume))
// yield return CodeInstruction.Call(typeof(GreedyConsume_Patch), nameof(GreedyConsume_Patch.ConsumeAndAdjustNipples));
// yield return instruction;
// }
// }
//}
} }

View file

@ -48,8 +48,10 @@ namespace RJW_Menstruation
{ {
description description
.AppendFormat("{0}: {1}\n", comp.curStage, comp.curStageHrs); .AppendFormat("{0}: {1}\n", comp.curStage, comp.curStageHrs);
if (comp.Pregnancy is Hediff_MultiplePregnancy preg) description if (comp.Pregnancy is Hediff_BasePregnancy rjwpreg) description
.AppendFormat("due: {0}\n", preg.DueDate()); .AppendFormat("due: {0}\n", rjwpreg.DueDate());
else if (comp.Pregnancy is Hediff_Pregnant biopreg) description
.AppendFormat("due: {0}\n", biopreg.DueDate());
description description
.AppendFormat("fertcums: {0}\n" + .AppendFormat("fertcums: {0}\n" +
"ovarypower: {1}\n" + "ovarypower: {1}\n" +

View file

@ -1,4 +1,5 @@
using HarmonyLib; using AlienRace;
using HarmonyLib;
using rjw; using rjw;
using rjw.Modules.Interactions.Internals.Implementation; using rjw.Modules.Interactions.Internals.Implementation;
using rjw.Modules.Interactions.Rules.PartKindUsageRules; using rjw.Modules.Interactions.Rules.PartKindUsageRules;
@ -16,6 +17,11 @@ namespace RJW_Menstruation
{ {
Harmony har = new Harmony("RJW_Menstruation"); Harmony har = new Harmony("RJW_Menstruation");
har.PatchAll(Assembly.GetExecutingAssembly()); har.PatchAll(Assembly.GetExecutingAssembly());
if (ModsConfig.IsActive("erdelf.HumanoidAlienRaces")) // Don't use the cached in Configurations, it isn't initialized yet
{
har.Patch(GenTypes.GetTypeInAnyAssembly("AlienRace.HarmonyPatches").GetMethod(nameof(AlienRace.HarmonyPatches.BirthOutcomeMultiplier)),
postfix: new HarmonyMethod(typeof(HAR_LitterSize_Undo).GetMethod(nameof(HAR_LitterSize_Undo.Postfix))));
}
InjectIntoRjwInteractionServices(); InjectIntoRjwInteractionServices();
} }

View file

@ -0,0 +1,322 @@
using RimWorld;
using rjw;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using Verse;
namespace RJW_Menstruation
{
public static class PregnancyCommon
{
private static readonly MethodInfo TryGetInheritedXenotype = typeof(PregnancyUtility).GetMethod("TryGetInheritedXenotype", BindingFlags.Static | BindingFlags.NonPublic, null, new Type[] { typeof(Pawn), typeof(Pawn), typeof(XenotypeDef).MakeByRefType() }, null);
private static readonly MethodInfo ShouldByHybrid = typeof(PregnancyUtility).GetMethod("ShouldByHybrid", BindingFlags.Static | BindingFlags.NonPublic, null, new Type[] { typeof(Pawn), typeof(Pawn) }, null);
public static string GetBabyInfo(IEnumerable<Pawn> babies)
{
if (babies == null || !babies.Any()) return "Null";
StringBuilder res = new StringBuilder();
IEnumerable<Pawn> babiesdistinct = babies.Distinct(new RaceComparer());
int iteration = 0;
foreach (Pawn baby in babiesdistinct)
{
int num = babies.Where(x => x.def.Equals(baby.def)).Count();
if (iteration > 0) res.Append(", ");
res.AppendFormat("{0} {1}", num, baby.def.label);
iteration++;
}
res.AppendFormat(" {0}", Translations.Dialog_WombInfo02);
return res.ToString();
}
public static string GetFatherInfo(IEnumerable<Pawn> babies, Pawn mother, bool is_parent_known)
{
if (babies == null || !babies.Any()) return "Null";
StringBuilder res = new StringBuilder();
res.AppendFormat("{0}: ", Translations.Dialog_WombInfo03);
if (!is_parent_known && Configurations.InfoDetail != Configurations.DetailLevel.All)
return res.Append(Translations.Dialog_FatherUnknown).ToString();
IEnumerable<Pawn> babiesdistinct = babies.Distinct(new FatherComparer(mother));
int iteration = 0;
foreach (Pawn baby in babiesdistinct)
{
if (iteration > 0) res.Append(", ");
res.Append(Utility.GetFather(baby, mother)?.LabelShort ?? Translations.Dialog_FatherUnknown);
iteration++;
}
return res.ToString();
}
/// <summary>
/// Decide pawnkind from mother and father <para/>
/// Come from RJW
/// </summary>
/// <param name="mother"></param>
/// <param name="father"></param>
/// <returns></returns>
/// <param name="noAnimalsFromHumanlikes"></param>
public static PawnKindDef BabyPawnKindDecider(Pawn mother, Pawn father, bool noAnimalsFromHumanlikes)
{
PawnKindDef motherKindDef = Utility.GetRacesPawnKind(mother);
PawnKindDef fatherKindDef = Utility.GetRacesPawnKind(father);
PawnKindDef spawn_kind_def = motherKindDef;
int flag = 0;
if (xxx.is_human(mother)) flag += 2;
if (xxx.is_human(father)) flag += 1;
//Mother - Father = Flag
//Human - Human = 3
//Human - Animal = 2
//Animal - Human = 1
//Animal - Animal = 0
switch (flag)
{
case 3:
if (!Rand.Chance(RJWPregnancySettings.humanlike_DNA_from_mother)) spawn_kind_def = fatherKindDef;
break;
case 2:
if (RJWPregnancySettings.bestiality_DNA_inheritance == 0f) spawn_kind_def = fatherKindDef;
else if (!Rand.Chance(RJWPregnancySettings.bestial_DNA_from_mother)) spawn_kind_def = fatherKindDef;
break;
case 1:
if (RJWPregnancySettings.bestiality_DNA_inheritance == 1f) spawn_kind_def = fatherKindDef;
else if (!Rand.Chance(RJWPregnancySettings.bestial_DNA_from_mother)) spawn_kind_def = fatherKindDef;
break;
case 0:
if (!Rand.Chance(RJWPregnancySettings.bestial_DNA_from_mother)) spawn_kind_def = fatherKindDef;
break;
}
bool IsAndroidmother = AndroidsCompatibility.IsAndroid(mother);
bool IsAndroidfather = AndroidsCompatibility.IsAndroid(father);
if (IsAndroidmother && !IsAndroidfather)
{
spawn_kind_def = fatherKindDef;
}
else if (!IsAndroidmother && IsAndroidfather)
{
spawn_kind_def = motherKindDef;
}
string MotherRaceName = "";
string FatherRaceName = "";
MotherRaceName = motherKindDef?.race?.defName;
PawnKindDef non_hybrid_kind_def = spawn_kind_def;
if (father != null)
FatherRaceName = fatherKindDef?.race?.defName;
if (FatherRaceName != "" && Configurations.UseHybridExtention)
{
spawn_kind_def = GetHybrid(father, mother);
//Log.Message("pawnkind: " + spawn_kind_def?.defName);
}
if (MotherRaceName != FatherRaceName && FatherRaceName != "")
{
if (!Configurations.UseHybridExtention || spawn_kind_def == null)
{
spawn_kind_def = non_hybrid_kind_def;
IEnumerable<RaceGroupDef> groups = DefDatabase<RaceGroupDef>.AllDefs.Where(x => !(x.hybridRaceParents.NullOrEmpty() || x.hybridChildKindDef.NullOrEmpty()));
//ModLog.Message(" found custom RaceGroupDefs " + groups.Count());
foreach (RaceGroupDef t in groups)
{
if ((t.hybridRaceParents.Contains(MotherRaceName) && t.hybridRaceParents.Contains(FatherRaceName))
|| (t.hybridRaceParents.Contains("Any") && (t.hybridRaceParents.Contains(MotherRaceName) || t.hybridRaceParents.Contains(FatherRaceName))))
{
//ModLog.Message(" has hybridRaceParents");
if (t.hybridChildKindDef.Contains("MotherKindDef"))
spawn_kind_def = motherKindDef;
else if (t.hybridChildKindDef.Contains("FatherKindDef") && father != null)
spawn_kind_def = fatherKindDef;
else
{
//ModLog.Message(" trying hybridChildKindDef " + t.defName);
List<PawnKindDef> child_kind_def_list = new List<PawnKindDef>();
child_kind_def_list.AddRange(DefDatabase<PawnKindDef>.AllDefs.Where(x => t.hybridChildKindDef.Contains(x.defName)));
//ModLog.Message(" found custom hybridChildKindDefs " + t.hybridChildKindDef.Count);
if (!child_kind_def_list.NullOrEmpty())
spawn_kind_def = child_kind_def_list.RandomElement();
}
}
}
}
}
else if (!Configurations.UseHybridExtention || spawn_kind_def == null)
{
spawn_kind_def = mother.RaceProps?.AnyPawnKind ?? motherKindDef;
}
if (spawn_kind_def.defName.Contains("Nymph"))
{
//child is nymph, try to find other PawnKindDef
List<PawnKindDef> spawn_kind_def_list = new List<PawnKindDef>();
spawn_kind_def_list.AddRange(DefDatabase<PawnKindDef>.AllDefs.Where(x => x.race == spawn_kind_def.race && !x.defName.Contains("Nymph")));
//no other PawnKindDef found try mother
if (spawn_kind_def_list.NullOrEmpty())
spawn_kind_def_list.AddRange(DefDatabase<PawnKindDef>.AllDefs.Where(x => x.race == motherKindDef.race && !x.defName.Contains("Nymph")));
//no other PawnKindDef found try father
if (spawn_kind_def_list.NullOrEmpty() && father != null)
spawn_kind_def_list.AddRange(DefDatabase<PawnKindDef>.AllDefs.Where(x => x.race == fatherKindDef.race && !x.defName.Contains("Nymph")));
//no other PawnKindDef found fallback to generic colonist
if (spawn_kind_def_list.NullOrEmpty())
spawn_kind_def = PawnKindDefOf.Colonist;
if (!spawn_kind_def_list.NullOrEmpty()) spawn_kind_def = spawn_kind_def_list.RandomElement();
}
// If both parents are humanlike, Biotech will attempt to assign genes to the child
// Normally not a problem, but with the hybrid system, two humanlikes might produce an animal
// So override it and force the child to be human
if (noAnimalsFromHumanlikes && mother.genes != null && father?.genes != null && !spawn_kind_def.race.race.Humanlike)
spawn_kind_def = Rand.Chance(RJWPregnancySettings.humanlike_DNA_from_mother) ? motherKindDef : fatherKindDef;
return spawn_kind_def;
}
public static void SetupBabyXenotype(Pawn mother, Pawn father, Pawn baby)
{
if (baby.genes == null || !ModsConfig.BiotechActive) return;
if (GeneUtility.SameHeritableXenotype(mother, father) && mother.genes.UniqueXenotype)
{
baby.genes.xenotypeName = mother.genes.xenotypeName;
baby.genes.iconDef = mother.genes.iconDef;
}
object[] args = new object[] { mother, father, null };
if ((bool)TryGetInheritedXenotype.Invoke(null, args))
{
baby.genes.SetXenotypeDirect((XenotypeDef)args[2]);
}
else if ((bool)ShouldByHybrid.Invoke(null, new object[] { mother, father }))
{
baby.genes.hybrid = true;
baby.genes.xenotypeName = "Hybrid".Translate();
}
}
public static PawnKindDef GetHybrid(Pawn first, Pawn second)
{
PawnKindDef res = null;
Pawn opposite = second;
HybridInformations info = null;
if (!Configurations.HybridOverride.NullOrEmpty())
{
info = Configurations.HybridOverride.FirstOrDefault(x => x.DefName == first.def?.defName && (x.hybridExtension?.Exists(y => y.DefName == second.def?.defName) ?? false));
if (info == null)
{
info = Configurations.HybridOverride.FirstOrDefault(x => x.DefName == second.def?.defName && (x.hybridExtension?.Exists(y => y.DefName == first.def?.defName) ?? false));
opposite = first;
}
}
if (info != null)
{
res = info.GetHybridWith(opposite.def.defName) ?? null;
}
if (res != null) return res;
PawnDNAModExtension dna;
dna = first.def.GetModExtension<PawnDNAModExtension>();
if (dna != null)
{
res = dna.GetHybridWith(second.def.defName) ?? null;
}
else
{
dna = second.def.GetModExtension<PawnDNAModExtension>();
if (dna != null)
{
res = dna.GetHybridWith(first.def.defName) ?? null;
}
}
return res;
}
private static void CopyBodyPartProperties(Hediff part, Hediff originalPart)
{
CompHediffBodyPart comp = part.TryGetComp<CompHediffBodyPart>();
CompHediffBodyPart originalComp = originalPart.TryGetComp<CompHediffBodyPart>();
if (comp != null && originalComp != null)
{
// the string properties should be the same between both pawns anyways, besides the name of the owner
part.Severity = originalPart.Severity;
comp.SizeBase = originalComp.SizeBase;
comp.SizeOwner = originalComp.SizeOwner;
comp.EffSize = originalComp.EffSize;
comp.FluidAmmount = originalComp.FluidAmmount;
comp.FluidModifier = originalComp.FluidModifier;
}
HediffComp_Menstruation originalMenstruationComp = originalPart.GetMenstruationCompFromVagina();
if (originalMenstruationComp != null)
{
part.GetMenstruationCompFromVagina()?.CopyCycleProperties(originalMenstruationComp);
}
HediffComp_Breast originalBreastComp = originalPart.GetBreastComp();
if (originalBreastComp != null)
{
part.GetBreastComp()?.CopyBreastProperties(originalBreastComp);
}
}
private static void CopyBodyPartRecord(Pawn baby, Pawn original, BodyPartRecord babyBPR, BodyPartRecord originalBPR)
{
if (babyBPR == null || originalBPR == null) return;
Hediff_BasePregnancy.RemoveBabyParts(baby, Genital_Helper.get_PartsHediffList(baby, babyBPR));
foreach (Hediff originalPart in Genital_Helper.get_PartsHediffList(original, originalBPR))
{
Hediff part = SexPartAdder.MakePart(originalPart.def, baby, babyBPR);
CopyBodyPartProperties(part, originalPart);
baby.health.AddHediff(part, babyBPR);
}
}
// Baby is the sibling to be changed, original is the first of the set and the one to copy to the rest.
public static void ProcessIdenticalSibling(Pawn baby, Pawn original)
{
// They'll be the same pawnkind, which lets us make a lot of useful assumptions
// However, some RNG might still be involved in genital generation (e.g. futas), so the easiest method is to clear out and re-generate
// A bit wasteful since Hediff_BasePregnancy.PostBirth already redid the genitals
CopyBodyPartRecord(baby, original, Genital_Helper.get_genitalsBPR(baby), Genital_Helper.get_genitalsBPR(original));
CopyBodyPartRecord(baby, original, Genital_Helper.get_breastsBPR(baby), Genital_Helper.get_breastsBPR(original));
CopyBodyPartRecord(baby, original, Genital_Helper.get_uddersBPR(baby), Genital_Helper.get_uddersBPR(original));
CopyBodyPartRecord(baby, original, Genital_Helper.get_anusBPR(baby), Genital_Helper.get_anusBPR(original));
}
public static string DueDate(this Hediff_BasePregnancy preg)
{
if (preg.pawn.Tile == -1) return "";
return GenDate.DateFullStringWithHourAt(GenDate.TickGameToAbs((int)preg.p_end_tick), Find.WorldGrid.LongLatOf(preg.pawn.Tile));
}
public static string DueDate(this Hediff_Pregnant preg)
{
if (preg.pawn.Tile == -1) return "";
int ticksRemaining = (int)((1f - preg.GestationProgress) * preg.pawn.RaceProps.gestationPeriodDays * GenDate.TicksPerDay);
int dueTickAbs = GenTicks.TicksAbs + ticksRemaining;
return GenDate.DateFullStringWithHourAt(dueTickAbs, Find.WorldGrid.LongLatOf(preg.pawn.Tile));
}
}
}

View file

@ -69,12 +69,14 @@
<Compile Include="EstrusPartKindUsageRule.cs" /> <Compile Include="EstrusPartKindUsageRule.cs" />
<Compile Include="HediffComps\HediffComp_InducedOvulator.cs" /> <Compile Include="HediffComps\HediffComp_InducedOvulator.cs" />
<Compile Include="HediffComps\HediffComp_PeriodicOvulator.cs" /> <Compile Include="HediffComps\HediffComp_PeriodicOvulator.cs" />
<Compile Include="HediffComps\HediffComp_PregeneratedBabies.cs" />
<Compile Include="HediffComps\MenstruationUtility.cs" /> <Compile Include="HediffComps\MenstruationUtility.cs" />
<Compile Include="Hediff_Estrus.cs" /> <Compile Include="Hediff_Estrus.cs" />
<Compile Include="IngestionOutcomeDoers.cs" /> <Compile Include="IngestionOutcomeDoers.cs" />
<Compile Include="Patch\Biotech_Patch.cs" /> <Compile Include="Patch\Biotech_Patch.cs" />
<Compile Include="Patch\GC_Patch.cs" /> <Compile Include="Patch\GC_Patch.cs" />
<Compile Include="Patch\Gizmo_Patch.cs" /> <Compile Include="Patch\Gizmo_Patch.cs" />
<Compile Include="PregnancyCommon.cs" />
<Compile Include="Recipe_Surgery.cs" /> <Compile Include="Recipe_Surgery.cs" />
<Compile Include="StatParts.cs" /> <Compile Include="StatParts.cs" />
<Compile Include="UI\Dialog_HybridCustom.cs" /> <Compile Include="UI\Dialog_HybridCustom.cs" />

View file

@ -125,6 +125,8 @@ namespace RJW_Menstruation
public static readonly string Option_PregnancyFromBaseRJW_Label = "Option_PregnancyFromBaseRJW_Label".Translate(); public static readonly string Option_PregnancyFromBaseRJW_Label = "Option_PregnancyFromBaseRJW_Label".Translate();
public static readonly string Option_PregnancyFromMultiplePregnancy_Label = "Option_PregnancyFromMultiplePregnancy_Label".Translate(); public static readonly string Option_PregnancyFromMultiplePregnancy_Label = "Option_PregnancyFromMultiplePregnancy_Label".Translate();
public static readonly string Option_PregnancyFromBiotech_Label = "Option_PregnancyFromBiotech_Label".Translate(); public static readonly string Option_PregnancyFromBiotech_Label = "Option_PregnancyFromBiotech_Label".Translate();
public static readonly string Option_EnableBiotechTwins_Label = "Option_EnableBiotechTwins_Label".Translate();
public static readonly string Option_EnableBiotechTwins_Desc = "Option_EnableBiotechTwins_Desc".Translate();
public static readonly string Option_EnableDraftedIcon_Label = "Option_EnableDraftedIcon_Label".Translate(); public static readonly string Option_EnableDraftedIcon_Label = "Option_EnableDraftedIcon_Label".Translate();
public static readonly string Option_EnableDraftedIcon_Desc = "Option_EnableDraftedIcon_Desc".Translate(); public static readonly string Option_EnableDraftedIcon_Desc = "Option_EnableDraftedIcon_Desc".Translate();

View file

@ -172,8 +172,8 @@ namespace RJW_Menstruation
Pawn fetus = comp.GetFetus(); Pawn fetus = comp.GetFetus();
if (fetus != null && Utility.ShowFetusInfo()) if (fetus != null && Utility.ShowFetusInfo())
{ {
string feinfo = m.GetBabyInfo(); string feinfo = PregnancyCommon.GetBabyInfo(m.babies);
string fainfo = m.GetFatherInfo() + " "; string fainfo = PregnancyCommon.GetFatherInfo(m.babies, m.pawn, m.is_parent_known) + " ";
if (feinfo.Length + fainfo.Length > 45) if (feinfo.Length + fainfo.Length > 45)
{ {
preginfoheight = fontheight + 2; preginfoheight = fontheight + 2;
@ -221,19 +221,36 @@ namespace RJW_Menstruation
{ {
if (p is Hediff_Pregnant hp && hp.Severity < 0.2f) cum = comp.GetCumIcon(); if (p is Hediff_Pregnant hp && hp.Severity < 0.2f) cum = comp.GetCumIcon();
else cum = ContentFinder<Texture2D>.Get("Womb/Empty", true); else cum = ContentFinder<Texture2D>.Get("Womb/Empty", true);
// TODO: Pregenerated babies (base on multiplepregnancy) HediffComp_PregeneratedBabies babiescomp = p.TryGetComp<HediffComp_PregeneratedBabies>();
if (Utility.ShowFetusInfo()) if (Utility.ShowFetusInfo())
{
string feinfo = PregnancyCommon.GetBabyInfo(babiescomp?.babies);
string fainfo = PregnancyCommon.GetFatherInfo(babiescomp?.babies, babiescomp.Pawn, true) + " "; // Keep all parents known, for now
if (feinfo == "Null") feinfo = "1 " + p.Mother.def.label + " " + Translations.Dialog_WombInfo02;
if (fainfo == "Null")
{
string father = p.Father?.LabelShort ?? Translations.Dialog_FatherUnknown;
fainfo = Translations.Dialog_WombInfo03 + ": " + father + " ";
}
if (feinfo.Length + fainfo.Length > 45)
{
preginfoheight = fontheight + 2;
buttonstyle.alignment = TextAnchor.UpperLeft;
fontstyleright.alignment = TextAnchor.LowerRight;
}
else
{ {
preginfoheight = fontheight; preginfoheight = fontheight;
Rect preginfo = new Rect(0f, mainRect.yMax - wombRectHeight - 2, wombRectWidth, preginfoheight);
fontstyleright.normal.textColor = Color.white;
fontstyleright.alignment = TextAnchor.MiddleRight;
buttonstyle.alignment = TextAnchor.MiddleLeft; buttonstyle.alignment = TextAnchor.MiddleLeft;
string father = p.Father?.LabelShort ?? Translations.Dialog_FatherUnknown; }
GUI.Box(preginfo, "1 " + p.Mother.def.label + " " + Translations.Dialog_WombInfo02, buttonstyle); Rect preginfo = new Rect(0f, mainRect.yMax - wombRectHeight - 2, wombRectWidth, preginfoheight);
GUI.Label(preginfo, Translations.Dialog_WombInfo03 + ": " + father + " ", fontstyleright); fontstyleright.normal.textColor = Color.white;
GUI.Box(preginfo, feinfo, buttonstyle);
GUI.Label(preginfo, fainfo, fontstyleright);
} }
} }
else cum = ContentFinder<Texture2D>.Get(("Womb/Empty"), true); else cum = ContentFinder<Texture2D>.Get(("Womb/Empty"), true);
@ -377,7 +394,7 @@ namespace RJW_Menstruation
anal = pawn.GetAnalIcon(showOrigin); anal = pawn.GetAnalIcon(showOrigin);
GUI.color = new Color(1.00f, 0.47f, 0.47f, 1); GUI.color = new Color(1.00f, 0.47f, 0.47f, 1);
GUI.Box(rect, "", boxstyle); GUI.Box(rect, "", boxstyle);
GUI.color = pawn.story.SkinColor; GUI.color = Utility.SafeSkinColor(pawn);
//Widgets.DrawTextureFitted(genitalIconRect, anal, 1.0f); //Widgets.DrawTextureFitted(genitalIconRect, anal, 1.0f);
//Widgets.DrawTextureFitted(genitalIconRect, vagina, 1.0f); //Widgets.DrawTextureFitted(genitalIconRect, vagina, 1.0f);
GUI.DrawTexture(genitalIconRect, anal, ScaleMode.ScaleToFit); GUI.DrawTexture(genitalIconRect, anal, ScaleMode.ScaleToFit);

View file

@ -188,8 +188,8 @@ namespace RJW_Menstruation
return null; return null;
} }
} }
// TODO: Biotech pregenerated babies HediffComp_PregeneratedBabies babiescomp = comp.Pregnancy.TryGetComp<HediffComp_PregeneratedBabies>();
return null; return babiescomp?.babies?.FirstOrDefault();
} }
public static void DrawBreastIcon(this Pawn pawn, Rect rect) public static void DrawBreastIcon(this Pawn pawn, Rect rect)
@ -209,7 +209,7 @@ namespace RJW_Menstruation
nipple = ContentFinder<Texture2D>.Get("Breasts/Breast_Breast00_Nipple00", false); nipple = ContentFinder<Texture2D>.Get("Breasts/Breast_Breast00_Nipple00", false);
areola = ContentFinder<Texture2D>.Get("Breasts/Breast_Breast00_Areola00", false); areola = ContentFinder<Texture2D>.Get("Breasts/Breast_Breast00_Areola00", false);
GUI.color = pawn.story?.SkinColor ?? Color.white; GUI.color = SafeSkinColor(pawn);
GUI.DrawTexture(rect, breast, ScaleMode.ScaleToFit); GUI.DrawTexture(rect, breast, ScaleMode.ScaleToFit);
GUI.color = Color.white; GUI.color = Color.white;
GUI.DrawTexture(rect, areola, ScaleMode.ScaleToFit); GUI.DrawTexture(rect, areola, ScaleMode.ScaleToFit);
@ -236,7 +236,7 @@ namespace RJW_Menstruation
breast = ContentFinder<Texture2D>.Get(icon, false); breast = ContentFinder<Texture2D>.Get(icon, false);
areola = ContentFinder<Texture2D>.Get(areolaicon, false); areola = ContentFinder<Texture2D>.Get(areolaicon, false);
nipple = ContentFinder<Texture2D>.Get(nippleicon, false); nipple = ContentFinder<Texture2D>.Get(nippleicon, false);
GUI.color = pawn.story.SkinColor; GUI.color = SafeSkinColor(pawn);
GUI.DrawTexture(rect, breast, ScaleMode.ScaleToFit); GUI.DrawTexture(rect, breast, ScaleMode.ScaleToFit);
GUI.color = comp.NippleColor; GUI.color = comp.NippleColor;
@ -255,7 +255,7 @@ namespace RJW_Menstruation
nipple = ContentFinder<Texture2D>.Get("Breasts/Breast_Breast00_Nipple00", false); nipple = ContentFinder<Texture2D>.Get("Breasts/Breast_Breast00_Nipple00", false);
areola = ContentFinder<Texture2D>.Get("Breasts/Breast_Breast00_Areola00", false); areola = ContentFinder<Texture2D>.Get("Breasts/Breast_Breast00_Areola00", false);
GUI.color = pawn.story.SkinColor; GUI.color = SafeSkinColor(pawn);
GUI.DrawTexture(rect, breast, ScaleMode.ScaleToFit); GUI.DrawTexture(rect, breast, ScaleMode.ScaleToFit);
GUI.color = Color.white; GUI.color = Color.white;
GUI.DrawTexture(rect, areola, ScaleMode.ScaleToFit); GUI.DrawTexture(rect, areola, ScaleMode.ScaleToFit);
@ -454,5 +454,18 @@ namespace RJW_Menstruation
if (pawn.IsAnimal() && !Configurations.EnableAnimalCycle) return false; if (pawn.IsAnimal() && !Configurations.EnableAnimalCycle) return false;
return true; return true;
} }
// Apparently with some mods, even doing pawn.story?.SkinColor can throw a null reference, so we're stuck doing this
public static Color SafeSkinColor(Pawn pawn)
{
try
{
return pawn.story?.SkinColor ?? Color.white;
}
catch (NullReferenceException)
{
return Color.white;
}
}
} }
} }

View file

@ -44,8 +44,24 @@ namespace RJW_Menstruation
public static readonly RecordDef AmountofCreampied = DefDatabase<RecordDef>.GetNamed("AmountofCreampied"); public static readonly RecordDef AmountofCreampied = DefDatabase<RecordDef>.GetNamed("AmountofCreampied");
public static readonly RecordDef AmountofFertilizedEggs = DefDatabase<RecordDef>.GetNamed("AmountofFertilizedEggs"); public static readonly RecordDef AmountofFertilizedEggs = DefDatabase<RecordDef>.GetNamed("AmountofFertilizedEggs");
public static readonly TaleDef TaleCameInside = DefDatabase<TaleDef>.GetNamed("CameInside"); public static readonly TaleDef TaleCameInside = DefDatabase<TaleDef>.GetNamed("CameInside");
public static readonly GeneDef ShortEggLifetime = DefDatabase<GeneDef>.GetNamed("Menstruation_ShortEggLifetime");
public static readonly GeneDef DoubleEggLifetime = DefDatabase<GeneDef>.GetNamed("Menstruation_DoubleEggLifetime");
public static readonly GeneDef QuadEggLifetime = DefDatabase<GeneDef>.GetNamed("Menstruation_QuadEggLifetime");
public static readonly GeneDef NeverEstrus = DefDatabase<GeneDef>.GetNamed("Menstruation_NeverEstrus");
public static readonly GeneDef FullEstrus = DefDatabase<GeneDef>.GetNamed("Menstruation_FullEstrus");
public static readonly GeneDef DoubleOvulation = DefDatabase<GeneDef>.GetNamed("Menstruation_DoubleOvulation");
public static readonly GeneDef QuadOvulation = DefDatabase<GeneDef>.GetNamed("Menstruation_QuadOvulation");
public static readonly GeneDef NoBleeding = DefDatabase<GeneDef>.GetNamed("Menstruation_NoBleeding");
public static readonly HashSet<GeneDef> WombGenes = new HashSet<GeneDef>() {
ShortEggLifetime,
DoubleEggLifetime,
QuadEggLifetime,
NeverEstrus,
FullEstrus,
DoubleOvulation,
QuadOvulation,
NoBleeding };
private static List<ThingDef> allraces = null; private static List<ThingDef> allraces = null;
private static List<PawnKindDef> allkinds = null; private static List<PawnKindDef> allkinds = null;

View file

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Manifest> <Manifest>
<identifier>RJW Menstruation</identifier> <identifier>RJW Menstruation</identifier>
<version>1.0.8.5</version> <version>1.0.8.6</version>
<dependencies> <dependencies>
</dependencies> </dependencies>
<incompatibleWith /> <incompatibleWith />

View file

@ -1,3 +1,10 @@
Version 1.0.8.6
- Updated Traditional Chinese translation by Hydrogen.
- Fix error when trying to terminate a non-Biotech pregnancy.
- Properly give the opportunity to name a newborn with Biotech and multiple pregnancy.
- Added several menstruation-related genes, with a placeholder graphic for now.
- Added experimental support for twins and hybrids with Biotech pregnancies, disabled by default.
Version 1.0.8.5 Version 1.0.8.5
- Added biosculpter recipe to restore 1 year's worth of eggs, with icon by DestinyPlayer. - Added biosculpter recipe to restore 1 year's worth of eggs, with icon by DestinyPlayer.
- Vaginal sex with the "avoid pregnancy" relation will (usually) pull out prevent cum from entering the womb if there's a risk of pregnancy. - Vaginal sex with the "avoid pregnancy" relation will (usually) pull out prevent cum from entering the womb if there's a risk of pregnancy.