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

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


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

namespace rjw
{
	///<summary>
	///This hediff class simulates pregnancy with mechanoids, mother may be human. It is not intended to be reasonable.
	///Differences from bestial pregnancy are that ... it is lethal
	///TODO: extend with something "friendlier"? than Mech_Scyther.... two Mech_Scyther's? muhahaha
	///</summary>
	[RJWAssociatedHediff("RJW_pregnancy_mech")]
	public class Hediff_MechanoidPregnancy : Hediff_BasePregnancy
	{
		public override bool canBeAborted
		{
			get
			{
				return false;
			}
		}

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

		public override void PregnancyMessage()
		{
			string message_title = "RJW_PregnantTitle".Translate(pawn.LabelIndefinite()).CapitalizeFirst();
			string message_text1 = "RJW_PregnantText".Translate(pawn.LabelIndefinite()).CapitalizeFirst();
			string message_text2 = "RJW_PregnantMechStrange".Translate();
			Find.LetterStack.ReceiveLetter(message_title, message_text1 + "\n" + message_text2, LetterDefOf.ThreatBig, pawn);
		}

		public void Hack()
		{
			is_hacked = true;
		}

		public override void Notify_PawnDied()
		{
			base.Notify_PawnDied();
			GiveBirth();
		}

		//Handles the spawning of pawns
		public override void GiveBirth()
		{
			Pawn mother = pawn;
			if (mother == null)
				return;

			if (!babies.NullOrEmpty())
			{
				foreach (Pawn baby in babies)
				{
					baby.Destroy();
					baby.Discard(true);
				}
				babies.Clear();
			}

			Faction spawn_faction = null;

			if (!is_hacked)
				spawn_faction = Faction.OfMechanoids;

			PawnKindDef children = null;

			if (RJWSettings.DevMode) ModLog.Message(xxx.get_pawnname(pawn) + " birth:" + this.ToString());

			foreach (HediffDef_MechImplants implant in DefDatabase<HediffDef_MechImplants>.AllDefs.Where(x => x.parentDefs.Contains(father.kindDef.ToString())))         //try to find predefined
			{
				string childrendef;																			//try to find predefined
				List<string> childlist = new List<string>();
				if (!implant.childrenDefs.NullOrEmpty())
				{
					foreach (var child in (implant.childrenDefs))
					{
						if (DefDatabase<PawnKindDef>.GetNamedSilentFail(child) != null)
							childlist.AddDistinct(child);
					}
					childrendef = childlist.RandomElement();												//try to find predefined
					children = DefDatabase<PawnKindDef>.GetNamedSilentFail(childrendef);
					if (children != null)
						continue;
				}
			}

			if (children == null)                                                                           //fallback, use fatherDef
				children = father.kindDef;

			PawnGenerationRequest request = new PawnGenerationRequest(
				kind: children,
				faction: spawn_faction,
				forceGenerateNewPawn: true,
				newborn: true
				);

			Pawn mech = PawnGenerator.GeneratePawn(request);
			PawnUtility.TrySpawnHatchedOrBornPawn(mech, mother);
			if (!is_hacked)
			{
				LordJob_MechanoidsDefend lordJob = new LordJob_MechanoidsDefend();
				Lord lord = LordMaker.MakeNewLord(mech.Faction, lordJob, mech.Map);
				lord.AddPawn(mech);
			}
			FilthMaker.TryMakeFilth(mech.PositionHeld, mech.MapHeld, mother.RaceProps.BloodDef, mother.LabelIndefinite());

			IEnumerable<BodyPartRecord> source = from x in mother.health.hediffSet.GetNotMissingParts() where
												x.IsInGroup(BodyPartGroupDefOf.Torso)
												&& !x.IsCorePart
												//&& x.groups.Contains(BodyPartGroupDefOf.Torso)
												//&& x.depth == BodyPartDepth.Inside
												//&& x.height == BodyPartHeight.Bottom
												//someday include depth filter
												//so it doesn't cut out external organs (breasts)?
												//vag  is genital part and genital is external
												//anal is internal
												//make sep part of vag?
												//&& x.depth == BodyPartDepth.Inside
												select x;

			if (source.Any())
			{
				foreach (BodyPartRecord part in source)
				{
					mother.health.DropBloodFilth();
				}
				if (RJWPregnancySettings.safer_mechanoid_pregnancy && is_checked)
				{
					foreach (BodyPartRecord part in source)
					{
						DamageDef surgicalCut = DamageDefOf.SurgicalCut;
						float amount = 5f;
						float armorPenetration = 999f;
						mother.TakeDamage(new DamageInfo(surgicalCut, amount, armorPenetration, -1f, null, part, null, DamageInfo.SourceCategory.ThingOrUnknown, null));
					}
				}
				else
				{
					foreach (BodyPartRecord part in source)
					{
						Hediff_MissingPart hediff_MissingPart = (Hediff_MissingPart)HediffMaker.MakeHediff(HediffDefOf.MissingBodyPart, mother, part);
						hediff_MissingPart.lastInjury = HediffDefOf.Cut;
						hediff_MissingPart.IsFresh = true;
						mother.health.AddHediff(hediff_MissingPart);
					}
				}
			}
			mother.health.RemoveHediff(this);
		}
	}
}

repo_name: LamkasDev/rjw

license: mit


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

namespace rjw
{
	internal class Hediff_Parasite : Hediff_Pregnant
	{
		[SyncMethod]
		new public static void DoBirthSpawn(Pawn mother, Pawn father)
		{
			//Rand.PopState();
			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
			int num = (mother.RaceProps.litterSizeCurve == null) ? 1 : Mathf.RoundToInt(Rand.ByCurve(mother.RaceProps.litterSizeCurve));
			if (num < 1)
			{
				num = 1;
			}
			PawnGenerationRequest request = new PawnGenerationRequest(
					kind: father.kindDef,
					faction: father.Faction,
					forceGenerateNewPawn: true,
					newborn: true,
					allowDowned: true,
					canGeneratePawnRelations: false,
					mustBeCapableOfViolence: true,
					colonistRelationChanceFactor: 0,
					allowFood: false,
					allowAddictions: false,
					relationWithExtraPawnChanceFactor: 0
				);

			Pawn pawn = null;
			for (int i = 0; i < num; i++)
			{
				pawn = PawnGenerator.GeneratePawn(request);
				if (PawnUtility.TrySpawnHatchedOrBornPawn(pawn, mother))
				{
					if (pawn.playerSettings != null && mother.playerSettings != null)
					{
						pawn.playerSettings.AreaRestriction = father.playerSettings.AreaRestriction;
					}
					if (pawn.RaceProps.IsFlesh)
					{
						pawn.relations.AddDirectRelation(PawnRelationDefOf.Parent, mother);
						if (father != null)
						{
							pawn.relations.AddDirectRelation(PawnRelationDefOf.Parent, father);
						}
					}
				}
				else
				{
					Find.WorldPawns.PassToWorld(pawn, PawnDiscardDecideMode.Discard);
				}
			}
			if (mother.Spawned)
			{
				FilthMaker.TryMakeFilth(mother.Position, mother.Map, ThingDefOf.Filth_AmnioticFluid, mother.LabelIndefinite(), 5);
				if (mother.caller != null)
				{
					mother.caller.DoCall();
				}
				if (pawn.caller != null)
				{
					pawn.caller.DoCall();
				}
			}
		}
	}
}


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

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

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

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

		public override string DebugString()
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append(base.DebugString());
			stringBuilder.Append("Current CurLifeStageIndex: " + pawn.ageTracker.CurLifeStageIndex + ", grown_to: " + grown_to);
			return stringBuilder.ToString();
		}

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

			if (!pawn.RaceProps.lifeStageAges.Any(x => x.def.reproductive))
			{
				if (pawn.health.hediffSet.HasHediff(xxx.RJW_NoManipulationFlag))
				{
					pawn.health.hediffSet.hediffs.Remove(pawn.health.hediffSet.GetFirstHediffOfDef(xxx.RJW_NoManipulationFlag));
				}
				pawn.health.RemoveHediff(this);
			}
		}

		public override void ExposeData()
		{
			base.ExposeData();
			Scribe_Values.Look(ref grown_to, "grown_to", 0);
			Scribe_Values.Look(ref lastTick, "lastTick", 0);
			Scribe_Values.Look(ref dayTick, "dayTick", 0);
		}

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

		[SyncMethod]
		internal void GrowUpTo(int stage, bool generated)
		{
			//Rand.PopState();
			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
			if (grown_to < stage)
				grown_to = stage;

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

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

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

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

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

				stage_completed++;
				pawn.health.RemoveHediff(this);
			}
		}

		//everyday grow 1 year until reproductive
		public void GrowFast()
		{
			if (RJWPregnancySettings.phantasy_pregnancy)
			{
				if (!pawn.ageTracker.CurLifeStage.reproductive)
				{
					pawn.ageTracker.AgeBiologicalTicks = (pawn.ageTracker.AgeBiologicalYears + 1) * GenDate.TicksPerYear + 1;
					pawn.ageTracker.DebugForceBirthdayBiological();
					GrowUpTo(pawn.ageTracker.CurLifeStageIndex, false);
				}
			}
		}

		public void TickRare()
		{
			//--ModLog.Message("Hediff_SimpleBaby::TickRare is called");
			if (pawn.ageTracker.CurLifeStageIndex > Grown_To)
			{
				GrowUpTo(pawn.ageTracker.CurLifeStageIndex, false);
			}

			// Update the graphics set
			//if (pawn.ageTracker.CurLifeStageIndex >= AgeStage.Toddler)
			//	pawn.Drawer.renderer.graphics.ResolveAllGraphics();

			if (xxx.RimWorldChildrenIsActive)
			{
				//if (Prefs.DevMode)
				//	Log.Message("RJW child tick - CnP active");
				//we do not disable our hediff anymore
	//			if (pawn.health.hediffSet.HasHediff(HediffDef.Named("RJW_NoManipulationFlag")))
				//{
				//	pawn.health.hediffSet.hediffs.Remove(pawn.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("RJW_NoManipulationFlag")));
				//	pawn.health.AddHediff(HediffDef.Named("NoManipulationFlag"), null, null);
				//}
				//if (pawn.health.hediffSet.HasHediff(HediffDef.Named("RJW_BabyState")))
				//{
				//	pawn.health.hediffSet.hediffs.Remove(pawn.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("RJW_BabyState")));
				//	pawn.health.AddHediff(HediffDef.Named("BabyState"), null, null);
	//				if (Prefs.DevMode) Log.Message("RJW_Babystate self-removing");
	//			}
				if (pawn.ageTracker.CurLifeStageIndex <= 1)
				{   //The UnhappBaby feature is not included in RJW, but will
					// Check if the baby is hungry, and if so, add the whiny baby hediff
					var hunger = pawn.needs.food;
					var joy = pawn.needs.joy;
					if ((joy != null)&&(hunger!=null))
					{ //There's no way to patch out the CnP adressing nill fields
						if (hunger.CurLevelPercentage<hunger.PercentageThreshHungry || joy.CurLevelPercentage <0.1)
						{
							if (!pawn.health.hediffSet.HasHediff(HediffDef.Named("UnhappyBaby"))){
								//--Log.Message("Adding unhappy baby hediff");
								pawn.health.AddHediff(HediffDef.Named("UnhappyBaby"), null, null);
							}
						}
					}
				}
			}
		}

		public override void Tick()
		{
			/*
			if (xxx.RimWorldChildrenIsActive)
			{
				if (pawn.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("RJW_BabyState"))!=null)
				{
					pawn.health.RemoveHediff(this);
				}
				return;
			}
			*/
			//This void call every frame. should not logmes no reason
			//--ModLog.Message("Hediff_SimpleBaby::PostTick is called");

			base.Tick();
			if (pawn.Spawned)
			{
				var thisTick = Find.TickManager.TicksGame;

				if ((thisTick - dayTick) >= GenDate.TicksPerDay)
				{
					GrowFast();
					dayTick = thisTick;
				}
				if ((thisTick - lastTick) >= 250)
				{
					TickRare();
					lastTick = thisTick;
				}
			}
		}

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


code:
using System;

namespace rjw
{
	public class RJWAssociatedHediffAttribute : Attribute
	{
		public string defName { get; private set; }

		public RJWAssociatedHediffAttribute(string defName)
		{
			this.defName = defName;
		}
	}
}


code:
using RimWorld;
using Verse;
using System;
using System.Linq;
using System.Collections.Generic;
using Multiplayer.API;
using rjw.Modules.Interactions.Enums;
using rjw.Modules.Interactions.Extensions;

///RimWorldChildren pregnancy:
//using RimWorldChildren;

