code:
/*
using System.Collections.Generic;
using Harmony;
using RimWorld;
using UnityEngine;
using Verse;

namespace rjw
{
	public static class Building_Bed_Patch
	{
		[HarmonyPatch(typeof(Building_Bed))]
		[HarmonyPatch("ForPrisoners", PropertyMethod.Setter)]
		public static class ForPrisoners
		{
			[HarmonyPostfix]
			public static void Postfix(Building_Bed __instance)
			{
				if (!xxx.config.whore_beds_enabled) return;

				if (!__instance.ForPrisoners) return;

				if (__instance is Building_WhoreBed)
				{
					xxx.Swap(ref __instance);
				}
			}
		}

		[HarmonyPatch(typeof(Building_Bed), "GetGizmos")]
		public static class GetGizmos
		{
			[HarmonyPostfix]
			public static void Postfix(Building_Bed __instance, ref IEnumerable<Gizmo> __result)
			{
				__result = Process(__instance, __result);
			}

			private static IEnumerable<Gizmo> Process(Building_Bed __instance, IEnumerable<Gizmo> __result)
			{
				foreach (var gizmo in __result)
				{
					yield return gizmo;
				}
				if (__instance!=null && xxx.config.whore_beds_enabled && !__instance.ForPrisoners && !__instance.Medical && __instance.def.building.bed_humanlike)
				{
					//--Log.Message("[RJW]Building_Bed_Patch::Process - before new Command_Toggle is called");
					yield return
						new Command_Toggle
						{
							defaultLabel = "CommandBedSetAsWhoreLabel".Translate(),
							defaultDesc = "CommandBedSetAsWhoreDesc".Translate(),
							icon = ContentFinder<Texture2D>.Get("UI/Commands/AsWhore"),
							isActive = () => __instance is Building_WhoreBed,
							toggleAction = () => xxx.Swap(ref __instance),
							hotKey = KeyBindingDefOf.Misc4
						};
				}
			}
		}
	}
}
*/


code:
using System.Reflection;
using Harmony;
using RimWorld;
using Verse;

namespace rjw
{
	/// <summary>
	/// Conditional patching class that only does patching if CnP is active
	/// </summary>
	public class CnPcompatibility
	{
		//CnP (or BnC) try to address the joy need of a child, prisoners don't have that.
		//Considering how you can arrest children without rjw, it is more of a bug of that mod than ours.
		[HarmonyPatch(typeof(Pawn_NeedsTracker), "BindDirectNeedFields")]
		static class jfpk
		{
			[HarmonyPostfix]
			static void postfix(ref Pawn_NeedsTracker __instance)
			{
				Pawn_NeedsTracker tr = __instance;
				FieldInfo fieldInfo = AccessTools.Field(typeof(Pawn_NeedsTracker), "pawn");
				Pawn pawn = fieldInfo.GetValue(__instance) as Pawn;
				if (xxx.is_human(pawn) && pawn.ageTracker.CurLifeStageIndex < AgeStage.Teenager)
				{
					if (tr.TryGetNeed(NeedDefOf.Joy) == null)
					{
						MethodInfo method = AccessTools.Method(typeof(Pawn_NeedsTracker), "AddNeed");
						method.Invoke(tr, new object[] { NeedDefOf.Joy });
					}
				}
			}
		}

		//For whatever reason, harmony shits itself, when trying to patch non void private fields, below are variants the code that would work nicely, if it didn't
		//[HarmonyPatch]
		//[StaticConstructorOnStartup]
		//internal static class joy_for_prison_kids
		//{
		//    [HarmonyTargetMethod]
		//    static MethodBase CalculateMethod(HarmonyInstance instance)
		//    {
		//        var r = AccessTools.Method(typeof(Pawn_NeedsTracker), "ShouldHaveNeed");

		//        //Log.Message("Found method "+ r.ReturnType + ' ' + r.FullDescription());
		//        //Log.Message("Parameters " + r.GetType() );
		//        //Log.Message("return " + r.ReturnParameter);
		//        return AccessTools.Method(typeof(Pawn_NeedsTracker), "ShouldHaveNeed");
		//    }
		//    [HarmonyPrepare]
		//    static bool should_patch(HarmonyInstance instance)
		//    {
		//        Log.Clear();
		//        Log.Message("Will patch "+ xxx.RimWorldChildrenIsActive);
		//        return xxx.RimWorldChildrenIsActive;
		//    }

		//doesn't work, gets:  System.Reflection.TargetParameterCountException: parameters do not match signature
		//static void Postfix(HarmonyInstance instance)
		//{
		//    Log.Message("FOO");
		//}
		//[HarmonyPrefix]
		//static bool foo(NeedDef nd)
		//{
		//    Log.Message("Prerun ");
		//    return true;
		//}
		//[HarmonyPostfix]
		//static void postfix(ref bool __result, ref Pawn_NeedsTracker __instance, NeedDef nd)
		//{
		//    if (nd != NeedDefOf.Joy) { return; }
		//    FieldInfo fieldInfo = AccessTools.Field(typeof(Pawn_NeedsTracker), "pawn");
		//    Pawn pawn = fieldInfo.GetValue(__instance) as Pawn;
		//    if (pawn.ageTracker.CurLifeStageIndex < AgeStage.Teenager)
		//    {
		//        __result = true;
		//    }
		//}
		//}
		/*
		public static void Patch(HarmonyInstance harmony)
		{
			if (!xxx.RimWorldChildrenIsActive) return;
			Doit(harmony);
		}
		private static void Doit(HarmonyInstance harmony)
		{
			var original = typeof(RimWorldChildren.ChildrenUtility).GetMethod("CanBreastfeed");
			var postfix = typeof(CnPcompatibility).GetMethod("CanBreastfeed");
			harmony.Patch(original, null, new HarmonyMethod(postfix));

			original = typeof(Building_Bed).GetMethod("get_AssigningCandidates");
			postfix = typeof(CnPcompatibility).GetMethod("BedCandidates");
			harmony.Patch(original, null, new HarmonyMethod(postfix));

			//doesn't work cannot reflect private class
			//original = typeof(RimWorldChildren.Hediff_UnhappyBaby).GetMethod("IsBabyUnhappy", BindingFlags.Static | BindingFlags.NonPublic);
			//Log.Message("original is nul " + (original == null));
			//var prefix = typeof(CnPcompatibility).GetMethod("IsBabyUnhappy");
			//harmony.Patch(original, new HarmonyMethod(prefix), null);
		}
		private static void CanBreastfeed(ref  bool __result, ref Pawn __instance)//Postfix
		{
			__result = __instance.health.hediffSet.HasHediff(HediffDef.Named("Lactating"));//I'm a simple man
		}
		private static void BedCandidates(ref IEnumerable<Pawn> __result, ref Building_Bed bed)
		{
			if (!RimWorldChildren.BedPatchMethods.IsCrib(bed)) return;
			__result = bed.Map.mapPawns.FreeColonists.Where(x => x.ageTracker.CurLifeStageIndex <= 2 && x.Faction == Faction.OfPlayer);//Basically do all the work second time but with a tweak
		}
		private static bool IsBabyUnhappy(ref bool __result, ref RimWorldChildren.Hediff_UnhappyBaby __instance)
		{
			var pawn = __instance.pawn;

			if ((pawn.needs?.joy.CurLevelPercentage??1) < 0.2f)
				__result = true;
			else
				__result =  false;
			return false;
		}
		*/
	}
}

repo_name: chippy/rjw

license: mit


code:
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Linq;
using Harmony;
using RimWorld;
using Verse;

namespace rjw
{
    // Add genitalia to pawns that have the comp.
	[HarmonyPatch(typeof(PawnGenerator), "GenerateTraits")]
	public static class PawnGenerator_GenitaliaPatch
	{
		[HarmonyPostfix]
		public static void AddGenitalia(ref Pawn pawn, ref PawnGenerationRequest request)
		{
			if (CompRJW.Comp(pawn) == null) return;
			if (CompRJW.Comp(pawn).orientation == Orientation.None)
			{
				CompRJW.Comp(pawn).Sexualize(pawn);
			}
		}
	}

	[StaticConstructorOnStartup]
	internal static class First
	{
		/*private static void show_bpr(String body_part_record_def_name)
		{
			var bpr = BodyDefOf.Human.AllParts.Find((BodyPartRecord can) => String.Equals(can.def.defName, body_part_record_def_name));
			--Log.Message(body_part_record_def_name + " BPR internals:");
			--Log.Message("  def: " + bpr.def.ToString());
			--Log.Message("  parts: " + bpr.parts.ToString());
			--Log.Message("  parts.count: " + bpr.parts.Count.ToString());
			--Log.Message("  height: " + bpr.height.ToString());
			--Log.Message("  depth: " + bpr.depth.ToString());
			--Log.Message("  coverage: " + bpr.coverage.ToString());
			--Log.Message("  groups: " + bpr.groups.ToString());
			--Log.Message("  groups.count: " + bpr.groups.Count.ToString());
			--Log.Message("  parent: " + bpr.parent.ToString());
			--Log.Message ("  fleshCoverage: " + bpr.fleshCoverage.ToString ());
			--Log.Message ("  absoluteCoverage: " + bpr.absoluteCoverage.ToString ());
			--Log.Message ("  absoluteFleshCoverage: " + bpr.absoluteFleshCoverage.ToString ());
		}*/

		//Children mod use same defname. but not has worker class. so overriding here.
		public static void inject_Reproduction()
		{
			PawnCapacityDef reproduction = DefDatabase<PawnCapacityDef>.GetNamed("Reproduction");
			reproduction.workerClass = typeof(PawnCapacityWorker_Fertility);
		}

		private static void inject_recipes()
		{
			//--Log.Message("[RJW] First::inject_recipes");
			// Inject the recipes to create the artificial privates into the crafting spot or machining bench.
			// BUT, also dynamically detect if EPOE is loaded and, if it is, inject the recipes into EPOE's
			// crafting benches instead.

			if (IsLoaded("Lord of the Rims - The Third Age"))
			{
				// no idea why it removes MakePegDick recipe
				Log.Warning("[RJW]The Third Age is installed. Unable to inject advanced recipes.");
				return;
			}

			//Vanilla benches
			var cra_spo = DefDatabase<ThingDef>.GetNamed("CraftingSpot");
			var mac_ben = DefDatabase<ThingDef>.GetNamed("TableMachining");
			var fab_ben = DefDatabase<ThingDef>.GetNamed("FabricationBench");
			var tai_ben = DefDatabase<ThingDef>.GetNamed("ElectricTailoringBench");

			//EPOE benches
			var bas_ben = DefDatabase<ThingDef>.GetNamed("TableBasicProsthetic", false);
			var sim_ben = DefDatabase<ThingDef>.GetNamed("TableSimpleProsthetic", false);
			var bio_ben = DefDatabase<ThingDef>.GetNamed("TableBionics", false);

			(bas_ben ?? cra_spo).AllRecipes.Add(DefDatabase<RecipeDef>.GetNamed("MakePegDick"));

			(sim_ben ?? mac_ben).AllRecipes.Add(DefDatabase<RecipeDef>.GetNamed("MakeHydraulicAnus"));
			(sim_ben ?? mac_ben).AllRecipes.Add(DefDatabase<RecipeDef>.GetNamed("MakeHydraulicBreasts"));
			(sim_ben ?? mac_ben).AllRecipes.Add(DefDatabase<RecipeDef>.GetNamed("MakeHydraulicPenis"));
			(sim_ben ?? mac_ben).AllRecipes.Add(DefDatabase<RecipeDef>.GetNamed("MakeHydraulicVagina"));

			(bio_ben ?? fab_ben).AllRecipes.Add(DefDatabase<RecipeDef>.GetNamed("MakeBionicAnus"));
			(bio_ben ?? fab_ben).AllRecipes.Add(DefDatabase<RecipeDef>.GetNamed("MakeBionicBreasts"));
			(bio_ben ?? fab_ben).AllRecipes.Add(DefDatabase<RecipeDef>.GetNamed("MakeBionicPenis"));
			(bio_ben ?? fab_ben).AllRecipes.Add(DefDatabase<RecipeDef>.GetNamed("MakeBionicVagina"));

			// Inject the bondage gear recipes into their appropriate benches
			if (xxx.config.bondage_gear_enabled)
			{
				tai_ben.AllRecipes.Add(DefDatabase<RecipeDef>.GetNamed("MakeHololock"));
				tai_ben.AllRecipes.Add(DefDatabase<RecipeDef>.GetNamed("MakeArmbinder"));
				tai_ben.AllRecipes.Add(DefDatabase<RecipeDef>.GetNamed("MakeChastityBelt"));
				tai_ben.AllRecipes.Add(DefDatabase<RecipeDef>.GetNamed("MakeGag"));
			}
		}

