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 lovin_patch_n_override(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 matin_patch_n_override(JobDriver_Mate __instance)
		{
			//only reproductive male/futa 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 || pawn.RaceProps.animalType == partner.RaceProps.animalType)
					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;
		}
	}

	//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 futa_animal_partner_search_patch(ref Job __result, Pawn pawn)
		{
			//ModLog.Message("patches_lovin::JobGiver_Mate fail, try rjw for " + xxx.get_pawnname(pawn));
			var parts = pawn.GetGenitalsList();
			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)
							&& (RJWSettings.WildMode || // go wild, mate everyone
								(!RJWSettings.matin_crossbreed && pawn.def == pawn3.def) || // mate same species - vanilla behavior
								(RJWSettings.matin_crossbreed && pawn.RaceProps.animalType != AnimalType.None && pawn.RaceProps.animalType == pawn3.RaceProps.animalType)); // mate same animalType
				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.ForGroup(ThingRequestGroup.Pawn);			// mate everyone, filtered by validator
			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));

				var any_ins = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

				//[RF] Rational Romance [1.0] loving
				if (xxx.RomanceDiversifiedIsActive && __instance.GetType() == JobDriverDoLovinCasual)
				{
					// not sure RR can even cause pregnancies but w/e
					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)
				{
					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)
				{
					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)
				{
					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? 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);
				}

				SexProps nonRJWsexI = SexUtility.SelectSextype(pawn, partner, false, false);
				nonRJWsexI.isCoreLovin = true;

				SexProps nonRJWsexR;
				nonRJWsexR = new SexProps()
				{
					pawn = partner,
					partner = pawn,
					sexType = nonRJWsexI.sexType,
					dictionaryKey = nonRJWsexI.dictionaryKey,
					isCoreLovin = true,
					isReceiver = true
				};

				SexUtility.ProcessSex(nonRJWsexI); //TODO: fix me someday
				SexUtility.SatisfyPersonal(nonRJWsexI);
				SexUtility.SatisfyPersonal(nonRJWsexR);
			}
			return true;
		}
	}
}

repo_name: Chinchillanon/rjw

license: mit


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

namespace rjw
{
	/// <summary>
	/// disable meditation effects for nymphs (i.e meditation on throne)
	/// </summary>
	[HarmonyPatch(typeof(JobDriver_Meditate), "MeditationTick")]
	internal static class PATCH_JobDriver_Meditate_MeditationTick
	{
		[HarmonyPrefix]
		private static bool Disable_For_Nymph(JobDriver_Meditate __instance)
		{
			Pawn pawn = __instance.pawn;

			if (xxx.is_nympho(pawn))
			{
				//ModLog.Message("JobGiver_Meditate::MeditationTick for nymph " + xxx.get_pawnname(pawn) + " __instance " + __instance);
				CompProperties_MeditationFocus t0 = __instance.Focus.Thing?.def?.comps?.Find(x => x is CompProperties_MeditationFocus) as CompProperties_MeditationFocus;
				if (t0 != null)
					if (t0.focusTypes.Contains(xxx.SexMeditationFocus))
						return true;

				return false;
			}

			//ModLog.Message("JobGiver_Meditate::MeditationTick pass");
			return true;
		}
	}

	[HarmonyPatch(typeof(JobGiver_Meditate), "TryGiveJob")]
	internal static class PATCH_JobGiver_Meditate_TryGiveJob
	{
		[HarmonyPostfix]
		public static void Disable_For_Nymph(ref Job __result, Pawn pawn)
		{
			if (__result != null)
				if (xxx.is_nympho(pawn))
				{
					//ModLog.Message("JobGiver_Meditate::TryGiveJob for nymph " + xxx.get_pawnname(pawn) + " job " + __result);
					CompProperties_MeditationFocus t1 = __result.targetA.Thing?.def.comps.Find(x => x is CompProperties_MeditationFocus) as CompProperties_MeditationFocus;
					CompProperties_MeditationFocus t2 = __result.targetB.Thing?.def.comps.Find(x => x is CompProperties_MeditationFocus) as CompProperties_MeditationFocus;
					CompProperties_MeditationFocus t3 = __result.targetC.Thing?.def.comps.Find(x => x is CompProperties_MeditationFocus) as CompProperties_MeditationFocus;
					//ModLog.Message("JobGiver_Meditate::TryGiveJob targetA " + t1);
					//ModLog.Message("JobGiver_Meditate::TryGiveJob targetB " + t2);
					//ModLog.Message("JobGiver_Meditate::TryGiveJob targetC " + t3);
					if (t1 != null)
						if (t1.focusTypes.Contains(xxx.SexMeditationFocus))
							return;
					if (t2 != null)
						if (t2.focusTypes.Contains(xxx.SexMeditationFocus))
							return;
					if (t3 != null)
						if (t3.focusTypes.Contains(xxx.SexMeditationFocus))
							return;

					//ModLog.Message("JobGiver_Meditate::Disable_For_Nymph no valid targets fail job");
					__result = null;
					//ModLog.Message("JobGiver_Meditate::Disable_For_Nymph " + xxx.get_pawnname(pawn) + " job " + __result);
				}
		}
	}
}


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;
using System.Collections.Generic;
using System.Reflection;

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;
			}

			//CompEggLayer compEggLayer = mother.TryGetComp<CompEggLayer>();
			//if (compEggLayer != null)
			//{
			//	ProcessVanillaEggPregnancy(mother, father);
			//	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(HediffDefOf.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 ProcessVanillaEggPregnancy(Pawn mother, Pawn father)
		{
			CompEggLayer compEggLayer = mother.TryGetComp<CompEggLayer>();
			if (compEggLayer != null)
			{
				if (!compEggLayer.FullyFertilized)
				{
					if (father == null)
						compEggLayer.Fertilize(mother);
					else
						compEggLayer.Fertilize(father);

					compEggLayer.ProduceEgg();
				}
			}

			CompHatcher compHatcher = mother.TryGetComp<CompHatcher>();
			if (compHatcher != null)
			{
				compHatcher.Hatch();
			}

			ModLog.Message("patches_pregnancy::PATCH_Hediff_Pregnant::DoBirthSpawn():ProcessVanillaEggPregnancy birthing:" + xxx.get_pawnname(mother));
			return true;
		}

		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)
		{
			var p = PregnancyHelper.GetPregnancies(mother);
			if (p.NullOrEmpty())
			{
				return false;
			}

			var birth = false;
			foreach (var x in p)
			{
				if (x is Hediff_BasePregnancy)
				{
					var preg = x as Hediff_BasePregnancy;
					ModLog.Message($"patches_pregnancy::{preg.GetType().Name}::DoBirthSpawn() birthing:" + xxx.get_pawnname(mother));
					preg.GiveBirth();
					birth = true;
				}
			}

			return birth;
		}

		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 abort_on_missing_genitals(Hediff_Pregnant __instance)
		{
			if (__instance.pawn.IsHashIntervalTick(1000))
			{
				if (!Genital_Helper.has_vagina(__instance.pawn))
				{
					__instance.pawn.health.RemoveHediff(__instance);
				}
			}
			return true;
		}
	}

    [HarmonyPatch(typeof(PawnColumnWorker_Pregnant), "GetIconFor")]
    public class PawnColumnWorker_Patch_Icon
    {
        public static void Postfix(Pawn pawn, ref Texture2D __result)
        {
            if (pawn.IsVisiblyPregnant()) __result = ContentFinder<Texture2D>.Get("UI/Icons/Animal/Pregnant", true);
        }

    }

    [HarmonyPatch(typeof(PawnColumnWorker_Pregnant), "GetTooltipText")]
    public class PawnColumnWorker_Patch_Tooltip
    {
        public static bool Prefix(Pawn pawn, ref string __result)
        {
            float gestationProgress = PregnancyHelper.GetPregnancy(pawn).Severity; // no multipregnancy support
            int num = (int)(pawn.RaceProps.gestationPeriodDays * GenDate.TicksPerDay);
            int numTicks = (int)(gestationProgress * (float)num);
            __result = "PregnantIconDesc".Translate(numTicks.ToStringTicksToDays("F0"), num.ToStringTicksToDays("F0"));
            return false;
        }

    }

    [HarmonyPatch(typeof(TransferableUIUtility), "DoExtraAnimalIcons")]
    public class TransferableUIUtility_Patch_Icon
    {
        //private static readonly Texture2D PregnantIcon = ContentFinder<Texture2D>.Get("UI/Icons/Animal/Pregnant", true);
        public static void Postfix(Transferable trad, Rect rect, ref float curX, Texture2D ___PregnantIcon)
        {
            Pawn pawn = trad.AnyThing as Pawn;
            if (pawn?.health?.hediffSet != null && pawn.IsVisiblyPregnant())
            {
                Rect rect3 = new Rect(curX - 24f, (rect.height - 24f) / 2f, 24f, 24f);
                curX -= 24f;
                if (Mouse.IsOver(rect3))
                {
                    TooltipHandler.TipRegion(rect3, PawnColumnWorker_Pregnant.GetTooltipText(pawn));
                }
                GUI.DrawTexture(rect3, ___PregnantIcon);
            }
        }
    }

	//[HarmonyPatch(typeof(AutoSlaughterManager))]
	//public class AutoSlaughterManager_AnimalsToSlaughter_rjw_preg
	//{
	//	public static void Postfix(AutoSlaughterManager __instance)
	//	{
	//		List<Pawn> __result = new List<Pawn>();
	//		var any_ins = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
	//		__result = (List<Pawn>)(__instance.GetType().GetField("AnimalsToSlaughter", any_ins).GetValue(__instance));
	//		//(__instance.GetType().GetField("AnimalsToSlaughter", any_ins).SetValue(__instance, newresult));

	//		//List<Pawn> __result = new List<Pawn>((List < Pawn > )typeof(AutoSlaughterManager).GetField("AnimalsToSlaughter", BindingFlags.Instance | BindingFlags.NonPublic));
	//		if (!__result.NullOrEmpty())
	//		{
	//			var newresult = __result;
	//			foreach (var pawn in __result)
	//			{
	//				if (pawn?.health?.hediffSet != null && pawn.IsPregnant(true))
	//				{
	//					if (__instance.configs.Any(x => x.allowSlaughterPregnant != true && x.animal == pawn.def))
	//					{
	//						newresult.Remove(pawn);
	//					}
	//				}

	//			}
	//			__result = newresult;
	//		}

	//		//return __result;
	//	}
	//}
}


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)
					|| x.modContentPack?.PackageId == "rim.job.world"
					|| x.modContentPack?.PackageId == "Safe.Job.World" //*sigh*
					//|| x.modContentPack?.PackageId == "Abraxas.RJW.RaceSupport" // for udders?
					)

					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 Update_Designators_Permissions(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;
	//	}
	//}
}


