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 static class PregnancyCommon { private static readonly MethodInfo TryGetInheritedXenotype = typeof(PregnancyUtility).GetMethod("TryGetInheritedXenotype", BindingFlags.Static | BindingFlags.NonPublic, null, new Type[] { typeof(Pawn), typeof(Pawn), typeof(XenotypeDef).MakeByRefType() }, null); private static readonly MethodInfo ShouldByHybrid = typeof(PregnancyUtility).GetMethod("ShouldByHybrid", BindingFlags.Static | BindingFlags.NonPublic, null, new Type[] { typeof(Pawn), typeof(Pawn) }, null); public static string GetBabyInfo(IEnumerable babies) { if (babies == null || !babies.Any()) return "Null"; StringBuilder res = new StringBuilder(); int iteration = 0; foreach (Pawn baby in babies.Distinct(new RaceComparer())) { 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 static string GetFatherInfo(IEnumerable babies, Pawn mother, bool is_parent_known) { if (babies == null || !babies.Any()) 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(); int iteration = 0; foreach (Pawn baby in babies.Distinct(new FatherComparer(mother))) { if (iteration > 0) res.Append(", "); res.Append(Utility.GetFather(baby, mother)?.LabelShort ?? Translations.Dialog_FatherUnknown); iteration++; } return res.ToString(); } /// /// Decide pawnkind from mother and father /// Come from RJW /// /// /// /// /// public static PawnKindDef BabyPawnKindDecider(Pawn mother, Pawn father, bool noAnimalsFromHumanlikes) { 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(); } // If both parents are humanlike, Biotech will attempt to assign genes to the child // Normally not a problem, but with the hybrid system, two humanlikes might produce an animal // So override it and force the child to be human if (noAnimalsFromHumanlikes && mother.genes != null && father?.genes != null && !spawn_kind_def.race.race.Humanlike) spawn_kind_def = Rand.Chance(RJWPregnancySettings.humanlike_DNA_from_mother) ? motherKindDef : fatherKindDef; return spawn_kind_def; } public static void SetupBabyXenotype(Pawn mother, Pawn father, Pawn baby) { if (baby.genes == null || !ModsConfig.BiotechActive) return; if (GeneUtility.SameHeritableXenotype(mother, father) && mother.genes.UniqueXenotype) { baby.genes.xenotypeName = mother.genes.xenotypeName; baby.genes.iconDef = mother.genes.iconDef; } object[] args = new object[] { mother, father, null }; if ((bool)TryGetInheritedXenotype.Invoke(null, args)) { baby.genes.SetXenotypeDirect((XenotypeDef)args[2]); } else if ((bool)ShouldByHybrid.Invoke(null, new object[] { mother, father })) { baby.genes.hybrid = true; baby.genes.xenotypeName = "Hybrid".Translate(); } } public static 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; } private static 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); } } private static void CopyBodyPartRecord(Pawn baby, Pawn original, BodyPartRecord babyBPR, BodyPartRecord originalBPR) { if (babyBPR == null || originalBPR == null) return; Hediff_BasePregnancy.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 static 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)); } public static string DueDate(this Hediff_BasePregnancy preg) { if (preg.pawn.Tile == -1) return ""; return GenDate.DateFullStringWithHourAt(GenDate.TickGameToAbs((int)preg.p_end_tick), Find.WorldGrid.LongLatOf(preg.pawn.Tile)); } public static string DueDate(this Hediff_Pregnant preg) { if (preg.pawn.Tile == -1) return ""; int ticksRemaining = (int)((1f - preg.GestationProgress) * preg.pawn.RaceProps.gestationPeriodDays * GenDate.TicksPerDay); int dueTickAbs = GenTicks.TicksAbs + ticksRemaining; return GenDate.DateFullStringWithHourAt(dueTickAbs, Find.WorldGrid.LongLatOf(preg.pawn.Tile)); } } }