diff --git a/1.4/Assemblies/RJW_Menstruation.dll b/1.4/Assemblies/RJW_Menstruation.dll index 1e66766..09c9244 100644 Binary files a/1.4/Assemblies/RJW_Menstruation.dll and b/1.4/Assemblies/RJW_Menstruation.dll differ diff --git a/1.4/Defs/GeneDefs/GeneDefs_Menstruation.xml b/1.4/Defs/GeneDefs/GeneDefs_Menstruation.xml index c62719a..d430d3f 100644 --- a/1.4/Defs/GeneDefs/GeneDefs_Menstruation.xml +++ b/1.4/Defs/GeneDefs/GeneDefs_Menstruation.xml @@ -21,11 +21,6 @@ UI/Genes/Placeholder 1 10 - -
  • - 0.75 -
  • -
    @@ -35,11 +30,6 @@ UI/Genes/Placeholder -1 12 - -
  • - 2.0 -
  • -
    @@ -50,11 +40,6 @@ -2 1 16 - -
  • - 4.0 -
  • -
    @@ -71,11 +56,6 @@ UI/Genes/Placeholder 1 20 - -
  • - true -
  • -
    @@ -86,11 +66,6 @@ -1 1 25 - -
  • - true -
  • -
    @@ -107,11 +82,6 @@ UI/Genes/Placeholder -1 30 - -
  • - 2 -
  • -
    @@ -121,11 +91,6 @@ UI/Genes/Placeholder -1 35 - -
  • - 4 -
  • -
    @@ -134,13 +99,8 @@ Menstruation Carriers of this gene will not bleed at the end of their cycle. UI/Genes/Placeholder - -1 + 1 40 - -
  • - true -
  • -
    diff --git a/1.4/Defs/ThingDefs/Apparel_Absorbers.xml b/1.4/Defs/ThingDefs/Apparel_Absorbers.xml index 87fa765..c27e718 100644 --- a/1.4/Defs/ThingDefs/Apparel_Absorbers.xml +++ b/1.4/Defs/ThingDefs/Apparel_Absorbers.xml @@ -59,16 +59,9 @@
  • Absorber
  • - -
  • Worker
  • -
  • Soldier
  • -
  • Nudist
  • -
  • Slave
  • -
  • - 0.2 false true 10 @@ -123,7 +116,6 @@
  • - 0.2 false true 8 @@ -198,15 +190,9 @@
  • Absorber
  • - -
  • Worker
  • -
  • Soldier
  • -
  • Slave
  • -
  • - 0.04 true false Absorber_Pad_Dirty @@ -268,7 +254,6 @@
  • - 0.04 true false Absorber_Pad_Dirty diff --git a/1.4/Languages/ChineseTraditional/DefInjected/HediffDef/Hediffs_Menstruation.xml b/1.4/Languages/ChineseTraditional/DefInjected/HediffDef/Hediffs_Menstruation.xml index fef8db8..df23159 100644 --- a/1.4/Languages/ChineseTraditional/DefInjected/HediffDef/Hediffs_Menstruation.xml +++ b/1.4/Languages/ChineseTraditional/DefInjected/HediffDef/Hediffs_Menstruation.xml @@ -13,10 +13,4 @@ 緩解源自於痛經及此類神經痛的苦楚。 環孢素 由於免疫抑製劑的作用,身體抵抗感染和疾病的能力受扼制。 - - 受費洛蒙影響 - 受附近某人處於發情期之故,此人的性需求及娛樂需求正急遽上升。 - - - diff --git a/1.4/Languages/ChineseTraditional/DefInjected/ThoughtDefs/Thoughts_sex.xml b/1.4/Languages/ChineseTraditional/DefInjected/ThoughtDefs/Thoughts_sex.xml index 86e27de..b1eeb56 100644 --- a/1.4/Languages/ChineseTraditional/DefInjected/ThoughtDefs/Thoughts_sex.xml +++ b/1.4/Languages/ChineseTraditional/DefInjected/ThoughtDefs/Thoughts_sex.xml @@ -11,13 +11,4 @@ 卵母細胞再生術 我可以繼續繁衍一小段時間了! - - 內射了{0} - 讓她懷孕正是我的職責所在。 - {0}內射了我 - 我希望這次能懷上,讓我盡到責任。 - {0}內射了我 - 我知道我有生育的義務,但是難道非他不可嗎? - 服用避孕藥 - 我的信仰要求我能夠懷孕。 diff --git a/1.4/Languages/ChineseTraditional/Keyed/RJW_Menstruation.xml b/1.4/Languages/ChineseTraditional/Keyed/RJW_Menstruation.xml index d190877..9e5a55e 100644 --- a/1.4/Languages/ChineseTraditional/Keyed/RJW_Menstruation.xml +++ b/1.4/Languages/ChineseTraditional/Keyed/RJW_Menstruation.xml @@ -154,18 +154,4 @@ (測試中!) 允許在「生機」(Biotech)追加的懷孕機制中出現多胞胎。 啟用這個選項將允許同卵或異卵雙胞胎出現在「生機」(Biotech)追加的懷孕機制中。 一併將啟用雜交系統;然而兩個類人(humanlikes)生物絕不可能生出動物。 - - 玩家小人刷新間隔 - 殖民者、囚犯和奴隸的子宮狀態應該每隔多久刷新一次? -較小的刷新間隔可以更精確地模擬真實子宮;較大的刷新間隔可以提升遊戲流暢度 - 非玩家小人刷新間隔 - 非玩家勢力控制下的小人的子宮應該每隔多久刷新一次? -較小的刷新間隔可以更精確地模擬真實子宮;較大的刷新間隔可以提升遊戲流暢度 - 動物子宮刷新間隔 - 動物們的子宮應該多久刷新一次? -較小的刷新間隔可以更精確地模擬真實子宮;較大的刷新間隔可以提升遊戲流暢度 - 啟用費洛蒙機制 - 擁有發情期的類人生物在發情期將刺激附近的雄性類人生物,使其性慾大增。 - 動物費洛蒙效果 - 選定擁有發情期的動物在發情期內對類人生物的影響。 diff --git a/1.4/Languages/English/Keyed/RJW_Menstruation.xml b/1.4/Languages/English/Keyed/RJW_Menstruation.xml index 7aa650e..0c796b0 100644 --- a/1.4/Languages/English/Keyed/RJW_Menstruation.xml +++ b/1.4/Languages/English/Keyed/RJW_Menstruation.xml @@ -37,7 +37,7 @@ Unknown - Absorbed + Enable womb icon diff --git a/1.4/Languages/Japanese/DefInjected/ApparelLayerDef/RJWMenstruation.xml b/1.4/Languages/Japanese/DefInjected/ApparelLayerDef/RJWMenstruation.xml deleted file mode 100644 index cf5232e..0000000 --- a/1.4/Languages/Japanese/DefInjected/ApparelLayerDef/RJWMenstruation.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - 性器 - \ No newline at end of file diff --git a/1.4/Languages/Japanese/DefInjected/Drugs/Pills_Menstruation.xml b/1.4/Languages/Japanese/DefInjected/Drugs/Pills_Menstruation.xml deleted file mode 100644 index a801670..0000000 --- a/1.4/Languages/Japanese/DefInjected/Drugs/Pills_Menstruation.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - 鎮痛剤 - 生理痛を24時間和らげます。 その他の痛みにも効果あり。 - シクロスポリン - 免疫抑制剤。 抗精子抗体を直すことができるが、24時間の間、体の免疫力は低下する。 - diff --git a/1.4/Languages/Japanese/DefInjected/HediffDef/RJWMenstruation.xml b/1.4/Languages/Japanese/DefInjected/HediffDef/RJWMenstruation.xml deleted file mode 100644 index e066065..0000000 --- a/1.4/Languages/Japanese/DefInjected/HediffDef/RJWMenstruation.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - 生理中 - 月経周期の終わりに膣からの出血。よく痛い。 - 不快 - 痒い - 痛い - 苦痛 - - - - - \ No newline at end of file diff --git a/1.4/Languages/Japanese/DefInjected/JobDef/RJWMenstruation.xml b/1.4/Languages/Japanese/DefInjected/JobDef/RJWMenstruation.xml deleted file mode 100644 index abd8a16..0000000 --- a/1.4/Languages/Japanese/DefInjected/JobDef/RJWMenstruation.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - 膣を洗っている - - - - \ No newline at end of file diff --git a/1.4/Languages/Japanese/DefInjected/StatDef/RJWMenstruation.xml b/1.4/Languages/Japanese/DefInjected/StatDef/RJWMenstruation.xml deleted file mode 100644 index c5ce3d0..0000000 --- a/1.4/Languages/Japanese/DefInjected/StatDef/RJWMenstruation.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - 吸収量 - 吸収可能な液体の最大量。 - - - \ No newline at end of file diff --git a/1.4/Languages/Japanese/DefInjected/ThingDef/RJWMenstruation.xml b/1.4/Languages/Japanese/DefInjected/ThingDef/RJWMenstruation.xml deleted file mode 100644 index 76f4ba9..0000000 --- a/1.4/Languages/Japanese/DefInjected/ThingDef/RJWMenstruation.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - -タンポン - -膣内の体液を吸収するタンポン。長時間に着けて感染症を引き起こすことがある。 - -汚れたタンポン - -濡れた使用済みタンポン。そのままにしておくと感染症を引き起こす可能性がある。 - -生理用品 - -膣からの体液を吸収するためのナプキン。 - -汚れた生理用品 - -濡れた使用済みナプキン - -卵巣再生薬 - -卵巣を回復させ、卵子の数をある程度を回復させる。 卵子がほとんど残っていない卵巣にはあまり効果がない。 更年期の回復はできない。 - -排卵剤 - -過排卵を誘発し、次の排卵で卵子を1~4個余分に産生させる。 更年期を早める可能性がある。 - -混じった物 - - \ No newline at end of file diff --git a/1.4/Languages/Japanese/DefInjected/ThoughtDefs/RJWMenstruation.xml b/1.4/Languages/Japanese/DefInjected/ThoughtDefs/RJWMenstruation.xml deleted file mode 100644 index 7092dde..0000000 --- a/1.4/Languages/Japanese/DefInjected/ThoughtDefs/RJWMenstruation.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - ぽたぽた - これを吸収するものはないのかな? - {0}に中出した - ライバルに中出した - {0}に中出した - 気持ちよかった。 - {0}に中出した - 妊娠するといいな。 - {0}に中出された - あいつの子を妊娠したくない! - {0}に中出された - そんなことをさせるなんて、私は何を考えていたんだろう? - {0}に中出された - 可能性は低いけど、妊娠できるかどうか心配で... - {0}に中出された - 妊娠しないか心配なんだ。 - {0}に中出された - 妊娠するとは思わないけど、妄想するのは楽しい。 - {0}に中出された - 妊娠することはないだろうけど、やっぱり嫌だ。 - 望まない妊娠 - 赤ん坊の世話なんてできるか?どうすればいい? - 望まない妊娠 - 赤ちゃんが生まれる。なんとかなるといいな。 - 避妊ピルを飲んだ - もう大丈夫です。 - 避妊ピルを飲んだ - 妊娠したい。 - 卵巣復活を得た - もう少しの間、繁殖ことができる。 - diff --git a/1.4/Languages/Japanese/Keyed/RJW_Menstruation.xml b/1.4/Languages/Japanese/Keyed/RJW_Menstruation.xml deleted file mode 100644 index 538bdc3..0000000 --- a/1.4/Languages/Japanese/Keyed/RJW_Menstruation.xml +++ /dev/null @@ -1,152 +0,0 @@ - - - RJW Menstruation Cycle - - 経血 - 空っぽ - 卵胞期 - 排卵期 - 黄体期 - 月経期 - 妊娠 - 出産後 - 無し - クリマクテリック - 更年期 - 無発情期 - 卵巣は卵子を放出するの準備をしています。この時期の終わりに排卵が起こります。 - 卵巣は卵子を放出するの準備をしています。精液が子宮に入ると排卵が起こります。 - 卵巣が卵子を子宮に放出しています。 - 子宮は受精卵を受け入れる準備ができている。この時期が終る前に受精卵が着床すれば妊娠となります。 - 子宮内膜は剥がれ落ち、着床できなかった受精卵を排出します。 - 子宮に赤ちゃんがいます。時間とケアによって、この世界の新しい住人となる。 - 子宮は最近の妊娠から回復しています。 - 子宮は繁殖能力がなく、妊娠することはできない。 - 卵巣が枯渇すると、月経周期が不規則になる。 - 。卵巣が枯渇し、子宮が卵子を産み出せなくなる。 - 繁殖期を過ぎた。時期が来れば発情周期は再開する。 - ステータス - 自分を搾乳する - ステータス - 胎児 - 父親 - 精液リスト - 受精卵 - 受精中 - 排卵済み - - - 精液をバケッツに集める - 子宮に精液をためる - 不明 - - - - Enable womb icon - Enable button in health tab - Enable animal cycle - Simulate animal cycles. Not recommended. - Implantation chance - Base implantation chance of fertilized egg This value affects the chance of impregnation. - Fertilization chance - The fertilization chance per ml of sperm per hour This value affects the chance of impregnation. - Cum decay ratio per hour - The amount of cum in the womb will drop by this amount every hour This value affects the chance of impregnation. - Cum fertility decay ratio per hour - Cum will lose fertility by this amount every hour This value affects fertilization chance indirectly. - Cycle acceleration - Accelerate menstruation cycle This can cause early menopause and infertility. Setting this lower than x12 is recommended. Rimworld's timescale: x6(default) - Colonist update interval - How often the womb of each of your colonists, prisoners, and slaves update. Lowering this will improve accuracy, increasing this can improve performance. - Non-colonist update interval - How often the womb of humans you don't control update. Lowering this will improve accuracy, increasing this can improve performance. - Animal update interval - How often the womb of animals update. Lowering this will improve accuracy, increasing this can improve performance. - Debug - Show debug information. - Womb status - Draw womb icon in status window. - Vagina and breast status - Draw vagina, anus and breast icons in the status window. - Fetus information level - Show all information about a fetus. - Show all information about a fetus after discovered pregnancy. - Show only image of a fetus after discovered pregnancy. - Do not show any information about a fetus. - Enable menopause - Enable menopause effect that makes pawn infertile when they run out of eggs If you have problems with long lived races, turn off this option. - Use multiple pregnancy - Use multiple pregnancy instead RJW's default pregnancy Disable this option if you are in trouble with impregnation RJW pregnancy should be turned on. - Enable hetero ovular twins - Allow pregnancy from multiple eggs at the same time. - Enable enzygotic twins - Enable a single egg to result in multiple identical offspring. - Enzygotic twins chance - The chance of identical twins. - Max enzygotic twins - The maximum number of identical siblings. - Enable egg overlay - Enable egg overlay on womb icon. - Bleeding amount - Estimated total bleeding amount - The approximate bleeding amount. The normal bleeding amount of a human is about 20~80ml per cycle. - Colonist - Prisoner - Allied Faction - Neutral - Hostile Faction - Target pawns: - The gizmo and button will appear for these pawns - Use hybrid extension - Overrides RJW and RaceSupport's hybrid definition. Dominant hybrid extension determines whose definition used first. Not recommended to change this. - Dominant hybrid extension - - - Breast growth during pregnancy - Change how much a pregnant pawn's breasts will grow when pregnant. Some pawns will grow more than others. - Nipple change during pregnancy - Change how much a pregnant pawn's nipples will change during pregnancy. - Permanent nipple change after pregnancy - Adjusts approximately how much of a pregnant pawn's nipples will remain changed after the pregnancy ends. - Customize Hybrids - Open custom hybrid editor. This will overrides hybrid definitions of XML files. - Allow shrink icon - Let icon become smaller if needed. - Egg lifespan multiplier - Multiply egg's lifespan. All non-implanted eggs will die at end of luteal stage regardless of this setting. - Enable vagina morph after birth - Enable permanent vagina stretch after birth. If you are using another mod handling this, turn off this option. - Morph power - Set morph power. - Enable gather cum gizmo - Estrus overrides RJW hookup settings - If enabled, a pawn in visible estrus will use these settings for potential impregnation hookups instead of the RJW settings. All settings default to their RJW counterparts. - Hookup minimum fuckability in estrus - Hookup minimum attractability in estrus - Hookup minimum opinion in estrus - Estimated sperm lifespan - Estimated egg lifespan - 排卵 {0} - 排卵時に各卵が放出される確率。 - 受精卵の着床確率。 この時間に受精する確率{0}% - Use basic RJW pregnancy - Use menstruation multiple pregnancy - Use Biotech pregnancy - (EXPERIMENTAL) Enable multiple babies/twins in a single Biotech pregnancy. - Enabling this option will allow identical and hetero ovular twins with Biotech. Also allows the hybrid system, but two humanlikes cannot produce an animal. - Show womb status when drafted - Draw womb icon for drafted pawns - Reset to default - - 精液を収集 - 膣を洗う - - Custom Hybrid Editor - Hybrids of {0} - When {0} breed with {1}, {2} will be born at {3} chance. If both races have hybrid definitions for each other, the father's definition will be used. - - 卵子がない - 子宮が必要 - {PAWN_labelShort} has completed {PAWN_possessive} egg restoration cycle. - - \ No newline at end of file diff --git a/1.4/MilkModule/Assemblies/MilkModule.dll b/1.4/MilkModule/Assemblies/MilkModule.dll new file mode 100644 index 0000000..95909f5 Binary files /dev/null and b/1.4/MilkModule/Assemblies/MilkModule.dll differ diff --git a/1.4/MilkModule/Defs/JobDefs/Jobs_MilkSelf_MC.xml b/1.4/MilkModule/Defs/JobDefs/Jobs_MilkSelf_MC.xml new file mode 100644 index 0000000..69cdc4d --- /dev/null +++ b/1.4/MilkModule/Defs/JobDefs/Jobs_MilkSelf_MC.xml @@ -0,0 +1,9 @@ + + + + LactateSelf_MC + MilkModule.JobDriver_MilkSelf_MC + lactating self + true + + diff --git a/1.4/Patches/CycleDisabledGenes.xml b/1.4/Patches/CycleDisabledGenes.xml deleted file mode 100644 index 18b4d83..0000000 --- a/1.4/Patches/CycleDisabledGenes.xml +++ /dev/null @@ -1,73 +0,0 @@ - - - - - -
  • Alpha Genes
  • - - - /Defs/GeneDef[defName="AG_Egglaying"] - -
  • - true -
  • -
    -
    - - - - -
  • Vanilla Races Expanded - Saurid
  • -
    - - /Defs/GeneDef[defName="VRESaurids_Oviparous"] - -
  • - true -
  • -
    -
    -
    - - - -
  • Vanilla Races Expanded - Phytokin
  • -
    - - /Defs/GeneDef[defName="VRE_SaplingBirth"] - -
  • - true -
  • -
    -
    -
    - - - -
  • Erin's Corvyia
  • -
    - - /Defs/GeneDef[defName="ERN_EggLayer"] - -
  • - true -
  • -
    -
    -
    - - - -
  • Outland - Genetics
  • -
    - - /Defs/GeneDef[defName="Outland_EggLayer"] - -
  • - true -
  • -
    -
    -
    - \ No newline at end of file diff --git a/1.4/Patches/Hediffs_PrivateParts.xml b/1.4/Patches/Hediffs_PrivateParts.xml index 58bd529..6b6a570 100644 --- a/1.4/Patches/Hediffs_PrivateParts.xml +++ b/1.4/Patches/Hediffs_PrivateParts.xml @@ -144,5 +144,11 @@ - + + + + + + + \ No newline at end of file diff --git a/1.4/source/RJW_Menstruation/RJW_Menstruation.sln b/1.4/source/RJW_Menstruation/RJW_Menstruation.sln index 37d1010..701c76e 100644 --- a/1.4/source/RJW_Menstruation/RJW_Menstruation.sln +++ b/1.4/source/RJW_Menstruation/RJW_Menstruation.sln @@ -5,6 +5,8 @@ VisualStudioVersion = 17.3.32929.385 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RJW_Menstruation", "RJW_Menstruation\RJW_Menstruation.csproj", "{EED2F3B9-8C20-4194-919E-8D151B29F70B}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MilkModule", "MilkModule\MilkModule.csproj", "{3591B3C1-EB57-44BF-AB69-A613E097A7F8}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -15,6 +17,10 @@ Global {EED2F3B9-8C20-4194-919E-8D151B29F70B}.Debug|Any CPU.Build.0 = Debug|Any CPU {EED2F3B9-8C20-4194-919E-8D151B29F70B}.Release|Any CPU.ActiveCfg = Release|Any CPU {EED2F3B9-8C20-4194-919E-8D151B29F70B}.Release|Any CPU.Build.0 = Release|Any CPU + {3591B3C1-EB57-44BF-AB69-A613E097A7F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3591B3C1-EB57-44BF-AB69-A613E097A7F8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3591B3C1-EB57-44BF-AB69-A613E097A7F8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3591B3C1-EB57-44BF-AB69-A613E097A7F8}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_Breast.cs b/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_Breast.cs index eacab7d..558031d 100644 --- a/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_Breast.cs +++ b/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_Breast.cs @@ -144,13 +144,11 @@ namespace RJW_Menstruation Scribe_Values.Look(ref baseAlpha, "baseAlpha", baseAlpha, true); Scribe_Values.Look(ref baseAreola, "baseAreola", baseAreola, true); Scribe_Values.Look(ref baseNipple, "baseNipple", baseNipple, true); - if (Scribe.mode == LoadSaveMode.PostLoadInit) - Initialize(); } public bool ShouldSimulate() { - if (!Pawn.ShouldCycle()) return false; + if (!Configurations.EnableAnimalCycle && Pawn.IsAnimal()) return false; if (Pawn.SpawnedOrAnyParentSpawned || Pawn.IsCaravanMember() || PawnUtility.IsTravelingInTransportPodWorldObject(Pawn)) return true; return false; diff --git a/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_InducedOvulator.cs b/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_InducedOvulator.cs index 3b4ab9c..a32969c 100644 --- a/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_InducedOvulator.cs +++ b/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_InducedOvulator.cs @@ -30,7 +30,7 @@ namespace RJW_Menstruation switch (CurrentVisibleStage) { case Stage.Follicular: - return Translations.Stage_Follicular_Induced_Desc + (EggHealth < 1f ? " " + Translations.Stage_Climacteric_Desc : ""); + return Translations.Stage_Follicular_Induced_Desc + (EggHealth < 1f ? Translations.Stage_Climacteric_Desc : ""); default: return base.GetCurStageDesc; } @@ -106,10 +106,19 @@ namespace RJW_Menstruation protected override bool ShouldBeInEstrus() { - if (curStage == Stage.Luteal && !hadOvulatoryStage) return false; - else return base.ShouldBeInEstrus(); + if (!loaded) + Initialize(); + switch (curStage) + { + case Stage.Follicular: + return curStageTicks > currentIntervalTicks - Props.estrusDaysBeforeOvulation * GenDate.TicksPerDay; + case Stage.Ovulatory: + return true; + case Stage.Luteal: + return hadOvulatoryStage && curStageTicks < EggLifespanTicks; + default: + return false; + } } - - protected override float RandomOvulationChance => 0; } } diff --git a/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_Menstruation.cs b/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_Menstruation.cs index f7793e1..e18dad4 100644 --- a/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_Menstruation.cs +++ b/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_Menstruation.cs @@ -127,6 +127,7 @@ namespace RJW_Menstruation protected float implantationChanceCache = -1.0f; protected int opcache = -1; protected float antisperm = 0.0f; + protected float? originvagsize = null; // RJW pregnancy, or Biotech pregnancy/labor/laborpushing protected Hediff pregnancy = null; @@ -178,8 +179,7 @@ namespace RJW_Menstruation public float HoursBetweenSimulations => (float)TickInterval / GenDate.TicksPerHour; - public Hediff Pregnancy - { + public Hediff Pregnancy { get { if (pregnancy == null) return null; @@ -198,9 +198,20 @@ namespace RJW_Menstruation get { 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; opcache = (int)(RaceCyclesPerYear() * - AverageLitterSize() * + avglittersize * yearsBeforeMenopause * (Pawn.RaceProps.lifeExpectancy / ThingDefOf.Human.race.lifeExpectancy)); if (opcache == 0) opcache = 1; @@ -255,7 +266,7 @@ namespace RJW_Menstruation public float TotalCum { - get => cums?.Sum(cum => cum.Volume) ?? 0; + get => cums?.Sum(cum => cum.Volume) ?? 0; } public float TotalFertCum { @@ -279,7 +290,7 @@ namespace RJW_Menstruation { get { - if (Pawn.IsBreeder()) return 0.5f; + if (QuirkUtility.HasQuirk(Pawn, QuirkUtility.Quirks.Breeder)) return 0.5f; return 1.0f; } @@ -289,7 +300,7 @@ namespace RJW_Menstruation private bool calculatingOvulationChance = false; public bool CalculatingOvulationChance { get => calculatingOvulationChance; } - protected float CalculatedOvulationChance() + protected float CalcuatedOvulationChance() { float ovulationChance = 1.0f; if (EggHealth <= 0.0f) return 0.0f; @@ -297,14 +308,20 @@ namespace RJW_Menstruation if (ModsConfig.BiotechActive && xxx.is_human(Pawn)) { if (Pawn.SterileGenes()) return 0.0f; + // Replicate how rjw.PawnCapacityWorker_Fertility.CalculateCapacityLevel does it, but without the age factor if (!Pawn.RaceHasFertility()) return 0.0f; if (AndroidsCompatibility.IsAndroid(Pawn) && parent.def != Genital_Helper.archotech_vagina) return 0.0f; - - float ageFactor = 1.0f; - StatDefOf.Fertility.GetStatPart()?.TransformValue(StatRequest.For(Pawn), ref ageFactor); - if (ageFactor <= 0.0f) return 0.0f; // Too young or too old - - if (Pawn.IsBreeder()) ovulationChance *= 10.0f; + foreach (var part in StatDefOf.Fertility.parts) + { + if(part is StatPart_FertilityByGenderAge fertilityByAge) + { + float factor = 1.0f; + fertilityByAge.TransformValue(StatRequest.For(Pawn), ref factor); + if (factor <= 0.0f) return 0.0f; // Too young or too old + } + else part.TransformValue(StatRequest.For(Pawn), ref ovulationChance); + } + if (Pawn.HasQuirk(QuirkUtility.Quirks.Breeder)) ovulationChance *= 10.0f; try { calculatingOvulationChance = true; @@ -315,20 +332,20 @@ namespace RJW_Menstruation return ovulationChance; } - protected float CalculatedImplantChance() + protected float CalcuatedImplantChance() { + float factor = 1.0f; if (ModsConfig.BiotechActive && xxx.is_human(Pawn)) { // Implant factor will be based solely on pawn age, plus any rollover from ovulation chance - float factor = 1.0f; - StatDefOf.Fertility.GetStatPart()?.TransformValue(StatRequest.For(Pawn), ref factor); - if (factor <= 0.0f) return 0.0f; + StatPart_FertilityByGenderAge fertilityStatPart = StatDefOf.Fertility.GetStatPart(); + fertilityStatPart?.TransformValue(StatRequest.For(Pawn), ref factor); if (OvulationChance > 1.0f) factor *= OvulationChance; return Props.baseImplantationChanceFactor * FertilityModifier * factor; } else { - return Pawn.health.capacities.GetLevel(xxx.reproduction) * Props.baseImplantationChanceFactor * FertilityModifier; + return Pawn.health.capacities.GetLevel(xxx.reproduction) * Props.baseImplantationChanceFactor * FertilityModifier * factor; } } @@ -336,17 +353,16 @@ namespace RJW_Menstruation { get { - if (ovulationChanceCache < 0.0f) ovulationChanceCache = CalculatedOvulationChance(); + if (ovulationChanceCache < 0.0f) ovulationChanceCache = CalcuatedOvulationChance(); return ovulationChanceCache; } } - // Before configuration setting public float ImplantChance { get { - if (implantationChanceCache < 0.0f) implantationChanceCache = CalculatedImplantChance(); + if (implantationChanceCache < 0.0f) implantationChanceCache = CalcuatedImplantChance(); return implantationChanceCache; } } @@ -564,6 +580,19 @@ namespace RJW_Menstruation } } + public float OriginVagSize + { + get + { + if (originvagsize == null) + { + originvagsize = parent.Severity; + } + return originvagsize ?? 0.1f; + } + set => originvagsize = value; + } + public int CurStageIntervalTicks { get => currentIntervalTicks; @@ -596,7 +625,7 @@ namespace RJW_Menstruation 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 float lutealProgressWhenImplanted = Math.Min(0.5f, maxImplantDelayHours / (Props.lutealIntervalDays * GenDate.HoursPerDay)); - + return GenMath.LerpDouble(0, discoveryTime, lutealProgressWhenImplanted, 1.0f, pregnancy.Severity); } } @@ -644,10 +673,9 @@ namespace RJW_Menstruation Scribe_Values.Look(ref ovarypower, "ovarypower", ovarypower, true); Scribe_Values.Look(ref eggstack, "eggstack", 0); Scribe_Values.Look(ref estrusflag, "estrusflag", false); + Scribe_Values.Look(ref originvagsize, "originvagsize", originvagsize, true); Scribe_Values.Look(ref DoCleanWomb, "DoCleanWomb", false); Scribe_References.Look(ref pregnancy, "pregnancy"); - if (Scribe.mode == LoadSaveMode.PostLoadInit) - Initialize(); } @@ -665,24 +693,25 @@ namespace RJW_Menstruation estrusLevel = Props.concealedEstrus ? EstrusLevel.Concealed : EstrusLevel.Visible; ovulationFactor = 1f; noBleeding = false; - opcache = -1; - + if (Pawn.genes == null || !ModsConfig.BiotechActive) return; - foreach (MenstruationModExtension extension in Pawn.genes.GenesListForReading.Select(gene => gene.def.GetModExtension()).Where(ext => ext != null)) - { - eggLifeSpanTicks = (int)(eggLifeSpanTicks * extension.eggLifeTimeFactor); - if (extension.alwaysEstrus) estrusLevel = EstrusLevel.Visible; - else if (extension.neverEstrus) estrusLevel = EstrusLevel.None; - ovulationFactor *= extension.ovulationFactor; - if (extension.noBleeding) noBleeding = true; - } - if (eggLifeSpanTicks < 0) eggLifeSpanTicks = 0; - if (ovulationFactor < 0f) ovulationFactor = 0f; + + if (Pawn.genes.HasGene(VariousDefOf.ShortEggLifetime)) eggLifeSpanTicks = eggLifeSpanTicks * 3 / 4; + else if (Pawn.genes.HasGene(VariousDefOf.DoubleEggLifetime)) eggLifeSpanTicks *= 2; + else if (Pawn.genes.HasGene(VariousDefOf.QuadEggLifetime)) eggLifeSpanTicks *= 4; + + if (Pawn.genes.HasGene(VariousDefOf.NeverEstrus)) estrusLevel = EstrusLevel.None; + else if (Pawn.genes.HasGene(VariousDefOf.FullEstrus)) estrusLevel = EstrusLevel.Visible; + + if (Pawn.genes.HasGene(VariousDefOf.DoubleOvulation)) ovulationFactor = 2f; + else if (Pawn.genes.HasGene(VariousDefOf.QuadOvulation)) ovulationFactor = 4f; + + noBleeding = Pawn.genes.HasGene(VariousDefOf.NoBleeding); } public bool ShouldSimulate() { - if (!Pawn.ShouldCycle()) return false; + if (!Configurations.EnableAnimalCycle && Pawn.IsAnimal()) return false; if (Pawn.SpawnedOrAnyParentSpawned || Pawn.IsCaravanMember() || PawnUtility.IsTravelingInTransportPodWorldObject(Pawn)) return true; return false; @@ -704,9 +733,6 @@ namespace RJW_Menstruation // If an exception makes it out, RW will remove the hediff, so catch it here try { - if (Pawn.IsHashIntervalTick(recalculateTickInterval)) TickInterval = -1; // Every so often, force TickInterval to be recalculated in case the pawn's status changed. - if (!Pawn.IsHashIntervalTick(TickInterval)) return; - if (!ShouldSimulate()) return; // Initialize immediately if needed, but if there's an error, then don't spam it every tick @@ -716,6 +742,9 @@ namespace RJW_Menstruation Initialize(); } + if (Pawn.IsHashIntervalTick(recalculateTickInterval)) TickInterval = -1; // Every so often, force TickInterval to be recalculated in case the pawn's status changed. + if (!Pawn.IsHashIntervalTick(TickInterval)) return; + if (initError) Log.Warning($"Attempting to process {Pawn}'s womb uninitialized"); if (Pregnancy != null && curStage != Stage.Pregnant) @@ -725,7 +754,7 @@ namespace RJW_Menstruation } BeforeSimulator(); - + if (ShouldBeInfertile()) GoNextStage(Stage.Infertile); switch (curStage) { @@ -802,7 +831,7 @@ namespace RJW_Menstruation tip.Append(": "); tip.Append(GetCurStageLabel); string fertInfo = GetFertilizingInfo; - if (CurrentVisibleStage == Stage.Luteal && fertInfo.Length > 0) + if(CurrentVisibleStage == Stage.Luteal && fertInfo.Length > 0) { tip.AppendLine(); tip.Append(fertInfo); @@ -813,7 +842,7 @@ namespace RJW_Menstruation protected virtual int TicksToNextStage() { - return Math.Max(0, (currentIntervalTicks - curStageTicks) / Configurations.CycleAcceleration); + return Math.Max(0,(currentIntervalTicks - curStageTicks) / Configurations.CycleAcceleration); } public override string CompDebugString() @@ -858,8 +887,8 @@ namespace RJW_Menstruation if (!precum && fertility > 0 && IsDangerDay && cummer.relations.GetPregnancyApproachForPartner(Pawn) == PregnancyApproach.AvoidPregnancy) { float successChance = pulloutSuccessRate; - if (cummer.HasImpregnationFetish()) successChance *= fetishPulloutSuccessModifier; - if (Pawn.HasImpregnationFetish()) successChance *= fetishPulloutSuccessModifier; + if (cummer.HasQuirk(QuirkUtility.Quirks.ImpregnationFetish)) successChance *= fetishPulloutSuccessModifier; + if (Pawn.HasQuirk(QuirkUtility.Quirks.ImpregnationFetish)) successChance *= fetishPulloutSuccessModifier; if (Rand.Chance(successChance)) return; } if (Pawn.HasIUD()) fertility /= 100f; @@ -871,7 +900,7 @@ namespace RJW_Menstruation bool merged = false; if (!cums.NullOrEmpty()) foreach (Cum cum in cums) { - if (cum.pawn?.Equals(cummer) ?? false) + if (cum.pawn.Equals(cummer)) { cum.MergeWithCum(volume, fertility); merged = true; @@ -886,7 +915,7 @@ namespace RJW_Menstruation bool merged = false; if (!cums.NullOrEmpty()) foreach (Cum cum in cums) { - if (cum.pawn?.Equals(cummer) ?? false) + if (cum.pawn.Equals(cummer)) { cum.MergeWithCum(volume, fertility); merged = true; @@ -923,7 +952,7 @@ namespace RJW_Menstruation bool merged = false; if (!cums.NullOrEmpty()) foreach (Cum cum in cums) { - if (cum.notcum && (cum.pawn?.Equals(cummer) ?? false) && cum.notcumLabel.Equals(notcumlabel)) + if (cum.notcum && cum.pawn.Equals(cummer) && cum.notcumLabel.Equals(notcumlabel)) { cum.MergeWithFluid(volume, decayresist, filthdef); merged = true; @@ -938,7 +967,7 @@ namespace RJW_Menstruation bool merged = false; if (!cums.NullOrEmpty()) foreach (Cum cum in cums) { - if (cum.notcum && (cum.pawn?.Equals(cummer) ?? false) && cum.notcumLabel.Equals(notcumlabel)) + if (cum.notcum && cum.pawn.Equals(cummer) && cum.notcumLabel.Equals(notcumlabel)) { cum.MergeWithFluid(volume, decayresist, filthdef); merged = true; @@ -1158,7 +1187,7 @@ namespace RJW_Menstruation if (cycleSpeed < 0f) cycleSpeed = Utility.RandGaussianLike(0.8f, 1.2f); if (cycleVariability < 0f) cycleVariability = MenstruationUtility.RandomVariabilityPercent(); - + InitOvary(); if (currentIntervalTicks < 0) @@ -1192,21 +1221,6 @@ 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() { int breedingSeasons = 0; @@ -1229,7 +1243,16 @@ namespace RJW_Menstruation public int GetOvaryPowerByAge() { - float avglittersize = AverageLitterSize(); + float avglittersize; + 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 fertEndAge = Pawn.RaceProps.lifeExpectancy * (Pawn.IsAnimal() ? RJWPregnancySettings.fertility_endage_female_animal : RJWPregnancySettings.fertility_endage_female_humanlike); @@ -1346,7 +1369,7 @@ namespace RJW_Menstruation //float fertFailChancePerHour = Mathf.Pow(1.0f - Configurations.FertilizeChance, totalFertPower * Props.basefertilizationChanceFactor); //float fertFailChancePerInterval = Mathf.Pow(fertFailChancePerHour, (float)TickInterval / GenDate.TicksPerHour); float fertFailChancePerInterval = Mathf.Pow(1.0f - Configurations.FertilizeChance, totalFertPower * Props.basefertilizationChanceFactor * HoursBetweenSimulations); - + if (Rand.Chance(fertFailChancePerInterval)) return null; Pawn.records.AddTo(VariousDefOf.AmountofFertilizedEggs, 1); @@ -1389,116 +1412,97 @@ namespace RJW_Menstruation } else if (Rand.Chance(Configurations.ImplantationChance * ImplantChance * InterspeciesImplantFactor(egg.fertilizer))) { - try + if (Configurations.Debug) Log.Message($"Implanting fertilized egg of {Pawn} into {parent}, father {egg.fertilizer}"); + if (pregnancy != null) { - if (Configurations.Debug) Log.Message($"Implanting fertilized egg of {Pawn} into {parent}, father {egg.fertilizer}"); - if (pregnancy == null) + if (Configurations.PregnancySource == Configurations.PregnancyType.Biotech && Configurations.EnableBiotechTwins && Configurations.EnableHeteroOvularTwins) { - Configurations.PregnancyType usePregnancy = xxx.is_human(Pawn) ? Configurations.PregnancySource : Configurations.PregnancyType.MultiplePregnancy; - switch (usePregnancy) + if (Configurations.Debug) Log.Message($"Adding to existing Biotech pregnancy {pregnancy}"); + HediffComp_PregeneratedBabies comp = pregnancy.TryGetComp(); + if (comp == null) Log.Warning($"Trying to add Biotech egg to {Pawn}'s pregnancy without a pregenerated baby comp: {pregnancy}"); + else { - case Configurations.PregnancyType.BaseRJW: - - if (Configurations.Debug) Log.Message($"Creating new base RJW pregnancy"); - PregnancyHelper.AddPregnancyHediff(Pawn, egg.fertilizer); - // I hate having to do this, but it gets the newest pregnancy - List pregnancies = new List(); - Pawn.health.hediffSet.GetHediffs(ref pregnancies); - pregnancy = pregnancies.MaxBy(hediff => hediff.loadID); - pregnant = true; - break; - - case Configurations.PregnancyType.MultiplePregnancy: - if (Configurations.Debug) Log.Message($"Creating new menstruation pregnancy"); - pregnancy = Hediff_BasePregnancy.Create(Pawn, egg.fertilizer); - pregnant = true; - deadeggs.Add(egg); - break; - - case Configurations.PregnancyType.Biotech: - if (Configurations.Debug) Log.Message($"Creating new biotech pregnancy"); - pregnancy = HediffMaker.MakeHediff(HediffDefOf.PregnantHuman, Pawn); - if (Configurations.EnableBiotechTwins) - pregnancy.TryGetComp().AddNewBaby(Pawn, egg.fertilizer); - ((Hediff_Pregnant)pregnancy).SetParents(Pawn, egg.fertilizer, PregnancyUtility.GetInheritedGeneSet(egg.fertilizer, Pawn)); - Pawn.health.AddHediff(pregnancy); - pregnant = true; - deadeggs.Add(egg); - break; + comp.AddNewBaby(Pawn, egg.fertilizer); + pregnant = true; + deadeggs.Add(egg); } - if (pregnancy is Hediff_BasePregnancy rjw_preg) + } + else if (Configurations.PregnancySource == Configurations.PregnancyType.MultiplePregnancy && Configurations.EnableHeteroOvularTwins) + { + if (pregnancy is Hediff_MultiplePregnancy h) { - // TODO: advance biotech pregnancy - rjw_preg.p_start_tick -= egg.ticksSinceFertilization / Configurations.CycleAcceleration; - rjw_preg.p_end_tick -= egg.ticksSinceFertilization / Configurations.CycleAcceleration; + if (Configurations.Debug) Log.Message($"Adding to existing pregnancy {h}"); + h.AddNewBaby(Pawn, egg.fertilizer); } + pregnant = true; + deadeggs.Add(egg); } else { - switch (pregnancy) - { - case Hediff_Pregnant vanillaPreg: // Not going to do the labor ones - if (!Configurations.EnableBiotechTwins || !Configurations.EnableHeteroOvularTwins) goto default; - if (Configurations.Debug) Log.Message($"Adding to existing Biotech pregnancy {vanillaPreg.GetUniqueLoadID()}"); - HediffComp_PregeneratedBabies comp = vanillaPreg.TryGetComp(); - if (comp == null) Log.Warning($"Trying to add Biotech egg to {Pawn}'s pregnancy without a pregenerated baby comp: {vanillaPreg.GetUniqueLoadID()}"); - else - comp.AddNewBaby(Pawn, egg.fertilizer); - pregnant = true; - deadeggs.Add(egg); - break; - case Hediff_MultiplePregnancy multiPreg: - if (!Configurations.EnableHeteroOvularTwins) goto default; - if (Configurations.Debug) Log.Message($"Adding to existing pregnancy {multiPreg.GetUniqueLoadID()}"); - multiPreg.AddNewBaby(Pawn, egg.fertilizer); - pregnant = true; - deadeggs.Add(egg); - break; - case Hediff_BasePregnancy _: - default: - pregnant = true; - deadeggs.Add(egg); - break; - } + pregnant = true; + break; } } - catch (Exception ex) + else { - Log.Error($"Error creating pregnancy in {Pawn}'s womb, father {egg.fertilizer}: {ex}"); - TakeLoosePregnancy(); - deadeggs.Add(egg); + Configurations.PregnancyType usePregnancy = xxx.is_human(Pawn) ? Configurations.PregnancySource : Configurations.PregnancyType.MultiplePregnancy; + switch (usePregnancy) + { + case Configurations.PregnancyType.BaseRJW: + + if (Configurations.Debug) Log.Message($"Creating new base RJW pregnancy"); + PregnancyHelper.AddPregnancyHediff(Pawn, egg.fertilizer); + // I hate having to do this, but it gets the newest pregnancy + List pregnancies = new List(); + Pawn.health.hediffSet.GetHediffs(ref pregnancies); + pregnancy = pregnancies.MaxBy(hediff => hediff.loadID); + pregnant = true; + break; + + case Configurations.PregnancyType.MultiplePregnancy: + if (Configurations.Debug) Log.Message($"Creating new menstruation pregnancy"); + pregnancy = Hediff_BasePregnancy.Create(Pawn, egg.fertilizer); + pregnant = true; + deadeggs.Add(egg); + break; + + case Configurations.PregnancyType.Biotech: + if (Configurations.Debug) Log.Message($"Creating new biotech pregnancy"); + pregnancy = HediffMaker.MakeHediff(HediffDefOf.PregnantHuman, Pawn); + if(Configurations.EnableBiotechTwins) + pregnancy.TryGetComp().AddNewBaby(Pawn, egg.fertilizer); + ((Hediff_Pregnant)pregnancy).SetParents(Pawn, egg.fertilizer, PregnancyUtility.GetInheritedGeneSet(egg.fertilizer, Pawn)); + Pawn.health.AddHediff(pregnancy); + pregnant = true; + deadeggs.Add(egg); + break; + } + if (pregnancy is Hediff_BasePregnancy rjw_preg) + { + // TODO: advance biotech pregnancy + rjw_preg.p_start_tick -= egg.ticksSinceFertilization / Configurations.CycleAcceleration; + rjw_preg.p_end_tick -= egg.ticksSinceFertilization / Configurations.CycleAcceleration; + } } } else - { + { if (Configurations.Debug) { - float interspeciesFactor = InterspeciesImplantFactor(egg.fertilizer); - float implantChance = Configurations.ImplantationChance * ImplantChance * interspeciesFactor; - Log.Message($"Fertilized egg of {Pawn} failed to implant (chance {implantChance.ToStringPercent()}, " + - (interspeciesFactor < 1.0f ? $"interspecies factor {interspeciesFactor.ToStringPercent()}, " : "") + - $"father {egg.fertilizer})"); + float implantChance = Configurations.ImplantationChance * ImplantChance * InterspeciesImplantFactor(egg.fertilizer); + Log.Message($"Fertilized egg of {Pawn} failed to implant (chance {implantChance.ToStringPercent()}, father {egg.fertilizer})"); } deadeggs.Add(egg); } } - bool clearAllEggs = true; - switch (Configurations.PregnancySource) + if (pregnant && + (Configurations.PregnancySource != Configurations.PregnancyType.MultiplePregnancy || !Configurations.EnableHeteroOvularTwins) && + (Configurations.PregnancySource != Configurations.PregnancyType.Biotech || !Configurations.EnableBiotechTwins || !Configurations.EnableHeteroOvularTwins)) { - case Configurations.PregnancyType.BaseRJW: - clearAllEggs = true; - break; - case Configurations.PregnancyType.MultiplePregnancy: - clearAllEggs = !Configurations.EnableHeteroOvularTwins; - break; - case Configurations.PregnancyType.Biotech: - clearAllEggs = !(Configurations.EnableBiotechTwins && Configurations.EnableHeteroOvularTwins); - break; - } - - if (pregnant && clearAllEggs) eggs.Clear(); + return true; + } else eggs.RemoveAll(egg => deadeggs.Contains(egg)); return pregnant; @@ -1548,7 +1552,12 @@ namespace RJW_Menstruation return amount; } absorber.absorbedfluids += amount; - absorber.CheckDirty(); + if (absorber.absorbedfluids > absorbable && !Pawn.apparel.IsLocked(absorber)) + { + absorber.def = absorber.DirtyDef; + //absorber.fluidColor = GetCumMixtureColor; + absorber.dirty = true; + } return 0; } @@ -1562,6 +1571,9 @@ namespace RJW_Menstruation return amount; } + + + protected void EggDecay() { HashSet deadeggs = new HashSet(); @@ -1587,11 +1599,6 @@ namespace RJW_Menstruation Pawn.health.AddHediff(hediff, parent.Part); } - protected void AdvanceStageTime() - { - curStageTicks += TickInterval * Configurations.CycleAcceleration; - } - protected virtual void FollicularAction() { if (!IsBreedingSeason()) @@ -1606,12 +1613,13 @@ namespace RJW_Menstruation } else { - AdvanceStageTime(); + curStageTicks += TickInterval * Configurations.CycleAcceleration; if (!estrusflag && curStageTicks > currentIntervalTicks - Props.estrusDaysBeforeOvulation * GenDate.TicksPerDay) { estrusflag = true; SetEstrus(); } + StayCurrentStage(); } } @@ -1619,7 +1627,7 @@ namespace RJW_Menstruation { if (curStageTicks < currentIntervalTicks) { - AdvanceStageTime(); + curStageTicks += TickInterval * Configurations.CycleAcceleration; return; } estrusflag = false; @@ -1634,11 +1642,11 @@ namespace RJW_Menstruation } catch (ArgumentException e) { - Log.WarningOnce($"Invalid litterSizeCurve for {Pawn.def}: {e}", 642201874 + Pawn.thingIDNumber); + Log.Warning($"Invalid litterSizeCurve for {Pawn.def}: {e}"); eggnum = 1f; } eggnum *= ovulationFactor; - int toOvulate = Math.Max(1, (int)eggnum + eggstack); + int toOvulate = (int)eggnum + eggstack; int ovulated = 0; for (int i = 0; i < toOvulate; i++) @@ -1649,7 +1657,7 @@ namespace RJW_Menstruation } ovarypower -= ovulated; eggstack = 0; - if (Configurations.Debug && ovulated < toOvulate) + if (Configurations.Debug && ovulated != toOvulate) Log.Message($"{Pawn} ovulated {ovulated}/{toOvulate} eggs ({OvulationChance.ToStringPercent()} chance)"); GoNextStage(Stage.Luteal); @@ -1679,19 +1687,21 @@ namespace RJW_Menstruation } else { - AdvanceStageTime(); + curStageTicks += TickInterval * Configurations.CycleAcceleration; + StayCurrentStage(); } } else { - AdvanceStageTime(); + curStageTicks += TickInterval * Configurations.CycleAcceleration; + StayCurrentStage(); } } protected virtual void BleedingAction() { - if (curStageTicks >= currentIntervalTicks || noBleeding) + if (curStageTicks >= currentIntervalTicks) { 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); @@ -1700,13 +1710,15 @@ namespace RJW_Menstruation GoOvulatoryStage(); else { - GoNextStage(Stage.Follicular, totalFollicularTicks - currentIntervalTicks); // I.e., the remaining follicular time equals the total minus the bleeding time elapsed + currentIntervalTicks = totalFollicularTicks - currentIntervalTicks; // I.e., the remaining follicular time equals the total minus the bleeding time elapsed + GoNextStage(Stage.Follicular, false); } } else { if (curStageTicks < currentIntervalTicks / 4) BleedOut(); - AdvanceStageTime(); + curStageTicks += TickInterval * Configurations.CycleAcceleration; + StayCurrentStage(); } } @@ -1719,10 +1731,16 @@ namespace RJW_Menstruation Implant(); } - if (Pregnancy != null) + if (pregnancy != null && Pawn.health.hediffSet.hediffs.Contains(pregnancy)) + { curStageTicks += TickInterval; + StayCurrentStageConst(Stage.Pregnant); + } else + { + if (pregnancy != null) pregnancy = null; GoNextStage(Stage.Recover); + } } protected virtual void RecoverAction() @@ -1744,18 +1762,22 @@ namespace RJW_Menstruation } else { - AdvanceStageTime(); + curStageTicks += TickInterval * Configurations.CycleAcceleration; + StayCurrentStage(); } } protected virtual void InfertileAction() { if (ShouldBeInfertile()) - return; - else if (IsBreedingSeason()) - GoNextStage(Stage.Follicular); + { + StayCurrentStageConst(Stage.Infertile); + } else - GoNextStage(Stage.Anestrus); + { + bool breedingSeason = IsBreedingSeason(); + GoNextStage(breedingSeason ? Stage.Follicular : Stage.Anestrus, breedingSeason); + } } protected virtual void AnestrusAction() @@ -1764,6 +1786,10 @@ namespace RJW_Menstruation { GoFollicularOrBleeding(); } + else + { + StayCurrentStage(); + } } protected virtual void ThoughtCumInside(Pawn cummer) @@ -1773,7 +1799,7 @@ namespace RJW_Menstruation MemoryThoughtHandler cummerMemories = cummer.needs.mood.thoughts.memories; MemoryThoughtHandler pawnMemories = Pawn.needs.mood.thoughts.memories; - if (cummer.IsProPregnancy(out Precept preceptm) || (cummer.IsTeratophile() != (Pawn.GetStatValue(StatDefOf.PawnBeauty) >= 0))) + if (cummer.IsProPregnancy(out Precept preceptm) || (cummer.HasQuirk(QuirkUtility.Quirks.Teratophile) != (Pawn.GetStatValue(StatDefOf.PawnBeauty) >= 0))) { if (cummer.relations.OpinionOf(Pawn) <= -5) cummerMemories.TryGainMemory(VariousDefOf.HaterCameInsideM, Pawn); @@ -1796,10 +1822,10 @@ namespace RJW_Menstruation } else pawnMemories.TryGainMemory(VariousDefOf.CameInsideFFetish, cummer); } - else if (Pawn.relations.OpinionOf(cummer) <= -5) - pawnMemories.TryGainMemory(VariousDefOf.HaterCameInsideF, cummer); - else if (Pawn.IsInEstrus() && Pawn.relations.OpinionOf(cummer) < RJWHookupSettings.MinimumRelationshipToHookup) - pawnMemories.TryGainMemory(VariousDefOf.HaterCameInsideFEstrus, cummer); + else if (Pawn.relations.OpinionOf(cummer) <= -5) + pawnMemories.TryGainMemory(VariousDefOf.HaterCameInsideF, cummer); + else if (Pawn.IsInEstrus() && Pawn.relations.OpinionOf(cummer) < RJWHookupSettings.MinimumRelationshipToHookup) + pawnMemories.TryGainMemory(VariousDefOf.HaterCameInsideFEstrus, 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); @@ -1825,10 +1851,10 @@ namespace RJW_Menstruation TaleRecorder.RecordTale(VariousDefOf.TaleCameInside, new object[] { cummer, Pawn }); } - public void GoNextStage(Stage nextstage, int? stageTicks = null) + public void GoNextStage(Stage nextstage, bool calculateHours = true) { curStageTicks = 0; - currentIntervalTicks = stageTicks ?? PeriodRandomizer(nextstage); + if (calculateHours) currentIntervalTicks = PeriodRandomizer(nextstage); curStage = nextstage; } @@ -1837,6 +1863,16 @@ namespace RJW_Menstruation GoNextStage(Stage.Ovulatory); } + //stage can be interrupted in other reasons + protected void StayCurrentStage() + { + } + + //stage never changes + protected void StayCurrentStageConst(Stage curstage) + { + } + protected void GoFollicularOrBleeding() { if (Props.bleedingIntervalDays == 0 || noBleeding) @@ -1896,13 +1932,10 @@ namespace RJW_Menstruation else return Rand.Range(0.6f, 1.0f); } - protected virtual float RandomOvulationChance => (float)Props.ovulationIntervalHours / GenDate.HoursPerDay; - protected Stage RandomStage() { Stage stage = Rand.ElementByWeight( Stage.Follicular, Props.follicularIntervalDays - Props.bleedingIntervalDays, - Stage.Ovulatory, RandomOvulationChance, Stage.Luteal, Props.lutealIntervalDays, Stage.Bleeding, Props.bleedingIntervalDays); @@ -1911,9 +1944,6 @@ namespace RJW_Menstruation case Stage.Follicular: curStageTicks = Rand.Range(0, (Props.follicularIntervalDays - Props.bleedingIntervalDays) * GenDate.TicksPerDay); break; - case Stage.Ovulatory: - curStageTicks = Rand.Range(0, Props.ovulationIntervalHours * GenDate.TicksPerHour); - break; case Stage.Luteal: curStageTicks = Rand.Range(0, Props.lutealIntervalDays * GenDate.TicksPerDay); break; @@ -1953,7 +1983,7 @@ namespace RJW_Menstruation public int EggsRestoredPerBiosculptor(float yearsWorth) { - return Math.Max(1, (int)(RaceCyclesPerYear() * yearsWorth * AverageLitterSize())); + return Math.Max(1, (int)((float)RaceCyclesPerYear() * yearsWorth)); } public void RestoreEggs(float yearsWorth) @@ -1972,7 +2002,7 @@ namespace RJW_Menstruation public Egg() { fertilized = false; - lifeSpanTicks = (int)(VariousDefOf.HumanVaginaCompProperties.eggLifespanDays * GenDate.TicksPerDay * Configurations.EggLifespanMultiplier); + lifeSpanTicks = (int)(96 * GenDate.TicksPerHour * Configurations.EggLifespanMultiplier); fertilizer = null; ageTicks = 0; } @@ -2014,6 +2044,28 @@ namespace RJW_Menstruation public class HediffComp_Anus : HediffComp { - public CompProperties_Anus Props => (CompProperties_Anus)props; + protected float? originanussize; + + 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) + { + } } } diff --git a/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_PeriodicOvulator.cs b/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_PeriodicOvulator.cs index e663ec2..ab5b2e2 100644 --- a/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_PeriodicOvulator.cs +++ b/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_PeriodicOvulator.cs @@ -29,14 +29,14 @@ namespace RJW_Menstruation protected override void InitializeExtraValues() { - base.InitializeExtraValues(); + base.InitializeExtraValues(); if (averageCycleIntervalTicks < 0) { averageCycleIntervalTicks = (int)(Props.cycleIntervalDays.RandomInRange * GenDate.TicksPerDay / cycleSpeed); if (ticksToNextCycle < -50000) ticksToNextCycle = Rand.Range(0, averageCycleIntervalTicks); // 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.Ovulatory || curStage == Stage.Luteal || curStage == Stage.Bleeding) + if ((curStage == Stage.Follicular || curStage == Stage.Luteal || curStage == Stage.Bleeding) && (averageCycleIntervalTicks - ticksToNextCycle) / 2 >= GenDate.TicksPerDay * (Props.follicularIntervalDays + Props.lutealIntervalDays) / cycleSpeed) GoNextStage(Stage.Anestrus); } @@ -73,7 +73,7 @@ namespace RJW_Menstruation protected override int TicksToNextStage() { - if (curStage == Stage.Anestrus && ticksToNextCycle > 0) return ticksToNextCycle / Configurations.CycleAcceleration; + if (curStage == Stage.Anestrus && ticksToNextCycle > 0) return ticksToNextCycle / Configurations.CycleAcceleration; else return base.TicksToNextStage(); } @@ -104,11 +104,13 @@ namespace RJW_Menstruation protected override void AnestrusAction() { - if (ticksToNextCycle <= 0 && IsBreedingSeason()) + if (ticksToNextCycle <= 0) { ticksToNextCycle = (int)(averageCycleIntervalTicks * (1 + Rand.Range(-cycleVariability, cycleVariability))); - GoNextStage(Stage.Follicular); + if (IsBreedingSeason()) GoNextStage(Stage.Follicular); + return; } + StayCurrentStage(); } public override void CopyCycleProperties(HediffComp_Menstruation original) diff --git a/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_Pheromones.cs b/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_Pheromones.cs index 42b2aac..8462b19 100644 --- a/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_Pheromones.cs +++ b/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_Pheromones.cs @@ -40,7 +40,7 @@ namespace RJW_Menstruation } catch (Exception ex) { - Log.Error($"Error when trying to emit pheromones from pawn {Pawn}: {ex}"); + Log.Error($"Error when trying to emit pheromones: {ex}"); } } diff --git a/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_PregeneratedBabies.cs b/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_PregeneratedBabies.cs index 79c13a9..ce0efe9 100644 --- a/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_PregeneratedBabies.cs +++ b/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/HediffComp_PregeneratedBabies.cs @@ -179,7 +179,7 @@ namespace RJW_Menstruation public static IEnumerable Transpiler(IEnumerable instructions) { if (birtherThing < 0) throw new InvalidOperationException("Could not locate index of birtherThing"); - if (GeneratePawn?.ReturnType != typeof(Pawn)) throw new InvalidOperationException("GeneratePawn not found"); + if (GeneratePawn == null || GeneratePawn.ReturnType != typeof(Pawn)) throw new InvalidOperationException("GeneratePawn not found"); foreach (CodeInstruction instruction in instructions) { if (instruction.Calls(GeneratePawn)) @@ -233,7 +233,7 @@ namespace RJW_Menstruation new Type[] {typeof(OutcomeChance), typeof(float), typeof(Precept_Ritual), typeof(List), typeof(Pawn), typeof(Thing), typeof(Pawn), typeof(Pawn), typeof(LordJob_Ritual), typeof(RitualRoleAssignments)}); public static IEnumerable Transpiler(IEnumerable instructions) { - if (ApplyBirthOutcome?.ReturnType != typeof(Thing)) throw new InvalidOperationException("ApplyBirthOutcome not found"); + if (ApplyBirthOutcome == null || ApplyBirthOutcome.ReturnType != typeof(Thing)) throw new InvalidOperationException("ApplyBirthOutcome not found"); foreach (CodeInstruction instruction in instructions) { if (instruction.Calls(ApplyBirthOutcome)) @@ -283,8 +283,8 @@ namespace RJW_Menstruation new Type[] { typeof(OutcomeChance), typeof(float), typeof(Precept_Ritual), typeof(List), typeof(Pawn), typeof(Thing), typeof(Pawn), typeof(Pawn), typeof(LordJob_Ritual), typeof(RitualRoleAssignments) }); public static IEnumerable Transpiler(IEnumerable instructions) { - if (ApplyBirthOutcome?.ReturnType != typeof(Thing)) throw new InvalidOperationException("ApplyBirthOutcome not found"); - foreach (CodeInstruction instruction in instructions) + if (ApplyBirthOutcome == null || ApplyBirthOutcome.ReturnType != typeof(Thing)) throw new InvalidOperationException("ApplyBirthOutcome not found"); + foreach (var instruction in instructions) { if (instruction.Calls(ApplyBirthOutcome)) yield return CodeInstruction.Call(typeof(Ritual_ChildBirth_Apply_Patch), nameof(Ritual_ChildBirth_Apply_Patch.ApplyBirthLoop)); diff --git a/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/MenstruationUtility.cs b/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/MenstruationUtility.cs index 2092d1f..ffd37aa 100644 --- a/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/MenstruationUtility.cs +++ b/1.4/source/RJW_Menstruation/RJW_Menstruation/HediffComps/MenstruationUtility.cs @@ -22,7 +22,7 @@ namespace RJW_Menstruation public static IEnumerable GetMenstruationComps(this Pawn pawn) { - List hedifflist = pawn.health.hediffSet.hediffs.FindAll(h => VariousDefOf.AllVaginas.Contains(h.def)); + List hedifflist = Genital_Helper.get_PartsHediffList(pawn, Genital_Helper.get_genitalsBPR(pawn))?.FindAll(h => VariousDefOf.AllVaginas.Contains(h.def)); if (hedifflist == null) yield break; foreach (Hediff hediff in hedifflist) { @@ -63,7 +63,17 @@ namespace RJW_Menstruation public static HediffComp_Anus GetAnusComp(this Pawn pawn) { - return pawn.health.hediffSet.hediffs.FirstOrDefault((Hediff h) => VariousDefOf.AllAnuses.Contains(h.def))?.TryGetComp(); + List hedifflist = Genital_Helper.get_PartsHediffList(pawn, Genital_Helper.get_genitalsBPR(pawn))?.FindAll((Hediff h) => h.def.defName.ToLower().Contains("anus")); + HediffComp_Anus result; + if (!hedifflist.NullOrEmpty()) + { + foreach (Hediff h in hedifflist) + { + result = h.TryGetComp(); + if (result != null) return result; + } + } + return null; } [Obsolete("This method is obsolete and can cause ambiguity. Use GetMenstruationCompFromVagina or GetMenstruationCompFromPregnancy instead.", true)] @@ -141,7 +151,7 @@ namespace RJW_Menstruation else if (gestationProgress < 0.8f) icon = fetustex + "04"; else icon = fetustex + "05"; - return TryGetTwinsIcon(icon, babycount) ?? ContentFinder.Get(icon, true); + return TryGetTwinsIcon(icon, babycount) ?? ContentFinder.Get((icon), true); } public static Texture2D TryGetTwinsIcon(string path, int babycount) @@ -160,7 +170,7 @@ namespace RJW_Menstruation List insectEggs = new List(); comp.Pawn.health.hediffSet.GetHediffs(ref insectEggs); - if (insectEggs?.Sum(hediff => hediff.eggssize) > 1.0f) return null; // same logic as "Stuffed" in GetInsectEggedIcon + if (!insectEggs.NullOrEmpty() && insectEggs.Sum(hediff => hediff.eggssize) > 1.0f) return null; // same logic as "Stuffed" in GetInsectEggedIcon string icon = comp.WombTex; float cumpercent = comp.TotalCumPercent; @@ -183,7 +193,7 @@ namespace RJW_Menstruation else if (cumpercent < 0.89f) icon += "_Cum_15"; else if (cumpercent < 0.95f) icon += "_Cum_16"; else icon += "_Cum_17"; - Texture2D cumtex = ContentFinder.Get(icon, true); + Texture2D cumtex = ContentFinder.Get((icon), true); return cumtex; } public static Texture2D GetInsectEggedIcon(this HediffComp_Menstruation comp) @@ -306,13 +316,15 @@ namespace RJW_Menstruation } - public static Texture2D GetGenitalIcon(this Pawn pawn, HediffComp_Menstruation comp) + public static Texture2D GetGenitalIcon(this Pawn pawn, HediffComp_Menstruation comp, bool drawOrigin = false) { Hediff hediff = comp?.parent; if (hediff == null) return ContentFinder.Get("Genitals/Vagina00", true); //HediffComp_Menstruation comp = hediff.GetMenstruationComp(); string icon; - float severity = hediff.Severity; + float severity; + if (drawOrigin) severity = comp.OriginVagSize; + else severity = hediff.Severity; if (comp != null) icon = comp.VagTex; else icon = "Genitals/Vagina"; @@ -329,18 +341,30 @@ namespace RJW_Menstruation else if (severity < 1.01f) icon += "10"; //cavernous else icon += "11"; //abyssal - return ContentFinder.Get(icon, true); + return ContentFinder.Get((icon), true); } - public static Texture2D GetAnalIcon(this Pawn pawn) + public static Texture2D GetAnalIcon(this Pawn pawn, bool drawOrigin = false) { - Hediff hediff = pawn.health.hediffSet.hediffs.FirstOrDefault(h => VariousDefOf.AllAnuses.Contains(h.def)) ?? - pawn.health.hediffSet.hediffs.FirstOrDefault(h => h.def.defName.ToLower().Contains("anus")); - if (hediff == null) return ContentFinder.Get("Genitals/Anal00", true); - - string icon = ((CompProperties_Anus)hediff.GetAnusComp()?.props)?.analTex ?? "Genitals/Anal"; - float severity = hediff.Severity; + Hediff hediff = Genital_Helper.get_PartsHediffList(pawn, Genital_Helper.get_anusBPR(pawn)).FirstOrDefault(h => VariousDefOf.AllAnuses.Contains(h.def)) ?? + Genital_Helper.get_PartsHediffList(pawn, Genital_Helper.get_anusBPR(pawn)).FirstOrDefault(h => h.def.defName.ToLower().Contains("anus")); + if (hediff == null) return ContentFinder.Get(("Genitals/Anal00"), true); + 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 else if (severity < 0.40f) icon += "01"; //tight else if (severity < 0.60f) icon += "02"; //average @@ -348,7 +372,7 @@ namespace RJW_Menstruation else if (severity < 1.01f) icon += "04"; //cavernous else icon += "05"; //abyssal - return ContentFinder.Get(icon, true); + return ContentFinder.Get((icon), true); } public static float GestationHours(this Hediff hediff) @@ -379,18 +403,6 @@ namespace RJW_Menstruation else return variability; } - public static bool ShouldCycle(this Pawn pawn) - { - if (!Configurations.EnableAnimalCycle && pawn.IsAnimal()) return false; - if (pawn.GetComp() != 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) { if (pawn.Dead) return false; @@ -438,8 +450,8 @@ namespace RJW_Menstruation } if (precept != null) return true; - else return pawn.IsBreeder() || - pawn.HasImpregnationFetish(); + else return pawn.HasQuirk(QuirkUtility.Quirks.Breeder) || + pawn.HasQuirk(QuirkUtility.Quirks.ImpregnationFetish); } public static float DamagePants(this Pawn pawn, float fluidAmount) diff --git a/1.4/source/RJW_Menstruation/RJW_Menstruation/Hediff_MultiplePregnancy.cs b/1.4/source/RJW_Menstruation/RJW_Menstruation/Hediff_MultiplePregnancy.cs index 61ce541..2cc6646 100644 --- a/1.4/source/RJW_Menstruation/RJW_Menstruation/Hediff_MultiplePregnancy.cs +++ b/1.4/source/RJW_Menstruation/RJW_Menstruation/Hediff_MultiplePregnancy.cs @@ -23,7 +23,7 @@ namespace RJW_Menstruation { if (is_discovered || !xxx.is_human(pawn) || - pawn.IsBreeder() || (pawn.Ideo?.HasPrecept(VariousDefOf.Pregnancy_Required) ?? false) || + pawn.HasQuirk(QuirkUtility.Quirks.Breeder) || (pawn.Ideo?.HasPrecept(VariousDefOf.Pregnancy_Required) ?? false) || (pawn.relations?.DirectRelations?.Find(x => x.def.Equals(PawnRelationDefOf.Spouse) || x.def.Equals(PawnRelationDefOf.Fiance))) != null) return; diff --git a/1.4/source/RJW_Menstruation/RJW_Menstruation/MenstruationModExtension.cs b/1.4/source/RJW_Menstruation/RJW_Menstruation/MenstruationModExtension.cs deleted file mode 100644 index c3a8fbe..0000000 --- a/1.4/source/RJW_Menstruation/RJW_Menstruation/MenstruationModExtension.cs +++ /dev/null @@ -1,15 +0,0 @@ -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; - } -} diff --git a/1.4/source/RJW_Menstruation/RJW_Menstruation/Patch/Biotech_Patch.cs b/1.4/source/RJW_Menstruation/RJW_Menstruation/Patch/Biotech_Patch.cs index eb8777a..4bd3cef 100644 --- a/1.4/source/RJW_Menstruation/RJW_Menstruation/Patch/Biotech_Patch.cs +++ b/1.4/source/RJW_Menstruation/RJW_Menstruation/Patch/Biotech_Patch.cs @@ -155,7 +155,7 @@ namespace RJW_Menstruation public static IEnumerable Transpiler(IEnumerable instructions) { - if (GetPregnancyHediff?.ReturnType != typeof(Hediff)) throw new InvalidOperationException("GetPregnancyHediff not found"); + if (GetPregnancyHediff == null || GetPregnancyHediff.ReturnType != typeof(Hediff)) throw new InvalidOperationException("GetPregnancyHediff not found"); if (Get_Attitude == null || Nullable.GetUnderlyingType(Get_Attitude.ReturnType) != typeof(PregnancyAttitude)) throw new InvalidOperationException("get_Attitude not found"); foreach (CodeInstruction instruction in instructions) { @@ -192,6 +192,23 @@ 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")] public class Notify_GenesChanged_Patch { @@ -202,17 +219,6 @@ namespace RJW_Menstruation } } - [HarmonyPatch(typeof(StatPart_FertilityByGenderAge), "AgeFactor")] - public class AgeFactor_Patch - { - public static void Postfix(ref float __result, Pawn pawn) - { - if (__result <= 0.0f) return; - if (pawn.GetMenstruationComps().Any(comp => comp.CalculatingOvulationChance)) - __result = 1.0f; - } - } - //[HarmonyPatch(typeof(ChildcareUtility), nameof(ChildcareUtility.SuckleFromLactatingPawn))] //public class GreedyConsume_Patch //{ diff --git a/1.4/source/RJW_Menstruation/RJW_Menstruation/Patch/Gizmo_Patch.cs b/1.4/source/RJW_Menstruation/RJW_Menstruation/Patch/Gizmo_Patch.cs index b0ab1a3..86ef4d9 100644 --- a/1.4/source/RJW_Menstruation/RJW_Menstruation/Patch/Gizmo_Patch.cs +++ b/1.4/source/RJW_Menstruation/RJW_Menstruation/Patch/Gizmo_Patch.cs @@ -43,8 +43,6 @@ namespace RJW_Menstruation private static Gizmo CreateGizmo_WombStatus(Pawn pawn, HediffComp_Menstruation comp) { - if (!comp.loaded) - comp.Initialize(); Texture2D icon, icon_overlay; StringBuilder description = new StringBuilder(); if (Configurations.Debug) diff --git a/1.4/source/RJW_Menstruation/RJW_Menstruation/Patch/Pawn_Patch.cs b/1.4/source/RJW_Menstruation/RJW_Menstruation/Patch/Pawn_Patch.cs index 98e5997..616f591 100644 --- a/1.4/source/RJW_Menstruation/RJW_Menstruation/Patch/Pawn_Patch.cs +++ b/1.4/source/RJW_Menstruation/RJW_Menstruation/Patch/Pawn_Patch.cs @@ -1,6 +1,5 @@ using HarmonyLib; using RimWorld; -using rjw; using System; using System.Collections.Generic; using System.Linq; @@ -37,6 +36,10 @@ namespace RJW_Menstruation if (t.Pawn == pawn && pawn.HasMenstruationComp()) opts.AddDistinct(MakeSelfMenu(pawn, t)); break; } + + + + } public static FloatMenuOption MakeSelfMenu(Pawn pawn, LocalTargetInfo target) @@ -50,28 +53,60 @@ namespace RJW_Menstruation } } - [HarmonyPatch(typeof(HealthCardUtility), "DrawOverviewTab")] - public class DrawOverviewTab_Patch + //[HarmonyPatch(typeof(HealthCardUtility), "DrawHediffListing")] + //public class DrawHediffListing_Patch + //{ + // public const float buttonWidth = 80f; + // public const float buttonHeight = 20f; + // + // public static void Postfix(Rect rect, Pawn pawn, bool showBloodLoss) + // { + // if (Configurations.EnableButtonInHT && pawn.HasMenstruationComp()) + // { + // Rect buttonrect = new Rect(rect.xMax - buttonWidth, rect.yMax - buttonHeight, buttonWidth, buttonHeight); + // if (Widgets.ButtonText(buttonrect, "Status")) + // { + // Dialog_WombStatus.ToggleWindow(pawn,pawn.GetMenstruationComp()); + // } + // } + // + // + // } + //} + + [HarmonyPatch(typeof(HealthCardUtility), "DrawHediffRow")] + public class DrawHediffRow_Patch { public const float buttonWidth = 50f; public const float buttonHeight = 20f; - public static void Prefix(Rect leftRect, Pawn pawn, float curY) + private static HediffComp_Menstruation GetFirstMenstruation(IEnumerable diffs) { - if (Configurations.EnableButtonInHT && pawn.ShowStatus() && pawn.ShouldCycle()) + foreach (Hediff diff in diffs) { - HediffComp_Menstruation comp = pawn.GetFirstMenstruationComp(); + HediffComp_Menstruation comp = diff.GetMenstruationCompFromVagina(); + if (comp != null) return comp; + } + return null; + } + + public static void Prefix(Rect rect, Pawn pawn, IEnumerable diffs, ref float curY) + { + if (Configurations.EnableButtonInHT && pawn.ShowStatus()) + { + HediffComp_Menstruation comp = GetFirstMenstruation(diffs); if (comp != null) { - Text.Font = GameFont.Tiny; - Rect buttonRect = new Rect(leftRect.xMax - buttonWidth - 8f, curY + 4f, buttonWidth, buttonHeight); - if (Widgets.ButtonText(buttonRect, Translations.Button_HealthTab)) + Rect buttonrect = new Rect((rect.xMax) / 2 - 5f, curY + 2f, buttonWidth, buttonHeight); + if (Widgets.ButtonText(buttonrect, Translations.Button_HealthTab)) { Dialog_WombStatus.ToggleWindow(pawn, comp); } } } + } + } [HarmonyPatch(typeof(CompBiosculpterPod), nameof(CompBiosculpterPod.CannotUseNowPawnCycleReason), new Type[] { typeof(Pawn), typeof(Pawn), typeof(CompBiosculpterPod_Cycle), typeof(bool) })] @@ -85,28 +120,4 @@ namespace RJW_Menstruation __result = Translations.CannotNoWomb; } } - - // Doesn't cover everything, but at least it'll get the auto equip - [HarmonyPatch(typeof(Apparel), nameof(Apparel.PawnCanWear))] - public class PawnCanWear_Patch - { - public static void Postfix(ref bool __result, Apparel __instance, Pawn pawn) - { - if (__result && __instance is Absorber) - { - __result = pawn.ShouldCycle() && pawn.GetMenstruationComps().Any(); - } - } - } - - // Might cause issues when it comes to caravans - //[HarmonyPatch(typeof(JobGiver_OptimizeApparel), nameof(JobGiver_OptimizeApparel.ApparelScoreRaw))] - //public class ApparelScoreRaw_Patch - //{ - // public static void Postfix(ref float __result, Pawn pawn, Apparel ap) - // { - // if (__result > 0f && ap is Absorber && !pawn.GetMenstruationComps().Any(comp => comp.TotalCum > 0)) - // __result = -10f; - // } - //} } diff --git a/1.4/source/RJW_Menstruation/RJW_Menstruation/Patch/RJW_Patch.cs b/1.4/source/RJW_Menstruation/RJW_Menstruation/Patch/RJW_Patch.cs index eb776e3..fdb90d9 100644 --- a/1.4/source/RJW_Menstruation/RJW_Menstruation/Patch/RJW_Patch.cs +++ b/1.4/source/RJW_Menstruation/RJW_Menstruation/Patch/RJW_Patch.cs @@ -24,14 +24,14 @@ namespace RJW_Menstruation if (sextype != xxx.rjwSextype.Vaginal && sextype != xxx.rjwSextype.DoublePenetration) return true; - if (!partner.ShouldCycle()) return true; + if (partner.IsAnimal() && !Configurations.EnableAnimalCycle) return true; if (!InteractionCanCausePregnancy(props)) return false; List pawnparts = Genital_Helper.get_PartsHediffList(pawn, Genital_Helper.get_genitalsBPR(pawn)); HediffComp_Menstruation comp; - if (pawn.HasImpregnationFetish() || partner.HasImpregnationFetish() || partner.IsInEstrus()) + if (pawn.HasQuirk(QuirkUtility.Quirks.ImpregnationFetish) || partner.HasQuirk(QuirkUtility.Quirks.ImpregnationFetish) || partner.IsInEstrus()) comp = partner.GetFertileMenstruationComp(); else comp = partner.GetRandomMenstruationComp(); if (comp == null) return true; @@ -100,9 +100,9 @@ namespace RJW_Menstruation { public static bool Prefix(Pawn pawn, Pawn partner) // partner has vagina { - if (!partner.ShouldCycle()) return true; + if (partner.IsAnimal() && !Configurations.EnableAnimalCycle) return true; HediffComp_Menstruation comp; - if (pawn.HasImpregnationFetish() || partner.HasImpregnationFetish() || partner.IsInEstrus()) + if (pawn.HasQuirk(QuirkUtility.Quirks.ImpregnationFetish) || partner.HasQuirk(QuirkUtility.Quirks.ImpregnationFetish) || partner.IsInEstrus()) comp = partner.GetFertileMenstruationComp(); else comp = partner.GetRandomMenstruationComp(); if (comp == null) @@ -125,14 +125,14 @@ namespace RJW_Menstruation { private static bool PregnancyBlocksImpregnation(this Pawn pawn, bool _) { - if (!pawn.ShouldCycle()) return pawn.IsPregnant(); + if (!Configurations.EnableAnimalCycle && pawn.IsAnimal()) return pawn.IsPregnant(); else if (pawn.GetMenstruationComps().Any()) return false; else return pawn.IsPregnant(); } private static readonly MethodInfo IsPregnant = AccessTools.Method(typeof(PawnExtensions), nameof(PawnExtensions.IsPregnant), new System.Type[] { typeof(Pawn), typeof(bool) }); public static IEnumerable Transpiler(IEnumerable instructions) { - if (IsPregnant?.ReturnType != typeof(bool)) throw new System.InvalidOperationException("IsPregnant not found"); + if (IsPregnant == null || IsPregnant.ReturnType != typeof(bool)) throw new System.InvalidOperationException("IsPregnant not found"); foreach (CodeInstruction instruction in instructions) { if (instruction.Calls(IsPregnant)) @@ -179,7 +179,7 @@ namespace RJW_Menstruation { // Awkward, but it'll have to do Pawn pawn = props.pawn; - if (__result == 0 || !pawn.HasImpregnationFetish() || !props.hasPartner()) return; + if (__result == 0 || !pawn.HasQuirk(QuirkUtility.Quirks.ImpregnationFetish) || !props.hasPartner()) return; // Check if the existing code would have added the count Pawn partner = props.partner; @@ -238,7 +238,7 @@ namespace RJW_Menstruation private static readonly FieldInfo MinimumFuckabilityToHookup = AccessTools.Field(typeof(RJWHookupSettings), nameof(RJWHookupSettings.MinimumFuckabilityToHookup)); public static IEnumerable Transpiler(IEnumerable instructions) { - if (MinimumFuckabilityToHookup?.FieldType != typeof(float)) throw new System.InvalidOperationException("MinimumFuckabilityToHookup not found"); + if (MinimumFuckabilityToHookup == null || MinimumFuckabilityToHookup.FieldType != typeof(float)) throw new System.InvalidOperationException("MinimumFuckabilityToHookup not found"); bool first_fuckability = true; foreach (CodeInstruction instruction in instructions) { @@ -274,8 +274,8 @@ namespace RJW_Menstruation private static readonly FieldInfo MinimumRelationshipToHookup = AccessTools.Field(typeof(RJWHookupSettings), nameof(RJWHookupSettings.MinimumRelationshipToHookup)); public static IEnumerable Transpiler(IEnumerable instructions) { - if (MinimumAttractivenessToHookup?.FieldType != typeof(float)) throw new System.InvalidOperationException("MinimumAttractivenessToHookup not found"); - if (MinimumRelationshipToHookup?.FieldType != typeof(float)) throw new System.InvalidOperationException("MinimumRelationshipToHookup not found"); + if (MinimumAttractivenessToHookup == null || MinimumAttractivenessToHookup.FieldType != typeof(float)) throw new System.InvalidOperationException("MinimumAttractivenessToHookup not found"); + if (MinimumRelationshipToHookup == null || MinimumRelationshipToHookup.FieldType != typeof(float)) throw new System.InvalidOperationException("MinimumRelationshipToHookup not found"); LocalBuilder pawn_index = null; // Like in the last one, we switch the arguments around for the second load bool first_attractiveness = true; @@ -342,12 +342,10 @@ namespace RJW_Menstruation { if (!pawn.IsHashIntervalTick(__instance.ticks_between_thrusts)) return; xxx.rjwSextype sextype = __instance.Sexprops.sexType; - if (!(target is Pawn partner)) return; + if (!(target is Pawn partner) || pawn == partner) return; if (sextype != xxx.rjwSextype.Vaginal && sextype != xxx.rjwSextype.DoublePenetration) return; if (__instance.Sexprops.usedCondom) return; - if (AndroidsCompatibility.IsAndroid(pawn)) return; if (!Impregnate_Patch.InteractionCanCausePregnancy(__instance.Sexprops)) return; - if (!partner.ShouldCycle()) return; // Archotech penises have more control. Or something. CompHediffBodyPart penisComp = pawn.GetGenitalsList()?.Find(genital => (genital as Hediff_PartBaseNatural)?.def.defName.ToLower().Contains("penis") ?? false)?.TryGetComp(); @@ -374,14 +372,26 @@ namespace RJW_Menstruation } } - [HarmonyPatch(typeof(PawnCapacityWorker_Fertility), "CalculateAgeImpact")] - public static class PawnCapacityWorker_Fertility_Age_Patch + [HarmonyPatch(typeof(PawnCapacityWorker_Fertility), nameof(PawnCapacityWorker_Fertility.CalculateCapacityLevel))] + public static class PawnCapacityWorker_Fertility_Patch { - public static void Postfix(ref float __result, Pawn pawn) + private static float GetFertilityStatOrOne(Thing thing, StatDef stat, bool applyPostProcess, int cacheStaleAfterTicks) { - if (__result <= 0.0f) return; + Pawn pawn = (Pawn)thing; if (pawn.GetMenstruationComps().Any(comp => comp.CalculatingOvulationChance)) - __result = 1.0f; + return 1.0f; + else return thing.GetStatValue(stat, applyPostProcess, cacheStaleAfterTicks); + } + private static readonly MethodInfo GetStatValue = AccessTools.Method(typeof(StatExtension), "GetStatValue", new System.Type[] { typeof(Thing), typeof(StatDef), typeof(bool), typeof(int) }); + public static IEnumerable Transpiler(IEnumerable instructions) + { + if (GetStatValue == null || GetStatValue.ReturnType != typeof(float)) throw new System.InvalidOperationException("GetStatValue not found"); + foreach (CodeInstruction instruction in instructions) + { + if (instruction.Calls(GetStatValue)) + yield return CodeInstruction.Call(typeof(PawnCapacityWorker_Fertility_Patch), nameof(GetFertilityStatOrOne)); + else yield return instruction; + } } } } diff --git a/1.4/source/RJW_Menstruation/RJW_Menstruation/PregnancyCommon.cs b/1.4/source/RJW_Menstruation/RJW_Menstruation/PregnancyCommon.cs index 9b9bdad..7cbf28c 100644 --- a/1.4/source/RJW_Menstruation/RJW_Menstruation/PregnancyCommon.cs +++ b/1.4/source/RJW_Menstruation/RJW_Menstruation/PregnancyCommon.cs @@ -20,8 +20,9 @@ namespace RJW_Menstruation StringBuilder res = new StringBuilder(); + IEnumerable babiesdistinct = babies.Distinct(new RaceComparer()); int iteration = 0; - foreach (Pawn baby in babies.Distinct(new RaceComparer())) + foreach (Pawn baby in babiesdistinct) { int num = babies.Where(x => x.def.Equals(baby.def)).Count(); if (iteration > 0) res.Append(", "); @@ -42,8 +43,9 @@ namespace RJW_Menstruation if (!is_parent_known && Configurations.InfoDetail != Configurations.DetailLevel.All) return res.Append(Translations.Dialog_FatherUnknown).ToString(); + IEnumerable babiesdistinct = babies.Distinct(new FatherComparer(mother)); int iteration = 0; - foreach (Pawn baby in babies.Distinct(new FatherComparer(mother))) + foreach (Pawn baby in babiesdistinct) { if (iteration > 0) res.Append(", "); res.Append(Utility.GetFather(baby, mother)?.LabelShort ?? Translations.Dialog_FatherUnknown); diff --git a/1.4/source/RJW_Menstruation/RJW_Menstruation/QuirkUtility.cs b/1.4/source/RJW_Menstruation/RJW_Menstruation/QuirkUtility.cs index 409d3d7..c8c2352 100644 --- a/1.4/source/RJW_Menstruation/RJW_Menstruation/QuirkUtility.cs +++ b/1.4/source/RJW_Menstruation/RJW_Menstruation/QuirkUtility.cs @@ -6,14 +6,14 @@ namespace RJW_Menstruation public static class QuirkUtility { // All quirks used in Menstruation - private enum Quirks + public enum Quirks { Breeder, ImpregnationFetish, Messy, Teratophile, } - private static bool HasQuirk(Pawn pawn, Quirks quirk) + public static bool HasQuirk(this Pawn pawn, Quirks quirk) { switch (quirk) { @@ -29,9 +29,5 @@ namespace RJW_Menstruation return false; } } - public static bool IsBreeder(this Pawn pawn) => HasQuirk(pawn, Quirks.Breeder); - public static bool HasImpregnationFetish(this Pawn pawn) => HasQuirk(pawn, Quirks.ImpregnationFetish); - public static bool IsMessy(this Pawn pawn) => HasQuirk(pawn, Quirks.Messy); - public static bool IsTeratophile(this Pawn pawn) => HasQuirk(pawn, Quirks.Teratophile); } } diff --git a/1.4/source/RJW_Menstruation/RJW_Menstruation/RJW_Menstruation.csproj b/1.4/source/RJW_Menstruation/RJW_Menstruation/RJW_Menstruation.csproj index 33cf9ec..0c4aa45 100644 --- a/1.4/source/RJW_Menstruation/RJW_Menstruation/RJW_Menstruation.csproj +++ b/1.4/source/RJW_Menstruation/RJW_Menstruation/RJW_Menstruation.csproj @@ -74,7 +74,6 @@ - diff --git a/1.4/source/RJW_Menstruation/RJW_Menstruation/Things.cs b/1.4/source/RJW_Menstruation/RJW_Menstruation/Things.cs index c95454a..5e4dd91 100644 --- a/1.4/source/RJW_Menstruation/RJW_Menstruation/Things.cs +++ b/1.4/source/RJW_Menstruation/RJW_Menstruation/Things.cs @@ -1,7 +1,6 @@ using RimWorld; using rjw; using System.Collections.Generic; -using System.Text; using System.Xml; using UnityEngine; using Verse; @@ -187,7 +186,6 @@ namespace RJW_Menstruation } public class AbsorberModExtension : DefModExtension { - public float passiveAbsorptionPerHour = 0.1f; public bool leakAfterDirty = false; public bool effectsAfterDirty = false; public ThingDef dirtyDef = null; @@ -200,7 +198,7 @@ namespace RJW_Menstruation public float absorbedfluids = 0; public bool dirty = false; public int wearTicks = 0; - public virtual float PassiveAbsorptionPerHour => def.GetModExtension().passiveAbsorptionPerHour; + protected virtual float PassiveAbsorptionPerHour => 0.1f; public virtual bool LeakAfterDirty => def.GetModExtension().leakAfterDirty; public virtual bool EffectAfterDirty => def.GetModExtension().effectsAfterDirty; public virtual ThingDef DirtyDef => def.GetModExtension().dirtyDef; @@ -213,25 +211,9 @@ namespace RJW_Menstruation public virtual void WearEffect(int tickInterval) { absorbedfluids += PassiveAbsorptionPerHour * tickInterval / GenDate.TicksPerHour; - CheckDirty(); if (dirty) wearTicks += tickInterval; } - public void CheckDirty() - { - if (absorbedfluids > this.GetStatValue(VariousDefOf.MaxAbsorbable) && !(Wearer?.apparel?.IsLocked(this) ?? false) && 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 void ExposeData() @@ -249,30 +231,15 @@ namespace RJW_Menstruation Scribe_Values.Look(ref fluidColor, "fluidColor", Color.white); } - public override string DescriptionDetailed - { - get - { - StringBuilder text = new StringBuilder(base.DescriptionDetailed); - text.AppendLine(); - text.Append(Translations.Description_Absorbed); - text.Append(": "); - text.Append(absorbedfluids.ToStringDecimalIfSmall()); - text.Append("/"); - text.Append(this.GetStatValue(VariousDefOf.MaxAbsorbable).ToStringDecimalIfSmall()); - text.Append("ml"); - - return text.ToString(); - } - } } public class Absorber_Tampon : Absorber { + protected override float PassiveAbsorptionPerHour => 0.5f; public override void DirtyEffect(int tickInterval) { - if (wearTicks > MinHrstoDirtyEffect * GenDate.TicksPerHour && Rand.MTBEventOccurs(100.0f, GenDate.TicksPerHour, tickInterval) && !(Wearer.apparel?.IsLocked(this) ?? false)) + if (wearTicks > MinHrstoDirtyEffect * GenDate.TicksPerHour && Rand.MTBEventOccurs(100.0f, GenDate.TicksPerHour, tickInterval) && !Wearer.apparel.IsLocked(this)) { Wearer.health.AddHediff(HediffDefOf.WoundInfection, Genital_Helper.get_genitalsBPR(Wearer)); } diff --git a/1.4/source/RJW_Menstruation/RJW_Menstruation/Translations.cs b/1.4/source/RJW_Menstruation/RJW_Menstruation/Translations.cs index 6c511cc..0da2b3c 100644 --- a/1.4/source/RJW_Menstruation/RJW_Menstruation/Translations.cs +++ b/1.4/source/RJW_Menstruation/RJW_Menstruation/Translations.cs @@ -44,7 +44,6 @@ namespace RJW_Menstruation public static readonly string Dialog_WombInfo08 = "Dialog_WombInfo08".Translate(); public static readonly string Dialog_WombInfo09 = "Dialog_WombInfo09".Translate(); public static readonly string Dialog_WombInfo10 = "Dialog_WombInfo10".Translate(); - public static readonly string Description_Absorbed = "Description_Absorbed".Translate(); public static readonly string Option1_Label_1 = "Option1_Label_1".Translate(); diff --git a/1.4/source/RJW_Menstruation/RJW_Menstruation/UI/Dialog_WombStatus.cs b/1.4/source/RJW_Menstruation/RJW_Menstruation/UI/Dialog_WombStatus.cs index 0affda5..0448aac 100644 --- a/1.4/source/RJW_Menstruation/RJW_Menstruation/UI/Dialog_WombStatus.cs +++ b/1.4/source/RJW_Menstruation/RJW_Menstruation/UI/Dialog_WombStatus.cs @@ -388,9 +388,10 @@ namespace RJW_Menstruation Rect genitalIconRect = new Rect(rect.x, rect.y + fontheight, genitalRectWidth, genitalRectHeight); Rect genitalVaginaLabelRect = new Rect(rect.x, rect.y + 10f, 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); - anal = pawn.GetAnalIcon(); + vagina = pawn.GetGenitalIcon(comp, showOrigin); + anal = pawn.GetAnalIcon(showOrigin); GUI.color = new Color(1.00f, 0.47f, 0.47f, 1); GUI.Box(rect, "", boxstyle); GUI.color = Utility.SafeSkinColor(pawn); diff --git a/1.4/source/RJW_Menstruation/RJW_Menstruation/Utility.cs b/1.4/source/RJW_Menstruation/RJW_Menstruation/Utility.cs index ae2cb4a..db91411 100644 --- a/1.4/source/RJW_Menstruation/RJW_Menstruation/Utility.cs +++ b/1.4/source/RJW_Menstruation/RJW_Menstruation/Utility.cs @@ -86,7 +86,7 @@ namespace RJW_Menstruation { res = 0.0f; } - if (pawn.IsMessy()) res *= Rand.Range(4.0f, 8.0f); + if (pawn.HasQuirk(QuirkUtility.Quirks.Messy)) res *= Rand.Range(4.0f, 8.0f); return res; } @@ -95,7 +95,16 @@ namespace RJW_Menstruation public static HediffComp_Breast GetBreastComp(this Pawn pawn) { - return pawn.health.hediffSet.hediffs.FirstOrDefault((Hediff h) => VariousDefOf.AllBreasts.Contains(h.def))?.TryGetComp(); + List hedifflist = Genital_Helper.get_PartsHediffList(pawn, Genital_Helper.get_breastsBPR(pawn))?.FindAll(h => VariousDefOf.AllBreasts.Contains(h.def)); + if (hedifflist.NullOrEmpty()) hedifflist = Genital_Helper.get_PartsHediffList(pawn, Genital_Helper.get_uddersBPR(pawn))?.FindAll(h => VariousDefOf.AllBreasts.Contains(h.def)); + if (hedifflist.NullOrEmpty()) return null; + HediffComp_Breast result; + foreach (Hediff h in hedifflist) + { + result = h.TryGetComp(); + if (result != null) return result; + } + return null; } public static HediffComp_Breast GetBreastComp(this Hediff hediff) @@ -183,7 +192,8 @@ namespace RJW_Menstruation public static void DrawBreastIcon(this Pawn pawn, Rect rect) { - Hediff hediff = pawn.health.hediffSet.hediffs.FirstOrDefault(h => VariousDefOf.AllBreasts.Contains(h.def)); + Hediff hediff = Genital_Helper.get_PartsHediffList(pawn, Genital_Helper.get_breastsBPR(pawn)).FirstOrDefault(h => VariousDefOf.AllBreasts.Contains(h.def)) ?? + Genital_Helper.get_PartsHediffList(pawn, Genital_Helper.get_uddersBPR(pawn)).FirstOrDefault(h => VariousDefOf.AllBreasts.Contains(h.def)); Texture2D breast, nipple, areola; if (hediff != null) { @@ -341,19 +351,20 @@ namespace RJW_Menstruation public static string GetVaginaLabel(this Pawn pawn) { - Hediff hediff = pawn.health.hediffSet.hediffs.Find(h => VariousDefOf.AllVaginas.Contains(h.def)); + Hediff hediff = Genital_Helper.get_PartsHediffList(pawn, Genital_Helper.get_genitalsBPR(pawn)).Find(h => VariousDefOf.AllVaginas.Contains(h.def)); return hediff.LabelBase.CapitalizeFirst() + "\n(" + hediff.LabelInBrackets + ")" + "\n" + xxx.CountOfSex.LabelCap.CapitalizeFirst() + ": " + pawn.records.GetAsInt(xxx.CountOfSex); } public static string GetAnusLabel(this Pawn pawn) { - Hediff hediff = pawn.health.hediffSet.hediffs.FirstOrDefault(h => VariousDefOf.AllAnuses.Contains(h.def)) ?? + Hediff hediff = Genital_Helper.get_PartsHediffList(pawn, Genital_Helper.get_anusBPR(pawn)).FirstOrDefault(h => VariousDefOf.AllAnuses.Contains(h.def)) ?? Genital_Helper.get_PartsHediffList(pawn, Genital_Helper.get_anusBPR(pawn)).FirstOrDefault(h => h.def.defName.ToLower().Contains("anus")); if (hediff != null) return hediff.LabelBase.CapitalizeFirst() + "\n(" + hediff.LabelInBrackets + ")"; else return ""; } public static string GetBreastLabel(this Pawn pawn) { - Hediff hediff = pawn.health.hediffSet.hediffs.FirstOrDefault(h => VariousDefOf.AllBreasts.Contains(h.def)); + Hediff hediff = Genital_Helper.get_PartsHediffList(pawn, Genital_Helper.get_breastsBPR(pawn)).FirstOrDefault(h => VariousDefOf.AllBreasts.Contains(h.def)) ?? + Genital_Helper.get_PartsHediffList(pawn, Genital_Helper.get_uddersBPR(pawn)).FirstOrDefault(h => VariousDefOf.AllBreasts.Contains(h.def)); if (hediff != null) return hediff.LabelBase.CapitalizeFirst() + "\n(" + hediff.LabelInBrackets + ")"; else return ""; } @@ -405,7 +416,7 @@ namespace RJW_Menstruation { Pawn res = pawn.GetFather(); if (res != null) return res; - else res = pawn.relations?.GetFirstDirectRelationPawn(PawnRelationDefOf.Parent, x => x != mother); + else res = pawn.relations?.GetFirstDirectRelationPawn(PawnRelationDefOf.Parent, x => x != mother) ?? null; return res; } @@ -437,7 +448,7 @@ namespace RJW_Menstruation { if (!Configurations.EnableWombIcon) return false; if (pawn.Drafted && !Configurations.EnableDraftedIcon) return false; - if (!pawn.ShouldCycle()) return false; + if (pawn.IsAnimal() && !Configurations.EnableAnimalCycle) return false; return true; } diff --git a/1.4/source/RJW_Menstruation/RJW_Menstruation/VariousDefOf.cs b/1.4/source/RJW_Menstruation/RJW_Menstruation/VariousDefOf.cs index 16903b2..cd4bb56 100644 --- a/1.4/source/RJW_Menstruation/RJW_Menstruation/VariousDefOf.cs +++ b/1.4/source/RJW_Menstruation/RJW_Menstruation/VariousDefOf.cs @@ -40,26 +40,44 @@ namespace RJW_Menstruation public static readonly ThoughtDef TookContraceptivePill = DefDatabase.GetNamed("TookContraceptivePill"); public static readonly ThoughtDef HateTookContraceptivePill = DefDatabase.GetNamed("HateTookContraceptivePill"); public static readonly ThoughtDef EggRestorationReceived = DefDatabase.GetNamed("EggRestorationReceived"); - public static readonly CompProperties_Menstruation HumanVaginaCompProperties = Genital_Helper.average_vagina.CompProps(); + public static readonly CompProperties_Menstruation HumanVaginaCompProperties = (CompProperties_Menstruation)Genital_Helper.average_vagina.comps.FirstOrDefault(x => x is CompProperties_Menstruation); public static readonly KeyBindingDef OpenStatusWindowKey = DefDatabase.GetNamed("OpenStatusWindow"); public static readonly RecordDef AmountofCreampied = DefDatabase.GetNamed("AmountofCreampied"); public static readonly RecordDef AmountofFertilizedEggs = DefDatabase.GetNamed("AmountofFertilizedEggs"); public static readonly TaleDef TaleCameInside = DefDatabase.GetNamed("CameInside"); + public static readonly GeneDef ShortEggLifetime = DefDatabase.GetNamed("Menstruation_ShortEggLifetime"); + public static readonly GeneDef DoubleEggLifetime = DefDatabase.GetNamed("Menstruation_DoubleEggLifetime"); + public static readonly GeneDef QuadEggLifetime = DefDatabase.GetNamed("Menstruation_QuadEggLifetime"); + public static readonly GeneDef NeverEstrus = DefDatabase.GetNamed("Menstruation_NeverEstrus"); + public static readonly GeneDef FullEstrus = DefDatabase.GetNamed("Menstruation_FullEstrus"); + public static readonly GeneDef DoubleOvulation = DefDatabase.GetNamed("Menstruation_DoubleOvulation"); + public static readonly GeneDef QuadOvulation = DefDatabase.GetNamed("Menstruation_QuadOvulation"); + public static readonly GeneDef NoBleeding = DefDatabase.GetNamed("Menstruation_NoBleeding"); + + public static readonly HashSet WombGenes = new HashSet() { + ShortEggLifetime, + DoubleEggLifetime, + QuadEggLifetime, + NeverEstrus, + FullEstrus, + DoubleOvulation, + QuadOvulation, + NoBleeding }; private static List allraces = null; private static List allkinds = null; private static HashSet allvaginas = null; private static HashSet allanuses = null; private static HashSet allbreasts = null; - private static HashSet egglayergenes = null; public static List AllRaces { get { if (allraces != null) return allraces; - allraces = DefDatabase.AllDefsListForReading.Where(thingdef => thingdef.race?.IsFlesh ?? false).ToList(); + List allThings = DefDatabase.AllDefsListForReading; + allraces = allThings.FindAll(x => x.race != null && x.race.IsFlesh); return allraces; } } @@ -68,8 +86,9 @@ namespace RJW_Menstruation get { if (allkinds != null) return allkinds; - allkinds = DefDatabase.AllDefsListForReading.Where(pawnkinddef => pawnkinddef.race != null).ToList(); + List allKinds = DefDatabase.AllDefsListForReading; + allkinds = allKinds.FindAll(x => x.race != null); return allkinds; } } @@ -142,16 +161,6 @@ namespace RJW_Menstruation return allbreasts; } } - public static HashSet EggLayerGenes - { - get - { - if (egglayergenes != null) return egglayergenes; - egglayergenes = DefDatabase.AllDefsListForReading.Where(geneDef => geneDef.GetModExtension()?.disableCycle ?? false).ToHashSet(); - - return egglayergenes; - } - } // Defs from Milkable Colonists public static readonly HediffDef Hediff_Lactating_Drug = DefDatabase.GetNamedSilentFail("Lactating_Drug"); diff --git a/About/About.xml b/About/About.xml index c4c11a0..9c10224 100644 --- a/About/About.xml +++ b/About/About.xml @@ -1,9 +1,7 @@ - rjw.menstruation RJW Menstruation Cycle lutepickle - https://gitgud.io/lutepickle/rjw_menstruation/
  • 1.2
  • 1.3
  • @@ -30,17 +28,13 @@
  • Abraxas.RJW.RaceSupport
  • rjw.milk.humanoid
  • - - -
  • conit.thebirdsandthebees
  • -
    -
    + rjw.menstruation Adds menstruation mechanics to vaginas: Wombs cycle between follicular, luteal, and bleeding phases Tracks eggs ovulated and cum in wombs to determine pregnancy Womb icon and status window -Estrus and pheromone effects +Estrus effects Race-specific fetus images for many vanilla animals Pregnancies from multiple eggs and different fathers Identical siblings diff --git a/About/Manifest.xml b/About/Manifest.xml index 09e409c..028cc7b 100644 --- a/About/Manifest.xml +++ b/About/Manifest.xml @@ -1,7 +1,7 @@ RJW Menstruation - 1.0.9.4 + 1.0.9.1 diff --git a/LoadFolders.xml b/LoadFolders.xml index f711d5c..1707748 100644 --- a/LoadFolders.xml +++ b/LoadFolders.xml @@ -14,5 +14,6 @@
  • 1.4
  • 1.4/RJW Menstruation Race Support
  • +
    \ No newline at end of file diff --git a/changelogs.txt b/changelogs.txt index e21994a..96315e4 100644 --- a/changelogs.txt +++ b/changelogs.txt @@ -1,36 +1,8 @@ -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 - - Japanese translation for most text by Lokuzt. - New womb, cum, and fetus graphics by Euldrop. - 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. - 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. - Climacteric/menopausal pawns will have their sex need slow properly when the womb's tick rate is non-default.