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

namespace rjw
{
	internal static class Genital_Helper
	{
		public static HediffDef penis = HediffDef.Named("Penis");
		public static HediffDef micropenis = HediffDef.Named("Micropenis");
		public static HediffDef small_penis = HediffDef.Named("SmallPenis");
		public static HediffDef big_penis = HediffDef.Named("BigPenis");
		public static HediffDef huge_penis = HediffDef.Named("HugePenis");
		public static HediffDef peg_penis = HediffDef.Named("PegDick");
		public static HediffDef bionic_penis = HediffDef.Named("BionicPenis");

		public static HediffDef vagina = HediffDef.Named("Vagina");
		public static HediffDef tight_vagina = HediffDef.Named("TightVagina");
		public static HediffDef loose_vagina = HediffDef.Named("LooseVagina");
		public static HediffDef hydraulic_vagina = HediffDef.Named("HydraulicVagina");
		public static HediffDef bionic_vagina = HediffDef.Named("BionicVagina");

		public static HediffDef breasts = HediffDef.Named("Breasts");
		public static HediffDef small_breasts = HediffDef.Named("SmallBreasts");
		public static HediffDef large_breasts = HediffDef.Named("LargeBreasts");
		public static HediffDef huge_breasts = HediffDef.Named("HugeBreasts");
		public static HediffDef hydraulic_breasts = HediffDef.Named("HydraulicBreasts");
		public static HediffDef bionic_breasts = HediffDef.Named("BionicBreasts");

		public static HediffDef anus = HediffDef.Named("Anus");
		public static HediffDef tight_anus = HediffDef.Named("TightAnus");
		public static HediffDef loose_anus = HediffDef.Named("LooseAnus");
		public static HediffDef hydraulic_anus = HediffDef.Named("HydraulicAnus");
		public static HediffDef bionic_anus = HediffDef.Named("BionicAnus");

		public static HediffDef dummy_privates_initializer = DefDatabase<HediffDef>.GetNamed("DummyPrivates");

		public static BodyPartRecord get_genitals(Pawn pawn)
		{
			//Log.Message("Genital_Helper::get_genitals( " + pawn.NameStringShort + " ) called");
			BodyPartRecord genitalPart = pawn.RaceProps.body.AllParts.Find(bpr => bpr.def.defName == "Genitals");

			//Log.Message("Genital_Helper::get_genitals( " + pawn.NameStringShort + " ) - checking genitalPart");
			if (genitalPart == null)
			{
				return null;
				//First.inject_genitals (pawn.RaceProps.body);
				//genitalPart = pawn.RaceProps.body.AllParts.Find ((BodyPartRecord bpr) => String.Equals (bpr.def.defName, "Genitals"));
			}

			return genitalPart;
		}

		public static BodyPartRecord get_breasts(Pawn pawn)
		{
			//Log.Message("[RJW] get_breasts( " + pawn.NameStringShort + " ) called");
			BodyPartRecord breastsPart = pawn.RaceProps.body.AllParts.Find(bpr => bpr.def.defName == "Chest");

			if (breastsPart == null)
			{
				//Log.Message("[RJW] get_breasts( " + pawn.NameStringShort + " ) - breastsPart is null");
				return null;
			}
			return breastsPart;
		}

		public static BodyPartRecord get_anus(Pawn pawn)
		{
			BodyPartRecord anusPart = pawn.RaceProps.body.AllParts.Find(bpr => bpr.def.defName == "Anus");

			if (anusPart == null)
			{
				return null;
			}
			return anusPart;
		}

		public static bool genitals_blocked(Pawn pawn)
		{
			if (pawn.apparel != null)
			{
				foreach (var app in pawn.apparel.WornApparel)
				{
					if ((app.def is bondage_gear_def gear_def) && (gear_def.blocks_genitals))
					{
						return true;
					}
				}
			}
			return false;
		}

		public static bool breasts_blocked(Pawn pawn)
		{
			if (pawn.apparel != null)
			{
				foreach (var app in pawn.apparel.WornApparel)
				{
					if ((app.def is bondage_gear_def gear_def) && (gear_def.blocks_breasts))
						return true;
				}
			}
			return false;
		}

		public static bool anus_blocked(Pawn pawn)
		{
			if (pawn.apparel != null)
			{
				foreach (var app in pawn.apparel.WornApparel)
				{
					if ((app.def is bondage_gear_def gear_def) && (gear_def.blocks_anus))
						return true;
				}
			}
			return false;
		}

		//Comments from nizhuan-jjr to previous authors: Why do you want to sexualize WorldPawns? Why not simply sexualize visible pawns?
		//I don't remove this but want to.
		public static bool pawns_require_sexualization()
		{
			int count_sexualized = 0;
			bool found_one = false;

			foreach (var p in Find.WorldPawns.AllPawnsAliveOrDead)  //Comments from nizhuan-jjr to previous authors: Really not sure why you want to sexualize dead pawns, do you want to harvest organs from those dead? This is done by Harvest Organ Post Mortem 2.0.
			{
				if ((!p.Dead) || p.Spawned)
				{
					if (Genital_Helper.is_sexualized(p))
					{
						++count_sexualized;
						if (count_sexualized > 50) //Late game worlds can have thousands of pawns. There's probably no point in checking all of them, and doing so could cause poor performance
							break;
					}
					else
					{
						found_one = true;
						break;
					}
				}
			}

			if (!found_one)
			{
				foreach (var m in Find.Maps)
				{
					count_sexualized = 0;
					foreach (var p in m.mapPawns.AllPawns)
					{
						if (Genital_Helper.is_sexualized(p))
						{
							++count_sexualized;
							if (count_sexualized > 50)
								break;
						}
						else
						{
							found_one = true;
							break;
						}
					}
				}
			}

			return found_one;
		}

		public static bool has_genitals(Pawn pawn)
		{
			BodyPartRecord genitalPart = get_genitals(pawn);
			if (genitalPart is null)
				return false;
			return pawn.health.hediffSet.hediffs.Any((Hediff hed) =>
												  (hed.Part == genitalPart) &&
												  (((hed as Hediff_Implant) != null) || ((hed as Hediff_AddedPart) != null)) &&
												  (hed.def != dummy_privates_initializer));
		}

		public static bool has_breasts(Pawn pawn)
		{
			BodyPartRecord breastsPart = get_breasts(pawn);
			if (breastsPart is null)
				return false;
			return pawn.health.hediffSet.hediffs.Any((Hediff hed) =>
									   (hed.Part == breastsPart) &&
									   (((hed as Hediff_Implant) != null) || ((hed as Hediff_AddedPart) != null)) &&
									   (hed.def != dummy_privates_initializer));
		}

		public static bool has_anus(Pawn pawn)
		{
			BodyPartRecord anusPart = get_anus(pawn);
			if (anusPart is null)
				return false;
			return pawn.health.hediffSet.hediffs.Any((Hediff hed) =>
						   (hed.Part == anusPart) &&
						   (((hed as Hediff_Implant) != null) || ((hed as Hediff_AddedPart) != null)) &&
						   (hed.def != dummy_privates_initializer));
		}

		public static bool has_penis(Pawn pawn)
		{
			BodyPartRecord penisPart = get_genitals(pawn);
			if (penisPart is null)
				return false;
			return pawn.health.hediffSet.hediffs.Any((Hediff hed) =>
						   (hed.Part == penisPart) &&
						   hed.def.defName.ToLower().Contains("penis"));
		}

		public static bool has_vagina(Pawn pawn)
		{
			BodyPartRecord vaginaPart = get_genitals(pawn);
			if (vaginaPart is null)
				return false;
			return pawn.health.hediffSet.hediffs.Any((Hediff hed) =>
						   (hed.Part == vaginaPart) &&
						   hed.def.defName.ToLower().Contains("vagina"));
		}

		public static bool is_sexualized(Pawn pawn)
		{
			return (xxx.is_female(pawn) ? has_genitals(pawn) && has_anus(pawn) && has_breasts(pawn) : has_genitals(pawn) && has_anus(pawn));
			//return has_genitals(pawn) && has_breasts(pawn) && has_anus(pawn);
		}

		public static void sexualize_everyone()
		{
			foreach (var p in Find.WorldPawns.AllPawnsAliveOrDead)
				if ((!p.Dead) || p.Spawned)
					Genital_Helper.sexualize(p);

			foreach (var m in Find.Maps)
				foreach (var p in m.mapPawns.AllPawns)
					Genital_Helper.sexualize(p);
		}

