code:
using System.Text;
using Verse;
using RimWorld;
using Multiplayer.API;
using System.Collections.Generic;
using rjw.Modules.Shared.Logs;
using System.Runtime.InteropServices;
using System.Linq;
using System;

namespace rjw
{
	/// <summary>
	/// Comp for things
	/// </summary>
	public class CompThingBodyPart : ThingComp
	{
		/// <summary>
		/// Comp for rjw Thing parts.
		/// </summary>

		public HediffDef_SexPart hediffDef;

		public float? depth;
		public float? maxGirth;
		public float? length;
		public float? girth;
		public float? volume;
		public float? size;				// for label and fallback if no other size is available

		public float originalOwnerSize;
		public string originalOwnerRace;      //race of 1st owner race
		public string previousOwner;          //erm

		public SexFluidDef fluid;
		public float fluidAmount;		// absolute amount of fluid, for display
		public float? partFluidMultiplier;	// multiplier on fluid amount, random to each part

		public bool initialised;

		public CompProperties_ThingBodyPart Props => (CompProperties_ThingBodyPart)props;

		/// <summary>
		/// Thing/part size in label
		/// </summary>

		private string _label = "";
		public override string TransformLabel(string label)
		{
			if (!initialised)
				Init();

			if (_label.NullOrEmpty())
			{
				var humanSize = CalculateSize(1.0f);
				_label = $"{label} ({hediffDef.GetStandardSizeLabel(humanSize)})";
			}

			return _label;
		}

		public override string CompInspectStringExtra()
		{
			return InspectStringLines().ToLineList();
		}

		private IEnumerable<string> InspectStringLines()
		{
			if (length.HasValue)
			{
				yield return "RJW_PartInfo_length".Translate(length.Value.ToString("F1"));
			}
			if (girth.HasValue)
			{
				yield return "RJW_PartInfo_girth".Translate(girth.Value.ToString("F1"));
			}
			if (depth.HasValue && hediffDef.genitalFamily != GenitalFamily.FemaleOvipositor)
			{
				yield return "RJW_PartInfo_depth".Translate(depth.Value.ToString("F1"));
			}
			if (maxGirth.HasValue && hediffDef.genitalFamily != GenitalFamily.FemaleOvipositor)
			{
				yield return "RJW_PartInfo_maxGirth".Translate(maxGirth.Value.ToString("F1"));
			}

			if (volume.HasValue)
			{
				if (PartSizeCalculator.Inversion.TryInvertBreastVolume(hediffDef, volume.Value, 1.0f, out float _, out BreastSize breastSize))
				{
					yield return "RJW_PartInfo_braCup".Translate(breastSize.GetCupSize());
					yield return "RJW_PartInfo_weight".Translate(breastSize.weight.ToString("F3"));
				}
			}

			if (fluid != null)
			{
				yield return "RJW_PartInfo_fluidTypeFluidAmountHeading".Translate(fluid.label, fluidAmount.ToString("F0")).CapitalizeFirst();
			}
		}

		public override void PostExposeData()
		{
			base.PostExposeData();
			Scribe_Defs.Look(ref hediffDef, "hediffDef");
			Scribe_Defs.Look(ref fluid, "fluid");
			Scribe_Values.Look(ref size, "size");
			Scribe_Values.Look(ref originalOwnerSize, "originalOwnerSize");
			Scribe_Values.Look(ref originalOwnerRace, "originalOwnerRace");
			Scribe_Values.Look(ref previousOwner, "previousOwner");
			Scribe_Values.Look(ref fluidAmount, "fluidAmount");
			Scribe_Values.Look(ref partFluidMultiplier, "partFluidMultiplier");
			Scribe_Values.Look(ref initialised, "initialised", defaultValue: true);
			Scribe_Values.Look(ref length, "length");
			Scribe_Values.Look(ref depth, "depth");
			Scribe_Values.Look(ref maxGirth, "maxGirth");
			Scribe_Values.Look(ref girth, "girth");
			Scribe_Values.Look(ref volume, "volume");
		}

		public override IEnumerable<StatDrawEntry> SpecialDisplayStats()
		{
			// Absolutely terrible idea, but this check was originally in the description override as some kind of failsafe,
			// so sticking it here for now so as not to break things
			// TODO: Ensure this can be removed
			if (!initialised)
			{
				Init();
			}

			// Chosen semi-arbitrarily. Change these if stat ordering seems janky.
			int statOrder = StatDisplayOrder.Thing_BodyPartEfficiency + 10;
			var implantCategory = StatCategoryDefOf.Implant;
			var ownerCategory = RJWStatCategoryDefOf.OriginalPartOwner;

			//yield return new StatDrawEntry(category, "RJW_StatEntry_PartSize".Translate(), size.ToString("F2"), "", statOrder++);

			if (fluid != null)
			{
				yield return new StatDrawEntry(implantCategory, "RJW_StatEntry_Fluid".Translate(), fluid.LabelCap, "", statOrder++);
			}

			if (fluidAmount != 0)
			{
				string fluidAmountKey = hediffDef.genitalFamily switch
				{
					GenitalFamily.Penis or GenitalFamily.MaleOvipositor => "RJW_StatEntry_FluidAmount_ejaculation",
					GenitalFamily.Vagina or GenitalFamily.Anus => "RJW_StatEntry_FluidAmount_wetness",
					_ => "RJW_StatEntry_FluidAmount"
				};

				yield return new StatDrawEntry(implantCategory, fluidAmountKey.Translate(), fluidAmount.ToString("F2"), "", statOrder++);
			}

			if (length.HasValue)
			{
				yield return new StatDrawEntry(implantCategory, "RJW_StatEntry_PartLength".Translate(), length.Value.ToString("F2"), "", statOrder++);
			}

			if (girth.HasValue)
			{
				yield return new StatDrawEntry(implantCategory, "RJW_StatEntry_PartGirth".Translate(), girth.Value.ToString("F2"), "", statOrder++);
			}

			if (depth.HasValue)
			{
				yield return new StatDrawEntry(implantCategory, "RJW_StatEntry_PartDepth".Translate(), depth.Value.ToString("F2"), "", statOrder++);
			}

			if (maxGirth.HasValue)
			{
				yield return new StatDrawEntry(implantCategory, "RJW_StatEntry_PartMaxGirth".Translate(), maxGirth.Value.ToString("F2"), "", statOrder++);
			}

			if (volume.HasValue)
			{
				if (PartSizeCalculator.Inversion.TryInvertBreastVolume(hediffDef, volume.Value, 1.0f, out float _, out BreastSize breastSize))
				{
					yield return new StatDrawEntry(implantCategory, "RJW_StatEntry_PartCupSize".Translate(), breastSize.GetCupSize(), "", statOrder++);
					yield return new StatDrawEntry(implantCategory, "RJW_StatEntry_PartWeight".Translate(), breastSize.weight.ToString("F3"), "", statOrder++);
				}
			}

			if (!hediffDef.partTags.NullOrEmpty())
			{
				var tags = hediffDef.partTags;
				yield return new StatDrawEntry(implantCategory, "RJW_StatEntry_PartTags".Translate(), tags.ToCommaList(), "", statOrder++);
			}

			// Ownership info
			if (!previousOwner.NullOrEmpty())
			{
				yield return new StatDrawEntry(ownerCategory, "RJW_StatEntry_PreviousOwner".Translate(), previousOwner, "", 1);
			}
			if (!originalOwnerRace.NullOrEmpty())
			{
				yield return new StatDrawEntry(ownerCategory, "RJW_StatEntry_OriginalOwnerRace".Translate(), originalOwnerRace, "", 2);
			}
			if (originalOwnerSize > 0f)
			{
				yield return new StatDrawEntry(ownerCategory, "RJW_StatEntry_OriginalOwnerSize".Translate(), originalOwnerSize.ToString("F1"), "", 3);
			}
		}

		public override void PostPostMake()
		{
			base.PostPostMake();
			Init();
		}

		/// <summary>
		/// fill comp data
		/// </summary>
		[SyncMethod]
		private void Init(Pawn pawn = null)
		{
			// TODO: Maybe un-link this from ThingDef xml and instead set randomly or inherit from removed part
			hediffDef = Props.hediffDef;

			bool originalOwnerIsKnown = true;

			if (pawn == null)
			{
				InitFromDef(hediffDef);
				initialised = true;
			}
			else
			{
				Hediff hd = HediffMaker.MakeHediff(hediffDef, pawn);

				HediffComp_SexPart hediffComp = hd.TryGetComp<HediffComp_SexPart>();
				if (hediffComp != null)
				{
					InitFromComp(hediffComp, originalOwnerIsKnown);
				}
			}
		}

		public bool HasSize() {
			return length.HasValue || girth.HasValue || depth.HasValue || maxGirth.HasValue || volume.HasValue || size.HasValue;
		}

		public float CalculateSize(float newBodySize)
		{
			// try invert our measurments to get an appropriate severity for the new body size
			if(length.HasValue)
			{
				if(PartSizeCalculator.Inversion.TryInvertLength(hediffDef, length.Value, newBodySize, out float newSize))
				{
					return newSize;
				}
			}
			if(girth.HasValue)
			{
				if(PartSizeCalculator.Inversion.TryInvertGirth(hediffDef, girth.Value, newBodySize, out float newSize))
				{
					return newSize;
				}
			}
			if(depth.HasValue)
			{
				if(PartSizeCalculator.Inversion.TryInvertLength(hediffDef, depth.Value, newBodySize, out float newSize))
				{
					return newSize;
				}
			}
			if(maxGirth.HasValue)
			{
				if(PartSizeCalculator.Inversion.TryInvertGirth(hediffDef, maxGirth.Value, newBodySize, out float newSize))
				{
					return newSize;
				}
			}
			if(volume.HasValue)
			{
				if(PartSizeCalculator.Inversion.TryInvertBreastVolume(hediffDef, volume.Value, newBodySize, out float newSize, out BreastSize _))
				{
					return newSize;
				}
			}

			if(size.HasValue)
			{
				// fallback to size if nothing else is available
				return size.Value;
			}

			// really got nothing, random size
			return hediffDef.GetRandomSize();
		}

		public void InitFromComp(HediffComp_SexPart hediffComp, bool recordOriginalOwner = true)
		{
			hediffDef = hediffComp.Def;
			fluid = hediffComp.Fluid;
			fluidAmount = hediffComp.FluidAmount;
			partFluidMultiplier = hediffComp.partFluidMultiplier;
			size = hediffComp.GetSeverity();

			if (!hediffDef.genitalTags.NullOrEmpty())
			{
				if (hediffDef.genitalTags.Contains(GenitalTag.CanPenetrate))
				{
					if (PartSizeCalculator.TryGetLength(hediffComp.parent, out float length))
					{
						this.length = length;
					}
					if (PartSizeCalculator.TryGetGirth(hediffComp.parent, out float girth))
					{
						this.girth = girth;
					}
				}
				if (hediffDef.genitalTags.Contains(GenitalTag.CanBePenetrated))
				{
					if (PartSizeCalculator.TryGetLength(hediffComp.parent, out float depth))
					{
						this.depth = depth;
					}
					if (PartSizeCalculator.TryGetGirth(hediffComp.parent, out float maxGirth))
					{
						this.maxGirth = maxGirth;
					}
				}
			}
			if (PartSizeCalculator.TryGetBreastSize(hediffComp.parent, out BreastSize breastSize))
			{
				this.volume = breastSize.volume;
			}

			if (recordOriginalOwner)
			{
				originalOwnerSize = hediffComp.originalOwnerSize;
				previousOwner = hediffComp.Pawn?.LabelNoCount;
				if (hediffComp.Pawn.IsHuman() || hediffComp.Pawn.Name != null)
				{
					originalOwnerRace = hediffComp.Pawn?.kindDef.race.LabelCap;
				}
			}
			initialised = true;

		}

