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

namespace rjw
{
	///<summary>
	///This hediff class simulates pregnancy with animal children, mother may be human. It is not intended to be reasonable.
	///Differences from humanlike pregnancy are that animals are given some training and that less punishing relations are used for parent-child.
	///</summary>
	class Hediff_BestialPregnancy : Hediff_BasePregnancy
	{
		private static readonly PawnRelationDef relation_birthgiver = DefDatabase<PawnRelationDef>.AllDefs.FirstOrDefault(d => d.defName== "RJW_Sire" );
		private static readonly PawnRelationDef relation_spawn = DefDatabase<PawnRelationDef>.AllDefs.FirstOrDefault(d => d.defName == "RJW_Pup");
		//static int max_train_level = TrainableUtility.TrainableDefsInListOrder.Sum(tr => tr.steps);

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

		//Makes half-human babies start off better. They start obedient, and if mother is a human, they get hediff to boost their training
		protected void train(Pawn baby, Pawn mother, Pawn father)
		{
			bool _;
			if (!xxx.is_human(baby) && baby.Faction == Faction.OfPlayer)
			{
				if (xxx.is_human(mother) && baby.Faction == Faction.OfPlayer && baby.training.CanAssignToTrain(TrainableDefOf.Obedience, out _).Accepted)
				{
					baby.training.Train(TrainableDefOf.Obedience, mother);
				}
				if (xxx.is_human(mother) && baby.Faction == Faction.OfPlayer && baby.training.CanAssignToTrain(TrainableDefOf.Tameness, out _).Accepted)
				{
					baby.training.Train(TrainableDefOf.Tameness, mother);
				}
			}
			//baby.RaceProps.TrainableIntelligence.LabelCap.
			//if (xxx.is_human(mother))
			//{
			//	Let the animals be born as colony property
			//	if (mother.IsPrisonerOfColony || mother.IsColonist)
			//	{
			//		baby.SetFaction(Faction.OfPlayer);
			//	}
			//	let it be trained half to the max
			//	var baby_int = baby.RaceProps.TrainableIntelligence;
			//	int max_int = TrainableUtility.TrainableDefsInListOrder.FindLastIndex(tr => (tr.requiredTrainableIntelligence == baby_int));
			//	if (max_int == -1)
			//		return;
			//	Log.Message("RJW training " + baby + " max_int is " + max_int);
			//	var available_tricks = TrainableUtility.TrainableDefsInListOrder.GetRange(0, max_int + 1);
			//	int max_steps = available_tricks.Sum(tr => tr.steps);
			//	Log.Message("RJW training " + baby + " vill do " + max_steps/2 + " steps");
			//	int t_score = Rand.Range(Mathf.RoundToInt(max_steps / 4), Mathf.RoundToInt(max_steps / 2));
			//	for (int i = 1; i <= t_score; i++)
			//	{
			//		var tr = available_tricks.Where(t => !baby.training.IsCompleted(t)). RandomElement();
			//		Log.Message("RJW training " + baby + " for " + tr);
			//		baby.training.Train(tr, mother);
			//	}

			//	baby.health.AddHediff(HediffDef.Named("RJW_smartPup"));
			//}
		}

		//Handles the spawning of pawns and adding relations
		public override void GiveBirth()
		{
			Pawn mother = pawn;
			if (mother == null)
				return;
			try
			{
				//fail if hediff added through debug, since babies not initialized
				if (babies.Count > 9999)
					Log.Message("RJW beastiality/animal pregnancy birthing pawn count: " + babies.Count);
			}
			catch
			{
				Initialize(mother, father);
			}
			List<Pawn> siblings = new List<Pawn>();
			foreach (Pawn baby in babies)
			{
				PawnUtility.TrySpawnHatchedOrBornPawn(baby, mother);

				Need_Sex sex_need = mother.needs.TryGetNeed<Need_Sex>();
				if (mother.Faction != null && !(mother.Faction?.IsPlayer ?? false) && sex_need != null)
				{
					sex_need.CurLevel = 1.0f;
				}

				baby.relations.AddDirectRelation(relation_birthgiver, mother);
				mother.relations.AddDirectRelation(relation_spawn, baby);
				if (father != null)
				{
					baby.relations.AddDirectRelation(relation_birthgiver, father);
					father.relations.AddDirectRelation(relation_spawn, baby);
				}

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

				PostBirth(mother, father, baby);

				mother.health.RemoveHediff(this);
			}
		}

		///This method should be the only one to create the hediff
		//Hediffs are made HediffMaker.MakeHediff which returns hediff of class different than the one needed, so we do the cast and then do all the same operations as in parent class
		//I don't know whether it'd be possible to use standard constructor instead of this retardation
		public static void Create(Pawn mother, Pawn father)
		{
			if (mother == null)
				return;

			BodyPartRecord torso = mother.RaceProps.body.AllParts.Find(x => x.def.defName == "Torso");
			//Log.Message("RJW beastial "+ mother + " " + father);

			Hediff_BestialPregnancy hediff = (Hediff_BestialPregnancy)HediffMaker.MakeHediff(HediffDef.Named("RJW_pregnancy_beast"), mother, torso);
			hediff.Initialize(mother, father);
		}
	}
}


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

namespace rjw
{
	class Hediff_HumanlikePregnancy : Hediff_BasePregnancy
	///<summary>
	///This hediff class simulates pregnancy resulting in humanlike childs.
	///</summary>
	{
		//Handles the spawning of pawns and adding relations
		public override void GiveBirth()
		{
			Pawn mother = pawn;
			if (mother == null)
				return;
			try
			{
				//fail if hediff added through debug, since babies not initialized
				if (babies.Count > 9999)
					Log.Message("RJW humanlike pregnancy birthing pawn count: " + babies.Count);
			}
			catch
			{
				Initialize(mother, father);
			}
			List<Pawn> siblings = new List<Pawn>();
			foreach (Pawn baby in babies)
			{
				PawnUtility.TrySpawnHatchedOrBornPawn(baby, mother);

				var sex_need = mother.needs.TryGetNeed<Need_Sex>();
				if (mother.Faction != null && !(mother.Faction?.IsPlayer ?? false) && sex_need != null)
				{
					sex_need.CurLevel = 1.0f;
				}
				if (mother.Faction != null)
				{
					baby.SetFaction(mother.Faction);
				}
				if (mother.IsPrisonerOfColony)
				{
					baby.guest.CapturedBy(Faction.OfPlayer);
				}

				baby.relations.AddDirectRelation(PawnRelationDefOf.Parent, mother);
				if (father != null)
				{
					baby.relations.AddDirectRelation(PawnRelationDefOf.Parent, father);
				}

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

				PostBirth(mother, father, baby);

				mother.health.RemoveHediff(this);
			}
		}

		///This method should be the only one to create the hediff
		public static void Create(Pawn mother, Pawn father)
		{
			if (mother == null)
				return;

			var torso = mother.RaceProps.body.AllParts.Find(x => x.def.defName == "Torso");
			//Log.Message("[RJW]Humanlike pregnancy " + mother + " is bred by " + father);

			var hediff = (Hediff_HumanlikePregnancy)HediffMaker.MakeHediff(HediffDef.Named("RJW_pregnancy"), mother, torso);
			hediff.Initialize(mother, father);
		}
	}
}

repo_name: randomtyping/rjw