		public static void sexualize(Pawn pawn)
		{
			//if (pawn.RaceProps.hasGenders && (!xxx.is_mechanoid(pawn) || !xxx.is_animal(pawn)) && !is_sexualized(pawn))
			if ((pawn.RaceProps.hasGenders || !xxx.is_animal(pawn)) && !is_sexualized(pawn))
			{
				sexualize_pawn(pawn);
			}
		}

		public static void sexualize_VisiblePawns()
		{
			foreach (Pawn p in Find.VisibleMap.mapPawns.AllPawnsSpawned)
			{
				sexualize(p);
			}
		}

		public static void add_genitals(Pawn pawn)
		{
			//Log.Message("Genital_Helper::add_genitals( " + pawn.NameStringShort + " ) called");
			BodyPartRecord genitalPart = get_genitals(pawn);
			//Log.Message("Genital_Helper::add_genitals( " + pawn.NameStringShort + " ) - checking genitals");
			if (genitalPart == null)
			{
				//Log.Message("[RJW] sexualize_pawn( " + pawn.NameStringShort + " ) doesn't have a genitalsPart");
				return;
			}
			else if (pawn.health.hediffSet.PartIsMissing(genitalPart))
			{
				//Log.Message("[RJW] sexualize_pawn( " + pawn.NameStringShort + " ) had a genital but was removed.");
				return;
			}
			if (has_genitals(pawn))
			{
				//Log.Message("[RJW] sexualize_pawn( " + pawn.NameStringShort + " ) already has genitals");
				return;
			}

			HediffDef privates;
			double value = Rand.Value;

			//Log.Message("Genital_Helper::add_genitals( " + pawn.NameStringShort + " ) - checking race");
			if (pawn.RaceProps.IsMechanoid)
			{
				//Log.Message("Genital_Helper::add_genitals( " + pawn.NameStringShort + " ) - race is mechanoid");
				if (value < 0.30)
					privates = pawn.gender == Gender.Male ? peg_penis : hydraulic_vagina;
				else
					privates = pawn.gender == Gender.Male ? bionic_penis : bionic_vagina;
			}
			else if (pawn.RaceProps.Humanlike)
			{
				//Log.Message("Genital_Helper::add_genitals( " + pawn.NameStringShort + " ) - race is humanlike");
				if (value < 0.10)
					privates = pawn.gender == Gender.Male ? micropenis : loose_vagina;
				else if (value < 0.20)
					privates = pawn.gender == Gender.Male ? small_penis : loose_vagina;
				else if (value < 0.70)
					privates = pawn.gender == Gender.Male ? penis : vagina;
				else if (value < 0.80)
					privates = pawn.gender == Gender.Male ? big_penis : tight_vagina;
				else if (value < 0.90)
					privates = pawn.gender == Gender.Male ? huge_penis : tight_vagina;
				else if (value < 0.95)
					privates = pawn.gender == Gender.Male ? peg_penis : hydraulic_vagina;
				else
					privates = pawn.gender == Gender.Male ? bionic_penis : bionic_vagina;
			}
			else
			{
				//Log.Message("Genital_Helper::add_genitals( " + pawn.NameStringShort + " ) - race is something else");
				privates = pawn.gender == Gender.Male ? penis : vagina;
			}
			//Log.Message("Genital_Helper::add_genitals( " + pawn.NameStringShort + " ) - adding hediff");
			pawn.health.AddHediff(privates, genitalPart);
		}

		public static void add_breasts(Pawn pawn)
		{
			//Log.Message("[RJW] add_breasts( " + pawn.NameStringShort + " ) called");
			BodyPartRecord breastsPart = get_breasts(pawn);

			if (breastsPart == null)
			{
				//Log.Message("[RJW] add_breasts( " + pawn.NameStringShort + " ) - pawn doesn't have a breastsPart");
				return;
			}
			else if (pawn.health.hediffSet.PartIsMissing(breastsPart))
			{
				//Log.Message("[RJW] sexualize_pawn( " + pawn.NameStringShort + " ) had breasts but were removed.");
				return;
			}
			if (has_breasts(pawn))
			{
				//Log.Message("[RJW] add_breasts( " + pawn.NameStringShort + " ) - pawn already has breasts");
				return;
			}

			//Log.Message("[RJW] add_breasts( " + pawn.NameStringShort + " ) - checking gender");

			if (pawn.gender == Gender.Female)
			{
				HediffDef bewbs;
				double value = Rand.Value;

				if (pawn.RaceProps.IsMechanoid)
				{
					if (value < 0.30)
						bewbs = hydraulic_breasts;
					else
						bewbs = bionic_breasts;
				}
				else if (pawn.RaceProps.Humanlike)
				{
					if (value < 0.10)
						bewbs = small_breasts;
					else if (value < 0.50)
						bewbs = breasts;
					else if (value < 0.70)
						bewbs = large_breasts;
					else if (value < 0.90)
						bewbs = huge_breasts;
					else if (value < 0.95)
						bewbs = hydraulic_breasts;
					else
						bewbs = bionic_breasts;
				}
				else
				{
					bewbs = breasts;
				}
				pawn.health.AddHediff(bewbs, breastsPart);
			}
		}

		public static void add_anus(Pawn pawn)
		{
			BodyPartRecord anusPart = get_anus(pawn);

			if (anusPart == null)
			{
				//Log.Message("[RJW] sexualize_pawn( " + pawn.NameStringShort + " ) doesn't have an anusPart");
				return;
			}
			else if (pawn.health.hediffSet.PartIsMissing(anusPart))
			{
				//Log.Message("[RJW] sexualize_pawn( " + pawn.NameStringShort + " ) had an anus but was removed.");
				return;
			}
			if (has_anus(pawn))
			{
				//Log.Message("[RJW] sexualize_pawn( " + pawn.NameStringShort + " ) already has an anus");
				return;
			}

			HediffDef asshole;
			double value = Rand.Value;
			if (pawn.RaceProps.IsMechanoid)
			{
				if (value < 0.30)
					asshole = hydraulic_anus;
				else
					asshole = bionic_anus;
			}
			else if (pawn.RaceProps.Humanlike)
			{
				if (value < 0.20)
					asshole = loose_anus;
				else if (value < 0.70)
					asshole = anus;
				else if (value < 0.90)
					asshole = tight_anus;
				else if (value < 0.95)
					asshole = hydraulic_anus;
				else
					asshole = bionic_anus;
			}
			else
			{
				asshole = anus;
			}

			pawn.health.AddHediff(asshole, anusPart);
		}

		public static void sexualize_pawn(Pawn pawn)
		{
			//Log.Message("[RJW] sexualize_pawn( " + pawn.NameStringShort + " ) called");

			if (!pawn.RaceProps.hasGenders)
			{
				//Log.Message("[RJW] sexualize_pawn() - unable to sexualize genderless pawn " + pawn.NameStringShort);
				return;
			}

			add_genitals(pawn);
			add_breasts(pawn);
			add_anus(pawn);

			var sex_need = pawn.needs.TryGetNeed<Need_Sex>();
			if (pawn.Faction != null && !pawn.Faction.IsPlayer && sex_need != null)
			{
				sex_need.ForceSetLevel(Rand.Range(0.01f, 0.5f));
			}
		}

		public static void inject_genitals(BodyDef target)
		{
			//Log.Message("[RJW] inject_genitals() called");
			BodyPartRecord tor_rec = target.corePart;

			if (tor_rec != null)
			{
				var gen_rec = new BodyPartRecord
				{
					def = DefDatabase<BodyPartDef>.GetNamed("Genitals"),
					height = BodyPartHeight.Bottom,
					depth = BodyPartDepth.Outside,
					coverage = 0.02f
				};
				gen_rec.groups.Add(BodyPartGroupDefOf.Torso);
				gen_rec.parent = tor_rec;

				// TODO lots of broken/missing stuff here

				//gen_rec.fleshCoverage = 1.0f;
				//gen_rec.absoluteCoverage = gen_rec.parent.absoluteCoverage * gen_rec.coverage;
				//gen_rec.absoluteFleshCoverage = gen_rec.absoluteCoverage * gen_rec.fleshCoverage;

				// coverage is set by XML (or hardcoded like above)
				// absoluteCoverage is derived from coverage and the parent's absoluteCoverage
				// fleshCoverage is derived from the parts' coverages
				// absoluteFleshCoverage is derived from absoluteCoverage and fleshCoverage

				// so inserting the genitals affects the Torso's fleshCoverage which affects its absoluteFleshCoverage
				tor_rec.parts.Add(gen_rec);
				//if (gen_rec.coverage <= tor_rec.fleshCoverage)
				//tor_rec.fleshCoverage -= gen_rec.coverage;
				//else {
				//tor_rec.fleshCoverage = 0.0f;
				//Log.Warning ("[RJW] Torso BPR fleshCoverage pushed below zero during genitals injection");
				//}
				//tor_rec.absoluteFleshCoverage = tor_rec.absoluteCoverage * tor_rec.fleshCoverage;

				target.AllParts.Add(gen_rec);
			}
			else
				Log.Error("[RJW] Failed to find the \"Torso\" BodyPartRecord");
		}

