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

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

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

	// Remove the hediff when the gear is removed
	[HarmonyPatch(typeof(Pawn_ApparelTracker))]
	[HarmonyPatch("Remove")]
	internal static class PATCH_Pawn_ApparelTracker_Remove
	{
		[HarmonyPostfix]
		private static void on_remove(Pawn_ApparelTracker __instance, Apparel ap)
		{
			var tra = __instance;
			// Log.Message ("Pawn_ApparelTracker.Remove called for " + ap.Label + " on " + tra.pawn.NameStringShort);
			if (ap.def is bondage_gear_def def)
				def.soul.on_remove(ap, tra.pawn);
		}
	}

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

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

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

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

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

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

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

			return true;
		}
	}

	// Prevent locked apparel from being removed from a corpse
	// TODO: Find out when this method is actually called (probably doesn't matter but still)
	[HarmonyPatch(typeof(Dialog_FormCaravan))]
	[HarmonyPatch("RemoveFromCorpseIfPossible")]
	internal static class PATCH_Dialog_FormCaravan_RemoveFromCorpseIfPossible
	{
		[HarmonyPrefix]
		private static bool prevent_locked_apparel_removal(Dialog_FormCaravan __instance, Thing thing)
		{
			var app = thing as Apparel;
			return (app == null) || (!app.has_lock());
		}
	}

	// Patch a method in a pawn gear tab to give the pawn a special job driver if the player tells them to
	// drop locked apparel they have equipped
	// TODO: Search for the key and, if the pawn can access it, give them a new job that has them go to the key
	// and use it to unlock the apparel
	[HarmonyPatch(typeof(ITab_Pawn_Gear))]
	[HarmonyPatch("InterfaceDrop")]
	internal static class PATCH_ITab_Pawn_Gear_InterfaceDrop
	{
		private static Pawn SelPawnForGear(ITab_Pawn_Gear tab)
		{
			var pro = typeof(ITab_Pawn_Gear).GetProperty("SelPawnForGear", xxx.ins_public_or_no);
			return (Pawn)pro.GetValue(tab, null);
		}

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

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

			return false;
		}

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

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


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

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

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

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

		private static Pawn find_partner(JobDriver_Lovin lov)
		{
			var any_ins = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
			return (Pawn)(typeof(JobDriver_Lovin).GetProperty("Partner", any_ins).GetValue(lov, null));
		}

		[HarmonyPrefix]
		private static bool on_cleanup_driver(JobDriver __instance, JobCondition condition)
		{
			if (__instance == null) return true;
			var lov = __instance as JobDriver_Lovin;
			//Edited by nizhuan-jjr: The PostFix method cannot do this.
			if ((lov != null) && (condition == JobCondition.Succeeded))
			{
				//--Log.Message("[RJW]patches_lovin::on_cleanup_driver is called0");
				var par = find_partner(lov);
				xxx.aftersex(lov.pawn, par, false, true); // note that JobDriver_Lovin will be called for both pawns
														  //lov.pawn.mindState.canLovinTick = Find.TickManager.TicksGame + xxx.generate_min_ticks_to_next_lovin(lov.pawn);
														  //if (par != null)
														  //    par.mindState.canLovinTick = Find.TickManager.TicksGame + xxx.generate_min_ticks_to_next_lovin(par);
			}
			else if (xxx.RomanceDiversifiedIsActive && condition == JobCondition.Succeeded && __instance.GetType() == JobDriverDoLovinCasual)
			{
				//--Log.Message("[RJW]patches_lovin::on_cleanup_driver is called1");
				var any_ins = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
				var casuallovin_par = (Pawn)(__instance.GetType().GetProperty("Partner", any_ins).GetValue(__instance, null));
				if (casuallovin_par != null)
				{
					xxx.aftersex(__instance.pawn, casuallovin_par, false, true); // note that JobDriver_DoLovinCasual will be called for both pawns
																				 //lov.pawn.mindState.canLovinTick = Find.TickManager.TicksGame + xxx.generate_min_ticks_to_next_lovin(lov.pawn);
																				 //casuallovin_par.mindState.canLovinTick = Find.TickManager.TicksGame + xxx.generate_min_ticks_to_next_lovin(casuallovin_par);
				}
			}
			return true;
		}
	}
}

repo_name: Thissal/RimJobWorld

license: mit


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


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

namespace rjw
{
	[HarmonyPatch(typeof(Hediff_Pregnant), "DoBirthSpawn")]
	internal static class PATCH_Hediff_Pregnant_DoBirthSpawn
	{
		[HarmonyPrefix]
		private static bool on_begin_DoBirthSpawn(ref Pawn mother, ref Pawn father)
		{
			//TODO: Set pregnant hediff to torso
			//Log.Message("patches_pregnancy::PATCH_Hediff_Pregnant::DoBirthSpawn() called");
			var mother_name = (mother != null) ? mother.NameStringShort : "NULL";
			var father_name = (father != null) ? father.NameStringShort : "NULL";

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

			if (father == null)
			{
				Log.Warning("Hediff_Pregnant::DoBirthSpawn() - no father defined");
			}
			// get a reference to the hediff we are applying
			Hediff_Pregnant self = (Hediff_Pregnant)mother.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("Pregnant"));

			// determine litter size
			int litter_size = (mother.RaceProps.litterSizeCurve == null) ? 1 : Mathf.RoundToInt(Rand.ByCurve(mother.RaceProps.litterSizeCurve, 300));
			if (litter_size < 1)
			{
				litter_size = 1;
			}
			float skin_whiteness = Rand.Range(0, 1);
			string last_name = null;

			// send a message about giving birth
			//Log.Message("Hediff_Pregnancy::DoBirthSpawn( " + mother_name + ", " + father_name + ", " + chance_successful + " ) - generating baby pawns");
			if (self.Visible && PawnUtility.ShouldSendNotificationAbout(mother))
			{
				Messages.Message("GivingBirth".Translate(new object[] { mother.LabelIndefinite() }).CapitalizeFirst(), mother, MessageSound.Standard);
			}

			//Log.Message("Hediff_Pregnancy::DoBirthSpawn( " + mother_name + ", " + father_name + ", " + chance_successful + " ) - creating spawn request");

			List<Pawn> siblings = new List<Pawn>();
			for (int i = 0; i < litter_size; i++)
			{
				Pawn spawn_parent = mother;
				if (father != null && Mod_Settings.pregnancy_use_parent_method && (100 * Rand.Value) > Mod_Settings.pregnancy_weight_parent)
				{
					spawn_parent = father;
				}
				PawnGenerationRequest request = new PawnGenerationRequest(spawn_parent.kindDef, spawn_parent.Faction, PawnGenerationContext.NonPlayer, spawn_parent.Map.Tile, false, true, false, false, false, false, 1, false, true, true, false, false, null, 0, 0, null, skin_whiteness, last_name);

				//Log.Message("Hediff_GenericPregnancy::DoBirthSpawn( " + mother_name + ", " + father_name + ", " + chance_successful + " ) - spawning baby");
				Pawn baby = PawnGenerator.GeneratePawn(request);

				if (PawnUtility.TrySpawnHatchedOrBornPawn(baby, mother))
				{
					if (baby.playerSettings != null && mother.playerSettings != null)
					{
						baby.playerSettings.AreaRestriction = mother.playerSettings.AreaRestriction;
					}
					if (baby.RaceProps.IsFlesh)
					{
						baby.relations.AddDirectRelation(PawnRelationDefOf.Parent, mother);
						if (father != null)
						{
							baby.relations.AddDirectRelation(PawnRelationDefOf.Parent, father);
						}

						foreach (Pawn sibling in siblings)
						{
							baby.relations.AddDirectRelation(PawnRelationDefOf.Sibling, sibling);
						}
						siblings.Add(baby);

						//inject RJW_BabyState to the newborn if RimWorldChildren is not active
						if (!xxx.RimWorldChildrenIsActive && baby.kindDef.race == ThingDefOf.Human && baby.ageTracker.CurLifeStageIndex <= 1 && baby.ageTracker.AgeBiologicalYears < 1 && !baby.Dead)
						{
							// Clean out drug randomly generated drug addictions
							baby.health.hediffSet.Clear();
							baby.health.AddHediff(HediffDef.Named("RJW_BabyState"), null, null);
							Hediff_SimpleBaby babystate = (Hediff_SimpleBaby)baby.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("RJW_BabyState"));
							if (babystate != null)
							{
								babystate.GrowUpTo(0, true);
							}
						}
					}
				}
				else
				{
					Find.WorldPawns.PassToWorld(baby, PawnDiscardDecideMode.Discard);
				}
			}

			//Log.Message("Hediff_Pregnancy::DoBirthSpawn( " + mother_name + ", " + father_name + ", " + chance_successful + " ) - removing pregnancy");
			mother.health.RemoveHediff(self);

			return false;
		}
	}

	/*
    [HarmonyPatch(typeof(Hediff_Pregnant), "Tick")]
    class PATCH_Hediff_Pregnant_Tick {
        [HarmonyPrefix]
        static bool on_begin_Tick( Hediff_Pregnant __instance ) {
            //if (__instance.pawn.IsHashIntervalTick(1000)) {
            //    //Log.Message("patches_pregnancy::PATCH_Hediff_Pregnant::Tick( " + __instance.pawn.NameStringShort + " ) - gestation_progress = " + __instance.GestationProgress);
            //    if (__instance.Severity < 0.95f) {
            //        __instance.Severity = 0.95f;
            //    }
            //}
            return true;
        }
    }
    */

	/*
    [HarmonyPatch(typeof(PawnRenderer), "RenderPawnInternal")]
    class PATCH_PawnRenderer_RenderPawnInternal {
        [HarmonyPrefix]
        static bool on_begin_RenderPawnInternal(PawnRenderer __instance, Vector3 rootLoc, Quaternion quat, bool renderBody, Rot4 bodyFacing, Rot4 headFacing, RotDrawMode bodyDrawType = RotDrawMode.Fresh, bool portrait = false, bool headStump = false) {
            Log.Message("PATCH_PawnRenderer_RenderPawnInternal() called");

            return true;
        }
    }

    [HarmonyPatch(typeof(PawnGraphicSet), "ResolveAllGraphics")]
    class PATCH_PawnGraphicSet_ResolveAllGraphics {
        [HarmonyPrefix]
        static bool on_begin_ResolveAllGraphics(PawnGraphicSet __instance) {
            Log.Message("PATCH_PawnGraphicSet_ResolveAllGraphics::ResolveAllGraphics() called");
            if (__instance.pawn.RaceProps.Humanlike && __instance.pawn.ageTracker.CurLifeStageIndex < 4) {
                Log.Message("   " + __instance.pawn.NameStringShort + ":  humanlike = true, lifeStage = " + __instance.pawn.ageTracker.CurLifeStageIndex);
                if (__instance.nakedGraphic != null) {
                    if (__instance.nakedGraphic.drawSize != null) {
                        __instance.nakedGraphic.drawSize *= 0.5f;
                    } else {
                        Log.Message("   __instance.nakedGraphic.drawSize is null");
                    }
                } else {
                    Log.Message("   __instance.nakedGraphic is null");
                }
                if (__instance.apparelGraphics != null) {
                    Log.Message("   __instance.apparelGraphic is present");
                } else {
                    Log.Message("   __instance.apparelGraphic is null");
                }
                if (__instance.rottingGraphic != null) {
                    Log.Message("   __instance.rottingGraphic is present");
                } else {
                    Log.Message("   __instance.rottingGraphic is null");
                }
            }

            if (__instance.pawn.RaceProps.Humanlike && __instance.pawn.ageTracker.CurLifeStageIndex < 4) {
                Log.Message("PATCH_PawnGraphicSet_ResolveAllGraphics::ResolveAllGraphics() - adjusting draw size for " + __instance.pawn.NameStringShort + "");
                if (__instance != null) {
                    var x = __instance.nakedGraphic.drawSize.x;
                    var y = __instance.nakedGraphic.drawSize.y;
                    Log.Message("   current size = " + x + "/" + y + ", new size = " + x * 0.5f + "/" + y * 0.5f);
                } else {
                    Log.Message("   __instance == null");
                }
            }

            return true;
        }
    }
    */
}


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

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

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

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

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

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

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

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


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

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

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

	[StaticConstructorOnStartup]
	internal class HediffDef_InsectEgg : HediffDef_EnemyImplants
	{
		public int bornTick = 900000;//1 Quadrum
		public int abortTick = 60000;//1 day
	}
}


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:
// This is kind of a hack but it's the only way I could find to add a hediff automatically to every pawn. The way it works is that there's a dummy hediff
// that gets added to a pawn's genitals on every birthday (this just uses the same code that generates cancer and other diseases of aging, but with a 100%
// chance to trigger). Then this class gets invoked on the dummy hediff, which it drops and replaces with proper sex parts.

