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

namespace rjw
{
	class Hediff_BasePregnancy : HediffWithComps
	{
		///<summary>
		///This hediff class simulates pregnancy.
		///</summary>
		/*It is different from vanilla in that if father disappears, this one still remembers him.
		Other weird variants can be derived
		As usuall, significant parts of this are completely stolen from Children and Pregnancy*/

		//Static fields
		private const int MiscarryCheckInterval = 1000;

		private const float MTBMiscarryStarvingDays = 0.5f;

		private const float MTBMiscarryWoundedDays = 0.5f;

		protected const int TicksPerDay = 60000;
		protected const int TicksPerHour = 2500;

		protected const string starvationMessage = "MessageMiscarriedStarvation";
		protected const string poorHealthMessage = "MessageMiscarriedPoorHealth";

		protected static readonly HashSet<String> non_genetic_traits = new HashSet<string>(DefDatabase<StringListDef>.GetNamed("NonInheritedTraits").strings);

		//Fields
		///All babies should be premade and stored here
		protected List<Pawn> babies;
		///Reference to daddy, goes null sometimes
		protected Pawn father;
		///Self explanatory
		protected bool is_discovered;
		///Contractions duration, effectively additional hediff stage, a dirty hack to make birthing process notable
		protected int contractions;
		///Gestation progress per tick
		protected float progress_per_tick;
		//
		// Properties
		//
		public float GestationProgress
		{
			get => Severity;
			private set => Severity = value;
		}

		private bool IsSeverelyWounded
		{
			get
			{
				float num = 0;
				List<Hediff> hediffs = pawn.health.hediffSet.hediffs;
				foreach (Hediff h in hediffs)
				{
					if (h is Hediff_Injury && (!h.IsPermanent() || !h.IsTended()))
					{
						num += h.Severity;
					}
				}
				List<Hediff_MissingPart> missingPartsCommonAncestors = pawn.health.hediffSet.GetMissingPartsCommonAncestors();
				foreach (Hediff_MissingPart mp in missingPartsCommonAncestors)
				{
					if (mp.IsFresh)
					{
						num += mp.Part.def.GetMaxHealth(pawn);
					}
				}
				return num > 38 * pawn.RaceProps.baseHealthScale;
			}
		}

		//
		//  Methods
		//

		public override void PostMake()
		{
			// Ensure the hediff always applies to the torso, regardless of incorrect directive
			base.PostMake();
			BodyPartRecord torso = pawn.RaceProps.body.AllParts.Find(x => x.def.defName == "Torso");
			if (Part != torso)
				Part = torso;

			Initialize(pawn, father);
		}

		public override bool Visible => is_discovered;

		public void DiscoverPregnancy()
		{
			is_discovered = true;
			if (PawnUtility.ShouldSendNotificationAbout(this.pawn))
			{
				if (!is_checked)
				{
					string key1 = "RJW_PregnantTitle";
					string message_title = TranslatorFormattedStringExtensions.Translate(key1, pawn.LabelIndefinite());
					string key2 = "RJW_PregnantText";
					string message_text = TranslatorFormattedStringExtensions.Translate(key2, pawn.LabelIndefinite());
					Find.LetterStack.ReceiveLetter(message_title, message_text, LetterDefOf.NeutralEvent, pawn, null);

				}
				else
				{
					PregnancyMessage();
				}
			}
		}

		public bool is_checked = false;

		public void CheckPregnancy()
		{
			is_checked = true;
			if (!is_discovered)
				DiscoverPregnancy();
			else
				PregnancyMessage();
		}

		public virtual void PregnancyMessage()
		{
			string key1 = "RJW_PregnantTitle";
			string message_title = TranslatorFormattedStringExtensions.Translate(key1, pawn.LabelIndefinite());
			string key2 = "RJW_PregnantNormal";
			string message_text = TranslatorFormattedStringExtensions.Translate(key2, pawn.LabelIndefinite());
			Find.LetterStack.ReceiveLetter(message_title, message_text, LetterDefOf.NeutralEvent, pawn, null);
		}

		// Quick method to simply return a body part instance by a given part name
		internal static BodyPartRecord GetPawnBodyPart(Pawn pawn, String bodyPart)
		{
			return pawn.RaceProps.body.AllParts.Find(x => x.def == DefDatabase<BodyPartDef>.GetNamed(bodyPart));
		}

		public void Miscarry()
		{
			pawn.health?.RemoveHediff(this);
		}

		public Pawn partstospawn(Pawn baby, Pawn mother, Pawn dad)
		{
			//decide what parts to inherit
			//default use own parts
			Pawn partstospawn = baby;
			//spawn with mother parts
			if (mother != null && Rand.Range(0, 100) <= 10)
				partstospawn = mother;
			//spawn with father parts
			if (dad != null && Rand.Range(0, 100) <= 10)
				partstospawn = dad;

			//Log.Message("[RJW] Pregnancy partstospawn " + partstospawn);
			return partstospawn;
		}

		public bool spawnfutachild(Pawn baby, Pawn mother, Pawn dad)
		{
			int futachance = 0;
			if (mother != null && Genital_Helper.is_futa(mother))
				futachance = futachance + 25;
			if (dad != null && Genital_Helper.is_futa(dad))
				futachance = futachance + 25;

			//Log.Message("[RJW] Pregnancy spawnfutachild " + futachance);
			return (Rand.Range(0, 100) <= futachance);
		}

		public override void Tick()
		{
			ageTicks++;
			if (pawn.IsHashIntervalTick(1000))
			{
				//Log.Message("[RJW] Pregnancy is ticking for " + pawn + " this is " + this.def.defName + " will end in " + 1/progress_per_tick/TicksPerDay + " days resulting in "+ babies[0].def.defName);
				if (pawn.needs.food != null && pawn.needs.food.CurCategory == HungerCategory.Starving && Rand.MTBEventOccurs(0.5f, TicksPerDay, MiscarryCheckInterval))
				{
					if (Visible && PawnUtility.ShouldSendNotificationAbout(pawn))
					{
						string text = "MessageMiscarriedStarvation".Translate(pawn.LabelIndefinite()).CapitalizeFirst();
						Messages.Message(text, pawn, MessageTypeDefOf.NegativeHealthEvent);
					}
					Miscarry();
					return;
				}
				//let beatings only be important when pregnancy is developed somewhat
				if (Visible && IsSeverelyWounded && Rand.MTBEventOccurs(0.5f, TicksPerDay, MiscarryCheckInterval))
				{
					if (Visible && PawnUtility.ShouldSendNotificationAbout(pawn))
					{
						string text = "MessageMiscarriedPoorHealth".Translate(pawn.LabelIndefinite()).CapitalizeFirst();
						Messages.Message(text, pawn, MessageTypeDefOf.NegativeHealthEvent);
					}
					Miscarry();
					return;
				}
			}

			if (xxx.has_quirk(pawn, "Breeder"))
				GestationProgress += progress_per_tick * 1.25f;
			else
				GestationProgress += progress_per_tick;

			// Check if pregnancy is far enough along to "show" for the body type
			if (!is_discovered)
			{
				BodyTypeDef bodyT = pawn?.story?.bodyType;
				//float threshold = 0f;

				if ((bodyT == BodyTypeDefOf.Thin && GestationProgress > 0.25f) ||
					(bodyT == BodyTypeDefOf.Female && GestationProgress > 0.35f) ||
					(GestationProgress > 0.50f))
					DiscoverPregnancy();

				//switch (bodyT)
				//{
				//case BodyType.Thin: threshold = 0.3f; break;
				//case BodyType.Female: threshold = 0.389f; break;
				//case BodyType.Male: threshold = 0.41f; break;
				//default: threshold = 0.5f; break;
				//}
				//if (GestationProgress > threshold){ DiscoverPregnancy(); }
			}

			// no birthings in caravan?
			// no idea if it has any effect on guests that left player map
			if (CurStageIndex == 3 && pawn.Map != null)
			{
				if (contractions == 0)
				{
					if (PawnUtility.ShouldSendNotificationAbout(this.pawn))
					{
						string text = "RJW_Contractions".Translate(pawn.LabelIndefinite());
						Messages.Message(text, pawn, MessageTypeDefOf.NeutralEvent);
					}
				}
				contractions++;
				if (contractions >= TicksPerHour)//birthing takes an hour
				{
					if (PawnUtility.ShouldSendNotificationAbout(this.pawn))
					{
						string message_title = "RJW_GaveBirthTitle".Translate(pawn.LabelIndefinite());
						string message_text = "RJW_GaveBirthText".Translate(pawn.LabelIndefinite());
						string baby_text = ((babies.Count == 1) ? "RJW_ABaby".Translate() : "RJW_NBabies".Translate(babies.Count));
						message_text = message_text + baby_text;
						Find.LetterStack.ReceiveLetter(message_title, message_text, LetterDefOf.PositiveEvent, pawn);
					}
					GiveBirth();
				}
			}
		}

