litter-biotech/Source/LitterBiotech.cs

159 lines
7.3 KiB
C#
Raw Normal View History

2023-01-15 10:55:51 +11:00
using HarmonyLib;
using RimWorld;
using LitterBiotech.Helpers;
using System.Collections.Generic;
using Verse;
namespace LitterBiotech
{
[StaticConstructorOnStartup]
public class LitterBiotechMod
{
static Dictionary<string, LaborState> laborStateMap = new Dictionary<string, LaborState>();
static LitterBiotechMod()
{
Harmony harmony = new Harmony(id: "rimworld.leboeuf.litterbiotech");
// Hediff_Labor state capture
harmony.Patch(AccessTools.Method(typeof(Hediff_Labor), nameof(Hediff_Labor.PostRemoved)),
postfix: new HarmonyMethod(typeof(LitterBiotechMod), nameof(Hediff_Labor_PostRemovedPostFix)));
// Gene_LitteredBirths multibirth logic
harmony.Patch(AccessTools.Method(typeof(Hediff_LaborPushing), nameof(Hediff_LaborPushing.PostRemoved)),
postfix: new HarmonyMethod(typeof(LitterBiotechMod), nameof(Hediff_LaborPushing_PostRemovedPostFix)));
LBTLogger.Message("Litter Biotech started successfully.");
if (LBTSettings.devMode)
{
LBTLogger.Message("Notice: Developer logging for Litter Biotech is currently active - it can be disabled in the mod settings.");
}
}
// Notes about Hediff_Labor_PostRemovedPostFix
// ===========================================
// Alright, didn't really want to make a patch for Labor as well as LaborPushing, but I have to because otherwise there's
// no way to assign a doctor on second/third/etc births if I only use LaborPushing, which can disproportionately cause
// more stillbirths than you might normally have
//
// I TRIED to modify the ChildBirth lord job and ritual code to allow the first doctor that was assigned and any childbirth
// attendees to just autoreassign themselves to the next births, but it wasn't working - for now, I'm not happy with how I'm
// doing this because multiple births now require:
// 1. (potentially several) forced pauses with letters telling the player to reassign a doctor to the mother who's giving birth to another child
// 2. A LOT of state carrying (that I likely overengineered :v) ) between hediff removes and adds
//
// I've got a dictionary now for storing state across hediffs and on multiple pawns, instead of what I was originally doing, which was using
// severity as a way of tracking state. LaborState class should help keep things a little more organized.
//
// I'll revisit this in the future (probably). Thanks for coming to my TED talk
static void Hediff_Labor_PostRemovedPostFix(ref Hediff_Labor __instance)
{
bool randomTwinsRoll;
int totalBirths;
bool laborStateIsNull = !laborStateMap.ContainsKey(__instance.pawn.ThingID);
bool hasLitteredBirthsGene = __instance.pawn.genes.HasGene(LitterBiotech.GeneDefOf.LitteredBirths);
// we'll never do additional processing if this is the guaranteed last birth (eg birth #4)
if (!laborStateIsNull && laborStateMap.TryGetValue(__instance.pawn.ThingID).birthCount == 4)
{
return;
}
// For now, littered birth overrides twin calculations, so if a LaborState already exists
// with littered births gene, move on
if (!laborStateIsNull && hasLitteredBirthsGene)
{
if (LBTSettings.devMode)
{
LBTLogger.MessageGroupHead("Found active LaborState and LitteredBirths gene - skipping additional Hediff_Labor_PostRemovedPostFix work");
LBTLogger.MessageGroupBody("Pawn: " + __instance.pawn.NameShortColored + " (" + __instance.pawn.ThingID + ")");
LBTLogger.MessageGroupFoot("birthCount: " + laborStateMap.TryGetValue(__instance.pawn.ThingID).birthCount);
}
return;
}
// Make a new LaborState for the null case with littered births
if (laborStateIsNull && hasLitteredBirthsGene)
{
LBTLogger.Message("Found littered births gene");
int litteredBirthsTotalRoll = Rand.RangeInclusive(2, 4);
laborStateMap.SetOrAdd(__instance.pawn.ThingID, new LaborState(__instance.pawn, litteredBirthsTotalRoll));
return;
}
// Finally, regardless of littered births gene, we only want new state creation on
// pawns that don't already have state, so return if state is !null (STATE SHOULD ALWAYS BE CLEANED IN LABORPUSHING POSTFIX)
if (!laborStateIsNull)
{
if (LBTSettings.devMode)
{
LBTLogger.Warning("Labor state for pawn " + __instance.pawn.NameShortColored + " (" + __instance.pawn.ThingID + ") is not null despite all checks passing for determining first instance of Hediff_Labor - this warning should never occur, and may indicate a bug in Hediff_LaborPushing of lingering labor state from a previous pregnancy");
}
return;
}
// For everything else, we do random twin handling
// -------
// If we fail a base chance twins roll, return without any additional processing and proceed with vanilla childbirth
// Notes on rolls:
// -> Chance with Littered Births gene: random between 2 and 4 (inclusive)
randomTwinsRoll = Rand.Chance(0.01f);
if (!randomTwinsRoll)
{
// We failed rolls - no additional processing, do vanilla single baby birth
if (LBTSettings.devMode)
{
LBTLogger.MessageGroupHead("Inside Hediff_Labor_PostRemovedPostFix random twins check fail");
LBTLogger.MessageGroupBody("Pawn: " + __instance.pawn.NameShortColored);
LBTLogger.MessageGroupBody("Random twins roll outcome: " + randomTwinsRoll);
}
return;
}
totalBirths = 2;
bool doTriplets = Rand.Chance(0.5f);
bool doQuadruplets = Rand.Chance(0.1f);
if (doTriplets) totalBirths = 3;
if (doTriplets && doQuadruplets) totalBirths = 4;
// Set new LaborState
laborStateMap.Add(__instance.pawn.ThingID, new LaborState(__instance.pawn, totalBirths));
}
static void Hediff_LaborPushing_PostRemovedPostFix(ref Hediff_LaborPushing __instance)
{
bool hasLitteredBirthsGene = __instance.pawn.genes.HasGene(LitterBiotech.GeneDefOf.LitteredBirths);
bool laborStateIsNull = !laborStateMap.ContainsKey(__instance.pawn.ThingID);
LaborState currentLaborState;
laborStateMap.TryGetValue(__instance.pawn.ThingID, out currentLaborState);
if (laborStateIsNull)
{
return;
}
if (currentLaborState.birthTotal == currentLaborState.birthCount)
{
laborStateMap.Remove(__instance.pawn.ThingID);
return;
}
((Hediff_Labor)__instance.pawn.health.AddHediff(RimWorld.HediffDefOf.PregnancyLabor)).SetParents(__instance.pawn, __instance.Father, PregnancyUtility.GetInheritedGeneSet(__instance.Father, __instance.pawn));
currentLaborState.birthCount++;
if (!hasLitteredBirthsGene)
{
if (LBTSettings.devMode)
{
LBTLogger.Message("Pawn " + __instance.pawn.NameShortColored + " (" + __instance.pawn.ThingID + ") is having random twins");
}
Find.LetterStack.ReceiveLetter("Twins!", __instance.pawn.NameShortColored + " is still in labor and is having twins!\n\nBe sure to gather your doctor and additional friends and family to ensure the other baby is also born healthy!", LitterBiotech.LetterDefOf.AnotherBaby, __instance.pawn);
return;
}
Find.LetterStack.ReceiveLetter("Another baby!", __instance.pawn.NameShortColored + " is still in labor and is having another baby!\n\nBe sure to gather your doctor and additional friends and family to ensure the next baby is also born healthy!", LitterBiotech.LetterDefOf.AnotherBaby, __instance.pawn);
}
}
}