code:
using Harmony;
using RimWorld;
using RimWorld.Planet;
using UnityEngine;
using Verse;
using Multiplayer.API;

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

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

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

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

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

			return ProcessDebugPregnancy(mother, father);
		}

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

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

			Log.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
				Log.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
				Log.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
				Log.Message(" override as Humanlike birthing: Father-" + xxx.get_pawnname(father) + " Mother-" + xxx.get_pawnname(mother));
				CreateAndBirth<Hediff_HumanlikePregnancy>();
				return false;
			}
			else
			{
				Log.Warning("Hediff_Pregnant::DoBirthSpawn() - rjw checks failed, vanilla pregnancy birth");
				Log.Warning("Hediff_Pregnant::DoBirthSpawn(): Father-" + xxx.get_pawnname(father) + " Mother-" + xxx.get_pawnname(mother));
				//vanilla pregnancy code, no effects on rjw

				return true;
			}
		}

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

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

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

		private static bool ProcessDebugPregnancy(Pawn mother, Pawn father)
		{
			void CreateAndBirth<T>() where T : Hediff_BasePregnancy
			{
				T hediff = Hediff_BasePregnancy.Create<T>(mother, father);
				hediff.Initialize(mother, father);
				hediff.GiveBirth();
			}

			//debug, add RJW pregnancy and birth it
			Log.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
					Log.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
					Log.Message(" override as Humanlike birthing, mother: " + xxx.get_pawnname(mother));
					CreateAndBirth<Hediff_HumanlikePregnancy>();
				}
				else
				{
					Log.Warning("Hediff_Pregnant::DoBirthSpawn() - debug vanilla pregnancy birth");
					return true;
				}
			}
			return false;
		}
	}

	[HarmonyPatch(typeof(Hediff_Pregnant), "Tick")]
	class PATCH_Hediff_Pregnant_Tick
	{
		[HarmonyPrefix]
		static bool on_begin_Tick(Hediff_Pregnant __instance)
		{
			if (__instance.pawn.IsHashIntervalTick(1000))
			{
				if (!Genital_Helper.has_vagina(__instance.pawn))
				{
					__instance.pawn.health.RemoveHediff(__instance);
				}
			}
			return true;
		}
	}
}


code:
using System.Linq;
using Verse;

using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using Harmony;
using Harmony.ILCopying;
using RimWorld;
using UnityEngine;
using Verse.AI;
using Verse.Grammar;
using OpCodes = System.Reflection.Emit.OpCodes;

namespace rjw
{
	/// <summary>
	/// Patch all races
	/// 1) into rjw parts recipes
	/// 2) remove bodyparts from non animals and human likes
	/// </summary>

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

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

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

						if (!x.recipeUsers.Contains(thingDef))
							x.recipeUsers.Add(item: thingDef);
					}
			}

			//TODO: fix errors?
			/*
			//remove rjw bodyparts from non animals and non humanlikes
			foreach (ThingDef thingDef in DefDatabase<ThingDef>.AllDefs.Where(thingDef =>
					thingDef.race != null && !(
					thingDef.race.Humanlike ||
					thingDef.race.Animal
					)))
			{
					if (thingDef.race.body.AllParts.Exists(x => x.def == xxx.genitalsDef))
						thingDef.race.body.AllParts.Remove(thingDef.race.body.AllParts.Find(bpr => bpr.def.defName == "Genitals"));

					if (thingDef.race.body.AllParts.Exists(x => x.def == xxx.breastsDef))
						thingDef.race.body.AllParts.Remove(thingDef.race.body.AllParts.Find(bpr => bpr.def.defName == "Chest"));

					if (thingDef.race.body.AllParts.Exists(x => x.def == xxx.anusDef))
						thingDef.race.body.AllParts.Remove(thingDef.race.body.AllParts.Find(bpr => bpr.def.defName == "Anus"));
			}
			*/
		}
	}
}

repo_name: Eggs/rjw

license: mit


code:
using Harmony;
using RimWorld;
using Verse;

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


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

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

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

			//TODO add support for animals? unlikely as they has weird meshes
			//for now, only draw humans
			if (pawn.RaceProps.Humanlike && RJWSettings.cum_overlays)
			{
				//find bukkake hediff. if it exists, use its draw function
				List<Hediff> hediffs = pawn.health.hediffSet.hediffs;
				if (hediffs.Exists(x => x.def == RJW_SemenoOverlayHediffDefOf.Hediff_Bukkake))
				{
					Hediff_Bukkake h = hediffs.Find(x => x.def == RJW_SemenoOverlayHediffDefOf.Hediff_Bukkake) as Hediff_Bukkake;

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

					//adjustments if the pawn is sleeping in a bed:
					bool inBed = false;
					Building_Bed building_Bed = pawn.CurrentBed();
					if (building_Bed != null)
					{
						inBed = !building_Bed.def.building.bed_showSleeperBody;
						AltitudeLayer altLayer = (AltitudeLayer)Mathf.Max((int)building_Bed.def.altitudeLayer, 15);
						Vector3 vector2 = pawn.Position.ToVector3ShiftedWithAltitude(altLayer);
						vector2.y += 0.02734375f+0.01f;//just copied from rimworld code+0.01f
						drawLoc.y = vector2.y;
					}

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

		}
	}

	//adds new gizmo for adding semen for testing
	[HarmonyPatch(typeof(Pawn))]
	[HarmonyPatch("GetGizmos")]
	class Patch_AddGizmo
	{
		static void Postfix(Pawn __instance, ref IEnumerable<Gizmo> __result)
		{

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

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

			if (Prefs.DevMode && RJWSettings.DevMode && !MP.IsInMultiplayer)
			{
				Command_Action addSemen = new Command_Action();
				addSemen.defaultDesc = "AddSemenHediff";
				addSemen.defaultLabel = "AddSemen";
				addSemen.action = delegate ()
				{
					Addsemen(__instance);
				};

				NewList.Add(addSemen);
			}

			IEnumerable<Gizmo> output = NewList;

			// make caller use the list
			__result = output;

		}

		[SyncMethod]
		static void Addsemen(Pawn pawn)
		{
			//Log.Message("add semen button is pressed for " + pawn);

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

				//select random part:
				BodyPartRecord randomPart;
				//filteredParts.TryRandomElement<BodyPartRecord>(out randomPart);
				//for testing - choose either genitals or anus:
				//Rand.PopState();
				//Rand.PushState(RJW_Multiplayer.PredictableSeed());
				if (Rand.Value > 0.5f)
				{
					randomPart = pawn.RaceProps.body.AllParts.Find(x => x.def == xxx.anusDef);
				}
				else
				{
					randomPart = pawn.RaceProps.body.AllParts.Find(x => x.def == xxx.genitalsDef);
				}

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


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

using System.Linq;
using System.Collections.Generic;

using System.Diagnostics;
using System.Text;
using UnityEngine;

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

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

	//TODO: make toggle/floatmenu for slime/demon parts switching/renaming
	//[HarmonyPatch(typeof(HealthCardUtility), "EntryClicked")]
	//internal static class PATCH_HealthCardUtility_EntryClicked
	//{
	//	[HarmonyPostfix]
	//	private static void Postfix(ref IEnumerable<Hediff> diffs, ref Pawn pawn)
	//	{
	//		BodyPartRecord part = diffs.First<Hediff>().Part;
	//		Hediff hed = diffs.First<Hediff>();

	//		Log.Message("[RJW]EntryClicked: " + part.Label);
	//		Log.Message("[RJW]EntryClicked: " + hed.Label);
	//		//Log.Message("[RJW]PATCH_HealthCardUtility_recipes list: " + floatMenuOption);
	//		//foreach (FloatMenuOption recipe in recipeOptionsMaker)
	//		//	{
	//		//		Log.Message("[RJW]PATCH_HealthCardUtility_recipes: " + recipe);
	//		//	}
	//		return;
	//	}
	//}
}


code:
using System.Linq;
using Verse;
using System.Collections.Generic;
using Harmony;
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
	[HarmonyPatch(typeof(FloatMenuMakerMap), "CanTakeOrder")]
	[StaticConstructorOnStartup]
	static class disable_FloatMenuMakerMap
	{
		[HarmonyPostfix]
		static void this_is_postfix(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: fix error
	//TODO: allow shared control over non colonists(droids, etc)?
	//disable drafting
	[HarmonyPatch(typeof(Pawn), "GetGizmos")]
	[StaticConstructorOnStartup]
	static class disable_Gizmos
	{
		[HarmonyPostfix]
		static void this_is_postfix(ref IEnumerable<Gizmo> __result, ref Pawn __instance)
		{
			Pawn pawn = __instance;
			List<Gizmo> gizmos = __result.ToList();

			if (RJWSettings.RPG_hero_control)
			{
				if ((pawn.IsDesignatedHero() && !pawn.IsHeroOwner()))    //not hero owner, disable drafting
				{
					foreach (var x in __result.ToList())
					{
						try
						{
							//Log.Message("disable_drafter gizmos: " + x);
							if ((x as Command).defaultLabel == "Draft")
							gizmos.Remove(x as Gizmo);
						}
						catch
						{ }
					};
				}
				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
					{
						foreach (var x in __result.ToList())
						{
							try
							{
								//this may cause error
								//ie pawn with shield, or maybe other equipment added gizmos
								//maybe because they are not Command?
								//w/e just catch error and ignore

								//Log.Message("disable_drafter gizmos: " + x);
								if ((x as Command).defaultLabel == "Draft")
									gizmos.Remove(x);
							}
							catch
							{ }
						};
					}
				}
			}
			__result = gizmos.AsEnumerable();
		}
	}
}


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

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

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

	///
	///Compact button group containing rjw designations on pawn
	///
	[HarmonyPatch(typeof(Pawn), "GetGizmos")]
	[StaticConstructorOnStartup]
	static class Rjw_buttons
	{
		[HarmonyPostfix]
		static void this_is_postfix(ref IEnumerable<Gizmo> __result, ref Pawn __instance)
		{
			if (!RJWSettings.show_RJW_designation_box) return;
			if (!(__instance.Faction == Faction.OfPlayer || __instance.IsPrisonerOfColony)) return;
			//Log.Message("[rjw]Harmony patch submit_button is called");
			var pawn = __instance;
			var gizmos = __result.ToList();
			gizmos.Add(new RJWdesignations(pawn));
			__result = gizmos.AsEnumerable();
		}
	}

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

			if (enabled && pawn.IsColonistPlayerControlled && pawn.Drafted)
				if (pawn.CanChangeDesignationColonist())
					if (!(pawn.kindDef.race.defName.Contains("Droid") && !AndroidsCompatibility.IsAndroid(pawn)))
					{
						gizmos.Add(new Command_Action
						{
							defaultLabel = "CommandSubmit".Translate(),
							icon = submit_icon,
							defaultDesc = "CommandSubmitDesc".Translate(),
							action = delegate
							{
								LayDownAndAccept(pawn);
							},
							hotKey = KeyBindingDefOf.Misc3
						});
					}
			__result = gizmos.AsEnumerable();
		}

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

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


