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

namespace rjw
{
	// Token: 0x020006B5 RID: 1717
	public class JobGiver_NymphSapper : ThinkNode_JobGiver
	{
		// Token: 0x06002E54 RID: 11860 RVA: 0x001041A7 File Offset: 0x001023A7
		public override ThinkNode DeepCopy(bool resolve = true)
		{
			JobGiver_NymphSapper jobGiver_NymphSapper = (JobGiver_NymphSapper)base.DeepCopy(resolve);
			jobGiver_NymphSapper.canMineMineables = this.canMineMineables;
			jobGiver_NymphSapper.canMineNonMineables = this.canMineNonMineables;
			return jobGiver_NymphSapper;
		}

		// Token: 0x06002E55 RID: 11861 RVA: 0x001041D0 File Offset: 0x001023D0
		protected override Job TryGiveJob(Pawn pawn)
		{
			if (!RJWSettings.NymphSappers)
				return null;
			if (!xxx.is_nympho(pawn))
				return null;

			IntVec3 intVec;
			{
				IAttackTarget attackTarget;
				if (!(from x in pawn.Map.mapPawns.FreeColonistsAndPrisonersSpawned
				where !x.ThreatDisabled(pawn) && pawn.CanReach(x, PathEndMode.OnCell, Danger.Deadly, false, TraverseMode.PassAllDestroyableThings)
				select x).TryRandomElement(out attackTarget))
				{
					return null;
				}
				intVec = attackTarget.Thing.Position;
			}
			using (PawnPath pawnPath = pawn.Map.pathFinder.FindPath(pawn.Position, intVec, TraverseParms.For(pawn, Danger.Deadly, TraverseMode.PassDoors, false), PathEndMode.OnCell))
			{
				IntVec3 cellBeforeBlocker;
				Thing thing = pawnPath.FirstBlockingBuilding(out cellBeforeBlocker, pawn);
				if (thing != null)
				{
					Job job = DigUtility.PassBlockerJob(pawn, thing, cellBeforeBlocker, this.canMineMineables, this.canMineNonMineables);
					if (job != null)
					{
						return job;
					}
				}
			}
			return JobMaker.MakeJob(JobDefOf.Goto, intVec, 500, true);
		}

		// Token: 0x04001A64 RID: 6756
		private bool canMineMineables = false;

		// Token: 0x04001A65 RID: 6757
		private bool canMineNonMineables = false;

		// Token: 0x04001A66 RID: 6758
		private const float ReachDestDist = 10f;

		// Token: 0x04001A67 RID: 6759
		private const int CheckOverrideInterval = 500;
	}
}


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

namespace rjw
{
	public struct nymph_story
	{
		public Backstory child;
		public Backstory adult;
		public List<Trait> traits;
	}

	public struct nymph_passion_chances
	{
		public float major;
		public float minor;

		public nymph_passion_chances(float maj, float min)
		{
			major = maj;
			minor = min;
		}
	}

	public static class nymph_backstories
	{
		public struct child
		{
			public static Backstory vatgrown_sex_slave;
		};

		public struct adult
		{
			public static Backstory feisty;
			public static Backstory curious;
			public static Backstory tender;
			public static Backstory chatty;
			public static Backstory broken;
			public static Backstory homekeeper;
		};