namespace rjw
{
	///
	/// This handles pregnancy chosing between different types of pregnancy awailable to it
	/// 1a:RimWorldChildren pregnancy for humanlikes
	/// 1b:RJW pregnancy for humanlikes
	/// 2:RJW pregnancy for bestiality
	/// 3:RJW pregnancy for insects
	/// 4:RJW pregnancy for mechanoids
	///

	public static class PregnancyHelper
	{
		//called by aftersex (including rape, breed, etc)
		//called by mcevent

		//pawn - "father"; partner = mother
		public static void impregnate(SexProps props)
		{

			if (RJWSettings.DevMode) ModLog.Message("Rimjobworld::impregnate(" + props.sexType + "):: " + xxx.get_pawnname(props.pawn) + " + " + xxx.get_pawnname(props.partner) + ":");

			//"mech" pregnancy
			if (props.sexType == xxx.rjwSextype.MechImplant)
			{
				if (RJWPregnancySettings.mechanoid_pregnancy_enabled && xxx.is_mechanoid(props.pawn))
				{
					var preg = GetPregnancy(props.partner);
					if (preg != null) // removing old pregnancies
					{
						if (preg is Hediff_MechanoidPregnancy)
						{
							if (RJWSettings.DevMode) ModLog.Message(" already pregnant by mechanoid");
							return;
						}

						if (preg is Hediff_BasePregnancy)
						{
							if (RJWSettings.DevMode) ModLog.Message(" removing rjw normal pregnancy");
							(preg as Hediff_BasePregnancy).Kill();
						}
						else
						{
							if (RJWSettings.DevMode) ModLog.Message(" removing vanilla or other mod pregnancy");
							props.partner.health.RemoveHediff(preg);
						}
					}

					// new pregnancy
					if (RJWSettings.DevMode) ModLog.Message(" mechanoid pregnancy started");
					Hediff_MechanoidPregnancy hediff = Hediff_BasePregnancy.Create<Hediff_MechanoidPregnancy>(props.partner, props.pawn);
					return;

					/*
					// Not an actual pregnancy. This implants mechanoid tech into the target.
					//may lead to pregnancy
					//old "chip pregnancies", maybe integrate them somehow?
					//Rand.PopState();
					//Rand.PushState(RJW_Multiplayer.PredictableSeed());
					HediffDef_MechImplants egg = (from x in DefDatabase<HediffDef_MechImplants>.AllDefs	select x).RandomElement();
					if (egg != null)
					{
						if (RJWSettings.DevMode) Log.Message(" planting MechImplants:" + egg.ToString());
						PlantSomething(egg, partner, !Genital_Helper.has_vagina(partner), 1);
						return;
					}
					else
					{
						if (RJWSettings.DevMode) Log.Message(" no mech implant found");
					}*/
				}
				return;
			}

			//"ovi" pregnancy/egglaying
			var AnalOk = props.sexType == xxx.rjwSextype.Anal && RJWPregnancySettings.insect_anal_pregnancy_enabled;
			var OralOk = props.sexType == xxx.rjwSextype.Oral && RJWPregnancySettings.insect_oral_pregnancy_enabled;
			// Sextype can result in pregnancy.
			if (!(props.sexType == xxx.rjwSextype.Vaginal || props.sexType == xxx.rjwSextype.DoublePenetration ||
				AnalOk || OralOk))
				return;

			Pawn giver = props.pawn; // orgasmer
			Pawn reciever = props.partner;
			List<Hediff> pawnparts = giver.GetGenitalsList();
			List<Hediff> partnerparts = reciever.GetGenitalsList();
			var interaction = Modules.Interactions.Helpers.InteractionHelper.GetWithExtension(props.dictionaryKey);

			//ModLog.Message(" RaceImplantEggs()" + pawn.RaceImplantEggs());
			//"insect" pregnancy
			//straight, female (partner) recives egg insertion from other/sex starter (pawn)

			if (CouldBeEgging(props, giver, reciever, pawnparts, partnerparts))
			{
				//TODO: add widget toggle for bind all/neutral/hostile pawns
				if (!props.isReceiver)
					if (CanCocoon(giver))
						if (giver.HostileTo(reciever) || reciever.IsPrisonerOfColony || reciever.health.hediffSet.HasHediff(xxx.submitting))
							if (!reciever.health.hediffSet.HasHediff(HediffDef.Named("RJW_Cocoon")))
							{
								reciever.health.AddHediff(HediffDef.Named("RJW_Cocoon"));
							}

				DoEgg(props);
				return;
			}

			if (!(props.sexType == xxx.rjwSextype.Vaginal || props.sexType == xxx.rjwSextype.DoublePenetration))
				return;

			//"normal" and "beastial" pregnancy
			if (RJWSettings.DevMode) ModLog.Message(" 'normal' pregnancy checks");

			//interaction stuff if for handling futa/see who penetrates who in interaction
			if (!props.isReceiver &&
				interaction.DominantHasTag(GenitalTag.CanPenetrate) &&
				interaction.SubmissiveHasFamily(GenitalFamily.Vagina))
			{
				if (RJWSettings.DevMode) ModLog.Message(" impregnate - by initiator");
			}
			else if (props.isReceiver &&
				interaction.DominantHasFamily(GenitalFamily.Vagina) &&
				interaction.SubmissiveHasTag(GenitalTag.CanPenetrate) &&
				interaction.HasInteractionTag(InteractionTag.Reverse))
			{
				if (RJWSettings.DevMode) ModLog.Message(" impregnate - by receiver (reverse)");
			}
			else
			{
				if (RJWSettings.DevMode) ModLog.Message(" no valid interaction tags/family");
				return;
			}

			if (!Modules.Interactions.Helpers.PartHelper.FindParts(giver, GenitalTag.CanFertilize).Any())
			{
				if (RJWSettings.DevMode) ModLog.Message(xxx.get_pawnname(giver) + " has no parts to Fertilize with");
				return;
			}
			if (!Modules.Interactions.Helpers.PartHelper.FindParts(reciever, GenitalTag.CanBeFertilized).Any())
			{
				if (RJWSettings.DevMode) ModLog.Message(xxx.get_pawnname(reciever) + " has no parts to be Fertilized");
				return;
			}

			if (CanImpregnate(giver, reciever, props.sexType))
			{
				Doimpregnate(giver, reciever);
			}
		}

		private static bool CouldBeEgging(SexProps props, Pawn giver, Pawn reciever, List<Hediff> pawnparts, List<Hediff> partnerparts)
		{
			//no ovipositor or fertilization possible
			if ((Genital_Helper.has_ovipositorF(giver, pawnparts) ||
				Genital_Helper.has_ovipositorM(giver, pawnparts) ||
				(Genital_Helper.has_penis_fertile(giver, pawnparts) && (giver.RaceImplantEggs() || reciever.health.hediffSet.GetHediffs<Hediff_InsectEgg>().Any()))
				) == false)
			{
				return false;
			}

			if ((props.sexType == xxx.rjwSextype.Vaginal || props.sexType == xxx.rjwSextype.DoublePenetration) &&
				Genital_Helper.has_vagina(reciever, partnerparts))
			{
				return true;
			}

			if ((props.sexType == xxx.rjwSextype.Anal || props.sexType == xxx.rjwSextype.DoublePenetration) &&
				Genital_Helper.has_anus(reciever) &&
				RJWPregnancySettings.insect_anal_pregnancy_enabled)
			{
				return true;
			}

			if (props.sexType == xxx.rjwSextype.Oral &&
				RJWPregnancySettings.insect_oral_pregnancy_enabled)
			{
				return true;
			}

			return false;
		}

		private static bool CanCocoon(Pawn pawn)
		{
			return xxx.is_insect(pawn);
		}

		public static Hediff GetPregnancy(Pawn pawn)
		{
			var set = pawn.health.hediffSet;

			Hediff preg =
				Hediff_BasePregnancy.KnownPregnancies()
				.Select(x => set.GetFirstHediffOfDef(HediffDef.Named(x)))
				.FirstOrDefault(x => x != null) ??
				set.GetFirstHediffOfDef(HediffDef.Named("Pregnant"));

			return preg;
		}

		///Can get preg with above conditions, do impregnation.

		[SyncMethod]
		public static void DoEgg(SexProps props)
		{
			if (RJWPregnancySettings.insect_pregnancy_enabled)
			{
				if (RJWSettings.DevMode) ModLog.Message(" insect pregnancy");

				//female "insect" plant eggs
				//futa "insect" 50% plant eggs
				if ((Genital_Helper.has_ovipositorF(props.pawn) && !Genital_Helper.has_penis_fertile(props.pawn)) ||
					(Rand.Value > 0.5f && Genital_Helper.has_ovipositorF(props.pawn)))
					//penis eggs someday?
					//(Rand.Value > 0.5f && (Genital_Helper.has_ovipositorF(pawn) || Genital_Helper.has_penis_fertile(pawn) && pawn.RaceImplantEggs())))
				{
					float maxeggssize = (props.partner.BodySize / 5) * (xxx.has_quirk(props.partner, "Incubator") ? 2f : 1f) * (Genital_Helper.has_ovipositorF(props.partner) ? 2f : 1f);
					float eggedsize = 0;

					foreach (Hediff_InsectEgg egg in props.partner.health.hediffSet.GetHediffs<Hediff_InsectEgg>())
					{
						if (egg.father != null)
							eggedsize += egg.father.RaceProps.baseBodySize / 5 * (egg.def as HediffDef_InsectEgg).eggsize * RJWPregnancySettings.egg_pregnancy_eggs_size;
						else
							eggedsize += egg.implanter.RaceProps.baseBodySize / 5 * (egg.def as HediffDef_InsectEgg).eggsize * RJWPregnancySettings.egg_pregnancy_eggs_size;
					}
					if (RJWSettings.DevMode) ModLog.Message(" determine " + xxx.get_pawnname(props.partner) + " size of eggs inside: " + eggedsize + ", max: " + maxeggssize);

					var eggs = props.pawn.health.hediffSet.GetHediffs<Hediff_InsectEgg>().ToList();
					BodyPartRecord partnerGenitals = null;

					if (props.sexType == xxx.rjwSextype.Anal)
						partnerGenitals = Genital_Helper.get_anusBPR(props.partner);
					else if (props.sexType == xxx.rjwSextype.Oral)
						partnerGenitals = Genital_Helper.get_stomachBPR(props.partner);
					else if (props.sexType == xxx.rjwSextype.DoublePenetration && Rand.Value > 0.5f && RJWPregnancySettings.insect_anal_pregnancy_enabled)
						partnerGenitals = Genital_Helper.get_anusBPR(props.partner);
					else
						partnerGenitals = Genital_Helper.get_genitalsBPR(props.partner);

					var removepreg = true;
					while (eggs.Any() && eggedsize < maxeggssize)
					{
						if (removepreg && props.sexType == xxx.rjwSextype.Vaginal)
						{
							var preg = GetPregnancy(props.partner);
							if (preg != null) // removing old pregnancies
							{
								if (preg is Hediff_MechanoidPregnancy)
								{
									if (RJWSettings.DevMode) ModLog.Message(" egging - pregnant by mechanoid, skip");
								}

								if (preg is Hediff_BasePregnancy)
								{
									if (RJWSettings.DevMode) ModLog.Message(" egging - removing rjw normal pregnancy");
									(preg as Hediff_BasePregnancy).Kill();
								}
								else
								{
									if (RJWSettings.DevMode) ModLog.Message(" egging - removing vanilla or other mod pregnancy");
									props.partner.health.RemoveHediff(preg);
								}
								removepreg = false;
							}
						}

						var egg = eggs.First();
						eggs.Remove(egg);

						props.pawn.health.RemoveHediff(egg);
						props.partner.health.AddHediff(egg, partnerGenitals);

						egg.Implanter(props.pawn);

						eggedsize += egg.eggssize * (egg.def as HediffDef_InsectEgg).eggsize * RJWPregnancySettings.egg_pregnancy_eggs_size;
					}
				}
				//male or futa fertilize eggs
				else if (!props.pawn.health.hediffSet.HasHediff(xxx.sterilized))
				{
					if (Genital_Helper.has_penis_fertile(props.pawn))
						if ((Genital_Helper.has_ovipositorF(props.pawn) || Genital_Helper.has_ovipositorM(props.pawn)) || (props.pawn.health.capacities.GetLevel(xxx.reproduction) > 0))
						{
							foreach (var egg in props.partner.health.hediffSet.GetHediffs<Hediff_InsectEgg>())
								egg.Fertilize(props.pawn);
						}
				}
				return;
			}
		}