		/*private static void show_bs(Backstory bs)
		{
			--Log.Message("Backstory \"" + bs.Title + "\" internals:");
			--Log.Message("  identifier: " + bs.identifier);
			--Log.Message("  slot: " + bs.slot.ToString());
			--Log.Message("  Title: " + bs.Title);
			--Log.Message("  TitleShort: " + bs.TitleShort);
			--Log.Message("  baseDesc: " + bs.baseDesc);
			--Log.Message("  skillGains: " + ((bs.skillGains == null) ? "null" : bs.skillGains.ToString()));
			--Log.Message("  skillGainsResolved: " + ((bs.skillGainsResolved == null) ? "null" : bs.skillGainsResolved.ToString()));
			--Log.Message("  workDisables: " + bs.workDisables.ToString());
			--Log.Message("  requiredWorkTags: " + bs.requiredWorkTags.ToString());
			--Log.Message("  spawnCategories: " + bs.spawnCategories.ToString());
			--Log.Message("  bodyTypeGlobal: " + bs.bodyTypeGlobal.ToString());
			--Log.Message("  bodyTypeFemale: " + bs.bodyTypeFemale.ToString());
			--Log.Message("  bodyTypeMale: " + bs.bodyTypeMale.ToString());
			--Log.Message("  forcedTraits: " + ((bs.forcedTraits == null) ? "null" : bs.forcedTraits.ToString()));
			--Log.Message("  disallowedTraits: " + ((bs.disallowedTraits == null) ? "null" : bs.disallowedTraits.ToString()));
			--Log.Message("  shuffleable: " + bs.shuffleable.ToString());
		}*/

		//Quick check to see if an another mod is loaded.
		private static bool IsLoaded(string mod)
		{
			return LoadedModManager.RunningModsListForReading.Any(x => x.Name == mod);
		}

		private static void CheckingCompatibleMods()
		{
			//RomanceDiversified
			try
			{
				xxx.straight = DefDatabase<TraitDef>.GetNamedSilentFail("Straight");
				xxx.bisexual = DefDatabase<TraitDef>.GetNamedSilentFail("Bisexual");
				xxx.asexual = DefDatabase<TraitDef>.GetNamedSilentFail("Asexual");
				xxx.faithful = DefDatabase<TraitDef>.GetNamedSilentFail("Faithful");
				xxx.philanderer = DefDatabase<TraitDef>.GetNamedSilentFail("Philanderer");
				xxx.polyamorous = DefDatabase<TraitDef>.GetNamedSilentFail("Polyamorous");
				if (xxx.straight is null)
				{
					xxx.RomanceDiversifiedIsActive = false;
					if (Prefs.DevMode) Log.Message("[RJW]RomanceDiversified is not detected");
				}
				else
				{
					xxx.RomanceDiversifiedIsActive = true;
					if (Prefs.DevMode) Log.Message("[RJW]RomanceDiversified is detected.");
				}
			}
			catch (Exception)
			{
				xxx.RomanceDiversifiedIsActive = false;
				xxx.straight = null;
				xxx.bisexual = null;
				xxx.asexual = null;
				xxx.faithful = null;
				xxx.philanderer = null;
				xxx.polyamorous = null;
				if (Prefs.DevMode) Log.Message("[RJW]RomanceDiversified is not detected.error");
			}

			if (IsLoaded("[KV] Consolidated Traits - 1.0"))
			{
				Log.Message("[RJW]Consolidated Traits found, adding trait compatibility.");
				xxx.CTIsActive = true;
			}

			if (IsLoaded("Lord of the Rims - The Third Age"))
			{
				Log.Warning("[RJW]The Third Age detected. Partially incompatible, erases defs that RJW needs.");
			}

			//Rimworld of Magic - need this for the check to avoid undead pregnancies
			if (IsLoaded("A RimWorld of Magic"))
			{
				xxx.RoMIsActive = true;
				if (Prefs.DevMode) Log.Message("[RJW]Rimworld of Magic is detected.");
			}

			//[SYR] Individuality
			if (IsLoaded("[SYR] Individuality"))
			{
				xxx.IndividualityIsActive = true;
				if (Prefs.DevMode) Log.Message("[RJW]Individuality is detected.");
			}

			//Humanoid Alien Races Framework 2.0
			if (IsLoaded("Humanoid Alien Races 2.0"))
			{
				xxx.AlienFrameworkIsActive = true;
				xxx.xenophobia = DefDatabase<TraitDef>.GetNamedSilentFail("Xenophobia");
				if (Prefs.DevMode) Log.Message("[RJW]Humanoid Alien Races 2.0 is detected. Xenophile and Xenophobe traits active.");
			}

			//Psychology
			if (IsLoaded("Psychology"))
			{
				xxx.PsychologyIsActive = true;
				xxx.prude = DefDatabase<TraitDef>.GetNamedSilentFail("Prude");
				xxx.lecher = DefDatabase<TraitDef>.GetNamedSilentFail("Lecher");
				xxx.polygamous = DefDatabase<TraitDef>.GetNamedSilentFail("Polygamous");
				if (Prefs.DevMode) Log.Message("[RJW]Psychology is detected. (Note: only partially supported)");
			}
			else
			{
				xxx.PsychologyIsActive = false;
				//Message disabled. Don't want to encourage users to install Psychology while it's still not fully supported.
				//if (Prefs.DevMode) Log.Message("[RJW]Psychology is not detected");
			}

			//SimpleSlavery
			if (IsLoaded("Simple Slavery[1.0]"))
			{
				xxx.SimpleSlavery = true;
				if (Prefs.DevMode) Log.Message("[RJW]SimpleSlavery is detected.");
			}
			else
			{
				xxx.SimpleSlavery = false;
				//if (Prefs.DevMode) Log.Message("[RJW]SimpleSlavery is not detected");
			}

			//Children and Pregnancy
			try
			{
				//bool CNPActive = ModsConfig.ActiveModsInLoadOrder.FirstIndexOf(m => m.Name == "Children and Pregnancy");//Won't work, name is wrong
				xxx.babystate = DefDatabase<HediffDef>.GetNamedSilentFail("BabyState");
				if (xxx.babystate is null)
				{
					xxx.RimWorldChildrenIsActive = false;
					if (Prefs.DevMode) Log.Message("[RJW]Children&Pregnancy is not detected");
				}
				else
				{
					xxx.RimWorldChildrenIsActive = true;
					if (Prefs.DevMode) Log.Message("[RJW]Children&Pregnancy is detected.");
				}
			}
			catch (Exception)
			{
				xxx.RimWorldChildrenIsActive = false; //A dirty way to check if the mod is active
				xxx.babystate = null;
				if (Prefs.DevMode) Log.Message("[RJW]Children&Pregnancy is not detected.error");
			}

			//Combat Extended
			try
			{
				xxx.MuscleSpasms = DefDatabase<HediffDef>.GetNamedSilentFail("MuscleSpasms");
				if (xxx.MuscleSpasms is null)
				{
					xxx.CombatExtendedIsActive = false;
					// Combat Extended doesn't seem to work with RJW. Don't want to encourage users to install it.
					//if (Prefs.DevMode) Log.Message("[RJW]Combat Extended is not detected");
				}
				else
				{
					xxx.CombatExtendedIsActive = true;
					if (Prefs.DevMode) Log.Message("[RJW]Combat Extended is detected. Current compatibility unknown, use at your own risk. ");
				}
			}
			catch (Exception)
			{
				xxx.CombatExtendedIsActive = false;
				xxx.MuscleSpasms = null;
				if (Prefs.DevMode) Log.Message("[RJW]Combat Extended is not detected.error");
			}
		}

		static First()
		{
			//--Log.Message("[RJW] First::First() called");

			// check for required mods
			//CheckModRequirements();
			//CheckIncompatibleMods();
			CheckingCompatibleMods();

			inject_recipes();
			inject_Reproduction();

			xxx.init(); // Must only be called after injections are complete
			nymph_backstories.init();
			std.init();
			bondage_gear_tradeability.init();

			var har = HarmonyInstance.Create("rjw");
			har.PatchAll(Assembly.GetExecutingAssembly());
			PATCH_Pawn_ApparelTracker_TryDrop.apply(har);
			//CnPcompatibility.Patch(har);                           CnP IS NO OUT YET
		}

		internal static void CheckModRequirements()
		{
			//--Log.Message("First::CheckModRequirements() called");
			List<string> required_mods = new List<string> {
				"HugsLib",
			};
			foreach (string required_mod in required_mods)
			{
				bool found = false;
				foreach (ModMetaData installed_mod in ModLister.AllInstalledMods)
				{
					if (installed_mod.Active && installed_mod.Name.Contains(required_mod))
					{
						found = true;
					}

					if (!found)
					{
						ErrorMissingRequirement(required_mod);
					}
				}
			}
		}

		internal static void CheckIncompatibleMods()
		{
			//--Log.Message("First::CheckIncompatibleMods() called");
			List<string> incompatible_mods = new List<string> {
				"Bogus Test Mod That Doesn't Exist"
			};
			foreach (string incompatible_mod in incompatible_mods)
			{
				foreach (ModMetaData installed_mod in ModLister.AllInstalledMods)
				{
					if (installed_mod.Active && installed_mod.Name.Contains(incompatible_mod))
					{
						ErrorIncompatibleMod(installed_mod);
					}
				}
			}
		}

		internal static void ErrorMissingRequirement(string missing)
		{
			Log.Error("Initialization error:  Unable to find required mod '" + missing + "' in mod list");
		}

		internal static void ErrorIncompatibleMod(ModMetaData othermod)
		{
			Log.Error("Initialization Error:  Incompatible mod '" + othermod.Name + "' detected in mod list");
		}
	}
}


code:
using System.Collections.Generic;
using System.Linq;
using Harmony;
using RimWorld;
using Verse;
using UnityEngine;

namespace rjw
{
	/// <summary>
	/// Harmony patch to toggle the RJW designation box showing
	/// </summary>
	[HarmonyPatch(typeof(PlaySettings), "DoPlaySettingsGlobalControls")]
	[StaticConstructorOnStartup]
	public static class RJW_corner_toggle
	{
		static readonly Texture2D icon = ContentFinder<Texture2D>.Get("UI/Commands/ComfortPrisoner_off");

		[HarmonyPostfix]
		public static void adding_RJW_toggle(WidgetRow row, bool worldView)
		{
			if (worldView) return;
			row.ToggleableIcon(ref RJWSettings.show_RJW_designation_box, icon, "RJW_designation_box_desc".Translate());
		}
	}

	[HarmonyPatch(typeof(Pawn), "GetGizmos")]
	[StaticConstructorOnStartup]
	static class Rjw_buttons

	///<summary>
	///Adds compact button group containing all the rjw settings on pawn
	///</summary>
	{
		[HarmonyPostfix]
		static void this_is_postfix(ref IEnumerable<Gizmo> __result, ref Pawn __instance)
		{
			if (!RJWSettings.show_RJW_designation_box) return;
			//Log.Message("[rjw]Harmony patch submit_button is called");
			var pawn = __instance;
			var gizmos = __result.ToList();
			gizmos.Add(new RJWdesignations(pawn));
			__result = gizmos.AsEnumerable();
		}
	}
}


code:
using Harmony;
using RimWorld;
using UnityEngine;
using Verse;
using Verse.Sound;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;

namespace rjw
{
	[HarmonyPatch(typeof(CharacterCardUtility), "DrawCharacterCard")]
	public static class SexcardPatch
	{
		public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			MethodInfo HighlightOpportunity = AccessTools.Method(typeof(UIHighlighter), "HighlightOpportunity");
			MethodInfo SexualityCardToggle = AccessTools.Method(typeof(SexcardPatch), nameof(SexualityCardToggle));
			bool traits = false;
			bool found = false;
			foreach (CodeInstruction i in instructions)
			{
				if (found)
				{
					yield return new CodeInstruction(OpCodes.Ldarg_0) { labels = i.labels.ListFullCopy() };//rect
					yield return new CodeInstruction(OpCodes.Ldarg_1);//pawn
					yield return new CodeInstruction(OpCodes.Ldarg_3);//creationRect
					yield return new CodeInstruction(OpCodes.Call, SexualityCardToggle);
					found = false;
					i.labels.Clear();
				}
				if (i.opcode == OpCodes.Call && i.operand == HighlightOpportunity)
				{
					found = true;
				}
				if (i.opcode == OpCodes.Ldstr && i.operand.Equals("Traits"))
				{
					traits = true;
				}
				if (traits && i.opcode == OpCodes.Ldc_R4 && i.operand.Equals(2f))//replaces rect y calculation
				{
					yield return new CodeInstruction(OpCodes.Ldc_R4, 0f);
					traits = false;
					continue;
				}
				yield return i;
			}
		}