using Verse;

namespace rjw
{
	public class Hediff_InitPrivates : Hediff_AddedPart
	{
		public override void PostAdd(DamageInfo? dinfo)
		{
			//Log.Message("[RJW]Hediff_InitPrivates::PostAdd is called0 - pawn is " + pawn.NameStringShort);
			if (!Genital_Helper.is_sexualized(pawn))
			{
				Genital_Helper.sexualize(pawn);
				//Log.Message("[RJW]Hediff_InitPrivates::PostAdd is called1 - pawn is " + pawn.NameStringShort);

				std.generate_on(pawn);
			}

			// Remove the dummy hediff
			pawn.health.RemoveHediff(this);
		}
	}
}


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

namespace rjw
{
	internal class Hediff_InsectEgg : HediffWithComps
	{
		/*public override void Tick()
		{
			base.Tick();
			Log.Message("[RJW]Hediff_InsectEgg::Tick() - InsectEgg growing");
		}*/

		protected int bornTick
		{
			get
			{
				return ((HediffDef_InsectEgg)this.def).bornTick;
			}
		}

		protected int abortTick
		{
			get
			{
				return ((HediffDef_InsectEgg)this.def).abortTick;
			}
		}

		public Pawn father;
		public Pawn mother;

		public string parentDef
		{
			get
			{
				return ((HediffDef_InsectEgg)def).parentDef;
			}
		}

		public List<string> parentDefs
		{
			get
			{
				return ((HediffDef_InsectEgg)def).parentDefs;
			}
		}

		public override void PostAdd(DamageInfo? dinfo)
		{
			//Log.Message("[RJW]Hediff_InsectEgg::PostAdd() - added parentDef:" + parentDef+"");
			base.PostAdd(dinfo);
		}

		public override void Tick()
		{
			this.ageTicks++;
			if (this.pawn.IsHashIntervalTick(1000))
			{
				if (father != null)
				{
					if (this.ageTicks >= bornTick)
					{
						BirthBaby();
					}
				}
				else
				{
					if (this.ageTicks >= abortTick)
					{
						Messages.Message("EggDead".Translate(new object[] { this.pawn.LabelIndefinite() }).CapitalizeFirst(), MessageSound.Standard);
						pawn.health.RemoveHediff(this);
					}
				}
			}
		}

		public override void ExposeData()
		{
			base.ExposeData();
			Scribe_References.Look<Pawn>(ref this.father, "father", false);
			Scribe_References.Look<Pawn>(ref this.mother, "mother", false);
		}

		public void BirthBaby()
		{
			Log.Message("[RJW]Hediff_InsectEgg::BirthBaby() - Egg of " + parentDef + " in " + pawn.ToString() + " birth!");
			PawnGenerationRequest request = new PawnGenerationRequest(PawnKindDef.Named(parentDef), Faction.OfInsects, PawnGenerationContext.NonPlayer, pawn.Map.Tile, false, true, false, false, false, false, 0, false, true, true, false, false, null, 0, 0, null, null, null);

			//Log.Message("Hediff_GenericPregnancy::DoBirthSpawn( " + mother_name + ", " + father_name + ", " + chance_successful + " ) - spawning baby");
			Pawn baby = PawnGenerator.GeneratePawn(request);
			if (PawnUtility.TrySpawnHatchedOrBornPawn(baby, pawn))
			{
				if (pawn.RaceProps.IsFlesh)
				{
					pawn.relations.AddDirectRelation(PawnRelationDefOf.Parent, mother);
					if (father != null)
					{
						pawn.relations.AddDirectRelation(PawnRelationDefOf.Parent, father);
					}
				}
			}
			else
			{
				Find.WorldPawns.PassToWorld(baby, PawnDiscardDecideMode.Discard);
			}

			if (this.Visible && baby != null)
			{
				Messages.Message("MessageGaveBirth".Translate(new object[] { this.pawn.LabelIndefinite() }).CapitalizeFirst(), baby, MessageSound.Standard);
			}
			pawn.health.RemoveHediff(this);
		}

		public void Fertilize(Pawn f)
		{
			if (father != null)
			{
				father = (Rand.Range(0, 1) == 0) ? father : f;
			}
			else
			{
				father = f;
				Log.Message("[RJW]Hediff_InsectEgg::Fertilize() - Egg in " + pawn.ToString() + " is fertilized by " + f.ToString());
			}
		}

		public override bool TryMergeWith(Hediff other)
		{
			return false;
		}

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

		public override string DebugString()
		{
			return base.DebugString() + " Age:" + this.ageTicks + "\nFertilized:" + (father != null).ToString();
		}
	}
}


code:
using Verse;

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


code:
using System.Linq;
using Verse;

namespace rjw
{
	internal class Hediff_Orgasm : HediffWithComps
	{
		public override void PostAdd(DamageInfo? dinfo)
		{
			Messages.Message("FeltOrgasm".Translate(new object[] { this.pawn.LabelIndefinite() }).CapitalizeFirst(), pawn, MessageSound.Standard);
		}
	}

	internal class Hediff_TransportCums : HediffWithComps
	{
		public override void PostAdd(DamageInfo? dinfo)
		{
			if (pawn.gender == Gender.Female)
			{
				Messages.Message("CumsTransported".Translate(new object[] { this.pawn.LabelIndefinite() }).CapitalizeFirst(), pawn, MessageSound.Standard);
				Pawn cumSender = (from p in Find.WorldPawns.AllPawnsAlive where p.gender == Gender.Male select p).RandomElement<Pawn>();
				Log.Message("[RJW]" + this.GetType().ToString() + "PostAdd() - Sending " + cumSender.NameStringShort + "'s cum into " + pawn.NameStringShort + "'s vagina");
				xxx.impregnate(pawn, cumSender);
			}
			pawn.health.RemoveHediff(this);
		}
	}
}


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

namespace rjw
{
	internal class Hediff_MechImplants : HediffWithComps
	{
		public override bool TryMergeWith(Hediff other)
		{
			return false;
		}
	}

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


code:
using RimWorld;
using RimWorld.Planet;
using UnityEngine;
using Verse;

namespace rjw
{
	internal class Hediff_Parasite : Hediff_Pregnant
	{
		new public static void DoBirthSpawn(Pawn mother, Pawn father)
		{
			int num = (mother.RaceProps.litterSizeCurve == null) ? 1 : Mathf.RoundToInt(Rand.ByCurve(mother.RaceProps.litterSizeCurve, 300));
			if (num < 1)
			{
				num = 1;
			}
			PawnGenerationRequest request = new PawnGenerationRequest(father.kindDef, father.Faction, PawnGenerationContext.NonPlayer, -1, false, true, false, false, true, false, 1f, false, true, true, false, false, null, null, null, null, null, null);
			Pawn pawn = null;
			for (int i = 0; i < num; i++)
			{
				pawn = PawnGenerator.GeneratePawn(request);
				if (PawnUtility.TrySpawnHatchedOrBornPawn(pawn, mother))
				{
					if (pawn.playerSettings != null && mother.playerSettings != null)
					{
						pawn.playerSettings.AreaRestriction = father.playerSettings.AreaRestriction;
					}
					if (pawn.RaceProps.IsFlesh)
					{
						pawn.relations.AddDirectRelation(PawnRelationDefOf.Parent, mother);
						if (father != null)
						{
							pawn.relations.AddDirectRelation(PawnRelationDefOf.Parent, father);
						}
					}
				}
				else
				{
					Find.WorldPawns.PassToWorld(pawn, PawnDiscardDecideMode.Discard);
				}
			}
			if (mother.Spawned)
			{
				FilthMaker.MakeFilth(mother.Position, mother.Map, ThingDefOf.FilthAmnioticFluid, mother.LabelIndefinite(), 5);
				if (mother.caller != null)
				{
					mother.caller.DoCall();
				}
				if (pawn.caller != null)
				{
					pawn.caller.DoCall();
				}
			}
		}
	}
}


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

namespace rjw
{
	public static class AgeStage
	{
		public const int Baby = 0;
		public const int Toddler = 1;
		public const int Child = 2;
		public const int Teenager = 3;
		public const int Adult = 4;
	}

	public class Hediff_SimpleBaby : HediffWithComps
	{
		// Keeps track of what stage the pawn has grown to
		private int grown_to = 0;

		//Properties
		public int Grown_To
		{
			get
			{
				return grown_to;
			}
		}

		public override string DebugString()
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append(base.DebugString());
			return stringBuilder.ToString();
		}

		public override void PostMake()
		{
			Severity = Math.Max(0, Severity);
			if (grown_to == 0 && !pawn.health.hediffSet.HasHediff(HediffDef.Named("RJW_NoManipulationFlag")))
			{
				pawn.health.AddHediff(HediffDef.Named("RJW_NoManipulationFlag"), null, null);
			}
			base.PostMake();
		}

		public override void ExposeData()
		{
			//Scribe_Values.LookValue<int> (ref grown_to, "grown_to", 0);
			Scribe_Values.Look<int>(ref grown_to, "grown_to", 0);

			base.ExposeData();
		}

		internal void GrowUpTo(int stage)
		{
			GrowUpTo(stage, true);
		}

