Compare commits

...

4 Commits

Author SHA1 Message Date
Vegapnk 0945754903 Beta Release Build 2023-06-05 16:53:16 +02:00
Vegapnk 8dc63c9308 Fallback Logic for non-parent eggs, closes #37 2023-06-05 16:52:55 +02:00
Vegapnk 7957f633c2 Some Tuning to mytosis 2023-06-05 16:31:11 +02:00
Vegapnk 04c27a1913 Drafted Orgasmic Mytosis Gene and Behaviour 2023-06-05 15:51:15 +02:00
10 changed files with 534 additions and 9 deletions

View File

@ -4,6 +4,8 @@
- Fix of icon-names (#36)
- Changes to the scenario (more building items, throne for start). Wealth is now at 12k, which is the same as crashlanded and lost tribe.
- Added the Orgasmic Mytosis Draft
- Many touches on the Halamyr Hive Logic and Fertilizitation (#37,#38)
**Changes:**
@ -15,6 +17,7 @@
- Custom background icons when Vanilla-Expanded-Framework is loaded
- Sexual Age Drainer & Youth Fountain now change age as configured in XML
- Draft for a Hive-Start Scenario
- Added Orgasmic Mytosis Gene: On Multiple Orgasms, spawn an identical copy of a pawn. Items and Implants are not copied.
**Internal:**

Binary file not shown.

View File

@ -67,4 +67,15 @@
<biostatMet>1</biostatMet>
</GeneDef>
<GeneDef ParentName="SpecialBase">
<defName>rjw_genes_sexual_mytosis</defName>
<label>Orgasmic Mytosis</label>
<!-- <geneClass>RJW_Genes.Gene_Aphrodisiac_Pheromones</geneClass> -->
<description>Carriers of this gene grow more unstable with ongoing multiple orgasms - climaxing in a process of mytosis. This will result in an (biologically) identical pawn and both twins are set in a regenerative state. Also, the pawn can have multiple orgasms: In a state of higher unstableness, they come quicker.</description>
<iconPath>UI/Icons/Genes/Gene_PsychicBonding</iconPath>
<displayOrderInCategory>5</displayOrderInCategory>
<biostatCpx>5</biostatCpx>
<biostatMet>-5</biostatMet>
</GeneDef>
</Defs>

View File

@ -0,0 +1,136 @@
<?xml version="1.0" encoding="utf-8" ?>
<Defs>
<HediffDef>
<defName>rjw_genes_orgasmic_mytosis_hediff</defName>
<hediffClass>HediffWithComps</hediffClass>
<label>Orgasmic Instability</label>
<description>Xenotypes with this Gene grow more unstable on orgasm - upon reaching critical level they will initate a process of mytosis.</description>
<defaultLabelColor>(240,200,110)</defaultLabelColor>
<scenarioCanAdd>false</scenarioCanAdd>
<maxSeverity>1.0</maxSeverity>
<isBad>false</isBad>
<tendable>false</tendable>
<comps>
<li Class="HediffCompProperties_SeverityPerDay">
<severityPerDay>-0.9</severityPerDay>
</li>
</comps>
<stages>
<li>
<label>stable</label>
<capMods>
<li>
<capacity>Moving</capacity>
<offset>0.05</offset>
</li>
<li>
<capacity>Consciousness</capacity>
<offset>0.08</offset>
</li>
</capMods>
</li>
<li>
<label>agitated</label>
<minSeverity>0.4</minSeverity>
<capMods>
<li>
<capacity>Moving</capacity>
<offset>0.15</offset>
</li>
<li>
<capacity>Consciousness</capacity>
<offset>0.15</offset>
</li>
</capMods>
</li>
<li>
<label>unstable</label>
<minSeverity>0.7</minSeverity>
<capMods>
<li>
<capacity>Moving</capacity>
<offset>-0.1</offset>
</li>
<li>
<capacity>Consciousness</capacity>
<offset>-0.1</offset>
</li>
<li>
<capacity>BloodPumping</capacity>
<offset>+0.2</offset>
</li>
</capMods>
</li>
<li>
<label>critical</label>
<minSeverity>0.9</minSeverity>
<capMods>
<li>
<capacity>Moving</capacity>
<offset>-0.25</offset>
</li>
<li>
<capacity>Consciousness</capacity>
<offset>-0.25</offset>
</li>
<li>
<capacity>BloodPumping</capacity>
<offset>+0.5</offset>
</li>
</capMods>
</li>
</stages>
</HediffDef>
<HediffDef>
<defName>rjw_genes_mytosis_shock_hediff</defName>
<hediffClass>HediffWithComps</hediffClass>
<label>Mytosis Shock</label>
<description>Recently underwent (successful) mytosis. As this is a taxing process, some time for regeneration is required. While regenerating, no new mytosis can be started.</description>
<defaultLabelColor>(240,200,110)</defaultLabelColor>
<scenarioCanAdd>false</scenarioCanAdd>
<maxSeverity>1.0</maxSeverity>
<isBad>true</isBad>
<tendable>false</tendable>
<comps>
<li Class="HediffCompProperties_SeverityPerDay">
<severityPerDay>-0.20</severityPerDay>
</li>
</comps>
<stages>
<li>
<label>wearing off</label>
<capMods>
<li>
<capacity>Moving</capacity>
<offset>-0.25</offset>
</li>
<li>
<capacity>Consciousness</capacity>
<offset>-0.25</offset>
</li>
</capMods>
</li>
<li>
<label>fresh</label>
<minSeverity>0.6</minSeverity>
<capMods>
<li>
<capacity>Moving</capacity>
<offset>-0.50</offset>
</li>
<li>
<capacity>Consciousness</capacity>
<offset>-0.7</offset>
</li>
</capMods>
</li>
</stages>
</HediffDef>
</Defs>

View File

@ -76,9 +76,10 @@ namespace RJW_Genes
public static readonly GeneDef rjw_genes_youth_fountain;
public static readonly GeneDef rjw_genes_sex_age_drain;
public static readonly GeneDef rjw_genes_aphrodisiac_pheromones;
public static readonly GeneDef rjw_genes_sexual_mytosis;
// LifeForce
public static readonly GeneDef rjw_genes_lifeforce;
// LifeForce
public static readonly GeneDef rjw_genes_lifeforce;
public static readonly GeneDef rjw_genes_pussyhealing;
public static readonly GeneDef rjw_genes_lifeforce_drain;
public static readonly GeneDef rjw_genes_cum_eater;

View File

@ -30,9 +30,10 @@ namespace RJW_Genes
/// </summary>
/// <param name="pawn">The pawn born, that maybe becomes a hive-xenotype.</param>
/// <param name="hasDroneParent">whether there was a drone parent involved</param>
public static void ManageHiveBirth(Pawn pawn, bool hasDroneParent = false)
public static void ManageHiveBirth(Pawn pawn, bool hasDroneParent = false, XenotypeDef fallbackQueenDef = null, XenotypeDef fallbackDroneDef = null)
{
XenotypeDef queenDef = TryFindParentQueenXenotype(pawn);
if (queenDef == null) queenDef = fallbackQueenDef;
HiveOffspringChanceDef hiveOffspringChanceDef = HiveUtility.LookupHiveInheritanceChances(queenDef);
// Case 1: Mother is Queen, Father is something else. Produce Worker.
@ -56,6 +57,7 @@ namespace RJW_Genes
else if (roll < hiveOffspringChanceDef.droneChance + hiveOffspringChanceDef.queenChance)
{
XenotypeDef droneDef = TryFindParentDroneXenotype(pawn);
if (droneDef == null) droneDef = fallbackDroneDef;
pawn.genes.SetXenotype(droneDef);
if (RJW_Genes_Settings.rjw_genes_detailed_debug) ModLog.Message($"{pawn} born as a new drone with xenotype {droneDef.defName} ({(hiveOffspringChanceDef.droneChance + hiveOffspringChanceDef.queenChance) * 100}% chance,rolled {roll}))");
}

View File

@ -23,9 +23,8 @@ namespace RJW_Genes
[HarmonyPostfix]
static void HandleHiveBasedInheritance(ref Thing __result)
static void HandleHiveBasedInheritance(ref Thing __result, ref Hediff_InsectEgg __instance)
{
// Check: Was the born thing a pawn?
if (__result == null || !(__result is Pawn))
{
@ -35,19 +34,70 @@ namespace RJW_Genes
Pawn pawn = (Pawn)__result;
// Important: Not all pawns have mother/father. Some Pawns are born in Growth-Vats or born from mod.
bool hasQueenParent = HiveBirthLogic.TryFindParentQueenXenotype(pawn) != null;
bool hasDroneParent = HiveBirthLogic.TryFindParentDroneXenotype(pawn) != null;
XenotypeDef queenDef = HiveBirthLogic.TryFindParentQueenXenotype(pawn) ?? TryFindParentQueenXenotypeFromEgg(__instance);
XenotypeDef droneDef = HiveBirthLogic.TryFindParentDroneXenotype(pawn) ?? TryFindParentDroneXenotypeFromEgg(__instance);
bool hasQueenParent = queenDef != null;
bool hasDroneParent = droneDef != null;
if (hasQueenParent)
{
if (RJW_Genes_Settings.rjw_genes_detailed_debug) ModLog.Message($"PostFix Hediff_InsectEgg::ProcessHumanLikeInsectEgg - Checking Hive Inheritance because {pawn} has a queen parent.");
HiveBirthLogic.ManageHiveBirth(pawn, hasDroneParent);
HiveBirthLogic.ManageHiveBirth(pawn, hasDroneParent, fallbackQueenDef: queenDef, fallbackDroneDef: droneDef);
} else
{
if (RJW_Genes_Settings.rjw_genes_detailed_debug) ModLog.Message($"Ignoring Postfix Hediff_InsectEgg::ProcessHumanLikeInsectEgg - No Queen Parent - No Action.");
}
}
/// <summary>
/// Tries to retrieve a queen-xenotype-def from a given egg.
/// Checking priority goes: Implanter > Fertilizer > Null Otherwise.
///
/// This is meant to be a fallback to the parent-relations which were not present in RJW 5.3.1.
/// Some comments and thoughts are captured in Issue #37.
/// </summary>
/// <param name="egg">An Egg for which queens are looked up for</param>
/// <returns>The relevant xenotypedef of a queen, or null.</returns>
public static XenotypeDef TryFindParentQueenXenotypeFromEgg(Hediff_InsectEgg egg)
{
XenotypeDef queenDef = null;
if (egg == null)
return null;
if (egg.implanter != null)
queenDef = HiveUtility.TryGetQueenXenotype(egg.implanter);
if (queenDef == null && egg.father != null)
queenDef = HiveUtility.TryGetQueenXenotype(egg.implanter);
return queenDef;
}
/// <summary>
/// Tries to retrieve a drone-xenotype-def from a given egg.
/// Checking priority goes: Implanter > Fertilizer > Null Otherwise.
///
/// This is meant to be a fallback to the parent-relations which were not present in RJW 5.3.1.
/// Some comments and thoughts are captured in Issue #37.
/// </summary>
/// <param name="egg">An Egg for which drones are looked up for</param>
/// <returns>The relevant xenotypedef of a drone, or null.</returns>
public static XenotypeDef TryFindParentDroneXenotypeFromEgg(Hediff_InsectEgg egg)
{
XenotypeDef droneDef = null;
if (egg == null)
return null;
if (egg.implanter != null)
droneDef = HiveUtility.TryGetQueenXenotype(egg.implanter);
if (droneDef == null && egg.father != null)
droneDef = HiveUtility.TryGetQueenXenotype(egg.implanter);
return droneDef;
}
}
}

View File

@ -0,0 +1,318 @@
using HarmonyLib;
using RimWorld;
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;
namespace RJW_Genes
{
[HarmonyPatch(typeof(JobDriver_Sex), nameof(JobDriver_Sex.Roll_Orgasm_Duration_Reset))]
public static class Patch_OrgasmMytosis
{
private const float SEVERITY_INCREASE_PER_ORGASM = 0.075f;
public static void Postfix(JobDriver_Sex __instance, ref int __result)
{
Pawn orgasmingPawn = __instance.pawn;
if (orgasmingPawn != null && GeneUtility.HasGeneNullCheck(orgasmingPawn, GeneDefOf.rjw_genes_sexual_mytosis) && ! orgasmingPawn.health.hediffSet.HasHediff(HediffDefOf.rjw_genes_mytosis_shock_hediff))
{
var mytosisHediff = GetOrgasmMytosisHediff(orgasmingPawn);
mytosisHediff.Severity += SEVERITY_INCREASE_PER_ORGASM;
if (mytosisHediff.Severity >= 1.0)
{
orgasmingPawn.health.RemoveHediff(mytosisHediff);
var copy = Multiply(orgasmingPawn);
ApplyMytosisShock(copy);
ApplyMytosisShock(orgasmingPawn);
orgasmingPawn.Strip();
}
else
{
float orgasm_time_reduction = Math.Max(1.0f - mytosisHediff.Severity, 0.1f);
__result = (int)(orgasm_time_reduction * __result);
}
}
}
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)
{
if (RJW_Genes_Settings.rjw_genes_detailed_debug) ModLog.Message("Hitting Multiply of Mytosis Pawn!");
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.outfits = toMultiply.outfits;
copy.relations = toMultiply.relations;
copy.skills = CopySkillTracker(copy,toMultiply.skills);
copy.equipment.DestroyAllEquipment();
copy.apparel.DestroyAll();
//TODO: Make a letter on birth!
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);
}
// Birthmother doesn't show as relation (See log below)
// copy.relations.AddDirectRelation(PawnRelationDefOf.ParentBirth, toMultiply);
copy.style = CopyStyleTracker(copy, toMultiply.style);
copy.story = CopyStoryTracker(copy, toMultiply.story);
copy.Draw();
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();
var skin = tracker.GetMelaninGene();
var hair = tracker.GetHairColorGene();
//ModLog.Message($"{toCopyTo} had Skin {skin.defName} and {hair.defName} as colour-genes");
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 ()
*/

View File

@ -16,5 +16,8 @@ namespace RJW_Genes
public static readonly HediffDef rjw_genes_succubus_drained;
public static readonly HediffDef rjw_genes_orgasm_rush_hediff;
public static readonly HediffDef rjw_genes_fertilin_craving;
public static readonly HediffDef rjw_genes_orgasmic_mytosis_hediff;
public static readonly HediffDef rjw_genes_mytosis_shock_hediff;
}
}

View File

@ -141,6 +141,7 @@
<Compile Include="Genes\Patch_AddNotifyOnGeneration.cs" />
<Compile Include="Genes\Special\AgeTransferExtension.cs" />
<Compile Include="Genes\Special\Patch_AgeDrain.cs" />
<Compile Include="Genes\Special\Patch_OrgasmMytosis.cs" />
<Compile Include="Interactions\SuccubusTailjob\CompAbility_SexInteractionRequirements.cs" />
<Compile Include="Genes\Life_Force\Abilities\CompAbilityEffect_PussyHeal.cs" />
<Compile Include="Genes\Life_Force\Abilities\CompProperties_AbilityLifeForceCost.cs" />