From 87f636b4e92dd2198f16fae38d509acdbc2dd0d4 Mon Sep 17 00:00:00 2001
From: svornost <11434-svornost@users.noreply.gitgud.io>
Date: Fri, 19 Jun 2020 21:24:35 -0700
Subject: [PATCH] Renormalize around [0,100].  Make spermY property actually
 affect sex of offspring in the "normal" non-Adam-Principle case.

---
 .../backwardsCompatibility.js                 |  8 +++-
 .../updateSlaveObject.js                      |  4 ++
 .../nursery/widgets/children/ChildState.js    |  2 +
 .../nursery/widgets/infants/InfantState.js    |  2 +
 src/gui/options/options.tw                    |  4 +-
 src/js/SlaveState.js                          |  2 +
 src/npc/descriptions/descriptionWidgets.js    |  3 ++
 src/npc/generate/generateGenetics.js          | 42 ++++++++++++++++---
 src/npc/generate/generateNewSlaveJS.js        |  1 +
 src/player/js/PlayerState.js                  |  2 +
 src/pregmod/managePersonalAffairs.tw          |  9 ++++
 11 files changed, 70 insertions(+), 9 deletions(-)

diff --git a/src/data/backwardsCompatibility/backwardsCompatibility.js b/src/data/backwardsCompatibility/backwardsCompatibility.js
index ed44cd202d2..07ce5966817 100644
--- a/src/data/backwardsCompatibility/backwardsCompatibility.js
+++ b/src/data/backwardsCompatibility/backwardsCompatibility.js
@@ -1252,8 +1252,11 @@ App.Update.slaveRecords = function(node) {
 		nurseryDiv.append(`Checking and fixing records for nursery cribs... `);
 		V.cribs.forEach((child) => {
 			if (child.actualAge < 3) {
-				// infants are not slaves, they need their own update code (but there isn't any yet)
+				// infants are not slaves, they need their own update code (but there isn't much yet)
 				// note that some infants have been *converted into* corrupted slaves by bad old BCs...no attempt is made to fix them here
+				if (child.spermY === undefined) {
+					child.spermY = normalRandInt(50, 5);
+				}
 				App.Facilities.Nursery.InfantDatatypeCleanup(child);
 			} else {
 				App.Update.Slave(child);
@@ -2033,6 +2036,9 @@ App.Update.oldVersions = function(node) {
 			V.PC.faceShape = "normal";
 		}
 	}
+	if (typeof V.PC.spermY === "undefined") {
+		V.PC.spermY = 50; // exactly
+	}
 
 	if ((typeof V.familyTesting === "undefined") && V.releaseID < 1065) {
 		// possibly vanilla FC; compel V.familyTesting to 0 so that the family upgrade will run on slaves
diff --git a/src/data/backwardsCompatibility/updateSlaveObject.js b/src/data/backwardsCompatibility/updateSlaveObject.js
index 0c05bba36ab..0e63c24a3c0 100644
--- a/src/data/backwardsCompatibility/updateSlaveObject.js
+++ b/src/data/backwardsCompatibility/updateSlaveObject.js
@@ -1007,6 +1007,10 @@ App.Update.Slave = function(slave, genepool = false) {
 	delete slave.relationTarget;
 	delete slave.recruiter;
 
+	if (slave.spermY === undefined) {
+		slave.spermY = normalRandInt(50, 3); // narrower range to avoid surprises
+	}
+
 	if (slave.geneticQuirks.albinism === 2 && !slave.albinismOverride) {
 		induceAlbinism(slave, 2);
 	}
diff --git a/src/facilities/nursery/widgets/children/ChildState.js b/src/facilities/nursery/widgets/children/ChildState.js
index 8b787dd8980..8de31f2d3e8 100644
--- a/src/facilities/nursery/widgets/children/ChildState.js
+++ b/src/facilities/nursery/widgets/children/ChildState.js
@@ -1568,6 +1568,8 @@ App.Facilities.Nursery.ChildState = class ChildState {
 			/** Slave can only ever birth girls */
 			girlsOnly: 0
 		};
+		/** chance of generating sperm with a Y chromosome (yields male baby). inherited by sons, with mutation */
+		this.spermY = 50;
 		/** Counts various acts slave participated in */
 		this.counter = new App.Entity.ChildActionsCountersState();
 		/** Values provided by players */
diff --git a/src/facilities/nursery/widgets/infants/InfantState.js b/src/facilities/nursery/widgets/infants/InfantState.js
index 87f0b6a7545..f50c3506b54 100644
--- a/src/facilities/nursery/widgets/infants/InfantState.js
+++ b/src/facilities/nursery/widgets/infants/InfantState.js
@@ -183,6 +183,8 @@ App.Facilities.Nursery.InfantState = class InfantState {
 			/** child can only ever birth girls */
 			girlsOnly: 0
 		};
+		/** chance of generating sperm with a Y chromosome (yields male baby). inherited by sons, with mutation */
+		this.spermY = 50;
 		/** how many weeks until the child is ready for release */
 		this.growTime = 156;
 	}
diff --git a/src/gui/options/options.tw b/src/gui/options/options.tw
index 46bd823470b..8cd9ef8151e 100644
--- a/src/gui/options/options.tw
+++ b/src/gui/options/options.tw
@@ -302,8 +302,8 @@
 		.addValue("Enabled", 1).on().addValue("Disabled", 0).off()
 		.addComment("<<if $seeDicksAffectsPregnancy === 1>>Currently <<print $seeDicks>>% of children will be born male. <</if>>Will not affect existing pregnancies already in-game.")>>
 
-		<<if $seeDicksAffectsPregnancy > 0>>
-			<<run _options.addOption("XX slaves can have sons", "adamPrinciple")
+		<<if $seeDicksAffectsPregnancy === 0>>
+			<<run _options.addOption("XX slaves only father daughters", "adamPrinciple")
 			.addValue("Enabled", 1).on().addValue("Disabled", 0).off()
 			.addComment("Will not affect existing pregnancies already in-game.")>>
 		<</if>>
diff --git a/src/js/SlaveState.js b/src/js/SlaveState.js
index cd6e9f7e093..861d83d377f 100644
--- a/src/js/SlaveState.js
+++ b/src/js/SlaveState.js
@@ -2124,6 +2124,8 @@ App.Entity.SlaveState = class SlaveState {
 			/** slave can only ever birth girls */
 			girlsOnly: 0
 		};
+		/** chance of generating sperm with a Y chromosome (yields male baby). inherited by sons, with mutation */
+		this.spermY = 50;
 		/** Counts various acts slave participated in */
 		this.counter = new App.Entity.SlaveActionsCountersState();
 		/** Values provided by players */
diff --git a/src/npc/descriptions/descriptionWidgets.js b/src/npc/descriptions/descriptionWidgets.js
index b9f5035a7c0..fb2d4a310b6 100644
--- a/src/npc/descriptions/descriptionWidgets.js
+++ b/src/npc/descriptions/descriptionWidgets.js
@@ -1536,6 +1536,9 @@ App.Desc.geneticQuirkAssessment = function(slave) {
 		if (slave.geneticQuirks.mLoss === 1 && V.geneticMappingUpgrade >= 2) {
 			r.push(`${He} is a myotonic dystrophy carrier.`);
 		}
+		if (slave.genes === "XY" && !V.seeDicksAffectsPregnancy) {
+			r.push(`Analysis of ${his} sperm shows that ${he} has a ${slave.spermY}% chance of fathering a son.`);
+		}
 	}
 	return r.join(` `);
 };
diff --git a/src/npc/generate/generateGenetics.js b/src/npc/generate/generateGenetics.js
index ff7dd2751c6..250afa87426 100644
--- a/src/npc/generate/generateGenetics.js
+++ b/src/npc/generate/generateGenetics.js
@@ -44,7 +44,8 @@ globalThis.generateGenetics = (function() {
 			clone: 0,
 			cloneID: 0,
 			geneticQuirks: {},
-			fetish: "none"
+			fetish: "none",
+			spermY: 50
 		};
 		if (actor1.ID > 0) {
 			mother = V.genePool.find(s => s.ID === actor1.ID);
@@ -114,10 +115,28 @@ globalThis.generateGenetics = (function() {
 		genes.sexualFlaw = setSexualFlaw(father, mother);
 		genes.behavioralFlaw = setBehavioralFlaw(father, mother);
 		genes.fetish = setFetish(father, mother);
+		genes.spermY = setSpermY(father, mother);
 
 		return genes;
 	}
 
+	// get spermY value of the parent that's donating the Y chromosome
+	function getSpermY(father, mother) {
+		let sourceSpermY = 50; // default if no inherited Y chromosome (should be impossible, but the Adam Principle is optional, so it can happen)
+		if (father !== 0 && father.genes === "XY") {
+			sourceSpermY = father.spermY;
+		} else if (mother.genes === "XY") {
+			sourceSpermY = father.spermY;
+		}
+		return sourceSpermY;
+	}
+
+	// generation chance of y-chormosome carrying sperm
+	function setSpermY(father, mother) {
+		// Y-linked trait, so figure out where the Y chromosome is coming from and start from there
+		return normalRandInt(getSpermY(father, mother), 5); // mutation
+	}
+
 	// gender
 	function setGender(father, mother) {
 		let gender;
@@ -128,17 +147,26 @@ globalThis.generateGenetics = (function() {
 		} else if (V.adamPrinciple === 1) {
 			if (father !== 0) {
 				if (father.genes === "XX" && mother.genes === "XX") {
-					gender = "XX";
+					gender = "XX"; // neither parent has a Y chromosome, it's definitely a girl
 				} else if (father.genes !== mother.genes) {
-					gender = jsEither(["XX", "XY"]);
+					gender = jsRandom(0, 99) < getSpermY(father, mother) ? "XY" : "XX"; // "normal" conception
 				} else {
-					gender = jsEither(["XX", "XY", "XY", "YY"]);
+					// both parents have a Y chromosome that they could donate; treat them as independent events
+					const motherY = jsRandom(0, 99) < mother.spermY;
+					const fatherY = jsRandom(0, 99) < father.spermY;
+					if (motherY && fatherY) {
+						gender = "YY"; // inviable, but retain for now
+					} else if (!motherY && !fatherY) {
+						gender = "XX"; // it's a girl!
+					} else {
+						gender = "XY"; // it's a boy!
+					}
 				}
 			} else {
-				gender = jsEither(["XX", "XY"]);
+				gender = jsRandom(0, 99) < getSpermY(father, mother) ? "XY" : "XX";
 			}
 		} else {
-			gender = jsEither(["XX", "XY"]);
+			gender = jsRandom(0, 99) < getSpermY(father, mother) ? "XY" : "XX";
 		}
 		return gender;
 	}
@@ -1156,6 +1184,7 @@ globalThis.generateChild = function (mother, ovum, incubator=false) {
 		child.origHColor = genes.hColor;
 		child.skin = getGeneticSkinColor(child);
 		child.hColor = getGeneticHairColor(child);
+		child.spermY = genes.spermY;
 		child.pubicHColor = child.hColor;
 		child.underArmHColor = child.hColor;
 		child.eyebrowHColor = child.hColor;
@@ -1209,6 +1238,7 @@ globalThis.generateChild = function (mother, ovum, incubator=false) {
 		child.origHColor = genes.hColor;
 		child.skin = getGeneticSkinColor(child);
 		child.hColor = getGeneticHairColor(child);
+		child.spermY = genes.spermY;
 		resetEyeColor(child, "both");
 		child.pubicHColor = child.hColor;
 		child.underArmHColor = child.hColor;
diff --git a/src/npc/generate/generateNewSlaveJS.js b/src/npc/generate/generateNewSlaveJS.js
index 61b02bfe171..b3c83d8531c 100644
--- a/src/npc/generate/generateNewSlaveJS.js
+++ b/src/npc/generate/generateNewSlaveJS.js
@@ -103,6 +103,7 @@ globalThis.GenerateNewSlave = (function() {
 		slave.hColor = getGeneticHairColor(slave);
 		slave.skin = getGeneticSkinColor(slave);
 		resetEyeColor(slave, "both");
+		slave.spermY = normalRandInt(50, 5);
 	}
 
 	function GenerateXXSlave() {
diff --git a/src/player/js/PlayerState.js b/src/player/js/PlayerState.js
index 115a70c6f1b..4efbdaf320a 100644
--- a/src/player/js/PlayerState.js
+++ b/src/player/js/PlayerState.js
@@ -1691,6 +1691,8 @@ App.Entity.PlayerState = class PlayerState {
 			/** slave can only ever birth girls */
 			girlsOnly: 0
 		};
+		/** chance of generating sperm with a Y chromosome (yields male baby). inherited by sons, with mutation */
+		this.spermY = 50;
 		/** Counts various thing you have done in */
 		this.counter = new App.Entity.PlayerActionsCountersState();
 		/** Values provided by players */
diff --git a/src/pregmod/managePersonalAffairs.tw b/src/pregmod/managePersonalAffairs.tw
index f782157ba7d..8a212bc09f3 100644
--- a/src/pregmod/managePersonalAffairs.tw
+++ b/src/pregmod/managePersonalAffairs.tw
@@ -17,6 +17,15 @@
 	<div>
 		You pause for a moment from your busy day to day life to return to <<if $masterSuite != 0>>$masterSuiteName<<else>>your room<</if>> to consider some things about yourself.
 	</div>
+
+<<if $geneticMappingUpgrade >= 1>>
+	<div>
+		<<if $PC.genes === "XY" && $seeDicksAffectsPregnancy === 0>>
+			Analysis of your sperm shows that you have a <<= $PC.spermY>>% chance of fathering a son.
+		<</if>>
+	</div>
+<</if>>
+
 	<div>
 		You take yourself in a full length mirror. You are <<= addA($PC.race)>> <<if $PC.dick != 0 && $PC.vagina != -1>>futanari<<elseif $PC.dick != 0>>man<<else>>woman<</if>> with<<if $PC.markings == "freckles">> freckled<<elseif $PC.markings == "heavily freckled">> heavily freckled<</if>> <<print $PC.skin>> skin, $PC.hColor hair, <<print App.Desc.eyesColor($PC)>> and a perfect $PC.faceShape face.
 		<<if $PC.actualAge >= 65>>
-- 
GitLab