		[SyncMethod]
		private void InitFromDef(HediffDef_SexPart hediffDef)
		{
			this.hediffDef = hediffDef;
			var size = hediffDef.GetRandomSize();
			var bodySize = 1.0f;

			fluid = hediffDef.fluid;
			partFluidMultiplier = hediffDef.GetRandomPartFluidMultiplier();
			var totalFluidMultiplier = hediffDef.GetFluidMultiplier(size, partFluidMultiplier.Value, bodySize);
			fluidAmount = hediffDef.GetFluidAmount(totalFluidMultiplier);

			if (!hediffDef.genitalTags.NullOrEmpty())
			{
				if (hediffDef.genitalTags.Contains(GenitalTag.CanPenetrate))
				{
					if (PartSizeCalculator.Internal.TryGetLength(hediffDef, size, bodySize, out float length))
					{
						this.length = length;
					}
					if (PartSizeCalculator.Internal.TryGetGirth(hediffDef, size, bodySize, out float girth))
					{
						this.girth = girth;
					}
				}
				if (hediffDef.genitalTags.Contains(GenitalTag.CanBePenetrated))
				{
					if (PartSizeCalculator.Internal.TryGetLength(hediffDef, size, bodySize, out float depth))
					{
						this.depth = depth;
					}
					if (PartSizeCalculator.Internal.TryGetGirth(hediffDef, size, bodySize, out float maxGirth))
					{
						this.maxGirth = maxGirth;
					}
				}
			}
			if (PartSizeCalculator.Internal.TryGetBreastSize(hediffDef, size, bodySize, out BreastSize breastSize))
			{
				this.volume = breastSize.volume;
			}

			this.size = size;
		}
	}
}


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

namespace rjw
{
	public enum Orientation
	{
		None,
		Asexual,
		Pansexual,
		Heterosexual,
		MostlyHeterosexual,
		LeaningHeterosexual,
		Bisexual,
		LeaningHomosexual,
		MostlyHomosexual,
		Homosexual
	}
}

repo_name: Euclidean/rjw

license: mit


code:
using Verse;
using RimWorld;
using Verse.AI;
using rjw.Modules.Interactions;
using System.Collections.Generic;
using rjw.Modules.Shared.Extensions;

namespace rjw
{
	/// <summary>
	/// data for sex related stuff/outcome
	/// </summary>
	public class SexProps : IExposable
	{
		public Pawn pawn;
		public Pawn partner;
		public bool hasPartner() => partner != null && partner != pawn;
		public InteractionDef dictionaryKey = null;
		public bool usedCondom = false;
		public bool isRape = false;
		public bool isReceiver = false;// as JobDriver_SexBaseReciever
		public bool isRevese = false;// brainfuck with who fucks who, see impregnation for clarity with tags n shit?.     interaction.HasInteractionTag(InteractionTag.Reverse)
		public bool isRapist = false;
		public bool isCoreLovin = false;//vanilla loving, skip some mechanics.  should really clean this up someday
		public bool isWhoring = false;
		public bool canBeGuilty = true;// can initiator pawn be counted guilty for percepts, player initiated/rmb actrions = false

		public int orgasms = 0; // The orgasms had by the pawn
		public SexProps()
		{
		}

		public Pawn initiator => isReceiver ? partner : pawn;
		public Pawn recipient => isReceiver ? pawn : partner;

		// TODO: replace dictionaryKey (breaking change)
		private InteractionDef interactionDef
		{
			get { return dictionaryKey; }
			set { dictionaryKey = value; }
		}
		private SexInteraction _interaction;
		public SexInteraction interaction {
			get
			{
				if (_interaction == null)
					_interaction = new SexInteraction(interactionDef);
				return _interaction;
			}
			set
			{
				_interaction = value;
				if (value != null)
				{
					interactionDef = value.Def;
				}
			}
		}

		public SexInteractionResolved _resolved;
		public SexInteractionResolved resolved {
			get
			{
				if (_resolved == null)
				{
					if (this.interactionDef == null || this.pawn == null || this.partner == null)
						ModLog.Error($"SexProps could not resolve interaction! Missing data. {this.interactionDef}, {this.pawn}, {this.partner}");
					else
					{
						ModLog.Warning("SexProps forced to try resolve interaction. This should only happen on save load.");
						_resolved = SexInteractionHelper.ResolveInteraction(this);
						if(_resolved == null)
						{
							ModLog.Warning($"SexProps failed to resolve {this.interaction.Def} for {this.initiator.GetName()} -> {this.recipient.GetName()}, using empty interaction.");
							new SexInteractionResolved { Interaction = this.interaction, InitiatorParts = new(), RecipientParts = new(), Penetrations = new() };
						}
					}
				}
				return _resolved;
			}
			set
			{
				_resolved = value;
				if (value != null)
				{
					_interaction = value.Interaction;
					interactionDef = value.Interaction.Def;
				}
			}
		}

		private xxx.rjwSextype? _sexType;
		public xxx.rjwSextype sexType { get
			{
				if (_sexType == null)
					_sexType = interaction.Sextype;
				return _sexType ?? xxx.rjwSextype.None;
			}
		}

		public bool IsInitiator() => !isReceiver;

		public bool IsSubmissive() => (isReceiver && !isRevese) || (!isReceiver && isRevese);

		public SexProps GetForPartner()
		{
			return new SexProps
			{
				pawn = partner,
				partner = pawn,
				usedCondom = usedCondom,
				isRape = isRape,
				isReceiver = !isReceiver,
				isRevese = isRevese,
				isRapist = isRapist,
				isCoreLovin = isCoreLovin,
				isWhoring = isWhoring,
				canBeGuilty = canBeGuilty,
				orgasms = orgasms,
				interaction = interaction,
				resolved = resolved
			};
		}

		public void ExposeData()
		{
			Scribe_References.Look(ref pawn, "pawn");
			Scribe_References.Look(ref partner, "partner");
			Scribe_Defs.Look(ref dictionaryKey, "dictionaryKey");
			Scribe_Values.Look(ref usedCondom, "usedCondom");
			Scribe_Values.Look(ref isRape, "isRape");
			Scribe_Values.Look(ref isReceiver, "isReceiver");
			Scribe_Values.Look(ref isRapist, "isRapist");
			Scribe_Values.Look(ref isCoreLovin, "isCoreLovin");
			Scribe_Values.Look(ref isWhoring, "isWhoring");
			Scribe_Values.Look(ref canBeGuilty, "canBeGuilty");
			Scribe_Values.Look(ref orgasms, "orgasms");
		}
	}
}


code:
using RimWorld;

namespace rjw
{
    [DefOf]
    public static class IssueDefOf
    {
		public static IssueDef Lovin;

        static IssueDefOf()
        {
            DefOfHelper.EnsureInitializedInCtor(typeof(IssueDefOf));
        }
    }
}


code:
using RimWorld;

namespace rjw
{
    [DefOf]
    public static class PreceptDefOf
    {
		[MayRequireIdeology]
		public static PreceptDef Lovin_Prohibited;

		[MayRequireIdeology]
		public static PreceptDef Lovin_Horrible;

		[MayRequireIdeology]
		public static PreceptDef Lovin_SpouseOnly_Strict;

		[MayRequireIdeology]
		public static PreceptDef Lovin_SpouseOnly_Moderate;

		[MayRequireIdeology]
		public static PreceptDef Lovin_SpouseOnly_Mild;

		public static PreceptDef Lovin_Free;

		[MayRequireIdeology]
		public static PreceptDef Lovin_FreeApproved;

        static PreceptDefOf()
        {
            DefOfHelper.EnsureInitializedInCtor(typeof(IssueDefOf));
        }
    }
}


code:
using RimWorld;
using Verse;

namespace rjw
{
	[DefOf]
	public static class RJWBodyPartDefOf
	{
		public static BodyPartDef Foot;
		public static BodyPartDef Paw;
		public static BodyPartDef Leg;
		public static BodyPartDef AnimalJaw;
		public static BodyPartDef Tail;
	}
}


code:
using Verse;
using RimWorld;

namespace rjw
{
    [DefOf]
    public static class RJWHediffDefOf
    {
        public static HediffDef HumpShroomAddiction;

        public static HediffDef HumpShroomEffect;
    }
}


code:
using Verse;
using RimWorld;

namespace rjw
{
	[DefOf]
	public static class RJWStatCategoryDefOf
	{
		[DefAlias("RJW_OriginalPartOwner")]
		public static StatCategoryDef OriginalPartOwner;

		static RJWStatCategoryDefOf()
		{
			DefOfHelper.EnsureInitializedInCtor(typeof(RJWStatCategoryDefOf));
		}
	}
}


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

namespace rjw
{
	[DefOf]
	public static class VanillaDutyDefOf
	{
		public static DutyDef EnterTransporter;

		static VanillaDutyDefOf()
		{
			DefOfHelper.EnsureInitializedInCtor(typeof(VanillaDutyDefOf));
		}
	}
}


code:
using Verse;
using RimWorld;

namespace rjw
{
	[DefOf]
	public static class VanillaRoomRoleDefOf
	{
		public static RoomRoleDef Laboratory;

		public static RoomRoleDef DiningRoom;

		public static RoomRoleDef RecRoom;

		static VanillaRoomRoleDefOf()
		{
			DefOfHelper.EnsureInitializedInCtor(typeof(VanillaRoomRoleDefOf));
		}
	}

}


code:
using Verse;
using RimWorld;

namespace rjw
{
	[DefOf]
	public static class VanillaThoughtDefOf
	{
		public static ThoughtDef AteHumanlikeMeatAsIngredient;

		static VanillaThoughtDefOf()
		{
			DefOfHelper.EnsureInitializedInCtor(typeof(VanillaThoughtDefOf));
		}
	}
}


code:
using Verse;
using RimWorld;

namespace rjw
{
	[DefOf]
	public static class VanillaTraitDefOf
	{
		public static TraitDef Tough;

		public static TraitDef Nerves;

		public static TraitDef Beauty;

		public static TraitDef TooSmart;

		public static TraitDef NaturalMood;

		public static TraitDef Cannibal;