license: mit


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

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

		protected int bornTick
		{
			get
			{
				return xxx.has_quirk(pawn, "Incubator") ? ((HediffDef_InsectEgg)this.def).bornTick / 2 : ((HediffDef_InsectEgg)this.def).bornTick;
			}
		}

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

		public float GestationProgress
		{
			get => Severity;
			private set => Severity = value;
		}

		public Pawn father;					//can be parentkind defined in egg
		public Pawn queen;					//can be any pawn
		public bool fertilized => father != null;
		protected List<Pawn> babies;

		///Contractions duration, effectively additional hediff stage, a dirty hack to make birthing process notable
		//protected const int TicksPerHour = 2500;
		protected int contractions = 0;

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

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

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

		public override void Tick()
		{
			this.ageTicks++;
			if (this.pawn.IsHashIntervalTick(1000))
			{
				if (this.ageTicks >= bornTick)
				{
					if (PawnUtility.ShouldSendNotificationAbout(this.pawn))
					{
						string key1 = "RJW_GaveBirthEggTitle";
						string message_title = TranslatorFormattedStringExtensions.Translate(key1, pawn.LabelIndefinite());
						string key2 = "RJW_GaveBirthEggText";
						string message_text = TranslatorFormattedStringExtensions.Translate(key2, pawn.LabelIndefinite());
						//Find.LetterStack.ReceiveLetter(message_title, message_text, LetterDefOf.NeutralEvent, pawn, null);
						Messages.Message(message_text, pawn, MessageTypeDefOf.SituationResolved);
					}
					GiveBirth();
					//someday add dmg to vag?
					//var dam = Rand.RangeInclusive(0, 1);
					//p.TakeDamage(new DamageInfo(DamageDefOf.Burn, dam, 999, -1.0f, null, rec, null));
				}
				else
				{
					//birthing takes an hour
					if (this.ageTicks >= bornTick - 2500 && contractions == 0)
					{
						if (PawnUtility.ShouldSendNotificationAbout(this.pawn))
						{
							string key = "RJW_EggContractions";
							string text = TranslatorFormattedStringExtensions.Translate(key, pawn.LabelIndefinite());
							Messages.Message(text, pawn, MessageTypeDefOf.NeutralEvent);
						}
						contractions++;
						pawn.health.AddHediff(HediffDef.Named("Hediff_Submitting"));
					}
				}
			}
		}

		public override void ExposeData()
		{
			base.ExposeData();
			Scribe_References.Look<Pawn>(ref this.father, "father", false);
			Scribe_References.Look<Pawn>(ref this.queen, "queen", false);
			Scribe_Collections.Look(ref babies, saveDestroyedThings: true, label: "babies", lookMode: LookMode.Deep, ctorArgs: new object[0]);
		}
		public override void Notify_PawnDied()
		{
			base.Notify_PawnDied();
			GiveBirth();
		}

		protected virtual void GenerateBabies()
		{

		}

		//should someday remake into birth eggs and then within few ticks hatch them
		[SyncMethod]
		public void GiveBirth()
		{
			Pawn mother = pawn;
			Pawn baby = null;
			//Rand.PopState();
			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
			if (fertilized)
			{
				//Log.Message("[RJW]Hediff_InsectEgg::BirthBaby() - Egg of " + parentDef + " in " + mother.ToString() + " birth!");
				PawnKindDef spawn_kind_def = father.kindDef;
				//egg mostlikely insect or implanter spawned factionless through debug, set to insect
				Faction spawn_faction = Faction.OfInsects;

				//Log.Message("[RJW]Hediff_InsectEgg::BirthBaby() - insect " + (queen.Faction == Faction.OfInsects || father.Faction == Faction.OfInsects || mother.Faction == Faction.OfInsects));
				//Log.Message("[RJW]Hediff_InsectEgg::BirthBaby() - human " + (xxx.is_human(queen) && xxx.is_human(father)));
				//Log.Message("[RJW]Hediff_InsectEgg::BirthBaby() - animal1 " + (!xxx.is_human(queen) && !(queen.Faction?.IsPlayer ?? false)));
				//Log.Message("[RJW]Hediff_InsectEgg::BirthBaby() - animal2 ");

				//this is probably fucked up, idk how to filter insects from non insects/spiders etc
				//Hive Insects... probably
				if (queen.Faction == Faction.OfInsects || father.Faction == Faction.OfInsects || mother.Faction == Faction.OfInsects)
				{
					//Log.Message("[RJW]Hediff_InsectEgg::BirthBaby() - insect ");
					spawn_faction = Faction.OfInsects;
					int chance = 5;

					//random chance to make insect neutral/tamable
					if (father.Faction == Faction.OfInsects)
						chance = 5;
					if (father.Faction != Faction.OfInsects)
						chance = 10;
					if (father.Faction == Faction.OfPlayer)
						chance = 25;
					if (queen.Faction == Faction.OfPlayer)
						chance += 25;
					if (queen.Faction == Faction.OfPlayer && xxx.is_human(queen))
						chance += (int)(25 * queen.GetStatValue(StatDefOf.PsychicSensitivity));
					if (Rand.Range(0, 100) <= chance)
						spawn_faction = null;

					//chance tame insect on birth
					if (spawn_faction == null)
						if (queen.Faction == Faction.OfPlayer && xxx.is_human(queen))
							if (Rand.Range(0, 100) <= (int)(50 * queen.GetStatValue(StatDefOf.PsychicSensitivity)))
								spawn_faction = Faction.OfPlayer;
				}
				//humanlikes
				else if (xxx.is_human(queen) && xxx.is_human(father))
				{
					spawn_faction = queen.Faction;
				}
				//TODO: humnlike + animal, merge with insect stuff?
				//else if (xxx.is_human(queen) && !xxx.is_human(father))
				//{
				//	spawn_faction = queen.Faction;
				//}
				//animal, spawn implanter faction (if not player faction/not tamed)
				else if (!xxx.is_human(queen) && !(queen.Faction?.IsPlayer ?? false))
				{
					spawn_faction = queen.Faction;
				}
				//spawn factionless(tamable, probably)
				else
				{
					spawn_faction = null;
				}

				//Log.Message("[RJW]Hediff_InsectEgg::BirthBaby() " + spawn_kind_def + " of " + spawn_faction + " in " + (int)(50 * queen.GetStatValue(StatDefOf.PsychicSensitivity)) + " chance!");
				PawnGenerationRequest request = new PawnGenerationRequest(spawn_kind_def, spawn_faction, PawnGenerationContext.NonPlayer, -1, false, true, false, false, false, false, 0, false, true, false, false, false, false, false, null, null, 0, 0);

				baby = PawnGenerator.GeneratePawn(request);
				if (PawnUtility.TrySpawnHatchedOrBornPawn(baby, mother))
				{
					Genital_Helper.sexualize_pawn(baby);
					if (spawn_faction == Faction.OfInsects)
					{
						//add ai to pawn?
						//LordManager.lords
						LordJob_DefendAndExpandHive lordJob = new LordJob_DefendAndExpandHive(true);
						Lord lord = LordMaker.MakeNewLord(baby.Faction, lordJob, baby.Map);
						lord.AddPawn(baby);
						lord.SetJob(lordJob);
						//Log.Message("[RJW]Hediff_InsectEgg::BirthBaby() " + baby.GetLord().DebugString());

					}
				}
				else
				{
					Find.WorldPawns.PassToWorld(baby, PawnDiscardDecideMode.Discard);
				}

				// Move the baby in front of the mother, rather than on top
				if (mother.CurrentBed() != null)
				{
					baby.Position = baby.Position + new IntVec3(0, 0, 1).RotatedBy(mother.CurrentBed().Rotation);
				}

				/*
				if (Visible && baby != null)
				{
					string key = "MessageGaveBirth";
					string text = TranslatorFormattedStringExtensions.Translate(key, mother.LabelIndefinite()).CapitalizeFirst();
					Messages.Message(text, baby, MessageTypeDefOf.NeutralEvent);
				}
				*/

				mother.records.AddTo(xxx.CountOfBirthInsect, 1);

				if (mother.records.GetAsInt(xxx.CountOfBirthInsect) > 100)
				{
					if (!xxx.has_quirk(mother, "Incubator"))
					{
						CompRJW.Comp(mother).quirks.AppendWithComma("Incubator");
					}
					if (!xxx.has_quirk(mother, "Impregnation fetish"))
					{
						CompRJW.Comp(mother).quirks.AppendWithComma("Impregnation fetish");
					}
				}
			}
			else
			{
				string key = "EggDead";
				string text = TranslatorFormattedStringExtensions.Translate(key, pawn.LabelIndefinite()).CapitalizeFirst();
				Messages.Message(text, pawn, MessageTypeDefOf.SituationResolved);
			}
			// Post birth
			if (mother.Spawned)
			{
				// Spawn guck
				if (mother.caller != null)
				{
					mother.caller.DoCall();
				}
				if (baby != null)
				{
					if (baby.caller != null)
					{
						baby.caller.DoCall();
					}
				}
				FilthMaker.MakeFilth(mother.Position, mother.Map, ThingDefOf.Filth_AmnioticFluid, mother.LabelIndefinite(), 5);
				int howmuch = xxx.has_quirk(mother, "Incubator") ? Rand.Range(1, 3) * 2 : Rand.Range(1, 3);
				//DebugThingPlaceHelper.DebugSpawn(ThingDef.Named("InsectJelly"), mother.InteractionCell, howmuch, false);
				int i = 0;
				while (i++ < howmuch)
					GenSpawn.Spawn(ThingDefOf.InsectJelly, mother.InteractionCell, mother.Map);
			}

			mother.health.RemoveHediff(this);
		}

		public void Fertilize(Pawn pawn)
		{
			if (!fertilized && ageTicks < abortTick)
			{
				father = pawn;
			}
		}

		public void Implanter(Pawn pawn)
		{
			if (queen == null)
			{
				queen = pawn;
				if (((HediffDef_InsectEgg)this.def).selffertilized)
					Fertilize(pawn);
			}
		}

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

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

		public override string DebugString()
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append(base.DebugString());
			stringBuilder.AppendLine(" Gestation progress: " + ((float)ageTicks / bornTick).ToStringPercent());
			stringBuilder.AppendLine(" Implanter: " + xxx.get_pawnname(queen));
			stringBuilder.AppendLine(" Fertilized:" + (fertilized).ToString());
			return stringBuilder.ToString();
		}
	}
}


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