		internal void GrowUpTo(int stage, bool generated)
		{
			grown_to = stage;

			// Update the Colonist Bar
			PortraitsCache.SetDirty(pawn);
			pawn.Drawer.renderer.graphics.ResolveAllGraphics();

			// At the toddler stage. Now we can move and talk.
			if (stage == 1)
			{
				Severity = Math.Max(0.5f, Severity);
				//pawn.needs.food.
			}
			// Re-enable skills that were locked out from toddlers
			if (stage == 2)
			{
				if (!generated)
				{
					if (xxx.RimWorldChildrenIsActive)
					{
						pawn.story.childhood = BackstoryDatabase.allBackstories["CustomBackstory_Rimchild"];
					}
					// Remove the hidden hediff stopping pawns from manipulating
				}
				if (pawn.health.hediffSet.HasHediff(HediffDef.Named("RJW_NoManipulationFlag")))
				{
					pawn.health.hediffSet.hediffs.Remove(pawn.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("RJW_NoManipulationFlag")));
				}
				Severity = Math.Max(0.75f, Severity);
			}
			// The child has grown to a teenager so we no longer need this effect
			if (stage == 3)
			{
				if (!generated && pawn.story.childhood.Title == "Child")
				{
					if (xxx.RimWorldChildrenIsActive)
					{
						pawn.story.childhood = BackstoryDatabase.allBackstories["CustomBackstory_Rimchild"];
					}
				}

				// Gain traits from life experiences
				if (pawn.story.traits.allTraits.Count < 3)
				{
					List<Trait> life_traitpool = new List<Trait>();
					// Try get cannibalism
					if (pawn.needs.mood.thoughts.memories.Memories.Find(x => x.def == ThoughtDefOf.AteHumanlikeMeatAsIngredient) != null)
					{
						life_traitpool.Add(new Trait(TraitDefOf.Cannibal, 0, false));
					}
					// Try to get bloodlust
					if (pawn.records.GetValue(RecordDefOf.KillsHumanlikes) > 0 || pawn.records.GetValue(RecordDefOf.AnimalsSlaughtered) >= 2)
					{
						life_traitpool.Add(new Trait(TraitDefOf.Bloodlust, 0, false));
					}
					// Try to get shooting accuracy
					if (pawn.records.GetValue(RecordDefOf.ShotsFired) > 100)
					{
						life_traitpool.Add(new Trait(TraitDef.Named("ShootingAccuracy"), 1, false));
					}
					else if (pawn.records.GetValue(RecordDefOf.ShotsFired) > 100 && (int)pawn.records.GetValue(RecordDefOf.PawnsDowned) == 0)
					{
						life_traitpool.Add(new Trait(TraitDef.Named("ShootingAccuracy"), -1, false));
					}
					// Try to get brawler
					else if (pawn.records.GetValue(RecordDefOf.ShotsFired) < 15 && pawn.records.GetValue(RecordDefOf.PawnsDowned) > 1)
					{
						life_traitpool.Add(new Trait(TraitDefOf.Brawler, 0, false));
					}
					// Try to get Green Thumb
					if (pawn.records.GetValue(RecordDefOf.PlantsSown) > 100)
					{
						life_traitpool.Add(new Trait(TraitDefOf.GreenThumb, 0, false));
					}
					// Try to get necrophiliac
					if (pawn.records.GetValue(RecordDefOf.CorpsesBuried) > 50)
					{
						life_traitpool.Add(new Trait(xxx.necrophiliac, 0, false));
					}
					// Try to get nymphomaniac
					if (pawn.records.GetValue(RecordDefOf.BodiesStripped) > 50)
					{
						life_traitpool.Add(new Trait(xxx.nymphomaniac, 0, false));
					}
					// Try to get rapist
					if (pawn.records.GetValue(RecordDefOf.TimeAsPrisoner) > 300)
					{
						life_traitpool.Add(new Trait(xxx.rapist, 0, false));
					}
					// Try to get Dislikes Men/Women
					int male_rivals = 0;
					int female_rivals = 0;
					foreach (Pawn colinist in Find.AnyPlayerHomeMap.mapPawns.AllPawnsSpawned)
					{
						if (pawn.relations.OpinionOf(colinist) <= -20)
						{
							if (colinist.gender == Gender.Male)
								male_rivals++;
							else
								female_rivals++;
						}
					}
					// Find which gender we hate
					if (male_rivals > 3 || female_rivals > 3)
					{
						if (male_rivals > female_rivals)
							life_traitpool.Add(new Trait(TraitDefOf.DislikesMen, 0, false));
						else if (female_rivals > male_rivals)
							life_traitpool.Add(new Trait(TraitDefOf.DislikesWomen, 0, false));
					}
					// Pyromaniac never put out any fires. Seems kinda stupid
					/*if ((int)pawn.records.GetValue (RecordDefOf.FiresExtinguished) == 0) {
						life_traitpool.Add (new Trait (TraitDefOf.Pyromaniac, 0, false));
					}*/
					// Neurotic
					if (pawn.records.GetValue(RecordDefOf.TimesInMentalState) > 6)
					{
						life_traitpool.Add(new Trait(TraitDef.Named("Neurotic"), 2, false));
					}
					else if (pawn.records.GetValue(RecordDefOf.TimesInMentalState) > 3)
					{
						life_traitpool.Add(new Trait(TraitDef.Named("Neurotic"), 1, false));
					}

					// People(male or female) can turn gay during puberty
					if (Rand.Value <= 0.03f && pawn.story.traits.allTraits.Count <= 3)
					{
						pawn.story.traits.GainTrait(new Trait(TraitDefOf.Gay, 0, true));
					}

					// Now let's try to add some life experience traits
					if (life_traitpool.Count > 0)
					{
						int i = 3;
						while (pawn.story.traits.allTraits.Count < 3 && i > 0)
						{
							Trait newtrait = life_traitpool.RandomElement();
							if (pawn.story.traits.HasTrait(newtrait.def) == false)
								pawn.story.traits.GainTrait(newtrait);
							i--;
						}
					}
				}

				pawn.health.RemoveHediff(this);
			}
		}

		public void TickRare()
		{
			//--Log.Message("[RJW]Hediff_SimpleBaby::TickRare is called");
			if (pawn.ageTracker.CurLifeStageIndex > Grown_To)
			{
				GrowUpTo(Grown_To + 1, false);
			}
			// Update the graphics set
			if (pawn.ageTracker.CurLifeStageIndex == AgeStage.Toddler)
				pawn.Drawer.renderer.graphics.ResolveAllGraphics();
			if (xxx.RimWorldChildrenIsActive)
			{
				if (pawn.health.hediffSet.HasHediff(HediffDef.Named("RJW_NoManipulationFlag")))
				{
					pawn.health.hediffSet.hediffs.Remove(pawn.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("RJW_NoManipulationFlag")));
					pawn.health.AddHediff(HediffDef.Named("NoManipulationFlag"), null, null);
				}
				if (pawn.health.hediffSet.HasHediff(HediffDef.Named("RJW_BabyState")))
				{
					pawn.health.hediffSet.hediffs.Remove(pawn.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("BabyState")));
					pawn.health.AddHediff(HediffDef.Named("BabyState"), null, null);
				}
				if (pawn.ageTracker.CurLifeStageIndex <= 1)
				{   //The UnhappBaby feature is not included in RJW, but will
					// Check if the baby is hungry, and if so, add the whiny baby hediff
					if (pawn.needs.food != null && (pawn.needs.food.CurLevelPercentage < pawn.needs.food.PercentageThreshHungry ||
						pawn.needs.joy.CurLevelPercentage < 0.1f) &&
						!pawn.health.hediffSet.HasHediff(HediffDef.Named("UnhappyBaby"))
					)
					{
						//--Log.Message("Adding unhappy baby hediff");
						pawn.health.AddHediff(HediffDef.Named("UnhappyBaby"), null, null);
					}
				}
			}
		}

		public override void PostTick()
		{
			/*
            if (xxx.RimWorldChildrenIsActive)
            {
                if (pawn.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("RJW_BabyState"))!=null)
                {
                    pawn.health.RemoveHediff(this);
                }
                return;
            }
            */
			//This void call every frame. should not logmes no reason
			//Log.Message("[RJW]Hediff_SimpleBaby::PostTick is called");
			base.PostTick();
			if (pawn.Spawned)
			{
				if (pawn.IsHashIntervalTick(120))
				{
					TickRare();
				}
			}
		}

		public override bool Visible
		{
			get { return false; }
		}
	}
}


code:
using RimWorld;
using Verse;

namespace rjw
{
	public class IncidentWorker_NymphJoins : IncidentWorker
	{
		protected override bool CanFireNowSub(IIncidentTarget target)
		{
			if (Mod_Settings.nymphos)
			{
				var m = (Map)target;
				var colonist_count = 0;
				var nymph_count = 0;
				foreach (var p in m.mapPawns.FreeColonists)
				{
					++colonist_count;
					if (xxx.is_nympho(p))
						++nymph_count;
				}
				var nymph_fraction = (float)nymph_count / (float)colonist_count;
				return (colonist_count >= 1) && (nymph_fraction < xxx.config.max_nymph_fraction);
			}
			else
				return false;
		}

		public override bool TryExecute(IncidentParms parms)
		{
			//Log.Message("IncidentWorker_NymphJoins::TryExecute() called");

			if (!Mod_Settings.nymphos)
			{
				return false;
			}

			Map m = (Map)parms.target;

			if (m == null)
			{
				//--Log.Message("IncidentWorker_NymphJoins::TryExecute() - map is null, abort!");
				return false;
			}
			else
			{
				//--Log.Message("IncidentWorker_NymphJoins::TryExecute() - map is ok");
			}

			IntVec3 loc;
			/*This could be an alternative
            if (!RCellFinder.TryFindRandomPawnEntryCell(out loc, m, CellFinder.EdgeRoadChance_Friendly + 0.2f, null))
            {
                return false;
            }
            */
			if (!CellFinder.TryFindRandomEdgeCellWith(m.reachability.CanReachColony, m, 1.0f, out loc)) // TODO check this ROADCHANCE
				return false;

			var p = nymph_generator.spawn_new(loc, ref m);

			Find.LetterStack.ReceiveLetter("Nymph Joins", "A wandering nymph has decided to join your colony.", LetterDefOf.Good, p);

			return true;
		}
	}
}


code:
using RimWorld;
using Verse;

namespace rjw
{
	public class IncidentWorker_Sexualize : IncidentWorker
	{
		public override bool TryExecute(IncidentParms parms)
		{
			Genital_Helper.sexualize_everyone();
			Find.LetterStack.ReceiveLetter("Sexualization Complete", "All pawns have been sexualized.", LetterDefOf.Good, null);
			return true;
		}
	}
}


code:
using System;
using RimWorld;
using Verse;

namespace rjw
{
	public class IncidentWorker_TestInc : IncidentWorker
	{
		public static void list_backstories()
		{
			foreach (var bs in BackstoryDatabase.allBackstories.Values)
				Log.Message("Backstory \"" + bs.Title + "\" has identifier \"" + bs.identifier + "\"");
		}

		public static void inject_designator()
		{
			var des = new Designator_ComfortPrisoner();
			Find.ReverseDesignatorDatabase.AllDesignators.Add(des);
		}

		public static void spawn_nymphs(Map m)
		{
			IntVec3 loc;
			if (!CellFinder.TryFindRandomEdgeCellWith(m.reachability.CanReachColony, m, 1.0f, out loc)) // TODO fix ROADCHANCE
				return;

			for (int i = 1; i <= 10; ++i)
				nymph_generator.spawn_new(loc, ref m);
			Find.LetterStack.ReceiveLetter("Nymphs!", "A whole group of nymphs has wandered into your colony.", LetterDefOf.BadNonUrgent, null);
		}