		public static void init()
		{
			{
				Backstory bs = new Backstory();
				bs.identifier = ""; // identifier will be set by ResolveReferences
				MiscTranslationDef MTdef = DefDatabase<MiscTranslationDef>.GetNamedSilentFail("rjw_vatgrown_sex_slave");
				if (MTdef != null)
				{
					bs.SetTitle(MTdef.label, MTdef.label);
					bs.SetTitleShort(MTdef.stringA, MTdef.stringA);
					bs.baseDesc = MTdef.description;
				}
				else
				{
					bs.SetTitle("Vat-Grown Sex Slave", "Vat-Grown Sex Slave");
					bs.SetTitleShort("SexSlave", "SexSlave");
					bs.baseDesc = "SexSlave Nymph";
				}
				// bs.skillGains = new Dictionary<string, int> ();
				bs.skillGainsResolved.Add(SkillDefOf.Social, 8);
				// bs.skillGainsResolved = new Dictionary<SkillDef, int> (); // populated by ResolveReferences
				bs.workDisables = WorkTags.Intellectual;
				bs.requiredWorkTags = WorkTags.None;
				bs.slot = BackstorySlot.Childhood;
				bs.spawnCategories = new List<string>() { "rjw_nymphsCategory", "Slave" }; // Not necessary (I Think)
				Unprivater.SetProtectedValue("bodyTypeGlobal", bs, "Thin");
				Unprivater.SetProtectedValue("bodyTypeFemale", bs, "Thin");
				Unprivater.SetProtectedValue("bodyTypeMale", bs, "Thin");
				Unprivater.SetProtectedValue("bodyTypeGlobalResolved", bs, BodyTypeDefOf.Thin);
				Unprivater.SetProtectedValue("bodyTypeFemaleResolved", bs, BodyTypeDefOf.Thin);
				Unprivater.SetProtectedValue("bodyTypeMaleResolved", bs, BodyTypeDefOf.Thin);
				//bs.bodyTypeFemale = BodyType.Female;
				//bs.bodyTypeMale = BodyType.Thin;
				bs.forcedTraits = new List<TraitEntry>();
				bs.forcedTraits.Add(new TraitEntry(xxx.nymphomaniac, 0));
				bs.disallowedTraits = new List<TraitEntry>();
				bs.disallowedTraits.Add(new TraitEntry(TraitDefOf.DislikesMen, 0));
				bs.disallowedTraits.Add(new TraitEntry(TraitDefOf.DislikesWomen, 0));
				bs.disallowedTraits.Add(new TraitEntry(TraitDefOf.TooSmart, 0));
				bs.shuffleable = true;
				bs.ResolveReferences();
				BackstoryDatabase.AddBackstory(bs);
				child.vatgrown_sex_slave = bs;
				//--ModLog.Message("nymph_backstories::init() succeed0");
			}

			{
				Backstory bs = new Backstory();
				bs.identifier = "";
				MiscTranslationDef MTdef = DefDatabase<MiscTranslationDef>.GetNamedSilentFail("rjw_feisty");
				if (MTdef != null)
				{
					bs.SetTitle(MTdef.label, MTdef.label);
					bs.SetTitleShort(MTdef.stringA, MTdef.stringA);
					bs.baseDesc = MTdef.description;
				}
				else
				{
					bs.SetTitle("Feisty Nymph", "Feisty Nymph");
					bs.SetTitleShort("Nymph", "Nymph");
					bs.baseDesc = "Feisty Nymph";
				}
				bs.skillGainsResolved.Add(SkillDefOf.Social, -3);
				bs.skillGainsResolved.Add(SkillDefOf.Shooting, 2);
				bs.skillGainsResolved.Add(SkillDefOf.Melee, 9);
				bs.workDisables = (WorkTags.Cleaning | WorkTags.Animals | WorkTags.Caring | WorkTags.Artistic | WorkTags.ManualSkilled);
				bs.requiredWorkTags = WorkTags.None;
				bs.slot = BackstorySlot.Adulthood;
				bs.spawnCategories = new List<string>() { "rjw_nymphsCategory", "Slave" }; // Not necessary (I Think)
				Unprivater.SetProtectedValue("bodyTypeGlobal", bs, "Thin");
				Unprivater.SetProtectedValue("bodyTypeFemale", bs, "Thin");
				Unprivater.SetProtectedValue("bodyTypeMale", bs, "Thin");
				Unprivater.SetProtectedValue("bodyTypeGlobalResolved", bs, BodyTypeDefOf.Thin);
				Unprivater.SetProtectedValue("bodyTypeFemaleResolved", bs, BodyTypeDefOf.Thin);
				Unprivater.SetProtectedValue("bodyTypeMaleResolved", bs, BodyTypeDefOf.Thin);
				bs.forcedTraits = new List<TraitEntry>();
				bs.forcedTraits.Add(new TraitEntry(xxx.nymphomaniac, 0));
				bs.disallowedTraits = new List<TraitEntry>();
				bs.disallowedTraits.Add(new TraitEntry(TraitDefOf.DislikesMen, 0));
				bs.disallowedTraits.Add(new TraitEntry(TraitDefOf.DislikesWomen, 0));
				bs.disallowedTraits.Add(new TraitEntry(TraitDefOf.TooSmart, 0));
				bs.shuffleable = true;
				bs.ResolveReferences();
				BackstoryDatabase.AddBackstory(bs);
				adult.feisty = bs;
				//--ModLog.Message("nymph_backstories::init() succeed1");
			}

			{
				Backstory bs = new Backstory();
				bs.identifier = "";
				MiscTranslationDef MTdef = DefDatabase<MiscTranslationDef>.GetNamedSilentFail("rjw_curious");
				if (MTdef != null)
				{
					bs.SetTitle(MTdef.label, MTdef.label);
					bs.SetTitleShort(MTdef.stringA, MTdef.stringA);
					bs.baseDesc = MTdef.description;
				}
				else
				{
					bs.SetTitle("Curious Nymph", "Curious Nymph");
					bs.SetTitleShort("Nymph", "Nymph");
					bs.baseDesc = "Curious Nymph";
				}
				bs.skillGainsResolved.Add(SkillDefOf.Construction, 2);
				bs.skillGainsResolved.Add(SkillDefOf.Crafting, 6);
				bs.workDisables = (WorkTags.Animals | WorkTags.Artistic | WorkTags.Caring | WorkTags.Cooking | WorkTags.Mining | WorkTags.PlantWork | WorkTags.Violent | WorkTags.ManualDumb);
				bs.requiredWorkTags = WorkTags.None;
				bs.slot = BackstorySlot.Adulthood;
				bs.spawnCategories = new List<string>() { "rjw_nymphsCategory", "Slave" }; // Not necessary (I Think)
				Unprivater.SetProtectedValue("bodyTypeGlobal", bs, "Thin");
				Unprivater.SetProtectedValue("bodyTypeFemale", bs, "Thin");
				Unprivater.SetProtectedValue("bodyTypeMale", bs, "Thin");
				Unprivater.SetProtectedValue("bodyTypeGlobalResolved", bs, BodyTypeDefOf.Thin);
				Unprivater.SetProtectedValue("bodyTypeFemaleResolved", bs, BodyTypeDefOf.Thin);
				Unprivater.SetProtectedValue("bodyTypeMaleResolved", bs, BodyTypeDefOf.Thin);
				bs.forcedTraits = new List<TraitEntry>();
				bs.forcedTraits.Add(new TraitEntry(xxx.nymphomaniac, 0));
				bs.disallowedTraits = new List<TraitEntry>();
				bs.disallowedTraits.Add(new TraitEntry(TraitDefOf.DislikesMen, 0));
				bs.disallowedTraits.Add(new TraitEntry(TraitDefOf.DislikesWomen, 0));
				bs.disallowedTraits.Add(new TraitEntry(TraitDefOf.TooSmart, 0));
				bs.shuffleable = true;
				bs.ResolveReferences();
				BackstoryDatabase.AddBackstory(bs);
				adult.curious = bs;
				//--ModLog.Message("nymph_backstories::init() succeed2");
			}

			{
				Backstory bs = new Backstory();
				bs.identifier = "";
				MiscTranslationDef MTdef = DefDatabase<MiscTranslationDef>.GetNamedSilentFail("rjw_tender");
				if (MTdef != null)
				{
					bs.SetTitle(MTdef.label, MTdef.label);
					bs.SetTitleShort(MTdef.stringA, MTdef.stringA);
					bs.baseDesc = MTdef.description;
				}
				else
				{
					bs.SetTitle("Tender Nymph", "Tender Nymph");
					bs.SetTitleShort("Nymph", "Nymph");
					bs.baseDesc = "Tender Nymph";
				}
				bs.skillGainsResolved.Add(SkillDefOf.Medicine, 4);
				bs.workDisables = (WorkTags.Animals | WorkTags.Artistic | WorkTags.Hauling | WorkTags.Violent | WorkTags.ManualSkilled);
				bs.requiredWorkTags = WorkTags.None;
				bs.slot = BackstorySlot.Adulthood;
				bs.spawnCategories = new List<string>() { "rjw_nymphsCategory", "Slave" }; // Not necessary (I Think)
				Unprivater.SetProtectedValue("bodyTypeGlobal", bs, "Thin");
				Unprivater.SetProtectedValue("bodyTypeFemale", bs, "Thin");
				Unprivater.SetProtectedValue("bodyTypeMale", bs, "Thin");
				Unprivater.SetProtectedValue("bodyTypeGlobalResolved", bs, BodyTypeDefOf.Thin);
				Unprivater.SetProtectedValue("bodyTypeFemaleResolved", bs, BodyTypeDefOf.Thin);
				Unprivater.SetProtectedValue("bodyTypeMaleResolved", bs, BodyTypeDefOf.Thin);
				bs.forcedTraits = new List<TraitEntry>();
				bs.forcedTraits.Add(new TraitEntry(xxx.nymphomaniac, 0));
				bs.disallowedTraits = new List<TraitEntry>();
				bs.disallowedTraits.Add(new TraitEntry(TraitDefOf.DislikesMen, 0));
				bs.disallowedTraits.Add(new TraitEntry(TraitDefOf.DislikesWomen, 0));
				bs.disallowedTraits.Add(new TraitEntry(TraitDefOf.TooSmart, 0));
				bs.shuffleable = true;
				bs.ResolveReferences();
				BackstoryDatabase.AddBackstory(bs);
				adult.tender = bs;
				//--ModLog.Message("nymph_backstories::init() succeed3");
			}

			{
				Backstory bs = new Backstory();
				bs.identifier = "";
				MiscTranslationDef MTdef = DefDatabase<MiscTranslationDef>.GetNamedSilentFail("rjw_chatty");
				if (MTdef != null)
				{
					bs.SetTitle(MTdef.label, MTdef.label);
					bs.SetTitleShort(MTdef.stringA, MTdef.stringA);
					bs.baseDesc = MTdef.description;
				}
				else
				{
					bs.SetTitle("Chatty Nymph", "Chatty Nymph");
					bs.SetTitleShort("Nymph", "Nymph");
					bs.baseDesc = "Chatty Nymph";
				}
				bs.skillGainsResolved.Add(SkillDefOf.Social, 6);
				bs.workDisables = (WorkTags.Animals | WorkTags.Caring | WorkTags.Artistic | WorkTags.Violent | WorkTags.ManualDumb | WorkTags.ManualSkilled);
				bs.requiredWorkTags = WorkTags.None;
				bs.slot = BackstorySlot.Adulthood;
				bs.spawnCategories = new List<string>() { "rjw_nymphsCategory", "Slave" }; // Not necessary (I Think)
				Unprivater.SetProtectedValue("bodyTypeGlobal", bs, "Thin");
				Unprivater.SetProtectedValue("bodyTypeFemale", bs, "Thin");
				Unprivater.SetProtectedValue("bodyTypeMale", bs, "Thin");
				Unprivater.SetProtectedValue("bodyTypeGlobalResolved", bs, BodyTypeDefOf.Thin);
				Unprivater.SetProtectedValue("bodyTypeFemaleResolved", bs, BodyTypeDefOf.Thin);
				Unprivater.SetProtectedValue("bodyTypeMaleResolved", bs, BodyTypeDefOf.Thin);
				bs.forcedTraits = new List<TraitEntry>();
				bs.forcedTraits.Add(new TraitEntry(xxx.nymphomaniac, 0));
				bs.disallowedTraits = new List<TraitEntry>();
				bs.disallowedTraits.Add(new TraitEntry(TraitDefOf.DislikesMen, 0));
				bs.disallowedTraits.Add(new TraitEntry(TraitDefOf.DislikesWomen, 0));
				bs.disallowedTraits.Add(new TraitEntry(TraitDefOf.TooSmart, 0));
				bs.shuffleable = true;
				bs.ResolveReferences();
				BackstoryDatabase.AddBackstory(bs);
				adult.chatty = bs;
				//--ModLog.Message("nymph_backstories::init() succeed4");
			}

			{
				Backstory bs = new Backstory();
				bs.identifier = "";
				MiscTranslationDef MTdef = DefDatabase<MiscTranslationDef>.GetNamedSilentFail("rjw_broken");
				if (MTdef != null)
				{
					bs.SetTitle(MTdef.label, MTdef.label);
					bs.SetTitleShort(MTdef.stringA, MTdef.stringA);
					bs.baseDesc = MTdef.description;
				}
				else
				{
					bs.SetTitle("Broken Nymph", "Broken Nymph");
					bs.SetTitleShort("Nymph", "Nymph");
					bs.baseDesc = "Broken Nymph";
				}
				bs.skillGainsResolved.Add(SkillDefOf.Social, -5);
				bs.skillGainsResolved.Add(SkillDefOf.Artistic, 8);
				bs.workDisables = (WorkTags.Cleaning | WorkTags.Animals | WorkTags.Caring | WorkTags.Violent | WorkTags.ManualSkilled);
				bs.requiredWorkTags = WorkTags.None;
				bs.slot = BackstorySlot.Adulthood;
				bs.spawnCategories = new List<string>() { "rjw_nymphsCategory", "Slave" }; // Not necessary (I Think)
				Unprivater.SetProtectedValue("bodyTypeGlobal", bs, "Thin");
				Unprivater.SetProtectedValue("bodyTypeFemale", bs, "Thin");
				Unprivater.SetProtectedValue("bodyTypeMale", bs, "Thin");
				Unprivater.SetProtectedValue("bodyTypeGlobalResolved", bs, BodyTypeDefOf.Thin);
				Unprivater.SetProtectedValue("bodyTypeFemaleResolved", bs, BodyTypeDefOf.Thin);
				Unprivater.SetProtectedValue("bodyTypeMaleResolved", bs, BodyTypeDefOf.Thin);
				bs.forcedTraits = new List<TraitEntry>();
				bs.forcedTraits.Add(new TraitEntry(xxx.nymphomaniac, 0));
				bs.disallowedTraits = new List<TraitEntry>();
				bs.disallowedTraits.Add(new TraitEntry(TraitDefOf.DislikesMen, 0));
				bs.disallowedTraits.Add(new TraitEntry(TraitDefOf.DislikesWomen, 0));
				bs.disallowedTraits.Add(new TraitEntry(TraitDefOf.TooSmart, 0));
				bs.shuffleable = true;
				bs.ResolveReferences();
				BackstoryDatabase.AddBackstory(bs);
				adult.broken = bs;
				//--ModLog.Message("nymph_backstories::init() succeed5");
			}

			//{
			//	Backstory bs = new Backstory();
			//	bs.identifier = "";
			//	MiscTranslationDef MTdef = DefDatabase<MiscTranslationDef>.GetNamedSilentFail("rjw_homekeeper");
			//	if (MTdef != null)
			//	{
			//		bs.SetTitle(MTdef.label, MTdef.label);
			//		bs.SetTitleShort(MTdef.stringA, MTdef.stringA);
			//		bs.baseDesc = MTdef.description;
			//	}
			//	else
			//	{
			//		bs.SetTitle("Home keeper Nymph", "Home keeper  Nymph");
			//		bs.SetTitleShort("Nymph", "Nymph");
			//		bs.baseDesc = "Home keeper Nymph";
			//	}
			//	bs.skillGainsResolved.Add(SkillDefOf.Cooking, 8);
			//	bs.workDisables = (WorkTags.Animals | WorkTags.Caring | WorkTags.Violent | WorkTags.Artistic | WorkTags.Crafting | WorkTags.PlantWork | WorkTags.Mining);
			//	bs.requiredWorkTags = (WorkTags.Cleaning | WorkTags.Cooking);
			//	bs.slot = BackstorySlot.Adulthood;
			//	bs.spawnCategories = new List<string>() { "rjw_nymphsCategory", "Slave" }; // Not necessary (I Think)
			//	Unprivater.SetProtectedValue("bodyTypeGlobal", bs, "Thin");
			//	Unprivater.SetProtectedValue("bodyTypeFemale", bs, "Thin");
			//	Unprivater.SetProtectedValue("bodyTypeMale", bs, "Thin");
			//	Unprivater.SetProtectedValue("bodyTypeGlobalResolved", bs, BodyTypeDefOf.Thin);
			//	Unprivater.SetProtectedValue("bodyTypeFemaleResolved", bs, BodyTypeDefOf.Thin);
			//	Unprivater.SetProtectedValue("bodyTypeMaleResolved", bs, BodyTypeDefOf.Thin);
			//	bs.forcedTraits = new List<TraitEntry>();
			//	bs.forcedTraits.Add(new TraitEntry(xxx.nymphomaniac, 0));
			//	bs.disallowedTraits = new List<TraitEntry>();
			//	bs.disallowedTraits.Add(new TraitEntry(TraitDefOf.DislikesMen, 0));
			//	bs.disallowedTraits.Add(new TraitEntry(TraitDefOf.DislikesWomen, 0));
			//	bs.disallowedTraits.Add(new TraitEntry(TraitDefOf.TooSmart, 0));
			//	bs.shuffleable = true;
			//	bs.ResolveReferences();
			//	BackstoryDatabase.AddBackstory(bs);
			//	adult.homekeeper = bs;
			//	//--ModLog.Message("nymph_backstories::init() succeed6");
			//}
		}

