842 lines
35 KiB
C#
842 lines
35 KiB
C#
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<Pawn, Pawn> 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<Pawn> 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<Pawn> 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<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 });
|
|
|
|
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;
|
|
}
|
|
|
|
protected 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);
|
|
}
|
|
}
|
|
|
|
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<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()
|
|
{
|
|
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<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;
|
|
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<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>
|
|
/// Decide pawnkind from mother and father <para/>
|
|
/// Come from RJW
|
|
/// </summary>
|
|
/// <param name="mother"></param>
|
|
/// <param name="father"></param>
|
|
/// <returns></returns>
|
|
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<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();
|
|
}
|
|
|
|
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<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;
|
|
}
|
|
|
|
/// <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 void Initialize(Pawn mother, Pawn dad)
|
|
{
|
|
enzygoticSiblings = new Dictionary<Pawn, Pawn>();
|
|
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;
|
|
}
|
|
}
|
|
|
|
/// <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();
|
|
}
|
|
}
|
|
|
|
}
|