		[SyncMethod]
		public static void Doimpregnate(Pawn pawn, Pawn partner)
		{
			if (RJWSettings.DevMode) ModLog.Message(" Doimpregnate " + xxx.get_pawnname(pawn) + " is a father, " + xxx.get_pawnname(partner) + " is a mother");

			if (AndroidsCompatibility.IsAndroid(pawn) && !AndroidsCompatibility.AndroidPenisFertility(pawn))
			{
				if (RJWSettings.DevMode) ModLog.Message(" Father is android with no arcotech penis, abort");
				return;
			}
			if (AndroidsCompatibility.IsAndroid(partner) && !AndroidsCompatibility.AndroidVaginaFertility(partner))
			{
				if (RJWSettings.DevMode) ModLog.Message(" Mother is android with no arcotech vagina, abort");
				return;
			}

			// fertility check
			float fertility = RJWPregnancySettings.humanlike_impregnation_chance / 100f;
			if (xxx.is_animal(partner))
				fertility = RJWPregnancySettings.animal_impregnation_chance / 100f;
			// IUD fertility check
			if (partner.health.hediffSet.GetFirstHediffOfDef(DefDatabase<HediffDef>.GetNamed("RJW_IUD")) != null)
				fertility /= 99f;

			// Interspecies modifier
			if (pawn.def.defName != partner.def.defName)
			{
				if (RJWPregnancySettings.complex_interspecies)
					fertility *= SexUtility.BodySimilarity(pawn, partner);
				else
					fertility *= RJWPregnancySettings.interspecies_impregnation_modifier;
			}
			else
			{
				//Egg fertility check
				CompEggLayer compEggLayer = partner.TryGetComp<CompEggLayer>();
				if (compEggLayer != null)
					fertility = 1.0f;
			}

			//Rand.PopState();
			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
			float ReproductionFactor = Math.Min(pawn.health.capacities.GetLevel(xxx.reproduction), partner.health.capacities.GetLevel(xxx.reproduction));
			float pregnancy_chance = fertility * ReproductionFactor;
			float pregnancy_roll_to_fail = Rand.Value;
			//BodyPartRecord torso = partner.RaceProps.body.AllParts.Find(x => x.def == BodyPartDefOf.Torso);

			if (pregnancy_roll_to_fail > pregnancy_chance || pregnancy_chance <= 0)
			{
				if (RJWSettings.DevMode) ModLog.Message(" Impregnation failed. Chance: " + pregnancy_chance.ToStringPercent() + " < " + pregnancy_roll_to_fail.ToStringPercent());
				return;
			}
			if (RJWSettings.DevMode) ModLog.Message(" Impregnation succeeded. Chance: " + pregnancy_chance.ToStringPercent() + " > " + pregnancy_roll_to_fail.ToStringPercent());

			PregnancyDecider(partner, pawn);
		}

		///For checking normal pregnancy, should not for egg implantion or such.
		public static bool CanImpregnate(Pawn fucker, Pawn fucked, xxx.rjwSextype sexType  = xxx.rjwSextype.Vaginal )
		{
			if (fucker == null || fucked == null) return false;

			if (RJWSettings.DevMode) ModLog.Message("Rimjobworld::CanImpregnate checks (" + sexType + "):: " + xxx.get_pawnname(fucker) + " + " + xxx.get_pawnname(fucked) + ":");

			if (sexType == xxx.rjwSextype.MechImplant && !RJWPregnancySettings.mechanoid_pregnancy_enabled)
			{
				if (RJWSettings.DevMode) ModLog.Message(" mechanoid 'pregnancy' disabled");
				return false;
			}
			if (!(sexType == xxx.rjwSextype.Vaginal || sexType == xxx.rjwSextype.DoublePenetration))
			{
				if (RJWSettings.DevMode) ModLog.Message(" sextype cannot result in pregnancy");
				return false;
			}

			if (AndroidsCompatibility.IsAndroid(fucker) && AndroidsCompatibility.IsAndroid(fucked))
			{
				if (RJWSettings.DevMode) ModLog.Message(xxx.get_pawnname(fucked) + " androids cant breed/reproduce androids");
				return false;
			}

			if ((fucker.IsUnsexyRobot() || fucked.IsUnsexyRobot()) && !(sexType == xxx.rjwSextype.MechImplant))
			{
				if (RJWSettings.DevMode) ModLog.Message(" unsexy robot cant be pregnant");
				return false;
			}

			if (!fucker.RaceHasPregnancy())
			{
				if (RJWSettings.DevMode) ModLog.Message(xxx.get_pawnname(fucked) + " filtered race that cant be pregnant");
				return false;
			}

			if (!fucked.RaceHasPregnancy())
			{
				if (RJWSettings.DevMode) ModLog.Message(xxx.get_pawnname(fucker) + " filtered race that cant impregnate");
				return false;
			}

			if (fucked.IsPregnant())
			{
				if (RJWSettings.DevMode) ModLog.Message(" already pregnant.");
				return false;
			}

			if ((from x in fucked.health.hediffSet.GetHediffs<Hediff_InsectEgg>() where x.def == DefDatabase<HediffDef_InsectEgg>.GetNamed(x.def.defName) select x).Any())
			{
				if (RJWSettings.DevMode) ModLog.Message(xxx.get_pawnname(fucked) + " cant get pregnant while eggs inside");
				return false;
			}

			var pawnparts = fucker.GetGenitalsList();
			var partnerparts = fucked.GetGenitalsList();
			if (!(Genital_Helper.has_penis_fertile(fucker, pawnparts) && Genital_Helper.has_vagina(fucked, partnerparts)) && !(Genital_Helper.has_penis_fertile(fucked, partnerparts) && Genital_Helper.has_vagina(fucker, pawnparts)))
			{
				if (RJWSettings.DevMode) ModLog.Message(" missing genitals for impregnation");
				return false;
			}

			if (fucker.health.capacities.GetLevel(xxx.reproduction) <= 0 || fucked.health.capacities.GetLevel(xxx.reproduction) <= 0)
			{
				if (RJWSettings.DevMode) ModLog.Message(" one (or both) pawn(s) infertile");
				return false;
			}

			if (xxx.is_human(fucked) && xxx.is_human(fucker) && (RJWPregnancySettings.humanlike_impregnation_chance == 0 || !RJWPregnancySettings.humanlike_pregnancy_enabled))
			{
				if (RJWSettings.DevMode) ModLog.Message(" human pregnancy chance set to 0% or pregnancy disabled.");
				return false;
			}
			else if (((xxx.is_animal(fucker) && xxx.is_human(fucked)) || (xxx.is_human(fucker) && xxx.is_animal(fucked))) && !RJWPregnancySettings.bestial_pregnancy_enabled)
			{
				if (RJWSettings.DevMode) ModLog.Message(" bestiality pregnancy chance set to 0% or pregnancy disabled.");
				return false;
			}
			else if (xxx.is_animal(fucked) && xxx.is_animal(fucker) && (RJWPregnancySettings.animal_impregnation_chance == 0 || !RJWPregnancySettings.animal_pregnancy_enabled))
			{
				if (RJWSettings.DevMode) ModLog.Message(" animal-animal pregnancy chance set to 0% or pregnancy disabled.");
				return false;
			}
			else if (fucker.def.defName != fucked.def.defName && (RJWPregnancySettings.interspecies_impregnation_modifier <= 0.0f && !RJWPregnancySettings.complex_interspecies))
			{
				if (RJWSettings.DevMode) ModLog.Message(" interspecies pregnancy disabled.");
				return false;
			}

			if (!(fucked.RaceProps.gestationPeriodDays > 0))
			{
				CompEggLayer compEggLayer = fucked.TryGetComp<CompEggLayer>();
				if (compEggLayer == null)
				{
					if (RJWSettings.DevMode) ModLog.Message(xxx.get_pawnname(fucked) + " mother.RaceProps.gestationPeriodDays is " + fucked.RaceProps.gestationPeriodDays + " cant impregnate");
					return false;
				}
			}

			return true;
		}

		//Plant babies for human/bestiality pregnancy
		public static void PregnancyDecider(Pawn mother, Pawn father)
		{
			//human-human
			if (RJWPregnancySettings.humanlike_pregnancy_enabled && xxx.is_human(mother) && xxx.is_human(father))
			{
				CompEggLayer compEggLayer = mother.TryGetComp<CompEggLayer>();
				if (compEggLayer != null)
				{
					// fertilize eggs of humanlikes ?!
					if (!compEggLayer.FullyFertilized)
					{
						compEggLayer.Fertilize(father);
						//if (!mother.kindDef.defName.Contains("Chicken"))
						//	if (compEggLayer.Props.eggFertilizedDef.defName.Contains("RJW"))
					}
				}
				else
					Hediff_BasePregnancy.Create<Hediff_HumanlikePregnancy>(mother, father);
			}
			//human-animal
			//maybe make separate option for human males vs female animals???
			else if (RJWPregnancySettings.bestial_pregnancy_enabled && (xxx.is_human(mother) ^ xxx.is_human(father)))
			{
				CompEggLayer compEggLayer = mother.TryGetComp<CompEggLayer>();
				if (compEggLayer != null)
				{
					if (!compEggLayer.FullyFertilized)
						compEggLayer.Fertilize(father);
				}
				else
					Hediff_BasePregnancy.Create<Hediff_BestialPregnancy>(mother, father);
			}
			//animal-animal
			else if (xxx.is_animal(mother) && xxx.is_animal(father))
			{
				CompEggLayer compEggLayer = mother.TryGetComp<CompEggLayer>();
				if (compEggLayer != null)
				{
					// fertilize eggs of same species
					if (!compEggLayer.FullyFertilized)
						if (mother.kindDef == father.kindDef)
							compEggLayer.Fertilize(father);
				}
				else if (RJWPregnancySettings.animal_pregnancy_enabled)
				{
					Hediff_BasePregnancy.Create<Hediff_BestialPregnancy>(mother, father);
				}
			}
		}

		//Plant Insect eggs/mech chips/other preg mod hediff?
		public static bool PlantSomething(HediffDef def, Pawn target, bool isToAnal = false, int amount = 1)
		{
			if (def == null)
				return false;
			if (!isToAnal && !Genital_Helper.has_vagina(target))
				return false;
			if (isToAnal && !Genital_Helper.has_anus(target))
				return false;

			BodyPartRecord Part = (isToAnal) ? Genital_Helper.get_anusBPR(target) : Genital_Helper.get_genitalsBPR(target);
			if (Part != null || Part.parts.Count != 0)
			{
				//killoff normal preg
				if (!isToAnal)
				{
					if (RJWSettings.DevMode) ModLog.Message(" removing other pregnancies");

					var preg = GetPregnancy(target);
					if (preg != null)
					{
						if (preg is Hediff_BasePregnancy)
						{
							(preg as Hediff_BasePregnancy).Kill();
						}
						else
						{
							target.health.RemoveHediff(preg);
						}
					}
				}

				for (int i = 0; i < amount; i++)
				{

					if (RJWSettings.DevMode) ModLog.Message(" planting something weird");
					target.health.AddHediff(def, Part);
				}

				return true;
			}
			return false;
		}

		///
		/// Remove CnP Pregnancy, that is added without passing rjw checks
		///
		public static void cleanup_CnP(Pawn pawn)
		{
			//They do subpar probability checks and disrespect our settings, but I fail to just prevent their doloving override.
			//probably needs harmonypatch
			//So I remove the hediff if it is created and recreate it if needed in our handler later

			if (RJWSettings.DevMode) ModLog.Message(" cleanup_CnP after love check");

			var h = pawn.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("HumanPregnancy"));
			if (h != null && h.ageTicks < 100)
			{
				pawn.health.RemoveHediff(h);
				if (RJWSettings.DevMode) ModLog.Message(" removed hediff from " + xxx.get_pawnname(pawn));
			}
		}

		///
		/// Remove Vanilla Pregnancy
		///
		public static void cleanup_vanilla(Pawn pawn)
		{
			if (RJWSettings.DevMode) ModLog.Message(" cleanup_vanilla after love check");

			var h = pawn.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("Pregnant"));
			if (h != null && h.ageTicks < 100)
			{
				pawn.health.RemoveHediff(h);
				if (RJWSettings.DevMode) ModLog.Message(" removed hediff from " + xxx.get_pawnname(pawn));
			}
		}

		///
		/// Below is stuff for RimWorldChildren
		/// its not used, we rely only on our own pregnancies
		///

		///
		/// This function tries to call Children and pregnancy utilities to see if that mod could handle the pregnancy
		///
		/// <returns>true if cnp pregnancy will work, false if rjw one should be used instead</returns>
		public static bool CnP_WillAccept(Pawn mother)
		{
			//if (!xxx.RimWorldChildrenIsActive)
				return false;

			//return RimWorldChildren.ChildrenUtility.RaceUsesChildren(mother);
		}

		///
		/// This funtcion tries to call Children and Pregnancy to create humanlike pregnancy implemented by the said mod.
		///
		public static void CnP_DoPreg(Pawn mother, Pawn father)
		{
			if (!xxx.RimWorldChildrenIsActive)
				return;

			//RimWorldChildren.Hediff_HumanPregnancy.Create(mother, father);
		}

	}
}


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