		public static void inject_breasts(BodyDef target)
		{
			//Log.Message("[RJW] inject_breasts() called");
			BodyPartRecord tor_rec = target.corePart;

			if (tor_rec != null)
			{
				var gen_rec = new BodyPartRecord
				{
					def = DefDatabase<BodyPartDef>.GetNamed("Chest"),
					height = BodyPartHeight.Top,
					depth = BodyPartDepth.Outside,
					coverage = 0.1f
				};
				gen_rec.groups.Add(BodyPartGroupDefOf.Torso);
				gen_rec.parent = tor_rec;

				// TODO lots of broken/missing stuff here

				//gen_rec.fleshCoverage = 1.0f;
				//gen_rec.absoluteCoverage = gen_rec.parent.absoluteCoverage * gen_rec.coverage;
				//gen_rec.absoluteFleshCoverage = gen_rec.absoluteCoverage * gen_rec.fleshCoverage;

				// coverage is set by XML (or hardcoded like above)
				// absoluteCoverage is derived from coverage and the parent's absoluteCoverage
				// fleshCoverage is derived from the parts' coverages
				// absoluteFleshCoverage is derived from absoluteCoverage and fleshCoverage

				// so inserting the genitals affects the Torso's fleshCoverage which affects its absoluteFleshCoverage
				tor_rec.parts.Add(gen_rec);
				//if (gen_rec.coverage <= tor_rec.fleshCoverage)
				//tor_rec.fleshCoverage -= gen_rec.coverage;
				//else {
				//tor_rec.fleshCoverage = 0.0f;
				//Log.Warning ("[RJW] Torso BPR fleshCoverage pushed below zero during genitals injection");
				//}
				//tor_rec.absoluteFleshCoverage = tor_rec.absoluteCoverage * tor_rec.fleshCoverage;

				target.AllParts.Add(gen_rec);
			}
			else
				Log.Error("[RJW] Failed to find the \"Torso\" BodyPartRecord");
		}

		public static void inject_anus(BodyDef target)
		{
			//Log.Message("[RJW] inject_anus() called");
			BodyPartRecord tor_rec = target.corePart;

			if (tor_rec != null)
			{
				var gen_rec = new BodyPartRecord
				{
					def = DefDatabase<BodyPartDef>.GetNamed("Anus"),
					height = BodyPartHeight.Bottom,
					depth = BodyPartDepth.Outside,
					coverage = 0.02f
				};
				gen_rec.groups.Add(BodyPartGroupDefOf.Torso);
				gen_rec.parent = tor_rec;

				// TODO lots of broken/missing stuff here

				//gen_rec.fleshCoverage = 1.0f;
				//gen_rec.absoluteCoverage = gen_rec.parent.absoluteCoverage * gen_rec.coverage;
				//gen_rec.absoluteFleshCoverage = gen_rec.absoluteCoverage * gen_rec.fleshCoverage;

				// coverage is set by XML (or hardcoded like above)
				// absoluteCoverage is derived from coverage and the parent's absoluteCoverage
				// fleshCoverage is derived from the parts' coverages
				// absoluteFleshCoverage is derived from absoluteCoverage and fleshCoverage

				// so inserting the genitals affects the Torso's fleshCoverage which affects its absoluteFleshCoverage
				tor_rec.parts.Add(gen_rec);
				//if (gen_rec.coverage <= tor_rec.fleshCoverage)
				//tor_rec.fleshCoverage -= gen_rec.coverage;
				//else {
				//tor_rec.fleshCoverage = 0.0f;
				//Log.Warning ("[RJW] Torso BPR fleshCoverage pushed below zero during genitals injection");
				//}
				//tor_rec.absoluteFleshCoverage = tor_rec.absoluteCoverage * tor_rec.fleshCoverage;

				target.AllParts.Add(gen_rec);
			}
			else
				Log.Error("[RJW] Failed to find the \"Torso\" BodyPartRecord");
		}
	}
}


code:
using Verse;

namespace rjw
{
	public class MapCom_Injector : MapComponent
	{
		public bool injected_designator = false;

		public bool triggered_after_load = false;

		public MapCom_Injector(Map m) : base(m)
		{
		}

		public override void MapComponentUpdate()
		{
		}

		public override void MapComponentTick()
		{
		}

		public override void MapComponentOnGUI()
		{
			if (!triggered_after_load)
			{
				triggered_after_load = true;
				//--Log.Message("[RJW]MapCom_Injector::triggered after load");

				if (Genital_Helper.pawns_require_sexualization())
				{
					//--Log.Message("[RJW]MapCom_Injector::sexualize everyone");
					Genital_Helper.sexualize_everyone();
				}
			}

			var currently_visible = Find.VisibleMap == map;

			if ((!injected_designator) && currently_visible)
			{
				Find.ReverseDesignatorDatabase.AllDesignators.Add(new Designator_ComfortPrisoner());
				injected_designator = true;
			}
			else if (injected_designator && (!currently_visible))
				injected_designator = false;
		}

		public override void ExposeData()
		{
		}
	}
}

repo_name: Hungarianmonarchy/RimJobWorld

license: mit


code:
using System;
using Verse;

namespace rjw
{
	public sealed class MiscTranslationDef : Def
	{
		public Type targetClass;
		public string stringA = null;
		public string stringB = null;
		public string stringC = null;

		private void Assert(bool check, string errorMessage)
		{
			if (!check)
			{
				Log.Error($"[RJW] Invalid data in MiscTranslationDef {defName}: {errorMessage}");
			}
		}

		public override void PostLoad()
		{
			Assert(targetClass != null, "targetClass field must be set");
		}

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


code:
using HugsLib;
using HugsLib.Core;
using HugsLib.Settings;
using Verse;

namespace rjw
{
	public class Mod_Settings : ModBase
	{
		public override string ModIdentifier
		{
			get
			{
				return "RJW";
			}
		}

		public override VersionShort GetVersion()
		{
			//Log.Message("GetVersion() called");
			return base.GetVersion();
		}

		public static bool WildMode;
		public static float sexneed_decay_rate;
		public static bool nymphos;
		public static bool std_floor;
		public static bool prisoner_beating;
		public static uint pregnancy_weight_parent; //to-do: convert 'em into float
		public static uint pregnancy_weight_species;
		public static uint pregnancy_coefficient_human;
		public static uint pregnancy_coefficient_animals;
		public static bool pregnancy_use_parent_method;
		public static uint sex_free_for_all_age;
		public static uint sex_minimum_age;
		public static float NonFutaWomenRaping_MaxVulnerability;
		public static float Rapee_MinVulnerability_human;
		public static float Rapee_MinVulnerability_animals;

		// Feature Toggles
		public bool animals_enabled; // Updated

		public bool comfort_prisoners_enabled; // Updated
		public bool ComfortColonist; // New
		public bool ComfortAnimal; // New
		public bool cum_enabled; // Updated
		public bool rape_me_sticky_enabled; // Updated
		public bool sounds_enabled; // Updated
		public bool stds_enabled; // Updated
		public bool bondage_gear_enabled; // Updated
		public bool nymph_joiners_enabled; // Updated
		public bool whore_beds_enabled; // Updated
		public bool necro_enabled; // Updated
		public bool beastiality_enabled; // Updated
		public bool random_rape_enabled; // Updated
		public bool always_accept_whores; // Updated
		public bool nymphs_always_JoinInBed; // Updated
		public bool zoophis_always_rape; // Updated
		public bool rapists_always_rape; // Updated
		public bool pawns_always_do_fapping; // Updated
		public bool pawns_always_rapeCP; // Updated
		public bool whores_always_findjob; // Updated

		// Display Toggles
		public bool show_regular_dick_and_vag; // Updated

		// STD config
		public bool std_show_roll_to_catch; // Updated

		public float std_min_severity_to_pitch; // Updated
		public float std_env_pitch_cleanliness_exaggeration; // Updated
		public float std_env_pitch_dirtiness_exaggeration; // Updated
		public float std_outdoor_cleanliness; // Updated