		public static void SexualityCardToggle(Rect rect, Pawn pawn, Rect creationRect)
		{
			if (pawn == null) return;

			Rect rectNew = new Rect(CharacterCardUtility.PawnCardSize.x - 50f, 2f, 24f, 24f);
			if (Current.ProgramState != ProgramState.Playing)
			{
				if (xxx.IndividualityIsActive) // Move icon lower to avoid overlap.
					rectNew = new Rect(creationRect.width - 24f, 56f, 24f, 24f);
				else
					rectNew = new Rect(creationRect.width - 24f, 30f, 24f, 24f);
			}
			Color old = GUI.color;

			GUI.color = rectNew.Contains(Event.current.mousePosition) ? new Color(0.25f, 0.59f, 0.75f) : new Color(1f, 1f, 1f);
			// TODO: Replace the placeholder icons with... something
			if (CompRJW.Comp(pawn).quirks.ToString() != "None")
				GUI.DrawTexture(rectNew, ContentFinder<Texture2D>.Get("UI/Commands/Service_on"));
			else
				GUI.DrawTexture(rectNew, ContentFinder<Texture2D>.Get("UI/Commands/Service_off"));

			TooltipHandler.TipRegion(rectNew, "SexcardTooltip".Translate());
			if (Widgets.ButtonInvisible(rectNew))
			{
				SoundDefOf.InfoCard_Open.PlayOneShotOnCamera();
				Find.WindowStack.Add(new Dialog_Sexcard(pawn));
			}
			GUI.color = old;
		}
	}
}


code:
using System.Text;
using RimWorld;
using UnityEngine;
using Verse;

namespace rjw
{
	public class Dialog_Sexcard : Window
	{
		private readonly Pawn pawn;

		public Dialog_Sexcard(Pawn editFor)
		{
			pawn = editFor;
		}

		public void SexualityCard(Rect rect, Pawn pawn)
		{
			CompRJW comp = pawn.TryGetComp<CompRJW>();
			if (pawn == null || comp == null) return;

			Text.Font = GameFont.Medium;
			Rect rect1 = new Rect(8f, 4f, rect.width - 8f, rect.height - 20f);
			Widgets.Label(rect1, "RJW");
			Text.Font = GameFont.Tiny;
			float num = rect1.y + 40f;
			Rect row1 = new Rect(10f, num, rect.width - 8f, 24f);
			Rect row2 = new Rect(10f, num + 24, rect.width - 8f, 24f);
			Rect row3 = new Rect(10f, num + 48, rect.width - 8f, 24f);
			Rect button1 = new Rect(10f, rect1.height - 10f, rect.width - 8f, 24f);
			Rect button2 = new Rect(10f, rect1.height - 34f, rect.width - 8f, 24f);

			string price;
			string sexuality;

			// Check for Rational Romance consistency, in case the player adds it mid-game or adds traits (such as with Prepare Carefully)
			if (xxx.RomanceDiversifiedIsActive || pawn.story.traits.HasTrait(TraitDefOf.Gay))
				CompRJW.RRTraitCheck(pawn);

			switch (CompRJW.Comp(pawn).orientation)
			{
				case Orientation.Asexual:
					sexuality = "Asexual";
					break;
				case Orientation.Bisexual:
					sexuality = "Bisexual";
					break;
				case Orientation.Heterosexual:
					sexuality = "Hetero";
					break;
				case Orientation.Homosexual:
					sexuality = "Gay";
					break;
				case Orientation.LeaningHeterosexual:
					sexuality = "Bisexual, leaning hetero";
					break;
				case Orientation.LeaningHomosexual:
					sexuality = "Bisexual, leaning gay";
					break;
				case Orientation.MostlyHeterosexual:
					sexuality = "Mostly hetero";
					break;
				case Orientation.MostlyHomosexual:
					sexuality = "Mostly gay";
					break;
				case Orientation.Pansexual:
					sexuality = "Pansexual";
					break;
				default:
					sexuality = "None";
					break;
			}

			Widgets.Label(row1, "Sexuality: " + sexuality);
			if (Mouse.IsOver(row1))
				Widgets.DrawHighlight(row1);

			string quirklist = CompRJW.Comp(pawn).quirks.ToString();
			Widgets.Label(row2, "Quirks".Translate() + quirklist);
			if (Mouse.IsOver(row2))
			{
				Widgets.DrawHighlight(row2);
				if (quirklist == "None")
					TooltipHandler.TipRegion(row2, "NoQuirks".Translate());
				else
				{
					StringBuilder tooltip = new StringBuilder();

					if (quirklist.Contains("Breeder"))
						tooltip.AppendLine("BreederQuirk".Translate());

					if (quirklist.Contains("Endytophile"))
						tooltip.AppendLine("EndytophileQuirk".Translate());

					if (quirklist.Contains("Exhibitionist"))
						tooltip.AppendLine("ExhibitionistQuirk".Translate());

					if (quirklist.Contains("Fertile"))
						tooltip.AppendLine("FertileQuirk".Translate());

					if (quirklist.Contains("Gerontophile"))
						tooltip.AppendLine("GerontophileQuirk".Translate());

					if (quirklist.Contains("Impregnation fetish"))
						tooltip.AppendLine("ImpregnationFetishQuirk".Translate());

					if (quirklist.Contains("Incubator"))
						tooltip.AppendLine("IncubatorQuirk".Translate());

					if (quirklist.Contains("Infertile"))
						tooltip.AppendLine("InfertileQuirk".Translate());

					if (quirklist.Contains("Messy"))
						tooltip.AppendLine("MessyQuirk".Translate());

					if (quirklist.Contains("Podophile"))
						tooltip.AppendLine("PodophileQuirk".Translate());

					if (quirklist.Contains("Pregnancy fetish"))
						tooltip.AppendLine("PregnancyFetishQuirk".Translate());

					if (quirklist.Contains("Sapiosexual"))
						tooltip.AppendLine("SapiosexualQuirk".Translate());

					if (quirklist.Contains("Somnophile"))
						tooltip.AppendLine("SomnophileQuirk".Translate());

					if (quirklist.Contains("Teratophile"))
						tooltip.AppendLine("TeratophileQuirk".Translate());

					if (quirklist.Contains("Vigorous"))
						tooltip.AppendLine("VigorousQuirk".Translate());

					TooltipHandler.TipRegion(row2, tooltip.ToString());
				}
			}

			if (RJWSettings.sex_minimum_age > pawn.ageTracker.AgeBiologicalYears)
				price = "Inapplicable (too young)";
			else if (pawn.ownership.OwnedRoom == null)
			{
				if (Current.ProgramState == ProgramState.Playing)
					price = WhoringHelper.WhoreMinPrice(pawn) + " - " + WhoringHelper.WhoreMaxPrice(pawn) + " (base, needs suitable bedroom)";
				else
					price = WhoringHelper.WhoreMinPrice(pawn) + " - " + WhoringHelper.WhoreMaxPrice(pawn) + " (base, modified by bedroom quality)";
			}
			else if (xxx.is_animal(pawn))
				price = "Incapable of whoring";
			else
				price = WhoringHelper.WhoreMinPrice(pawn) + " - " + WhoringHelper.WhoreMaxPrice(pawn);

			Widgets.Label(row3, "WhorePrice".Translate() + price);
			if (Mouse.IsOver(row3))
				Widgets.DrawHighlight(row3);

			// TODO: Add translation.
			if (Prefs.DevMode || Current.ProgramState != ProgramState.Playing)
			{
				if (Widgets.ButtonText(button1, Current.ProgramState != ProgramState.Playing ? "Reroll sexuality" : "[DEV] Reroll sexuality"))
				{
					CompRJW.Comp(pawn).Sexualize(pawn, true);
				}
			}
			if (pawn.health.hediffSet.HasHediff(Genital_Helper.archotech_penis) || pawn.health.hediffSet.HasHediff(Genital_Helper.archotech_vagina))
			{
				BodyPartRecord genitalia = Genital_Helper.get_genitals(pawn);
				if (pawn.health.hediffSet.HasHediff(HediffDef.Named("ImpregnationBlocker")))
				{
					if (Widgets.ButtonText(button2, "[Archotech genitalia] Enable reproduction"))
					{
						Hediff blocker = pawn.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("ImpregnationBlocker"));
						if (blocker != null)
							pawn.health.RemoveHediff(blocker);
						pawn.health.AddHediff(HediffDef.Named("FertilityEnhancer"), genitalia);
					}
				}
				else if (Widgets.ButtonText(button2, "[Archotech genitalia] Disable reproduction"))
				{
					Hediff enhancer = pawn.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("FertilityEnhancer"));
					if (enhancer != null)
						pawn.health.RemoveHediff(enhancer);
					pawn.health.AddHediff(HediffDef.Named("ImpregnationBlocker"), genitalia);
				}
			}
		}

		public override void DoWindowContents(Rect inRect)
		{
			bool flag = false;
			soundClose = SoundDefOf.InfoCard_Close;
			closeOnClickedOutside = true;
			absorbInputAroundWindow = false;
			forcePause = true;
			preventCameraMotion = false;
			if (Event.current.type == EventType.KeyDown && (Event.current.keyCode == KeyCode.Return || Event.current.keyCode == KeyCode.Escape))
			{
				flag = true;
				Event.current.Use();
			}
			Rect windowRect = inRect.ContractedBy(17f);
			Rect mainRect = new Rect(windowRect.x, windowRect.y, windowRect.width, windowRect.height - 20f);
			Rect okRect = new Rect(inRect.width / 3, mainRect.yMax + 10f, inRect.width / 3f, 30f);
			if (Current.ProgramState == ProgramState.Playing)
			{
				SexualityCard(mainRect, Find.Selector.SingleSelectedThing as Pawn);
			}
			else
			{
				SexualityCard(mainRect, pawn);
			}
			if (Widgets.ButtonText(okRect, "CloseButton".Translate()) || flag)
			{
				Close();
			}
		}
	}
}


code:
using System.Collections.Generic;
using Verse;
using Harmony;
using UnityEngine;
using System;

namespace rjw
{
	[HarmonyPatch(typeof(RimWorld.PawnWoundDrawer))]
	[HarmonyPatch("RenderOverBody")]
	[HarmonyPatch(new Type[] { typeof(Vector3), typeof(Mesh), typeof(Quaternion), typeof(bool) })]
	class patch_semenOverlay
	{

		static void Postfix(RimWorld.PawnWoundDrawer __instance, Vector3 drawLoc, Mesh bodyMesh, Quaternion quat, bool forPortrait)
		{
			Pawn pawn = Traverse.Create(__instance).Field("pawn").GetValue<Pawn>();//get local variable

			if (pawn.RaceProps.Humanlike && RJWSettings.cum_overlays)//for now, only draw humans
			{
				//find bukkake hediff. if it exists, use its draw function
				List<Hediff> hediffs = pawn.health.hediffSet.hediffs;
				if (hediffs.Exists(x => x.def == RJW_HediffDefOf.Hediff_Bukkake))
				{
					Hediff_Bukkake h = hediffs.Find(x => x.def == RJW_HediffDefOf.Hediff_Bukkake) as Hediff_Bukkake;

					quat.ToAngleAxis(out float angle, out Vector3 axis);//angle changes when pawn is e.g. downed

					h.DrawSemen(drawLoc, quat, forPortrait, angle);
				}
			}

		}
	}

	//adds new gizmo for adding semen for testing, feel free to comment out once not needed anymore
	[HarmonyPatch(typeof(Verse.Pawn))]
	[HarmonyPatch("GetGizmos")]
	class Patch_AddGizmo
	{
		static void Postfix(Verse.Pawn __instance, ref IEnumerable<Gizmo> __result)
		{

			List<Gizmo> NewList = new List<Gizmo>();

			// copy vanilla entries into the new list
			foreach (Gizmo entry in __result)
			{
				NewList.Add(entry);
			}

			if (Prefs.DevMode && RJWSettings.DevMode)
			{
				Command_Action addSemen = new Command_Action();
				addSemen.defaultDesc = "AddSemenHediff";
				addSemen.defaultLabel = "AddSemen";
				addSemen.action = delegate ()
				{
					Pawn pawn = __instance;

					if (!pawn.Dead && pawn.records != null)
					{
						//get all acceptable body parts:
						IEnumerable<BodyPartRecord> filteredParts = SemenHelper.getAvailableBodyParts(pawn);

						//select random part:
						BodyPartRecord randomPart;
						filteredParts.TryRandomElement<BodyPartRecord>(out randomPart);

						if (randomPart != null)
						{
							SemenHelper.cumOn(pawn, randomPart, 0.2f, null, SemenHelper.CUM_NORMAL);
						}

					};
				};

				NewList.Add(addSemen);
			}

			IEnumerable<Gizmo> output = NewList;

			// make caller use the list
			__result = output;

		}
	}

}