namespace rjw
{
	internal class Hediff_Orgasm : HediffWithComps
	{
		public override void PostAdd(DamageInfo? dinfo)
		{
			string key = "FeltOrgasm";
			string text = TranslatorFormattedStringExtensions.Translate(key, pawn.LabelIndefinite()).CapitalizeFirst();
			Messages.Message(text, pawn, MessageTypeDefOf.NeutralEvent);
		}
	}

	internal class Hediff_TransportCums : HediffWithComps
	{
		public override void PostAdd(DamageInfo? dinfo)
		{
			if (pawn.gender == Gender.Female)
			{
				string key = "CumsTransported";
				string text = TranslatorFormattedStringExtensions.Translate(key, pawn.LabelIndefinite()).CapitalizeFirst();
				Messages.Message(text, pawn, MessageTypeDefOf.NeutralEvent);
				PawnGenerationRequest req = new PawnGenerationRequest(PawnKindDefOf.Drifter, fixedGender:Gender.Male );
				Pawn cumSender = PawnGenerator.GeneratePawn(req);
				Find.WorldPawns.PassToWorld(cumSender);
				//Pawn cumSender = (from p in Find.WorldPawns.AllPawnsAlive where p.gender == Gender.Male select p).RandomElement<Pawn>();
				//--Log.Message("[RJW]" + this.GetType().ToString() + "PostAdd() - Sending " + xxx.get_pawnname(cumSender) + "'s cum into " + xxx.get_pawnname(pawn) + "'s vagina");
				PregnancyHelper.impregnate(pawn, cumSender, xxx.rjwSextype.Vaginal);
			}
			pawn.health.RemoveHediff(this);
		}
	}