		// Age Config

		public float significant_pain_threshold; // Updated
		public float extreme_pain_threshold; // Updated
		public float base_chance_to_hit_prisoner; // Updated
		public int min_ticks_between_hits; // Updated
		public int max_ticks_between_hits; // Updated

		public float max_nymph_fraction; // Updated
		public float opp_inf_initial_immunity; // Updated
		public float comfort_prisoner_rape_mtbh_mul; // Updated
		public float whore_mtbh_mul; // Updated
		public float nymph_spawn_with_std_mul; // Updated

		//Mod Settings handles
		private SettingHandle<bool> option_WildMode;
		private SettingHandle<int> option_sexneed_decay_rate;
		private SettingHandle<bool> option_nymphs_join;
		private SettingHandle<bool> option_STD_floor_catch;
		private SettingHandle<bool> option_rape_beating;
		private SettingHandle<int> option_pregnancy_weight_parent;
		private SettingHandle<int> option_pregnancy_weight_species;
		private SettingHandle<int> option_pregnancy_coefficient_human;
		private SettingHandle<int> option_pregnancy_coefficient_animals;
		private SettingHandle<bool> option_pregnancy_use_parent_method;
		private SettingHandle<int> option_sex_free_for_all_age;
		private SettingHandle<int> option_sex_minimum_age;
		private SettingHandle<int> option_NonFutaWomenRaping_MaxVulnerability;
		private SettingHandle<int> option_Rapee_MinVulnerability_human;
		private SettingHandle<int> option_Rapee_MinVulnerability_animals;

		public override void Initialize()
		{
			Logger.Message("Initialize() called");
			base.Initialize();
		}

		public override void DefsLoaded()
		{
			Log.Message("DefsLoaded() called");

			this.option_WildMode = Settings.GetHandle<bool>("WildMode", "WildMode_name".Translate(), "WildMode_desc".Translate(), false);
			this.option_sexneed_decay_rate = Settings.GetHandle<int>("sexneed_decay_rate", "sexneed_decay_rate_name".Translate(), "sexneed_decay_rate_desc".Translate(), 100, Validators.IntRangeValidator(0, 1000000));
			this.option_sexneed_decay_rate.SpinnerIncrement = 25;
			this.option_nymphs_join = Settings.GetHandle<bool>("nymphs_join", "NymphsJoin".Translate(), "NymphsJoin_desc".Translate(), true);
			this.option_STD_floor_catch = Settings.GetHandle<bool>("STD_floor_catch", "STD_FromFloors".Translate(), "STD_FromFloors_desc".Translate(), true);
			this.option_rape_beating = Settings.GetHandle<bool>("rape_beating", "PrisonersBeating".Translate(), "PrisonersBeating_desc".Translate(), true);
			this.option_pregnancy_weight_parent = Settings.GetHandle<int>("pregnancy_weight_parent", "OffspringLookLikeTheirMother".Translate(), "OffspringLookLikeTheirMother_desc".Translate(), 50, Validators.IntRangeValidator(0, 100));
			this.option_pregnancy_weight_parent.SpinnerIncrement = 10;
			this.option_pregnancy_weight_species = Settings.GetHandle<int>("pregnancy_weight_species", "OffspringIsHuman".Translate(), "OffspringIsHuman_desc".Translate(), 50, Validators.IntRangeValidator(0, 100));
			this.option_pregnancy_weight_species.SpinnerIncrement = 10;
			this.option_pregnancy_coefficient_human = Settings.GetHandle<int>("pregnancy_coefficient_human", "PregnantCoeffecientForHuman".Translate(), "PregnantCoeffecientForHuman_desc".Translate(), 20, Validators.IntRangeValidator(0, 300));
			this.option_pregnancy_coefficient_human.SpinnerIncrement = 10;
			this.option_pregnancy_coefficient_animals = Settings.GetHandle<int>("pregnancy_coefficient_animals", "PregnantCoeffecientForAnimals".Translate(), "PregnantCoeffecientForAnimals_desc".Translate(), 50, Validators.IntRangeValidator(0, 300));
			this.option_pregnancy_coefficient_animals.SpinnerIncrement = 10;
			this.option_pregnancy_use_parent_method = Settings.GetHandle<bool>("pregnancy_use_parent_method", "UseParentMethod".Translate(), "UseParentMethod_desc".Translate(), true);
			this.option_sex_free_for_all_age = Settings.GetHandle<int>("sex_free_for_all_age", "SexFreeForAllAge".Translate(), "SexFreeForAllAge_desc".Translate(), 15, Validators.IntRangeValidator(0, 9999));
			this.option_sex_free_for_all_age.SpinnerIncrement = 1;
			this.option_sex_minimum_age = Settings.GetHandle<int>("sex_minimum_age", "SexMinimumAge".Translate(), "SexMinimumAge_desc".Translate(), 15, Validators.IntRangeValidator(0, 9999));
			this.option_sex_minimum_age.SpinnerIncrement = 1;
			this.option_NonFutaWomenRaping_MaxVulnerability = Settings.GetHandle<int>("nonFutaWomenRaping_MaxVulnerability", "NonFutaWomenRaping_MaxVulnerability".Translate(), "NonFutaWomenRaping_MaxVulnerability_desc".Translate(), 20, Validators.IntRangeValidator(0, 300));
			this.option_NonFutaWomenRaping_MaxVulnerability.SpinnerIncrement = 10;
			this.option_Rapee_MinVulnerability_human = Settings.GetHandle<int>("rapee_MinVulnerability_human", "Rapee_MinVulnerability_human".Translate(), "Rapee_MinVulnerability_human_desc".Translate(), 50, Validators.IntRangeValidator(0, 300));
			this.option_Rapee_MinVulnerability_human.SpinnerIncrement = 10;
			this.option_Rapee_MinVulnerability_animals = Settings.GetHandle<int>("rapee_MinVulnerability_animals", "Rapee_MinVulnerability_animals".Translate(), "Rapee_MinVulnerability_animals_desc".Translate(), 40, Validators.IntRangeValidator(0, 300));
			this.option_Rapee_MinVulnerability_animals.SpinnerIncrement = 10;

			this.SettingsChanged();
		}

		public override void SettingsChanged()
		{
			base.SettingsChanged();
			WildMode = option_WildMode.Value;
			sexneed_decay_rate = ((float)option_sexneed_decay_rate.Value) / 100f;
			nymphos = option_nymphs_join.Value;
			std_floor = option_STD_floor_catch.Value;
			prisoner_beating = option_rape_beating.Value;
			pregnancy_weight_parent = (uint)option_pregnancy_weight_parent.Value;
			pregnancy_weight_species = (uint)option_pregnancy_weight_species.Value;
			pregnancy_coefficient_human = (uint)option_pregnancy_coefficient_human.Value;
			pregnancy_coefficient_animals = (uint)option_pregnancy_coefficient_animals.Value;
			pregnancy_use_parent_method = option_pregnancy_use_parent_method.Value;
			sex_free_for_all_age = (uint)option_sex_free_for_all_age.Value;
			sex_minimum_age = (uint)option_sex_minimum_age.Value;
			NonFutaWomenRaping_MaxVulnerability = ((float)option_NonFutaWomenRaping_MaxVulnerability.Value) / 100f;
			Rapee_MinVulnerability_human = ((float)option_Rapee_MinVulnerability_human.Value) / 100f;
			Rapee_MinVulnerability_animals = ((float)option_Rapee_MinVulnerability_animals.Value) / 100f;

			Log.Message("[RJW] Settings Changed:");
			Log.Message("WildMode = " + WildMode);
			Log.Message("sexneed_decay_rate = " + sexneed_decay_rate);
			Log.Message("nymphos = " + nymphos);
			Log.Message("std_floor = " + std_floor);
			Log.Message("prisoner_beating = " + prisoner_beating);
			Log.Message("pregnancy_weight_parent = " + pregnancy_weight_parent);
			Log.Message("pregnancy_weight_species = " + pregnancy_weight_species);
			Log.Message("pregnancy_coefficient_human = " + pregnancy_coefficient_human);
			Log.Message("pregnancy_coefficient_animals = " + pregnancy_coefficient_animals);
			Log.Message("pregnancy_use_parent_method = " + pregnancy_use_parent_method);
			Log.Message("sex_free_for_all_age = " + sex_free_for_all_age);
			Log.Message("sex_minimum_age = " + sex_minimum_age);
			Log.Message("NonFutaWomenRaping_MaxVulnerability = " + NonFutaWomenRaping_MaxVulnerability);
			Log.Message("Rapee_MinVulnerability_human = " + Rapee_MinVulnerability_human);
			Log.Message("Rapee_MinVulnerability_animals = " + Rapee_MinVulnerability_animals);
		}

