Compare commits

...

87 commits

Author SHA1 Message Date
lutepickle
32800a3499 Null check for apparel tracker in DirtyEffect 2024-02-22 22:09:12 -08:00
lutepickle
ff95b8da39 It's safe and a bit quicker to unconditionally remove forced in CheckDirty 2024-02-17 15:37:14 -08:00
lutepickle
8306439576 Refactor allraces and allkinds 2024-02-16 22:03:03 -08:00
lutepickle
ebb486179b Reformat RandomOvulationChance 2024-02-16 20:47:59 -08:00
lutepickle
f79050483d Remove the originsize system. Nobody used it 2024-02-16 20:24:10 -08:00
lutepickle
d483e22ac2 Little cleanup 2024-02-16 16:55:53 -08:00
lutepickle
7e5b2000da Mark the Conit fork of Birds & Bees as incompatible 2024-02-16 13:52:46 -08:00
lutepickle
836d3d69c8 Let CalculatedImpactChance return early to avoid unnecessarily hitting OvulationChance 2024-02-12 22:19:10 -08:00
lutepickle
50310988ca Be sure the periodic can go into anestrus if it initializes into ovulatory 2024-02-11 10:47:01 -08:00
lutepickle
82dea0f425 Always attempt to ovulate at least one egg 2024-02-11 07:30:25 -08:00
lutepickle
bd39194fd1 Add Outland - Genetics' egglaying genes to the no cycle list 2024-02-11 06:43:56 -08:00
lutepickle
56b11069da Little refactor in GetBabyInfo and GetFatherInfo 2024-02-10 10:11:24 -08:00
lutepickle
02b0045fb3 Some input sanitization for the egg life span and ovulation genes 2024-02-08 21:33:39 -08:00
lutepickle
fcbc19825e Move gene properties and cycle disabled genes to ModExtensions 2024-02-08 20:30:49 -08:00
lutepickle
7b00579969 Exclude Erin's Covyia egg genes from cycling 2024-02-08 16:00:13 -08:00
lutepickle
cb8267bf0f Let a pawn be randomly initialized into their ovulatory stage 2024-02-04 19:49:20 -08:00
lutepickle
2dda753ab9 Be sure CheckDirty updates the stats if the old absorber had stats and the new doesn't 2024-02-04 17:07:10 -08:00
lutepickle
d34509dfc9 Fix CheckDirty having an inverted lock test 2024-01-27 05:49:50 -08:00
lutepickle
9902c50d81 Don't let egglaying animals menstruate. 2024-01-22 11:12:49 -08:00
lutepickle
a29aaf50c1 Just let everyone use the menstruation genes 2024-01-20 14:51:20 -08:00
lutepickle
d74d488d6e Reset the ovulation power cache when genes change 2024-01-12 05:21:24 -08:00
lutepickle
8e9acaa8ca Little refactor in ShouldCycle 2024-01-09 10:26:28 -08:00
lutepickle
8e6c38045c Small refactor in ShouldCycle 2024-01-08 16:32:05 -08:00
lutepickle
9e671b4a24 Move average litter size to its own function. Have the biosculptor account for average litter size 2024-01-08 14:23:03 -08:00
lutepickle
546d121cde Redo the quirk API to make the rest of the code a bit cleaner 2024-01-08 08:13:52 -08:00
lutepickle
40b23fc9c9 Do a bit of housekeeping when an absorber goes dirty 2024-01-01 19:59:40 -08:00
lutepickle
f410f6bc0f Don't let pawns without a cycle wear absorbers 2024-01-01 07:35:52 -08:00
lutepickle
a9879b0b1b Remove force wear from absorbers when they become dirty 2024-01-01 07:08:29 -08:00
lutepickle
1a8189d9af Fix passive absorption not making absorbers dirty. Add absorbed amount to detailed description. Move passive absorption rate to XML 2023-12-26 16:02:49 -08:00
lutepickle
0dd3507010 Simplify CalculatedImplantChance a little 2023-12-05 16:32:48 -08:00
lutepickle
3498caf3a2 Let <= 0 age fertility stay in the patches 2023-12-05 16:23:23 -08:00
lutepickle
cb8d824a2c Simplify the too young/old check in the ovulation calculation a little 2023-12-05 16:20:36 -08:00
lutepickle
8cc72acf36 Just delete the milk module instead of trying to make it build with the new milkable colonists mod 2023-12-05 16:11:14 -08:00
lutepickle
c254ce039a Do the age adjustment for ovulation in FertilityByGenderAge instead of trying to patch RJW's StatWorker 2023-12-05 16:08:55 -08:00
lutepickle
02a96b957a Refactor EggLayerGenes a little 2023-12-04 12:14:18 -08:00
lutepickle
32ba7bcef8 Add sapling birth from VE Phytokin to the egg laying genes list 2023-12-04 11:51:09 -08:00
lutepickle
dc33024d42 Move the hash interval to the start of CompPostTick, since ShouldSimulate is getting heavy 2023-12-04 11:44:45 -08:00
lutepickle
0e708c6c89 Have the womb status button patched into the health tab instead of the hediff row. Modified version of code contributed by Fern. 2023-12-01 19:39:20 -08:00
lutepickle
c12f9dbc87 Don't cycle for egglaying genes (Alpha Genes and VE Saurid) 2023-11-29 09:10:19 -08:00
lutepickle
cb5522b0c3 Add interspecies factor to implantation failure debug message 2023-11-11 21:20:31 -08:00
lutepickle
68c4081f11 Have PregnantAction use get_Pregnancy instead since the same logic is in there 2023-10-29 20:45:29 -07:00
lutepickle
d0eb2e8383 Move the stage time advancement into its own function 2023-10-29 18:11:51 -07:00
lutepickle
a6afe494d8 Add an implanted egg to deadeggs even if getting the comp fails 2023-10-29 16:23:01 -07:00
lutepickle
a8af20d024 Make Implant adding to biotech pregnancy use the casted Hediff_Pregnant 2023-10-29 16:21:27 -07:00
lutepickle
95bdf4ca73 Don't reset the timer on periodic ovulators until it's breeding season 2023-10-28 21:30:55 -07:00
lutepickle
b07d12b8f8 Remove StayCurrentStage and StayCurrentStageConst, as they don't do anything anymore 2023-10-28 20:09:59 -07:00
lutepickle
0a24b5556f Refactor InfertileAction 2023-10-28 17:19:09 -07:00
lutepickle
441ee1de48 Refactor GoNextStage to directly take a stage time 2023-10-28 10:43:23 -07:00
lutepickle
f2a599ab08 Fix typos in function names 2023-10-27 06:56:27 -07:00
lutepickle
db42fa0234 Optimize Notify_UpdatedGenes 2023-10-26 23:01:38 -07:00
lutepickle
89ecbcd5aa Properly put a space between the follicular description and the climacteric description for induced ovulators 2023-10-26 16:32:28 -07:00
lutepickle
9be4bc8e54 Tidy up VariousDefOf.HumanVaginaCompProperties 2023-10-26 15:49:05 -07:00
lutepickle
e8710149e5 Refactor the egg removing logic to make it a bit clearer 2023-10-23 09:57:52 -07:00
lutepickle
9a19dc3c25 Update changelog 2023-10-22 16:40:00 -07:00
lutepickle
1ce69571f0 Refactor adding eggs to existing pregnancy, fix implanting eggs in a non-biotech pregnancy when set to biotech (e.g. animals) 2023-10-22 16:37:50 -07:00
lutepickle
d5043c0b62 Update Traditional Chinese by Hydrogen 2023-10-15 19:09:55 -07:00
lutepickle
1fcfb6f41d Enable absorbers in the default outfits 2023-10-15 09:54:06 -07:00
lutepickle
5f97bd1433 Replace most instances of rjw part hediff lists with a simple check against the AllVaginas, etc lists 2023-10-11 08:21:47 -07:00
lutepickle
f08ff58b64 Split animal cycle check into generic ShouldCycle function, exclude egglaying races from cycling 2023-10-11 08:06:31 -07:00
lutepickle
ebf0db8ae6 Only show the button in the health tab for genitals, as opening the dialog for non-genital wombs causes errors. 2023-09-29 21:11:22 -07:00
lutepickle
4212fbab7b Be sure to specify pawn on pheromone exception 2023-09-15 18:37:23 -07:00
lutepickle
d5f2954aed Clarify ImplantChance: it's before the implant configuration setting 2023-09-06 12:16:43 -07:00
lutepickle
9cd87dda27 Refactor InducedOvulator.ShouldBeInEstrus 2023-09-05 11:56:07 -07:00
lutepickle
76eec3852a Use explicit types in a few places 2023-09-05 10:46:24 -07:00
lutepickle
1c29218fdb More clear changelog entry 2023-09-01 19:39:10 -07:00
lutepickle
8cdb00a8c2 Only give the littersizecurve ovulation warning once per pawn 2023-08-19 18:26:54 -07:00
lutepickle
690e61916c Ensure that bleeding ends right away if the pawn gains the no bleeding gene 2023-08-19 18:24:05 -07:00
lutepickle
aa84a2b8bc Revert "More descriptive error on race support incorrectly installed"
Apparently it goes off even if race support is installed right

This reverts commit 2737a680fa.
2023-08-16 19:39:07 -07:00
lutepickle
2737a680fa More descriptive error on race support incorrectly installed 2023-08-16 19:34:35 -07:00
lutepickle
3692238c00 No bleed gene should have negative metabolism 2023-08-14 11:15:14 -07:00
lutepickle
caccc36fbf Remove redundant debug check 2023-08-10 20:22:32 -07:00
lutepickle
2c2b5a1ee2 Remove unnecessary "?? null" from GetFather 2023-08-10 18:48:56 -07:00
lutepickle
990774606c Compact the transpiler type checks a bit 2023-08-08 08:06:45 -07:00
lutepickle
9cdb887bdd If implant errors, see if there's a loose pregnancy to take. 2023-07-31 05:44:18 -07:00
lutepickle
0c38513084 Eliminate egg if there is an error implanting it. 2023-07-29 14:42:46 -07:00
lutepickle
f0c971eba0 Rebuild 2023-07-28 17:28:43 -07:00
lutepickle
214ab3ae88 Have the default constructor for eggs use the human lifespan 2023-07-28 09:50:13 -07:00
lutepickle
eb0d40ea1a Check for null pawn cum when merging cum 2023-07-27 14:44:14 -07:00
lutepickle
2350cf7355 Let a pawn precum into themself if it's otherwise valid 2023-07-27 14:42:45 -07:00
lutepickle
c37a7b0763 Don't precum into animals with cycle disabled 2023-07-27 14:41:45 -07:00
lutepickle
a7e452b254 Don't have androids with natural penises precum 2023-07-25 20:22:24 -07:00
lutepickle
ab1c57a901 Even more JP translations from Lokuzt 2023-07-22 12:28:52 -07:00
lutepickle
6e4364797d Partial Japanese translation by Lokuzt 2023-07-20 09:41:22 -07:00
lutepickle
2dc0885d53 Also initialize breasts on load 2023-07-03 09:21:55 -07:00
lutepickle
468188006a Rebuild 2023-07-03 09:10:13 -07:00
lutepickle
9d0d5f1d91 Initialize womb when displaying gizmo if needed. 2023-07-03 09:09:56 -07:00
lutepickle
39fe33acc6 Initialize after loading a pawn. This should cut down on uninitialized errors. 2023-07-03 09:08:38 -07:00
45 changed files with 841 additions and 484 deletions

Binary file not shown.

View file

@ -21,6 +21,11 @@
<iconPath>UI/Genes/Placeholder</iconPath> <iconPath>UI/Genes/Placeholder</iconPath>
<biostatMet>1</biostatMet> <biostatMet>1</biostatMet>
<displayOrderInCategory>10</displayOrderInCategory> <displayOrderInCategory>10</displayOrderInCategory>
<modExtensions>
<li Class ="RJW_Menstruation.MenstruationModExtension">
<eggLifeTimeFactor>0.75</eggLifeTimeFactor>
</li>
</modExtensions>
</GeneDef> </GeneDef>
<GeneDef ParentName="Menstruation_EggLifetime"> <GeneDef ParentName="Menstruation_EggLifetime">
@ -30,6 +35,11 @@
<iconPath>UI/Genes/Placeholder</iconPath> <iconPath>UI/Genes/Placeholder</iconPath>
<biostatMet>-1</biostatMet> <biostatMet>-1</biostatMet>
<displayOrderInCategory>12</displayOrderInCategory> <displayOrderInCategory>12</displayOrderInCategory>
<modExtensions>
<li Class ="RJW_Menstruation.MenstruationModExtension">
<eggLifeTimeFactor>2.0</eggLifeTimeFactor>
</li>
</modExtensions>
</GeneDef> </GeneDef>
<GeneDef ParentName="Menstruation_EggLifetime"> <GeneDef ParentName="Menstruation_EggLifetime">
@ -40,6 +50,11 @@
<biostatMet>-2</biostatMet> <biostatMet>-2</biostatMet>
<biostatCpx>1</biostatCpx> <biostatCpx>1</biostatCpx>
<displayOrderInCategory>16</displayOrderInCategory> <displayOrderInCategory>16</displayOrderInCategory>
<modExtensions>
<li Class ="RJW_Menstruation.MenstruationModExtension">
<eggLifeTimeFactor>4.0</eggLifeTimeFactor>
</li>
</modExtensions>
</GeneDef> </GeneDef>
<GeneDef Name="Menstruation_Estrus" Abstract="True"> <GeneDef Name="Menstruation_Estrus" Abstract="True">
@ -56,6 +71,11 @@
<iconPath>UI/Genes/Placeholder</iconPath> <iconPath>UI/Genes/Placeholder</iconPath>
<biostatMet>1</biostatMet> <biostatMet>1</biostatMet>
<displayOrderInCategory>20</displayOrderInCategory> <displayOrderInCategory>20</displayOrderInCategory>
<modExtensions>
<li Class ="RJW_Menstruation.MenstruationModExtension">
<neverEstrus>true</neverEstrus>
</li>
</modExtensions>
</GeneDef> </GeneDef>
<GeneDef ParentName="Menstruation_Estrus"> <GeneDef ParentName="Menstruation_Estrus">
@ -66,6 +86,11 @@
<biostatMet>-1</biostatMet> <biostatMet>-1</biostatMet>
<biostatCpx>1</biostatCpx> <biostatCpx>1</biostatCpx>
<displayOrderInCategory>25</displayOrderInCategory> <displayOrderInCategory>25</displayOrderInCategory>
<modExtensions>
<li Class ="RJW_Menstruation.MenstruationModExtension">
<alwaysEstrus>true</alwaysEstrus>
</li>
</modExtensions>
</GeneDef> </GeneDef>
<GeneDef Name="Menstruation_Ovulation" Abstract="True"> <GeneDef Name="Menstruation_Ovulation" Abstract="True">
@ -82,6 +107,11 @@
<iconPath>UI/Genes/Placeholder</iconPath> <iconPath>UI/Genes/Placeholder</iconPath>
<biostatMet>-1</biostatMet> <biostatMet>-1</biostatMet>
<displayOrderInCategory>30</displayOrderInCategory> <displayOrderInCategory>30</displayOrderInCategory>
<modExtensions>
<li Class ="RJW_Menstruation.MenstruationModExtension">
<ovulationFactor>2</ovulationFactor>
</li>
</modExtensions>
</GeneDef> </GeneDef>
<GeneDef ParentName="Menstruation_Ovulation"> <GeneDef ParentName="Menstruation_Ovulation">
@ -91,6 +121,11 @@
<iconPath>UI/Genes/Placeholder</iconPath> <iconPath>UI/Genes/Placeholder</iconPath>
<biostatMet>-1</biostatMet> <biostatMet>-1</biostatMet>
<displayOrderInCategory>35</displayOrderInCategory> <displayOrderInCategory>35</displayOrderInCategory>
<modExtensions>
<li Class ="RJW_Menstruation.MenstruationModExtension">
<ovulationFactor>4</ovulationFactor>
</li>
</modExtensions>
</GeneDef> </GeneDef>
<GeneDef> <GeneDef>
@ -99,8 +134,13 @@
<displayCategory>Menstruation</displayCategory> <displayCategory>Menstruation</displayCategory>
<description>Carriers of this gene will not bleed at the end of their cycle.</description> <description>Carriers of this gene will not bleed at the end of their cycle.</description>
<iconPath>UI/Genes/Placeholder</iconPath> <iconPath>UI/Genes/Placeholder</iconPath>
<biostatMet>1</biostatMet> <biostatMet>-1</biostatMet>
<displayOrderInCategory>40</displayOrderInCategory> <displayOrderInCategory>40</displayOrderInCategory>
<modExtensions>
<li Class ="RJW_Menstruation.MenstruationModExtension">
<noBleeding>true</noBleeding>
</li>
</modExtensions>
</GeneDef> </GeneDef>
<!-- Pheromones? --> <!-- Pheromones? -->

View file

@ -59,9 +59,16 @@
<tags> <tags>
<li>Absorber</li> <li>Absorber</li>
</tags> </tags>
<defaultOutfitTags>
<li>Worker</li>
<li>Soldier</li>
<li>Nudist</li>
<li>Slave</li>
</defaultOutfitTags>
</apparel> </apparel>
<modExtensions> <modExtensions>
<li Class="RJW_Menstruation.AbsorberModExtension"> <li Class="RJW_Menstruation.AbsorberModExtension">
<passiveAbsorptionPerHour>0.2</passiveAbsorptionPerHour>
<leakAfterDirty>false</leakAfterDirty> <leakAfterDirty>false</leakAfterDirty>
<effectsAfterDirty>true</effectsAfterDirty> <effectsAfterDirty>true</effectsAfterDirty>
<minHourstoDirtyEffect>10</minHourstoDirtyEffect> <minHourstoDirtyEffect>10</minHourstoDirtyEffect>
@ -116,6 +123,7 @@
</apparel> </apparel>
<modExtensions> <modExtensions>
<li Class="RJW_Menstruation.AbsorberModExtension"> <li Class="RJW_Menstruation.AbsorberModExtension">
<passiveAbsorptionPerHour>0.2</passiveAbsorptionPerHour>
<leakAfterDirty>false</leakAfterDirty> <leakAfterDirty>false</leakAfterDirty>
<effectsAfterDirty>true</effectsAfterDirty> <effectsAfterDirty>true</effectsAfterDirty>
<minHourstoDirtyEffect>8</minHourstoDirtyEffect> <minHourstoDirtyEffect>8</minHourstoDirtyEffect>
@ -190,9 +198,15 @@
<tags> <tags>
<li>Absorber</li> <li>Absorber</li>
</tags> </tags>
<defaultOutfitTags>
<li>Worker</li>
<li>Soldier</li>
<li>Slave</li>
</defaultOutfitTags>
</apparel> </apparel>
<modExtensions> <modExtensions>
<li Class="RJW_Menstruation.AbsorberModExtension"> <li Class="RJW_Menstruation.AbsorberModExtension">
<passiveAbsorptionPerHour>0.04</passiveAbsorptionPerHour>
<leakAfterDirty>true</leakAfterDirty> <leakAfterDirty>true</leakAfterDirty>
<effectsAfterDirty>false</effectsAfterDirty> <effectsAfterDirty>false</effectsAfterDirty>
<dirtyDef>Absorber_Pad_Dirty</dirtyDef> <dirtyDef>Absorber_Pad_Dirty</dirtyDef>
@ -254,6 +268,7 @@
</apparel> </apparel>
<modExtensions> <modExtensions>
<li Class="RJW_Menstruation.AbsorberModExtension"> <li Class="RJW_Menstruation.AbsorberModExtension">
<passiveAbsorptionPerHour>0.04</passiveAbsorptionPerHour>
<leakAfterDirty>true</leakAfterDirty> <leakAfterDirty>true</leakAfterDirty>
<effectsAfterDirty>false</effectsAfterDirty> <effectsAfterDirty>false</effectsAfterDirty>
<dirtyDef>Absorber_Pad_Dirty</dirtyDef> <dirtyDef>Absorber_Pad_Dirty</dirtyDef>

