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

namespace rjw.Modules.Interactions.Rules.PartKindUsageRules.Implementation
{
	public class WhoringPartKindUsageRule : IPartPreferenceRule
	{
		public IEnumerable<Weighted<LewdablePartKind>> ModifiersForDominant(InteractionContext context)
		{
			if (context.Internals.InteractionType == InteractionType.Whoring)
			{
				yield return new Weighted<LewdablePartKind>(Multipliers.Frequent, LewdablePartKind.Vagina);
				yield return new Weighted<LewdablePartKind>(Multipliers.Frequent, LewdablePartKind.Penis);
				yield return new Weighted<LewdablePartKind>(Multipliers.Frequent, LewdablePartKind.FemaleOvipositor);
				yield return new Weighted<LewdablePartKind>(Multipliers.Frequent, LewdablePartKind.MaleOvipositor);

				yield return new Weighted<LewdablePartKind>(Multipliers.Common, LewdablePartKind.Mouth);
				yield return new Weighted<LewdablePartKind>(Multipliers.Common, LewdablePartKind.Tongue);
				yield return new Weighted<LewdablePartKind>(Multipliers.Common, LewdablePartKind.Beak);
				yield return new Weighted<LewdablePartKind>(Multipliers.Common, LewdablePartKind.Breasts);
				yield return new Weighted<LewdablePartKind>(Multipliers.Uncommon, LewdablePartKind.Anus);

				yield return new Weighted<LewdablePartKind>(Multipliers.AlmostNever, LewdablePartKind.Hand);
				yield return new Weighted<LewdablePartKind>(Multipliers.AlmostNever, LewdablePartKind.Foot);

				yield return new Weighted<LewdablePartKind>(Multipliers.Rare, LewdablePartKind.Tail);
			}
		}

		public IEnumerable<Weighted<LewdablePartKind>> ModifiersForSubmissive(InteractionContext context)
		{
			if (context.Internals.InteractionType == InteractionType.Whoring)
			{
				yield return new Weighted<LewdablePartKind>(Multipliers.Frequent, LewdablePartKind.Vagina);
				yield return new Weighted<LewdablePartKind>(Multipliers.Frequent, LewdablePartKind.Penis);
				yield return new Weighted<LewdablePartKind>(Multipliers.Frequent, LewdablePartKind.FemaleOvipositor);
				yield return new Weighted<LewdablePartKind>(Multipliers.Frequent, LewdablePartKind.MaleOvipositor);

				yield return new Weighted<LewdablePartKind>(Multipliers.Common, LewdablePartKind.Mouth);
				yield return new Weighted<LewdablePartKind>(Multipliers.Common, LewdablePartKind.Tongue);
				yield return new Weighted<LewdablePartKind>(Multipliers.Common, LewdablePartKind.Beak);
				yield return new Weighted<LewdablePartKind>(Multipliers.Common, LewdablePartKind.Breasts);
				yield return new Weighted<LewdablePartKind>(Multipliers.Uncommon, LewdablePartKind.Anus);

				yield return new Weighted<LewdablePartKind>(Multipliers.AlmostNever, LewdablePartKind.Hand);
				yield return new Weighted<LewdablePartKind>(Multipliers.AlmostNever, LewdablePartKind.Foot);

				yield return new Weighted<LewdablePartKind>(Multipliers.Rare, LewdablePartKind.Tail);
			}
		}
	}
}


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

namespace rjw
{
	[StaticConstructorOnStartup]
	public static class RJW_Multiplayer
	{
		static RJW_Multiplayer()
		{
			if (!MP.enabled) return;

			// This is where the magic happens and your attributes
			// auto register, similar to Harmony's PatchAll.
			MP.RegisterAll();

				/*
				Log.Message("RJW MP compat testing");
				var type = AccessTools.TypeByName("rjw.RJWdesignations");
				//Log.Message("rjw MP compat " + type.Name);
				Log.Message("is host " + MP.IsHosting);
				Log.Message("PlayerName " + MP.PlayerName);
				Log.Message("IsInMultiplayer " + MP.IsInMultiplayer);

				//MP.RegisterSyncMethod(type, "Comfort");
				/*
				MP.RegisterSyncMethod(type, "<GetGizmos>Service");
				MP.RegisterSyncMethod(type, "<GetGizmos>BreedingHuman");
				MP.RegisterSyncMethod(type, "<GetGizmos>BreedingAnimal");
				MP.RegisterSyncMethod(type, "<GetGizmos>Breeder");
				MP.RegisterSyncMethod(type, "<GetGizmos>Milking");
				MP.RegisterSyncMethod(type, "<GetGizmos>Hero");
				*/

			// You can choose to not auto register and do it manually
			// with the MP.Register* methods.

			// Use MP.IsInMultiplayer to act upon it in other places
			// user can have it enabled and not be in session
		}

		//generate PredictableSeed for Verse.Rand
		public static int PredictableSeed()
		{
			int seed = 0;
			try
			{
				Map map = Find.CurrentMap;
				//int seedHourOfDay = GenLocalDate.HourOfDay(map);
				//int seedDayOfYear = GenLocalDate.DayOfYear(map);
				//int seedYear = GenLocalDate.Year(map);
				seed = (GenLocalDate.HourOfDay(map) + GenLocalDate.DayOfYear(map)) * GenLocalDate.Year(map);
				//int seed = (seedHourOfDay + seedDayOfYear) * seedYear;
				//Log.Warning("seedHourOfDay: " + seedHourOfDay + "\nseedDayOfYear: " + seedDayOfYear + "\nseedYear: " + seedYear + "\n" + seed);
			}
			catch
			{
				seed = Rand.Int;
			}
			return seed;
		}

