Compare commits

..

7 commits

11 changed files with 201 additions and 24 deletions

Binary file not shown.

View file

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

View file

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

View file

@ -1297,8 +1297,19 @@ namespace RJW_Menstruation
if (Configurations.Debug) Log.Message($"Implanting fertilized egg of {Pawn} into {parent}, father {egg.fertilizer}");
if (pregnancy != null)
{
// TODO: Modified Biotech pregnancy
if (Configurations.PregnancySource == Configurations.PregnancyType.MultiplePregnancy && Configurations.EnableHeteroOvularTwins)
if (Configurations.PregnancySource == Configurations.PregnancyType.Biotech && Configurations.EnableBiotechTwins && Configurations.EnableHeteroOvularTwins)
{
if (Configurations.Debug) Log.Message($"Adding to existing Biotech pregnancy {pregnancy}");
HediffComp_PregeneratedBabies comp = pregnancy.TryGetComp<HediffComp_PregeneratedBabies>();
if (comp == null) Log.Warning($"Trying to add Biotech egg to {Pawn}'s pregnancy without a pregenerated baby comp: {pregnancy}");
else
{
comp.AddNewBaby(Pawn, egg.fertilizer);
pregnant = true;
deadeggs.Add(egg);
}
}
else if (Configurations.PregnancySource == Configurations.PregnancyType.MultiplePregnancy && Configurations.EnableHeteroOvularTwins)
{
if (pregnancy is Hediff_MultiplePregnancy h)
{
@ -1340,8 +1351,12 @@ namespace RJW_Menstruation
case Configurations.PregnancyType.Biotech:
if (Configurations.Debug) Log.Message($"Creating new biotech pregnancy");
pregnancy = HediffMaker.MakeHediff(HediffDefOf.PregnantHuman, Pawn);
if(Configurations.EnableBiotechTwins)
pregnancy.TryGetComp<HediffComp_PregeneratedBabies>().AddNewBaby(Pawn, egg.fertilizer);
((Hediff_Pregnant)pregnancy).SetParents(Pawn, egg.fertilizer, PregnancyUtility.GetInheritedGeneSet(egg.fertilizer, Pawn));
Pawn.health.AddHediff(pregnancy);
pregnant = true;
deadeggs.Add(egg);
break;
}
if (pregnancy is Hediff_BasePregnancy rjw_preg)
@ -1361,7 +1376,9 @@ namespace RJW_Menstruation
}
}
if (pregnant && (Configurations.PregnancySource != Configurations.PregnancyType.MultiplePregnancy || !Configurations.EnableHeteroOvularTwins))
if (pregnant &&
(Configurations.PregnancySource != Configurations.PregnancyType.MultiplePregnancy || !Configurations.EnableHeteroOvularTwins) &&
(Configurations.PregnancySource != Configurations.PregnancyType.Biotech || !Configurations.EnableBiotechTwins || !Configurations.EnableHeteroOvularTwins))
{
eggs.Clear();
return true;

View file

@ -1,10 +1,12 @@
using RimWorld;
using RimWorld.BaseGen;
using HarmonyLib;
using Mono.Cecil.Cil;
using RimWorld;
using rjw;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using Verse;
namespace RJW_Menstruation
@ -93,7 +95,7 @@ namespace RJW_Menstruation
{
Pawn baby = PawnGenerator.GeneratePawn(request);
if (baby == null) break;
PregnancyCommon.SetupBabyXenotype(mother, father, baby);
PregnancyCommon.SetupBabyXenotype(mother, father, baby); // Probably redundant with Biotech post-birth xenotyping
if (division > 1)
{
if (i == 0)
@ -136,4 +138,131 @@ namespace RJW_Menstruation
}
}
}
[HarmonyPatch(typeof(PregnancyUtility), nameof(PregnancyUtility.ApplyBirthOutcome))]
public static class ApplyBirthOutcome_PregeneratedBabies_Patch
{
private static Pawn GetPregeneratedBaby(PawnGenerationRequest request, Thing birtherThing)
{
// Don't test for the config set here. We can do it at the functions that call ApplyBirthOutcome
// Easier to work out twins that way
// From e.g. a vat
if (!(birtherThing is Pawn mother) || !xxx.is_human(mother))
return PawnGenerator.GeneratePawn(request);
// No babies found. Could be an unmodified pregnancy
HediffComp_PregeneratedBabies comp = mother.health.hediffSet.GetFirstHediff<Hediff_LaborPushing>()?.TryGetComp<HediffComp_PregeneratedBabies>();
if (comp == null || !comp.HasBaby)
return PawnGenerator.GeneratePawn(request);
Pawn baby = comp.PopBaby();
if (baby == null) return PawnGenerator.GeneratePawn(request); // Shouldn't happen
if (request.ForceDead) baby.Kill(null, null);
return baby;
}
private static readonly MethodInfo ApplyBirthOutcome = typeof(PregnancyUtility).GetMethod(nameof(PregnancyUtility.ApplyBirthOutcome));
private static readonly int birtherThing = ApplyBirthOutcome.GetParameters().FirstIndexOf(parameter => parameter.Name == "birtherThing" && parameter.ParameterType == typeof(Thing));
private static readonly MethodInfo GeneratePawn = typeof(PawnGenerator).GetMethod(nameof(PawnGenerator.GeneratePawn), new Type[] {typeof (PawnGenerationRequest)});
public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{
if (birtherThing < 0) throw new InvalidOperationException("Could not locate index of birtherThing");
if (GeneratePawn == null || GeneratePawn.ReturnType != typeof(Pawn)) throw new InvalidOperationException("GeneratePawn not found");
foreach (CodeInstruction instruction in instructions)
{
if (instruction.Calls(GeneratePawn))
{
yield return new CodeInstruction(OpCodes.Ldarg, birtherThing);
yield return CodeInstruction.Call(typeof(ApplyBirthOutcome_PregeneratedBabies_Patch), nameof(GetPregeneratedBaby));
}
else yield return instruction;
}
}
}
[HarmonyPatch(typeof(Hediff_LaborPushing), nameof(Hediff_LaborPushing.PreRemoved))]
public static class Hediff_LaborPushing_PreRemoved_Patch
{
private static Thing ApplyBirthLoop(OutcomeChance outcome, float quality, Precept_Ritual ritual, List<GeneDef> genes, Pawn geneticMother, Thing birtherThing, Pawn father, Pawn doctor, LordJob_Ritual lordJobRitual, RitualRoleAssignments assignments)
{
if (birtherThing is Pawn mother)
{
HediffComp_PregeneratedBabies comp = mother.health.hediffSet.GetFirstHediff<Hediff_LaborPushing>().TryGetComp<HediffComp_PregeneratedBabies>();
if (comp?.HasBaby ?? false)
{
OutcomeChance thisOutcome = outcome;
Precept_Ritual precept_Ritual = (Precept_Ritual)comp.Pawn.Ideo.GetPrecept(PreceptDefOf.ChildBirth);
float birthQuality = PregnancyUtility.GetBirthQualityFor(mother);
do
{
PregnancyUtility.ApplyBirthOutcome(thisOutcome, quality, ritual, genes, geneticMother, birtherThing, father, doctor, lordJobRitual, assignments);
// No more babies if mom dies halfway through. Unrealistic maybe, but saves a lot of headache in ApplyBirthOutcome
if (mother.health.Dead) break;
thisOutcome = ((RitualOutcomeEffectWorker_ChildBirth)precept_Ritual.outcomeEffect).GetOutcome(birthQuality, null);
} while (comp.HasBaby);
// PreRemoved doesn't use the return value
return null;
}
}
return PregnancyUtility.ApplyBirthOutcome(outcome, quality, ritual, genes, geneticMother, birtherThing, father, doctor, lordJobRitual, assignments);
}
private static readonly MethodInfo ApplyBirthOutcome = typeof(PregnancyUtility).GetMethod(nameof(PregnancyUtility.ApplyBirthOutcome),
new Type[] {typeof(OutcomeChance), typeof(float), typeof(Precept_Ritual), typeof(List<GeneDef>), typeof(Pawn), typeof(Thing), typeof(Pawn), typeof(Pawn), typeof(LordJob_Ritual), typeof(RitualRoleAssignments)});
public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{
if (ApplyBirthOutcome == null || ApplyBirthOutcome.ReturnType != typeof(Thing)) throw new InvalidOperationException("ApplyBirthOutcome not found");
foreach (CodeInstruction instruction in instructions)
{
if (instruction.Calls(ApplyBirthOutcome))
yield return CodeInstruction.Call(typeof(Hediff_LaborPushing_PreRemoved_Patch), nameof(Hediff_LaborPushing_PreRemoved_Patch.ApplyBirthLoop));
else yield return instruction;
}
}
}
// Much the same as the other one
[HarmonyPatch(typeof(RitualOutcomeEffectWorker_ChildBirth), nameof (RitualOutcomeEffectWorker_ChildBirth.Apply))]
public static class Ritual_ChildBirth_Apply_Patch
{
private static Thing ApplyBirthLoop(OutcomeChance outcome, float quality, Precept_Ritual ritual, List<GeneDef> genes, Pawn geneticMother, Thing birtherThing, Pawn father, Pawn doctor, LordJob_Ritual lordJobRitual, RitualRoleAssignments assignments)
{
if (birtherThing is Pawn mother)
{
HediffComp_PregeneratedBabies comp = mother.health.hediffSet.GetFirstHediff<Hediff_LaborPushing>().TryGetComp<HediffComp_PregeneratedBabies>();
if (comp?.HasBaby ?? false)
{
// Don't reroll the outcome every time, I think
// This is all one ritual, so every baby has the same ritual outcome
// I don't think this will add the ritual memory every time?
// Though even if it does, that's probably okay. More babies more memories after all
do
{
PregnancyUtility.ApplyBirthOutcome(outcome, quality, ritual, genes, geneticMother, birtherThing, father, doctor, lordJobRitual, assignments);
if (mother.health.Dead) break;
} while (comp.HasBaby);
return null;
}
}
return PregnancyUtility.ApplyBirthOutcome(outcome, quality, ritual, genes, geneticMother, birtherThing, father, doctor, lordJobRitual, assignments);
}
private static readonly MethodInfo ApplyBirthOutcome = typeof(PregnancyUtility).GetMethod(nameof(PregnancyUtility.ApplyBirthOutcome),
new Type[] { typeof(OutcomeChance), typeof(float), typeof(Precept_Ritual), typeof(List<GeneDef>), typeof(Pawn), typeof(Thing), typeof(Pawn), typeof(Pawn), typeof(LordJob_Ritual), typeof(RitualRoleAssignments) });
public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{
if (ApplyBirthOutcome == null || ApplyBirthOutcome.ReturnType != typeof(Thing)) throw new InvalidOperationException("ApplyBirthOutcome not found");
foreach (var instruction in instructions)
{
if (instruction.Calls(ApplyBirthOutcome))
yield return CodeInstruction.Call(typeof(Ritual_ChildBirth_Apply_Patch), nameof(Ritual_ChildBirth_Apply_Patch.ApplyBirthLoop));
else yield return instruction;
}
}
}
}