namespace rjw
{
	public class Recipe_Abortion : Recipe_RemoveHediff
	{
		public override IEnumerable<BodyPartRecord> GetPartsToApplyOn(Pawn pawn, RecipeDef recipe)
		{
			BodyPartRecord part = pawn.RaceProps.body.corePart;
			if (recipe.appliedOnFixedBodyParts[0] != null)
				part = pawn.RaceProps.body.AllParts.Find(x => x.def == recipe.appliedOnFixedBodyParts[0]);
			if (part != null)
			{
				bool isMatch = Hediff_BasePregnancy.KnownPregnancies() // For every known pregnancy
					.Where(x => pawn.health.hediffSet.HasHediff(HediffDef.Named(x), true) && recipe.removesHediff == HediffDef.Named(x)) // Find matching bodyparts
					.Select(x => (Hediff_BasePregnancy)pawn.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named(x))) // get pregnancy hediff
					.Where(pregnancy => pregnancy.is_discovered &&
						(!pregnancy.is_checked || !(pregnancy is Hediff_MechanoidPregnancy))) // Find visible pregnancies or unchecked mech
					.Any(); // return true if found something
				if (isMatch)
				{
					yield return part;
				}
			}
		}
		public override void ApplyOnPawn(Pawn pawn, BodyPartRecord part, Pawn billDoer, List<Thing> ingredients, Bill bill)
		{
			foreach (Hediff x in pawn.health.hediffSet.hediffs.Where(x => x is Hediff_MechanoidPregnancy))
			{
				(x as Hediff_MechanoidPregnancy).GiveBirth();
				return;
			}
			base.ApplyOnPawn(pawn, part, billDoer, ingredients, bill);
		}
	}
	public class Recipe_PregnancyAbortMech : Recipe_Abortion
	{

		public override IEnumerable<BodyPartRecord> GetPartsToApplyOn(Pawn pawn, RecipeDef recipe)
		{
			BodyPartRecord part = pawn.RaceProps.body.corePart;
			if (recipe.appliedOnFixedBodyParts[0] != null)
				part = pawn.RaceProps.body.AllParts.Find(x => x.def == recipe.appliedOnFixedBodyParts[0]);
			if (part != null)
			{
				bool isMatch = Hediff_BasePregnancy.KnownPregnancies() // For every known pregnancy
					.Where(x => pawn.health.hediffSet.HasHediff(HediffDef.Named(x), true) && recipe.removesHediff == HediffDef.Named(x)) // Find matching bodyparts
					.Select(x => (Hediff_BasePregnancy)pawn.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named(x))) // get pregnancy hediff
					.Where(pregnancy => pregnancy.is_checked && pregnancy is Hediff_MechanoidPregnancy) // Find checked/visible pregnancies
					.Any(); // return true if found something
				if (isMatch)
				{
					yield return part;
				}
			}
		}
	}
}


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

namespace rjw
{
	public class Recipe_ClaimChild :  RecipeWorker
	{
		public override IEnumerable<BodyPartRecord> GetPartsToApplyOn(Pawn pawn, RecipeDef recipe)
		{
			if (pawn != null)//I have no idea how it works but Recipe_ShutDown : RecipeWorker does not return values when not applicable
			{
				//Log.Message("RJW Claim child check on " + pawn);
				if (xxx.is_human(pawn) && !pawn.IsColonist)
				{
					if ( (pawn.ageTracker.CurLifeStageIndex < 2))//Guess it is hardcoded for now to `baby` and `toddler` of standard 4 stages of human life
					{
						BodyPartRecord brain = pawn.health.hediffSet.GetBrain();
						if (brain != null)
						{
							//Log.Message("RJW Claim child is applicable");
							yield return brain;
						}
					}
				}
			}
		}

		public override void ApplyOnPawn(Pawn pawn, BodyPartRecord part, Pawn billDoer, List<Thing> ingredients, Bill bill)
		{
			if (pawn == null)
			{
				ModLog.Error("Error applying medical recipe, pawn is null");
				return;
			}
			pawn.SetFaction(Faction.OfPlayer);
			//we could do
			//pawn.SetFaction(billDoer.Faction);
			//but that is useless because GetPartsToApplyOn does not support factions anyway and all recipes are hardcoded to player.
		}
	}
}


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

namespace rjw
{
	public class Recipe_DeterminePregnancy : RecipeWorker
	{
		public override IEnumerable<BodyPartRecord> GetPartsToApplyOn(Pawn pawn, RecipeDef recipe)
		{
			/* Males can be impregnated by mechanoids, probably
			if (!xxx.is_female(pawn))
			{
				yield break;
			}
			*/
			BodyPartRecord part = pawn.RaceProps.body.corePart;
			if (recipe.appliedOnFixedBodyParts[0] != null)
				part = pawn.RaceProps.body.AllParts.Find(x => x.def == recipe.appliedOnFixedBodyParts[0]);
			if (part != null && (pawn.ageTracker.CurLifeStage.reproductive)
				|| pawn.IsPregnant(true))
			{
				yield return part;
			}
		}

		public override void ApplyOnPawn(Pawn pawn, BodyPartRecord part, Pawn billDoer, List<Thing> ingredients, Bill bill)
		{
			var preg = (PregnancyHelper.GetPregnancy(pawn) as Hediff_BasePregnancy);

			if (preg != null)
			{
				preg.CheckPregnancy();
			}
			else
			{
				Messages.Message(xxx.get_pawnname(billDoer) + " has determined " + xxx.get_pawnname(pawn) + " is not pregnant.", MessageTypeDefOf.NeutralEvent);
			}
		}
	}
	public class Recipe_DeterminePaternity : RecipeWorker
	{
		public override IEnumerable<BodyPartRecord> GetPartsToApplyOn(Pawn pawn, RecipeDef recipe)
		{
			BodyPartRecord part = pawn.RaceProps.body.corePart;
			if (recipe.appliedOnFixedBodyParts[0] != null)
				part = pawn.RaceProps.body.AllParts.Find(x => x.def == recipe.appliedOnFixedBodyParts[0]);
			if (part != null && pawn.IsPregnant(true))
			{
				yield return part;
			}
		}

		public override void ApplyOnPawn(Pawn pawn, BodyPartRecord part, Pawn billDoer, List<Thing> ingredients, Bill bill)
		{
			var preg = (PregnancyHelper.GetPregnancy(pawn) as Hediff_BasePregnancy);

			if (preg != null)
			{
				Messages.Message(xxx.get_pawnname(billDoer) + " has determined "+ xxx.get_pawnname(pawn) + " is pregnant and " + preg.father + " is the father.", MessageTypeDefOf.NeutralEvent);

				preg.CheckPregnancy();
				preg.is_parent_known = true;
			}
		}
	}
}


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

namespace rjw
{
	/// <summary>
	/// IUD - prevent pregnancy
	/// </summary>
	public class Recipe_InstallIUD : Recipe_InstallImplantToExistParts
	{
		public override IEnumerable<BodyPartRecord> GetPartsToApplyOn(Pawn pawn, RecipeDef recipe)
		{
			if (!xxx.is_female(pawn))
			{
				return new List<BodyPartRecord>();
			}
			return base.GetPartsToApplyOn(pawn, recipe);
		}
	}
}


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

namespace rjw
{
	public class Recipe_PregnancyHackMech : RecipeWorker
	{
		public override IEnumerable<BodyPartRecord> GetPartsToApplyOn(Pawn pawn, RecipeDef recipe)
		{
			BodyPartRecord part = pawn.RaceProps.body.corePart;
			if (recipe.appliedOnFixedBodyParts[0] != null)
				part = pawn.RaceProps.body.AllParts.Find(x => x.def == recipe.appliedOnFixedBodyParts[0]);
			if (part != null && pawn.health.hediffSet.HasHediff(HediffDef.Named("RJW_pregnancy_mech"), part, true))
			{
				Hediff_MechanoidPregnancy pregnancy = (Hediff_MechanoidPregnancy)pawn.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("RJW_pregnancy_mech"));
				//Log.Message("RJW_pregnancy_mech hack check: " + pregnancy.is_checked);
				if (pregnancy.is_checked && !pregnancy.is_hacked)
					yield return part;
			}
		}

		public override void ApplyOnPawn(Pawn pawn, BodyPartRecord part, Pawn billDoer, List<Thing> ingredients, Bill bill)
		{

			if (pawn.health.hediffSet.HasHediff(HediffDef.Named("RJW_pregnancy_mech")))
			{
				Hediff_MechanoidPregnancy pregnancy = (Hediff_MechanoidPregnancy)pawn.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("RJW_pregnancy_mech"));
				pregnancy.Hack();
			}
		}
	}
}


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

namespace rjw
{
	/// <summary>
	/// Sterilization
	/// </summary>
	public class Recipe_InstallImplantToExistParts : Recipe_InstallImplant
	{
		public override IEnumerable<BodyPartRecord> GetPartsToApplyOn(Pawn pawn, RecipeDef recipe)
		{
			bool blocked = Genital_Helper.genitals_blocked(pawn) || xxx.is_slime(pawn);

			if (!blocked)
				foreach (BodyPartRecord record in pawn.RaceProps.body.AllParts.Where(x => recipe.appliedOnFixedBodyParts.Contains(x.def)))
				{
					if (!pawn.health.hediffSet.hediffs.Any((Hediff x) => x.def == recipe.addsHediff))
					{
						yield return record;
					}
				}
		}
	}
}


code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse;

namespace rjw.Modules.Quirks
{
	public interface IQuirkService
	{
		bool HasQuirk(Pawn pawn, string quirk);
	}
}


code:
using rjw.Modules.Quirks;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse;

namespace rjw.Modules.Quirks.Implementation
{
	public class QuirkService : IQuirkService
	{
		public static IQuirkService Instance { get; private set; }

		static QuirkService()
		{
			Instance = new QuirkService();
		}

		private QuirkService() { }

		public bool HasQuirk(Pawn pawn, string quirk)
		{
			//No paw ! hum ... I meant pawn !
			if (pawn == null)
			{
				return false;
			}

			//No quirk !
			if (string.IsNullOrWhiteSpace(quirk))
			{
				return false;
			}

			string loweredQuirk = quirk.ToLower();

			string pawnQuirks = CompRJW.Comp(pawn).quirks
				.ToString()
				.ToLower();

			return pawnQuirks.Contains(loweredQuirk);
		}
	}
}


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

namespace rjw.Modules.Quirks
{
	public static class Quirks
	{
		public const string Podophile = "Podophile";
		public const string Impregnation = "Impregnation fetish";
	}
}


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

namespace rjw.Modules.Shared.Comparers
{
	public class StringComparer_IgnoreCase : IEqualityComparer<string>
	{
		public bool Equals(string x, string y)
		{
			return String.Equals(x, y, StringComparison.InvariantCultureIgnoreCase);
		}

		public int GetHashCode(string obj)
		{
			if (obj == null)
			{
				return 0;
			}

			return obj.GetHashCode();
		}
	}
}


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

namespace rjw.Modules.Shared.Enums
{
	public enum PawnState
	{
		Healthy,
		Downed,
		Unconscious,
		Dead
	}
}


code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse;

namespace rjw.Modules.Shared.Extensions
{
	public static class BodyPartRecordExtension
	{
		public static bool IsMissingForPawn(this BodyPartRecord self, Pawn pawn)
		{
			if (pawn == null)
			{
				throw new ArgumentNullException(nameof(pawn));
			}
			if (self == null)
			{
				throw new ArgumentNullException(nameof(self));
			}

			return pawn.health.hediffSet.hediffs
				.Where(hediff => hediff.Part == self)
				.Where(hediff => hediff is Hediff_MissingPart)
				.Any();
		}
	}
}


code:
using System;
using Verse;

namespace rjw.Modules.Shared.Extensions
{
	public static class PawnExtensions
	{
		public static string GetName(this Pawn pawn)
		{
			if (pawn == null)
			{
				return "null";
			}

			if (String.IsNullOrWhiteSpace(pawn.Name?.ToStringFull) == false)
			{
				return pawn.Name.ToStringFull;
			}

			return pawn.def.defName;
		}
	}
}


code:
using System;
using UnityEngine;
using Verse;

namespace rjw.Modules.Shared.Helpers
{
	public static  class AgeHelper
	{
		public const int ImmortalRaceAgeClamp = 25;
		public const int NonHumanRaceAgeClamp = 25;