		//generate PredictableSeed for Verse.Rand
		[SyncMethod]
		public static float RJW_MP_RAND()
		{
			return Rand.Value;
		}
	}
}

license: mit


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

namespace rjw.Modules.Nymphs
{
	public interface INymphService
	{
		Pawn GenerateNymph(Map map, PawnKindDef nymphKind = null, Faction faction = null);
		IEnumerable<Pawn> GenerateNymphs(Map map, int count);

		PawnKindDef RandomNymphKind();
		IEnumerable<PawnKindDef> ListNymphKindDefs();

		void SetManhunter(Pawn nymph);

	}
}


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

namespace rjw.Modules.Nymphs.Implementation
{
	public class NymphService : INymphService
	{
		private static ILog _log = LogManager.GetLogger<NymphService, NymphLogProvider>();

		public static readonly INymphService Instance;

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

		public Pawn GenerateNymph(Map map, PawnKindDef nymphKind = null, Faction faction = null)
		{
			if (nymphKind == null)
			{
				nymphKind = RandomNymphKind();
			}

			PawnGenerationRequest request = new PawnGenerationRequest(
				kind: nymphKind,
				faction: faction,
				tile: map.Tile,
				forceGenerateNewPawn: true,
				canGeneratePawnRelations: true,
				colonistRelationChanceFactor: 0.0f,
				inhabitant: true,
				relationWithExtraPawnChanceFactor: 0
				);

			Pawn nymph = PawnGenerator.GeneratePawn(request);

			_log.Debug($"Generated Nymph {nymph.GetName()}");

			return nymph;
		}

		public IEnumerable<Pawn> GenerateNymphs(Map map, int count)
		{
			if (count <= 0)
			{
				yield break;
			}

			PawnKindDef nymphKind = RandomNymphKind();

			for (int i = 0; i < count; i++)
			{
				yield return GenerateNymph(map, nymphKind);
			}
		}

		public PawnKindDef RandomNymphKind()
		{
			return ListNymphKindDefs()
				.RandomElement();
		}
		public IEnumerable<PawnKindDef> ListNymphKindDefs()
		{
			return DefDatabase<PawnKindDef>.AllDefs
				.Where(x => x.defName.Contains("Nymph"));
		}

		public void SetManhunter(Pawn nymph)
		{
			if (RJWSettings.NymphPermanentManhunter)
			{
				nymph.mindState.mentalStateHandler.TryStartMentalState(MentalStateDefOf.ManhunterPermanent);
			}
			else
			{
				nymph.mindState.mentalStateHandler.TryStartMentalState(MentalStateDefOf.Manhunter);
			}
		}
	}
}


code:
using Multiplayer.API;
using RimWorld;
using rjw.Modules.Nymphs.Implementation;
using rjw.Modules.Shared.Logs;
using System.Collections.Generic;
using UnityEngine;
using Verse;

namespace rjw.Modules.Nymphs.Incidents
{
	public abstract class IncidentWorker_BaseNymphRaid : IncidentWorker_NeutralGroup
	{
		private static ILog _log = LogManager.GetLogger<IncidentWorker_BaseNymphRaid, NymphLogProvider>();

		protected static readonly INymphService _nymphGeneratorService;

		static IncidentWorker_BaseNymphRaid()
		{
			_nymphGeneratorService = NymphService.Instance;
		}

		protected virtual float ThreatPointMultiplier => 1f;

		[SyncMethod]
		protected override bool CanFireNowSub(IncidentParms parms)
		{
			//No multiplayer support ... I guess
			if (MP.IsInMultiplayer)
			{
				_log.Debug("Could not fire because multiplayer api is on");
				return false;
			}

			//No map
			if (parms.target is Map == false)
			{
				_log.Debug("Could not fire because the incident target is not a map");
				return false;
			}

			return base.CanFireNowSub(parms);
		}

		protected virtual int GetNymphCount(IncidentParms parms, Map map)
		{
			//Calculating nymphs manyness
			int count;
			count = Mathf.RoundToInt(parms.points / parms.pawnKind.combatPower * ThreatPointMultiplier);
			//Cap the min
			count = Mathf.Max(count, 1);

			return count;
		}

		[SyncMethod]
		protected override void ResolveParmsPoints(IncidentParms parms)
		{
			if (parms.points <= 0)
			{
				parms.points = StorytellerUtility.DefaultThreatPointsNow(parms.target) * ThreatPointMultiplier;
			}
			_log.Debug($"Incident generated with {parms.points} points");
		}

		[SyncMethod]
		protected override bool TryExecuteWorker(IncidentParms parms)
		{
			_log.Debug($"Generating incident");

			//Walk from the edge
			parms.raidArrivalMode = PawnsArrivalModeDefOf.EdgeWalkIn;
			if (parms.raidArrivalMode.Worker.TryResolveRaidSpawnCenter(parms) == false)
			{
				_log.Debug($"Incident failed to fire, no spawn points available");
				return false;
			}

			//Find PawnKind
			parms.pawnKind = Nymph_Generator.GetFixedNymphPawnKindDef();

			if(parms.pawnKind == null)
			{
				_log.Debug($"Incident failed to fire, no Pawn Kind for nymphs");
				return false;
			}

			//Calculating nymphs manyness
			int count = GetNymphCount(parms, (Map)parms.target);

			_log.Debug($"Will generate {count} nymphs");

			List<Pawn> nymphs = GenerateNymphs(parms.target as Map, count);

			parms.raidArrivalMode.Worker.Arrive(nymphs, parms);

			SetManhunters(nymphs);

			Find.LetterStack.ReceiveLetter(
				"RJW_nymph_incident_raid_title".Translate(),
				"RJW_nymph_incident_raid_description".Translate(),
				LetterDefOf.ThreatBig,
				nymphs);

			return true;
		}