		// Applies permanent damage to a randomly chosen colonist, to test that this works
		public static void damage_virally(Map m)
		{
			var vir_dam = DefDatabase<DamageDef>.GetNamed("ViralDamage");
			var p = m.mapPawns.FreeColonists.RandomElement();
			var lun = BodyDefOf.Human.AllParts.Find((BodyPartRecord bpr) => String.Equals(bpr.def.defName, "LeftLung"));
			var dam_def = HealthUtility.GetHediffDefFromDamage(vir_dam, p, lun);
			var inj = (Hediff_Injury)HediffMaker.MakeHediff(dam_def, p, null);
			inj.Severity = 2.0f;
			inj.TryGetComp<HediffComp_GetsOld>().IsOld = true;
			p.health.AddHediff(inj, lun, null);
		}

		// Gives all colonists on the map a severe syphilis or HIV infection
		public static void infect_the_colonists(Map m)
		{
			foreach (var p in m.mapPawns.FreeColonists)
			{
				if (Rand.Value < 0.50f)
					std.infect(p, std.syphilis);
				// var std_hed_def = (Rand.Value < 0.50f) ? std.syphilis.hediff_def : std.hiv.hediff_def;
				// p.health.AddHediff (std_hed_def);
				// p.health.hediffSet.GetFirstHediffOfDef (std_hed_def).Severity = Rand.Range (0.50f, 0.90f);
			}
		}

		// Reduces the sex need of the selected pawn
		public static void reduce_sex_need_on_select(Map m)
		{
			Pawn pawn = Find.Selector.SingleSelectedThing as Pawn;
			if (pawn != null)
			{
				if (pawn.needs.TryGetNeed<Need_Sex>() != null)
				{
					//--Log.Message("[RJW]TestInc::reduce_sex_need_on_select is called");
					pawn.needs.TryGetNeed<Need_Sex>().CurLevel -= 0.5f;
				}
			}
		}

		public override bool TryExecute(IncidentParms parms)
		{
			var m = (Map)parms.target;

			// list_backstories ();
			// inject_designator ();
			// spawn_nymphs (m);
			// damage_virally (m);
			//infect_the_colonists(m);
			reduce_sex_need_on_select(m);

			return true;
		}
	}
}


code:
using RimWorld;
using UnityEngine;
using Verse;

namespace rjw
{
	public class IncidentWorker_TestInc2 : IncidentWorker
	{
		// Testing the mechanism of some build-in functions
		public static void test_funcion()
		{
			float a = Mathf.InverseLerp(0, 2, 3); //gives 1
			float b = Mathf.InverseLerp(0.2f, 2, 0.1f); //gives 0
			float c = Mathf.InverseLerp(2f, 1, 2.5f); //gives 0
													  //--Log.Message("[RJW]TestInc2::test_function is called - value a is " + a);
													  //--Log.Message("[RJW]TestInc2::test_function is called - value b is " + b);
													  //--Log.Message("[RJW]TestInc2::test_function is called - value c is " + c);
		}

		// Gives the wanted information of the selected thing
		public static void info_on_select(Map m)
		{
			Pawn p = Find.Selector.SingleSelectedThing as Pawn;
			if (p != null)
			{
				//--Log.Message("[RJW]TestInc2::info_on_select is called");
				foreach (var q in m.mapPawns.AllPawns)
				{
					xxx.would_fuck(p, q, true);
				}
			}
		}

		public override bool TryExecute(IncidentParms parms)
		{
			var m = (Map)parms.target;

			info_on_select(m);

			return true;
		}
	}
}


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

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

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

		private JobDriver_RapeEnemy _tmpInstance;

		public virtual bool CanUseThisJobForPawn(Pawn rapist)
		{
			return intance.CanUseThisJobForPawn(rapist) || TargetDefNames.Contains(rapist.def.defName);
		}

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


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

namespace rjw
{
	public class JobDriver_Beastiality : JobDriver
	{
		private int duration;

		private int ticks_between_hearts;

		private int ticks_between_hits = 50;

		private int ticks_between_thrusts;

		protected TargetIndex iprisoner = TargetIndex.A;

		private bool isAnalSex = false;

		//private List<Apparel> worn_apparel;  // Edited by nizhuan-jjr: No Dropping Clothes on attackers!

		// Same as in JobDriver_Lovin
		private static readonly SimpleCurve LovinIntervalHoursFromAgeCurve = new SimpleCurve
		{
			new CurvePoint(1f,  12f),
			new CurvePoint(16f, 6f),
			new CurvePoint(22f, 9f),
			new CurvePoint(30f, 12f),
			new CurvePoint(50f, 18f),
			new CurvePoint(75f, 24f)
		};

		protected Pawn animal
		{
			get
			{
				return (Pawn)(CurJob.GetTarget(iprisoner));
			}
		}

		protected override IEnumerable<Toil> MakeNewToils()
		{
			//Log.Message("[RJW] JobDriver_Beastiality::MakeNewToils() called");
			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;
			bool pawnHasPenis = Genital_Helper.has_penis(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);

			//this.FailOnDespawnedNullOrForbidden (iprisoner);
			//this.FailOn (() => (!Prisoner.health.capacities.CanBeAwake) || (!comfort_prisoners.is_designated (Prisoner)));
			this.FailOn(() => !pawn.CanReserve(animal, 1, 0));  // Fail if someone else reserves the prisoner before the pawn arrives
			yield return Toils_Reserve.Reserve(iprisoner, 1, 0);
			//Log.Message("[RJW] JobDriver_Beastiality::MakeNewToils() - moving towards animal");
			yield return Toils_Goto.GotoThing(iprisoner, PathEndMode.OnCell);

			var rape = new Toil();
			rape.initAction = delegate
			{
				//Log.Message("[RJW] JobDriver_Beastiality::MakeNewToils() - reserving animal");
				pawn.Reserve(animal, 1, 0); // animal rapin seems like a solitary activity
				if (!pawnHasPenis)
					animal.Drawer.rotator.Face(pawn.DrawPos);

				//--Log.Message("[RJW] JobDriver_Beastiality::MakeNewToils() - Setting animal job driver");
				var dri = animal.jobs.curDriver as JobDriver_GettinRaped;
				if (dri == null)
				{
					var gettin_raped = new Job(xxx.gettin_raped);
					animal.jobs.StartJob(gettin_raped, JobCondition.InterruptForced, null, true, true, null);
					(animal.jobs.curDriver as JobDriver_GettinRaped).increase_time(duration);
				}
				else
				{
					dri.rapist_count += 1;
					dri.increase_time(duration);
				}
				// Try to take off the attacker's clothing
				//Log.Message("[RJW] JobDriver_Beastiality::MakeNewToils() - stripping necro lover");
				/* Edited by nizhuan-jjr: No Dropping Clothes on attackes!
                        worn_apparel = pawn.apparel.WornApparel.ListFullCopy<Apparel>();
                        while (pawn.apparel != null && pawn.apparel.WornApparelCount > 0) {
                            Apparel apparel = pawn.apparel.WornApparel.RandomElement<Apparel>();
                            pawn.apparel.Remove(apparel);
                        }
                */
			};
			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);
				/*
				if (pawn.IsHashIntervalTick (ticks_between_hits))
					roll_to_hit (pawn, animal);
                    */
			};
			rape.AddFinishAction(delegate
			{
				//--Log.Message("[RJW] JobDriver_Beastiality::MakeNewToils() - finished violating");
				//// Trying to add some interactions and social logs
				xxx.processAnalSex(pawn, animal, ref isAnalSex, pawnHasPenis);

				if ((animal.jobs != null) &&
					(animal.jobs.curDriver != null) &&
					(animal.jobs.curDriver as JobDriver_GettinRaped != null))
					(animal.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_Beastiality::MakeNewToils() - creating aftersex toil");
					xxx.aftersex(pawn, animal, false, false, isAnalSex);
					pawn.mindState.canLovinTick = Find.TickManager.TicksGame + xxx.generate_min_ticks_to_next_lovin(pawn);
					if (!animal.Dead)
					{
						animal.mindState.canLovinTick = Find.TickManager.TicksGame + xxx.generate_min_ticks_to_next_lovin(animal);
					}

					//Log.Message("[RJW] JobDriver_Beastiality::MakeNewToils() - putting clothes back on");
					/* Edited by nizhuan-jjr: No Dropping Clothes on attackers!
                            if (pawn.apparel != null) {
                                foreach (Apparel apparel in worn_apparel) {
                                    pawn.apparel.Wear(apparel);//  WornApparel.Add(apparel);
                                }
                            }
                    */
				},
				defaultCompleteMode = ToilCompleteMode.Instant
			};
		}
	}
}


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

namespace rjw
{
	public class JobDriver_ComfortPrisonerRapin : JobDriver
	{
		private int duration;

		private int ticks_between_hearts;

		private int ticks_between_hits = 50;

		private int ticks_between_thrusts;

		protected TargetIndex iprisoner = TargetIndex.A;

		private bool isAnalSex = false;

		//private List<Apparel> worn_apparel;// Edited by nizhuan-jjr:No Dropping Clothes on Attackers!

		// Same as in JobDriver_Lovin
		private static readonly SimpleCurve LovinIntervalHoursFromAgeCurve = new SimpleCurve
		{
			new CurvePoint(1f,  12f),
			new CurvePoint(16f, 6f),
			new CurvePoint(22f, 9f),
			new CurvePoint(30f, 12f),
			new CurvePoint(50f, 18f),
			new CurvePoint(75f, 24f)
		};

		protected Pawn Prisoner
		{
			get
			{
				return (Pawn)(CurJob.GetTarget(iprisoner));
			}
		}

		public static void roll_to_hit(Pawn rapist, Pawn p)
		{
			if (!Mod_Settings.prisoner_beating)
			{
				return;
			}

			float rand_value = Rand.Value;
			float victim_pain = p.health.hediffSet.PainTotal;
			// bloodlust makes the aggressor more likely to hit the prisoner
			float beating_chance = xxx.config.base_chance_to_hit_prisoner * (xxx.is_bloodlust(rapist) ? 1.25f : 1.0f);
			// psychopath makes the aggressor more likely to hit the prisoner past the significant_pain_threshold
			float beating_threshold = xxx.is_psychopath(rapist) ? xxx.config.extreme_pain_threshold : xxx.config.significant_pain_threshold;

			//Log.Message("roll_to_hit:  rand = " + rand_value + ", beating_chance = " + beating_chance + ", victim_pain = " + victim_pain + ", beating_threshold = " + beating_threshold);
			if ((victim_pain < beating_threshold && rand_value < beating_chance) || (rand_value < (beating_chance / 2)))
			{
				//Log.Message("   done told her twice already...");
				if (InteractionUtility.TryGetRandomVerbForSocialFight(rapist, out Verb v))
				{
					rapist.meleeVerbs.TryMeleeAttack(p, v);
				}
			}

			/*
            //if (p.health.hediffSet.PainTotal < xxx.config.significant_pain_threshold)
			if ((Rand.Value < 0.50f) &&
				((Rand.Value < 0.33f) || (p.health.hediffSet.PainTotal < xxx.config.significant_pain_threshold) ||
			     (xxx.is_bloodlust (rapist) || xxx.is_psychopath (rapist)))) {
				Verb v;
				if (InteractionUtility.TryGetRandomVerbForSocialFight (rapist, out v))
					rapist.meleeVerbs.TryMeleeAttack (p, v);
			}
            */
		}