		//
		//These functions are different from CnP
		//
		public override void ExposeData()//If you didn't know, this one is used to save the object for later loading of the game... and to load the data into the object, <sigh>
		{
			base.ExposeData();
			Scribe_References.Look(ref father, "father");
			Scribe_Values.Look(ref is_discovered, "is_discovered");
			Scribe_Values.Look(ref contractions, "contractions");
			Scribe_Collections.Look(ref babies, saveDestroyedThings: true, label: "babies", lookMode: LookMode.Deep, ctorArgs: new object[0]);
			Scribe_Values.Look(ref progress_per_tick, "progress_per_tick", 1);
		}

		//This should generate pawns to be born in due time. Should take into account all settings and parent races
		protected virtual void GenerateBabies()
		{
			Pawn mother = pawn;
			//Log.Message("Generating babies for " + this.def.defName);
			if (mother == null)
			{
				Log.Error("Hediff_BasePregnancy::GenerateBabies() - no mother defined");
				return;
			}

			if (father == null)
			{
				Log.Warning("Hediff_BasePregnancy::GenerateBabies() - no father defined");

				//birthing with debug has no father
				//make father lover
				if (father == null)
				{
					Pawn target = mother.relations.GetFirstDirectRelationPawn(PawnRelationDefOf.Lover);
					if (target != null)
						father = Genital_Helper.has_penis(target) ? target : null;
				}

				//make father spouse
				if (father == null)
				{
					Pawn target = mother.relations.GetFirstDirectRelationPawn(PawnRelationDefOf.Spouse);
					if (target != null)
						father = Genital_Helper.has_penis(target) ? target : null;
				}

				//make father bonded animal
				//this is for testing only... so far
				if (father == null)
				{
					Pawn target = mother.relations.GetFirstDirectRelationPawn(PawnRelationDefOf.Bond);
					if (target != null)
						father = Genital_Helper.has_penis(target) && xxx.is_zoophile(mother) ? target : null;
				}
			}

			//Babies will have average number of traits of their parents, this way it will hopefully be compatible with various mods that change number of allowed traits
			int trait_count = 0;

			List<Trait> traitpool = new List<Trait>();
			float skin_whiteness = Rand.Range(0, 1);

			//Log.Message("[RJW]Generating babies " + traitpool.Count + " traits at first");
			if (xxx.has_traits(mother) && mother.RaceProps.Humanlike)
			{
				foreach (Trait momtrait in mother.story.traits.allTraits)
				{
					if (!RJWPregnancySettings.trait_filtering_enabled || !non_genetic_traits.Contains(momtrait.def.defName))
						traitpool.Add(momtrait);
				}
				skin_whiteness = mother.story.melanin;
				trait_count = mother.story.traits.allTraits.Count;
			}
			if (father != null && xxx.has_traits(father) && father.RaceProps.Humanlike)
			{
				foreach (Trait poptrait in father.story.traits.allTraits)
				{
					if (!RJWPregnancySettings.trait_filtering_enabled || !non_genetic_traits.Contains(poptrait.def.defName))
						traitpool.Add(poptrait);
				}
				skin_whiteness = Rand.Range(skin_whiteness, father.story.melanin);
				trait_count = Mathf.RoundToInt((trait_count + father.story.traits.allTraits.Count) / 2f);
			}
			//this probably doesnt matter in 1.0 remove it and wait for bug reports =)
			//trait_count = trait_count > 2 ? trait_count : 2;//Tynan has hardcoded 2-3 traits per pawn.  In the above, result may be less than that. Let us not have disfunctional babies.

			//Log.Message("[RJW]Generating babies " + traitpool.Count + " traits aFter parents");
			Pawn parent = mother; //race of child
			Pawn parent2= father; //name for child

			//Decide on which parent is first to be inherited
			if (father != null && RJWPregnancySettings.use_parent_method)
			{
				//Log.Message("The baby race needs definition");
				if (xxx.is_human(mother) && xxx.is_human(father))
				{
					//Log.Message("It's of two humanlikes");
					if (!Rand.Chance(RJWPregnancySettings.humanlike_DNA_from_mother))
					{
						//Log.Message("Mother will birth father race");
						parent = father;
					}
					else
					{
						//Log.Message("Mother will birth own race");
					}
				}
				else
				{
					if (!Rand.Chance(RJWPregnancySettings.bestial_DNA_from_mother))
					{
						//Log.Message("Mother will birth father race");
						parent = father;
					}
					else
					{
						//Log.Message("Mother will birth own race");
					}
				}

			}
			//Log.Message("[RJW] The main parent is " + parent);
			//Log.Message("Mother: " + xxx.get_pawnname(mother) + " kind: " + mother.kindDef);
			//Log.Message("Father: " + xxx.get_pawnname(father) + " kind: " + father.kindDef);
			//Log.Message("Baby base: " + xxx.get_pawnname(parent) + " kind: " + parent.kindDef);

			// Surname passing
			string last_name = "";
			if (xxx.is_human(father))
				last_name = NameTriple.FromString(father.Name.ToStringFull).Last;
			if (xxx.is_human(mother) && last_name == "")
				last_name = NameTriple.FromString(mother.Name.ToStringFull).Last;
			//Log.Message("[RJW] Bady surname will be " + last_name);

			//Pawn generation request
			int MapTile = -1;
			PawnKindDef spawn_kind_def = parent.kindDef;
			Faction spawn_faction = mother.IsPrisoner ? null : mother.Faction;
			PawnGenerationRequest request = new PawnGenerationRequest(spawn_kind_def, spawn_faction, PawnGenerationContext.NonPlayer, MapTile, false, true, false, false, false, false, 1, false, true, true, false, false, false, false, null, null, null, 0, 0, null, skin_whiteness, last_name);

			//Log.Message("[RJW] Generated request, making babies");
			//Litter size. Let's use the main parent litter size, increased by fertility.
			float litter_size = (parent.RaceProps.litterSizeCurve == null) ? 1 : Rand.ByCurve(parent.RaceProps.litterSizeCurve);
			//Log.Message("[RJW] base Litter size " + litter_size);
			litter_size *= mother.health.capacities.GetLevel(xxx.reproduction);
			litter_size *= father == null ? 1 : father.health.capacities.GetLevel(xxx.reproduction);
			litter_size = Math.Max(1, litter_size);
			//Log.Message("[RJW] Litter size (w fertility) " + litter_size);

			//Babies size vs mother body size
			//assuming mother belly is 1/3 of mother body size
			float baby_size = spawn_kind_def.RaceProps.lifeStageAges[0].def.bodySizeFactor * spawn_kind_def.RaceProps.baseBodySize; // adult size/5
			//Log.Message("[RJW] Baby size " + baby_size);
			float max_litter = 1f / 3f / baby_size;
			//Log.Message("[RJW] Max size " + max_litter);
			max_litter *= ((xxx.has_quirk(mother, "Breeder") || xxx.has_quirk(mother, "Incubator")) ? 2 : 1);
			//Log.Message("[RJW] Max size (w quirks) " + max_litter);

			//Generate random amount of babies within litter/max size
			litter_size = (Rand.Range(litter_size, max_litter));
			//Log.Message("[RJW] Litter size roll 1:" + litter_size);
			litter_size = Mathf.RoundToInt(litter_size);
			//Log.Message("[RJW] Litter size roll 2:" + litter_size);
			litter_size = Math.Max(1, litter_size);
			//Log.Message("[RJW] final Litter size " + litter_size);

			for (int i = 0; i < litter_size; i++)
			{
				Pawn baby = PawnGenerator.GeneratePawn(request);
				//Choose traits to add to the child. Unlike CnP this will allow for some random traits
				if (xxx.is_human(baby) && traitpool.Count > 0)
				{
					int baby_trait_count = baby.story.traits.allTraits.Count;
					if (baby_trait_count > trait_count / 2)
					{
						baby.story.traits.allTraits.RemoveRange(0, Math.Max(trait_count / 2, 1) - 1);
					}
					List<Trait> to_add = new List<Trait>();
					while (to_add.Count < Math.Min(trait_count, traitpool.Count))
					{
						Trait gentrait = traitpool.RandomElement();
						if (!to_add.Contains(gentrait))
						{
							to_add.Add(gentrait);
						}
					}
					foreach (Trait trait in to_add)
					{
						// Skip to next check if baby already has the trait, or it conflicts with existing one.
						if (baby.story.traits.HasTrait(trait.def) || baby.story.traits.allTraits.Any(x => x.def.ConflictsWith(trait))) continue;

						baby.story.traits.GainTrait(trait);

						if (baby.story.traits.allTraits.Count >= trait_count)
						{
							break;
						}
					}
				}
				babies.Add(baby);
			}
		}

		//Handles the spawning of pawns and adding relations
		//this is extended by other scripts
		public virtual void GiveBirth()
		{
		}