	internal class Hediff_TransportEggs : HediffWithComps
	{
		public override void PostAdd(DamageInfo? dinfo)
		{
			if (pawn.gender == Gender.Female)
			{
				string key = "EggsTransported";
				string text = TranslatorFormattedStringExtensions.Translate(key, pawn.LabelIndefinite()).CapitalizeFirst();
				Messages.Message(text, pawn, MessageTypeDefOf.NeutralEvent);

				PawnKindDef spawn = PawnKindDefOf.Megascarab;
				PawnGenerationRequest req1 = new PawnGenerationRequest(spawn, fixedGender:Gender.Female );
				PawnGenerationRequest req2 = new PawnGenerationRequest(spawn, fixedGender:Gender.Male );
				Pawn implanter = PawnGenerator.GeneratePawn(req1);
				Pawn fertilizer = PawnGenerator.GeneratePawn(req2);
				Find.WorldPawns.PassToWorld(implanter);
				Find.WorldPawns.PassToWorld(fertilizer);
				Genital_Helper.sexualize_pawn(implanter);
				Genital_Helper.sexualize_pawn(fertilizer);
				//Pawn cumSender = (from p in Find.WorldPawns.AllPawnsAlive where p.gender == Gender.Male select p).RandomElement<Pawn>();
				//--Log.Message("[RJW]" + this.GetType().ToString() + "PostAdd() - Sending " + xxx.get_pawnname(cumSender) + "'s cum into " + xxx.get_pawnname(pawn) + "'s vagina");
				PregnancyHelper.impregnate(pawn, implanter, xxx.rjwSextype.Vaginal);
				PregnancyHelper.impregnate(pawn, fertilizer, xxx.rjwSextype.Vaginal);
			}
			pawn.health.RemoveHediff(this);
		}
	}
}


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

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


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>
	class Hediff_MechanoidPregnancy : Hediff_BasePregnancy
	{
		public override void PregnancyMessage()
		{
			string message_title = "RJW_PregnantTitle".Translate(pawn.LabelIndefinite());
			string message_text1 = "RJW_PregnantText".Translate(pawn.LabelIndefinite());
			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;
			try
			{
				//fail if hediff added through debug, since babies not initialized
				if (babies.Count > 9999)
					Log.Message("RJW mech pregnancy birthing pawn count: " + babies.Count);
			}
			catch
			{
				Initialize(mother, father);
			}
			foreach (Pawn baby in babies)
			{
				Faction spawn_faction = null;
				if (!is_hacked)
					spawn_faction = Faction.OfMechanoids;

				Pawn baby1 = PawnGenerator.GeneratePawn(new PawnGenerationRequest(PawnKindDef.Named("Mech_Scyther"), spawn_faction));
				PawnUtility.TrySpawnHatchedOrBornPawn(baby1, mother);
				if (!is_hacked)
				{
					LordJob_MechanoidsDefendShip lordJob = new LordJob_MechanoidsDefendShip(mother, baby1.Faction, 50f, mother.Position);
					Lord lord = LordMaker.MakeNewLord(baby1.Faction, lordJob, baby1.Map);
					lord.AddPawn(baby1);
				}
				FilthMaker.MakeFilth(baby1.PositionHeld, baby1.MapHeld, mother.RaceProps.BloodDef, mother.LabelIndefinite());
			}

			IEnumerable<BodyPartRecord> source = from x in mother.health.hediffSet.GetNotMissingParts()
												 where x.IsInGroup(BodyPartGroupDefOf.Torso)
												 && !x.IsCorePart
												 //someday include depth filter
												 //so it doesnt 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)
				{
					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);

					//idk blood doesnt drop
					//mother.health.DropBloodFilth();
					//FilthMaker.MakeFilth(corpse.PositionHeld, corpse.MapHeld, mother.RaceProps.BloodDef, mother.LabelIndefinite());
				}
			}
			mother.health.RemoveHediff(this);
		}

		///This method should be the only one to create the hediff
		//Hediffs are made HediffMaker.MakeHediff which returns hediff of class different than the one needed, so we do the cast and then do all the same operations as in parent class
		//I don't know whether it'd be possible to use standard constructor instead of this retardation
		public static void Create(Pawn mother, Pawn father)
		{
			if (mother == null)
				return;

			BodyPartRecord torso = mother.RaceProps.body.AllParts.Find(x => x.def.defName == "Torso");
			//Log.Message("RJW beastial "+ mother + " " + father);

			Hediff_MechanoidPregnancy hediff = (Hediff_MechanoidPregnancy)HediffMaker.MakeHediff(HediffDef.Named("RJW_pregnancy_mech"), mother, torso);
			hediff.Initialize(mother, father);
		}
	}
}


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(father.kindDef, father.Faction, PawnGenerationContext.NonPlayer, -1, false, true, false, false, true, false, 1f, false, true, true, false, false, false,false, null, null, null, null);
			Pawn pawn = null;
			for (int i = 0; i < num; i++)
			{
				pawn = PawnGenerator.GeneratePawn(request);
				if (PawnUtility.TrySpawnHatchedOrBornPawn(pawn, mother))
				{
					if (pawn.playerSettings != null && mother.playerSettings != null)
					{
						pawn.playerSettings.AreaRestriction = father.playerSettings.AreaRestriction;
					}
					if (pawn.RaceProps.IsFlesh)
					{
						pawn.relations.AddDirectRelation(PawnRelationDefOf.Parent, mother);
						if (father != null)
						{
							pawn.relations.AddDirectRelation(PawnRelationDefOf.Parent, father);
						}
					}
				}
				else
				{
					Find.WorldPawns.PassToWorld(pawn, PawnDiscardDecideMode.Discard);
				}
			}
			if (mother.Spawned)
			{
				FilthMaker.MakeFilth(mother.Position, mother.Map, ThingDefOf.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;

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

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

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

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

			base.ExposeData();
		}

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

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

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

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

				// Gain traits from life experiences
				if (pawn.story.traits.allTraits.Count < 3)
				{
					List<Trait> life_traitpool = new List<Trait>();
					// Try get cannibalism
					if (pawn.needs.mood.thoughts.memories.Memories.Find(x => x.def == ThoughtDefOf.AteHumanlikeMeatAsIngredient) != null)
					{
						life_traitpool.Add(new Trait(TraitDefOf.Cannibal, 0, false));
					}
					// Try to get bloodlust
					if (pawn.records.GetValue(RecordDefOf.KillsHumanlikes) > 0 || pawn.records.GetValue(RecordDefOf.AnimalsSlaughtered) >= 2)
					{
						life_traitpool.Add(new Trait(TraitDefOf.Bloodlust, 0, false));
					}
					// Try to get shooting accuracy
					if (pawn.records.GetValue(RecordDefOf.ShotsFired) > 100 && (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--;
						}
					}
				}

				pawn.health.RemoveHediff(this);
			}
		}

		public void TickRare()
		{
			//--Log.Message("[RJW]Hediff_SimpleBaby::TickRare is called");
			if (pawn.ageTracker.CurLifeStageIndex > Grown_To)
			{
				GrowUpTo(Grown_To + 1, false);
			}
			// Update the graphics set
			if (pawn.ageTracker.CurLifeStageIndex == AgeStage.Toddler)
				pawn.Drawer.renderer.graphics.ResolveAllGraphics();
			if (xxx.RimWorldChildrenIsActive)
			{
				//if (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 PostTick()
		{
			/*
			if (xxx.RimWorldChildrenIsActive)
			{
				if (pawn.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("RJW_BabyState"))!=null)
				{
					pawn.health.RemoveHediff(this);
				}
				return;
			}
			*/
			//This void call every frame. should not logmes no reason
			//--Log.Message("[RJW]Hediff_SimpleBaby::PostTick is called");
			base.PostTick();
			if (pawn.Spawned)
			{
				if (pawn.IsHashIntervalTick(120))
				{
					TickRare();
				}
			}
		}

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


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

///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
		private static readonly HashSet<string> Pregnancy_filter = new HashSet<string>(DefDatabase<StringListDef>.GetNamed("Pregnancy_filter").strings);
		public static readonly HashSet<String> EggRace_filter = new HashSet<string>(DefDatabase<StringListDef>.GetNamed("EggRace_filter").strings);

		//pawn - "father"; partner = mother
		public static void impregnate(Pawn pawn, Pawn partner, xxx.rjwSextype sextype = xxx.rjwSextype.None)
		{

			if (RJWSettings.DevMode) Log.Message("Rimjobworld::impregnate(" + sextype + "):: " + xxx.get_pawnname(pawn) + " + " + xxx.get_pawnname(partner) + ":");

			//"mech" pregnancy
			if (sextype == xxx.rjwSextype.MechImplant)
			{
				if (RJWPregnancySettings.mechanoid_enabled)
				{
					if (RJWSettings.DevMode) Log.Message(" mechanoid pregnancy");

					//new pregnancy
						Hediff_MechanoidPregnancy.Create(partner, pawn);
						Hediff_MechanoidPregnancy hediff = (Hediff_MechanoidPregnancy)partner.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("RJW_pregnancy_mech"));
						if (RJWSettings.DevMode) Log.Message("[RJW] removing other pregnancies");
						if (partner.health.hediffSet.HasHediff(HediffDef.Named("RJW_pregnancy")))
						partner.health.RemoveHediff(partner.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("RJW_pregnancy")));
						if (partner.health.hediffSet.HasHediff(HediffDef.Named("RJW_pregnancy_beast")))
						partner.health.RemoveHediff(partner.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("RJW_pregnancy_beast")));
						if (partner.health.hediffSet.HasHediff(HediffDef.Named("Pregnant")))
						partner.health.RemoveHediff(partner.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("Pregnant")));
						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;
			}

			// Sextype can result in pregnancy.
			if (!(sextype == xxx.rjwSextype.Vaginal || sextype == xxx.rjwSextype.DoublePenetration))
				return;

			//"insect" pregnancy
			//straight, female (partner) recives egg insertion from other/sex starter (pawn)
			if (Genital_Helper.has_vagina(partner) &&
					(Genital_Helper.has_ovipositorF(pawn) ||
						(Genital_Helper.has_ovipositorM(pawn) ||
							(Genital_Helper.has_penis(pawn) && EggRace_filter.Contains(pawn.kindDef.race.defName))))
				)
			{
				DoEgg(pawn, partner);
				return;
			}
			//reverse, female (pawn) starts sex/passive bestiality and fills herself with eggs - this is likely fucked up and needs fixing at jobdriver, processsex and aftersex levels
			else
			if (Genital_Helper.has_vagina(pawn) &&
					(Genital_Helper.has_ovipositorF(partner) ||
						(Genital_Helper.has_ovipositorM(partner) ||
							(Genital_Helper.has_penis(partner) && EggRace_filter.Contains(partner.kindDef.race.defName))))
				)
			{
				DoEgg(partner, pawn);
				return;
			}

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

			//futa-futa docking?
			//if (CanImpregnate(partner, pawn, sextype) && CanImpregnate(pawn, partner, sextype))
			//{
			//Log.Message("[RJW] futa-futa docking...");
			//return;
			//Doimpregnate(pawn, partner);
			//Doimpregnate(partner, pawn);
			//}
			//normal, when female is passive/recives interaction
			if (Genital_Helper.has_penis(pawn) && Genital_Helper.has_vagina(partner) && CanImpregnate(pawn, partner, sextype))
			{
				if (RJWSettings.DevMode) Log.Message(" impregnate forward");
				Doimpregnate(pawn, partner);
			}
			//reverse, when female active/starts interaction
			else if (Genital_Helper.has_vagina(pawn) && Genital_Helper.has_penis(partner) && CanImpregnate(partner, pawn, sextype))
			{
				if (RJWSettings.DevMode) Log.Message(" impregnate reverse");
				Doimpregnate(partner, pawn);
			}
		}

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

		[SyncMethod]
		public static void DoEgg(Pawn pawn, Pawn partner)
		{
			if (RJWPregnancySettings.insect_pregnancy_enabled)
			{
				if (RJWSettings.DevMode) Log.Message(" insect pregnancy");

				//female "insect" plant eggs
				if (Genital_Helper.has_ovipositorF(pawn))
				{
					float maxeggssize = partner.BodySize * (xxx.has_quirk(partner, "Incubator") ? 2f : 1f) * (Genital_Helper.has_ovipositorF(partner) ? 2f : 0.5f);
					float eggedsize = 0;
					foreach (Hediff_InsectEgg egg in partner.health.hediffSet.GetHediffs<Hediff_InsectEgg>())
					{
						eggedsize += egg.queen.RaceProps.baseBodySize / 5 * ((HediffDef_InsectEgg)egg.def).eggsize;
					}
					if (RJWSettings.DevMode) Log.Message(" determine " + xxx.get_pawnname(partner) + " size of eggs inside: " + eggedsize + " (max) " + maxeggssize);
					if (eggedsize < maxeggssize)
					{
						string defname = ((xxx.is_human(pawn) || !xxx.is_insect(pawn)) && !EggRace_filter.Contains(pawn.kindDef.race.defName)) ? "Megascarab" : pawn.def.defName;
						float eggssize = ((xxx.is_human(pawn) || !xxx.is_insect(pawn)) && !EggRace_filter.Contains(pawn.kindDef.race.defName)) ? 0.04f : pawn.RaceProps.baseBodySize / 5;
						//TODO move "Megascarab" to InsectEgg and rework heddif, so it defaults to Megascarab if not insect
						//once there is some sort of genetic inheritance form parts/ovis
						//Rand.PopState();
						//Rand.PushState(RJW_Multiplayer.PredictableSeed());
						HediffDef_InsectEgg egg = (from x in DefDatabase<HediffDef_InsectEgg>.AllDefs where x.IsParent(defname) select x).RandomElement();
						if (egg != null)
						{
							int count;
							float maxeggs = 100;
							float mineggs = 0;
							if (egg.eggsize != 0)
							{
								maxeggs = ((maxeggssize - eggedsize) / eggssize);
								if (RJWSettings.DevMode) Log.Message(" max eggs: " + maxeggs);
							}
							if (maxeggs >= 1)
								mineggs = 1;

							count = Rand.Range((int)mineggs, (int)(maxeggs));

							if (RJWSettings.DevMode) Log.Message(" planting eggs: " + egg.ToString() + " (" + count + ")");

							PlantSomething(egg, partner, false, count);

							//set implanter/queen
							foreach (var egg1 in (from x in partner.health.hediffSet.GetHediffs<Hediff_InsectEgg>() where x.IsParent(defname) select x))
							{
								if (egg1.queen == null)
								{
									if (RJWSettings.DevMode) Log.Message(" set implanter/queen:" + xxx.get_pawnname(pawn));
									egg1.Implanter(pawn);
								}
							}

							if (!pawn.IsColonist)
								if (!partner.health.hediffSet.HasHediff(HediffDef.Named("RJW_Cocoon")) && partner.Faction != pawn.Faction)
									partner.health.AddHediff(HediffDef.Named("RJW_Cocoon"));
						}
					}
				}
				//male "insect" fertilize eggs
				else if (!pawn.health.hediffSet.HasHediff(xxx.sterilized))
				{
					foreach (var egg in (from x in partner.health.hediffSet.GetHediffs<Hediff_InsectEgg>() where x.IsParent(pawn.def.defName) select x))
					{
						if (!egg.fertilized)
						{
							if (RJWSettings.DevMode) Log.Message(xxx.get_pawnname(pawn) + " fertilize eggs:" + egg.ToString());
							egg.Fertilize(pawn);
						}
					}
				}
				return;
			}
		}

		[SyncMethod]
		public static void Doimpregnate(Pawn pawn, Pawn partner)
		{
			if (RJWSettings.DevMode) Log.Message("[RJW] Doimpregnate " + xxx.get_pawnname(pawn) + " is a father, " + xxx.get_pawnname(partner) + " is a mother");
			// fertility check
			float fertility = RJWPregnancySettings.humanlike_impregnation_chance / 100f;
			if (xxx.is_animal(partner))
				fertility = RJWPregnancySettings.animal_impregnation_chance / 100f;

			// Interspecies modifier
			if (pawn.def.defName != partner.def.defName)
			{
				if (RJWPregnancySettings.complex_interspecies)
					fertility *= SexUtility.BodySimilarity(pawn, partner);
				else
					fertility *= RJWPregnancySettings.interspecies_impregnation_modifier;
			}

			//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_threshold = fertility * ReproductionFactor;
			float non_pregnancy_chance = Rand.Value;
			BodyPartRecord torso = partner.RaceProps.body.AllParts.Find(x => x.def == BodyPartDefOf.Torso);

			if (non_pregnancy_chance > pregnancy_threshold || pregnancy_threshold == 0)
			{
				if (RJWSettings.DevMode) Log.Message("[RJW] Impregnation failed. Chance: " + pregnancy_threshold.ToStringPercent() + " fail roll: " + non_pregnancy_chance.ToStringPercent());
				return;
			}
			if (RJWSettings.DevMode) Log.Message("[RJW] Impregnation succeeded. Chance: " + pregnancy_threshold.ToStringPercent() + " succeess roll: " + non_pregnancy_chance.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) Log.Message("Rimjobworld::CanImpregnate(" + sextype + "):: " + xxx.get_pawnname(fucker) + " + " + xxx.get_pawnname(fucked) + ":");

			if (sextype == xxx.rjwSextype.MechImplant && !RJWPregnancySettings.mechanoid_enabled)
			{
				if (RJWSettings.DevMode) Log.Message(" mechanoid 'pregnancy' disabled");
				return false;
			}

			if (!(sextype == xxx.rjwSextype.Vaginal || sextype == xxx.rjwSextype.DoublePenetration))
			{
				if (RJWSettings.DevMode) Log.Message(" sextype cannot result in pregnancy");
				return false;
			}

			// TODO: Add fertility nullifiers for undead.
			if (PregnancyHelper.Pregnancy_filter.Contains(fucker.kindDef.defName) ||
				(xxx.RoMIsActive && (fucked.health.hediffSet.HasHediff(HediffDef.Named("TM_UndeadHD")) || fucked.health.hediffSet.HasHediff(HediffDef.Named("TM_UndeadAnimalHD")))))
			{
				if (RJWSettings.DevMode) Log.Message(xxx.get_pawnname(fucked) + " filtered race that cant be pregnant");
				return false;
			}

			if (PregnancyHelper.Pregnancy_filter.Contains(fucked.kindDef.defName) ||
				(xxx.RoMIsActive && (fucker.health.hediffSet.HasHediff(HediffDef.Named("TM_UndeadHD")) || fucker.health.hediffSet.HasHediff(HediffDef.Named("TM_UndeadAnimalHD")))))
			{
				if (RJWSettings.DevMode) Log.Message(xxx.get_pawnname(fucker) + " filtered race that cant impregnate");
				return false;
			}

			if (fucked.health.hediffSet.HasHediff(HediffDef.Named("RJW_pregnancy")) ||
				fucked.health.hediffSet.HasHediff(HediffDef.Named("RJW_pregnancy_beast")) ||
				fucked.health.hediffSet.HasHediff(HediffDef.Named("Pregnant")))
			{
				if (RJWSettings.DevMode) Log.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) Log.Message(xxx.get_pawnname(fucked) + " cant get pregnant while eggs inside");
				return false;
			}

			if (!(Genital_Helper.has_penis(fucker) && Genital_Helper.has_vagina(fucked)) && !(Genital_Helper.has_penis(fucked) && Genital_Helper.has_vagina(fucker)))
			{
				if (RJWSettings.DevMode) Log.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) Log.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) Log.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) Log.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) Log.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) Log.Message(" interspecies pregnancy disabled.");
				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))
			{
				Hediff_HumanlikePregnancy.Create(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_animal(father)) || (xxx.is_animal(mother) && xxx.is_human(father))))
			{
				Hediff_BestialPregnancy.Create(mother, father);
			}
			//animal-animal
			else if (xxx.is_animal(mother) && xxx.is_animal(father))
			{
				CompEggLayer compEggLayer = mother.TryGetComp<CompEggLayer>();
				// fertilize eggs of same species
				if (compEggLayer != null)
				{
					if (mother.kindDef == father.kindDef)
						compEggLayer.Fertilize(father);
				}
				else if (RJWPregnancySettings.animal_pregnancy_enabled)
				{
					Hediff_BestialPregnancy.Create(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_anus(target) : Genital_Helper.get_genitals(target);
			if (Part != null || Part.parts.Count != 0)
			{
				for (int i = 0; i < amount; i++)
				{
					if (RJWSettings.DevMode) Log.Message("[RJW] planting something weird");
					target.health.AddHediff(def, Part);
				}

				//killoff normal preg
				if (!isToAnal)
				{
					if (RJWSettings.DevMode) Log.Message("[RJW] removing other pregnancies");
					if (target.health.hediffSet.HasHediff(HediffDef.Named("RJW_pregnancy")))
						target.health.RemoveHediff(target.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("RJW_pregnancy")));
					if (target.health.hediffSet.HasHediff(HediffDef.Named("RJW_pregnancy_beast")))
						target.health.RemoveHediff(target.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("RJW_pregnancy_beast")));
					if (target.health.hediffSet.HasHediff(HediffDef.Named("Pregnant")))
						target.health.RemoveHediff(target.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("Pregnant")));
				}

				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 (Prefs.DevMode) Log.Message("[RJW] cleanup_CnP after love check");

			var h = pawn.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("HumanPregnancy"));
			if (h != null && h.ageTicks < 10) {
				pawn.health.RemoveHediff(h);
				if (Prefs.DevMode) Log.Message("[RJW] removed hediff from " + xxx.get_pawnname(pawn));
			}
		}

		///
		/// Remove Vanilla Pregnancy
		///
		public static void cleanup_vanilla(Pawn pawn)
		{
			if (Prefs.DevMode) Log.Message("[RJW] cleanup_vanilla after love check");

			var h = pawn.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("Pregnant"));
			if (h != null && h.ageTicks < 10) {
				pawn.health.RemoveHediff(h);
				if (Prefs.DevMode) Log.Message("[RJW] 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 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)
			{
				if (pawn.health.hediffSet.HasHediff(HediffDef.Named("RJW_pregnancy"), true) && recipe.removesHediff == HediffDef.Named("RJW_pregnancy"))
				{
					Hediff_HumanlikePregnancy pregnancy = (Hediff_HumanlikePregnancy)pawn.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("RJW_pregnancy"));
					if (pregnancy.is_checked)
						yield return part;
				}

				else if (pawn.health.hediffSet.HasHediff(HediffDef.Named("RJW_pregnancy_beast"), true) && recipe.removesHediff == HediffDef.Named("RJW_pregnancy_beast"))
				{
					Hediff_BestialPregnancy pregnancy = (Hediff_BestialPregnancy)pawn.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("RJW_pregnancy_beast"));
					if (pregnancy.is_checked)
						yield return part;
				}

				else if (pawn.health.hediffSet.HasHediff(HediffDef.Named("RJW_pregnancy_mech"), true) && recipe.removesHediff == HediffDef.Named("RJW_pregnancy_mech"))
				{
					Hediff_MechanoidPregnancy pregnancy = (Hediff_MechanoidPregnancy)pawn.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("RJW_pregnancy_mech"));
					if (pregnancy.is_checked)
						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)
			{
				Log.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 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.health.hediffSet.HasHediff(HediffDef.Named("RJW_pregnancy"), true)
				|| pawn.health.hediffSet.HasHediff(HediffDef.Named("RJW_pregnancy_beast"), true)
				|| pawn.health.hediffSet.HasHediff(HediffDef.Named("RJW_pregnancy_mech"), true)
				)
			{
				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")))
            {
                Hediff_HumanlikePregnancy pregnancy = (Hediff_HumanlikePregnancy)pawn.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("RJW_pregnancy"));
				pregnancy.CheckPregnancy();
            }

            else if (pawn.health.hediffSet.HasHediff(HediffDef.Named("RJW_pregnancy_beast")))
            {
                Hediff_BestialPregnancy pregnancy = (Hediff_BestialPregnancy)pawn.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("RJW_pregnancy_beast"));
				pregnancy.CheckPregnancy();
            }

            else 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.CheckPregnancy();
            }

            else
            {
                Messages.Message(xxx.get_pawnname(billDoer) + " has determined " + xxx.get_pawnname(pawn) + " is not pregnant.", MessageTypeDefOf.NeutralEvent);
            }
        }
    }
}


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)
					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 RimWorld;