		static VanillaTraitDefOf()
		{
			DefOfHelper.EnsureInitializedInCtor(typeof(VanillaTraitDefOf));
		}
	}
}


code:
using Verse;
using RimWorld;
using Multiplayer.API;

namespace rjw
{
	public static class PawnDesignations_Breedee
	{
		public static bool UpdateCanDesignateBreeding(this Pawn pawn)
		{
			//no permission to change designation for NON prisoner hero/ other player
			if (!pawn.CanChangeDesignationPrisoner() && !pawn.CanChangeDesignationColonist())
				return pawn.GetRJWPawnData().CanDesignateBreeding = false;

			//no permission to change designation for prisoner hero/ self
			if (!pawn.CanChangeDesignationPrisoner())
				return pawn.GetRJWPawnData().CanDesignateBreeding = false;

			//cant have penetrative sex
			if (!xxx.can_be_fucked(pawn))
				return pawn.GetRJWPawnData().CanDesignateBreeding = false;

			if (RJWSettings.bestiality_enabled && xxx.is_human(pawn))
			{
				if (!pawn.IsDesignatedHero())
				{
					if ((xxx.is_zoophile(pawn) || (RJWSettings.override_RJW_designation_checks && !MP.IsInMultiplayer)) && pawn.IsColonist)
						return pawn.GetRJWPawnData().CanDesignateBreeding = true;
				}
				else if (pawn.IsHeroOwner())
					return pawn.GetRJWPawnData().CanDesignateBreeding = true;

				if (pawn.IsPrisonerOfColony || xxx.is_slave(pawn))
					return pawn.GetRJWPawnData().CanDesignateBreeding = true;
			}

			if (RJWSettings.animal_on_animal_enabled && xxx.is_animal(pawn)
				&& pawn.Faction == Faction.OfPlayer)
				return pawn.GetRJWPawnData().CanDesignateBreeding = true;

			return pawn.GetRJWPawnData().CanDesignateBreeding = false;
		}
		public static bool CanDesignateBreeding(this Pawn pawn)
		{
			return pawn.GetRJWPawnData().CanDesignateBreeding;
		}
		public static void ToggleBreeding(this Pawn pawn)
		{
			pawn.UpdateCanDesignateBreeding();
			if (pawn.CanDesignateBreeding())
			{
				if (!pawn.IsDesignatedBreeding())
					DesignateBreeding(pawn);
				else
					UnDesignateBreeding(pawn);
			}
		}
		public static bool IsDesignatedBreeding(this Pawn pawn)
		{
			if (pawn.GetRJWPawnData().Breeding)
			{
				if (!xxx.is_animal(pawn))
				{
					if (!RJWSettings.bestiality_enabled)
						UnDesignateBreeding(pawn);

					else if (!pawn.IsDesignatedHero())
						if (!(xxx.is_zoophile(pawn) || pawn.IsPrisonerOfColony || xxx.is_slave(pawn)))
							if (!(RJWSettings.WildMode || (RJWSettings.override_RJW_designation_checks && !MP.IsInMultiplayer)))
								UnDesignateBreeding(pawn);
				}
				else
				{
					if (!RJWSettings.animal_on_animal_enabled)
						UnDesignateBreeding(pawn);

					else if (!pawn.Faction?.IsPlayer ?? false)
						UnDesignateBreeding(pawn);
				}
				if (pawn.Dead)
					pawn.UnDesignateBreeding();
			}

			return pawn.GetRJWPawnData().Breeding;
		}
		[SyncMethod]
		public static void DesignateBreeding(this Pawn pawn)
		{
			DesignatorsData.rjwBreeding.AddDistinct(pawn);
			pawn.GetRJWPawnData().Breeding = true;
		}
		[SyncMethod]
		public static void UnDesignateBreeding(this Pawn pawn)
		{
			DesignatorsData.rjwBreeding.Remove(pawn);
			pawn.GetRJWPawnData().Breeding = false;
		}
	}
}


code:
using Verse;
using RimWorld;
using Multiplayer.API;

namespace rjw
{
	public static class PawnDesignations_Breeder
	{
		public static bool UpdateCanDesignateBreedingAnimal(this Pawn pawn)
		{
			//no permission to change designation for NON prisoner hero/ other player
			if (!pawn.CanChangeDesignationPrisoner() && !pawn.CanChangeDesignationColonist())
				return pawn.GetRJWPawnData().CanDesignateBreedingAnimal = false;

			//no permission to change designation for prisoner hero/ self
			if (!pawn.CanChangeDesignationPrisoner())
				return pawn.GetRJWPawnData().CanDesignateBreedingAnimal = false;

			//Log.Message("CanDesignateAnimal for " + xxx.get_pawnname(pawn) + " " + SaveStorage.bestiality_enabled);
			//Log.Message("checking animal props " + (pawn.Faction?.IsPlayer.ToString()?? "tynanfag") + xxx.is_animal(pawn) + xxx.can_rape(pawn));
			if ((RJWSettings.bestiality_enabled || RJWSettings.animal_on_animal_enabled)
				&& xxx.is_animal(pawn)
				&& xxx.can_fuck(pawn)
				&& pawn.Faction == Faction.OfPlayer)
				return pawn.GetRJWPawnData().CanDesignateBreedingAnimal = true;

			return pawn.GetRJWPawnData().CanDesignateBreedingAnimal = false;
		}
		public static bool CanDesignateBreedingAnimal(this Pawn pawn)
		{
			return pawn.GetRJWPawnData().CanDesignateBreedingAnimal;
		}
		public static void ToggleBreedingAnimal(this Pawn pawn)
		{
			pawn.UpdateCanDesignateBreedingAnimal();
			if (pawn.CanDesignateBreedingAnimal())
			{
				if (!pawn.IsDesignatedBreedingAnimal())
					DesignateBreedingAnimal(pawn);
				else
					UnDesignateBreedingAnimal(pawn);
			}
		}
		public static bool IsDesignatedBreedingAnimal(this Pawn pawn)
		{
			if (pawn.GetRJWPawnData().BreedingAnimal)
			{
				if (!pawn.Faction?.IsPlayer ?? false)
					UnDesignateBreedingAnimal(pawn);

				if (pawn.Dead)
					pawn.UnDesignateBreedingAnimal();
			}

			return pawn.GetRJWPawnData().BreedingAnimal;
		}
		[SyncMethod]
		public static void DesignateBreedingAnimal(this Pawn pawn)
		{
			DesignatorsData.rjwBreedingAnimal.AddDistinct(pawn);
			pawn.GetRJWPawnData().BreedingAnimal = true;
		}
		[SyncMethod]
		public static void UnDesignateBreedingAnimal(this Pawn pawn)
		{
			DesignatorsData.rjwBreedingAnimal.Remove(pawn);
			pawn.GetRJWPawnData().BreedingAnimal = false;
		}
	}
}


code:
using Verse;
using Multiplayer.API;

namespace rjw
{
	public static class PawnDesignations_Comfort
	{
		public static bool UpdateCanDesignateComfort(this Pawn pawn)
		{
			//rape disabled
			if (!RJWSettings.rape_enabled)
				return pawn.GetRJWPawnData().CanDesignateComfort = false;

			//no permission to change designation for NON prisoner hero/ other player
			if (!pawn.CanChangeDesignationPrisoner() && !pawn.CanChangeDesignationColonist())
				return pawn.GetRJWPawnData().CanDesignateComfort = false;

			//no permission to change designation for prisoner hero/ self
			if (!pawn.CanChangeDesignationPrisoner())
				return pawn.GetRJWPawnData().CanDesignateComfort = false;

			//cant sex
			if (!(xxx.can_fuck(pawn) || xxx.can_be_fucked(pawn)))
				return pawn.GetRJWPawnData().CanDesignateComfort = false;

			if (!pawn.IsDesignatedHero())
			{
				if ((xxx.is_masochist(pawn) || (RJWSettings.override_RJW_designation_checks && !MP.IsInMultiplayer)) && pawn.IsColonist)
					return pawn.GetRJWPawnData().CanDesignateComfort = true;
			}
			else if (pawn.IsHeroOwner())
				return pawn.GetRJWPawnData().CanDesignateComfort = true;

			if (pawn.IsPrisonerOfColony || xxx.is_slave(pawn))
				return pawn.GetRJWPawnData().CanDesignateComfort = true;

			return pawn.GetRJWPawnData().CanDesignateComfort = false;
		}
		public static bool CanDesignateComfort(this Pawn pawn)
		{
			return pawn.GetRJWPawnData().CanDesignateComfort;
		}
		public static void ToggleComfort(this Pawn pawn)
		{
			pawn.UpdateCanDesignateComfort();
			if (pawn.CanDesignateComfort())
			{
				if (!pawn.IsDesignatedComfort())
					DesignateComfort(pawn);
				else
					UnDesignateComfort(pawn);
			}
		}
		public static bool IsDesignatedComfort(this Pawn pawn)
		{
			if (pawn.GetRJWPawnData().Comfort)
			{
				if (!pawn.IsDesignatedHero())
				{
					if (!pawn.IsPrisonerOfColony)
						if (!(xxx.is_masochist(pawn) || xxx.is_slave(pawn)))
						{
							if (!pawn.IsColonist)
								UnDesignateComfort(pawn);
							else if (!(RJWSettings.WildMode || (RJWSettings.override_RJW_designation_checks && !MP.IsInMultiplayer)))
								UnDesignateComfort(pawn);
						}
				}

				if (pawn.Dead)
					pawn.UnDesignateComfort();
			}

			return pawn.GetRJWPawnData().Comfort;
		}
		[SyncMethod]
		public static void DesignateComfort(this Pawn pawn)
		{
			DesignatorsData.rjwComfort.AddDistinct(pawn);
			pawn.GetRJWPawnData().Comfort = true;
		}
		[SyncMethod]
		public static void UnDesignateComfort(this Pawn pawn)
		{
			DesignatorsData.rjwComfort.Remove(pawn);
			pawn.GetRJWPawnData().Comfort = false;
		}
	}
}


code:
using Verse;
using RimWorld;
using Multiplayer.API;

