Adopted Breeding Genes by Shabalox

This commit is contained in:
Vegapnk 2022-11-20 20:53:05 +01:00
parent 95fc9b89a0
commit 21fc56fe2b
14 changed files with 264 additions and 3 deletions

View file

@ -0,0 +1,9 @@
using Verse;
namespace Genes.Breeding
{
internal class Gene_MechBreeder : Gene
{
// This one does not do anything, the patch is some where else checking for the pawn to have this Gene!
}
}

View file

@ -0,0 +1,64 @@

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using HarmonyLib;
using rjw;
namespace RJW_Genes
{
/// <summary>
/// This Class patches the RJW-Mechbirth to not deal damage when the pawn has the MechBreeder Gene.
/// This harmony patch was kindly provided by 'shabalox' https://github.com/Shabalox/RJW_Genes_Addons/
/// </summary>
[HarmonyPatch(typeof(Hediff_MechanoidPregnancy), "GiveBirth")]
public static class PatchMechBirth
{
[HarmonyTranspiler]
public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator il)
{
bool found_call = false;
bool found_skip = false;
Label skip_label = il.DefineLabel();
MethodInfo ismechbreeder = AccessTools.Method(typeof(GeneUtility), "IsMechbreeder");
foreach (CodeInstruction codeInstruction in instructions)
{
//Check if the first opcode after endfinally ldloc_0 is and in that case add the label to skip the code
if (found_skip && codeInstruction.opcode == OpCodes.Ldloc_0)
{
codeInstruction.labels.Add(skip_label);
}
found_skip = false;
if (codeInstruction.opcode == OpCodes.Endfinally)
{
found_skip = true;
}
yield return codeInstruction;
if (codeInstruction.opcode == OpCodes.Call)
{
if (codeInstruction.operand.ToString() == "Boolean TryMakeFilth(Verse.IntVec3, Verse.Map, Verse.ThingDef, System.String, Int32, RimWorld.FilthSourceFlags)")
{
found_call = true;
}
}
//Triggers after the pop opcode (after generating filth in c#).
else if (found_call)
{
//Load pawn, call function to check if a mechbreeder, and skip past the part which does damage
yield return new CodeInstruction(OpCodes.Ldloc_0, null);
yield return new CodeInstruction(OpCodes.Call, ismechbreeder);
yield return new CodeInstruction(OpCodes.Brtrue_S, skip_label);
found_call = false;
}
}
yield break;
}
}
}

View file

@ -0,0 +1,22 @@
using HarmonyLib;
using rjw;
using Verse;
namespace RJW_Genes
{
/// <summary>
/// Kindly provided by 'shabalox' https://github.com/Shabalox/RJW_Genes_Addons/
/// </summary>
[HarmonyPatch(typeof(PawnExtensions), "RaceImplantEggs")]
public static class PatchPawnExtensions
{
[HarmonyPostfix]
public static void Postfix(Pawn pawn, ref bool __result)
{
if (!__result)
{
__result = GeneUtility.isInsectBreeder(pawn);
}
}
}
}

View file

@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using System.Reflection.Emit;
using System.Reflection;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using HarmonyLib;
using Verse;
using RimWorld;
using rjw;
namespace RJW_Genes
{
/// <summary>
/// This Class patches the RJW-DoEgg to allow up to MaxEggSizeMul times the original amount of eggs.
/// This harmony patch was kindly provided by 'shabalox' https://github.com/Shabalox/RJW_Genes_Addons/
/// </summary>
[HarmonyPatch(typeof(PregnancyHelper), "DoEgg")]
static class PatchPregnancyHelper
{
[HarmonyTranspiler]
public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator il)
{
//MethodInfo isinsectincubator = AccessTools.Method(typeof(GeneUtility), "IsInsectIncubator");
MethodInfo maxeggsizemul = AccessTools.Method(typeof(GeneUtility), "MaxEggSizeMul");
FieldInfo partner = AccessTools.Field(typeof(SexProps), "partner");
Label skiplabel = il.DefineLabel();
bool finished = false;
foreach (CodeInstruction codeInstruction in instructions)
{
if (!finished)
{
if (codeInstruction.opcode == OpCodes.Ldc_R4 && codeInstruction.operand.ToString() == "0")
{
yield return new CodeInstruction(OpCodes.Ldarg_0, null);
yield return new CodeInstruction(OpCodes.Ldfld, partner);
//yield return new CodeInstruction(OpCodes.Call, isinsectincubator);
yield return new CodeInstruction(OpCodes.Callvirt, maxeggsizemul);
//yield return new CodeInstruction(OpCodes.Brfalse_S, skiplabel);
yield return new CodeInstruction(OpCodes.Ldloc_0, null);
//yield return new CodeInstruction(OpCodes.Ldc_R4, 2f);
yield return new CodeInstruction(OpCodes.Mul, null);
yield return new CodeInstruction(OpCodes.Stloc_0, null);
//codeInstruction.labels.Add(skiplabel);
finished = true;
}
}
yield return codeInstruction;
}
}
}
}

View file

@ -0,0 +1,44 @@
using Verse;
namespace RJW_Genes
{
public class GeneUtility
{
public static bool IsMechbreeder(Pawn pawn)
{
if (pawn.genes == null)
{
return false;
}
return pawn.genes.HasGene(GeneDefOf.rjw_genes_mechbreeder);
}
public static bool IsInsectIncubator(Pawn pawn)
{
if (pawn.genes == null)
{
return false;
}
return pawn.genes.HasGene(GeneDefOf.rjw_genes_insectincubator);
}
public static bool isInsectBreeder(Pawn pawn)
{
if (pawn.genes == null)
{
return false;
}
return pawn.genes.HasGene(GeneDefOf.rjw_genes_insectbreeder);
}
public static float MaxEggSizeMul(Pawn pawn)
{
float MaxEggSize = 1;
if (IsInsectIncubator(pawn))
{
MaxEggSize *= 2;
}
return MaxEggSize;
}
}
}