using Verse;

namespace rjw
{
	public class ThoughtWorker_ItchyCrotch : ThoughtWorker
	{
		protected override ThoughtState CurrentStateInternal(Pawn p)
		{
			int sev = std.genital_rash_severity(p);
			if (sev <= 0)
				return ThoughtState.Inactive;
			else if (sev == 1)
				return ThoughtState.ActiveAtStage(0);
			else
				return ThoughtState.ActiveAtStage(1);
		}
	}
}


code:
using RimWorld;
using Verse;

namespace rjw
{
	public class ThoughtWorker_SyphiliticThoughts : ThoughtWorker
	{
		protected override ThoughtState CurrentStateInternal(Pawn p)
		{
			//Log.Message("0");
			var syp = p.health.hediffSet.GetFirstHediffOfDef(std.syphilis.hediff_def);
			//Log.Message("1");
			if (syp != null)
			{
				//Log.Message("2");
				if (syp.Severity >= 0.80f)
				{
					//Log.Message("3");
					return ThoughtState.ActiveAtStage(1);
				}
				else if (syp.Severity >= 0.50f)
				{
					//Log.Message("4");
					return ThoughtState.ActiveAtStage(0);
				}
				//Log.Message("5");
			}
			//Log.Message("6");
			return ThoughtState.Inactive;
		}
	}
}