namespace rjw
{
	public static class PawnDesignations_Hero
	{
		public static bool UpdateCanDesignateHero(this Pawn pawn)
		{
			if ((RJWSettings.RPG_hero_control)
				&& xxx.is_human(pawn)
				&& pawn.IsColonist
				&& !xxx.is_slave(pawn)
				&& !pawn.IsPrisoner)
			{
				if (!pawn.IsDesignatedHero())
				{
					foreach (Pawn item in DesignatorsData.rjwHero)
					{
						if (item.IsHeroOwner())
						{
							if (RJWSettings.RPG_hero_control_Ironman && !item.GetRJWPawnData().Ironman)
								SetHeroIronman(item);
							if (item.Dead && !item.GetRJWPawnData().Ironman)
							{
								UnDesignateHero(item);
								//Log.Warning("CanDesignateHero:: "  + MP.PlayerName + " hero is dead remove hero tag from " + item.Name);
							}
							else
							{
								//Log.Warning("CanDesignateHero:: "  + MP.PlayerName + " already has hero - " + item.Name);
								return pawn.GetRJWPawnData().CanDesignateHero = false;
							}

						}
						else
							continue;
					}
					return pawn.GetRJWPawnData().CanDesignateHero = true;
				}
			}
			return pawn.GetRJWPawnData().CanDesignateHero = false;
		}
		public static bool CanDesignateHero(this Pawn pawn)
		{
			return pawn.GetRJWPawnData().CanDesignateHero;
		}
		public static void ToggleHero(this Pawn pawn)
		{
			pawn.UpdateCanDesignateHero();
			if (pawn.CanDesignateHero() && Find.Selector.NumSelected <= 1)
			{
				if (!pawn.IsDesignatedHero())
					DesignateHero(pawn);
			}
		}
		public static bool IsDesignatedHero(this Pawn pawn)
		{
			return pawn.GetRJWPawnData().Hero;
		}
		public static void DesignateHero(this Pawn pawn)
		{
			SyncHero(pawn, MP.PlayerName);
		}
		[SyncMethod]
		public static void UnDesignateHero(this Pawn pawn)
		{
			DesignatorsData.rjwHero.Remove(pawn);
			pawn.GetRJWPawnData().Hero = false;
		}
		public static bool IsHeroOwner(this Pawn pawn)
		{
			if (!MP.enabled)
				return pawn.GetRJWPawnData().HeroOwner == "Player" || pawn.GetRJWPawnData().HeroOwner == null || pawn.GetRJWPawnData().HeroOwner == "";
			else
				return pawn.GetRJWPawnData().HeroOwner == MP.PlayerName;
		}
		[SyncMethod]
		static void SyncHero(Pawn pawn, string theName)
		{
			if (!MP.enabled)
				theName = "Player";
			pawn.GetRJWPawnData().Hero = true;
			pawn.GetRJWPawnData().HeroOwner = theName;
			pawn.GetRJWPawnData().Ironman = RJWSettings.RPG_hero_control_Ironman;
			DesignatorsData.rjwHero.AddDistinct(pawn);
			string text = "RJW_Message_BecameHero".Translate(pawn, theName).CapitalizeFirst();
			Messages.Message(text, pawn, MessageTypeDefOf.NeutralEvent);
			//Log.Message(MP.PlayerName + "  set " + pawn.Name + " to hero:" + pawn.GetPawnData().Hero);

			pawn.UpdatePermissions();
		}
		[SyncMethod]
		public static void SetHeroIronman(this Pawn pawn)
		{
			pawn.GetRJWPawnData().Ironman = true;
		}
	}
}


code:
using Verse;
using Multiplayer.API;

namespace rjw
{
	public static class PawnDesignations_Milking
	{
		public static bool UpdateCanDesignateMilking(this Pawn pawn)
		{
			return pawn.GetRJWPawnData().CanDesignateMilking = false;
		}
		public static bool CanDesignateMilking(this Pawn pawn)
		{
			return pawn.GetRJWPawnData().CanDesignateMilking = false;
		}
		public static void ToggleMilking(this Pawn pawn)
		{
			if (pawn.CanDesignateMilking())
			{
				if (!pawn.IsDesignatedMilking())
					DesignateMilking(pawn);
				else
					UnDesignateMilking(pawn);
			}
		}
		public static bool IsDesignatedMilking(this Pawn pawn)
		{
			if (pawn.GetRJWPawnData().Milking)
			{
				if (!pawn.IsDesignatedHero())
					if (!(pawn.IsColonist || pawn.IsPrisonerOfColony || xxx.is_slave(pawn)))
						UnDesignateMilking(pawn);

				if (pawn.Dead)
					pawn.UnDesignateMilking();
			}

			return pawn.GetRJWPawnData().Milking;
		}
		[SyncMethod]
		public static void DesignateMilking(this Pawn pawn)
		{
			DesignatorsData.rjwMilking.AddDistinct(pawn);
			pawn.GetRJWPawnData().Milking = true;
		}
		[SyncMethod]
		public static void UnDesignateMilking(this Pawn pawn)
		{
			DesignatorsData.rjwMilking.Remove(pawn);
			pawn.GetRJWPawnData().Milking = false;
		}
	}
}


code:
using Verse;
using Multiplayer.API;

namespace rjw
{
	public static class PawnDesignations_Service
	{
		public static bool UpdateCanDesignateService(this Pawn pawn)
		{
			//no permission to change designation for NON prisoner hero/ other player
			if (!pawn.CanChangeDesignationPrisoner() && !pawn.CanChangeDesignationColonist())
				return pawn.GetRJWPawnData().CanDesignateService = false;

			//no permission to change designation for prisoner hero/ self
			if (!pawn.CanChangeDesignationPrisoner())
				return pawn.GetRJWPawnData().CanDesignateService = false;

			//cant sex
			if (!(xxx.can_fuck(pawn) || xxx.can_be_fucked(pawn)))
				return pawn.GetRJWPawnData().CanDesignateService = false;

			if (!pawn.IsDesignatedHero())
			{
				if (pawn.IsColonist)
					return pawn.GetRJWPawnData().CanDesignateService = true;
			}
			else if (pawn.IsHeroOwner())
				return pawn.GetRJWPawnData().CanDesignateService = true;

			if (pawn.IsPrisonerOfColony || xxx.is_slave(pawn))
				return pawn.GetRJWPawnData().CanDesignateService = true;

			return pawn.GetRJWPawnData().CanDesignateService = false;
		}
		public static bool CanDesignateService(this Pawn pawn)
		{
			return pawn.GetRJWPawnData().CanDesignateService;
		}
		public static void ToggleService(this Pawn pawn)
		{
			pawn.UpdateCanDesignateService();
			if (pawn.CanDesignateService())
			{
				if (!pawn.IsDesignatedService())
					DesignateService(pawn);
				else
					UnDesignateService(pawn);
			}
		}
		public static bool IsDesignatedService(this Pawn pawn)
		{
			if (pawn.GetRJWPawnData().Service)
			{
				if (!pawn.IsDesignatedHero())
					if (!(pawn.IsColonist || pawn.IsPrisonerOfColony || xxx.is_slave(pawn)))
						UnDesignateService(pawn);

				if (pawn.Dead)
					pawn.UnDesignateService();
			}

			return pawn.GetRJWPawnData().Service;
		}
		[SyncMethod]
		public static void DesignateService(this Pawn pawn)
		{
			DesignatorsData.rjwService.AddDistinct(pawn);
			pawn.GetRJWPawnData().Service = true;
		}
		[SyncMethod]
		public static void UnDesignateService(this Pawn pawn)
		{
			DesignatorsData.rjwService.Remove(pawn);
			pawn.GetRJWPawnData().Service = false;
		}
	}
}


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

namespace rjw
{
	public static class PawnDesignations_Utility
	{
		public static bool UpdatePermissions(this Pawn pawn)
		{
			pawn.UpdateCanChangeDesignationPrisoner();
			pawn.UpdateCanChangeDesignationColonist();
			pawn.UpdateCanDesignateService();
			pawn.UpdateCanDesignateComfort();
			pawn.UpdateCanDesignateBreedingAnimal();
			pawn.UpdateCanDesignateBreeding();
			pawn.UpdateCanDesignateHero();
			return true;
		}
		public static bool UpdateCanChangeDesignationColonist(this Pawn pawn)
		{
			//check if pawn is a hero of other player
			//if yes - limit widget access in mp for this pawn

			if ((pawn.IsDesignatedHero() && !pawn.IsHeroOwner()))
			{
				//Log.Warning("CanChangeDesignationColonist:: Pawn:" + xxx.get_pawnname(pawn));
				//Log.Warning("CanChangeDesignationColonist:: IsDesignatedHero:" + pawn.IsDesignatedHero());
				//Log.Warning("CanChangeDesignationColonist:: IsHeroOwner:" + pawn.IsHeroOwner());
				return pawn.GetRJWPawnData().CanChangeDesignationColonist = false;
			}

			return pawn.GetRJWPawnData().CanChangeDesignationColonist = true;
		}
		public static bool CanChangeDesignationColonist(this Pawn pawn)
		{
			return pawn.GetRJWPawnData().CanChangeDesignationColonist;
		}
		public static bool UpdateCanChangeDesignationPrisoner(this Pawn pawn)
		{
			//check if player hero is a slave/prisoner
			//if yes - limit widget access in mp for all widgets

			//Stopwatch sw = new Stopwatch();
			//sw.Start();

			//Log.Warning("rjwHero:: Count " + DesignatorsData.rjwHero.Count);
			//Log.Warning("rjwComfort:: Count " + DesignatorsData.rjwComfort.Count);
			//Log.Warning("rjwService:: Count " + DesignatorsData.rjwService.Count);
			//Log.Warning("rjwMilking:: Count " + DesignatorsData.rjwMilking.Count);
			//Log.Warning("rjwBreeding:: Count " + DesignatorsData.rjwBreeding.Count);
			//Log.Warning("rjwBreedingAnimal:: Count " + DesignatorsData.rjwBreedingAnimal.Count);

			foreach (Pawn item in DesignatorsData.rjwHero)
			{
				//Log.Warning("CanChangeDesignationPrisoner:: Pawn:" + xxx.get_pawnname(item));
				//Log.Warning("CanChangeDesignationPrisoner:: IsHeroOwner:" + item.IsHeroOwner());
				if (item.IsHeroOwner())
				{
					if (item.IsPrisonerOfColony || item.IsPrisoner || xxx.is_slave(item))
					{
						//Log.Warning("CanChangeDesignationPrisoner:: Pawn:" + xxx.get_pawnname(item));
						//Log.Warning("CanChangeDesignationPrisoner:: Hero of " + MP.PlayerName);
						//Log.Warning("CanChangeDesignationPrisoner:: is prisoner(colony):" + item.IsPrisonerOfColony);
						//Log.Warning("CanChangeDesignationPrisoner:: is prisoner:" + item.IsPrisoner);
						//Log.Warning("CanChangeDesignationPrisoner:: is slave:" + xxx.is_slave(item));
						return pawn.GetRJWPawnData().CanChangeDesignationPrisoner = false;
					}
				}
			}
			//sw.Stop();
			//Log.Warning("Elapsed={0}" + sw.Elapsed);

			return pawn.GetRJWPawnData().CanChangeDesignationPrisoner = true;
		}
		public static bool CanChangeDesignationPrisoner(this Pawn pawn)
		{
			return pawn.GetRJWPawnData().CanChangeDesignationPrisoner;
		}
	}
}


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

namespace rjw
{
	/// <summary>
	/// Handles creation of RJWdesignations button group for gui
	/// </summary>
	public class RJWdesignations : Command
	{
		//is actually a group of four buttons, but I've wanted them be small and so vanilla gizmo system is mostly bypassed for them

		private const float ContentPadding = 5f;
		private const float IconSize = 32f;
		private const float IconGap = 1f;