code:
namespace rjw
{
	/// <summary>
	/// Patch:
	/// recipes
	/// </summary>

	//TODO: inject rjw recipes
	//[HarmonyPatch(typeof(HealthCardUtility), "GenerateSurgeryOption")]
	//internal static class PATCH_HealthCardUtility_recipes
	//{
	//	//private static FloatMenuOption GenerateSurgeryOption(Pawn pawn, Thing thingForMedBills, RecipeDef recipe, IEnumerable<ThingDef> missingIngredients, BodyPartRecord part = null)
	//	//public FloatMenuOption(string label, Action action, MenuOptionPriority priority = MenuOptionPriority.Default, Action mouseoverGuiAction = null, Thing revalidateClickTarget = null, float extraPartWidth = 0, Func<Rect, bool> extraPartOnGUI = null, WorldObject revalidateWorldClickTarget = null);
	//	//floatMenuOption = new FloatMenuOption(text, action, MenuOptionPriority.Default, null, null, 0f, null, null);
	//	[HarmonyPostfix]
	//	private static void Postfix(ref FloatMenuOption __result, ref Pawn pawn)
	//	{

	//		ModLog.Message("PATCH_HealthCardUtility_recipes");
	//		ModLog.Message("PATCH_HealthCardUtility_recipes list: " + __result);
	//		//foreach (FloatMenuOption recipe in recipeOptionsMaker)
	//		//	{
	//		//		ModLog.Message("PATCH_HealthCardUtility_recipes: " + recipe);
	//		//	}

	//		return;
	//	}
	//}

	//erm.. idk ?
	//[HarmonyPatch(typeof(HealthCardUtility), "GetTooltip")]
	//internal static class PATCH_HealthCardUtility_GetTooltip
	//{
	//	[HarmonyPostfix]
	//	private static void Postfix(Pawn pawn)
	//	{
	//		ModLog.Message("GetTooltip");
	//		//ModLog.Message("PATCH_HealthCardUtility_recipes list: " + floatMenuOption);
	//		//foreach (FloatMenuOption recipe in recipeOptionsMaker)
	//		//	{
	//		//		ModLog.Message("PATCH_HealthCardUtility_recipes: " + recipe);
	//		//	}
	//		return;
	//	}
	//}

	//TODO: make toggle/floatmenu to parts switching
	//[HarmonyPatch(typeof(HealthCardUtility), "EntryClicked")]
	//internal static class PATCH_HealthCardUtility_EntryClicked
	//{
	//	[HarmonyPostfix]
	//	private static void Postfix(Pawn pawn)
	//	{
	//		ModLog.Message("EntryClicked");
	//		//ModLog.Message("PATCH_HealthCardUtility_recipes list: " + floatMenuOption);
	//		//foreach (FloatMenuOption recipe in recipeOptionsMaker)
	//		//	{
	//		//		ModLog.Message("PATCH_HealthCardUtility_recipes: " + recipe);
	//		//	}
	//		return;
	//	}
	//}
}


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

namespace rjw
{
	/// <summary>
	/// Patch ui for hero mode
	/// - disable pawn control for non owned hero
	/// - disable equipment management for non owned hero
	/// hardcore mode:
	/// - disable equipment management for non hero
	/// - disable pawn rmb menu for non hero
	/// - remove drafting widget for non hero
	/// </summary>

	//disable forced works(rmb workgivers)
	[HarmonyPatch(typeof(FloatMenuMakerMap), "CanTakeOrder")]
	[StaticConstructorOnStartup]
	static class disable_FloatMenuMakerMap
	{
		[HarmonyPostfix]
		static void NonHero_disable_controls(ref bool __result, Pawn pawn)
		{
			if (RJWSettings.RPG_hero_control)
			{
				if ((pawn.IsDesignatedHero() && !pawn.IsHeroOwner()))
				{
					__result = false;   //not hero owner, disable menu
					return;
				}

				if (!pawn.IsDesignatedHero() && RJWSettings.RPG_hero_control_HC)
				{
					if (pawn.Drafted && pawn.CanChangeDesignationPrisoner() && pawn.CanChangeDesignationColonist())
					{
						//allow control over drafted pawns, this is limited by below disable_Gizmos patch
					}
					else
					{
						__result = false; //not hero, disable menu
					}
				}
			}
		}
	}

	//TODO: disable equipment management
	/*
	//disable equipment management
	[HarmonyPatch(typeof(ITab_Pawn_Gear), "CanControl")]
	static class disable_equipment_management
	{
		[HarmonyPostfix]
		static bool this_is_postfix(ref bool __result, Pawn selPawnForGear)
		{
			Pawn pawn = selPawnForGear;

			if (RJWSettings.RPG_hero_control)
			{
				if ((pawn.IsDesignatedHero() && !pawn.IsHeroOwner()))	//not hero owner, disable drafting
				{
					__result = false;   //not hero owner, disable menu
				}
				else if (!pawn.IsDesignatedHero() && RJWSettings.RPG_hero_control_HC)   //not hero, disable drafting
				{
					if (false)
					{
						//add some filter for bots and stuff? if there is such stuff
						//so it can be drafted and controlled for fighting
					}
					else
					{
						__result = false; //not hero, disable menu
					}
				}
			}
			return true;
		}
	}
	*/

	//TODO: allow shared control over non colonists(droids, etc)?
	//disable command gizmos
	[HarmonyPatch(typeof(Pawn), "GetGizmos")]
	[StaticConstructorOnStartup]
	static class disable_Gizmos
	{
		[HarmonyPostfix]
		[HarmonyPriority(100)]
		static IEnumerable<Gizmo> NonHero_disable_gizmos(IEnumerable<Gizmo> __result, Pawn __instance)
		{

			Pawn pawn = __instance;
			string disablementReason = string.Empty;

			if (RJWSettings.RPG_hero_control)
			{
				if ((pawn.IsDesignatedHero() && !pawn.IsHeroOwner()))	//not hero owner, disable drafting
				{
					disablementReason = "ForHeroRefuse1Desc";
				}
				else if (!pawn.IsDesignatedHero() && RJWSettings.RPG_hero_control_HC)   //not hero, disable drafting
				{
					//no permission to change designation for NON prisoner hero/ other player
					if (pawn.CanChangeDesignationPrisoner() && pawn.CanChangeDesignationColonist()
							&& (pawn.kindDef.race.defName.Contains("AIRobot")
							|| (pawn.kindDef.race.defName.Contains("Droid") && !pawn.kindDef.race.defName.Contains("AndDroid"))
							|| pawn.kindDef.race.defName.Contains("RPP_Bot")
							))
					//if (false)
					{
						//add some filter for bots and stuff? if there is such stuff
						//so it can be drafted and controlled for fighting
					}
					else
					{
						disablementReason = "ForHeroRefuseHCDesc";
					}
				}
			}

			foreach (var gizmo in __result)
			{
				if ( disablementReason.NullOrEmpty() || (!(gizmo is Command)) )  //we do not filter out non-command Gizmos, such as shield bar or psychic entropy
				{
					yield return gizmo;
				}

				//ModLog.Message("Gizmo for " + xxx.get_pawnname(__instance) + " type: " + gizmo.GetType()+ ": " + gizmo);

				if (!disablementReason.NullOrEmpty())
				{
					if (gizmo is Verse.Command_VerbTarget) { //weapon icons
						gizmo.Disable(disablementReason.Translate());
						yield return gizmo;
					}
				}
				//all other command gizmos are dropped

			}
		}
	}
}


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 add_RJW_toggle(WidgetRow row, bool worldView)
		{
			if (worldView) return;
			if (!RJWSettings.show_RJW_designation_box) 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]
		[HarmonyPriority(99)]
		static IEnumerable<Gizmo> add_designation_box(IEnumerable<Gizmo> __result, Pawn __instance)
		{
			foreach (var gizmo in __result)
			{
				yield return gizmo;
			}

			if (!RJWSettings.show_RJW_designation_box) yield break;
			if (!(__instance.Faction == Faction.OfPlayer || __instance.IsPrisonerOfColony)) yield break;
			//ModLog.Message("Harmony patch submit_button is called");
			var pawn = __instance;
			yield return new RJWdesignations(pawn);
		}
	}

	///
	///Submit gizmo
	///
	[HarmonyPatch(typeof(Pawn), "GetGizmos")]
	[StaticConstructorOnStartup]
	static class submit_button
	{
		[HarmonyPostfix]
		[HarmonyPriority(101)]
		static IEnumerable<Gizmo> add_button(IEnumerable<Gizmo> __result, Pawn __instance)
		{
			foreach (var gizmo in __result)
			{
				yield return gizmo;
			}

			//ModLog.Message("Harmony patch submit_button is called");
			var pawn = __instance;
			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)))
					{
						yield return new Command_Action
						{
							defaultLabel = "CommandSubmit".Translate(),
							icon = submit_icon,
							defaultDesc = "CommandSubmitDesc".Translate(),
							action = delegate
							{
								LayDownAndAccept(pawn);
							},
							hotKey = KeyBindingDefOf.Misc3
						};
					}

		}

		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;