		public virtual void PostBirth(Pawn mother, Pawn father, Pawn baby)
		{
			if (xxx.is_human(baby))
			{
				baby.story.childhood = null;
				baby.story.adulthood = null;
			}
			//inject RJW_BabyState to the newborn if RimWorldChildren is not active
			//cnp patches its hediff right into pawn generator, so its already in if it can
			if (!xxx.RimWorldChildrenIsActive)
			{
				if (xxx.is_human(baby) && baby.ageTracker.CurLifeStageIndex <= 1 && baby.ageTracker.AgeBiologicalYears < 1 && !baby.Dead)
				{
					// Clean out drugs, implants, and stuff randomly generated
					//no support for custom race stuff, if there is any
					baby.health.hediffSet.Clear();
					baby.health.AddHediff(HediffDef.Named("RJW_BabyState"), null, null);//RJW_Babystate.tick_rare actually forces CnP switch to CnP one if it can, don't know wat do
					Hediff_SimpleBaby babystate = (Hediff_SimpleBaby)baby.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("RJW_BabyState"));
					if (babystate != null)
					{
						babystate.GrowUpTo(0, true);
					}
				}
			}
			else
			{
				if (xxx.is_human(mother))
				{
					mother.health.AddHediff(HediffDef.Named("PostPregnancy"), null, null);
					mother.health.AddHediff(HediffDef.Named("Lactating"), mother.RaceProps.body.AllParts.Find(x => x.def.defName == "Torso"), null);
					mother.needs.mood.thoughts.memories.TryGainMemory(ThoughtDef.Named("IGaveBirth"));
				}

				if (xxx.is_human(father))
				{
					father.needs.mood.thoughts.memories.TryGainMemory(ThoughtDef.Named("PartnerGaveBirth"));
				}

				if (xxx.is_human(baby))
				{
					Log.Message("Rewriting story of " + baby);
					baby.story.childhood = BackstoryDatabase.allBackstories["NA_Childhood"];
				}
			}

			//spawn futa
			bool isfuta = spawnfutachild(baby, mother, father);

			Genital_Helper.add_anus(baby, partstospawn(baby, mother, father));

			if (baby.gender == Gender.Female || isfuta)
				Genital_Helper.add_breasts(baby, partstospawn(baby, mother, father), Gender.Female);

			if (isfuta)
			{
				Genital_Helper.add_genitals(baby, partstospawn(baby, mother, father), Gender.Male);
				Genital_Helper.add_genitals(baby, partstospawn(baby, mother, father), Gender.Female);
				baby.gender = Gender.Female;                //set gender to female for futas, should cause no errors since babies already generated with relations n stuff
			}
			else
				Genital_Helper.add_genitals(baby, partstospawn(baby, mother, father));

			if (mother.Spawned)
			{
				// 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);
				}
				// Spawn guck
				FilthMaker.MakeFilth(mother.Position, mother.Map, ThingDefOf.Filth_AmnioticFluid, mother.LabelIndefinite(), 5);
				mother.caller?.DoCall();
				baby.caller?.DoCall();
			}

			if (xxx.is_human(baby))
				mother.records.AddTo(xxx.CountOfBirthHuman, 1);
			if (xxx.is_animal(baby))
				mother.records.AddTo(xxx.CountOfBirthAnimal, 1);

			if ((mother.records.GetAsInt(xxx.CountOfBirthHuman) > 10 || mother.records.GetAsInt(xxx.CountOfBirthAnimal) > 20))
			{
				if (!xxx.has_quirk(mother, "Breeder"))
				{
					CompRJW.Comp(mother).quirks.AppendWithComma("Breeder");
				}
				if (!xxx.has_quirk(mother, "Impregnation fetish"))
				{
					CompRJW.Comp(mother).quirks.AppendWithComma("Impregnation fetish");
				}
			}
		}

		//This method is doing the work of the constructor since hediffs are created through HediffMaker instead of normal oop way
		//This can't be in PostMake() because there wouldn't be mother and father.
		protected void Initialize(Pawn mother, Pawn dad)
		{
			BodyPartRecord torso = mother.RaceProps.body.AllParts.Find(x => x.def.defName == "Torso");
			mother.health.AddHediff(this, torso);
			//Log.Message("[RJW]Base pregnancy hediff generated: " + this.Label);
			father = dad;
			babies = new List<Pawn>();
			contractions = 0;
			//Log.Message("[RJW]Base pregnancy generating babies before: " + this.babies.Count);
			GenerateBabies();
			progress_per_tick = babies.NullOrEmpty() ? 1f : (1.0f) / (babies[0].RaceProps.gestationPeriodDays * TicksPerDay);
			//Log.Message("[RJW]Base pregnancy generating babies after: " + this.babies.Count);
		}

		public override string DebugString()
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append(base.DebugString());
			stringBuilder.AppendLine("Gestation progress: " + GestationProgress.ToStringPercent());
			stringBuilder.AppendLine("Time left: " + ((int)((1f - GestationProgress) * babies[0].RaceProps.gestationPeriodDays * TicksPerDay)).ToStringTicksToPeriod());
			stringBuilder.AppendLine(" Father: " + xxx.get_pawnname(father));
			return stringBuilder.ToString();
		}
	}
}


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);
		}
	}
}

repo_name: chippy/rjw

license: mit


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);
		}
	}
}


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

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;
		public Pawn queen;
		public bool fertilized = false;
		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]);
			Scribe_Values.Look<bool>(ref this.fertilized, "fertilized", false);
		}
		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
		public void GiveBirth()
		{
			Pawn mother = pawn;
			Pawn baby = null;
			if (fertilized)
			{
				//Log.Message("[RJW]Hediff_InsectEgg::BirthBaby() - Egg of " + parentDef + " in " + mother.ToString() + " birth!");
				PawnKindDef spawn_kind_def = father.kindDef;
				Faction 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;

				//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, true, false, false, false, false, null, null, null, null);

				baby = PawnGenerator.GeneratePawn(request);
				if (PawnUtility.TrySpawnHatchedOrBornPawn(baby, mother))
				{
					Genital_Helper.sexualize_pawn(baby);
					if (spawn_faction == Faction.OfInsects)
					{
						//add ai to pawn?
						LordJob_DefendAndExpandHive lordJob = new LordJob_DefendAndExpandHive(true);
						Lord lord = LordMaker.MakeNewLord(baby.Faction, lordJob, baby.Map);
						lord.AddPawn(baby);
						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);
			}

			mother.health.RemoveHediff(this);
		}

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

		public void Implanter(Pawn pawn)
		{
			if (queen == null)
			{
				queen = pawn;
			}
		}

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

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

		public override string DebugString()
		{
			return base.DebugString() + "  Implanter: " + xxx.get_pawnname(queen) + "\n  Age: " + (float)(this.ageTicks/this.bornTick) + "\n  Fertilized:" + (fertilized).ToString() ;
		}
	}
}


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 bool is_hacked = false;

		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;

namespace rjw
{
	internal class Hediff_Parasite : Hediff_Pregnant
	{
		new public static void DoBirthSpawn(Pawn mother, Pawn father)
		{
			int num = (mother.RaceProps.litterSizeCurve == null) ? 1 : Mathf.RoundToInt(Rand.ByCurve(mother.RaceProps.litterSizeCurve));
			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;

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

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

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

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

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

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

			base.ExposeData();
		}

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

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

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

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

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

namespace rjw
{
	public class IncidentWorker_NymphJoins : IncidentWorker
	{
		protected override bool CanFireNowSub(IncidentParms parms)
		{
			if (!RJWSettings.nymphos) return false;
			Map map = (Map)parms.target;
			float colonist_count = map.mapPawns.FreeColonistsCount;
			float nymph_count = map.mapPawns.FreeColonists.Count(xxx.is_nympho);

			float nymph_fraction = nymph_count / colonist_count;
			return colonist_count >= 1 && (nymph_fraction < xxx.config.max_nymph_fraction);
		}

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

			if (!RJWSettings.nymphos) return false;
			Map map = (Map) parms.target;

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

			if (!RCellFinder.TryFindRandomPawnEntryCell(out IntVec3 loc, map, CellFinder.EdgeRoadChance_Friendly + 0.2f))
			{
				//--Log.Message("IncidentWorker_NymphJoins::TryExecute() - no entry, abort!");
				return false;
			}

			Pawn pawn = nymph_generator.spawn_nymph(loc, ref map, Faction.OfPlayer);
			Find.LetterStack.ReceiveLetter("Nymph Joins", "A wandering nymph has decided to join your colony.",
				LetterDefOf.PositiveEvent, pawn);

			return true;
		}
	}
}


code:
using RimWorld;
using Verse;

namespace rjw
{
	public class IncidentWorker_NymphVisitorGroup : IncidentWorker_NeutralGroup
	{