View file

@ -13,4 +13,10 @@
<Hediff_PainReliever.description>緩解源自於痛經及此類神經痛的苦楚。</Hediff_PainReliever.description> <Hediff_PainReliever.description>緩解源自於痛經及此類神經痛的苦楚。</Hediff_PainReliever.description>
<Hediff_Cyclosporine.label>環孢素</Hediff_Cyclosporine.label> <Hediff_Cyclosporine.label>環孢素</Hediff_Cyclosporine.label>
<Hediff_Cyclosporine.description>由於免疫抑製劑的作用,身體抵抗感染和疾病的能力受扼制。</Hediff_Cyclosporine.description> <Hediff_Cyclosporine.description>由於免疫抑製劑的作用,身體抵抗感染和疾病的能力受扼制。</Hediff_Cyclosporine.description>
<!--Auto generated: Wed Aug 30 16:05:07 2023-->
<Hediff_AffectedByPheromones.label>受費洛蒙影響</Hediff_AffectedByPheromones.label>
<Hediff_AffectedByPheromones.description>受附近某人處於發情期之故,此人的性需求及娛樂需求正急遽上升。</Hediff_AffectedByPheromones.description>
<Hediff_AffectedByPheromones.stages.0.label></Hediff_AffectedByPheromones.stages.0.label>
<Hediff_AffectedByPheromones.stages.1.label></Hediff_AffectedByPheromones.stages.1.label>
<Hediff_AffectedByPheromones.stages.2.label></Hediff_AffectedByPheromones.stages.2.label>
</LanguageData> </LanguageData>

View file

@ -11,4 +11,13 @@
<!--Auto generated: Thu Jan 5 22:13:15 2023--> <!--Auto generated: Thu Jan 5 22:13:15 2023-->
<EggRestorationReceived.stages.0.label>卵母細胞再生術</EggRestorationReceived.stages.0.label> <EggRestorationReceived.stages.0.label>卵母細胞再生術</EggRestorationReceived.stages.0.label>
<EggRestorationReceived.stages.0.description>我可以繼續繁衍一小段時間了!</EggRestorationReceived.stages.0.description> <EggRestorationReceived.stages.0.description>我可以繼續繁衍一小段時間了!</EggRestorationReceived.stages.0.description>
<!--Auto generated: Wed Aug 30 16:05:07 2023-->
<CameInsideMIdeo.stages.0.label>內射了{0}</CameInsideMIdeo.stages.0.label>
<CameInsideMIdeo.stages.0.description>讓她懷孕正是我的職責所在。</CameInsideMIdeo.stages.0.description>
<CameInsideFIdeo.stages.0.label>{0}內射了我</CameInsideFIdeo.stages.0.label>
<CameInsideFIdeo.stages.0.description>我希望這次能懷上,讓我盡到責任。</CameInsideFIdeo.stages.0.description>
<HaterCameInsideFIdeo.stages.0.label>{0}內射了我</HaterCameInsideFIdeo.stages.0.label>
<HaterCameInsideFIdeo.stages.0.description>我知道我有生育的義務,但是難道非他不可嗎?</HaterCameInsideFIdeo.stages.0.description>
<HateTookContraceptivePillIdeo.stages.0.label>服用避孕藥</HateTookContraceptivePillIdeo.stages.0.label>
<HateTookContraceptivePillIdeo.stages.0.description>我的信仰要求我能夠懷孕。</HateTookContraceptivePillIdeo.stages.0.description>
</LanguageData> </LanguageData>

View file

@ -154,4 +154,18 @@
<Option_EnableBiotechTwins_Label>(測試中!) 允許在「生機」(Biotech)追加的懷孕機制中出現多胞胎。</Option_EnableBiotechTwins_Label> <Option_EnableBiotechTwins_Label>(測試中!) 允許在「生機」(Biotech)追加的懷孕機制中出現多胞胎。</Option_EnableBiotechTwins_Label>
<Option_EnableBiotechTwins_Desc>啟用這個選項將允許同卵或異卵雙胞胎出現在「生機」(Biotech)追加的懷孕機制中。 <Option_EnableBiotechTwins_Desc>啟用這個選項將允許同卵或異卵雙胞胎出現在「生機」(Biotech)追加的懷孕機制中。
一併將啟用雜交系統;然而兩個類人(humanlikes)生物絕不可能生出動物。</Option_EnableBiotechTwins_Desc> 一併將啟用雜交系統;然而兩個類人(humanlikes)生物絕不可能生出動物。</Option_EnableBiotechTwins_Desc>
<!--Auto generated: Wed Aug 30 16:05:07 2023-->
<Option_ColonistUpdateInterval_Label>玩家小人刷新間隔</Option_ColonistUpdateInterval_Label>
<Option_ColonistUpdateInterval_Desc>殖民者、囚犯和奴隸的子宮狀態應該每隔多久刷新一次?
較小的刷新間隔可以更精確地模擬真實子宮;較大的刷新間隔可以提升遊戲流暢度</Option_ColonistUpdateInterval_Desc>
<Option_NonColonistUpdateInterval_Label>非玩家小人刷新間隔</Option_NonColonistUpdateInterval_Label>
<Option_NonColonistUpdateInterval_Desc>非玩家勢力控制下的小人的子宮應該每隔多久刷新一次?
較小的刷新間隔可以更精確地模擬真實子宮;較大的刷新間隔可以提升遊戲流暢度</Option_NonColonistUpdateInterval_Desc>
<Option_AnimalUpdateInterval_Label>動物子宮刷新間隔</Option_AnimalUpdateInterval_Label>
<Option_AnimalUpdateInterval_Desc>動物們的子宮應該多久刷新一次?
較小的刷新間隔可以更精確地模擬真實子宮;較大的刷新間隔可以提升遊戲流暢度</Option_AnimalUpdateInterval_Desc>
<Option_EnablePheromones_Label>啟用費洛蒙機制</Option_EnablePheromones_Label>
<Option_EnablePheromones_Desc>擁有發情期的類人生物在發情期將刺激附近的雄性類人生物,使其性慾大增。</Option_EnablePheromones_Desc>
<Option_AnimalPheromoneEffect_Label>動物費洛蒙效果</Option_AnimalPheromoneEffect_Label>
<Option_AnimalPheromoneEffect_Desc>選定擁有發情期的動物在發情期內對類人生物的影響。</Option_AnimalPheromoneEffect_Desc>
</LanguageData> </LanguageData>

View file

@ -37,7 +37,7 @@
<Dialog_WombInfo08></Dialog_WombInfo08> <Dialog_WombInfo08></Dialog_WombInfo08>
<Dialog_WombInfo09></Dialog_WombInfo09> <Dialog_WombInfo09></Dialog_WombInfo09>
<Dialog_FatherUnknown>Unknown</Dialog_FatherUnknown> <Dialog_FatherUnknown>Unknown</Dialog_FatherUnknown>
<Description_Absorbed>Absorbed</Description_Absorbed>
<Option1_Label_1>Enable womb icon</Option1_Label_1> <Option1_Label_1>Enable womb icon</Option1_Label_1>

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<LanguageData>
<Absorber.label>性器</Absorber.label>
</LanguageData>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<LanguageData>
<PainReliever.label>鎮痛剤</PainReliever.label>
<PainReliever.description>生理痛を24時間和らげます。&#10;&#10;その他の痛みにも効果あり。</PainReliever.description>
<Cyclosporine.label>シクロスポリン</Cyclosporine.label>
<Cyclosporine.description>免疫抑制剤。&#10;&#10;抗精子抗体を直すことができるが、24時間の間、体の免疫力は低下する。</Cyclosporine.description>
</LanguageData>

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<LanguageData>
<Hediff_MenstrualCramp.label>生理中</Hediff_MenstrualCramp.label>
<Hediff_MenstrualCramp.description>月経周期の終わりに膣からの出血。よく痛い。</Hediff_MenstrualCramp.description>
<Hediff_MenstrualCramp.stages.0.label>不快</Hediff_MenstrualCramp.stages.0.label>
<Hediff_MenstrualCramp.stages.1.label>痒い</Hediff_MenstrualCramp.stages.1.label>
<Hediff_MenstrualCramp.stages.2.label>痛い</Hediff_MenstrualCramp.stages.2.label>
<Hediff_MenstrualCramp.stages.3.label>苦痛</Hediff_MenstrualCramp.stages.3.label>
</LanguageData>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<LanguageData>
<VaginaWashing.reportString>膣を洗っている</VaginaWashing.reportString>
</LanguageData>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<LanguageData>
<MaxAbsorbable.label>吸収量</MaxAbsorbable.label>
<MaxAbsorbable.description>吸収可能な液体の最大量。</MaxAbsorbable.description>
</LanguageData>

View file

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<LanguageData>
<Absorber_Tampon.label>タンポン</Absorber_Tampon.label>
<Absorber_Tampon.description>膣内の体液を吸収するタンポン。長時間に着けて感染症を引き起こすことがある。</Absorber_Tampon.description>
<Absorber_Tampon_Dirty.label>汚れたタンポン</Absorber_Tampon_Dirty.label>
<Absorber_Tampon_Dirty.description>濡れた使用済みタンポン。そのままにしておくと感染症を引き起こす可能性がある。</Absorber_Tampon_Dirty.description>
<Absorber_Pad.label>生理用品</Absorber_Pad.label>
<Absorber_Pad.description>膣からの体液を吸収するためのナプキン。</Absorber_Pad.description>
<Absorber_Pad_Dirty.label>汚れた生理用品</Absorber_Pad_Dirty.label>
<Absorber_Pad_Dirty.description>濡れた使用済みナプキン</Absorber_Pad_Dirty.description>
<OvaryRegenerationPill.label>卵巣再生薬</OvaryRegenerationPill.label>
<OvaryRegenerationPill.description>卵巣を回復させ、卵子の数をある程度を回復させる。&#10;卵子がほとんど残っていない卵巣にはあまり効果がない。&#10;更年期の回復はできない。</OvaryRegenerationPill.description>
<SuperovulationInducingAgent.label>排卵剤</SuperovulationInducingAgent.label>
<SuperovulationInducingAgent.description>過排卵を誘発し、次の排卵で卵子を14個余分に産生させる。&#10;&#10;更年期を早める可能性がある。</SuperovulationInducingAgent.description>
<FilthMixture.label>混じった物</FilthMixture.label>
</LanguageData>

View file

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8" ?>
<LanguageData>
<LeakingFluids.stages.0.label>ぽたぽた</LeakingFluids.stages.0.label>
<LeakingFluids.stages.0.description>これを吸収するものはないのかな?</LeakingFluids.stages.0.description>
<HaterCameInsideM.stages.0.label>{0}に中出した</HaterCameInsideM.stages.0.label>
<HaterCameInsideM.stages.0.description>ライバルに中出した</HaterCameInsideM.stages.0.description>
<CameInsideM.stages.0.label>{0}に中出した</CameInsideM.stages.0.label>
<CameInsideM.stages.0.description>気持ちよかった。</CameInsideM.stages.0.description>
<CameInsideFFetish.stages.0.label>{0}に中出した</CameInsideFFetish.stages.0.label>
<CameInsideFFetish.stages.0.description>妊娠するといいな。</CameInsideFFetish.stages.0.description>
<HaterCameInsideF.stages.0.label>{0}に中出された</HaterCameInsideF.stages.0.label>
<HaterCameInsideF.stages.0.description>あいつの子を妊娠したくない!</HaterCameInsideF.stages.0.description>
<HaterCameInsideFEstrus.stages.0.label>{0}に中出された</HaterCameInsideFEstrus.stages.0.label>
<HaterCameInsideFEstrus.stages.0.description>そんなことをさせるなんて、私は何を考えていたんだろう?</HaterCameInsideFEstrus.stages.0.description>
<CameInsideFLowFert.stages.0.label>{0}に中出された</CameInsideFLowFert.stages.0.label>
<CameInsideFLowFert.stages.0.description>可能性は低いけど、妊娠できるかどうか心配で...</CameInsideFLowFert.stages.0.description>
<CameInsideF.stages.0.label>{0}に中出された</CameInsideF.stages.0.label>
<CameInsideF.stages.0.description>妊娠しないか心配なんだ。</CameInsideF.stages.0.description>
<CameInsideFFetishSafe.stages.0.label>{0}に中出された</CameInsideFFetishSafe.stages.0.label>
<CameInsideFFetishSafe.stages.0.description>妊娠するとは思わないけど、妄想するのは楽しい。</CameInsideFFetishSafe.stages.0.description>
<HaterCameInsideFSafe.stages.0.label>{0}に中出された</HaterCameInsideFSafe.stages.0.label>
<HaterCameInsideFSafe.stages.0.description>妊娠することはないだろうけど、やっぱり嫌だ。</HaterCameInsideFSafe.stages.0.description>
<UnwantedPregnancy.stages.0.label>望まない妊娠</UnwantedPregnancy.stages.0.label>
<UnwantedPregnancy.stages.0.description>赤ん坊の世話なんてできるか?どうすればいい?</UnwantedPregnancy.stages.0.description>
<UnwantedPregnancyMild.stages.0.label>望まない妊娠</UnwantedPregnancyMild.stages.0.label>
<UnwantedPregnancyMild.stages.0.description>赤ちゃんが生まれる。なんとかなるといいな。</UnwantedPregnancyMild.stages.0.description>
<TookContraceptivePill.stages.0.label>避妊ピルを飲んだ</TookContraceptivePill.stages.0.label>
<TookContraceptivePill.stages.0.description>もう大丈夫です。</TookContraceptivePill.stages.0.description>
<HateTookContraceptivePill.stages.0.label>避妊ピルを飲んだ</HateTookContraceptivePill.stages.0.label>
<HateTookContraceptivePill.stages.0.description>妊娠したい。</HateTookContraceptivePill.stages.0.description>
<EggRestorationReceived.stages.0.label>卵巣復活を得た</EggRestorationReceived.stages.0.label>
<EggRestorationReceived.stages.0.description>もう少しの間、繁殖ことができる。</EggRestorationReceived.stages.0.description>
</LanguageData>

View file

