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

namespace rjw
{
	[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("RJW_Fertility");
			reproduction.workerClass = typeof(PawnCapacityWorker_Fertility);
		}

		public static void inject_whoringtab()
		{
			//InjectTab(typeof(MainTab.MainTabWindow_Brothel), def => def.race?.Humanlike == true);
		}

		private static void InjectTab(Type tabType, Func<ThingDef, bool> qualifier)
		{
			var defs = DefDatabase<ThingDef>.AllDefs.Where(qualifier).ToList();
			defs.RemoveDuplicates();

			var tabBase = InspectTabManager.GetSharedInstance(tabType);

			foreach (var def in defs)
			{
				if (def.inspectorTabs == null || def.inspectorTabsResolved == null) continue;

				if (!def.inspectorTabs.Contains(tabType))
				{
					def.inspectorTabs.Add(tabType);
					def.inspectorTabsResolved.Add(tabBase);
					//Log.Message(def.defName+": "+def.inspectorTabsResolved.Select(d=>d.GetType().Name).Aggregate((a,b)=>a+", "+b));
				}
			}
		}

		private static void inject_recipes()
		{
			//--ModLog.Message(" 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.

			try
			{
				//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");

				// Inject the bondage gear recipes into their appropriate benches
				//if (xxx.config.bondage_gear_enabled)
				//{
					//fab_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("MakeChastityBeltO"));
					//tai_ben.AllRecipes.Add(DefDatabase<RecipeDef>.GetNamed("MakeChastityCage"));
					//tai_ben.AllRecipes.Add(DefDatabase<RecipeDef>.GetNamed("MakeBallGag"));
					//tai_ben.AllRecipes.Add(DefDatabase<RecipeDef>.GetNamed("MakeRingGag"));
				//}
			}
			catch
			{
				ModLog.Warning("Unable to inject recipes.");
				ModLog.Warning("Yay! Your medieval mod broke recipes. And, likely, parts too, expect errors.");
			}
		}