		private static readonly SimpleCurve PointsCurve = new SimpleCurve
		{
			new CurvePoint(45f, 0f),
			new CurvePoint(50f, 1f),
			new CurvePoint(100f, 1f),
			new CurvePoint(200f, 0.25f),
			new CurvePoint(300f, 0.1f),
			new CurvePoint(500f, 0f)
		};

		protected override void ResolveParmsPoints(IncidentParms parms)
		{
			if (!(parms.points >= 0f))
			{
				parms.points = Rand.ByCurve(PointsCurve);
			}
		}

		protected override bool TryExecuteWorker(IncidentParms parms)
		{
			//--Log.Message("IncidentWorker_NymphVisitorGroup::TryExecute() called");

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

			Map map = (Map)parms.target;

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

			if (!RCellFinder.TryFindRandomPawnEntryCell(out IntVec3 loc, map, CellFinder.EdgeRoadChance_Friendly + 0.2f))
			{
				//--Log.Message("IncidentWorker_NymphJoins::TryExecute() - no entry, abort!");
				return false;
			}

			Pawn pawn = nymph_generator.spawn_nymph(loc, ref map);

			Find.LetterStack.ReceiveLetter("Nymph wanders in", "A wandering nymph has decided to visit your colony.", LetterDefOf.NeutralEvent, pawn);
			return true;
		}
	}
}


code:
using System;
using RimWorld;
using Verse;

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

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

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

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

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

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

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

		protected override bool TryExecuteWorker(IncidentParms parms)
		{
			var m = (Map)parms.target;

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

			return true;
		}
	}
}


code:
using RimWorld;
using UnityEngine;
using Verse;

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

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

		protected override bool TryExecuteWorker(IncidentParms parms)
		{
			var m = (Map)parms.target;

			info_on_select(m);

			return true;
		}
	}
}


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

using RimWorld;
using Verse;

namespace rjw
{
	internal class InteractionWorker_AnalSexAttempt : InteractionWorker
	{
		//initiator - rapist
		//recipient - victim
		public static bool AttemptAnalSex(Pawn initiator, Pawn recipient)
		{
			//--Log.Message(xxx.get_pawnname(initiator) + " is attempting to anally rape " + xxx.get_pawnname(recipient));
			return true;
		}

		public override float RandomSelectionWeight(Pawn initiator, Pawn recipient)
		{
			// this interaction is triggered by the jobdriver
			if (initiator == null || recipient == null)
				return 0.0f;

			return 0.0f; // base.RandomSelectionWeight(initiator, recipient);
		}

		public override void Interacted(Pawn initiator, Pawn recipient, List<RulePackDef> extraSentencePacks, out string letterText, out string letterLabel, out LetterDef letterDef)
		{
			//add something fancy here later?
			letterText = null;
			letterLabel = null;
			letterDef = null;
			//Find.LetterStack.ReceiveLetter("Rape attempt", "A wandering nymph has decided to join your colony.", LetterDefOf.NegativeEvent, recipient);

			if (initiator == null || recipient == null)
				return;
			//--Log.Message("[RJW] InteractionWorker_AnalRapeAttempt::Interacted( " + xxx.get_pawnname(initiator) + ", " + xxx.get_pawnname(recipient) + " ) called");
			AttemptAnalSex(initiator, recipient);
		}
	}

	internal class InteractionWorker_VaginalSexAttempt : InteractionWorker
	{
		//initiator - rapist
		//recipient - victim
		public static bool AttemptAnalSex(Pawn initiator, Pawn recipient)
		{
			//--Log.Message(xxx.get_pawnname(initiator) + " is attempting to anally rape " + xxx.get_pawnname(recipient));
			return false;
		}

		public override float RandomSelectionWeight(Pawn initiator, Pawn recipient)
		{
			// this interaction is triggered by the jobdriver
			if (initiator == null || recipient == null)
				return 0.0f;

			return 0.0f; // base.RandomSelectionWeight(initiator, recipient);
		}

		public override void Interacted(Pawn initiator, Pawn recipient, List<RulePackDef> extraSentencePacks, out string letterText, out string letterLabel, out LetterDef letterDef)
		{
			//add something fancy here later?
			letterText = null;
			letterLabel = null;
			letterDef = null;
			//Find.LetterStack.ReceiveLetter("Rape attempt", "A wandering nymph has decided to join your colony.", LetterDefOf.NegativeEvent, recipient);

			if (initiator == null || recipient == null)
				return;
			//--Log.Message("[RJW] InteractionWorker_AnalRapeAttempt::Interacted( " + xxx.get_pawnname(initiator) + ", " + xxx.get_pawnname(recipient) + " ) called");
			AttemptAnalSex(initiator, recipient);
		}
	}

	internal class InteractionWorker_OtherSexAttempt : InteractionWorker
	{
		//initiator - rapist
		//recipient - victim
		public static bool AttemptAnalSex(Pawn initiator, Pawn recipient)
		{
			//--Log.Message(xxx.get_pawnname(initiator) + " is attempting to anally rape " + xxx.get_pawnname(recipient));
			return false;
		}

		public override float RandomSelectionWeight(Pawn initiator, Pawn recipient)
		{
			// this interaction is triggered by the jobdriver
			if (initiator == null || recipient == null)
				return 0.0f;

			return 0.0f; // base.RandomSelectionWeight(initiator, recipient);
		}

		public override void Interacted(Pawn initiator, Pawn recipient, List<RulePackDef> extraSentencePacks, out string letterText, out string letterLabel, out LetterDef letterDef)
		{
			//add something fancy here later?
			letterText = null;
			letterLabel = null;
			letterDef = null;
			//Find.LetterStack.ReceiveLetter("Rape attempt", "A wandering nymph has decided to join your colony.", LetterDefOf.NegativeEvent, recipient);

			if (initiator == null || recipient == null)
				return;
			//--Log.Message("[RJW] InteractionWorker_AnalRapeAttempt::Interacted( " + xxx.get_pawnname(initiator) + ", " + xxx.get_pawnname(recipient) + " ) called");
			AttemptAnalSex(initiator, recipient);
		}
	}
}


code:
using RimWorld;
using Verse;

namespace rjw
{
	[DefOf]
	public static class JobDefOfZ
	{
		public static JobDef MilkHuman;

		static JobDefOfZ()
		{
			DefOfHelper.EnsureInitializedInCtor(typeof(JobDefOf));
		}
	}
}


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

namespace rjw
{
	public class JobDriver_BestialityForFemale : JobDriver
	{
		private readonly TargetIndex PartnerInd = TargetIndex.A;
		private readonly TargetIndex BedInd = TargetIndex.B;
		private readonly TargetIndex SlotInd = TargetIndex.C;
		private int ticks_left = 200;
		private const int ticks_between_hearts = 100;

		public Pawn Actor => GetActor();
		public Pawn Partner => (Pawn)(job.GetTarget(PartnerInd));
		public Building_Bed Bed => (Building_Bed)(job.GetTarget(BedInd));
		public IntVec3 SleepSpot => (IntVec3)job.GetTarget(SlotInd);

		public override void ExposeData()
		{
			base.ExposeData();
			Scribe_Values.Look(ref ticks_left, "ticksLeft", 0, false);
		}

		private static bool IsInOrByBed(Building_Bed b, Pawn p)
		{
			for (int i = 0; i < b.SleepingSlotsCount; i++)
			{
				if (b.GetSleepingSlotPos(i).InHorDistOf(p.Position, 1f))
				{
					return true;
				}
			}
			return false;
		}
		public override bool TryMakePreToilReservations(bool errorOnFailed)
		{
			return pawn.Reserve(Partner, job, 1, 0, null, errorOnFailed);
		}