code:
using RimWorld;
using Verse;

namespace rjw
{
	public class ThoughtWorker_WastingAway : ThoughtWorker
	{
		protected override ThoughtState CurrentStateInternal(Pawn p)
		{
			if (!std.is_wasting_away(p))
				return ThoughtState.Inactive;
			else
				return ThoughtState.ActiveAtStage(0);
		}
	}
}


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

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

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

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

		public static List<std_def> all => DefDatabase<std_def>.AllDefsListForReading;

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

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

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

			return tr;
		}

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

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

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

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

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

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

			roll_for_syphilis_damage(p);
		}

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

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

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

			if (id == null) return;

			id.Severity = 1.0f - bf;

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

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

		[SyncMethod]
		public static void roll_to_catch(Pawn catcher, Pawn pitcher)
		{
			if (!RJWSettings.stds_enabled) return;
			//Rand.PopState();
			//Rand.PushState(RJW_Multiplayer.PredictableSeed());

			// Archotech genitalia automagically purge any known STDs.
			if (catcher.health.hediffSet.HasHediff(Genital_Helper.archotech_vagina) || pitcher.health.hediffSet.HasHediff(Genital_Helper.archotech_vagina) ||
				catcher.health.hediffSet.HasHediff(Genital_Helper.archotech_penis) || pitcher.health.hediffSet.HasHediff(Genital_Helper.archotech_penis)) return;

			bool has_artificial_genitals = catcher.health.hediffSet.HasDirectlyAddedPartFor(xxx.genitals);
			float thru_genitals = !has_artificial_genitals ? 1.00f : 0.15f;
			float on_genitals = !has_artificial_genitals ? 1.00f : 0.00f;

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

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

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

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

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

		[SyncMethod]
		public static void generate_on(Pawn p)
		{
			if (p == null) return;
			//prevent error on world gen for pawns with broken bodies(no genitals)
			if (p.RaceProps.body.HasPartWithTag(BodyPartTagDefOf.RJW_FertilitySource))
				return;
			if (!xxx.is_human(p))
				return;
			float nymph_mul = !xxx.is_nympho(p) ? 1.0f : xxx.config.nymph_spawn_with_std_mul;
			//Rand.PopState();
			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
			foreach (std_def sd in all)
				if (Rand.Value < sd.spawn_chance * nymph_mul)
				{
					Hediff hed = infect(p, sd, false);
					float sev;
					{
						float r = Rand.Range(sd.hediff_def.minSeverity, sd.hediff_def.maxSeverity);
						sev = Mathf.Clamp(sd.spawn_severity * r, sd.hediff_def.minSeverity, sd.hediff_def.maxSeverity);
					}
					hed.Severity = sev;
				}
		}

		[SyncMethod]
		public static void roll_for_syphilis_damage(Pawn p)
		{
			Hediff syp = p.health.hediffSet.GetFirstHediffOfDef(syphilis.hediff_def);
			if (syp == null || !(syp.Severity >= 0.60f) || syp.FullyImmune()) return;

			// A 30% chance per day of getting any permanent damage works out to ~891 in 1 million for each roll
			// The equation is (1-x)^(60000/150)=.7
			// Important Note:
			// this function is called from Need_Sex::NeedInterval(), where it involves a needsex_tick and a std_tick to actually trigger this roll_for_syphilis_damage.
			// j(this is not exactly the same as the value in Need_Sex, that value is 0, but here j should be 1) std_ticks per this function called, k needsex_ticks per std_tick, 150 ticks per needsex_tick, and x is the chance per 150 ticks,
			// The new equation should be .7 = (1-x)^(400/kj)
			// 1-x = .7^(kj/400), x =1-.7^(kj/400)
			// Since k=10,j=1, so kj=10, new x is 1-.7^(10/400)=0.0088772362, let it be 888/100000
			//Rand.PopState();
			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
			if (Rand.RangeInclusive(1, 100000) <= 888)
			{
				BodyPartRecord part;
				float sev;

				float rv = Rand.Value;
				if (rv < 0.10f)
				{
					part = BodyDefOf.Human.AllParts.Find(bpr => string.Equals(bpr.def.defName, "Brain"));
					sev = 1.0f;
				}
				else if (rv < 0.50f)
				{
					part = BodyDefOf.Human.AllParts.Find(bpr => string.Equals(bpr.def.defName, "Liver"));
					sev = Rand.RangeInclusive(1, 3);
				}
				else if (rv < 0.75f)
				{
					//LeftKidney, probably
					part = BodyDefOf.Human.AllParts.Find(bpr => string.Equals(bpr.def.defName, "Kidney"));
					sev = Rand.RangeInclusive(1, 2);
				}
				else
				{
					//RightKidney, probably
					part = BodyDefOf.Human.AllParts.FindLast(bpr => string.Equals(bpr.def.defName, "Kidney"));
					sev = Rand.RangeInclusive(1, 2);
				}

				if (part != null && !p.health.hediffSet.PartIsMissing(part) && !p.health.hediffSet.HasDirectlyAddedPartFor(part))
				{
					DamageDef vir_dam = DefDatabase<DamageDef>.GetNamed("ViralDamage");
					HediffDef dam_def = HealthUtility.GetHediffDefFromDamage(vir_dam, p, part);
					Hediff_Injury inj = (Hediff_Injury)HediffMaker.MakeHediff(dam_def, p, null);
					inj.Severity = sev;
					inj.TryGetComp<HediffComp_GetsPermanent>().IsPermanent = true;
					p.health.AddHediff(inj, part, null);
					string message_title = syphilis.label + " Damage";
					string baby_pronoun = p.gender == Gender.Male ? "his" : "her";
					string message_text = "RJW_Syphilis_Damage_Message".Translate(xxx.get_pawnname(p), baby_pronoun, part.def.label, syphilis.label);
					Find.LetterStack.ReceiveLetter(message_title, message_text, LetterDefOf.ThreatSmall, p);
				}
			}
		}
	}
}


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

namespace rjw
{
	[StaticConstructorOnStartup]
	public static class BukkakeContent
	{
		//UI:
		public static readonly Texture2D SemenIcon_little = ContentFinder<Texture2D>.Get("Bukkake/SemenIcon_little", true);
		public static readonly Texture2D SemenIcon_some = ContentFinder<Texture2D>.Get("Bukkake/SemenIcon_some", true);
		public static readonly Texture2D SemenIcon_dripping = ContentFinder<Texture2D>.Get("Bukkake/SemenIcon_dripping", true);
		public static readonly Texture2D SemenIcon_drenched = ContentFinder<Texture2D>.Get("Bukkake/SemenIcon_drenched", true);