		public static nymph_passion_chances get_passion_chances(Backstory child_bs, Backstory adult_bs, SkillDef skill_def)
		{
			var maj = 0.0f;
			var min = 0.0f;

			if (adult_bs == adult.feisty)
			{
				if (skill_def == SkillDefOf.Melee) { maj = 0.50f; min = 1.00f; }
				else if (skill_def == SkillDefOf.Shooting) { maj = 0.25f; min = 0.75f; }
				else if (skill_def == SkillDefOf.Social) { maj = 0.10f; min = 0.67f; }
			}
			else if (adult_bs == adult.curious)
			{
				if (skill_def == SkillDefOf.Construction) { maj = 0.15f; min = 0.40f; }
				else if (skill_def == SkillDefOf.Crafting) { maj = 0.50f; min = 1.00f; }
				else if (skill_def == SkillDefOf.Social) { maj = 0.20f; min = 1.00f; }
			}
			else if (adult_bs == adult.tender)
			{
				if (skill_def == SkillDefOf.Medicine) { maj = 0.20f; min = 0.60f; }
				else if (skill_def == SkillDefOf.Social) { maj = 0.50f; min = 1.00f; }
			}
			else if (adult_bs == adult.chatty)
			{
				if (skill_def == SkillDefOf.Social) { maj = 1.00f; min = 1.00f; }
			}
			else if (adult_bs == adult.broken)
			{
				if (skill_def == SkillDefOf.Artistic) { maj = 0.50f; min = 1.00f; }
				else if (skill_def == SkillDefOf.Social) { maj = 0.00f; min = 0.33f; }
			}
			else if (adult_bs == adult.homekeeper)
			{
				if (skill_def == SkillDefOf.Cooking) { maj = 0.50f; min = 1.00f; }
				else if (skill_def == SkillDefOf.Social) { maj = 0.00f; min = 0.33f; }
			}

			return new nymph_passion_chances(maj, min);
		}

		// Randomly chooses backstories and traits for a nymph
		[SyncMethod]
		public static nymph_story generate()
		{
			var tr = new nymph_story();

			tr.child = child.vatgrown_sex_slave;

			tr.traits = new List<Trait>();
			tr.traits.Add(new Trait(xxx.nymphomaniac, 0, true));

			//Rand.PopState();
			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
			var beauty = 0;
			var rv2 = Rand.Value;
			var story = (BackstoryDatabase.allBackstories.Where(x => x.Value.spawnCategories.Contains("rjw_nymphsCategory") && x.Value.slot == BackstorySlot.Adulthood)).ToList().RandomElement().Value;

			if (story == adult.feisty)
			{
				tr.adult = adult.feisty;
				beauty = Rand.RangeInclusive(0, 2);
				if (rv2 < 0.33)
					tr.traits.Add(new Trait(TraitDefOf.Brawler));
				else if (rv2 < 0.67)
					tr.traits.Add(new Trait(TraitDefOf.Bloodlust));
				else
					tr.traits.Add(new Trait(xxx.rapist));
			}
			else if (story == adult.curious)
			{
				tr.adult = adult.curious;
				beauty = Rand.RangeInclusive(0, 2);
				if (rv2 < 0.33)
					tr.traits.Add(new Trait(TraitDefOf.Transhumanist));
			}
			else if (story == adult.tender)
			{
				tr.adult = adult.tender;
				beauty = Rand.RangeInclusive(1, 2);
				if (rv2 < 0.50)
					tr.traits.Add(new Trait(TraitDefOf.Kind));
			}
			else if (story == adult.chatty)
			{
				tr.adult = adult.chatty;
				beauty = 2;
				if (rv2 < 0.33)
					tr.traits.Add(new Trait(TraitDefOf.Greedy));
			}
			else if (story == adult.broken)
			{
				tr.adult = adult.broken;
				beauty = Rand.RangeInclusive(0, 2);
				if (rv2 < 0.33)
					tr.traits.Add(new Trait(TraitDefOf.DrugDesire, 1));
				else if (rv2 < 0.67)
					tr.traits.Add(new Trait(TraitDefOf.DrugDesire, 2));
			}
			//else
			//{
			//	tr.adult = adult.homekeeper;
			//	beauty = Rand.RangeInclusive(1, 2);
			//	if (rv2 < 0.33)
			//		tr.traits.Add(new Trait(TraitDefOf.Kind));
			//}

			if (beauty > 0)
				tr.traits.Add(new Trait(TraitDefOf.Beauty, beauty, false));

			return tr;
		}
	}
}

repo_name: Euclidean/rjw

license: mit


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

namespace rjw
{
	public static class Nymph_Generator
	{
		/// <summary>
		/// Returns true if the given pawnGenerationRequest is for a nymph pawnKind.
		/// </summary>
		public static bool IsNymph(PawnGenerationRequest pawnGenerationRequest)
		{
			return pawnGenerationRequest.KindDef != null && pawnGenerationRequest.KindDef.defName.Contains("Nymph");
		}

		private static bool is_trait_conflicting_or_duplicate(Pawn pawn, Trait t)
		{
			foreach (var existing in pawn.story.traits.allTraits)
				if ((existing.def == t.def) || (t.def.ConflictsWith(existing)))
					return true;
			return false;
		}

		public static bool IsNymphBodyType(Pawn pawn)
		{
			return pawn.story.bodyType == BodyTypeDefOf.Female || pawn.story.bodyType == BodyTypeDefOf.Thin;
		}

		[SyncMethod]
		public static Gender RandomNymphGender()
		{
			//with males 100% its still 99%, coz im  to lazy to fix it
			//float rnd = Rand.Value;
			//Rand.PopState();
			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
			float chance = RJWSettings.male_nymph_chance;

			Gender Pawngender = Rand.Chance(chance) ? Gender.Male: Gender.Female;
			//ModLog.Message(" setnymphsex: " + (rnd < chance) + " rnd:" + rnd + " chance:" + chance);
			//ModLog.Message(" setnymphsex: " + Pawngender);
			return Pawngender;
		}

		/// <summary>
		/// Replaces a pawn's backstory and traits to turn it into a nymph
		/// </summary>
		[SyncMethod]
		public static void set_story(Pawn pawn)
		{
			var gen_sto = nymph_backstories.generate();

			pawn.story.childhood = gen_sto.child;
			pawn.story.adulthood = gen_sto.adult;

			// add broken body to broken nymph
			if (pawn.story.adulthood == nymph_backstories.adult.broken)
			{
				pawn.health.AddHediff(xxx.feelingBroken);
				//Rand.PopState();
				//Rand.PushState(RJW_Multiplayer.PredictableSeed());
				(pawn.health.hediffSet.GetFirstHediffOfDef(xxx.feelingBroken)).Severity = Rand.Range(0.4f, 1.0f);
			}

			//The mod More Trait Slots will adjust the max number of traits pawn can get, and therefore,
			//I need to collect pawns' traits and assign other_traits back to the pawn after adding the nymph_story traits.
			Stack<Trait> other_traits = new Stack<Trait>();
			int numberOfTotalTraits = 0;
			if (!pawn.story.traits.allTraits.NullOrEmpty())
			{
				foreach (Trait t in pawn.story.traits.allTraits)
				{
					other_traits.Push(t);
					++numberOfTotalTraits;
				}
			}

			pawn.story.traits.allTraits.Clear();
			var trait_count = 0;
			foreach (var t in gen_sto.traits)
			{
				pawn.story.traits.GainTrait(t);
				++trait_count;
			}
			while (trait_count < numberOfTotalTraits)
			{
				Trait t = other_traits.Pop();
				if (!is_trait_conflicting_or_duplicate(pawn, t))
					pawn.story.traits.GainTrait(t);
				++trait_count;
			}
		}

		[SyncMethod]
		private static int sum_previous_gains(SkillDef def, Pawn_StoryTracker sto, Pawn_AgeTracker age)
		{
			int total_gain = 0;
			int gain;

			// Gains from backstories
			if (sto.childhood.skillGainsResolved.TryGetValue(def, out gain))
				total_gain += gain;
			if (sto.adulthood.skillGainsResolved.TryGetValue(def, out gain))
				total_gain += gain;

			// Gains from traits
			foreach (var trait in sto.traits.allTraits)
				if (trait.CurrentData.skillGains.TryGetValue(def, out gain))
					total_gain += gain;

			// Gains from age
			//Rand.PopState();
			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
			var rgain = Rand.Value * (float)total_gain * 0.35f;
			var age_factor = Mathf.Clamp01((age.AgeBiologicalYearsFloat - 17.0f) / 10.0f); // Assume nymphs are 17~27
			total_gain += (int)(age_factor * rgain);

			return Mathf.Clamp(total_gain, 0, 20);
		}

		/// <summary>
		/// Set a nymph's initial skills & passions from backstory, traits, and age
		/// </summary>
		[SyncMethod]
		public static void set_skills(Pawn pawn)
		{
			foreach (var skill_def in DefDatabase<SkillDef>.AllDefsListForReading)
			{
				var rec = pawn.skills.GetSkill(skill_def);
				if (!rec.TotallyDisabled)
				{
					//Rand.PopState();
					//Rand.PushState(RJW_Multiplayer.PredictableSeed());
					rec.Level = sum_previous_gains(skill_def, pawn.story, pawn.ageTracker);
					rec.xpSinceLastLevel = rec.XpRequiredForLevelUp * Rand.Range(0.10f, 0.90f);

					var pas_cha = nymph_backstories.get_passion_chances(pawn.story.childhood, pawn.story.adulthood, skill_def);
					var rv = Rand.Value;
					if (rv < pas_cha.major) rec.passion = Passion.Major;
					else if (rv < pas_cha.minor) rec.passion = Passion.Minor;
					else rec.passion = Passion.None;
				}
				else
					rec.passion = Passion.None;
			}
		}

		[SyncMethod]
		public static PawnKindDef GetFixedNymphPawnKindDef()
		{
			var def = DefDatabase<PawnKindDef>.AllDefs.Where(x => x.defName.Contains("Nymph")).RandomElement();
			//var def = PawnKindDef.Named("Nymph");
			// This is 18 in the xml but something is overwriting it to 5.
			//def.minGenerationAge = 18;
			return def;
		}