code:
using System;
using System.Collections.Generic;
using Harmony;
using RimWorld;
using Verse;
using Verse.AI.Group;
using System.Reflection;

namespace rjw
{
	//TODO: Remove this patch for compatibility with other mods and add them to genital helper class.
	/*
	[HarmonyPatch(typeof(PawnGenerator), "GeneratePawn", new Type[] { typeof(PawnGenerationRequest) })]
	internal static class Patches_GenerateRapist
	{
		public static void Postfix(Pawn __result, ref PawnGenerationRequest request)
		{
			if (__result == null) return;

			if (__result.RaceProps.IsMechanoid)
			{
				BodyPartRecord genitalPart = __result.RaceProps.body.AllParts.Find(bpr => bpr.def.defName == "Genitals");
				__result.health.AddHediff(HediffDef.Named("BionicPenis"), genitalPart);
			}

			if (PawnGenerationContext.NonPlayer.Includes(request.Context))
			{
				Need need = __result.needs.TryGetNeed<Need_Sex>();
				if (need != null)
				{
					//need.ForceSetLevel(Rand.Range(0f,1f));
					need.ForceSetLevel(Rand.Range(0.01f, 0.2f));
				}
			}
		}
	}*/

	[HarmonyPatch(typeof(LordJob_AssaultColony), "CreateGraph")]
	internal static class Patches_AssaultColonyForRape
	{
		public static void Postfix(StateGraph __result)
		{
			//--Log.Message("[ABF]AssaultColonyForRape::CreateGraph");
			if (__result == null) return;
			//--Log.Message("[RJW]AssaultColonyForRape::CreateGraph");
			foreach (var trans in __result.transitions)
			{
				if (HasDesignatedTransition(trans))
				{
					foreach (Trigger t in trans.triggers)
					{
						if (t.filters == null)
						{
							t.filters = new List<TriggerFilter>() { new Trigger_SexSatisfy(0.3f) };
						}
						else
						{
							t.filters.Add(new Trigger_SexSatisfy(0.3f));
						}
					}
					//--Log.Message("[ABF]AssaultColonyForRape::CreateGraph Adding SexSatisfyTrigger to " + trans.ToString());
				}
			}
		}

		private static bool HasDesignatedTransition(Transition t)
		{
			if (t.target == null) return false;
			if (t.target.GetType() == typeof(LordToil_KidnapCover)) return true;

			foreach (Trigger ta in t.triggers)
			{
				if (ta.GetType() == typeof(Trigger_FractionColonyDamageTaken)) return true;
			}
			return false;
		}
	}

	/*
	[HarmonyPatch(typeof(JobGiver_Manhunter), "TryGiveJob")]
	static class Patches_ABF_MunHunt
	{
		public static void Postfix(Job __result, ref Pawn pawn)
		{
			//--Log.Message("[RJW]Patches_ABF_MunHunt::Postfix called");
			if (__result == null) return;

			if (__result.def == JobDefOf.Wait || __result.def == JobDefOf.Goto) __result = null;
		}
	}
	*/

	/*
	//temporaly added code.  Adding record will make savedata error...
	[HarmonyPatch(typeof(DefMap<RecordDef, float>), "ExposeData")]
	internal static class Patches_DefMapTweak
	{
		public static bool Prefix(DefMap<RecordDef, float> __instance)
		{
			var field = __instance.GetType().GetField("values", BindingFlags.GetField | BindingFlags.SetField | BindingFlags.NonPublic | BindingFlags.Instance);
			var l = (field.GetValue(__instance) as List<float>);
			while (l.Count < DefDatabase<RecordDef>.DefCount)
			{
				l.Add(0f);
			}
			//field.SetValue(__instance, (int)() + 1);
			return true;
		}
	}
	[HarmonyPatch(typeof(MemoryThoughtHandler), "TryGainMemory", new Type[]{typeof(Thought_Memory),typeof(Pawn)})]
	internal static class Patches_MemoriesDebug
	{
		public static bool Prefix(MemoryThoughtHandler __instance, ref Thought_Memory newThought, ref Pawn otherPawn)
		{
			Log.Message(__instance.xxx.get_pawnname(pawn) + " adding " + newThought.LabelCap);
			return true;
		}
	}
	*/
	[HarmonyPatch(typeof(SkillRecord), "CalculateTotallyDisabled")]
	internal static class Patches_SkillRecordDebug
	{
		public static bool Prefix(SkillRecord __instance,ref bool __result)
		{
			var field = __instance.GetType().GetField("pawn", BindingFlags.GetField | BindingFlags.SetField | BindingFlags.NonPublic | BindingFlags.Instance);
			Pawn pawn = (field.GetValue(__instance) as Pawn);
			if (__instance.def == null)
			{
				Log.Message("no def!");
				__result = false;
				return false;
			}
			return true;
		}
	}
}


code:
using System.Linq;
using Harmony;
using RimWorld;
using Verse;
using System;
using System.Collections.Generic;

namespace rjw
{

	// This will generate backstories and traits for the pawns not spawned through the IncidentWorker_NymphJoins
	[HarmonyPatch(typeof(PawnGenerator), "GenerateNewPawnInternal")]
	internal static class Patch_PawnGenerator_GenerateNewNakedPawn
	{
		[HarmonyPrefix]
		private static void OnBegin_GenerateNewNakedPawn(ref PawnGenerationRequest request)
		{
			//--Log.Message("[RJW]Patch_PawnGenerator_GenerateNewNakedPawn::OnBegin_GenerateNewNakedPawn is called0");
			PawnGenerationRequest PGR = request;
			PawnKindDef pkd = PGR.KindDef;
			if (pkd != null && pkd.defName == "Nymph")
			{
				//--Log.Message("[RJW]Patch_PawnGenerator_GenerateNewNakedPawn::OnBegin_GenerateNewNakedPawn is called1");
				if (pkd.minGenerationAge != 20)
				{
					//--Log.Message("[RJW]Patch_PawnGenerator_GenerateNewNakedPawn::OnBegin_GenerateNewNakedPawn is called2");
					pkd.minGenerationAge = 20;
					pkd.maxGenerationAge = 27;
					PGR = new PawnGenerationRequest(pkd,
												 PGR.Faction,
												 PGR.Context,
												 PGR.Tile,	// tile(default is -1)
												 PGR.ForceGenerateNewPawn, // Force generate new pawn
												 PGR.Newborn, // Newborn
												 PGR.AllowDead, // Allow dead
												 PGR.AllowDowned, // Allow downed
												 PGR.CanGeneratePawnRelations, // Can generate pawn relations
												 PGR.MustBeCapableOfViolence, // Must be capable of violence
												 PGR.ColonistRelationChanceFactor, // Colonist relation chance factor
												 PGR.ForceAddFreeWarmLayerIfNeeded, // Force add free warm layer if needed
												 PGR.AllowGay, // Allow gay
												 PGR.AllowFood, // Allow food
												 PGR.Inhabitant, // Inhabitant
												 PGR.CertainlyBeenInCryptosleep, // Been in Cryosleep
												 PGR.ForceRedressWorldPawnIfFormerColonist, //forceRedressWorldPawnIfFormerColonist
												 PGR.WorldPawnFactionDoesntMatter, //worldPawnFactionDoesntMatter
												 c => (c.story.bodyType == BodyTypeDefOf.Female) || (c.story.bodyType == BodyTypeDefOf.Thin), // Validator
												 c => (c.story.bodyType == BodyTypeDefOf.Female) || (c.story.bodyType == BodyTypeDefOf.Thin), // Validator
												 PGR.MinChanceToRedressWorldPawn,
												 PGR.FixedBiologicalAge, // Fixed biological age
												 PGR.FixedChronologicalAge, // Fixed chronological age
												 Gender.Female, // Fixed gender
												 PGR.FixedMelanin, // Fixed melanin
												 PGR.FixedLastName); // Fixed last name
					request = PGR;
				}
			}
			//return;
		}
	}

	// This will generate backstories and traits for the pawns not spawned through the IncidentWorker_NymphJoins
	[HarmonyPatch(typeof(PawnGenerator), "GenerateTraits")]
	internal static class Patch_PawnGenerator_GenerateTraits
	{
		[HarmonyPostfix]
		private static void After_GenerateTraits(Pawn pawn, PawnGenerationRequest request)
		{
			if (request.KindDef != null && request.KindDef.defName == "Nymph" && request.Faction != Faction.OfPlayer)
			{
				nymph_generator.set_story(pawn);
			}
		}
	}
}


code:
using System;
using System.Reflection;
using Harmony;
using RimWorld;
using RimWorld.Planet;
using Verse;
using Verse.AI;

namespace rjw
{
	[HarmonyPatch(typeof(Pawn_ApparelTracker))]
	[HarmonyPatch("Wear")]
	internal static class PATCH_Pawn_ApparelTracker_Wear
	{
		// Prevents pawns from equipping a piece of apparel if it conflicts with some locked apparel that they're already wearing
		[HarmonyPrefix]
		private static bool prevent_wear_by_gear(Pawn_ApparelTracker __instance, ref Apparel newApparel)
		{
			var tra = __instance;
			// //--Log.Message ("Pawn_ApparelTracker.Wear called for " + newApparel.Label + " on " + tra.xxx.get_pawnname(pawn));
			foreach (var app in tra.WornApparel)
				if (app.has_lock() && (!ApparelUtility.CanWearTogether(newApparel.def, app.def, tra.pawn.RaceProps.body)))
				{
					if (PawnUtility.ShouldSendNotificationAbout(tra.pawn))
						Messages.Message(xxx.get_pawnname(tra.pawn) + " can't wear " + newApparel.def.label + " (blocked by " + app.def.label + ")", tra.pawn, MessageTypeDefOf.NegativeEvent);
					return false;
				}
			return true;
		}

		// Add the appropriate hediff when gear is worn
		[HarmonyPostfix]
		private static void on_wear(Pawn_ApparelTracker __instance, Apparel newApparel)
		{
			var tra = __instance;
			if ((newApparel.def is bondage_gear_def def) &&
				(newApparel.Wearer == tra.pawn))
			{ // Check that the apparel was actually worn
				def.soul.on_wear(tra.pawn, newApparel);
			}
		}
	}

	// Remove the hediff when the gear is removed
	[HarmonyPatch(typeof(Pawn_ApparelTracker))]
	[HarmonyPatch("Notify_ApparelRemoved")]
	internal static class PATCH_Pawn_ApparelTracker_Remove
	{
		[HarmonyPostfix]
		private static void on_remove(Pawn_ApparelTracker __instance, Apparel apparel)
		{
			if (apparel.def is bondage_gear_def def)
			{
				def.soul.on_remove(apparel, __instance.pawn);
			}
		}
	}

	// Patch the TryDrop method so that locked apparel cannot be dropped by JobDriver_RemoveApparel or by stripping
	// a corpse or downed pawn
	internal static class PATCH_Pawn_ApparelTracker_TryDrop
	{
		// Can't call MakeByRefType in an attribute, which is why this method is necessary.
		private static MethodInfo get_target()
		{
			return typeof(Pawn_ApparelTracker).GetMethod("TryDrop", new Type[] {
				typeof(Apparel),
				typeof(Apparel).MakeByRefType (),
				typeof(IntVec3),
				typeof(bool)
			});
		}

		// Can't just put [HarmonyTargetMethod] on get_target, which is why this method is necessary. BTW I don't
		// know why HarmonyTargetMethod wasn't working. I'm sure I had everything set up properly so it might be a
		// bug in Harmony itself, but I can't be bothered to look into it in detail.
		public static void apply(HarmonyInstance har)
		{
			var tar = get_target();
			var pat = typeof(PATCH_Pawn_ApparelTracker_TryDrop).GetMethod("prevent_locked_apparel_drop");
			har.Patch(tar, new HarmonyMethod(pat), null);
		}

		public static bool prevent_locked_apparel_drop(Pawn_ApparelTracker __instance, ref Apparel ap, ref bool __result)
		{
			if (!ap.has_lock())
				return true;
			else
			{
				__result = false;
				return false;
			}
		}
	}