		protected override IEnumerable<Toil> MakeNewToils()
		{
			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);

			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(() => (!Prisoner.health.capacities.CanBeAwake) || (!comfort_prisoners.is_designated(Prisoner)));
			this.FailOn(() => !pawn.CanReserve(Prisoner, comfort_prisoners.max_rapists_per_prisoner, 0)); // Fail if someone else reserves the prisoner before the pawn arrives
			yield return Toils_Goto.GotoThing(iprisoner, PathEndMode.OnCell);

			var rape = new Toil();
			rape.initAction = delegate
			{
				//Log.Message("JobDriver_ComfortPrisonerRapin::MakeNewToils() - reserving prisoner");
				pawn.Reserve(Prisoner, comfort_prisoners.max_rapists_per_prisoner, 0);
				if (!pawnHasPenis)
					Prisoner.Drawer.rotator.Face(pawn.DrawPos);

				//Log.Message("JobDriver_ComfortPrisonerRapin::MakeNewToils() - Setting victim job driver");
				var dri = Prisoner.jobs.curDriver as JobDriver_GettinRaped;
				if (dri == null)
				{
					var gettin_raped = new Job(xxx.gettin_raped);
					Prisoner.jobs.StartJob(gettin_raped, JobCondition.InterruptForced, null, false, true, null);
					(Prisoner.jobs.curDriver as JobDriver_GettinRaped).increase_time(duration);
				}
				else
				{
					dri.rapist_count += 1;
					dri.increase_time(duration);
				}

				//Log.Message("JobDriver_ComfortPrisonerRapin::MakeNewToils() - Removing victim's clothing");
				// Try to take off the attacker's clothing and add to inventory
				/* Edited by nizhuan-jjr: No Dropping clothes on attackers!
                        if (pawn.apparel != null) {
                            worn_apparel = pawn.apparel.WornApparel.ListFullCopy<Apparel>();
                            while (pawn.apparel != null && pawn.apparel.WornApparelCount > 0) {
                                Apparel apparel = pawn.apparel.WornApparel.RandomElement<Apparel>();
                                pawn.apparel.Remove(apparel);
                                if (pawn.inventory != null && pawn.inventory.innerContainer != null) {
                                    pawn.inventory.innerContainer.TryAdd(apparel);
                                }
                            }
                        }
                */
			};
			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, Prisoner);
				if (pawn.IsHashIntervalTick(ticks_between_hits))
					roll_to_hit(pawn, Prisoner);
			};
			rape.AddFinishAction(delegate
			{
				//// Trying to add some interactions and social logs
				xxx.processAnalSex(pawn, Prisoner, ref isAnalSex, pawnHasPenis);
				//Log.Message("JobDriver_ComfortPrisonerRapin::MakeNewToils() - Clearing victim job");
				if ((Prisoner.jobs != null) &&
				(Prisoner.jobs.curDriver != null) &&
				(Prisoner.jobs.curDriver as JobDriver_GettinRaped != null))
				{
					(Prisoner.jobs.curDriver as JobDriver_GettinRaped).rapist_count -= 1;
					xxx.processBrokenBody(Prisoner);
					xxx.ExtraSatisfyForBrokenCP(Prisoner);
				}
			});
			rape.defaultCompleteMode = ToilCompleteMode.Delay;
			rape.defaultDuration = duration;
			yield return rape;

			yield return new Toil
			{
				initAction = delegate
				{
					//Log.Message("JobDriver_ComfortPrisonerRapin::MakeNewToils() - Calling aftersex");
					xxx.aftersex(pawn, Prisoner, true, isAnalSex: isAnalSex);
					pawn.mindState.canLovinTick = Find.TickManager.TicksGame + xxx.generate_min_ticks_to_next_lovin(pawn);
					if (!Prisoner.Dead)
					{
						//xxx.aftersex (Prisoner, pawn, true);
						Prisoner.mindState.canLovinTick = Find.TickManager.TicksGame + xxx.generate_min_ticks_to_next_lovin(Prisoner);
					}
				},
				defaultCompleteMode = ToilCompleteMode.Instant
			};
		}
	}
}


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

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

		private int ticks_left;

		private TargetIndex ibed = TargetIndex.A;

		private static readonly SimpleCurve fap_interval_from_age = new SimpleCurve {
			new CurvePoint (16f, 0.75f),
			new CurvePoint (22f, 0.75f),
			new CurvePoint (30f, 1.50f),
			new CurvePoint (50f, 3.00f),
			new CurvePoint (75f, 5.00f)
		};

		private Building_Bed Bed
		{
			get
			{
				return (Building_Bed)((Thing)base.CurJob.GetTarget(ibed));
			}
		}

		protected override IEnumerable<Toil> MakeNewToils()
		{
			ticks_left = (int)(2500.0f * Rand.Range(0.20f, 0.70f));

			this.FailOnDespawnedOrNull(ibed);
			this.KeepLyingDown(ibed);
			yield return Toils_Bed.ClaimBedIfNonMedical(ibed, TargetIndex.None);
			yield return Toils_Bed.GotoBed(ibed);

			Toil do_fappin = Toils_LayDown.LayDown(ibed, true, false, false, false);
			do_fappin.AddPreTickAction(delegate
			{
				--this.ticks_left;
				if (this.ticks_left <= 0)
					this.ReadyForNextToil();
				else if (pawn.IsHashIntervalTick(ticks_between_hearts))
					MoteMaker.ThrowMetaIcon(pawn.Position, pawn.Map, ThingDefOf.Mote_Heart);
			});
			do_fappin.AddFinishAction(delegate
			{
				pawn.mindState.canLovinTick = Find.TickManager.TicksGame + generate_min_ticks_to_next_fappin(pawn);
				xxx.satisfy(pawn, null);
			});
			do_fappin.socialMode = RandomSocialMode.Off;
			yield return do_fappin;
		}

		private int generate_min_ticks_to_next_fappin(Pawn p)
		{
			if (!DebugSettings.alwaysDoLovin)
			{
				var interval = fap_interval_from_age.Evaluate(pawn.ageTracker.AgeBiologicalYearsFloat);
				var rinterval = Math.Max(0.5f, Rand.Gaussian(interval, 0.3f));
				return (int)((xxx.is_nympho(p) ? 0.5f : 1.0f) * rinterval * 2500.0f);
			}
			else
				return 50;
		}
	}
}


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;

		protected Pawn Partner
		{
			get
			{
				return (Pawn)(CurJob.GetTarget(ipartner));
			}
		}

		protected Building_Bed Bed
		{
			get
			{
				return (Building_Bed)(CurJob.GetTarget(ibed));
			}
		}

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

			if (Partner.CurJob.def == xxx.nymph_rapin)
			{
				//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.def != xxx.nymph_rapin));
				//get_loved.defaultCompleteMode = ToilCompleteMode.Never;
				//get_loved.initAction = delegate {
				//    Log.Message("[RJW]JobDriver_GettinLoved::MakeNewToils - nymph section is called");
				//};
				//get_loved.AddPreTickAction(delegate {
				//    if (pawn.IsHashIntervalTick(100))
				//        MoteMaker.ThrowMetaIcon(pawn.Position, pawn.Map, ThingDefOf.Mote_Heart);
				//});
				//get_loved.socialMode = RandomSocialMode.Off;
				//yield return get_loved;

				this.FailOnDespawnedOrNull(ipartner);
				this.FailOn(() => !Partner.health.capacities.CanBeAwake);
				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.nymph_rapin));
				get_loved.defaultCompleteMode = ToilCompleteMode.Never;
				get_loved.AddPreTickAction(delegate
				{
					if (pawn.IsHashIntervalTick(100))
						MoteMaker.ThrowMetaIcon(pawn.Position, pawn.Map, ThingDefOf.Mote_Heart);
				});
				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(100))
					{
						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;

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 bool was_laying_down;

		internal RJW_DummyContainer dummy_container;

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

		protected override IEnumerable<Toil> MakeNewToils()
		{
			ticks_between_hearts = Rand.RangeInclusive(70, 130);
			was_laying_down = (pawn.jobs.curDriver != null) && pawn.jobs.curDriver.layingDown != Verse.AI.LayingDownState.NotLaying;

			var get_raped = new Toil();
			get_raped.defaultCompleteMode = ToilCompleteMode.Never;
			get_raped.initAction = delegate
			{
				pawn.pather.StopDead();
				pawn.jobs.curDriver.layingDown = Verse.AI.LayingDownState.NotLaying;
				pawn.jobs.curDriver.asleep = false;
				pawn.mindState.awokeVoluntarily = false;
				// Added by nizhuan-jjr: human pawns get raped will put on clothes again.
				if (!xxx.is_animal(pawn))
				{
					dummy_container = new RJW_DummyContainer();
					foreach (Apparel apparel in pawn.apparel.GetDirectlyHeldThings().OfType<Apparel>().ToList<Apparel>())
					{
						if (!(apparel is rjw.bondage_gear))
						{
							pawn.apparel.GetDirectlyHeldThings().TryTransferToContainer(apparel, dummy_container.GetDirectlyHeldThings(), true);
							if ((pawn.outfits != null) && pawn.outfits.forcedHandler.IsForced(apparel))
							{
								dummy_container.forcedApparel.Add(apparel);
							}
						}
					}
				}
			};
			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
			{
				pawn.jobs.curDriver.layingDown = was_laying_down ? Verse.AI.LayingDownState.NotLaying : Verse.AI.LayingDownState.LayingInBed;

				// Added by nizhuan-jjr: human pawns get raped will put on clothes again.
				if (!xxx.is_animal(pawn))
				{
					if (dummy_container != null)
					{
						foreach (Apparel apparel in dummy_container.GetDirectlyHeldThings().OfType<Apparel>().ToList<Apparel>())
						{
							if (!(apparel is rjw.bondage_gear))
							{
								dummy_container.GetDirectlyHeldThings().TryTransferToContainer(apparel, pawn.apparel.GetDirectlyHeldThings(), true);
								if (dummy_container.forcedApparel.Contains(apparel) && (pawn.outfits != null))
								{
									pawn.outfits.forcedHandler.SetForced(apparel, true);
								}
							}
						}
					}
				}
			});
			get_raped.socialMode = RandomSocialMode.Off;
			yield return get_raped;

			/*
            var after_rape = new Toil();
            after_rape.defaultCompleteMode = ToilCompleteMode.Delay;
            after_rape.defaultDuration = 150;
            after_rape.socialMode = RandomSocialMode.Off;
            after_rape.AddFinishAction(delegate {
                pawn.jobs.curDriver.layingDown = was_laying_down ? Verse.AI.LayingDownState.NotLaying : Verse.AI.LayingDownState.LayingInBed;

                // Added by nizhuan-jjr: human pawns get raped will put on clothes again.
                if (!xxx.is_animal(pawn))
                {
                    if (dummy_container != null)
                    {
                        foreach (Apparel apparel in dummy_container.GetDirectlyHeldThings().OfType<Apparel>().ToList<Apparel>())
                        {
                            if (!(apparel is rjw.bondage_gear))
                            {
                                dummy_container.GetDirectlyHeldThings().TryTransferToContainer(apparel, pawn.apparel.GetDirectlyHeldThings(), true);
                                if (dummy_container.forcedApparel.Contains(apparel) && (pawn.outfits != null))
                                {
                                    pawn.outfits.forcedHandler.SetForced(apparel, true);
                                }
                            }
                        }
                    }
                }
            });
            yield return after_rape;
            */
		}
	}

	//create a dummy container to solve the problem of storage of taken-off apparels
	public class RJW_DummyContainer : IExposable, IThingHolder, ILoadReferenceable
	{
		public List<Apparel> forcedApparel;
		private ThingOwner innerContainer;
		private string UID;

		public RJW_DummyContainer()
		{
			forcedApparel = new List<Apparel>();
			innerContainer = new ThingOwner<Apparel>(this, false, LookMode.Deep);
			UID = "RJW_DUMMY_CONTAINER" + Find.TickManager.TicksGame;
		}

		public void GetChildHolders(List<IThingHolder> outChildren)
		{
			ThingOwnerUtility.AppendThingHoldersFromThings(outChildren, GetDirectlyHeldThings());
		}

		public ThingOwner GetDirectlyHeldThings()
		{
			return innerContainer;
		}

		public IThingHolder ParentHolder
		{
			get
			{
				return ParentHolder;
			}
		}

		public void ExposeData()
		{
			innerContainer.ExposeData();
			Scribe_Collections.Look<Apparel>(ref forcedApparel, "forcedApparel", LookMode.Reference, new object[0]);
			Scribe_Values.Look<String>(ref UID, "UID", "", false);
		}

		public string GetUniqueLoadID()
		{
			return UID;
		}
	}
}


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

