From 2d59f5c89e57c1e64c0d749a89904938e7a8fe2e Mon Sep 17 00:00:00 2001
From: Svornost <11434-svornost@users.noreply.gitgud.io>
Date: Tue, 13 Oct 2020 15:11:55 -0700
Subject: [PATCH] Cleanup and type fixes for impregnation

---
 devTools/types/FC/human.d.ts         |  16 ----
 src/js/ibcJS.js                      |   4 +-
 src/js/wombJS.js                     | 124 +++++++++++++--------------
 src/npc/generate/generateGenetics.js |  31 ++-----
 src/player/js/PlayerState.js         |   4 +-
 src/pregmod/surrogacy.tw             |  22 +----
 6 files changed, 75 insertions(+), 126 deletions(-)

diff --git a/devTools/types/FC/human.d.ts b/devTools/types/FC/human.d.ts
index d30ac1dd6e3..4ab7343d54c 100644
--- a/devTools/types/FC/human.d.ts
+++ b/devTools/types/FC/human.d.ts
@@ -382,22 +382,6 @@ declare global {
 			spermY: number;
 			inbreedingCoeff?: number;
 		}
-		interface Fetus {
-			ID: string;
-			/** initial age */
-			age: number;
-			/** initial real age (first week in mother) */
-			realAge: number;
-			motherID: number;
-			/** We can store who is father too. */
-			fatherID: number;
-			volume: number;
-			reserve: string;
-			identical: number;
-			splitted?: number;
-			twinID?: string;
-			genetics?: FetusGenetics;
-		}
 		//#endregion
 
 		type LimbState = InstanceType<typeof App.Entity.LimbState>;
diff --git a/src/js/ibcJS.js b/src/js/ibcJS.js
index df28d1be46b..58e02d5dbcc 100644
--- a/src/js/ibcJS.js
+++ b/src/js/ibcJS.js
@@ -372,8 +372,8 @@ globalThis.ibc = (() => {
 	};
 
 	/** Determine the kinship between slaves `a` and `b`
-	 * @param {IBCRelative} [a] or zero
-	 * @param {IBCRelative} [b] or zero
+	 * @param {IBCRelative|0} [a] or zero
+	 * @param {IBCRelative|0} [b] or zero
 	 * @param {boolean} [ignore_coeffs=false]
 	 * @returns {number}
 	 */
diff --git a/src/js/wombJS.js b/src/js/wombJS.js
index 4b289e49bff..97b31d28bfe 100644
--- a/src/js/wombJS.js
+++ b/src/js/wombJS.js
@@ -107,40 +107,40 @@ globalThis.WombInit = function(actor) {
 	});
 };
 