View file

@ -0,0 +1,128 @@
using RimWorld;
using Verse;
using rjw;
using System;
namespace RJW_Genes
{
public class GenitaliaUtility
{
/// <summary>
/// Returns the first (non) overwritten gene from the rjw_genes genitalia genes.
/// In case the pawn has none, as default the human one is returned.
/// </summary>
/// <param name="pawn">the pawn whom to find genitaliagenes for</param>
/// <returns>The first GeneDef of the pawn related to GenitaliaTypes</returns>
public static GeneDef GetGenitaliaTypeGeneForPawn(Pawn pawn)
{
foreach (var gene in pawn.genes.GenesListForReading)
{
if (gene.def.defName.Contains("rjw_genes") && gene.def.defName.EndsWith("_genitalia"))
if (!gene.Overridden)
return gene.def;
}
return GeneDefOf.rjw_genes_human_genitalia;
}
public static HediffDef GetPenisForGene(GeneDef gene)
{
switch (gene.defName)
{
case "rjw_genes_human_genitalia": return Genital_Helper.average_penis;
case "rjw_genes_equine_genitalia": return Genital_Helper.equine_penis;
case "rjw_genes_canine_genitalia": return Genital_Helper.canine_penis;
case "rjw_genes_feline_genitalia": return Genital_Helper.feline_penis;
case "rjw_genes_demonic_genitalia": return Genital_Helper.demon_penis;
case "rjw_genes_dragon_genitalia": return Genital_Helper.dragon_penis;
case "rjw_genes_slime_genitalia": return Genital_Helper.slime_penis;
case "rjw_genes_ovipositor_genitalia": return Genital_Helper.ovipositorM;
default: return Genital_Helper.average_penis;
}
}
public static HediffDef GetVaginaForGene(GeneDef gene)
{
switch (gene.defName)
{
case "rjw_genes_human_genitalia": return Genital_Helper.average_vagina;
case "rjw_genes_equine_genitalia": return Genital_Helper.equine_vagina;
case "rjw_genes_canine_genitalia": return Genital_Helper.canine_vagina;
case "rjw_genes_feline_genitalia": return Genital_Helper.feline_vagina;
case "rjw_genes_demonic_genitalia": return Genital_Helper.demon_vagina;
case "rjw_genes_dragon_genitalia": return Genital_Helper.dragon_vagina;
case "rjw_genes_slime_genitalia": return Genital_Helper.slime_vagina;
case "rjw_genes_ovipositor_genitalia": return Genital_Helper.ovipositorF;
default: return Genital_Helper.average_vagina;
}
}
public static HediffDef GetAnusForGene(GeneDef gene)
{
switch (gene.defName)
{
//TODO: Do I want the default to be generic or average for feline,equine and canine?
case "rjw_genes_human_genitalia": return Genital_Helper.average_anus;
case "rjw_genes_equine_genitalia": return Genital_Helper.average_anus;
case "rjw_genes_canine_genitalia": return Genital_Helper.average_anus;
case "rjw_genes_feline_genitalia": return Genital_Helper.average_anus;
case "rjw_genes_demonic_genitalia": return Genital_Helper.demon_anus;
case "rjw_genes_dragon_genitalia": return Genital_Helper.average_anus;
case "rjw_genes_slime_genitalia": return Genital_Helper.slime_anus;
case "rjw_genes_ovipositor_genitalia": return Genital_Helper.insect_anus;
default: return Genital_Helper.generic_anus;
}
}
public static HediffDef GetBreastsForGene(GeneDef gene)
{
switch (gene.defName)
{
//TODO: Do I want the default to be generic or average?
case "rjw_genes_human_genitalia": return Genital_Helper.average_breasts;
case "rjw_genes_equine_genitalia": return Genital_Helper.average_breasts;
case "rjw_genes_canine_genitalia": return Genital_Helper.average_breasts;
case "rjw_genes_feline_genitalia": return Genital_Helper.average_breasts;
case "rjw_genes_demonic_genitalia": return Genital_Helper.average_breasts;
case "rjw_genes_dragon_genitalia": return Genital_Helper.average_breasts;
case "rjw_genes_slime_genitalia": return Genital_Helper.slime_breasts;
case "rjw_genes_ovipositor_genitalia": return Genital_Helper.average_breasts;
default: return Genital_Helper.generic_breasts;
}
}
public static bool PawnStillNeedsGenitalia(Pawn pawn)
{
// There is the issue that the genes fire in a pseudo-random order
// Hence it can happen that the pawn still needs genitalia
// I wanted to make a simple lookup, but I think the genes are applied for all humans encountered so it could be huge
// So the heuristic is to check if the pawn has any of the 3 standard genitalia OR has all genes ticked that says "I don't want genitalia".
if (pawn == null) return false;
bool pawn_has_any_genitalia =
Genital_Helper.has_genitals(pawn) || Genital_Helper.has_anus(pawn) || Genital_Helper.has_breasts(pawn);
bool pawn_is_not_supposed_to_have_genitalia =
pawn.genes.GenesListForReading.Any(x => x.def.defName == "rjw_genes_no_penis");
if (pawn_is_not_supposed_to_have_genitalia)
return false;
else
return !pawn_has_any_genitalia;
}
public static bool IsBreasts(Hediff candidate)
{
return candidate.def.defName.ToLower().Contains("breast");
}
}
}