namespace rjw
{
	public class JobDriver_InvitingVisitors : JobDriver
	{
		public bool successfulPass = true;

		private Building_WhoreBed TargetBed { get => (TargetThingB as Building_WhoreBed); }

		private TargetIndex TargetBedIndex = TargetIndex.B;

		private Pawn TargetPawn
		{
			get => (TargetThingA as Pawn);
		}

		private TargetIndex TargetPawnIndex = TargetIndex.A;

		private Pawn Whore
		{
			get => GetActor();
		}

		private bool DoesTargetPawnAcceptAdvance()
		{
			if (xxx.config.always_accept_whores)
				return true;
			if (PawnUtility.WillSoonHaveBasicNeed(TargetPawn))
			{
				return false;
			}
			if (PawnUtility.EnemiesAreNearby(TargetPawn, 9, false))
			{
				return false;
			}
			if (TargetPawn.jobs.curJob == null || (TargetPawn.jobs.curJob.def == JobDefOf.WaitWander || TargetPawn.jobs.curJob.def == JobDefOf.GotoWander || TargetPawn.jobs.curJob.def.joyKind != null))
			{
				//--Log.Message("[RJW]JobDriver_InvitingVisitors::DoesTargetPawnAcceptAdvance() is called");
				return (xxx.WillPawnTryHookup(TargetPawn) && xxx.IsHookupAppealing(TargetPawn, Whore) && xxx.CanAfford(TargetPawn, Whore, TargetBed.priceOfWhore));
			}
			else return false;
		}

		protected override IEnumerable<Toil> MakeNewToils()
		{
			this.FailOnDespawnedOrNull(TargetPawnIndex);
			this.FailOnDespawnedNullOrForbidden(TargetBedIndex);
			this.FailOn(() => Whore is null || !xxx.CanUse(Whore, TargetBed));//|| !Whore.CanReserve(TargetPawn)
			yield return Toils_Goto.GotoThing(TargetPawnIndex, PathEndMode.Touch);

			Toil TryItOn = new Toil();
			TryItOn.AddFailCondition(() => !xxx.IsTargetPawnOkay(TargetPawn));
			TryItOn.defaultCompleteMode = ToilCompleteMode.Delay;
			TryItOn.initAction = delegate
			{
				//--Log.Message("[RJW]JobDriver_InvitingVisitors::MakeNewToils - TryItOn - initAction is called");
				Whore.jobs.curDriver.ticksLeftThisToil = 50;
				MoteMaker.ThrowMetaIcon(Whore.Position, Whore.Map, ThingDefOf.Mote_Heart);
			};
			yield return TryItOn;

			Toil AwaitResponse = new Toil();
			AwaitResponse.AddFailCondition(() => !successfulPass);
			AwaitResponse.defaultCompleteMode = ToilCompleteMode.Instant;
			AwaitResponse.initAction = delegate
			{
				List<RulePackDef> extraSentencePacks = new List<RulePackDef>();
				successfulPass = DoesTargetPawnAcceptAdvance();
				//--Log.Message("[RJW]JobDriver_InvitingVisitors::MakeNewToils - AwaitResponse - initAction is called");
				if (successfulPass)
				{
					MoteMaker.ThrowMetaIcon(TargetPawn.Position, TargetPawn.Map, ThingDefOf.Mote_Heart);
					if (xxx.RomanceDiversifiedIsActive)
					{
						extraSentencePacks.Add(RulePackDef.Named("HookupSucceeded"));
					}
					Messages.Message("RJW_VisitorAcceptWhore".Translate(new object[] { TargetPawn.NameStringShort, Whore.NameStringShort }), TargetPawn, MessageSound.Benefit);
				}
				else
				{
					MoteMaker.ThrowMetaIcon(TargetPawn.Position, TargetPawn.Map, ThingDefOf.Mote_IncapIcon);
					if (xxx.RomanceDiversifiedIsActive)
					{
						Whore.needs.mood.thoughts.memories.TryGainMemory(ThoughtDef.Named("RebuffedMyHookupAttempt"), TargetPawn);
						TargetPawn.needs.mood.thoughts.memories.TryGainMemory(ThoughtDef.Named("FailedHookupAttemptOnMe"), Whore);
						extraSentencePacks.Add(RulePackDef.Named("HookupFailed"));
					}
					//Disabled rejection notifications
					//Messages.Message("RJW_VisitorRejectWhore".Translate(new object[] { TargetPawn.NameStringShort, Whore.NameStringShort }), TargetPawn, MessageSound.Silent);
				}
				if (xxx.RomanceDiversifiedIsActive)
				{
					Find.PlayLog.Add(new PlayLogEntry_Interaction(DefDatabase<InteractionDef>.GetNamed("TriedHookupWith"), pawn, TargetPawn, extraSentencePacks));
				}
			};
			yield return AwaitResponse;

			Toil BothGoToBed = new Toil();
			BothGoToBed.AddFailCondition(() => !successfulPass || !xxx.CanUse(Whore, TargetBed));
			BothGoToBed.defaultCompleteMode = ToilCompleteMode.Instant;
			BothGoToBed.initAction = delegate
			{
				//--Log.Message("[RJW]JobDriver_InvitingVisitors::MakeNewToils - BothGoToBed - initAction is called0");
				if (successfulPass)
				{
					if (!xxx.CanUse(Whore, TargetBed))
					{
						//--Log.Message("[RJW]JobDriver_InvitingVisitors::MakeNewToils - BothGoToBed - cannot use the whore bed");
						return;
					}
					//--Log.Message("[RJW]JobDriver_InvitingVisitors::MakeNewToils - BothGoToBed - initAction is called1");
					JobTag? tag = null;
					Whore.jobs.jobQueue.EnqueueFirst(new Job(xxx.whore_is_serving_visitors, TargetPawn, TargetBed, TargetBed.SleepPosOfAssignedPawn(Whore)), tag);
					//TargetPawn.jobs.jobQueue.EnqueueFirst(new Job(DefDatabase<JobDef>.GetNamed("WhoreIsServingVisitors"), Whore, TargetBed, (TargetBed.MaxAssignedPawnsCount>1)?TargetBed.GetSleepingSlotPos(1): TargetBed.)), null);
					Whore.jobs.curDriver.EndJobWith(JobCondition.InterruptOptional);
					//TargetPawn.jobs.curDriver.EndJobWith(JobCondition.InterruptOptional);
				}
			};
			yield return BothGoToBed;
		}
	}
}


code:
/*
 *
 * This class is no longer needed. The changes are now just patched into JobDriver_Lovin.MakeNewToils

using System;
using System.Collections.Generic;

using Verse;
using Verse.AI;
using RimWorld;

namespace rjw {
	public class JobDriver_LovinPP : JobDriver_Lovin {
		private Pawn Partner
		{
			get
			{
				return (Pawn)((Thing)base.CurJob.GetTarget(TargetIndex.A));
			}
		}

		protected override IEnumerable<Toil> MakeNewToils ()
		{
			this.FailOn (() => (! xxx.can_fuck (pawn)));
			this.FailOn (() => (! xxx.can_fuck (Partner)));

			foreach (var t in base.MakeNewToils ())
				yield return t;

			// Apply the after-effects of sex. We have to do both pawns here because the partner
			// pawn's Lovin' job will fail and not run this code.
			yield return new Toil {
				initAction = delegate {
					xxx.aftersex (pawn, Partner);
					xxx.aftersex (Partner, pawn);
				},
				defaultCompleteMode = ToilCompleteMode.Instant
			};
		}
	}
}

*/


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

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

		private int ticks_left;

		private TargetIndex ipartner = TargetIndex.A;

		private TargetIndex ibed = TargetIndex.B;

		protected Pawn Partner
		{
			get
			{
				return (Pawn)(CurJob.GetTarget(ipartner));
			}
		}

		protected Building_Bed Bed
		{
			get
			{
				return (Building_Bed)(CurJob.GetTarget(ibed));
			}
		}

		protected override IEnumerable<Toil> MakeNewToils()
		{
			//--Log.Message("JobDriver_NymphJoinInBed::MakeNewToils() called");
			this.FailOnDespawnedOrNull(ipartner);
			this.FailOnDespawnedOrNull(ibed);
			this.FailOn(() => !Partner.health.capacities.CanBeAwake);
			this.FailOn(() => !xxx.is_laying_down_alone(Partner));
			yield return Toils_Reserve.Reserve(ipartner, comfort_prisoners.max_rapists_per_prisoner, 0);
			yield return Toils_Goto.GotoThing(ipartner, PathEndMode.OnCell);
			yield return new Toil
			{
				initAction = delegate
				{
					//--Log.Message("JobDriver_NymphJoinInBed::MakeNewToils() - setting initAction");
					ticks_left = (int)(2500.0f * Rand.Range(0.30f, 1.30f));
					var gettin_loved = new Job(xxx.gettin_loved, pawn, Bed);
					Partner.jobs.StartJob(gettin_loved, JobCondition.InterruptForced, null, false, true, null);
				},
				defaultCompleteMode = ToilCompleteMode.Instant
			};
			var do_lovin = new Toil();
			do_lovin.defaultCompleteMode = ToilCompleteMode.Never;
			do_lovin.FailOn(() => (Partner.CurJob == null) || (Partner.CurJob.def != xxx.gettin_loved));
			do_lovin.AddPreTickAction(delegate
			{
				--ticks_left;
				if (ticks_left <= 0)
					ReadyForNextToil();
				else if (pawn.IsHashIntervalTick(ticks_between_hearts))
					MoteMaker.ThrowMetaIcon(pawn.Position, pawn.Map, ThingDefOf.Mote_Heart);
			});
			do_lovin.socialMode = RandomSocialMode.Off;
			yield return do_lovin;
			yield return new Toil
			{
				initAction = delegate
				{
					//--Log.Message("JobDriver_NymphJoinInBed::MakeNewToils() - setting pawn.got_some_lovin memory in second initAction");
					var sex_mem = (Thought_Memory)ThoughtMaker.MakeThought(ThoughtDefOf.GotSomeLovin);
					var pawn_memories = pawn.needs.mood.thoughts.memories as MemoryThoughtHandler;
					if (pawn_memories != null)
					{
						pawn.needs.mood.thoughts.memories.TryGainMemory(sex_mem, Partner);
					}

					//--Log.Message("JobDriver_NymphJoinInBed::MakeNewToils() - setting Partner.got_some_lovin memory in second initAction");
					var sex_mem2 = (Thought_Memory)ThoughtMaker.MakeThought(ThoughtDefOf.GotSomeLovin); // Is this neccessary?
					if (Partner.needs != null && Partner.needs.mood != null && Partner.needs.mood.thoughts != null)
					{
						Partner.needs.mood.thoughts.memories.TryGainMemory(sex_mem2, pawn);
					}

					//--Log.Message("JobDriver_NymphJoinInBed::MakeNewToils() - calling aftersex in second initAction");
					xxx.aftersex(pawn, Partner);
					//--Log.Message("JobDriver_NymphJoinInBed::MakeNewToils() - calling aftersex again in second initAction");
					//xxx.aftersex (Partner, pawn);
					//--Log.Message("JobDriver_NymphJoinInBed::MakeNewToils() - setting mindstate in second initAction");
					pawn.mindState.canLovinTick = Find.TickManager.TicksGame + xxx.generate_min_ticks_to_next_lovin(pawn);
					//--Log.Message("JobDriver_NymphJoinInBed::MakeNewToils() - setting mindstate again in second initAction");
					Partner.mindState.canLovinTick = Find.TickManager.TicksGame + xxx.generate_min_ticks_to_next_lovin(Partner);
				},
				defaultCompleteMode = ToilCompleteMode.Instant
			};
		}
	}
}


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