		[SyncMethod]
		public static Pawn GenerateNymph(IntVec3 around_loc, ref Map map, Faction faction = null)
		{
			// Most of the special properties of nymphs are in harmony patches to PawnGenerator.
			PawnGenerationRequest request = new PawnGenerationRequest(
				kind: GetFixedNymphPawnKindDef(),
				faction: faction,
				tile: map.Tile,
				forceGenerateNewPawn: true,
				canGeneratePawnRelations: true,
				colonistRelationChanceFactor: 0.0f,
				inhabitant: true,
				relationWithExtraPawnChanceFactor: 0
				);

			//Rand.PopState();
			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
			Pawn pawn = PawnGenerator.GeneratePawn(request);
			//Log.Message(""+ pawn.Faction);
			//IntVec3 spawn_loc = CellFinder.RandomClosewalkCellNear(around_loc, map, 6);//RandomSpawnCellForPawnNear could be an alternative
			//GenSpawn.Spawn(pawn, spawn_loc, map);
			return pawn;
		}
	}
}


code:
using System;
using RimWorld;
using Verse;

namespace rjw
{
	public class BackstoryDef : Def
	{
		public BackstoryDef()
		{
			this.slot = BackstorySlot.Childhood;
		}

		public static BackstoryDef Named(string defName)
		{
			return DefDatabase<BackstoryDef>.GetNamed(defName, true);
		}

		public override void ResolveReferences()
		{
			base.ResolveReferences();
			if (BackstoryDatabase.allBackstories.ContainsKey(this.defName))
			{
				ModLog.Error("BackstoryDatabase already contains: " + this.defName);
				return;
			}
			{
				//Log.Warning("BackstoryDatabase does not contains: " + this.defName);
			}
			if (!this.title.NullOrEmpty())
			{
				Backstory backstory = new Backstory();
				backstory.SetTitle(this.title, this.titleFemale);
				backstory.SetTitleShort(this.titleShort, this.titleFemaleShort);
				backstory.baseDesc = this.baseDescription;
				backstory.slot = this.slot;
				backstory.spawnCategories.Add(this.categoryName);
				backstory.ResolveReferences();
				backstory.PostLoad();
				backstory.identifier = this.defName;
				BackstoryDatabase.allBackstories.Add(backstory.identifier, backstory);
				//Log.Warning("BackstoryDatabase added: " + backstory.identifier);
				//Log.Warning("BackstoryDatabase added: " + backstory.spawnCategories.ToCommaList());
			}
		}

		public string baseDescription;

		public string title;

		public string titleShort;

		public string titleFemale;

		public string titleFemaleShort;

		public string categoryName;

		public BackstorySlot slot;
	}
}


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

namespace rjw
{
	//MicroComputer
	internal class Hediff_MicroComputer : Hediff_MechImplants
	{
		protected int nextEventTick = 60000;

		public override void ExposeData()
		{
			base.ExposeData();
			Scribe_Values.Look<int>(ref this.nextEventTick, "nextEventTick", 60000, false);
		}

		[SyncMethod]
		public override void PostMake()
		{
			base.PostMake();
			//Rand.PopState();
			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
			nextEventTick = Rand.Range(mcDef.minEventInterval, mcDef.maxEventInterval);
		}

		[SyncMethod]
		public override void Tick()
		{
			base.Tick();
			if (this.pawn.IsHashIntervalTick(1000))
			{
				if (this.ageTicks >= nextEventTick)
				{
					HediffDef randomEffectDef = DefDatabase<HediffDef>.GetNamed(randomEffect);
					if (randomEffectDef != null)
					{
						pawn.health.AddHediff(randomEffectDef);
					}
					else
					{
						//--ModLog.Message("" + this.GetType().ToString() + "::Tick() - There is no Random Effect");
					}
					this.ageTicks = 0;
				}
			}
		}

		protected HediffDef_MechImplants mcDef
		{
			get
			{
				return ((HediffDef_MechImplants)def);
			}
		}

		protected List<string> randomEffects
		{
			get
			{
				return mcDef.randomHediffDefs;
			}
		}

		protected string randomEffect
		{
			get
			{
				//Rand.PopState();
				//Rand.PushState(RJW_Multiplayer.PredictableSeed());
				return randomEffects.RandomElement<string>();
			}
		}
	}
}


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

namespace rjw
{
	[StaticConstructorOnStartup]
	internal class HediffDef_EnemyImplants : HediffDef
	{
		//single parent eggs
		public string parentDef = "";
		//multiparent eggs
		public List<string> parentDefs = new List<string>();

		//for implanting eggs
		public bool IsParent(string defnam)
		{
			return
					//predefined parent eggs
					parentDef == defnam ||
					parentDefs.Contains(defnam) ||
					//dynamic egg
					(parentDef == "Unknown" && defnam == "Unknown" && RJWPregnancySettings.egg_pregnancy_implant_anyone);
		}
	}

	[StaticConstructorOnStartup]
	internal class HediffDef_InsectEgg : HediffDef_EnemyImplants
	{
		//this is filled from xml
		//1 day = 60000 ticks
		public float eggsize = 1;
		public bool selffertilized = false;

		public List<string> childrenDefs = new List<string>();
		public string UnFertEggDef;
		public string FertEggDef;
	}

	[StaticConstructorOnStartup]
	internal class HediffDef_MechImplants : HediffDef_EnemyImplants
	{
		public List<string> randomHediffDefs = new List<string>();
		public int minEventInterval = 30000;
		public int maxEventInterval = 90000;

		public List<string> childrenDefs = new List<string>();
	}
}


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

namespace rjw
{
	public abstract class Hediff_BasePregnancy : HediffWithComps
	{
		///<summary>
		///This hediff class simulates pregnancy.
		///</summary>

		//Static fields
		private const int MiscarryCheckInterval = 1000;
		protected const int TicksPerDay = 60000;

		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
		public List<Pawn> babies;
		///Reference to daddy, goes null sometimes
		public Pawn father;
		///Is pregnancy visible?
		public bool is_discovered;
		public bool ShouldMiscarry = false;
		public bool ImmortalMiscarry = false;
		///Is pregnancy type checked?
		public bool is_checked = false;
		///Mechanoid pregnancy, false - spawn aggressive mech
		public bool is_hacked = false;
		///Contractions duration, effectively additional hediff stage, a dirty hack to make birthing process notable
		public int contractions;
		///Gestation progress per tick
		public float progress_per_tick;
		//public string last_name
		//{
		//	get
		//	{
		//		string last_name = "";
		//		if (xxx.is_human(father))
		//			last_name = NameTriple.FromString(father.Name.ToStringFull).Last;
		//		else if (xxx.is_human(pawn))
		//			last_name = NameTriple.FromString(pawn.Name.ToStringFull).Last;
		//		return last_name;
		//	}
		//}

		//public float skin_whiteness
		//{
		//	get
		//	{
		//		float skin_whiteness = Rand.Range(0, 1);
		//		if (xxx.has_traits(pawn) && pawn.RaceProps.Humanlike)
		//		{
		//			skin_whiteness = pawn.story.melanin;
		//		}
		//		if (father != null && xxx.has_traits(father) && father.RaceProps.Humanlike)
		//		{
		//			skin_whiteness = Rand.Range(skin_whiteness, father.story.melanin);
		//		}
		//		return skin_whiteness;
		//	}
		//}

		//public List<Trait> traitpool
		//{
		//	get
		//	{
		//		List<Trait> traitpool = new List<Trait>();
		//		if (xxx.has_traits(pawn) && pawn.RaceProps.Humanlike)
		//		{
		//			foreach (Trait momtrait in pawn.story.traits.allTraits)
		//			{
		//				if (!RJWPregnancySettings.trait_filtering_enabled || !non_genetic_traits.Contains(momtrait.def.defName))
		//					traitpool.Add(momtrait);
		//			}
		//		}
		//		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);
		//			}
		//		}
		//		return traitpool;
		//	}
		//}