		protected override IEnumerable<Toil> MakeNewToils()
		{
			this.FailOnDespawnedOrNull(PartnerInd);
			this.FailOnDespawnedNullOrForbidden(BedInd);
			this.FailOn(() => Actor is null || !Actor.CanReserveAndReach(Partner, PathEndMode.Touch, Danger.Deadly));
			this.FailOn(() => pawn.Drafted);
			yield return Toils_Reserve.Reserve(PartnerInd, 1, 0);
			//yield return Toils_Reserve.Reserve(BedInd, Bed.SleepingSlotsCount, 0);
			Toil gotoAnimal = Toils_Goto.GotoThing(PartnerInd, PathEndMode.Touch);
			yield return gotoAnimal;

			bool partnerHasPenis = Genital_Helper.has_penis(Partner) || Genital_Helper.has_penis_infertile(Partner);

			Toil gotoBed = new Toil
			{
				initAction = delegate
				{
					Actor.pather.StartPath(SleepSpot, PathEndMode.OnCell);
					Partner.pather.StartPath(Actor, PathEndMode.Touch);
				},
				tickAction = delegate
				{
					if (Partner.IsHashIntervalTick(150) && Partner.Downed)
					{
						Partner.pather.StartPath(Actor, PathEndMode.Touch);
					}
				},
				defaultCompleteMode = ToilCompleteMode.PatherArrival
			};
			gotoBed.FailOnBedNoLongerUsable(BedInd);
			gotoBed.AddFailCondition(() => Partner.Downed);
			yield return gotoBed;
			gotoBed.AddFinishAction(delegate
			{
				var gettin_loved = new Job(xxx.gettin_loved, Actor, Bed);
				Partner.jobs.StartJob(gettin_loved, JobCondition.InterruptForced, null, false, true, null);
			});

			Toil waitInBed = new Toil
			{
				initAction = delegate
				{
					ticksLeftThisToil = 5000;
					ticks_left = (int)(2000.0f * Rand.Range(0.30f, 1.30f));
				},
				tickAction = delegate
				{
					Actor.GainComfortFromCellIfPossible();
					if (IsInOrByBed(Bed, Partner))
					{
						ticksLeftThisToil = 0;
					}
				},
				defaultCompleteMode = ToilCompleteMode.Delay,
			};
			waitInBed.FailOn(() => pawn.GetRoom(RegionType.Set_Passable) == null);
			yield return waitInBed;

			Toil loveToil = new Toil
			{
				initAction = delegate
				{
					if (!partnerHasPenis)
						Actor.rotationTracker.Face(Partner.DrawPos);
				},
				defaultCompleteMode = ToilCompleteMode.Never, //Changed from Delay
			};
			loveToil.AddPreTickAction(delegate
			{
				//Actor.Reserve(Partner, 1, 0);
				--ticks_left;
				xxx.reduce_rest(Actor, 1);
				xxx.reduce_rest(Partner, 2);
				if (ticks_left <= 0)
					ReadyForNextToil();
				else if (pawn.IsHashIntervalTick(ticks_between_hearts))
				{
					MoteMaker.ThrowMetaIcon(Actor.Position, Actor.Map, ThingDefOf.Mote_Heart);
				}
				Actor.GainComfortFromCellIfPossible();
				Partner.GainComfortFromCellIfPossible();
			});
			loveToil.AddFailCondition(() => Partner.Dead || !IsInOrByBed(Bed, Partner));
			loveToil.socialMode = RandomSocialMode.Off;
			yield return loveToil;

			Toil afterSex = new Toil
			{
				initAction = delegate
				{
					//Log.Message("JobDriver_BestialityForFemale::MakeNewToils() - Calling aftersex");
					// Trying to add some interactions and social logs
					SexUtility.ProcessSex(pawn, Partner);
				},
				defaultCompleteMode = ToilCompleteMode.Instant
			};
			yield return afterSex;
		}
	}
}


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

namespace rjw
{
	public class JobDriver_BestialityForMale : JobDriver
	{
		private int duration;
		private int ticks_between_hearts;
		private int ticks_between_hits = 50;
		private int ticks_between_thrusts;
		protected TargetIndex target_animal = TargetIndex.A;

		protected Pawn animal => (Pawn)(job.GetTarget(target_animal));

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

		protected override IEnumerable<Toil> MakeNewToils()
		{
			//--Log.Message("[RJW] JobDriver_BestialityForMale::MakeNewToils() called");
			duration = (int)(2500.0f * Rand.Range(0.50f, 0.90f));
			ticks_between_hearts = Rand.RangeInclusive(70, 130);
			ticks_between_hits = Rand.Range(xxx.config.min_ticks_between_hits, xxx.config.max_ticks_between_hits);
			ticks_between_thrusts = 100;

			if (xxx.is_bloodlust(pawn))
				ticks_between_hits = (int)(ticks_between_hits * 0.75);
			if (xxx.is_brawler(pawn))
				ticks_between_hits = (int)(ticks_between_hits * 0.90);

			//this.FailOn (() => (!animal.health.capacities.CanBeAwake) || (!comfort_prisoners.is_designated (animal)));
			// Fail if someone else reserves the prisoner before the pawn arrives or colonist can't reach animal
			this.FailOn(() => !pawn.CanReserveAndReach(animal, PathEndMode.Touch, Danger.Deadly));
			this.FailOn(() => animal.HostileTo(pawn));
			this.FailOnDespawnedNullOrForbidden(target_animal);
			this.FailOn(() => pawn.Drafted);
			yield return Toils_Reserve.Reserve(target_animal, 1, 0);
			//Log.Message("[RJW] JobDriver_BestialityForMale::MakeNewToils() - moving towards animal");
			yield return Toils_Goto.GotoThing(target_animal, PathEndMode.Touch);
			yield return Toils_Interpersonal.WaitToBeAbleToInteract(pawn);
			yield return Toils_Interpersonal.GotoInteractablePosition(target_animal);
			if (xxx.is_kind(pawn)
				|| (xxx.CTIsActive && xxx.has_traits(pawn) && pawn.story.traits.HasTrait(TraitDef.Named("RCT_AnimalLover"))))
			{
				yield return TalkToAnimal(pawn, animal);
				yield return TalkToAnimal(pawn, animal);
			}
			if (Rand.Chance(0.6f))
				yield return TalkToAnimal(pawn, animal);
			yield return Toils_Goto.GotoThing(target_animal, PathEndMode.OnCell);

			SexUtility.RapeAttemptAlert(pawn, animal);

			Toil rape = new Toil();
			rape.initAction = delegate
			{
				//--Log.Message("[RJW] JobDriver_BestialityForMale::MakeNewToils() - reserving animal");

				//--Log.Message("[RJW] JobDriver_BestialityForMale::MakeNewToils() - Setting animal job driver");
				if (!(animal.jobs.curDriver is JobDriver_GettinRaped dri))
				{
					//wild animals may flee or attack
					if (pawn.Faction != animal.Faction && animal.RaceProps.wildness > Rand.Range(0.22f, 1.0f)
						&& !(pawn.TicksPerMoveCardinal < (animal.TicksPerMoveCardinal / 2) && !animal.Downed && xxx.is_not_dying(animal)))
					{
						animal.jobs.StopAll(); // Wake up if sleeping.

						float aggro = animal.kindDef.RaceProps.manhunterOnTameFailChance;
						if (animal.kindDef.RaceProps.predator)
							aggro += 0.2f;
						else
							aggro -= 0.1f;

						if (Rand.Chance(aggro) && animal.CanSee(pawn))
						{
							animal.rotationTracker.FaceTarget(pawn);
							LifeStageUtility.PlayNearestLifestageSound(animal, (ls) => ls.soundAngry, 1.4f);
							MoteMaker.ThrowMetaIcon(animal.Position, animal.Map, ThingDefOf.Mote_IncapIcon);
							MoteMaker.ThrowMetaIcon(pawn.Position, pawn.Map, ThingDefOf.Mote_ColonistFleeing); //red '!'
							animal.mindState.mentalStateHandler.TryStartMentalState(MentalStateDefOf.Manhunter);
							if (animal.kindDef.RaceProps.herdAnimal && Rand.Chance(0.2f))
							{ // 20% chance of turning the whole herd hostile...
								List<Pawn> packmates = animal.Map.mapPawns.AllPawnsSpawned.Where(x =>
									x != animal && x.def == animal.def && x.Faction == animal.Faction &&
									x.Position.InHorDistOf(animal.Position, 24f) && x.CanSee(animal)).ToList();

								foreach (Pawn packmate in packmates)
								{
									packmate.mindState.mentalStateHandler.TryStartMentalState(MentalStateDefOf.Manhunter);
								}
							}
							Messages.Message(pawn.Name.ToStringShort + " is being attacked by " + xxx.get_pawnname(animal) + ".", pawn, MessageTypeDefOf.ThreatSmall);
						}
						else
						{
							MoteMaker.ThrowMetaIcon(animal.Position, animal.Map, ThingDefOf.Mote_ColonistFleeing);
							LifeStageUtility.PlayNearestLifestageSound(animal, (ls) => ls.soundCall);
							animal.mindState.StartFleeingBecauseOfPawnAction(pawn);
							animal.mindState.mentalStateHandler.TryStartMentalState(DefDatabase<MentalStateDef>.GetNamed("PanicFlee"));
						}
						pawn.jobs.EndCurrentJob(JobCondition.Incompletable);
					}
					else
					{
						Job gettin_bred = new Job(xxx.gettin_bred, pawn, animal);
						animal.jobs.StartJob(gettin_bred, JobCondition.InterruptForced, null, true);
						(animal.jobs.curDriver as JobDriver_GettinRaped)?.increase_time(duration);
					}
				}
				else
				{
					dri.rapist_count += 1;
					dri.increase_time(duration);
				}
			};
			rape.tickAction = delegate
			{
				if (pawn.IsHashIntervalTick(ticks_between_hearts))
					MoteMaker.ThrowMetaIcon(pawn.Position, pawn.Map, ThingDefOf.Mote_Heart);
				if (pawn.IsHashIntervalTick(ticks_between_thrusts))
					xxx.sexTick(pawn, animal, false);
				/*
				if (pawn.IsHashIntervalTick (ticks_between_hits))
					roll_to_hit (pawn, animal);
					*/
				xxx.reduce_rest(animal, 1);
				xxx.reduce_rest(pawn, 2);

			};
			rape.AddFinishAction(delegate
			{
				//--Log.Message("[RJW] JobDriver_BestialityForMale::MakeNewToils() - finished violating");
				if (animal.jobs?.curDriver is JobDriver_GettinRaped)
					(animal.jobs.curDriver as JobDriver_GettinRaped).rapist_count -= 1;

				if (xxx.is_human(pawn))
					pawn.Drawer.renderer.graphics.ResolveApparelGraphics();
			});
			rape.defaultCompleteMode = ToilCompleteMode.Delay;
			rape.defaultDuration = duration;
			yield return rape;

			yield return new Toil
			{
				initAction = delegate
				{
					//Log.Message("[RJW] JobDriver_BestialityForMale::MakeNewToils() - creating aftersex toil");
					SexUtility.ProcessSex(pawn, animal);
				},
				defaultCompleteMode = ToilCompleteMode.Instant
			};
		}