		//on pawn:
		public static readonly Material semenSplatch1 = MaterialPool.MatFrom("Bukkake/splatch_1", ShaderDatabase.Cutout);
		public static readonly Material semenSplatch2 = MaterialPool.MatFrom("Bukkake/splatch_2", ShaderDatabase.Cutout);
		public static readonly Material semenSplatch3 = MaterialPool.MatFrom("Bukkake/splatch_3", ShaderDatabase.Cutout);
		public static readonly Material semenSplatch4 = MaterialPool.MatFrom("Bukkake/splatch_4", ShaderDatabase.Cutout);
		public static readonly Material semenSplatch5 = MaterialPool.MatFrom("Bukkake/splatch_5", ShaderDatabase.Cutout);
		public static readonly Material semenSplatch6 = MaterialPool.MatFrom("Bukkake/splatch_6", ShaderDatabase.Cutout);
		public static readonly Material semenSplatch7 = MaterialPool.MatFrom("Bukkake/splatch_7", ShaderDatabase.Cutout);
		public static readonly Material semenSplatch8 = MaterialPool.MatFrom("Bukkake/splatch_8", ShaderDatabase.Cutout);
		public static readonly Material semenSplatch9 = MaterialPool.MatFrom("Bukkake/splatch_9", ShaderDatabase.Cutout);

		[SyncMethod]
		public static Material pickRandomSplatch()
		{
			//Rand.PopState();
			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
			int rand = Rand.Range(0, 8);
			switch (rand)
			{
				case 0:
					return semenSplatch1;
				case 1:
					return semenSplatch2;
				case 2:
					return semenSplatch3;
				case 3:
					return semenSplatch4;
				case 4:
					return semenSplatch5;
				case 5:
					return semenSplatch6;
				case 6:
					return semenSplatch7;
				case 7:
					return semenSplatch8;
				case 8:
					return semenSplatch9;
			}
			return null;
		}
	}
}


code:
using RimWorld;
using Verse;

namespace rjw
{
	//I took the liberty of adding the new DefOf files since I find that easier than managing all the defs via xxx.cs
	[DefOf]
	public static class RJW_SemenoOverlayHediffDefOf
	{
		public static HediffDef Hediff_Semen;//for humans & animals; also parent for insect and mech spunk
		public static HediffDef Hediff_InsectSpunk;
		public static HediffDef Hediff_MechaFluids;

		public static HediffDef Hediff_Bukkake;//Master + manager

	}

}


code:
using RimWorld;
using Verse;

namespace rjw
{
	[DefOf]
	static class RJW_SemenOverlayJobDefOf
	{
		public static JobDef CleanSelf;
	}
}


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

namespace rjw
{
	class Hediff_Bukkake : HediffWithComps
	{
		/*
		Whenever semen is applied, this hediff is also added to the pawn.
		Since there is always only a single hediff of this type on the pawn, it serves as master controller, adding up the individual Semen hediffs, applying debuffs and drawing overlays
		*/

		private static readonly float semenWeight = 0.2f;//how much individual semen_hediffs contribute to the overall bukkake severity
		List<Hediff> hediffs_semen;
		Dictionary<string, SemenSplatch> splatches;

		public override void ExposeData()
		{
			base.ExposeData();
			//Scribe_Values.Look<Dictionary<string, SemenSplatch>>(ref splatches, "splatches", new Dictionary<string, SemenSplatch>()); - causes errors when loading. for now, just make a new dictionary
			splatches = new Dictionary<string, SemenSplatch>();//instead of loading, just recreate anew
			hediffs_semen = new List<Hediff>();
		}

		public override void PostMake()
		{
			base.PostMake();
			splatches = new Dictionary<string, SemenSplatch>();
		}

		public override void PostTick()
		{
			if (pawn.RaceProps.Humanlike)//for now, only humans are supported
			{
				hediffs_semen = this.pawn.health.hediffSet.hediffs.FindAll(x => (x.def == RJW_SemenoOverlayHediffDefOf.Hediff_Semen || x.def == RJW_SemenoOverlayHediffDefOf.Hediff_InsectSpunk || x.def == RJW_SemenoOverlayHediffDefOf.Hediff_MechaFluids));
				float bukkakeLevel = CalculateBukkakeLevel();//sum of severity of all the semen hediffs x semenWeight
				this.Severity = bukkakeLevel;
				bool updatePortrait = false;

				//loop through all semen hediffs, add missing ones to dictionary
				for (int i = 0; i < hediffs_semen.Count(); i++)
				{
					Hediff_Semen h = (Hediff_Semen)hediffs_semen[i];
					string ID = h.GetUniqueLoadID();//unique ID for each hediff
					if (!splatches.ContainsKey(ID))//if it isn't here yet, make new object
					{
						updatePortrait = true;
						bool leftSide = h.Part.Label.Contains("left") ? true : false;//depending on whether the body part is left or right, drawing-offset on x-aixs may be inverted

						splatches[ID] = new SemenSplatch(h, pawn.story.bodyType, h.Part.def, leftSide, h.semenType);
					}

				}

				//remove splatch objects once their respective semen hediff is gone
				List<string> removeKeys = new List<string>();
				foreach (string key in splatches.Keys)
				{
					SemenSplatch s = splatches[key];

					if (!hediffs_semen.Contains(s.hediff_Semen))
					{
						removeKeys.Add(key);
						updatePortrait = true;
					}
				}
				//loop over and remove elements that should be destroyed:
				foreach (string key in removeKeys)
				{
					SemenSplatch s = splatches[key];
					splatches.Remove(key);
				}

				if (updatePortrait)//right now, portraits are only updated when a completely new semen hediff is added or an old one removed - maybe that should be done more frequently
				{
					PortraitsCache.SetDirty(pawn);
				}
			}
		}

		//called from the PawnWoundDrawer (see HarmonyPatches)
		public void DrawSemen(Vector3 drawLoc, Quaternion quat, bool forPortrait, float angle, bool inBed = false)
		{
			Rot4 bodyFacing = pawn.Rotation;
			int facingDir = bodyFacing.AsInt;//0: north, 1:east, 2:south, 3:west
			foreach (string key in splatches.Keys)
			{
				SemenSplatch s = splatches[key];
				s.Draw(drawLoc, quat, forPortrait, facingDir, angle, inBed);
			}
		}

		//new Hediff_Bukkake added to pawn -> just combine the two
		public override bool TryMergeWith(Hediff other)
		{
			if (other == null || other.def != this.def)
			{
				return false;
			}
			return true;
		}

		private float CalculateBukkakeLevel()
		{
			float num = 0f;
			for (int i = 0; i < hediffs_semen.Count; i++)
			{
				num += hediffs_semen[i].Severity * semenWeight;
			}
			return num;
		}

		//class for handling drawing of the individual splatches
		private class SemenSplatch
		{
			public readonly Hediff_Semen hediff_Semen;
			public readonly Material semenMaterial;
			public readonly BodyPartDef bodyPart;
			private bool mirrorMesh;

			private const float maxSize = 0.20f;//1.0 = 1 tile
			private const float minSize = 0.05f;

			//data taken from SemenHelper.cs:
			private readonly float[] xAdjust;
			private readonly float[] zAdjust;
			private readonly float[] yAdjust;

			public SemenSplatch(Hediff_Semen hediff, BodyTypeDef bodyType, BodyPartDef bodyPart, bool leftSide = false, int semenType = SemenHelper.CUM_NORMAL)
			{
				hediff_Semen = hediff;
				semenMaterial = new Material(BukkakeContent.pickRandomSplatch());//needs to create new material in order to allow for different colors
				semenMaterial.SetTextureScale("_MainTex", new Vector2(-1, 1));
				this.bodyPart = bodyPart;
				//Rand.PopState();
				//Rand.PushState(RJW_Multiplayer.PredictableSeed());

				//set color:
				switch (semenType)
				{
					case SemenHelper.CUM_NORMAL:
						semenMaterial.color = SemenHelper.color_normal;
						break;
					case SemenHelper.CUM_INSECT:
						semenMaterial.color = SemenHelper.color_insect;
						break;
					case SemenHelper.CUM_MECHA:
						semenMaterial.color = SemenHelper.color_mecha;
						break;
				}

				mirrorMesh = (Rand.Value > 0.5f);//in 50% of the cases, flip mesh horizontally for more variance

				//x,y,z adjustments to draw splatches over the approximately correct locations; values stored in semen helper - accessed by unique combinations of bodyTypes and bodyParts
				SemenHelper.key k = new SemenHelper.key(bodyType, bodyPart);
				if (SemenHelper.splatchAdjust.Keys.Contains(k))
				{
					SemenHelper.values helperValues = SemenHelper.splatchAdjust[k];

					//invert, x-adjust (horizontal) depending on left/right body side:
					if (!leftSide)
					{
						float[] xAdjTemp = new float[4];
						for (int i = 0; i < xAdjTemp.Length; i++)
						{
							xAdjTemp[i] = helperValues.x[i] * -1f;
						}
						xAdjust = xAdjTemp;
					}
					else
					{
						xAdjust = helperValues.x;
					}

					zAdjust = helperValues.z;//vertical adjustment

				}
				else//fallback in the the key can't be found
				{
					if (Prefs.DevMode)
					{
						Log.Message("created semen splatch for undefined body type or part. BodyType: " + bodyType + " , BodyPart: " + bodyPart);
					}
					xAdjust = new float[] { 0f, 0f, 0f, 0f };
					zAdjust = new float[] { 0f, 0f, 0f, 0f };
				}

				//y adjustments: plane/layer of drawing, > 0 -> above certain objects, < 0 -> below
				SemenHelper.key_layer k2 = new SemenHelper.key_layer(leftSide, bodyPart);

				if (SemenHelper.layerAdjust.Keys.Contains(k2))
				{
					SemenHelper.values_layer helperValues_layer = SemenHelper.layerAdjust[k2];
					yAdjust = helperValues_layer.y;
				}
				else
				{
					yAdjust = new float[] { 0.02f, 0.02f, 0.02f, 0.02f };//over body in every direction
				}

			}