		//
		// 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)
				{
					//this errors somewhy,
					//ModLog.Message(" 1 " + h.Part.IsCorePart);
					//ModLog.Message(" 2 " + h.Part.parent.IsCorePart);
					//if (h.Part.IsCorePart || h.Part.parent.IsCorePart)
					if (h is Hediff_Injury && (!h.IsPermanent() || !h.IsTended()))
					{
						if (h.Part != null)
							if (h.Part.IsCorePart || h.Part.parent.IsCorePart)
								num += h.Severity;
					}
				}
				List<Hediff_MissingPart> missingPartsCommonAncestors = pawn.health.hediffSet.GetMissingPartsCommonAncestors();
				foreach (Hediff_MissingPart mp in missingPartsCommonAncestors)
				{
					if (mp.IsFreshNonSolidExtremity)
					{
						if (mp.Part != null)
							if (mp.Part.IsCorePart || mp.Part.parent.IsCorePart)
								num += mp.Part.def.GetMaxHealth(pawn);
					}
				}
				return num > 38 * pawn.RaceProps.baseHealthScale;
			}
		}

		/// <summary>
		/// Indicates pregnancy can be aborted using usual means.
		/// </summary>
		public virtual bool canBeAborted
		{
			get
			{
				return true;
			}
		}

		/// <summary>
		/// Indicates pregnancy can be miscarried.
		/// </summary>
		public virtual bool canMiscarry
		{
			get
			{
				return true;
			}
		}

		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;

			//(debug->add heddif)
			//init empty preg, instabirth, cause error during birthing
			//Initialize(pawn, Trytogetfather(ref pawn));
		}

		public override bool Visible => is_discovered;

		public virtual 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()).CapitalizeFirst();
					string key2 = "RJW_PregnantText";

					string message_text = TranslatorFormattedStringExtensions.Translate(key2, pawn.LabelIndefinite()).CapitalizeFirst();
					Find.LetterStack.ReceiveLetter(message_title, message_text, LetterDefOf.NeutralEvent, pawn, null);

				}
				else
				{
					PregnancyMessage();
				}
			}
		}

		public virtual 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 virtual void Miscarry()
		{
			if (!babies.NullOrEmpty())
				foreach (Pawn baby in babies)
				{
					baby.Discard();
				}
			pawn.health?.RemoveHediff(this);
		}

		/// <summary>
		/// Called on abortion (noy implemented yet)
		/// </summary>
		public virtual void Abort()
		{
			Miscarry();
		}

		/// <summary>
		/// Mechanoids can remove pregnancy
		/// </summary>
		public virtual void Kill()
		{
			Miscarry();
		}

		[SyncMethod]
		public Pawn partstospawn(Pawn baby, Pawn mother, Pawn dad)
		{
			//decide what parts to inherit
			//default use own parts
			Pawn partstospawn = baby;
			//Rand.PopState();
			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
			//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;

			//ModLog.Message(" Pregnancy partstospawn " + partstospawn);
			return partstospawn;
		}

		[SyncMethod]
		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;

			//ModLog.Message(" Pregnancy spawnfutachild " + futachance);
			//Rand.PopState();
			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
			//theres 1% change baby will be futa
			//bug ... or ... feature ... nature finds a way
			return (Rand.Range(0, 100) <= futachance);
		}

		[SyncMethod]
		public static Pawn Trytogetfather(ref Pawn mother)
		{
			//birthing with debug has no father
			//Postmake.initialize() has no father
			ModLog.Warning("Hediff_BasePregnancy::Trytogetfather() - debug? no father defined, trying to add one");

			Pawn pawn = mother;
			Pawn father = null;

			//possible fathers
			List<Pawn> partners = pawn.relations.RelatedPawns.Where(x =>
					Genital_Helper.has_penis_fertile(x) && !x.Dead &&
					(pawn.relations.DirectRelationExists(PawnRelationDefOf.Lover, x) ||
					pawn.relations.DirectRelationExists(PawnRelationDefOf.Fiance, x) ||
					pawn.relations.DirectRelationExists(PawnRelationDefOf.Spouse, x))
					).ToList();

			//add bonded animal
			if (RJWSettings.bestiality_enabled && xxx.is_zoophile(mother))
				partners.AddRange(pawn.relations.RelatedPawns.Where(x =>
					Genital_Helper.has_penis_fertile(x) &&
					pawn.relations.DirectRelationExists(PawnRelationDefOf.Bond, x)).ToList());

			if (partners.Any())
			{
				father = partners.RandomElement();
				ModLog.Warning("Hediff_BasePregnancy::Trytogetfather() - father set to: " + xxx.get_pawnname(father));
			}

			if (father == null)
			{
				father = mother;
				ModLog.Warning("Hediff_BasePregnancy::Trytogetfather() - father is null, set to: " + xxx.get_pawnname(mother));
			}
			return father;
		}

		[SyncMethod]
		public override void Tick()
		{
			ageTicks++;
			GestationProgress += progress_per_tick;

			if (pawn.IsHashIntervalTick(1000))
			{
				if (canMiscarry)
				{
					//miscarry with immortal partens
					if (ImmortalMiscarry)
						{
							Miscarry();
							return;
						}
					//ModLog.Message(" 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);
					//Rand.PopState();
					//Rand.PushState(RJW_Multiplayer.PredictableSeed());
					//miscarry after starving 0.5 day
					if (pawn.needs.food != null && pawn.needs.food.CurCategory == HungerCategory.Starving && Rand.MTBEventOccurs(0.5f, TicksPerDay, MiscarryCheckInterval))
					{
						var hed = pawn.health.hediffSet.GetFirstHediffOfDef(HediffDefOf.Malnutrition);
						if (hed.Severity > 0.4)
						{
							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
					//miscarry after SeverelyWounded 0.5 day
					if (Visible && ((IsSeverelyWounded && Rand.MTBEventOccurs(0.5f, TicksPerDay, MiscarryCheckInterval)) || ShouldMiscarry))
					{
						if (Visible && PawnUtility.ShouldSendNotificationAbout(pawn))
						{
							string text = "MessageMiscarriedPoorHealth".Translate(pawn.LabelIndefinite()).CapitalizeFirst();
							Messages.Message(text, pawn, MessageTypeDefOf.NegativeHealthEvent);
						}
						Miscarry();
						return;
					}
				}

				// 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)) // todo: Modded bodies? (FemaleBB for, example)
						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(); }
				}

				if (CurStageIndex == 3)
				{
					if (contractions == 0)
					{
						if (PawnUtility.ShouldSendNotificationAbout(pawn))
						{
							string text = "RJW_Contractions".Translate(pawn.LabelIndefinite());
							Messages.Message(text, pawn, MessageTypeDefOf.NeutralEvent);
						}
						contractions++;
					}
					if (GestationProgress >= 1 && (pawn.CarriedBy == null || pawn.CarriedByCaravan()))//birthing takes an hour
					{
						if (PawnUtility.ShouldSendNotificationAbout(pawn))
						{
							string message_title = "RJW_GaveBirthTitle".Translate(pawn.LabelIndefinite()).CapitalizeFirst();
							string message_text = "RJW_GaveBirthText".Translate(pawn.LabelIndefinite()).CapitalizeFirst();
							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();
					}
				}
			}
		}

		public override void Notify_PawnDied()
		{
			base.Notify_PawnDied();
			if (CurStageIndex == 3)
				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", true);
			Scribe_Values.Look(ref is_checked, "is_checked");
			Scribe_Values.Look(ref is_hacked, "is_hacked");
			Scribe_Values.Look(ref is_discovered, "is_discovered");
			Scribe_Values.Look(ref ShouldMiscarry, "ShouldMiscarry");
			Scribe_Values.Look(ref ImmortalMiscarry, "ImmortalMiscarry");
			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
		[SyncMethod]
		protected virtual void GenerateBabies()
		{
			Pawn mother = pawn;
			//Log.Message("Generating babies for " + this.def.defName);
			if (mother == null)
			{
				ModLog.Error("Hediff_BasePregnancy::GenerateBabies() - no mother defined");
				return;
			}

			if (father == null)
			{
				father = Trytogetfather(ref mother);
			}

			//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;
			// not anymore. Using number of traits originally generated by game as a guide

			//Rand.PopState();
			//Rand.PushState(RJW_Multiplayer.PredictableSeed());

			Pawn parent = mother; //race of child
			Pawn parent2 = father; //name for child
			bool is_humanmother = xxx.is_human(mother);
			bool is_humanfather = xxx.is_human(father);

			//Decide on which parent is first to be inherited
			if (father != null && RJWPregnancySettings.use_parent_method)
			{
				//Log.Message("The baby race needs definition");
				//humanality
				if (is_humanmother && is_humanfather)
				{
					//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
				{
					//bestiality
					if (is_humanmother ^ is_humanfather)
					{
						if (RJWPregnancySettings.bestiality_DNA_inheritance == 0.0f)
						{
							//Log.Message("mother will birth beast");
							if (is_humanmother)
								parent = father;
							else
								parent = mother;
						}
						else if (RJWPregnancySettings.bestiality_DNA_inheritance == 1.0f)
						{
							//Log.Message("mother will birth humanlike");
							if (is_humanmother)
								parent = mother;
							else
								parent = father;
						}
						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");
							}
						}

					}
					//animality
					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");
					}
				}
			}

			bool IsAndroidmother = AndroidsCompatibility.IsAndroid(mother);
			bool IsAndroidfather = AndroidsCompatibility.IsAndroid(father);
			//androids can only birth non androids
			if (IsAndroidmother && !IsAndroidfather)
			{
				parent = father;
			}
			else if (!IsAndroidmother && IsAndroidfather)
			{
				parent = mother;
			}
			else if (IsAndroidmother && IsAndroidfather)
			{
				ModLog.Warning("Both parents are andoids, what have you done monster!");
				//this should never happen but w/e
			}

			//ModLog.Message(" 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);

			string last_name = "";
			if (xxx.is_human(father))
				last_name = NameTriple.FromString(father.Name.ToStringFull).Last;
			else if (xxx.is_human(pawn))
				last_name = NameTriple.FromString(pawn.Name.ToStringFull).Last;

			float skin_whiteness = Rand.Range(0, 1);
			if (xxx.has_traits(pawn) && pawn.RaceProps.Humanlike)
			{
				skin_whiteness = pawn.story.melanin;
			}
			if (father != null && xxx.has_traits(father) && father.RaceProps.Humanlike)
			{
				skin_whiteness = Rand.Range(skin_whiteness, father.story.melanin);
			}

			List<Trait> traitpool = new List<Trait>();
			if (xxx.has_traits(pawn) && pawn.RaceProps.Humanlike)
			{
				foreach (Trait momtrait in pawn.story.traits.allTraits)
				{
					if (!RJWPregnancySettings.trait_filtering_enabled || !non_genetic_traits.Contains(momtrait.def.defName))
						traitpool.Add(momtrait);
				}
			}
			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);
				}
			}

			//Pawn generation request
			PawnKindDef spawn_kind_def = parent.kindDef;
			Faction spawn_faction = mother.IsPrisoner ? null : mother.Faction;

			//ModLog.Message(" default child spawn_kind_def - " + spawn_kind_def);
			string MotherRaceName = "";
			string FatherRaceName = "";
			MotherRaceName = mother.kindDef.race.defName;
			if (father != null)
				FatherRaceName = father.kindDef.race.defName;
			//ModLog.Message(" MotherRaceName is " + MotherRaceName);
			//ModLog.Message(" FatherRaceName is " + FatherRaceName);
			if (MotherRaceName != FatherRaceName && FatherRaceName != "")
			{
				var groups = DefDatabase<RaceGroupDef>.AllDefs.Where(x => !x.hybridRaceParents.NullOrEmpty() && !x.hybridChildKindDef.NullOrEmpty());
				//ModLog.Message(" found custom RaceGroupDefs " + groups.Count());
				foreach (var t in groups)
				{
					//ModLog.Message(" trying custom RaceGroupDef " + t.defName);
					//ModLog.Message(" custom hybridRaceParents " + t.hybridRaceParents.Count());
					//ModLog.Message(" contains hybridRaceParents MotherRaceName? " + t.hybridRaceParents.Contains(MotherRaceName));
					//ModLog.Message(" contains hybridRaceParents FatherRaceName? " + t.hybridRaceParents.Contains(FatherRaceName));
					if ((t.hybridRaceParents.Contains(MotherRaceName) && t.hybridRaceParents.Contains(FatherRaceName))
						|| (t.hybridRaceParents.Contains("Any") && (t.hybridRaceParents.Contains(MotherRaceName) || t.hybridRaceParents.Contains(FatherRaceName))))
					{
						//ModLog.Message(" has hybridRaceParents");
						if (t.hybridChildKindDef.Contains("MotherKindDef"))
							spawn_kind_def = mother.kindDef;
						else if (t.hybridChildKindDef.Contains("FatherKindDef") && father != null)
							spawn_kind_def = father.kindDef;
						else
						{
							//ModLog.Message(" trying hybridChildKindDef " + t.defName);
							var child_kind_def_list = new List<PawnKindDef>();
							child_kind_def_list.AddRange(DefDatabase<PawnKindDef>.AllDefs.Where(x => t.hybridChildKindDef.Contains(x.defName)));

							//ModLog.Message(" found custom hybridChildKindDefs " + t.hybridChildKindDef.Count);
							if (!child_kind_def_list.NullOrEmpty())
								spawn_kind_def = child_kind_def_list.RandomElement();
						}
					}
				}
			}

			if (spawn_kind_def.defName.Contains("Nymph"))
			{
				//child is nymph, try to find other PawnKindDef
				var spawn_kind_def_list = new List<PawnKindDef>();
				spawn_kind_def_list.AddRange(DefDatabase<PawnKindDef>.AllDefs.Where(x => x.race == spawn_kind_def.race && !x.defName.Contains("Nymph")));
				//no other PawnKindDef found try mother
				if (spawn_kind_def_list.NullOrEmpty())
					spawn_kind_def_list.AddRange(DefDatabase<PawnKindDef>.AllDefs.Where(x => x.race == mother.kindDef.race && !x.defName.Contains("Nymph")));
				//no other PawnKindDef found try father
				if (spawn_kind_def_list.NullOrEmpty() && father !=null)
					spawn_kind_def_list.AddRange(DefDatabase<PawnKindDef>.AllDefs.Where(x => x.race == father.kindDef.race && !x.defName.Contains("Nymph")));
				//no other PawnKindDef found fallback to generic colonist
				if (spawn_kind_def_list.NullOrEmpty())
					spawn_kind_def = PawnKindDefOf.Colonist;

				spawn_kind_def = spawn_kind_def_list.RandomElement();
			}
			//ModLog.Message(" final child spawn_kind_def - " + spawn_kind_def);

			//pregnancies with slimes birth only slimes
			//should somehow merge with above
			if (xxx.is_slime(mother))
			{
				parent = mother;
				spawn_kind_def = parent.kindDef;
			}
			if (father != null)
			{
				if (xxx.is_slime(father))
				{
					parent = father;
					spawn_kind_def = parent.kindDef;
				}
			}

			PawnGenerationRequest request = new PawnGenerationRequest(
				kind: spawn_kind_def,
				faction: spawn_faction,
				forceGenerateNewPawn: true,
				newborn: true,
				allowDowned: true,
				canGeneratePawnRelations: false,
				colonistRelationChanceFactor: 0,
				allowFood: false,
				allowAddictions: false,
				relationWithExtraPawnChanceFactor: 0,
				fixedMelanin: skin_whiteness,
				fixedLastName: last_name
				);

			//ModLog.Message(" Generated request, making babies");
			//Litter size. Let's use the main parent litter size, reduced by fertility.
			float litter_size = (parent.RaceProps.litterSizeCurve == null) ? 1 : Rand.ByCurve(parent.RaceProps.litterSizeCurve);
			//ModLog.Message(" base Litter size " + litter_size);
			litter_size *= Math.Min(mother.health.capacities.GetLevel(xxx.reproduction), 1);
			litter_size *= Math.Min(father == null ? 1 : father.health.capacities.GetLevel(xxx.reproduction), 1);
			litter_size = Math.Max(1, litter_size);
			//ModLog.Message(" 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
																																	//ModLog.Message(" Baby size " + baby_size);
			float max_litter = 1f / 3f / baby_size;
			//ModLog.Message(" Max size " + max_litter);
			max_litter *= (mother.Has(Quirk.Breeder) || mother.Has(Quirk.Incubator)) ? 2 : 1;
			//ModLog.Message(" Max size (w quirks) " + max_litter);

			//Generate random amount of babies within litter/max size
			litter_size = (Rand.Range(litter_size, max_litter));
			//ModLog.Message(" Litter size roll 1:" + litter_size);
			litter_size = Mathf.RoundToInt(litter_size);
			//ModLog.Message(" Litter size roll 2:" + litter_size);
			litter_size = Math.Max(1, litter_size);
			//ModLog.Message(" 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)
				{
					updateTraits(baby, traitpool);
				}
				babies.Add(baby);
			}
		}

		/// <summary>
		/// Update pawns traits
		/// Uses original pawns trains and given list of traits as a source of traits to select.
		/// </summary>
		/// <param name="pawn">humanoid pawn</param>
		/// <param name="traitpool">list of parent traits</param>
		/// <param name="traitLimit">maximum allowed number of traits</param>
		void updateTraits(Pawn pawn, List<Trait> parenttraitpool, int traitLimit = -1)
		{
			if (pawn?.story?.traits == null)
			{
				return;
			}

			if (traitLimit == -1)
			{
				traitLimit = pawn.story.traits.allTraits.Count;
			}

			//Personal pool
			List<Trait> personalTraitPool = new List<Trait>(pawn.story.traits.allTraits);
			//Parents pool
			if (parenttraitpool != null)
			{
				personalTraitPool.AddRange(parenttraitpool);
			}

			//Game suggested traits.
			var forcedTraits = personalTraitPool
				.Where(x => x.ScenForced)
				.Distinct(new TraitComparer(ignoreDegree: true)); // result can be a mess, because game allows this mess to be created in scenario editor

			List<Trait> selectedTraits = new List<Trait>();
			selectedTraits.AddRange(forcedTraits); // enforcing scenario forced traits

			var comparer = new TraitComparer(); // trait comparision implementation, because without game compares traits *by reference*, makeing them all unique.

			while (selectedTraits.Count < traitLimit && personalTraitPool.Count > 0)
			{
				int index = Rand.Range(0, personalTraitPool.Count); // getting trait and removing from the pull
				var trait = personalTraitPool[index];
				personalTraitPool.RemoveAt(index);

				if (!selectedTraits.Any(x => comparer.Equals(x, trait) ||  // skipping traits conflicting with already added
											 x.def.ConflictsWith(trait)))
				{
					selectedTraits.Add(new Trait(trait.def, trait.Degree, false));
				}
			}

			pawn.story.traits.allTraits = selectedTraits;
		}

		//Handles the spawning of pawns
		//this is extended by other scripts
		public abstract void GiveBirth();

		//Add relations
		//post pregnancy effects
		//update birth stats
		//make baby futa if needed
		public virtual void PostBirth(Pawn mother, Pawn father, Pawn baby)
		{
			BabyPostBirth(mother, father, baby);

			//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(mother))
				{
					//BnC compatibility
					if (xxx.BnC_RJW_PostPregnancy == null)
					{
						mother.health.AddHediff(xxx.PostPregnancy, null, null);
						mother.health.AddHediff(xxx.Lactating, mother.RaceProps.body.AllParts.Find(x => x.def.defName == "Chest"), null);
					}
					if (xxx.is_human(baby))
					{
						if (mother.records.GetAsInt(xxx.CountOfBirthHuman) == 0 &&
							mother.records.GetAsInt(xxx.CountOfBirthAnimal) == 0 &&
							mother.records.GetAsInt(xxx.CountOfBirthEgg) == 0)
						{
							mother.needs.mood.thoughts.memories.TryGainMemory(xxx.IGaveBirthFirstTime);
						}
						else
						{
							mother.needs.mood.thoughts.memories.TryGainMemory(xxx.IGaveBirth);
						}
					}
				}

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

			if (baby.playerSettings != null && mother.playerSettings != null)
			{
				baby.playerSettings.AreaRestriction = mother.playerSettings.AreaRestriction;
			}

			//if (xxx.is_human(baby))
			//{
			//	baby.story.melanin = skin_whiteness;
			//	baby.story.birthLastName = last_name;
			//}

			//ModLog.Message("" + this.GetType().ToString() + " post PostBirth 1: " + baby.story.birthLastName);
			//spawn futa
			bool isfuta = spawnfutachild(baby, mother, father);

			if (!RacePartDef_Helper.TryRacePartDef_partAdders(pawn))
			{
				if (isfuta)
				{
					SexPartAdder.add_genitals(baby, partstospawn(baby, mother, father), Gender.Male);
					SexPartAdder.add_genitals(baby, partstospawn(baby, mother, father), Gender.Female);
					SexPartAdder.add_breasts(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
				{
					SexPartAdder.add_genitals(baby, partstospawn(baby, mother, father));
					SexPartAdder.add_breasts(baby, partstospawn(baby, mother, father));
				}
				SexPartAdder.add_anus(baby, partstospawn(baby, mother, father));
			}

			//ModLog.Message("" + this.GetType().ToString() + " post PostBirth 2: " + baby.story.birthLastName);
			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.TryMakeFilth(mother.Position, mother.Map, ThingDefOf.Filth_AmnioticFluid, mother.LabelIndefinite(), 5);
				mother.caller?.DoCall();
				baby.caller?.DoCall();
				father?.caller?.DoCall();
			}

			//ModLog.Message("" + this.GetType().ToString() + " post PostBirth 3: " + baby.story.birthLastName);
			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))
			{
				mother.Add(Quirk.Breeder);
				mother.Add(Quirk.ImpregnationFetish);
			}
		}

		public static void BabyPostBirth(Pawn mother, Pawn father, Pawn baby)
		{
			if (!xxx.is_human(baby)) return;

			if (!xxx.RimWorldChildrenIsActive)
			{
				baby.story.childhood = null;
				baby.story.adulthood = null;

				try
				{
					// set child to civil
					Backstory bs = null;
					BackstoryDatabase.TryGetWithIdentifier("rjw_childC", out bs);

					// set child to tribal if both parents tribals
					if (father != null)
						if (father.story != null)
							if (mother.story.GetBackstory(BackstorySlot.Adulthood) != null && father.story.GetBackstory(BackstorySlot.Adulthood).spawnCategories.Contains("Tribal"))
								BackstoryDatabase.TryGetWithIdentifier("rjw_childT", out bs);
							else if(mother.story.GetBackstory(BackstorySlot.Adulthood) == null && father.story.GetBackstory(BackstorySlot.Childhood).spawnCategories.Contains("Tribal"))
								BackstoryDatabase.TryGetWithIdentifier("rjw_childT", out bs);

					baby.story.childhood = bs;
				}
				catch (Exception e)
				{
					ModLog.Warning(e.ToString());
				}
			}
			else
			{
				ModLog.Message("PostBirth::RimWorldChildrenIsActive:: Rewriting story of " + baby);
				//var disabledBaby = BackstoryDatabase.allBackstories["CustomBackstory_NA_Childhood_Disabled"]; // should be this, but bnc/cnp is broken and cant undisable work
				var BabyStory = BackstoryDatabase.allBackstories["CustomBackstory_NA_Childhood"];
				if (BabyStory != null)
				{
					baby.story.childhood = BabyStory;
				}
				else
				{
					ModLog.Error("Couldn't find the required Backstory: CustomBackstory_NA_Childhood!");
				}
				//BnC compatibility
				if (xxx.BnC_RJW_PostPregnancy != null)
				{
					baby.health.AddHediff(xxx.BabyState, null, null);
					baby.health.AddHediff(xxx.NoManipulationFlag, null, null);
				}
			}
		}

		//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 father.
		public virtual void Initialize(Pawn mother, Pawn dad)
		{
			BodyPartRecord torso = mother.RaceProps.body.AllParts.Find(x => x.def.defName == "Torso");
			mother.health.AddHediff(this, torso);
			//ModLog.Message("" + this.GetType().ToString() + " pregnancy hediff generated: " + this.Label);
			//ModLog.Message("" + this.GetType().ToString() + " mother: " + mother + " father: " + dad);
			father = dad;
			if (father != null)
			{
				babies = new List<Pawn>();
				contractions = 0;
				//ModLog.Message("" + this.GetType().ToString() + " generating babies before: " + this.babies.Count);
				GenerateBabies();
			}
			progress_per_tick = babies.NullOrEmpty() ? 1f : (1.0f) / (babies[0].RaceProps.gestationPeriodDays * TicksPerDay);
			if (pawn.Has(Quirk.Breeder) || pawn.health.hediffSet.HasHediff(HediffDef.Named("FertilityEnhancer")))
				progress_per_tick *= 1.25f;
			progress_per_tick /= RJWPregnancySettings.normal_pregnancy_duration;
			if (xxx.ImmortalsIsActive && (mother.health.hediffSet.HasHediff(xxx.IH_Immortal) || father.health.hediffSet.HasHediff(xxx.IH_Immortal)))
			{
				ImmortalMiscarry = true;
				ShouldMiscarry = true;
			}
			//ModLog.Message("" + this.GetType().ToString() + " generating babies after: " + this.babies.Count);
		}

		private static Dictionary<Type, string> _hediffOfClass = null;
		protected static Dictionary<Type, string> hediffOfClass
		{
			get
			{
				if (_hediffOfClass == null)
				{
					_hediffOfClass = new Dictionary<Type, string>();
					var allRJWPregnancies = AppDomain.CurrentDomain.GetAssemblies()
						.SelectMany(
							a => a
							.GetTypes()
							.Where(t => t.IsSubclassOf(typeof(Hediff_BasePregnancy)))
						);

					foreach (var pregClass in allRJWPregnancies)
					{
						var attribute = (RJWAssociatedHediffAttribute)pregClass.GetCustomAttributes(typeof(RJWAssociatedHediffAttribute), false).FirstOrDefault();
						if (attribute != null)
						{
							_hediffOfClass[pregClass] = attribute.defName;
						}
					}
				}

				return _hediffOfClass;
			}
		}

		/// <summary>
		/// Creates pregnancy hediff and assigns it to mother
		/// </summary>
		/// <typeparam name="T">type of pregnancy, should be subclass of Hediff_BasePregnancy</typeparam>
		/// <param name="mother"></param>
		/// <param name="father"></param>
		/// <returns>created hediff</returns>
		public static T Create<T>(Pawn mother, Pawn father) where T : Hediff_BasePregnancy
		{
			if (mother == null)
				return null;
			//if (mother.RaceHasOviPregnancy() && !(T is Hediff_MechanoidPregnancy))
			//{
			//	//return null;
			//}
			//else
			//{
			//}

			BodyPartRecord torso = mother.RaceProps.body.AllParts.Find(x => x.def.defName == "Torso");

			string defName = hediffOfClass.ContainsKey(typeof(T)) ? hediffOfClass[typeof(T)] : "RJW_pregnancy";
			if (RJWSettings.DevMode) ModLog.Message($"Hediff_BasePregnancy::create hediff:{defName} class:{typeof(T).FullName}");

			T hediff = HediffHelper.MakeHediff<T>(HediffDef.Named(defName), mother, torso);
			hediff.Initialize(mother, father);
			return hediff;
		}

		/// <summary>
		/// list of all known RJW pregnancy hediff names (new can be regicreted by mods)
		/// </summary>
		/// <returns></returns>
		public static IEnumerable<string> KnownPregnancies()
		{
			return hediffOfClass.Values.Distinct(); // todo: performance
		}

		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 * RJWPregnancySettings.normal_pregnancy_duration)).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>
	[RJWAssociatedHediff("RJW_pregnancy_beast")]
	public 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()).CapitalizeFirst();
			string message_text1 = "RJW_PregnantText".Translate(pawn.LabelIndefinite()).CapitalizeFirst();
			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;

			if (babies.NullOrEmpty())
			{
				ModLog.Warning(" no babies (debug?) " + this.GetType().Name);
				if (father == null)
				{
					father = Trytogetfather(ref pawn);
				}
				Initialize(mother, father);
			}
			List<Pawn> siblings = new List<Pawn>();
			foreach (Pawn baby in babies)
			{
				//backup melanin, LastName for when baby reset by other mod on spawn/backstorychange
				//var skin_whiteness = baby.story.melanin;
				//var last_name = baby.story.birthLastName;

				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 && mother != father)
				{
					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);

				//restore melanin, LastName for when baby reset by other mod on spawn/backstorychange
				//baby.story.melanin = skin_whiteness;
				//baby.story.birthLastName = last_name;
			}
			mother.health.RemoveHediff(this);
		}
	}
}


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