	// Prevent locked apparel from being removed from a pawn who's out on a caravan
	// TODO: Allow the apparel to be removed if the key is in the caravan's inventory
	//[HarmonyPatch (typeof (WITab_Caravan_Gear))]
	//[HarmonyPatch ("TryRemoveFromCurrentWearer")]
	//static class PATCH_WITab_Caravan_Gear_TryRemoveFromCurrentWearer {
	//	[HarmonyPrefix]
	//	static bool prevent_locked_apparel_removal (WITab_Caravan_Gear __instance, Thing t, ref bool __result)
	//	{
	//		var app = t as Apparel;
	//		if ((app == null) || (! app.has_lock ()))
	//			return true;
	//		else {
	//			__result = false;
	//			return false;
	//		}
	//	}
	//}

	// Check for the special case where a player uses the caravan gear tab to equip a piece of lockable apparel on a pawn that
	// is wearing an identical piece of already locked apparel, and make sure that doesn't work or change anything. This is to
	// fix a bug where, even though the new apparel would fail to be equipped on the pawn, it would somehow copy the holostamp
	// info from the equipped apparel.
	[HarmonyPatch(typeof(WITab_Caravan_Gear))]
	[HarmonyPatch("TryEquipDraggedItem")]
	internal static class PATCH_WITab_Caravan_Gear_TryEquipDraggedItem
	{
		private static Thing get_dragged_item(WITab_Caravan_Gear tab)
		{
			return (Thing)typeof(WITab_Caravan_Gear).GetField("draggedItem", xxx.ins_public_or_no).GetValue(tab);
		}

		private static void set_dragged_item(WITab_Caravan_Gear tab, Thing t)
		{
			typeof(WITab_Caravan_Gear).GetField("draggedItem", xxx.ins_public_or_no).SetValue(tab, t);
		}

		[HarmonyPrefix]
		private static bool prevent_locked_apparel_conflict(WITab_Caravan_Gear __instance, ref Pawn p)
		{
			if (__instance == null) return true;
			if (get_dragged_item(__instance) is Apparel dragged_app && p.apparel != null && dragged_app.has_lock())
			{
				foreach (var equipped_app in p.apparel.WornApparel)
				{
					if (equipped_app.has_lock() && (!ApparelUtility.CanWearTogether(dragged_app.def, equipped_app.def, p.RaceProps.body)))
					{
						set_dragged_item(__instance, null);
						return false;
					}
				}
			}

			return true;
		}
	}
	/* GONE IN b19
	// Prevent locked apparel from being removed from a corpse
	// TODO: Find out when this method is actually called (probably doesn't matter but still)
	[HarmonyPatch(typeof(Dialog_FormCaravan))]
	[HarmonyPatch("RemoveFromCorpseIfPossible")]
	internal static class PATCH_Dialog_FormCaravan_RemoveFromCorpseIfPossible
	{
		[HarmonyPrefix]
		private static bool prevent_locked_apparel_removal(Dialog_FormCaravan __instance, Thing thing)
		{
			var app = thing as Apparel;
			return (app == null) || (!app.has_lock());
		}
	}
	*/
	// Patch a method in a pawn gear tab to give the pawn a special job driver if the player tells them to
	// drop locked apparel they have equipped
	// TODO: Search for the key and, if the pawn can access it, give them a new job that has them go to the key
	// and use it to unlock the apparel
	[HarmonyPatch(typeof(ITab_Pawn_Gear))]
	[HarmonyPatch("InterfaceDrop")]
	internal static class PATCH_ITab_Pawn_Gear_InterfaceDrop
	{
		private static Pawn SelPawnForGear(ITab_Pawn_Gear tab)
		{
			var pro = typeof(ITab_Pawn_Gear).GetProperty("SelPawnForGear", xxx.ins_public_or_no);
			return (Pawn)pro.GetValue(tab, null);
		}

		[HarmonyPrefix]
		private static bool drop_locked_apparel(ITab_Pawn_Gear __instance, Thing t)
		{
			var tab = __instance;
			var app = t as Apparel;
			if ((app == null) || (!app.has_lock()))
				return true;
			else
			{
				var p = SelPawnForGear(tab);
				if ((p.apparel != null) && (p.apparel.WornApparel.Contains(app)))
				{
					p.jobs.TryTakeOrderedJob(new Job(DefDatabase<JobDef>.GetNamed("StruggleInBondageGear"), app));
					return false;
				}
				else
					return true;
			}
		}
	}

	// Patch the method that prisoners use to find apparel in their cell so that they don't try to wear
	// something that conflicts with locked apparel. The method used to prevent pawns from trying to
	// optimize away locked apparel won't work here because prisoners don't check GetSpecialApparelScoreOffset.
	[HarmonyPatch(typeof(JobGiver_PrisonerGetDressed))]
	[HarmonyPatch("FindGarmentCoveringPart")]
	internal static class PATCH_JobGiver_PrisonerGetDressed_FindGarmentCoveringPart
	{
		private static bool conflicts(Pawn_ApparelTracker tra, Apparel new_app)
		{
			foreach (var worn_app in tra.WornApparel)
				if (worn_app.has_lock() && (!ApparelUtility.CanWearTogether(worn_app.def, new_app.def, tra.pawn.RaceProps.body)))
					return true;

			return false;
		}

		[HarmonyPrefix]
		private static bool prevent_locked_apparel_conflict(JobGiver_PrisonerGetDressed __instance, Pawn pawn, BodyPartGroupDef bodyPartGroupDef, ref Apparel __result)
		{
			// If the prisoner is not wearing locked apparel then just run the regular method
			if (!pawn.is_wearing_locked_apparel())
				return true;

			// Otherwise, duplicate the logic in the regular method except with an additional check
			// to filter out apparel that conflicts with worn locked apparel.
			else
			{
				var room = pawn.GetRoom();
				if (room.isPrisonCell)
					foreach (var c in room.Cells)
						foreach (var t in c.GetThingList(pawn.Map))
						{
							if ((t is Apparel app) &&
								app.def.apparel.bodyPartGroups.Contains(bodyPartGroupDef) &&
								pawn.CanReserve(app) &&
								ApparelUtility.HasPartsToWear(pawn, app.def) &&
								(!conflicts(pawn.apparel, app)))
							{
								__result = app;
								return false;
							}
						}
				__result = null;
				return false;
			}
		}
	}
}


code:
using System;
using System.Reflection;
using Harmony;
using RimWorld;
using Verse;
using Verse.AI;

namespace rjw
{
	// Add a fail condition to JobDriver_Lovin that prevents pawns from lovin' if they aren't physically able
	//have genitals
	[HarmonyPatch(typeof(JobDriver_Lovin))]
	[HarmonyPatch("MakeNewToils")]
	internal static class PATCH_JobDriver_Lovin_MakeNewToils
	{
		[HarmonyPrefix]
		private static bool on_begin_lovin(JobDriver_Lovin __instance)
		{
			//--Log.Message("[RJW]patches_lovin::PATCH_JobDriver_Lovin_MakeNewToils is called0");
			//if (__instance == null) return true;
			__instance.FailOn(() => (!(xxx.can_fuck(__instance.pawn) || xxx.can_be_fucked(__instance.pawn))));
			return true;
		}
	}

	//JobDriver_DoLovinCasual from RomanceDiversified should have handled whether pawns can do casual lovin,
	//so I don't bothered to do a check here,unless some bugs occur due to this.

	// Call xxx.aftersex after pawns have finished lovin'
	// You might be thinking, "wouldn't it be easier to add this code as a finish condition to JobDriver_Lovin in the patch above?" I tried that
	// at first but it didn't work because the finish condition is always called regardless of how the job ends (i.e. if it's interrupted or not)
	// and there's no way to find out from within the finish condition how the job ended. I want to make sure not apply the effects of sex if the
	// job was interrupted somehow.
	[HarmonyPatch(typeof(JobDriver))]
	[HarmonyPatch("Cleanup")]
	internal static class PATCH_JobDriver_Cleanup
	{
		private readonly static Type JobDriverDoLovinCasual = AccessTools.TypeByName("JobDriver_DoLovinCasual");//not very good solution, some other mod but RR can have same named jobdriver

		private readonly static Type JobDriverLovin = typeof(JobDriver_Lovin);

		[HarmonyPrefix]
		private static bool on_cleanup_driver(JobDriver __instance, JobCondition condition)
		{
			if (__instance == null)
				return true;

			if (condition == JobCondition.Succeeded)
			{
				Pawn pawn = __instance.pawn;
				Pawn partner = __instance.pawn;

				//Log.Message("[RJW]patches_lovin::on_cleanup_driver" + xxx.get_pawnname(pawn) + "+" + JobDriverLovin);

				//[RF] Rational Romance [1.0] loving
				if (xxx.RomanceDiversifiedIsActive && __instance.GetType() == JobDriverDoLovinCasual)
				{
					// not sure RR can even cause pregnancies but w/e
					var any_ins = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
					partner = (Pawn)(__instance.GetType().GetProperty("Partner", any_ins).GetValue(__instance, null));
					Log.Message("[RJW]patches_lovin::on_cleanup_driver RomanceDiversified/RationalRomance:" + xxx.get_pawnname(pawn) + "+" + xxx.get_pawnname(partner));
				}
				//Vanilla loving
				else if (__instance.GetType() == JobDriverLovin)
				{
					var any_ins = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
					partner = (Pawn)(__instance.GetType().GetProperty("Partner", any_ins).GetValue(__instance, null));
				//CnP loving
					if (xxx.RimWorldChildrenIsActive && RJWPregnancySettings.humanlike_pregnancy_enabled && xxx.is_human(pawn) && xxx.is_human(partner))
					{
						Log.Message("[RJW]patches_lovin:: RimWorldChildren/ChildrenAndPregnancy pregnancy:" + xxx.get_pawnname(pawn) + "+" + xxx.get_pawnname(partner));
						PregnancyHelper.cleanup_CnP(pawn);
						PregnancyHelper.cleanup_CnP(partner);
					}
				}
				else
					return true;

				//vanilla will probably be fucked up for non humanlikes... but only humanlikes do loving, right?
				//if rjw pregnancy enabled, remove vanilla for:
				//human-human
				//animal-animal
				//bestiality
				//always remove when someone is insect or mech
				if (RJWPregnancySettings.humanlike_pregnancy_enabled && xxx.is_human(pawn) && xxx.is_human(partner)
					|| RJWPregnancySettings.animal_pregnancy_enabled && xxx.is_animal(pawn) && xxx.is_animal(partner)
					|| (RJWPregnancySettings.bestial_pregnancy_enabled && xxx.is_human(pawn) && xxx.is_animal(partner)
					|| RJWPregnancySettings.bestial_pregnancy_enabled && xxx.is_animal(pawn) && xxx.is_human(partner))
					|| xxx.is_insect(pawn) || xxx.is_insect(partner) || xxx.is_mechanoid(pawn) || xxx.is_mechanoid(partner)
					)
				{
					Log.Message("[RJW]patches_lovin::on_cleanup_driver vanilla pregnancy:" + xxx.get_pawnname(pawn) + "+" + xxx.get_pawnname(partner));
					PregnancyHelper.cleanup_vanilla(pawn);
					PregnancyHelper.cleanup_vanilla(partner);
				}

				SexUtility.ProcessSex(pawn, partner, false, true);
			}
			return true;
		}
	}
}


code:
namespace rjw
{
	/*
	[HarmonyPatch(typeof(Pawn_NeedsTracker))]
	[HarmonyPatch("ShouldHaveNeed")]
	static class patches_need
	{
		[HarmonyPostfix]
		static void on_postfix(Pawn_NeedsTracker __instance, NeedDef nd, ref bool __result){
			Pawn p=(Pawn)(typeof(Pawn_NeedsTracker).GetField("pawn", xxx.ins_public_or_no).GetValue(__instance));
			__result = __result && (nd.defName != "Sex" || (p!=null && p.Map!=null));
		}
	}
	*/
}


code:
using System.Collections.Generic;
using Harmony;
using RimWorld;
using RimWorld.Planet;
using UnityEngine;
using Verse;