			public void Draw(Vector3 drawPos, Quaternion quat, bool forPortrait, int facingDir = 0, float angle = 0,bool inBed=false)
			{
				if (inBed)
				{
					if (this.bodyPart != BodyPartDefOf.Jaw && this.bodyPart != BodyPartDefOf.Head)//when pawn is in bed (=bed with sheets), only draw semen on head
					{
						return;
					}
				}

				float size = minSize+((maxSize-minSize)*hediff_Semen.Severity);
				Mesh mesh = MeshMakerPlanes.NewPlaneMesh(size);

				if (mirrorMesh)//horizontal flip
				{
					mesh = flipMesh(mesh);
				}

				//rotation:
				if (angle == 0)//normal situation (pawn standing upright)
				{
					drawPos.x += xAdjust[facingDir];
					drawPos.z += zAdjust[facingDir];
				}
				else//if downed etc, more complex calculation becomes necessary
				{
					float radian = angle / 180 * (float)Math.PI;
					radian = -radian;
					drawPos.x += Mathf.Cos(radian) * xAdjust[hediff_Semen.pawn.Rotation.AsInt] - Mathf.Sin(radian) * zAdjust[facingDir];//facingDir doesn't appear to be chosen correctly in all cases
					drawPos.z += Mathf.Cos(radian) * zAdjust[hediff_Semen.pawn.Rotation.AsInt] + Mathf.Sin(radian) * xAdjust[facingDir];
				}

				//drawPos.y += yAdjust[facingDir];// 0.00: over body; 0.01: over body but under face, 0.02: over face, but under hair, -99 = "never" visible
				drawPos.y += yAdjust[facingDir];

				// this causes crash, pawn tick -> draw-> full ram-> crash
				GenDraw.DrawMeshNowOrLater(mesh, drawPos, quat, semenMaterial, forPortrait);
			}

			//flips mesh UV horizontally, thereby mirroring the texture
			private Mesh flipMesh(Mesh meshToFlip)
			{
				var uvs = meshToFlip.uv;
				if (uvs.Length != 4)
				{
					return (meshToFlip);
				}
				for (var i = 0; i < uvs.Length; i++)
				{
					if (Mathf.Approximately(uvs[i].x, 1.0f))
						uvs[i].x = 0.0f;
					else
						uvs[i].x = 1.0f;
				}
				meshToFlip.uv = uvs;
				return (meshToFlip);
			}
		}
	}
}


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

namespace rjw
{
	public class Hediff_Semen : HediffWithComps
	{
		public int semenType = SemenHelper.CUM_NORMAL;//-> different colors

		public string giverName = null;//not utilized right now, maybe in the future save origin of the semen

		public override string LabelInBrackets
		{
			get
			{
				StringBuilder stringBuilder = new StringBuilder();
				stringBuilder.Append(base.LabelInBrackets);
				if (this.sourceHediffDef != null)
				{
					if (stringBuilder.Length != 0)
					{
						stringBuilder.Append(", ");
					}
					stringBuilder.Append(this.sourceHediffDef.label);
				}
				else if (this.source != null)
				{
					if (stringBuilder.Length != 0)
					{
						stringBuilder.Append(", ");
					}
					stringBuilder.Append(this.source.label);
					if (this.sourceBodyPartGroup != null)
					{
						stringBuilder.Append(" ");
						stringBuilder.Append(this.sourceBodyPartGroup.LabelShort);
					}
				}
				return stringBuilder.ToString();
			}
		}

		public override string SeverityLabel
		{
			get
			{
				if (this.Severity == 0f)
				{
					return null;
				}
				return this.Severity.ToString("F1");
			}
		}

		[SyncMethod]
		public override bool TryMergeWith(Hediff other)
		{
			//if a new Semen hediff is added to the same body part, they are combined. if severity reaches more than 1, spillover to other body parts occurs

			Hediff_Semen hediff_Semen = other as Hediff_Semen;
			if (hediff_Semen != null && hediff_Semen.def == this.def && hediff_Semen.Part == base.Part && this.def.injuryProps.canMerge)
			{
				semenType = hediff_Semen.semenType;//take over new creature color

				float totalAmount = hediff_Semen.Severity + this.Severity;
				if (totalAmount > 1.0f)
				{
					BodyPartDef spillOverTo = SemenHelper.spillover(this.Part.def);//SemenHelper saves valid other body parts for spillover
					if (spillOverTo != null)
					{
						//Rand.PopState();
						//Rand.PushState(RJW_Multiplayer.PredictableSeed());
						IEnumerable<BodyPartRecord> availableParts = SemenHelper.getAvailableBodyParts(pawn);//gets all non missing, valid body parts
						IEnumerable<BodyPartRecord> filteredParts = availableParts.Where(x => x.def == spillOverTo);//filters again for valid spill target
						BodyPartRecord spillPart = filteredParts.RandomElement<BodyPartRecord>();//then pick one
						if (spillPart != null)
						{
							SemenHelper.cumOn(pawn, spillPart, totalAmount - this.Severity, null, semenType);
						}
					}
				}

				return (base.TryMergeWith(other));

			}
			return (false);
		}

		public override void ExposeData()
		{
			base.ExposeData();
			Scribe_Values.Look<int>(ref semenType, "semenType", SemenHelper.CUM_NORMAL);

			if (Scribe.mode == LoadSaveMode.PostLoadInit && base.Part == null)
			{
				//Log.Error("Hediff_Semen has null part after loading.", false);
				this.pawn.health.hediffSet.hediffs.Remove(this);
				return;
			}
		}

		//handles the icon in the health tab and its color
		public override TextureAndColor StateIcon
		{
			get
			{
				TextureAndColor tex = TextureAndColor.None;
				Color color = Color.white;
				switch (semenType)
				{
					case SemenHelper.CUM_NORMAL:
						color = SemenHelper.color_normal;
						break;
					case SemenHelper.CUM_INSECT:
						color = SemenHelper.color_insect;
						break;
					case SemenHelper.CUM_MECHA:
						color = SemenHelper.color_mecha;
						break;
				}

				Texture2D tex2d = BukkakeContent.SemenIcon_little;
				switch (this.CurStageIndex)
				{
					case 0:
						tex2d = BukkakeContent.SemenIcon_little;
						break;
					case 1:
						tex2d = BukkakeContent.SemenIcon_some;
						break;
					case 2:
						tex2d = BukkakeContent.SemenIcon_dripping;
						break;
					case 3:
						tex2d = BukkakeContent.SemenIcon_drenched;
						break;
				}

				tex = new TextureAndColor(tex2d, color);

				return tex;
			}
		}

	}
}


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

namespace rjw
{
	class JobDriver_CleanSelf : JobDriver
	{
		float cleanAmount = 1f;//severity of a single SemenHediff removed per cleaning-round; 1f = remove entirely
		int cleaningTime = 120;//ticks - 120 = 2 real seconds, 3 in-game minutes

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

		protected override IEnumerable<Toil> MakeNewToils()
		{
			this.FailOn(delegate
			{
				List<Hediff> hediffs = pawn.health.hediffSet.hediffs;
				return !hediffs.Exists(x => x.def == RJW_SemenoOverlayHediffDefOf.Hediff_Bukkake);//fail if bukkake disappears - means that also all the semen is gone
			});
			Toil cleaning = Toils_General.Wait(cleaningTime, TargetIndex.None);//duration of
			cleaning.WithProgressBarToilDelay(TargetIndex.A);

			yield return cleaning;
			yield return new Toil()
			{
				initAction = delegate ()
				{
					//get one of the semen hediffs, reduce its severity
					Hediff hediff = pawn.health.hediffSet.hediffs.Find(x => (x.def == RJW_SemenoOverlayHediffDefOf.Hediff_Semen || x.def == RJW_SemenoOverlayHediffDefOf.Hediff_InsectSpunk || x.def == RJW_SemenoOverlayHediffDefOf.Hediff_MechaFluids));
					if (hediff != null)
					{
						hediff.Severity -= cleanAmount;
					}
				}
			};
			yield break;

		}
	}
}