		public static int ScaleToHumanAge(Pawn pawn, int humanLifespan = 80)
		{
			float pawnAge = pawn.ageTracker.AgeBiologicalYearsFloat;

			if (pawn.def.defName == "Human") return (int)pawnAge; // Human, no need to scale anything.

			float lifeExpectancy = pawn.RaceProps.lifeExpectancy;

			if (RJWSettings.UseAdvancedAgeScaling == true)
			{
				//pseudo-immortal & immortal races
				if (lifeExpectancy >= 500)
				{
					return CalculateForImmortals(pawn, humanLifespan);
				}

				if (lifeExpectancy != humanLifespan)
				{
					//other races
					return CalculateForNonHuman(pawn, humanLifespan);
				}
			}

			float ageScaling = humanLifespan / lifeExpectancy;
			float scaledAge = pawnAge * ageScaling;

			return (int)Mathf.Max(scaledAge, 1);
		}

		private static int CalculateForImmortals(Pawn pawn, int humanLifespan)
		{
			float age = pawn.ageTracker.AgeBiologicalYearsFloat;
			float lifeExpectancy = pawn.RaceProps.lifeExpectancy;
			float growth = pawn.ageTracker.Growth;

			//Growth and hacks
			{
				growth = ImmortalGrowthHacks(pawn, age, growth);

				if (growth < 1)
				{
					return (int)Mathf.Lerp(0, ImmortalRaceAgeClamp, growth);
				}
			}

			//curve
			{
				float life = age / lifeExpectancy;

				//Hypothesis : very long living races looks "young" until the end of their lifespan
				if (life < 0.9f)
				{
					return ImmortalRaceAgeClamp;
				}

				return (int)Mathf.LerpUnclamped(ImmortalRaceAgeClamp, humanLifespan, Mathf.InverseLerp(0.9f, 1, life));
			}
		}

		private static float ImmortalGrowthHacks(Pawn pawn, float age, float originalGrowth)
		{
			if (pawn.ageTracker.CurLifeStage.reproductive == false)
			{
				//Hopefully, reproductive life stage will mean that we're dealing with an adult
				return Math.Min(1, age / ImmortalRaceAgeClamp);
			}

			return 1;
		}

		private static int CalculateForNonHuman(Pawn pawn, int humanLifespan)
		{
			float age = pawn.ageTracker.AgeBiologicalYearsFloat;
			float lifeExpectancy = pawn.RaceProps.lifeExpectancy;
			float growth = pawn.ageTracker.Growth;

			//Growth and hacks
			{
				growth = NonHumanGrowthHacks(pawn, age, growth);

				if (growth < 1)
				{
					return (int)Mathf.Lerp(0, NonHumanRaceAgeClamp, growth);
				}
			}

			//curve
			{
				float life = age / lifeExpectancy;

				return (int)Mathf.LerpUnclamped(NonHumanRaceAgeClamp, humanLifespan, life);
			}
		}

		private static float NonHumanGrowthHacks(Pawn pawn, float age, float originalGrowth)
		{
			if (pawn.ageTracker.CurLifeStage.reproductive == false)
			{
				//Hopefully, reproductive life stage will mean that we're dealing with an adult
				return Math.Min(1, age / NonHumanRaceAgeClamp);
			}

			return 1;
		}

	}
}


code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse;

namespace rjw.Modules.Shared.Helpers
{
	public static class RandomHelper
	{
		private static Random _random;

		static RandomHelper()
		{
			_random = new Random();
		}

		/// <remarks>this is not foolproof</remarks>
		public static TType WeightedRandom<TType>(IList<Weighted<TType>> weights)
		{
			if (weights == null || weights.Any() == false || weights.Where(e => e.Weight < 0).Any())
			{
				return default(TType);
			}

			Weighted<TType> result;

			if (weights.TryRandomElementByWeight(e => e.Weight, out result) == true)
			{
				return result.Element;
			}

			return weights.RandomElement().Element;
		}
	}
}


code:
using rjw.Modules.Shared.Enums;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse;

namespace rjw.Modules.Shared
{
	public interface IPawnStateService
	{
		PawnState Detect(Pawn pawn);
	}
}


code:
using rjw.Modules.Shared.Enums;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse;

namespace rjw.Modules.Shared.Implementation
{
	public class PawnStateService : IPawnStateService
	{
		public static IPawnStateService Instance { get; private set; }

		static PawnStateService()
		{
			Instance = new PawnStateService();
		}

		public PawnState Detect(Pawn pawn)
		{
			if (pawn.Dead)
			{
				return PawnState.Dead;
			}

			if (pawn.Downed)
			{
				if (pawn.health.capacities.CanBeAwake == false)
				{
					return PawnState.Unconscious;
				}

				return PawnState.Downed;
			}

			return PawnState.Healthy;
		}
	}
}


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

namespace rjw.Modules.Shared.Logs
{
	public interface ILog
	{
		void Debug(string message);
		void Debug(string message, Exception e);
		void Message(string message);
		void Message(string message, Exception e);
		void Warning(string message);
		void Warning(string message, Exception e);
		void Error(string message);
		void Error(string message, Exception e);
	}
}


code:
namespace rjw.Modules.Shared.Logs
{
	public interface ILogProvider
	{
		bool IsActive { get; }
	}
}


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

namespace rjw.Modules.Shared.Logs
{
	public static class LogManager
	{
		private class Logger : ILog
		{
			private readonly string _loggerTypeName;
			private readonly ILogProvider _logProvider;

			public Logger(string typeName)
			{
				_loggerTypeName = typeName;
			}
			public Logger(string typeName, ILogProvider logProvider)
			{
				_loggerTypeName = typeName;

				_logProvider = logProvider;
			}

			public void Debug(string message)
			{
				LogDebug(CreateLogMessage(message));
			}
			public void Debug(string message, Exception exception)
			{
				LogDebug(CreateLogMessage(message, exception));
			}

			public void Message(string message)
			{
				LogMessage(CreateLogMessage(message));
			}
			public void Message(string message, Exception exception)
			{
				LogMessage(CreateLogMessage(message, exception));
			}

			public void Warning(string message)
			{
				LogWarning(CreateLogMessage(message));
			}
			public void Warning(string message, Exception exception)
			{
				LogWarning(CreateLogMessage(message, exception));
			}

			public void Error(string message)
			{
				LogError(CreateLogMessage(message));
			}
			public void Error(string message, Exception exception)
			{
				LogError(CreateLogMessage(message, exception));
			}

			private string CreateLogMessage(string message)
			{
				return $"[{_loggerTypeName}] {message}";
			}
			private string CreateLogMessage(string message, Exception exception)
			{
				return $"{CreateLogMessage(message)}{Environment.NewLine}{exception}";
			}

			private void LogDebug(string message)
			{
				if (_logProvider?.IsActive != false && RJWSettings.DevMode == true)
				{
					ModLog.Message(message);
				}
			}
			private void LogMessage(string message)
			{
				if (_logProvider?.IsActive != false)
				{
					ModLog.Message(message);
				}
			}
			private void LogWarning(string message)
			{
				if (_logProvider?.IsActive != false)
				{
					ModLog.Warning(message);
				}
			}
			private void LogError(string message)
			{
				if (_logProvider?.IsActive != false)
				{
					ModLog.Error(message);
				}
			}
		}

		public static ILog GetLogger<TType, TLogProvider>()
			where TLogProvider : ILogProvider, new()
		{
			return new Logger(typeof(TType).Name, new TLogProvider());
		}
		public static ILog GetLogger<TType>()
		{
			return new Logger(typeof(TType).Name);
		}
		public static ILog GetLogger(string staticTypeName)
		{
			return new Logger(staticTypeName);
		}
	}
}


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

namespace rjw.Modules.Shared
{
	public static class Multipliers
	{
		public const float Never = 0f;

		public const float AlmostNever = 0.1f;
		public const float VeryRare = 0.2f;

		public const float Rare = 0.5f;
		public const float Uncommon = 0.8f;

		public const float Average = 1.0f;

		public const float Common = 1.2f;
		public const float Frequent = 1.5f;

		public const float VeryFrequent = 1.8f;
		public const float Doubled = 2.0f;
		public const float DoubledPlus = 2.5f;
	}
}


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

namespace rjw.Modules.Shared
{
	public class Weighted<TType>
	{
		public TType Element { get; set; }
		public float Weight { get; set; }

		public Weighted(float weight, TType element)
		{
			Weight = weight;
			Element = element;
		}
	}
}


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

namespace rjw
{
	public class Need_Sex : Need_Seeker
	{
		public bool isInvisible => pawn.Map == null;

		private bool BootStrapTriggered = false;

		public int needsex_tick = needsex_tick_timer;
		public const int needsex_tick_timer = 10;
		private static float decay_per_day = 0.3f;
		private float decay_rate_modifier = RJWSettings.sexneed_decay_rate;

		public float thresh_frustrated()
		{
			return 0.05f;
		}

		public float thresh_horny()
		{
			return 0.25f;
		}

		public float thresh_neutral()
		{
			return 0.50f;
		}

		public float thresh_satisfied()
		{
			return 0.75f;
		}

		public float thresh_ahegao()
		{
			return 0.95f;
		}

		public Need_Sex(Pawn pawn) : base(pawn)
		{
			//if (xxx.is_mechanoid(pawn)) return; //Added by nizhuan-jjr:Misc.Robots are not allowed to have sex, so they don't need sex actually.
			threshPercents = new List<float>
			{
				thresh_frustrated(),
				thresh_horny(),
				thresh_neutral(),
				thresh_satisfied(),
				thresh_ahegao()
			};
		}

		public static float brokenbodyfactor(Pawn pawn)
		{
			//This adds in the broken body system
			float broken_body_factor = 1f;
			if (pawn.health.hediffSet.HasHediff(xxx.feelingBroken))
			{
				switch (pawn.health.hediffSet.GetFirstHediffOfDef(xxx.feelingBroken).CurStageIndex)
				{
					case 0:
						return 0.75f;
					case 1:
						return 1.4f;
					case 2:
						return 2f;
				}
			}
			return broken_body_factor;
		}

		//public override bool ShowOnNeedList
		//{
		//	get
		//	{
		//		if (Genital_Helper.has_genitals(pawn))
		//			return true;

		//		ModLog.Message("curLevelInt " + curLevelInt);
		//		return false;
		//	}
		//}

		//public override string GetTipString()
		//{
		//	return string.Concat(new string[]
		//	{
		//		this.LabelCap,
		//		": ",
		//		this.CurLevelPercentage.ToStringPercent(),
		//		"\n",
		//		this.def.description,
		//		"\n",
		//	});
		//}

		public static float druggedfactor(Pawn pawn)
		{
			if (pawn.health.hediffSet.HasHediff(HediffDef.Named("HumpShroomAddiction")) && !pawn.health.hediffSet.HasHediff(HediffDef.Named("HumpShroomEffect")))
			{
				//ModLog.Message("Need_Sex::druggedfactor 0.5 pawn is " + xxx.get_pawnname(pawn));
				return 0.5f;
			}

			if (pawn.health.hediffSet.HasHediff(HediffDef.Named("HumpShroomEffect")))
			{
				//ModLog.Message("Need_Sex::druggedfactor 3 pawn is " + xxx.get_pawnname(pawn));
				return 3f;
			}

			//ModLog.Message("Need_Sex::druggedfactor 1 pawn is " + xxx.get_pawnname(pawn));
			return 1f;
		}

		static float diseasefactor(Pawn pawn)
		{
			return 1f;
		}
		static float futafactor(Pawn pawn)
		{
			var parts = pawn.GetGenitalsList();
			return (((Genital_Helper.has_penis_fertile(pawn, parts) || Genital_Helper.has_penis_infertile(pawn, parts)) && Genital_Helper.has_vagina(pawn, parts)) ? 2.0f : 1.0f);
		}

		static float agefactor(Pawn pawn)
		{
			if (xxx.is_human(pawn))
			{
				//countdown
				Need_Sex horniness = pawn.needs.TryGetNeed<Need_Sex>();
				if (horniness.CurLevel > 0.5f)
					return 1f;

				//set to 50% for underage

				if (RJWSettings.sex_age_legacymode)
				{
					int age = pawn.ageTracker.AgeBiologicalYears;
					if (age < RJWSettings.sex_minimum_age)
						return 0f;
				}
				//{
				//	int age = pawn.ageTracker.AgeBiologicalYears;
				//	int t = 12; // humans teen/reproductive stage
				//	if (pawn.GetRJWPawnData().RaceSupportDef?.limitedSex != null)
				//	{
				//		t = pawn.GetRJWPawnData().RaceSupportDef.limitedSex;
				//	}
				//	else if (!pawn.ageTracker.CurLifeStage.reproductive)
				//		if (pawn.ageTracker.Growth < 1)
				//			return 0f;

				//	if (age < t)
				//		return 0f;
				//}
				if (pawn.ageTracker.Growth < 1 && !pawn.ageTracker.CurLifeStage.reproductive)
					return 0f;
			}
			return 1f;
		}