namespace rjw
{
	[HarmonyPatch(typeof(Hediff_Pregnant), "DoBirthSpawn")]
	internal static class PATCH_Hediff_Pregnant_DoBirthSpawn
	{
		/// <summary>
		/// This one overrides vanilla pregnancy hediff behavior.
		/// 0 - try to find suitable father for debug pregnancy
		/// 1st part if character pregnant and rjw pregnancies enabled - creates rjw pregnancy and instantly births it
		/// 2nd part if character pregnant with rjw pregnancy - birth it
		/// 3rd part - debug - create rjw/vanila pregnancy and birth it
		/// </summary>
		/// <param name="mother"></param>
		/// <param name="father"></param>
		/// <returns></returns>
		[HarmonyPrefix]
		private static bool on_begin_DoBirthSpawn(ref Pawn mother, ref Pawn father)
		{
			//--Log.Message("patches_pregnancy::PATCH_Hediff_Pregnant::DoBirthSpawn() called");
			//var mother_name = xxx.get_pawnname(mother);
			//var father_name = xxx.get_pawnname(father);

			if (mother == null)
			{
				Log.Error("Hediff_Pregnant::DoBirthSpawn() - no mother defined -> exit");
				return false;
			}

			if (father == null)
			{
				Log.Warning("Hediff_Pregnant::DoBirthSpawn() - no father defined");
				//birthing with debug has no father
				//make father lover
				if (father == null)
				{
					Pawn target = mother.relations.GetFirstDirectRelationPawn(PawnRelationDefOf.Lover);
					if (target != null)
						father = Genital_Helper.has_penis(target) ? target : null;
				}

				//make father spouse
				if (father == null)
				{
					Pawn target = mother.relations.GetFirstDirectRelationPawn(PawnRelationDefOf.Spouse);
					if (target != null)
						father = Genital_Helper.has_penis(target) ? target : null;
				}

				//make father bonded animal
				//this is for testing only... so far
				if (father == null)
				{
					Pawn target = mother.relations.GetFirstDirectRelationPawn(PawnRelationDefOf.Bond);
					if (target != null)
						father = Genital_Helper.has_penis(target) ? target : null;
				}
			}

			//debug?
			if (mother.gender == Gender.Male)
			{
				Log.Error("Hediff_Pregnant::DoBirthSpawn() - mother is male -> exit");
				return false;
			}

			// get a reference to the hediff we are applying
			//do birth for vanilla pregnancy, if using rjw - add RJW pregnancy and birth it instead
			Hediff_Pregnant self = (Hediff_Pregnant)mother.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("Pregnant"));
			if (self != null)
			{
				Log.Message("patches_pregnancy::PATCH_Hediff_Pregnant::DoBirthSpawn():Vanilla_pregnancy birthing:" + xxx.get_pawnname(mother));
				if (RJWPregnancySettings.animal_pregnancy_enabled && ((xxx.is_animal(father) || father == null) && xxx.is_animal(mother)))
				{
					//RJW Bestial pregnancy animal-animal
					Log.Message(" override as Bestial birthing(animal-animal):" + xxx.get_pawnname(mother));
					Hediff_BestialPregnancy.Create(mother, father);
					Hediff_BestialPregnancy hediff = (Hediff_BestialPregnancy)mother.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("RJW_pregnancy_beast"));
					hediff.GiveBirth();
					if (self != null)
						mother.health.RemoveHediff(self);

					return false;
				}
				else if (RJWPregnancySettings.bestial_pregnancy_enabled && ((xxx.is_animal(father) && xxx.is_human(mother)) || (xxx.is_human(father) && xxx.is_animal(mother))))
				{
					//RJW Bestial pregnancy human-animal
					Log.Message(" override as Bestial birthing(human-animal):" + xxx.get_pawnname(mother));
					Hediff_BestialPregnancy.Create(mother, father);
					Hediff_BestialPregnancy hediff = (Hediff_BestialPregnancy)mother.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("RJW_pregnancy_beast"));
					hediff.GiveBirth();
					if (self != null)
						mother.health.RemoveHediff(self);

					return false;
				}
				else if (RJWPregnancySettings.humanlike_pregnancy_enabled)
				{
					//RJW Humanlike pregnancy
					Log.Message(" override as Humanlike birthing:" + xxx.get_pawnname(mother));
					Hediff_HumanlikePregnancy.Create(mother, father);
					Hediff_HumanlikePregnancy hediff = (Hediff_HumanlikePregnancy)mother.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("RJW_pregnancy"));
					hediff.GiveBirth();
					if (self != null)
						mother.health.RemoveHediff(self);

					return false;
				}
				else
				{
					Log.Warning("Hediff_Pregnant::DoBirthSpawn() - vanilla pregnancy birth");
					//vanilla code
					int num = (mother.RaceProps.litterSizeCurve == null) ? 1 : Mathf.RoundToInt(Rand.ByCurve(mother.RaceProps.litterSizeCurve));
					if (num < 1)
					{
						num = 1;
					}
					PawnGenerationRequest request = new PawnGenerationRequest(mother.kindDef, mother.Faction, PawnGenerationContext.NonPlayer, -1, forceGenerateNewPawn: false, newborn: true);
					Pawn pawn = null;
					for (int i = 0; i < num; i++)
					{
						pawn = PawnGenerator.GeneratePawn(request);
						if (PawnUtility.TrySpawnHatchedOrBornPawn(pawn, mother))
						{
							if (pawn.playerSettings != null && mother.playerSettings != null)
							{
								pawn.playerSettings.AreaRestriction = mother.playerSettings.AreaRestriction;
							}
							if (pawn.RaceProps.IsFlesh)
							{
								pawn.relations.AddDirectRelation(PawnRelationDefOf.Parent, mother);
								if (father != null)
								{
									pawn.relations.AddDirectRelation(PawnRelationDefOf.Parent, father);
								}
							}
						}
						else
						{
							Find.WorldPawns.PassToWorld(pawn, PawnDiscardDecideMode.Discard);
						}
						TaleRecorder.RecordTale(TaleDefOf.GaveBirth, mother, pawn);
					}
					if (mother.Spawned)
					{
						FilthMaker.MakeFilth(mother.Position, mother.Map, ThingDefOf.Filth_AmnioticFluid, mother.LabelIndefinite(), 5);
						if (mother.caller != null)
						{
							mother.caller.DoCall();
						}
						if (pawn.caller != null)
						{
							pawn.caller.DoCall();
						}
					}
					if (self != null)
						mother.health.RemoveHediff(self);

					return false;
				}
			}

			// do birth for RJW pregnancies
			Hediff_HumanlikePregnancy rjwH = (Hediff_HumanlikePregnancy)mother.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("RJW_pregnancy"));
			if (rjwH != null)
			{
				//RJW Bestial pregnancy
				Log.Message("patches_pregnancy::PATCH_Hediff_Pregnant::DoBirthSpawn():RJW_pregnancy birthing:" + xxx.get_pawnname(mother));
				rjwH.GiveBirth();
				if (self == null)
					return false;
			}

			Hediff_BestialPregnancy rjwB = (Hediff_BestialPregnancy)mother.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("RJW_pregnancy_beast"));
			if (rjwB != null)
			{
				//RJW Humanlike pregnancy
				Log.Message("patches_pregnancy::PATCH_Hediff_Pregnant::DoBirthSpawn():RJW_pregnancy_beast birthing:" + xxx.get_pawnname(mother));
				rjwB.GiveBirth();
				if (self == null)
					return false;
			}

			//debug, add RJW pregnancy and birth it
			Log.Message("patches_pregnancy::PATCH_Hediff_Pregnant::DoBirthSpawn():Debug_pregnancy birthing:" + xxx.get_pawnname(mother));
			/*
			if (true)
			{
				//RJW Mech pregnancy
				Log.Message(" override as mech birthing:" + xxx.get_pawnname(mother));
				Hediff_MechanoidPregnancy.Create(mother, father);
				Hediff_MechanoidPregnancy hediff = (Hediff_MechanoidPregnancy)mother.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("RJW_pregnancy_mech"));
				hediff.GiveBirth();
				return false;
			}
			*/

			if (((xxx.is_animal(father) || xxx.is_animal(mother)) && RJWPregnancySettings.bestial_pregnancy_enabled)
				|| (xxx.is_animal(mother) && RJWPregnancySettings.animal_pregnancy_enabled))
			{
				//RJW Bestial pregnancy
				Log.Message(" override as Bestial birthing:" + xxx.get_pawnname(mother));
				Hediff_BestialPregnancy.Create(mother, father);
				Hediff_BestialPregnancy hediff = (Hediff_BestialPregnancy)mother.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("RJW_pregnancy_beast"));
				hediff.GiveBirth();
			}
			else if (RJWPregnancySettings.humanlike_pregnancy_enabled)
			{
				//RJW Humanlike pregnancy
				Log.Message(" override as Humanlike birthing:" + xxx.get_pawnname(mother));
				Hediff_HumanlikePregnancy.Create(mother, father);
				Hediff_HumanlikePregnancy hediff = (Hediff_HumanlikePregnancy)mother.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("RJW_pregnancy"));
				hediff.GiveBirth();
			}
			else
			{
				Log.Warning("Hediff_Pregnant::DoBirthSpawn() - debug vanilla pregnancy birth");
				//vanilla code
				int num = (mother.RaceProps.litterSizeCurve == null) ? 1 : Mathf.RoundToInt(Rand.ByCurve(mother.RaceProps.litterSizeCurve));
				if (num < 1)
				{
					num = 1;
				}
				PawnGenerationRequest request = new PawnGenerationRequest(mother.kindDef, mother.Faction, PawnGenerationContext.NonPlayer, -1, forceGenerateNewPawn: false, newborn: true);
				Pawn pawn = null;
				for (int i = 0; i < num; i++)
				{
					pawn = PawnGenerator.GeneratePawn(request);
					if (PawnUtility.TrySpawnHatchedOrBornPawn(pawn, mother))
					{
						if (pawn.playerSettings != null && mother.playerSettings != null)
						{
							pawn.playerSettings.AreaRestriction = mother.playerSettings.AreaRestriction;
						}
						if (pawn.RaceProps.IsFlesh)
						{
							pawn.relations.AddDirectRelation(PawnRelationDefOf.Parent, mother);
							if (father != null)
							{
								pawn.relations.AddDirectRelation(PawnRelationDefOf.Parent, father);
							}
						}
					}
					else
					{
						Find.WorldPawns.PassToWorld(pawn, PawnDiscardDecideMode.Discard);
					}
					TaleRecorder.RecordTale(TaleDefOf.GaveBirth, mother, pawn);
				}
				if (mother.Spawned)
				{
					FilthMaker.MakeFilth(mother.Position, mother.Map, ThingDefOf.Filth_AmnioticFluid, mother.LabelIndefinite(), 5);
					if (mother.caller != null)
					{
						mother.caller.DoCall();
					}
					if (pawn.caller != null)
					{
						pawn.caller.DoCall();
					}
				}
				if (self != null)
					mother.health.RemoveHediff(self);
			}
			return false;
		}
	}

	[HarmonyPatch(typeof(Hediff_Pregnant), "Tick")]
	class PATCH_Hediff_Pregnant_Tick {
		[HarmonyPrefix]
		static bool on_begin_Tick( Hediff_Pregnant __instance ) {
			if (__instance.pawn.IsHashIntervalTick(1000)) {
				//	//--Log.Message("patches_pregnancy::PATCH_Hediff_Pregnant::Tick( " + __instance.xxx.get_pawnname(pawn) + " ) - gestation_progress = " + __instance.GestationProgress);
				//	if (__instance.Severity < 0.95f) {
				//		__instance.Severity = 0.95f;
				//	}
				if (!Genital_Helper.has_vagina(__instance.pawn))
				{
					__instance.pawn.health.RemoveHediff(__instance);
				}

			}
			return true;
		}
	}
}


code:
using System.Collections.Generic;
using System.Linq;
using Harmony;
using RimWorld;
using Verse;
using UnityEngine;

namespace rjw
{
	//[HarmonyPatch(typeof(Pawn), "GetGizmos", new Type[0])]
	[HarmonyPatch(typeof(Pawn), "GetGizmos")]
	[StaticConstructorOnStartup]
	static class submit_button
	{
		[HarmonyPostfix]
		static void this_is_postfix(ref IEnumerable<Gizmo> __result, ref Pawn __instance)
		{
			//Log.Message("[rjw]Harmony patch submit_button is called");
			var pawn = __instance;
			var gizmos = __result.ToList();
			var enabled = RJWSettings.submit_button_enabled;

			if (enabled && pawn.IsColonistPlayerControlled &&  pawn.Drafted)
			{
				gizmos.Add(new Command_Action
				{
					defaultLabel = "CommandSubmit".Translate(),
					icon = submit_icon,
					defaultDesc = "CommandSubmitDesc".Translate(),
					action = delegate
					{
						pawn.health.AddHediff(submit_hediff);
					},
					hotKey = KeyBindingDefOf.Misc3
				});
			}
			__result = gizmos.AsEnumerable();
		}

		static Texture2D submit_icon = ContentFinder<Texture2D>.Get("UI/Commands/Submit", true);
		static HediffDef submit_hediff= HediffDef.Named("Hediff_Submitting");