View file

@ -115,7 +115,10 @@ namespace RJW_Menstruation
if (hediff is Hediff_MechanoidPregnancy)
return ContentFinder<Texture2D>.Get(("Womb/Mechanoid_Fluid"), true);
ThingDef babydef = comp.Pawn.def; // TODO: Pregenerated babies
ThingDef babydef = comp.Pawn.def;
HediffComp_PregeneratedBabies babiescomp = hediff?.TryGetComp<HediffComp_PregeneratedBabies>();
if (babiescomp?.HasBaby ?? false)
babydef = babiescomp.babies.First().def;
float gestationProgress = comp.StageProgress;
int babycount = hediff is Hediff_BasePregnancy preg ? preg.babies.Count : 1;

View file

@ -111,7 +111,7 @@ namespace RJW_Menstruation
}
[HarmonyPatch(typeof(PregnancyUtility), nameof(PregnancyUtility.ApplyBirthOutcome))]
public class ApplyBirthOutcome_Patch
public class ApplyBirthOutcome_Breast_Patch
{
public static void PostFix(Thing birtherThing)
{

View file

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

View file

@ -221,19 +221,36 @@ namespace RJW_Menstruation
{
if (p is Hediff_Pregnant hp && hp.Severity < 0.2f) cum = comp.GetCumIcon();
else cum = ContentFinder<Texture2D>.Get("Womb/Empty", true);
// TODO: Pregenerated babies (base on multiplepregnancy)
HediffComp_PregeneratedBabies babiescomp = p.TryGetComp<HediffComp_PregeneratedBabies>();
if (Utility.ShowFetusInfo())
{
preginfoheight = fontheight;
string feinfo = PregnancyCommon.GetBabyInfo(babiescomp?.babies);
string fainfo = PregnancyCommon.GetFatherInfo(babiescomp?.babies, babiescomp.Pawn, true) + " "; // Keep all parents known, for now
if (feinfo == "Null") feinfo = "1 " + p.Mother.def.label + " " + Translations.Dialog_WombInfo02;
if (fainfo == "Null")
{
string father = p.Father?.LabelShort ?? Translations.Dialog_FatherUnknown;
fainfo = Translations.Dialog_WombInfo03 + ": " + father + " ";
}
if (feinfo.Length + fainfo.Length > 45)
{
preginfoheight = fontheight + 2;
buttonstyle.alignment = TextAnchor.UpperLeft;
fontstyleright.alignment = TextAnchor.LowerRight;
}
else
{
preginfoheight = fontheight;
buttonstyle.alignment = TextAnchor.MiddleLeft;
}
Rect preginfo = new Rect(0f, mainRect.yMax - wombRectHeight - 2, wombRectWidth, preginfoheight);
fontstyleright.normal.textColor = Color.white;
fontstyleright.alignment = TextAnchor.MiddleRight;
buttonstyle.alignment = TextAnchor.MiddleLeft;
string father = p.Father?.LabelShort ?? Translations.Dialog_FatherUnknown;
GUI.Box(preginfo, "1 " + p.Mother.def.label + " " + Translations.Dialog_WombInfo02, buttonstyle);
GUI.Label(preginfo, Translations.Dialog_WombInfo03 + ": " + father + " ", fontstyleright);
GUI.Box(preginfo, feinfo, buttonstyle);
GUI.Label(preginfo, fainfo, fontstyleright);
}
}
else cum = ContentFinder<Texture2D>.Get(("Womb/Empty"), true);

View file

@ -188,8 +188,8 @@ namespace RJW_Menstruation
return null;
}
}
// TODO: Biotech pregenerated babies
return null;
HediffComp_PregeneratedBabies babiescomp = comp.Pregnancy.TryGetComp<HediffComp_PregeneratedBabies>();
return babiescomp?.babies?.FirstOrDefault();
}
public static void DrawBreastIcon(this Pawn pawn, Rect rect)

View file

@ -1,6 +1,7 @@
Version 1.0.8.6
- Updated Traditional Chinese translation by Hydrogen.
- Added several menstruation-related genes.
- Added experimental support for twins and hybrids with Biotech pregnancies, disabled by default.
Version 1.0.8.5
- Added biosculpter recipe to restore 1 year's worth of eggs, with icon by DestinyPlayer.