		private readonly Pawn parent;
		private Rect gizmoRect;
		/// <summary>
		/// This should keep track of last pressed pseudobutton. It is set in the pseudobutton callback.
		/// Then, the callback to encompassed Gizmo is performed by game, and this field is used to determine what exact button was pressed
		/// The callback is called by the tynancode right after the loop which detects click on button,
		/// so it will be called then and only then when it should be (no unrelated events).
		/// event handling  shit is needed to apply settings to all selected pawns.
		/// </summary>
		private static SubIcon lastClicked;

		static readonly List<SubIcon> subIcons = new List<SubIcon> {
			new Comfort(),
			//new Service(),
			//new BreederHuman(),
			new BreedingHuman(),
			new BreedingAnimal(),
			new Breeder(),
			//new Milking(),
			new Hero(),
			new HelpImStupid()
		};

		public RJWdesignations(Pawn pawn)
		{
			parent = pawn;
			defaultLabel = "RJWdesignations";
			defaultDesc = "RJWdesignations";
		}

		public override GizmoResult GizmoOnGUI(Vector2 topLeft, float maxWidth, GizmoRenderParms parms)
		{
			Rect rect = new Rect(topLeft.x, topLeft.y, 75f, 75f);
			gizmoRect = rect.ContractedBy(ContentPadding);
			Widgets.DrawWindowBackground(rect);

			//Log.Message("RJWgizmo");
			foreach (SubIcon icon in subIcons)
			{
				if (DrawSubIcon(icon))
				{
					lastClicked = icon;
					icon.state = icon.applied(parent);
					return new GizmoResult(GizmoState.Interacted, Event.current);
				}
			}
			return new GizmoResult(GizmoState.Clear);
		}

		//this and class mess below was supposed to be quick shortcut to not write four repeated chunks of code in GizmoOnGUI
		private bool DrawSubIcon(SubIcon icon)
		{
			if (!icon.applicable(parent)) { return false; }
			//Log.Message("sub gizmo");
			Rect iconRect = new Rect(gizmoRect.x + icon.offset.x, gizmoRect.y + icon.offset.y, IconSize, IconSize);
			TooltipHandler.TipRegion(iconRect, icon.desc(parent).Translate());
			bool applied = icon.applied(parent);
			Texture2D texture = applied ? icon.cancel : icon.texture(parent);
			GUI.DrawTexture(iconRect, texture);
			//GUI.color = Color.white;

			return Widgets.ButtonInvisible(iconRect, false);
		}

		public override void ProcessInput(Event ev)
		{
			SubIcon icon = lastClicked;
			if (icon.state)
				icon.unapply(parent);
			else
				icon.apply(parent);
		}

		[StaticConstructorOnStartup]//this  is needed for textures
		public abstract class SubIcon
		{
			public abstract Texture2D texture(Pawn pawn);
			public abstract Vector2 offset { get; }
			public abstract string desc(Pawn pawn);
			public abstract bool applicable(Pawn pawn);
			public abstract bool applied(Pawn pawn);
			public abstract void apply(Pawn pawn);
			public abstract void unapply(Pawn pawn);

			static readonly Texture2D cancellText = ContentFinder<Texture2D>.Get("UI/Commands/cancel");
			public virtual Texture2D cancel => cancellText;

			public bool state;
		}
		[StaticConstructorOnStartup]
		public class Comfort : SubIcon
		{
			//comfort raping
			static readonly Texture2D iconAccept = ContentFinder<Texture2D>.Get("UI/Commands/ComfortPrisoner_off");
			static readonly Texture2D iconRefuse = ContentFinder<Texture2D>.Get("UI/Commands/ComfortPrisoner_Refuse");
			static readonly Texture2D iconCancel = ContentFinder<Texture2D>.Get("UI/Commands/ComfortPrisoner_on");
			public override Texture2D texture(Pawn pawn) => (pawn.CanDesignateComfort() || pawn.IsDesignatedComfort()) && xxx.is_human(pawn) ? iconAccept : iconRefuse;
			public override Texture2D cancel { get; } = iconCancel;

			static readonly Vector2 posComf = new Vector2(IconGap + IconSize, 0);
			public override Vector2 offset => posComf;

			public override string desc(Pawn pawn) => pawn.CanDesignateComfort() ? "ForComfortDesc" :
														!pawn.CanChangeDesignationPrisoner() ? "ForHeroRefuse2Desc" :
														!pawn.CanChangeDesignationColonist() ? "ForHeroRefuse1Desc" : "ForComfortRefuseDesc";

			public override bool applicable(Pawn pawn) => RJWSettings.rape_enabled && xxx.is_human(pawn);
			public override bool applied(Pawn pawn) => pawn.IsDesignatedComfort();
			public override void apply(Pawn pawn) => pawn.ToggleComfort();
			public override void unapply(Pawn pawn) => pawn.ToggleComfort();
		}
		[StaticConstructorOnStartup]
		public class Service : SubIcon
		{
			//whoring
			static readonly Texture2D iconAccept = ContentFinder<Texture2D>.Get("UI/Commands/Service_off");
			static readonly Texture2D iconRefuse = ContentFinder<Texture2D>.Get("UI/Commands/Service_Refuse");
			static readonly Texture2D iconCancel = ContentFinder<Texture2D>.Get("UI/Commands/Service_on");
			public override Texture2D texture(Pawn pawn) => (pawn.CanDesignateService() || pawn.IsDesignatedService()) && xxx.is_human(pawn) ? iconAccept : iconRefuse;
			public override Texture2D cancel { get; } = iconCancel;

			public override Vector2 offset { get; } = new Vector2(0, 0);

			public override string desc(Pawn pawn) => pawn.CanDesignateService() ? "ForServiceDesc" :
														!pawn.CanChangeDesignationPrisoner() ? "ForHeroRefuse2Desc" :
														!pawn.CanChangeDesignationColonist() ? "ForHeroRefuse1Desc" : "ForServiceRefuseDesc";

			public override bool applicable(Pawn pawn) => xxx.is_human(pawn);
			public override bool applied(Pawn pawn) => pawn.IsDesignatedService() && xxx.is_human(pawn);
			public override void apply(Pawn pawn) => pawn.ToggleService();
			public override void unapply(Pawn pawn) => pawn.ToggleService();
		}
		[StaticConstructorOnStartup]
		public class BreedingHuman : SubIcon
		{
			//Breed humanlike
			static readonly Texture2D iconAccept = ContentFinder<Texture2D>.Get("UI/Commands/Breeding_Pawn_off");
			static readonly Texture2D iconRefuse = ContentFinder<Texture2D>.Get("UI/Commands/Breeding_Pawn_Refuse");
			static readonly Texture2D iconCancel = ContentFinder<Texture2D>.Get("UI/Commands/Breeding_Pawn_on");
			public override Texture2D texture(Pawn pawn) => (pawn.CanDesignateBreeding() || pawn.IsDesignatedBreeding()) && xxx.is_human(pawn) ? iconAccept : iconRefuse;
			public override Texture2D cancel { get; } = iconCancel;

			public override Vector2 offset { get; } = new Vector2(0, IconSize + IconGap);

			public override string desc(Pawn pawn) => pawn.CanDesignateBreeding() && xxx.is_human(pawn) ? "ForBreedingDesc" :
														!pawn.CanChangeDesignationPrisoner() ? "ForHeroRefuse2Desc" :
														!pawn.CanChangeDesignationColonist() ? "ForHeroRefuse1Desc" : "ForBreedingRefuseDesc";

			public override bool applicable(Pawn pawn) =>  RJWSettings.bestiality_enabled && xxx.is_human(pawn);
			public override bool applied(Pawn pawn) => pawn.IsDesignatedBreeding() && xxx.is_human(pawn);
			public override void apply(Pawn pawn) => pawn.ToggleBreeding();
			public override void unapply(Pawn pawn) => pawn.ToggleBreeding();
		}

		[StaticConstructorOnStartup]
		public class BreedingAnimal : SubIcon
		{
			//Breed animal
			static readonly Texture2D iconAccept = ContentFinder<Texture2D>.Get("UI/Commands/Breeding_Animal_off");
			static readonly Texture2D iconCancel = ContentFinder<Texture2D>.Get("UI/Commands/Breeding_Animal_on");
			public override Texture2D texture(Pawn pawn) => iconAccept;
			public override Texture2D cancel { get; } = iconCancel;

			public override Vector2 offset { get; } = new Vector2(0, IconSize + IconGap);

			public override string desc(Pawn pawn) => !pawn.CanChangeDesignationPrisoner() ? "ForHeroRefuse2Desc" : "ForBreedingDesc";

			public override bool applicable(Pawn pawn) => (pawn.CanDesignateBreeding() || pawn.IsDesignatedBreeding()) && xxx.is_animal(pawn);
			public override bool applied(Pawn pawn) => pawn.IsDesignatedBreeding() && xxx.is_animal(pawn);
			public override void apply(Pawn pawn) => pawn.ToggleBreeding();
			public override void unapply(Pawn pawn) => pawn.ToggleBreeding();
		}
		[StaticConstructorOnStartup]
		public class Breeder : SubIcon
		{
			//Breeder(fucker) animal
			static readonly Texture2D iconAccept = ContentFinder<Texture2D>.Get("UI/Commands/Breeder_Animal_off");
			static readonly Texture2D iconCancel = ContentFinder<Texture2D>.Get("UI/Commands/Breeder_Animal_on");
			public override Texture2D texture(Pawn pawn) => iconAccept;
			public override Texture2D cancel { get; } = iconCancel;

			public override Vector2 offset { get; } = new Vector2(0, 0);

			public override string desc(Pawn pawn) => !pawn.CanChangeDesignationPrisoner() ? "ForHeroRefuse2Desc" : "ForBreedingAnimalDesc";

			public override bool applicable(Pawn pawn) => pawn.CanDesignateBreedingAnimal() || pawn.IsDesignatedBreedingAnimal();
			public override bool applied(Pawn pawn) => pawn.IsDesignatedBreedingAnimal();
			public override void apply(Pawn pawn) => pawn.ToggleBreedingAnimal();
			public override void unapply(Pawn pawn) => pawn.ToggleBreedingAnimal();
		}
		[StaticConstructorOnStartup]
		public class Milking : SubIcon
		{
			static readonly Texture2D iconAccept = ContentFinder<Texture2D>.Get("UI/Commands/Milking_off");
			static readonly Texture2D iconCancel = ContentFinder<Texture2D>.Get("UI/Commands/Milking_on");
			public override Texture2D texture(Pawn pawn) => iconAccept;
			public override Texture2D cancel { get; } = iconCancel;

			public override Vector2 offset { get; } = new Vector2(IconGap + IconSize, IconSize + IconGap);

			public override string desc(Pawn pawn) => !pawn.CanChangeDesignationPrisoner() ? "ForHeroRefuse2Desc" : "ForMilkingDesc";