@ -0,0 +1,152 @@
<?xml version="1.0" encoding="utf-8"?>
<LanguageData>
<Mod_Title>RJW Menstruation Cycle</Mod_Title>
<Menstrual_Blood>経血</Menstrual_Blood>
<Info_noCum>空っぽ</Info_noCum>
<Stage_Follicular>卵胞期</Stage_Follicular>
<Stage_Ovulatory>排卵期</Stage_Ovulatory>
<Stage_Luteal>黄体期</Stage_Luteal>
<Stage_Bleeding>月経期</Stage_Bleeding>
<Stage_Pregnant>妊娠</Stage_Pregnant>
<Stage_Recover>出産後</Stage_Recover>
<Stage_None>無し</Stage_None>
<Stage_Climacteric>クリマクテリック</Stage_Climacteric>
<Stage_Menopause>更年期</Stage_Menopause>
<Stage_Anestrus>無発情期</Stage_Anestrus>
<Stage_Follicular_Desc>卵巣は卵子を放出するの準備をしています。この時期の終わりに排卵が起こります。</Stage_Follicular_Desc>
<Stage_Follicular_Induced_Desc>卵巣は卵子を放出するの準備をしています。精液が子宮に入ると排卵が起こります。</Stage_Follicular_Induced_Desc>
<Stage_Ovulatory_Desc>卵巣が卵子を子宮に放出しています。</Stage_Ovulatory_Desc>
<Stage_Luteal_Desc>子宮は受精卵を受け入れる準備ができている。この時期が終る前に受精卵が着床すれば妊娠となります。</Stage_Luteal_Desc>
<Stage_Bleeding_Desc>子宮内膜は剥がれ落ち、着床できなかった受精卵を排出します。</Stage_Bleeding_Desc>
<Stage_Pregnant_Desc>子宮に赤ちゃんがいます。時間とケアによって、この世界の新しい住人となる。</Stage_Pregnant_Desc>
<Stage_Recover_Desc>子宮は最近の妊娠から回復しています。</Stage_Recover_Desc>
<Stage_None_Desc>子宮は繁殖能力がなく、妊娠することはできない。</Stage_None_Desc>
<Stage_Climacteric_Desc>卵巣が枯渇すると、月経周期が不規則になる。</Stage_Climacteric_Desc>
<Stage_Menopause_Desc>。卵巣が枯渇し、子宮が卵子を産み出せなくなる。</Stage_Menopause_Desc>
<Stage_Anestrus_Desc>繁殖期を過ぎた。時期が来れば発情周期は再開する。</Stage_Anestrus_Desc>
<Button_HealthTab>ステータス</Button_HealthTab>
<Button_MilkTooltip>自分を搾乳する</Button_MilkTooltip>
<Dialog_WombInfo01>ステータス</Dialog_WombInfo01>
<Dialog_WombInfo02>胎児</Dialog_WombInfo02>
<Dialog_WombInfo03>父親</Dialog_WombInfo03>
<Dialog_WombInfo04>精液リスト</Dialog_WombInfo04>
<Dialog_WombInfo05>受精卵</Dialog_WombInfo05>
<Dialog_WombInfo06>受精中</Dialog_WombInfo06>
<Dialog_WombInfo07>排卵済み</Dialog_WombInfo07>
<Dialog_WombInfo08></Dialog_WombInfo08>
<Dialog_WombInfo09></Dialog_WombInfo09>
<Dialog_DoCleanWomb_Tooltip>精液をバケッツに集める</Dialog_DoCleanWomb_Tooltip>
<Dialog_DontCleanWomb_Tooltip>子宮に精液をためる</Dialog_DontCleanWomb_Tooltip>
<Dialog_FatherUnknown>不明</Dialog_FatherUnknown>
<Option1_Label_1>Enable womb icon</Option1_Label_1>
<Option1_Label_2>Enable button in health tab</Option1_Label_2>
<Option2_Label>Enable animal cycle</Option2_Label>
<Option2_Desc>Simulate animal cycles.&#10;Not recommended.</Option2_Desc>
<Option3_Label>Implantation chance</Option3_Label>
<Option3_Desc>Base implantation chance of fertilized egg&#10;This value affects the chance of impregnation.</Option3_Desc>
<Option4_Label>Fertilization chance</Option4_Label>
<Option4_Desc>The fertilization chance per ml of sperm per hour&#10;This value affects the chance of impregnation.</Option4_Desc>
<Option5_Label>Cum decay ratio per hour</Option5_Label>
<Option5_Desc>The amount of cum in the womb will drop by this amount every hour&#10;This value affects the chance of impregnation.</Option5_Desc>
<Option6_Label>Cum fertility decay ratio per hour</Option6_Label>
<Option6_Desc>Cum will lose fertility by this amount every hour&#10;This value affects fertilization chance indirectly.</Option6_Desc>
<Option7_Label>Cycle acceleration</Option7_Label>
<Option7_Desc>Accelerate menstruation cycle&#10;This can cause early menopause and infertility.&#10;Setting this lower than x12 is recommended.&#10;Rimworld's timescale: x6(default)</Option7_Desc>
<Option_ColonistUpdateInterval_Label>Colonist update interval</Option_ColonistUpdateInterval_Label>
<Option_ColonistUpdateInterval_Desc>How often the womb of each of your colonists, prisoners, and slaves update.&#10;Lowering this will improve accuracy, increasing this can improve performance.</Option_ColonistUpdateInterval_Desc>
<Option_NonColonistUpdateInterval_Label>Non-colonist update interval</Option_NonColonistUpdateInterval_Label>
<Option_NonColonistUpdateInterval_Desc>How often the womb of humans you don't control update.&#10;Lowering this will improve accuracy, increasing this can improve performance.</Option_NonColonistUpdateInterval_Desc>
<Option_AnimalUpdateInterval_Label>Animal update interval</Option_AnimalUpdateInterval_Label>
<Option_AnimalUpdateInterval_Desc>How often the womb of animals update.&#10;Lowering this will improve accuracy, increasing this can improve performance.</Option_AnimalUpdateInterval_Desc>
<Option8_Label>Debug</Option8_Label>
<Option8_Desc>Show debug information.</Option8_Desc>
<Option9_Label>Womb status</Option9_Label>
<Option9_Desc>Draw womb icon in status window.</Option9_Desc>
<Option10_Label>Vagina and breast status</Option10_Label>
<Option10_Desc>Draw vagina, anus and breast icons in the status window.</Option10_Desc>
<Option11_Label>Fetus information level</Option11_Label>
<Option11_Desc_1>Show all information about a fetus.</Option11_Desc_1>
<Option11_Desc_2>Show all information about a fetus after discovered pregnancy.</Option11_Desc_2>
<Option11_Desc_3>Show only image of a fetus after discovered pregnancy.</Option11_Desc_3>
<Option11_Desc_4>Do not show any information about a fetus.</Option11_Desc_4>
<Option12_Label>Enable menopause</Option12_Label>
<Option12_Desc>Enable menopause effect that makes pawn infertile when they run out of eggs&#10;If you have problems with long lived races, turn off this option.</Option12_Desc>
<Option13_Label>Use multiple pregnancy</Option13_Label>
<Option13_Desc>Use multiple pregnancy instead RJW's default pregnancy&#10;Disable this option if you are in trouble with impregnation&#10;RJW pregnancy should be turned on.</Option13_Desc>
<Option14_Label>Enable hetero ovular twins</Option14_Label>
<Option14_Desc>Allow pregnancy from multiple eggs at the same time.</Option14_Desc>
<Option15_Label>Enable enzygotic twins</Option15_Label>
<Option15_Desc>Enable a single egg to result in multiple identical offspring.</Option15_Desc>
<Option16_Label>Enzygotic twins chance</Option16_Label>
<Option16_Desc>The chance of identical twins.</Option16_Desc>
<Option17_Label>Max enzygotic twins</Option17_Label>
<Option17_Desc>The maximum number of identical siblings.</Option17_Desc>
<Option18_Label>Enable egg overlay</Option18_Label>
<Option18_Desc>Enable egg overlay on womb icon.</Option18_Desc>
<Option19_Label_1>Bleeding amount</Option19_Label_1>
<Option19_Label_2>Estimated total bleeding amount</Option19_Label_2>
<Option19_Desc>The approximate bleeding amount.&#10;The normal bleeding amount of a human is about 20~80ml per cycle.</Option19_Desc>
<Option20_Label_1>Colonist</Option20_Label_1>
<Option20_Label_2>Prisoner</Option20_Label_2>
<Option20_Label_3>Allied Faction</Option20_Label_3>
<Option20_Label_4>Neutral</Option20_Label_4>
<Option20_Label_5>Hostile Faction</Option20_Label_5>
<Option21_Label>Target pawns:</Option21_Label>
<Option21_Desc>The gizmo and button will appear for these pawns</Option21_Desc>
<Option22_Label>Use hybrid extension</Option22_Label>
<Option22_Desc>Overrides RJW and RaceSupport's hybrid definition.&#10;Dominant hybrid extension determines whose definition used first. Not recommended to change this.</Option22_Desc>
<Option23_Label>Dominant hybrid extension</Option23_Label>
<Option23_Label_1></Option23_Label_1>
<Option23_Label_2></Option23_Label_2>
<Option_MaxBreastIncrementFactor_Label>Breast growth during pregnancy</Option_MaxBreastIncrementFactor_Label>
<Option_MaxBreastIncrementFactor_Desc>Change how much a pregnant pawn's breasts will grow when pregnant. Some pawns will grow more than others.</Option_MaxBreastIncrementFactor_Desc>
<Option_MaxNippleIncrementFactor_Label>Nipple change during pregnancy</Option_MaxNippleIncrementFactor_Label>
<Option_MaxNippleIncrementFactor_Desc>Change how much a pregnant pawn's nipples will change during pregnancy.</Option_MaxNippleIncrementFactor_Desc>
<Option_PermanentNippleChange_Label>Permanent nipple change after pregnancy</Option_PermanentNippleChange_Label>
<Option_PermanentNippleChange_Desc>Adjusts approximately how much of a pregnant pawn's nipples will remain changed after the pregnancy ends.</Option_PermanentNippleChange_Desc>
<Option28_Label>Customize Hybrids</Option28_Label>
<Option28_Tooltip>Open custom hybrid editor.&#10;This will overrides hybrid definitions of XML files.</Option28_Tooltip>
<Option29_Label>Allow shrink icon</Option29_Label>
<Option29_Desc>Let icon become smaller if needed.</Option29_Desc>
<Option30_Label>Egg lifespan multiplier</Option30_Label>
<Option30_Desc>Multiply egg's lifespan.&#10;All non-implanted eggs will die at end of luteal stage regardless of this setting.</Option30_Desc>
<Option31_Label>Enable vagina morph after birth</Option31_Label>
<Option31_Desc>Enable permanent vagina stretch after birth.&#10;If you are using another mod handling this, turn off this option.</Option31_Desc>
<Option32_Label>Morph power</Option32_Label>
<Option32_Desc>Set morph power.</Option32_Desc>
<Option_EnableGatherCumGizmo_Label>Enable gather cum gizmo</Option_EnableGatherCumGizmo_Label>
<Option_EstrusOverride_Label>Estrus overrides RJW hookup settings</Option_EstrusOverride_Label>
<Option_EstrusOverride_Desc>If enabled, a pawn in visible estrus will use these settings for potential impregnation hookups instead of the RJW settings.&#10;All settings default to their RJW counterparts.</Option_EstrusOverride_Desc>
<Option_EstrusFuckability_Label>Hookup minimum fuckability in estrus</Option_EstrusFuckability_Label>
<Option_EstrusAttractability_Label>Hookup minimum attractability in estrus</Option_EstrusAttractability_Label>
<Option_EstrusRelationship_Label>Hookup minimum opinion in estrus</Option_EstrusRelationship_Label>
<EstimatedCumLifespan>Estimated sperm lifespan</EstimatedCumLifespan>
<EstimatedEggLifespan>Estimated egg lifespan</EstimatedEggLifespan>
<OvulationChanceLabel>排卵 {0}</OvulationChanceLabel>
<OvulationChanceDesc>排卵時に各卵が放出される確率。</OvulationChanceDesc>
<FertilityDesc>受精卵の着床確率。&#10;この時間に受精する確率{0}%</FertilityDesc>
<Option_PregnancyFromBaseRJW_Label>Use basic RJW pregnancy</Option_PregnancyFromBaseRJW_Label>
<Option_PregnancyFromMultiplePregnancy_Label>Use menstruation multiple pregnancy</Option_PregnancyFromMultiplePregnancy_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_Desc>Draw womb icon for drafted pawns</Option_EnableDraftedIcon_Desc>
<Button_ResetToDefault>Reset to default</Button_ResetToDefault>
<Gizmo_GatherCum>精液を収集</Gizmo_GatherCum>
<FloatMenu_CleanSelf>膣を洗う</FloatMenu_CleanSelf>
<CustomHybrid_List_Title>Custom Hybrid Editor</CustomHybrid_List_Title>
<CustomHybrid_Title>Hybrids of {0}</CustomHybrid_Title>
<CustomHybrid_Tooltip>When {0} breed with {1}, {2} will be born at {3} chance.&#10;If both races have hybrid definitions for each other, the father's definition will be used.</CustomHybrid_Tooltip>
<CannotNoEggs>卵子がない</CannotNoEggs>
<CannotNoWomb>子宮が必要</CannotNoWomb>
<EggRestorationCompleted>{PAWN_labelShort} has completed {PAWN_possessive} egg restoration cycle.</EggRestorationCompleted>
</LanguageData>

View file

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<Defs>
<JobDef>
<defName>LactateSelf_MC</defName>
<driverClass>MilkModule.JobDriver_MilkSelf_MC</driverClass>
<reportString>lactating self</reportString>
<allowOpportunisticPrefix>true</allowOpportunisticPrefix>
</JobDef>
</Defs>

View file

@ -0,0 +1,73 @@
<?xml version="1.0" encoding="utf-8"?>
<Patch>
<Operation Class="PatchOperationFindMod">
<mods>
<li>Alpha Genes</li>
</mods>
<match Class="PatchOperationAddModExtension">
<xpath>/Defs/GeneDef[defName="AG_Egglaying"]</xpath>
<value>
<li Class="RJW_Menstruation.MenstruationModExtension">
<disableCycle>true</disableCycle>
</li>
</value>
</match>
</Operation>
<Operation Class="PatchOperationFindMod">
<mods>
<li>Vanilla Races Expanded - Saurid</li>
</mods>
<match Class="PatchOperationAddModExtension">
<xpath>/Defs/GeneDef[defName="VRESaurids_Oviparous"]</xpath>
<value>
<li Class="RJW_Menstruation.MenstruationModExtension">
<disableCycle>true</disableCycle>
</li>
</value>
</match>
</Operation>
<Operation Class="PatchOperationFindMod">
<mods>
<li>Vanilla Races Expanded - Phytokin</li>
</mods>
<match Class="PatchOperationAddModExtension">
<xpath>/Defs/GeneDef[defName="VRE_SaplingBirth"]</xpath>
<value>
<li Class="RJW_Menstruation.MenstruationModExtension">
<disableCycle>true</disableCycle>
</li>
</value>
</match>
</Operation>
<Operation Class="PatchOperationFindMod">
<mods>
<li>Erin's Corvyia</li>
</mods>
<match Class="PatchOperationAddModExtension">
<xpath>/Defs/GeneDef[defName="ERN_EggLayer"]</xpath>
<value>
<li Class="RJW_Menstruation.MenstruationModExtension">
<disableCycle>true</disableCycle>
</li>
</value>
</match>
</Operation>
<Operation Class="PatchOperationFindMod">
<mods>
<li>Outland - Genetics</li>
</mods>
<match Class="PatchOperationAddModExtension">
<xpath>/Defs/GeneDef[defName="Outland_EggLayer"]</xpath>
<value>
<li Class="RJW_Menstruation.MenstruationModExtension">
<disableCycle>true</disableCycle>
</li>
</value>
</match>
</Operation>
</Patch>

View file

@ -144,11 +144,5 @@
</li> </li>
</comps> </comps>
</value> </value>
</Operation> </Operation>
</Patch> </Patch>

View file

@ -5,8 +5,6 @@ VisualStudioVersion = 17.3.32929.385
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RJW_Menstruation", "RJW_Menstruation\RJW_Menstruation.csproj", "{EED2F3B9-8C20-4194-919E-8D151B29F70B}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RJW_Menstruation", "RJW_Menstruation\RJW_Menstruation.csproj", "{EED2F3B9-8C20-4194-919E-8D151B29F70B}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MilkModule", "MilkModule\MilkModule.csproj", "{3591B3C1-EB57-44BF-AB69-A613E097A7F8}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -17,10 +15,6 @@ Global
{EED2F3B9-8C20-4194-919E-8D151B29F70B}.Debug|Any CPU.Build.0 = Debug|Any CPU {EED2F3B9-8C20-4194-919E-8D151B29F70B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EED2F3B9-8C20-4194-919E-8D151B29F70B}.Release|Any CPU.ActiveCfg = Release|Any CPU {EED2F3B9-8C20-4194-919E-8D151B29F70B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EED2F3B9-8C20-4194-919E-8D151B29F70B}.Release|Any CPU.Build.0 = Release|Any CPU {EED2F3B9-8C20-4194-919E-8D151B29F70B}.Release|Any CPU.Build.0 = Release|Any CPU
{3591B3C1-EB57-44BF-AB69-A613E097A7F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3591B3C1-EB57-44BF-AB69-A613E097A7F8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3591B3C1-EB57-44BF-AB69-A613E097A7F8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3591B3C1-EB57-44BF-AB69-A613E097A7F8}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View file

@ -144,11 +144,13 @@ namespace RJW_Menstruation
Scribe_Values.Look(ref baseAlpha, "baseAlpha", baseAlpha, true); Scribe_Values.Look(ref baseAlpha, "baseAlpha", baseAlpha, true);
Scribe_Values.Look(ref baseAreola, "baseAreola", baseAreola, true); Scribe_Values.Look(ref baseAreola, "baseAreola", baseAreola, true);
Scribe_Values.Look(ref baseNipple, "baseNipple", baseNipple, true); Scribe_Values.Look(ref baseNipple, "baseNipple", baseNipple, true);
if (Scribe.mode == LoadSaveMode.PostLoadInit)
Initialize();
} }
public bool ShouldSimulate() public bool ShouldSimulate()
{ {
if (!Configurations.EnableAnimalCycle && Pawn.IsAnimal()) return false; if (!Pawn.ShouldCycle()) return false;
if (Pawn.SpawnedOrAnyParentSpawned || Pawn.IsCaravanMember() || PawnUtility.IsTravelingInTransportPodWorldObject(Pawn)) return true; if (Pawn.SpawnedOrAnyParentSpawned || Pawn.IsCaravanMember() || PawnUtility.IsTravelingInTransportPodWorldObject(Pawn)) return true;
return false; return false;

View file

@ -30,7 +30,7 @@ namespace RJW_Menstruation
switch (CurrentVisibleStage) switch (CurrentVisibleStage)
{ {
case Stage.Follicular: case Stage.Follicular:
return Translations.Stage_Follicular_Induced_Desc + (EggHealth < 1f ? Translations.Stage_Climacteric_Desc : ""); return Translations.Stage_Follicular_Induced_Desc + (EggHealth < 1f ? " " + Translations.Stage_Climacteric_Desc : "");
default: default:
return base.GetCurStageDesc; return base.GetCurStageDesc;
} }
@ -106,19 +106,10 @@ namespace RJW_Menstruation
protected override bool ShouldBeInEstrus() protected override bool ShouldBeInEstrus()
{ {
if (!loaded) if (curStage == Stage.Luteal && !hadOvulatoryStage) return false;
Initialize(); else return base.ShouldBeInEstrus();
switch (curStage)
{
case Stage.Follicular:
return curStageTicks > currentIntervalTicks - Props.estrusDaysBeforeOvulation * GenDate.TicksPerDay;
case Stage.Ovulatory:
return true;
case Stage.Luteal:
return hadOvulatoryStage && curStageTicks < EggLifespanTicks;
default:
return false;
}
} }
protected override float RandomOvulationChance => 0;
} }
} }

View file