+App.Entity.Fetus = class {
+	/** Construct a new fetus
+	 * @param {number} age - initial age, after conception, in weeks
+	 * @param {number} fatherID
+	 * @param {FC.HumanState} mother
+	 * @param {string} name - name of ovum (generally ovumNN where NN is the number in the batch)
+	 */
+	constructor(age, fatherID, mother, name) {
+		/** Unique identifier for this fetus */
+		this.ID = generateNewID();
+		/** Week since conception */
+		this.age = age;
+		/** Week in mother (age since implantation) */
+		this.realAge = 1;
+		this.fatherID = fatherID;
+		this.volume = 1;
+		this.reserve = "";
+		/** All identical multiples share the same twinID */
+		this.twinID = "";
+		this.motherID = mother.ID;
+		this.genetics = generateGenetics(mother, fatherID, name);
+	}
+};
+
 /**
- *
  * @param {FC.HumanState} actor
  * @param {number} fCount
  * @param {number} fatherID
  * @param {number} age
- * @param {App.Entity.SlaveState} [surrogate]
+ * @param {FC.HumanState} [surrogate] genetic mother
  */
 globalThis.WombImpregnate = function(actor, fCount, fatherID, age, surrogate) {
-	let i;
-	for (i = 0; i < fCount; i++) {
-		/** @type {FC.Fetus} */
-		const tf = {
-			ID: generateNewID(),
-			age: age, // initial age
-			realAge: 1, // initial real age (first week in mother)
-			fatherID: fatherID, // We can store who is father too.
-			volume: 1, // Initial, to create property. Updated with actual data after WombGetVolume call.
-			reserve: "", // Initial, to create property. Used later to mark if this child is to be kept.
-			identical: 0, // Initial, to create property. Updated with actual data during fetalSplit call.
-			splitted: 0, // marker for already splitted fetus.
-			motherID: surrogate ? surrogate.ID : actor.ID // Initial biological mother ID setup.
-		};
-		if (surrogate) {
-			if (actor.eggType === "human") {
-				tf.genetics = generateGenetics(surrogate, fatherID, i + 1); // Stored genetic information.
-			}
-		} else {
-			if (actor.eggType === "human") {
-				tf.genetics = generateGenetics(actor, fatherID, i + 1); // Stored genetic information.
-			}
-		}
-		tf.ID = generateNewID();
-
+	for (let i = 0; i < fCount; i++) {
+		const tf = new App.Entity.Fetus(age, fatherID, surrogate || actor, `ovum${i}`);
 		try {
 			if (actor.womb.length === 0) {
 				actor.pregWeek = age;
@@ -157,28 +157,29 @@ globalThis.WombImpregnate = function(actor, fCount, fatherID, age, surrogate) {
 	WombUpdatePregVars(actor);
 };
 
+/**
+ * @param {FC.HumanState} actor (surrogate mother)
+ * @param {number} fCount
+ * @param {FC.HumanState} mother (genetic mother)
+ * @param {number} fatherID
+ * @param {number} age
+ */
 globalThis.WombSurrogate = function(actor, fCount, mother, fatherID, age) {
 	WombImpregnate(actor, fCount, fatherID, age, mother);
 };
 
-globalThis.WombImpregnateClone = function(actor, fCount, mother, motherOriginal, age) {
-	let i;
-	for (i = 0; i < fCount; i++) {
-		/** @type {FC.Fetus} */
-		const tf = {
-			ID: generateNewID(),
-			age: age, // initial age
-			realAge: 1, // initial real age (first week in mother)
-			fatherID: mother.ID, // We can store who is father too.
-			volume: 1, // Initial, to create property. Updated with actual data after WombGetVolume call.
-			reserve: "", // Initial, to create property. Used later to mark if this child is to be kept.
-			identical: 0, // Initial, to create property. Updated with actual data during fetalSplit call.
-			splitted: 0, // marker for already splitted fetus.
-			motherID: mother.ID, // Initial biological mother ID setup.
-			genetics: generateGenetics(mother, mother.ID, i + 1) // Stored genetic information.
-		};
-		// set up common relatives for the slave and her clone
-		setMissingParents(mother);
+/**
+ * @param {FC.HumanState} actor (surrogate mother)
+ * @param {number} fCount
+ * @param {FC.HumanState} mother (genetic parent being cloned)
+ * @param {number} age
+ */
+globalThis.WombImpregnateClone = function(actor, fCount, mother, age) {
+	setMissingParents(mother);
+	const motherOriginal = V.genePool.find(s => s.ID === mother.ID) || mother;
+
+	for (let i = 0; i < fCount; i++) {
+		const tf = new App.Entity.Fetus(age, mother.ID, mother, `ovum${i}`);
 
 		// gene corrections
 		tf.fatherID = -7;
@@ -193,6 +194,7 @@ globalThis.WombImpregnateClone = function(actor, fCount, mother, motherOriginal,
 		} else {
 			tf.genetics.motherName = mother.slaveName;
 			tf.genetics.fatherName = mother.slaveName;
+			// @ts-ignore - mother isn't the player, so must be a slave
 			tf.genetics.clone = SlaveFullName(mother);
 			tf.genetics.cloneID = mother.ID;
 		}
@@ -648,34 +650,26 @@ globalThis.WombSort = function(actor) {
 	});
 };
 
-// now function work with chance. Literary we give it "one from X" as chance.
+/** Split fetuses into identical twins based on chance
+ * @param {FC.HumanState} actor
+ * @param {number} chance
+ */
 globalThis.fetalSplit = function(actor, chance) {
-	actor.womb.forEach(function(s) {
-		if ((jsRandom(1, chance) >= chance) && s.splitted !== 1) {
-			/** @type {FC.Fetus} */
-			const nft = {
-				ID: generateNewID(),
-				age: s.age,
-				realAge: s.realAge,
-				fatherID: s.fatherID,
-				motherID: s.motherID,
-				volume: s.volume,
-				reserve: "", // splitted fetus is new separate, reserve - it's not genetic to split.
-				genetics: clone(s.genetics),
-				identical: 1 // this is marker that this fetus has at least one twin.
-			};
-			s.splitted = 1; // this is marker that this is already splitted fetus (to not split second time in loop), only source fetus needed it.
-			s.identical = 1; // this is marker that this fetus has at least one twin.
-
+	for (const s of actor.womb) {
+		if (jsRandom(1, chance) >= chance) {
+			// if this fetus is not already an identical, generate a new twin ID before cloning it
 			if (s.twinID === "" || s.twinID === undefined) {
 				s.twinID = generateNewID();
 			}
 
-			nft.twinID = s.twinID;
+			// clone the fetus with a new fetus ID
+			const nft = clone(s);
+			nft.ID = generateNewID();
+			nft.reserve = ""; // new fetus does not inherit reserve status
 
 			actor.womb.push(nft);
 		}
-	});
+	}
 	WombNormalizePreg(actor);
 };
 
diff --git a/src/npc/generate/generateGenetics.js b/src/npc/generate/generateGenetics.js
index dca973f09e8..ead4214e8da 100644
--- a/src/npc/generate/generateGenetics.js
+++ b/src/npc/generate/generateGenetics.js
@@ -2,8 +2,10 @@
 
 globalThis.generateGenetics = (function() {
 	let mother;
+	/** @type {FC.HumanState|0} */
 	let activeMother;
 	let father;
+	/** @type {FC.HumanState|0} */
 	let activeFather;
 
 
@@ -15,10 +17,10 @@ globalThis.generateGenetics = (function() {
 	/**
 	 * @param {FC.HumanState} actor1
 	 * @param {number} actor2 Slave ID of actor 2
-	 * @param {number} x
+	 * @param {string} name Initial name of ovum
 	 * @returns {FC.FetusGenetics}
 	 */
-	function generateGenetics(actor1, actor2, x) {
+	function generateGenetics(actor1, actor2, name) {
 		const genes = {
 			gender: /** @type {FC.GenderGenes} */ ("XX"),
 			name: "blank",
@@ -91,10 +93,10 @@ globalThis.generateGenetics = (function() {
 		}
 
 		genes.gender = setGender(father, mother);
-		genes.name = setName(x);
-		genes.mother = setMotherID(actor1.ID);
+		genes.name = name;
+		genes.mother = actor1.ID;
 		genes.motherName = setMotherName(activeMother);
-		genes.father = setFatherID(actor2);
+		genes.father = actor2;
 		genes.fatherName = setFatherName(father, activeFather, actor2);
 		genes.inbreedingCoeff = ibc.kinship(mother, father);
 		genes.nationality = setNationality(father, mother);
@@ -172,16 +174,6 @@ globalThis.generateGenetics = (function() {
 		return gender;
 	}
 
-	// name
-	function setName(x) {
-		return `ovum${x}`;
-	}
-
-	// motherID
-	function setMotherID(actor1ID) {
-		return actor1ID;
-	}
-
 	// motherName
 	function setMotherName(activeMother) {
 		let motherName;
@@ -192,11 +184,6 @@ globalThis.generateGenetics = (function() {
 		return motherName;
 	}
 
-	// fatherID
-	function setFatherID(actor2) {
-		return actor2;
-	}
-
 	// fatherName
 	function setFatherName(father, activeFather, actor2) {
 		let fatherName;
@@ -687,8 +674,8 @@ globalThis.generateGenetics = (function() {
 
 	/**
 	 * Genetic quirks
-	 * @param {App.Entity.SlaveState|number} father
-	 * @param {App.Entity.SlaveState} mother
+	 * @param {FC.HumanState|0} father
+	 * @param {FC.HumanState} mother
 	 * @param {string} sex
 	 * @returns {object}
 	 */
diff --git a/src/player/js/PlayerState.js b/src/player/js/PlayerState.js
index c41c21ccbf7..b2216313b96 100644
--- a/src/player/js/PlayerState.js
+++ b/src/player/js/PlayerState.js
@@ -253,7 +253,8 @@ App.Entity.PlayerState = class PlayerState {
 		/** Player's original surname
 		 * @type {string|number} */
 		this.birthSurname = "";
-		/** Player sex ("XX", "XY") */
+		/** Player sex ("XX", "XY")
+		 * @type {FC.GenderGenes} */
 		this.genes = "XY";
 		// exclusive major player variables here
 		/** your title's gender
@@ -1646,6 +1647,7 @@ App.Entity.PlayerState = class PlayerState {
 		this.sexualQuirk = "none";
 		/** 0: does not have; 1: carrier; 2: active
 		 * * heterochromia is an exception. String = active
+		 * @type {FC.GeneticQuirks}
 		 */
 		this.geneticQuirks = {
 			/** Oversized breasts. Increased growth rate, reduced shrink rate. Breasts try to return to oversized state if reduced. */
diff --git a/src/pregmod/surrogacy.tw b/src/pregmod/surrogacy.tw
index dbebf1dbc0e..ef0e0d23470 100644
--- a/src/pregmod/surrogacy.tw
+++ b/src/pregmod/surrogacy.tw
@@ -123,29 +123,11 @@
 	<<if $receptrix.ID == -1>>
 		Since the surgery required only a local anesthetic, you remain fully aware of the procedure as the autosurgery carries it out. You slowly rise to your feet, a hand to your lower belly, appreciating the clone growing within you.
 		<<set $PC.pregKnown = 1>>
-		<<if $donatrix.ID != -1>>
-			<<set _babyDaddy = $genePool.find(function(s) { return s.ID == $donatrix.ID; })>>
-			<<if ndef _babyDaddy>>
-				<<set _babyDaddy = $slaveIndices[$donatrix.ID]>>
-			<</if>>
-			<<run WombImpregnateClone($PC, 1, $donatrix, _babyDaddy, 1)>>
-		<<else>>
-			<<set _babyDaddy = $PC>>
-			<<run WombImpregnateClone($PC, 1, $PC, _babyDaddy, 1)>>
-		<</if>>
+		<<run WombImpregnateClone($PC, 1, $donatrix, 1)>>
 		<<run WombNormalizePreg($PC)>>
 	<<else>>
 		<<set $receptrix.pregKnown = 1>>
-		<<if $donatrix.ID != -1>>
-			<<set _babyDaddy = $genePool.find(function(s) { return s.ID == $donatrix.ID; })>>
-			<<if ndef _babyDaddy>>
-				<<set _babyDaddy = $slaveIndices[$donatrix.ID]>>
-			<</if>>
-			<<run WombImpregnateClone($receptrix, 1, $donatrix, _babyDaddy, 1)>>
-		<<else>>
-			<<set _babyDaddy = $PC>>
-			<<run WombImpregnateClone($receptrix, 1, $PC, _babyDaddy, 1)>>
-		<</if>>
+		<<run WombImpregnateClone($receptrix, 1, $donatrix, 1)>>
 		<<run WombNormalizePreg($receptrix)>>
 		<<setLocalPronouns $receptrix>>
 		<<if $receptrix.fetish == "mindbroken">>
-- 
GitLab