namespace rjw
{
	[RJWAssociatedHediff("RJW_pregnancy")]
	public 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;

			if (babies.NullOrEmpty())
			{
				ModLog.Warning(" no babies (debug?) " + this.GetType().Name);
				if (father == null)
				{
					father = Trytogetfather(ref pawn);
				}
				Initialize(mother, father);
			}
			List<Pawn> siblings = new List<Pawn>();
			foreach (Pawn baby in babies)
			{
				//backup melanin, LastName for when baby reset by other mod on spawn/backstorychange
				//var skin_whiteness = baby.story.melanin;
				//var last_name = baby.story.birthLastName;

				//ModLog.Message("" + this.GetType().ToString() + " pre TrySpawnHatchedOrBornPawn: " + baby.story.birthLastName);
				PawnUtility.TrySpawnHatchedOrBornPawn(baby, mother);
				//ModLog.Message("" + this.GetType().ToString() + " post TrySpawnHatchedOrBornPawn: " + baby.story.birthLastName);

				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)
				{
					if (mother.Faction != baby.Faction)
						baby.SetFaction(mother.Faction);
				}
				if (!mother.IsColonist && mother.IsPrisonerOfColony)
				{
					baby.guest.CapturedBy(Faction.OfPlayer);
				}

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

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

				//ModLog.Message("" + this.GetType().ToString() + " pre PostBirth: " + baby.story.birthLastName);
				PostBirth(mother, father, baby);
				//ModLog.Message("" + this.GetType().ToString() + " post PostBirth: " + baby.story.birthLastName);