@ -127,7 +127,6 @@ namespace RJW_Menstruation
protected float implantationChanceCache = -1.0f; protected float implantationChanceCache = -1.0f;
protected int opcache = -1; protected int opcache = -1;
protected float antisperm = 0.0f; protected float antisperm = 0.0f;
protected float? originvagsize = null;
// RJW pregnancy, or Biotech pregnancy/labor/laborpushing // RJW pregnancy, or Biotech pregnancy/labor/laborpushing
protected Hediff pregnancy = null; protected Hediff pregnancy = null;
@ -179,7 +178,8 @@ namespace RJW_Menstruation
public float HoursBetweenSimulations => (float)TickInterval / GenDate.TicksPerHour; public float HoursBetweenSimulations => (float)TickInterval / GenDate.TicksPerHour;
public Hediff Pregnancy { public Hediff Pregnancy
{
get get
{ {
if (pregnancy == null) return null; if (pregnancy == null) return null;
@ -198,20 +198,9 @@ namespace RJW_Menstruation
get get
{ {
if (opcache > 0) return opcache; if (opcache > 0) return opcache;
float avglittersize;
try
{
avglittersize = Mathf.Max(Rand.ByCurveAverage(Pawn.RaceProps.litterSizeCurve), 1.0f);
}
catch
{
// Any exceptions in that will have been reported elsewhere in the code by now
avglittersize = 1.0f;
};
avglittersize *= ovulationFactor;
const float yearsBeforeMenopause = 6.0f; const float yearsBeforeMenopause = 6.0f;
opcache = (int)(RaceCyclesPerYear() * opcache = (int)(RaceCyclesPerYear() *
avglittersize * AverageLitterSize() *
yearsBeforeMenopause * yearsBeforeMenopause *
(Pawn.RaceProps.lifeExpectancy / ThingDefOf.Human.race.lifeExpectancy)); (Pawn.RaceProps.lifeExpectancy / ThingDefOf.Human.race.lifeExpectancy));
if (opcache == 0) opcache = 1; if (opcache == 0) opcache = 1;
@ -266,7 +255,7 @@ namespace RJW_Menstruation
public float TotalCum public float TotalCum
{ {
get => cums?.Sum(cum => cum.Volume) ?? 0; get => cums?.Sum(cum => cum.Volume) ?? 0;
} }
public float TotalFertCum public float TotalFertCum
{ {
@ -290,7 +279,7 @@ namespace RJW_Menstruation
{ {
get get
{ {
if (QuirkUtility.HasQuirk(Pawn, QuirkUtility.Quirks.Breeder)) return 0.5f; if (Pawn.IsBreeder()) return 0.5f;
return 1.0f; return 1.0f;
} }
@ -300,7 +289,7 @@ namespace RJW_Menstruation
private bool calculatingOvulationChance = false; private bool calculatingOvulationChance = false;
public bool CalculatingOvulationChance { get => calculatingOvulationChance; } public bool CalculatingOvulationChance { get => calculatingOvulationChance; }
protected float CalcuatedOvulationChance() protected float CalculatedOvulationChance()
{ {
float ovulationChance = 1.0f; float ovulationChance = 1.0f;
if (EggHealth <= 0.0f) return 0.0f; if (EggHealth <= 0.0f) return 0.0f;
@ -308,20 +297,14 @@ namespace RJW_Menstruation
if (ModsConfig.BiotechActive && xxx.is_human(Pawn)) if (ModsConfig.BiotechActive && xxx.is_human(Pawn))
{ {
if (Pawn.SterileGenes()) return 0.0f; if (Pawn.SterileGenes()) return 0.0f;
// Replicate how rjw.PawnCapacityWorker_Fertility.CalculateCapacityLevel does it, but without the age factor
if (!Pawn.RaceHasFertility()) return 0.0f; if (!Pawn.RaceHasFertility()) return 0.0f;
if (AndroidsCompatibility.IsAndroid(Pawn) && parent.def != Genital_Helper.archotech_vagina) return 0.0f; if (AndroidsCompatibility.IsAndroid(Pawn) && parent.def != Genital_Helper.archotech_vagina) return 0.0f;
foreach (var part in StatDefOf.Fertility.parts)
{ float ageFactor = 1.0f;
if(part is StatPart_FertilityByGenderAge fertilityByAge) StatDefOf.Fertility.GetStatPart<StatPart_FertilityByGenderAge>()?.TransformValue(StatRequest.For(Pawn), ref ageFactor);
{ if (ageFactor <= 0.0f) return 0.0f; // Too young or too old
float factor = 1.0f;
fertilityByAge.TransformValue(StatRequest.For(Pawn), ref factor); if (Pawn.IsBreeder()) ovulationChance *= 10.0f;
if (factor <= 0.0f) return 0.0f; // Too young or too old
}
else part.TransformValue(StatRequest.For(Pawn), ref ovulationChance);
}
if (Pawn.HasQuirk(QuirkUtility.Quirks.Breeder)) ovulationChance *= 10.0f;
try try
{ {
calculatingOvulationChance = true; calculatingOvulationChance = true;
@ -332,20 +315,20 @@ namespace RJW_Menstruation
return ovulationChance; return ovulationChance;
} }
protected float CalcuatedImplantChance() protected float CalculatedImplantChance()
{ {
float factor = 1.0f;
if (ModsConfig.BiotechActive && xxx.is_human(Pawn)) if (ModsConfig.BiotechActive && xxx.is_human(Pawn))
{ {
// Implant factor will be based solely on pawn age, plus any rollover from ovulation chance // Implant factor will be based solely on pawn age, plus any rollover from ovulation chance
StatPart_FertilityByGenderAge fertilityStatPart = StatDefOf.Fertility.GetStatPart<StatPart_FertilityByGenderAge>(); float factor = 1.0f;
fertilityStatPart?.TransformValue(StatRequest.For(Pawn), ref factor); StatDefOf.Fertility.GetStatPart<StatPart_FertilityByGenderAge>()?.TransformValue(StatRequest.For(Pawn), ref factor);
if (factor <= 0.0f) return 0.0f;
if (OvulationChance > 1.0f) factor *= OvulationChance; if (OvulationChance > 1.0f) factor *= OvulationChance;
return Props.baseImplantationChanceFactor * FertilityModifier * factor; return Props.baseImplantationChanceFactor * FertilityModifier * factor;
} }
else else
{ {
return Pawn.health.capacities.GetLevel(xxx.reproduction) * Props.baseImplantationChanceFactor * FertilityModifier * factor; return Pawn.health.capacities.GetLevel(xxx.reproduction) * Props.baseImplantationChanceFactor * FertilityModifier;
} }
} }
@ -353,16 +336,17 @@ namespace RJW_Menstruation
{ {
get get
{ {
if (ovulationChanceCache < 0.0f) ovulationChanceCache = CalcuatedOvulationChance(); if (ovulationChanceCache < 0.0f) ovulationChanceCache = CalculatedOvulationChance();
return ovulationChanceCache; return ovulationChanceCache;
} }
} }
// Before configuration setting
public float ImplantChance public float ImplantChance
{ {
get get
{ {
if (implantationChanceCache < 0.0f) implantationChanceCache = CalcuatedImplantChance(); if (implantationChanceCache < 0.0f) implantationChanceCache = CalculatedImplantChance();
return implantationChanceCache; return implantationChanceCache;
} }
} }
@ -580,19 +564,6 @@ namespace RJW_Menstruation
} }
} }
public float OriginVagSize
{
get
{
if (originvagsize == null)
{
originvagsize = parent.Severity;
}
return originvagsize ?? 0.1f;
}
set => originvagsize = value;
}
public int CurStageIntervalTicks public int CurStageIntervalTicks
{ {
get => currentIntervalTicks; get => currentIntervalTicks;
@ -625,7 +596,7 @@ namespace RJW_Menstruation
else if (Pawn.story?.bodyType == BodyTypeDefOf.Female) discoveryTime = 0.35f; else if (Pawn.story?.bodyType == BodyTypeDefOf.Female) discoveryTime = 0.35f;
// Estimated; there's no way to get the exact value after the fact without writing it into the save // Estimated; there's no way to get the exact value after the fact without writing it into the save
float lutealProgressWhenImplanted = Math.Min(0.5f, maxImplantDelayHours / (Props.lutealIntervalDays * GenDate.HoursPerDay)); float lutealProgressWhenImplanted = Math.Min(0.5f, maxImplantDelayHours / (Props.lutealIntervalDays * GenDate.HoursPerDay));
return GenMath.LerpDouble(0, discoveryTime, lutealProgressWhenImplanted, 1.0f, pregnancy.Severity); return GenMath.LerpDouble(0, discoveryTime, lutealProgressWhenImplanted, 1.0f, pregnancy.Severity);
} }
} }
@ -673,9 +644,10 @@ namespace RJW_Menstruation
Scribe_Values.Look(ref ovarypower, "ovarypower", ovarypower, true); Scribe_Values.Look(ref ovarypower, "ovarypower", ovarypower, true);
Scribe_Values.Look(ref eggstack, "eggstack", 0); Scribe_Values.Look(ref eggstack, "eggstack", 0);
Scribe_Values.Look(ref estrusflag, "estrusflag", false); Scribe_Values.Look(ref estrusflag, "estrusflag", false);
Scribe_Values.Look(ref originvagsize, "originvagsize", originvagsize, true);
Scribe_Values.Look(ref DoCleanWomb, "DoCleanWomb", false); Scribe_Values.Look(ref DoCleanWomb, "DoCleanWomb", false);
Scribe_References.Look(ref pregnancy, "pregnancy"); Scribe_References.Look(ref pregnancy, "pregnancy");
if (Scribe.mode == LoadSaveMode.PostLoadInit)
Initialize();
} }
@ -693,25 +665,24 @@ namespace RJW_Menstruation
estrusLevel = Props.concealedEstrus ? EstrusLevel.Concealed : EstrusLevel.Visible; estrusLevel = Props.concealedEstrus ? EstrusLevel.Concealed : EstrusLevel.Visible;
ovulationFactor = 1f; ovulationFactor = 1f;
noBleeding = false; noBleeding = false;
opcache = -1;
if (Pawn.genes == null || !ModsConfig.BiotechActive) return; if (Pawn.genes == null || !ModsConfig.BiotechActive) return;
foreach (MenstruationModExtension extension in Pawn.genes.GenesListForReading.Select(gene => gene.def.GetModExtension<MenstruationModExtension>()).Where(ext => ext != null))
if (Pawn.genes.HasGene(VariousDefOf.ShortEggLifetime)) eggLifeSpanTicks = eggLifeSpanTicks * 3 / 4; {
else if (Pawn.genes.HasGene(VariousDefOf.DoubleEggLifetime)) eggLifeSpanTicks *= 2; eggLifeSpanTicks = (int)(eggLifeSpanTicks * extension.eggLifeTimeFactor);
else if (Pawn.genes.HasGene(VariousDefOf.QuadEggLifetime)) eggLifeSpanTicks *= 4; if (extension.alwaysEstrus) estrusLevel = EstrusLevel.Visible;
else if (extension.neverEstrus) estrusLevel = EstrusLevel.None;
if (Pawn.genes.HasGene(VariousDefOf.NeverEstrus)) estrusLevel = EstrusLevel.None; ovulationFactor *= extension.ovulationFactor;
else if (Pawn.genes.HasGene(VariousDefOf.FullEstrus)) estrusLevel = EstrusLevel.Visible; if (extension.noBleeding) noBleeding = true;
}
if (Pawn.genes.HasGene(VariousDefOf.DoubleOvulation)) ovulationFactor = 2f; if (eggLifeSpanTicks < 0) eggLifeSpanTicks = 0;
else if (Pawn.genes.HasGene(VariousDefOf.QuadOvulation)) ovulationFactor = 4f; if (ovulationFactor < 0f) ovulationFactor = 0f;
noBleeding = Pawn.genes.HasGene(VariousDefOf.NoBleeding);
} }
public bool ShouldSimulate() public bool ShouldSimulate()
{ {
if (!Configurations.EnableAnimalCycle && Pawn.IsAnimal()) return false; if (!Pawn.ShouldCycle()) return false;
if (Pawn.SpawnedOrAnyParentSpawned || Pawn.IsCaravanMember() || PawnUtility.IsTravelingInTransportPodWorldObject(Pawn)) return true; if (Pawn.SpawnedOrAnyParentSpawned || Pawn.IsCaravanMember() || PawnUtility.IsTravelingInTransportPodWorldObject(Pawn)) return true;
return false; return false;
@ -733,6 +704,9 @@ namespace RJW_Menstruation
// If an exception makes it out, RW will remove the hediff, so catch it here // If an exception makes it out, RW will remove the hediff, so catch it here
try try
{ {
if (Pawn.IsHashIntervalTick(recalculateTickInterval)) TickInterval = -1; // Every so often, force TickInterval to be recalculated in case the pawn's status changed.
if (!Pawn.IsHashIntervalTick(TickInterval)) return;
if (!ShouldSimulate()) return; if (!ShouldSimulate()) return;
// Initialize immediately if needed, but if there's an error, then don't spam it every tick // Initialize immediately if needed, but if there's an error, then don't spam it every tick
@ -742,9 +716,6 @@ namespace RJW_Menstruation
Initialize(); Initialize();
} }
if (Pawn.IsHashIntervalTick(recalculateTickInterval)) TickInterval = -1; // Every so often, force TickInterval to be recalculated in case the pawn's status changed.
if (!Pawn.IsHashIntervalTick(TickInterval)) return;
if (initError) Log.Warning($"Attempting to process {Pawn}'s womb uninitialized"); if (initError) Log.Warning($"Attempting to process {Pawn}'s womb uninitialized");
if (Pregnancy != null && curStage != Stage.Pregnant) if (Pregnancy != null && curStage != Stage.Pregnant)
@ -754,7 +725,7 @@ namespace RJW_Menstruation
} }
BeforeSimulator(); BeforeSimulator();
if (ShouldBeInfertile()) GoNextStage(Stage.Infertile); if (ShouldBeInfertile()) GoNextStage(Stage.Infertile);
switch (curStage) switch (curStage)
{ {
@ -831,7 +802,7 @@ namespace RJW_Menstruation
tip.Append(": "); tip.Append(": ");
tip.Append(GetCurStageLabel); tip.Append(GetCurStageLabel);
string fertInfo = GetFertilizingInfo; string fertInfo = GetFertilizingInfo;
if(CurrentVisibleStage == Stage.Luteal && fertInfo.Length > 0) if (CurrentVisibleStage == Stage.Luteal && fertInfo.Length > 0)
{ {
tip.AppendLine(); tip.AppendLine();
tip.Append(fertInfo); tip.Append(fertInfo);
@ -842,7 +813,7 @@ namespace RJW_Menstruation
protected virtual int TicksToNextStage() protected virtual int TicksToNextStage()
{ {
return Math.Max(0,(currentIntervalTicks - curStageTicks) / Configurations.CycleAcceleration); return Math.Max(0, (currentIntervalTicks - curStageTicks) / Configurations.CycleAcceleration);
} }
public override string CompDebugString() public override string CompDebugString()
@ -887,8 +858,8 @@ namespace RJW_Menstruation
if (!precum && fertility > 0 && IsDangerDay && cummer.relations.GetPregnancyApproachForPartner(Pawn) == PregnancyApproach.AvoidPregnancy) if (!precum && fertility > 0 && IsDangerDay && cummer.relations.GetPregnancyApproachForPartner(Pawn) == PregnancyApproach.AvoidPregnancy)
{ {
float successChance = pulloutSuccessRate; float successChance = pulloutSuccessRate;
if (cummer.HasQuirk(QuirkUtility.Quirks.ImpregnationFetish)) successChance *= fetishPulloutSuccessModifier; if (cummer.HasImpregnationFetish()) successChance *= fetishPulloutSuccessModifier;
if (Pawn.HasQuirk(QuirkUtility.Quirks.ImpregnationFetish)) successChance *= fetishPulloutSuccessModifier; if (Pawn.HasImpregnationFetish()) successChance *= fetishPulloutSuccessModifier;
if (Rand.Chance(successChance)) return; if (Rand.Chance(successChance)) return;
} }
if (Pawn.HasIUD()) fertility /= 100f; if (Pawn.HasIUD()) fertility /= 100f;
@ -900,7 +871,7 @@ namespace RJW_Menstruation
bool merged = false; bool merged = false;
if (!cums.NullOrEmpty()) foreach (Cum cum in cums) if (!cums.NullOrEmpty()) foreach (Cum cum in cums)
{ {
if (cum.pawn.Equals(cummer)) if (cum.pawn?.Equals(cummer) ?? false)
{ {
cum.MergeWithCum(volume, fertility); cum.MergeWithCum(volume, fertility);
merged = true; merged = true;
@ -915,7 +886,7 @@ namespace RJW_Menstruation
bool merged = false; bool merged = false;
if (!cums.NullOrEmpty()) foreach (Cum cum in cums) if (!cums.NullOrEmpty()) foreach (Cum cum in cums)
{ {
if (cum.pawn.Equals(cummer)) if (cum.pawn?.Equals(cummer) ?? false)
{ {
cum.MergeWithCum(volume, fertility); cum.MergeWithCum(volume, fertility);
merged = true; merged = true;
@ -952,7 +923,7 @@ namespace RJW_Menstruation
bool merged = false; bool merged = false;
if (!cums.NullOrEmpty()) foreach (Cum cum in cums) if (!cums.NullOrEmpty()) foreach (Cum cum in cums)
{ {
if (cum.notcum && cum.pawn.Equals(cummer) && cum.notcumLabel.Equals(notcumlabel)) if (cum.notcum && (cum.pawn?.Equals(cummer) ?? false) && cum.notcumLabel.Equals(notcumlabel))
{ {
cum.MergeWithFluid(volume, decayresist, filthdef); cum.MergeWithFluid(volume, decayresist, filthdef);
merged = true; merged = true;
@ -967,7 +938,7 @@ namespace RJW_Menstruation
bool merged = false; bool merged = false;
if (!cums.NullOrEmpty()) foreach (Cum cum in cums) if (!cums.NullOrEmpty()) foreach (Cum cum in cums)
{ {
if (cum.notcum && cum.pawn.Equals(cummer) && cum.notcumLabel.Equals(notcumlabel)) if (cum.notcum && (cum.pawn?.Equals(cummer) ?? false) && cum.notcumLabel.Equals(notcumlabel))
{ {
cum.MergeWithFluid(volume, decayresist, filthdef); cum.MergeWithFluid(volume, decayresist, filthdef);
merged = true; merged = true;
@ -1187,7 +1158,7 @@ namespace RJW_Menstruation
if (cycleSpeed < 0f) cycleSpeed = Utility.RandGaussianLike(0.8f, 1.2f); if (cycleSpeed < 0f) cycleSpeed = Utility.RandGaussianLike(0.8f, 1.2f);
if (cycleVariability < 0f) cycleVariability = MenstruationUtility.RandomVariabilityPercent(); if (cycleVariability < 0f) cycleVariability = MenstruationUtility.RandomVariabilityPercent();
InitOvary(); InitOvary();
if (currentIntervalTicks < 0) if (currentIntervalTicks < 0)
@ -1221,6 +1192,21 @@ namespace RJW_Menstruation
{ {
} }
protected float AverageLitterSize()
{
float avglittersize;
try
{
avglittersize = Mathf.Max(Rand.ByCurveAverage(Pawn.RaceProps.litterSizeCurve), 1.0f);
}
catch (NullReferenceException)
{
avglittersize = 1.0f;
}
avglittersize *= ovulationFactor;
return avglittersize;
}
protected virtual float RaceCyclesPerYear() protected virtual float RaceCyclesPerYear()
{ {
int breedingSeasons = 0; int breedingSeasons = 0;
@ -1243,16 +1229,7 @@ namespace RJW_Menstruation
public int GetOvaryPowerByAge() public int GetOvaryPowerByAge()
{ {
float avglittersize; float avglittersize = AverageLitterSize();
try
{
avglittersize = Mathf.Max(Rand.ByCurveAverage(Pawn.RaceProps.litterSizeCurve), 1.0f);
}
catch (NullReferenceException)
{
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);
@ -1369,7 +1346,7 @@ namespace RJW_Menstruation
//float fertFailChancePerHour = Mathf.Pow(1.0f - Configurations.FertilizeChance, totalFertPower * Props.basefertilizationChanceFactor); //float fertFailChancePerHour = Mathf.Pow(1.0f - Configurations.FertilizeChance, totalFertPower * Props.basefertilizationChanceFactor);
//float fertFailChancePerInterval = Mathf.Pow(fertFailChancePerHour, (float)TickInterval / GenDate.TicksPerHour); //float fertFailChancePerInterval = Mathf.Pow(fertFailChancePerHour, (float)TickInterval / GenDate.TicksPerHour);
float fertFailChancePerInterval = Mathf.Pow(1.0f - Configurations.FertilizeChance, totalFertPower * Props.basefertilizationChanceFactor * HoursBetweenSimulations); float fertFailChancePerInterval = Mathf.Pow(1.0f - Configurations.FertilizeChance, totalFertPower * Props.basefertilizationChanceFactor * HoursBetweenSimulations);
if (Rand.Chance(fertFailChancePerInterval)) return null; if (Rand.Chance(fertFailChancePerInterval)) return null;
Pawn.records.AddTo(VariousDefOf.AmountofFertilizedEggs, 1); Pawn.records.AddTo(VariousDefOf.AmountofFertilizedEggs, 1);
@ -1412,97 +1389,116 @@ namespace RJW_Menstruation
} }
else if (Rand.Chance(Configurations.ImplantationChance * ImplantChance * InterspeciesImplantFactor(egg.fertilizer))) else if (Rand.Chance(Configurations.ImplantationChance * ImplantChance * InterspeciesImplantFactor(egg.fertilizer)))
{ {
if (Configurations.Debug) Log.Message($"Implanting fertilized egg of {Pawn} into {parent}, father {egg.fertilizer}"); try
if (pregnancy != null)
{ {
if (Configurations.PregnancySource == Configurations.PregnancyType.Biotech && Configurations.EnableBiotechTwins && Configurations.EnableHeteroOvularTwins) if (Configurations.Debug) Log.Message($"Implanting fertilized egg of {Pawn} into {parent}, father {egg.fertilizer}");
if (pregnancy == null)
{ {
if (Configurations.Debug) Log.Message($"Adding to existing Biotech pregnancy {pregnancy}"); Configurations.PregnancyType usePregnancy = xxx.is_human(Pawn) ? Configurations.PregnancySource : Configurations.PregnancyType.MultiplePregnancy;
HediffComp_PregeneratedBabies comp = pregnancy.TryGetComp<HediffComp_PregeneratedBabies>(); switch (usePregnancy)
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); case Configurations.PregnancyType.BaseRJW:
pregnant = true;
deadeggs.Add(egg); if (Configurations.Debug) Log.Message($"Creating new base RJW pregnancy");
PregnancyHelper.AddPregnancyHediff(Pawn, egg.fertilizer);
// I hate having to do this, but it gets the newest pregnancy
List<Hediff_BasePregnancy> pregnancies = new List<Hediff_BasePregnancy>();
Pawn.health.hediffSet.GetHediffs(ref pregnancies);
pregnancy = pregnancies.MaxBy(hediff => hediff.loadID);
pregnant = true;
break;
case Configurations.PregnancyType.MultiplePregnancy:
if (Configurations.Debug) Log.Message($"Creating new menstruation pregnancy");
pregnancy = Hediff_BasePregnancy.Create<Hediff_MultiplePregnancy>(Pawn, egg.fertilizer);
pregnant = true;
deadeggs.Add(egg);
break;
case Configurations.PregnancyType.Biotech:
if (Configurations.Debug) Log.Message($"Creating new biotech pregnancy");
pregnancy = HediffMaker.MakeHediff(HediffDefOf.PregnantHuman, Pawn);
if (Configurations.EnableBiotechTwins)
pregnancy.TryGetComp<HediffComp_PregeneratedBabies>().AddNewBaby(Pawn, egg.fertilizer);
((Hediff_Pregnant)pregnancy).SetParents(Pawn, egg.fertilizer, PregnancyUtility.GetInheritedGeneSet(egg.fertilizer, Pawn));
Pawn.health.AddHediff(pregnancy);
pregnant = true;
deadeggs.Add(egg);
break;
} }
} if (pregnancy is Hediff_BasePregnancy rjw_preg)
else if (Configurations.PregnancySource == Configurations.PregnancyType.MultiplePregnancy && Configurations.EnableHeteroOvularTwins)
{
if (pregnancy is Hediff_MultiplePregnancy h)
{ {
if (Configurations.Debug) Log.Message($"Adding to existing pregnancy {h}"); // TODO: advance biotech pregnancy
h.AddNewBaby(Pawn, egg.fertilizer); rjw_preg.p_start_tick -= egg.ticksSinceFertilization / Configurations.CycleAcceleration;
rjw_preg.p_end_tick -= egg.ticksSinceFertilization / Configurations.CycleAcceleration;
} }
pregnant = true;
deadeggs.Add(egg);
} }
else else
{ {
pregnant = true; switch (pregnancy)
break; {
case Hediff_Pregnant vanillaPreg: // Not going to do the labor ones
if (!Configurations.EnableBiotechTwins || !Configurations.EnableHeteroOvularTwins) goto default;
if (Configurations.Debug) Log.Message($"Adding to existing Biotech pregnancy {vanillaPreg.GetUniqueLoadID()}");
HediffComp_PregeneratedBabies comp = vanillaPreg.TryGetComp<HediffComp_PregeneratedBabies>();
if (comp == null) Log.Warning($"Trying to add Biotech egg to {Pawn}'s pregnancy without a pregenerated baby comp: {vanillaPreg.GetUniqueLoadID()}");
else
comp.AddNewBaby(Pawn, egg.fertilizer);
pregnant = true;
deadeggs.Add(egg);
break;
case Hediff_MultiplePregnancy multiPreg:
if (!Configurations.EnableHeteroOvularTwins) goto default;
if (Configurations.Debug) Log.Message($"Adding to existing pregnancy {multiPreg.GetUniqueLoadID()}");
multiPreg.AddNewBaby(Pawn, egg.fertilizer);
pregnant = true;
deadeggs.Add(egg);
break;
case Hediff_BasePregnancy _:
default:
pregnant = true;
deadeggs.Add(egg);
break;
}
} }
} }
else catch (Exception ex)
{ {
Configurations.PregnancyType usePregnancy = xxx.is_human(Pawn) ? Configurations.PregnancySource : Configurations.PregnancyType.MultiplePregnancy; Log.Error($"Error creating pregnancy in {Pawn}'s womb, father {egg.fertilizer}: {ex}");
switch (usePregnancy) TakeLoosePregnancy();
{ deadeggs.Add(egg);
case Configurations.PregnancyType.BaseRJW:
if (Configurations.Debug) Log.Message($"Creating new base RJW pregnancy");
PregnancyHelper.AddPregnancyHediff(Pawn, egg.fertilizer);
// I hate having to do this, but it gets the newest pregnancy
List<Hediff_BasePregnancy> pregnancies = new List<Hediff_BasePregnancy>();
Pawn.health.hediffSet.GetHediffs(ref pregnancies);
pregnancy = pregnancies.MaxBy(hediff => hediff.loadID);
pregnant = true;
break;
case Configurations.PregnancyType.MultiplePregnancy:
if (Configurations.Debug) Log.Message($"Creating new menstruation pregnancy");
pregnancy = Hediff_BasePregnancy.Create<Hediff_MultiplePregnancy>(Pawn, egg.fertilizer);
pregnant = true;
deadeggs.Add(egg);
break;
case Configurations.PregnancyType.Biotech:
if (Configurations.Debug) Log.Message($"Creating new biotech pregnancy");
pregnancy = HediffMaker.MakeHediff(HediffDefOf.PregnantHuman, Pawn);
if(Configurations.EnableBiotechTwins)
pregnancy.TryGetComp<HediffComp_PregeneratedBabies>().AddNewBaby(Pawn, egg.fertilizer);
((Hediff_Pregnant)pregnancy).SetParents(Pawn, egg.fertilizer, PregnancyUtility.GetInheritedGeneSet(egg.fertilizer, Pawn));
Pawn.health.AddHediff(pregnancy);
pregnant = true;
deadeggs.Add(egg);
break;
}
if (pregnancy is Hediff_BasePregnancy rjw_preg)
{
// TODO: advance biotech pregnancy
rjw_preg.p_start_tick -= egg.ticksSinceFertilization / Configurations.CycleAcceleration;
rjw_preg.p_end_tick -= egg.ticksSinceFertilization / Configurations.CycleAcceleration;
}
} }
} }
else else
{ {
if (Configurations.Debug) if (Configurations.Debug)
{ {
float implantChance = Configurations.ImplantationChance * ImplantChance * InterspeciesImplantFactor(egg.fertilizer); float interspeciesFactor = InterspeciesImplantFactor(egg.fertilizer);
Log.Message($"Fertilized egg of {Pawn} failed to implant (chance {implantChance.ToStringPercent()}, father {egg.fertilizer})"); float implantChance = Configurations.ImplantationChance * ImplantChance * interspeciesFactor;
Log.Message($"Fertilized egg of {Pawn} failed to implant (chance {implantChance.ToStringPercent()}, " +
(interspeciesFactor < 1.0f ? $"interspecies factor {interspeciesFactor.ToStringPercent()}, " : "") +
$"father {egg.fertilizer})");
} }
deadeggs.Add(egg); deadeggs.Add(egg);
} }
} }
if (pregnant && bool clearAllEggs = true;
(Configurations.PregnancySource != Configurations.PregnancyType.MultiplePregnancy || !Configurations.EnableHeteroOvularTwins) && switch (Configurations.PregnancySource)
(Configurations.PregnancySource != Configurations.PregnancyType.Biotech || !Configurations.EnableBiotechTwins || !Configurations.EnableHeteroOvularTwins))
{ {
eggs.Clear(); case Configurations.PregnancyType.BaseRJW:
return true; clearAllEggs = true;
break;
case Configurations.PregnancyType.MultiplePregnancy:
clearAllEggs = !Configurations.EnableHeteroOvularTwins;
break;
case Configurations.PregnancyType.Biotech:
clearAllEggs = !(Configurations.EnableBiotechTwins && Configurations.EnableHeteroOvularTwins);
break;
} }
if (pregnant && clearAllEggs)
eggs.Clear();
else else
eggs.RemoveAll(egg => deadeggs.Contains(egg)); eggs.RemoveAll(egg => deadeggs.Contains(egg));
return pregnant; return pregnant;
@ -1552,12 +1548,7 @@ namespace RJW_Menstruation
return amount; return amount;
} }
absorber.absorbedfluids += amount; absorber.absorbedfluids += amount;
if (absorber.absorbedfluids > absorbable && !Pawn.apparel.IsLocked(absorber)) absorber.CheckDirty();
{
absorber.def = absorber.DirtyDef;
//absorber.fluidColor = GetCumMixtureColor;
absorber.dirty = true;
}
return 0; return 0;
} }
@ -1571,9 +1562,6 @@ namespace RJW_Menstruation
return amount; return amount;
} }
protected void EggDecay() protected void EggDecay()
{ {
HashSet<Egg> deadeggs = new HashSet<Egg>(); HashSet<Egg> deadeggs = new HashSet<Egg>();
@ -1599,6 +1587,11 @@ namespace RJW_Menstruation
Pawn.health.AddHediff(hediff, parent.Part); Pawn.health.AddHediff(hediff, parent.Part);
} }
protected void AdvanceStageTime()
{
curStageTicks += TickInterval * Configurations.CycleAcceleration;
}
protected virtual void FollicularAction() protected virtual void FollicularAction()
{ {
if (!IsBreedingSeason()) if (!IsBreedingSeason())
@ -1613,13 +1606,12 @@ namespace RJW_Menstruation
} }
else else
{ {
curStageTicks += TickInterval * Configurations.CycleAcceleration; AdvanceStageTime();
if (!estrusflag && curStageTicks > currentIntervalTicks - Props.estrusDaysBeforeOvulation * GenDate.TicksPerDay) if (!estrusflag && curStageTicks > currentIntervalTicks - Props.estrusDaysBeforeOvulation * GenDate.TicksPerDay)
{ {
estrusflag = true; estrusflag = true;
SetEstrus(); SetEstrus();
} }
StayCurrentStage();
} }
} }
@ -1627,7 +1619,7 @@ namespace RJW_Menstruation
{ {
if (curStageTicks < currentIntervalTicks) if (curStageTicks < currentIntervalTicks)
{ {
curStageTicks += TickInterval * Configurations.CycleAcceleration; AdvanceStageTime();
return; return;
} }
estrusflag = false; estrusflag = false;
@ -1642,11 +1634,11 @@ namespace RJW_Menstruation
} }
catch (ArgumentException e) catch (ArgumentException e)
{ {
Log.Warning($"Invalid litterSizeCurve for {Pawn.def}: {e}"); Log.WarningOnce($"Invalid litterSizeCurve for {Pawn.def}: {e}", 642201874 + Pawn.thingIDNumber);
eggnum = 1f; eggnum = 1f;
} }
eggnum *= ovulationFactor; eggnum *= ovulationFactor;
int toOvulate = (int)eggnum + eggstack; int toOvulate = Math.Max(1, (int)eggnum + eggstack);
int ovulated = 0; int ovulated = 0;
for (int i = 0; i < toOvulate; i++) for (int i = 0; i < toOvulate; i++)
@ -1657,7 +1649,7 @@ namespace RJW_Menstruation
} }
ovarypower -= ovulated; ovarypower -= ovulated;
eggstack = 0; eggstack = 0;
if (Configurations.Debug && ovulated != toOvulate) if (Configurations.Debug && ovulated < toOvulate)
Log.Message($"{Pawn} ovulated {ovulated}/{toOvulate} eggs ({OvulationChance.ToStringPercent()} chance)"); Log.Message($"{Pawn} ovulated {ovulated}/{toOvulate} eggs ({OvulationChance.ToStringPercent()} chance)");
GoNextStage(Stage.Luteal); GoNextStage(Stage.Luteal);
@ -1687,21 +1679,19 @@ namespace RJW_Menstruation
} }
else else
{ {
curStageTicks += TickInterval * Configurations.CycleAcceleration; AdvanceStageTime();
StayCurrentStage();
} }
} }
else else
{ {
curStageTicks += TickInterval * Configurations.CycleAcceleration; AdvanceStageTime();
StayCurrentStage();
} }
} }
protected virtual void BleedingAction() protected virtual void BleedingAction()
{ {
if (curStageTicks >= currentIntervalTicks) if (curStageTicks >= currentIntervalTicks || noBleeding)
{ {
Hediff hediff = Pawn.health.hediffSet.GetFirstHediffOfDef(VariousDefOf.Hediff_MenstrualCramp); Hediff hediff = Pawn.health.hediffSet.GetFirstHediffOfDef(VariousDefOf.Hediff_MenstrualCramp);
if (hediff != null && !Pawn.GetMenstruationComps().Any(comp => comp != this && comp.curStage == Stage.Bleeding)) Pawn.health.RemoveHediff(hediff); if (hediff != null && !Pawn.GetMenstruationComps().Any(comp => comp != this && comp.curStage == Stage.Bleeding)) Pawn.health.RemoveHediff(hediff);
@ -1710,15 +1700,13 @@ namespace RJW_Menstruation
GoOvulatoryStage(); GoOvulatoryStage();
else else
{ {
currentIntervalTicks = totalFollicularTicks - currentIntervalTicks; // I.e., the remaining follicular time equals the total minus the bleeding time elapsed GoNextStage(Stage.Follicular, totalFollicularTicks - currentIntervalTicks); // I.e., the remaining follicular time equals the total minus the bleeding time elapsed
GoNextStage(Stage.Follicular, false);
} }
} }
else else
{ {
if (curStageTicks < currentIntervalTicks / 4) BleedOut(); if (curStageTicks < currentIntervalTicks / 4) BleedOut();
curStageTicks += TickInterval * Configurations.CycleAcceleration; AdvanceStageTime();
StayCurrentStage();
} }
} }
@ -1731,16 +1719,10 @@ namespace RJW_Menstruation
Implant(); Implant();
} }
if (pregnancy != null && Pawn.health.hediffSet.hediffs.Contains(pregnancy)) if (Pregnancy != null)
{
curStageTicks += TickInterval; curStageTicks += TickInterval;
StayCurrentStageConst(Stage.Pregnant);
}
else else
{
if (pregnancy != null) pregnancy = null;
GoNextStage(Stage.Recover); GoNextStage(Stage.Recover);
}
} }
protected virtual void RecoverAction() protected virtual void RecoverAction()
@ -1762,22 +1744,18 @@ namespace RJW_Menstruation
} }
else else
{ {
curStageTicks += TickInterval * Configurations.CycleAcceleration; AdvanceStageTime();
StayCurrentStage();
} }
} }
protected virtual void InfertileAction() protected virtual void InfertileAction()
{ {
if (ShouldBeInfertile()) if (ShouldBeInfertile())
{ return;
StayCurrentStageConst(Stage.Infertile); else if (IsBreedingSeason())
} GoNextStage(Stage.Follicular);
else else
{ GoNextStage(Stage.Anestrus);
bool breedingSeason = IsBreedingSeason();
GoNextStage(breedingSeason ? Stage.Follicular : Stage.Anestrus, breedingSeason);
}
} }
protected virtual void AnestrusAction() protected virtual void AnestrusAction()
@ -1786,10 +1764,6 @@ namespace RJW_Menstruation
{ {
GoFollicularOrBleeding(); GoFollicularOrBleeding();
} }
else
{
StayCurrentStage();
}
} }
protected virtual void ThoughtCumInside(Pawn cummer) protected virtual void ThoughtCumInside(Pawn cummer)
@ -1799,7 +1773,7 @@ namespace RJW_Menstruation
MemoryThoughtHandler cummerMemories = cummer.needs.mood.thoughts.memories; MemoryThoughtHandler cummerMemories = cummer.needs.mood.thoughts.memories;
MemoryThoughtHandler pawnMemories = Pawn.needs.mood.thoughts.memories; MemoryThoughtHandler pawnMemories = Pawn.needs.mood.thoughts.memories;
if (cummer.IsProPregnancy(out Precept preceptm) || (cummer.HasQuirk(QuirkUtility.Quirks.Teratophile) != (Pawn.GetStatValue(StatDefOf.PawnBeauty) >= 0))) if (cummer.IsProPregnancy(out Precept preceptm) || (cummer.IsTeratophile() != (Pawn.GetStatValue(StatDefOf.PawnBeauty) >= 0)))
{ {
if (cummer.relations.OpinionOf(Pawn) <= -5) if (cummer.relations.OpinionOf(Pawn) <= -5)
cummerMemories.TryGainMemory(VariousDefOf.HaterCameInsideM, Pawn); cummerMemories.TryGainMemory(VariousDefOf.HaterCameInsideM, Pawn);
@ -1822,10 +1796,10 @@ namespace RJW_Menstruation
} }
else pawnMemories.TryGainMemory(VariousDefOf.CameInsideFFetish, cummer); else pawnMemories.TryGainMemory(VariousDefOf.CameInsideFFetish, cummer);
} }
else if (Pawn.relations.OpinionOf(cummer) <= -5) else if (Pawn.relations.OpinionOf(cummer) <= -5)
pawnMemories.TryGainMemory(VariousDefOf.HaterCameInsideF, cummer); pawnMemories.TryGainMemory(VariousDefOf.HaterCameInsideF, cummer);
else if (Pawn.IsInEstrus() && Pawn.relations.OpinionOf(cummer) < RJWHookupSettings.MinimumRelationshipToHookup) else if (Pawn.IsInEstrus() && Pawn.relations.OpinionOf(cummer) < RJWHookupSettings.MinimumRelationshipToHookup)
pawnMemories.TryGainMemory(VariousDefOf.HaterCameInsideFEstrus, cummer); pawnMemories.TryGainMemory(VariousDefOf.HaterCameInsideFEstrus, cummer);
else if (!Pawn.relations.DirectRelationExists(PawnRelationDefOf.Spouse, cummer) && !Pawn.relations.DirectRelationExists(PawnRelationDefOf.Fiance, cummer)) else if (!Pawn.relations.DirectRelationExists(PawnRelationDefOf.Spouse, cummer) && !Pawn.relations.DirectRelationExists(PawnRelationDefOf.Fiance, cummer))
{ {
if (Pawn.health.capacities.GetLevel(xxx.reproduction) < 0.50f) pawnMemories.TryGainMemory(VariousDefOf.CameInsideFLowFert, cummer); if (Pawn.health.capacities.GetLevel(xxx.reproduction) < 0.50f) pawnMemories.TryGainMemory(VariousDefOf.CameInsideFLowFert, cummer);
@ -1851,10 +1825,10 @@ namespace RJW_Menstruation
TaleRecorder.RecordTale(VariousDefOf.TaleCameInside, new object[] { cummer, Pawn }); TaleRecorder.RecordTale(VariousDefOf.TaleCameInside, new object[] { cummer, Pawn });
} }
public void GoNextStage(Stage nextstage, bool calculateHours = true) public void GoNextStage(Stage nextstage, int? stageTicks = null)
{ {
curStageTicks = 0; curStageTicks = 0;
if (calculateHours) currentIntervalTicks = PeriodRandomizer(nextstage); currentIntervalTicks = stageTicks ?? PeriodRandomizer(nextstage);
curStage = nextstage; curStage = nextstage;
} }
@ -1863,16 +1837,6 @@ namespace RJW_Menstruation
GoNextStage(Stage.Ovulatory); GoNextStage(Stage.Ovulatory);
} }
//stage can be interrupted in other reasons
protected void StayCurrentStage()
{
}
//stage never changes
protected void StayCurrentStageConst(Stage curstage)
{
}
protected void GoFollicularOrBleeding() protected void GoFollicularOrBleeding()
{ {
if (Props.bleedingIntervalDays == 0 || noBleeding) if (Props.bleedingIntervalDays == 0 || noBleeding)
@ -1932,10 +1896,13 @@ namespace RJW_Menstruation
else return Rand.Range(0.6f, 1.0f); else return Rand.Range(0.6f, 1.0f);
} }
protected virtual float RandomOvulationChance => (float)Props.ovulationIntervalHours / GenDate.HoursPerDay;
protected Stage RandomStage() protected Stage RandomStage()
{ {
Stage stage = Rand.ElementByWeight( Stage stage = Rand.ElementByWeight(
Stage.Follicular, Props.follicularIntervalDays - Props.bleedingIntervalDays, Stage.Follicular, Props.follicularIntervalDays - Props.bleedingIntervalDays,
Stage.Ovulatory, RandomOvulationChance,
Stage.Luteal, Props.lutealIntervalDays, Stage.Luteal, Props.lutealIntervalDays,
Stage.Bleeding, Props.bleedingIntervalDays); Stage.Bleeding, Props.bleedingIntervalDays);
@ -1944,6 +1911,9 @@ namespace RJW_Menstruation
case Stage.Follicular: case Stage.Follicular:
curStageTicks = Rand.Range(0, (Props.follicularIntervalDays - Props.bleedingIntervalDays) * GenDate.TicksPerDay); curStageTicks = Rand.Range(0, (Props.follicularIntervalDays - Props.bleedingIntervalDays) * GenDate.TicksPerDay);
break; break;
case Stage.Ovulatory:
curStageTicks = Rand.Range(0, Props.ovulationIntervalHours * GenDate.TicksPerHour);
break;
case Stage.Luteal: case Stage.Luteal:
curStageTicks = Rand.Range(0, Props.lutealIntervalDays * GenDate.TicksPerDay); curStageTicks = Rand.Range(0, Props.lutealIntervalDays * GenDate.TicksPerDay);
break; break;
@ -1983,7 +1953,7 @@ namespace RJW_Menstruation
public int EggsRestoredPerBiosculptor(float yearsWorth) public int EggsRestoredPerBiosculptor(float yearsWorth)
{ {
return Math.Max(1, (int)((float)RaceCyclesPerYear() * yearsWorth)); return Math.Max(1, (int)(RaceCyclesPerYear() * yearsWorth * AverageLitterSize()));
} }
public void RestoreEggs(float yearsWorth) public void RestoreEggs(float yearsWorth)
@ -2002,7 +1972,7 @@ namespace RJW_Menstruation
public Egg() public Egg()
{ {
fertilized = false; fertilized = false;
lifeSpanTicks = (int)(96 * GenDate.TicksPerHour * Configurations.EggLifespanMultiplier); lifeSpanTicks = (int)(VariousDefOf.HumanVaginaCompProperties.eggLifespanDays * GenDate.TicksPerDay * Configurations.EggLifespanMultiplier);
fertilizer = null; fertilizer = null;
ageTicks = 0; ageTicks = 0;
} }
@ -2044,28 +2014,6 @@ namespace RJW_Menstruation
public class HediffComp_Anus : HediffComp public class HediffComp_Anus : HediffComp
{ {
protected float? originanussize; public CompProperties_Anus Props => (CompProperties_Anus)props;
public float OriginAnusSize
{
get
{
if (originanussize == null)
{
originanussize = parent.Severity;
}
return originanussize ?? 0.1f;
}
}
public override void CompExposeData()
{
base.CompExposeData();
Scribe_Values.Look(ref originanussize, "originanussize", originanussize, true);
}
public override void CompPostTick(ref float severityAdjustment)
{
}
} }
} }