		public override void MapLoaded(Map map)
		{
			Log.Message("[RJW] Settings loaded:");
			Log.Message("WildMode = " + WildMode);
			Log.Message("sexneed_decay_rate = " + sexneed_decay_rate);
			Log.Message("nymphos = " + nymphos);
			Log.Message("std_floor = " + std_floor);
			Log.Message("prisoner_beating = " + prisoner_beating);
			Log.Message("pregnancy_weight_parent = " + pregnancy_weight_parent);
			Log.Message("pregnancy_weight_species = " + pregnancy_weight_species);
			Log.Message("pregnancy_coefficient_human = " + pregnancy_coefficient_human);
			Log.Message("pregnancy_coefficient_animals = " + pregnancy_coefficient_animals);
			Log.Message("pregnancy_use_parent_method = " + pregnancy_use_parent_method);
			Log.Message("sex_free_for_all_age = " + sex_free_for_all_age);
			Log.Message("sex_minimum_age = " + sex_minimum_age);
			Log.Message("NonFutaWomenRaping_MaxVulnerability = " + NonFutaWomenRaping_MaxVulnerability);
			Log.Message("Rapee_MinVulnerability_human = " + Rapee_MinVulnerability_human);
			Log.Message("Rapee_MinVulnerability_animals = " + Rapee_MinVulnerability_animals);
			base.MapLoaded(map);
		}

		/*
		public override void Update() {
			base.Update();
		}

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

        public override void MapComponentsInitializing(Map map) {
            Logger.Message("MapComponentsInitializing() called");
            base.MapComponentsInitializing(map);
        }

        public override void MapDiscarded(Map map) {
            Logger.Message("MapDiscarded() called");
            base.MapDiscarded(map);
        }

        public override void MapGenerated(Map map) {
            Logger.Message("MapGenerated() called");
            base.MapGenerated(map);
        }

        public override void MapLoaded(Map map) {
            Logger.Message("MapLoaded() called");
            base.MapLoaded(map);
        }

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

        public override void SceneLoaded(Scene scene) {
            Logger.Message("SceneLoaded() called");
            base.SceneLoaded(scene);
        }

        public override void Tick(int currentTick) {
            base.Tick(currentTick);
        }

        public override void WorldLoaded() {
            Logger.Message("WorldLoaded() called");
            base.WorldLoaded();
        }

		private void MakeSettingsCategoryToggle(string labelId, Action buttonAction)
		{
			var toolToggle = Settings.GetHandle<bool>(labelId, labelId.Translate(), null);
			toolToggle.Unsaved = true;
			toolToggle.CustomDrawer = rect =>
			{
				if (Widgets.ButtonText(rect, "setting_showToggles_btn".Translate())) buttonAction();
				return false;
			};
		}
		*/
	}
}


code:
using Verse;

namespace rjw
{
	/* TO BE FINISHED
	public enum RandomDialogueJobType
	{
		Rape = 0,
		Love = 1,
		Other = 2
	}

	public sealed class RandomDialoguesDef : Def
	{
		public RandomDialogueJobType JobType;
		public string RD0;
		public string RD1;
		public string RD2;
		public string RD3;
		public string RD4;
		public string RD5;
		public string RD6;
		public string RD7;
		public string RD8;
		public string RD9;
		public string RD10;
		public string RD11;
		public string RD12;
		public string RD13;
		public string RD14;
		public string RD15;
		public string RD16;
		public string RD17;
		public string RD18;
		public string RD19;
	}

    [DefOf]
    public static class RandomDialoguesDefOf
    {
        //0 means before, 1 means during, 2 means after
        //public readonly static RandomDialoguesDef rape0;
        //public readonly static RandomDialoguesDef rape1;
        //public readonly static RandomDialoguesDef rape2;

        //public readonly static RandomDialoguesDef love0;
        //public readonly static RandomDialoguesDef love1;
        //public readonly static RandomDialoguesDef love2;

        //public readonly static RandomDialoguesDef other0;
        //public readonly static RandomDialoguesDef other1;
        //public readonly static RandomDialoguesDef other2;
    }
    */

	// TO BE FINISHED
	/*
    public static class RandomDialogues
    {
        public static void processBeforeDialogue(Pawn psubject,Pawn pobject,int Type = 4)
        {
            switch (Type)
            {
                case 0:
                    string traitDefName;
                    if (xxx.has_traits(psubject)&&Rand.Value<0.25){
                        traitDefName = psubject.random_pick_a_trait();
                        switch (traitDefName)
                        {
                            case "Nymphomaniac":
                                break;

                            case "Rapist":
                                break;

                            case "Necrophiliac":
                                break;

                            case "Zoophiliac":
                                break;

                            case "Bloodlust":
                                break;

                            case "Psychopath":
                                break;

                            case "Cannibal":
                                break;

                            case "Masochist":
                                break;

                            case "Prosthophile":
                                break;

                            case "Prosthophobe":
                                break;

                            case "Jealous":
                                break;

                            case "Gay":
                                break;

                            default:
                                break;
                        }
                    }
                    if (xxx.has_traits(pobject))
                    {
                    }
                    return;

                case 1:
                    return;

                case 2:
                    return;

                default:
                    Log.Error("[RJW]RandomDialogues::processBeforeDialogue - Type not found");
                    return;
            }
        }

        public static void processDuringDialogue(Pawn psubject, Pawn pobject, int Type = 4)
        {
            switch (Type)
            {
                case 0:
                    return;

                case 1:
                    return;

                case 2:
                    return;

                default:
                    Log.Error("[RJW]RandomDialogues::processBeforeDialogue - Type not found");
                    return;
            }
        }

        public static void processAfterDialogue(Pawn psubject, Pawn pobject, int Type = 4)
        {
            switch (Type)
            {
                case 0:
                    return;

                case 1:
                    return;

                case 2:
                    return;

                default:
                    Log.Error("[RJW]RandomDialogues::processBeforeDialogue - Type not found");
                    return;
            }
        }
    }
    */
}


code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
namespace rjw.Source.Common
{

    public static class Unprivater
    {
        internal const BindingFlags flags = BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
        /// <summary>
        /// T is for returned type. For instance-class fields
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="fieldName"></param>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static T GetProtectedValue<T>(string fieldName, object obj)
        {
            var fieldinfo = obj.GetType().GetField(fieldName, flags);
            var readData = fieldinfo.GetValue(obj);
            if (readData is T)
            {
                return (T)readData;
            }
            else
            {
                try
                {
                    return (T)Convert.ChangeType(readData, typeof(T));
                }
                catch (InvalidCastException)
                {
                    return default(T);
                }
            }
        }

        /// <summary>
        /// T is for returned type. For instance-class properties ( [ get; thingies ] )
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="fieldName"></param>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static T GetProtectedProperty<T>(string propertyName, object obj)
        {
            var propinfo = obj.GetType().GetProperty(propertyName, flags);

            var getter = propinfo.GetGetMethod(nonPublic: true);
            var readData = getter.Invoke(obj, null);
            if (readData is T)
            {
                return (T)readData;
            }
            else
            {
                try
                {
                    return (T)Convert.ChangeType(readData, typeof(T));
                }
                catch (InvalidCastException)
                {
                    return default(T);
                }
            }
        }

        /// <summary>
        /// T is for returned type. For static-class fields
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="fieldName"></param>
        /// <param name="t"></param>
        /// <returns></returns>
        public static T GetProtectedValue<T>(string fieldName, Type t)
        {
            var fieldinfo = t.GetField(fieldName, flags);
            var readData = fieldinfo.GetValue(null);
            if (readData is T)
            {
                return (T)readData;
            }
            else
            {
                try
                {
                    return (T)Convert.ChangeType(readData, typeof(T));
                }
                catch (InvalidCastException)
                {
                    return default(T);
                }
            }
        }