using RimWorld;
using Verse;
using HarmonyLib;

namespace rjw
{
	[HarmonyPatch(typeof(CompAbilityEffect_WordOfLove), "ValidateTarget")]
	internal static class PATCH_CompAbilityEffect_WordOfLove_ValidateTarget
	{
		[HarmonyPrefix]
		static bool GenderChecks(ref LocalTargetInfo target, LocalTargetInfo ___selectedTarget, ref bool __result)
		{
			Pawn pawn = ___selectedTarget.Pawn;
			Pawn pawn2 = target.Pawn;
			if (pawn != pawn2 && pawn != null && pawn2 != null)
			{
				__result = !xxx.is_asexual(pawn) && (xxx.is_bisexual(pawn) || xxx.is_pansexual(pawn) || (xxx.is_heterosexual(pawn) && pawn.gender != pawn2.gender) || (xxx.is_homosexual(pawn) && pawn.gender == pawn2.gender));
				if (__result == false)
				{ Messages.Message("AbilityCantApplyWrongAttractionGender".Translate(pawn, pawn2), pawn, MessageTypeDefOf.RejectInput, false); }
				return false;
			}
			return true;
		}
	}
}


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 StatsReportUtilityPatch
	{
		[HarmonyPatch(typeof(StatsReportUtility))]
		[StaticConstructorOnStartup]
		public static class Patch_StatsReportUtility
		{
			private static StatDrawEntry DescriptionEntry(Hediff thing)
			{
				return new StatDrawEntry(StatCategoryDefOf.BasicsImportant, "Description".Translate(), "", thing.DescriptionFlavor, 99999, null, Dialog_InfoCard.DefsToHyperlinks(thing.def.descriptionHyperlinks), false);
			}
		}
	}

	class HediffPatch
	{
		[HarmonyPatch(typeof(Hediff))]
		[StaticConstructorOnStartup]
		public static class Patch_Hediff
		{
			public virtual string DescriptionFlavor
			{
				get
				{
					return this.def.description;
				}
			}
		}
	}
}


code:
using Verse;
using RimWorld;

namespace rjw
{
	/// <summary>
	/// FeelingBroken raise/lower severity
	/// </summary>
	public class HediffCompProperties_FeelingBrokenSeverityReduce : HediffCompProperties
	{
		public HediffCompProperties_FeelingBrokenSeverityReduce()
		{
			this.compClass = typeof(HediffComp_FeelingBrokenSeverityReduce);
		}

		public SimpleCurve severityPerDayReduce;
	}
	class HediffComp_FeelingBrokenSeverityReduce : HediffComp_SeverityPerDay
	{
		private HediffCompProperties_FeelingBrokenSeverityReduce Props
		{
			get
			{
				return (HediffCompProperties_FeelingBrokenSeverityReduce)this.props;
			}
		}
		public override void CompPostTick(ref float severityAdjustment)
		{
			base.CompPostTick(ref severityAdjustment);
			if (base.Pawn.IsHashIntervalTick(SeverityUpdateInterval))
			{
				float num = this.SeverityChangePerDay();
				num *= 0.00333333341f;
				if (xxx.has_traits(Pawn))
				{
					if (xxx.RoMIsActive)
						if (Pawn.story.traits.HasTrait(xxx.Succubus))
							num *= 4.0f;
					if (Pawn.story.traits.HasTrait(TraitDefOf.Tough))
					{
						num *= 2.0f;
					}
					if (Pawn.story.traits.HasTrait(TraitDefOf.Tough))
					{
						num *= 2.0f;
					}
					if (Pawn.story.traits.HasTrait(TraitDefOf.Wimp))
					{
						num *= 0.5f;
					}
					if (Pawn.story.traits.HasTrait(TraitDefOf.Nerves))
					{
						int td = Pawn.story.traits.DegreeOfTrait(TraitDefOf.Nerves);
						switch (td)
						{
							case -2:
								num *= 2.0f;
								break;
							case -1:
								num *= 1.5f;
								break;
							case 1:
								num *= 0.5f;
								break;
							case 2:
								num *= 0.25f;
								break;
						}
					}
				}
				severityAdjustment += num;
			}
		}
		public override float SeverityChangePerDay()
		{
			return this.Props.severityPerDayReduce.Evaluate(this.parent.ageTicks / GenDate.TicksPerDay);
		}
	}

	public class HediffCompProperties_FeelingBrokenSeverityIncrease : HediffCompProperties
	{
		public HediffCompProperties_FeelingBrokenSeverityIncrease()
		{
			this.compClass = typeof(HediffComp_FeelingBrokenSeverityIncrease);
		}

		public SimpleCurve severityPerDayIncrease;
	}
	class HediffComp_FeelingBrokenSeverityIncrease : AdvancedHediffComp
	{
		private HediffCompProperties_FeelingBrokenSeverityIncrease Props
		{
			get
			{
				return (HediffCompProperties_FeelingBrokenSeverityIncrease)this.props;
			}
		}
		public override void CompPostMerged(Hediff other)
		{
			float num = Props.severityPerDayIncrease.Evaluate(this.parent.ageTicks / GenDate.TicksPerDay);
			if (xxx.has_traits(Pawn))
			{
				if (xxx.RoMIsActive)
					if (Pawn.story.traits.HasTrait(xxx.Succubus))
						num *= 0.25f;

				if (Pawn.story.traits.HasTrait(TraitDefOf.Tough))
				{
					num *= 0.5f;
				}
				if (Pawn.story.traits.HasTrait(TraitDefOf.Wimp))
				{
					num *= 2.0f;
				}
				if (Pawn.story.traits.HasTrait(TraitDefOf.Nerves))
				{
					int td = Pawn.story.traits.DegreeOfTrait(TraitDefOf.Nerves);
					switch (td)
					{
						case -2:
							num *= 0.25f;
							break;
						case -1:
							num *= 0.5f;
							break;
						case 1:
							num *= 1.5f;
							break;
						case 2:
							num *= 2.0f;
							break;
					}
				}
			}
			other.Severity *= num;
		}
	}

	public class AdvancedHediffWithComps : HediffWithComps
	{
		public override bool TryMergeWith(Hediff other)
		{
			for (int i = 0; i < this.comps.Count; i++)
			{
				if(this.comps[i] is AdvancedHediffComp) ((AdvancedHediffComp)this.comps[i]).CompBeforeMerged(other);
			}
			return base.TryMergeWith(other);
		}
	}
	public class AdvancedHediffComp : HediffComp
	{
		public virtual void CompBeforeMerged(Hediff other)
		{
		}
	}
}


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

namespace rjw
{
//TODO figure out how this thing works and move eggs to comps