View file

@ -29,14 +29,14 @@ namespace RJW_Menstruation
protected override void InitializeExtraValues() protected override void InitializeExtraValues()
{ {
base.InitializeExtraValues(); base.InitializeExtraValues();
if (averageCycleIntervalTicks < 0) if (averageCycleIntervalTicks < 0)
{ {
averageCycleIntervalTicks = (int)(Props.cycleIntervalDays.RandomInRange * GenDate.TicksPerDay / cycleSpeed); averageCycleIntervalTicks = (int)(Props.cycleIntervalDays.RandomInRange * GenDate.TicksPerDay / cycleSpeed);
if (ticksToNextCycle < -50000) if (ticksToNextCycle < -50000)
ticksToNextCycle = Rand.Range(0, averageCycleIntervalTicks); ticksToNextCycle = Rand.Range(0, averageCycleIntervalTicks);
// 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.Ovulatory || curStage == Stage.Luteal || curStage == Stage.Bleeding)
&& (averageCycleIntervalTicks - ticksToNextCycle) / 2 >= GenDate.TicksPerDay * (Props.follicularIntervalDays + Props.lutealIntervalDays) / cycleSpeed) && (averageCycleIntervalTicks - ticksToNextCycle) / 2 >= GenDate.TicksPerDay * (Props.follicularIntervalDays + Props.lutealIntervalDays) / cycleSpeed)
GoNextStage(Stage.Anestrus); GoNextStage(Stage.Anestrus);
} }
@ -73,7 +73,7 @@ namespace RJW_Menstruation
protected override int TicksToNextStage() protected override int TicksToNextStage()
{ {
if (curStage == Stage.Anestrus && ticksToNextCycle > 0) return ticksToNextCycle / Configurations.CycleAcceleration; if (curStage == Stage.Anestrus && ticksToNextCycle > 0) return ticksToNextCycle / Configurations.CycleAcceleration;
else return base.TicksToNextStage(); else return base.TicksToNextStage();
} }
@ -104,13 +104,11 @@ namespace RJW_Menstruation
protected override void AnestrusAction() protected override void AnestrusAction()
{ {
if (ticksToNextCycle <= 0) if (ticksToNextCycle <= 0 && IsBreedingSeason())
{ {
ticksToNextCycle = (int)(averageCycleIntervalTicks * (1 + Rand.Range(-cycleVariability, cycleVariability))); ticksToNextCycle = (int)(averageCycleIntervalTicks * (1 + Rand.Range(-cycleVariability, cycleVariability)));
if (IsBreedingSeason()) GoNextStage(Stage.Follicular); GoNextStage(Stage.Follicular);
return;
} }
StayCurrentStage();
} }
public override void CopyCycleProperties(HediffComp_Menstruation original) public override void CopyCycleProperties(HediffComp_Menstruation original)