			public override bool applicable(Pawn pawn) => pawn.CanDesignateMilking() || pawn.IsDesignatedMilking();
			public override bool applied(Pawn pawn) => pawn.IsDesignatedMilking();
			public override void apply(Pawn pawn) => pawn.ToggleMilking();
			public override void unapply(Pawn pawn) => pawn.ToggleMilking();
		}
		[StaticConstructorOnStartup]
		public class Hero : SubIcon
		{
			static readonly Texture2D iconAccept = ContentFinder<Texture2D>.Get("UI/Commands/Hero_off");
			static readonly Texture2D iconCancel = ContentFinder<Texture2D>.Get("UI/Commands/Hero_on");
			public override Texture2D texture(Pawn pawn) => iconAccept;
			public override Texture2D cancel { get; } = iconCancel;

			public override Vector2 offset { get; } = new Vector2(IconGap + IconSize, IconSize + IconGap);

			//public override string desc(Pawn pawn) => pawn.CanDesignateHero() || (pawn.IsDesignatedHero() && pawn.IsHeroOwner()) ? "ForHeroDesc" : "Hero of " + pawn.GetPawnData().HeroOwner;
			public override string desc(Pawn pawn) => pawn.CanDesignateHero() ? "ForHeroDesc" :
														pawn.IsDesignatedHero() ? "Hero of " + pawn.GetRJWPawnData().HeroOwner : "ForHeroDesc";

			public override bool applicable(Pawn pawn) => pawn.CanDesignateHero() || pawn.IsDesignatedHero();
			public override bool applied(Pawn pawn) => pawn.IsDesignatedHero();
			public override void apply(Pawn pawn) => pawn.ToggleHero();
			public override void unapply(Pawn pawn) => pawn.ToggleHero();
		}
		[StaticConstructorOnStartup]
		public class HelpImStupid : SubIcon
		{
			// a question mark that shows when no other icons show; shows a hint to look at mod settings
			static readonly Texture2D myicon = ContentFinder<Texture2D>.Get("UI/Commands/HelpImStupid");
			public override Texture2D texture(Pawn pawn) => myicon;
			public override Texture2D cancel { get; } = myicon;

			static readonly Vector2 posComf = new Vector2(IconGap + IconSize, 0);
			public override Vector2 offset => posComf;

			public override string desc(Pawn pawn) => "GoToModSettings";

			public override bool applicable(Pawn pawn) => !(RJWSettings.rape_enabled && xxx.is_human(pawn)) && // comfort
														!(pawn.CanDesignateHero() || pawn.IsDesignatedHero()) && // hero
														//!(pawn.CanDesignateMilking() || pawn.IsDesignatedMilking()) && // milking
														!(pawn.CanDesignateBreedingAnimal() || pawn.IsDesignatedBreedingAnimal()) && // breeder
														!((pawn.CanDesignateBreeding() || pawn.IsDesignatedBreeding()) && xxx.is_animal(pawn)) && // breeding animal
														!(RJWSettings.bestiality_enabled && xxx.is_human(pawn));// && // breeding human
														//!(pawn.IsDesignatedService() && xxx.is_human(pawn)); // service

			public override bool applied(Pawn pawn) => true;
			public override void apply(Pawn pawn) { }
			public override void unapply(Pawn pawn) { }
		}
	}
}


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

// Non-pregnancy Biotech-related patches
namespace rjw
{
    [HarmonyPatch]
    class LifeStageWorker_HumanlikeX_Notify_LifeStageStarted
    {
        static IEnumerable<MethodBase> TargetMethods()
        {
            const string lifeStageStarted = nameof(LifeStageWorker.Notify_LifeStageStarted);
            yield return AccessTools.Method(typeof(LifeStageWorker_HumanlikeChild), lifeStageStarted);
            yield return AccessTools.Method(typeof(LifeStageWorker_HumanlikeAdult), lifeStageStarted);
        }

        // Fixes errors caused by trying to spawn a biotech-only effector when a child starts a new lifestage
        // and by trying to send a biotech-only letter when a child turns three
        [HarmonyTranspiler]
        static IEnumerable<CodeInstruction> FixLifeStageStartError(IEnumerable<CodeInstruction> instructions, MethodBase original)
        {
            PropertyInfo thingSpawned = AccessTools.DeclaredProperty(typeof(Thing), nameof(Thing.Spawned));
            MethodInfo shouldSendNotificationsAbout = AccessTools.Method(typeof(PawnUtility), nameof(PawnUtility.ShouldSendNotificationAbout));
            bool foundAny = false;

            foreach (var instruction in instructions)
            {
                yield return instruction;

                // if (pawn.Spawned) SpawnBiotechOnlyEffector()
                // => if (pawn.Spawned && ModsConfig.IsBiotechActive) SpawnBiotechOnlyEffector()
                if (instruction.Calls(thingSpawned.GetMethod) || instruction.Calls(shouldSendNotificationsAbout))
                {
                    yield return CodeInstruction.Call(typeof(ModsConfig), "get_BiotechActive");
                    yield return new CodeInstruction(OpCodes.And);
                    foundAny = true;
                }
            }

            if (!foundAny)
            {
                ModLog.Error("Failed to patch " + original.Name);
            }
        }
    }

    [HarmonyPatch(typeof(WidgetsWork), "get_WorkBoxBGTex_AgeDisabled")]
    class WidgetsWork_WorkBoxBGTex_AgeDisabled
    {
        [HarmonyPrefix]
        static bool DontLoadMissingTexture(ref Texture2D __result)
        {
            if (!ModsConfig.BiotechActive)
            {
                __result = WidgetsWork.WorkBoxBGTex_Awful;
                return false;
            }

            return true;
        }
    }

    // If biotech is disabled, TeachOpportunity will NRE due to a null ConceptDef argument
    // Silence the error that interrupts AgeTick results by adding a null check.
    [HarmonyPatch(typeof(LessonAutoActivator), nameof(LessonAutoActivator.TeachOpportunity), new [] {typeof(ConceptDef), typeof(Thing), typeof(OpportunityType)})]
    static class Patch_LessonAutoActivator_TeachOpportunity
    {
        public static bool Prefix(ConceptDef conc)
        {
            // call the underlying method if concept is non-null
            return conc != null;
        }
    }

    // If biotech is disabled, make the Baby() method return false in the targeted method.
    [HarmonyPatch]
    static class Patch_DevelopmentalStageExtensions_Baby_Callers
    {

        static IEnumerable<MethodBase> TargetMethods()
        {
            // Make babies follow adult rules for food eligibility instead of requiring milk/baby food
            yield return AccessTools.Method(typeof(FoodUtility), nameof(FoodUtility.WillEat), new [] {typeof(Pawn), typeof(ThingDef), typeof(Pawn), typeof(bool), typeof(bool)});
            // Make babies able to be fed in bed as a patient in lieu of nursing
            yield return AccessTools.Method(typeof(WorkGiver_FeedPatient), nameof(WorkGiver_FeedPatient.HasJobOnThing), new [] {typeof(Pawn), typeof(Thing), typeof(bool)});
        }

        [HarmonyTranspiler]
        public static IEnumerable<CodeInstruction> BabyMethodShouldReturnFalse(IEnumerable<CodeInstruction> instructions, MethodBase original)
        {
            bool foundAny = false;
            MethodInfo developmentalStageIsBabyMethod = AccessTools.Method(typeof(DevelopmentalStageExtensions), nameof(DevelopmentalStageExtensions.Baby));
            List<CodeInstruction> codeInstructions = instructions.ToList();
            foreach (CodeInstruction instruction in codeInstructions)
            {
                yield return instruction;
                if (instruction.Calls(developmentalStageIsBabyMethod))
                {
                    foundAny = true;
                    if (!ModsConfig.BiotechActive)
                    {
                        // After calling the Baby() method, AND the result with 0, to make it as if DevelopmentalStageExtensions::Baby() returned false.
                        yield return new CodeInstruction(OpCodes.Ldc_I4_0);
                        yield return new CodeInstruction(OpCodes.And);
                    }
                }
            }
            if (!foundAny)
            {
                ModLog.Error("Failed to patch " + original.Name);
            }
        }
    }

	// Check for fertile penis instead of gender when compiling list of pawns that could fertilize ovum
	[HarmonyPatch]
	static class Patch_HumanOvum_CanFertilizeReport
	{
		static IEnumerable<MethodBase> TargetMethods()
		{
			yield return AccessTools.Method(typeof(HumanOvum), "CanFertilizeReport", new[] { typeof(Pawn) });
		}

		static bool hasPenis(Pawn p)
		{
			// need to use current hediffSet, else has_penis_fertile reads data from save (an issue if a pawn lost their penis since then)
			List<Hediff> parts = p?.health?.hediffSet?.hediffs;
			return Genital_Helper.has_penis_fertile(p, parts);
		}

		[HarmonyTranspiler]
		public static IEnumerable<CodeInstruction> CheckForPenisInsteadOfGender(IEnumerable<CodeInstruction> instructions, MethodBase original)
		{
			MethodInfo checkForPenis = AccessTools.Method(typeof(Patch_HumanOvum_CanFertilizeReport), nameof(Patch_HumanOvum_CanFertilizeReport.hasPenis));
			bool foundGenderCheck = false;

			foreach (CodeInstruction ci in instructions)
			{
				string cis = ci.ToString();
				if (cis.Contains("Verse.Gender Verse.Pawn::gender"))
				{
					// check for fertile penis instead of male gender
					foundGenderCheck = true;
					yield return new CodeInstruction(OpCodes.Call, checkForPenis);
				}
				else
				{
					yield return ci;
				}
			}
			if (!foundGenderCheck)
			{
				ModLog.Warning("Failed to patch " + original.Name);
			}
		}
	}

	// If babies are malnourished, mark them as needing urgent medical rest, despite being permanently downed.
	[HarmonyPatch(typeof(HealthAIUtility), nameof(HealthAIUtility.ShouldSeekMedicalRestUrgent), new [] {typeof(Pawn)})]
    static class Patch_HealthAIUtility_ShouldSeekMedicalRestUrgent
    {
        public static bool Prefix(Pawn pawn, ref bool __result)
        {
            if (!ModsConfig.BiotechActive && pawn.DevelopmentalStage.Baby())
            {
                __result = true;
                return false;
            }
            return true;
        }
    }
}


code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Emit;
using HarmonyLib;

namespace rjw
{
	public static class CodeExtensions
	{
		/// <summary>
		/// <para>Breaks apart a list of instructions at some specific point, identified by a
		/// predicate function.  The first instruction that the predicate matches will be
		/// included in the `before` list, while all remaining instructions will be
		/// in the `after` list.</para>
		/// <para>If no instructions matched the predicate, all instructions will be in
		/// the `before` list and the `after` list will be empty.</para>
		/// <para>This is useful for breaking apart the instructions into more manageable
		/// chunks to better identify the target of your patch.</para>
		/// </summary>
		/// <param name="instructions">This list of instructions.</param>
		/// <param name="predicate">The predicate to identify the break point.</param>
		/// <returns>A tuple, the first being the instructions up to and including the
		/// instruction that matched the predicate and the second being everything
		/// afterward.</returns>
		public static (List<CodeInstruction> before, List<CodeInstruction> after) SplitAfter(
			this List<CodeInstruction> instructions,
			Predicate<CodeInstruction> predicate)
		{
			if (instructions.FindIndex(predicate) is var idx and not -1)
			{
				var before = instructions.Take(idx + 1).ToList();
				var after = instructions.Skip(before.Count).ToList();
				return (before, after);
			}

			return (instructions, new());
		}