	[StaticConstructorOnStartup]
	public class HediffDef_PartBase : HediffDef
	{
		public bool discovered = false;
		public string Eggs = "";					//for ovi eggs, maybe
		public string FluidType = "";				//cummies/milk - insectjelly/honey etc
		public string DefaultBodyPart = "";			//Bodypart to move this part to, after fucking up with pc or other mod
		public List<string> DefaultBodyPartList;	//Bodypart list to move this part to, after fucking up with pc or other mod
		public float FluidAmmount = 0;				//amount of Milk/Ejaculation/Wetness
		public bool produceEggs;					//set in xml
		public int minEggTick = 12000;
		public int maxEggTick = 120000;
		public int minEggsProduced = 1;             // min amount of eggs produces by a bodypart
		public int maxEggsProduced = 1;             // max amount of eggs produces by a bodypart
	}
}


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

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

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

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

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

		public void HealWounds()
		{
			IEnumerable<Hediff> enumerable = from hd in pawn.health.hediffSet.hediffs
											 where !hd.IsTended() && hd.TendableNow()
											 select hd;
			if (enumerable != null)
			{
				foreach (Hediff item in enumerable)
				{
					HediffWithComps val = item as HediffWithComps;
					if (val != null)
						if (val.Bleeding)
						{
							//Log.Message("TrySealWounds " + xxx.get_pawnname(pawn) + ", Bleeding " + item.Label);
							//HediffComp_TendDuration val2 = HediffUtility.TryGetComp<HediffComp_TendDuration>(val);
							val.Heal(0.5f);
							//val2.tendQuality = 1f;
							//val2.tendTicksLeft = 10000;
							//pawn.health.Notify_HediffChanged(item);
						}
						// tend infections
						// tend lifeThreatening chronic
						else if ((!val.def.chronic && val.def.lethalSeverity > 0f) || (val.CurStage?.lifeThreatening ?? false))
						{
							//Log.Message("TryHeal " + xxx.get_pawnname(pawn) + ", infection(?) " + item.Label);
							HediffComp_TendDuration val2 = HediffUtility.TryGetComp<HediffComp_TendDuration>(val);
							val2.tendQuality = 1f;
							val2.tendTicksLeft = 10000;
							pawn.health.Notify_HediffChanged(item);
						}
				}
			}
		}

		public void SatisfyHunger()
		{
			Need_Food need = pawn.needs.TryGetNeed<Need_Food>();
			if (need == null)
			{
				return;
			}
			//pawn.PositionHeld.IsInPrisonCell(pawn.Map)
			//Log.Message("Cocoon::SatisfyHunger() " + xxx.get_pawnname(pawn) + " IsInPrisonCell " + pawn.PositionHeld.IsInPrisonCell(pawn.Map));
			//Log.Message("Cocoon::SatisfyHunger() " + xxx.get_pawnname(pawn) + " GetRoom " + pawn.PositionHeld.GetRoom(pawn.Map));
			//Log.Message("Cocoon::SatisfyHunger() " + xxx.get_pawnname(pawn) + " GetRoom " + pawn.PositionHeld.GetZone(pawn.Map));

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

		public void SatisfyThirst()
		{
			if (!xxx.DubsBadHygieneIsActive)
				return;

			Need need = pawn.needs.AllNeeds.Find(x => x.def == xxx.DBHThirst);
			if (need == null)
			{
				return;
			}

			if (need.CurLevel < 0.15f)
			{
				//Log.Message("Cocoon::SatisfyThirst() " + xxx.get_pawnname(pawn) + " need to drink");
				float nutrition_amount = need.MaxLevel / 5f;
				pawn.needs.TryGetNeed(need.def).CurLevel += nutrition_amount;
			}
		}

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


code:
using System.Linq;
using Verse;
using RimWorld;
using System.Text;
using Multiplayer.API;
using UnityEngine;

namespace rjw
{
	public class Hediff_PartBaseArtifical : Hediff_Implant
	{
		public override bool ShouldRemove => false;

		public bool discovered = false;

		// Used for ovipositors.
		public int nextEggTick = -1;
		public float lastsize = -1;
		public float oviTick = 0;

		public override void ExposeData()
		{
			base.ExposeData();

			Scribe_Values.Look(ref this.nextEggTick, "nextEggTick");
			Scribe_Values.Look(ref this.lastsize, "lastsize");
			Scribe_Values.Look(ref this.oviTick, "oviTick");
			//			Scribe_Values.Look(ref this.produceEggs, "produceEggs");
			//			Scribe_Defs.Look(ref this.pawnKindDefOverride, "pawnKindDefOverride");
			//			Scribe_Values.Look(ref this.genitalType, "genitalType");
		}

		public override string LabelBase
		{
			get
			{
				/*
				 * make patch to make/save capmods?
				if (CapMods.Count < 5)
				{
					PawnCapacityModifier pawnCapacityModifier = new PawnCapacityModifier();
					pawnCapacityModifier.capacity = PawnCapacityDefOf.Moving;
					pawnCapacityModifier.offset += 0.5f;
					CapMods.Add(pawnCapacityModifier);
				}
				*/

				//name/kind
				return this.def.label;
			}
		}

		//public override string LabelInBrackets
		//{
		//	get
		//	{
		//		string size = "on fire!";
		//		size = (this.comps.Find(x => x is CompHediffBodyPart) as CompHediffBodyPart).Size;
		//		return size;

		//		//vanilla
		//		//return (this.CurStage != null && !this.CurStage.label.NullOrEmpty()) ? this.CurStage.label : null;
		//	}
		//}

		//overrides comps
		//public override string TipStringExtra
		//{
		//	get
		//	{
		//		StringBuilder stringBuilder = new StringBuilder();
		//		foreach (StatDrawEntry current in HediffStatsUtility.SpecialDisplayStats(this.CurStage, this))
		//		{
		//			if (current.ShouldDisplay)
		//			{
		//				stringBuilder.AppendLine(current.LabelCap + ": " + current.ValueString);
		//			}
		//		}
		//		//stringBuilder.AppendLine("Size: " + this.TryGetComp<CompHediffBodyPart>.Size);
		//		//stringBuilder.AppendLine("1");// size?
		//		//stringBuilder.AppendLine("2");// erm something?
		//		return stringBuilder.ToString();
		//	}
		//}

		/// <summary>
		/// stack hediff in health tab?
		/// </summary>
		public override int UIGroupKey
		{
			get
			{
				return loadID;
			}
		}

		/// <summary>
		/// do not merge same rjw parts into one
		/// </summary>
		public override bool TryMergeWith(Hediff other)
		{
			return false;
		}

		/// <summary>
		/// show rjw parts in health tab or not
		/// </summary>
		public override bool Visible
		{
			get
			{
				if (RJWSettings.ShowRjwParts == RJWSettings.ShowParts.Hide)
				{
					discovered = false;
				}
				else if (!discovered)
				{
					if (RJWSettings.ShowRjwParts != RJWSettings.ShowParts.Hide)
					{
						discovered = true;
						return discovered;
					}

					//show at game start
					if (Current.ProgramState != ProgramState.Playing && Prefs.DevMode)
						return true;

					//show for hero
					if (pawn.IsDesignatedHero() && pawn.IsHeroOwner())
					{
						discovered = true;
						return discovered;
					}

					//show if no clothes
					if (pawn.apparel != null)// animals?
					{
						bool hasPants;
						bool hasShirt;
						pawn.apparel.HasBasicApparel(out hasPants, out hasShirt);// naked?

						if (!hasPants)
						{
							bool flag3 = false;
							foreach (BodyPartRecord current in this.pawn.health.hediffSet.GetNotMissingParts(BodyPartHeight.Undefined, BodyPartDepth.Undefined, null, null))
							{
								if (current.IsInGroup(BodyPartGroupDefOf.Legs))
								{
									flag3 = true;
									break;
								}
							}
							if (!flag3)
							{
								hasPants = true;
							}
						}

						if (this.def.defName.ToLower().Contains("breast") || this.def.defName.ToLower().Contains("chest"))
							discovered = !hasShirt;
						else
							discovered = !hasPants;

					}
				}

				return discovered;
			}
		}

		/// <summary>
		/// egg production ticks
		/// </summary>
		public override void Tick()
		{
			var thisTick = Find.TickManager.TicksGame;

			if ((thisTick - oviTick) >= 10000)
			{
				var partBase = def as HediffDef_PartBase;
				if (partBase != null)
				{
					if (partBase.produceEggs)
					{
						//Log.Message("genital tick");
						//Log.Message("pawn " + pawn.Label);
						//Log.Message("id " + pawn.ThingID);
						var IsPlayerFaction = pawn.Faction?.IsPlayer ?? false; //colonists/animals
						var IsPlayerHome = pawn.Map?.IsPlayerHome ?? false;

						if (IsPlayerHome || IsPlayerFaction || pawn.IsPrisonerOfColony)
						{
							//Log.Message("-2 ");
							if (pawn.health.capacities.GetLevel(PawnCapacityDefOf.Moving) <= 0.5)
							{
								return;
							}

							//Log.Message("-3 ");
							if (thisTick >= nextEggTick)
							{
								float maxEggsSize = (pawn.BodySize / 5) * (xxx.has_quirk(pawn, "Incubator") ? 2f : 1f) *
													(Genital_Helper.has_ovipositorF(pawn) ? 2f : 0.5f);
								float eggedsize = 0;
								//Log.Message("-4 ");
								foreach (var ownEgg in pawn.health.hediffSet.GetHediffs<Hediff_InsectEgg>())
								{
									if (ownEgg.father != null)
										eggedsize += ownEgg.father.RaceProps.baseBodySize / 5;
									else if (ownEgg.implanter != null)
										eggedsize += ownEgg.implanter.RaceProps.baseBodySize / 5;
									else //something fucked up, father/implanter null / immortal pawn reborn /egg is broken?
										eggedsize += ownEgg.eggssize;
								}

								//Log.Message("-5 ");
								if (RJWSettings.DevMode) ModLog.Message($"{xxx.get_pawnname(pawn)} filled with {eggedsize} out of max capacity of {maxEggsSize} eggs.");
								if (eggedsize < maxEggsSize)
								{
									HediffDef_InsectEgg egg = null;
									string defname = "";

									//Log.Message("-6 ");
									while (egg == null)
									{
										if (defname == "")
										{
											if (RJWSettings.DevMode) ModLog.Message(" trying to find " + pawn.kindDef.defName + " egg");
											defname = pawn.kindDef.defName;
										}
										else
										{

											if (RJWSettings.DevMode) ModLog.Message(" no " + defname + " egg found, defaulting to Unknown egg");
											defname = "Unknown";
										}

										//Rand.PopState();
										//Rand.PushState(RJW_Multiplayer.PredictableSeed());
										//Log.Message("-7 ");
										egg = TryGetEgg(defname);
									}

									//Log.Message("-8 ");
									if (RJWSettings.DevMode) ModLog.Message("I choose you " + egg + "!");

									//Log.Message("-9 ");
									var genitals = Genital_Helper.get_genitalsBPR(pawn);
									if (genitals != null)
									{
										//Log.Message("-10 ");
										var addedEgg = pawn.health.AddHediff(egg, genitals) as Hediff_InsectEgg;
										//Log.Message("-11 ");
										addedEgg?.Implanter(pawn);
									}
									//Log.Message("-12 ");
								}

								// Reset for next egg.
								nextEggTick = TryGetnextEggTick();
							}
						}
					}
				}
				oviTick = thisTick;
			}
		}

		[SyncMethod]
		private int TryGetnextEggTick()
		{
			var partBase = def as HediffDef_PartBase;
			return Find.TickManager.TicksGame + Rand.Range(partBase.minEggTick, partBase.maxEggTick);
		}

		[SyncMethod]
		private HediffDef_InsectEgg TryGetEgg(string defname)
		{
			return (from x in DefDatabase<HediffDef_InsectEgg>.AllDefs where x.IsParent(defname) select x).RandomElement();
		}
	}
}


code:
using System.Linq;
using Verse;
using RimWorld;
using System.Text;
using Multiplayer.API;
using UnityEngine;

namespace rjw
{
	public class Hediff_PartBaseNatural : HediffWithComps
	{
		public override bool ShouldRemove => false;

		public bool discovered = false;

		// Used for ovipositors.
		public int nextEggTick = -1;
		public float lastsize = -1;
		public float oviTick = 0;
		public float dayTick = 0;

		public override void ExposeData()
		{
			base.ExposeData();

			Scribe_Values.Look(ref this.nextEggTick, "nextEggTick");
			Scribe_Values.Look(ref this.lastsize, "lastsize");
			Scribe_Values.Look(ref this.oviTick, "oviTick");
			Scribe_Values.Look(ref this.dayTick, "dayTick");
			//			Scribe_Values.Look(ref this.produceEggs, "produceEggs");
			//			Scribe_Defs.Look(ref this.pawnKindDefOverride, "pawnKindDefOverride");
			//			Scribe_Values.Look(ref this.genitalType, "genitalType");
		}

		public override string LabelBase
		{
			get
			{
				/*
				 * make patch to make/save capmods?
				if (CapMods.Count < 5)
				{
					PawnCapacityModifier pawnCapacityModifier = new PawnCapacityModifier();
					pawnCapacityModifier.capacity = PawnCapacityDefOf.Moving;
					pawnCapacityModifier.offset += 0.5f;
					CapMods.Add(pawnCapacityModifier);
				}
				*/

				//name/kind
				return this.def.label;
			}
		}

		//public override string LabelInBrackets
		//{
		//	get
		//	{
		//		string size = "on fire!";
		//		size = (this.comps.Find(x => x is CompHediffBodyPart) as CompHediffBodyPart).Size;
		//		return size;

		//		//vanilla
		//		//return (this.CurStage != null && !this.CurStage.label.NullOrEmpty()) ? this.CurStage.label : null;
		//	}
		//}

		//overrides comps
		//public override string TipStringExtra
		//{
		//	get
		//	{
		//		StringBuilder stringBuilder = new StringBuilder();
		//		foreach (StatDrawEntry current in HediffStatsUtility.SpecialDisplayStats(this.CurStage, this))
		//		{
		//			if (current.ShouldDisplay)
		//			{
		//				stringBuilder.AppendLine(current.LabelCap + ": " + current.ValueString);
		//			}
		//		}
		//		//stringBuilder.AppendLine("Size: " + this.TryGetComp<CompHediffBodyPart>.Size);
		//		//stringBuilder.AppendLine("1");// size?
		//		//stringBuilder.AppendLine("2");// erm something?
		//		return stringBuilder.ToString();
		//	}
		//}

		/// <summary>
		/// stack hediff in health tab?
		/// </summary>
		public override int UIGroupKey
		{
			get
			{
				return loadID;
			}
		}

		/// <summary>
		/// do not merge same rjw parts into one
		/// </summary>
		public override bool TryMergeWith(Hediff other)
		{
			return false;
		}

		/// <summary>
		/// show rjw parts in health tab or not
		/// </summary>
		public override bool Visible
		{
			get
			{
				if (RJWSettings.ShowRjwParts == RJWSettings.ShowParts.Hide)
				{
					discovered = false;
				}
				else if (!discovered)
				{
					if (RJWSettings.ShowRjwParts != RJWSettings.ShowParts.Hide)
					{
						discovered = true;
						return discovered;
					}

					//show at game start
					if (Current.ProgramState != ProgramState.Playing && Prefs.DevMode)
						return true;

					//show for hero
					if (pawn.IsDesignatedHero() && pawn.IsHeroOwner())
					{
						discovered = true;
						return discovered;
					}

					//show if no clothes
					if (pawn.apparel != null)// animals?
					{
						bool hasPants;
						bool hasShirt;
						pawn.apparel.HasBasicApparel(out hasPants, out hasShirt);// naked?

						if (!hasPants)
						{
							bool flag3 = false;
							foreach (BodyPartRecord current in this.pawn.health.hediffSet.GetNotMissingParts(BodyPartHeight.Undefined, BodyPartDepth.Undefined, null, null))
							{
								if (current.IsInGroup(BodyPartGroupDefOf.Legs))
								{
									flag3 = true;
									break;
								}
							}
							if (!flag3)
							{
								hasPants = true;
							}
						}

						if (this.def.defName.ToLower().Contains("breast") || this.def.defName.ToLower().Contains("chest"))
							discovered = !hasShirt;
						else
							discovered = !hasPants;

					}
				}

				return discovered;
			}
		}

		/// <summary>
		/// egg production ticks, part growning for childrens
		/// </summary>
		public override void Tick()
		{
			var thisTick = Find.TickManager.TicksGame;

			//change size for growing pawn/child
			if ((thisTick - dayTick) >= GenDate.TicksPerDay)
			{
				//change pawn parts sizes for kids(?)
				//update size if pawn bodysize changed from last check
				if (pawn.BodySize != lastsize)
				{
					var t = this.TryGetComp<CompHediffBodyPart>();
					if (t != null)
					{
						t.updatesize(); // update/reset part size
						lastsize = pawn.BodySize;
					}
				}
				dayTick = thisTick;
			}

			if ((thisTick - oviTick) >= 10000)
			{
				var partBase = def as HediffDef_PartBase;
				if (partBase != null)
				{
					if (partBase.produceEggs)
					{
						//Log.Message("genital ovi tick");
						//Log.Message("pawn " + pawn.Label);
						//Log.Message("id " + pawn.ThingID);
						var IsPlayerFaction = pawn.Faction?.IsPlayer ?? false; //colonists/animals
						var IsPlayerHome = pawn.Map?.IsPlayerHome ?? false;

						if (IsPlayerHome || IsPlayerFaction || pawn.IsPrisonerOfColony)
						{
							//Log.Message("-2 ");
							if (pawn.health.capacities.GetLevel(PawnCapacityDefOf.Moving) <= 0.5)
							{
								return;
							}

							//Log.Message("-3 ");
							if (thisTick >= nextEggTick)
							{
								float maxEggsSize = (pawn.BodySize / 5) * (xxx.has_quirk(pawn, "Incubator") ? 2f : 1f) *
													(Genital_Helper.has_ovipositorF(pawn) ? 2f : 0.5f);
								float eggedsize = 0;
								//Log.Message("-4 ");
								foreach (var ownEgg in pawn.health.hediffSet.GetHediffs<Hediff_InsectEgg>())
								{
									if (ownEgg.father != null)
										eggedsize += ownEgg.father.RaceProps.baseBodySize / 5;
									else if (ownEgg.implanter != null)
										eggedsize += ownEgg.implanter.RaceProps.baseBodySize / 5;
									else //something fucked up, father/implanter null / immortal pawn reborn /egg is broken?
										eggedsize += ownEgg.eggssize;
								}

								//Log.Message("-5 ");
								if (RJWSettings.DevMode) ModLog.Message($"{xxx.get_pawnname(pawn)} filled with {eggedsize} out of max capacity of {maxEggsSize} eggs.");
								if (eggedsize < maxEggsSize)
								{
									HediffDef_InsectEgg egg = null;
									string defname = "";

									//Log.Message("-6 ");
									while (egg == null)
									{
										if (defname == "")
										{
											if (RJWSettings.DevMode) ModLog.Message(" trying to find " + pawn.kindDef.defName + " egg");
											defname = pawn.kindDef.defName;
										}
										else
										{

											if (RJWSettings.DevMode) ModLog.Message(" no " + defname + " egg found, defaulting to Unknown egg");
											defname = "Unknown";
										}

										//Rand.PopState();
										//Rand.PushState(RJW_Multiplayer.PredictableSeed());
										//Log.Message("-7 ");
										egg = TryGetEgg(defname);
									}

									//Log.Message("-8 ");
									if (RJWSettings.DevMode) ModLog.Message("I choose you " + egg + "!");

									//Log.Message("-9 ");
									var genitals = Genital_Helper.get_genitalsBPR(pawn);
									if (genitals != null)
									{
										// Calculator amount of eggs added
										int neweggs = 1;
										int mineggs = 1;
										int maxeggs = 1;
										mineggs = Genital_Helper.min_EggsProduced(pawn);
										maxeggs = Genital_Helper.max_EggsProduced(pawn);
										neweggs = (int)(mineggs + (Rand.Value * (maxeggs - mineggs)) + 0.5);
										if (RJWSettings.DevMode)
										{
											ModLog.Message("Min Egg amount : " + mineggs);
											ModLog.Message("Max Egg amount : " + maxeggs);
											ModLog.Message("Actual Egg amount : " + neweggs);
										}

										//Log.Message("-10 ");
										//Log.Message("genitals bpr: " + genitals);
										for (int i = 0; i < neweggs; i++)
										{
											var addedEgg = pawn.health.AddHediff(egg, genitals) as Hediff_InsectEgg;

											// ModLog.Message(" i = " + i);
											addedEgg?.Implanter(pawn);
										}
									}
									//Log.Message("-12 ");
								}

								// Reset for next egg.
								nextEggTick = TryGetnextEggTick();
							}
						}
					}
				}
				oviTick = thisTick;
			}
		}

		[SyncMethod]
		private int TryGetnextEggTick()
		{
			var partBase = def as HediffDef_PartBase;
			return Find.TickManager.TicksGame + Rand.Range(partBase.minEggTick, partBase.maxEggTick);
		}

		[SyncMethod]
		private HediffDef_InsectEgg TryGetEgg(string defname)
		{
			return (from x in DefDatabase<HediffDef_InsectEgg>.AllDefs where x.IsParent(defname) select x).RandomElement();
		}
	}
}


code:
using System.Linq;
using Verse;

namespace rjw
{
	public class Hediff_PartsSizeChangerPC : HediffWithComps
	{
		public override void PostAdd(DamageInfo? dinfo)
		{
			foreach (Hediff hed in pawn.health.hediffSet.hediffs.Where(x => x.Part != null && x.Part == Part && (x is Hediff_PartBaseNatural || x is Hediff_PartBaseArtifical)).ToList())
			{
				CompHediffBodyPart CompHediff = hed.TryGetComp<rjw.CompHediffBodyPart>();
				if (CompHediff != null)
				{
					//Log.Message("  Hediff_PartsSizeChanger: " + hed.Label);
					//Log.Message("  Hediff_PartsSizeChanger: " + hed.Severity);
					//Log.Message("  Hediff_PartsSizeChanger: " + CompHediff.SizeBase);
					//Log.Message("  Hediff_PartsSizeChanger: " + "-----");
					//Log.Message("  Hediff_PartsSizeChanger: " + this.Label);
					//Log.Message("  Hediff_PartsSizeChanger: " + this.Severity);
					CompHediff.SizeBase = this.CurStage.minSeverity;
					CompHediff.initComp(reroll: true);
					CompHediff.updatesize();
					//Log.Message("  Hediff_PartsSizeChanger: " + "-----");
					//Log.Message("  Hediff_PartsSizeChanger: " + hed.Label);
					//Log.Message("  Hediff_PartsSizeChanger: " + hed.Severity);
					//Log.Message("  Hediff_PartsSizeChanger: " + CompHediff.SizeBase);
				}
			}
			pawn.health.RemoveHediff(this);
		}
	}
	public class Hediff_PartsSizeChangerCE : HediffWithComps
	{
		public override void PostAdd(DamageInfo? dinfo)
		{
			foreach (Hediff hed in pawn.health.hediffSet.hediffs.Where(x => x.Part != null && x.Part == Part && (x is Hediff_PartBaseNatural || x is Hediff_PartBaseArtifical)).ToList())
			{
				CompHediffBodyPart CompHediff = hed.TryGetComp<rjw.CompHediffBodyPart>();
				if (CompHediff != null)
				{
					CompHediff.SizeBase = this.def.initialSeverity;
					CompHediff.initComp(reroll: true);
					CompHediff.updatesize();
				}
			}
			pawn.health.RemoveHediff(this);
		}
	}
}


code:
using Verse;

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


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

namespace rjw
{
	public class PartAdder
	{
		public float chance = 0f;
		public string rjwPart;
		public List<string> bodyParts;
	}
}


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

namespace rjw
{
	public class PartProps : DefModExtension
	{
		/// <summary>
		/// just a text
		/// </summary>

		public List<string> props;

		public static bool TryGetProps(Hediff hediff, out List<string> p)
		{
			return TryGetPartProps(hediff, extension => extension.props, out p);
		}

		public static bool TryGetPartProps(
			Hediff hediff,
			Func<PartProps, List<string>> getList,
			out List<string> p)
		{
			if (!hediff.def.HasModExtension<PartProps>())
			{
				p = null;
				return false;
			}

			var extension = hediff.def.GetModExtension<PartProps>();
			var list = getList(extension);

			if (list == null)
			{
				p = null;
				return false;
			}

			p = list;
			return true;
		}
	}
}


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

namespace rjw
{
	public class PartSizeExtension : DefModExtension
	{
		/// <summary>
		/// Human standard would be 1.0. Null for no weight display.
		/// </summary>
		public bool? bodysizescale = false; // rescales parts sizes based on bodysize of initial owner race
		public float? density = null;

		public List<float> lengths;
		public List<float> girths;
		public List<float> cupSizes;

		public static bool TryGetLength(Hediff hediff, out float size)
		{
			return TryGetSizeFromCurve(hediff, extension => extension.lengths, true, out size);
		}

		public static bool TryGetGirth(Hediff hediff, out float size)
		{
			return TryGetSizeFromCurve(hediff, extension => extension.girths, true, out size);
		}

		public static bool TryGetCupSize(Hediff hediff, out float size)
		{
			// Cup size is already "scaled" because the same breast volume has a smaller cup size on a larger band size.
			return TryGetSizeFromCurve(hediff, extension => extension.cupSizes, false, out size);
		}

		public static float GetBandSize(Hediff hediff)
		{
			var size = GetUnderbustSize(hediff);
			size /= PartStagesDef.Instance.bandSizeInterval;
			size = (float)Math.Round(size, MidpointRounding.AwayFromZero);
			size *= PartStagesDef.Instance.bandSizeInterval;
			return size;
		}

		public static float GetUnderbustSize(Hediff hediff)
		{
			return PartStagesDef.Instance.bandSizeBase * GetLinearScale(hediff);
		}

		static float GetLinearScale(Hediff hediff)
		{
			var t = hediff.TryGetComp<CompHediffBodyPart>();
			if (t != null)
			{
				return t.SizeOwner;
			}
			return hediff.pawn.BodySize;
		}

		public static bool TryGetOverbustSize(Hediff hediff, out float size)
		{
			if (!TryGetCupSize(hediff, out var cupSize))
			{
				size = 0f;
				return false;
			}

			// Cup size is rounded up, so to do the math backwards subtract .9
			size = GetUnderbustSize(hediff) + ((cupSize - .9f) * PartStagesDef.Instance.cupSizeInterval);
			return true;
		}

		static bool TryGetSizeFromCurve(
			Hediff hediff,
			Func<PartSizeExtension, List<float>> getList,
			bool shouldScale,
			out float size)
		{
			if (!hediff.def.HasModExtension<PartSizeExtension>())
			{
				size = 0f;
				return false;
			}

			var extension = hediff.def.GetModExtension<PartSizeExtension>();
			var list = getList(extension);

			if (list == null)
			{
				size = 0f;
				return false;
			}

			var curve = new SimpleCurve(hediff.def.stages.Zip(list, (stage, size) => new CurvePoint(stage.minSeverity, size)));
			var scaleFactor = shouldScale ? GetLinearScale(hediff) : 1.0f;
			size = curve.Evaluate(hediff.Severity) * scaleFactor;
			return true;
		}

		public static bool TryGetPenisWeight(Hediff hediff, out float weight)
		{
			if (!TryGetLength(hediff, out float length) ||
				!TryGetGirth(hediff, out float girth))
			{
				weight = 0f;
				return false;
			}

			var density = hediff.def.GetModExtension<PartSizeExtension>().density;
			if (density == null)
			{
				weight = 0f;
				return false;
			}

			var r = girth / (2.0 * Math.PI);
			var volume = r * r * Math.PI * length;

			weight = (float)(volume * density.Value / 1000f);
			return true;
		}

		public static bool TryGetBreastWeight(Hediff hediff, out float weight)
		{
			if (!TryGetCupSize(hediff, out float rawSize))
			{
				weight = 0f;
				return false;
			}

			var density = hediff.def.GetModExtension<PartSizeExtension>().density;
			if (density == null)
			{
				weight = 0f;
				return false;
			}

			// Up a band size and down a cup size is about the same volume.
			var extraBandSize =  PartStagesDef.Instance.bandSizeBase * (1.0f - GetLinearScale(hediff));
			var extraCupSizes = extraBandSize / PartStagesDef.Instance.bandSizeInterval;
			var size = rawSize + extraCupSizes;

			var pounds = 0.765f
				+ 0.415f * size
				+ -0.0168f * size * size
				+ 2.47E-03f * size * size * size;
			var kg = Math.Max(0, pounds * 0.45359237f);
			weight = kg * density.Value;
			return true;
		}
	}
}


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

namespace rjw
{
	public class InteractionExtension : DefModExtension
	{
		/// <summary>
		/// </summary>
		public string RMBLabel = ""; // rmb menu
		public string rjwSextype = ""; // xxx.rjwSextype

		public List<string> rulepack_defs = new List<string>(); //rulepack(s) for this interaction
	}
}


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

using RimWorld;
using Verse;

namespace rjw
{
	internal class InteractionWorker_AnalSexAttempt : InteractionWorker
	{
		//initiator - rapist
		//recipient - victim
		public static bool AttemptAnalSex(Pawn initiator, Pawn recipient)
		{
			//--Log.Message(xxx.get_pawnname(initiator) + " is attempting to anally rape " + xxx.get_pawnname(recipient));
			return true;
		}

		public override float RandomSelectionWeight(Pawn initiator, Pawn recipient)
		{
			// this interaction is triggered by the jobdriver
			if (initiator == null || recipient == null)
				return 0.0f;

			return 0.0f; // base.RandomSelectionWeight(initiator, recipient);
		}

		public override void Interacted(Pawn initiator, Pawn recipient, List<RulePackDef> extraSentencePacks, out string letterText, out string letterLabel, out LetterDef letterDef, out LookTargets lookTargets)
		{
			//add something fancy here later?
			letterText = null;
			letterLabel = null;
			letterDef = null;
			lookTargets = recipient;
			//Find.LetterStack.ReceiveLetter("Rape attempt", "A wandering nymph has decided to join your colony.", LetterDefOf.NegativeEvent, recipient);

			if (initiator == null || recipient == null)
				return;
			//--ModLog.Message(" InteractionWorker_AnalRapeAttempt::Interacted( " + xxx.get_pawnname(initiator) + ", " + xxx.get_pawnname(recipient) + " ) called");
			AttemptAnalSex(initiator, recipient);
		}
	}

	internal class InteractionWorker_VaginalSexAttempt : InteractionWorker
	{
		//initiator - rapist
		//recipient - victim
		public static bool AttemptAnalSex(Pawn initiator, Pawn recipient)
		{
			//--Log.Message(xxx.get_pawnname(initiator) + " is attempting to anally rape " + xxx.get_pawnname(recipient));
			return false;
		}

		public override float RandomSelectionWeight(Pawn initiator, Pawn recipient)
		{
			// this interaction is triggered by the jobdriver
			if (initiator == null || recipient == null)
				return 0.0f;

			return 0.0f; // base.RandomSelectionWeight(initiator, recipient);
		}

		public override void Interacted(Pawn initiator, Pawn recipient, List<RulePackDef> extraSentencePacks, out string letterText, out string letterLabel, out LetterDef letterDef, out LookTargets lookTargets)
		{
			//add something fancy here later?
			letterText = null;
			letterLabel = null;
			letterDef = null;
			lookTargets = recipient;
			//Find.LetterStack.ReceiveLetter("Rape attempt", "A wandering nymph has decided to join your colony.", LetterDefOf.NegativeEvent, recipient);

			if (initiator == null || recipient == null)
				return;
			//--ModLog.Message(" InteractionWorker_AnalRapeAttempt::Interacted( " + xxx.get_pawnname(initiator) + ", " + xxx.get_pawnname(recipient) + " ) called");
			AttemptAnalSex(initiator, recipient);
		}
	}

	internal class InteractionWorker_OtherSexAttempt : InteractionWorker
	{
		//initiator - rapist
		//recipient - victim
		public static bool AttemptAnalSex(Pawn initiator, Pawn recipient)
		{
			//--Log.Message(xxx.get_pawnname(initiator) + " is attempting to anally rape " + xxx.get_pawnname(recipient));
			return false;
		}

		public override float RandomSelectionWeight(Pawn initiator, Pawn recipient)
		{
			// this interaction is triggered by the jobdriver
			if (initiator == null || recipient == null)
				return 0.0f;

			return 0.0f; // base.RandomSelectionWeight(initiator, recipient);
		}

		public override void Interacted(Pawn initiator, Pawn recipient, List<RulePackDef> extraSentencePacks, out string letterText, out string letterLabel, out LetterDef letterDef, out LookTargets lookTargets)
		{
			//add something fancy here later?
			letterText = null;
			letterLabel = null;
			letterDef = null;
			lookTargets = recipient;
			//Find.LetterStack.ReceiveLetter("Rape attempt", "A wandering nymph has decided to join your colony.", LetterDefOf.NegativeEvent, recipient);

			if (initiator == null || recipient == null)
				return;
			//--ModLog.Message(" InteractionWorker_AnalRapeAttempt::Interacted( " + xxx.get_pawnname(initiator) + ", " + xxx.get_pawnname(recipient) + " ) called");
			AttemptAnalSex(initiator, recipient);
		}
	}
}


code:
using System.Collections.Generic;
using RimWorld;
using Verse;
using Verse.AI;
using Multiplayer.API;

namespace rjw
{
	public class JobDriver_BestialityForFemale : JobDriver_SexBaseInitiator
	{
		public IntVec3 SleepSpot => Bed.SleepPosOfAssignedPawn(pawn);
		public override bool TryMakePreToilReservations(bool errorOnFailed)
		{
			return pawn.Reserve(Target, job, 1, 0, null, errorOnFailed);
		}

		[SyncMethod]
		protected override IEnumerable<Toil> MakeNewToils()
		{
			setup_ticks();
			var PartnerJob = xxx.gettin_loved;

			this.FailOnDespawnedOrNull(iTarget);
			this.FailOnDespawnedNullOrForbidden(iBed);
			this.FailOn(() => !pawn.CanReserveAndReach(Partner, PathEndMode.Touch, Danger.Deadly));
			this.FailOn(() => pawn.Drafted);
			this.FailOn(() => Partner.IsFighting());
			this.FailOn(() => !Partner.CanReach(pawn, PathEndMode.Touch, Danger.Deadly));

			yield return Toils_Reserve.Reserve(iTarget, 1, 0);
			Toil gotoAnimal = Toils_Goto.GotoThing(iTarget, PathEndMode.Touch);
			yield return gotoAnimal;

			Toil gotoBed = new Toil();
			gotoBed.defaultCompleteMode = ToilCompleteMode.PatherArrival;
			gotoBed.FailOnBedNoLongerUsable(iBed);
			gotoBed.AddFailCondition(() => Partner.Downed);
			gotoBed.initAction = delegate
			{
				pawn.pather.StartPath(SleepSpot, PathEndMode.OnCell);
				Partner.jobs.StopAll();
				Job job = JobMaker.MakeJob(JobDefOf.GotoMindControlled, SleepSpot);
				Partner.jobs.StartJob(job, JobCondition.InterruptForced);
			};
			yield return gotoBed;

			Toil waitInBed = new Toil();
			waitInBed.FailOn(() => pawn.GetRoom(RegionType.Set_Passable) == null);
			waitInBed.defaultCompleteMode = ToilCompleteMode.Delay;
			waitInBed.initAction = delegate
			{
				ticksLeftThisToil = 5000;
			};
			waitInBed.tickAction = delegate
			{
				pawn.GainComfortFromCellIfPossible();
				if (IsInOrByBed(Bed, Partner) && pawn.PositionHeld == Partner.PositionHeld)
				{
					ReadyForNextToil();
				}
			};
			yield return waitInBed;

			Toil StartPartnerJob = new Toil();
			StartPartnerJob.defaultCompleteMode = ToilCompleteMode.Instant;
			StartPartnerJob.socialMode = RandomSocialMode.Off;
			StartPartnerJob.initAction = delegate
			{
				var gettin_loved = JobMaker.MakeJob(PartnerJob, pawn, Bed);
				Partner.jobs.StartJob(gettin_loved, JobCondition.InterruptForced);
			};
			yield return StartPartnerJob;

			Toil SexToil = new Toil();
			SexToil.defaultCompleteMode = ToilCompleteMode.Never;
			SexToil.socialMode = RandomSocialMode.Off;
			SexToil.handlingFacing = true;
			SexToil.FailOn(() => Partner.Dead);
			SexToil.FailOn(() => Partner.CurJob.def != PartnerJob);
			SexToil.initAction = delegate
			{
				Start();

				// TODO: replace this quick n dirty way
				CondomUtility.GetCondomFromRoom(pawn);
				// Try to use whore's condom first, then client's
				Sexprops.usedCondom = CondomUtility.TryUseCondom(pawn) || CondomUtility.TryUseCondom(Partner);
			};
			SexToil.AddPreTickAction(delegate
			{
				if (pawn.IsHashIntervalTick(ticks_between_hearts))
					if (xxx.is_zoophile(pawn))
						ThrowMetaIconF(pawn.Position, pawn.Map, FleckDefOf.Heart);
					else
						ThrowMetaIconF(pawn.Position, pawn.Map, xxx.mote_noheart);
				SexTick(pawn, Partner);
				SexUtility.reduce_rest(Partner, 2);
				SexUtility.reduce_rest(pawn, 1);
				if (ticks_left <= 0)
					ReadyForNextToil();
			});
			SexToil.AddFinishAction(delegate
			{
				End();
			});
			yield return SexToil;

			yield return new Toil
			{
				initAction = delegate
				{
					//ModLog.Message(" JobDriver_BestialityForMale::MakeNewToils() - creating aftersex toil");
					SexUtility.ProcessSex(Sexprops);
				},
				defaultCompleteMode = ToilCompleteMode.Instant
			};
		}
	}
}


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

namespace rjw
{
	public class JobDriver_BestialityForMale : JobDriver_Rape
	{
		public override bool TryMakePreToilReservations(bool errorOnFailed)
		{
			return pawn.Reserve(Target, job, 1, 0, null, errorOnFailed);
		}

		[SyncMethod]
		protected override IEnumerable<Toil> MakeNewToils()
		{
			//--ModLog.Message(" JobDriver_BestialityForMale::MakeNewToils() called");

			setup_ticks();
			var PartnerJob = xxx.gettin_bred;

			//this.FailOn (() => (!Partner.health.capacities.CanBeAwake) || (!comfort_prisoners.is_designated (Partner)));
			// Fail if someone else reserves the prisoner before the pawn arrives or colonist can't reach animal
			this.FailOn(() => !pawn.CanReserveAndReach(Partner, PathEndMode.Touch, Danger.Deadly));
			this.FailOn(() => Partner.HostileTo(pawn));
			this.FailOnDespawnedNullOrForbidden(iTarget);
			this.FailOn(() => pawn.Drafted);

			yield return Toils_Reserve.Reserve(iTarget, 1, 0);
			//ModLog.Message(" JobDriver_BestialityForMale::MakeNewToils() - moving towards animal");
			yield return Toils_Goto.GotoThing(iTarget, PathEndMode.Touch);
			yield return Toils_Interpersonal.WaitToBeAbleToInteract(pawn);
			yield return Toils_Interpersonal.GotoInteractablePosition(iTarget);

			if (xxx.is_kind(pawn)
				|| (xxx.CTIsActive && xxx.has_traits(pawn) && pawn.story.traits.HasTrait(xxx.RCT_AnimalLover)))
			{
				yield return TalkToAnimal(pawn, Partner);
				yield return TalkToAnimal(pawn, Partner);
			}

			if (Rand.Chance(0.6f))
				yield return TalkToAnimal(pawn, Partner);

			yield return Toils_Goto.GotoThing(iTarget, PathEndMode.OnCell);

			SexUtility.RapeTargetAlert(pawn, Partner);

			Toil StartPartnerJob = new Toil();
			StartPartnerJob.defaultCompleteMode = ToilCompleteMode.Instant;
			StartPartnerJob.socialMode = RandomSocialMode.Off;
			StartPartnerJob.initAction = delegate
			{
				//--ModLog.Message(" JobDriver_BestialityForMale::MakeNewToils() - Setting animal job driver");
				var dri = Partner.jobs.curDriver as JobDriver_SexBaseRecieverRaped;
				if (dri == null)
				{
					//wild animals may flee or attack
					if (pawn.Faction != Partner.Faction && Partner.RaceProps.wildness > Rand.Range(0.22f, 1.0f)
						&& !(pawn.TicksPerMoveCardinal < (Partner.TicksPerMoveCardinal / 2) && !Partner.Downed && xxx.is_not_dying(Partner)))
					{
						Partner.jobs.StopAll(); // Wake up animal if sleeping.

						float aggro = Partner.kindDef.RaceProps.manhunterOnTameFailChance;
						if (Partner.kindDef.RaceProps.predator)
							aggro += 0.2f;
						else
							aggro -= 0.1f;

						//wild animals may attack
						if (Rand.Chance(aggro) && Partner.CanSee(pawn))
						{
							Partner.rotationTracker.FaceTarget(pawn);
							LifeStageUtility.PlayNearestLifestageSound(Partner, (ls) => ls.soundAngry, 1.4f);
							ThrowMetaIconF(Partner.Position, Partner.Map, FleckDefOf.IncapIcon);
							ThrowMetaIcon(pawn.Position, pawn.Map, ThingDefOf.Mote_ColonistFleeing); //red '!'
							Partner.mindState.mentalStateHandler.TryStartMentalState(MentalStateDefOf.Manhunter);
							if (Partner.kindDef.RaceProps.herdAnimal && Rand.Chance(0.2f))
							{ // 20% chance of turning the whole herd hostile...
								List<Pawn> packmates = Partner.Map.mapPawns.AllPawnsSpawned.Where(x =>
									x != Partner && x.def == Partner.def && x.Faction == Partner.Faction &&
									x.Position.InHorDistOf(Partner.Position, 24f) && x.CanSee(Partner)).ToList();

								foreach (Pawn packmate in packmates)
								{
									packmate.mindState.mentalStateHandler.TryStartMentalState(MentalStateDefOf.Manhunter);
								}
							}
							Messages.Message(pawn.Name.ToStringShort + " is being attacked by " + xxx.get_pawnname(Partner) + ".", pawn, MessageTypeDefOf.ThreatSmall);
						}
						//wild animals may flee
						else
						{
							ThrowMetaIcon(Partner.Position, Partner.Map, ThingDefOf.Mote_ColonistFleeing);
							LifeStageUtility.PlayNearestLifestageSound(Partner, (ls) => ls.soundCall);
							Partner.mindState.StartFleeingBecauseOfPawnAction(pawn);
							Partner.mindState.mentalStateHandler.TryStartMentalState(MentalStateDefOf.PanicFlee);
						}
						pawn.jobs.EndCurrentJob(JobCondition.Incompletable);
					}
					else
					{
						Job gettin_bred = JobMaker.MakeJob(PartnerJob, pawn, Partner);
						Partner.jobs.StartJob(gettin_bred, JobCondition.InterruptForced, null, true);
					}
				}
			};
			yield return StartPartnerJob;

			Toil SexToil = new Toil();
			SexToil.defaultCompleteMode = ToilCompleteMode.Never;
			SexToil.defaultDuration = duration;
			SexToil.handlingFacing = true;
			SexToil.FailOn(() => Partner.CurJob.def != PartnerJob);
			SexToil.initAction = delegate
			{
				Partner.pather.StopDead();
				Partner.jobs.curDriver.asleep = false;
				Start();
			};
			SexToil.tickAction = delegate
			{
				if (pawn.IsHashIntervalTick(ticks_between_hearts))
					if (xxx.is_zoophile(pawn))
						ThrowMetaIconF(pawn.Position, pawn.Map, FleckDefOf.Heart);
					else
						ThrowMetaIconF(pawn.Position, pawn.Map, xxx.mote_noheart);
				SexTick(pawn, Partner);
				//no hitting wild animals, and getting rect by their Manhunter
				/*
				if (pawn.IsHashIntervalTick (ticks_between_hits))
					roll_to_hit (pawn, Partner);
					*/
				SexUtility.reduce_rest(Partner, 1);
				SexUtility.reduce_rest(pawn, 2);
				if (ticks_left <= 0)
					ReadyForNextToil();
			};
			SexToil.AddFinishAction(delegate
			{
				End();
			});
			yield return SexToil;

			yield return new Toil
			{
				initAction = delegate
				{
					//ModLog.Message(" JobDriver_BestialityForMale::MakeNewToils() - creating aftersex toil");
					SexUtility.ProcessSex(Sexprops);
				},
				defaultCompleteMode = ToilCompleteMode.Instant
			};
		}

		[SyncMethod]
		private Toil TalkToAnimal(Pawn pawn, Pawn animal)
		{
			Toil toil = new Toil();
			toil.initAction = delegate
			{
				pawn.interactions.TryInteractWith(animal, SexUtility.AnimalSexChat);
			};
			//Rand.PopState();
			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
			toil.defaultCompleteMode = ToilCompleteMode.Delay;
			toil.defaultDuration = Rand.Range(120, 220);
			return toil;
		}
	}
}


code:
using System.Collections.Generic;
using RimWorld;
using Verse;
using Verse.AI;
using Multiplayer.API;

namespace rjw
{
	/// <summary>
	/// This is the driver for animals mounting breeders.
	/// </summary>
	public class JobDriver_Breeding : JobDriver_Rape
	{
		public override bool TryMakePreToilReservations(bool errorOnFailed)
		{
			return pawn.Reserve(Target, job, BreederHelper.max_animals_at_once, 0);
		}

		[SyncMethod]
		protected override IEnumerable<Toil> MakeNewToils()
		{
			setup_ticks();
			var PartnerJob = xxx.gettin_raped;

			//--Log.Message("JobDriver_Breeding::MakeNewToils() - setting fail conditions");
			this.FailOnDespawnedNullOrForbidden(iTarget);
			this.FailOn(() => !pawn.CanReserve(Partner, BreederHelper.max_animals_at_once, 0)); // Fail if someone else reserves the target before the animal arrives.
			this.FailOn(() => !pawn.CanReach(Partner, PathEndMode.Touch, Danger.Some)); // Fail if animal cannot reach target.
			this.FailOn(() => pawn.Drafted);

			// Path to target
			yield return Toils_Goto.GotoThing(iTarget, PathEndMode.OnCell);

			//if (!(pawn.IsDesignatedBreedingAnimal() && Partner.IsDesignatedBreeding()));
			if (!(pawn.IsAnimal() && Partner.IsAnimal()))
				SexUtility.RapeTargetAlert(pawn, Partner);

			Toil StartPartnerJob = new Toil();
			StartPartnerJob.defaultCompleteMode = ToilCompleteMode.Instant;
			StartPartnerJob.socialMode = RandomSocialMode.Off;
			StartPartnerJob.initAction = delegate
			{
				var dri = Partner.jobs.curDriver as JobDriver_SexBaseRecieverRaped;
				if (dri == null)
				{
					Job gettin_raped = JobMaker.MakeJob(PartnerJob, pawn);
					Building_Bed Bed = null;
					if (Partner.GetPosture() == PawnPosture.LayingInBed)
						Bed = Partner.CurrentBed();

					Partner.jobs.StartJob(gettin_raped, JobCondition.InterruptForced, null, false, true, null);
					if (Bed != null)
						(Partner.jobs.curDriver as JobDriver_SexBaseRecieverRaped)?.Set_bed(Bed);
				}
			};
			yield return StartPartnerJob;

			// Breed target
			var SexToil = new Toil();
			SexToil.defaultCompleteMode = ToilCompleteMode.Never;
			SexToil.defaultDuration = duration;
			SexToil.handlingFacing = true;
			SexToil.FailOn(() => Partner.CurJob.def != PartnerJob);
			SexToil.initAction = delegate
			{
				Partner.pather.StopDead();
				Partner.jobs.curDriver.asleep = false;
				Start();
			};
			SexToil.tickAction = delegate
			{
				if (pawn.IsHashIntervalTick(ticks_between_hearts))
					if (xxx.is_zoophile(pawn) || xxx.is_animal(pawn))
						ThrowMetaIconF(pawn.Position, pawn.Map, FleckDefOf.Heart);
					else
						ThrowMetaIconF(pawn.Position, pawn.Map, xxx.mote_noheart);
				SexTick(pawn, Partner);
				if (!Partner.Dead)
					SexUtility.reduce_rest(Partner, 1);
				SexUtility.reduce_rest(pawn, 2);
				if (ticks_left <= 0)
					ReadyForNextToil();
			};
			SexToil.AddFinishAction(delegate
			{
				End();
			});
			yield return SexToil;

			yield return new Toil
			{
				initAction = delegate
				{
					//Log.Message("JobDriver_Breeding::MakeNewToils() - Calling aftersex");
					//// Trying to add some interactions and social logs
					Sexprops.isRape = !(pawn.relations.DirectRelationExists(PawnRelationDefOf.Bond, Partner) ||
					 	(xxx.is_animal(pawn) && (pawn.RaceProps.wildness - pawn.RaceProps.petness + 0.18f) > Rand.Range(0.36f, 1.8f)));
					SexUtility.ProcessSex(Sexprops);
				},
				defaultCompleteMode = ToilCompleteMode.Instant
			};
		}
	}
}


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

namespace rjw
{
	//JobDriver_GotoMindControlled
	public class JobDriver_Knotted : JobDriver_Goto
	{
		protected override IEnumerable<Toil> MakeNewToils()
		{
			if (job.def.waitAfterArriving > 0)
			{
				yield return Toils_General.Wait(job.def.waitAfterArriving);
			}
		}
	}
}