2022-10-24 01:02:02 +00:00
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
{
2022-11-18 14:48:43 +00:00
protected Dictionary < Pawn , Pawn > enzygoticSiblings = new Dictionary < Pawn , Pawn > ( ) ; // Each pawn and who they split from
2022-10-24 01:02:02 +00:00
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 ( )
{
2022-10-27 20:57:25 +00:00
this . GetMenstruationCompFromPregnancy ( ) . Pregnancy = null ;
2022-10-24 01:02:02 +00:00
base . Miscarry ( ) ;
}
public override void GiveBirth ( )
{
if ( babies . NullOrEmpty ( ) )
{
ModLog . Warning ( " no babies (debug?) " + this . GetType ( ) . Name ) ;
if ( father = = null )
{
father = Trytogetfather ( ref pawn ) ;
}
2022-11-18 14:48:43 +00:00
Initialize ( pawn , father , SelectDnaGivingParent ( pawn , father ) ) ;
2022-10-24 01:02:02 +00:00
}
foreach ( Pawn baby in babies )
{
if ( xxx . is_animal ( baby ) )
{
BestialBirth ( baby ) ;
}
else
{
HumanlikeBirth ( baby ) ;
}
baby . ageTracker . AgeChronologicalTicks = 0 ;
}
pawn . health . RemoveHediff ( this ) ;
2022-10-27 20:57:25 +00:00
HediffComp_Menstruation comp = this . GetMenstruationCompFromPregnancy ( ) ;
2022-10-24 01:02:02 +00:00
if ( comp ! = null ) comp . Pregnancy = null ;
2022-10-29 15:56:55 +00:00
HediffComp_Breast breastcomp = pawn . GetBreastComp ( ) ;
if ( ModsConfig . BiotechActive & & xxx . is_human ( pawn ) & & breastcomp ! = null )
pawn . health . AddHediff ( HediffDefOf . Lactating ) ;
breastcomp ? . GaveBirth ( ) ;
2022-10-24 01:02:02 +00:00
}
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 } ) ;
2022-12-25 04:24:49 +00:00
if ( ModsConfig . BiotechActive )
{
// Ugly, but it'll have to do
OutcomeChance bestOutcome = RitualOutcomeEffectDefOf . ChildBirth . outcomeChances . Find ( chance = > chance . positivityIndex = = 1 ) ;
string label = bestOutcome . label ;
string description = bestOutcome . description . Formatted ( mother . Named ( "MOTHER" ) ) ;
ChoiceLetter_BabyBirth choiceLetter_BabyBirth = ( ChoiceLetter_BabyBirth ) LetterMaker . MakeLetter (
label , description , LetterDefOf . BabyBirth , baby
) ;
choiceLetter_BabyBirth . Start ( ) ;
Find . LetterStack . ReceiveLetter ( choiceLetter_BabyBirth ) ;
}
2022-10-24 01:02:02 +00:00
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 ;
}
2022-10-27 20:57:25 +00:00
HediffComp_Menstruation originalMenstruationComp = originalPart . GetMenstruationCompFromVagina ( ) ;
2022-10-24 01:02:02 +00:00
if ( originalMenstruationComp ! = null )
{
2022-10-27 20:57:25 +00:00
part . GetMenstruationCompFromVagina ( ) ? . CopyCycleProperties ( originalMenstruationComp ) ;
2022-10-24 01:02:02 +00:00
}
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 ) ) ;
}
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 ) ;
}
2022-11-18 14:48:43 +00:00
protected override void GenerateBabies ( DnaGivingParent _ )
2022-10-24 01:02:02 +00:00
{
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 ;
2022-11-17 03:19:18 +00:00
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 = "" ;
2022-10-24 01:02:02 +00:00
PawnGenerationRequest request = new PawnGenerationRequest (
2022-10-24 02:23:00 +00:00
developmentalStages : DevelopmentalStage . Newborn ,
2022-10-24 01:02:02 +00:00
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 ,
2022-10-24 23:53:54 +00:00
forceNoBackstory : true ,
2022-11-21 05:26:03 +00:00
forcedEndogenes : PregnancyUtility . GetInheritedGenes ( father , mother ) ,
forcedXenotype : ModsConfig . BiotechActive ? XenotypeDefOf . Baseliner : null
2022-10-24 01:02:02 +00:00
) ;
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 ;
2022-11-25 15:52:16 +00:00
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 ( ) ;
}
}
2022-10-24 01:02:02 +00:00
if ( division > 1 )
{
if ( i = = 0 )
2022-11-26 23:20:16 +00:00
{
2022-11-26 23:20:52 +00:00
if ( baby . IsHAR ( ) ) // Have HAR determine the first baby's properties
2022-11-26 23:20:16 +00:00
baby . Drawer . renderer . graphics . ResolveAllGraphics ( ) ;
2022-10-24 01:02:02 +00:00
firstbaby = baby ;
request . FixedGender = baby . gender ;
2022-11-25 15:52:16 +00:00
request . ForcedEndogenes = baby . genes ? . Endogenes . Select ( gene = > gene . def ) . ToList ( ) ;
2022-10-24 01:02:02 +00:00
}
else
{
2022-11-04 16:18:27 +00:00
enzygoticSiblings . Add ( baby , firstbaby ) ;
2022-10-24 01:02:02 +00:00
if ( baby . story ! = null )
{
2022-10-24 23:53:54 +00:00
baby . story . headType = firstbaby . story . headType ;
2022-10-24 01:02:02 +00:00
baby . story . hairDef = firstbaby . story . hairDef ;
baby . story . bodyType = firstbaby . story . bodyType ;
}
2022-11-04 15:59:39 +00:00
if ( baby . genes ! = null & & ModsConfig . BiotechActive )
2022-11-04 15:43:58 +00:00
{
baby . genes . SetXenotypeDirect ( firstbaby . genes . Xenotype ) ;
baby . genes . xenotypeName = firstbaby . genes . xenotypeName ;
baby . genes . iconDef = firstbaby . genes . iconDef ;
baby . genes . hybrid = firstbaby . genes . hybrid ;
}
2022-10-24 01:02:02 +00:00
if ( baby . IsHAR ( ) )
HARCompatibility . CopyHARProperties ( baby , firstbaby ) ;
2022-11-17 17:19:46 +00:00
if ( Configurations . AnimalGeneticsActivated )
AnimalGeneticsCompatibility . CopyGenes ( baby , firstbaby ) ;
2022-10-24 01:02:02 +00:00
}
}
babies . Add ( baby ) ;
}
return true ;
}
public Pawn GenerateBaby ( PawnGenerationRequest request , Pawn mother , Pawn father , List < Trait > parentTraits , int traitSeed )
{
2022-11-17 17:19:46 +00:00
if ( Configurations . AnimalGeneticsActivated ) AnimalGeneticsCompatibility . PreConception ( mother , father ) ;
2022-10-24 01:02:02 +00:00
Pawn baby = PawnGenerator . GeneratePawn ( request ) ;
2022-11-17 17:19:46 +00:00
if ( Configurations . AnimalGeneticsActivated ) AnimalGeneticsCompatibility . PostConception ( ) ;
2022-10-24 01:02:02 +00:00
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 ;
}
2022-11-04 15:43:58 +00:00
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 ;
2022-11-20 20:42:40 +00:00
if ( ! ( motherInheritableXenotype ? . inheritable ? ? false ) ) motherInheritableXenotype = null ;
if ( ! ( fatherInheritableXenotype ? . inheritable ? ? false ) ) fatherInheritableXenotype = null ;
2022-11-04 15:43:58 +00:00
// 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 ;
}
2022-10-24 01:02:02 +00:00
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 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 ( ) ;
}
}
}