View file

@ -40,7 +40,7 @@ namespace RJW_Menstruation
} }
catch (Exception ex) catch (Exception ex)
{ {
Log.Error($"Error when trying to emit pheromones: {ex}"); Log.Error($"Error when trying to emit pheromones from pawn {Pawn}: {ex}");
} }
} }

View file

@ -179,7 +179,7 @@ namespace RJW_Menstruation
public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{ {
if (birtherThing < 0) throw new InvalidOperationException("Could not locate index of birtherThing"); if (birtherThing < 0) throw new InvalidOperationException("Could not locate index of birtherThing");
if (GeneratePawn == null || GeneratePawn.ReturnType != typeof(Pawn)) throw new InvalidOperationException("GeneratePawn not found"); if (GeneratePawn?.ReturnType != typeof(Pawn)) throw new InvalidOperationException("GeneratePawn not found");
foreach (CodeInstruction instruction in instructions) foreach (CodeInstruction instruction in instructions)
{ {
if (instruction.Calls(GeneratePawn)) if (instruction.Calls(GeneratePawn))
@ -233,7 +233,7 @@ namespace RJW_Menstruation
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)}); 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) public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{ {
if (ApplyBirthOutcome == null || ApplyBirthOutcome.ReturnType != typeof(Thing)) throw new InvalidOperationException("ApplyBirthOutcome not found"); if (ApplyBirthOutcome?.ReturnType != typeof(Thing)) throw new InvalidOperationException("ApplyBirthOutcome not found");
foreach (CodeInstruction instruction in instructions) foreach (CodeInstruction instruction in instructions)
{ {
if (instruction.Calls(ApplyBirthOutcome)) if (instruction.Calls(ApplyBirthOutcome))
@ -283,8 +283,8 @@ namespace RJW_Menstruation
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) }); 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) public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{ {
if (ApplyBirthOutcome == null || ApplyBirthOutcome.ReturnType != typeof(Thing)) throw new InvalidOperationException("ApplyBirthOutcome not found"); if (ApplyBirthOutcome?.ReturnType != typeof(Thing)) throw new InvalidOperationException("ApplyBirthOutcome not found");
foreach (var instruction in instructions) foreach (CodeInstruction instruction in instructions)
{ {
if (instruction.Calls(ApplyBirthOutcome)) if (instruction.Calls(ApplyBirthOutcome))
yield return CodeInstruction.Call(typeof(Ritual_ChildBirth_Apply_Patch), nameof(Ritual_ChildBirth_Apply_Patch.ApplyBirthLoop)); yield return CodeInstruction.Call(typeof(Ritual_ChildBirth_Apply_Patch), nameof(Ritual_ChildBirth_Apply_Patch.ApplyBirthLoop));

View file

@ -22,7 +22,7 @@ namespace RJW_Menstruation
public static IEnumerable<HediffComp_Menstruation> GetMenstruationComps(this Pawn pawn) public static IEnumerable<HediffComp_Menstruation> GetMenstruationComps(this Pawn pawn)
{ {
List<Hediff> hedifflist = Genital_Helper.get_PartsHediffList(pawn, Genital_Helper.get_genitalsBPR(pawn))?.FindAll(h => VariousDefOf.AllVaginas.Contains(h.def)); List<Hediff> hedifflist = pawn.health.hediffSet.hediffs.FindAll(h => VariousDefOf.AllVaginas.Contains(h.def));
if (hedifflist == null) yield break; if (hedifflist == null) yield break;
foreach (Hediff hediff in hedifflist) foreach (Hediff hediff in hedifflist)
{ {
@ -63,17 +63,7 @@ namespace RJW_Menstruation
public static HediffComp_Anus GetAnusComp(this Pawn pawn) public static HediffComp_Anus GetAnusComp(this Pawn pawn)
{ {
List<Hediff> hedifflist = Genital_Helper.get_PartsHediffList(pawn, Genital_Helper.get_genitalsBPR(pawn))?.FindAll((Hediff h) => h.def.defName.ToLower().Contains("anus")); return pawn.health.hediffSet.hediffs.FirstOrDefault((Hediff h) => VariousDefOf.AllAnuses.Contains(h.def))?.TryGetComp<HediffComp_Anus>();
HediffComp_Anus result;
if (!hedifflist.NullOrEmpty())
{
foreach (Hediff h in hedifflist)
{
result = h.TryGetComp<HediffComp_Anus>();
if (result != null) return result;
}
}
return null;
} }
[Obsolete("This method is obsolete and can cause ambiguity. Use GetMenstruationCompFromVagina or GetMenstruationCompFromPregnancy instead.", true)] [Obsolete("This method is obsolete and can cause ambiguity. Use GetMenstruationCompFromVagina or GetMenstruationCompFromPregnancy instead.", true)]
@ -151,7 +141,7 @@ namespace RJW_Menstruation
else if (gestationProgress < 0.8f) icon = fetustex + "04"; else if (gestationProgress < 0.8f) icon = fetustex + "04";
else icon = fetustex + "05"; else icon = fetustex + "05";
return TryGetTwinsIcon(icon, babycount) ?? ContentFinder<Texture2D>.Get((icon), true); return TryGetTwinsIcon(icon, babycount) ?? ContentFinder<Texture2D>.Get(icon, true);
} }
public static Texture2D TryGetTwinsIcon(string path, int babycount) public static Texture2D TryGetTwinsIcon(string path, int babycount)
@ -170,7 +160,7 @@ namespace RJW_Menstruation
List<Hediff_InsectEgg> insectEggs = new List<Hediff_InsectEgg>(); List<Hediff_InsectEgg> insectEggs = new List<Hediff_InsectEgg>();
comp.Pawn.health.hediffSet.GetHediffs(ref insectEggs); comp.Pawn.health.hediffSet.GetHediffs(ref insectEggs);
if (!insectEggs.NullOrEmpty() && insectEggs.Sum(hediff => hediff.eggssize) > 1.0f) return null; // same logic as "Stuffed" in GetInsectEggedIcon if (insectEggs?.Sum(hediff => hediff.eggssize) > 1.0f) return null; // same logic as "Stuffed" in GetInsectEggedIcon
string icon = comp.WombTex; string icon = comp.WombTex;
float cumpercent = comp.TotalCumPercent; float cumpercent = comp.TotalCumPercent;
@ -193,7 +183,7 @@ namespace RJW_Menstruation
else if (cumpercent < 0.89f) icon += "_Cum_15"; else if (cumpercent < 0.89f) icon += "_Cum_15";
else if (cumpercent < 0.95f) icon += "_Cum_16"; else if (cumpercent < 0.95f) icon += "_Cum_16";
else icon += "_Cum_17"; else icon += "_Cum_17";
Texture2D cumtex = ContentFinder<Texture2D>.Get((icon), true); Texture2D cumtex = ContentFinder<Texture2D>.Get(icon, true);
return cumtex; return cumtex;
} }
public static Texture2D GetInsectEggedIcon(this HediffComp_Menstruation comp) public static Texture2D GetInsectEggedIcon(this HediffComp_Menstruation comp)
@ -316,15 +306,13 @@ namespace RJW_Menstruation
} }
public static Texture2D GetGenitalIcon(this Pawn pawn, HediffComp_Menstruation comp, bool drawOrigin = false) public static Texture2D GetGenitalIcon(this Pawn pawn, HediffComp_Menstruation comp)
{ {
Hediff hediff = comp?.parent; Hediff hediff = comp?.parent;
if (hediff == null) return ContentFinder<Texture2D>.Get("Genitals/Vagina00", true); if (hediff == null) return ContentFinder<Texture2D>.Get("Genitals/Vagina00", true);
//HediffComp_Menstruation comp = hediff.GetMenstruationComp(); //HediffComp_Menstruation comp = hediff.GetMenstruationComp();
string icon; string icon;
float severity; float severity = hediff.Severity;
if (drawOrigin) severity = comp.OriginVagSize;
else severity = hediff.Severity;
if (comp != null) icon = comp.VagTex; if (comp != null) icon = comp.VagTex;
else icon = "Genitals/Vagina"; else icon = "Genitals/Vagina";
@ -341,30 +329,18 @@ namespace RJW_Menstruation
else if (severity < 1.01f) icon += "10"; //cavernous else if (severity < 1.01f) icon += "10"; //cavernous
else icon += "11"; //abyssal else icon += "11"; //abyssal
return ContentFinder<Texture2D>.Get((icon), true); return ContentFinder<Texture2D>.Get(icon, true);
} }
public static Texture2D GetAnalIcon(this Pawn pawn, bool drawOrigin = false) public static Texture2D GetAnalIcon(this Pawn pawn)
{ {
Hediff hediff = Genital_Helper.get_PartsHediffList(pawn, Genital_Helper.get_anusBPR(pawn)).FirstOrDefault(h => VariousDefOf.AllAnuses.Contains(h.def)) ?? Hediff hediff = pawn.health.hediffSet.hediffs.FirstOrDefault(h => VariousDefOf.AllAnuses.Contains(h.def)) ??
Genital_Helper.get_PartsHediffList(pawn, Genital_Helper.get_anusBPR(pawn)).FirstOrDefault(h => h.def.defName.ToLower().Contains("anus")); pawn.health.hediffSet.hediffs.FirstOrDefault(h => h.def.defName.ToLower().Contains("anus"));
if (hediff == null) return ContentFinder<Texture2D>.Get(("Genitals/Anal00"), true); if (hediff == null) return ContentFinder<Texture2D>.Get("Genitals/Anal00", true);
string icon = ((CompProperties_Anus)hediff.GetAnusComp()?.props)?.analTex ?? "Genitals/Anal";
float severity = hediff.Severity;
string icon;
float severity;
HediffComp_Anus comp = hediff.GetAnusComp();
if (comp != null)
{
CompProperties_Anus Props = (CompProperties_Anus)comp.props;
icon = Props.analTex ?? "Genitals/Anal";
if (drawOrigin) severity = comp.OriginAnusSize;
else severity = hediff.Severity;
}
else
{
icon = "Genitals/Anal";
severity = hediff.Severity;
}
if (severity < 0.20f) icon += "00"; //micro if (severity < 0.20f) icon += "00"; //micro
else if (severity < 0.40f) icon += "01"; //tight else if (severity < 0.40f) icon += "01"; //tight
else if (severity < 0.60f) icon += "02"; //average else if (severity < 0.60f) icon += "02"; //average
@ -372,7 +348,7 @@ namespace RJW_Menstruation
else if (severity < 1.01f) icon += "04"; //cavernous else if (severity < 1.01f) icon += "04"; //cavernous
else icon += "05"; //abyssal else icon += "05"; //abyssal
return ContentFinder<Texture2D>.Get((icon), true); return ContentFinder<Texture2D>.Get(icon, true);
} }
public static float GestationHours(this Hediff hediff) public static float GestationHours(this Hediff hediff)
@ -403,6 +379,18 @@ namespace RJW_Menstruation
else return variability; else return variability;
} }
public static bool ShouldCycle(this Pawn pawn)
{
if (!Configurations.EnableAnimalCycle && pawn.IsAnimal()) return false;
if (pawn.GetComp<CompEggLayer>() != null) return false;
if (pawn.RaceHasOviPregnancy()) return false;
if (ModsConfig.BiotechActive && pawn.genes != null &&
pawn.genes.GenesListForReading.Select(gene => gene.def).Intersect(VariousDefOf.EggLayerGenes).Any()) return false;
return true;
}
public static bool IsInEstrus(this Pawn pawn, bool visible = true) public static bool IsInEstrus(this Pawn pawn, bool visible = true)
{ {
if (pawn.Dead) return false; if (pawn.Dead) return false;
@ -450,8 +438,8 @@ namespace RJW_Menstruation
} }
if (precept != null) return true; if (precept != null) return true;
else return pawn.HasQuirk(QuirkUtility.Quirks.Breeder) || else return pawn.IsBreeder() ||
pawn.HasQuirk(QuirkUtility.Quirks.ImpregnationFetish); pawn.HasImpregnationFetish();
} }
public static float DamagePants(this Pawn pawn, float fluidAmount) public static float DamagePants(this Pawn pawn, float fluidAmount)

View file

@ -23,7 +23,7 @@ namespace RJW_Menstruation
{ {
if (is_discovered || if (is_discovered ||
!xxx.is_human(pawn) || !xxx.is_human(pawn) ||
pawn.HasQuirk(QuirkUtility.Quirks.Breeder) || (pawn.Ideo?.HasPrecept(VariousDefOf.Pregnancy_Required) ?? false) || pawn.IsBreeder() || (pawn.Ideo?.HasPrecept(VariousDefOf.Pregnancy_Required) ?? false) ||
(pawn.relations?.DirectRelations?.Find(x => x.def.Equals(PawnRelationDefOf.Spouse) || (pawn.relations?.DirectRelations?.Find(x => x.def.Equals(PawnRelationDefOf.Spouse) ||
x.def.Equals(PawnRelationDefOf.Fiance))) != null) x.def.Equals(PawnRelationDefOf.Fiance))) != null)
return; return;

View file

@ -0,0 +1,15 @@
using Verse;
namespace RJW_Menstruation
{
public class MenstruationModExtension : DefModExtension
{
public float eggLifeTimeFactor = 1.0f;
public bool neverEstrus = false;
public bool alwaysEstrus = false;
public float ovulationFactor = 1.0f;
public bool noBleeding = false;
public bool disableCycle = false;
}
}

View file

@ -155,7 +155,7 @@ namespace RJW_Menstruation
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 InvalidOperationException("GetPregnancyHediff not found"); if (GetPregnancyHediff?.ReturnType != typeof(Hediff)) throw new InvalidOperationException("GetPregnancyHediff not found");
if (Get_Attitude == null || Nullable.GetUnderlyingType(Get_Attitude.ReturnType) != typeof(PregnancyAttitude)) throw new InvalidOperationException("get_Attitude not found"); 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)
{ {
@ -192,23 +192,6 @@ namespace RJW_Menstruation
} }
} }
[HarmonyPatch(typeof(Pawn_GeneTracker), "AddGene", new Type[] { typeof(Gene), typeof(bool) })]
public class AddGene_Patch
{
public static bool Prefix(ref Gene __result, Gene gene, Pawn ___pawn)
{
if (!VariousDefOf.WombGenes.Contains(gene.def)) return true;
bool keepGene;
if (PawnGenerator.IsBeingGenerated(___pawn))
// During pawn generation, the vagina hediff doesn't exist yet, so use gender to decide instead
// Not the most accurate, but close enough
keepGene = ___pawn.gender == Gender.Female;
else keepGene = ___pawn.GetMenstruationComps().Any();
if (!keepGene) __result = null;
return keepGene;
}
}
[HarmonyPatch(typeof(Pawn_GeneTracker), "Notify_GenesChanged")] [HarmonyPatch(typeof(Pawn_GeneTracker), "Notify_GenesChanged")]
public class Notify_GenesChanged_Patch public class Notify_GenesChanged_Patch
{ {
@ -219,6 +202,17 @@ namespace RJW_Menstruation
} }
} }
[HarmonyPatch(typeof(StatPart_FertilityByGenderAge), "AgeFactor")]
public class AgeFactor_Patch
{
public static void Postfix(ref float __result, Pawn pawn)
{
if (__result <= 0.0f) return;
if (pawn.GetMenstruationComps().Any(comp => comp.CalculatingOvulationChance))
__result = 1.0f;
}
}
//[HarmonyPatch(typeof(ChildcareUtility), nameof(ChildcareUtility.SuckleFromLactatingPawn))] //[HarmonyPatch(typeof(ChildcareUtility), nameof(ChildcareUtility.SuckleFromLactatingPawn))]
//public class GreedyConsume_Patch //public class GreedyConsume_Patch
//{ //{

View file