		private Toil TalkToAnimal(Pawn pawn, Pawn animal)
		{
			Toil toil = new Toil();
			toil.initAction = delegate
			{
				pawn.interactions.TryInteractWith(animal, SexUtility.AnimalSexChat);
			};
			toil.defaultCompleteMode = ToilCompleteMode.Delay;
			toil.defaultDuration = Rand.Range(120, 220);
			return toil;
		}
	}
}


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

namespace rjw
{
	/// <summary>
	/// This is the driver for animals mounting breeders.
	/// </summary>
	public class JobDriver_Breeding : JobDriver
	{
		protected TargetIndex PartnerIndex = TargetIndex.A;

		public Pawn Target => (Pawn)(job.GetTarget(PartnerIndex));

		public override bool TryMakePreToilReservations(bool errorOnFailed)
		{
			return pawn.Reserve(Target, job, BreederHelper.max_animals_at_once, 0);
		}

		public virtual void roll_to_hit(Pawn pawn, Pawn partner)
		{
			if (!RJWSettings.rape_beating || !xxx.is_human(pawn))
				return;

			float rand_value = Rand.Value;
			float victim_pain = partner.health.hediffSet.PainTotal;
			float chance_to_hit = xxx.config.base_chance_to_hit_prisoner/5;
			float threshold = xxx.config.minor_pain_threshold;

			if ((victim_pain < threshold && rand_value < chance_to_hit))
			{
				if (InteractionUtility.TryGetRandomVerbForSocialFight(pawn, out Verb v))
					pawn.meleeVerbs.TryMeleeAttack(partner, v);
			}
		}

		protected override IEnumerable<Toil> MakeNewToils()
		{
			int duration = (int)(2000.0f * Rand.Range(0.50f, 0.90f));
			int ticks_between_hearts = Rand.RangeInclusive(70, 130);
			int ticks_between_hits = Rand.Range(xxx.config.min_ticks_between_hits, xxx.config.max_ticks_between_hits);
			int ticks_between_thrusts = 100;

			//--Log.Message("JobDriver_ComfortPrisonerRapin::MakeNewToils() - setting fail conditions");
			this.FailOnDespawnedNullOrForbidden(PartnerIndex);
			this.FailOn(() => !pawn.CanReserve(Target, BreederHelper.max_animals_at_once, 0)); // Fail if someone else reserves the target before the animal arrives.
			this.FailOn(() => !pawn.CanReach(Target, PathEndMode.Touch, Danger.Some)); // Fail if animal cannot reach target.
			this.FailOn(() => !(Target.IsDesignatedBreeding() || (RJWSettings.animal_on_animal_enabled && xxx.is_animal(Target)))); // Fail if not designated and not animal-on-animal
			this.FailOn(() => Target.CurJob == null);
			this.FailOn(() => pawn.Drafted);

			// Path to target
			yield return Toils_Goto.GotoThing(PartnerIndex, PathEndMode.OnCell);

			SexUtility.RapeAttemptAlert(pawn, Target);

			// Breed target
			var breed = new Toil();
			breed.initAction = delegate
			{
				//Log.Message("JobDriver_ComfortPrisonerRapin::MakeNewToils() - Setting victim job driver");
				Job currentJob = Target.jobs.curJob;

				if (currentJob.def != xxx.gettin_raped)
				{
					Job gettin_raped = new Job(xxx.gettin_raped, pawn, Target);
					Building_Bed Bed = null;
					//Log.Message(xxx.get_pawnname(pawn) + " LayingInBed:" + pawn.GetPosture());
					//Log.Message(xxx.get_pawnname(Target) + " LayingInBed:" + Target.GetPosture());
					if (Target.GetPosture() == PawnPosture.LayingInBed)
					{
						Bed = Target.CurrentBed();
						//Log.Message(xxx.get_pawnname(Target) + ": bed:" + Bed);
					}

					Target.jobs.StartJob(gettin_raped, JobCondition.InterruptForced);

					//var dri = Target.jobs.curDriver as JobDriver_GettinRaped;
					//if (xxx.is_animal(pawn) && xxx.is_animal(Target)) // No alert spam for animal-on-animal
						//dri.disable_alert = true;

					(Target.jobs.curDriver as JobDriver_GettinRaped).increase_time(duration);
					if (Bed != null)
						(Target.jobs.curDriver as JobDriver_GettinRaped)?.set_bed(Bed);
				}
				else
				{
					if (Target.jobs.curDriver is JobDriver_GettinRaped dri)
					{
						dri.rapist_count += 1;
						dri.increase_time(duration);
					}
				}
			};

			breed.tickAction = delegate
			{
				if (pawn.IsHashIntervalTick(ticks_between_hearts))
					MoteMaker.ThrowMetaIcon(pawn.Position, pawn.Map, ThingDefOf.Mote_Heart);
				if (pawn.IsHashIntervalTick(ticks_between_thrusts))
					xxx.sexTick(pawn, Target);
				if (!xxx.is_zoophile(Target) && pawn.IsHashIntervalTick(ticks_between_hits))
					roll_to_hit(pawn, Target);

				if (!Target.Dead)
					xxx.reduce_rest(Target, 1);
				xxx.reduce_rest(pawn, 2);

				if (Genital_Helper.has_penis(pawn) || Genital_Helper.has_penis_infertile(pawn))
				{
					// Face same direction, most of animal sex is likely doggystyle.
					Target.Rotation = pawn.Rotation;
				}
			};

			breed.AddFinishAction(delegate
			{
				if ((Target.jobs != null)
				&& (Target.jobs.curDriver != null)
				&& (Target.jobs.curDriver as JobDriver_GettinRaped != null))
				{
					(Target.jobs.curDriver as JobDriver_GettinRaped).rapist_count -= 1;
				}
				pawn.stances.StaggerFor(Rand.Range(0,50));
				Target.stances.StaggerFor(Rand.Range(10,300));
			});

			breed.defaultCompleteMode = ToilCompleteMode.Delay;
			breed.defaultDuration = duration;
			yield return breed;

			yield return new Toil
			{
				initAction = delegate
				{
					//Log.Message("JobDriver_Breeding::MakeNewToils() - Calling aftersex");
					//// Trying to add some interactions and social logs
					bool violent = !(pawn.relations.DirectRelationExists(PawnRelationDefOf.Bond, Target) ||
					                 (xxx.is_animal(pawn) && (pawn.RaceProps.wildness - pawn.RaceProps.petness + 0.18f) > Rand.Range(0.36f, 1.8f)));
					SexUtility.ProcessSex(pawn, Target, violent);
				},
				defaultCompleteMode = ToilCompleteMode.Instant
			};
		}
	}
}


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_HediffDefOf.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_HediffDefOf.Hediff_Semen || x.def == RJW_HediffDefOf.Hediff_InsectSpunk || x.def == RJW_HediffDefOf.Hediff_MechaFluids));
					if (hediff != null)
					{
						hediff.Severity -= cleanAmount;
					}
				}
			};
			yield break;

		}
	}
}


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

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

		private int ticks_left;

		private readonly TargetIndex ibed = TargetIndex.A;
		private Building_Bed Bed => (Building_Bed)((Thing)job.GetTarget(ibed));

		public override bool TryMakePreToilReservations(bool errorOnFailed)
		{
			return pawn.Reserve(Bed, job, Bed.SleepingSlotsCount, 0, null, errorOnFailed);
		}

		protected override IEnumerable<Toil> MakeNewToils()
		{
			// Faster fapping when frustrated.
			ticks_left = (int)(xxx.need_some_sex(pawn) > 2f ? 2500.0f * Rand.Range(0.2f, 0.7f) : 2500.0f * Rand.Range(0.2f, 0.4f));

			this.FailOnDespawnedOrNull(ibed);
			this.FailOn(() => pawn.Drafted);
			this.KeepLyingDown(ibed);
			yield return Toils_Bed.ClaimBedIfNonMedical(ibed);
			yield return Toils_Bed.GotoBed(ibed);

			Toil do_fappin = Toils_LayDown.LayDown(ibed, true, false, false, false);
			do_fappin.AddPreTickAction(delegate
			{
				--ticks_left;
				xxx.reduce_rest(pawn, 1);
				if (ticks_left <= 0)
					ReadyForNextToil();
				else if (pawn.IsHashIntervalTick(ticks_between_hearts))
					MoteMaker.ThrowMetaIcon(pawn.Position, pawn.Map, ThingDefOf.Mote_Heart);
			});
			do_fappin.AddFinishAction(delegate
			{
			    //Moved satisfy and tick increase to aftersex, since it works with solo acts now.
				SexUtility.Aftersex(pawn, xxx.rjwSextype.Masturbation);
				if (SexUtility.ConsiderCleaning(pawn))
				{
					LocalTargetInfo own_cum = pawn.PositionHeld.GetFirstThing<Filth>(pawn.Map);

					Job clean = new Job(JobDefOf.Clean);
					clean.AddQueuedTarget(TargetIndex.A, own_cum);

					pawn.jobs.jobQueue.EnqueueFirst(clean);

				}
			});
			do_fappin.socialMode = RandomSocialMode.Off;
			yield return do_fappin;
		}
	}
}


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