		static float fall_per_tick(Pawn pawn)
		{
			var fall_per_tick =
				//def.fallPerDay *
				decay_per_day / GenDate.TicksPerDay *
				brokenbodyfactor(pawn) *
				druggedfactor(pawn) *
				diseasefactor(pawn) *
				agefactor(pawn) *
				futafactor(pawn);
			//--ModLog.Message("Need_Sex::NeedInterval is called - pawn is " + xxx.get_pawnname(pawn) + " is has both genders " + (Genital_Helper.has_penis(pawn) && Genital_Helper.has_vagina(pawn)));
			//ModLog.Message(" " + xxx.get_pawnname(pawn) + "'s sex need stats:: fall_per_tick: " + fall_per_tick + ", sex_need_factor_from_lifestage: " + sex_need_factor_from_lifestage(pawn) );
			return fall_per_tick;
		}

		public override void NeedInterval() //150 ticks between each calls from Pawn_NeedsTracker()
		{
			if (isInvisible) return; // no caravans

			if (needsex_tick <= 0) // every 10 ticks - real tick
			{
				if (xxx.is_asexual(pawn))
				{
					CurLevel = 0.5f;
					return;
				}

				//--ModLog.Message("Need_Sex::NeedInterval is called0 - pawn is "+xxx.get_pawnname(pawn));
				needsex_tick = needsex_tick_timer;

				if (!def.freezeWhileSleeping || pawn.Awake())
				{
					var fall_per_call = 150 * needsex_tick_timer * fall_per_tick(pawn);
					CurLevel -= fall_per_call * xxx.get_sex_drive(pawn) * RJWSettings.sexneed_decay_rate;
					// Each day has 60000 ticks, each hour has 2500 ticks, so each hour has 50/3 calls, in other words, each call takes .06 hour.

					//ModLog.Message(" " + xxx.get_pawnname(pawn) + "'s sex need stats:: Decay/call: " + fall_per_call * decay_rate_modifier * xxx.get_sex_drive(pawn) + ", Cur.lvl: " + CurLevel + ", Dec. rate: " + decay_rate_modifier + ", Sex drive: " +  xxx.get_sex_drive(pawn));

					if (!RJWSettings.Disable_MeditationFocusDrain && CurLevel < thresh_frustrated())
					{
						SexUtility.OffsetPsyfocus(pawn, -0.01f);
					}
					//if (CurLevel < thresh_horny())
					//{
					//	SexUtility.OffsetPsyfocus(pawn, -0.01f);
					//}
					//if (CurLevel < thresh_frustrated() || CurLevel > thresh_ahegao())
					//{
					//	SexUtility.OffsetPsyfocus(pawn, -0.05f);
					//}
				}

				//--ModLog.Message("Need_Sex::NeedInterval is called1");

				//If they need it, they should seek it
				if (CurLevel < thresh_horny() && (pawn.mindState.canLovinTick - Find.TickManager.TicksGame > 300) )
				{
					pawn.mindState.canLovinTick = Find.TickManager.TicksGame + 300;
				}

				// the bootstrap of the mapInjector will only be triggered once per visible pawn.
				if (!BootStrapTriggered)
				{
					//--ModLog.Message("Need_Sex::NeedInterval::calling boostrap - pawn is " + xxx.get_pawnname(pawn));
					xxx.bootstrap(pawn.Map);
					BootStrapTriggered = true;
				}
			}
			else
			{
				needsex_tick--;
			}
			//--ModLog.Message("Need_Sex::NeedInterval is called2 - needsex_tick is "+needsex_tick);
		}
	}
}


code:
using Verse;

namespace rjw
{
	/// <summary>
	/// Looks up and returns a BodyPartTagDef defined in the XML
	/// </summary>
	public static class BodyPartTagDefOf {
		public static BodyPartTagDef RJW_Fertility
		{
			get
			{
				if (a == null) a = (BodyPartTagDef)GenDefDatabase.GetDef(typeof(BodyPartTagDef), "RJW_Fertility");
				return a;
			}
		}
		private static BodyPartTagDef a;
	}

}


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

namespace rjw
{
	/// <summary>
	/// Calculates a pawn's fertility based on its age and fertility sources
	/// </summary>
	public class PawnCapacityWorker_Fertility : PawnCapacityWorker
	{
		public override float CalculateCapacityLevel(HediffSet diffSet, List<PawnCapacityUtility.CapacityImpactor> impactors = null)
		{
			Pawn pawn = diffSet.pawn;
			var parts = pawn.GetGenitalsList();

			if (!Genital_Helper.has_penis_fertile(pawn, parts) && !Genital_Helper.has_vagina(pawn, parts))
				return 0;

			if (Genital_Helper.has_ovipositorF(pawn, parts) || Genital_Helper.has_ovipositorM(pawn, parts))
				return 0;

			//ModLog.Message("PawnCapacityWorker_Fertility::CalculateCapacityLevel is called for: " + xxx.get_pawnname(pawn));
			RaceProperties race = diffSet.pawn.RaceProps;

			if (!pawn.RaceHasFertility())
			{
				//Log.Message(" Fertility_filter, no fertility for : " + pawn.kindDef.defName);
				return 0f;
			}

			//androids only fertile with archotech parts
			if (AndroidsCompatibility.IsAndroid(pawn) && !(AndroidsCompatibility.AndroidPenisFertility(pawn) || AndroidsCompatibility.AndroidVaginaFertility(pawn)))
			{
				//Log.Message(" Android has no archotech genitals set fertility to 0 for: " + pawn.kindDef.defName);
				return 0f;
			}

			float startAge = 0f;	//raise fertility
			float startMaxAge = 0f;	//max fertility
			float endAge = race.lifeExpectancy * (RJWPregnancySettings.fertility_endage_male * 0.7f); // Age when males start to lose potency.
			float zeroFertility = race.lifeExpectancy * RJWPregnancySettings.fertility_endage_male;	// Age when fertility hits 0%.

			if (xxx.is_female(pawn))
			{
				if (xxx.is_animal(pawn))
				{
					endAge = race.lifeExpectancy * (RJWPregnancySettings.fertility_endage_female_animal * 0.6f);
					zeroFertility = race.lifeExpectancy * RJWPregnancySettings.fertility_endage_female_animal;
				}
				else
				{
					endAge = race.lifeExpectancy * (RJWPregnancySettings.fertility_endage_female_humanlike * 0.6f); // Age when fertility begins to drop.
					zeroFertility = race.lifeExpectancy * RJWPregnancySettings.fertility_endage_female_humanlike; // Age when fertility hits 0%.
				}
			}
			//If pawn is an animal, first reproductive lifestage is (usually) adult, so set startMaxAge at first reproductive stage, and startAge at stage before that.
			int adult;
			if (xxx.is_animal(pawn) && (adult = race.lifeStageAges.FirstIndexOf((ls) => ls.def.reproductive)) > 0)
			{
				startAge = race.lifeStageAges[adult - 1].minAge;
				startMaxAge = race.lifeStageAges[adult].minAge;
			}
			else
			{
				foreach (LifeStageAge lifestage in race.lifeStageAges) {
					if (lifestage.def.reproductive)
						//presumably teen stage
						if (startAge == 0f && startMaxAge == 0f) {
							startAge = lifestage.minAge;
							startMaxAge = (Mathf.Max(startAge + (startAge + endAge) * 0.08f, startAge));
						}
						//presumably adult stage
						else {
							if (startMaxAge > lifestage.minAge)
								startMaxAge = lifestage.minAge;
						}
				}
			}
			//Log.Message(" Fertility ages for " + pawn.Name + " are: " + startAge + ", " + startMaxAge + ", " + endAge + ", " + endMaxAge);

			float result = PawnCapacityUtility.CalculateTagEfficiency(diffSet, BodyPartTagDefOf.RJW_Fertility, 1f, FloatRange.ZeroToOne, impactors);
			result *= GenMath.FlatHill(startAge, startMaxAge, endAge, zeroFertility, pawn.ageTracker.AgeBiologicalYearsFloat);

			//ModLog.Message("PawnCapacityWorker_Fertility::CalculateCapacityLevel result is: " + result);
			return result;

		}

		public override bool CanHaveCapacity(BodyDef body)
		{
			return body.HasPartWithTag(BodyPartTagDefOf.RJW_Fertility);
		}
	}
}


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

namespace rjw
{
	public class Recipe_InstallAnus : Recipe_InstallPrivates
	{
		public override IEnumerable<BodyPartRecord> GetPartsToApplyOn(Pawn p, RecipeDef r)
		{
			var gen_blo = Genital_Helper.anus_blocked(p);
			foreach (BodyPartRecord part in base.GetPartsToApplyOn(p, r))
				if ((!gen_blo) || (part != xxx.anus))
					yield return part;
		}
	}
}


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

namespace rjw
{
	public class Recipe_InstallBreasts : Recipe_InstallPrivates
	{
		public override IEnumerable<BodyPartRecord> GetPartsToApplyOn(Pawn p, RecipeDef r)
		{
			var gen_blo = Genital_Helper.breasts_blocked(p);
			foreach (BodyPartRecord part in base.GetPartsToApplyOn(p, r))
				if ((!gen_blo) || (part != xxx.breasts))
					yield return part;
		}
	}
}


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

namespace rjw
{
	public class Recipe_InstallGenitals : Recipe_InstallPrivates
	{
		public override IEnumerable<BodyPartRecord> GetPartsToApplyOn(Pawn p, RecipeDef r)
		{
			var gen_blo = Genital_Helper.genitals_blocked(p);
			foreach (BodyPartRecord part in base.GetPartsToApplyOn(p, r))
				if ((!gen_blo) || (part != xxx.genitals))
					yield return part;

		}
	}
}


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

namespace rjw
{
	public class Recipe_InstallPart : rjw_CORE_EXPOSED.Recipe_InstallOrReplaceBodyPart
	{
		public override void ApplyOnPawn(Pawn pawn, BodyPartRecord part, Pawn billDoer, List<Thing> ingredients, Bill bill)
		{
			GenderHelper.Sex before = GenderHelper.GetSex(pawn);

			base.ApplyOnPawn(pawn, part, billDoer, ingredients, bill);

			GenderHelper.Sex after = GenderHelper.GetSex(pawn);

			GenderHelper.ChangeSex(pawn, before, after);
		}
	}

	public class Recipe_InstallGenitals : Recipe_InstallPart
	{
		public override bool blocked(Pawn p)
		{
			return (Genital_Helper.genitals_blocked(p) || xxx.is_slime(p));//|| xxx.is_demon(p)
		}
	}

	public class Recipe_InstallBreasts : Recipe_InstallPart
	{
		public override bool blocked(Pawn p)
		{
			return (Genital_Helper.breasts_blocked(p) || xxx.is_slime(p));//|| xxx.is_demon(p)
		}
	}

	public class Recipe_InstallAnus : Recipe_InstallPart
	{
		public override bool blocked(Pawn p)
		{
			return (Genital_Helper.anus_blocked(p) || xxx.is_slime(p));//|| xxx.is_demon(p)
		}
	}
}


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

namespace rjw
{
	public class Recipe_GrowBreasts : Recipe_Surgery
	{
		public override void ApplyOnPawn(Pawn pawn, BodyPartRecord part, Pawn billDoer, List<Thing> ingredients, Bill bill)
		{
			if (billDoer != null)
			{
				if (CheckSurgeryFail(billDoer, pawn, ingredients, part, bill))
				{
					return;
				}
				TaleRecorder.RecordTale(TaleDefOf.DidSurgery, new object[]
				{
					billDoer,
					pawn
				});
			}

			var oldBoobs = pawn.health.hediffSet.hediffs.FirstOrDefault(hediff => hediff.def == bill.recipe.removesHediff);
			var newBoobs = bill.recipe.addsHediff;
			var newSize = BreastSize_Helper.GetSize(newBoobs);

			GenderHelper.ChangeSex(pawn, () =>
			{
				BreastSize_Helper.HurtBreasts(pawn, part, 3 * (newSize - 1));
				if (pawn.health.hediffSet.PartIsMissing(part))
				{
					return;
				}
				if (oldBoobs != null)
				{
					pawn.health.RemoveHediff(oldBoobs);
				}
				pawn.health.AddHediff(newBoobs, part);
			});
		}