        // needs testing, dunno if it works for statics
        public static T GetProtectedProperty<T>(string propertyName, Type t)
        {
            var propinfo = t.GetProperty(propertyName, flags);

            var getter = propinfo.GetGetMethod(nonPublic: true);
            var readData = getter.Invoke(null, null);
            if (readData is T)
            {
                return (T)readData;
            }
            else
            {
                try
                {
                    return (T)Convert.ChangeType(readData, typeof(T));
                }
                catch (InvalidCastException)
                {
                    return default(T);
                }
            }
        }

    }

}


code:
namespace rjw
{
	/*This is useless for now.
	public class Verb_Fuck : Verb
	{
		private const int TargetCooldown = 40;

		protected override bool TryCastShot()
		{
			Pawn casterPawn = base.CasterPawn;

			Thing thing = this.currentTarget.Thing;
			casterpawn.rotationTracker.Face(thing.DrawPos);

			this.SoundJuicy().PlayOneShot(new TargetInfo(thing.Position, casterPawn.Map, false));
			casterPawn.Drawer.Notify_MeleeAttackOn(thing);

			casterpawn.rotationTracker.FaceCell(thing.Position);
			if (casterPawn.caller != null) {
				casterPawn.caller.Notify_DidMeleeAttack();
			}
			return true;
		}

		private SoundDef SoundJuicy()
		{
			return SoundDef.Named ("Sex");
		}
	}
    */
}


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

namespace rjw
{
	public static class bondage_gear_tradeability
	{
		public static void init()
		{
			// Prevents bondage gear from appearing on traders if it is disabled in the config
			if (!xxx.config.bondage_gear_enabled)
			{
				foreach (var def in DefDatabase<bondage_gear_def>.AllDefs)
					if (def.tradeability == Tradeability.Stockable)
						def.tradeability = Tradeability.Sellable;
			}
		}
	}

	public static class bondage_gear_extensions
	{
		public static bool has_lock(this Apparel app)
		{
			return (app.TryGetComp<CompHoloCryptoStamped>() != null);
		}

		public static bool is_wearing_locked_apparel(this Pawn p)
		{
			if (p.apparel != null)
				foreach (var app in p.apparel.WornApparel)
					if (app.has_lock())
						return true;
			return false;
		}

		// Tries to get p started on the job of using an item on either another pawn or on themself (if "other" is null).
		// Of course in order for this method to work, the item's useJob has to be able to handle use on another pawn. This
		// is true for the holokey and bondage gear in RJW but not the items in the core game
		public static void start_job(this CompUsable usa, Pawn p, LocalTargetInfo tar)
		{
			if (p.CanReserveAndReach(usa.parent, PathEndMode.Touch, Danger.Deadly) &&
				((tar == null) || p.CanReserveAndReach(tar, PathEndMode.Touch, Danger.Deadly)))
			{
				var comfor = usa.parent.GetComp<CompForbiddable>();
				if (comfor != null)
					comfor.Forbidden = false;
				var job = new Job(((CompProperties_Usable)usa.props).useJob, usa.parent, tar);
				p.jobs.TryTakeOrderedJob(job);
			}
		}

		// Creates a menu option to use an item. "tar" is expected to be a pawn, corpse or null if it doesn't apply (in which
		// case the pawn will presumably use the item on themself). "required_work" can also be null.
		public static FloatMenuOption make_option(this CompUsable usa, string label, Pawn p, LocalTargetInfo tar, WorkTypeDef required_work)
		{
			if ((tar != null) && (!p.CanReserve(tar)))
				return new FloatMenuOption(label + " (" + "Reserved".Translate() + ")", null, MenuOptionPriority.DisabledOption);
			else if ((tar != null) && (!p.CanReach(tar, PathEndMode.Touch, Danger.Deadly)))
				return new FloatMenuOption(label + " (" + "NoPath".Translate() + ")", null, MenuOptionPriority.DisabledOption);
			else if ((required_work != null) && p.story.WorkTypeIsDisabled(required_work))
				return new FloatMenuOption(label + " (" + "CannotPrioritizeWorkTypeDisabled".Translate(new object[] { required_work.gerundLabel }) + ")", null, MenuOptionPriority.DisabledOption);
			else
				return new FloatMenuOption(
					label,
					delegate
					{
						usa.start_job(p, tar);
					},
					MenuOptionPriority.Default);
		}
	}

	public class bondage_gear_def : ThingDef
	{
		public Type soul_type;
		public HediffDef equipped_hediff = null;
		public bool gives_bound_moodlet = false;
		public bool gives_gagged_moodlet = false;
		public bool blocks_genitals = false;
		public bool blocks_anus = false;
		public bool blocks_breasts = false;
		private bondage_gear_soul soul_ins = null;

		public bondage_gear_soul soul
		{
			get
			{
				if (soul_ins == null)
					soul_ins = (bondage_gear_soul)Activator.CreateInstance(soul_type);
				return soul_ins;
			}
		}
	}

	public class bondage_gear_soul
	{
		// Adds the bondage gear's associated HediffDef and spawns a matching holokey
		public virtual void on_wear(Pawn wearer, Apparel gear)
		{
			var def = (bondage_gear_def)gear.def;

			if (def.equipped_hediff != null)
				wearer.health.AddHediff(def.equipped_hediff);

			var gear_stamp = gear.TryGetComp<CompHoloCryptoStamped>();
			if (gear_stamp != null)
			{
				var key = ThingMaker.MakeThing(xxx.holokey);
				var key_stamp = key.TryGetComp<CompHoloCryptoStamped>();
				key_stamp.copy_stamp_from(gear_stamp);
				if (wearer.Map != null)
					GenSpawn.Spawn(key, wearer.Position, wearer.Map);
				else
					wearer.inventory.TryAddItemNotForSale(key);
			}
		}

		// Removes the gear's HediffDef
		public virtual void on_remove(Apparel gear, Pawn former_wearer)
		{
			var def = (bondage_gear_def)gear.def;
			if (def.equipped_hediff != null)
			{
				var hed = former_wearer.health.hediffSet.GetFirstHediffOfDef(def.equipped_hediff);
				if (hed != null)
					former_wearer.health.RemoveHediff(hed);
			}
		}
	}

	// Give bondage gear an extremely low score when it's not being worn so pawns never equip it on themselves and give
	// it an extremely high score when it is being worn so pawns never try to take it off to equip something "better".
	public class bondage_gear : Apparel
	{
		public override float GetSpecialApparelScoreOffset()
		{
			return (Wearer == null) ? -1e5f : 1e5f;
		}
	}

	public class armbinder : bondage_gear
	{
		// Prevents pawns in armbinders from melee attacking
		//public override bool AllowVerbCast (IntVec3 root, TargetInfo targ)
		//{
		//	return false;
		//}
	}

	public class force_off_gear_def : RecipeDef
	{
		public ThingDef removes_apparel;
		public BodyPartDef failure_affects;
		public List<BodyPartDef> destroys_one_of = null;
		public List<BodyPartDef> major_burns_on = null;
		public List<BodyPartDef> minor_burns_on = null;
	}
}


code:
using UnityEngine;

using Verse;

namespace rjw
{
	[StaticConstructorOnStartup]
	internal static class comfort_prisoner_true_tex
	{
		public static Texture2D gizmo = ContentFinder<Texture2D>.Get("UI/Commands/comfort_prisoner_true");
	}

	[StaticConstructorOnStartup]
	internal static class comfort_prisoner_false_tex
	{
		public static Texture2D gizmo = ContentFinder<Texture2D>.Get("UI/Commands/comfort_prisoner_false");
	}

	[StaticConstructorOnStartup]
	internal static class comfort_prisoner_invisible_tex
	{
		public static Texture2D gizmo = ContentFinder<Texture2D>.Get("UI/Commands/comfort_prisoner_invisible");
	}

	public static class comfort_prisoners
	{
		public const int max_rapists_per_prisoner = 6;//TODO: make option

		public static DesignationDef designation_def = DefDatabase<DesignationDef>.GetNamed("ComfortPrisoner");

		public static DesignationDef designation_def_no_sticky = DefDatabase<DesignationDef>.GetNamed("ComfortPrisonerNoSticky");

		public static bool is_designated(Pawn p)
		{
			foreach (var des in p.Map.designationManager.AllDesignationsOn(p))
				if (des.def == designation_def || des.def == designation_def_no_sticky)
					return true;

			return false;
		}
	}
}


code:
using Verse;

namespace rjw
{
	public class config : Def
	{
		// Feature Toggles
		public bool animals_enabled;

		public bool comfort_prisoners_enabled;
		public bool colonists_can_be_comfort_prisoners;
		public bool cum_enabled;
		public bool rape_me_sticky_enabled;
		public bool sounds_enabled;
		public bool stds_enabled;
		public bool bondage_gear_enabled;
		public bool nymph_joiners_enabled;
		public bool whore_beds_enabled;
		public bool necro_enabled;
		public bool beastiality_enabled;
		public bool random_rape_enabled;
		public bool always_accept_whores;
		public bool nymphs_always_JoinInBed;
		public bool zoophis_always_rape;
		public bool rapists_always_rape;
		public bool pawns_always_do_fapping;
		public bool pawns_always_rapeCP;
		public bool whores_always_findjob;