		protected List<Pawn> GenerateNymphs(Map map, int count)
		{
			List<Pawn> result = new List<Pawn>();

			PawnKindDef nymphKind = _nymphGeneratorService.RandomNymphKind();

			for (int i = 1; i <= count; ++i)
			{
				Pawn nymph = _nymphGeneratorService.GenerateNymph(map, nymphKind);

				//Set it wild
				nymph.ChangeKind(PawnKindDefOf.WildMan);

				result.Add(nymph);
			}

			return result;
		}

		protected void SetManhunters(List<Pawn> nymphs)
		{
			foreach (var nymph in nymphs)
			{
				_nymphGeneratorService.SetManhunter(nymph);
			}
		}
	}
}


code:
using Multiplayer.API;
using RimWorld;
using rjw.Modules.Nymphs.Implementation;
using rjw.Modules.Shared.Extensions;
using rjw.Modules.Shared.Logs;
using System.Collections.Generic;
using Verse;

namespace rjw.Modules.Nymphs.Incidents
{
	public class IncidentWorker_NymphJoin : IncidentWorker
	{
		private static ILog _log = LogManager.GetLogger<IncidentWorker_NymphJoin, NymphLogProvider>();

		protected static readonly INymphService _nymphGeneratorService;

		static IncidentWorker_NymphJoin()
		{
			_nymphGeneratorService = NymphService.Instance;
		}

		[SyncMethod]
		protected override bool CanFireNowSub(IncidentParms parms)
		{
			//The event is disabled, don't fire !
			if (RJWSettings.NymphTamed == false)
			{
				_log.Debug("The incident can't fire as it is disabled in RJW settings");
				return false;
			}

			//No multiplayer support
			if (MP.IsInMultiplayer)
			{
				_log.Debug("Could not fire because multiplayer api is on");
				return false;
			}

			//No map
			if (parms.target is Map == false)
			{
				_log.Debug("Could not fire because the incident target is not a map");
				return false;
			}

			return base.CanFireNowSub(parms);
		}

		[SyncMethod]
		protected override bool TryExecuteWorker(IncidentParms parms)
		{
			_log.Debug($"Generating incident");

			//Walk from the edge
			parms.raidArrivalMode = PawnsArrivalModeDefOf.EdgeWalkIn;
			if (parms.raidArrivalMode.Worker.TryResolveRaidSpawnCenter(parms) == false)
			{
				_log.Debug($"Incident failed to fire, no spawn points available");
				return false;
			}

			Pawn nymph = GenerateNymph(parms.target as Map);

			_log.Debug($"Generated nymph {nymph.GetName()}");

			parms.raidArrivalMode.Worker.Arrive(new List<Pawn>() { nymph }, parms);

			JoinPlayerFaction(nymph);

			Find.LetterStack.ReceiveLetter(
				"RJW_nymph_incident_join_title".Translate(),
				"RJW_nymph_incident_join_description".Translate(),
				LetterDefOf.PositiveEvent,
				nymph);

			return true;
		}

		protected Pawn GenerateNymph(Map map)
		{
			Pawn nymph = _nymphGeneratorService.GenerateNymph(map);

			nymph.ChangeKind(PawnKindDefOf.WildMan);

			return nymph;
		}

		protected void JoinPlayerFaction(Pawn nymph)
		{
			nymph.SetFaction(Faction.OfPlayer);
		}
	}
}


code:
using Multiplayer.API;
using RimWorld;
using rjw.Modules.Shared.Logs;
using UnityEngine;
using Verse;

namespace rjw.Modules.Nymphs.Incidents
{
	public class IncidentWorker_NymphRaidEasy : IncidentWorker_BaseNymphRaid
	{
		private static ILog _log = LogManager.GetLogger<IncidentWorker_NymphRaidEasy, NymphLogProvider>();

		[SyncMethod]
		protected override bool CanFireNowSub(IncidentParms parms)
		{
			//The event is disabled, don't fire !
			if (RJWSettings.NymphRaidEasy == false)
			{
				_log.Debug("The incident can't fire as it is disabled in RJW settings");
				return false;
			}

			return base.CanFireNowSub(parms);
		}
		protected override int GetNymphCount(IncidentParms parms, Map map)
		{
			int count;

			//Calculating nymphs manyness
			if (RJWSettings.NymphRaidRP)
			{
				count = base.GetNymphCount(parms, map);
			}
			else
			{
				count = (Find.World.worldPawns.AllPawnsAlive.Count + map.mapPawns.FreeColonistsAndPrisonersSpawnedCount);
				//Cap the max
				count = Mathf.Min(count, 100);
				//Cap the min
				count = Mathf.Max(count, 1);
			}

			return count;
		}
	}
}


code:
using Multiplayer.API;
using RimWorld;
using rjw.Modules.Shared.Logs;
using UnityEngine;
using Verse;

namespace rjw.Modules.Nymphs.Incidents
{
	public class IncidentWorker_NymphRaidHard : IncidentWorker_BaseNymphRaid
	{
		private static ILog _log = LogManager.GetLogger<IncidentWorker_NymphRaidHard, NymphLogProvider>();

		protected override float ThreatPointMultiplier => 1.5f;

		[SyncMethod]
		protected override bool CanFireNowSub(IncidentParms parms)
		{
			//The event is disabled, don't fire !
			if (RJWSettings.NymphRaidHard == false)
			{
				_log.Debug("The incident can't fire as it is disabled in RJW settings");
				return false;
			}

			return base.CanFireNowSub(parms);
		}
		protected override int GetNymphCount(IncidentParms parms, Map map)
		{
			int count;

			//Calculating nymphs manyness
			if (RJWSettings.NymphRaidRP)
			{
				count = base.GetNymphCount(parms, map);
			}
			else
			{
				count = map.mapPawns.AllPawnsSpawnedCount;
				//Cap the max
				count = Mathf.Min(count, 1000);
				//Cap the min
				count = Mathf.Max(count, 1);
			}

			return count;
		}
	}
}