		/// <summary>
		/// <para>Checks the next instructions in this list of instructions to make sure
		/// they all match some expectation, one predicate per instruction.  If the
		/// predicates all match, it produces two new lists, one with the matched
		/// instructions and the other with those matched instructions removed.</para>
		/// <para>If any predicate fails to match, the `cut` list will be empty and
		/// the `remainder` list will be the same instance as `instructions`.</para>
		/// <para>Use this to validate that the code actually is the code you expect
		/// and isolate it from other instructions so you can tweak it safely.</para>
		/// </summary>
		/// <param name="instructions">This list of instructions.</param>
		/// <param name="predicates">One or more predicates to test each instruction.</param>
		/// <returns>A tuple, the first being the list of cut instructions and the second
		/// being the remaining instructions after the cut.</returns>
		public static (List<CodeInstruction> cut, List<CodeInstruction> remainder) CutMatchingSequence(
			this List<CodeInstruction> instructions,
			params Predicate<CodeInstruction>[] predicates)
		{
			if (predicates.Length == 0)
				return (new(), instructions);
			if (instructions.Count < predicates.Length)
				return (new(), instructions);
			if (instructions.Zip(predicates, (il, pred) => pred(il)).Contains(false))
				return (new(), instructions);

			var cut = instructions.Take(predicates.Length).ToList();
			var remainder = instructions.Skip(predicates.Length).ToList();
			return (cut, remainder);
		}

		/// <summary>
		/// <para>Determines if this instruction is some form of `stloc` that matches the
		/// given index in the locals table.</para>
		/// <para>Why is this not a part of Harmony's API?</para>
		/// </summary>
		/// <param name="il">This instruction.</param>
		/// <param name="index">The locals table index to check for.</param>
		/// <returns>Whether this is a `stloc` targeting the given index.</returns>
		public static bool IsStlocOf(this CodeInstruction il, int index) => index switch
		{
			0 when il.opcode == OpCodes.Stloc_0 => true,
			1 when il.opcode == OpCodes.Stloc_1 => true,
			2 when il.opcode == OpCodes.Stloc_2 => true,
			3 when il.opcode == OpCodes.Stloc_3 => true,
			// Most common thing we'll see...
			_ when il.IsStloc() && il.operand is LocalBuilder lb => lb.LocalIndex == index,
			// ...but these are also technically possible with `stloc_s` and `stloc`.
			_ when il.IsStloc() && il.operand is byte bIndex => bIndex == Convert.ToByte(index),
			_ when il.IsStloc() && il.operand is ushort uIndex => uIndex == Convert.ToUInt16(index),
			_ => false
		};

		/// <summary>
		/// Copies the labels from this instruction to its replacement.  If the replacement
		/// instruction also has labels, it ensures that duplicate labels are discarded.
		/// </summary>
		/// <param name="il">This instruction.</param>
		/// <param name="newIl">The instruction that will replace it.</param>
		/// <returns>The replacement instruction.</returns>
		public static CodeInstruction Replace(this CodeInstruction il, CodeInstruction newIl)
		{
			var copyLabels = newIl.labels.ToList();
			newIl.labels.Clear();
			newIl.labels.AddRange(copyLabels.Concat(il.labels).Distinct());
			return newIl;
		}
	}
}


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

namespace rjw
{
	///
	///unset designators on GuestRelease
	///until pawn leaves map, pawn is still guest/slave/prisoner, so they can actually be changed back
	///
	[HarmonyPatch(typeof(GenGuest), "GuestRelease")]
	[StaticConstructorOnStartup]
	static class PATCH_GenGuest_GuestRelease
	{
		[HarmonyPrefix]
		private static void GuestRelease_Update_Designators(Pawn p)
		{
			if (p != null)
			{
				//ModLog.Message("GenGuest GuestRelease");
				//ModLog.Message("pawn: " + xxx.get_pawnname(p));
				p.UnDesignateComfort();
				p.UnDesignateService();
				p.UnDesignateBreeding();
				p.UnDesignateBreedingAnimal();
				p.UnDesignateMilking();
				p.UnDesignateHero();
				//ModLog.Message(p.IsDesignatedComfort().ToString());
			}
		}
	}

	///
	///unset designators on Pawn_ExitMap
	///now pawn actually leaves map and get their factions reset
	///
	[HarmonyPatch(typeof(Pawn), "ExitMap")]
	[StaticConstructorOnStartup]
	static class PATCH_Pawn_ExitMap
	{
		[HarmonyPrefix]
		private static void Pawn_ExitMap_Update_Designators(Pawn __instance, bool allowedToJoinOrCreateCaravan, Rot4 exitDir)
		{
			Pawn p = __instance;
			if (p != null)
				if (!p.IsColonist)
				{
					//ModLog.Message("Pawn ExitMap");
					//ModLog.Message("pawn: " + xxx.get_pawnname(p));
					p.UnDesignateComfort();
					p.UnDesignateService();
					p.UnDesignateBreeding();
					p.UnDesignateBreedingAnimal();
					p.UnDesignateMilking();
					p.UnDesignateHero();
					//ModLog.Message(p.IsDesignatedComfort().ToString());
				}
		}
	}
}


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

namespace rjw
{
	[StaticConstructorOnStartup]
	internal static class First
	{
		/*private static void show_bpr(String body_part_record_def_name)
		{
			var bpr = BodyDefOf.Human.AllParts.Find((BodyPartRecord can) => String.Equals(can.def.defName, body_part_record_def_name));
			--Log.Message(body_part_record_def_name + " BPR internals:");
			--Log.Message("  def: " + bpr.def.ToString());
			--Log.Message("  parts: " + bpr.parts.ToString());
			--Log.Message("  parts.count: " + bpr.parts.Count.ToString());
			--Log.Message("  height: " + bpr.height.ToString());
			--Log.Message("  depth: " + bpr.depth.ToString());
			--Log.Message("  coverage: " + bpr.coverage.ToString());
			--Log.Message("  groups: " + bpr.groups.ToString());
			--Log.Message("  groups.count: " + bpr.groups.Count.ToString());
			--Log.Message("  parent: " + bpr.parent.ToString());
			--Log.Message ("  fleshCoverage: " + bpr.fleshCoverage.ToString ());
			--Log.Message ("  absoluteCoverage: " + bpr.absoluteCoverage.ToString ());
			--Log.Message ("  absoluteFleshCoverage: " + bpr.absoluteFleshCoverage.ToString ());
		}*/

		//Children mod use same defname. but not has worker class. so overriding here.
		public static void inject_Reproduction()
		{
			PawnCapacityDef reproduction = DefDatabase<PawnCapacityDef>.GetNamed("RJW_Fertility");
			reproduction.workerClass = typeof(PawnCapacityWorker_Fertility);
		}

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

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

			var tabBase = InspectTabManager.GetSharedInstance(tabType);

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

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

		private static void inject_recipes()
		{
			//--ModLog.Message(" First::inject_recipes");
			// Inject the recipes to create the artificial privates into the crafting spot or machining bench.
			// BUT, also dynamically detect if EPOE is loaded and, if it is, inject the recipes into EPOE's
			// crafting benches instead.

			try
			{
				//Vanilla benches
				var cra_spo = DefDatabase<ThingDef>.GetNamed("CraftingSpot");
				var mac_ben = DefDatabase<ThingDef>.GetNamed("TableMachining");
				var fab_ben = DefDatabase<ThingDef>.GetNamed("FabricationBench");
				var tai_ben = DefDatabase<ThingDef>.GetNamed("ElectricTailoringBench");

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

		/*private static void show_bs(Backstory bs)
		{
			--Log.Message("Backstory \"" + bs.Title + "\" internals:");
			--Log.Message("  identifier: " + bs.identifier);
			--Log.Message("  slot: " + bs.slot.ToString());
			--Log.Message("  Title: " + bs.Title);
			--Log.Message("  TitleShort: " + bs.TitleShort);
			--Log.Message("  baseDesc: " + bs.baseDesc);
			--Log.Message("  skillGains: " + ((bs.skillGains == null) ? "null" : bs.skillGains.ToString()));
			--Log.Message("  skillGainsResolved: " + ((bs.skillGainsResolved == null) ? "null" : bs.skillGainsResolved.ToString()));
			--Log.Message("  workDisables: " + bs.workDisables.ToString());
			--Log.Message("  requiredWorkTags: " + bs.requiredWorkTags.ToString());
			--Log.Message("  spawnCategories: " + bs.spawnCategories.ToString());
			--Log.Message("  bodyTypeGlobal: " + bs.bodyTypeGlobal.ToString());
			--Log.Message("  bodyTypeFemale: " + bs.bodyTypeFemale.ToString());
			--Log.Message("  bodyTypeMale: " + bs.bodyTypeMale.ToString());
			--Log.Message("  forcedTraits: " + ((bs.forcedTraits == null) ? "null" : bs.forcedTraits.ToString()));
			--Log.Message("  disallowedTraits: " + ((bs.disallowedTraits == null) ? "null" : bs.disallowedTraits.ToString()));
			--Log.Message("  shuffleable: " + bs.shuffleable.ToString());
		}*/

		//Quick check to see if an another mod is loaded.
		private static bool IsLoaded(string mod)
		{
			return LoadedModManager.RunningModsListForReading.Any(x => x.Name == mod);
		}

		private static void CheckingCompatibleMods()
		{
			{//Humanoid Alien Races Framework 2.0
				xxx.xenophobia = DefDatabase<TraitDef>.GetNamedSilentFail("Xenophobia");
				if (xxx.xenophobia is null)
				{
					xxx.AlienFrameworkIsActive = false;
				}
				else
				{
					xxx.AlienFrameworkIsActive = true;
					if (RJWSettings.DevMode) ModLog.Message("Humanoid Alien Races 2.0 is detected. Xenophile and Xenophobe traits active.");
				}
			}

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

					// any mod that features a polyamorous trait
					if(xxx.polyamorous is null)
					{
						xxx.AnyPolyamoryModIsActive = false;
					}
					else
					{
						xxx.AnyPolyamoryModIsActive = true;
					}
				}

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

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

					// any mod that features a polygamous trait
					if (xxx.polygamous is null)
					{
						xxx.AnyPolygamyModIsActive = false;
					}
					else
					{
						xxx.AnyPolygamyModIsActive = true;
					}
				}

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

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

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

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

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

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

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

			{// RJW-EX
				xxx.RjwEx_Armbinder = DefDatabase<HediffDef>.GetNamedSilentFail("Armbinder");
				if (xxx.RjwEx_Armbinder is null) { xxx.RjwExIsActive = false;}
				else { xxx.RjwExIsActive = true; }

			}

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

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

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

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

			//bondage_gear_tradeability.init();

			var har = new Harmony("rjw");
			har.PatchAll(Assembly.GetExecutingAssembly());
			PATCH_Pawn_ApparelTracker_TryDrop.apply(har);
			//CnPcompatibility.Patch(har);							//CnP IS NO OUT YET
		}