		//static void LayDownAndAccept(Pawn pawn)
		//{
		//	//Log.Message("Submit button is pressed for " + pawn);
		//	pawn.health.AddHediff(submit_hediff);
		//}
	}
}


code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Verse;

namespace rjw
{
	public class HediffCompProperties_SeverityPerDayIfRest : HediffCompProperties
	{
		public HediffCompProperties_SeverityPerDayIfRest()
		{
			this.compClass = typeof(HediffComp_SeverityPerDayIfRest);
		}

		public SimpleCurve severityPerDayByRestDays;
	}
	class HediffComp_SeverityPerDayIfRest : HediffComp_SeverityPerDay
	{
		private HediffCompProperties_SeverityPerDayIfRest Props
		{
			get
			{
				return (HediffCompProperties_SeverityPerDayIfRest)this.props;
			}
		}
		public override void CompPostTick(ref float severityAdjustment)
		{
			base.CompPostTick(ref severityAdjustment);
			if (base.Pawn.IsHashIntervalTick(SeverityUpdateInterval))
			{
				float num = this.SeverityChangePerDay();
				num *= 0.00333333341f;
				severityAdjustment += num;
			}
		}
		protected override float SeverityChangePerDay()
		{
			return this.Props.severityPerDayByRestDays.Evaluate(this.parent.ageTicks / 60000f);
		}
	}

	public class HediffCompProperties_WhenRapid : HediffCompProperties
	{
		public HediffCompProperties_WhenRapid()
		{
			this.compClass = typeof(HediffComp_WhenRapid);
		}

		public SimpleCurve severityRateByRestDays;
	}
	class HediffComp_WhenRapid : AdvancedHediffComp
	{
		private HediffCompProperties_WhenRapid Props
		{
			get
			{
				return (HediffCompProperties_WhenRapid)this.props;
			}
		}
		public override void CompPostMerged(Hediff other)
		{
			other.Severity *= Props.severityRateByRestDays.Evaluate(this.parent.ageTicks / 60000f);
		}
	}
	public class AdvancedHediffWithComps : HediffWithComps
	{
		public override bool TryMergeWith(Hediff other)
		{
			for (int i = 0; i < this.comps.Count; i++)
			{
				if(this.comps[i] is AdvancedHediffComp) ((AdvancedHediffComp)this.comps[i]).CompBeforeMerged(other);
			}
			return base.TryMergeWith(other);
		}
	}
	public class AdvancedHediffComp : HediffComp
	{
		public virtual void CompBeforeMerged(Hediff other)
		{
		}
	}
}


code:
using System;
using System.Collections.Generic;
using System.Linq;
using Verse;
using RimWorld;
using UnityEngine;

namespace rjw
{
	class Hediff_Bukkake : HediffWithComps
	{
		/*
		Whenever semen is applied, this hediff is also added to the pawn.
		Since there is always only a single hediff of this type on the pawn, it serves as master controller, adding up the individual Semen hediffs, applying debuffs and drawing overlays
		*/

		private static readonly float semenWeight = 0.2f;//how much individual semen_hediffs contribute to the overall bukkake severity
		List<Hediff> hediffs_semen;
		Dictionary<string, SemenSplatch> splatches;

		public override void ExposeData()
		{
			base.ExposeData();
			//Scribe_Values.Look<Dictionary<string, SemenSplatch>>(ref splatches, "splatches", new Dictionary<string, SemenSplatch>()); - causes errors when loading. for now, just make a new dictionary
			splatches = new Dictionary<string, SemenSplatch>();//instead of loading, just recreate anew
			hediffs_semen = new List<Hediff>();
		}

		public override void PostMake()
		{
			base.PostMake();
			splatches = new Dictionary<string, SemenSplatch>();
		}

		public override void PostTick()
		{
			if (pawn.RaceProps.Humanlike)//for now, only humans are supported
			{
				hediffs_semen = this.pawn.health.hediffSet.hediffs.FindAll(x => (x.def == RJW_HediffDefOf.Hediff_Semen || x.def == RJW_HediffDefOf.Hediff_InsectSpunk || x.def == RJW_HediffDefOf.Hediff_MechaFluids));
				float bukkakeLevel = CalculateBukkakeLevel();//sum of severity of all the semen hediffs x semenWeight
				this.Severity = bukkakeLevel;
				bool updatePortrait = false;

				//loop through all semen hediffs, add missing ones to dictionary
				for (int i = 0; i < hediffs_semen.Count(); i++)
				{
					Hediff_Semen h = (Hediff_Semen)hediffs_semen[i];
					string ID = h.GetUniqueLoadID();//unique ID for each hediff
					if (!splatches.ContainsKey(ID))//if it isn't here yet, make new object
					{
						updatePortrait = true;
						bool leftSide = h.Part.Label.Contains("left") ? true : false;//depending on whether the body part is left or right, drawing-offset on x-aixs may be inverted

						splatches[ID] = new SemenSplatch(h, pawn.story.bodyType, h.Part.def, leftSide, h.semenType);
					}

				}

				//remove splatch objects once their respective semen hediff is gone
				List<string> removeKeys = new List<string>();
				foreach (string key in splatches.Keys)
				{
					SemenSplatch s = splatches[key];

					if (!hediffs_semen.Contains(s.hediff_Semen))
					{
						removeKeys.Add(key);
						updatePortrait = true;
					}
				}
				//loop over and remove elements that should be destroyed:
				foreach (string key in removeKeys)
				{
					SemenSplatch s = splatches[key];
					splatches.Remove(key);
				}

				if (updatePortrait)//right now, portraits are only updated when a completely new semen hediff is added or an old one removed - maybe that should be done more frequently
				{
					PortraitsCache.SetDirty(pawn);
				}
			}
		}

		//called from the PawnWoundDrawer (see HarmonyPatches)
		public void DrawSemen(Vector3 drawLoc, Quaternion quat, bool forPortrait, float angle)
		{
			Rot4 bodyFacing = pawn.Rotation;
			int facingDir = bodyFacing.AsInt;//0: north, 1:east, 2:south, 3:west
			foreach (string key in splatches.Keys)
			{
				SemenSplatch s = splatches[key];
				s.Draw(drawLoc, quat, forPortrait, facingDir, angle);
			}
		}

		//new Hediff_Bukkake added to pawn -> just combine the two
		public override bool TryMergeWith(Hediff other)
		{
			if (other == null || other.def != this.def)
			{
				return false;
			}
			return true;
		}

		private float CalculateBukkakeLevel()
		{
			float num = 0f;
			for (int i = 0; i < hediffs_semen.Count; i++)
			{
				num += hediffs_semen[i].Severity * semenWeight;
			}
			return num;
		}

		//class for handling drawing of the individual splatches
		private class SemenSplatch
		{
			public readonly Hediff_Semen hediff_Semen;
			public readonly Material semenMaterial;
			private bool mirrorMesh;

			private const float maxSize = 0.20f;//1.0 = 1 tile

			//data taken from SemenHelper.cs:
			private readonly float[] xAdjust;
			private readonly float[] zAdjust;
			private readonly float[] yAdjust;

			public SemenSplatch(Hediff_Semen hediff, BodyTypeDef bodyType, BodyPartDef bodyPart, bool leftSide = false, int semenType = SemenHelper.CUM_NORMAL)
			{
				hediff_Semen = hediff;
				semenMaterial = new Material(BukkakeContent.pickRandomSplatch());//needs to create new material in order to allow for different colors
				semenMaterial.SetTextureScale("_MainTex", new Vector2(-1, 1));

				//set color:
				switch (semenType)
				{
					case SemenHelper.CUM_NORMAL:
						semenMaterial.color = SemenHelper.color_normal;
						break;
					case SemenHelper.CUM_INSECT:
						semenMaterial.color = SemenHelper.color_insect;
						break;
					case SemenHelper.CUM_MECHA:
						semenMaterial.color = SemenHelper.color_mecha;
						break;
				}

				mirrorMesh = (Rand.Value > 0.5f);//in 50% of the cases, flip mesh horizontally for more variance

				//x,y,z adjustments to draw splatches over the approximately correct locations; values stored in semen helper - accessed by unique combinations of bodyTypes and bodyParts
				SemenHelper.key k = new SemenHelper.key(bodyType, bodyPart);
				if (SemenHelper.splatchAdjust.Keys.Contains(k))
				{
					SemenHelper.values helperValues = SemenHelper.splatchAdjust[k];

					//invert, x-adjust (horizontal) depending on left/right body side:
					if (!leftSide)
					{
						float[] xAdjTemp = new float[4];
						for (int i = 0; i < xAdjTemp.Length; i++)
						{
							xAdjTemp[i] = helperValues.x[i] * -1f;
						}
						xAdjust = xAdjTemp;
					}
					else
					{
						xAdjust = helperValues.x;
					}

					zAdjust = helperValues.z;//vertical adjustment

				}
				else//fallback in the the key can't be found
				{
					if (Prefs.DevMode)
					{
						Log.Message("created semen splatch for undefined body type or part. BodyType: " + bodyType + " , BodyPart: " + bodyPart);
					}
					xAdjust = new float[] { 0f, 0f, 0f, 0f };
					zAdjust = new float[] { 0f, 0f, 0f, 0f };
				}

				//y adjustments: plane/layer of drawing, > 0 -> above certain objects, < 0 -> below
				SemenHelper.key_layer k2 = new SemenHelper.key_layer(leftSide, bodyPart);

				if (SemenHelper.layerAdjust.Keys.Contains(k2))
				{
					SemenHelper.values_layer helperValues_layer = SemenHelper.layerAdjust[k2];
					yAdjust = helperValues_layer.y;
				}
				else
				{
					yAdjust = new float[] { 0.02f, 0.02f, 0.02f, 0.02f };//over body in every direction
				}

			}

			public void Draw(Vector3 drawPos, Quaternion quat, bool forPortrait, int facingDir = 0, float angle = 0)
			{
				float size = maxSize * hediff_Semen.Severity;
				Mesh mesh = MeshMakerPlanes.NewPlaneMesh(size);

				if (mirrorMesh)//horizontal flip
				{
					mesh = flipMesh(mesh);
				}

				//rotation:
				if (angle == 0)//normal situation (pawn standing upright)
				{
					drawPos.x += xAdjust[facingDir];
					drawPos.z += zAdjust[facingDir];
				}
				else//if downed etc, more complex calculation becomes necessary
				{
					float radian = angle / 180 * (float)Math.PI;
					radian = -radian;
					drawPos.x += Mathf.Cos(radian) * xAdjust[hediff_Semen.pawn.Rotation.AsInt] - Mathf.Sin(radian) * zAdjust[facingDir];//facingDir doesn't appear to be chosen correctly in all cases
					drawPos.z += Mathf.Cos(radian) * zAdjust[hediff_Semen.pawn.Rotation.AsInt] + Mathf.Sin(radian) * xAdjust[facingDir];
				}

				//drawPos.y += yAdjust[facingDir];// 0.00: over body; 0.01: over body but under face, 0.02: over face, but under hair, -99 = "never" visible
				drawPos.y += yAdjust[facingDir];

				GenDraw.DrawMeshNowOrLater(mesh, drawPos, quat, semenMaterial, forPortrait);
			}

			//flips mesh UV horizontally, thereby mirroring the texture
			private Mesh flipMesh(Mesh meshToFlip)
			{
				var uvs = meshToFlip.uv;
				if (uvs.Length != 4)
				{
					return (meshToFlip);
				}
				for (var i = 0; i < uvs.Length; i++)
				{
					if (Mathf.Approximately(uvs[i].x, 1.0f))
						uvs[i].x = 0.0f;
					else
						uvs[i].x = 1.0f;
				}
				meshToFlip.uv = uvs;
				return (meshToFlip);
			}

		}
	}
}


code:
using RimWorld;
using System.Collections.Generic;
using System.Linq;
using Verse;

namespace rjw
{
	public class Cocoon : HediffWithComps
	{
		public int tickNext;

		public override void PostMake()
		{
			Severity = 1.0f;
			SetNextTick();
		}

		public override void ExposeData()
		{
			base.ExposeData();
			Scribe_Values.Look(ref tickNext, "tickNext", 1000, true);
		}

		public override void Tick()
		{
			if (Find.TickManager.TicksGame >= tickNext)
			{
				//Log.Message("Cocoon::Tick() " + base.xxx.get_pawnname(pawn));
				TryHealWounds();
				TryFeed();
				SetNextTick();
			}
		}