@ -43,6 +43,8 @@ namespace RJW_Menstruation
private static Gizmo CreateGizmo_WombStatus(Pawn pawn, HediffComp_Menstruation comp) private static Gizmo CreateGizmo_WombStatus(Pawn pawn, HediffComp_Menstruation comp)
{ {
if (!comp.loaded)
comp.Initialize();
Texture2D icon, icon_overlay; Texture2D icon, icon_overlay;
StringBuilder description = new StringBuilder(); StringBuilder description = new StringBuilder();
if (Configurations.Debug) if (Configurations.Debug)

View file

@ -1,5 +1,6 @@
using HarmonyLib; using HarmonyLib;
using RimWorld; using RimWorld;
using rjw;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@ -36,10 +37,6 @@ namespace RJW_Menstruation
if (t.Pawn == pawn && pawn.HasMenstruationComp()) opts.AddDistinct(MakeSelfMenu(pawn, t)); if (t.Pawn == pawn && pawn.HasMenstruationComp()) opts.AddDistinct(MakeSelfMenu(pawn, t));
break; break;
} }
} }
public static FloatMenuOption MakeSelfMenu(Pawn pawn, LocalTargetInfo target) public static FloatMenuOption MakeSelfMenu(Pawn pawn, LocalTargetInfo target)
@ -53,60 +50,28 @@ namespace RJW_Menstruation
} }
} }
//[HarmonyPatch(typeof(HealthCardUtility), "DrawHediffListing")] [HarmonyPatch(typeof(HealthCardUtility), "DrawOverviewTab")]
//public class DrawHediffListing_Patch public class DrawOverviewTab_Patch
//{
// public const float buttonWidth = 80f;
// public const float buttonHeight = 20f;
//
// public static void Postfix(Rect rect, Pawn pawn, bool showBloodLoss)
// {
// if (Configurations.EnableButtonInHT && pawn.HasMenstruationComp())
// {
// Rect buttonrect = new Rect(rect.xMax - buttonWidth, rect.yMax - buttonHeight, buttonWidth, buttonHeight);
// if (Widgets.ButtonText(buttonrect, "Status"))
// {
// Dialog_WombStatus.ToggleWindow(pawn,pawn.GetMenstruationComp());
// }
// }
//
//
// }
//}
[HarmonyPatch(typeof(HealthCardUtility), "DrawHediffRow")]
public class DrawHediffRow_Patch
{ {
public const float buttonWidth = 50f; public const float buttonWidth = 50f;
public const float buttonHeight = 20f; public const float buttonHeight = 20f;
private static HediffComp_Menstruation GetFirstMenstruation(IEnumerable<Hediff> diffs) public static void Prefix(Rect leftRect, Pawn pawn, float curY)
{ {
foreach (Hediff diff in diffs) if (Configurations.EnableButtonInHT && pawn.ShowStatus() && pawn.ShouldCycle())
{ {
HediffComp_Menstruation comp = diff.GetMenstruationCompFromVagina(); HediffComp_Menstruation comp = pawn.GetFirstMenstruationComp();
if (comp != null) return comp;
}
return null;
}
public static void Prefix(Rect rect, Pawn pawn, IEnumerable<Hediff> diffs, ref float curY)
{
if (Configurations.EnableButtonInHT && pawn.ShowStatus())
{
HediffComp_Menstruation comp = GetFirstMenstruation(diffs);
if (comp != null) if (comp != null)
{ {
Rect buttonrect = new Rect((rect.xMax) / 2 - 5f, curY + 2f, buttonWidth, buttonHeight); Text.Font = GameFont.Tiny;
if (Widgets.ButtonText(buttonrect, Translations.Button_HealthTab)) Rect buttonRect = new Rect(leftRect.xMax - buttonWidth - 8f, curY + 4f, buttonWidth, buttonHeight);
if (Widgets.ButtonText(buttonRect, Translations.Button_HealthTab))
{ {
Dialog_WombStatus.ToggleWindow(pawn, comp); Dialog_WombStatus.ToggleWindow(pawn, comp);
} }
} }
} }
} }
} }
[HarmonyPatch(typeof(CompBiosculpterPod), nameof(CompBiosculpterPod.CannotUseNowPawnCycleReason), new Type[] { typeof(Pawn), typeof(Pawn), typeof(CompBiosculpterPod_Cycle), typeof(bool) })] [HarmonyPatch(typeof(CompBiosculpterPod), nameof(CompBiosculpterPod.CannotUseNowPawnCycleReason), new Type[] { typeof(Pawn), typeof(Pawn), typeof(CompBiosculpterPod_Cycle), typeof(bool) })]
@ -120,4 +85,28 @@ namespace RJW_Menstruation
__result = Translations.CannotNoWomb; __result = Translations.CannotNoWomb;
} }
} }
// Doesn't cover everything, but at least it'll get the auto equip
[HarmonyPatch(typeof(Apparel), nameof(Apparel.PawnCanWear))]
public class PawnCanWear_Patch
{
public static void Postfix(ref bool __result, Apparel __instance, Pawn pawn)
{
if (__result && __instance is Absorber)
{
__result = pawn.ShouldCycle() && pawn.GetMenstruationComps().Any();
}
}
}
// Might cause issues when it comes to caravans
//[HarmonyPatch(typeof(JobGiver_OptimizeApparel), nameof(JobGiver_OptimizeApparel.ApparelScoreRaw))]
//public class ApparelScoreRaw_Patch
//{
// public static void Postfix(ref float __result, Pawn pawn, Apparel ap)
// {
// if (__result > 0f && ap is Absorber && !pawn.GetMenstruationComps().Any(comp => comp.TotalCum > 0))
// __result = -10f;
// }
//}
} }

View file