		internal static void CheckModRequirements()
		{
			//--Log.Message("First::CheckModRequirements() called");
			List<string> required_mods = new List<string> {
				"HugsLib",
			};
			foreach (string required_mod in required_mods)
			{
				bool found = false;
				foreach (ModMetaData installed_mod in ModLister.AllInstalledMods)
				{
					if (installed_mod.Active && installed_mod.Name.Contains(required_mod))
					{
						found = true;
					}

					if (!found)
					{
						ErrorMissingRequirement(required_mod);
					}
				}
			}
		}

		internal static void CheckIncompatibleMods()
		{
			//--Log.Message("First::CheckIncompatibleMods() called");
			List<string> incompatible_mods = new List<string> {
				"Bogus Test Mod That Doesn't Exist",
				"Lost Forest"
			};
			foreach (string incompatible_mod in incompatible_mods)
			{
				foreach (ModMetaData installed_mod in ModLister.AllInstalledMods)
				{
					if (installed_mod.Active && installed_mod.Name.Contains(incompatible_mod))
					{
						ErrorIncompatibleMod(installed_mod);
					}
				}
			}
		}

		internal static void ErrorMissingRequirement(string missing)
		{
			ModLog.Error("Initialization error:  Unable to find required mod '" + missing + "' in mod list");
		}

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


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

/// <summary>
/// patches GenSpawn to:
/// check if spawned thing is pawn, and add sexualize it if parts missing
/// </summary>
namespace rjw
{
	[HarmonyPatch(typeof(GenSpawn), "Spawn", new Type[] { typeof(Thing), typeof(IntVec3), typeof(Map), typeof(Rot4), typeof(WipeMode), typeof(bool), typeof(bool) })]
	static class Patch_GenSpawn_Spawn
	{
		[HarmonyPostfix]

		static void Sexualize_GenSpawn_Spawn(ref Thing __result)
		{
			if (__result != null)
				if (__result is Pawn)
				{
					//ModLog.Message("Sexualize_GenSpawn_Spawn:: " + xxx.get_pawnname(__result));
					Pawn pawn = __result as Pawn;
					if (pawn.kindDef.race.defName.Contains("AIRobot") // No genitalia/sexuality for roombas.
						|| pawn.kindDef.race.defName.Contains("AIPawn") // ...nor MAI.
						|| pawn.kindDef.race.defName.Contains("RPP_Bot")
						|| pawn.kindDef.race.defName.Contains("PRFDrone") // Project RimFactory Revived drones
						) return;

					if (!Genital_Helper.has_genitals(pawn))
					{
						Sexualizer.sexualize_pawn(pawn);
					}
				}
		}
	}
}


code:
using HarmonyLib;
using RimWorld;
using System.Collections.Generic;
using System;
using Verse;
using rjw.Modules.Shared.Extensions;
using System.Linq;
using rjw.Modules.Interactions;

namespace rjw
{
	/// <summary>
	/// Patches HealthCardUtility to add RJW options to the dev menu
	/// </summary>
	[HarmonyPatch(typeof(HealthCardUtility), nameof(HealthCardUtility.DoDebugOptions))]
	static class Patch_HealthCardDevTool
	{
        public static bool buildingDebugMenu = false;
        public static Pawn pawn = null;

        public static void Prefix(Pawn pawn)
		{
            Patch_HealthCardDevTool.pawn = pawn;
            Patch_HealthCardDevTool.buildingDebugMenu = true;
		}

		public static void Postfix()
		{
            Patch_HealthCardDevTool.pawn = null;
            Patch_HealthCardDevTool.buildingDebugMenu = false;
		}
	}

    [HarmonyPatch(typeof(FloatMenu), MethodType.Constructor, new Type[] { typeof(List<FloatMenuOption>) })]
	static class Patch_HealthCardDevTool_FloatMenu
	{
        public static void Prefix(ref List<FloatMenuOption> options)
        {
            if(Patch_HealthCardDevTool.buildingDebugMenu && Patch_HealthCardDevTool.pawn != null)
            {
                Patch_HealthCardDevTool.buildingDebugMenu = false;

                var pawn = Patch_HealthCardDevTool.pawn;
                var parts = pawn.GetLewdParts();

                var editableParts = new List<ISexPartHediff>();
                editableParts.AddRange(parts.Breasts.AsSexPartHediffs());
                editableParts.AddRange(parts.Vaginas.AsSexPartHediffs());
                editableParts.AddRange(parts.Penises.AsSexPartHediffs());
                editableParts.AddRange(parts.Anuses.AsSexPartHediffs());

                if(editableParts.Count > 0)
                {
                    options.Add(new FloatMenuOption("PartsEditDevEntry".Translate(), delegate
                    {
                        Find.WindowStack.Add(new Dialog_Partcard(pawn, editableParts, xxx.get_pawnname(pawn).CapitalizeFirst()));
                    }));
                }
            }
        }
	}
}


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

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

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

		[HarmonyPostfix]
		static void Sexualize_GenerateNewPawnInternal(ref PawnGenerationRequest request, ref Pawn __result)
		{
			//ModLog.Message("After_GenerateNewPawnInternal:: " + xxx.get_pawnname(__result));
			if (__result.GetCompRJW() != null && __result.GetCompRJW().orientation == Orientation.None)
			{
				//ModLog.Message("After_GenerateNewPawnInternal::Sexualize " + xxx.get_pawnname(__result));
				__result.GetCompRJW().Sexualize(__result);
			}
		}

		[HarmonyPostfix]
		static void Fix_Newborn_styles(ref PawnGenerationRequest request, ref Pawn __result)
		{
			if (request.AllowedDevelopmentalStages == DevelopmentalStage.Newborn)
			{
				if(__result.style != null)
				{
					__result.style.beardDef = BeardDefOf.NoBeard;
					__result.style.SetupTattoos_NoIdeology();
				}
			}
		}
	}
}


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

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

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

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

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

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

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

		//resets/doest work for some races
		static void changestory(Pawn pawn)
		{
			pawn.story.Childhood = null;
			pawn.story.Adulthood = null;

			// set child to tribal
			try
			{
				if (!ModsConfig.BiotechActive)
					pawn.story.Childhood = DefDatabase<BackstoryDef>.AllDefsListForReading.FirstOrDefault(x => x.defName.Contains("rjw_childT"));
				else
					pawn.story.Childhood = DefDatabase<BackstoryDef>.GetNamed("Newborn79");
			}
			catch (Exception e)
			{
				ModLog.Warning(e.ToString());
			}
		}

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

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


code:
using HarmonyLib;
using RimWorld;
using rjw.RMB;
using System.Collections.Generic;
using UnityEngine;
using Verse;

namespace rjw
{
	/// <summary>
	/// Patches FloatMenuMakerMap to add manual RJW interactions to the context menu
	/// </summary>
	[HarmonyPatch(typeof(FloatMenuMakerMap), nameof(FloatMenuMakerMap.ChoicesAtFor))]
	static class Patch_RMBMenu
	{
		public static void Postfix(List<FloatMenuOption> __result, Vector3 clickPos, Pawn pawn)
		{
			RMB_Menu.AddSexFloatMenuOptions(pawn, __result, clickPos);
		}
	}
}


code:
using HarmonyLib;
using RimWorld;
using UnityEngine;
using Verse;
using Verse.Sound;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;

namespace rjw
{
	[HarmonyPatch(typeof(CharacterCardUtility), "DrawCharacterCard")]
	public static class SexcardPatch
	{
		public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			MethodInfo LifestageAndXenotypeOptions = AccessTools.Method(typeof(CharacterCardUtility), "LifestageAndXenotypeOptions");
			MethodInfo SexualityCardToggle = AccessTools.Method(typeof(SexcardPatch), nameof(SexualityCardToggle));
			bool traits = false;
			bool found = false;
			foreach (CodeInstruction i in instructions)
			{
				if (found)
				{
					yield return new CodeInstruction(OpCodes.Ldarg_0) { labels = i.labels.ListFullCopy() };//rect
					yield return new CodeInstruction(OpCodes.Ldarg_1);//pawn
					yield return new CodeInstruction(OpCodes.Ldarg_3);//creationRect
					yield return new CodeInstruction(OpCodes.Call, SexualityCardToggle);
					found = false;
					i.labels.Clear();
				}
				if (i.opcode == OpCodes.Call && i.operand as MethodInfo == LifestageAndXenotypeOptions)
				{
					found = true;
				}
				if (i.opcode == OpCodes.Ldstr && i.operand.Equals("Traits"))
				{
					traits = true;
				}
				if (traits && i.opcode == OpCodes.Ldc_R4 && i.operand.Equals(2f))//replaces rect y calculation
				{
					yield return new CodeInstruction(OpCodes.Ldc_R4, 0f);
					traits = false;
					continue;
				}
				yield return i;
			}
		}

		public static void SexualityCardToggle(Rect rect, Pawn pawn, Rect creationRect)
		{
			if (pawn == null) return;
			if (pawn.GetCompRJW() == null) return;

			//character bio
			Rect rectNew = new Rect(CharacterCardUtility.BasePawnCardSize.x - 50f, 2f, 24f, 24f);
			//character creator/game start
			if (Current.ProgramState != ProgramState.Playing)
			{
				//1.4
				if (ModsConfig.BiotechActive)
				{
					rectNew = new Rect(creationRect.width - 24f, 108f, 24f, 24f);
				}
				//1.3
				else if (ModsConfig.IdeologyActive) // Move icon lower to avoid overlap.
				{
					rectNew = new Rect(creationRect.width - 24f, 82f, 24f, 24f);
					if (xxx.IndividualityIsActive) // Move icon lower to avoid overlap.
						rectNew = new Rect(creationRect.width - 24f, 108f, 24f, 24f);
				}
				//1.1, 1.2
				else if (xxx.IndividualityIsActive) // Move icon lower to avoid overlap.
					rectNew = new Rect(creationRect.width - 24f, 56f, 24f, 24f);
				else
					rectNew = new Rect(creationRect.width - 24f, 30f, 24f, 24f);
			}
			Color old = GUI.color;

			GUI.color = rectNew.Contains(Event.current.mousePosition) ? new Color(0.25f, 0.59f, 0.75f) : new Color(1f, 1f, 1f);
			GUI.DrawTexture(rectNew, ContentFinder<Texture2D>.Get("RJW-LOGO"));

			TooltipHandler.TipRegion(rectNew, "SexcardTooltip".Translate());
			if (Widgets.ButtonInvisible(rectNew))
			{
				SoundDefOf.InfoCard_Open.PlayOneShotOnCamera();
				Find.WindowStack.Add(new Dialog_Sexcard(pawn));
			}
			GUI.color = old;
		}
	}
}