namespace rjw
{
	public class JobDriver_RandomRape : JobDriver
	{
		private int duration;

		private int ticks_between_hearts;

		private int ticks_between_hits = 50;

		private int ticks_between_thrusts;

		protected TargetIndex iprisoner = TargetIndex.A;

		private bool isAnalSex = false;

		//private List<Apparel> worn_apparel;// Edited by nizhuan-jjr: No Dropping Clothes on attackers!

		// Same as in JobDriver_Lovin
		private static readonly SimpleCurve LovinIntervalHoursFromAgeCurve = new SimpleCurve
		{
			new CurvePoint(1f,  12f),
			new CurvePoint(16f, 6f),
			new CurvePoint(22f, 9f),
			new CurvePoint(30f, 12f),
			new CurvePoint(50f, 18f),
			new CurvePoint(75f, 24f)
		};

		protected Pawn Prisoner
		{
			get
			{
				return (Pawn)(CurJob.GetTarget(iprisoner));
			}
		}

		public static void roll_to_hit(Pawn rapist, Pawn p)
		{
			if (!Mod_Settings.prisoner_beating)
			{
				return;
			}

			float rand_value = Rand.Value;
			float victim_pain = p.health.hediffSet.PainTotal;
			// bloodlust makes the aggressor more likely to hit the prisoner
			float beating_chance = xxx.config.base_chance_to_hit_prisoner * (xxx.is_bloodlust(rapist) ? 1.25f : 1.0f);
			// psychopath makes the aggressor more likely to hit the prisoner past the significant_pain_threshold
			float beating_threshold = xxx.is_psychopath(rapist) ? xxx.config.extreme_pain_threshold : xxx.config.significant_pain_threshold;

			//Log.Message("roll_to_hit:  rand = " + rand_value + ", beating_chance = " + beating_chance + ", victim_pain = " + victim_pain + ", beating_threshold = " + beating_threshold);
			if ((victim_pain < beating_threshold && rand_value < beating_chance) || (rand_value < (beating_chance / 2)))
			{
				//Log.Message("   done told her twice already...");
				if (InteractionUtility.TryGetRandomVerbForSocialFight(rapist, out Verb v))
				{
					rapist.meleeVerbs.TryMeleeAttack(p, v);
				}
			}

			/*
            //if (p.health.hediffSet.PainTotal < xxx.config.significant_pain_threshold)
			if ((Rand.Value < 0.50f) &&
				((Rand.Value < 0.33f) || (p.health.hediffSet.PainTotal < xxx.config.significant_pain_threshold) ||
			     (xxx.is_bloodlust (rapist) || xxx.is_psychopath (rapist)))) {
				Verb v;
				if (InteractionUtility.TryGetRandomVerbForSocialFight (rapist, out v))
					rapist.meleeVerbs.TryMeleeAttack (p, v);
			}
            */
		}

		protected override IEnumerable<Toil> MakeNewToils()
		{
			//Log.Message("[RJW] JobDriver_RandomRape::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;
			bool pawnHasPenis = Genital_Helper.has_penis(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);

			this.FailOnDespawnedNullOrForbidden(iprisoner);
			this.FailOn(() => (!Prisoner.health.capacities.CanBeAwake)); // || (!comfort_prisoners.is_designated (Prisoner)));
			this.FailOn(() => !pawn.CanReserve(Prisoner, comfort_prisoners.max_rapists_per_prisoner, 0)); // Fail if someone else reserves the prisoner before the pawn arrives
			yield return Toils_Goto.GotoThing(iprisoner, PathEndMode.OnCell);

			var rape = new Toil();
			rape.initAction = delegate
			{
				pawn.Reserve(Prisoner, comfort_prisoners.max_rapists_per_prisoner, 0);
				if (!pawnHasPenis)
					Prisoner.Drawer.rotator.Face(pawn.DrawPos);
				var dri = Prisoner.jobs.curDriver as JobDriver_GettinRaped;
				if (dri == null)
				{
					var gettin_raped = new Job(xxx.gettin_raped);
					Prisoner.jobs.StartJob(gettin_raped, JobCondition.InterruptForced, null, false, true, null);
					(Prisoner.jobs.curDriver as JobDriver_GettinRaped).increase_time(duration);
				}
				else
				{
					dri.rapist_count += 1;
					dri.increase_time(duration);
				}

				// Try to take off the attacker's clothing
				/* Edited by nizhuan-jjr: No Dropping Clothes on attackers!
                        worn_apparel = pawn.apparel.WornApparel.ListFullCopy<Apparel>();
                        while (pawn.apparel != null && pawn.apparel.WornApparelCount > 0) {
                            Apparel apparel = pawn.apparel.WornApparel.RandomElement<Apparel>();
                            pawn.apparel.Remove(apparel);
                        }
                */
				//pawn.apparel.WornApparel.RemoveAll(null);

				//List<Apparel> worn = pawn.apparel.WornApparel;
				//while (pawn.apparel != null && pawn.apparel.WornApparelCount > 0) {
				//    Apparel apparel = pawn.apparel.WornApparel.RemoveAll(null);
				//    pawn.apparel.Remove(apparel);
				//}
			};
			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, Prisoner);
				if (pawn.IsHashIntervalTick(ticks_between_hits))
					roll_to_hit(pawn, Prisoner);
			};
			rape.AddFinishAction(delegate
			{
				//// Trying to add some interactions and social logs
				xxx.processAnalSex(pawn, Prisoner, ref isAnalSex, pawnHasPenis);

				if ((Prisoner.jobs != null) &&
					(Prisoner.jobs.curDriver != null) &&
					(Prisoner.jobs.curDriver as JobDriver_GettinRaped != null))
				{
					(Prisoner.jobs.curDriver as JobDriver_GettinRaped).rapist_count -= 1;
				}
			});
			rape.defaultCompleteMode = ToilCompleteMode.Delay;
			rape.defaultDuration = duration;
			yield return rape;

