using RimWorld; using rjw; using System.Collections.Generic; using System.Linq; using System.Text; using UnityEngine; using Verse; namespace RJW_Menstruation { public class Hediff_MultiplePregnancy : Hediff_BasePregnancy { protected Dictionary enzygoticSiblings; // 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.Has(Quirk.Breeder) || (pawn.relations?.DirectRelations?.Find(x => x.def.Equals(PawnRelationDefOf.Spouse) || x.def.Equals(PawnRelationDefOf.Fiance))) != null) return; if (pawn.Has(Quirk.ImpregnationFetish) || 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); } 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(); } public string GetBabyInfo() { if (babies.NullOrEmpty()) return "Null"; StringBuilder res = new StringBuilder(); IEnumerable babiesdistinct = babies.Distinct(new RaceComparer()); int iteration = 0; foreach (Pawn baby in babiesdistinct) { int num = babies.Where(x => x.def.Equals(baby.def)).Count(); if (iteration > 0) res.Append(", "); res.AppendFormat("{0} {1}", num, baby.def.label); iteration++; } res.AppendFormat(" {0}", Translations.Dialog_WombInfo02); return res.ToString(); } public string GetFatherInfo() { if (babies.NullOrEmpty()) return "Null"; StringBuilder res = new StringBuilder(); res.AppendFormat("{0}: ", Translations.Dialog_WombInfo03); if (!is_parent_known && Configurations.InfoDetail != Configurations.DetailLevel.All) return res.Append(Translations.Dialog_FatherUnknown).ToString(); IEnumerable babiesdistinct = babies.Distinct(new FatherComparer(pawn)); int iteration = 0; foreach (Pawn baby in babiesdistinct) { if (iteration > 0) res.Append(", "); res.Append(Utility.GetFather(baby, pawn)?.LabelShort ?? Translations.Dialog_FatherUnknown); iteration++; } return res.ToString(); } 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(); 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 }); 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(); 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; } protected void CopyBodyPartProperties(Hediff part, Hediff originalPart) { CompHediffBodyPart comp = part.TryGetComp(); CompHediffBodyPart originalComp = originalPart.TryGetComp(); if (comp != null && originalComp != null) { // the string properties should be the same between both pawns anyways, besides the name of the owner part.Severity = originalPart.Severity; comp.SizeBase = originalComp.SizeBase; comp.SizeOwner = originalComp.SizeOwner; comp.EffSize = originalComp.EffSize; comp.FluidAmmount = originalComp.FluidAmmount; comp.FluidModifier = originalComp.FluidModifier; } HediffComp_Menstruation originalMenstruationComp = originalPart.GetMenstruationCompFromVagina(); if (originalMenstruationComp != null) { part.GetMenstruationCompFromVagina()?.CopyCycleProperties(originalMenstruationComp); } HediffComp_Breast originalBreastComp = originalPart.GetBreastComp(); if (originalBreastComp != null) { part.GetBreastComp()?.CopyBreastProperties(originalBreastComp); } } protected void CopyBodyPartRecord(Pawn baby, Pawn original, BodyPartRecord babyBPR, BodyPartRecord originalBPR) { if (babyBPR == null || originalBPR == null) return; RemoveBabyParts(baby, Genital_Helper.get_PartsHediffList(baby, babyBPR)); foreach (Hediff originalPart in Genital_Helper.get_PartsHediffList(original, originalBPR)) { Hediff part = SexPartAdder.MakePart(originalPart.def, baby, babyBPR); CopyBodyPartProperties(part, originalPart); baby.health.AddHediff(part, babyBPR); } } // Baby is the sibling to be changed, original is the first of the set and the one to copy to the rest. public virtual void ProcessIdenticalSibling(Pawn baby, Pawn original) { // They'll be the same pawnkind, which lets us make a lot of useful assumptions // However, some RNG might still be involved in genital generation (e.g. futas), so the easiest method is to clear out and re-generate // A bit wasteful since Hediff_BasePregnancy.PostBirth already redid the genitals CopyBodyPartRecord(baby, original, Genital_Helper.get_genitalsBPR(baby), Genital_Helper.get_genitalsBPR(original)); CopyBodyPartRecord(baby, original, Genital_Helper.get_breastsBPR(baby), Genital_Helper.get_breastsBPR(original)); CopyBodyPartRecord(baby, original, Genital_Helper.get_uddersBPR(baby), Genital_Helper.get_uddersBPR(original)); CopyBodyPartRecord(baby, original, Genital_Helper.get_anusBPR(baby), Genital_Helper.get_anusBPR(original)); if (baby.IsHAR()) HARCompatibility.CopyHARPropertiesPostBirth(baby, original); } 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) ProcessIdenticalSibling(baby, original); } // From RJW's trait code protected List GetInheritableTraits(Pawn mother, Pawn father) { List traitpool = new List(); List momtraits = new List(); List poptraits = new List(); List traits_to_inherit = new List(); 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() { 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: BabyPawnKindDecider(mother, father), //fixedIdeo: mother.Ideo, forbidAnyTitle: true, forceNoBackstory: true, forcedEndogenes: PregnancyUtility.GetInheritedGenes(father, mother) ); int division = 1; Pawn firstbaby = null; int traitSeed = Rand.Int; List 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; if (division > 1) { if (i == 0) { firstbaby = baby; request.FixedGender = baby.gender; request.ForcedEndogenes = baby.genes?.Endogenes.Select(gene => gene.def).ToList(); if (baby.genes != null && ModsConfig.BiotechActive) { if (GeneUtility.SameHeritableXenotype(mother, father) && mother.genes.UniqueXenotype) { baby.genes.xenotypeName = mother.genes.xenotypeName; baby.genes.iconDef = mother.genes.iconDef; } XenotypeDef xenoTypeDef = BabyXenoTypeDecider(mother, father, out bool hybridBaby); if (xenoTypeDef != null) baby.genes.SetXenotypeDirect(xenoTypeDef); if(hybridBaby) { baby.genes.hybrid = true; baby.genes.xenotypeName = "Hybrid".Translate(); } } } else { enzygoticSiblings.Add(baby, firstbaby); if (baby.story != null) { baby.story.headType = firstbaby.story.headType; baby.story.hairDef = firstbaby.story.hairDef; baby.story.bodyType = firstbaby.story.bodyType; } 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 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; } /// /// Decide pawnkind from mother and father /// Come from RJW /// /// /// /// public PawnKindDef BabyPawnKindDecider(Pawn mother, Pawn father) { PawnKindDef motherKindDef = Utility.GetRacesPawnKind(mother); PawnKindDef fatherKindDef = Utility.GetRacesPawnKind(father); PawnKindDef spawn_kind_def = motherKindDef; int flag = 0; if (xxx.is_human(mother)) flag += 2; if (xxx.is_human(father)) flag += 1; //Mother - Father = Flag //Human - Human = 3 //Human - Animal = 2 //Animal - Human = 1 //Animal - Animal = 0 switch (flag) { case 3: if (!Rand.Chance(RJWPregnancySettings.humanlike_DNA_from_mother)) spawn_kind_def = fatherKindDef; break; case 2: if (RJWPregnancySettings.bestiality_DNA_inheritance == 0f) spawn_kind_def = fatherKindDef; else if (!Rand.Chance(RJWPregnancySettings.bestial_DNA_from_mother)) spawn_kind_def = fatherKindDef; break; case 1: if (RJWPregnancySettings.bestiality_DNA_inheritance == 1f) spawn_kind_def = fatherKindDef; else if (!Rand.Chance(RJWPregnancySettings.bestial_DNA_from_mother)) spawn_kind_def = fatherKindDef; break; case 0: if (!Rand.Chance(RJWPregnancySettings.bestial_DNA_from_mother)) spawn_kind_def = fatherKindDef; break; } bool IsAndroidmother = AndroidsCompatibility.IsAndroid(mother); bool IsAndroidfather = AndroidsCompatibility.IsAndroid(father); if (IsAndroidmother && !IsAndroidfather) { spawn_kind_def = fatherKindDef; } else if (!IsAndroidmother && IsAndroidfather) { spawn_kind_def = motherKindDef; } string MotherRaceName = ""; string FatherRaceName = ""; MotherRaceName = motherKindDef?.race?.defName; PawnKindDef non_hybrid_kind_def = spawn_kind_def; if (father != null) FatherRaceName = fatherKindDef?.race?.defName; if (FatherRaceName != "" && Configurations.UseHybridExtention) { spawn_kind_def = GetHybrid(father, mother); //Log.Message("pawnkind: " + spawn_kind_def?.defName); } if (MotherRaceName != FatherRaceName && FatherRaceName != "") { if (!Configurations.UseHybridExtention || spawn_kind_def == null) { spawn_kind_def = non_hybrid_kind_def; IEnumerable groups = DefDatabase.AllDefs.Where(x => !(x.hybridRaceParents.NullOrEmpty() || x.hybridChildKindDef.NullOrEmpty())); //ModLog.Message(" found custom RaceGroupDefs " + groups.Count()); foreach (RaceGroupDef t in groups) { if ((t.hybridRaceParents.Contains(MotherRaceName) && t.hybridRaceParents.Contains(FatherRaceName)) || (t.hybridRaceParents.Contains("Any") && (t.hybridRaceParents.Contains(MotherRaceName) || t.hybridRaceParents.Contains(FatherRaceName)))) { //ModLog.Message(" has hybridRaceParents"); if (t.hybridChildKindDef.Contains("MotherKindDef")) spawn_kind_def = motherKindDef; else if (t.hybridChildKindDef.Contains("FatherKindDef") && father != null) spawn_kind_def = fatherKindDef; else { //ModLog.Message(" trying hybridChildKindDef " + t.defName); List child_kind_def_list = new List(); child_kind_def_list.AddRange(DefDatabase.AllDefs.Where(x => t.hybridChildKindDef.Contains(x.defName))); //ModLog.Message(" found custom hybridChildKindDefs " + t.hybridChildKindDef.Count); if (!child_kind_def_list.NullOrEmpty()) spawn_kind_def = child_kind_def_list.RandomElement(); } } } } } else if (!Configurations.UseHybridExtention || spawn_kind_def == null) { spawn_kind_def = mother.RaceProps?.AnyPawnKind ?? motherKindDef; } if (spawn_kind_def.defName.Contains("Nymph")) { //child is nymph, try to find other PawnKindDef List spawn_kind_def_list = new List(); spawn_kind_def_list.AddRange(DefDatabase.AllDefs.Where(x => x.race == spawn_kind_def.race && !x.defName.Contains("Nymph"))); //no other PawnKindDef found try mother if (spawn_kind_def_list.NullOrEmpty()) spawn_kind_def_list.AddRange(DefDatabase.AllDefs.Where(x => x.race == motherKindDef.race && !x.defName.Contains("Nymph"))); //no other PawnKindDef found try father if (spawn_kind_def_list.NullOrEmpty() && father != null) spawn_kind_def_list.AddRange(DefDatabase.AllDefs.Where(x => x.race == fatherKindDef.race && !x.defName.Contains("Nymph"))); //no other PawnKindDef found fallback to generic colonist if (spawn_kind_def_list.NullOrEmpty()) spawn_kind_def = PawnKindDefOf.Colonist; if (!spawn_kind_def_list.NullOrEmpty()) spawn_kind_def = spawn_kind_def_list.RandomElement(); } return spawn_kind_def; } public XenotypeDef BabyXenoTypeDecider(Pawn mother, Pawn father, out bool hybrid) { hybrid = false; bool hybridMother = mother?.genes?.hybrid ?? false; bool hybridFather = father?.genes?.hybrid ?? false; if (hybridMother && hybridFather) { hybrid = true; return null; } XenotypeDef motherInheritableXenotype = mother?.genes?.Xenotype; XenotypeDef fatherInheritableXenotype = father?.genes?.Xenotype; if (!motherInheritableXenotype.inheritable) motherInheritableXenotype = null; if (!fatherInheritableXenotype.inheritable) fatherInheritableXenotype = null; // If they're the same (or both null) if (motherInheritableXenotype == fatherInheritableXenotype) { // Both null, but one's a hybrid if (motherInheritableXenotype == null && (hybridMother || hybridFather)) hybrid = true; return motherInheritableXenotype; } // If one is null and the other isn't if ((motherInheritableXenotype == null) != (fatherInheritableXenotype == null)) return motherInheritableXenotype ?? fatherInheritableXenotype; // So two different inheritable ones hybrid = true; return null; } public PawnKindDef GetHybrid(Pawn first, Pawn second) { PawnKindDef res = null; Pawn opposite = second; HybridInformations info = null; if (!Configurations.HybridOverride.NullOrEmpty()) { info = Configurations.HybridOverride.FirstOrDefault(x => x.DefName == first.def?.defName && (x.hybridExtension?.Exists(y => y.DefName == second.def?.defName) ?? false)); if (info == null) { info = Configurations.HybridOverride.FirstOrDefault(x => x.DefName == second.def?.defName && (x.hybridExtension?.Exists(y => y.DefName == first.def?.defName) ?? false)); opposite = first; } } if (info != null) { res = info.GetHybridWith(opposite.def.defName) ?? null; } if (res != null) return res; PawnDNAModExtension dna; dna = first.def.GetModExtension(); if (dna != null) { res = dna.GetHybridWith(second.def.defName) ?? null; } else { dna = second.def.GetModExtension(); if (dna != null) { res = dna.GetHybridWith(first.def.defName) ?? null; } } return res; } /// /// Copy from RJW /// /// /// /// public void UpdateTraits(Pawn pawn, List parentTraits) { if (pawn?.story?.traits == null) { return; } int traitLimit = pawn.story.traits.allTraits.Count; //Personal pool List personalTraitPool = new List(pawn.story.traits.allTraits); //Parents List parentTraitPool = new List(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 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 selectedTraits = new List(); 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 void Initialize(Pawn mother, Pawn dad) { enzygoticSiblings = new Dictionary(); base.Initialize(mother, dad); } public string DueDate() { if (pawn.Tile == -1) return ""; return GenDate.DateFullStringWithHourAt(GenDate.TickGameToAbs((int)p_end_tick), Find.WorldGrid.LongLatOf(pawn.Tile)); } public override bool TryMergeWith(Hediff other) { return false; } } /// /// Copy from RJW /// public class TraitComparer : IEqualityComparer { 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 { 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 { 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(); } } }