code:
using Multiplayer.API;
using RimWorld;
using rjw.Modules.Nymphs.Implementation;
using rjw.Modules.Shared.Extensions;
using rjw.Modules.Shared.Logs;
using System.Collections.Generic;
using Verse;

namespace rjw.Modules.Nymphs.Incidents
{
	public class IncidentWorker_NymphVisitor : IncidentWorker
	{
		private static ILog _log = LogManager.GetLogger<IncidentWorker_NymphVisitor, NymphLogProvider>();

		protected static readonly INymphService _nymphService;

		static IncidentWorker_NymphVisitor()
		{
			_nymphService = NymphService.Instance;
		}

		[SyncMethod]
		protected override bool CanFireNowSub(IncidentParms parms)
		{
			//The event is disabled, don't fire !
			if (RJWSettings.NymphWild == false)
			{
				_log.Debug("The incident can't fire as it is disabled in RJW settings");
				return false;
			}

			//No multiplayer support ... I guess
			if (MP.IsInMultiplayer)
			{
				_log.Debug("Could not fire because multiplayer api is on");
				return false;
			}

			//No map
			if (parms.target is Map == false)
			{
				_log.Debug("Could not fire because the incident target is not a map");
				return false;
			}

			return base.CanFireNowSub(parms);
		}

		[SyncMethod]
		protected override bool TryExecuteWorker(IncidentParms parms)
		{
			_log.Debug($"Generating incident");

			//Walk from the edge
			parms.raidArrivalMode = PawnsArrivalModeDefOf.EdgeWalkIn;
			if (parms.raidArrivalMode.Worker.TryResolveRaidSpawnCenter(parms) == false)
			{
				_log.Debug($"Incident failed to fire, no spawn points available");
				return false;
			}

			Pawn nymph = GenerateNymph(parms.target as Map);

			_log.Debug($"Generated nymph {nymph.GetName()}");

			parms.raidArrivalMode.Worker.Arrive(new List<Pawn>() { nymph }, parms);

			SetManhunter(nymph);

			Find.LetterStack.ReceiveLetter(
				"RJW_nymph_incident_wander_title".Translate(),
				"RJW_nymph_incident_wander_description".Translate(),
				LetterDefOf.ThreatSmall,
				nymph);

			return true;
		}

		protected Pawn GenerateNymph(Map map)
		{
			Pawn nymph = _nymphService.GenerateNymph(map);

			nymph.ChangeKind(PawnKindDefOf.WildMan);

			return nymph;
		}

		protected void SetManhunter(Pawn nymph)
		{
			_nymphService.SetManhunter(nymph);
		}
	}
}


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, 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 rjw.Modules.Shared.Logs;

namespace rjw.Modules.Nymphs
{
	public class NymphLogProvider : ILogProvider
	{
		public bool IsActive => RJWSettings.DebugNymph;
	}
}


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

