rjw_menstruation/1.4/source/RJW_Menstruation/RJW_Menstruation/Hediff_MultiplePregnancy.cs

543 lines
22 KiB
C#

using RimWorld;
using rjw;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using Verse;
namespace RJW_Menstruation
{
public class Hediff_MultiplePregnancy : Hediff_BasePregnancy
{
protected Dictionary<Pawn, Pawn> enzygoticSiblings = new Dictionary<Pawn, Pawn>(); // Each pawn and who they split from
public override void DiscoverPregnancy()
{
PregnancyThought();
base.DiscoverPregnancy();
}
protected void PregnancyThought()
{
if (is_discovered ||
!xxx.is_human(pawn) ||
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;
if (pawn.WantsToGetPregnant() || pawn.relations?.DirectRelations?.Find(x => x.def.Equals(PawnRelationDefOf.Lover)) != null)
{
pawn.needs.mood.thoughts.memories.TryGainMemory(VariousDefOf.UnwantedPregnancyMild);
}
else
{
pawn.needs.mood.thoughts.memories.TryGainMemory(VariousDefOf.UnwantedPregnancy);
}
}
public override void Miscarry()
{
this.GetMenstruationCompFromPregnancy().Pregnancy = null;
base.Miscarry();
}
public override void GiveBirth()
{
if (babies.NullOrEmpty())
{
ModLog.Warning(" no babies (debug?) " + this.GetType().Name);
if (father == null)
{
father = Trytogetfather(ref pawn);
}
Initialize(pawn, father, SelectDnaGivingParent(pawn, father));
}
foreach (Pawn baby in babies)
{
if (xxx.is_animal(baby))
{
BestialBirth(baby);
}
else
{
HumanlikeBirth(baby);
}
baby.ageTracker.AgeChronologicalTicks = 0;
}
pawn.health.RemoveHediff(this);
HediffComp_Menstruation comp = this.GetMenstruationCompFromPregnancy();
if(comp != null) comp.Pregnancy = null;
HediffComp_Breast breastcomp = pawn.GetBreastComp();
if (ModsConfig.BiotechActive && xxx.is_human(pawn) && breastcomp != null)
pawn.health.AddHediff(HediffDefOf.Lactating);
breastcomp?.GaveBirth();
}
private void HumanlikeBirth(Pawn baby)
{
Pawn mother = pawn; Pawn father = Utility.GetFather(baby, pawn);
//backup melanin, LastName for when baby reset by other mod on spawn/backstorychange
//var skin_whiteness = baby.story.melanin;
//var last_name = baby.story.birthLastName;
PawnUtility.TrySpawnHatchedOrBornPawn(baby, mother);
Need_Sex sex_need = mother.needs?.TryGetNeed<Need_Sex>();
if (mother.Faction != null && !(mother.Faction?.IsPlayer ?? false) && sex_need != null)
{
sex_need.CurLevel = 1.0f;
}
if (mother.Faction != null)
{
if (mother.Faction != baby.Faction)
baby.SetFaction(mother.Faction);
}
if (mother.IsSlaveOfColony)
{
if (mother.SlaveFaction != null)
baby.SetFaction(mother.SlaveFaction);
else if (mother.HomeFaction != null)
baby.SetFaction(mother.HomeFaction);
else if (mother.Faction != null)
baby.SetFaction(mother.Faction);
else
baby.SetFaction(Faction.OfPlayer);
baby.guest.SetGuestStatus(Faction.OfPlayer, GuestStatus.Slave);
}
else if (mother.IsPrisonerOfColony)
{
if (mother.HomeFaction != null)
baby.SetFaction(mother.HomeFaction);
baby.guest.SetGuestStatus(Faction.OfPlayer, GuestStatus.Prisoner);
}
if (xxx.is_human(mother)) TaleRecorder.RecordTale(TaleDefOf.GaveBirth, new object[] { mother, baby });
if (ModsConfig.BiotechActive)
{
// Ugly, but it'll have to do
OutcomeChance bestOutcome = RitualOutcomeEffectDefOf.ChildBirth.BestOutcome;
string label = bestOutcome.label;
string description = bestOutcome.description.Formatted(mother.Named("MOTHER"));
baby.babyNamingDeadline = Find.TickManager.TicksGame + GenDate.TicksPerDay;
ChoiceLetter_BabyBirth choiceLetter_BabyBirth = (ChoiceLetter_BabyBirth)LetterMaker.MakeLetter(
label, description, LetterDefOf.BabyBirth, baby
);
choiceLetter_BabyBirth.Start();
Find.LetterStack.ReceiveLetter(choiceLetter_BabyBirth);
}
PostBirth(mother, father, baby);
}
private void BestialBirth(Pawn baby)
{
Pawn mother = pawn; Pawn father = Utility.GetFather(baby, pawn);
//backup melanin, LastName for when baby reset by other mod on spawn/backstorychange
//var skin_whiteness = baby.story.melanin;
//var last_name = baby.story.birthLastName;
PawnUtility.TrySpawnHatchedOrBornPawn(baby, mother);
Need_Sex sex_need = mother.needs?.TryGetNeed<Need_Sex>();
if (mother.Faction != null && !(mother.Faction?.IsPlayer ?? false) && sex_need != null)
{
sex_need.CurLevel = 1.0f;
}
if (mother.Faction != null)
{
if (mother.Faction != baby.Faction)
baby.SetFaction(mother.Faction);
}
Train(baby, mother);
PostBirth(mother, father, baby);
//restore melanin, LastName for when baby reset by other mod on spawn/backstorychange
//baby.story.melanin = skin_whiteness;
//baby.story.birthLastName = last_name;
}
public override void PostBirth(Pawn mother, Pawn father, Pawn baby)
{
base.PostBirth(mother, father, baby);
// Has to happen on birth since RJW redoes the genitals at birth
if (!enzygoticSiblings.NullOrEmpty() && enzygoticSiblings.TryGetValue(baby, out Pawn original) && baby != original)
PregnancyCommon.ProcessIdenticalSibling(baby, original);
}
// From RJW's trait code
protected List<Trait> GetInheritableTraits(Pawn mother, Pawn father)
{
List<Trait> traitpool = new List<Trait>();
List<Trait> momtraits = new List<Trait>();
List<Trait> poptraits = new List<Trait>();
List<Trait> traits_to_inherit = new List<Trait>();
float max_num_momtraits_inherited = RJWPregnancySettings.max_num_momtraits_inherited;
float max_num_poptraits_inherited = RJWPregnancySettings.max_num_poptraits_inherited;
float max_num_traits_inherited = max_num_momtraits_inherited + max_num_poptraits_inherited;
int i = 1;
int j = 1;
if (xxx.has_traits(mother) && mother.RaceProps.Humanlike)
{
foreach (Trait momtrait in mother.story.traits.allTraits)
{
if (!non_genetic_traits.Contains(momtrait.def.defName) && !momtrait.ScenForced)
momtraits.Add(momtrait);
}
}
if (father != null && xxx.has_traits(father) && father.RaceProps.Humanlike)
{
foreach (Trait poptrait in father.story.traits.allTraits)
{
if (!non_genetic_traits.Contains(poptrait.def.defName) && !poptrait.ScenForced)
poptraits.Add(poptrait);
}
}
int rand_trait_index;
if (!momtraits.NullOrEmpty())
{
i = 1;
while (momtraits.Count > 0 && i <= max_num_momtraits_inherited)
{
rand_trait_index = Rand.Range(0, momtraits.Count);
traits_to_inherit.Add(momtraits[rand_trait_index]);
momtraits.RemoveAt(rand_trait_index);
}
}
if (!poptraits.NullOrEmpty())
{
j = 1;
while (poptraits.Count > 0 && j <= max_num_poptraits_inherited)
{
rand_trait_index = Rand.Range(0, poptraits.Count);
traits_to_inherit.Add(poptraits[rand_trait_index]);
poptraits.RemoveAt(rand_trait_index);
}
}
if (poptraits.NullOrEmpty() || momtraits.NullOrEmpty())
{
foreach (Trait traits in traits_to_inherit)
{
traitpool.Add(traits);
}
}
else
{
if (traits_to_inherit.Count() != max_num_traits_inherited)
{
if (momtraits.Count != 0 && i != max_num_momtraits_inherited)
{
while (poptraits != null && momtraits.Count() > 0 && i <= max_num_momtraits_inherited)
{
rand_trait_index = Rand.Range(0, momtraits.Count);
if (!traits_to_inherit.Contains(momtraits[rand_trait_index]))
{
traits_to_inherit.Add(momtraits[rand_trait_index]);
}
momtraits.RemoveAt(rand_trait_index);
}
}
if (poptraits != null && poptraits.Count != 0 && j != max_num_poptraits_inherited)
{
while (poptraits.Count > 0 && i < max_num_poptraits_inherited)
{
rand_trait_index = Rand.Range(0, poptraits.Count);
if (!traits_to_inherit.Contains(poptraits[rand_trait_index]))
{
traits_to_inherit.Add(poptraits[rand_trait_index]);
}
poptraits.RemoveAt(rand_trait_index);
}
}
}
foreach (Trait traits in traits_to_inherit)
{
traitpool.Add(traits);
}
}
return traitpool;
}
public override void ExposeData()
{
base.ExposeData();
Scribe_Collections.Look(ref enzygoticSiblings, "enzygoticSiblings", keyLookMode: LookMode.Reference, valueLookMode: LookMode.Reference);
}
protected override void GenerateBabies(DnaGivingParent _)
{
AddNewBaby(pawn, father);
}
protected void Train(Pawn baby, Pawn mother)
{
if (xxx.is_human(baby) || baby.Faction != Faction.OfPlayer) return;
if (xxx.is_human(mother) && baby.Faction == Faction.OfPlayer && baby.training.CanAssignToTrain(TrainableDefOf.Obedience, out _).Accepted)
{
baby.training.Train(TrainableDefOf.Obedience, mother);
}
if (xxx.is_human(mother) && baby.Faction == Faction.OfPlayer && baby.training.CanAssignToTrain(TrainableDefOf.Tameness, out _).Accepted)
{
baby.training.Train(TrainableDefOf.Tameness, mother);
}
}
public bool AddNewBaby(Pawn mother, Pawn father)
{
string lastname;
if (xxx.is_human(mother)) lastname = NameTriple.FromString(mother.Name.ToStringFull).Last;
else if (xxx.is_human(father)) lastname = NameTriple.FromString(father.Name.ToStringFull).Last;
else lastname = "";
PawnGenerationRequest request = new PawnGenerationRequest(
developmentalStages: DevelopmentalStage.Newborn,
allowDowned: true,
faction: mother.Faction,
canGeneratePawnRelations: false,
forceGenerateNewPawn: true,
colonistRelationChanceFactor: 0,
allowFood: false,
allowAddictions: false,
relationWithExtraPawnChanceFactor: 0,
fixedLastName: lastname,
kind: PregnancyCommon.BabyPawnKindDecider(mother, father, false),
//fixedIdeo: mother.Ideo,
forbidAnyTitle: true,
forceNoBackstory: true,
forcedEndogenes: PregnancyUtility.GetInheritedGenes(father, mother),
forcedXenotype: ModsConfig.BiotechActive ? XenotypeDefOf.Baseliner : null
);
int division = 1;
Pawn firstbaby = null;
int traitSeed = Rand.Int;
List<Trait> parentTraits = GetInheritableTraits(mother, father);
while (Rand.Chance(Configurations.EnzygoticTwinsChance) && division < Configurations.MaxEnzygoticTwins) division++;
for (int i = 0; i < division; i++)
{
Pawn baby = GenerateBaby(request, mother, father, parentTraits, traitSeed);
if (baby == null) break;
PregnancyCommon.SetupBabyXenotype(mother, father, baby);
// HAR and some xenotype mods don't randomize graphics until it's rendered
// So poke it early
baby.Drawer.renderer.graphics.ResolveAllGraphics();
if (division > 1)
{
if (i == 0)
{
firstbaby = baby;
request.FixedGender = baby.gender;
request.ForcedEndogenes = baby.genes?.Endogenes.Select(gene => gene.def).ToList();
}
else
{
enzygoticSiblings.Add(baby, firstbaby);
if (baby.story != null)
{
baby.story.headType = firstbaby.story.headType;
baby.story.hairDef = firstbaby.story.hairDef;
baby.story.HairColor = firstbaby.story.HairColor;
baby.story.bodyType = firstbaby.story.bodyType;
baby.story.furDef = firstbaby.story.furDef;
baby.story.skinColorOverride = firstbaby.story.skinColorOverride;
}
if (baby.genes != null && ModsConfig.BiotechActive)
{
baby.genes.SetXenotypeDirect(firstbaby.genes.Xenotype);
baby.genes.xenotypeName = firstbaby.genes.xenotypeName;
baby.genes.iconDef = firstbaby.genes.iconDef;
baby.genes.hybrid = firstbaby.genes.hybrid;
}
if (baby.IsHAR())
HARCompatibility.CopyHARProperties(baby, firstbaby);
if (Configurations.AnimalGeneticsActivated)
AnimalGeneticsCompatibility.CopyGenes(baby, firstbaby);
}
}
babies.Add(baby);
}
return true;
}
public Pawn GenerateBaby(PawnGenerationRequest request, Pawn mother, Pawn father, List<Trait> parentTraits, int traitSeed)
{
if (Configurations.AnimalGeneticsActivated) AnimalGeneticsCompatibility.PreConception(mother, father);
Pawn baby = PawnGenerator.GeneratePawn(request);
if (Configurations.AnimalGeneticsActivated) AnimalGeneticsCompatibility.PostConception();
if (baby == null)
{
Log.Error("Baby not generated. Request: " + request.ToString());
return null;
}
if (xxx.is_human(baby) || (baby.relations != null && !RJWSettings.Disable_bestiality_pregnancy_relations))
{
baby.SetMother(mother);
if (mother != father)
{
if (father.gender != Gender.Female) baby.SetFather(father);
else baby.relations.AddDirectRelation(PawnRelationDefOf.Parent, father);
}
}
if (xxx.is_human(baby))
{
// Ensure the same inherited traits are chosen each run
// Has to happen right here so GeneratePawn up there still gets unique results
Rand.PushState(traitSeed); // With a seed just to make sure that fraternal twins *don't* get trait-duped
UpdateTraits(baby, parentTraits);
Rand.PopState();
}
return baby;
}
/// <summary>
/// Copy from RJW
/// </summary>
/// <param name="pawn"></param>
/// <param name="parentTraits"></param>
///
public void UpdateTraits(Pawn pawn, List<Trait> parentTraits)
{
if (pawn?.story?.traits == null)
{
return;
}
int traitLimit = pawn.story.traits.allTraits.Count;
//Personal pool
List<Trait> personalTraitPool = new List<Trait>(pawn.story.traits.allTraits);
//Parents
List<Trait> parentTraitPool = new List<Trait>(parentTraits);
parentTraitPool.RemoveAll(x => x.ScenForced);
int numberInherited;
if (parentTraitPool != null)
numberInherited = System.Math.Min(parentTraitPool.Count(), Rand.RangeInclusive(0, 2)); // Not 3; give a better chance for a natural trait to appear
else
numberInherited = 0;
//Game suggested traits.
IEnumerable<Trait> forcedTraits = personalTraitPool
.Where(x => x.ScenForced)
.Distinct(new TraitComparer(ignoreDegree: true)); // result can be a mess, because game allows this mess to be created in scenario editor
List<Trait> selectedTraits = new List<Trait>();
TraitComparer comparer = new TraitComparer(); // trait comparision implementation, because without game compares traits *by reference*, makeing them all unique.
selectedTraits.AddRange(forcedTraits); // enforcing scenario forced traits
for (int i = 0; i < numberInherited; i++) // add parent traits first
{
int index = Rand.Range(0, parentTraitPool.Count);
Trait trait = parentTraitPool[index];
parentTraitPool.RemoveAt(index);
if (!selectedTraits.Any(x => comparer.Equals(x, trait) ||
x.def.ConflictsWith(trait)))
selectedTraits.Add(new Trait(trait.def, trait.Degree, false));
}
while (selectedTraits.Count < traitLimit && personalTraitPool.Count > 0)
{
int index = Rand.Range(0, personalTraitPool.Count); // getting trait and removing from the pull
Trait trait = personalTraitPool[index];
personalTraitPool.RemoveAt(index);
if (!selectedTraits.Any(x => comparer.Equals(x, trait) || // skipping traits conflicting with already added
x.def.ConflictsWith(trait)))
selectedTraits.Add(new Trait(trait.def, trait.Degree, false));
}
pawn.story.traits.allTraits = selectedTraits;
}
public override bool TryMergeWith(Hediff other)
{
return false;
}
}
/// <summary>
/// Copy from RJW
/// </summary>
public class TraitComparer : IEqualityComparer<Trait>
{
readonly bool ignoreForced;
readonly bool ignoreDegree;
public TraitComparer(bool ignoreDegree = false, bool ignoreForced = true)
{
this.ignoreDegree = ignoreDegree;
this.ignoreForced = ignoreForced;
}
public bool Equals(Trait x, Trait y)
{
return
x.def == y.def &&
(ignoreDegree || (x.Degree == y.Degree)) &&
(ignoreForced || (x.ScenForced == y.ScenForced));
}
public int GetHashCode(Trait obj)
{
return
(obj.def.GetHashCode() << 5) +
(ignoreDegree ? 0 : obj.Degree) +
((ignoreForced || obj.ScenForced) ? 0 : 0x10);
}
}
public class RaceComparer : IEqualityComparer<Pawn>
{
public bool Equals(Pawn x, Pawn y)
{
return x.def.Equals(y.def);
}
public int GetHashCode(Pawn obj)
{
return obj.def.GetHashCode();
}
}
public class FatherComparer : IEqualityComparer<Pawn>
{
readonly Pawn mother;
public FatherComparer(Pawn mother)
{
this.mother = mother;
}
public bool Equals(Pawn x, Pawn y)
{
if (Utility.GetFather(x, mother) == null && Utility.GetFather(y, mother) == null) return true;
return Utility.GetFather(x, mother)?.Label.Equals(Utility.GetFather(y, mother)?.Label) ?? false;
}
public int GetHashCode(Pawn obj)
{
return obj.def.GetHashCode();
}
}
}