namespace rjw
{
	public abstract class JobDriver_GatherHumanBodyResources : JobDriver_GatherAnimalBodyResources
	{
		private float gatherProgress;

		/*
		//maybe change?
		protected abstract int GatherResourcesIntervalDays
		{
			get;
		}

		//add breastsize modifier?
		protected abstract int ResourceAmount
		{
			get;
		}
		//add more  milks?
		protected abstract ThingDef ResourceDef
		{
			get;
		}
		*/

		protected override IEnumerable<Toil> MakeNewToils()
		{
			ToilFailConditions.FailOnDespawnedNullOrForbidden<JobDriver_GatherHumanBodyResources>(this, TargetIndex.A);
			ToilFailConditions.FailOnNotCasualInterruptible<JobDriver_GatherHumanBodyResources>(this, TargetIndex.A);
			yield return Toils_Goto.GotoThing(TargetIndex.A, PathEndMode.Touch);
			Toil wait = new Toil();
			wait.initAction = delegate
			{
				Pawn milker = base.pawn;
				LocalTargetInfo target = base.job.GetTarget(TargetIndex.A);
				Pawn target2 = (Pawn)target.Thing;
				milker.pather.StopDead();
				PawnUtility.ForceWait(target2, 15000, null, true);
			};
			wait.tickAction = delegate
			{
				Pawn milker = base.pawn;
				milker.skills.Learn(SkillDefOf.Animals, 0.13f, false);
				gatherProgress += StatExtension.GetStatValue(milker, StatDefOf.AnimalGatherSpeed, true);
				if (gatherProgress >= WorkTotal)
				{
					GetComp((Pawn)base.job.GetTarget(TargetIndex.A)).Gathered(base.pawn);
					milker.jobs.EndCurrentJob(JobCondition.Succeeded, true);
				}
			};
			wait.AddFinishAction((Action)delegate
			{
				Pawn milker = base.pawn;
				LocalTargetInfo target = base.job.GetTarget(TargetIndex.A);
				Pawn target2 = (Pawn)target.Thing;
				if (target2 != null && target2.CurJobDef == JobDefOf.Wait_MaintainPosture)
				{
					milker.jobs.EndCurrentJob(JobCondition.InterruptForced, true);
				}
			});
			ToilFailConditions.FailOnDespawnedOrNull<Toil>(wait, TargetIndex.A);
			ToilFailConditions.FailOnCannotTouch<Toil>(wait, TargetIndex.A, PathEndMode.Touch);
			wait.AddEndCondition((Func<JobCondition>)delegate
			{
				if (GetComp((Pawn)base.job.GetTarget(TargetIndex.A)).ActiveAndFull)
				{
					return JobCondition.Ongoing;
				}
				return JobCondition.Incompletable;
			});
			wait.defaultCompleteMode = ToilCompleteMode.Never;
			ToilEffects.WithProgressBar(wait, TargetIndex.A, (Func<float>)(() => gatherProgress / WorkTotal), false, -0.5f);
			wait.activeSkill = (() => SkillDefOf.Animals);
			yield return wait;
		}
	}
}


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

namespace rjw
{
	public class JobDriver_GettinLoved : JobDriver
	{
		private TargetIndex ipartner = TargetIndex.A;
		private TargetIndex ibed = TargetIndex.B;

		private int tick_interval = 100;

		protected Pawn Partner => (Pawn)(job.GetTarget(ipartner));
		protected Building_Bed Bed => (Building_Bed)(job.GetTarget(ibed));

		public override bool TryMakePreToilReservations(bool errorOnFailed)
		{
			return true;
			//return this.pawn.Reserve(this.Partner, this.job, 1, 0, null) && this.pawn.Reserve(this.Bed, this.job, 1, 0, null);
		}

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

			float partner_ability = xxx.get_sex_ability(Partner);

			// More/less hearts based on partner ability.
			if (partner_ability < 0.8f)
				tick_interval += 100;
			else if (partner_ability > 2.0f)
				tick_interval -= 25;

			// More/less hearts based on opinion.
			if (pawn.relations.OpinionOf(Partner) < 0)
				tick_interval += 50;
			else if (pawn.relations.OpinionOf(Partner) > 60)
				tick_interval -= 25;

			if (Partner.CurJob.def == xxx.casual_sex)
			{
				this.FailOnDespawnedOrNull(ipartner);
				this.FailOn(() => !Partner.health.capacities.CanBeAwake);
				this.FailOn(() => pawn.Drafted);
				this.KeepLyingDown(ibed);
				yield return Toils_Reserve.Reserve(ipartner, 1, 0);
				yield return Toils_Reserve.Reserve(ibed, Bed.SleepingSlotsCount, 0);
				Toil get_loved = Toils_LayDown.LayDown(ibed, true, false, false, false);
				get_loved.FailOn(() => Partner.CurJob == null || Partner.CurJob.def != xxx.casual_sex);
				get_loved.defaultCompleteMode = ToilCompleteMode.Never;
				get_loved.AddPreTickAction(delegate
				{
					if (pawn.IsHashIntervalTick(tick_interval))
					{
						MoteMaker.ThrowMetaIcon(pawn.Position, pawn.Map, ThingDefOf.Mote_Heart);
						xxx.sexTick(pawn, Partner);
					}
				});
				get_loved.socialMode = RandomSocialMode.Off;
				yield return get_loved;
			}
			else if (Partner.CurJob.def == xxx.whore_is_serving_visitors)
			{
				this.FailOnDespawnedOrNull(ipartner);
				this.FailOn(() => !Partner.health.capacities.CanBeAwake || Partner.CurJob == null);
				yield return Toils_Goto.GotoThing(ipartner, PathEndMode.OnCell);
				yield return Toils_Reserve.Reserve(ipartner, 1, 0);
				Toil get_loved = new Toil();
				get_loved.FailOn(() => (Partner.CurJob.def != xxx.whore_is_serving_visitors));
				get_loved.defaultCompleteMode = ToilCompleteMode.Never;
				get_loved.initAction = delegate
				{
					//--Log.Message("[RJW]JobDriver_GettinLoved::MakeNewToils - whore section is called");
				};
				get_loved.AddPreTickAction(delegate
				{
					if (pawn.IsHashIntervalTick(tick_interval))
					{
						MoteMaker.ThrowMetaIcon(pawn.Position, pawn.Map, ThingDefOf.Mote_Heart);
						xxx.sexTick(pawn, Partner);
					}
				});
				get_loved.socialMode = RandomSocialMode.Off;
				yield return get_loved;
			}
			else if (Partner.CurJob.def == xxx.bestialityForFemale)
			{
				this.FailOnDespawnedOrNull(ipartner);
				this.FailOn(() => !Partner.health.capacities.CanBeAwake || Partner.CurJob == null);
				yield return Toils_Goto.GotoThing(ipartner, PathEndMode.OnCell);
				yield return Toils_Reserve.Reserve(ipartner, 1, 0);
				Toil get_loved = new Toil();
				get_loved.FailOn(() => (Partner.CurJob.def != xxx.bestialityForFemale));
				get_loved.defaultCompleteMode = ToilCompleteMode.Never;
				get_loved.initAction = delegate
				{
					//--Log.Message("[RJW]JobDriver_GettinLoved::MakeNewToils - bestialityForFemale section is called");
				};
				get_loved.AddPreTickAction(delegate
				{
					if (pawn.IsHashIntervalTick(tick_interval))
					{
						MoteMaker.ThrowMetaIcon(pawn.Position, pawn.Map, ThingDefOf.Mote_Heart);
						xxx.sexTick(pawn, Partner);
					}
				});
				get_loved.socialMode = RandomSocialMode.Off;
				yield return get_loved;
			}
		}
	}
}


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