@ -24,14 +24,14 @@ namespace RJW_Menstruation
if (sextype != xxx.rjwSextype.Vaginal && sextype != xxx.rjwSextype.DoublePenetration) return true; if (sextype != xxx.rjwSextype.Vaginal && sextype != xxx.rjwSextype.DoublePenetration) return true;
if (partner.IsAnimal() && !Configurations.EnableAnimalCycle) return true; if (!partner.ShouldCycle()) return true;
if (!InteractionCanCausePregnancy(props)) return false; if (!InteractionCanCausePregnancy(props)) return false;
List<Hediff> pawnparts = Genital_Helper.get_PartsHediffList(pawn, Genital_Helper.get_genitalsBPR(pawn)); List<Hediff> pawnparts = Genital_Helper.get_PartsHediffList(pawn, Genital_Helper.get_genitalsBPR(pawn));
HediffComp_Menstruation comp; HediffComp_Menstruation comp;
if (pawn.HasQuirk(QuirkUtility.Quirks.ImpregnationFetish) || partner.HasQuirk(QuirkUtility.Quirks.ImpregnationFetish) || partner.IsInEstrus()) if (pawn.HasImpregnationFetish() || partner.HasImpregnationFetish() || partner.IsInEstrus())
comp = partner.GetFertileMenstruationComp(); comp = partner.GetFertileMenstruationComp();
else comp = partner.GetRandomMenstruationComp(); else comp = partner.GetRandomMenstruationComp();
if (comp == null) return true; if (comp == null) return true;
@ -100,9 +100,9 @@ namespace RJW_Menstruation
{ {
public static bool Prefix(Pawn pawn, Pawn partner) // partner has vagina public static bool Prefix(Pawn pawn, Pawn partner) // partner has vagina
{ {
if (partner.IsAnimal() && !Configurations.EnableAnimalCycle) return true; if (!partner.ShouldCycle()) return true;
HediffComp_Menstruation comp; HediffComp_Menstruation comp;
if (pawn.HasQuirk(QuirkUtility.Quirks.ImpregnationFetish) || partner.HasQuirk(QuirkUtility.Quirks.ImpregnationFetish) || partner.IsInEstrus()) if (pawn.HasImpregnationFetish() || partner.HasImpregnationFetish() || partner.IsInEstrus())
comp = partner.GetFertileMenstruationComp(); comp = partner.GetFertileMenstruationComp();
else comp = partner.GetRandomMenstruationComp(); else comp = partner.GetRandomMenstruationComp();
if (comp == null) if (comp == null)
@ -125,14 +125,14 @@ namespace RJW_Menstruation
{ {
private static bool PregnancyBlocksImpregnation(this Pawn pawn, bool _) private static bool PregnancyBlocksImpregnation(this Pawn pawn, bool _)
{ {
if (!Configurations.EnableAnimalCycle && pawn.IsAnimal()) return pawn.IsPregnant(); if (!pawn.ShouldCycle()) return pawn.IsPregnant();
else if (pawn.GetMenstruationComps().Any()) return false; else if (pawn.GetMenstruationComps().Any()) return false;
else return pawn.IsPregnant(); else return pawn.IsPregnant();
} }
private static readonly MethodInfo IsPregnant = AccessTools.Method(typeof(PawnExtensions), nameof(PawnExtensions.IsPregnant), new System.Type[] { typeof(Pawn), typeof(bool) }); private static readonly MethodInfo IsPregnant = AccessTools.Method(typeof(PawnExtensions), nameof(PawnExtensions.IsPregnant), new System.Type[] { typeof(Pawn), typeof(bool) });
public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{ {
if (IsPregnant == null || IsPregnant.ReturnType != typeof(bool)) throw new System.InvalidOperationException("IsPregnant not found"); if (IsPregnant?.ReturnType != typeof(bool)) throw new System.InvalidOperationException("IsPregnant not found");
foreach (CodeInstruction instruction in instructions) foreach (CodeInstruction instruction in instructions)
{ {
if (instruction.Calls(IsPregnant)) if (instruction.Calls(IsPregnant))
@ -179,7 +179,7 @@ namespace RJW_Menstruation
{ {
// Awkward, but it'll have to do // Awkward, but it'll have to do
Pawn pawn = props.pawn; Pawn pawn = props.pawn;
if (__result == 0 || !pawn.HasQuirk(QuirkUtility.Quirks.ImpregnationFetish) || !props.hasPartner()) return; if (__result == 0 || !pawn.HasImpregnationFetish() || !props.hasPartner()) return;
// Check if the existing code would have added the count // Check if the existing code would have added the count
Pawn partner = props.partner; Pawn partner = props.partner;
@ -238,7 +238,7 @@ namespace RJW_Menstruation
private static readonly FieldInfo MinimumFuckabilityToHookup = AccessTools.Field(typeof(RJWHookupSettings), nameof(RJWHookupSettings.MinimumFuckabilityToHookup)); private static readonly FieldInfo MinimumFuckabilityToHookup = AccessTools.Field(typeof(RJWHookupSettings), nameof(RJWHookupSettings.MinimumFuckabilityToHookup));
public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{ {
if (MinimumFuckabilityToHookup == null || MinimumFuckabilityToHookup.FieldType != typeof(float)) throw new System.InvalidOperationException("MinimumFuckabilityToHookup not found"); if (MinimumFuckabilityToHookup?.FieldType != typeof(float)) throw new System.InvalidOperationException("MinimumFuckabilityToHookup not found");
bool first_fuckability = true; bool first_fuckability = true;
foreach (CodeInstruction instruction in instructions) foreach (CodeInstruction instruction in instructions)
{ {
@ -274,8 +274,8 @@ namespace RJW_Menstruation
private static readonly FieldInfo MinimumRelationshipToHookup = AccessTools.Field(typeof(RJWHookupSettings), nameof(RJWHookupSettings.MinimumRelationshipToHookup)); private static readonly FieldInfo MinimumRelationshipToHookup = AccessTools.Field(typeof(RJWHookupSettings), nameof(RJWHookupSettings.MinimumRelationshipToHookup));
public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{ {
if (MinimumAttractivenessToHookup == null || MinimumAttractivenessToHookup.FieldType != typeof(float)) throw new System.InvalidOperationException("MinimumAttractivenessToHookup not found"); if (MinimumAttractivenessToHookup?.FieldType != typeof(float)) throw new System.InvalidOperationException("MinimumAttractivenessToHookup not found");
if (MinimumRelationshipToHookup == null || MinimumRelationshipToHookup.FieldType != typeof(float)) throw new System.InvalidOperationException("MinimumRelationshipToHookup not found"); if (MinimumRelationshipToHookup?.FieldType != typeof(float)) throw new System.InvalidOperationException("MinimumRelationshipToHookup not found");
LocalBuilder pawn_index = null; LocalBuilder pawn_index = null;
// Like in the last one, we switch the arguments around for the second load // Like in the last one, we switch the arguments around for the second load
bool first_attractiveness = true; bool first_attractiveness = true;
@ -342,10 +342,12 @@ namespace RJW_Menstruation
{ {
if (!pawn.IsHashIntervalTick(__instance.ticks_between_thrusts)) return; if (!pawn.IsHashIntervalTick(__instance.ticks_between_thrusts)) return;
xxx.rjwSextype sextype = __instance.Sexprops.sexType; xxx.rjwSextype sextype = __instance.Sexprops.sexType;
if (!(target is Pawn partner) || pawn == partner) return; if (!(target is Pawn partner)) return;
if (sextype != xxx.rjwSextype.Vaginal && sextype != xxx.rjwSextype.DoublePenetration) return; if (sextype != xxx.rjwSextype.Vaginal && sextype != xxx.rjwSextype.DoublePenetration) return;
if (__instance.Sexprops.usedCondom) return; if (__instance.Sexprops.usedCondom) return;
if (AndroidsCompatibility.IsAndroid(pawn)) return;
if (!Impregnate_Patch.InteractionCanCausePregnancy(__instance.Sexprops)) return; if (!Impregnate_Patch.InteractionCanCausePregnancy(__instance.Sexprops)) return;
if (!partner.ShouldCycle()) return;
// Archotech penises have more control. Or something. // Archotech penises have more control. Or something.
CompHediffBodyPart penisComp = pawn.GetGenitalsList()?.Find(genital => (genital as Hediff_PartBaseNatural)?.def.defName.ToLower().Contains("penis") ?? false)?.TryGetComp<CompHediffBodyPart>(); CompHediffBodyPart penisComp = pawn.GetGenitalsList()?.Find(genital => (genital as Hediff_PartBaseNatural)?.def.defName.ToLower().Contains("penis") ?? false)?.TryGetComp<CompHediffBodyPart>();
@ -372,26 +374,14 @@ namespace RJW_Menstruation
} }
} }
[HarmonyPatch(typeof(PawnCapacityWorker_Fertility), nameof(PawnCapacityWorker_Fertility.CalculateCapacityLevel))] [HarmonyPatch(typeof(PawnCapacityWorker_Fertility), "CalculateAgeImpact")]
public static class PawnCapacityWorker_Fertility_Patch public static class PawnCapacityWorker_Fertility_Age_Patch
{ {
private static float GetFertilityStatOrOne(Thing thing, StatDef stat, bool applyPostProcess, int cacheStaleAfterTicks) public static void Postfix(ref float __result, Pawn pawn)
{ {
Pawn pawn = (Pawn)thing; if (__result <= 0.0f) return;
if (pawn.GetMenstruationComps().Any(comp => comp.CalculatingOvulationChance)) if (pawn.GetMenstruationComps().Any(comp => comp.CalculatingOvulationChance))
return 1.0f; __result = 1.0f;
else return thing.GetStatValue(stat, applyPostProcess, cacheStaleAfterTicks);
}
private static readonly MethodInfo GetStatValue = AccessTools.Method(typeof(StatExtension), "GetStatValue", new System.Type[] { typeof(Thing), typeof(StatDef), typeof(bool), typeof(int) });
public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{
if (GetStatValue == null || GetStatValue.ReturnType != typeof(float)) throw new System.InvalidOperationException("GetStatValue not found");
foreach (CodeInstruction instruction in instructions)
{
if (instruction.Calls(GetStatValue))
yield return CodeInstruction.Call(typeof(PawnCapacityWorker_Fertility_Patch), nameof(GetFertilityStatOrOne));
else yield return instruction;
}
} }
} }
} }

View file

@ -20,9 +20,8 @@ namespace RJW_Menstruation
StringBuilder res = new StringBuilder(); StringBuilder res = new StringBuilder();
IEnumerable<Pawn> babiesdistinct = babies.Distinct(new RaceComparer());
int iteration = 0; int iteration = 0;
foreach (Pawn baby in babiesdistinct) foreach (Pawn baby in babies.Distinct(new RaceComparer()))
{ {
int num = babies.Where(x => x.def.Equals(baby.def)).Count(); int num = babies.Where(x => x.def.Equals(baby.def)).Count();
if (iteration > 0) res.Append(", "); if (iteration > 0) res.Append(", ");
@ -43,9 +42,8 @@ namespace RJW_Menstruation
if (!is_parent_known && Configurations.InfoDetail != Configurations.DetailLevel.All) if (!is_parent_known && Configurations.InfoDetail != Configurations.DetailLevel.All)
return res.Append(Translations.Dialog_FatherUnknown).ToString(); return res.Append(Translations.Dialog_FatherUnknown).ToString();
IEnumerable<Pawn> babiesdistinct = babies.Distinct(new FatherComparer(mother));
int iteration = 0; int iteration = 0;
foreach (Pawn baby in babiesdistinct) foreach (Pawn baby in babies.Distinct(new FatherComparer(mother)))
{ {
if (iteration > 0) res.Append(", "); if (iteration > 0) res.Append(", ");
res.Append(Utility.GetFather(baby, mother)?.LabelShort ?? Translations.Dialog_FatherUnknown); res.Append(Utility.GetFather(baby, mother)?.LabelShort ?? Translations.Dialog_FatherUnknown);

View file

@ -6,14 +6,14 @@ namespace RJW_Menstruation
public static class QuirkUtility public static class QuirkUtility
{ {
// All quirks used in Menstruation // All quirks used in Menstruation
public enum Quirks private enum Quirks
{ {
Breeder, Breeder,
ImpregnationFetish, ImpregnationFetish,
Messy, Messy,
Teratophile, Teratophile,
} }
public static bool HasQuirk(this Pawn pawn, Quirks quirk) private static bool HasQuirk(Pawn pawn, Quirks quirk)
{ {
switch (quirk) switch (quirk)
{ {
@ -29,5 +29,9 @@ namespace RJW_Menstruation
return false; return false;
} }
} }
public static bool IsBreeder(this Pawn pawn) => HasQuirk(pawn, Quirks.Breeder);
public static bool HasImpregnationFetish(this Pawn pawn) => HasQuirk(pawn, Quirks.ImpregnationFetish);
public static bool IsMessy(this Pawn pawn) => HasQuirk(pawn, Quirks.Messy);
public static bool IsTeratophile(this Pawn pawn) => HasQuirk(pawn, Quirks.Teratophile);
} }
} }

View file

@ -74,6 +74,7 @@
<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="MenstruationModExtension.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" />

View file

@ -1,6 +1,7 @@
using RimWorld; using RimWorld;
using rjw; using rjw;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text;
using System.Xml; using System.Xml;
using UnityEngine; using UnityEngine;
using Verse; using Verse;
@ -186,6 +187,7 @@ namespace RJW_Menstruation
} }
public class AbsorberModExtension : DefModExtension public class AbsorberModExtension : DefModExtension
{ {
public float passiveAbsorptionPerHour = 0.1f;
public bool leakAfterDirty = false; public bool leakAfterDirty = false;
public bool effectsAfterDirty = false; public bool effectsAfterDirty = false;
public ThingDef dirtyDef = null; public ThingDef dirtyDef = null;
@ -198,7 +200,7 @@ namespace RJW_Menstruation
public float absorbedfluids = 0; public float absorbedfluids = 0;
public bool dirty = false; public bool dirty = false;
public int wearTicks = 0; public int wearTicks = 0;
protected virtual float PassiveAbsorptionPerHour => 0.1f; public virtual float PassiveAbsorptionPerHour => def.GetModExtension<AbsorberModExtension>().passiveAbsorptionPerHour;
public virtual bool LeakAfterDirty => def.GetModExtension<AbsorberModExtension>().leakAfterDirty; public virtual bool LeakAfterDirty => def.GetModExtension<AbsorberModExtension>().leakAfterDirty;
public virtual bool EffectAfterDirty => def.GetModExtension<AbsorberModExtension>().effectsAfterDirty; public virtual bool EffectAfterDirty => def.GetModExtension<AbsorberModExtension>().effectsAfterDirty;
public virtual ThingDef DirtyDef => def.GetModExtension<AbsorberModExtension>().dirtyDef; public virtual ThingDef DirtyDef => def.GetModExtension<AbsorberModExtension>().dirtyDef;
@ -211,9 +213,25 @@ namespace RJW_Menstruation
public virtual void WearEffect(int tickInterval) public virtual void WearEffect(int tickInterval)
{ {
absorbedfluids += PassiveAbsorptionPerHour * tickInterval / GenDate.TicksPerHour; absorbedfluids += PassiveAbsorptionPerHour * tickInterval / GenDate.TicksPerHour;
CheckDirty();
if (dirty) wearTicks += tickInterval; if (dirty) wearTicks += tickInterval;
} }
public void CheckDirty()
{
if (absorbedfluids > this.GetStatValue(VariousDefOf.MaxAbsorbable) && !(Wearer?.apparel?.IsLocked(this) ?? false) && DirtyDef != def && DirtyDef != null)
{
bool oldHasStats = !def.equippedStatOffsets.NullOrEmpty();
bool newHasStats = !DirtyDef.equippedStatOffsets.NullOrEmpty();
def = DirtyDef;
dirty = true;
Wearer.outfits?.forcedHandler?.SetForced(this, false);
if (oldHasStats || newHasStats)
Wearer.health.capacities.Notify_CapacityLevelsDirty();
Wearer.apparel.Notify_ApparelChanged();
}
}
public override Color DrawColorTwo => fluidColor; public override Color DrawColorTwo => fluidColor;
public override void ExposeData() public override void ExposeData()
@ -231,15 +249,30 @@ namespace RJW_Menstruation
Scribe_Values.Look(ref fluidColor, "fluidColor", Color.white); Scribe_Values.Look(ref fluidColor, "fluidColor", Color.white);
} }
public override string DescriptionDetailed
{
get
{
StringBuilder text = new StringBuilder(base.DescriptionDetailed);
text.AppendLine();
text.Append(Translations.Description_Absorbed);
text.Append(": ");
text.Append(absorbedfluids.ToStringDecimalIfSmall());
text.Append("/");
text.Append(this.GetStatValue(VariousDefOf.MaxAbsorbable).ToStringDecimalIfSmall());
text.Append("ml");
return text.ToString();
}
}
} }
public class Absorber_Tampon : Absorber public class Absorber_Tampon : Absorber
{ {
protected override float PassiveAbsorptionPerHour => 0.5f;
public override void DirtyEffect(int tickInterval) public override void DirtyEffect(int tickInterval)
{ {
if (wearTicks > MinHrstoDirtyEffect * GenDate.TicksPerHour && Rand.MTBEventOccurs(100.0f, GenDate.TicksPerHour, tickInterval) && !Wearer.apparel.IsLocked(this)) if (wearTicks > MinHrstoDirtyEffect * GenDate.TicksPerHour && Rand.MTBEventOccurs(100.0f, GenDate.TicksPerHour, tickInterval) && !(Wearer.apparel?.IsLocked(this) ?? false))
{ {
Wearer.health.AddHediff(HediffDefOf.WoundInfection, Genital_Helper.get_genitalsBPR(Wearer)); Wearer.health.AddHediff(HediffDefOf.WoundInfection, Genital_Helper.get_genitalsBPR(Wearer));
} }

View file

@ -44,6 +44,7 @@ namespace RJW_Menstruation
public static readonly string Dialog_WombInfo08 = "Dialog_WombInfo08".Translate(); public static readonly string Dialog_WombInfo08 = "Dialog_WombInfo08".Translate();
public static readonly string Dialog_WombInfo09 = "Dialog_WombInfo09".Translate(); public static readonly string Dialog_WombInfo09 = "Dialog_WombInfo09".Translate();
public static readonly string Dialog_WombInfo10 = "Dialog_WombInfo10".Translate(); public static readonly string Dialog_WombInfo10 = "Dialog_WombInfo10".Translate();
public static readonly string Description_Absorbed = "Description_Absorbed".Translate();
public static readonly string Option1_Label_1 = "Option1_Label_1".Translate(); public static readonly string Option1_Label_1 = "Option1_Label_1".Translate();

View file

@ -388,10 +388,9 @@ namespace RJW_Menstruation
Rect genitalIconRect = new Rect(rect.x, rect.y + fontheight, genitalRectWidth, genitalRectHeight); Rect genitalIconRect = new Rect(rect.x, rect.y + fontheight, genitalRectWidth, genitalRectHeight);
Rect genitalVaginaLabelRect = new Rect(rect.x, rect.y + 10f, genitalRectWidth, fontheight); Rect genitalVaginaLabelRect = new Rect(rect.x, rect.y + 10f, genitalRectWidth, fontheight);
Rect genitalAnusLabelRect = new Rect(rect.x, rect.y + fontheight + genitalRectHeight, genitalRectWidth, fontheight); Rect genitalAnusLabelRect = new Rect(rect.x, rect.y + fontheight + genitalRectHeight, genitalRectWidth, fontheight);
bool showOrigin = Mouse.IsOver(genitalIconRect) && Input.GetMouseButton(0);
vagina = pawn.GetGenitalIcon(comp, showOrigin); vagina = pawn.GetGenitalIcon(comp);
anal = pawn.GetAnalIcon(showOrigin); anal = pawn.GetAnalIcon();
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 = Utility.SafeSkinColor(pawn); GUI.color = Utility.SafeSkinColor(pawn);

View file

@ -86,7 +86,7 @@ namespace RJW_Menstruation
{ {
res = 0.0f; res = 0.0f;
} }
if (pawn.HasQuirk(QuirkUtility.Quirks.Messy)) res *= Rand.Range(4.0f, 8.0f); if (pawn.IsMessy()) res *= Rand.Range(4.0f, 8.0f);
return res; return res;
} }
@ -95,16 +95,7 @@ namespace RJW_Menstruation
public static HediffComp_Breast GetBreastComp(this Pawn pawn) public static HediffComp_Breast GetBreastComp(this Pawn pawn)
{ {
List<Hediff> hedifflist = Genital_Helper.get_PartsHediffList(pawn, Genital_Helper.get_breastsBPR(pawn))?.FindAll(h => VariousDefOf.AllBreasts.Contains(h.def)); return pawn.health.hediffSet.hediffs.FirstOrDefault((Hediff h) => VariousDefOf.AllBreasts.Contains(h.def))?.TryGetComp<HediffComp_Breast>();
if (hedifflist.NullOrEmpty()) hedifflist = Genital_Helper.get_PartsHediffList(pawn, Genital_Helper.get_uddersBPR(pawn))?.FindAll(h => VariousDefOf.AllBreasts.Contains(h.def));
if (hedifflist.NullOrEmpty()) return null;
HediffComp_Breast result;
foreach (Hediff h in hedifflist)
{
result = h.TryGetComp<HediffComp_Breast>();
if (result != null) return result;
}
return null;
} }
public static HediffComp_Breast GetBreastComp(this Hediff hediff) public static HediffComp_Breast GetBreastComp(this Hediff hediff)
@ -192,8 +183,7 @@ namespace RJW_Menstruation
public static void DrawBreastIcon(this Pawn pawn, Rect rect) public static void DrawBreastIcon(this Pawn pawn, Rect rect)
{ {
Hediff hediff = Genital_Helper.get_PartsHediffList(pawn, Genital_Helper.get_breastsBPR(pawn)).FirstOrDefault(h => VariousDefOf.AllBreasts.Contains(h.def)) ?? Hediff hediff = pawn.health.hediffSet.hediffs.FirstOrDefault(h => VariousDefOf.AllBreasts.Contains(h.def));
Genital_Helper.get_PartsHediffList(pawn, Genital_Helper.get_uddersBPR(pawn)).FirstOrDefault(h => VariousDefOf.AllBreasts.Contains(h.def));
Texture2D breast, nipple, areola; Texture2D breast, nipple, areola;
if (hediff != null) if (hediff != null)
{ {
@ -351,20 +341,19 @@ namespace RJW_Menstruation
public static string GetVaginaLabel(this Pawn pawn) public static string GetVaginaLabel(this Pawn pawn)
{ {
Hediff hediff = Genital_Helper.get_PartsHediffList(pawn, Genital_Helper.get_genitalsBPR(pawn)).Find(h => VariousDefOf.AllVaginas.Contains(h.def)); Hediff hediff = pawn.health.hediffSet.hediffs.Find(h => VariousDefOf.AllVaginas.Contains(h.def));
return hediff.LabelBase.CapitalizeFirst() + "\n(" + hediff.LabelInBrackets + ")" + "\n" + xxx.CountOfSex.LabelCap.CapitalizeFirst() + ": " + pawn.records.GetAsInt(xxx.CountOfSex); return hediff.LabelBase.CapitalizeFirst() + "\n(" + hediff.LabelInBrackets + ")" + "\n" + xxx.CountOfSex.LabelCap.CapitalizeFirst() + ": " + pawn.records.GetAsInt(xxx.CountOfSex);
} }
public static string GetAnusLabel(this Pawn pawn) public static string GetAnusLabel(this Pawn pawn)
{ {
Hediff hediff = Genital_Helper.get_PartsHediffList(pawn, Genital_Helper.get_anusBPR(pawn)).FirstOrDefault(h => VariousDefOf.AllAnuses.Contains(h.def)) ?? Hediff hediff = pawn.health.hediffSet.hediffs.FirstOrDefault(h => VariousDefOf.AllAnuses.Contains(h.def)) ??
Genital_Helper.get_PartsHediffList(pawn, Genital_Helper.get_anusBPR(pawn)).FirstOrDefault(h => h.def.defName.ToLower().Contains("anus")); Genital_Helper.get_PartsHediffList(pawn, Genital_Helper.get_anusBPR(pawn)).FirstOrDefault(h => h.def.defName.ToLower().Contains("anus"));
if (hediff != null) return hediff.LabelBase.CapitalizeFirst() + "\n(" + hediff.LabelInBrackets + ")"; if (hediff != null) return hediff.LabelBase.CapitalizeFirst() + "\n(" + hediff.LabelInBrackets + ")";
else return ""; else return "";
} }
public static string GetBreastLabel(this Pawn pawn) public static string GetBreastLabel(this Pawn pawn)
{ {
Hediff hediff = Genital_Helper.get_PartsHediffList(pawn, Genital_Helper.get_breastsBPR(pawn)).FirstOrDefault(h => VariousDefOf.AllBreasts.Contains(h.def)) ?? Hediff hediff = pawn.health.hediffSet.hediffs.FirstOrDefault(h => VariousDefOf.AllBreasts.Contains(h.def));
Genital_Helper.get_PartsHediffList(pawn, Genital_Helper.get_uddersBPR(pawn)).FirstOrDefault(h => VariousDefOf.AllBreasts.Contains(h.def));
if (hediff != null) return hediff.LabelBase.CapitalizeFirst() + "\n(" + hediff.LabelInBrackets + ")"; if (hediff != null) return hediff.LabelBase.CapitalizeFirst() + "\n(" + hediff.LabelInBrackets + ")";
else return ""; else return "";
} }
@ -416,7 +405,7 @@ namespace RJW_Menstruation
{ {
Pawn res = pawn.GetFather(); Pawn res = pawn.GetFather();
if (res != null) return res; if (res != null) return res;
else res = pawn.relations?.GetFirstDirectRelationPawn(PawnRelationDefOf.Parent, x => x != mother) ?? null; else res = pawn.relations?.GetFirstDirectRelationPawn(PawnRelationDefOf.Parent, x => x != mother);
return res; return res;
} }
@ -448,7 +437,7 @@ namespace RJW_Menstruation
{ {
if (!Configurations.EnableWombIcon) return false; if (!Configurations.EnableWombIcon) return false;
if (pawn.Drafted && !Configurations.EnableDraftedIcon) return false; if (pawn.Drafted && !Configurations.EnableDraftedIcon) return false;
if (pawn.IsAnimal() && !Configurations.EnableAnimalCycle) return false; if (!pawn.ShouldCycle()) return false;
return true; return true;
} }

View file

@ -40,44 +40,26 @@ namespace RJW_Menstruation
public static readonly ThoughtDef TookContraceptivePill = DefDatabase<ThoughtDef>.GetNamed("TookContraceptivePill"); public static readonly ThoughtDef TookContraceptivePill = DefDatabase<ThoughtDef>.GetNamed("TookContraceptivePill");
public static readonly ThoughtDef HateTookContraceptivePill = DefDatabase<ThoughtDef>.GetNamed("HateTookContraceptivePill"); public static readonly ThoughtDef HateTookContraceptivePill = DefDatabase<ThoughtDef>.GetNamed("HateTookContraceptivePill");
public static readonly ThoughtDef EggRestorationReceived = DefDatabase<ThoughtDef>.GetNamed("EggRestorationReceived"); public static readonly ThoughtDef EggRestorationReceived = DefDatabase<ThoughtDef>.GetNamed("EggRestorationReceived");
public static readonly CompProperties_Menstruation HumanVaginaCompProperties = (CompProperties_Menstruation)Genital_Helper.average_vagina.comps.FirstOrDefault(x => x is CompProperties_Menstruation); public static readonly CompProperties_Menstruation HumanVaginaCompProperties = Genital_Helper.average_vagina.CompProps<CompProperties_Menstruation>();
public static readonly KeyBindingDef OpenStatusWindowKey = DefDatabase<KeyBindingDef>.GetNamed("OpenStatusWindow"); public static readonly KeyBindingDef OpenStatusWindowKey = DefDatabase<KeyBindingDef>.GetNamed("OpenStatusWindow");
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;
private static HashSet<HediffDef> allvaginas = null; private static HashSet<HediffDef> allvaginas = null;
private static HashSet<HediffDef> allanuses = null; private static HashSet<HediffDef> allanuses = null;
private static HashSet<HediffDef> allbreasts = null; private static HashSet<HediffDef> allbreasts = null;
private static HashSet<GeneDef> egglayergenes = null;
public static List<ThingDef> AllRaces public static List<ThingDef> AllRaces
{ {
get get
{ {
if (allraces != null) return allraces; if (allraces != null) return allraces;
allraces = DefDatabase<ThingDef>.AllDefsListForReading.Where(thingdef => thingdef.race?.IsFlesh ?? false).ToList();
List<ThingDef> allThings = DefDatabase<ThingDef>.AllDefsListForReading;
allraces = allThings.FindAll(x => x.race != null && x.race.IsFlesh);
return allraces; return allraces;
} }
} }
@ -86,9 +68,8 @@ namespace RJW_Menstruation
get get
{ {
if (allkinds != null) return allkinds; if (allkinds != null) return allkinds;
allkinds = DefDatabase<PawnKindDef>.AllDefsListForReading.Where(pawnkinddef => pawnkinddef.race != null).ToList();
List<PawnKindDef> allKinds = DefDatabase<PawnKindDef>.AllDefsListForReading;
allkinds = allKinds.FindAll(x => x.race != null);
return allkinds; return allkinds;
} }
} }
@ -161,6 +142,16 @@ namespace RJW_Menstruation
return allbreasts; return allbreasts;
} }
} }
public static HashSet<GeneDef> EggLayerGenes
{
get
{
if (egglayergenes != null) return egglayergenes;
egglayergenes = DefDatabase<GeneDef>.AllDefsListForReading.Where(geneDef => geneDef.GetModExtension<MenstruationModExtension>()?.disableCycle ?? false).ToHashSet();
return egglayergenes;
}
}
// Defs from Milkable Colonists // Defs from Milkable Colonists
public static readonly HediffDef Hediff_Lactating_Drug = DefDatabase<HediffDef>.GetNamedSilentFail("Lactating_Drug"); public static readonly HediffDef Hediff_Lactating_Drug = DefDatabase<HediffDef>.GetNamedSilentFail("Lactating_Drug");

View file

@ -1,7 +1,9 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<ModMetaData> <ModMetaData>
<packageId>rjw.menstruation</packageId>
<name>RJW Menstruation Cycle</name> <name>RJW Menstruation Cycle</name>
<author>lutepickle</author> <author>lutepickle</author>
<url>https://gitgud.io/lutepickle/rjw_menstruation/</url>
<supportedVersions> <supportedVersions>
<li>1.2</li> <li>1.2</li>
<li>1.3</li> <li>1.3</li>
@ -28,13 +30,17 @@
<li>Abraxas.RJW.RaceSupport</li> <li>Abraxas.RJW.RaceSupport</li>
<li>rjw.milk.humanoid</li> <li>rjw.milk.humanoid</li>
</loadAfter> </loadAfter>
<packageId>rjw.menstruation</packageId> <incompatibleWithByVersion>
<v1.4>
<li>conit.thebirdsandthebees</li> <!--Breaks fertility calculations-->
</v1.4>
</incompatibleWithByVersion>
<description>Adds menstruation mechanics to vaginas: <description>Adds menstruation mechanics to vaginas:
Wombs cycle between follicular, luteal, and bleeding phases Wombs cycle between follicular, luteal, and bleeding phases
Tracks eggs ovulated and cum in wombs to determine pregnancy Tracks eggs ovulated and cum in wombs to determine pregnancy
Womb icon and status window Womb icon and status window
Estrus effects Estrus and pheromone effects
Race-specific fetus images for many vanilla animals Race-specific fetus images for many vanilla animals
Pregnancies from multiple eggs and different fathers Pregnancies from multiple eggs and different fathers
Identical siblings Identical siblings

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.9.1</version> <version>1.0.9.4</version>
<dependencies> <dependencies>
</dependencies> </dependencies>
<incompatibleWith /> <incompatibleWith />

View file

@ -14,6 +14,5 @@
<v1.4> <v1.4>
<li>1.4</li> <li>1.4</li>
<li IfModActive="ASMR.RJW.RaceSupport">1.4/RJW Menstruation Race Support</li> <li IfModActive="ASMR.RJW.RaceSupport">1.4/RJW Menstruation Race Support</li>
<!-- <li IfModActive="rjw.milk.humanoid">1.3/MilkModule</li> -->
</v1.4> </v1.4>
</loadFolders> </loadFolders>

View file

@ -1,8 +1,36 @@
Version 1.0.9.4
- Pawns with the egglaying genes from Erin's Corvyia and Outland - Genetics no longer have a menstrual cycle.
Version 1.0.9.3
- The biosculptor egg restoration cycle will now give more eggs to races that ovulate more than one egg at a time.
- All pawns can now use all menstruation genes, regardless of gender or having a womb.
- Egglaying animals no longer have a menstrual cycle.
- Fix bug preventing absorbers from becoming dirty.
Version 1.0.9.2
- Updated Traditional Chinese translation by Hydrogen.
- Fixed the no bleeding gene having positive metabolic efficiency instead of negative.
- Fixed ovulation chance not accounting for genes or precepts.
- Better handle wombs that are not in the genitals body part.
- Handle errors more gracefully when starting a pregnancy.
- Fix implanting multiple eggs in an animal when configured to use Biotech pregnancies.
- An equipped tampon or pad will now show how much fluid it has absorbed in its tooltip.
- Passive absorption will now make a tampon or pad dirty after enough time, even if no fluid was added.
- It will take a cloth tampon about 2 days to become dirty passively. A cloth pad will take about 10 days.
- An absorber that was force worn will no longer be forced once it becomes dirty.
- Pawns without a menstrual cycle will no longer equip absorbers.
- Egglaying races no longer have a menstrual cycle, regardless of vagina type.
- Pawns with the egglaying genes from Alpha Genes, VE Saurids, or Phytokin no longer have a menstrual cycle.
- The womb status button will now appear in a pawn's health tab when using Compact Hediffs, with thanks to Fern.
- Tampons and pads are now allowed by default in the worker, soldier, and slave starting outfits.
Version 1.0.9.1 Version 1.0.9.1
- Japanese translation for most text by Lokuzt.
- New womb, cum, and fetus graphics by Euldrop. - New womb, cum, and fetus graphics by Euldrop.
- Add new ideology-related thoughts with the sexperience-ideology mod. - Add new ideology-related thoughts with the sexperience-ideology mod.
- New pheromone system: Pawns in visible estrus will increase the sex drive of nearby males. Can be disabled in the options. - New pheromone system: Pawns in visible estrus will increase the sex drive of nearby males. Can be disabled in the options.
- Using the egg restoration biosculptor cycle will give a small mood buff. - Using the egg restoration biosculptor cycle will give a small mood buff.
- Fix some errors related to precum.
- Periodic ovulators that don't bleed will properly enter anestrus after their luteal stage. - Periodic ovulators that don't bleed will properly enter anestrus after their luteal stage.
- Climacteric/menopausal pawns will have their sex need slow properly when the womb's tick rate is non-default. - Climacteric/menopausal pawns will have their sex need slow properly when the womb's tick rate is non-default.