		// Display Toggles
		public bool show_regular_dick_and_vag;

		// STD config
		public bool std_show_roll_to_catch;

		public float std_min_severity_to_pitch;
		public float std_env_pitch_cleanliness_exaggeration;
		public float std_env_pitch_dirtiness_exaggeration;
		public float std_outdoor_cleanliness;

		// Age Config
		//public int sex_free_for_all_age;
		//public int sex_minimum_age;

		public float significant_pain_threshold;    // 0.6
		public float extreme_pain_threshold;        // 0.8
		public float base_chance_to_hit_prisoner;   // 50
		public int min_ticks_between_hits;          // 500
		public int max_ticks_between_hits;          // 700

		public float max_nymph_fraction;
		public float opp_inf_initial_immunity;
		public float comfort_prisoner_rape_mtbh_mul;
		public float whore_mtbh_mul;
		public float nymph_spawn_with_std_mul;
		//public float chance_to_rim;
	}
}


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

namespace rjw
{
	public class std_def : Verse.Def
	{
		public HediffDef hediff_def;
		public HediffDef cohediff_def = null;
		public float catch_chance;
		public float environment_pitch_chance = 0.0f;
		public float spawn_chance = 0.0f;
		public float spawn_severity = 0.0f;
		public float autocure_below_severity = -1.0f;
		public bool applied_on_genitals;
	}

	public static class std
	{
		public static std_def hiv;
		public static std_def herpes;
		public static std_def warts;
		public static std_def syphilis;
		public readonly static HediffDef immunodeficiency = DefDatabase<HediffDef>.GetNamed("Immunodeficiency");

		public static void init()
		{
			hiv = DefDatabase<std_def>.GetNamed("HIV");
			herpes = DefDatabase<std_def>.GetNamed("Herpes");
			warts = DefDatabase<std_def>.GetNamed("Warts");
			syphilis = DefDatabase<std_def>.GetNamed("Syphilis");
		}

		public static List<std_def> all
		{
			get
			{
				return DefDatabase<std_def>.AllDefsListForReading;
			}
		}

		// Returns how severely affected this pawn's crotch is by rashes and warts, on a scale from 0 to 3.
		public static int genital_rash_severity(Pawn p)
		{
			var tr = 0;

			var her = p.health.hediffSet.GetFirstHediffOfDef(herpes.hediff_def);
			if ((her != null) && (her.Severity >= 0.25f))
				++tr;

			var war = p.health.hediffSet.GetFirstHediffOfDef(warts.hediff_def);
			if (war != null)
				tr += (war.Severity < 0.40f) ? 1 : 2;

			return tr;
		}

		public static Hediff get_infection(Pawn p, std_def sd)
		{
			return p.health.hediffSet.GetFirstHediffOfDef(sd.hediff_def);
		}

		public static float get_severity(Pawn p, std_def sd)
		{
			var hed = get_infection(p, sd);
			return (hed != null) ? hed.Severity : 0.0f;
		}

		public static Hediff infect(Pawn p, std_def sd, bool include_coinfection = true)
		{
			var existing = get_infection(p, sd);
			if (existing == null)
			{
				var part = (sd.applied_on_genitals) ? xxx.genitals : null;
				p.health.AddHediff(sd.hediff_def, part);
				if (include_coinfection && (sd.cohediff_def != null))
					p.health.AddHediff(sd.cohediff_def, part);
				//Log.Message("[RJW]std::infect genitals std");
				return get_infection(p, sd);
			}
			else
				return existing;
		}

		public static void show_infection_letter(Pawn p, std_def sd, String source = null, float? chance = null)
		{
			StringBuilder info;
			{
				info = new StringBuilder();
				info.Append(p.NameStringShort + " has caught " + sd.label + ((source != null) ? " from " + source + "." : ""));
				if (chance.HasValue)
					info.Append(" (" + chance.Value.ToStringPercent() + " chance)");
				info.AppendLine(); info.AppendLine();
				info.Append(sd.description);
			}
			Find.LetterStack.ReceiveLetter("Infection: " + sd.label, info.ToString(), LetterDefOf.ThreatSmall, p);
		}

		public static void update(Pawn p)
		{
			update_immunodeficiency(p);

			// Check if any infections are below the autocure threshold and cure them if so
			foreach (var sd in all)
			{
				var inf = get_infection(p, sd);
				if ((inf != null) && (inf.Severity < sd.autocure_below_severity))
				{
					p.health.RemoveHediff(inf);
					if (sd.cohediff_def != null)
					{
						var coinf = p.health.hediffSet.GetFirstHediffOfDef(sd.cohediff_def);
						if (coinf != null)
							p.health.RemoveHediff(coinf);
					}
				}
			}

			roll_for_syphilis_damage(p);
		}

		public static bool is_wasting_away(Pawn p)
		{
			var id = p.health.hediffSet.GetFirstHediffOfDef(immunodeficiency);
			return ((id != null) && (id.CurStageIndex > 0));
		}

		public static void update_immunodeficiency(Pawn p)
		{
			var min_bf_for_id = 1.0f - immunodeficiency.minSeverity;
			var id = p.health.hediffSet.GetFirstHediffOfDef(immunodeficiency);
			var bf = p.health.capacities.GetLevel(PawnCapacityDefOf.BloodFiltration);
			var has = (id != null);
			var should_have = (bf <= min_bf_for_id);

			if (has && (!should_have))
			{
				p.health.RemoveHediff(id);
				id = null;
			}
			else if ((!has) && should_have)
			{
				p.health.AddHediff(immunodeficiency);
				id = p.health.hediffSet.GetFirstHediffOfDef(immunodeficiency);
			}

			if (id != null)
			{
				id.Severity = 1.0f - bf;

				// Roll for and apply opportunistic infections:
				// Pawns will have a 90% chance for at least one infection each year at 0% filtration, and a 0%
				// chance at 40% filtration, scaling linearly.
				// Let x = chance infected per roll
				// Then chance not infected per roll = 1 - x
				// And chance not infected on any roll in one day = (1 - x) ^ (60000 / 150) = (1 - x) ^ 400
				// And chance not infected on any roll in one year = (1 - x) ^ (400 * 60) = (1 - x) ^ 24000
				// So 0.10 = (1 - x) ^ 24000
				//    log (0.10) = 24000 log (1 - x)
				//    x = 0.00009593644334648975435114691213 = ~96 in 1 million
				// Important Note:
				// this function is called from Need_Sex::NeedInterval(), where it involves a needsex_tick and a std_tick to actually trigger this update_immunodeficiency.
				// j(this is not exactly the same as the value in Need_Sex, that value is 0, but here j should be 1) std_ticks per this function called, k needsex_ticks per std_tick, 150 ticks per needsex_tick, and x is the chance per 150 ticks,
				// The new equation should be .1 = (1-x)^(24000/kj)
				// log(.1) = (24000/kj) log(1-x),  so log(1-x)= (kj/24000) log(.1), 1-x = .1^(kj/24000), x= 1-.1^(kj/24000)
				// Since k=10,j=1, so kj=10, new x is 1-.1^(10/24000)=0.0009589504, let it be 959/1000000
				if ((Rand.RangeInclusive(1, 1000000) <= 959) && (Rand.Value < bf / min_bf_for_id))
				{
					BodyPartRecord part;
					{
						var rv = Rand.Value;
						if (rv < 0.25f)
							part = BodyDefOf.Human.AllParts.Find((BodyPartRecord bpr) => String.Equals(bpr.def.defName, "Jaw"));
						else if (rv < 0.50f)
							part = BodyDefOf.Human.AllParts.Find((BodyPartRecord bpr) => String.Equals(bpr.def.defName, "LeftLung"));
						else if (rv < 0.75f)
							part = BodyDefOf.Human.AllParts.Find((BodyPartRecord bpr) => String.Equals(bpr.def.defName, "RightLung"));
						else
							part = BodyDefOf.Human.AllParts.RandomElement();
					}

					if ((part != null) &&
						(!p.health.hediffSet.PartIsMissing(part)) && (!p.health.hediffSet.HasDirectlyAddedPartFor(part)) &&
						(p.health.hediffSet.GetFirstHediffOfDef(HediffDefOf.WoundInfection) == null) && // If the pawn already has a wound infection, we can't properly set the immunity for the new one
						(p.health.immunity.GetImmunity(HediffDefOf.WoundInfection) <= 0.0f))
					{ // Dont spawn infection if pawn already has immunity
						p.health.AddHediff(HediffDefOf.WoundInfection, part);
						p.health.HealthTick(); // Creates the immunity record
						var ir = p.health.immunity.GetImmunityRecord(HediffDefOf.WoundInfection);
						if (ir != null)
							ir.immunity = xxx.config.opp_inf_initial_immunity;
						Find.LetterStack.ReceiveLetter("Opportunistic Infection", "RJW_Opportunistic_Infection_Message".Translate(new object[] { p.NameStringShort }), LetterDefOf.ThreatSmall, null);
					}
				}
			}
		}