		public void TryHealWounds()
		{
			IEnumerable<Hediff> enumerable = from hd in pawn.health.hediffSet.hediffs
											 where !hd.IsTended()
											 select hd;
			if (enumerable != null)
			{
				foreach (Hediff item in enumerable)
				{
					HediffWithComps val = item as HediffWithComps;
					if (val != null && val.TendableNow())
						if (val.Bleeding)
						{
							//Log.Message("TrySealWounds " + xxx.get_pawnname(pawn) + ", Bleeding " + item.Label);
							HediffComp_TendDuration val2 = HediffUtility.TryGetComp<HediffComp_TendDuration>(val);
							val2.tendQuality = 2f;
							val2.tendTicksLeft = Find.TickManager.TicksGame;
							pawn.health.Notify_HediffChanged(item);
						}
						// infections  etc
						else// if (val.def.lethalSeverity > 0f)
						{
							//Log.Message("TryHeal " + xxx.get_pawnname(pawn) + ", infection(?) " + item.Label);
							HediffComp_TendDuration val2 = HediffUtility.TryGetComp<HediffComp_TendDuration>(val);
							val2.tendQuality = 2f;
							val2.tendTicksLeft = Find.TickManager.TicksGame;
							pawn.health.Notify_HediffChanged(item);
						}
				}
			}
		}

		public void TryFeed()
		{
			Need_Food need = pawn.needs.TryGetNeed<Need_Food>();
			if (need == null)
			{
				return;
			}

			if (need.CurLevel < 0.10f)
			{
				//Log.Message("Cocoon::TryFeed() " + xxx.get_pawnname(pawn) + " need to be fed");
				float nutrition_amount = need.MaxLevel / 5f;
				pawn.needs.food.CurLevel += nutrition_amount;
			}
		}

		public void SetNextTick()
		{
			//make actual tick every 16.6 sec
			tickNext = Find.TickManager.TicksGame + 1000;
			//Log.Message("Cocoon::SetNextTick() " + tickNext);
		}
	}
}


code:
using Verse;

namespace rjw
{
	public class Hediff_ID : Hediff
	{
		public override string LabelBase
		{
			get
			{
				if (!pawn.health.hediffSet.HasHediff(std.hiv.hediff_def))
					return base.LabelBase;
				else
					return "AIDS";
			}
		}
	}
}


code:
using Verse;

namespace rjw
{
	public class Hediff_RegularPrivatesImplant : Hediff_Implant
	{
		public override bool Visible
		{
			get
			{
				return xxx.config.show_regular_dick_and_vag;
			}
		}
	}
}


code:
using RimWorld;
using System.Linq;
using Verse;
using Verse.AI;

namespace rjw
{
	internal class Hediff_Orgasm : HediffWithComps
	{
		public override void PostAdd(DamageInfo? dinfo)
		{
			string key = "FeltOrgasm";
			string text = TranslatorFormattedStringExtensions.Translate(key, pawn.LabelIndefinite()).CapitalizeFirst();
			Messages.Message(text, pawn, MessageTypeDefOf.NeutralEvent);
		}
	}

	internal class Hediff_TransportCums : HediffWithComps
	{
		public override void PostAdd(DamageInfo? dinfo)
		{
			if (pawn.gender == Gender.Female)
			{
				string key = "CumsTransported";
				string text = TranslatorFormattedStringExtensions.Translate(key, pawn.LabelIndefinite()).CapitalizeFirst();
				Messages.Message(text, pawn, MessageTypeDefOf.NeutralEvent);
				PawnGenerationRequest req = new PawnGenerationRequest(PawnKindDefOf.Drifter, fixedGender:Gender.Male );
				Pawn cumSender = PawnGenerator.GeneratePawn(req);
				Find.WorldPawns.PassToWorld(cumSender);
				//Pawn cumSender = (from p in Find.WorldPawns.AllPawnsAlive where p.gender == Gender.Male select p).RandomElement<Pawn>();
				//--Log.Message("[RJW]" + this.GetType().ToString() + "PostAdd() - Sending " + xxx.get_pawnname(cumSender) + "'s cum into " + xxx.get_pawnname(pawn) + "'s vagina");
				PregnancyHelper.impregnate(pawn, cumSender, xxx.rjwSextype.Vaginal);
			}
			pawn.health.RemoveHediff(this);
		}
	}

	internal class Hediff_TransportEggs : HediffWithComps
	{
		public override void PostAdd(DamageInfo? dinfo)
		{
			if (pawn.gender == Gender.Female)
			{
				string key = "EggsTransported";
				string text = TranslatorFormattedStringExtensions.Translate(key, pawn.LabelIndefinite()).CapitalizeFirst();
				Messages.Message(text, pawn, MessageTypeDefOf.NeutralEvent);

				PawnKindDef spawn = PawnKindDefOf.Megascarab;
				PawnGenerationRequest req1 = new PawnGenerationRequest(spawn, fixedGender:Gender.Female );
				PawnGenerationRequest req2 = new PawnGenerationRequest(spawn, fixedGender:Gender.Male );
				Pawn implanter = PawnGenerator.GeneratePawn(req1);
				Pawn fertilizer = PawnGenerator.GeneratePawn(req2);
				Find.WorldPawns.PassToWorld(implanter);
				Find.WorldPawns.PassToWorld(fertilizer);
				Genital_Helper.sexualize_pawn(implanter);
				Genital_Helper.sexualize_pawn(fertilizer);
				//Pawn cumSender = (from p in Find.WorldPawns.AllPawnsAlive where p.gender == Gender.Male select p).RandomElement<Pawn>();
				//--Log.Message("[RJW]" + this.GetType().ToString() + "PostAdd() - Sending " + xxx.get_pawnname(cumSender) + "'s cum into " + xxx.get_pawnname(pawn) + "'s vagina");
				PregnancyHelper.impregnate(pawn, implanter, xxx.rjwSextype.Vaginal);
				PregnancyHelper.impregnate(pawn, fertilizer, xxx.rjwSextype.Vaginal);
			}
			pawn.health.RemoveHediff(this);
		}
	}
}


code:
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Verse;
using UnityEngine;

namespace rjw
{
	public class Hediff_Semen : HediffWithComps
	{
		public int semenType = SemenHelper.CUM_NORMAL;//-> different colors

		public string giverName = null;//not utilized right now, maybe in the future save origin of the semen

		public override string LabelInBrackets
		{
			get
			{
				StringBuilder stringBuilder = new StringBuilder();
				stringBuilder.Append(base.LabelInBrackets);
				if (this.sourceHediffDef != null)
				{
					if (stringBuilder.Length != 0)
					{
						stringBuilder.Append(", ");
					}
					stringBuilder.Append(this.sourceHediffDef.label);
				}
				else if (this.source != null)
				{
					if (stringBuilder.Length != 0)
					{
						stringBuilder.Append(", ");
					}
					stringBuilder.Append(this.source.label);
					if (this.sourceBodyPartGroup != null)
					{
						stringBuilder.Append(" ");
						stringBuilder.Append(this.sourceBodyPartGroup.LabelShort);
					}
				}
				return stringBuilder.ToString();
			}
		}

		public override string SeverityLabel
		{
			get
			{
				if (this.Severity == 0f)
				{
					return null;
				}
				return this.Severity.ToString("F1");
			}
		}

		public override bool TryMergeWith(Hediff other)
		{
			//if a new Semen hediff is added to the same body part, they are combined. if severity reaches more than 1, spillover to other body parts occurs

			Hediff_Semen hediff_Semen = other as Hediff_Semen;
			if (hediff_Semen != null && hediff_Semen.def == this.def && hediff_Semen.Part == base.Part && this.def.injuryProps.canMerge)
			{
				semenType = hediff_Semen.semenType;//take over new creature color

				float totalAmount = hediff_Semen.Severity + this.Severity;
				if (totalAmount > 1.0f)
				{
					BodyPartDef spillOverTo = SemenHelper.spillover(this.Part.def);//SemenHelper saves valid other body parts for spillover
					if (spillOverTo != null)
					{
						IEnumerable<BodyPartRecord> availableParts = SemenHelper.getAvailableBodyParts(pawn);//gets all non missing, valid body parts
						IEnumerable<BodyPartRecord> filteredParts = availableParts.Where(x => x.def == spillOverTo);//filters again for valid spill target
						BodyPartRecord spillPart = filteredParts.RandomElement<BodyPartRecord>();//then pick one
						if (spillPart != null)
						{
							SemenHelper.cumOn(pawn, spillPart, totalAmount - this.Severity, null, semenType);
						}
					}
				}

				return (base.TryMergeWith(other));

			}
			return (false);
		}

		public override void ExposeData()
		{
			base.ExposeData();
			Scribe_Values.Look<int>(ref semenType, "semenType", SemenHelper.CUM_NORMAL);

			if (Scribe.mode == LoadSaveMode.PostLoadInit && base.Part == null)
			{
				//Log.Error("Hediff_Semen has null part after loading.", false);
				this.pawn.health.hediffSet.hediffs.Remove(this);
				return;
			}
		}

		//handles the icon in the health tab and its color
		public override TextureAndColor StateIcon
		{
			get
			{
				TextureAndColor tex = TextureAndColor.None;
				Color color = Color.white;
				switch (semenType)
				{
					case SemenHelper.CUM_NORMAL:
						color = SemenHelper.color_normal;
						break;
					case SemenHelper.CUM_INSECT:
						color = SemenHelper.color_insect;
						break;
					case SemenHelper.CUM_MECHA:
						color = SemenHelper.color_mecha;
						break;
				}

				Texture2D tex2d = BukkakeContent.SemenIcon_little;
				switch (this.CurStageIndex)
				{
					case 0:
						tex2d = BukkakeContent.SemenIcon_little;
						break;
					case 1:
						tex2d = BukkakeContent.SemenIcon_some;
						break;
					case 2:
						tex2d = BukkakeContent.SemenIcon_dripping;
						break;
					case 3:
						tex2d = BukkakeContent.SemenIcon_drenched;
						break;
				}

				tex = new TextureAndColor(tex2d, color);

				return tex;
			}
		}

	}
}


code:
using Verse;

//Hediff worker for pawns' "lay down and submit" button
namespace rjw
{
	public class Hediff_Submitting: HediffWithComps
	{
		public override bool ShouldRemove {
			get
			{
				Pawn daddy = pawn.CarriedBy;
				if (daddy != null && daddy.Faction == pawn.Faction)
				{
					return true;
				}
				else
					return base.ShouldRemove;
			}
		}
	}
}


code:
using System.Collections.Generic;
using Verse;

namespace rjw
{
	//MicroComputer
	internal class Hediff_MicroComputer : Hediff_MechImplants
	{
		protected int nextEventTick = 60000;

		public override void ExposeData()
		{
			base.ExposeData();
			Scribe_Values.Look<int>(ref this.nextEventTick, "nextEventTick", 60000, false);
		}

		public override void PostMake()
		{
			base.PostMake();
			nextEventTick = Rand.Range(mcDef.minEventInterval, mcDef.maxEventInterval);
		}

		public override void Tick()
		{
			base.Tick();
			if (this.pawn.IsHashIntervalTick(1000))
			{
				if (this.ageTicks >= nextEventTick)
				{
					HediffDef randomEffectDef = DefDatabase<HediffDef>.GetNamed(randomEffect);
					if (randomEffectDef != null)
					{
						pawn.health.AddHediff(randomEffectDef);
					}
					else
					{
						//--Log.Message("[RJW]" + this.GetType().ToString() + "::Tick() - There is no Random Effect");
					}
					this.ageTicks = 0;
				}
			}
		}

		protected HediffDef_MechImplants mcDef
		{
			get { return ((HediffDef_MechImplants)def); }
		}

		protected List<string> randomEffects
		{
			get { return mcDef.randomHediffDefs; }
		}

		protected string randomEffect
		{
			get { return randomEffects.RandomElement<string>(); }
		}
	}
}


code:
using System.Collections.Generic;
using Verse;

namespace rjw
{
	[StaticConstructorOnStartup]
	internal class HediffDef_EnemyImplants : HediffDef
	{
		public string parentDef = "";
		public List<string> parentDefs = new List<string>();

		public bool IsParent(string defnam)
		{
			return parentDef == defnam || parentDefs.Contains(defnam);
		}
	}

	[StaticConstructorOnStartup]
	internal class HediffDef_InsectEgg : HediffDef_EnemyImplants
	{
		//this is filled from xml
		//1 day = 60000 ticks
		public int bornTick = 180000;//3 days
		public int abortTick = 60000;//1 day
		public float eggsize = 1;
	}

	[StaticConstructorOnStartup]
	internal class HediffDef_MechImplants : HediffDef_EnemyImplants
	{
		public List<string> randomHediffDefs = new List<string>();
		public int minEventInterval = 30000;
		public int maxEventInterval = 90000;
	}
}