namespace rjw
{
	public class JobDriver_GettinRaped : JobDriver
	{
		private int ticks_between_hearts;

		private int ticks_remaining = 10;

		public int rapist_count = 1; // Defaults to 1 so the first rapist doesn't have to add themself

		private Pawn Initiator => (Pawn)(job.GetTarget(TargetIndex.A));
		private Pawn Receiver => (Pawn)(job.GetTarget(TargetIndex.B));

		//private bool was_laying_down;

		private Building_Bed Bed;

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

		public void set_bed(Building_Bed newBed)
		{
			Bed = newBed;
		}

		public override bool TryMakePreToilReservations(bool errorOnFailed)
		{
			return true;
		}

		private void RapeAlert(bool silent = false)
		{
			if (!silent)
				Messages.Message(xxx.get_pawnname(Receiver) + " is getting raped.", Receiver, MessageTypeDefOf.NegativeEvent);
			else
				Messages.Message(xxx.get_pawnname(Receiver) + " is getting raped.", Receiver, MessageTypeDefOf.SilentInput);
		}

		protected override IEnumerable<Toil> MakeNewToils()
		{
			ticks_between_hearts = Rand.RangeInclusive(70, 130);
			//was_laying_down = pawn.GetPosture() != PawnPosture.Standing;
			//Log.Message(xxx.get_pawnname(Initiator) + ": was_laying_down:" + was_laying_down + " LayingInBed:" + Initiator.GetPosture());
			//Log.Message(xxx.get_pawnname(Receiver) + ": was_laying_down:" + was_laying_down + " LayingInBed:" + Receiver.GetPosture());
			//was_laying_down = (pawn.jobs.curDriver != null) && pawn.GetPosture() != PawnPosture.Standing;
			//Log.Message(xxx.get_pawnname(pawn) + ": bed:" + Bed);

			var get_raped = new Toil();
			get_raped.defaultCompleteMode = ToilCompleteMode.Never;
			get_raped.initAction = delegate
			{
				pawn.pather.StopDead();
				//pawn.jobs.posture = PawnPosture.Standing;
				pawn.jobs.curDriver.asleep = false;

				switch (RJWPreferenceSettings.rape_alert_sound)
				{
					case RJWPreferenceSettings.RapeAlert.Enabled:
						RapeAlert();
						break;
					case RJWPreferenceSettings.RapeAlert.Humanlikes:
						if (xxx.is_human(Receiver))
							RapeAlert();
						else
							RapeAlert(true);
						break;
					case RJWPreferenceSettings.RapeAlert.Colonists:
						if (Receiver.Faction == Faction.OfPlayer)
							RapeAlert();
						else
							RapeAlert(true);
						break;
					default:
						RapeAlert(true);
						break;
				}

				//Messages.Message("GetinRapedNow".Translate(new object[] { pawn.LabelIndefinite() }).CapitalizeFirst(), pawn, MessageTypeDefOf.NegativeEvent);

				if (Initiator == null || Receiver == null) return;
				bool partnerHasHands = Receiver.health.hediffSet.GetNotMissingParts().Any(part => part.IsInGroup(BodyPartGroupDefOf.RightHand) || part.IsInGroup(BodyPartGroupDefOf.LeftHand));

				// Hand check is for monstergirls and other bipedal 'animals'.
				if ((!xxx.is_animal(Initiator) && partnerHasHands) || Rand.Chance(0.3f)) // 30% chance of face-to-face regardless, for variety.
				{ // Face-to-face
					Initiator.rotationTracker.Face(Receiver.DrawPos);
					Receiver.rotationTracker.Face(Initiator.DrawPos);
				}
				else
				{ // From behind / animal stuff should mostly use this
					Initiator.rotationTracker.Face(Receiver.DrawPos);
					Receiver.Rotation = Initiator.Rotation;
				}
				// TODO: The above works, but something is forcing the partners to face each other during sex. Need to figure it out.

				//prevent Receiver standing up and interrupting rape, probably
				if (Receiver.health.hediffSet.HasHediff(HediffDef.Named("Hediff_Submitting")))
					Receiver.health.AddHediff(HediffDef.Named("Hediff_Submitting"));
			};
			get_raped.tickAction = delegate
			{
				--ticks_remaining;
				/*
				if ((ticks_remaining <= 0) || (rapist_count <= 0))
					ReadyForNextToil();
				*/
				if ((rapist_count > 0) && (pawn.IsHashIntervalTick(ticks_between_hearts / rapist_count)))
					MoteMaker.ThrowMetaIcon(pawn.Position, pawn.Map, xxx.mote_noheart);

			};
			get_raped.AddEndCondition(new Func<JobCondition>(() =>
			{
				if ((ticks_remaining <= 0) || (rapist_count <= 0))
					return JobCondition.Succeeded;
				return JobCondition.Ongoing;
			}));
			get_raped.AddFinishAction(delegate
			{
				if (Bed != null && pawn.Downed)
				{
					Job tobed = new Job(JobDefOf.Rescue, pawn, Bed);
					tobed.count = 1;
					Initiator.jobs.jobQueue.EnqueueFirst(tobed);
					//Log.Message(xxx.get_pawnname(Initiator) + ": job tobed:" + tobed);
				}
			});
			get_raped.socialMode = RandomSocialMode.Off;
			yield return get_raped;
		}
	}
}


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

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

		private int ticks_left;

		private readonly TargetIndex ipawn = TargetIndex.A;
		private readonly TargetIndex ipartner = TargetIndex.B;
		private readonly TargetIndex ibed = TargetIndex.C;

		protected Pawn Top => (Pawn)(job.GetTarget(ipawn));
		protected Pawn Partner => (Pawn)(job.GetTarget(ipartner));
		protected Building_Bed Bed => (Building_Bed)(job.GetTarget(ibed));

		public override bool TryMakePreToilReservations(bool errorOnFailed)
		{
			return Top.Reserve(Partner, job, xxx.max_rapists_per_prisoner, 0, null, errorOnFailed);
		}

		protected override IEnumerable<Toil> MakeNewToils()
		{
			//--Log.Message("JobDriver_JoinInBed::MakeNewToils() called");
			this.FailOnDespawnedOrNull(ipartner);
			this.FailOnDespawnedOrNull(ibed);
			this.FailOn(() => !Partner.health.capacities.CanBeAwake);
			this.FailOn(() => !(xxx.is_laying_down_alone(Partner) || xxx.in_same_bed(Partner, Top)));
			this.FailOn(() => pawn.Drafted);
			yield return Toils_Reserve.Reserve(ipartner, xxx.max_rapists_per_prisoner, 0);
			yield return Toils_Goto.GotoThing(ipartner, PathEndMode.OnCell);
			yield return new Toil
			{
				initAction = delegate
				{
					//--Log.Message("JobDriver_JoinInBed::MakeNewToils() - setting initAction");
					ticks_left = (int)(2500.0f * Rand.Range(0.30f, 1.30f));
					Job gettin_loved = new Job(xxx.gettin_loved, Top, Bed);
					Partner.jobs.StartJob(gettin_loved, JobCondition.InterruptForced);
				},
				defaultCompleteMode = ToilCompleteMode.Instant
			};
			Toil do_lovin = new Toil {defaultCompleteMode = ToilCompleteMode.Never};
			do_lovin.FailOn(() => (Partner.CurJob == null) || (Partner.CurJob.def != xxx.gettin_loved));
			do_lovin.AddPreTickAction(delegate
			{
				--ticks_left;
				xxx.reduce_rest(Partner, 1);
				xxx.reduce_rest(Top, 2);
				if (ticks_left <= 0)
					ReadyForNextToil();
				else if (Top.IsHashIntervalTick(ticks_between_hearts))
					MoteMaker.ThrowMetaIcon(Top.Position, Top.Map, ThingDefOf.Mote_Heart);
			});
			do_lovin.socialMode = RandomSocialMode.Off;
			yield return do_lovin;
			yield return new Toil
			{
				initAction = delegate
				{
					// Trying to add some interactions and social logs
					SexUtility.ProcessSex(Top, Partner, false, true);
				},
				defaultCompleteMode = ToilCompleteMode.Instant
			};
		}
	}
}


code:
using RimWorld;
using Verse;

namespace rjw
{
	public class JobDriver_MilkHuman : JobDriver_GatherHumanBodyResources
	{
		protected override float WorkTotal => 400f;

		protected override CompHasGatherableBodyResource GetComp(Pawn animal)
		{
			return ThingCompUtility.TryGetComp<CompMilkableHuman>(animal);
		}
	}
}