		public static void roll_to_catch(Pawn catcher, Pawn pitcher)
		{
			var has_artificial_genitals = catcher.health.hediffSet.HasDirectlyAddedPartFor(xxx.genitals);
			var thru_genitals = (!has_artificial_genitals) ? 1.00f : 0.15f;
			var on_genitals = (!has_artificial_genitals) ? 1.00f : 0.00f;

			// Roll for and apply food poisoning from rimming(a.k.a. licking anus)
			// Edited by nizhuan-jjr: get food poisoning by licking anus makes no senses, so I choose to commnet out and save some computation here.
			/*
            if (Genital_Helper.has_anus(catcher)&&(Rand.Value < xxx.config.chance_to_rim) && (!pitcher.health.hediffSet.HasHediff(HediffDefOf.FoodPoisoning))) {
                pitcher.health.AddHediff(HediffDefOf.FoodPoisoning);
                if (PawnUtility.ShouldSendNotificationAbout(pitcher)) {
                    Messages.Message("RJW_Poisoned_by_Rimming_Message".Translate(new object[] {pitcher.NameStringShort, catcher.NameStringShort}), pitcher, MessageTypeDefOf.NegativeEvent);
                }
            }
            */

			float cleanliness_factor;
			{
				var room = catcher.GetRoom();
				var cle = (room != null) ? room.GetStat(RoomStatDefOf.Cleanliness) : xxx.config.std_outdoor_cleanliness;
				var exa = (cle >= 0.0f) ? xxx.config.std_env_pitch_cleanliness_exaggeration : xxx.config.std_env_pitch_dirtiness_exaggeration;
				cleanliness_factor = Mathf.Max(0.0f, 1.0f - exa * cle);
			}

			if (xxx.config.std_show_roll_to_catch)
				//--Log.Message(catcher.NameStringShort + " is rolling to catch STDs (cleanliness factor: " + cleanliness_factor.ToString() + ")" + ((!has_artificial_genitals) ? "" : " (has artificial genitals)"));

				foreach (var sd in all)
				{
					if (!catcher.health.hediffSet.HasHediff(sd.hediff_def))
					{
						if (catcher.health.immunity.GetImmunity(sd.hediff_def) <= 0.0f)
						{
							var catch_chance = sd.catch_chance * (sd.applied_on_genitals ? on_genitals : thru_genitals);
							var catch_rv = Rand.Value;
							if (xxx.config.std_show_roll_to_catch)
								//--Log.Message("  Chance to catch " + sd.label + ": " + catch_chance.ToStringPercent() + "; rolled: " + catch_rv.ToString());
								if (catch_rv < catch_chance)
								{
									String pitch_source; float pitch_chance;
									{
										if (get_severity(pitcher, sd) >= xxx.config.std_min_severity_to_pitch)
										{
											pitch_source = pitcher.NameStringShort;
											pitch_chance = 1.0f;
										}
										else
										{
											pitch_source = "the environment";
											pitch_chance = sd.environment_pitch_chance * cleanliness_factor;
											if (!Mod_Settings.std_floor)
											{
												pitch_chance = -9001f;
											}
										}
									}
									var pitch_rv = Rand.Value;

									if (xxx.config.std_show_roll_to_catch)
										//--Log.Message("    Chance to pitch (from " + pitch_source + "): " + pitch_chance.ToStringPercent() + "; rolled: " + pitch_rv.ToString());
										if (pitch_rv < pitch_chance)
										{
											infect(catcher, sd);
											show_infection_letter(catcher, sd, pitch_source, catch_chance * pitch_chance);
											//if (xxx.config.std_show_roll_to_catch)
											//--Log.Message("      INFECTED!");
										}
								}
						}
						//else
						//if (xxx.config.std_show_roll_to_catch)
						//--Log.Message("  Still immune to " + sd.label);
					}
					//else
					//if (xxx.config.std_show_roll_to_catch)
					//--Log.Message("  Already infected with " + sd.label);
				}
		}

		public static void generate_on(Pawn p)
		{
			if (!xxx.is_human(p))
				return;
			var nymph_mul = (!xxx.is_nympho(p)) ? 1.0f : xxx.config.nymph_spawn_with_std_mul;
			foreach (var sd in all)
				if (Rand.Value < sd.spawn_chance * nymph_mul)
				{
					var hed = infect(p, sd, false);
					float sev;
					{
						var r = Rand.Range(sd.hediff_def.minSeverity, sd.hediff_def.maxSeverity);
						sev = Mathf.Clamp(sd.spawn_severity * r, sd.hediff_def.minSeverity, sd.hediff_def.maxSeverity);
					}
					hed.Severity = sev;
				}
		}

		public static void roll_for_syphilis_damage(Pawn p)
		{
			var syp = p.health.hediffSet.GetFirstHediffOfDef(syphilis.hediff_def);
			if ((syp != null) && (syp.Severity >= 0.60f) && (!syp.FullyImmune()))
			{
				// A 30% chance per day of getting any permanent damage works out to ~891 in 1 million for each roll
				// The equation is (1-x)^(60000/150)=.7
				// Important Note:
				// this function is called from Need_Sex::NeedInterval(), where it involves a needsex_tick and a std_tick to actually trigger this roll_for_syphilis_damage.
				// j(this is not exactly the same as the value in Need_Sex, that value is 0, but here j should be 1) std_ticks per this function called, k needsex_ticks per std_tick, 150 ticks per needsex_tick, and x is the chance per 150 ticks,
				// The new equation should be .7 = (1-x)^(400/kj)
				// 1-x = .7^(kj/400), x =1-.7^(kj/400)
				// Since k=10,j=1, so kj=10, new x is 1-.7^(10/400)=0.0088772362, let it be 888/100000
				if (Rand.RangeInclusive(1, 100000) <= 888)
				{
					BodyPartRecord part; float sev;
					{
						var rv = Rand.Value;
						if (rv < 0.10f)
						{
							part = BodyDefOf.Human.AllParts.Find((BodyPartRecord bpr) => String.Equals(bpr.def.defName, "Brain"));
							sev = 1.0f;
						}
						else if (rv < 0.50f)
						{
							part = BodyDefOf.Human.AllParts.Find((BodyPartRecord bpr) => String.Equals(bpr.def.defName, "Liver"));
							sev = (float)Rand.RangeInclusive(1, 3);
						}
						else if (rv < 0.75f)
						{
							part = BodyDefOf.Human.AllParts.Find((BodyPartRecord bpr) => String.Equals(bpr.def.defName, "LeftKidney"));
							sev = (float)Rand.RangeInclusive(1, 2);
						}
						else
						{
							part = BodyDefOf.Human.AllParts.Find((BodyPartRecord bpr) => String.Equals(bpr.def.defName, "RightKidney"));
							sev = (float)Rand.RangeInclusive(1, 2);
						}
					}

					if ((part != null) && (!p.health.hediffSet.PartIsMissing(part)) && (!p.health.hediffSet.HasDirectlyAddedPartFor(part)))
					{
						var vir_dam = DefDatabase<DamageDef>.GetNamed("ViralDamage");
						var dam_def = HealthUtility.GetHediffDefFromDamage(vir_dam, p, part);
						var inj = (Hediff_Injury)HediffMaker.MakeHediff(dam_def, p, null);
						inj.Severity = sev;
						inj.TryGetComp<HediffComp_GetsOld>().IsOld = true;
						p.health.AddHediff(inj, part, null);
						Find.LetterStack.ReceiveLetter(syphilis.label + " Damage", "RJW_Syphilis_Damage_Message".Translate(new object[] { p.NameStringShort, (p.gender == Gender.Male ? "Prohis".Translate() : "Proher".Translate()), part.def.label, syphilis.label }), LetterDefOf.ThreatSmall, p);
					}
				}
			}
		}
	}
}