2023-06-05 13:51:15 +00:00
using HarmonyLib ;
using RimWorld ;
2024-06-04 11:08:37 +00:00
using RimWorld.BaseGen ;
2023-06-05 13:51:15 +00:00
using RimWorld.QuestGen ;
using rjw ;
using rjw.Modules.Shared.Extensions ;
using System ;
using System.Collections.Generic ;
using System.Linq ;
using System.Text ;
using System.Threading.Tasks ;
using Verse ;
2024-06-04 11:08:37 +00:00
2023-06-05 13:51:15 +00:00
namespace RJW_Genes
{
2023-06-18 19:02:31 +00:00
/// <summary>
/// There was a big change with RJW 5.3.6 and I got a new Issue #52 documenting it.
/// Basically, the reroll and orgasm logic was changed.
/// </summary>
2023-06-05 13:51:15 +00:00
2023-06-18 19:02:31 +00:00
[HarmonyPatch(typeof(JobDriver_Sex), "SetupOrgasmTicks")]
2023-06-05 13:51:15 +00:00
public static class Patch_OrgasmMytosis
{
2023-06-05 14:31:11 +00:00
private const float SEVERITY_INCREASE_PER_ORGASM = 0.075f ;
2023-06-05 13:51:15 +00:00
2024-06-04 11:08:37 +00:00
public static void Postfix ( JobDriver_Sex __instance )
2023-06-05 13:51:15 +00:00
{
Pawn orgasmingPawn = __instance . pawn ;
2024-06-04 11:08:37 +00:00
bool hasPollutedMytosis = false ;
2024-06-26 16:26:10 +00:00
if ( orgasmingPawn = = null | | orgasmingPawn . genes = = null ) { return ; }
if ( ( GeneUtility . HasGeneNullCheck ( orgasmingPawn , GeneDefOf . rjw_genes_sexual_mytosis ) | | hasPollutedMytosis ) & & ! orgasmingPawn . health . hediffSet . HasHediff ( HediffDefOf . rjw_genes_mytosis_shock_hediff ) )
2023-06-05 13:51:15 +00:00
{
var mytosisHediff = GetOrgasmMytosisHediff ( orgasmingPawn ) ;
mytosisHediff . Severity + = SEVERITY_INCREASE_PER_ORGASM ;
2024-06-04 11:08:37 +00:00
if ( hasPollutedMytosis & & orgasmingPawn . Spawned & & GridsUtility . IsPolluted ( orgasmingPawn . Position , orgasmingPawn . Map ) )
{
mytosisHediff . Severity - = SEVERITY_INCREASE_PER_ORGASM ;
}
2023-06-05 13:51:15 +00:00
if ( mytosisHediff . Severity > = 1.0 )
{
orgasmingPawn . health . RemoveHediff ( mytosisHediff ) ;
var copy = Multiply ( orgasmingPawn ) ;
ApplyMytosisShock ( copy ) ;
ApplyMytosisShock ( orgasmingPawn ) ;
orgasmingPawn . Strip ( ) ;
}
else
{
2023-06-05 14:31:11 +00:00
float orgasm_time_reduction = Math . Max ( 1.0f - mytosisHediff . Severity , 0.1f ) ;
2023-06-18 19:02:31 +00:00
__instance . sex_ticks = ( int ) ( __instance . sex_ticks * orgasm_time_reduction ) ;
2023-06-05 13:51:15 +00:00
}
}
}
private static void ApplyMytosisShock ( Pawn copy )
{
var stunA = HediffMaker . MakeHediff ( HediffDefOf . rjw_genes_mytosis_shock_hediff , copy ) ;
stunA . Severity = 1 ;
copy . health . AddHediff ( stunA ) ;
}
/// <summary>
/// Helps to get the Orgasm Mytosis Hediff of a Pawn. If it does not exist, one is added.
/// </summary>
/// <param name="orgasmed">The pawn that had the orgasm, for which a hediff is looked up or created.</param>
/// <returns></returns>
public static Hediff GetOrgasmMytosisHediff ( Pawn orgasmed )
{
Hediff orgasmicMytosisHediff = orgasmed . health . hediffSet . GetFirstHediffOfDef ( HediffDefOf . rjw_genes_orgasmic_mytosis_hediff ) ;
if ( orgasmicMytosisHediff = = null )
{
orgasmicMytosisHediff = HediffMaker . MakeHediff ( HediffDefOf . rjw_genes_orgasmic_mytosis_hediff , orgasmed ) ;
orgasmicMytosisHediff . Severity = 0 ;
orgasmed . health . AddHediff ( orgasmicMytosisHediff ) ;
}
return orgasmicMytosisHediff ;
}
public static Pawn Multiply ( Pawn toMultiply )
{
2023-06-05 14:31:11 +00:00
if ( RJW_Genes_Settings . rjw_genes_detailed_debug ) ModLog . Message ( "Hitting Multiply of Mytosis Pawn!" ) ;
2023-06-05 13:51:15 +00:00
PawnGenerationRequest request = new PawnGenerationRequest (
kind : toMultiply . kindDef ,
faction : toMultiply . Faction ,
forceGenerateNewPawn : true ,
developmentalStages : DevelopmentalStage . Adult ,
allowDowned : true ,
canGeneratePawnRelations : false ,
colonistRelationChanceFactor : 0 ,
allowFood : false ,
allowAddictions : false ,
relationWithExtraPawnChanceFactor : 0 ,
forbidAnyTitle : true ,
forceNoBackstory : true ,
fixedGender : toMultiply . gender
) ;
/ *
* Devnote : Adding these will lead to deadly issues !
fixedBiologicalAge : toMultiply . ageTracker . AgeBiologicalTicks ,
fixedChronologicalAge : toMultiply . ageTracker . AgeChronologicalTicks ,
* /
Pawn copy = PawnGenerator . GeneratePawn ( request ) ;
copy . gender = toMultiply . gender ;
copy . ageTracker = toMultiply . ageTracker ;
copy . Name = CreateCloneName ( toMultiply , 2 ) ;
copy . health = CopyRelevantHediffs ( copy , toMultiply ) ;
copy . genes = CopyGeneTracker ( copy , toMultiply . genes ) ;
copy . ideo = toMultiply . ideo ;
copy . records = new Pawn_RecordsTracker ( copy ) ;
copy . relations = toMultiply . relations ;
copy . skills = CopySkillTracker ( copy , toMultiply . skills ) ;
copy . equipment . DestroyAllEquipment ( ) ;
copy . apparel . DestroyAll ( ) ;
PawnUtility . TrySpawnHatchedOrBornPawn ( copy , toMultiply ) ;
// Move the copy in front of the origin, rather than on top
if ( toMultiply . Spawned )
if ( toMultiply . CurrentBed ( ) ! = null )
{
copy . Position = copy . Position + new IntVec3 ( 0 , 0 , 1 ) . RotatedBy ( toMultiply . CurrentBed ( ) . Rotation ) ;
}
2023-06-05 14:31:11 +00:00
// Birthmother doesn't show as relation (See log below)
// copy.relations.AddDirectRelation(PawnRelationDefOf.ParentBirth, toMultiply);
2023-06-05 13:51:15 +00:00
copy . style = CopyStyleTracker ( copy , toMultiply . style ) ;
copy . story = CopyStoryTracker ( copy , toMultiply . story ) ;
2024-06-26 16:26:10 +00:00
copy . genes . xenotypeName = toMultiply . genes . xenotypeName ;
copy . story . favoriteColor = toMultiply . story . favoriteColor ;
2024-05-23 22:33:37 +00:00
2024-06-05 09:50:25 +00:00
Find . LetterStack . ReceiveLetter ( "Orgasmic Mytosis" , $"{toMultiply.NameShortColored} performed mytosis on orgasm! The pawn and its clone entered a regenerative state." ,
RimWorld . LetterDefOf . NeutralEvent , copy ) ;
2023-06-05 13:51:15 +00:00
return copy ;
}
private static Name CreateCloneName ( Pawn toCopyFrom , int additions = 1 )
{
if ( toCopyFrom . Name is NameTriple )
{
NameTriple casted = ( NameTriple ) toCopyFrom . Name ;
String Postfix = " " + RandomNamePostFix ( additions ) ;
Name newName = new NameTriple ( first : casted . First + Postfix , nick : casted . Nick + Postfix , last : casted . Last ) ;
if ( newName . UsedThisGame )
return CreateCloneName ( toCopyFrom , additions ) ;
return newName ;
}
return toCopyFrom . Name ;
}
private static Pawn_GeneTracker CopyGeneTracker ( Pawn toCopyTo , Pawn_GeneTracker toCopyFrom )
{
var tracker = new Pawn_GeneTracker ( toCopyTo ) ;
// Due to Overwrite logics, we first add Endogenes and then a second pass on xenogenes
// Pass 1: Endogenes
foreach ( Gene gene in toCopyFrom . GenesListForReading ) {
GeneDef def = gene . def ;
if ( ! toCopyFrom . Xenogenes . Contains ( gene ) )
tracker . AddGene ( def , false ) ;
}
// Pass 2: Xenogenes
foreach ( Gene gene in toCopyFrom . GenesListForReading )
{
GeneDef def = gene . def ;
if ( toCopyFrom . Xenogenes . Contains ( gene ) )
tracker . AddGene ( def , true ) ;
}
tracker . Reset ( ) ;
return tracker ;
}
private static Pawn_StoryTracker CopyStoryTracker ( Pawn toCopyTo , Pawn_StoryTracker toCopyFrom )
{
var tracker = new Pawn_StoryTracker ( toCopyTo ) ;
tracker . Childhood = toCopyFrom . Childhood ;
tracker . Adulthood = toCopyFrom . Adulthood ;
tracker . headType = toCopyFrom . headType ;
tracker . bodyType = toCopyFrom . bodyType ;
tracker . hairDef = toCopyFrom . hairDef ;
tracker . furDef = toCopyFrom . furDef ;
tracker . traits = toCopyFrom . traits ;
tracker . skinColorOverride = toCopyFrom . skinColorOverride ;
tracker . HairColor = toCopyFrom . HairColor ;
return tracker ;
}
private static Pawn_SkillTracker CopySkillTracker ( Pawn toCopyTo , Pawn_SkillTracker toCopyFrom )
{
var tracker = new Pawn_SkillTracker ( toCopyTo ) ;
tracker . skills = toCopyFrom . skills ;
return tracker ;
}
private static Pawn_HealthTracker CopyRelevantHediffs ( Pawn toCopyTo , Pawn copiedFrom )
{
var toCopyFrom = copiedFrom . health ;
var tracker = toCopyTo . health ;
// Step 0: Remove everything, Reset
tracker . RemoveAllHediffs ( ) ;
tracker . Reset ( ) ;
// Step 1: Copy ALL Hediffs
foreach ( Hediff hed in toCopyFrom . hediffSet . hediffs )
{
// DevNote: There were a lot of issues around bodyparts:
// Some Hediffs really need to know their bodypart, e.g. an implanted arm can either be left or right.
// Ignoring this will lead to many errors, mostly around nullpointers.
BodyPartRecord originalBPR = hed . Part ;
if ( originalBPR ! = null ) {
BodyPartRecord copyBPR = toCopyTo . RaceProps ? . body . AllParts . Find ( bpr = > bpr . def = = originalBPR . def ) ;
if ( copyBPR ! = null & & ! copyBPR . IsMissingForPawn ( toCopyTo ) ) {
Hediff copiedHediff = HediffMaker . MakeHediff ( hed . def , toCopyTo , copyBPR ) ;
tracker . AddHediff ( copiedHediff ) ;
}
} else
{
Hediff copiedHediff = HediffMaker . MakeHediff ( hed . def , toCopyTo ) ;
tracker . AddHediff ( copiedHediff ) ;
}
}
// Step 2: Remove all Artifical Parts
List < Hediff > hediffsToRemove = new List < Hediff > ( ) ;
foreach ( Hediff hed in tracker . hediffSet . hediffs )
{
if ( hed is Hediff_AddedPart & & ( ( Hediff_AddedPart ) hed ) . def . countsAsAddedPartOrImplant )
{
hediffsToRemove . Add ( hed ) ;
}
}
tracker . hediffSet . hediffs . RemoveAll ( x = > hediffsToRemove . Contains ( x ) ) ;
// Step 3: Tend issues from Removal
foreach ( Hediff copiedHediff in tracker . hediffSet . hediffs )
{
if ( copiedHediff . Bleeding )
copiedHediff . Tended ( 1.0f , 1.0f ) ;
}
return tracker ;
}
private static Pawn_StyleTracker CopyStyleTracker ( Pawn toCopyTo , Pawn_StyleTracker toCopyFrom )
{
var tracker = new Pawn_StyleTracker ( toCopyTo ) ;
tracker . beardDef = toCopyFrom . beardDef ;
tracker . BodyTattoo = toCopyFrom . BodyTattoo ;
tracker . FaceTattoo = toCopyFrom . FaceTattoo ;
return tracker ;
}
private static String RandomNamePostFix ( int numberOfParts )
{
List < String > additions = new List < String > ( )
{
"A" , "B" , "C" , "D" , "E" , "F" , "X" , "Y" , "Z" ,
"Two" ,
"Alpha" , "Beta" , "Gamma" , "Delta" , "Epsilon" , "Zeta" , "Eta" , "Theta" , "Iota" , "Kappa" , "Lambda" , "Mu" , "Nu" , "Xi" , "Omicron" , "Pi" , "Rho" , "Sigma" , "Tau" , "Upsilon" , "Phi" , "Chi" , "Psi" , "Omega"
} ;
additions . Shuffle ( ) ;
return String . Join ( " " , additions . Take ( numberOfParts ) ) ;
}
}
}
/ *
*
* Warning :
* Tried to add pawn relation ParentBirth with self , pawn = Henri
UnityEngine . StackTraceUtility : ExtractStackTrace ( )
Verse . Log : Warning ( string )
RimWorld . Pawn_RelationsTracker : AddDirectRelation ( RimWorld . PawnRelationDef , Verse . Pawn )
RJW_Genes . Patch_OrgasmMytosis : Multiply ( Verse . Pawn )
RJW_Genes . Patch_OrgasmMytosis : Postfix ( rjw . JobDriver_Sex , int & )
( wrapper dynamic - method ) rjw . JobDriver_Sex : rjw . JobDriver_Sex . Roll_Orgasm_Duration_Reset_Patch1 ( rjw . JobDriver_Sex )
( wrapper dynamic - method ) rjw . JobDriver_Sex : rjw . JobDriver_Sex . Orgasm_Patch2 ( rjw . JobDriver_Sex )
( wrapper dynamic - method ) rjw . JobDriver_Sex : rjw . JobDriver_Sex . SexTick_Patch1 ( rjw . JobDriver_Sex , Verse . Pawn , Verse . Thing , bool , bool )
rjw . JobDriver_Rape / < > c__DisplayClass1_0 : < MakeNewToils > b__6 ( )
( wrapper dynamic - method ) Verse . AI . JobDriver : Verse . AI . JobDriver . DriverTick_Patch0 ( Verse . AI . JobDriver )
Verse . AI . Pawn_JobTracker : JobTrackerTick ( )
Verse . Pawn : Tick ( )
Verse . TickList : Tick ( )
( wrapper dynamic - method ) Verse . TickManager : Verse . TickManager . DoSingleTick_Patch2 ( Verse . TickManager )
Verse . TickManager : TickManagerUpdate ( )
Verse . Game : UpdatePlay ( )
Verse . Root_Play : Update ( )
* /