				//restore melanin, LastName for when baby reset by other mod on spawn/backstorychange
				//baby.story.melanin = skin_whiteness;
				//baby.story.birthLastName = last_name;
			}
			mother.health.RemoveHediff(this);
		}
	}
}


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

namespace rjw
{
	public class Hediff_InsectEgg : HediffWithComps
	{
		public int bornTick = 5000;
		public int abortTick = 0;

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

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

		public Pawn father;					//can be parentkind defined in egg
		public Pawn implanter;					//can be any pawn
		public bool canbefertilized = true;
		public bool fertilized => father != null;
		public float eggssize = 0.1f;
		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 override string LabelBase
		{
			get
			{
				if (Prefs.DevMode)
				{
					//if (father != null)
					//	return father.kindDef.race.label + " egg";
					//else if (implanter != null)
					if (implanter != null)
						return implanter.kindDef.race.label + " egg";
				}

				if (eggssize <= 0.10f)
					return "Small egg";
				if (eggssize <= 0.3f)
					return "Medium egg";
				else if (eggssize <= 0.5f)
					return "Big egg";
				else
					return "Huge egg";

				//return Label;
			}
		}

		public override string LabelInBrackets
		{
			get
			{
				if (Prefs.DevMode)
				{
					if (fertilized)
						return "Fertilized";
					else
						return "Unfertilized";
				}
				return null;
			}
		}

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

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

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

		public override void Tick()
		{
			ageTicks++;
			if (pawn.IsHashIntervalTick(1000))
			{
				//birthing takes an hour
				if (ageTicks >= bornTick - 2500 && contractions == 0 && !(pawn.jobs.curDriver is JobDriver_Sex))
				{
					if (PawnUtility.ShouldSendNotificationAbout(pawn) && (pawn.IsColonist || pawn.IsPrisonerOfColony))
					{
						string key = "RJW_EggContractions";
						string text = TranslatorFormattedStringExtensions.Translate(key, pawn.LabelIndefinite());
						Messages.Message(text, pawn, MessageTypeDefOf.NeutralEvent);
					}
					contractions++;
					if (!Genital_Helper.has_ovipositorF(pawn))
						pawn.health.AddHediff(xxx.submitting);
				}
				else if (ageTicks >= bornTick && contractions != 0 && (pawn.CarriedBy == null || pawn.CarriedByCaravan()))
				{
					if (PawnUtility.ShouldSendNotificationAbout(pawn) && (pawn.IsColonist || pawn.IsPrisonerOfColony))
					{
						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));
				}
			}
		}

		public override void ExposeData()
		{
			base.ExposeData();
			Scribe_Values.Look(ref bornTick, "bornTick");
			Scribe_Values.Look(ref abortTick, "abortTick");
			Scribe_References.Look(ref father, "father", true);
			Scribe_References.Look(ref implanter, "implanter", true);
			Scribe_Collections.Look(ref babies, saveDestroyedThings: true, label: "babies", lookMode: LookMode.Deep, ctorArgs: new object[0]);
		}
		public override void Notify_PawnDied()
		{
			base.Notify_PawnDied();
			GiveBirth();
		}

		protected virtual void GenerateBabies()
		{

		}

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

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

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

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

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

				if (spawn_kind_def.defName.Contains("Nymph"))
				{
					//child is nymph, try to find other PawnKindDef
					var spawn_kind_def_list = new List<PawnKindDef>();
					spawn_kind_def_list.AddRange(DefDatabase<PawnKindDef>.AllDefs.Where(x => x.race == spawn_kind_def.race && !x.defName.Contains("Nymph")));
					//no other PawnKindDef found try mother
					if (spawn_kind_def_list.NullOrEmpty())
						spawn_kind_def_list.AddRange(DefDatabase<PawnKindDef>.AllDefs.Where(x => x.race == mother.kindDef.race && !x.defName.Contains("Nymph")));
					//no other PawnKindDef found try implanter
					if (spawn_kind_def_list.NullOrEmpty() && implanter != null)
						spawn_kind_def_list.AddRange(DefDatabase<PawnKindDef>.AllDefs.Where(x => x.race == implanter.kindDef.race && !x.defName.Contains("Nymph")));
					//no other PawnKindDef found fallback to generic colonist
					if (spawn_kind_def_list.NullOrEmpty())
						spawn_kind_def = PawnKindDefOf.Colonist;

					spawn_kind_def = spawn_kind_def_list.RandomElement();
				}