namespace rjw
{
	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 static nymph_passion_chances get_passion_chances(BackstoryDef child_bs, BackstoryDef adult_bs, SkillDef skill_def)
		{
			var maj = 0.0f;
			var min = 0.0f;

			if (adult_bs.defName.Contains("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.defName.Contains("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.defName.Contains("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.defName.Contains("chatty"))
			{
				if (skill_def == SkillDefOf.Social) { maj = 1.00f; min = 1.00f; }
			}
			else if (adult_bs.defName.Contains("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.defName.Contains("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 void generate(Pawn pawn)
		{
			var tr = pawn.story;
			//reset stories
			tr.Childhood = DefDatabase<BackstoryDef>.AllDefsListForReading.Where(x => x.slot == BackstorySlot.Childhood && x.spawnCategories.Contains("rjw_nymphsCategory")).RandomElement();
			tr.Adulthood = DefDatabase<BackstoryDef>.AllDefsListForReading.Where(x => x.slot == BackstorySlot.Adulthood && x.spawnCategories.Contains("rjw_nymphsCategory")).RandomElement();

			pawn.story.traits.allTraits.AddDistinct(new Trait(xxx.nymphomaniac, 0));

			//Rand.PopState();
			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
			var beauty = 0;
			var rv2 = Rand.Value;

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

			if (beauty > 0)
				pawn.story.traits.allTraits.AddDistinct(new Trait(TraitDefOf.Beauty, beauty, false));
		}
	}
}


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 randomly generated backstory and traits to turn it into propper a nymph
		/// </summary>
		[SyncMethod]
		public static void set_story(Pawn pawn)
		{
			//C# rjw story
			nymph_backstories.generate(pawn);

			//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.

			//backup generated + rjw 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();

			//xml nymph story forced traits
			var trait_count = 0;
			foreach (var t in pawn.story.Adulthood.forcedTraits)
			{
				pawn.story.traits.GainTrait(new Trait(t.def, t.degree));
				++trait_count;
			}

			//xml story + rjw backup + w/e generator generated backup
			while (trait_count < numberOfTotalTraits)
			{
				Trait t = other_traits.Pop();
				if (!is_trait_conflicting_or_duplicate(pawn, t))
					pawn.story.traits.GainTrait(t);
				++trait_count;
			}

			// add broken body hediff to broken nymph
			if (pawn.story.Adulthood.defName.Contains("broken"))
			{
				pawn.health.AddHediff(xxx.feelingBroken);
				(pawn.health.hediffSet.GetFirstHediffOfDef(xxx.feelingBroken)).Severity = Rand.Range(0.4f, 1.0f);
			}

			//reset disabled skills/works
			pawn.skills.Notify_SkillDisablesChanged();
			pawn.Notify_DisabledWorkTypesChanged();
		}

		[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.skillGains.TryGetValue(def, out gain))
				total_gain += gain;
			if (sto.Adulthood.skillGains.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(Faction faction = null)
		{
			// Most of the special properties of nymphs are in harmony patches to PawnGenerator.
			PawnGenerationRequest request = new PawnGenerationRequest(
				kind: GetFixedNymphPawnKindDef(),
				faction: faction,
				forceGenerateNewPawn: true,
				canGeneratePawnRelations: true,
				colonistRelationChanceFactor: 0.0f,
				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;
using RimWorld;

namespace rjw
{
	//MicroComputer
	internal class Hediff_MicroComputer : Hediff_MechImplants
	{
		protected int nextEventTick = GenDate.TicksPerDay;

		public override void ExposeData()
		{
			base.ExposeData();
			Scribe_Values.Look<int>(ref this.nextEventTick, "nextEventTick", GenDate.TicksPerDay, 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:
#nullable enable

using System.Collections.Generic;
using Verse;

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

		//for implanting eggs
		public bool IsParent(string defName)
		{
			// Predefined parent eggs.
			if (parentDef == defName) return true;
			if (parentDefs.Contains(defName)) return true;
			// Dynamic egg.
			if (parentDef != "Unknown" || defName != "Unknown") return false;
			return 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();
		public string? UnFertEggDef = null;
		public string? FertEggDef = null;
	}

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

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


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;
using static rjw.PregnancyHelper;

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

		//Static fields
		private const int MiscarryCheckInterval = 1000;

		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 = false;
		public bool is_parent_known = false;
		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
		public float p_start_tick = 0;
		public float p_end_tick = 0;
		public float lastTick = 0;
		public GeneSet geneSet = null;
		//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 string TipStringExtra
		{
			get
			{
				StringBuilder stringBuilder = new StringBuilder();
				stringBuilder.Append(base.TipStringExtra);
				if(is_parent_known)
					stringBuilder.AppendLine("Father: " + xxx.get_pawnname(father));
				else
					stringBuilder.AppendLine("Father: " + "Unknown");

				return stringBuilder.ToString();
			}
		}

		/// <summary>
		/// do not merge pregnancies
		/// </summary>
		//public override bool TryMergeWith(Hediff other)
		//{
		//	return false;
		//}

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

		public override void Tick()
		{
			ageTicks++;
			float thisTick = Find.TickManager.TicksGame;
			GestationProgress = (1 + thisTick - p_start_tick) / (p_end_tick - p_start_tick);

			if ((thisTick - lastTick) >= 1000)
			{
				if (babies.NullOrEmpty())
				{
					ModLog.Warning(" no babies (debug?) " + this.GetType().Name);
					if (father == null)
					{
						father = Trytogetfather(ref pawn);
					}
					Initialize(pawn, father, SelectDnaGivingParent(pawn, father));
					GestationProgress = (1 + thisTick - p_start_tick) / (p_end_tick - p_start_tick);
					return;
				}
				lastTick = thisTick;
				if (canMiscarry)
				{
					//miscarry with immortal partners
					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);
					if (!Find.Storyteller.difficulty.babiesAreHealthy)
					{
						//miscarry after starving
						if (pawn.needs.food != null && pawn.needs.food.CurCategory == HungerCategory.Starving)
						{
							var hed = pawn.health.hediffSet.GetFirstHediffOfDef(HediffDefOf.Malnutrition);
							if (hed != null)
								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
						if (Visible && (IsSeverelyWounded || 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();
					}
				}
			}
		}

		/// <summary>
		/// Give birth if contraction stage, even if mother died
		/// </summary>
		public override void Notify_PawnDied()
		{
			base.Notify_PawnDied();
			if (CurStageIndex == 3)
				GiveBirth();
		}

		public override void ExposeData()
		{
			base.ExposeData();
			Scribe_References.Look(ref father, "father", true);
			Scribe_Deep.Look(ref geneSet, "genes");
			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 is_parent_known, "is_parent_known");
			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 p_start_tick, "p_start_tick", 0);
			Scribe_Values.Look(ref p_end_tick, "p_end_tick", 0);
			Scribe_Values.Look(ref lastTick, "lastTick", 0);
		}

		/// <summary>
		/// The parent that gives the "DNA" of a baby. Indicates whether the baby
		/// should be the same species as its mother or as its father.
		/// </summary>
		public enum DnaGivingParent
		{
			Mother,
			Father
		}

		/// <summary>
		/// Decides which parent's species the baby should have. This method is
		/// random for some configurations!
		/// </summary>
		public static DnaGivingParent SelectDnaGivingParent(Pawn mother, Pawn father)
		{
			bool IsAndroidmother = AndroidsCompatibility.IsAndroid(mother);
			bool IsAndroidfather = AndroidsCompatibility.IsAndroid(father);
			//androids can only birth non androids
			if (IsAndroidmother && !IsAndroidfather)
			{
				return DnaGivingParent.Father;
			}
			else if (!IsAndroidmother && IsAndroidfather)
			{
				return DnaGivingParent.Mother;
			}
			else if (IsAndroidmother && IsAndroidfather)
			{
				ModLog.Warning("Both parents are andoids, what have you done monster!");
				//this should never happen but w/e
			}

			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");
						return DnaGivingParent.Father;
					}
					else
					{
						//Log.Message("Mother will birth own race");
						return DnaGivingParent.Mother;
					}
				}
				//bestiality
				else if (is_humanmother ^ is_humanfather)
				{
					//TODO: fix gene inheritance from mother if ModsConfig.BiotechActive(also line 528)
					//if (RJWPregnancySettings.bestiality_DNA_inheritance == 0.0f || ModsConfig.BiotechActive)
					if (RJWPregnancySettings.bestiality_DNA_inheritance == 0.0f)
					{
						//Log.Message("mother will birth beast");
						if (is_humanmother)
							return DnaGivingParent.Father;
						else
							return DnaGivingParent.Mother;
					}
					else if (RJWPregnancySettings.bestiality_DNA_inheritance == 1.0f)
					{
						//Log.Message("mother will birth humanlike");
						if (is_humanmother)
							return DnaGivingParent.Mother;
						else
							return DnaGivingParent.Father;
					}
					else
					{
						if (!Rand.Chance(RJWPregnancySettings.bestial_DNA_from_mother))
						{
							//Log.Message("Mother will birth father race");
							return DnaGivingParent.Father;
						}
						else
						{
							//Log.Message("Mother will birth own race");
							return DnaGivingParent.Mother;
						}
					}

				}
				//animality
				else
				{
					if (!Rand.Chance(RJWPregnancySettings.bestial_DNA_from_mother))
					{
						//Log.Message("Mother will birth father race");
						return DnaGivingParent.Father;
					}
					else
					{
						//Log.Message("Mother will birth own race");
						return DnaGivingParent.Mother;
					}
				}
			}
			else
			{
				return DnaGivingParent.Mother;
			}
		}

		//This should generate pawns to be born in due time. Should take into account all settings and parent races
		[SyncMethod]
		protected virtual void GenerateBabies(DnaGivingParent dnaGivingParent)
		{
			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)
			{
				ModLog.Error("Hediff_BasePregnancy::GenerateBabies() - no father defined");
				return;
			}

			//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

			Pawn parent = (dnaGivingParent == DnaGivingParent.Father) ? father : mother;
			//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>();
			List<Trait> momtraits = new List<Trait>();
			List<Trait> poptraits = new List<Trait>();
			List<Trait> traits_to_inherit = new List<Trait>();
			System.Random rd = new System.Random();
			int rand_trait_index = 0;
			float max_num_momtraits_inherited = RJWPregnancySettings.max_num_momtraits_inherited;
            float max_num_poptraits_inherited = RJWPregnancySettings.max_num_poptraits_inherited;
			float max_num_traits_inherited = max_num_momtraits_inherited + max_num_poptraits_inherited;
			int i = 1;
			int j = 1;
			//create list object to store traits of pop and mom
			if (xxx.has_traits(pawn) && pawn.RaceProps.Humanlike)
			{
				foreach (Trait momtrait in pawn.story.traits.allTraits)
				{
					if (!non_genetic_traits.Contains(momtrait.def.defName) && !momtrait.ScenForced)
						momtraits.Add(momtrait);
				}
			}
			if (father != null && xxx.has_traits(father) && father.RaceProps.Humanlike)
			{
				foreach (Trait poptrait in father.story.traits.allTraits)
				{
					if (!non_genetic_traits.Contains(poptrait.def.defName) && !poptrait.ScenForced)
						poptraits.Add(poptrait);
				}
			}
			//add traits from pop and mom to list for traits to inherit
			if(!momtraits.NullOrEmpty())
            {
				i = 1;
				while (momtraits.Count > 0 && i <= max_num_momtraits_inherited)
				{
					rand_trait_index = rd.Next(0, momtraits.Count);
					traits_to_inherit.Add(momtraits[rand_trait_index]);
					momtraits.RemoveAt(rand_trait_index);
                }
            }
			if(!poptraits.NullOrEmpty())
            {
				j = 1;
				while (poptraits.Count > 0 && j <= max_num_poptraits_inherited)
				{
					rand_trait_index = rd.Next(0, poptraits.Count);
					traits_to_inherit.Add(poptraits[rand_trait_index]);
					poptraits.RemoveAt(rand_trait_index);
				}
			}

			//if there is only mom or even no mom traits to be considered, then there is no need to check duplicated tratis from parents
			//then the traits_to_inherit List is ready to be transfered into traitpool
			if (poptraits.NullOrEmpty() || momtraits.NullOrEmpty())
            {
				foreach(Trait traits in traits_to_inherit)
                {
					traitpool.Add(traits);
                }
            }
			//if there are traits from both pop and mom, need to check if there are duplicated traits from parents
			else
            {
			//if length of traits_to_inherit equals maximum number of traits, then it means traits from bot parents reaches maximum value ,and no duplication, this list is ready to go
			//then the following big if chunk is not going to be executed, it will go directly to the last foreach chunk
				if (traits_to_inherit.Count() != max_num_traits_inherited)
				{
			//if not equivalent, it may due to removal of duplicated elements or number of traits of mom or pop are less than maximum number of traits can be inherited from mom or pop
			//now check if all the traits of mom has been added to traitpool for babies and if number of traits from mom has reached maximum while make sure momtraits is not null first
					if (momtraits.Count != 0 && i != max_num_momtraits_inherited)
					{
						while (poptraits != null && momtraits.Count > 0 && i <= max_num_momtraits_inherited)
						{
							rand_trait_index = rd.Next(0, momtraits.Count);
							//if this newly selected traits is not duplciated with traits already in traits_to_inherit
							if (!traits_to_inherit.Contains(momtraits[rand_trait_index]))
							{
								traits_to_inherit.Add(momtraits[rand_trait_index]);
							}
							momtraits.RemoveAt(rand_trait_index);
						}
					}
			//do processing to poptraits in the same way as momtraits
					if (poptraits != null && poptraits.Count != 0 && j != max_num_poptraits_inherited)
					{
						while (poptraits.Count > 0 && i <= max_num_poptraits_inherited)
						{
							rand_trait_index = rd.Next(0, poptraits.Count);
							//if this newly selected traits is not duplciated with traits already in traits_to_inherit
							if (!traits_to_inherit.Contains(poptraits[rand_trait_index]))
							{
								traits_to_inherit.Add(poptraits[rand_trait_index]);
							}
							poptraits.RemoveAt(rand_trait_index);
						}
					}
				}
			//add all traits in finalized trait_to_inherit List into traitpool List for further processing
			//if the above if chunk is executed, the following foreach chunk still needs to be executed, therefore there is no need to put this foreach chunk into an else chunk
				foreach (Trait traits in traits_to_inherit)
				{
					traitpool.Add(traits);
				}
			}

			//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,
				developmentalStages: DevelopmentalStage.Newborn,
				allowDowned: true,
				canGeneratePawnRelations: false,
				colonistRelationChanceFactor: 0,
				allowFood: false,
				allowAddictions: false,
				relationWithExtraPawnChanceFactor: 0,
				//fixedMelanin: skin_whiteness,
				fixedLastName: last_name,
				//forceNoIdeo: true,
				forbidAnyTitle: true,
				forceNoBackstory: true
				);

			//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 body size vs mother body size (bonus childrens boost for multi children pregnancy, ~1.7 for humans ~ x2.2 orassans)
			//assuming mother belly is 1/3 of mother body size
			float baby_size = spawn_kind_def.RaceProps.lifeStageAges[0].def.bodySizeFactor; // adult size/5, probably?
			baby_size *= spawn_kind_def.RaceProps.baseBodySize; // adult size
			//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 (i = 0; i < litter_size; i++)
			{
				Pawn baby = PawnGenerator.GeneratePawn(request);

				if (xxx.is_human(baby))
				{
					if (mother.IsSlaveOfColony)
					{
						//Log.Message("mother.SlaveFaction " + mother.SlaveFaction);
						//Log.Message("mother.HomeFaction " + mother.HomeFaction);
						//Log.Message("mother.Faction " + mother.Faction);

						if (mother.SlaveFaction != null)
							baby.SetFaction(mother.SlaveFaction);
						else if (mother.HomeFaction != null)
							baby.SetFaction(mother.HomeFaction);
						else if (mother.Faction != null)
							baby.SetFaction(mother.Faction);
						else
							baby.SetFaction(Faction.OfPlayer);
						baby.guest.SetGuestStatus(Faction.OfPlayer, GuestStatus.Slave);
					}
					else if (mother.IsPrisonerOfColony)
					{
						//Log.Message("mother.HomeFaction " + mother.HomeFaction);
						if (mother.HomeFaction != null)
							baby.SetFaction(mother.HomeFaction);
						baby.guest.SetGuestStatus(Faction.OfPlayer, GuestStatus.Prisoner);
					}

					//Choose traits to add to the child. Unlike CnP this will allow for some random traits
					if (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(baby))
			{
				RemoveBabyParts(baby, Genital_Helper.get_AllPartsHediffList(baby));
				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 RemoveBabyParts(Pawn baby, List<Hediff> list)
		{
			//ModLog.Message(" RemoveBabyParts( " + xxx.get_pawnname(baby) + " ) - " + list.Count);
			if (!list.NullOrEmpty())
				foreach (var x in list)
				{
					//ModLog.Message(" RemoveBabyParts( " + xxx.get_pawnname(baby) + " ) - " + x.def.defName);

					baby.health.RemoveHediff(x);
					//baby.health.RemoveHediff(baby.health.hediffSet.hediffs.Remove(x));
				}
		}
		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
					String bsDef = null;
					bool isTribal = false;

					// set child to tribal if both parents tribals
					if (father != null && father.story != null)
					{
						if (mother.story.GetBackstory(BackstorySlot.Adulthood) != null && father.story.GetBackstory(BackstorySlot.Adulthood).spawnCategories.Contains("Tribal"))
							isTribal = true;
						else if (mother.story.GetBackstory(BackstorySlot.Adulthood) == null && father.story.GetBackstory(BackstorySlot.Childhood).spawnCategories.Contains("Tribal"))
							isTribal = true;
					}
					else
					{
						//(int)Find.GameInitData.playerFaction.def.techLevel < 4;
						if ((int)mother.Faction.def.techLevel < 4)
							isTribal = true;
					}

					if (isTribal)
					{
						bsDef = "rjw_childT";
						if (baby.GetRJWPawnData().RaceSupportDef != null)
							if (!baby.GetRJWPawnData().RaceSupportDef.backstoryChildTribal.NullOrEmpty())
								bsDef = baby.GetRJWPawnData().RaceSupportDef.backstoryChildTribal.RandomElement();
					}
					else
					{
						bsDef = "rjw_childC";
						if (baby.GetRJWPawnData().RaceSupportDef != null)
							if (!baby.GetRJWPawnData().RaceSupportDef.backstoryChildCivil.NullOrEmpty())
								bsDef = baby.GetRJWPawnData().RaceSupportDef.backstoryChildCivil.RandomElement();
					}

					baby.story.Childhood = DefDatabase<BackstoryDef>.AllDefsListForReading.FirstOrDefault(x => x.defName.Contains(bsDef));
				}
				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 = DefDatabase<BackstoryDef>.AllDefsListForReading.FirstOrDefault(x => x.defName.Contains("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);
				}
			}
		}

		protected void InitializeIfNoBabies()
		{
			if (babies.NullOrEmpty())
			{
				ModLog.Warning(" no babies (debug?) " + this.GetType().Name);
				if (father == null)
				{
					father = Trytogetfather(ref pawn);
				}
				Initialize(pawn, father, SelectDnaGivingParent(pawn, father));
			}
		}

		//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, DnaGivingParent dnaGivingParent)
		{
			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(dnaGivingParent);
				//if (ModsConfig.BiotechActive)
				//	geneSet = PregnancyUtility.GetInheritedGeneSet(father, mother);
				//	SetParents(mother, father, PregnancyUtility.GetInheritedGeneSet(father, mother));

			}

			float p_end_tick_mods = 1;
			if (babies[0].RaceProps?.gestationPeriodDays < 1)
			{
				if (xxx.is_human(babies[0]))
					p_end_tick_mods = 45 * GenDate.TicksPerDay; //default human
				else
					p_end_tick_mods = GenDate.TicksPerDay;
			}
			else
			{
				p_end_tick_mods = babies[0].RaceProps.gestationPeriodDays * GenDate.TicksPerDay;
			}

			if (pawn.Has(Quirk.Breeder) || pawn.health.hediffSet.HasHediff(HediffDef.Named("FertilityEnhancer")))
				p_end_tick_mods /= 1.25f;

			p_end_tick_mods *= RJWPregnancySettings.normal_pregnancy_duration;

			p_start_tick = Find.TickManager.TicksGame;
			p_end_tick = p_start_tick + p_end_tick_mods;
			lastTick = p_start_tick;

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

		public static T Create<T>(Pawn mother, Pawn father) where T : Hediff_BasePregnancy
		{
			return Create<T>(mother, father, SelectDnaGivingParent(mother, father));
		}

		/// <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, DnaGivingParent dnaGivingParent) 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, dnaGivingParent);
			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[babies.Count-1].RaceProps.gestationPeriodDays * TicksPerDay * RJWPregnancySettings.normal_pregnancy_duration)).ToStringTicksToPeriod());
			return stringBuilder.ToString();
		}
		//public override IEnumerable<Gizmo> GetGizmos()
		//{
		//	foreach (Gizmo gizmo in base.GetGizmos())
		//	{
		//		yield return gizmo;
		//	}
		//	if (!DebugSettings.ShowDevGizmos)
		//	{
		//		yield break;
		//	}
		//	if (CurStageIndex < 2)
		//	{
		//		Command_Action command_Action = new Command_Action();
		//		command_Action.defaultLabel = "DEV: Next trimester";
		//		command_Action.action = delegate
		//		{
		//			HediffStage hediffStage = def.stages[CurStageIndex + 1];
		//			severityInt = hediffStage.minSeverity;
		//		};
		//		yield return command_Action;
		//	}
		//	if (ModsConfig.BiotechActive && pawn.RaceProps.Humanlike && pawn.health.hediffSet.GetFirstHediffOfDef(HediffDefOf.PregnancyLabor) == null)
		//	{
		//		Command_Action command_Action2 = new Command_Action();
		//		command_Action2.defaultLabel = "DEV: Start Labor";
		//		command_Action2.action = delegate
		//		{
		//			StartLabor();
		//			pawn.health.RemoveHediff(this);
		//		};
		//		yield return command_Action2;
		//	}
		//}
		//public void StartLabor()
		//{
		//	if (ModLister.CheckBiotech("labor"))
		//	{
		//		((Hediff_Labor)pawn.health.AddHediff(HediffDefOf.PregnancyLabor)).SetParents(pawn, father, PregnancyUtility.GetInheritedGeneSet(father, pawn));
		//		Hediff firstHediffOfDef = pawn.health.hediffSet.GetFirstHediffOfDef(HediffDefOf.MorningSickness);
		//		if (firstHediffOfDef != null)
		//		{
		//			pawn.health.RemoveHediff(firstHediffOfDef);
		//		}
		//		Hediff firstHediffOfDef2 = pawn.health.hediffSet.GetFirstHediffOfDef(HediffDefOf.PregnancyMood);
		//		if (firstHediffOfDef2 != null)
		//		{
		//			pawn.health.RemoveHediff(firstHediffOfDef2);
		//		}
		//		if (PawnUtility.ShouldSendNotificationAbout(pawn))
		//		{
		//			Find.LetterStack.ReceiveLetter("LetterColonistPregnancyLaborLabel".Translate(pawn), "LetterColonistPregnancyLabor".Translate(pawn), LetterDefOf.NeutralEvent);
		//		}
		//	}
		//}
	}
}


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 = PawnRelationDefOf.Parent;
		//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;

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

				if (!RJWSettings.Disable_bestiality_pregnancy_relations)
				{
					baby.relations.AddDirectRelation(relation_birthgiver, mother);
					if (father != null && mother != father)
					{
						baby.relations.AddDirectRelation(relation_birthgiver, father);
					}
				}
				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.Linq;
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;

			InitializeIfNoBabies();
			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.IsSlaveOfColony)
				{
					//Log.Message("mother.SlaveFaction " + mother.SlaveFaction);
					//Log.Message("mother.HomeFaction " + mother.HomeFaction);
					//Log.Message("mother.Faction " + mother.Faction);

					if (mother.SlaveFaction != null)
						baby.SetFaction(mother.SlaveFaction);
					else if (mother.HomeFaction != null)
						baby.SetFaction(mother.HomeFaction);
					else if (mother.Faction != null)
						baby.SetFaction(mother.Faction);
					else
						baby.SetFaction(Faction.OfPlayer);
					baby.guest.SetGuestStatus(Faction.OfPlayer, GuestStatus.Slave);
				}
				else if (mother.IsPrisonerOfColony)
				{
					//Log.Message("mother.HomeFaction " + mother.HomeFaction);
					if (mother.HomeFaction != null)
						baby.SetFaction(mother.HomeFaction);
					baby.guest.SetGuestStatus(Faction.OfPlayer, GuestStatus.Prisoner);
				}

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

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