code:
using 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(TraitDef.Named("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(TraitDef.Named("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;
			}
		}
		protected override float SeverityChangePerDay()
		{
			return this.Props.severityPerDayReduce.Evaluate(this.parent.ageTicks / 60000f);
		}
	}

	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 / 60000f);
				if (xxx.has_traits(Pawn))
				{
				if (xxx.RoMIsActive)
					if (Pawn.story.traits.HasTrait(TraitDef.Named("Succubus")))
						num *= 0.25f;
				if (Pawn.story.traits.HasTrait(TraitDefOf.Tough))
					{
						num *= 0.5f;
					}
					if (Pawn.story.traits.HasTrait(TraitDef.Named("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 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()
											 select hd;
			if (enumerable != null)
			{
				foreach (Hediff item in enumerable)
				{
					HediffWithComps val = item as HediffWithComps;
					if (val != null && val.TendableNow())
						if (val.Bleeding)
						{
							//Log.Message("TrySealWounds " + xxx.get_pawnname(pawn) + ", Bleeding " + item.Label);
							HediffComp_TendDuration val2 = HediffUtility.TryGetComp<HediffComp_TendDuration>(val);
							val2.tendQuality = 2f;
							val2.tendTicksLeft = Find.TickManager.TicksGame;
							pawn.health.Notify_HediffChanged(item);
						}
						// infections  etc
						else// if (val.def.lethalSeverity > 0f)
						{
							//Log.Message("TryHeal " + xxx.get_pawnname(pawn) + ", infection(?) " + item.Label);
							HediffComp_TendDuration val2 = HediffUtility.TryGetComp<HediffComp_TendDuration>(val);
							val2.tendQuality = 2f;
							val2.tendTicksLeft = Find.TickManager.TicksGame;
							pawn.health.Notify_HediffChanged(item);
						}
				}
			}
		}

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

			if (need.CurLevel < 0.10f)
			{
				//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 == DefDatabase<NeedDef>.GetNamed("DBHThirst"));
			if (need == null)
			{
				return;
			}

			if (need.CurLevel < 0.10f)
			{
				//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 Verse;

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


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

namespace rjw
{
	public class HediffDef_PartBase : Hediff_Implant
	{
		public bool discovered = false;

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

		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
			{
				/* penis
				string size = "Average";
				if (Severity < 0.1f)
					size = "Micro";
				if (Severity < 0.25f)
					size = "Small";
				if (Severity > 0.75f)
					size = "Big";
				if (Severity > 0.9f)
					size = "Huge";

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

		//stack hediff in health tab
		public override int UIGroupKey
		{
			get
			{
				if (RJWSettings.StackRjwParts)
					//(Label x count)
					return this.Label.GetHashCode();
				else
					//dont
					return loadID;
			}
		}

		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("1");// size?
				//stringBuilder.AppendLine("2");// erm something?
				return stringBuilder.ToString();
			}
		}

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

		public override bool Visible
		{
			get
			{
				if (RJWSettings.ShowRjwParts == RJWSettings.ShowParts.Hide)
				{
					discovered = false;
				}
				else if (!discovered)
				{
					if (RJWSettings.ShowRjwParts == RJWSettings.ShowParts.Show)
					{
						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("udder") || this.def.defName.ToLower().Contains("chest"))
							discovered = !hasShirt;
						else
							discovered = !hasPants;

					}
				}

				return discovered;
			}
		}
	}
}


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 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)
		{
			//add something fancy here later?
			letterText = null;
			letterLabel = null;
			letterDef = null;
			//Find.LetterStack.ReceiveLetter("Rape attempt", "A wandering nymph has decided to join your colony.", LetterDefOf.NegativeEvent, recipient);

			if (initiator == null || recipient == null)
				return;
			//--Log.Message("[RJW] 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)
		{
			//add something fancy here later?
			letterText = null;
			letterLabel = null;
			letterDef = null;
			//Find.LetterStack.ReceiveLetter("Rape attempt", "A wandering nymph has decided to join your colony.", LetterDefOf.NegativeEvent, recipient);

			if (initiator == null || recipient == null)
				return;
			//--Log.Message("[RJW] 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)
		{
			//add something fancy here later?
			letterText = null;
			letterLabel = null;
			letterDef = null;
			//Find.LetterStack.ReceiveLetter("Rape attempt", "A wandering nymph has decided to join your colony.", LetterDefOf.NegativeEvent, recipient);

			if (initiator == null || recipient == null)
				return;
			//--Log.Message("[RJW] 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
	{
		private readonly TargetIndex PartnerInd = TargetIndex.A;
		private readonly TargetIndex BedInd = TargetIndex.B;
		private readonly TargetIndex SlotInd = TargetIndex.C;
		private int ticks_left = 200;
		private const int ticks_between_hearts = 100;

		public Pawn Actor => GetActor();
		public Pawn Partner => (Pawn)(job.GetTarget(PartnerInd));
		public Building_Bed Bed => (Building_Bed)(job.GetTarget(BedInd));
		public IntVec3 SleepSpot => (IntVec3)job.GetTarget(SlotInd);

		protected bool usedCondom;

		public override void ExposeData()
		{
			base.ExposeData();
			Scribe_Values.Look(ref ticks_left, "ticksLeft", 0, false);
		}

		private static bool IsInOrByBed(Building_Bed b, Pawn p)
		{
			for (int i = 0; i < b.SleepingSlotsCount; i++)
			{
				if (b.GetSleepingSlotPos(i).InHorDistOf(p.Position, 1f))
				{
					return true;
				}
			}
			return false;
		}
		public override bool TryMakePreToilReservations(bool errorOnFailed)
		{
			return pawn.Reserve(Partner, job, 1, 0, null, errorOnFailed);
		}

		[SyncMethod]
		protected override IEnumerable<Toil> MakeNewToils()
		{
			this.FailOnDespawnedOrNull(PartnerInd);
			this.FailOnDespawnedNullOrForbidden(BedInd);
			this.FailOn(() => Actor is null || !Actor.CanReserveAndReach(Partner, PathEndMode.Touch, Danger.Deadly));
			this.FailOn(() => pawn.Drafted);
			yield return Toils_Reserve.Reserve(PartnerInd, 1, 0);
			//yield return Toils_Reserve.Reserve(BedInd, Bed.SleepingSlotsCount, 0);
			Toil gotoAnimal = Toils_Goto.GotoThing(PartnerInd, PathEndMode.Touch);
			yield return gotoAnimal;

			bool partnerHasPenis = Genital_Helper.has_penis(Partner) || Genital_Helper.has_penis_infertile(Partner);

			Toil gotoBed = new Toil
			{
				initAction = delegate
				{
					Actor.pather.StartPath(SleepSpot, PathEndMode.OnCell);
					Partner.pather.StartPath(SleepSpot, PathEndMode.OnCell);
				},
				defaultCompleteMode = ToilCompleteMode.PatherArrival
			};
			gotoBed.FailOnBedNoLongerUsable(BedInd);
			gotoBed.AddFailCondition(() => Partner.Downed);
			yield return gotoBed;
			gotoBed.AddFinishAction(delegate
			{
				var gettin_loved = new Job(xxx.gettin_loved, Actor, Bed);
				Partner.jobs.StartJob(gettin_loved, JobCondition.InterruptForced, null, false, true, null);
			});

			Toil waitInBed = new Toil
			{
				initAction = delegate
				{
					//Rand.PopState();
					//Rand.PushState(RJW_Multiplayer.PredictableSeed());
					ticksLeftThisToil = 5000;
					ticks_left = (int)(2000.0f * Rand.Range(0.30f, 1.30f));
				},
				tickAction = delegate
				{
					Actor.GainComfortFromCellIfPossible();
					if (IsInOrByBed(Bed, Partner))
					{
						ticksLeftThisToil = 0;
					}
				},
				defaultCompleteMode = ToilCompleteMode.Delay,
			};
			waitInBed.FailOn(() => pawn.GetRoom(RegionType.Set_Passable) == null);
			yield return waitInBed;

			Toil loveToil = new Toil
			{
				initAction = delegate
				{
					usedCondom = CondomUtility.TryUseCondom(Actor);

					if (!partnerHasPenis)
						Actor.rotationTracker.Face(Partner.DrawPos);
				},
				defaultCompleteMode = ToilCompleteMode.Never, //Changed from Delay
			};
			loveToil.AddPreTickAction(delegate
			{
				//Actor.Reserve(Partner, 1, 0);
				--ticks_left;
				xxx.reduce_rest(Actor, 1);
				xxx.reduce_rest(Partner, 2);
				if (ticks_left <= 0)
					ReadyForNextToil();
				else if (pawn.IsHashIntervalTick(ticks_between_hearts))
				{
					MoteMaker.ThrowMetaIcon(Actor.Position, Actor.Map, ThingDefOf.Mote_Heart);
				}
				Actor.GainComfortFromCellIfPossible();
				Partner.GainComfortFromCellIfPossible();
			});
			loveToil.AddFailCondition(() => Partner.Dead || !IsInOrByBed(Bed, Partner));
			loveToil.socialMode = RandomSocialMode.Off;
			yield return loveToil;

			Toil afterSex = new Toil
			{
				initAction = delegate
				{
					//Log.Message("JobDriver_BestialityForFemale::MakeNewToils() - Calling aftersex");
					// Trying to add some interactions and social logs
					SexUtility.ProcessSex(Partner, pawn, usedCondom);
				},
				defaultCompleteMode = ToilCompleteMode.Instant
			};
			yield return afterSex;
		}
	}
}


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
	{
		private int duration;
		private int ticks_between_hearts;
		private int ticks_between_hits = 50;
		private int ticks_between_thrusts;
		protected TargetIndex target_animal = TargetIndex.A;

		protected Pawn animal => (Pawn)(job.GetTarget(target_animal));

		public override bool TryMakePreToilReservations(bool errorOnFailed)
		{
			return pawn.Reserve(animal, job, 1, 0, null, errorOnFailed);
		}

		[SyncMethod]
		protected override IEnumerable<Toil> MakeNewToils()
		{
			//--Log.Message("[RJW] JobDriver_BestialityForMale::MakeNewToils() called");
			//Rand.PopState();
			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
			duration = (int)(2500.0f * Rand.Range(0.50f, 0.90f));
			ticks_between_hearts = Rand.RangeInclusive(70, 130);
			ticks_between_hits = Rand.Range(xxx.config.min_ticks_between_hits, xxx.config.max_ticks_between_hits);
			ticks_between_thrusts = 100;

			if (xxx.is_bloodlust(pawn))
				ticks_between_hits = (int)(ticks_between_hits * 0.75);
			if (xxx.is_brawler(pawn))
				ticks_between_hits = (int)(ticks_between_hits * 0.90);

			//this.FailOn (() => (!animal.health.capacities.CanBeAwake) || (!comfort_prisoners.is_designated (animal)));
			// Fail if someone else reserves the prisoner before the pawn arrives or colonist can't reach animal
			this.FailOn(() => !pawn.CanReserveAndReach(animal, PathEndMode.Touch, Danger.Deadly));
			this.FailOn(() => animal.HostileTo(pawn));
			this.FailOnDespawnedNullOrForbidden(target_animal);
			this.FailOn(() => pawn.Drafted);
			yield return Toils_Reserve.Reserve(target_animal, 1, 0);
			//Log.Message("[RJW] JobDriver_BestialityForMale::MakeNewToils() - moving towards animal");
			yield return Toils_Goto.GotoThing(target_animal, PathEndMode.Touch);
			yield return Toils_Interpersonal.WaitToBeAbleToInteract(pawn);
			yield return Toils_Interpersonal.GotoInteractablePosition(target_animal);
			if (xxx.is_kind(pawn)
				|| (xxx.CTIsActive && xxx.has_traits(pawn) && pawn.story.traits.HasTrait(TraitDef.Named("RCT_AnimalLover"))))
			{
				yield return TalkToAnimal(pawn, animal);
				yield return TalkToAnimal(pawn, animal);
			}
			if (Rand.Chance(0.6f))
				yield return TalkToAnimal(pawn, animal);
			yield return Toils_Goto.GotoThing(target_animal, PathEndMode.OnCell);

			SexUtility.RapeTargetAlert(pawn, animal);

			bool usedCondom = false;

			Toil rape = new Toil();
			rape.initAction = delegate
			{
				usedCondom = CondomUtility.TryUseCondom(pawn);

				//--Log.Message("[RJW] JobDriver_BestialityForMale::MakeNewToils() - reserving animal");

				//--Log.Message("[RJW] JobDriver_BestialityForMale::MakeNewToils() - Setting animal job driver");
				if (!(animal.jobs.curDriver is JobDriver_GettinRaped dri))
				{
					//wild animals may flee or attack
					if (pawn.Faction != animal.Faction && animal.RaceProps.wildness > Rand.Range(0.22f, 1.0f)
						&& !(pawn.TicksPerMoveCardinal < (animal.TicksPerMoveCardinal / 2) && !animal.Downed && xxx.is_not_dying(animal)))
					{
						animal.jobs.StopAll(); // Wake up if sleeping.

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

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

								foreach (Pawn packmate in packmates)
								{
									packmate.mindState.mentalStateHandler.TryStartMentalState(MentalStateDefOf.Manhunter);
								}
							}
							Messages.Message(pawn.Name.ToStringShort + " is being attacked by " + xxx.get_pawnname(animal) + ".", pawn, MessageTypeDefOf.ThreatSmall);
						}
						else
						{
							MoteMaker.ThrowMetaIcon(animal.Position, animal.Map, ThingDefOf.Mote_ColonistFleeing);
							LifeStageUtility.PlayNearestLifestageSound(animal, (ls) => ls.soundCall);
							animal.mindState.StartFleeingBecauseOfPawnAction(pawn);
							animal.mindState.mentalStateHandler.TryStartMentalState(DefDatabase<MentalStateDef>.GetNamed("PanicFlee"));
						}
						pawn.jobs.EndCurrentJob(JobCondition.Incompletable);
					}
					else
					{
						Job gettin_bred = new Job(xxx.gettin_bred, pawn, animal);
						animal.jobs.StartJob(gettin_bred, JobCondition.InterruptForced, null, true);
						(animal.jobs.curDriver as JobDriver_GettinRaped)?.increase_time(duration);
					}
				}
				else
				{
					dri.rapist_count += 1;
					dri.increase_time(duration);
				}
			};
			rape.tickAction = delegate
			{
				if (pawn.IsHashIntervalTick(ticks_between_hearts))
					MoteMaker.ThrowMetaIcon(pawn.Position, pawn.Map, ThingDefOf.Mote_Heart);
				if (pawn.IsHashIntervalTick(ticks_between_thrusts))
					xxx.sexTick(pawn, animal, false);
				/*
				if (pawn.IsHashIntervalTick (ticks_between_hits))
					roll_to_hit (pawn, animal);
					*/
				xxx.reduce_rest(animal, 1);
				xxx.reduce_rest(pawn, 2);

			};
			rape.AddFinishAction(delegate
			{
				//--Log.Message("[RJW] JobDriver_BestialityForMale::MakeNewToils() - finished violating");
				if (animal.jobs?.curDriver is JobDriver_GettinRaped)
					(animal.jobs.curDriver as JobDriver_GettinRaped).rapist_count -= 1;

				if (xxx.is_human(pawn))
					pawn.Drawer.renderer.graphics.ResolveApparelGraphics();
			});
			rape.defaultCompleteMode = ToilCompleteMode.Delay;
			rape.defaultDuration = duration;
			yield return rape;

			yield return new Toil
			{
				initAction = delegate
				{
					//Log.Message("[RJW] JobDriver_BestialityForMale::MakeNewToils() - creating aftersex toil");
					SexUtility.ProcessSex(pawn, animal, usedCondom);
				},
				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
	{
		protected TargetIndex PartnerIndex = TargetIndex.A;

		public Pawn Target => (Pawn)(job.GetTarget(PartnerIndex));

		public override bool TryMakePreToilReservations(bool errorOnFailed)
		{
			return pawn.Reserve(Target, job, BreederHelper.max_animals_at_once, 0);
		}

		[SyncMethod]
		public virtual void roll_to_hit(Pawn pawn, Pawn partner)
		{
			SexUtility.Sex_Beatings(pawn, partner, true);
		}

		[SyncMethod]
		protected override IEnumerable<Toil> MakeNewToils()
		{
			//Rand.PopState();
			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
			int duration = (int)(2000.0f * Rand.Range(0.50f, 0.90f));
			int ticks_between_hearts = Rand.RangeInclusive(70, 130);
			int ticks_between_hits = Rand.Range(xxx.config.min_ticks_between_hits, xxx.config.max_ticks_between_hits);
			int ticks_between_thrusts = 100;

			//--Log.Message("JobDriver_ComfortPrisonerRapin::MakeNewToils() - setting fail conditions");
			this.FailOnDespawnedNullOrForbidden(PartnerIndex);
			this.FailOn(() => !pawn.CanReserve(Target, BreederHelper.max_animals_at_once, 0)); // Fail if someone else reserves the target before the animal arrives.
			this.FailOn(() => !pawn.CanReach(Target, PathEndMode.Touch, Danger.Some)); // Fail if animal cannot reach target.
			this.FailOn(() => !(Target.IsDesignatedBreeding() || (RJWSettings.animal_on_animal_enabled && xxx.is_animal(Target)))); // Fail if not designated and not animal-on-animal
			this.FailOn(() => Target.CurJob == null);
			this.FailOn(() => pawn.Drafted);

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

			SexUtility.RapeTargetAlert(pawn, Target);

			bool usedCondom = false;

			// Breed target
			var breed = new Toil();
			breed.initAction = delegate
			{
				usedCondom = CondomUtility.TryUseCondom(pawn);

				//Log.Message("JobDriver_ComfortPrisonerRapin::MakeNewToils() - Setting victim job driver");
				Job currentJob = Target.jobs.curJob;

				if (currentJob.def != xxx.gettin_raped)
				{
					Job gettin_raped = new Job(xxx.gettin_raped, pawn, Target);
					Building_Bed Bed = null;
					//Log.Message(xxx.get_pawnname(pawn) + " LayingInBed:" + pawn.GetPosture());
					//Log.Message(xxx.get_pawnname(Target) + " LayingInBed:" + Target.GetPosture());
					if (Target.GetPosture() == PawnPosture.LayingInBed)
					{
						Bed = Target.CurrentBed();
						//Log.Message(xxx.get_pawnname(Target) + ": bed:" + Bed);
					}

					Target.jobs.StartJob(gettin_raped, JobCondition.InterruptForced);

					//var dri = Target.jobs.curDriver as JobDriver_GettinRaped;
					//if (xxx.is_animal(pawn) && xxx.is_animal(Target)) // No alert spam for animal-on-animal
						//dri.disable_alert = true;

					(Target.jobs.curDriver as JobDriver_GettinRaped).increase_time(duration);
					if (Bed != null)
						(Target.jobs.curDriver as JobDriver_GettinRaped)?.set_bed(Bed);
				}
				else
				{
					if (Target.jobs.curDriver is JobDriver_GettinRaped dri)
					{
						dri.rapist_count += 1;
						dri.increase_time(duration);
					}
				}
			};

			breed.tickAction = delegate
			{
				if (pawn.IsHashIntervalTick(ticks_between_hearts))
					MoteMaker.ThrowMetaIcon(pawn.Position, pawn.Map, ThingDefOf.Mote_Heart);
				if (pawn.IsHashIntervalTick(ticks_between_thrusts))
					xxx.sexTick(pawn, Target);
				if (!xxx.is_zoophile(Target) && pawn.IsHashIntervalTick(ticks_between_hits))
					roll_to_hit(pawn, Target);

				if (!Target.Dead)
					xxx.reduce_rest(Target, 1);
				xxx.reduce_rest(pawn, 2);

				if (Genital_Helper.has_penis(pawn) || Genital_Helper.has_penis_infertile(pawn))
				{
					// Face same direction, most of animal sex is likely doggystyle.
					Target.Rotation = pawn.Rotation;
				}
			};

			breed.AddFinishAction(delegate
			{
				if ((Target.jobs != null)
				&& (Target.jobs.curDriver != null)
				&& (Target.jobs.curDriver as JobDriver_GettinRaped != null))
				{
					(Target.jobs.curDriver as JobDriver_GettinRaped).rapist_count -= 1;
				}
				pawn.stances.StaggerFor(Rand.Range(0,50));
				Target.stances.StaggerFor(Rand.Range(10,300));
			});

			breed.defaultCompleteMode = ToilCompleteMode.Delay;
			breed.defaultDuration = duration;
			yield return breed;

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


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

namespace rjw
{
	public class JobDriver_Fappin : JobDriver
	{
		private const int ticks_between_hearts = 100;

		private int ticks_left;

		private readonly TargetIndex ibed = TargetIndex.A;
		private Building_Bed Bed => (Building_Bed)((Thing)job.GetTarget(ibed));

		public override bool TryMakePreToilReservations(bool errorOnFailed)
		{
			return pawn.Reserve(Bed, job, Bed.SleepingSlotsCount, 0, null, errorOnFailed);
		}

		[SyncMethod]
		protected override IEnumerable<Toil> MakeNewToils()
		{
			// Faster fapping when frustrated.
			//Rand.PopState();
			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
			ticks_left = (int)(xxx.need_some_sex(pawn) > 2f ? 2500.0f * Rand.Range(0.2f, 0.7f) : 2500.0f * Rand.Range(0.2f, 0.4f));

			this.FailOnDespawnedOrNull(ibed);
			this.FailOn(() => pawn.Drafted);
			this.KeepLyingDown(ibed);
			yield return Toils_Bed.ClaimBedIfNonMedical(ibed);
			yield return Toils_Bed.GotoBed(ibed);

			Toil do_fappin = Toils_LayDown.LayDown(ibed, true, false, false, false);
			do_fappin.AddPreTickAction(delegate
			{
				--ticks_left;
				xxx.reduce_rest(pawn, 1);
				if (ticks_left <= 0)
					ReadyForNextToil();
				else if (pawn.IsHashIntervalTick(ticks_between_hearts))
					MoteMaker.ThrowMetaIcon(pawn.Position, pawn.Map, ThingDefOf.Mote_Heart);
			});
			do_fappin.AddFinishAction(delegate
			{
			    //Moved satisfy and tick increase to aftersex, since it works with solo acts now.
				SexUtility.Aftersex(pawn, xxx.rjwSextype.Masturbation);
				if (SexUtility.ConsiderCleaning(pawn))
				{
					LocalTargetInfo own_cum = pawn.PositionHeld.GetFirstThing<Filth>(pawn.Map);

					Job clean = new Job(JobDefOf.Clean);
					clean.AddQueuedTarget(TargetIndex.A, own_cum);

					pawn.jobs.jobQueue.EnqueueFirst(clean);

				}
			});
			do_fappin.socialMode = RandomSocialMode.Off;
			yield return do_fappin;
		}
	}
}


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

namespace rjw
{
	public class JobDriver_GettinLoved : JobDriver
	{
		private TargetIndex ipartner = TargetIndex.A;
		private TargetIndex ibed = TargetIndex.B;

		private int tick_interval = 100;

		protected Pawn Partner => (Pawn)(job.GetTarget(ipartner));
		protected Building_Bed Bed => (Building_Bed)(job.GetTarget(ibed));

		public override bool TryMakePreToilReservations(bool errorOnFailed)
		{
			return true;
			//return this.pawn.Reserve(this.Partner, this.job, 1, 0, null) && this.pawn.Reserve(this.Bed, this.job, 1, 0, null);
		}

		public float CalculateSatisfactionPerTick()
		{
			return 1.0f;
		}

		protected override IEnumerable<Toil> MakeNewToils()
		{
			//--Log.Message("[RJW]JobDriver_GettinLoved::MakeNewToils is called");

			float partner_ability = xxx.get_sex_ability(Partner);

			// More/less hearts based on partner ability.
			if (partner_ability < 0.8f)
				tick_interval += 100;
			else if (partner_ability > 2.0f)
				tick_interval -= 25;

			// More/less hearts based on opinion.
			if (pawn.relations.OpinionOf(Partner) < 0)
				tick_interval += 50;
			else if (pawn.relations.OpinionOf(Partner) > 60)
				tick_interval -= 25;

			if (Partner.CurJob.def == xxx.casual_sex)
			{
				this.FailOnDespawnedOrNull(ipartner);
				this.FailOn(() => !Partner.health.capacities.CanBeAwake);
				this.FailOn(() => pawn.Drafted);
				this.KeepLyingDown(ibed);
				yield return Toils_Reserve.Reserve(ipartner, 1, 0);
				yield return Toils_Reserve.Reserve(ibed, Bed.SleepingSlotsCount, 0);
				Toil get_loved = Toils_LayDown.LayDown(ibed, true, false, false, false);
				get_loved.FailOn(() => Partner.CurJob == null || Partner.CurJob.def != xxx.casual_sex);
				get_loved.defaultCompleteMode = ToilCompleteMode.Never;
				get_loved.AddPreTickAction(delegate
				{
					if (pawn.IsHashIntervalTick(tick_interval))
					{
						MoteMaker.ThrowMetaIcon(pawn.Position, pawn.Map, ThingDefOf.Mote_Heart);
						xxx.sexTick(pawn, Partner);
					}
				});
				get_loved.socialMode = RandomSocialMode.Off;
				yield return get_loved;
			}
			else if (Partner.CurJob.def == xxx.whore_is_serving_visitors)
			{
				this.FailOnDespawnedOrNull(ipartner);
				this.FailOn(() => !Partner.health.capacities.CanBeAwake || Partner.CurJob == null);
				yield return Toils_Goto.GotoThing(ipartner, PathEndMode.OnCell);
				yield return Toils_Reserve.Reserve(ipartner, 1, 0);
				Toil get_loved = new Toil();
				get_loved.FailOn(() => (Partner.CurJob.def != xxx.whore_is_serving_visitors));
				get_loved.defaultCompleteMode = ToilCompleteMode.Never;
				get_loved.initAction = delegate
				{
					//--Log.Message("[RJW]JobDriver_GettinLoved::MakeNewToils - whore section is called");
				};
				get_loved.AddPreTickAction(delegate
				{
					if (pawn.IsHashIntervalTick(tick_interval))
					{
						MoteMaker.ThrowMetaIcon(pawn.Position, pawn.Map, ThingDefOf.Mote_Heart);
						xxx.sexTick(pawn, Partner);
					}
				});
				get_loved.socialMode = RandomSocialMode.Off;
				yield return get_loved;
			}
			else if (Partner.CurJob.def == xxx.bestialityForFemale)
			{
				this.FailOnDespawnedOrNull(ipartner);
				this.FailOn(() => !Partner.health.capacities.CanBeAwake || Partner.CurJob == null);
				yield return Toils_Goto.GotoThing(ipartner, PathEndMode.OnCell);
				yield return Toils_Reserve.Reserve(ipartner, 1, 0);
				Toil get_loved = new Toil();
				get_loved.FailOn(() => (Partner.CurJob.def != xxx.bestialityForFemale));
				get_loved.defaultCompleteMode = ToilCompleteMode.Never;
				get_loved.initAction = delegate
				{
					//--Log.Message("[RJW]JobDriver_GettinLoved::MakeNewToils - bestialityForFemale section is called");
				};
				get_loved.AddPreTickAction(delegate
				{
					if (pawn.IsHashIntervalTick(tick_interval))
					{
						MoteMaker.ThrowMetaIcon(pawn.Position, pawn.Map, ThingDefOf.Mote_Heart);
						xxx.sexTick(pawn, Partner);
					}
				});
				get_loved.socialMode = RandomSocialMode.Off;
				yield return get_loved;
			}
		}
	}
}


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

namespace rjw
{
	public class JobDriver_GettinRaped : JobDriver
	{
		private int ticks_between_hearts;

		private int ticks_remaining = 10;

		public int rapist_count = 1; // Defaults to 1 so the first rapist doesn't have to add themself

		private Pawn Initiator => (Pawn)(job.GetTarget(TargetIndex.A));
		private Pawn Receiver => (Pawn)(job.GetTarget(TargetIndex.B));

		//private bool was_laying_down;

		private Building_Bed Bed;

		public void increase_time(int min_ticks_remaining)
		{
			if (min_ticks_remaining > ticks_remaining)
				ticks_remaining = min_ticks_remaining;
		}

		public void set_bed(Building_Bed newBed)
		{
			Bed = newBed;
		}

		public override bool TryMakePreToilReservations(bool errorOnFailed)
		{
			return true;
		}

		public float CalculateSatisfactionPerTick()
		{
			return 1.0f;
		}

		[SyncMethod]
		protected override IEnumerable<Toil> MakeNewToils()
		{
			//Rand.PopState();
			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
			ticks_between_hearts = Rand.RangeInclusive(70, 130);
			//was_laying_down = pawn.GetPosture() != PawnPosture.Standing;
			//Log.Message(xxx.get_pawnname(Initiator) + ": was_laying_down:" + was_laying_down + " LayingInBed:" + Initiator.GetPosture());
			//Log.Message(xxx.get_pawnname(Receiver) + ": was_laying_down:" + was_laying_down + " LayingInBed:" + Receiver.GetPosture());
			//was_laying_down = (pawn.jobs.curDriver != null) && pawn.GetPosture() != PawnPosture.Standing;
			//Log.Message(xxx.get_pawnname(pawn) + ": bed:" + Bed);

			var get_raped = new Toil();
			get_raped.defaultCompleteMode = ToilCompleteMode.Never;
			get_raped.initAction = delegate
			{
				pawn.pather.StopDead();
				//pawn.jobs.posture = PawnPosture.Standing;
				pawn.jobs.curDriver.asleep = false;

				SexUtility.BeeingRapedAlert(Initiator, Receiver);

				//Messages.Message("GetinRapedNow".Translate(new object[] { pawn.LabelIndefinite() }).CapitalizeFirst(), pawn, MessageTypeDefOf.NegativeEvent);

				if (Initiator == null || Receiver == null) return;
				bool partnerHasHands = Receiver.health.hediffSet.GetNotMissingParts().Any(part => part.IsInGroup(BodyPartGroupDefOf.RightHand) || part.IsInGroup(BodyPartGroupDefOf.LeftHand));

				// Hand check is for monstergirls and other bipedal 'animals'.
				if ((!xxx.is_animal(Initiator) && partnerHasHands) || Rand.Chance(0.3f)) // 30% chance of face-to-face regardless, for variety.
				{ // Face-to-face
					Initiator.rotationTracker.Face(Receiver.DrawPos);
					Receiver.rotationTracker.Face(Initiator.DrawPos);
				}
				else
				{ // From behind / animal stuff should mostly use this
					Initiator.rotationTracker.Face(Receiver.DrawPos);
					Receiver.Rotation = Initiator.Rotation;
				}
				// TODO: The above works, but something is forcing the partners to face each other during sex. Need to figure it out.

				//prevent Receiver standing up and interrupting rape
				if (Receiver.health.hediffSet.HasHediff(HediffDef.Named("Hediff_Submitting")))
					Receiver.health.AddHediff(HediffDef.Named("Hediff_Submitting"));
			};
			get_raped.tickAction = delegate
			{
				--ticks_remaining;
				/*
				if ((ticks_remaining <= 0) || (rapist_count <= 0))
					ReadyForNextToil();
				*/
				if ((rapist_count > 0) && (pawn.IsHashIntervalTick(ticks_between_hearts / rapist_count)))
					MoteMaker.ThrowMetaIcon(pawn.Position, pawn.Map, xxx.mote_noheart);

			};
			get_raped.AddEndCondition(new Func<JobCondition>(() =>
			{
				if ((ticks_remaining <= 0) || (rapist_count <= 0))
					return JobCondition.Succeeded;
				return JobCondition.Ongoing;
			}));
			get_raped.AddFinishAction(delegate
			{
				if (Bed != null && pawn.Downed)
				{
					Job tobed = new Job(JobDefOf.Rescue, pawn, Bed);
					tobed.count = 1;
					Initiator.jobs.jobQueue.EnqueueFirst(tobed);
					//Log.Message(xxx.get_pawnname(Initiator) + ": job tobed:" + tobed);
				}
				else if (Receiver.IsColonist)
					Receiver.health.AddHediff(HediffDef.Named("Hediff_Submitting"));
				else
					Receiver.stances.stunner.StunFor(600, pawn);

			});
			get_raped.socialMode = RandomSocialMode.Off;
			yield return get_raped;
		}
	}
}


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

namespace rjw
{
	public class JobDriver_JoinInBed : JobDriver
	{
		private const int ticks_between_hearts = 100;

		private int ticks_left;

		private readonly TargetIndex ipawn = TargetIndex.A;
		private readonly TargetIndex ipartner = TargetIndex.B;
		private readonly TargetIndex ibed = TargetIndex.C;

		protected Pawn Top => (Pawn)(job.GetTarget(ipawn));
		protected Pawn Partner => (Pawn)(job.GetTarget(ipartner));
		protected Building_Bed Bed => (Building_Bed)(job.GetTarget(ibed));

		public override bool TryMakePreToilReservations(bool errorOnFailed)
		{
			return Top.Reserve(Partner, job, xxx.max_rapists_per_prisoner, 0, null, errorOnFailed);
		}

		[SyncMethod]
		protected override IEnumerable<Toil> MakeNewToils()
		{
			//--Log.Message("JobDriver_JoinInBed::MakeNewToils() called");
			this.FailOnDespawnedOrNull(ipartner);
			this.FailOnDespawnedOrNull(ibed);
			this.FailOn(() => !Partner.health.capacities.CanBeAwake);
			this.FailOn(() => !(Partner.InBed() || xxx.in_same_bed(Partner, Top)));
			this.FailOn(() => pawn.Drafted);
			yield return Toils_Reserve.Reserve(ipartner, xxx.max_rapists_per_prisoner, 0);
			yield return Toils_Goto.GotoThing(ipartner, PathEndMode.OnCell);
			bool usedCondom = false;
			yield return new Toil
			{
				initAction = delegate
				{
					usedCondom = CondomUtility.TryUseCondom(Top) || CondomUtility.TryUseCondom(Partner);
					//--Log.Message("JobDriver_JoinInBed::MakeNewToils() - setting initAction");
					//Rand.PopState();
					//Rand.PushState(RJW_Multiplayer.PredictableSeed());
					ticks_left = (int)(2500.0f * Rand.Range(0.30f, 1.30f));
					Job gettin_loved = new Job(xxx.gettin_loved, Top, Bed);
					Partner.jobs.StartJob(gettin_loved, JobCondition.InterruptForced);
				},
				defaultCompleteMode = ToilCompleteMode.Instant
			};
			Toil do_lovin = new Toil {defaultCompleteMode = ToilCompleteMode.Never};
			do_lovin.FailOn(() => (Partner.CurJob == null) || (Partner.CurJob.def != xxx.gettin_loved));
			do_lovin.AddPreTickAction(delegate
			{
				--ticks_left;
				xxx.reduce_rest(Partner, 1);
				xxx.reduce_rest(Top, 2);
				if (ticks_left <= 0)
					ReadyForNextToil();
				else if (Top.IsHashIntervalTick(ticks_between_hearts))
					MoteMaker.ThrowMetaIcon(Top.Position, Top.Map, ThingDefOf.Mote_Heart);
			});
			do_lovin.socialMode = RandomSocialMode.Off;
			yield return do_lovin;
			yield return new Toil
			{
				initAction = delegate
				{
					// Trying to add some interactions and social logs
					SexUtility.ProcessSex(Top, Partner, usedCondom /*condom*/, false /*rape*/, false/*isCoreLovin*/, false /*whoring*/);
				},
				defaultCompleteMode = ToilCompleteMode.Instant
			};
		}
	}
}


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

namespace rjw
{
	public class JobDriver_PrisonerComfortRapin : JobDriver
	{
		protected int duration;
		protected int ticks_between_hearts;
		protected int ticks_between_hits = 50;
		protected int ticks_between_thrusts;

		protected TargetIndex iprisoner = TargetIndex.A;

		protected virtual float chance_to_hit_prisoner => xxx.config.base_chance_to_hit_prisoner;

		protected Pawn Target => (Pawn)(job.GetTarget(iprisoner));

		public override bool TryMakePreToilReservations(bool errorOnFailed)
		{
			return pawn.Reserve(Target, job, xxx.max_rapists_per_prisoner, 0, null, errorOnFailed);
		}

		public virtual void roll_to_hit(Pawn pawn, Pawn partner)
		{
			SexUtility.Sex_Beatings(pawn, partner, true);
		}

		[SyncMethod]
		protected override IEnumerable<Toil> MakeNewToils()
		{
			//Rand.PopState();
			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
			duration = (int)(2000.0f * Rand.Range(0.50f, 0.90f));
			ticks_between_hearts = Rand.RangeInclusive(70, 130);
			ticks_between_hits = Rand.Range(xxx.config.min_ticks_between_hits, xxx.config.max_ticks_between_hits);
			ticks_between_thrusts = 100;
			bool pawnHasPenis = Genital_Helper.has_penis(pawn) || Genital_Helper.has_penis_infertile(pawn);

			if (xxx.is_bloodlust(pawn))
				ticks_between_hits = (int)(ticks_between_hits * 0.75);
			if (xxx.is_brawler(pawn))
				ticks_between_hits = (int)(ticks_between_hits * 0.90);

			//--Log.Message("JobDriver_ComfortPrisonerRapin::MakeNewToils() - setting fail conditions");
			this.FailOnDespawnedNullOrForbidden(iprisoner);
			//this.FailOn(() => (!Target.health.capacities.CanBeAwake) || (!comfort_prisoners.is_designated(Target)));//this is wrong
			this.FailOn(() => (!Target.IsDesignatedComfort()));
			this.FailOn(() => !pawn.CanReserve(Target, xxx.max_rapists_per_prisoner, 0)); // Fail if someone else reserves the prisoner before the pawn arrives
			this.FailOn(() => pawn.Drafted);
			yield return Toils_Goto.GotoThing(iprisoner, PathEndMode.OnCell);

			SexUtility.RapeTargetAlert(pawn, Target);

			bool usedCondom = false;

			Toil rape = new Toil();
			rape.initAction = delegate
			{
				// Try rapist condom. Unlike normal rape try use comfort prisoner condom as well
				usedCondom = CondomUtility.TryUseCondom(pawn) || CondomUtility.TryUseCondom(Target);

				//--Log.Message("JobDriver_ComfortPrisonerRapin::MakeNewToils() - reserving prisoner");
				//pawn.Reserve(Target, comfort_prisoners.max_rapists_per_prisoner, 0);
				if (!pawnHasPenis)
					Target.rotationTracker.Face(pawn.DrawPos);

				//--Log.Message("JobDriver_ComfortPrisonerRapin::MakeNewToils() - Setting victim job driver");
				JobDriver_GettinRaped dri = Target.jobs.curDriver as JobDriver_GettinRaped;
				if (dri == null)
				{
					Job gettin_raped = new Job(xxx.gettin_raped, pawn, Target);
					Building_Bed Bed = null;
					//Log.Message(xxx.get_pawnname(pawn) + " LayingInBed:" + pawn.GetPosture());
					//Log.Message(xxx.get_pawnname(Target) + " LayingInBed:" + Target.GetPosture());
					if (Target.GetPosture() == PawnPosture.LayingInBed)
					{
						Bed = Target.CurrentBed();
						//Log.Message(xxx.get_pawnname(Target) + ": bed:" + Bed);
					}

					Target.jobs.StartJob(gettin_raped, JobCondition.InterruptForced, null, false, true, null);
					(Target.jobs.curDriver as JobDriver_GettinRaped)?.increase_time(duration);
					if (Bed != null)
						(Target.jobs.curDriver as JobDriver_GettinRaped)?.set_bed(Bed);
				}
				else
				{
					dri.rapist_count += 1;
					dri.increase_time(duration);
				}
			};
			rape.tickAction = delegate
			{
				if (pawn.IsHashIntervalTick(ticks_between_hearts))
					MoteMaker.ThrowMetaIcon(pawn.Position, pawn.Map, ThingDefOf.Mote_Heart);
				if (pawn.IsHashIntervalTick(ticks_between_thrusts))
					xxx.sexTick(pawn, Target);
				if (pawn.IsHashIntervalTick(ticks_between_hits))
					roll_to_hit(pawn, Target);
				xxx.reduce_rest(Target, 1);
				xxx.reduce_rest(pawn, 2);
			};
			rape.AddFinishAction(delegate
			{
				//Log.Message("JobDriver_ComfortPrisonerRapin::MakeNewToils() - Clearing victim job");
				if (Target.jobs?.curDriver is JobDriver_GettinRaped)
				{
					//Log.Message("JobDriver_ComfortPrisonerRapin::MakeNewToils() - Victim present");
					(Target.jobs.curDriver as JobDriver_GettinRaped).rapist_count -= 1;
				}
			});
			rape.defaultCompleteMode = ToilCompleteMode.Delay;
			rape.defaultDuration = duration;
			yield return rape;

			yield return new Toil
			{
				initAction = delegate
				{
					// Trying to add some interactions and social logs
					SexUtility.ProcessSex(pawn, Target, usedCondom, true);
					Target.records.Increment(xxx.GetRapedAsComfortPrisoner);
				},
				defaultCompleteMode = ToilCompleteMode.Instant
			};
		}
	}
}


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

namespace rjw
{
	public class JobDriver_QuickFap : JobDriver
	{
		private const int ticks_between_hearts = 100;
		private int ticks_left;

		public IntVec3 cell => (IntVec3)job.GetTarget(TargetIndex.A);

		public override bool TryMakePreToilReservations(bool errorOnFailed)
		{
			return true; // No reservations needed.
		}

		[SyncMethod]
		protected override IEnumerable<Toil> MakeNewToils()
		{
			//this.FailOn(() => PawnUtility.PlayerForcedJobNowOrSoon(pawn));
			this.FailOn(() => pawn.health.Downed);
			this.FailOn(() => pawn.IsBurning());
			this.FailOn(() => pawn.IsFighting());
			this.FailOn(() => pawn.Drafted);

			//Rand.PopState();
			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
			// Faster fapping when frustrated.
			ticks_left = (int)(xxx.need_some_sex(pawn) > 2f ? 2500.0f * Rand.Range(0.2f, 0.7f) : 2500.0f * Rand.Range(0.2f, 0.4f));

			Toil findfapspot = new Toil
			{
				initAction = delegate
				{
					pawn.pather.StartPath(cell, PathEndMode.OnCell);
				},
				defaultCompleteMode = ToilCompleteMode.PatherArrival
			};
			yield return findfapspot;

			//Log.Message("[RJW] Making new toil for QuickFap.");

			Toil fap = Toils_General.Wait(ticks_left);
			fap.tickAction = delegate
			{
				--ticks_left;
				xxx.reduce_rest(pawn, 1);
				if (ticks_left <= 0)
					ReadyForNextToil();
				else if (pawn.IsHashIntervalTick(ticks_between_hearts))
					MoteMaker.ThrowMetaIcon(pawn.Position, pawn.Map, ThingDefOf.Mote_Heart);
			};
			fap.AddFinishAction(delegate
			{
				SexUtility.Aftersex(pawn, xxx.rjwSextype.Masturbation);
				if (!SexUtility.ConsiderCleaning(pawn)) return;

				LocalTargetInfo own_cum = pawn.PositionHeld.GetFirstThing<Filth>(pawn.Map);

				Job clean = new Job(JobDefOf.Clean);
				clean.AddQueuedTarget(TargetIndex.A, own_cum);

				pawn.jobs.jobQueue.EnqueueFirst(clean);
			});
			yield return fap;
		}
	}
}


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

namespace rjw
{
	public class JobDriver_RandomRape : JobDriver_Rape
	{
		//Add some stuff. planning became bersek when failed to rape.
	}
}


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

namespace rjw
{
	public class JobDriver_Rape : JobDriver
	{
		protected int duration;

		protected int ticks_between_hearts;
		protected int ticks_between_hits = 50;
		protected int ticks_between_thrusts;
		protected bool usedCondom;

		protected TargetIndex iTarget = TargetIndex.A;
		public Pawn Target => (Pawn)(job.GetTarget(iTarget));

		public override bool TryMakePreToilReservations(bool errorOnFailed)
		{
			return pawn.Reserve(Target, job, xxx.max_rapists_per_prisoner, 0, null, errorOnFailed);
		}

		public static void roll_to_hit(Pawn pawn, Pawn partner)
		{
			SexUtility.Sex_Beatings(pawn, partner, true);
		}

		[SyncMethod]
		protected override IEnumerable<Toil> MakeNewToils()
		{
			//Rand.PopState();
			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
			//Log.Message("[RJW]" + this.GetType().ToString() + "::MakeNewToils() called");
			duration = (int)(2000.0f * Rand.Range(0.50f, 0.90f));
			ticks_between_hearts = Rand.RangeInclusive(70, 130);
			ticks_between_hits = Rand.Range(xxx.config.min_ticks_between_hits, xxx.config.max_ticks_between_hits);
			ticks_between_thrusts = 100;

			if (xxx.is_bloodlust(pawn))
				ticks_between_hits = (int)(ticks_between_hits * 0.75);
			if (xxx.is_brawler(pawn))
				ticks_between_hits = (int)(ticks_between_hits * 0.90);

			this.FailOnDespawnedNullOrForbidden(iTarget);
			this.FailOn(() => !pawn.CanReserve(Target, xxx.max_rapists_per_prisoner, 0)); // Fail if someone else reserves the prisoner before the pawn arrives
			this.FailOn(() => pawn.IsFighting());
			this.FailOn(() => Target.IsFighting());
			this.FailOn(() => pawn.Drafted);
			yield return Toils_Goto.GotoThing(iTarget, PathEndMode.OnCell);

			SexUtility.RapeTargetAlert(pawn, Target);

			usedCondom = CondomUtility.TryUseCondom(pawn);

			//Log.Message("[RJW] JobDriver_Rape::make toils() called");
			var rape = new Toil();
			rape.initAction = delegate
			{
				//Log.Message("[RJW]" + this.GetType().ToString() + "::initAction() called");
				//pawn.Reserve(Target, comfort_prisoners.max_rapists_per_prisoner, 0);
				//if (!pawnHasPenis)
				//	Target.rotationTracker.Face(pawn.DrawPos);
				JobDriver_GettinRaped dri = Target.jobs.curDriver as JobDriver_GettinRaped;
				if (dri == null)
				{
					Job gettin_raped = new Job(xxx.gettin_raped, pawn, Target);
					Building_Bed Bed = null;
					//Log.Message(xxx.get_pawnname(pawn) + " LayingInBed:" + pawn.GetPosture());
					//Log.Message(xxx.get_pawnname(Target) + " LayingInBed:" + Target.GetPosture());
					if (Target.GetPosture() == PawnPosture.LayingInBed)
					{
						Bed = Target.CurrentBed();
						//Log.Message(xxx.get_pawnname(Target) + ": bed:" + Bed);
					}
					Target.jobs.StartJob(gettin_raped, JobCondition.InterruptForced, null, false, true, null);
					(Target.jobs.curDriver as JobDriver_GettinRaped)?.increase_time(duration);
					if (Bed !=null)
						(Target.jobs.curDriver as JobDriver_GettinRaped)?.set_bed(Bed);
				}
				else
				{
					dri.rapist_count += 1;
					dri.increase_time(duration);
				}
				rape.FailOn(() => Target.CurJob == null || Target.CurJob.def != xxx.gettin_raped || Target.IsFighting() || pawn.IsFighting());
			};
			rape.tickAction = delegate
			{
				//Log.Message("[RJW] JobDriver_Rape::tickAction() called");
				if (pawn.IsHashIntervalTick(ticks_between_hearts))
					MoteMaker.ThrowMetaIcon(pawn.Position, pawn.Map, ThingDefOf.Mote_Heart);
				if (pawn.IsHashIntervalTick(ticks_between_thrusts))
					xxx.sexTick(pawn, Target, false);
				if (pawn.IsHashIntervalTick(ticks_between_hits))
					roll_to_hit(pawn, Target);
				xxx.reduce_rest(Target, 1);
				xxx.reduce_rest(pawn, 2);
			};
			rape.AddFinishAction(delegate
			{
				if (Target.jobs?.curDriver is JobDriver_GettinRaped)
				{
					(Target.jobs.curDriver as JobDriver_GettinRaped).rapist_count -= 1;
				}
			});
			rape.defaultCompleteMode = ToilCompleteMode.Delay;
			rape.defaultDuration = duration;
			yield return rape;

			yield return new Toil
			{
				initAction = delegate
				{
					//Log.Message("[RJW] JobDriver_Rape::aftersex() called");
					//// Trying to add some interactions and social logs
					SexUtility.ProcessSex(pawn, Target, usedCondom, true);
				},
				defaultCompleteMode = ToilCompleteMode.Instant
			};
		}
	}
}


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

namespace rjw
{
	internal class JobDef_RapeEnemy : JobDef
	{
		public List<string> TargetDefNames = new List<string>();
		public int priority = 0;

		protected JobDriver_RapeEnemy instance
		{
			get
			{
				if (_tmpInstance == null)
				{
					_tmpInstance = (JobDriver_RapeEnemy)Activator.CreateInstance(driverClass);
				}
				return _tmpInstance;
			}
		}

		private JobDriver_RapeEnemy _tmpInstance;

		public virtual bool CanUseThisJobForPawn(Pawn rapist)
		{
			if (rapist.CurJob != null && rapist.CurJob.def != JobDefOf.LayDown)
				return false;

			return instance.CanUseThisJobForPawn(rapist);// || TargetDefNames.Contains(rapist.def.defName);
		}

		public virtual Pawn FindVictim(Pawn rapist, Map m)
		{
			return instance.FindVictim(rapist, m);
		}
	}

	public class JobDriver_RapeEnemy : JobDriver_Rape
	{
		private static readonly HediffDef is_submitting = HediffDef.Named("Hediff_Submitting");//used in find_victim

		//override can_rape mechanics
		protected bool requireCanRape = true;

		public virtual bool CanUseThisJobForPawn(Pawn rapist)
		{
			return xxx.is_human(rapist);
		}

		// this is probably useseless, maybe there be something in future
		public virtual bool considerStillAliveEnemies => true;

		[SyncMethod]
		public virtual Pawn FindVictim(Pawn rapist, Map m)
		{
			//Log.Message("[RJW]" + this.GetType().ToString() + "::TryGiveJob( " + xxx.get_pawnname(rapist) + " ) map " + m?.ToString());
			if (rapist == null || m == null) return null;
			//Log.Message("[RJW]" + this.GetType().ToString() + "::TryGiveJob( " + xxx.get_pawnname(rapist) + " ) can rape " + xxx.can_rape(rapist));
			if (requireCanRape && !xxx.can_rape(rapist)) return null;
			Pawn best_target = null;
			List<Pawn> animalTargets = new List<Pawn>();
			float best_fuckability = 0.20f; // Don't rape pawns with <20% fuckability

			var targets = m.mapPawns.AllPawnsSpawned
				.Where(target => !target.IsForbidden(rapist) && target != rapist && target.HostileTo(rapist))
				.Where(target => xxx.can_path_to_target(rapist, target.Position))
				.ToList();

			if (targets.Any(target => IsBlocking(rapist, target)))
			{
				return null;
			}

			var validTargets = targets
				.Where(target => IsValidTarget(rapist, target))
				.ToList();

			foreach (var target in validTargets)
			{
				float fuc = GetFuckability(rapist, target);

				if (fuc > best_fuckability)
				{
					if (xxx.is_animal(rapist))
					{
						animalTargets.Add(target);
					}
					else
					{
						best_target = target;
						best_fuckability = fuc;
					}
				}
			}
			//Log.Message("[RJW]"+this.GetType().ToString()+"::TryGiveJob( " + xxx.get_pawnname(rapist) + " -> " + xxx.get_pawnname(best_rapee) + " ) - fuckability:" + best_fuckability + " ");
			return animalTargets.Any() ? animalTargets.RandomElement() : best_target;
		}

		bool IsBlocking(Pawn rapist, Pawn target)
		{
			return considerStillAliveEnemies && !target.Downed && rapist.CanSee(target);
		}

		bool IsValidTarget(Pawn rapist, Pawn target)
		{
			if (!RJWSettings.bestiality_enabled)
			{
				if (xxx.is_animal(target) && xxx.is_human(rapist))
				{
					//bestiality disabled, skip.
					return false;
				}
				if (xxx.is_animal(rapist) && xxx.is_human(target))
				{
					//bestiality disabled, skip.
					return false;
				}
			}

			if (!RJWSettings.animal_on_animal_enabled)
				if ((xxx.is_animal(target) && xxx.is_animal(rapist)))
				{
					//animal_on_animal disabled, skip.
					return false;
				}

			if (target.CurJob?.def == xxx.gettin_raped || target.CurJob?.def == xxx.gettin_loved)
			{
				//already having sex with someone, skip, give chance to other victims.
				return false;
			}

			return Can_rape_Easily(target) &&
				(xxx.is_human(target) || xxx.is_animal(target)) &&
				rapist.CanReserveAndReach(target, PathEndMode.OnCell, Danger.Some, xxx.max_rapists_per_prisoner, 0);
		}

		public virtual float GetFuckability(Pawn rapist, Pawn target)
		{
			//Log.Message("[RJW]JobDriver_RapeEnemy::GetFuckability(" + rapist.ToString() + "," + target.ToString() + ")");
			if (target.health.hediffSet.HasHediff(is_submitting))//it's not about attractiveness anymore, it's about showing who's whos bitch
			{
				return 2 * SexAppraiser.would_fuck(rapist, target, invert_opinion: true, ignore_bleeding: true, ignore_gender: true);
			}
			return !SexAppraiser.would_rape(rapist, target) ? 0f
				: SexAppraiser.would_fuck(rapist, target, invert_opinion: true, ignore_bleeding: true, ignore_gender: true);
		}

		protected bool Can_rape_Easily(Pawn pawn)
		{
			return xxx.can_get_raped(pawn) && !pawn.IsBurning();
		}
	}
}


code:
using RimWorld;
using Verse;

namespace rjw
{
	internal class JobDriver_RapeEnemyByAnimal : JobDriver_RapeEnemy
	{
		public override bool CanUseThisJobForPawn(Pawn rapist)
		{
			if (rapist.CurJob != null && (rapist.CurJob.def != JobDefOf.LayDown || rapist.CurJob.def != JobDefOf.Wait_Wander || rapist.CurJob.def != JobDefOf.GotoWander))
				return false;

			return xxx.is_animal(rapist) && !xxx.is_insect(rapist) && (RJWSettings.bestiality_enabled || RJWSettings.animal_on_animal_enabled);
		}
	}
}


code:
using RimWorld;
using Verse;

namespace rjw
{
	internal class JobDriver_RapeEnemyByHumanlike : JobDriver_RapeEnemy
	{
		public override bool CanUseThisJobForPawn(Pawn rapist)
		{
			if (rapist.CurJob != null && (rapist.CurJob.def != JobDefOf.LayDown || rapist.CurJob.def != JobDefOf.Wait_Wander || rapist.CurJob.def != JobDefOf.GotoWander))
				return false;

			return xxx.is_human(rapist);
		}
	}
}


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

namespace rjw
{
	internal class JobDriver_RapeEnemyByInsect : JobDriver_RapeEnemy
	{
		//override mechanics

		//public JobDriver_RapeEnemyByInsect()
		//{
		//	this.requierCanRape = false;
		//}

		public override bool CanUseThisJobForPawn(Pawn rapist)
		{
			if (rapist.CurJob != null && (rapist.CurJob.def != JobDefOf.LayDown || rapist.CurJob.def != JobDefOf.Wait_Wander || rapist.CurJob.def != JobDefOf.GotoWander))
				return false;

			//someday add check for insect-insect breeding
			return xxx.is_insect(rapist) && RJWSettings.bestiality_enabled;
		}

		public override float GetFuckability(Pawn rapist, Pawn target)
		{
			//Plant Eggs to everyone.
			if (rapist.gender == Gender.Female)
			{
				return 1f;
			}
			//Feritlize eggs to everyone with planted eggs.
			else
			{
				//if ((from x in target.health.hediffSet.GetHediffs<Hediff_InsectEgg>() where (x.IsParent(rapist.def.defName) && !x.fertilized) select x).Count() > 0)
				if ((from x in target.health.hediffSet.GetHediffs<Hediff_InsectEgg>() where x.IsParent(rapist) select x).Count() > 0)
				{
					return 1f;
				}
			}
			return 0f;
		}
	}
}


code:
using RimWorld;
using Verse;

namespace rjw
{
	internal class JobDriver_RapeEnemyByMech : JobDriver_RapeEnemy
	{
		//public JobDriver_RapeEnemyByMech()
		//{
		//	this.requierCanRape = false;
		//}

		public override bool CanUseThisJobForPawn(Pawn rapist)
		{
			if (rapist.CurJob != null && (rapist.CurJob.def != JobDefOf.LayDown || rapist.CurJob.def != JobDefOf.Wait_Wander || rapist.CurJob.def != JobDefOf.GotoWander))
				return false;

			return xxx.is_mechanoid(rapist);
		}

		public override float GetFuckability(Pawn rapist, Pawn target)
		{
			//--Log.Message("[RJW]JobDriver_RapeEnemyByMech::GetFuckability("+ rapist.ToString()+","+ target.ToString() + ") - Force Rape");
			//Plant chips to humanlikes.
			if (xxx.is_human(target))
				return 1f;
			else
				return 0f;
		}

	}
}


code:
using RimWorld;
using Verse;

namespace rjw
{
	internal class JobDriver_RapeEnemyToParasite : JobDriver_RapeEnemy
	{
		public JobDriver_RapeEnemyToParasite()
		{
			this.requireCanRape = false;
		}

		//not implemented
		public override bool CanUseThisJobForPawn(Pawn rapist)
		{
			if (rapist.CurJob != null && (rapist.CurJob.def != JobDefOf.LayDown || rapist.CurJob.def != JobDefOf.Wait_Wander || rapist.CurJob.def != JobDefOf.GotoWander))
				return false;

			return false;
		}
	}
}


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

namespace rjw
{
	public abstract class JobDriver_Sex : JobDriver
	{
		protected float satisfaction = 1.0f;

		public bool shouldreserve = true;

		public int stackCount = 0;

		public int ticks_between_hearts;
		public int ticks_between_hits = 50;
		public int ticks_between_thrusts;

		public Thing Target = null;

		//private Building_Bed Bed;

		public override bool TryMakePreToilReservations(bool errorOnFailed)
		{
			Log.Message("shouldreserve " + shouldreserve);
			if (shouldreserve)
				return pawn.Reserve(Target, job, xxx.max_rapists_per_prisoner, stackCount, null, errorOnFailed);
			else
				return true; // No reservations needed.

			//return this.pawn.Reserve(this.Partner, this.job, 1, 0, null) && this.pawn.Reserve(this.Bed, this.job, 1, 0, null);
		}

		public void CalculateSatisfactionPerTick()
		{
				satisfaction = 1.0f;
		}

		protected override IEnumerable<Toil> MakeNewToils()
		{
			return null;
		}
	}
}


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

namespace rjw
{
	public class JobDriver_ViolateCorpse : JobDriver
	{
		private int duration;
		private int ticks_between_hearts;
		private int ticks_between_hits = 50;
		private int ticks_between_thrusts;

		protected TargetIndex icorpse = TargetIndex.A;
		protected Corpse Target => (Corpse)(job.GetTarget(icorpse));

		public static void sexTick(Pawn pawn, Thing Target)
		{
			if (!xxx.has_quirk(pawn, "Endytophile"))
			{
				xxx.DrawNude(pawn, true);
			}

			if (RJWSettings.sounds_enabled)
				SoundDef.Named("Sex").PlayOneShot(new TargetInfo(pawn.Position, pawn.Map));

			pawn.Drawer.Notify_MeleeAttackOn(Target);
			pawn.rotationTracker.FaceCell(Target.Position);
		}

		public override bool TryMakePreToilReservations(bool errorOnFailed)
		{
			return pawn.Reserve(Target, job, 1, -1, null, errorOnFailed);
		}

		[SyncMethod]
		protected override IEnumerable<Toil> MakeNewToils()
		{
			//--Log.Message("[RJW] JobDriver_ViolateCorpse::MakeNewToils() called");
			//Rand.PopState();
			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
			duration = (int)(2000.0f * Rand.Range(0.50f, 0.90f));
			ticks_between_hearts = Rand.RangeInclusive(70, 130);
			ticks_between_hits = Rand.Range(xxx.config.min_ticks_between_hits, xxx.config.max_ticks_between_hits);
			ticks_between_thrusts = 100;

			if (xxx.is_bloodlust(pawn))
				ticks_between_hits = (int)(ticks_between_hits * 0.75);
			if (xxx.is_brawler(pawn))
				ticks_between_hits = (int)(ticks_between_hits * 0.90);

			this.FailOnDespawnedNullOrForbidden(icorpse);
			this.FailOn(() => !pawn.CanReserve(Target, 1, 0));  // Fail if someone else reserves the prisoner before the pawn arrives
			this.FailOn(() => pawn.IsFighting());
			this.FailOn(() => pawn.Drafted);
			this.FailOn(Target.IsBurning);

			//--Log.Message("[RJW] JobDriver_ViolateCorpse::MakeNewToils() - moving towards Target");
			yield return Toils_Goto.GotoThing(icorpse, PathEndMode.OnCell);

			var alert = RJWPreferenceSettings.rape_alert_sound == RJWPreferenceSettings.RapeAlert.Disabled ?
				MessageTypeDefOf.SilentInput : MessageTypeDefOf.NeutralEvent;
			Messages.Message(xxx.get_pawnname(pawn) + " is trying to rape a corpse of " + xxx.get_pawnname(Target.InnerPawn), pawn, alert);

			var rape = new Toil();
			rape.initAction = delegate
			{
				//--Log.Message("[RJW] JobDriver_ViolateCorpse::MakeNewToils() - reserving Target");
				//pawn.Reserve(Target, 1, 0); // Target rapin seems like a solitary activity

				//--Log.Message("[RJW] JobDriver_ViolateCorpse::MakeNewToils() - stripping Target");
				Target.Strip();
			};
			rape.tickAction = delegate
			{
				if (pawn.IsHashIntervalTick(ticks_between_hearts))
					MoteMaker.ThrowMetaIcon(pawn.Position, pawn.Map, ThingDefOf.Mote_Heart);
				if (pawn.IsHashIntervalTick(ticks_between_thrusts))
					sexTick(pawn, Target);
				/*
				if (pawn.IsHashIntervalTick (ticks_between_hits))
					roll_to_hit (pawn, Corpse);
					*/
				xxx.reduce_rest(pawn, 2);
			};
			rape.AddFinishAction(delegate
			{
				if (xxx.is_human(pawn))
					pawn.Drawer.renderer.graphics.ResolveApparelGraphics();
			});
			rape.defaultCompleteMode = ToilCompleteMode.Delay;
			rape.defaultDuration = duration;
			yield return rape;

			yield return new Toil
			{
				initAction = delegate
				{
					//--Log.Message("[RJW] JobDriver_ViolateCorpse::MakeNewToils() - creating aftersex toil");
					//Addded by nizhuan-jjr: Try to apply an aftersex process for the pawn and the Target
					if (Target.InnerPawn != null)
					{
						SexUtility.ProcessSex(pawn, Target.InnerPawn, false, true);
					}
				},
				defaultCompleteMode = ToilCompleteMode.Instant
			};
		}
	}
}


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

namespace rjw
{
	//Rape to Prisoner of QuestPrisonerWillingToJoin
	class JobGiver_AIRapePrisoner : ThinkNode_JobGiver
	{
		private const float min_fuckability = 0.10f; // Don't rape prisoners with <10% fuckability

		[SyncMethod]
		public static Pawn find_victim(Pawn pawn, Map m)
		{
			IEnumerable<Pawn> targets = m.mapPawns.AllPawns.Where(x
				=> x != pawn
				&& IsPrisonerOf(x, pawn.Faction)
				&& xxx.can_get_raped(x)
				&& !x.Position.IsForbidden(pawn)
				);

			List<Pawn> valid_targets = new List<Pawn>();

			foreach (Pawn target in targets)
			{
				if (!pawn.CanReserve(target, xxx.max_rapists_per_prisoner, 0)) continue;

				//--Log.Message(xxx.get_pawnname(pawn) + "->" + xxx.get_pawnname(target) + ":" + fuc);
				if (SexAppraiser.would_fuck(pawn, target, true, true) > min_fuckability)
				{
					valid_targets.Add(target);
				}
			}

			//Rand.PopState();
			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
			return valid_targets.Any() ? valid_targets.RandomElement() : null;
		}

		protected override Job TryGiveJob(Pawn pawn)
		{
			//Log.Message("[RJW] JobGiver_AIRapePrisoner::TryGiveJob( " + xxx.get_pawnname(pawn) + " ) called ");

			if (!xxx.can_rape(pawn)) return null;

			if (SexUtility.ReadyForLovin(pawn) || xxx.need_some_sex(pawn) > 1f)
			{
				// don't allow pawns marked as comfort prisoners to rape others
				if (xxx.is_healthy(pawn))
				{
					Pawn prisoner = find_victim(pawn, pawn.Map);

					if (prisoner != null)
					{
						//--Log.Message("[RJW] JobGiver_RandomRape::TryGiveJob( " + xxx.get_pawnname(p) + " ) - found victim " + xxx.get_pawnname(prisoner));
						return new Job(xxx.random_rape, prisoner);
					}
				}
			}

			return null;
		}

		protected static bool IsPrisonerOf(Pawn pawn,Faction faction)
		{
			if (pawn?.guest == null) return false;
			return pawn.guest.HostFaction == faction && pawn.guest.IsPrisoner;
		}
	}
}


code:
// #define TESTMODE // Uncomment to enable logging.

using System.Diagnostics;
using RimWorld;
using Verse;
using Verse.AI;

namespace rjw
{
	/// <summary>
	/// Pawn tries to find animal to do loving/raping.
	/// </summary>
	public class JobGiver_Bestiality : ThinkNode_JobGiver
	{
		[Conditional("TESTMODE")]
		private static void DebugText(string msg)
		{
			Log.Message(msg);
		}

		protected override Job TryGiveJob(Pawn pawn)
		{
			if (pawn.Drafted) return null;

			// Most checks are now done in ThinkNode_ConditionalBestiality
			DebugText("[RJW] JobGiver_Bestiality::TryGiveJob( " + xxx.get_pawnname(pawn) + " ) called");

			if (!SexUtility.ReadyForLovin(pawn) && !xxx.is_frustrated(pawn))
				return null;

			Pawn target = BreederHelper.find_breeder_animal(pawn, pawn.Map);
			DebugText("[RJW] JobGiver_Bestiality::TryGiveJob - target is " + (target == null ? "no target found" : xxx.get_pawnname(target)));

			if (target == null) return null;

			if (xxx.can_rape(pawn))
			{
				return new Job(xxx.bestiality, target);
			}

			Building_Bed bed = pawn.ownership.OwnedBed;
			if (!xxx.can_be_fucked(pawn) || bed == null || !target.CanReach(bed, PathEndMode.OnCell, Danger.Some) || target.Downed) return null;

			// TODO: Should rename this to BestialityInBed or somesuch, since it's not limited to females.
			return new Job(xxx.bestialityForFemale, target, bed, bed.SleepPosOfAssignedPawn(pawn));
		}
	}
}


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

namespace rjw
{
	/// <summary>
	/// Attempts to give a breeding job to an eligible animal.
	/// </summary>
	public class JobGiver_Breed : ThinkNode_JobGiver
	{
		[SyncMethod]
		protected override Job TryGiveJob(Pawn animal)
		{
			//Log.Message("[RJW] JobGiver_Breed::TryGiveJob( " + xxx.get_pawnname(animal) + " ) called0" + (SexUtility.ReadyForLovin(animal)));

			if (!SexUtility.ReadyForLovin(animal))
				return null;

			if(xxx.is_healthy(animal) && xxx.can_rape(animal))
			{
				//Log.Message("[RJW] JobGiver_Breed::TryGiveJob( " + xxx.get_pawnname(animal) + " ) called2");
				List<Pawn> valid_targets = new List<Pawn>();

				//search for desiganted target to sex
				if (animal.IsDesignatedBreedingAnimal())
				{
					Pawn designated_target = BreederHelper.find_designated_breeder(animal, animal.Map);
					if (designated_target != null)
					{
						valid_targets.Add(designated_target);
					}
				}

				//some weird shit happens, animal tries to rape and fails, needs investigation someday
				/*
				//search for animal to sex
				if (RJWSettings.animal_on_animal_enabled)
				{
					//Using bestiality target finder, since it works best for this.
					//search for same race mate
					if (!valid_targets.Any())
					{
						Pawn animal_target = BreederHelper.find_breeder_animal(animal, animal.Map);
						if (animal_target != null)
						{
							valid_targets.Add(animal_target);
						}
					}

					//search for any other animal/human to sex
					if (!valid_targets.Any())
					{
						Pawn animal_target = BreederHelper.find_breeder_animal(animal, animal.Map, false);
						if (animal_target != null)
						{
							valid_targets.Add(animal_target);
						}
					}

				}
				*/

				//Log.Message("[RJW] JobGiver_Breed::TryGiveJob( " + xxx.get_pawnname(animal) + " ) called3 - (" + ((target == null) ? "no target found" : xxx.get_pawnname(target))+") is the prisoner");

				if (valid_targets != null && valid_targets.Any())
				{
					//Rand.PopState();
					//Rand.PushState(RJW_Multiplayer.PredictableSeed());
					var target = valid_targets.RandomElement();
					//Log.Message("Target: " + xxx.get_pawnname(target));
					return new Job(DefDatabase<JobDef>.GetNamed("Breed"), target, animal);
				}
			}
			return null;
		}
	}
}


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

namespace rjw
{
	public class JobGiver_ComfortPrisonerRape : ThinkNode_JobGiver
	{
		public static Pawn find_prisoner_to_rape(Pawn pawn, Map m)
		{
			if (!DesignatorsData.rjwComfort.Any()) return null;
			Pawn best_rapee = null;
			float best_fuckability = 0.10f; // Don't rape prisoners with <10% fuckability
			IEnumerable<Pawn> targets = DesignatorsData.rjwComfort.Where(x
				=> x != pawn
				&& xxx.can_get_raped(x)
				&& pawn.CanReserveAndReach(x, PathEndMode.Touch, Danger.Some, xxx.max_rapists_per_prisoner, 0)
				&& !x.IsForbidden(pawn)
				&& SexAppraiser.would_rape(pawn, x)
				);

			if (xxx.is_animal(pawn))
			{
				// Animals only consider targets they can see, instead of seeking them out.
				targets = targets.Where(x => pawn.CanSee(x)).ToList();
			}

			foreach (Pawn target in targets)
			{
				if (!xxx.can_path_to_target(pawn, target.Position))
					continue;// too far

				float fuc = 0.0f;
				if (xxx.is_animal(target))
					fuc = SexAppraiser.would_fuck_animal(pawn, target, true);
				else if (xxx.is_human(target))
					fuc = SexAppraiser.would_fuck(pawn, target, true);
				//--Log.Message(pawn.Name + " -> " + candidate.Name + " (" + fuc.ToString() + " / " + best_fuckability.ToString() + ")");

				if (fuc > best_fuckability)
				{
					best_rapee = target;
					best_fuckability = fuc;
				}
			}
			return best_rapee;
		}

		protected override Job TryGiveJob(Pawn pawn)
		{
			//Log.Message("[RJW] JobGiver_ComfortPrisonerRape::TryGiveJob( " + xxx.get_pawnname(pawn) + " ) called0");

			if (!RJWSettings.WildMode)
			{
				// don't allow pawns marked as comfort prisoners to rape others
				if (!xxx.is_healthy(pawn) || pawn.IsDesignatedComfort() || (!SexUtility.ReadyForLovin(pawn) && !xxx.is_frustrated(pawn))) return null;
			}

			if (pawn.Drafted) return null;

			//Log.Message("[RJW] JobGiver_ComfortPrisonerRape::TryGiveJob( " + xxx.get_pawnname(pawn) + " ) called1");
			if (!xxx.can_rape(pawn)) return null;

			// It's unnecessary to include other job checks. Pawns seem to only look for new jobs when between jobs or layind down idle.
			if (!(pawn.jobs.curJob == null || pawn.jobs.curJob.def == JobDefOf.LayDown)) return null;

			//--Log.Message("[RJW] JobGiver_ComfortPrisonerRape::TryGiveJob( " + xxx.get_pawnname(pawn) + " ) called2");
			// Faction check.
			if (!(pawn.Faction?.IsPlayer ?? false) && !pawn.IsPrisonerOfColony) return null;

			Pawn target = find_prisoner_to_rape(pawn, pawn.Map);
			//--Log.Message("[RJW] JobGiver_ComfortPrisonerRape::TryGiveJob( " + xxx.get_pawnname(pawn) + " ) called3 - (" + ((target == null) ? "no target found" : xxx.get_pawnname(target))+") is the prisoner");

			if (target == null) return null;

			//Log.Message("giving job to " + pawner + " with target " + target);
			if (xxx.is_animal(target))
				return new Job(xxx.bestiality, target);
			else
				return new Job(xxx.comfort_prisoner_rapin, target);
		}
	}
}