		/*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()
		{
			{//Humanoid Alien Races Framework 2.0
				xxx.xenophobia = DefDatabase<TraitDef>.GetNamedSilentFail("Xenophobia");
				if (xxx.xenophobia is null)
				{
					xxx.AlienFrameworkIsActive = false;
				}
				else
				{
					xxx.AlienFrameworkIsActive = true;
					if (RJWSettings.DevMode) ModLog.Message("Humanoid Alien Races 2.0 is detected. Xenophile and Xenophobe traits active.");
				}
			}

			{//relations-orientation mods
				{//RomanceDiversified
					xxx.straight = DefDatabase<TraitDef>.GetNamedSilentFail("Straight");
					xxx.faithful = DefDatabase<TraitDef>.GetNamedSilentFail("Faithful");
					xxx.philanderer = DefDatabase<TraitDef>.GetNamedSilentFail("Philanderer");
					xxx.polyamorous = DefDatabase<TraitDef>.GetNamedSilentFail("Polyamorous");
					if (xxx.straight is null || xxx.faithful is null || xxx.philanderer is null || xxx.polyamorous is null)
					{
						xxx.RomanceDiversifiedIsActive = false;
					}
					else
					{
						xxx.RomanceDiversifiedIsActive = true;
						if (RJWSettings.DevMode) ModLog.Message("RomanceDiversified is detected.");
					}
				}

				{//[SYR] Individuality
					xxx.SYR_CreativeThinker = DefDatabase<TraitDef>.GetNamedSilentFail("SYR_CreativeThinker");
					xxx.SYR_Haggler = DefDatabase<TraitDef>.GetNamedSilentFail("SYR_Haggler");
					if (xxx.SYR_CreativeThinker is null || xxx.SYR_Haggler is null)
					{
						xxx.IndividualityIsActive = false;
						if (RJWPreferenceSettings.sexuality_distribution == RJWPreferenceSettings.Rjw_sexuality.SYRIndividuality)
							RJWPreferenceSettings.sexuality_distribution = RJWPreferenceSettings.Rjw_sexuality.Vanilla;
					}
					else
					{
						xxx.IndividualityIsActive = true;
						if (RJWSettings.DevMode) ModLog.Message("Individuality is detected.");
					}
				}

				{//Psychology
					xxx.prude = DefDatabase<TraitDef>.GetNamedSilentFail("Prude");
					xxx.lecher = DefDatabase<TraitDef>.GetNamedSilentFail("Lecher");
					xxx.polygamous = DefDatabase<TraitDef>.GetNamedSilentFail("Polygamous");
					if (xxx.prude is null || xxx.lecher is null || xxx.polygamous is null)
					{
						xxx.PsychologyIsActive = false;
						if (RJWPreferenceSettings.sexuality_distribution == RJWPreferenceSettings.Rjw_sexuality.Psychology)
							RJWPreferenceSettings.sexuality_distribution = RJWPreferenceSettings.Rjw_sexuality.Vanilla;
					}
					else
					{
						xxx.PsychologyIsActive = true;
						if (RJWSettings.DevMode) ModLog.Message("Psychology is detected. (Note: only partially supported)");
					}
				}

				if (xxx.PsychologyIsActive == false && xxx.IndividualityIsActive == false)
				{
					if (RJWPreferenceSettings.sexuality_distribution != RJWPreferenceSettings.Rjw_sexuality.Vanilla)
					{
						RJWPreferenceSettings.sexuality_distribution = RJWPreferenceSettings.Rjw_sexuality.Vanilla;
					}
				}
			}

			{//SimpleSlavery
				xxx.Enslaved = DefDatabase<HediffDef>.GetNamedSilentFail("Enslaved");
				if (xxx.Enslaved is null)
				{
					xxx.SimpleSlaveryIsActive = false;
				}
				else
				{
					xxx.SimpleSlaveryIsActive = true;
					if (RJWSettings.DevMode) ModLog.Message("SimpleSlavery is detected.");
				}
			}

			{//[KV] Consolidated Traits
				xxx.RCT_NeatFreak = DefDatabase<TraitDef>.GetNamedSilentFail("RCT_NeatFreak");
				xxx.RCT_Savant = DefDatabase<TraitDef>.GetNamedSilentFail("RCT_Savant");
				xxx.RCT_Inventor = DefDatabase<TraitDef>.GetNamedSilentFail("RCT_Inventor");
				xxx.RCT_AnimalLover = DefDatabase<TraitDef>.GetNamedSilentFail("RCT_AnimalLover");
				if (xxx.RCT_NeatFreak is null || xxx.RCT_Savant is null || xxx.RCT_Inventor is null || xxx.RCT_AnimalLover is null)
				{
					xxx.CTIsActive = false;
				}
				else
				{
					xxx.CTIsActive = true;
					if (RJWSettings.DevMode) ModLog.Message("Consolidated Traits found, adding trait compatibility.");
				}
			}

			{//Rimworld of Magic
				xxx.Succubus = DefDatabase<TraitDef>.GetNamedSilentFail("Succubus");
				xxx.Warlock = DefDatabase<TraitDef>.GetNamedSilentFail("Warlock");
				xxx.TM_Mana = DefDatabase<NeedDef>.GetNamedSilentFail("TM_Mana");
				xxx.TM_ShapeshiftHD = DefDatabase<HediffDef>.GetNamedSilentFail("TM_ShapeshiftHD");
				if (xxx.Succubus is null || xxx.Warlock is null || xxx.TM_Mana is null || xxx.TM_ShapeshiftHD is null)
				{
					xxx.RoMIsActive = false;
				}
				else
				{
					xxx.RoMIsActive = true;
					if (RJWSettings.DevMode) ModLog.Message("Rimworld of Magic is detected.");
				}
			}

			{//Nightmare Incarnation
				xxx.NI_Need_Mana = DefDatabase<NeedDef>.GetNamedSilentFail("NI_Need_Mana");
				if (xxx.NI_Need_Mana is null)
				{
					xxx.NightmareIncarnationIsActive = false;
				}
				else
				{
					xxx.NightmareIncarnationIsActive = true;
					if (RJWSettings.DevMode) ModLog.Message("Nightmare Incarnation is detected.");
				}
			}

			{//DubsBadHygiene
				xxx.DBHThirst = DefDatabase<NeedDef>.GetNamedSilentFail("DBHThirst");
				if (xxx.DBHThirst is null)
				{
					xxx.DubsBadHygieneIsActive = false;
				}
				else
				{
					xxx.DubsBadHygieneIsActive = true;
					if (RJWSettings.DevMode) ModLog.Message("Dubs Bad Hygiene is detected.");
				}
			}

			{//Immortals
				xxx.IH_Immortal = DefDatabase<HediffDef>.GetNamedSilentFail("IH_Immortal");
				if (xxx.IH_Immortal is null)
				{
					xxx.ImmortalsIsActive = false;
				}
				else
				{
					xxx.ImmortalsIsActive = true;
					if (RJWSettings.DevMode) ModLog.Message("Immortals is detected.");
				}
			}

			{//Children and Pregnancy
				xxx.BabyState = DefDatabase<HediffDef>.GetNamedSilentFail("BabyState");
				xxx.PostPregnancy = DefDatabase<HediffDef>.GetNamedSilentFail("PostPregnancy");
				xxx.Lactating = DefDatabase<HediffDef>.GetNamedSilentFail("Lactating");
				xxx.NoManipulationFlag = DefDatabase<HediffDef>.GetNamedSilentFail("NoManipulationFlag");
				xxx.IGaveBirthFirstTime = DefDatabase<ThoughtDef>.GetNamedSilentFail("IGaveBirthFirstTime");
				xxx.IGaveBirth = DefDatabase<ThoughtDef>.GetNamedSilentFail("IGaveBirth");
				xxx.PartnerGaveBirth = DefDatabase<ThoughtDef>.GetNamedSilentFail("PartnerGaveBirth");
				if (xxx.BabyState is null || xxx.PostPregnancy is null || xxx.Lactating is null || xxx.NoManipulationFlag is null || xxx.IGaveBirthFirstTime is null || xxx.IGaveBirth is null ||  xxx.PartnerGaveBirth is null)
				{
					xxx.RimWorldChildrenIsActive = false;
				}
				else
				{
					xxx.RimWorldChildrenIsActive = true;
					if (RJWSettings.DevMode) ModLog.Message("Children&Pregnancy is detected.");
				}
			}

			{//Babies and Children
				xxx.BnC_RJW_PostPregnancy = DefDatabase<HediffDef>.GetNamedSilentFail("BnC_RJW_PostPregnancy");
			}

			{//Combat Extended
				xxx.MuscleSpasms = DefDatabase<HediffDef>.GetNamedSilentFail("MuscleSpasms");
				if (xxx.MuscleSpasms is null)
				{
					xxx.CombatExtendedIsActive = false;
				}
				else
				{
					xxx.CombatExtendedIsActive = true;
					if (RJWSettings.DevMode) ModLog.Message("Combat Extended is detected. Current compatibility unknown, use at your own risk. ");
				}
			}
		}

		static First()
		{
			//--ModLog.Message(" First::First() called");

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

			inject_Reproduction();
			//inject_recipes(); // No Longer needed.  Benches defined in recipe defs

			nymph_backstories.init();
			//bondage_gear_tradeability.init();

			var har = new Harmony("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",
				"Lost Forest"
			};
			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)
		{
			ModLog.Error("Initialization error:  Unable to find required mod '" + missing + "' in mod list");
		}

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


code:
using HarmonyLib;
using Verse;
using System;
using RimWorld;

/// <summary>
/// patches PawnGenerator to add genitals to pawns and spawn nymph when needed
/// </summary>
namespace rjw
{
	[HarmonyPatch(typeof(PawnGenerator), "GenerateNewPawnInternal")]
	static class Patch_PawnGenerator
	{
		[HarmonyPrefix]
		static void Before_GenerateNewPawnInternal(ref PawnGenerationRequest request)
		{
			if (Nymph_Generator.IsNymph(request))
			{
				request = new PawnGenerationRequest(
					kind: request.KindDef = Nymph_Generator.GetFixedNymphPawnKindDef(),
					canGeneratePawnRelations: request.CanGeneratePawnRelations = false,
					validatorPreGear: Nymph_Generator.IsNymphBodyType,
					validatorPostGear: Nymph_Generator.IsNymphBodyType,
					fixedGender: request.FixedGender = Nymph_Generator.RandomNymphGender()
					);
			}
		}

		[HarmonyPostfix]
		static void After_GenerateNewPawnInternal(ref PawnGenerationRequest request, ref Pawn __result)
		{
			if (Nymph_Generator.IsNymph(request))
			{
				Nymph_Generator.set_story(__result);
				Nymph_Generator.set_skills(__result);
			}

			//ModLog.Message("After_GenerateNewPawnInternal:: " + xxx.get_pawnname(__result));
			if (CompRJW.Comp(__result) != null && CompRJW.Comp(__result).orientation == Orientation.None)
			{
				//ModLog.Message("After_GenerateNewPawnInternal::Sexualize " + xxx.get_pawnname(__result));
				CompRJW.Comp(__result).Sexualize(__result);
			}
		}
	}
}

repo_name: ElToro/rjw

license: mit


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

/// <summary>
/// patches PawnUtility to fix humanlike childrens post birth
/// </summary>
namespace rjw
{
	[HarmonyPatch(typeof(PawnUtility), "TrySpawnHatchedOrBornPawn")]
	static class Patch_PawnUtility
	{
		//[HarmonyPrefix]
		//static void prefix_TrySpawnHatchedOrBornPawn(ref Pawn pawn, Thing motherOrEgg)
		//{
		//	//ModLog.Message("prefix_TrySpawnHatchedOrBornPawn::");
		//	//ModLog.Message(" " + __result);
		//	//ModLog.Message(" " + xxx.get_pawnname(pawn));
		//	//ModLog.Message(" " + xxx.get_pawnname(motherOrEgg as Pawn));
		//	//ModLog.Message(" " + RJWPregnancySettings.humanlike_pregnancy_enabled);
		//	//ModLog.Message(" " + xxx.is_human(pawn));
		//	//ModLog.Message(" " + !xxx.is_mechanoid(pawn));
		//	//string last_name = NameTriple.FromString(pawn.Name.ToStringFull).Last;
		//	//ModLog.Message(" prefix_TrySpawnHatchedOrBornPawn Baby surname will be " + last_name);
		//}

		//resets pawn/doesn't work for some races with C# constructors
		[HarmonyPostfix]
		static void postfix_TrySpawnHatchedOrBornPawn(ref bool __result, ref Pawn pawn, Thing motherOrEgg)
		{
			//ModLog.Message("postfix_TrySpawnHatchedOrBornPawn::");
			//ModLog.Message(" " + __result);
			//ModLog.Message(" " + xxx.get_pawnname(pawn));
			//ModLog.Message(" " + xxx.get_pawnname(motherOrEgg as Pawn));
			//ModLog.Message(" " + RJWPregnancySettings.humanlike_pregnancy_enabled);
			//ModLog.Message(" " + xxx.is_human(pawn));
			//ModLog.Message(" " + !xxx.is_mechanoid(pawn));
			//string last_name = NameTriple.FromString(pawn.Name.ToStringFull).Last;
			//ModLog.Message(" postfix_TrySpawnHatchedOrBornPawn 1 Baby surname will be " + last_name);
			//var skin_whiteness = pawn.story.melanin;
			//var last_name1 = pawn.story.birthLastName;
			if (__result
				&& RJWPregnancySettings.humanlike_pregnancy_enabled
				&& xxx.is_human(pawn)
				&& !xxx.is_mechanoid(pawn))
			{
				changestory(pawn);
				removeimplants(pawn);
				removeskills(pawn);
				grow(pawn);
			}
			//ModLog.Message(" postfix_TrySpawnHatchedOrBornPawn 2 Baby surname will be " + NameTriple.FromString(pawn.Name.ToStringFull).Last);
			//pawn.story.melanin = skin_whiteness;
			//pawn.story.birthLastName = last_name1;
		}

		static void removeimplants(Pawn pawn)
		{
			if (!pawn.health.hediffSet.hediffs.NullOrEmpty())
			{
				var hdlist = pawn.health.hediffSet.hediffs.ToList();
				foreach (Hediff hd in hdlist)
				{
					if (hd == null) continue;

					// remove DeathAcidifier
					//if (hd.def.defName == "DeathAcidifier")
					//{
					//	pawn.health.RemoveHediff(hd);
					//	continue;
					//}

					// remove implants
					if (hd is Hediff_Implant)
					{
						var part = hd.Part;
						pawn.health.RestorePart(part);
					}

					// remove immortality
					if (xxx.ImmortalsIsActive && hd.def == xxx.IH_Immortal)
					{
						pawn.health.RemoveHediff(hd);
					}
				}
			}
		}

		//resets/doest work for some races
		static void changestory(Pawn pawn)
		{
			if (!xxx.RimWorldChildrenIsActive)
			{
				pawn.story.childhood = null;
				pawn.story.adulthood = null;

				// set child to tribal
				try
				{
					Backstory bs = null;
					BackstoryDatabase.TryGetWithIdentifier("rjw_childT", out bs);

					pawn.story.childhood = bs;
				}
				catch (Exception e)
				{
					ModLog.Warning(e.ToString());
				}
			}
		}

		static void grow(Pawn pawn)
		{
			if (!xxx.RimWorldChildrenIsActive)
			{
				// add growing
				if (!pawn.Dead)
				{
					pawn.health.AddHediff(xxx.RJW_BabyState, null, null);
					Hediff_SimpleBaby pawnstate = (Hediff_SimpleBaby)pawn.health.hediffSet.GetFirstHediffOfDef(xxx.RJW_BabyState);
					if (pawnstate != null)
					{
						pawnstate.GrowUpTo(0, true);
					}
				}
			}
		}

		static void removeskills(Pawn pawn)
		{
			// remove skills
			foreach (var skill in pawn.skills?.skills)
				skill.Level = 0;
		}
	}
}


code:
using HarmonyLib;
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;
			if (CompRJW.Comp(pawn) == null) return;

			Rect rectNew = new Rect(CharacterCardUtility.BasePawnCardSize.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;
using System;
using System.Collections.Generic;
using System.Linq;
using Multiplayer.API;

namespace rjw
{
	//TODO: check slime stuff working in mp
	//TODO: separate menus in sub scripts
	//TODO: add demon switch parts
	//TODO: figure out and make interface?
	//TODO: add vibration toggle for bionics(+50% partner satisfaction?)
	public class Dialog_Sexcard : Window
	{
		private readonly Pawn pawn;

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

		public static breasts Breasts;
		public enum breasts
		{
			selectone,
			none,
			featureless_chest,
			flat_breasts,
			small_breasts,
			average_breasts,
			large_breasts,
			huge_breasts,
			slime_breasts,
		};

		public static anuses Anuses;
		public enum anuses
		{
			selectone,
			none,
			micro_anus,
			tight_anus,
			average_anus,
			loose_anus,
			gaping_anus,
			slime_anus,
		};

		public static vaginas Vaginas;
		public enum vaginas
		{
			selectone,
			none,
			micro_vagina,
			tight_vagina,
			average_vagina,
			loose_vagina,
			gaping_vagina,
			slime_vagina,
			feline_vagina,
			canine_vagina,
			equine_vagina,
			dragon_vagina,
		};

		public static penises Penises;
		public enum penises
		{
			selectone,
			none,
			micro_penis,
			small_penis,
			average_penis,
			big_penis,
			huge_penis,
			slime_penis,
			feline_penis,
			canine_penis,
			equine_penis,
			dragon_penis,
			raccoon_penis,
			hemipenis,
			crocodilian_penis,
		};

		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");//rjw

			Text.Font = GameFont.Tiny;
			float num = rect1.y + 40f;
			Rect row1 = new Rect(10f, num, rect.width - 8f, 24f);//sexuality
			Rect row2 = new Rect(10f, num + 24, rect.width - 8f, 24f);//quirks
			Rect row3 = new Rect(10f, num + 48, rect.width - 8f, 24f);//whore price

			//Rect sexuality_button = new Rect(10f, rect1.height - 0f, rect.width - 8f, 24f);//change sex pref
			Rect button1 = new Rect(10f, rect1.height - 10f, rect.width - 8f, 24f);//re sexualize
			Rect button2 = new Rect(10f, rect1.height - 34f, rect.width - 8f, 24f);//archtech toggle
			Rect button3 = new Rect(10f, rect1.height - 58f, rect.width - 8f, 24f);//breast
			Rect button4 = new Rect(10f, rect1.height - 82f, rect.width - 8f, 24f);//anus
			Rect button5 = new Rect(10f, rect1.height - 106f, rect.width - 8f, 24f);//vagina
			Rect button6 = new Rect(10f, rect1.height - 130f, rect.width - 8f, 24f);//penis 1
			Rect button7 = new Rect(10f, rect1.height - 154f, rect.width - 8f, 24f);//penis 2

			DrawSexuality(pawn, row1);
			DrawQuirks(pawn, row2);
			DrawWhoring(pawn, row3);

			// TODO: Add translations. or not
			if (Prefs.DevMode || Current.ProgramState != ProgramState.Playing)
			{
				if (Widgets.ButtonText(button1, Current.ProgramState != ProgramState.Playing ? "Reroll sexuality" : "[DEV] Reroll sexuality"))
				{
					Re_sexualize(pawn);
				}
			}
			if (pawn.health.hediffSet.HasHediff(Genital_Helper.archotech_penis) || pawn.health.hediffSet.HasHediff(Genital_Helper.archotech_vagina))
			{
				if (pawn.health.hediffSet.HasHediff(HediffDef.Named("ImpregnationBlocker")))
				{
					if (Widgets.ButtonText(button2, "[Archotech genitalia] Enable reproduction"))
					{
						Change_Archotechmode(pawn);
					}
				}
				else if (!pawn.health.hediffSet.HasHediff(HediffDef.Named("FertilityEnhancer")))
				{
					if (Widgets.ButtonText(button2, "[Archotech genitalia] Enchance fertility"))
					{
						Change_Archotechmode(pawn);
					}
				}
				else if (Widgets.ButtonText(button2, "[Archotech genitalia] Disable reproduction"))
				{
					Change_Archotechmode(pawn);
				}
			}
			// TODO: add mp synchronizers
			// TODO: clean that mess
			// TODO: add demon toggles
			if (MP.IsInMultiplayer)
				return;

			//List<String> Parts = null;
			//if (xxx.is_slime(pawn))
			//	Parts = new List<string>(DefDatabase<StringListDef>.GetNamed("SlimeMorphFilters").strings);
			//if (xxx.is_demon(pawn))
			//	Parts = new List<string>(DefDatabase<StringListDef>.GetNamed("DemonMorphFilters").strings);
			// TODO: replace that mess with floating menu
			//if (Event.current.type == EventType.MouseDown && Event.current.button == 0 && Event.current.clickCount == 2 && Mouse.IsOver(rect))
			//{
				//Event.current.Use();//?
				//DrawMedOperationsTab(0, pawn, Parts,0);
			//}

			//if (Parts.Any() && (pawn.IsColonistPlayerControlled || pawn.IsPrisonerOfColony))
			//if (xxx.is_slime(pawn) && (pawn.IsColonistPlayerControlled || pawn.IsPrisonerOfColony))
			//{
			//	BodyPartRecord bpr_genitalia = Genital_Helper.get_genitals(pawn);
			//	BodyPartRecord bpr_breasts = Genital_Helper.get_breasts(pawn);
			//	BodyPartRecord bpr_anus = Genital_Helper.get_anus(pawn);
			//	BodyPartRecord bpr = null;
			//	HediffDef hed = null;

			//	if (Widgets.ButtonText(button3, "Morph breasts"))
			//	{
			//		Find.WindowStack.Add(new FloatMenu(new List<FloatMenuOption>()
			//	{
			//		new FloatMenuOption("none", (() => Breasts = breasts.none), MenuOptionPriority.Default),
			//		new FloatMenuOption(Genital_Helper.featureless_chest.label.CapitalizeFirst(), (() => Breasts = breasts.featureless_chest), MenuOptionPriority.Default),
			//		new FloatMenuOption(Genital_Helper.flat_breasts.label.CapitalizeFirst(), (() => Breasts = breasts.flat_breasts), MenuOptionPriority.Default),
			//		new FloatMenuOption(Genital_Helper.small_breasts.label.CapitalizeFirst(), (() => Breasts = breasts.small_breasts), MenuOptionPriority.Default),
			//		new FloatMenuOption(Genital_Helper.average_breasts.label.CapitalizeFirst(), (() => Breasts = breasts.average_breasts), MenuOptionPriority.Default),
			//		new FloatMenuOption(Genital_Helper.large_breasts.label.CapitalizeFirst(), (() => Breasts = breasts.large_breasts), MenuOptionPriority.Default),
			//		new FloatMenuOption(Genital_Helper.huge_breasts.label.CapitalizeFirst(), (() => Breasts = breasts.huge_breasts), MenuOptionPriority.Default),
			//		new FloatMenuOption(Genital_Helper.slime_breasts.label.CapitalizeFirst(), (() => Breasts = breasts.slime_breasts), MenuOptionPriority.Default),
			//	}));
			//	}
			//	switch (Breasts)
			//	{
			//		case breasts.none:
			//			bpr = bpr_breasts;
			//			break;
			//		case breasts.featureless_chest:
			//			bpr = bpr_breasts;
			//			hed = Genital_Helper.featureless_chest;
			//			break;
			//		case breasts.flat_breasts:
			//			bpr = bpr_breasts;
			//			hed = Genital_Helper.flat_breasts;
			//			break;
			//		case breasts.small_breasts:
			//			bpr = bpr_breasts;
			//			hed = Genital_Helper.small_breasts;
			//			break;
			//		case breasts.average_breasts:
			//			bpr = bpr_breasts;
			//			hed = Genital_Helper.average_breasts;
			//			break;
			//		case breasts.large_breasts:
			//			bpr = bpr_breasts;
			//			hed = Genital_Helper.large_breasts;
			//			break;
			//		case breasts.huge_breasts:
			//			bpr = bpr_breasts;
			//			hed = Genital_Helper.huge_breasts;
			//			break;
			//		case breasts.slime_breasts:
			//			bpr = bpr_breasts;
			//			hed = Genital_Helper.slime_breasts;
			//			break;
			//		default:
			//			break;
			//	}

			//	if (Widgets.ButtonText(button4, "Morph anus"))
			//	{
			//		Find.WindowStack.Add(new FloatMenu(new List<FloatMenuOption>()
			//		{
			//			new FloatMenuOption("none", (() => Anuses = anuses.none), MenuOptionPriority.Default),
			//			new FloatMenuOption(Genital_Helper.micro_anus.label.CapitalizeFirst(), (() => Anuses = anuses.micro_anus), MenuOptionPriority.Default),
			//			new FloatMenuOption(Genital_Helper.tight_anus.label.CapitalizeFirst(), (() => Anuses = anuses.tight_anus), MenuOptionPriority.Default),
			//			new FloatMenuOption(Genital_Helper.average_anus.label.CapitalizeFirst(), (() => Anuses = anuses.average_anus), MenuOptionPriority.Default),
			//			new FloatMenuOption(Genital_Helper.loose_anus.label.CapitalizeFirst(), (() => Anuses = anuses.loose_anus), MenuOptionPriority.Default),
			//			new FloatMenuOption(Genital_Helper.gaping_anus.label.CapitalizeFirst(), (() => Anuses = anuses.gaping_anus), MenuOptionPriority.Default),
			//			new FloatMenuOption(Genital_Helper.slime_anus.label.CapitalizeFirst(), (() => Anuses = anuses.slime_anus), MenuOptionPriority.Default),
			//		}));
			//	}
			//	switch (Anuses)
			//	{
			//		case anuses.none:
			//			bpr = bpr_anus;
			//			break;
			//		case anuses.micro_anus:
			//			bpr = bpr_anus;
			//			hed = Genital_Helper.micro_anus;
			//			break;
			//		case anuses.tight_anus:
			//			bpr = bpr_anus;
			//			hed = Genital_Helper.tight_anus;
			//			break;
			//		case anuses.average_anus:
			//			bpr = bpr_anus;
			//			hed = Genital_Helper.average_anus;
			//			break;
			//		case anuses.loose_anus:
			//			bpr = bpr_anus;
			//			hed = Genital_Helper.loose_anus;
			//			break;
			//		case anuses.gaping_anus:
			//			bpr = bpr_anus;
			//			hed = Genital_Helper.gaping_anus;
			//			break;
			//		case anuses.slime_anus:
			//			bpr = bpr_anus;
			//			hed = Genital_Helper.slime_anus;
			//			break;
			//		default:
			//			break;
			//	}

			//	if (Widgets.ButtonText(button5, "Morph vagina"))
			//	{
			//		Find.WindowStack.Add(new FloatMenu(new List<FloatMenuOption>()
			//		{
			//			new FloatMenuOption("none", (() => Vaginas = vaginas.none), MenuOptionPriority.Default),
			//			new FloatMenuOption(Genital_Helper.micro_vagina.label.CapitalizeFirst(), (() => Vaginas = vaginas.micro_vagina), MenuOptionPriority.Default),
			//			new FloatMenuOption(Genital_Helper.tight_vagina.label.CapitalizeFirst(), (() => Vaginas = vaginas.tight_vagina), MenuOptionPriority.Default),
			//			new FloatMenuOption(Genital_Helper.average_vagina.label.CapitalizeFirst(), (() => Vaginas = vaginas.average_vagina), MenuOptionPriority.Default),
			//			new FloatMenuOption(Genital_Helper.loose_vagina.label.CapitalizeFirst(), (() => Vaginas = vaginas.loose_vagina), MenuOptionPriority.Default),
			//			new FloatMenuOption(Genital_Helper.gaping_vagina.label.CapitalizeFirst(), (() => Vaginas = vaginas.gaping_vagina), MenuOptionPriority.Default),
			//			new FloatMenuOption(Genital_Helper.slime_vagina.label.CapitalizeFirst(), (() => Vaginas = vaginas.slime_vagina), MenuOptionPriority.Default),
			//			new FloatMenuOption(Genital_Helper.feline_vagina.label.CapitalizeFirst(), (() => Vaginas = vaginas.feline_vagina), MenuOptionPriority.Default),
			//			new FloatMenuOption(Genital_Helper.canine_vagina.label.CapitalizeFirst(), (() => Vaginas = vaginas.canine_vagina), MenuOptionPriority.Default),
			//			new FloatMenuOption(Genital_Helper.equine_vagina.label.CapitalizeFirst(), (() => Vaginas = vaginas.equine_vagina), MenuOptionPriority.Default),
			//			new FloatMenuOption(Genital_Helper.dragon_vagina.label.CapitalizeFirst(), (() => Vaginas = vaginas.dragon_vagina), MenuOptionPriority.Default),
			//		}));
			//	}
			//	switch (Vaginas)
			//	{
			//		case vaginas.none:
			//			bpr = bpr_genitalia;
			//			break;
			//		case vaginas.micro_vagina:
			//			bpr = bpr_genitalia;
			//			hed = Genital_Helper.micro_vagina;
			//			break;
			//		case vaginas.tight_vagina:
			//			bpr = bpr_genitalia;
			//			hed = Genital_Helper.tight_vagina;
			//			break;
			//		case vaginas.average_vagina:
			//			bpr = bpr_genitalia;
			//			hed = Genital_Helper.average_vagina;
			//			break;
			//		case vaginas.loose_vagina:
			//			bpr = bpr_genitalia;
			//			hed = Genital_Helper.loose_vagina;
			//			break;
			//		case vaginas.gaping_vagina:
			//			bpr = bpr_genitalia;
			//			hed = Genital_Helper.gaping_vagina;
			//			break;
			//		case vaginas.slime_vagina:
			//			bpr = bpr_genitalia;
			//			hed = Genital_Helper.slime_vagina;
			//			break;
			//		case vaginas.feline_vagina:
			//			bpr = bpr_genitalia;
			//			hed = Genital_Helper.feline_vagina;
			//			break;
			//		case vaginas.canine_vagina:
			//			bpr = bpr_genitalia;
			//			hed = Genital_Helper.canine_vagina;
			//			break;
			//		case vaginas.equine_vagina:
			//			bpr = bpr_genitalia;
			//			hed = Genital_Helper.equine_vagina;
			//			break;
			//		case vaginas.dragon_vagina:
			//			bpr = bpr_genitalia;
			//			hed = Genital_Helper.dragon_vagina;
			//			break;
			//		default:
			//			break;
			//	}

			//	if (Widgets.ButtonText(button6, "Morph penis"))
			//	{
			//		Find.WindowStack.Add(new FloatMenu(new List<FloatMenuOption>()
			//		{
			//			new FloatMenuOption("none", (() => Penises = penises.none), MenuOptionPriority.Default),
			//			new FloatMenuOption(Genital_Helper.micro_penis.label.CapitalizeFirst(), (() => Penises = penises.micro_penis), MenuOptionPriority.Default),
			//			new FloatMenuOption(Genital_Helper.small_penis.label.CapitalizeFirst(), (() => Penises = penises.small_penis), MenuOptionPriority.Default),
			//			new FloatMenuOption(Genital_Helper.average_penis.label.CapitalizeFirst(), (() => Penises = penises.average_penis), MenuOptionPriority.Default),
			//			new FloatMenuOption(Genital_Helper.big_penis.label.CapitalizeFirst(), (() => Penises = penises.big_penis), MenuOptionPriority.Default),
			//			new FloatMenuOption(Genital_Helper.huge_penis.label.CapitalizeFirst(), (() => Penises = penises.huge_penis), MenuOptionPriority.Default),
			//			new FloatMenuOption(Genital_Helper.slime_penis.label.CapitalizeFirst(), (() => Penises = penises.slime_penis), MenuOptionPriority.Default),
			//			new FloatMenuOption(Genital_Helper.feline_penis.label.CapitalizeFirst(), (() => Penises = penises.feline_penis), MenuOptionPriority.Default),
			//			new FloatMenuOption(Genital_Helper.canine_penis.label.CapitalizeFirst(), (() => Penises = penises.canine_penis), MenuOptionPriority.Default),
			//			new FloatMenuOption(Genital_Helper.equine_penis.label.CapitalizeFirst(), (() => Penises = penises.equine_penis), MenuOptionPriority.Default),
			//			new FloatMenuOption(Genital_Helper.dragon_penis.label.CapitalizeFirst(), (() => Penises = penises.dragon_penis), MenuOptionPriority.Default),
			//			new FloatMenuOption(Genital_Helper.raccoon_penis.label.CapitalizeFirst(), (() => Penises = penises.raccoon_penis), MenuOptionPriority.Default),
			//			new FloatMenuOption(Genital_Helper.hemipenis.label.CapitalizeFirst(), (() => Penises = penises.hemipenis), MenuOptionPriority.Default),
			//			new FloatMenuOption(Genital_Helper.crocodilian_penis.label.CapitalizeFirst(), (() => Penises = penises.crocodilian_penis), MenuOptionPriority.Default),
			//		}));
			//	}
			//	switch (Penises)
			//	{
			//		case penises.none:
			//			bpr = bpr_genitalia;
			//			break;
			//		case penises.micro_penis:
			//			bpr = bpr_genitalia;
			//			hed = Genital_Helper.micro_penis;
			//			break;
			//		case penises.small_penis:
			//			bpr = bpr_genitalia;
			//			hed = Genital_Helper.small_penis;
			//			break;
			//		case penises.average_penis:
			//			bpr = bpr_genitalia;
			//			hed = Genital_Helper.average_penis;
			//			break;
			//		case penises.big_penis:
			//			bpr = bpr_genitalia;
			//			hed = Genital_Helper.big_penis;
			//			break;
			//		case penises.huge_penis:
			//			bpr = bpr_genitalia;
			//			hed = Genital_Helper.huge_penis;
			//			break;
			//		case penises.slime_penis:
			//			bpr = bpr_genitalia;
			//			hed = Genital_Helper.slime_penis;
			//			break;
			//		case penises.feline_penis:
			//			bpr = bpr_genitalia;
			//			hed = Genital_Helper.feline_penis;
			//			break;
			//		case penises.canine_penis:
			//			bpr = bpr_genitalia;
			//			hed = Genital_Helper.canine_penis;
			//			break;
			//		case penises.equine_penis:
			//			bpr = bpr_genitalia;
			//			hed = Genital_Helper.equine_penis;
			//			break;
			//		case penises.dragon_penis:
			//			bpr = bpr_genitalia;
			//			hed = Genital_Helper.dragon_penis;
			//			break;
			//		case penises.raccoon_penis:
			//			bpr = bpr_genitalia;
			//			hed = Genital_Helper.raccoon_penis;
			//			break;
			//		case penises.hemipenis:
			//			bpr = bpr_genitalia;
			//			hed = Genital_Helper.hemipenis;
			//			break;
			//		case penises.crocodilian_penis:
			//			bpr = bpr_genitalia;
			//			hed = Genital_Helper.crocodilian_penis;
			//			break;
			//		default:
			//			break;
			//	}

			//	if (bpr != null)
			//	{
			//		//Log.Message("start ");
			//		if (bpr != bpr_genitalia)
			//		{
			//			if (pawn.needs.TryGetNeed<Need_Food>().CurLevel > 0.5f)
			//			{
			//				pawn.needs.food.CurLevel -= 0.5f;
			//				pawn.health.RestorePart(bpr);
			//				if (hed != null)
			//					pawn.health.AddHediff(hed, bpr);
			//			}
			//			Anuses = anuses.selectone;
			//			Breasts = breasts.selectone;
			//		}
			//		else if (bpr == bpr_genitalia && Vaginas != vaginas.selectone)
			//		{
			//			if (pawn.needs.TryGetNeed<Need_Food>().CurLevel > 0.5f)
			//			{
			//				pawn.needs.food.CurLevel -= 0.5f;
			//				List<Hediff> list = new List<Hediff>();
			//				foreach (Hediff heddif in pawn.health.hediffSet.hediffs.Where(x =>
			//						x.Part == bpr &&
			//						x.def.defName.ToLower().Contains("vagina")))
			//					list.Add(heddif);

			//				foreach (Hediff heddif in list)
			//					pawn.health.hediffSet.hediffs.Remove(heddif);

			//				if (hed != null)
			//					pawn.health.AddHediff(hed, bpr);
			//			}
			//			Vaginas = vaginas.selectone;
			//		}
			//		else if (bpr == bpr_genitalia && Penises != penises.selectone)
			//		{
			//			if (pawn.needs.TryGetNeed<Need_Food>().CurLevel > 0.5f)
			//			{
			//				pawn.needs.food.CurLevel -= 0.5f;
			//				List<Hediff> list = new List<Hediff>();
			//				foreach (Hediff heddif in pawn.health.hediffSet.hediffs.Where(x =>
			//						x.Part == bpr &&
			//						x.def.defName.ToLower().Contains("penis") ||
			//						x.def.defName.ToLower().Contains("tentacle")))
			//					list.Add(heddif);

			//				foreach (Hediff heddif in list)
			//					pawn.health.hediffSet.hediffs.Remove(heddif);

			//				if (hed != null)
			//					pawn.health.AddHediff(hed, bpr);
			//			}
			//			Penises = penises.selectone;
			//		}
			//		//Log.Message("end ");
			//	}
			//}
		}

		static void DrawSexuality(Pawn pawn, Rect row)
		{
			string sexuality;

			if (RJWPreferenceSettings.sexuality_distribution == RJWPreferenceSettings.Rjw_sexuality.Vanilla)
				CompRJW.VanillaTraitCheck(pawn);
			if (RJWPreferenceSettings.sexuality_distribution == RJWPreferenceSettings.Rjw_sexuality.SYRIndividuality)
				CompRJW.CopyIndividualitySexuality(pawn);
			if (RJWPreferenceSettings.sexuality_distribution == RJWPreferenceSettings.Rjw_sexuality.Psychology)
				CompRJW.CopyPsychologySexuality(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;
			}

			//allow to change pawn sexuality for:
			//own hero, game start
			if (RJWPreferenceSettings.sexuality_distribution == RJWPreferenceSettings.Rjw_sexuality.RimJobWorld &&
				(((Current.ProgramState == ProgramState.Playing &&
				pawn.IsDesignatedHero() && pawn.IsHeroOwner()) ||
				Prefs.DevMode) ||
				Current.ProgramState == ProgramState.Entry))

			{
				if (Widgets.ButtonText(row, "Sexuality: " + sexuality, false))
				{
					Find.WindowStack.Add(new FloatMenu(new List<FloatMenuOption>()			//this needs fixing in 1.1 with vanilla orientation traits
					{
						new FloatMenuOption("Asexual", (() => Change_orientation(pawn, Orientation.Asexual)), MenuOptionPriority.Default),
						new FloatMenuOption("Pansexual", (() => Change_orientation(pawn, Orientation.Pansexual)), MenuOptionPriority.Default),
						new FloatMenuOption("Heterosexual", (() => Change_orientation(pawn, Orientation.Heterosexual)), MenuOptionPriority.Default),
						new FloatMenuOption("MostlyHeterosexual", (() => Change_orientation(pawn, Orientation.MostlyHeterosexual)), MenuOptionPriority.Default),
						new FloatMenuOption("LeaningHeterosexual", (() => Change_orientation(pawn, Orientation.LeaningHeterosexual)), MenuOptionPriority.Default),
						new FloatMenuOption("Bisexual", (() => Change_orientation(pawn, Orientation.Bisexual)), MenuOptionPriority.Default),
						new FloatMenuOption("LeaningHomosexual", (() => Change_orientation(pawn, Orientation.LeaningHomosexual)), MenuOptionPriority.Default),
						new FloatMenuOption("MostlyHomosexual", (() => Change_orientation(pawn, Orientation.MostlyHomosexual)), MenuOptionPriority.Default),
						new FloatMenuOption("Homosexual", (() => Change_orientation(pawn, Orientation.Homosexual)), MenuOptionPriority.Default),
					}));
				}
			}
			else
			{
				Widgets.Label(row, "Sexuality: " + sexuality);
				if (Mouse.IsOver(row))
					Widgets.DrawHighlight(row);
			}
		}

		static void DrawQuirks(Pawn pawn, Rect row)
		{

			var quirks = Quirk.All
				.Where(quirk => pawn.Has(quirk))
				.OrderBy(quirk => quirk.Key)
				.ToList();

			// Not actually localized.
			var quirkString = quirks.Select(quirk => quirk.Key).ToCommaList();

			if ((Current.ProgramState == ProgramState.Playing &&
				pawn.IsDesignatedHero() && pawn.IsHeroOwner() ||
				Prefs.DevMode) ||
				Current.ProgramState == ProgramState.Entry)

			{
				var quirksAll = Quirk.All
								.OrderBy(quirk => quirk.Key)
								.ToList();

				if (!RJWSettings.DevMode)
				{
					quirksAll.Remove(Quirk.Breeder);
					quirksAll.Remove(Quirk.Incubator);
				}

				if (xxx.is_insect(pawn))
					quirksAll.Add(Quirk.Incubator);

				if (Widgets.ButtonText(row, "Quirks".Translate() + quirkString, false))
				{
					var list = new List<FloatMenuOption>();
					list.Add(new FloatMenuOption("Reset", (() => QuirkAdder.Clear(pawn)), MenuOptionPriority.Default));
					foreach (Quirk quirk in quirksAll)
					{
						if (!pawn.Has(quirk))
							list.Add(new FloatMenuOption(quirk.Key, (() => QuirkAdder.Add(pawn, quirk))));
						//else
						//	list.Add(new FloatMenuOption(quirk.Key, (() => QuirkAdder.Remove(pawn, quirk))));
						//TODO: fix quirk description box in 1.1 menus
						//list.Add(new FloatMenuOption(quirk.Key, (() => QuirkAdder.Add(pawn, quirk)), MenuOptionPriority.Default, delegate
						//{
						//	TooltipHandler.TipRegion(row, quirk.LocaliztionKey.Translate(pawn.Named("pawn")));
						//}
						//));
					}
					Find.WindowStack.Add(new FloatMenu(list));
				}
			}
			else
			{
				// TODO: long quirk list support
				// This can be too long and line wrap.
				// Should probably be a vertical list like traits.
				Widgets.Label(row, "Quirks".Translate() + quirkString);

			}

			if (!Mouse.IsOver(row)) return;

			Widgets.DrawHighlight(row);
			if (quirks.NullOrEmpty())
			{
				TooltipHandler.TipRegion(row, "NoQuirks".Translate());
			}
			else
			{
				StringBuilder stringBuilder = new StringBuilder();
				foreach (var q in quirks)
				{
					stringBuilder.AppendLine(q.Key.Colorize(Color.yellow));
					stringBuilder.AppendLine(q.LocaliztionKey.Translate(pawn.Named("pawn")).AdjustedFor(pawn).Resolve());
					stringBuilder.AppendLine("");
				}
				string str = stringBuilder.ToString().TrimEndNewlines();
				TooltipHandler.TipRegion(row, str);
			}
		}

		static string DrawWhoring(Pawn pawn, Rect row)
		{
			string price;
			if (pawn.ageTracker.AgeBiologicalYears < RJWSettings.sex_minimum_age)
				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(row, "WhorePrice".Translate() + price);
			if (Mouse.IsOver(row))
				Widgets.DrawHighlight(row);
			return price;
		}

		[SyncMethod]
		static void Change_orientation(Pawn pawn, Orientation orientation)
		{
			CompRJW.Comp(pawn).orientation = orientation;
		}

		[SyncMethod]
		static void Change_Archotechmode(Pawn pawn)
		{
			BodyPartRecord genitalia = Genital_Helper.get_genitalsBPR(pawn);
			Hediff blocker = pawn.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("ImpregnationBlocker"));
			Hediff enhancer = pawn.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("FertilityEnhancer"));

			if (blocker != null)
			{
				pawn.health.RemoveHediff(blocker);
			}
			else if (enhancer == null)
			{
				pawn.health.AddHediff(HediffDef.Named("FertilityEnhancer"), genitalia);
			}
			else
			{
				if (enhancer != null)
					pawn.health.RemoveHediff(enhancer);
				pawn.health.AddHediff(HediffDef.Named("ImpregnationBlocker"), genitalia);
			}
		}

		[SyncMethod]
		static void Re_sexualize(Pawn pawn)
		{
			CompRJW.Comp(pawn).Sexualize(pawn, true);
		}

		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);
			SexualityCard(mainRect, pawn);
			if (Widgets.ButtonText(okRect, "CloseButton".Translate()) || flag)
			{
				Close();
			}
		}
	}
}


code:
using HarmonyLib;
using RimWorld;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Emit;
using System.Text;
using Verse;
using Verse.AI.Group;

namespace rjw
{
	class SocialCardUtilityPatch
	{
		[HarmonyPatch(typeof(SocialCardUtility))]
		[HarmonyPatch("DrawDebugOptions")]
		public static class Patch_SocialCardUtility_DrawDebugOptions
		{
			//Create a new menu option that will contain some of the relevant data for debugging RJW
			//Window space is limited, so keep to one line per pawn. Additional data may need a separate menu
			static FloatMenuOption newMenuOption(Pawn pawn) {
				return new FloatMenuOption("RJW WouldFuck", () =>
				{
					StringBuilder stringBuilder = new StringBuilder();
					stringBuilder.AppendLine();
					stringBuilder.AppendLine("canFuck: " + xxx.can_fuck(pawn) + ", canBeFucked: " + xxx.can_be_fucked(pawn) + ", loving: " + xxx.can_do_loving(pawn));
					stringBuilder.AppendLine("canRape: " + xxx.can_rape(pawn) + ", canBeRaped: " + xxx.can_get_raped(pawn));

					if (!pawn.IsColonist)
						if (pawn.Faction != null)
						{
							int pawnAmountOfSilvers = pawn.inventory.innerContainer.TotalStackCountOfDef(ThingDefOf.Silver);
							int caravanAmountOfSilvers = 0;
							Lord lord = pawn.GetLord();
							List<Pawn> caravanMembers = pawn.Map.mapPawns.PawnsInFaction(pawn.Faction).Where(x => x.GetLord() == lord && x.inventory?.innerContainer?.TotalStackCountOfDef(ThingDefOf.Silver) > 0).ToList();

							caravanAmountOfSilvers += caravanMembers.Sum(member => member.inventory.innerContainer.TotalStackCountOfDef(ThingDefOf.Silver));

							stringBuilder.AppendLine("pawnSilvers: " + pawnAmountOfSilvers + ", caravanSilvers: " + caravanAmountOfSilvers);
						}
					stringBuilder.AppendLine();

					stringBuilder.AppendLine("Humans - Colonists:");
					List<Pawn> pawns = pawn.Map.mapPawns.AllPawnsSpawned.Where(x => x != pawn && x.RaceProps.Humanlike && x.IsColonist).OrderBy(x => xxx.get_pawnname(x)).ToList();
					foreach (Pawn partner in pawns)
					{
						stringBuilder.AppendLine(partner.LabelShort + " (" + partner.gender.GetLabel() +
							", age: " + partner.ageTracker.AgeBiologicalYears +
							", " + CompRJW.Comp(partner).orientation +
							"): (would fuck) " + SexAppraiser.would_fuck(pawn, partner).ToString("F3") +
							"): (be fucked) " + SexAppraiser.would_fuck(partner, pawn).ToString("F3") +
							": (rape) " + SexAppraiser.would_rape(pawn, partner));
					}
					stringBuilder.AppendLine();

					stringBuilder.AppendLine("Humans - Prisoners:");
					pawns = pawn.Map.mapPawns.AllPawnsSpawned.Where(x => x != pawn && x.RaceProps.Humanlike && !x.IsColonist && x.IsPrisonerOfColony).OrderBy(x => xxx.get_pawnname(x)).ToList();
					foreach (Pawn partner in pawns)
					{
						stringBuilder.AppendLine(partner.LabelShort + " (" + partner.gender.GetLabel() +
							", age: " + partner.ageTracker.AgeBiologicalYears +
							", " + CompRJW.Comp(partner).orientation +
							"): (would fuck) " + SexAppraiser.would_fuck(pawn, partner).ToString("F3") +
							"): (be fucked) " + SexAppraiser.would_fuck(partner, pawn).ToString("F3") +
							": (rape) " + SexAppraiser.would_rape(pawn, partner));
					}
					stringBuilder.AppendLine();

					stringBuilder.AppendLine("Humans - non Colonists:");
					pawns = pawn.Map.mapPawns.AllPawnsSpawned.Where(x => x != pawn && x.RaceProps.Humanlike && !x.IsColonist && !x.IsPrisonerOfColony).OrderBy(x => xxx.get_pawnname(x)).ToList();
					foreach (Pawn partner in pawns)
					{
						stringBuilder.AppendLine(partner.LabelShort + " (" + partner.gender.GetLabel() +
							", age: " + partner.ageTracker.AgeBiologicalYears +
							", " + CompRJW.Comp(partner).orientation +
							"): (would fuck) " + SexAppraiser.would_fuck(pawn, partner).ToString("F3") +
							"): (be fucked) " + SexAppraiser.would_fuck(partner, pawn).ToString("F3") +
							": (rape) " + SexAppraiser.would_rape(pawn, partner));
					}
					stringBuilder.AppendLine();

					stringBuilder.AppendLine("Animals - Colony:");
					pawns = pawn.Map.mapPawns.AllPawnsSpawned.Where(x => x != pawn && x.RaceProps.Animal && x.Faction == Faction.OfPlayer).OrderBy(x => xxx.get_pawnname(x)).ToList();
					foreach (Pawn partner in pawns)
					{
						stringBuilder.AppendLine(partner.LabelShort + " (" + partner.gender.GetLabel() +
							", age: " + partner.ageTracker.AgeBiologicalYears +
							//", " + CompRJW.Comp(partner).orientation +
							"): (would fuck) " + SexAppraiser.would_fuck(pawn, partner).ToString("F3") +
							"): (be fucked) " + SexAppraiser.would_fuck(partner, pawn).ToString("F3") +
							": (rape) " + SexAppraiser.would_rape(pawn, partner));
					}
					stringBuilder.AppendLine();

					stringBuilder.AppendLine("Animals - non Colony:");
					pawns = pawn.Map.mapPawns.AllPawnsSpawned.Where(x => x != pawn && x.RaceProps.Animal && x.Faction != Faction.OfPlayer).OrderBy(x => xxx.get_pawnname(x)).ToList();
					foreach (Pawn partner in pawns)
					{
						stringBuilder.AppendLine(partner.LabelShort + " (" + partner.gender.GetLabel() +
							", age: " + partner.ageTracker.AgeBiologicalYears +
							//", " + CompRJW.Comp(partner).orientation +
							"): (would fuck) " + SexAppraiser.would_fuck(pawn, partner).ToString("F3") +
							"): (be fucked) " + SexAppraiser.would_fuck(partner, pawn).ToString("F3") +
							": (rape) " + SexAppraiser.would_rape(pawn, partner));
					}
					Find.WindowStack.Add(new Dialog_MessageBox(stringBuilder.ToString(), null, null, null, null, null, false, null, null));
				}, MenuOptionPriority.Default, null, null, 0.0f, null, null);
			}

			static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
			{
				List<CodeInstruction> codes = instructions.ToList();
				var addFunc = typeof(List<FloatMenuOption>).GetMethod("Add", new Type[] { typeof(FloatMenuOption) });
				//Identify the last time options.Add() is called so we can place our new option afterwards
				CodeInstruction lastAdd = codes.FindLast(ins => ins.opcode == OpCodes.Callvirt && ins.operand == addFunc);
				for (int i = 0; i < codes.Count; ++i) {
					yield return codes[i];
					if (codes[i] == lastAdd) {
						yield return new CodeInstruction(OpCodes.Ldloc_1);
						yield return new CodeInstruction(OpCodes.Ldarg_1);
						yield return new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(Patch_SocialCardUtility_DrawDebugOptions), nameof(newMenuOption)));
						yield return new CodeInstruction(OpCodes.Callvirt, addFunc);
					}
				}
			}
		}
	}
}


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

namespace rjw
{
	//ABF?? raid rape before leaving?
	[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;
			//--ModLog.Message("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;
		}
	}

	//disable kidnaping
	[HarmonyPatch(typeof(KidnapAIUtility), "TryFindGoodKidnapVictim")]
	internal static class Patches_TryFindGoodKidnapVictim
	{
		public static void Postfix(ref bool __result)
		{
			if (!__result) return;
			//ModLog.Message("KidnapAIUtility::TryFindGoodKidnapVictim");
			//Log.Message("__result " + __result);
			if (!RJWSettings.ForbidKidnap) return;
			__result = false;
		}
	}

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

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

	[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)
			{
				ModLog.Message("no def!");
				__result = false;
				return false;
			}
			return true;
		}
	}
}


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

namespace rjw
{
	///
	/// Harmony patch to toggle the RJW designation box showing
	///
	[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());
		}
	}

	///
	///Compact button group containing rjw designations on pawn
	///
	[HarmonyPatch(typeof(Pawn), "GetGizmos")]
	[StaticConstructorOnStartup]
	static class Rjw_buttons
	{
		[HarmonyPostfix]
		static void this_is_postfix(ref IEnumerable<Gizmo> __result, ref Pawn __instance)
		{
			if (!RJWSettings.show_RJW_designation_box) return;
			if (!(__instance.Faction == Faction.OfPlayer || __instance.IsPrisonerOfColony)) 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();
		}
	}

AnimalTab
Extensions
        public static bool Pregnant( this Pawn pawn )
        {
            // get hediff
            var _hediff = pawn.health.hediffSet.GetFirstHediffOfDef( HediffDefOf.Pregnant );

            // if pregnant, and pregnancy is far enough advanced to be visible
            if ( _hediff?.Visible ?? false )
                return true;

            // not (visibly) pregnant.
            return false;
        }	///
	///Submit gizmo
	///
	[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)
				if (pawn.CanChangeDesignationColonist())
					if (!(pawn.kindDef.race.defName.Contains("Droid") && !AndroidsCompatibility.IsAndroid(pawn)))
					{
						gizmos.Add(new Command_Action
						{
							defaultLabel = "CommandSubmit".Translate(),
							icon = submit_icon,
							defaultDesc = "CommandSubmitDesc".Translate(),
							action = delegate
							{
								LayDownAndAccept(pawn);
							},
							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");

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


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

namespace rjw
{
	/// <summary>
	/// Conditional patching class that only does patching if CnP is active
	/// </summary>
	public class patch_CnPBnC
	{
		private readonly static Type Hediff_Baby = AccessTools.TypeByName("Hediff_Baby");
		Notify_TraitChanged()
			//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(HediffGiver_Birthday), "GrowUpTo")]
		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 });
					}
				}
			}
		}
	}
}


code:
using System;
using HarmonyLib;
using Verse;
using Verse.AI;

namespace rjw
{
	[HarmonyPatch(typeof(JobDriver), "Cleanup")]
	internal static class PATCH_JobDriver_DubsBadHygiene
	{
		//not very good solution, some other mod can have same named jobdriver but w/e

		//Dubs Bad Hygiene washing
		private readonly static Type JobDriver_useWashBucket = AccessTools.TypeByName("JobDriver_useWashBucket");
		//private readonly static Type JobDriver_washAtCell = AccessTools.TypeByName("JobDriver_washAtCell");

		private readonly static Type JobDriver_UseHotTub = AccessTools.TypeByName("JobDriver_UseHotTub");
		private readonly static Type JobDriver_takeShower = AccessTools.TypeByName("JobDriver_takeShower");
		private readonly static Type JobDriver_takeBath = AccessTools.TypeByName("JobDriver_takeBath");

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

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

				//ModLog.Message("patches_DubsBadHygiene::on_cleanup_driver" + xxx.get_pawnname(pawn));

				if (xxx.DubsBadHygieneIsActive)
					//clear one instance of semen
					if (
						__instance.GetType() == JobDriver_useWashBucket// ||
						//__instance.GetType() == JobDriver_washAtCell
						)
					{
						Hediff hediff = pawn.health.hediffSet.hediffs.Find(x => (  x.def == RJW_SemenoOverlayHediffDefOf.Hediff_Semen
																				|| x.def == RJW_SemenoOverlayHediffDefOf.Hediff_InsectSpunk
																				|| x.def == RJW_SemenoOverlayHediffDefOf.Hediff_MechaFluids
																				));
						if (hediff != null)
						{
							//ModLog.Message("patches_DubsBadHygiene::" + __instance.GetType()  + " clear => " + hediff.Label);
							hediff.Severity -= 1f;
						}
					}
					//clear all instance of semen
					else if (
							__instance.GetType() == JobDriver_UseHotTub ||
							__instance.GetType() == JobDriver_takeShower ||
							__instance.GetType() == JobDriver_takeBath
							)
						{
							foreach (Hediff hediff in pawn.health.hediffSet.hediffs)
								{
									if (hediff.def == RJW_SemenoOverlayHediffDefOf.Hediff_Semen ||
										hediff.def == RJW_SemenoOverlayHediffDefOf.Hediff_InsectSpunk ||
										hediff.def == RJW_SemenoOverlayHediffDefOf.Hediff_MechaFluids
										)
									{
										//ModLog.Message("patches_DubsBadHygiene::" + __instance.GetType() + " clear => " + hediff.Label);
										hediff.Severity -= 1f;
									}
								}
						}
			}
			return true;
		}
	}
}


code:
using System;
using System.Reflection;
using HarmonyLib;
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(Harmony 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 apparel = t as Apparel;
			if ((apparel == null) || (!apparel.has_lock()))
				return true;
			else
			{
				var p = SelPawnForGear(tab);
				if ((p.apparel != null) && (p.apparel.WornApparel.Contains(apparel)))
				{
					p.jobs.TryTakeOrderedJob(JobMaker.MakeJob(xxx.struggle_in_BondageGear, apparel));
					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 HarmonyLib;
using RimWorld;
using Verse;
using Verse.AI;

namespace rjw
{
	/// <summary>
	/// Patch:
	/// Core Loving, Mating
	/// Rational Romance LovinCasual
	/// CnP, remove pregnancy if rjw human preg enabled
	/// </summary>

	// Add a fail condition to JobDriver_Lovin that prevents pawns from lovin' if they aren't physically able/have genitals
	[HarmonyPatch(typeof(JobDriver_Lovin), "MakeNewToils")]
	internal static class PATCH_JobDriver_Lovin_MakeNewToils
	{
		[HarmonyPrefix]
		private static bool on_begin_lovin(JobDriver_Lovin __instance)
		{
			Pawn pawn = __instance.pawn;
			Pawn partner = null;
			Building_Bed Bed = null;
			var any_ins = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
			partner = (Pawn)(__instance.GetType().GetProperty("Partner", any_ins).GetValue(__instance, null));
			Bed = (Building_Bed)(__instance.GetType().GetProperty("Bed", any_ins).GetValue(__instance, null));

			__instance.FailOn(() => (!(xxx.can_fuck(pawn) || xxx.can_be_fucked(pawn))));
			__instance.FailOn(() => (!(xxx.can_fuck(partner) || xxx.can_be_fucked(partner))));

			if (RJWSettings.override_lovin)
			{
				pawn.jobs.EndCurrentJob(JobCondition.Incompletable);
				Job job = JobMaker.MakeJob(xxx.casual_sex, partner, Bed);
				pawn.jobs.jobQueue.EnqueueFirst(job);
			}

			return true;
		}
	}

	// Add a fail condition to JobDriver_Mate that prevents animals from lovin' if they aren't physically able/have genitals
	[HarmonyPatch(typeof(JobDriver_Mate), "MakeNewToils")]
	internal static class PATCH_JobDriver_Mate_MakeNewToils
	{
		[HarmonyPrefix]
		private static bool on_begin_matin(JobDriver_Mate __instance)
		{
			//only reproductive male starts mating job
			Pawn pawn = __instance.pawn;
			Pawn partner = null;
			var any_ins = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
			partner = (Pawn)(__instance.GetType().GetProperty("Female", any_ins).GetValue(__instance, null));

			__instance.FailOn(() => (!(xxx.can_fuck(pawn) || xxx.can_be_fucked(pawn))));
			__instance.FailOn(() => (!(xxx.can_fuck(partner) || xxx.can_be_fucked(partner))));

			if (RJWSettings.override_matin)
			{
				//__instance.FailOn(() => (!RJWSettings.animal_on_animal_enabled));
				//if (!RJWSettings.animal_on_animal_enabled)
				//	return true;

				Job job;

				if (pawn.kindDef == partner.kindDef)
					job = JobMaker.MakeJob(xxx.animalMate, partner);
				else if (RJWSettings.animal_on_animal_enabled)
					job = JobMaker.MakeJob(xxx.animalBreed, partner);
				else
					return true;

				pawn.jobs.EndCurrentJob(JobCondition.Incompletable);
				pawn.jobs.jobQueue.EnqueueFirst(job);
			}

			return true;
		}
	}

	//// Add a fail condition to JobDriver_Mate that prevents animals from lovin' if they aren't physically able/have genitals
	//[HarmonyPatch(typeof(PawnUtility), "FertileMateTarget")]
	//internal static class PATCH_JobDriver_Mate_MakeNewToils
	//{
	//	[HarmonyPostfix]
	//	public static void Postfix(Job __result, Pawn pawn)
	//	{
	//		if (__result == null)
	//		{
	//			if (female.gender != Gender.Female || !female.ageTracker.CurLifeStage.reproductive)
	//			{
	//				return false;
	//			}
	//			CompEggLayer compEggLayer = female.TryGetComp<CompEggLayer>();
	//			if (compEggLayer != null)
	//			{
	//				return !compEggLayer.FullyFertilized;
	//			}
	//			return !female.health.hediffSet.HasHediff(HediffDefOf.Pregnant, false);

	//			__result = JobMaker.MakeJob(JobDefOf.Mate, pawn2);

	//		}
	//	}
	//}

	//Patch for futa animals to initiate mating (vanialla - only male)
	[HarmonyPatch(typeof(JobGiver_Mate), "TryGiveJob")]
	internal static class PATCH_JobGiver_Mate_TryGiveJob
	{
		[HarmonyPostfix]
		public static void Postfix(ref Job __result, Pawn pawn)
		{
			//ModLog.Message("patches_lovin::JobGiver_Mate fail, try rjw for " + xxx.get_pawnname(pawn));
			var partBPR = Genital_Helper.get_genitalsBPR(pawn);
			var parts = Genital_Helper.get_PartsHediffList(pawn, partBPR);
			if (!(Genital_Helper.has_penis_fertile(pawn, parts) || Genital_Helper.has_penis_infertile(pawn, parts)) || !pawn.ageTracker.CurLifeStage.reproductive)
			{
				//ModLog.Message("patches_lovin::JobGiver_Mate " + xxx.get_pawnname(pawn) + ", has no penis " + (Genital_Helper.has_penis(pawn) || Genital_Helper.has_penis_infertile(pawn)));
				//ModLog.Message("patches_lovin::JobGiver_Mate " + xxx.get_pawnname(pawn) + ", not reproductive " + !pawn.ageTracker.CurLifeStage.reproductive);
				return;
			}
			Predicate<Thing> validator = delegate (Thing t)
			{
				Pawn pawn3 = t as Pawn;
				var valid = !pawn3.Downed
							&& pawn3 != pawn
							&& pawn3.CanCasuallyInteractNow()
							&& !pawn3.IsForbidden(pawn)
							&& !pawn3.HostileTo(pawn)
							&& PawnUtility.FertileMateTarget(pawn, pawn3);
				if (!valid && pawn3 != pawn)
				{
					//ModLog.Message("patches_lovin::JobGiver_Mate " + xxx.get_pawnname(pawn3) + ", not valid");
					//ModLog.Message("patches_lovin::JobGiver_Mate Downed " + pawn3.Downed);
					//ModLog.Message("patches_lovin::JobGiver_Mate CanCasuallyInteractNow " + pawn3.CanCasuallyInteractNow());
					//ModLog.Message("patches_lovin::JobGiver_Mate IsForbidden " + pawn3.IsForbidden(pawn));
					//ModLog.Message("patches_lovin::JobGiver_Mate FertileMateTarget " + PawnUtility.FertileMateTarget(pawn, pawn3));
				}
				return valid;
			};

			ThingRequest request = ThingRequest.ForDef(pawn.def);			// mate sames species
			//if (RJWSettings.WildMode)										// go wild xD
			//	request = ThingRequest.ForGroup(ThingRequestGroup.Pawn);	// mate everyone
			//add animal check?

			Pawn pawn2 = (Pawn)GenClosest.ClosestThingReachable(pawn.Position, pawn.Map, request, PathEndMode.Touch, TraverseParms.For(pawn), 30f, validator);
			if (pawn2 == null)
			{
				//ModLog.Message("patches_lovin::JobGiver_Mate " + xxx.get_pawnname(pawn) + ", no valid partner found");
				return;
			}
			//__result = JobMaker.MakeJob(xxx.animalBreed, pawn2);
			__result = JobMaker.MakeJob(JobDefOf.Mate, pawn2);
			//ModLog.Message("patches_lovin::JobGiver_Mate " + xxx.get_pawnname(pawn) + ", female " + xxx.get_pawnname(pawn2) + ", job " + __result);
		}
	}

	//check if female can be impregnated for animal mating (also check rjw pregnancies)
	[HarmonyPatch(typeof(PawnUtility), "FertileMateTarget")]
	internal static class PATCH_PawnUtility_FertileMateTarget
	{
		[HarmonyPrefix]
		public static bool Prefix(Pawn male, Pawn female, ref bool __state)
		{
			__state = female.IsPregnant();
			//Log.Message("Prefix FertileMateTarget is vanilla/rjw pregnant: " + __state);
			return true;
		}

		[HarmonyPostfix]
		public static bool Postfix(bool __result, ref bool __state)
		{
			if (__result)
			{
				//Log.Message("Postfix FertileMateTarget is fertile and not vanilla pregnant: " + __result);
				if (__state)
				{
					//Log.Message("Postfix FertileMateTarget is fertile and rjw pregnant: " + __state);
					__result = false;
				}
			}
			return __result;
		}
	}

	//Suboptions when starting rjw sex through workgiver
	//[HarmonyPatch(typeof(FloatMenuOption), "Chosen")]
	//internal static class PATCH_test
	//{
	//	[HarmonyPostfix]
	//	public static void Postfix(FloatMenuOption __instance)
	//	{
	//		try
	//		{
	//			if (Find.Selector.NumSelected == 1) //&& (Find.Selector.SingleSelectedThing as Pawn).IsDesignatedHero()
	//			{
	//				//TODO: check if rjw jobdriver,
	//				//Insert another menu or 2 that will modify initiator sex jobdriver to
	//				//use selected parts - oral/anal/vaginal/etc
	//				//use selectable sex action - give/receive?
	//				//use selectable sex action - fuck/fisting?
	//				Log.Message("---");
	//				ModLog.Message("FloatMenuOption::Chosen " + __instance);
	//				ModLog.Message("FloatMenuOption::Chosen " + __instance.Label);
	//				ModLog.Message("FloatMenuOption::Chosen " + __instance.action.Target);
	//				ModLog.Message("FloatMenuOption::Chosen initiator - " + Find.Selector.SingleSelectedThing);
	//				ModLog.Message("FloatMenuOption::Chosen target - " + __instance.revalidateClickTarget);
	//				ModLog.Message("FloatMenuOption::Chosen initiator - " + (Find.Selector.SingleSelectedThing as Pawn).CurJob);
	//				Log.Message("---");
	//			}
	//		}
	//		catch
	//		{ }
	//	}
	//}

	//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.
	//this prob needs a patch like above, but who cares

	// 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), "Cleanup")]
	internal static class PATCH_JobDriver_Loving_Cleanup
	{
		//RomanceDiversified lovin
		//not very good solution, some other mod can have same named jobdrivers, but w/e
		private readonly static Type JobDriverDoLovinCasual = AccessTools.TypeByName("JobDriver_DoLovinCasual");

		//Nightmare Incarnation
		//not very good solution, some other mod can have same named jobdrivers, but w/e
		private readonly static Type JobDriverNIManaSqueeze = AccessTools.TypeByName("JobDriver_NIManaSqueeze");

		//vanilla lovin
		private readonly static Type JobDriverLovin = typeof(JobDriver_Lovin);

		//vanilla mate
		private readonly static Type JobDriverMate = typeof(JobDriver_Mate);

		[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 = null;

				//ModLog.Message("patches_lovin::on_cleanup_driver" + xxx.get_pawnname(pawn));

				//[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));
					ModLog.Message("patches_lovin::on_cleanup_driver RomanceDiversified/RationalRomance:" + xxx.get_pawnname(pawn) + "+" + xxx.get_pawnname(partner));
				}
				//Nightmare Incarnation
				else if (xxx.NightmareIncarnationIsActive && __instance.GetType() == JobDriverNIManaSqueeze)
				{
					var any_ins = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
					partner = (Pawn)(__instance.GetType().GetProperty("Victim", any_ins).GetValue(__instance, null));
					ModLog.Message("patches_lovin::on_cleanup_driver Nightmare Incarnation:" + 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))
					{
						ModLog.Message("patches_lovin:: RimWorldChildren/ChildrenAndPregnancy pregnancy:" + xxx.get_pawnname(pawn) + "+" + xxx.get_pawnname(partner));
						PregnancyHelper.cleanup_CnP(pawn);
						PregnancyHelper.cleanup_CnP(partner);
					}
					else
						ModLog.Message("patches_lovin:: JobDriverLovin pregnancy:" + xxx.get_pawnname(pawn) + "+" + xxx.get_pawnname(partner));
				}
				//Vanilla mating
				else if (__instance.GetType() == JobDriverMate)
				{
					var any_ins = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
					partner = (Pawn)(__instance.GetType().GetProperty("Female", any_ins).GetValue(__instance, null));
					ModLog.Message("patches_lovin:: JobDriverMate pregnancy:" + xxx.get_pawnname(pawn) + "+" + xxx.get_pawnname(partner));
				}
				else
					return true;

				// TODO: Doing TryUseCondom here is a bit weird... it should happen before.
				var usedCondom = CondomUtility.TryUseCondom(pawn) || CondomUtility.TryUseCondom(partner);

				//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)
					)
				{
					ModLog.Message("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, usedCondom, false, true);
			}
			return true;
		}
	}
}


code:
using HarmonyLib;
using RimWorld;
using Verse;

namespace rjw
{
	/// <summary>
	/// disable meditation effects for nymphs
	/// </summary>
	[HarmonyPatch(typeof(JobDriver_Meditate), "MeditationTick")]
	internal static class PATCH_JobDriver_Meditate_MeditationTick
	{
		[HarmonyPrefix]
		private static bool on_JobDriver_Meditate(JobDriver_Meditate __instance)
		{
			Pawn pawn = __instance.pawn;
			if (xxx.is_nympho(pawn))
				return false;

			return true;
		}
	}

	/// <summary>
	/// disable meditation for nymphs
	/// </summary>
	[HarmonyPatch(typeof(JobGiver_Meditate), "GetPriority")]
	internal static class PATCH_JobGiver_Meditate_GetPriority
	{
		[HarmonyPostfix]
		public static void Postfix(ref float __result, Pawn pawn)
		{
			if (xxx.is_nympho(pawn))
				__result = 0f;
		}
	}
}


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 HarmonyLib;
using RimWorld;
using RimWorld.Planet;
using UnityEngine;
using Verse;
using Multiplayer.API;

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 instead of vanilla
		/// 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]
		[SyncMethod]
		private static bool on_begin_DoBirthSpawn(ref Pawn mother, ref Pawn father)
		{
			//--Log.Message("patches_pregnancy::PATCH_Hediff_Pregnant::DoBirthSpawn() called");

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

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

			// get a reference to the hediff we are applying
			//do birth for vanilla pregnancy Hediff
			//if using rjw pregnancies - add RJW pregnancy Hediff and birth it instead
			Hediff_Pregnant self = (Hediff_Pregnant)mother.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("Pregnant"));
			if (self != null)
			{
				return ProcessVanillaPregnancy(self, mother, father);
			}

			// do birth for existing RJW pregnancies
			if (ProcessRJWPregnancy(mother, father))
			{
				return false;
			}

			return ProcessDebugPregnancy(mother, father);
		}

		private static bool ProcessVanillaPregnancy(Hediff_Pregnant pregnancy, Pawn mother, Pawn father)
		{
			void CreateAndBirth<T>() where T : Hediff_BasePregnancy
			{
				T hediff = Hediff_BasePregnancy.Create<T>(mother, father);
				hediff.GiveBirth();
				if (pregnancy != null)
					mother.health.RemoveHediff(pregnancy);
			}

			if (father == null)
			{
				father = Hediff_BasePregnancy.Trytogetfather(ref mother);
			}

			ModLog.Message("patches_pregnancy::PATCH_Hediff_Pregnant::DoBirthSpawn():Vanilla_pregnancy birthing:" + xxx.get_pawnname(mother));
			if (RJWPregnancySettings.animal_pregnancy_enabled && ((father == null || xxx.is_animal(father)) && xxx.is_animal(mother)))
			{
				//RJW Bestial pregnancy animal-animal
				ModLog.Message(" override as Bestial birthing(animal-animal): Father-" + xxx.get_pawnname(father) + " Mother-" + xxx.get_pawnname(mother));
				CreateAndBirth<Hediff_BestialPregnancy>();
				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
				ModLog.Message(" override as Bestial birthing(human-animal): Father-" + xxx.get_pawnname(father) + " Mother-" + xxx.get_pawnname(mother));
				CreateAndBirth<Hediff_BestialPregnancy>();
				return false;
			}
			else if (RJWPregnancySettings.humanlike_pregnancy_enabled && (xxx.is_human(father) && xxx.is_human(mother)))
			{
				//RJW Humanlike pregnancy
				ModLog.Message(" override as Humanlike birthing: Father-" + xxx.get_pawnname(father) + " Mother-" + xxx.get_pawnname(mother));
				CreateAndBirth<Hediff_HumanlikePregnancy>();
				return false;
			}
			else
			{
				ModLog.Warning("Hediff_Pregnant::DoBirthSpawn() - checks failed, vanilla pregnancy birth");
				ModLog.Warning("Hediff_Pregnant::DoBirthSpawn(): Father-" + xxx.get_pawnname(father) + " Mother-" + xxx.get_pawnname(mother));
				//vanilla pregnancy code, no effects on rjw

				return true;
			}
		}

		private static bool ProcessRJWPregnancy(Pawn mother, Pawn father)
		{
			Hediff_BasePregnancy preg =
			   (Hediff_BasePregnancy)mother.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("RJW_pregnancy")) ??		//RJW Humanlike pregnancy
			   (Hediff_BasePregnancy)mother.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("RJW_pregnancy_beast")) ??	//RJW Bestial pregnancy
			   (Hediff_BasePregnancy)mother.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("RJW_pregnancy_mech"));	//RJW Bestial pregnancy

			if (preg != null)
			{
				ModLog.Message($"patches_pregnancy::{preg.GetType().Name}::DoBirthSpawn() birthing:" + xxx.get_pawnname(mother));
				preg.GiveBirth();
				return true;
			}

			return false;
		}

		private static bool ProcessDebugPregnancy(Pawn mother, Pawn father)
		{
			void CreateAndBirth<T>() where T : Hediff_BasePregnancy
			{
				T hediff = Hediff_BasePregnancy.Create<T>(mother, father);
				hediff.GiveBirth();
			}
			//CreateAndBirth<Hediff_HumanlikePregnancy>();
			//CreateAndBirth<Hediff_BestialPregnancy>();
			//CreateAndBirth<Hediff_MechanoidPregnancy>();
			//return false;

			//debug, add RJW pregnancy and birth it
			ModLog.Message("patches_pregnancy::PATCH_Hediff_Pregnant::DoBirthSpawn():Debug_pregnancy birthing:" + xxx.get_pawnname(mother));
			if (father == null)
			{
				father = Hediff_BasePregnancy.Trytogetfather(ref mother);

				if (RJWPregnancySettings.bestial_pregnancy_enabled && ((xxx.is_animal(father) || xxx.is_animal(mother)))
					|| (xxx.is_animal(mother) && RJWPregnancySettings.animal_pregnancy_enabled))
				{
					//RJW Bestial pregnancy
					ModLog.Message(" override as Bestial birthing, mother: " + xxx.get_pawnname(mother));
					CreateAndBirth<Hediff_BestialPregnancy>();
				}
				else if (RJWPregnancySettings.humanlike_pregnancy_enabled && ((father == null || xxx.is_human(father)) && xxx.is_human(mother)))
				{
					//RJW Humanlike pregnancy
					ModLog.Message(" override as Humanlike birthing, mother: " + xxx.get_pawnname(mother));
					CreateAndBirth<Hediff_HumanlikePregnancy>();
				}
				else
				{
					ModLog.Warning("Hediff_Pregnant::DoBirthSpawn() - debug vanilla pregnancy birth");
					return true;
				}
			}
			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))
			{
				if (!Genital_Helper.has_vagina(__instance.pawn))
				{
					__instance.pawn.health.RemoveHediff(__instance);
				}
			}
			return true;
		}
	}
}


code:
using System.Linq;
using Verse;

namespace rjw
{
	/// <summary>
	/// Patch all races into rjw parts recipes
	/// </summary>

	[StaticConstructorOnStartup]
	public static class HarmonyPatches
	{
		static HarmonyPatches()
		{
			//summons carpet bombing

			//inject races into rjw recipes
			foreach (RecipeDef x in	DefDatabase<RecipeDef>.AllDefsListForReading.Where(x => x.IsSurgery && (x.targetsBodyPart || !x.appliedOnFixedBodyParts.NullOrEmpty())))
			{
				if (x.appliedOnFixedBodyParts.Contains(xxx.genitalsDef)
					|| x.appliedOnFixedBodyParts.Contains(xxx.breastsDef)
					|| x.appliedOnFixedBodyParts.Contains(xxx.anusDef)
					)

					foreach (ThingDef thingDef in DefDatabase<ThingDef>.AllDefs.Where(thingDef =>
							thingDef.race != null && (
							thingDef.race.Humanlike ||
							thingDef.race.Animal
							)))
					{
						//filter out something, probably?
						//if (thingDef.race. == "Human")
						//	continue;

						if (!x.recipeUsers.Contains(thingDef))
						{
							x.recipeUsers.Add(item: thingDef);
							//Log.Message("recipe: " + x.defName + ", thing: " + thingDef.defName);

						}
					}
			}
		}
	}
}


code:
using HarmonyLib;
using RimWorld;
using System;
using Verse;

namespace rjw
{
	///<summary>
	///RJW Designators checks/update
	///update designators only for selected pawn, once, instead of every tick(60 times per sec)
	///</summary>
	[HarmonyPatch(typeof(Selector), "Select")]
	[StaticConstructorOnStartup]
	static class PawnSelect
	{
		[HarmonyPrefix]
		private static bool pawnSelect(Selector __instance, ref object obj)
		{
			if (obj is Pawn)
			{
				//ModLog.Message("Selector patch");
				Pawn pawn = (Pawn)obj;
				//ModLog.Message("pawn: " + xxx.get_pawnname(pawn));
				pawn.UpdatePermissions();
			}
			return true;
		}
	}

	//[HarmonyPatch(typeof(Dialog_InfoCard), "Setup")]
	////[HarmonyPatch(typeof(Dialog_InfoCard), "Dialog_InfoCard", new Type[] {typeof(Def)})]
	//[StaticConstructorOnStartup]
	//static class Button
	//{
	//	[HarmonyPostfix]
	//	public static bool Postfix()
	//	{
	//		ModLog.Message("InfoCardButton");
	//		return true;
	//	}
	//}
}