		public override IEnumerable<BodyPartRecord> GetPartsToApplyOn(Pawn pawn, RecipeDef recipeDef)
		{
			yield break;
			var chest = Genital_Helper.get_breastsBPR(pawn);

			if (pawn.health.hediffSet.PartIsMissing(chest)
				|| Genital_Helper.breasts_blocked(pawn))
			{
				yield break;
			}

			var old = recipeDef.removesHediff;
			if (old == null ? BreastSize_Helper.HasNipplesOnly(pawn, chest) : pawn.health.hediffSet.HasHediff(old, chest))
			{
				yield return chest;
			}
		}
	}
}


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

namespace rjw
{
	/// <summary>
	/// Removes heddifs (restraints/cocoon)
	/// </summary>
	public class Recipe_RemoveRestraints : Recipe_RemoveHediff
	{
		public override bool AvailableOnNow(Thing pawn, BodyPartRecord part = null)
		{
			return true;
		}

		public override IEnumerable<BodyPartRecord> GetPartsToApplyOn(Pawn pawn, RecipeDef recipe)
		{
			List<Hediff> allHediffs = pawn.health.hediffSet.hediffs;
			int i = 0;
			while (true)
			{
				if (i >= allHediffs.Count)
				{
					yield break;
				}
				if (allHediffs[i].def == recipe.removesHediff && allHediffs[i].Visible)
				{
					break;
				}
				i++;
			}

			yield return allHediffs[i].Part;
		}
	}
}


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

namespace rjw
{
	public class Recipe_ShrinkBreasts : Recipe_Surgery
	{
		public override void ApplyOnPawn(Pawn pawn, BodyPartRecord part, Pawn billDoer, List<Thing> ingredients, Bill bill)
		{
			if (billDoer != null)
			{
				if (CheckSurgeryFail(billDoer, pawn, ingredients, part, bill))
				{
					return;
				}
				TaleRecorder.RecordTale(TaleDefOf.DidSurgery, new object[]
				{
					billDoer,
					pawn
				});
			}

			if (!BreastSize_Helper.TryGetBreastSize(pawn, out var oldSize))
			{
				throw new ApplicationException("Recipe_ShrinkBreasts could not find any breasts to shrink.");
			}

			var oldBoobs = pawn.health.hediffSet.GetFirstHediffOfDef(BreastSize_Helper.GetHediffDef(oldSize));
			var newSize = oldSize - 1;
			var newBoobs = BreastSize_Helper.GetHediffDef(newSize);

			// I can't figure out how to spawn a stack of 2 meat.
			for (var i = 0; i < 2; i++)
			{
				GenSpawn.Spawn(pawn.RaceProps.meatDef, billDoer.Position, billDoer.Map);
			}

			GenderHelper.ChangeSex(pawn, () =>
			{
				BreastSize_Helper.HurtBreasts(pawn, part, 5);
				if (pawn.health.hediffSet.PartIsMissing(part))
				{
					return;
				}
				pawn.health.RemoveHediff(oldBoobs);
				pawn.health.AddHediff(newBoobs, part);
			});
		}

		public override IEnumerable<BodyPartRecord> GetPartsToApplyOn(Pawn pawn, RecipeDef recipeDef)
		{
			yield break;
			var chest = Genital_Helper.get_breastsBPR(pawn);

			if (Genital_Helper.breasts_blocked(pawn))
			{
				yield break;
			}

			if (BreastSize_Helper.TryGetBreastSize(pawn, out var size)
				//&& size > BreastSize_Helper.GetSize(Genital_Helper.flat_breasts))
				)
			{
				yield return chest;
			}
		}
	}
}


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

namespace rjw
{
	public class Recipe_RemoveAnus : Recipe_RemovePart
	{
		public override IEnumerable<BodyPartRecord> GetPartsToApplyOn(Pawn p, RecipeDef r)
		{
			if (Genital_Helper.has_anus(p))
			{
				bool blocked = Genital_Helper.anus_blocked(p) || xxx.is_slime(p);//|| xxx.is_demon(p)

				foreach (BodyPartRecord part in p.health.hediffSet.GetNotMissingParts())
					if (r.appliedOnFixedBodyParts.Contains(part.def) && (!blocked))
						yield return part;
			}
		}
	}
}


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

namespace rjw
{
	public class Recipe_RemoveBreasts : Recipe_RemovePart
	{
		public override IEnumerable<BodyPartRecord> GetPartsToApplyOn(Pawn p, RecipeDef r)
		{
			if (Genital_Helper.has_breasts(p))
			{
				bool blocked = Genital_Helper.breasts_blocked(p) || xxx.is_slime(p);//|| xxx.is_demon(p)

				foreach (BodyPartRecord part in p.health.hediffSet.GetNotMissingParts())
					if (r.appliedOnFixedBodyParts.Contains(part.def) && (!blocked))
						yield return part;
			}
		}
	}
}


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

namespace rjw
{
	public class Recipe_RemoveGenitals : Recipe_RemovePart
	{
		public override IEnumerable<BodyPartRecord> GetPartsToApplyOn(Pawn p, RecipeDef r)
		{
			if (Genital_Helper.has_genitals(p))
			{
				bool blocked = Genital_Helper.genitals_blocked(p) || xxx.is_slime(p);//|| xxx.is_demon(p)

				foreach (BodyPartRecord part in p.health.hediffSet.GetNotMissingParts())
					if (r.appliedOnFixedBodyParts.Contains(part.def) && (!blocked))
						yield return part;
			}
		}
	}
}


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

namespace rjw
{
	public class Recipe_RemovePart : rjw_CORE_EXPOSED.Recipe_RemoveBodyPart
	{
		// Quick and dirty method to guess whether the player is harvesting the genitals or amputating them
		// due to infection. The core code can't do this properly because it considers the private part
		// hediffs as "unclean".
		public override bool blocked(Pawn p)
		{
			return xxx.is_slime(p);//|| xxx.is_demon(p)
		}

		public bool is_harvest(Pawn p, BodyPartRecord part)
		{
			foreach (Hediff hed in p.health.hediffSet.hediffs)
			{
				if ((hed.Part?.def == part.def) && hed.def.isBad && (hed.Severity >= 0.70f))
					return false;
			}
			return true;
		}

		public override IEnumerable<BodyPartRecord> GetPartsToApplyOn(Pawn p, RecipeDef r)
		{
			foreach (BodyPartRecord part in base.GetPartsToApplyOn(p, r))
			{
				if (r.appliedOnFixedBodyParts.Contains(part.def) && !blocked(p))
					yield return part;
			}
		}

		public override void ApplyOnPawn(Pawn p, BodyPartRecord part, Pawn doer, List<Thing> ingredients, Bill bill)
		{
			var har = is_harvest(p, part);

			base.ApplyOnPawn(p, part, doer, ingredients, bill);

			if (har)
			{
				if (!p.Dead)
				{
					//Log.Message("alive harvest " + part);
					ThoughtUtility.GiveThoughtsForPawnOrganHarvested(p, doer);
				}
				else
				{
					//Log.Message("dead harvest " + part);
					ThoughtUtility.GiveThoughtsForPawnExecuted(p, doer, PawnExecutionKind.OrganHarvesting);
				}
			}
		}

		public override string GetLabelWhenUsedOn(Pawn p, BodyPartRecord part)
		{
			return recipe.label.CapitalizeFirst();
		}
	}
	/*
	public class Recipe_RemovePenis : Recipe_RemovePart
	{
		public override bool blocked(Pawn p)
		{
			return (Genital_Helper.genitals_blocked(p)
				|| !(Genital_Helper.has_penis(p)
					&& Genital_Helper.has_penis_infertile(p)
					&& Genital_Helper.has_ovipositorF(p)));
		}
	}

	public class Recipe_RemoveVagina : Recipe_RemovePart
	{
		public override bool blocked(Pawn p)
		{
			return (Genital_Helper.genitals_blocked(p)
				|| !Genital_Helper.has_vagina(p));
		}
	}

	public class Recipe_RemoveGenitals : Recipe_RemovePart
	{
		public override bool blocked(Pawn p)
		{
			return (Genital_Helper.genitals_blocked(p)
				|| !Genital_Helper.has_genitals(p));
		}
	}

	public class Recipe_RemoveBreasts : Recipe_RemovePart
	{
		public override bool blocked(Pawn p)
		{
			return (Genital_Helper.breasts_blocked(p)
				|| !Genital_Helper.has_breasts(p));
		}
	}

	public class Recipe_RemoveAnus : Recipe_RemovePart
	{
		public override bool blocked(Pawn p)
		{
			return (Genital_Helper.anus_blocked(p)
				|| !Genital_Helper.has_anus(p));
		}
	}
	*/
}


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

namespace rjw
{
	public class Recipe_RemovePenis : Recipe_RemovePart
	{
		public override IEnumerable<BodyPartRecord> GetPartsToApplyOn(Pawn p, RecipeDef r)
		{
			if (Genital_Helper.has_penis(p) || Genital_Helper.has_penis_infertile(p) || Genital_Helper.has_ovipositorF(p))
			{
				bool blocked = Genital_Helper.genitals_blocked(p) || xxx.is_slime(p);

				foreach (var part in p.health.hediffSet.GetNotMissingParts())
					if (r.appliedOnFixedBodyParts.Contains(part.def) && (!blocked))
						yield return part;
			}
		}
	}
}


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

namespace rjw
{
	public class Recipe_RemoveVagina : Recipe_RemovePart
	{
		public override IEnumerable<BodyPartRecord> GetPartsToApplyOn(Pawn p, RecipeDef r)
		{
			if (Genital_Helper.has_vagina(p) )
			{
				bool blocked = Genital_Helper.genitals_blocked(p) || xxx.is_slime(p);

				foreach (var part in p.health.hediffSet.GetNotMissingParts())
					if (r.appliedOnFixedBodyParts.Contains(part.def) && (!blocked))
						yield return part;
			}
		}
	}
}


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

namespace rjw
{
	public class Recipe_MakeFuta : rjw_CORE_EXPOSED.Recipe_AddBodyPart
	{
		public override void ApplyOnPawn(Pawn pawn, BodyPartRecord part, Pawn billDoer, List<Thing> ingredients, Bill bill)
		{
			GenderHelper.Sex before = GenderHelper.GetSex(pawn);

			base.ApplyOnPawn(pawn, part, billDoer, ingredients, bill);

			GenderHelper.Sex after = GenderHelper.GetSex(pawn);

			GenderHelper.ChangeSex(pawn, before, after);
		}
	}

	//Female to futa
	public class Recipe_MakeFutaF : Recipe_MakeFuta
	{
		public override IEnumerable<BodyPartRecord> GetPartsToApplyOn(Pawn p, RecipeDef r)
		{
			var parts = p.GetGenitalsList();

			bool blocked = Genital_Helper.genitals_blocked(p) || xxx.is_slime(p); //|| xxx.is_demon(p);
			bool has_vag = Genital_Helper.has_vagina(p, parts);
			bool has_cock = Genital_Helper.has_penis_fertile(p, parts) || Genital_Helper.has_penis_infertile(p, parts) || Genital_Helper.has_ovipositorF(p, parts);

			foreach (BodyPartRecord part in base.GetPartsToApplyOn(p, r))
				if (r.appliedOnFixedBodyParts.Contains(part.def) && !blocked && (has_vag && !has_cock))
					yield return part;
		}
	}

	//Male to futa
	public class Recipe_MakeFutaM : Recipe_MakeFuta
	{
		public override IEnumerable<BodyPartRecord> GetPartsToApplyOn(Pawn p, RecipeDef r)
		{
			var parts = p.GetGenitalsList();

			bool blocked = Genital_Helper.genitals_blocked(p) || xxx.is_slime(p); //|| xxx.is_demon(p);
			bool has_vag = Genital_Helper.has_vagina(p, parts);
			bool has_cock = Genital_Helper.has_penis_fertile(p, parts) || Genital_Helper.has_penis_infertile(p, parts) || Genital_Helper.has_ovipositorF(p, parts);

			foreach (BodyPartRecord part in base.GetPartsToApplyOn(p, r))
				if (r.appliedOnFixedBodyParts.Contains(part.def) && !blocked && (!has_vag && has_cock))
					yield return part;
		}
	}

	//TODO: maybe merge with above to make single recipe, but meh for now just copy-paste recipes or some filter to disable multiparts if normal part doesn't exist yet