				if (!spawn_kind_def.RaceProps.Humanlike)                    //TODO: fix inconsistencies betwin this and egg itself
				{
					//TODO: merge of old birthing and CompHatcher.Hatch()?
					Thing egg;
					ThingDef EggDef;
					string childrendef = "";
					PawnKindDef children = null;

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

					//make egg
					EggDef = DefDatabase<ThingDef>.GetNamedSilentFail(((HediffDef_InsectEgg)def).FertEggDef);		//try to find predefined
					if (EggDef == null)                                                                             //fail, use generic
						EggDef = (DefDatabase<ThingDef>.GetNamedSilentFail("RJW_EggInsectFertilized"));
					egg = ThingMaker.MakeThing(EggDef);

					//make child
					List<string> childlist = new List<string>();
					if (!((HediffDef_InsectEgg)def).childrenDefs.NullOrEmpty())
					{
						foreach (var child in ((HediffDef_InsectEgg)def).childrenDefs)
						{
							if (DefDatabase<PawnKindDef>.GetNamedSilentFail(child) != null)
								childlist.AddDistinct(child);
						}
						childrendef = childlist.RandomElement();													//try to find predefined
					}
					if (!childrendef.NullOrEmpty())
						children = DefDatabase<PawnKindDef>.GetNamedSilentFail(childrendef);

					if (children == null)																			//use fatherDef
						children = spawn_kind_def;

					//put child into egg
					if (children != null)
					{
						var t = egg.TryGetComp<CompHatcher>();
						t.Props.hatcherPawn = children;
						t.hatcheeParent = implanter;
						t.otherParent = father;
						t.hatcheeFaction = implanter.Faction;
					}

					if (mother.MapHeld?.areaManager?.Home[mother.PositionHeld] == false)
						egg.SetForbidden(true, false);

					//poop egg
					GenPlace.TryPlaceThing(egg, mother.PositionHeld, mother.MapHeld, ThingPlaceMode.Near);
				}
				else //humanlike baby
				{
					//ModLog.Message("Hediff_InsectEgg::BirthBaby() " + spawn_kind_def + " of " + spawn_faction + " in " + (int)(50 * implanter.GetStatValue(StatDefOf.PsychicSensitivity)) + " chance!");
					PawnGenerationRequest request = new PawnGenerationRequest(
						kind: spawn_kind_def,
						faction: spawn_faction,
						forceGenerateNewPawn: true,
						newborn: true,
						allowDowned: true,
						canGeneratePawnRelations: false,
						colonistRelationChanceFactor: 0,
						allowFood: false,
						allowAddictions: false,
						relationWithExtraPawnChanceFactor: 0
						);

					baby = PawnGenerator.GeneratePawn(request);

					PawnUtility.TrySpawnHatchedOrBornPawn(baby, mother);
					if (spawn_faction == Faction.OfInsects || (spawn_faction != null && (spawn_faction.def.defName.Contains("insect") || spawn_faction == implanter.Faction)))
					{
						//ModLog.Message("Hediff_InsectEgg::BirthBaby() GetLord");
						//ModLog.Message("Hediff_InsectEgg::BirthBaby() " + implanter.GetLord());
						//add ai to pawn?
						//LordManager.lords
						Lord lord = implanter.GetLord();
						if (lord != null)
						{
							//ModLog.Message("Hediff_InsectEgg::BirthBaby() lord: " +lord);
							//ModLog.Message("Hediff_InsectEgg::BirthBaby() LordJob: " + lord.LordJob);
							//ModLog.Message("Hediff_InsectEgg::BirthBaby() CurLordToil: " + lord.CurLordToil);
							lord.AddPawn(baby);
						}
						else
						{
							//ModLog.Message("Hediff_InsectEgg::BirthBaby() lord null");
							//LordJob_DefendAndExpandHive lordJob = new LordJob_DefendAndExpandHive();
							//lord = LordMaker.MakeNewLord(baby.Faction, lordJob, mother.Map);
							//lord.AddPawn(baby);
							//lord.SetJob(lordJob);
						}
						//ModLog.Message("Hediff_InsectEgg::BirthBaby() " + baby.GetLord().DebugString());
					}

					Hediff_BasePregnancy.BabyPostBirth(mother, father, baby);
					Sexualizer.sexualize_pawn(baby);

					// Move the baby in front of the mother, rather than on top
					if (mother.Spawned)
						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.CountOfBirthEgg, 1);

				if (mother.records.GetAsInt(xxx.CountOfBirthEgg) > 100)
				{
					mother.Add(Quirk.Incubator);
					mother.Add(Quirk.ImpregnationFetish);
				}
			}
			else
			{
				if (PawnUtility.ShouldSendNotificationAbout(pawn) && (pawn.IsColonist || pawn.IsPrisonerOfColony))
				{
					string key = "EggDead";
					string text = TranslatorFormattedStringExtensions.Translate(key, pawn.LabelIndefinite()).CapitalizeFirst();
					Messages.Message(text, pawn, MessageTypeDefOf.SituationResolved);
				}
				Thing egg;
				var UEgg = DefDatabase<ThingDef>.GetNamedSilentFail(((HediffDef_InsectEgg)def).UnFertEggDef);
				if (UEgg == null)
					UEgg = (DefDatabase<ThingDef>.GetNamedSilentFail("RJW_EggInsectUnfertilized"));
				egg = ThingMaker.MakeThing(UEgg);

				Log.Message("1");
				if (mother.MapHeld?.areaManager?.Home[mother.PositionHeld] == false)
					egg.SetForbidden(true, false);
				Log.Message("2");
				GenPlace.TryPlaceThing(egg, mother.PositionHeld, mother.MapHeld, ThingPlaceMode.Near);
				Log.Message("3");
			}
			// 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.TryMakeFilth(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);
				int i = 0;
				if (xxx.is_insect(baby) || xxx.is_insect(mother) || xxx.is_insect(implanter) || xxx.is_insect(father))
				{
					while (i++ < howmuch)
					{
						Thing jelly = ThingMaker.MakeThing(ThingDefOf.InsectJelly);
						if (mother.MapHeld?.areaManager?.Home[mother.PositionHeld] == false )
							jelly.SetForbidden(true, false);
						GenPlace.TryPlaceThing(jelly, mother.PositionHeld, mother.MapHeld, ThingPlaceMode.Near);
					}
				}
			}
			mother.health.RemoveHediff(this);
		}

		//set father/final egg type
		public void Fertilize(Pawn Pawn)
		{
			if (implanter == null) // immortal ressurrected?
			{
				return;
			}
			if (xxx.ImmortalsIsActive && (Pawn.health.hediffSet.HasHediff(xxx.IH_Immortal) || pawn.health.hediffSet.HasHediff(xxx.IH_Immortal)))
			{
				return;
			}
			if (!AndroidsCompatibility.IsAndroid(pawn))
				if (!fertilized && canbefertilized && ageTicks < abortTick)
				{
					if (RJWSettings.DevMode) ModLog.Message(xxx.get_pawnname(pawn) + " fertilize eggs:" + this.ToString());
					father = Pawn;
					ChangeEgg(implanter);
				}
		}

		//set implanter/base egg type
		public void Implanter(Pawn Pawn)
		{
			if (implanter == null)
			{
				if (RJWSettings.DevMode) ModLog.Message("Hediff_InsectEgg:: set implanter:" + xxx.get_pawnname(Pawn));
				implanter = Pawn;
				ChangeEgg(implanter);

				if (!implanter.health.hediffSet.HasHediff(xxx.sterilized))
				{
					if (((HediffDef_InsectEgg)def).selffertilized)
						Fertilize(implanter);
				}
				else
					canbefertilized = false;
			}
		}

		//Change egg type after implanting/fertilizing
		public void ChangeEgg(Pawn Pawn)
		{
			if (Pawn != null)
			{
				eggssize = Pawn.RaceProps.baseBodySize / 5;
				float gestationPeriod = Pawn.RaceProps?.gestationPeriodDays * 60000 ?? 450000;
				//float gestationPeriod = 10000 ;
				gestationPeriod = (xxx.has_quirk(pawn, "Incubator") || pawn.health.hediffSet.HasHediff(HediffDef.Named("FertilityEnhancer"))) ? gestationPeriod / 2 : gestationPeriod;
				gestationPeriod *= RJWPregnancySettings.egg_pregnancy_duration;
				bornTick = (int)(gestationPeriod * Pawn.RaceProps.baseBodySize + 0.5f);
				abortTick = (int)(bornTick / 3);
				if (!Genital_Helper.has_ovipositorF(pawn))
					Severity = eggssize;
				else if (eggssize > 0.1f)
					Severity = 0.1f;
				else
					Severity = eggssize;
			}
		}

		//for setting implanter/fertilize eggs
		public bool IsParent(Pawn parent)
		{
			//anyone can fertilize
			if (RJWPregnancySettings.egg_pregnancy_fertilize_anyone) return true;

			//only set egg parent or implanter can fertilize
			else return parentDef == parent.kindDef.defName
						|| parentDefs.Contains(parent.kindDef.defName)
						|| implanter.kindDef == parent.kindDef; // unknown eggs
		}

		public override string DebugString()
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append(base.DebugString());
			stringBuilder.AppendLine(" Gestation progress: " + ((float)ageTicks / bornTick).ToStringPercent());
			if (RJWSettings.DevMode) stringBuilder.AppendLine(" Implanter: " + xxx.get_pawnname(implanter));
			if (RJWSettings.DevMode) stringBuilder.AppendLine(" Father: " + xxx.get_pawnname(father));
			if (RJWSettings.DevMode) stringBuilder.AppendLine(" bornTick: " + bornTick);
			//stringBuilder.AppendLine(" potential father: " + parentDef);
			return stringBuilder.ToString();
		}
	}
}


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

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

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

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

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


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

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


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

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

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

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

		public void Hack()
		{
			is_hacked = true;
		}

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

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

			if (!babies.NullOrEmpty())
				foreach (Pawn baby in babies)
					baby.Discard(true);

			Faction spawn_faction = null;

			if (!is_hacked)
				spawn_faction = Faction.OfMechanoids;

			PawnKindDef children = null;

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

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

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

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

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

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

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