321 lines
15 KiB
C#
321 lines
15 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 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<Pawn> 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<Pawn> 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();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Decide pawnkind from mother and father <para/>
|
|
/// Come from RJW
|
|
/// </summary>
|
|
/// <param name="mother"></param>
|
|
/// <param name="father"></param>
|
|
/// <returns></returns>
|
|
|
|
/// <param name="noAnimalsFromHumanlikes"></param>
|
|
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<RaceGroupDef> groups = DefDatabase<RaceGroupDef>.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<PawnKindDef> child_kind_def_list = new List<PawnKindDef>();
|
|
child_kind_def_list.AddRange(DefDatabase<PawnKindDef>.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<PawnKindDef> spawn_kind_def_list = new List<PawnKindDef>();
|
|
spawn_kind_def_list.AddRange(DefDatabase<PawnKindDef>.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<PawnKindDef>.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<PawnKindDef>.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<PawnDNAModExtension>();
|
|
if (dna != null)
|
|
{
|
|
res = dna.GetHybridWith(second.def.defName) ?? null;
|
|
}
|
|
else
|
|
{
|
|
dna = second.def.GetModExtension<PawnDNAModExtension>();
|
|
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>();
|
|
CompHediffBodyPart originalComp = originalPart.TryGetComp<CompHediffBodyPart>();
|
|
|
|
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));
|
|
}
|
|
}
|
|
}
|