Support for multiple concurrent pregnancies

This commit is contained in:
lutepickle 2022-07-11 18:23:08 -07:00
parent 768f000c5d
commit c0043d2936
11 changed files with 136 additions and 74 deletions

Binary file not shown.

View File

@ -199,13 +199,16 @@ namespace RJW_Menstruation
{
foreach (Pawn child in parent.pawn.relations.Children)
{
bool isFetus;
if (PregnancyHelper.GetPregnancy(parent.pawn) is Hediff_BasePregnancy preg)
bool isFetus = false;
foreach (Hediff_BasePregnancy preg in parent.pawn.health.hediffSet.GetHediffs<Hediff_BasePregnancy>())
{
isFetus = preg.babies.Contains(child);
if (preg.babies.Contains(child))
{
isFetus = true;
break;
}
}
else isFetus = false;
if (
parent.pawn.ageTracker.BirthAbsTicks - child.ageTracker.BirthAbsTicks > ageOfLastBirth &&
!isFetus &&
@ -276,7 +279,7 @@ namespace RJW_Menstruation
// Scenario B: Pregnant, grow in the second half of first trimester
else if (parent.pawn.IsPregnant())
{
float pregnancySize = Mathf.InverseLerp(BREAST_GROWTH_START, BREAST_GROWTH_END, parent.pawn.GetPregnancyProgress()) * MAX_BREAST_INCREMENT;
float pregnancySize = Mathf.InverseLerp(BREAST_GROWTH_START, BREAST_GROWTH_END, parent.pawn.GetFarthestPregnancyProgress()) * MAX_BREAST_INCREMENT;
if (breastSizeIncreased > pregnancySize)
{
debugGrowthStatus = "Shrinking due to being oversize for pregnancy";

View File

@ -119,6 +119,9 @@ namespace RJW_Menstruation
protected HediffComp_Breast breastcache = null;
protected float antisperm = 0.0f;
protected float? originvagsize = null;
protected Hediff_BasePregnancy pregnancy = null;
public Hediff_BasePregnancy Pregnancy { get => pregnancy; set => pregnancy = value; }
public int OvaryPowerThreshold
{
@ -173,7 +176,7 @@ namespace RJW_Menstruation
get
{
float res = Props.maxCumCapacity * parent.pawn.BodySize;
if (curStage != Stage.Pregnant || (parent.pawn.GetRJWPregnancy()?.Severity ?? 0f) < 0.175f) res *= 500f;
if (curStage != Stage.Pregnant || (pregnancy?.Severity ?? 0f) < 0.175f) res *= 500f;
return res;
}
}
@ -236,7 +239,7 @@ namespace RJW_Menstruation
{
if (curStage == Stage.Pregnant)
{
if (Configurations.InfoDetail == Configurations.DetailLevel.All || (PregnancyHelper.GetPregnancy(parent.pawn)?.Visible ?? false))
if (Configurations.InfoDetail == Configurations.DetailLevel.All || (pregnancy?.Visible ?? false))
return Stage.Pregnant;
else
return Stage.Luteal;
@ -520,6 +523,7 @@ namespace RJW_Menstruation
Scribe_Values.Look(ref estrusflag, "estrusflag", estrusflag, true);
Scribe_Values.Look(ref originvagsize, "originvagsize", originvagsize, true);
Scribe_Values.Look(ref DoCleanWomb, "DoCleanWomb", DoCleanWomb, true);
Scribe_References.Look(ref pregnancy, "pregnancy");
}
@ -544,6 +548,7 @@ namespace RJW_Menstruation
{
HugsLibController.Instance.TickDelayScheduler.TryUnscheduleCallback(actionref);
Log.Message(parent.pawn.Label + " tick scheduler removed");
pregnancy?.Miscarry();
base.CompPostPostRemoved();
}
@ -895,18 +900,13 @@ namespace RJW_Menstruation
InitOvary();
Hediff_BasePregnancy pregnancy = parent.pawn.GetRJWPregnancy();
if (pregnancy != null)
if (pregnancy == null && // If there's no pregnancy registered in this womb...
PregnancyHelper.GetPregnancy(parent.pawn) is Hediff_BasePregnancy preg && // ...but the pawn is pregnant...
preg.GetMenstruationComp() == null) // ...and another womb doesn't have it, then pick it up
{
Hediff hediff = PregnancyHelper.GetPregnancy(parent.pawn);
if (hediff != null)
{
if (hediff is Hediff_BasePregnancy preg)
{
currentIntervalHours = (int)(preg.GestationHours());
curStage = Stage.Pregnant;
}
}
currentIntervalHours = (int)preg.GestationHours();
curStage = Stage.Pregnant;
pregnancy = preg;
}
if (parent.pawn.IsAnimal())
@ -1116,9 +1116,18 @@ namespace RJW_Menstruation
deadeggs.Add(egg);
continue;
}
else if (parent.pawn.health.hediffSet.GetHediffs<Hediff_InsectEgg>().Any())
{
deadeggs.Add(egg);
continue;
}
else if (pregnancy is Hediff_MechanoidPregnancy)
{
deadeggs.Add(egg);
continue;
}
else if (Rand.Range(0.0f, 1.0f) <= Configurations.ImplantationChance * ImplantFactor * InterspeciesImplantFactor(egg.fertilizer))
{
Hediff_BasePregnancy pregnancy = parent.pawn.GetRJWPregnancy();
if (pregnancy != null)
{
if (Configurations.UseMultiplePregnancy && Configurations.EnableHeteroOvularTwins)
@ -1143,17 +1152,16 @@ namespace RJW_Menstruation
if (!Configurations.UseMultiplePregnancy)
{
PregnancyHelper.PregnancyDecider(parent.pawn, egg.fertilizer);
Hediff_BasePregnancy hediff = (Hediff_BasePregnancy)PregnancyHelper.GetPregnancy(parent.pawn);
currentIntervalHours = (int)hediff?.GestationHours();
// I hate having to do this, but it gets the newest pregnancy
pregnancy = parent.pawn.health.hediffSet.GetHediffs<Hediff_BasePregnancy>().MaxBy(hediff => hediff.loadID);
currentIntervalHours = (int)pregnancy?.GestationHours();
pregnant = true;
break;
}
else
{
Hediff_BasePregnancy.Create<Hediff_MultiplePregnancy>(parent.pawn, egg.fertilizer);
Hediff_BasePregnancy hediff = (Hediff_BasePregnancy)PregnancyHelper.GetPregnancy(parent.pawn);
currentIntervalHours = (int)hediff?.GestationHours();
pregnancy = Hediff_BasePregnancy.Create<Hediff_MultiplePregnancy>(parent.pawn, egg.fertilizer);
currentIntervalHours = (int)pregnancy?.GestationHours();
pregnant = true;
deadeggs.Add(egg);
}
@ -1449,13 +1457,18 @@ namespace RJW_Menstruation
Implant();
}
if (parent.pawn.GetRJWPregnancy() != null)
if (parent.pawn.health.hediffSet.hediffs.Contains(pregnancy))
{
curStageHrs += 1;
StayCurrentStageConst(Stage.Pregnant);
}
else
{
if (pregnancy != null)
{
Log.Warning("Pawn does not have specified pregnancy, but the womb is still marked as pregnant");
pregnancy = null;
}
if (Breast != null)
{
Breast.BirthTransition();

View File

@ -55,6 +55,11 @@ namespace RJW_Menstruation
return null;
}
public static HediffComp_Menstruation GetMenstruationComp(this Hediff_BasePregnancy pregnancy)
{
return pregnancy.pawn.GetMenstruationComps().FirstOrDefault(comp => comp.Pregnancy == pregnancy);
}
public static HediffComp_Anus GetAnusComp(this Pawn pawn)
{
var hedifflist = Genital_Helper.get_PartsHediffList(pawn, Genital_Helper.get_genitalsBPR(pawn))?.FindAll((Hediff h) => h.def.defName.ToLower().Contains("anus"));
@ -84,6 +89,36 @@ namespace RJW_Menstruation
return 1.0f - Mathf.Pow(1.0f - Configurations.FertilizeChance, comp.TotalFertCum * comp.Props.basefertilizationChanceFactor);
}
// A looser CanImpregnate
public static bool CanFertilize(Pawn fucker, Pawn fucked, xxx.rjwSextype sexType = xxx.rjwSextype.Vaginal, bool availableWomb = false)
{
if (fucker == null || fucked == null) return false;
if (!fucked.GetMenstruationComps().Any()) return PregnancyHelper.CanImpregnate(fucker, fucked, sexType);
if (sexType != xxx.rjwSextype.Vaginal && sexType != xxx.rjwSextype.DoublePenetration) return false;
if (AndroidsCompatibility.IsAndroid(fucker) && AndroidsCompatibility.IsAndroid(fucked)) return false;
if (fucker.IsUnsexyRobot() || fucked.IsUnsexyRobot()) return false;
if (!fucker.RaceHasPregnancy() || !fucked.RaceHasPregnancy()) return false;
if (!Genital_Helper.has_penis_fertile(fucker, fucker.GetGenitalsList())) return false;
if (xxx.is_human(fucked) && xxx.is_human(fucker) && !RJWPregnancySettings.humanlike_pregnancy_enabled) return false;
if (xxx.is_animal(fucker) != xxx.is_animal(fucked) && !RJWPregnancySettings.bestial_pregnancy_enabled) return false;
if (xxx.is_animal(fucker) && xxx.is_animal(fucked) && !RJWPregnancySettings.animal_pregnancy_enabled) return false;
if (fucker.def.defName != fucked.def.defName && RJWPregnancySettings.interspecies_impregnation_modifier <= 0.0f && !RJWPregnancySettings.complex_interspecies) return false;
if (availableWomb && fucked.GetMenstruationComps().All(comp => comp.Pregnancy != null)) return false;
return true;
}
public static Texture2D GetPregnancyIcon(this HediffComp_Menstruation comp, Hediff hediff)
{
string icon = "";
@ -166,9 +201,9 @@ namespace RJW_Menstruation
}
public static Texture2D GetEggIcon(this HediffComp_Menstruation comp, bool includeOvary)
{
if (comp.parent.pawn.IsPregnant(Configurations.InfoDetail != Configurations.DetailLevel.All) && !(PregnancyHelper.GetPregnancy(comp.parent.pawn) is Hediff_MechanoidPregnancy))
{
if (comp.parent.pawn.GetPregnancyProgress() < 0.2f) return ContentFinder<Texture2D>.Get("Eggs/Egg_Implanted00", true);
if (comp.Pregnancy != null && !(comp.Pregnancy is Hediff_MechanoidPregnancy))
{
if (comp.GetPregnancyProgress() < 0.2f) return ContentFinder<Texture2D>.Get("Eggs/Egg_Implanted00", true);
else return ContentFinder<Texture2D>.Get("Womb/Empty", true);
}

View File

@ -33,6 +33,12 @@ namespace RJW_Menstruation
}
}
public override void Miscarry()
{
base.Miscarry();
this.GetMenstruationComp().Pregnancy = null;
}
public override void GiveBirth()
{
@ -62,6 +68,7 @@ namespace RJW_Menstruation
}
pawn.health.RemoveHediff(this);
this.GetMenstruationComp().Pregnancy = null;
}
public string GetBabyInfo()

View File

@ -49,8 +49,8 @@ namespace RJW_Menstruation
else description += comp.GetCurStageLabel + "\n";
if (pawn.IsPregnant())
{
Hediff hediff = PregnancyHelper.GetPregnancy(pawn);
if (Utility.ShowFetusImage((Hediff_BasePregnancy)hediff))
Hediff_BasePregnancy hediff = comp.Pregnancy;
if (Utility.ShowFetusImage(hediff))
{
icon = comp.GetPregnancyIcon(hediff);
if (hediff is Hediff_BasePregnancy h)

View File

@ -15,12 +15,15 @@ namespace RJW_Menstruation
[HarmonyPatch(typeof(PregnancyHelper), nameof(PregnancyHelper.impregnate))]
public static class Impregnate_Patch
{
public static bool Prefix(SexProps props)
public static bool Prefix(SexProps props, out HediffComp_Menstruation __state)
{
xxx.rjwSextype sextype = props.sexType;
Pawn pawn = props.pawn;
Pawn partner = props.partner;
if (PregnancyHelper.GetPregnancy(partner) is Hediff_BasePregnancy oldestPregnancy) __state = oldestPregnancy.GetMenstruationComp();
else __state = null;
if (sextype != xxx.rjwSextype.Vaginal && sextype != xxx.rjwSextype.DoublePenetration) return true;
if (partner.IsAnimal() && !Configurations.EnableAnimalCycle) return true;
@ -35,7 +38,7 @@ namespace RJW_Menstruation
else comp = pawn.GetRandomMenstruationComp();
if (comp == null) return true;
if (Genital_Helper.has_penis_fertile(pawn, pawnparts) && PregnancyHelper.CanImpregnate(pawn, partner, sextype))
if (Genital_Helper.has_penis_fertile(pawn, pawnparts) && MenstruationUtility.CanFertilize(pawn, partner, sextype))
{
PregnancyHelper.Doimpregnate(pawn, partner);
return false;
@ -47,8 +50,20 @@ namespace RJW_Menstruation
else comp.CumIn(pawn, pawn.GetCumVolume(pawnparts), 0);
return true;
}
public static void Postfix(SexProps props, HediffComp_Menstruation __state)
{
if (__state == null || __state.Pregnancy != null) return;
// It was pregnant, but not anymore. This probably means the pregnancy was destroyed by e.g. a mech implant
Pawn pawn = props.partner;
Hediff_BasePregnancy newestPregnancy = pawn.health.hediffSet.GetHediffs<Hediff_BasePregnancy>().MaxBy(hediff => hediff.loadID);
if (newestPregnancy == null) return;
foreach (HediffComp_Menstruation comp in pawn.GetMenstruationComps())
{
if (comp.Pregnancy == newestPregnancy) return; // One of the wombs did get it
}
__state.Pregnancy = newestPregnancy;
}
/// <summary>
@ -129,9 +144,9 @@ namespace RJW_Menstruation
// This is stricter than can_impregnate, so quickly filter out scenarios that are negative anyways.
if (__result == false || __instance != Quirk.ImpregnationFetish) return;
__result =
(PregnancyHelper.CanImpregnate(pawn, partner) && (partner.GetMenstruationComps()?.Any(comp => comp.IsDangerDay) ?? true))
(MenstruationUtility.CanFertilize(pawn, partner, availableWomb: true) && (partner.GetMenstruationComps()?.Any(comp => comp.IsDangerDay) ?? true))
||
(PregnancyHelper.CanImpregnate(partner, pawn) && (pawn.GetMenstruationComps()?.Any(comp => comp.IsDangerDay) ?? true));
(MenstruationUtility.CanFertilize(partner, pawn, availableWomb: true) && (pawn.GetMenstruationComps()?.Any(comp => comp.IsDangerDay) ?? true));
}
}
@ -150,9 +165,9 @@ namespace RJW_Menstruation
else __result--;
if (
(PregnancyHelper.CanImpregnate(pawn, partner, props.sexType) && (partner.GetMenstruationComps()?.Any(comp => comp.IsDangerDay) ?? true))
(MenstruationUtility.CanFertilize(pawn, partner, props.sexType, true) && (partner.GetMenstruationComps()?.Any(comp => comp.IsDangerDay) ?? true))
||
(PregnancyHelper.CanImpregnate(partner, pawn, props.sexType) && (pawn.GetMenstruationComps()?.Any(comp => comp.IsDangerDay) ?? true)))
(MenstruationUtility.CanFertilize(partner, pawn, props.sexType, true) && (pawn.GetMenstruationComps()?.Any(comp => comp.IsDangerDay) ?? true)))
__result++;
}
}
@ -174,15 +189,15 @@ namespace RJW_Menstruation
}
public static void Postfix(ref float __result, Pawn fucker, Pawn fucked)
{
if (fucker.IsInEstrus(true) && PregnancyHelper.CanImpregnate(fucked, fucker))
if (fucker.IsInEstrus(true) && MenstruationUtility.CanFertilize(fucked, fucker))
{
__result *= (1f + GetNetFertility(fucker, fucked) / 4);
}
else if (fucker.IsInEstrus(false) && PregnancyHelper.CanImpregnate(fucked, fucker))
else if (fucker.IsInEstrus(false) && MenstruationUtility.CanFertilize(fucked, fucker))
{
__result *= (1f + GetNetFertility(fucker, fucked) / 40);
}
else if(xxx.is_animal(fucker) && fucked.IsInEstrus(true) && PregnancyHelper.CanImpregnate(fucker, fucked))
else if(xxx.is_animal(fucker) && fucked.IsInEstrus(true) && MenstruationUtility.CanFertilize(fucker, fucked))
{
__result *= 1.25f;
}

View File

@ -101,6 +101,7 @@
<HintPath>..\..\..\..\..\rjw\1.3\Assemblies\RJW.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Xml" />
<Reference Include="UnityEngine">
<HintPath>..\..\..\..\..\..\RimWorldWin64_Data\Managed\UnityEngine.dll</HintPath>
@ -158,7 +159,7 @@
<ItemGroup>
<PackageReference Include="Lib.Harmony">
<Version>2.1.1</Version>
<ExcludeAssets>runtime</ExcludeAssets>
<ExcludeAssets>runtime</ExcludeAssets>
</PackageReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />

View File

@ -151,16 +151,15 @@ namespace RJW_Menstruation
fontstyleleft.normal.textColor = Color.white;
float preginfoheight = 0f;
bool pregnant = pawn.IsPregnant();
Hediff hediff = PregnancyHelper.GetPregnancy(pawn);
if (pregnant && Utility.ShowFetusImage((Hediff_BasePregnancy)hediff))
Hediff_BasePregnancy hediff = comp.Pregnancy;
if (hediff != null && Utility.ShowFetusImage(hediff))
{
womb = comp.GetPregnancyIcon(hediff);
if (hediff is Hediff_MultiplePregnancy m)
{
if (m.GestationProgress < 0.2f) cum = comp.GetCumIcon();
else cum = ContentFinder<Texture2D>.Get(("Womb/Empty"), true);
Pawn fetus = pawn.GetFetus();
Pawn fetus = comp.GetFetus();
if (fetus != null && Utility.ShowFetusInfo())
{
string feinfo = m.GetBabyInfo();
@ -188,7 +187,7 @@ namespace RJW_Menstruation
{
if (b.GestationProgress < 0.2f) cum = comp.GetCumIcon();
else cum = ContentFinder<Texture2D>.Get(("Womb/Empty"), true);
Pawn fetus = pawn.GetFetus();
Pawn fetus = comp.GetFetus();
if (fetus != null && Utility.ShowFetusInfo())
{
preginfoheight = fontheight;

View File

@ -141,37 +141,27 @@ namespace RJW_Menstruation
}
public static float GetPregnancyProgress(this Pawn pawn)
public static float GetFarthestPregnancyProgress(this Pawn pawn)
{
Hediff hediff = PregnancyHelper.GetPregnancy(pawn);
if (hediff is Hediff_BasePregnancy h)
{
return h.GestationProgress;
}
return -1;
return pawn.health.hediffSet.GetHediffs<Hediff_BasePregnancy>().MaxBy(hediff => hediff.GestationProgress)?.GestationProgress ?? -1;
}
public static Pawn GetFetus(this Pawn pawn)
public static float GetPregnancyProgress(this HediffComp_Menstruation comp)
{
Hediff hediff = PregnancyHelper.GetPregnancy(pawn);
if (hediff is Hediff_BasePregnancy h)
{
if (!h.babies.NullOrEmpty()) return h.babies.First();
else
{
Log.Error("Baby not exist: baby was not created or removed. Remove pregnancy.");
pawn.health.RemoveHediff(hediff);
return null;
}
}
return null;
if (comp.Pregnancy == null) return -1;
else return comp.Pregnancy.GestationProgress;
}
public static Hediff_BasePregnancy GetRJWPregnancy(this Pawn pawn)
public static Pawn GetFetus(this HediffComp_Menstruation comp)
{
return (Hediff_BasePregnancy)pawn.health.hediffSet.hediffs.FirstOrDefault(x => x is Hediff_BasePregnancy);
if (comp.Pregnancy == null) return null;
if (!comp.Pregnancy.babies.NullOrEmpty()) return comp.Pregnancy.babies.First();
else
{
Log.Error("Baby not exist: baby was not created or removed. Remove pregnancy.");
comp.Pregnancy.Miscarry();
return null;
}
}
public static void DrawBreastIcon(this Pawn pawn, Rect rect , bool drawOrigin = false)

View File

@ -2,8 +2,7 @@ Version 1.0.7.0
- Not save compatible with previous versions. Expect tons of red errors and strangeness if you try, but theoretically it should eventually work again.
- Experimental support for multiple vaginas.
- Known issue: A pregnant pawn will have the fetus graphic appear in all wombs.
- Unknown effect if a womb becomes pregnant while another womb already is.
- Super-experimental support for multiple concurrent pregnancies.
- Overhauled many things under the hood.
- Cycle randomization completely redone. Everyone now has a (hidden) cycle speed and cycle variability.
- Ovary initialization redone. All pawns now get a total number of eggs based on their age and racial properties.