			yield return new Toil
			{
				initAction = delegate
				{
					xxx.aftersex(pawn, Prisoner, true, isAnalSex: isAnalSex);
					pawn.mindState.canLovinTick = Find.TickManager.TicksGame + xxx.generate_min_ticks_to_next_lovin(pawn);
					if (!Prisoner.Dead)
					{
						//xxx.aftersex(Prisoner, pawn, true); //Edited by nizhuan-jjr: Not sure why apply twice aftersex effect.
						Prisoner.mindState.canLovinTick = Find.TickManager.TicksGame + xxx.generate_min_ticks_to_next_lovin(Prisoner);
					}
					/* Edited by nizhuan-jjr: No Dropping Clothes on attackers!
                            if (pawn.apparel != null) {
                                foreach (Apparel apparel in worn_apparel) {
                                    pawn.apparel.Wear(apparel);//  WornApparel.Add(apparel);
                                }
                            }
                    */
				},
				defaultCompleteMode = ToilCompleteMode.Instant
			};
		}
	}
}


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

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

		protected int ticks_between_hearts;

		protected int ticks_between_hits = 50;

		protected int ticks_between_thrusts;

		protected TargetIndex iTarget = TargetIndex.A;

		protected bool isAnalSex = false;

		protected bool requierCanRape = true;

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

		public virtual void think_after_sex(Pawn pawn, Pawn part, bool violent = false, bool isCoreLovin = false)
		{
			xxx.think_after_sex(pawn, part, violent, isCoreLovin);
		}

		public virtual void roll_to_hit(Pawn rapist, Pawn p)
		{
			float rand_value = (1 - Rand.Value * p.health.capacities.GetLevel(PawnCapacityDefOf.Consciousness));
			float victim_pain = p.health.hediffSet.PainTotal;

			float beating_chance = xxx.config.base_chance_to_hit_prisoner * (xxx.is_bloodlust(rapist) ? 1.25f : 1.0f);
			float beating_threshold = xxx.is_psychopath(rapist) ? xxx.config.extreme_pain_threshold : xxx.config.significant_pain_threshold;

			if ((victim_pain < beating_threshold && rand_value < beating_chance) || (rand_value < (beating_chance / 2)))
			{
				if (InteractionUtility.TryGetRandomVerbForSocialFight(rapist, out Verb v))
				{
					rapist.meleeVerbs.TryMeleeAttack(p, v);
				}
			}
		}

		protected readonly SimpleCurve LovinIntervalHoursFromAgeCurve = new SimpleCurve
		{
			new CurvePoint(1f,  12f),
			new CurvePoint(16f, 6f),
			new CurvePoint(22f, 9f),
			new CurvePoint(30f, 12f),
			new CurvePoint(50f, 18f),
			new CurvePoint(75f, 24f)
		};

		public virtual void aftersex(Pawn pawn, Pawn part, bool violent = false, bool isCoreLovin = false, bool isAnalSex = false)
		{
			var pawn_name = (pawn != null) ? pawn.NameStringShort : "NULL";
			var part_name = (pawn != null) ? part.NameStringShort : "NULL";
			Log.Message("[RJW]" + this.GetType().ToString() + "::aftersex( " + pawn_name + ", " + part_name + " ) called");
			pawn.Drawer.rotator.Face(part.DrawPos);
			pawn.Drawer.rotator.FaceCell(part.Position);

			part.Drawer.rotator.Face(pawn.DrawPos);
			part.Drawer.rotator.FaceCell(pawn.Position);

			if (violent)
			{
				pawn.Drawer.Notify_MeleeAttackOn(part);
			}

			if (xxx.config.sounds_enabled)
			{
				SoundDef.Named("Cum").PlayOneShot(new TargetInfo(part.Position, pawn.Map, false));
			}

			bool pawnIsNotHuman = xxx.is_animal(pawn);
			bool partIsNotHuman = xxx.is_animal(part);
			Log.Message("[RJW]" + this.GetType().ToString() + "::aftersex( " + pawn_name + ", " + part_name + " ) - applying cum effect");
			if (xxx.config.cum_enabled)
			{
				int pawn_cum = pawnIsNotHuman ? 4 : Math.Min((int)(pawn.RaceProps.lifeExpectancy / pawn.ageTracker.AgeBiologicalYears), 2);
				int part_cum = partIsNotHuman ? 4 : Math.Min((int)(part.RaceProps.lifeExpectancy / part.ageTracker.AgeBiologicalYears), 2);
				if (pawn.gender == Gender.Female)
					pawn_cum /= 2;
				if (part.gender == Gender.Female)
					part_cum /= 2;
				FilthMaker.MakeFilth(pawn.PositionHeld, pawn.MapHeld, xxx.cum, pawn.LabelIndefinite(), pawn_cum);
				if (!isCoreLovin)
				{
					FilthMaker.MakeFilth(part.PositionHeld, part.MapHeld, xxx.cum, part.LabelIndefinite(), part_cum);
				}
			}

			Log.Message("[RJW]" + this.GetType().ToString() + "::aftersex( " + pawn_name + ", " + part_name + " ) - checking satisfaction");
			xxx.satisfy(pawn, part, violent, isCoreLovin);
			Log.Message("[RJW]" + this.GetType().ToString() + "::aftersex( " + pawn_name + ", " + part_name + " ) - checking thoughts");
			think_after_sex(pawn, part, violent, isCoreLovin);

			std.roll_to_catch(pawn, part);

			Impregnate(pawn, part, isAnalSex);
			Log.Message("[RJW]" + this.GetType().ToString() + "::aftersex( " + pawn_name + ", " + part_name + " ) - checking disease");
		}

		protected virtual void Impregnate(Pawn pawn, Pawn part, bool isAnalSex)
		{
			if (xxx.is_animal(pawn) || xxx.is_animal(part)) return;
			if (!isAnalSex)
			{
				Log.Message("[RJW]" + this.GetType().ToString() + "::aftersex( " + pawn.Name.ToStringShort + ", " + part.Name.ToStringShort + " ) - checking pregnancy");
				xxx.impregnate(pawn, part);
			}
		}

		protected Pawn Target
		{
			get
			{
				return (Pawn)(CurJob.GetTarget(iTarget));
			}
		}

		protected override IEnumerable<Toil> MakeNewToils()
		{
			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);

			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, comfort_prisoners.max_rapists_per_prisoner, 0)); // Fail if someone else reserves the Target before the pawn arrives
			yield return Toils_Goto.GotoThing(iTarget, PathEndMode.OnCell);

			var rape = new Toil();
			rape.initAction = delegate
			{
				pawn.Reserve(Target, comfort_prisoners.max_rapists_per_prisoner, 0);
				if (!pawnHasPenis)
					Target.Drawer.rotator.Face(pawn.DrawPos);
				var dri = Target.jobs.curDriver as JobDriver_GettinRaped;
				if (dri == null)
				{
					var gettin_raped = new Job(xxx.gettin_raped);
					Target.jobs.StartJob(gettin_raped, JobCondition.InterruptForced, null, false, true, null);
					(Target.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, Target);
				if (pawn.IsHashIntervalTick(ticks_between_hits))
					roll_to_hit(pawn, Target);
			};
			rape.AddFinishAction(delegate
			{
				//// Trying to add some interactions and social logs
				xxx.processAnalSex(pawn, Target, ref isAnalSex, pawnHasPenis);

				if ((Target.jobs != null) &&
					(Target.jobs.curDriver != null) &&
					(Target.jobs.curDriver as JobDriver_GettinRaped != null))
				{
					(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
				{
					aftersex(pawn, Target, true, false, isAnalSex);
					pawn.mindState.canLovinTick = Find.TickManager.TicksGame + xxx.generate_min_ticks_to_next_lovin(pawn);
					if (!Target.Dead)
					{
						Target.mindState.canLovinTick = Find.TickManager.TicksGame + xxx.generate_min_ticks_to_next_lovin(Target);
					}
				},
				defaultCompleteMode = ToilCompleteMode.Instant
			};
		}

		// Should move these function to common
		public static bool PlantSomething(HediffDef def, Pawn target, bool isToAnal = false, int amount = 1)
		{
			if (def == null) return false;
			if (!isToAnal && !Genital_Helper.has_vagina(target)) return false;
			if (isToAnal && !Genital_Helper.has_anus(target)) return false;
			BodyPartRecord genitalPart = (isToAnal) ? Genital_Helper.get_anus(target) : Genital_Helper.get_genitals(target);
			if (genitalPart != null || genitalPart.parts.Count != 0)
			{
				for (int i = 0; i < amount; i++)
				{
					target.health.AddHediff(def, genitalPart);
				}
				return true;
			}

			return false;
		}

		public virtual Pawn FindVictim(Pawn rapist, Map m, float targetAcquireRadius)
		{
			if (rapist == null || m == null) return null;
			if (!xxx.can_rape(rapist) && requierCanRape) return null;
			Pawn best_rapee = null;
			var best_fuckability = 0.20f; // Don't rape prisoners with <20% fuckability
			foreach (var target in m.mapPawns.AllPawns)
			{
				//if (target.Faction != Faction.OfPlayer) continue;
				if (rapist.Faction == target.Faction || (!FactionUtility.HostileTo(rapist.Faction, target.Faction) && rapist.Faction != null)) continue;

				if (IntVec3Utility.ManhattanDistanceFlat(target.Position, rapist.Position) >= targetAcquireRadius) continue; //Too far to fuck i think.

				//Log.Message("[ABF]"+this.GetType().ToString()+"::TryGiveJob( " + rapist.NameStringShort + " -> " + target.NameStringShort + " ) - checking\nCanReserve:"+ rapist.CanReserve(target, comfort_prisoners.max_rapists_per_prisoner, 0) + "\nTargetPositionForbidden:"+ target.Position.IsForbidden(rapist)+"\nCanGetRape:" + xxx.can_get_raped(target));
				if (target != rapist && rapist.CanReserve(target, comfort_prisoners.max_rapists_per_prisoner, 0) && !target.Position.IsForbidden(rapist) && Can_rape_Easily(target))
				{
					if (xxx.is_human(target) || (xxx.is_zoophiliac(rapist) && xxx.is_animal(target) && xxx.config.animals_enabled))
					{
						var fuc = GetFuckability(rapist, target);
						//var fuc = xxx.would_fuck(rapist, target); //Cant Use default would fuck because victims are always bleeding.
						//Log.Message("[ABF]"+this.GetType().ToString()+ "::FindVictim( " + rapist.NameStringShort + " -> " + target.NameStringShort + " ) - fuckability:" + fuc + " ");
						if ((fuc > best_fuckability) && (Rand.Value < 0.9 * fuc))
						{
							best_rapee = target;
							best_fuckability = fuc;
						}
						//else { Log.Message("[ABF] JobGiver_RapeEnemy::TryGiveJob( " + rapist.NameStringShort + " -> " + target.NameStringShort + " ) - is not good for me "+ "( " + fuc + " )"); }
					}
					//else { Log.Message("[ABF] JobGiver_RapeEnemy::TryGiveJob( " + rapist.NameStringShort + " -> " + target.NameStringShort + " ) - is not human or not zoophilia"); }
				}
				//else { Log.Message("[ABF] JobGiver_RapeEnemy::TryGiveJob( " + rapist.NameStringShort + " -> " + target.NameStringShort + " ) - is not good"); }
			}
			//Log.Message("[RJW]"+this.GetType().ToString()+"::TryGiveJob( " + rapist.NameStringShort + " -> " + best_rapee.NameStringShort + " ) - fuckability:" + best_fuckability + " ");
			return best_rapee;
		}

		public virtual float GetFuckability(Pawn rapist, Pawn target)
		{
			//Log.Message("[RJW]JobDriver_RapeEnemy::GetFuckability(" + rapist.ToString() + "," + target.ToString() + ")");
			return xxx.would_fuck(rapist, target, false, true);
		}

		protected bool Can_rape_Easily(Pawn p)
		{
			return xxx.can_get_raped(p) && p.Downed;
		}
	}
}


code:
using Verse;

namespace rjw
{
	internal class JobDriver_RapeEnemyByAnimal : JobDriver_RapeEnemy
	{
		public override bool CanUseThisJobForPawn(Pawn rapist)
		{
			return xxx.is_animal(rapist);
		}
	}
}


code:
using System.Linq;
using Verse;

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

		public override bool CanUseThisJobForPawn(Pawn rapist)
		{
			return xxx.is_insect(rapist);
		}

		protected override void Impregnate(Pawn pawn, Pawn part, bool isAnalSex)
		{
			if (xxx.is_human(part))
			{
				if (pawn.gender == Gender.Female)
				{
					HediffDef_InsectEgg egg = (from x in DefDatabase<HediffDef_InsectEgg>.AllDefs where x.IsParent(pawn.def.defName) select x).RandomElement<HediffDef_InsectEgg>();
					if (egg != null)
					{
						Log.Message("[RJW]JobDriver_RapeEnemyByInsect::aftersex() - Planting egg " + egg.ToString());
						PlantSomething(egg, part, isAnalSex, Rand.Range(1, 2));
					}
					else
					{
						Log.Message("[RJW]JobDriver_RapeEnemyByInsect::aftersex() - There is no EggData of " + pawn.def.defName);
					}
				}
				else
				{
					Log.Message("[RJW]JobDriver_RapeEnemyByInsect::aftersex() - Fertilize eggs");
					foreach (var egg in (from x in part.health.hediffSet.GetHediffs<Hediff_InsectEgg>() where x.IsParent(pawn.def.defName) select x))
					{
						egg.Fertilize(pawn);
					}
				}
			}
		}

		public override float GetFuckability(Pawn rapist, Pawn target)
		{
			if (rapist.gender == Gender.Female)
			{
				//Log.Message("[RJW]" + this.GetType().ToString() + "::GetFuckability(" + rapist.ToString() + ") - going to plant egg ->"+ target.ToString());
				return 1f; //Plant Eggs to everyone.
			}
			else
			{
				if ((from x in target.health.hediffSet.GetHediffs<Hediff_InsectEgg>() where x.IsParent(rapist.def.defName) select x).Count() > 0)
				{
					return 1f;//Trying to feritlize eggs to everyone planted eggs.
				}
			}
			return 0f;
		}
	}
}


code:
using System.Linq;
using Verse;

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

		public override bool CanUseThisJobForPawn(Pawn rapist)
		{
			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");
			return 1f;
		}

		protected override void Impregnate(Pawn pawn, Pawn part, bool isAnalSex)
		{
			if (pawn.RaceProps.IsMechanoid && xxx.is_human(part))
			{
				//Log.Message("[RJW]JobDriver_RapeEnemyByMech::aftersex - mech raped humans");
				/*foreach (var item in DefDatabase<HediffDef_MechImplants>.AllDefs)
				{
					Log.Message(pawn.def.defName + "Getting Implants\n" +item.defName + "\nParentDef:" + item.parentDef + "\nParentDefs:" + String.Join(",",item.parentDefs.ToArray()) );
				}*/
				HediffDef_MechImplants egg = (from x in DefDatabase<HediffDef_MechImplants>.AllDefs where x.IsParent(pawn.def.defName) select x).RandomElement<HediffDef_MechImplants>();

				PlantSomething(egg, part, isAnalSex, 1);
			}
		}
	}
}