2022-10-24 01:02:02 +00:00
using RimWorld ;
using rjw ;
2022-12-28 17:03:39 +00:00
using System ;
2022-10-24 01:02:02 +00:00
using System.Collections.Generic ;
using System.Linq ;
2022-12-28 17:03:39 +00:00
using System.Reflection ;
2022-10-24 01:02:02 +00:00
using System.Text ;
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 ) | |
2023-04-24 03:08:56 +00:00
pawn . HasQuirk ( QuirkUtility . Quirks . Breeder ) | | ( pawn . Ideo ? . HasPrecept ( VariousDefOf . Pregnancy_Required ) ? ? false ) | |
2022-10-24 01:02:02 +00:00
( pawn . relations ? . DirectRelations ? . Find ( x = > x . def . Equals ( PawnRelationDefOf . Spouse ) | |
x . def . Equals ( PawnRelationDefOf . Fiance ) ) ) ! = null )
return ;
2023-04-24 03:08:56 +00:00
if ( pawn . WantsToGetPregnant ( ) | | pawn . relations ? . DirectRelations ? . Find ( x = > x . def . Equals ( PawnRelationDefOf . Lover ) ) ! = null )
2022-10-24 01:02:02 +00:00
{
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
}
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
2023-01-07 19:50:37 +00:00
OutcomeChance bestOutcome = RitualOutcomeEffectDefOf . ChildBirth . BestOutcome ;
2022-12-25 04:24:49 +00:00
string label = bestOutcome . label ;
string description = bestOutcome . description . Formatted ( mother . Named ( "MOTHER" ) ) ;
2023-03-02 00:29:58 +00:00
baby . babyNamingDeadline = Find . TickManager . TicksGame + GenDate . TicksPerDay ;
2022-12-25 04:24:49 +00:00
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;
}
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 )
2023-01-08 04:18:17 +00:00
PregnancyCommon . ProcessIdenticalSibling ( baby , original ) ;
2022-10-24 01:02:02 +00:00
}
// 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 ,
2023-01-08 04:58:28 +00:00
kind : PregnancyCommon . BabyPawnKindDecider ( mother , father , false ) ,
2022-10-24 01:02:02 +00:00
//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 ;
2023-01-08 04:18:17 +00:00
PregnancyCommon . SetupBabyXenotype ( mother , father , baby ) ;
2023-01-10 16:20:22 +00:00
// HAR and some xenotype mods don't randomize graphics until it's rendered
// So poke it early
baby . Drawer . renderer . graphics . ResolveAllGraphics ( ) ;
2022-10-24 01:02:02 +00:00
if ( division > 1 )
{
if ( i = = 0 )
2023-01-10 13:46:16 +00:00
{
2023-01-10 16:20:22 +00:00
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 ;
2023-03-01 18:21:59 +00:00
baby . story . HairColor = firstbaby . story . HairColor ;
2022-10-24 01:02:02 +00:00
baby . story . bodyType = firstbaby . story . bodyType ;
2023-01-08 04:18:17 +00:00
baby . story . furDef = firstbaby . story . furDef ;
2023-01-10 13:46:16 +00:00
baby . story . skinColorOverride = firstbaby . story . skinColorOverride ;
2022-10-24 01:02:02 +00:00
}
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>
/// 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 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 ( ) ;
}
}
}