	//add multi parts
	public class Recipe_AddMultiPart : Recipe_MakeFuta
	{
		public override IEnumerable<BodyPartRecord> GetPartsToApplyOn(Pawn p, RecipeDef r)
		{
			//don't add artificial - peg, hydraulics, bionics, archo, ovi
			if (r.addsHediff.addedPartProps?.solid ?? false)
				yield break;

			//don't add if artificial parts present
			if (p.health.hediffSet.hediffs.Any((Hediff hed) =>
				(hed.Part != null) &&
				r.appliedOnFixedBodyParts.Contains(hed.Part.def) &&
				(hed.def.addedPartProps?.solid ?? false)))
				yield break;

			var parts = p.GetGenitalsList();

			//don't add if no ovi
			//if (Genital_Helper.has_ovipositorF(p, parts) || Genital_Helper.has_ovipositorM(p, parts))
			//	yield break;

			//don't add if same part type not present yet
			if (!Genital_Helper.has_vagina(p, parts) && r.defName.ToLower().Contains("vagina"))
				yield break;
			if (!Genital_Helper.has_penis_fertile(p, parts) && r.defName.ToLower().Contains("penis"))
				yield break;

			//cant install parts when part blocked, on slimes, on demons
			bool blocked = (xxx.is_slime(p) //|| xxx.is_demon(p)
				|| (Genital_Helper.genitals_blocked(p) && r.appliedOnFixedBodyParts.Contains(xxx.genitalsDef))
				|| (Genital_Helper.anus_blocked(p) && r.appliedOnFixedBodyParts.Contains(xxx.anusDef))
				|| (Genital_Helper.breasts_blocked(p) && r.appliedOnFixedBodyParts.Contains(xxx.breastsDef)));

			foreach (BodyPartRecord part in base.GetPartsToApplyOn(p, r))
				if (r.appliedOnFixedBodyParts.Contains(part.def) && !blocked)
					yield return part;
		}
	}

	public class Recipe_AddSinglePart : Recipe_MakeFuta
	{
		public override IEnumerable<BodyPartRecord> GetPartsToApplyOn(Pawn p, RecipeDef r)
		{
			//cant install parts when part blocked, on slimes, or when already has one
			bool blocked = xxx.is_slime(p) || p.health.hediffSet.HasHediff(r.addsHediff);

			foreach (BodyPartRecord part in base.GetPartsToApplyOn(p, r))
				if (r.appliedOnFixedBodyParts.Contains(part.def) && !blocked)
					yield return part;
		}
	}

	public class Recipe_RemoveSinglePart : Recipe_MakeFuta
	{
		public override IEnumerable<BodyPartRecord> GetPartsToApplyOn(Pawn p, RecipeDef r)
		{
			//cant install parts when part blocked, on slimes, or when already has one
			bool blocked = xxx.is_slime(p) || !p.health.hediffSet.HasHediff(r.removesHediff);

			foreach (BodyPartRecord part in base.GetPartsToApplyOn(p, r))
				if (r.appliedOnFixedBodyParts.Contains(part.def) && !blocked)
					yield return part;
		}
	}
}


code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using RimWorld;
using Verse;

namespace rjw
{
	public class PawnRelationWorker_Child_Beast : PawnRelationWorker_Child
	{
		public override bool InRelation(Pawn me, Pawn other)
		{
			if (!xxx.is_animal(other) || me == other)
			{
				return false;
			}

			return RelationChecker.isChildOf(other, me);
		}
	}

	public class PawnRelationWorker_Sibling_Beast : PawnRelationWorker_Sibling
	{
		public override bool InRelation(Pawn me, Pawn other)
		{
			if (!xxx.is_animal(other) || me == other)
			{
				return false;
			}

			return RelationChecker.isSiblingOf(other, me);
		}
	}
	public class PawnRelationWorker_HalfSibling_Beast : PawnRelationWorker_HalfSibling
	{
		public override bool InRelation(Pawn me, Pawn other)
		{
			if (!xxx.is_animal(other) || me == other)
			{
				return false;
			}

			return RelationChecker.isHalfSiblingOf(other, me);
		}
	}
	public class PawnRelationWorker_Grandparent_Beast : PawnRelationWorker_Grandparent
	{
		public override bool InRelation(Pawn me, Pawn other)
		{
			if (!xxx.is_animal(other) || me == other)
			{
				return false;
			}

			return RelationChecker.isGrandchildOf(me, other);
		}
	}
	public class PawnRelationWorker_Grandchild_Beast : PawnRelationWorker_Grandchild
	{
		public override bool InRelation(Pawn me, Pawn other)
		{
			if (!xxx.is_animal(other) || me == other)
			{
				return false;
			}

			return RelationChecker.isGrandparentOf(me, other);  //if other isGrandchildOf of me, me is their grandparent
		}
	}
	public class PawnRelationWorker_NephewOrNiece_Beast : PawnRelationWorker_NephewOrNiece
	{
		public override bool InRelation(Pawn me, Pawn other)
		{
			if (!xxx.is_animal(other) || me == other)
			{
				return false;
			}

			return RelationChecker.isUncleOrAuntOf(me, other);
		}
	}
	public class PawnRelationWorker_UncleOrAunt_Beast : PawnRelationWorker_UncleOrAunt
	{
		public override bool InRelation(Pawn me, Pawn other)
		{
			if (!xxx.is_animal(other) || me == other)
			{
				return false;
			}

			return RelationChecker.isNephewOrNieceOf(me, other);
		}
	}

	public class PawnRelationWorker_Cousin_Beast : PawnRelationWorker_Cousin
	{
		public override bool InRelation(Pawn me, Pawn other)
		{
			if (!xxx.is_animal(other) || me == other)
			{
				return false;
			}

			return RelationChecker.isCousinOf(me, other);
		}
	}

	public class PawnRelationWorker_GreatGrandparent_Beast : PawnRelationWorker_GreatGrandparent
	{
		public override bool InRelation(Pawn me, Pawn other)
		{
			if (!xxx.is_animal(other) || me == other)
			{
				return false;
			}

			return RelationChecker.isGreatGrandChildOf(me, other);
		}
	}

	public class PawnRelationWorker_GreatGrandchild_Beast : PawnRelationWorker_GreatGrandchild
	{
		public override bool InRelation(Pawn me, Pawn other)
		{
			if (!xxx.is_animal(other) || me == other)
			{
				return false;
			}

			return RelationChecker.isGreatGrandparentOf(me, other);
		}
	}

	public class PawnRelationWorker_GranduncleOrGrandaunt_Beast : PawnRelationWorker_GranduncleOrGrandaunt
	{
		public override bool InRelation(Pawn me, Pawn other)
		{
			if (!xxx.is_animal(other) || me == other)
			{
				return false;
			}

			return RelationChecker.isGrandnephewOrGrandnieceOf(me, other);
		}
	}

	public class PawnRelationWorker_GrandnephewOrGrandniece_Beast : PawnRelationWorker_GrandnephewOrGrandniece
	{
		public override bool InRelation(Pawn me, Pawn other)
		{
			if (!xxx.is_animal(other) || me == other)
			{
				return false;
			}

			return RelationChecker.isGreatUncleOrAuntOf(me, other);
		}
	}

	public class PawnRelationWorker_CousinOnceRemoved_Beast : PawnRelationWorker_CousinOnceRemoved
	{
		public override bool InRelation(Pawn me, Pawn other)
		{
			if (!xxx.is_animal(other) || me == other)
			{
				return false;
			}

			return RelationChecker.isCousinOnceRemovedOf(me, other);
		}
	}

	public class PawnRelationWorker_SecondCousin_Beast : PawnRelationWorker_SecondCousin
	{
		public override bool InRelation(Pawn me, Pawn other)
		{
			if (!xxx.is_animal(other) || me == other)
			{
				return false;
			}

			return RelationChecker.isSecondCousinOf(me, other);
		}
	}
	/*
	public class PawnRelationWorker_Kin_Beast : PawnRelationWorker_Kin
	{
		public override bool InRelation(Pawn me, Pawn other)
		{
			if (!xxx.is_animal(other) || me == other)
			{
				return false;
			}

			if (base.InRelation(me, other) == true)
			{
				return true;
			}
			return false;
		}
	}
	*/
}


code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using RimWorld;
using Verse;

namespace rjw
{
	public class PawnRelationWorker_Child_Humanlike : PawnRelationWorker_Child
	{
		public override bool InRelation(Pawn me, Pawn other)
		{
			if (xxx.is_animal(other) || me == other)
			{
				return false;
			}

			if (base.InRelation(me, other) == true)
			{
				return true;
			}
			return false;
		}
	}

	public class PawnRelationWorker_Sibling_Humanlike : PawnRelationWorker_Sibling
	{
		public override bool InRelation(Pawn me, Pawn other)
		{
			if (xxx.is_animal(other) || me == other)
			{
				return false;
			}

			if (base.InRelation(me, other) == true)
			{
				return true;
			}
			return false;
		}
	}
	public class PawnRelationWorker_HalfSibling_Humanlike : PawnRelationWorker_HalfSibling
	{
		public override bool InRelation(Pawn me, Pawn other)
		{
			if (xxx.is_animal(other) || me == other)
			{
				return false;
			}

			return RelationChecker.isHalfSiblingOf(other, me);
		}
	}
	public class PawnRelationWorker_Grandparent_Humanlike : PawnRelationWorker_Grandparent
	{
		public override bool InRelation(Pawn me, Pawn other)
		{
			if (xxx.is_animal(other) || me == other)
			{
				return false;
			}

			return RelationChecker.isGrandchildOf(me, other);
		}
	}
	public class PawnRelationWorker_Grandchild_Humanlike : PawnRelationWorker_Grandchild
	{
		public override bool InRelation(Pawn me, Pawn other)
		{
			if (xxx.is_animal(other) || me == other)
			{
				return false;
			}

			return RelationChecker.isGrandparentOf(me, other);
		}
	}
	public class PawnRelationWorker_NephewOrNiece_Humanlike : PawnRelationWorker_NephewOrNiece
	{
		public override bool InRelation(Pawn me, Pawn other)
		{
			if (xxx.is_animal(other) || me == other)
			{
				return false;
			}

			return RelationChecker.isUncleOrAuntOf(me, other);
		}
	}
	public class PawnRelationWorker_UncleOrAunt_Humanlike : PawnRelationWorker_UncleOrAunt
	{
		public override bool InRelation(Pawn me, Pawn other)
		{
			if (xxx.is_animal(other) || me == other)
			{
				return false;
			}

			return RelationChecker.isNephewOrNieceOf(me, other);
		}
	}

	public class PawnRelationWorker_Cousin_Humanlike : PawnRelationWorker_Cousin
	{
		public override bool InRelation(Pawn me, Pawn other)
		{
			if (xxx.is_animal(other) || me == other)
			{
				return false;
			}

			return RelationChecker.isCousinOf(me, other);
		}
	}

	public class PawnRelationWorker_GreatGrandparent_Humanlike : PawnRelationWorker_GreatGrandparent
	{
		public override bool InRelation(Pawn me, Pawn other)
		{
			if (xxx.is_animal(other) || me == other)
			{
				return false;
			}

			return RelationChecker.isGreatGrandChildOf(me, other);
		}
	}

	public class PawnRelationWorker_GreatGrandchild_Humanlike : PawnRelationWorker_GreatGrandchild
	{
		public override bool InRelation(Pawn me, Pawn other)
		{
			if (xxx.is_animal(other) || me == other)
			{
				return false;
			}

			return RelationChecker.isGreatGrandparentOf(me, other);
		}
	}

	public class PawnRelationWorker_GranduncleOrGrandaunt_Humanlike : PawnRelationWorker_GranduncleOrGrandaunt
	{
		public override bool InRelation(Pawn me, Pawn other)
		{
			if (xxx.is_animal(other) || me == other)
			{
				return false;
			}

			return RelationChecker.isGrandnephewOrGrandnieceOf(me, other);
		}
	}

	public class PawnRelationWorker_GrandnephewOrGrandniece_Humanlike : PawnRelationWorker_GrandnephewOrGrandniece
	{
		public override bool InRelation(Pawn me, Pawn other)
		{
			if (xxx.is_animal(other) || me == other)
			{
				return false;
			}

			return RelationChecker.isGreatUncleOrAuntOf(me, other);
		}
	}

	public class PawnRelationWorker_CousinOnceRemoved_Humanlike : PawnRelationWorker_CousinOnceRemoved
	{
		public override bool InRelation(Pawn me, Pawn other)
		{
			if (xxx.is_animal(other) || me == other)
			{
				return false;
			}

			return RelationChecker.isCousinOnceRemovedOf(me, other);
		}
	}

	public class PawnRelationWorker_SecondCousin_Humanlike : PawnRelationWorker_SecondCousin
	{
		public override bool InRelation(Pawn me, Pawn other)
		{
			if (xxx.is_animal(other) || me == other)
			{
				return false;
			}

			return RelationChecker.isSecondCousinOf(me, other);
		}
	}

	public class PawnRelationWorker_Kin_Humanlike : PawnRelationWorker_Kin
	{
		public override bool InRelation(Pawn me, Pawn other)
		{
			if (xxx.is_animal(other) || me == other)
			{
				return false;
			}

			if (base.InRelation(me, other) == true)
			{
				return true;
			}
			return false;
		}
	}
}