code:
App.Patch.register({
	releaseID: 1259,
	descriptionOfChanges: "Move V.genePool from an array to an object; Add App.Entity.GenePoolRecord",
	pre: (div) => {
		App.Patch.log("Adding V.genePoolDefaults");
		// we use a static copy of App.Entity.GenePoolRecord here as it may have changed since this patch was released
		// @ts-expect-error V.genePoolDefaults is a read only property
		V.genePoolDefaults = {
			"releaseID": 1259,
			"seed": "",
			"ID": 0,
			"slaveName": "blank",
			"slaveSurname": 0,
			"birthName": "blank",
			"birthSurname": 0,
			"pronoun": 0,
			"genes": "XX",
			"natural": {
				"height": 170,
				"boobs": 500,
				"artSeed": 13552227457244
			},
			"father": 0,
			"mother": 0,
			"clone": 0,
			"cloneID": 0,
			"birthWeek": 2,
			"actualAge": 18,
			"visualAge": 18,
			"physicalAge": 18,
			"ovaryAge": 18,
			"nationality": "slave",
			"race": "white",
			"origRace": "white",
			"career": "a slave",
			"health": {
				"condition": 0,
				"shortDamage": 0,
				"longDamage": 0,
				"illness": 0,
				"tired": 0,
				"health": 0
			},
			"markings": "none",
			"eye": {
				"left": {
					"type": 1,
					"vision": 2,
					"iris": "brown",
					"pupil": "circular",
					"sclera": "white"
				},
				"right": {
					"type": 1,
					"vision": 2,
					"iris": "brown",
					"pupil": "circular",
					"sclera": "white"
				},
				"origColor": "brown"
			},
			"hears": 0,
			"smells": 0,
			"tastes": 0,
			"voice": 2,
			"voiceImplant": 0,
			"electrolarynx": 0,
			"accent": 0,
			"earImplant": 0,
			"earShape": "normal",
			"earT": "none",
			"earTNatural": 0,
			"earTColor": "hairless",
			"earTEffectColor": "none",
			"earTEffect": "none",
			"horn": "none",
			"hornColor": "none",
			"PLimb": 0,
			"leg": {
				"left": {
					"type": 1,
					"scar": {},
					"brand": {}
				},
				"right": {
					"type": 1,
					"scar": {},
					"brand": {}
				}
			},
			"arm": {
				"left": {
					"type": 1,
					"scar": {},
					"brand": {}
				},
				"right": {
					"type": 1,
					"scar": {},
					"brand": {}
				}
			},
			"heels": 0,
			"tail": "none",
			"PTail": 0,
			"tailShape": "none",
			"tailColor": "none",
			"tailEffectColor": "none",
			"tailEffect": "none",
			"appendages": "none",
			"appendagesColor": "none",
			"appendagesEffectColor": "none",
			"appendagesEffect": "none",
			"PBack": 0,
			"wingsShape": "none",
			"patternColor": "black",
			"hColor": "brown",
			"origHColor": "brown",
			"hEffectColor": "none",
			"hEffect": "none",
			"pubicHColor": "brown",
			"underArmHColor": "brown",
			"eyebrowHColor": "brown",
			"hLength": 60,
			"eyebrowFullness": "natural",
			"hStyle": "neat",
			"pubicHStyle": "neat",
			"underArmHStyle": "neat",
			"eyebrowHStyle": "natural",
			"skin": "light",
			"origSkin": "light",
			"face": 0,
			"faceImplant": 0,
			"ageImplant": 0,
			"faceShape": "normal",
			"lips": 15,
			"lipsImplant": 0,
			"lipsTat": 0,
			"teeth": "normal",
			"weight": 0,
			"muscles": 0,
			"height": 170,
			"heightImplant": 0,
			"waist": 0,
			"hips": 0,
			"hipsImplant": 0,
			"butt": 0,
			"buttImplant": 0,
			"buttImplantType": "none",
			"buttTat": 0,
			"shoulders": 0,
			"shouldersImplant": 0,
			"boobs": 0,
			"boobsImplant": 0,
			"boobsImplantType": "none",
			"boobShape": "normal",
			"nipples": "cute",
			"areolae": 0,
			"areolaeShape": "circle",
			"boobsTat": 0,
			"vagina": 0,
			"vaginaLube": 0,
			"vaginaTat": 0,
			"ovaImplant": 0,
			"wombImplant": "none",
			"ballType": "human",
			"eggType": "human",
			"broodmother": 0,
			"broodmotherFetuses": 0,
			"broodmotherOnHold": 0,
			"broodmotherCountDown": 0,
			"labia": 0,
			"clit": 0,
			"foreskin": 0,
			"anus": 0,
			"analArea": 1,
			"dick": 0,
			"dickTat": 0,
			"prostate": 0,
			"prostateImplant": 0,
			"balls": 0,
			"ballsImplant": 0,
			"scrotum": 0,
			"ovaries": 0,
			"anusTat": 0,
			"brand": {},
			"piercing": {
				"ear": {
					"weight": 0,
					"desc": ""
				},
				"nose": {
					"weight": 0,
					"desc": ""
				},
				"eyebrow": {
					"weight": 0,
					"desc": ""
				},
				"lips": {
					"weight": 0,
					"desc": ""
				},
				"tongue": {
					"weight": 0,
					"desc": ""
				},
				"nipple": {
					"weight": 0,
					"desc": ""
				},
				"areola": {
					"weight": 0,
					"desc": ""
				},
				"navel": {
					"weight": 0,
					"desc": ""
				},
				"corset": {
					"weight": 0,
					"desc": ""
				},
				"genitals": {
					"weight": 0,
					"desc": "",
					"smart": false
				},
				"vagina": {
					"weight": 0,
					"desc": ""
				},
				"dick": {
					"weight": 0,
					"desc": ""
				},
				"anus": {
					"weight": 0,
					"desc": ""
				}
			},
			"shouldersTat": 0,
			"armsTat": 0,
			"legsTat": 0,
			"backTat": 0,
			"stampTat": 0,
			"chem": 0,
			"addict": 0,
			"devotion": 0,
			"intelligence": 0,
			"intelligenceImplant": 0,
			"geneticQuirks": {
				"macromastia": 0,
				"gigantomastia": 0,
				"potent": 0,
				"fertility": 0,
				"hyperFertility": 0,
				"superfetation": 0,
				"polyhydramnios": 0,
				"uterineHypersensitivity": 0,
				"galactorrhea": 0,
				"gigantism": 0,
				"dwarfism": 0,
				"neoteny": 0,
				"progeria": 0,
				"pFace": 0,
				"uFace": 0,
				"albinism": 0,
				"heterochromia": 0,
				"rearLipedema": 0,
				"wellHung": 0,
				"wGain": 0,
				"wLoss": 0,
				"androgyny": 0,
				"mGain": 0,
				"mLoss": 0,
				"twinning": 0,
				"girlsOnly": 0
			},
			"spermY": 50,
			"bellyTat": 0,
			"abortionTat": -1,
			"birthsTat": -1,
			"pubertyAgeXX": 13,
			"pubertyXX": 0,
			"pubertyAgeXY": 13,
			"pubertyXY": 0,
			"scar": {},
			"premature": 0,
			"vasectomy": 0,
			"hormoneBalance": 0,
			"behavioralFlaw": "none",
			"behavioralQuirk": "none",
			"sexualFlaw": "none",
			"sexualQuirk": "none",
			"geneMods": {
				"NCS": 0,
				"rapidCellGrowth": 0,
				"immortality": 0,
				"flavoring": 0,
				"aggressiveSperm": 0,
				"livestock": 0,
				"progenitor": 0
			},
			"milkFlavor": "none",
			"albinismOverride": null,
			"inbreedingCoeff": 0,
			"trueVirgin": 0
		};

		/** @type {FC.GenePoolRecord[]} */
		App.Patch.log("Moving V.genePool from an array to an object");
		// @ts-ignore
		const GenePoolArray = /** @type {FC.GenePoolRecord[]} */ (_.cloneDeep(V.genePool));
		// @ts-expect-error V.genePoolDefaults is a read only property
		V.genePool = {};
		GenePoolArray.forEach((record) => {
			// we exclude duplicates because the new system cannot have duplicate IDs
			// and the old system just grabbed the first match for a given ID
			if (!isInGenePool(/** @type {FC.HumanState} */ (record))) {
				// send the record through verification to make sure it is up to date
				if (record.ID === -1) {
					App.Verify.playerState(/** @type {FC.PlayerState} */ (record), "PC GenePool record", div);
				} else {
					App.Verify.slaveState(
						`Slave GenePool record for ID '${record.ID}'`,
						/** @type {FC.SlaveState} */ (record),
						"gene pool",
						div
					);
				}
				// App.Verify.*State can add missing records to the gene pool
				// we want to make sure all records are complete and up to data
				// so delete any existing record for this id
				deleteGenePoolRecord(/** @type {FC.HumanState} */ (record), true);
				// add the record to the gene pool
				addToGenePool(/** @type {FC.HumanState} */ (record));
			}
		});
	},
});


code:
App.Patch.register({
	releaseID: 1260,
	descriptionOfChanges: "Adds indices for V.incubator.tanks to match V.slaves and V.cribs",
	pre: (div) => {
		if (V.incubator.capacity > 0) {
			App.Patch.log("Adding V.incubator.tankIndices");
			// @ts-expect-error V.incubator.tanks was an array at this point
			V.incubator.tankIndices = V.incubator.tanks.reduce((acc, tank, i) => { acc[tank.ID] = i; return acc; }, {});
		}
	}
});

repo_name: Nithhogg/fc

license: mit


code:
App.Patch.register({
	releaseID: 1261,
	descriptionOfChanges: "Some actors have eye objects that are missing iris and origColor values and some have missing/corrupted partners props, this fixes that",
	humanState: (div, actor, location) => {
		if (!("partners" in actor)) {
			App.Patch.log(`Adding partners set`);
			setProp(actor, "partners", new Set());
		}
		if (!(actor.partners instanceof Set)) {
			if (Array.isArray(actor.partners)) {
				actor.partners = new Set(getProp(actor, "partners"));
			} else {
				App.Patch.log(`partners was '${Serial.stringify(actor.partners)}' of type '${typeof actor.partners}', setting partners to an empty set`);
				actor.partners = new Set([]);
			}
		}

		const origEye = clone(actor?.eye);

		const origColor = actor?.eye?.origColor ??
			actor?.eye?.left?.iris ??
			actor?.eye?.right?.iris ??
			getProp(actor, "eyeColor", randomRaceEye(actor.race));
		const defaultEye = {
			"left": {
				"type": 1,
				"vision": 2,
				"iris": origColor,
				"pupil": "circular",
				"sclera": "white"
			},
			"right": {
				"type": 1,
				"vision": 2,
				"iris": origColor,
				"pupil": "circular",
				"sclera": "white"
			},
			"origColor": origColor
		};
		actor.eye = actor.eye ?? defaultEye;
		// we do left and right separately because if one is missing then they are missing that eye and we don't want to add it back
		if (actor.eye.left) {
			App.Utils.assignMissingDefaults(actor.eye.left, defaultEye.left);
		}
		if (actor.eye.right) {
			App.Utils.assignMissingDefaults(actor.eye.right, defaultEye.right);
		}
		actor.eye.origColor = actor.eye.origColor ?? origColor;
		if (Serial.stringify(origEye) !== Serial.stringify(actor.eye)) {
			App.Patch.log('Fixed corrupted eye object');
		}
		deleteProps(actor, "eyeColor");
		return actor;
	},
	fetus: (div, fetus, mother) => {
		fetus.genetics.eyeColor = fetus.genetics.eyeColor ?? mother?.eye?.origColor ?? randomRaceEye(fetus.genetics.race);
		return fetus;
	}
});


code:
App.Patch.register({
	releaseID: 1262,
	descriptionOfChanges: "The hero slave system has been reworked giving each database its own ID range; This makes sure to fix the IDs in V.heroSlavesPurchased",
	pre: (div) => {
		if (V.heroSlavesPurchased.length !== 0) {
			App.Patch.log("Reassigning purchased hero slave IDs");
			/** @type {{[key: number]: number}} old ID: new ID */
			const lookup = {
				// XX
				// No need all the IDs stayed the same
				// XX Incest
				900110: 902001,
				900111: 902002,
				900113: 902003,
				// XX Hyper Preg
				907001: 904001,
				900114: 904002,
				// XX Fruit Girls
				// I really wish I had just left these in the 700000 range, oh well
				700001: 906001,
				700002: 906002,
				700003: 906003,
				700004: 906004,
				700005: 906005,
				700006: 906006,
				700007: 906007,
				700008: 906008,
				// XX Fruit Girls Extreme
				700009: 907001,
				// XX Extreme
				900099: 908001,
				900100: 908002,
				900101: 908003,
				900102: 908004,
				900104: 908005,
				900105: 908006,
				900106: 908007,
				900107: 908008,
				900108: 908009,
				900109: 908010,
				// XY
				// No need all the IDs stayed the same
				// XY Incest
				800038: 802001,
				// XY Hyper Preg
				// No need because their aren't any yet
				// XX Extreme
				800036: 808001,
				800037: 808002,
			};
			V.heroSlavesPurchased.forEach((value, index, purchased) => {
				if (value in lookup) {
					purchased[index] = lookup[value];
				}
			});
		}
	}
});


code:
App.Patch.register({
	releaseID: 1263,
	descriptionOfChanges: "V.incubator.upgrade must be moved to V.incubator.upgrades before work on further facility conversions can advance.",
	pre: (div) => {
		V.incubator.upgrades = clone(V.incubator.upgrade);
		deleteProps(V.incubator, "upgrade");
	}
});


code:
App.Patch.register({
	releaseID: 1264,
	descriptionOfChanges: `Some old saves incorrectly serialized 'undefined' as '[ "(revive:eval)", "undefined" ]'`,
	slaveState: (div, actor, location) => {
		if (typeof actor.clothingBaseColor !== 'string' && actor.clothingBaseColor !== undefined) {
			actor.clothingBaseColor = undefined;
			App.Patch.log("Fixed corrupted 'clothingBaseColor'");
		}
		if (typeof actor.glassesColor !== 'string' && actor.glassesColor !== undefined) {
			actor.glassesColor = undefined;
			App.Patch.log("Fixed corrupted 'glassesColor'");
		}
		if (typeof actor.shoeColor !== 'string' && actor.shoeColor !== undefined) {
			actor.shoeColor = undefined;
			App.Patch.log("Fixed corrupted 'shoeColor'");
		}
		if (typeof actor.kindness !== 'string' && actor.kindness !== undefined) {
			actor.kindness = undefined;
			App.Patch.log("Fixed corrupted 'kindness'");
		}

		return actor;
	},
});


code:
App.Patch.register({
	releaseID: 1265,
	descriptionOfChanges: "Remove incubatorSettings from all HumanStates except for TankSlaveStates",
	humanState: (div, actor, location) => {
		if (location !== "tank slave pool" && "incubatorSettings" in actor) {
			deleteProps(actor, "incubatorSettings");
		}
		return actor;
	}
});


code:
App.Patch.register({
	releaseID: 1266,
	descriptionOfChanges: `Recent changes to SC meant that imported/exported slaves were not serialized correctly. This 'fixes' (data is lost) those slaves.`,
	humanState: (div, actor, location) => {
		if (!(actor.partners instanceof Set)) {
			actor.partners = new Set([]);
		}
		return actor;
	}
});


code:
App.Patch.register({
	releaseID: 1267,
	descriptionOfChanges: "Adds eye color to fetuses if it is missing due to bugs in getGenePoolRecord()",
	fetus: (div, fetus, mother) => {
		if (fetus.genetics.eyeColor === undefined) {
			const motherColor = getGenePoolRecord(mother).eye.origColor;
			fetus.genetics.eyeColor = motherColor;
			App.Patch.log(`Setting undefined eye color to '${motherColor}'`);
		}
		return fetus;
	}
});


code:
App.Patch.register({
	releaseID: 1268,
	descriptionOfChanges: "Converts V.servantsQuarters into an object",
	pre: () => {
		V.servantsQuarters = {
			capacity: Math.max(+V.servantsQuarters, 0) ?? 0,
			name: getProp(V, "servantsQuartersName", "the Servants' Quarters"),
			decoration: getProp(V, "servantsQuartersDecoration", "standard"),
			upgrades: {
				monitoring: Math.clamp(+getProp(V, "servantsQuartersUpgradeMonitoring", 0), 0, 1),
			},
			rules: {
				stewardessImpregnates: Math.clamp(+getProp(V, "stewardessImpregnates", 0), 0, 1),
			},
		};
		deleteProps(V,
			"servantsQuartersName",
			"servantsQuartersDecoration",
			"servantsQuartersUpgradeMonitoring",
			"stewardessImpregnates"
		);
		// Pre-conversion saves don't have the same name for the monitoring upgrade, which messes up V.costs
		// calculation into a NaN if the save is set in Main. We must recalculate costs and set V.costs before
		// getting back to Main or we'll get a NaN variable stuck in V.NaNArray for the week despite the calculation
		// being correct post-BC.
		V.costs = Math.trunc(calculateCosts.predict());
	}
});


code:
App.Patch.register({
	releaseID: 1269,
	descriptionOfChanges: "Moves the detached slaves into V.detached",
	pre: (div) => {
		App.Patch.log("Moving detached slaves");
		V.detached = {
			"traitor": {
				"actor": getProp(V, "traitor"),
				"type": getProp(V, "traitorType"),
				"weeks": getProp(V, "traitorWeeks"),
				"stats": getProp(V, "traitorStats"),
			},
			"hostage": {
				"actor": getProp(V, "hostage"),
				"wife": getProp(V, "hostageWife"),
			},
			"shelter": {
				"actor": getProp(V, "shelterSlave"),
				"bought": getProp(V, "shelterSlaveBought"),
			},
			"boomerang": {
				"actor": getProp(V, "boomerangSlave"),
				"buyer": getProp(V, "boomerangBuyer"),
				"weeks": getProp(V, "boomerangWeeks"),
				"stats": getProp(V, "boomerangStats"),
			}
		};
		deleteProps(V,
			"traitor", "traitorType", "traitorWeeks", "traitorStats",
			"hostage", "hostageWife",
			"shelterSlave", "shelterSlaveBought",
			"boomerangSlave", "boomerangBuyer", "boomerangWeeks", "boomerangStats",
		);
	}
});


code:
App.Patch.register({
	releaseID: 1270,
	descriptionOfChanges: "Moves most temporary variables to V.transistion",
	pre: (div) => {
		App.Patch.log("Moving variables");
		V.temp = {
			newCheatGirls: getProp(V, "newCheatGirls"),
			limitedCheatStart: getProp(V, "limitedCheatStart") === 1,
			AS: getProp(V, "AS"),
			activeFetus: getProp(V, "activeFetus"),
			gameover: getProp(V, "gameover"),
			prostheticsConfig: getProp(V, "prostheticsConfig"),
			readySlave: getProp(V, "readySlave"),
			newSlavePool: getProp(V, "newSlavePool"),
			slavesToImportMax: getProp(V, "slavesToImportMax"),
			activeArcologyIdx: getProp(V, "activeArcologyIdx"),
			showEmptyBudgetEntries: getProp(V, "showAllEntries"),
			cheatActor: getProp(V, "tempActor"),
			activeSlave: getProp(V, "activeSlave"),
			customLanguage: getProp(V, "seed"),
			applyCareerBonus: getProp(V, "applyCareerBonus"),
			careerBonusNeeded: getProp(V, "careerBonusNeeded"),
		};
		App.Utils.moveProperties(V.temp.showEmptyBudgetEntries, V.temp.showEmptyBudgetEntries, {
			cash: "costsBudget",
			rep: "repBudget",
		},
		false, true);
		deleteProps(V,
			// moved to V.temp above
			"newCheatGirls", "limitedCheatStart", "AS", "activeFetus",
			"gameover", "prostheticsConfig", "readySlave", "newSlavePool",
			"slavesToImportMax", "activeArcologyIdx", "showAllEntries",
			"tempActor", "activeSlave", "seed",
			"applyCareerBonus", "careerBonusNeeded",

			// removed because they were unused
			"oldLimbs", "backupActor",
		);
	}
});


code:
App.Patch.register({
	releaseID: 1271,
	descriptionOfChanges: "Remove incubator temporaries which are no longer used",
	pre: (div) => {
		App.Patch.log("Removing incubator temporaries");
		deleteProps(V.temp,	"readySlave", "newSlavePool");
		deleteProps(V.incubator, "readySlaves");
	}
});


code:
App.Patch.register({
	releaseID: 1272,
	descriptionOfChanges: "Remove slave pool indices",
	pre: (div) => {
		App.Patch.log("Removing slave pool indices");
		deleteProps(V,
			"slaveIndices",
			"cribsIndices",
		);
		deleteProps(V.incubator, "tankIndices");
		App.Patch.log("Converting slave pools to maps");
		const slaves = new Map();
		const cribs = new Map();
		const tanks = new Map();
		V.slaves.forEach(s => { if (s !== null) { slaves.set(s.ID, s); } });
		V.cribs.forEach(s => { if (s !== null) { cribs.set(s.ID, s); } });
		V.incubator.tanks.forEach(s => { if (s !== null) { tanks.set(s.ID, s); } });
		V.slaves = slaves;
		V.cribs = cribs;
		V.incubator.tanks = tanks;
	}
});


code:
App.Patch.register({
	releaseID: 1273,
	descriptionOfChanges: "Add clothes toggle to cheat mode",
	pre: (div) => {
		if (V.cheatMode === 1) {
			V.cheatClothes = 1;
		} else {
			V.cheatClothes = 0;
		}
	}
});


code:
App.Patch.register({
	releaseID: 1274,
	descriptionOfChanges: "Adds passage history directly into FC instead of relying on SC patches",
	pre: (div) => {
		V.passageHistory = V.passageHistory ?? [];
	}
});


code:
App.Patch.register({
	releaseID: 1275,
	descriptionOfChanges: "Add a flag to facilities to indicate whether they're located in the penthouse or not",
	pre: (div) => {
		App.Patch.log("Patching facility location");
		for (const establishedFacility of Object.values(App.Entity.facilities).filter(f => f.established)) {
			let baseName = establishedFacility.desc.baseName;
			if (baseName === "dojo") {
				baseName = "armory";
			} else if (baseName === "HGSuite") {
				baseName = "headGirlSuite";
			}
			console.log(baseName, App.Data.Facilities[baseName]);
			/** @type {FC.FacilityFramework} */
			const desc = App.Data.Facilities[baseName];
			establishedFacility.isInPenthouse = desc.penthouseFacility;
		}
	}
});


code:
App.Patch.register({
	releaseID: 1276,
	descriptionOfChanges: "Adds slave history for stat tracking.",
	pre: (div) => {
		setProp(V, "slaveHistory", 0);
	},
	slaveState: (div, actor, location) => {
		actor.history = actor.history ?? [];
		return actor;
	}
});


code:
App.Patch.register({
	releaseID: 1277,
	descriptionOfChanges: "Adds extra values to existing MissingParentRecords.",
	pre: (div) => {
		for (const [ID, record] of Object.entries(V.missingTable)) {
			const isKing = ["the late king", "the former king"].includes(record.slaveName.toLowerCase());
			record.yours = !isKing;
			 // we have no way of knowing if a normal slave died or not :(
			record.dead = isKing; // but we do know that the king is dead
			if (isKing) {
				let nationality;
				/** @type {FC.Race} */
				let race = "white";
				let relative;
				// give the king an actual name
				// first, try and find relatives in your slaves
				if (getSlaves().some(s => s.father === record.ID)) { // adult children fathered by the King
					relative = getSlaves().find(s => s.father === record.ID).value;
				} else if (getSlaves().some(s => s.pregSource === record.ID)) { // queens still carrying the King's child
					relative = getSlaves().find(s => s.pregSource === record.ID).value;
				} else if (getTankSlaves().some(s => s.father === record.ID)) { // incubator occupants fathered by the King
					relative = getTankSlaves().find(s => s.father === record.ID).value;
				}
				// if any are found, use their nationality, race, and birthSurname
				// unfortunately, while nursery children can be found, they currently lose all the data we need
				if (!!relative) {
					nationality = relative.nationality; // incubator slaves set this to Stateless, which we'll try to handle below
					race = relative.race;
					record.slaveSurname = relative.birthSurname;
					if (nationality === "Stateless") { // if all that's left is an incubator slave, pick a random nationality appropriate to their race
						if (race === "asian") {
							nationality = jsEither(["Japanese", "Cambodian", "Bhutanese", "Thai"]);
						} else if (race === "white") {
							nationality = jsEither(["Luxembourgian", "Belgian", "Danish", "Dutch", "Swedish", "Norwegian", "British", "Monégasque"]);
						} else if (race === "southern european") {
							nationality = "Spanish";
						} else if (race === "middle eastern") {
							nationality = jsEither(["Moroccan", "Omani", "Jordanian", "Bahraini", "Kuwaiti", "Qatari", "Saudi"]);
						} else if (race === "malay") {
							nationality = jsEither(["Bruneian", "Malaysian"]);
						} else if (race === "indo-aryan") {
							nationality = "Emirati";
						} else if (race === "pacific islander") {
							nationality = "Tongan";
						} else if (race === "black") {
							nationality = jsEither(["Swazi, Mosotho"]);
						}
						// this is clumsy, and still defaults to white American names for races unused by realRoyalties, i.e. semitic, amerindian, latina, and mixed
						// could we reverse-engineer nationality from surname instead?
					}
				} else {
					// if no immediate family are found at all, we can default to the realRoyalties method
					// the record is unlikely to be needed in that case, though, so could this be replaced with "a late king" or just "King"?
					nationality = App.Data.misc.royalNationalities.random();
					switch (nationality) {
						case "Japanese":
						case "Cambodian":
						case "Bhutanese":
						case "Thai":
							race = "asian";
							break;
						case "Luxembourgian":
						case "Belgian":
						case "Danish":
						case "Dutch":
						case "Swedish":
						case "Norwegian":
						case "British":
						case "Monégasque":
							race = "white";
							break;
						case "Spanish":
							race = "southern european";
							break;
						case "Moroccan":
						case "Omani":
						case "Jordanian":
						case "Bahraini":
						case "Kuwaiti":
						case "Qatari":
						case "Saudi":
							race = "middle eastern";
							break;
						case "Bruneian":
						case "Malaysian":
							race = "malay";
							break;
						case "Emirati":
							race = "indo-aryan";
							break;
						case "Tongan":
							race = "pacific islander";
							break;
						case "Swazi":
						case "Mosotho":
							race = "black";
							break;
					}
					if (nationality === "Japanese") {
						record.slaveSurname = "Yamato";
					} else if (nationality === "Luxembourgian") {
						record.slaveSurname = "Luxembourg-Nassau";
					} else if (nationality === "Belgian") {
						record.slaveSurname = "Saxe-Coburg and Gotha";
					} else if (nationality === "Danish") {
						record.slaveSurname = "Glücksburg";
					} else if (nationality === "Dutch") {
						record.slaveSurname = "Orange-Nassau";
					} else if (nationality === "Swedish") {
						record.slaveSurname = "Bernadotte";
					} else if (nationality === "Spanish") {
						record.slaveSurname = "Bourbon";
					} else if (nationality === "Norwegian") {
						record.slaveSurname = "Glücksburg";
					} else if (nationality === "Cambodian") {
						record.slaveSurname = "Norodom";
					} else if (nationality === "Moroccan") {
						record.slaveSurname = "Alawi";
					} else if (nationality === "Omani") {
						record.slaveSurname = "Al Said";
					} else if (nationality === "Jordanian") {
						record.slaveSurname = "Hāshim";
					} else if (nationality === "Bruneian") {
						record.slaveSurname = "Bolkiah";
					} else if (nationality === "Emirati") {
						record.slaveSurname = "Al Nahyan";
					} else if (nationality === "Bahraini") {
						record.slaveSurname = "Al Khalifah";
					} else if (nationality === "Kuwaiti") {
						record.slaveSurname = "Al Sabah";
					} else if (nationality === "Malaysian") {
						record.slaveSurname = "Pahang";
					} else if (nationality === "Qatari") {
						record.slaveSurname = "Al Thani";
					} else if (nationality === "Saudi") {
						record.slaveSurname = "Al Saud";
					} else if (nationality === "Tongan") {
						record.slaveSurname = "Tupou";
					} else if (nationality === "Swazi") {
						record.slaveSurname = "Dlamini";
					} else if (nationality === "Mosotho") {
						record.slaveSurname = "Moshesh";
					} else if (nationality === "British") {
						record.slaveSurname = "Windsor";
					} else if (nationality === "Monégasque") {
						record.slaveSurname = "Grimaldi";
					} else if (nationality === "Bhutanese") {
						record.slaveSurname = "Wangchuck";
					} else if (nationality === "Thai") {
						record.slaveSurname = "Chakri";
					}
				}
				record.slaveName = generateName(nationality, race, true);
				const pair = record.slaveSurname ? [record.slaveName, record.slaveSurname] : [record.slaveName];
				if ((V.surnameOrder !== 1 && ["Cambodian", "Chinese", "Ancient Chinese Revivalist", "Hungarian", "Japanese", "Edo Revivalist", "Korean", "Mongolian", "Taiwanese", "Vietnamese"].includes(nationality)) || (V.surnameOrder === 2)) {
					pair.reverse();
				}
				record.fullName = "King " + pair.join(" ");
			}
			V.missingTable[ID] = record;
		}
	},
});


code:
App.Patch.register({
	releaseID: 1278,
	descriptionOfChanges: "Adds income retirement policy.",
	pre: (div) => {
		V.policies.retirement.income = 0;
	},
});


code:
App.Patch.register({
	releaseID: 1279,
	descriptionOfChanges: "Adds experimental AI chat.",
	pre: (div) => {
		V.aiChat= 0;
		V.aiChatUrl= "http://localhost:5000/v1/chat/completions";
		V.aiChatTemp= 0.5;
		V.aiChatMinP= 0.05;
	},
});


code:
App.Patch.register({
	releaseID: 1280,
	descriptionOfChanges: "Adds controllers for fertKnown and pregKnown as well as the showcase policy",
	pre: (div) => {
		V.menstruationKnown = 0;
		V.pregnancyKnown = 0;
		V.boughtItem.toys.chastity = 1;
		V.policies.contraceptivesBan = 0;
		V.scenarios = {};
		V.scenarios.contraceptivesBan = 0;
	},
});


code:
App.Patch.register({
	releaseID: 1281,
	descriptionOfChanges: "Refactors how trinket data is stored to hopefully make it a little better. Also removes several unused variables from V.",
	pre: (div) => {
		App.Patch.log("Converting V.trinkets' data structure");
		V.trinkets.forEach((trinket, desc, map) => {
			if (typeof trinket === "number") {
				if (trinket > 0) {
					map.set(desc, {
						slaveTrinket: false,
						count: trinket,
					});
				} else {
					map.delete(desc);
				}
			} else if (Array.isArray(trinket)) {
				map.set(desc, {
					slaveTrinket: true,
					data: new Set(trinket),
				});
			} else {
				throw new Error(`Unhandled trinket "${trinket}" with the type "${typeof trinket}" and key of "${desc}"`);
			}
		});
		App.Patch.log("Removing old variables");
		deleteProps(V, "deathIDs", "burstIDs", "birthIDs", "induceIDs");
	},
});


code:
App.Patch.register({
	releaseID: 1282,
	descriptionOfChanges: "Adds location backgrounds variable",
	pre: (div) => {
		V.aiLocationBackgrounds = 0;
	},
});


code:
App.Patch.register({
	releaseID: 1283,
	descriptionOfChanges: "Moves the missingParentTag from the detached slave's actors to their stats",
	pre: (div) => {
		App.Patch.log("Moving missingParentTag from the detached slave's actors to their stats");
		const traitor = getTraitor();
		const boomerang = getBoomerang();
		if (typeof boomerang.stats === "number") {
			boomerang.stats = {};
		}
		if (typeof traitor.stats === "number") {
			traitor.stats = {};
		}
		if (traitor.actor && getProp(traitor.actor, "missingParentTag", undefined)) {
			traitor.stats = traitor.stats ?? {};
			traitor.stats.missingParentTag = getProp(traitor.actor, "missingParentTag", undefined);
			deleteProps(traitor.actor, "missingParentTag");
		}
		if (boomerang.actor && getProp(boomerang.actor, "missingParentTag", undefined)) {
			boomerang.stats = boomerang.stats ?? {};
			boomerang.stats.missingParentTag = getProp(boomerang.actor, "missingParentTag", undefined);
			deleteProps(boomerang.actor, "missingParentTag");
		}
	},
});


code:
App.Patch.register({
	releaseID: 1284,
	descriptionOfChanges: "Adds new clothing option category",
	pre: (div) => {
		V.boughtItem.clothing.newmisc = 0;
	},
});


code:
App.Patch.register({
	descriptionOfChanges: "Adds support for saves from release 1055 and before",
	releaseID: 1055 +1,
	pre: (div) => {
		if (!jsDef(V.incubator)) {
			// @ts-ignore
			V.incubator = {capacity: 0, tanks: []};
		} else if (typeof V.incubator === "number") {
			App.Patch.log("Converting incubator");
			if (V.incubator > 0) {
				const storage = V.incubator;
				const incubatorTanks = getProp(V, "tanks", []);
				// @ts-ignore
				V.incubator = {capacity: storage, tanks: incubatorTanks};
			} else {
				// @ts-ignore
				V.incubator = {capacity: 0, tanks: []};
			}
		}
		deleteProps(V, "tanks");
	},
	humanState: (div, actor, location) => {
		if (!App.Utils.objectExistsAndHasKeys(actor.natural, ["height", "boobs", "artSeed"])) {
			App.Patch.log("Patching natural");
			const template = {
				height: 170,
				boobs: adjustBreastSize(actor),
				artSeed: jsRandom(0, 10 ** 14, undefined, undefined),
			};
			if (actor.geneticQuirks.dwarfism === 2 && actor.geneticQuirks.gigantism !== 2) {
				template.height = Height.randomAdult(actor, {limitMult: [-4, -1], spread: 0.15});
			} else if (actor.geneticQuirks.gigantism === 2) {
				template.height = Height.randomAdult(actor, {limitMult: [3, 10], spread: 0.15});
			} else {
				template.height = Height.randomAdult(actor);
			}
			actor.natural = actor.natural ?? template;
			actor.natural.height = actor.natural.height ?? template.height;
			actor.natural.boobs = actor.natural.boobs ?? template.boobs;
			actor.natural.artSeed = actor.natural.artSeed ?? template.artSeed;
		}
		return actor;
	},
	playerState: (div, actor, location) => {
		if (!App.Utils.objectExistsAndHasKeys(actor.badRumors, ["penetrative", "birth", "weakness"])) {
			App.Patch.log("Patching badRumors");
			const template = {
				penetrative: 0,
				birth: 0,
				weakness: 0,
			};
			actor.badRumors = actor.badRumors ?? template;
			actor.badRumors.penetrative = actor.badRumors.penetrative ?? template.penetrative;
			actor.badRumors.birth = actor.badRumors.birth ?? template.birth;
			actor.badRumors.weakness = actor.badRumors.weakness ?? template.weakness;
		}
		// make sure PC in in the genePool
		if (Array.isArray(V.genePool)) {
			if (!V.genePool.find(s => s.ID === -1)) {
				App.Patch.log("Adding PC to genePool");
				V.genePool.push(clone(V.PC));
			}
		} else {
			if (!isInGenePool(V.PC)) {
				App.Patch.log("Adding PC to genePool");
				addToGenePool(V.PC);
			}
		}
		return actor;
	}
});


code:
App.Data.Pronouns = {};

/**
 * @readonly
 * @enum {0|1|2|3|4|1000}
 */
// @ts-ignore
App.Data.Pronouns.Kind = Object.freeze({
	female: 0,
	male: 1,
	neutral: 2,
	toy: 2,
	epicene: 3,
	plural: 3,
	ai: 4,
	custom: 1000
});

/**
 * @param {string} pronoun
 * @param {string} possessive
 * @param {string} possessivePronoun
 * @param {string} object
 * @param {string} objectReflexive
 * @param {string} noun
 * @returns {FC.Data.Pronouns.Definition}
 */
App.Data.Pronouns.makePronounsData = function(
	pronoun, possessive, possessivePronoun, object, objectReflexive, noun) {
	return {
		pronoun: pronoun,
		possessive: possessive,
		possessivePronoun: possessivePronoun,
		object: object,
		objectReflexive: objectReflexive,
		noun: noun
	};
};

App.Data.Pronouns.Std = {
	0: App.Data.Pronouns.makePronounsData("she", "her", "hers", "her", "herself", "girl"),
	1: App.Data.Pronouns.makePronounsData("he", "his", "his", "him", "himself", "boy"),
	2: App.Data.Pronouns.makePronounsData("it", "its", "its", "it", "itself", "toy"),
	3: App.Data.Pronouns.makePronounsData("they", "their", "theirs", "them", "themselves", "toys"),
	4: App.Data.Pronouns.makePronounsData("it", "its", "its", "it", "itself", "program"),
};


code:
/**
 * Runs verification for all ChildState objects in ``
 * TODO:@franklygeorge handle ChildState when it is actually implemented
 * @param {HTMLDivElement} [div]
 */
App.Verify.childStates = (div) => {
};

/**
 * @param {string} identifier
 * @param {FC.ChildState} actor
 * @param {App.Patch.Utils.HumanStateLocation} location
 * @param {HTMLDivElement} [div]
 */
App.Verify.childState = (identifier, actor, location, div) => {
	const original = _.cloneDeep(actor);
	try {
		App.Patch.Utils.childState(identifier, actor, location);
	} catch (e) {
		console.error(e);
		actor = original;
		return;
	}
	// add missing props
	App.Utils.assignMissingDefaults(actor, App.Patch.Utils.childTemplate(actor));
	// verify
	actor = App.Verify.Utils.verify("childState", identifier, actor, location, div);
	App.Verify.womb(identifier, actor, div);
};

// ***************************************************************************************************** \\
// *************************** Put your verification functions below this line *************************** \\
// ***************************************************************************************************** \\

/**
 * @type {App.Verify.Utils.FunctionChildState}
 */
App.Verify.I.childAge = (actor) => {
	return /** @type {FC.ChildState} */ (App.Verify.I.slaveAge(actor, "unknown"));
};

/**
 * @type {App.Verify.Utils.FunctionChildState}
 */
App.Verify.I.childHealth = (actor) => {
	return /** @type {FC.ChildState} */ (App.Verify.I.slaveHealth(actor, "unknown"));
};

/**
 * @type {App.Verify.Utils.FunctionChildState}
 */
App.Verify.I.childPhysical = (actor) => {
	return /** @type {FC.ChildState} */ (App.Verify.I.slavePhysical(actor, "unknown"));
};

/**
 * @type {App.Verify.Utils.FunctionChildState}
 */
App.Verify.I.childSkin = (actor) => {
	return /** @type {FC.ChildState} */ (App.Verify.I.slaveSkin(actor, "unknown"));
};

/**
 * @type {App.Verify.Utils.FunctionChildState}
 */
App.Verify.I.childFace = (actor) => {
	return /** @type {FC.ChildState} */ (App.Verify.I.slaveFace(actor, "unknown"));
};

/**
 * @type {App.Verify.Utils.FunctionChildState}
 */
App.Verify.I.childHair = (actor) => {
	return /** @type {FC.ChildState} */ (App.Verify.I.slaveHair(actor, "unknown"));
};

/**
 * @type {App.Verify.Utils.FunctionChildState}
 */
App.Verify.I.childBoobs = (actor) => {
	return /** @type {FC.ChildState} */ (App.Verify.I.slaveBoobs(actor, "unknown"));
};

/**
 * @type {App.Verify.Utils.FunctionChildState}
 */
App.Verify.I.childButt = (actor) => {
	return /** @type {FC.ChildState} */ (App.Verify.I.slaveButt(actor, "unknown"));
};

/**
 * @type {App.Verify.Utils.FunctionChildState}
 */
App.Verify.I.childGeneticQuirks = (actor) => {
	return /** @type {FC.ChildState} */ (App.Verify.I.slaveGeneticQuirks(actor, "unknown"));
};

/**
 * @type {App.Verify.Utils.FunctionChildState}
 */
App.Verify.I.childPregnancy = (actor) => {
	return /** @type {FC.ChildState} */ (App.Verify.I.slavePregnancy(actor, "unknown"));
};

/**
 * @type {App.Verify.Utils.FunctionChildState}
 */
App.Verify.I.childBelly = (actor) => {
	return /** @type {FC.ChildState} */ (App.Verify.I.slaveBelly(actor, "unknown"));
};

/**
 * @type {App.Verify.Utils.FunctionChildState}
 */
App.Verify.I.childGenitalia = (actor) => {
	return /** @type {FC.ChildState} */ (App.Verify.I.slaveGenitalia(actor, "unknown"));
};

/**
 * @type {App.Verify.Utils.FunctionChildState}
 */
App.Verify.I.childImplants = (actor) => {
	return /** @type {FC.ChildState} */ (App.Verify.I.slaveImplants(actor, "unknown"));
};

/**
 * @type {App.Verify.Utils.FunctionChildState}
 */
App.Verify.I.childPiercings = (actor) => {
	return /** @type {FC.ChildState} */ (App.Verify.I.slavePiercings(actor, "unknown"));
};

/**
 * @type {App.Verify.Utils.FunctionChildState}
 */
App.Verify.I.childTattoo = (actor) => {
	return /** @type {FC.ChildState} */ (App.Verify.I.slaveTattoo(actor, "unknown"));
};

/**
 * @type {App.Verify.Utils.FunctionChildState}
 */
App.Verify.I.childCosmetics = (actor) => {
	return /** @type {FC.ChildState} */ (App.Verify.I.slaveCosmetics(actor, "unknown"));
};

/**
 * @type {App.Verify.Utils.FunctionChildState}
 */
App.Verify.I.childDiet = (actor) => {
	return /** @type {FC.ChildState} */ (App.Verify.I.slaveDiet(actor, "unknown"));
};

/**
 * @type {App.Verify.Utils.FunctionChildState}
 */
App.Verify.I.childPorn = (actor) => {
	return /** @type {FC.ChildState} */ (App.Verify.I.slavePorn(actor, "unknown"));
};

/**
 * @type {App.Verify.Utils.FunctionChildState}
 */
App.Verify.I.childRelation = (actor) => {
	return /** @type {FC.ChildState} */ (App.Verify.I.slaveRelation(actor, "unknown"));
};

/**
 * @type {App.Verify.Utils.FunctionChildState}
 */
App.Verify.I.childSkill = (actor) => {
	return /** @type {FC.ChildState} */ (App.Verify.I.slaveSkill(actor, "unknown"));
};

/**
 * @type {App.Verify.Utils.FunctionChildState}
 */
App.Verify.I.childStat = (actor) => {
	return /** @type {FC.ChildState} */ (App.Verify.I.slaveStat(actor, "unknown"));
};

/**
 * @type {App.Verify.Utils.FunctionChildState}
 */
App.Verify.I.childPreferences = (actor) => {
	return /** @type {FC.ChildState} */ (App.Verify.I.slavePreferences(actor, "unknown"));
};

/**
 * @type {App.Verify.Utils.FunctionChildState}
 */
App.Verify.I.childRules = (actor) => {
	return /** @type {FC.ChildState} */ (App.Verify.I.slaveRules(actor, "unknown"));
};

/**
 * @type {App.Verify.Utils.FunctionChildState}
 */
App.Verify.I.childMisc = (actor) => {
	return /** @type {FC.ChildState} */ (App.Verify.I.slaveMisc(actor, "unknown"));
};


code:
/**
 * Runs verification for all CustomSlaveOrder objects
 * @see App.Entity.CustomSlaveOrder
 * @see V.customSlave
 * @see V.huskSlave
 * @param {HTMLDivElement} [div]
 */
App.Verify.customSlaveOrders = (div) => {
	if (V.customSlave) {
		App.Verify.customSlaveOrder(V.customSlave, "V.customSlave", div);
	}
	if (V.huskSlave) {
		App.Verify.customSlaveOrder(V.huskSlave, "V.huskSlave", div);
	}
};

/**
 * Runs verification of the provided CustomSlaveOrder object
 * @param {FC.CustomSlaveOrder} order
 * @param {"V.customSlave"|"V.huskSlave"} location
 * @param {HTMLDivElement} [div]
 */
App.Verify.customSlaveOrder = (order, location, div) => {
	const original = _.cloneDeep(order);
	try {
		App.Patch.Utils.customSlaveOrder(location, order);
	} catch (e) {
		console.error(e);
		order = original;
		return;
	}
	// add missing props
	App.Utils.assignMissingDefaults(order, new App.Entity.CustomSlaveOrder());
	// verify
	order = App.Verify.Utils.verify("customSlaveOrder", location, order, location, div);
};

// ***************************************************************************************************** \\
// *************************** Put your verification functions below this line *************************** \\
// ***************************************************************************************************** \\

/**
 * @type {App.Verify.Utils.FunctionCustomSlaveOrder}
 */
App.Verify.I.customSlaveOrderMisc = (order) => {
	//TODO: figure out what needs verified in App.Entity.CustomSlaveOrder and add it here
	return order;
};


code:
/**
 * Runs verification for all fetuses in all SlaveStates objects
 * @see App.Utils.getAllSlaves
 * @param {HTMLDivElement} [div]
 */
App.Verify.fetuses = (div) => {
	let i;
	let f;
	const humans = getHumanStates();
	for (i in humans) {
		if (humans[i].womb && Array.isArray(humans[i].womb)) {
			for (f in humans[i].womb) {
				App.Verify.fetus(`getHumanStates()[${i}].womb[${f}]`, humans[i].womb[f], humans[i], div);
			}
		}
	}
};

/**
 * Runs verification for a single Fetus object.
 * @param {string} identifier
 * @param {FC.Fetus} fetus
 * @param {FC.HumanState} mother
 * @param {HTMLDivElement} [div]
 */
App.Verify.fetus = (identifier, fetus, mother, div) => {
	const original = _.cloneDeep(fetus);
	try {
		App.Patch.Utils.fetus(identifier, fetus, mother);
	} catch (e) {
		console.error(e);
		fetus = original;
		return;
	}
	// add missing props
	App.Utils.assignMissingDefaults(fetus, App.Patch.Utils.fetusTemplate(fetus, mother));
	// verify
	fetus = App.Verify.Utils.verify("fetus", identifier, fetus, mother, div);
};

// ***************************************************************************************************** \\
// *************************** Put your verification functions below this line *************************** \\
// ***************************************************************************************************** \\

/**
 * @type {App.Verify.Utils.FunctionFetus}
 */
App.Verify.I.fetusGeneticQuirks = (actor, mother) => {
	const quirks = {};
	App.Data.geneticQuirks.forEach((value, q) => quirks[q] = 0);
	// add any new quirks
	actor.genetics.geneticQuirks = Object.assign(clone(quirks), actor.genetics.geneticQuirks);
	return actor;
};


code:
/**
 * Runs verification on V, does not do full verification of V.
 * For full verification use App.Verify.everything().
 * @see App.Verify.everything
 * @param {HTMLDivElement} [div]
 */
App.Verify.gameVariables = (div) => {
	if (V.releaseID !== App.Version.release) {
		throw new Error(`V.releaseID (${V.releaseID}) !== App.Version.release (${App.Version.release}). BC must be ran before 'App.Verify.gameVariables()' is called.`);
	}
	// add nonexistant properties to V from `App.Data.defaultGameStateVariables` and `App.Data.resetOnNGPlus`
	App.Utils.assignMissingDefaults(V, App.Data.defaultGameStateVariables, App.Data.resetOnNGPlus);
	// verify
	App.Verify.Utils.verify("gameVariables", "V", undefined, "V", div);
};

// ***************************************************************************************************** \\
// *************************** Put your verification functions below this line *************************** \\
// ***************************************************************************************************** \\

/**
 * @type {App.Verify.Utils.FunctionGameVariables}
 */
App.Verify.I.genePool = () => {
	const AllHumanIDs = getHumanStates().map((s) => s.ID);
	// remove all unneeded records from the genePool
	for (const [key, record] of Object.entries(_.cloneDeep(V.genePool))) {
		if (key === "-1" || AllHumanIDs.includes(Number(key))) { continue; }
		deleteGenePoolRecord(/** @type {FC.HumanState} */ (record)); // by default deleteGenePoolRecord only deletes records that are no longer needed
	}

	// update all records in the genePool
	/** @type {FC.HumanState[]} */
	const updatedRecords = [];
	for (const [key, record] of Object.entries(_.cloneDeep(V.genePool))) {
		const actor = key === "-1" ? V.PC : getSlave(record.ID) ?? baseSlave();
		// make the record a valid PlayerState or HumanState object
		App.Utils.assignMissingDefaults(record, actor);

		// patch record without verification
		if (key === "-1") {
			App.Patch.Utils.playerState(
				`GenePool record with ID '${record.ID}'`,
				/** @type {FC.PlayerState} */ (record),
				"gene pool",
				undefined, V.releaseID,
			);
		} else {
			App.Patch.Utils.slaveState(
				`GenePool record with ID '${record.ID}'`,
				/** @type {FC.SlaveState} */ (record),
				"gene pool",
				undefined, V.releaseID,
			);
		}
		// force delete old record
		deleteGenePoolRecord(/** @type {FC.HumanState} */ (record), true); // force deletion; by default deleteGenePoolRecord only deletes records that are no longer needed
		// store new record
		updatedRecords.push(/** @type {FC.HumanState} */ (record));
	}

	// update V.genePoolDefaults to ensure it matches any changes in structure
	// @ts-expect-error genePoolDefaults is read only and shouldn't be set normally
	V.genePoolDefaults = new App.Entity.GenePoolRecord(V.genePoolDefaults.seed);

	// add updated records back into the genePool
	updatedRecords.forEach((record) => {
		addToGenePool(record);
	});

	// add any missing records to the genePool
	getHumanStates().forEach((human) => {
		if (!isInGenePool(human)) {
			addToGenePool(human);
		}
	});
};

/**
 * @type {App.Verify.Utils.FunctionGameVariables}
 */
App.Verify.I.economy = () => {
	V.AProsperityCap = Math.max(+V.AProsperityCap, 0) ?? 0;
	if (V.economy === 0.5) {
		V.economy = 200;
	} else if (V.economy === 1) {
		V.economy = 100;
	} else if (V.economy === 1.5) {
		V.economy = 67;
	} else {
		V.economy = Math.max(+V.economy, 20) ?? 100;
	}
	V.difficultySwitch = Math.clamp(+V.difficultySwitch, 0, 1) ?? 0;
	V.baseDifficulty = Math.clamp(+V.baseDifficulty, 1, 5) ?? 3;
	V.localEcon = Math.max(+V.localEcon, 20) ?? V.economy;
	V.econRate = Math.clamp(+V.econRate, 1, 4) ?? 2;
	V.slaveCostFactor = Math.max(+V.slaveCostFactor, 0) ?? 1;
	V.menialSupplyFactor = Math.clamp(+V.menialSupplyFactor, -50000, 50000) ?? (1 - V.slaveCostFactor) * 400 * 1000 * 0.5; /* (1 - slaveCostFactor) * price elasticity * base price * 0.5 */
	V.menialDemandFactor = Math.clamp(+V.menialDemandFactor, -50000, 50000) ?? -V.menialSupplyFactor;

	V.slaveCostRandom = Math.clamp(+V.slaveCostRandom, -3, 3) ?? 0;
	V.demandTimer = Math.max(+V.demandTimer, 0) ?? 0;
	V.elapsedDemandTimer = Math.max(+V.elapsedDemandTimer, 0) ?? 0;
	V.supplyTimer = Math.max(+V.supplyTimer, 0) ?? 0;
	V.elapsedSupplyTimer = Math.max(+V.elapsedSupplyTimer, 0) ?? 0;
	V.deltaSupply = Math.clamp(+V.deltaSupply, -6500, 6500) ?? 0;
	V.deltaDemand = Math.clamp(+V.deltaDemand, -6500, 6500) ?? 0;
	V.deltaSupplyOld = Math.clamp(+V.deltaSupply, -6500, 6500) ?? 0;
	V.deltaDemandOld = Math.clamp(+V.deltaDemand, -6500, 6500) ?? 0;
	V.sexSubsidies.lowerClass = Math.clamp(+V.sexSubsidies.lowerClass, 0, 4) ?? 0;
	V.sexSubsidies.middleClass = Math.clamp(+V.sexSubsidies.middleClass, 0, 4) ?? 0;
	V.sexSubsidies.upperClass = Math.clamp(+V.sexSubsidies.upperClass, 0, 4) ?? 0;
	V.sexSubsidies.topClass = Math.clamp(+V.sexSubsidies.topClass, 0, 4) ?? 0;
	V.sexSupplyBarriers.lowerClass = Math.clamp(+V.sexSupplyBarriers.lowerClass, 0, 4) ?? 0;
	V.sexSupplyBarriers.middleClass = Math.clamp(+V.sexSupplyBarriers.middleClass, 0, 4) ?? 0;
	V.sexSupplyBarriers.upperClass = Math.clamp(+V.sexSupplyBarriers.upperClass, 0, 4) ?? 0;
	V.sexSupplyBarriers.topClass = Math.clamp(+V.sexSupplyBarriers.topClass, 0, 4) ?? 0;
	V.NPCSexSupply.lowerClass = Math.max(+V.NPCSexSupply.lowerClass, 500) ?? 3000;
	V.NPCSexSupply.middleClass = Math.max(+V.NPCSexSupply.middleClass, 500) ?? 3000;
	V.NPCSexSupply.upperClass = Math.max(+V.NPCSexSupply.upperClass, 500) ?? 3000;
	V.NPCSexSupply.topClass = Math.max(+V.NPCSexSupply.topClass, 500) ?? 3000;

	V.rentDefaults.lowerClass = Math.max(+V.rentDefaults.lowerClass, 0) ?? 20;
	V.rentDefaults.middleClass = Math.max(+V.rentDefaults.middleClass, 0) ?? 50;
	V.rentDefaults.upperClass = Math.max(+V.rentDefaults.upperClass, 0) ?? 180;
	V.rentDefaults.topClass = Math.max(+V.rentDefaults.topClass, 0) ?? 650;

	V.whoreBudget.lowerClass = Math.max(+V.whoreBudget.lowerClass, 8) ?? (0.8 + (V.rent.lowerClass / V.rentDefaults.lowerClass) / 5) * 7;
	V.whoreBudget.middleClass = Math.max(+V.whoreBudget.middleClass, 40) ?? (0.8 + (V.rent.middleClass / V.rentDefaults.middleClass) / 5) * 40;
	V.whoreBudget.upperClass = Math.max(+V.whoreBudget.upperClass, 200) ?? (0.8 + (V.rent.upperClass / V.rentDefaults.upperClass) / 5) * 200;
	V.whoreBudget.topClass = Math.max(+V.whoreBudget.topClass, 1200) ?? (0.8 + (V.rent.topClass / V.rentDefaults.topClass) / 5) * 1500;

	// fixing potential massive oversupply
	if (V.NPCSexSupply.lowerClass > V.lowerClass * V.whoreBudget.lowerClass) {
		V.NPCSexSupply.lowerClass = V.lowerClass * V.whoreBudget.lowerClass;
	}

	V.NPCMarketShare.lowerClass = Math.clamp(+V.NPCMarketShare.lowerClass, 0, 1000) ?? 0;
	V.NPCMarketShare.middleClass = Math.clamp(+V.NPCMarketShare.middleClass, 0, 1000) ?? 0;
	V.NPCMarketShare.upperClass = Math.clamp(+V.NPCMarketShare.upperClass, 0, 1000) ?? 0;
	V.NPCMarketShare.topClass = Math.clamp(+V.NPCMarketShare.topClass, 0, 1000) ?? 0;
	V.econWeatherDamage = Math.max(+V.econWeatherDamage, 0) ?? 0;
	V.disasterResponse = Math.clamp(+V.disasterResponse, 0, 2) ?? 0;
	V.antiWeatherFreeze = Math.clamp(+V.antiWeatherFreeze, 0, 2) ?? 0;
	V.GDP = Math.max(+V.GDP, 1) ?? 278.6;
	V.NPCSlaves = Math.max(+V.NPCSlaves, 0) ?? 0;
	V.visitors = Math.max(+V.visitors, 0) ?? 0;

	V.LSCBase = Math.max(+V.LSCBase, 0) ?? 800;
	V.rentEffectL = Math.max(+V.rentEffectL, 0) ?? 1;
	V.lowerClass = Math.max(+V.lowerClass, 0) ?? 3120;

	V.MCBase = Math.max(+V.MCBase, 0) ?? 200;
	V.rentEffectM = Math.max(+V.rentEffectM, 0) ?? 1;
	V.middleClass = Math.max(+V.middleClass, 0) ?? 890;

	V.UCBase = Math.max(+V.UCBase, 0) ?? 40;
	V.rentEffectU = Math.max(+V.rentEffectU, 0) ?? 1;
	V.upperClass = Math.max(+V.upperClass, 0) ?? 200;

	V.TCBase = Math.max(+V.TCBase, 0) ?? 20;
	V.rentEffectT = Math.max(+V.rentEffectT, 0) ?? 1;
	V.topClass = Math.max(+V.topClass, 0) ?? 40;
};

/**
 * @type {App.Verify.Utils.FunctionGameVariables}
 */
App.Verify.I.arcology = () => {
	V.arcologies[0].ownership = Math.clamp(+V.arcologies[0].ownership, 0, 100) ?? 0;
	V.arcologies[0].minority = Math.clamp(+V.arcologies[0].minority, 0, 100) ?? 0;
	V.arcologies[0].prosperity = Math.clamp(+V.arcologies[0].prosperity, 1, V.AProsperityCap) ?? 1;
	V.arcologies[0].childhoodFertilityInducedNCSResearch = V.arcologies[0].childhoodFertilityInducedNCSResearch ?? 0;
};

/**
 * @type {App.Verify.Utils.FunctionGameVariables}
 */
App.Verify.I.facilityNames = () => {
	V.arcologies[0].name = V.arcologies[0].name ?? "Arcology X-4";
	V.brothelName = V.brothelName ?? "the Brothel";
	V.dairyName = V.dairyName ?? "the Dairy";
	V.clubName = V.clubName ?? "the Club";
	V.servantsQuarters.name = V.servantsQuarters.name ?? "the Servants' Quarters";
	V.schoolroomName = V.schoolroomName ?? "the Schoolroom";
	V.spaName = V.spaName ?? "the Spa";
	V.nurseryName = V.nurseryName ?? "the Nursery";
	V.clinicName = V.clinicName ?? "the Clinic";
	V.arcadeName = V.arcadeName ?? "the Arcade";
	V.cellblockName = V.cellblockName ?? "the Cellblock";
	V.masterSuiteName = V.masterSuiteName ?? "the Master Suite";
	V.HGSuiteName = V.HGSuiteName ?? "the Head Girl Suite";
	V.farmyardName = V.farmyardName ?? "the Farmyard";
};

/**
 * @type {App.Verify.Utils.FunctionGameVariables}
 */
App.Verify.I.facilityBrothel = () => {
	/* ads */
	V.brothelAdsSpending = Math.clamp(+V.brothelAdsSpending, 0, 5000) ?? 0;
	V.brothelAdsStacked = Math.clamp(+V.brothelAdsStacked, -1, 1) ?? 0;
	V.brothelAdsImplanted = Math.clamp(+V.brothelAdsImplanted, -1, 1) ?? 0;
	V.brothelAdsModded = Math.clamp(+V.brothelAdsModded, -1, 1) ?? 0;
	V.brothelAdsPreg = Math.clamp(+V.brothelAdsPreg, -1, 1) ?? 0;
	V.brothelAdsOld = Math.clamp(+V.brothelAdsOld, -3, 1) ?? 0;
	V.brothelAdsXX = Math.clamp(+V.brothelAdsXX, -1, 1) ?? 0;
	/* upgrades */
	V.brothel = Math.max(+V.brothel, 0) ?? 0;
	V.brothelUpgradeDrugs = Math.clamp(+V.brothelUpgradeDrugs, 0, 2) ?? 0;
	V.brothelBoost.selected = Math.clamp(+V.brothelBoost.selected, 0, 10) ?? 0;
	V.brothelBoost.eligible = Math.clamp(+V.brothelBoost.eligible, 0, 10) ?? 0;
	/* madam */
	V.MadamID = App.Verify.Utils.findSlaveId(s => s.assignment === Job.MADAM);
	V.MadamIgnoresFlaws = Math.clamp(+V.MadamIgnoresFlaws, 0, 1) ?? 0;
};

/**
 * @type {App.Verify.Utils.FunctionGameVariables}
 */
App.Verify.I.facilityDairy = () => {
	/* upgrades */
	V.dairy = Math.max(+V.dairy, 0) ?? 0;
	V.dairyFeedersUpgrade = Math.clamp(+V.dairyFeedersUpgrade, 0, 1) ?? 0;
	V.dairyFeedersSetting = Math.clamp(+V.dairyFeedersSetting, 0, 2) ?? 0;
	V.dairyPregUpgrade = Math.clamp(+V.dairyPregUpgrade, 0, 1) ?? 0;
	V.dairyPregSetting = Math.clamp(+V.dairyPregSetting, 0, 3) ?? 0;
	V.dairyStimulatorsUpgrade = Math.clamp(+V.dairyStimulatorsUpgrade, 0, 1) ?? 0;
	V.dairyStimulatorsSetting = Math.clamp(+V.dairyStimulatorsSetting, 0, 2) ?? 0;
	V.dairyRestraintsUpgrade = Math.clamp(+V.dairyRestraintsUpgrade, 0, 1) ?? 0;
	V.dairyRestraintsSetting = Math.clamp(+V.dairyRestraintsSetting, 0, 2) ?? 0;
	V.dairySlimMaintainUpgrade = Math.clamp(+V.dairySlimMaintainUpgrade, 0, 1) ?? 0;
	V.dairySlimMaintain = Math.clamp(+V.dairySlimMaintain, 0, 1) ?? 0;
	V.dairyPrepUpgrade = Math.clamp(+V.dairyPrepUpgrade, 0, 1) ?? 0;
	V.dairyHyperPregRemodel = Math.clamp(+V.dairyHyperPregRemodel, 0, 1) ?? 0;
	V.dairyImplantsSetting = Math.clamp(+V.dairyImplantsSetting, 0, 3) ?? 0;
	V.dairyWeightSetting = Math.clamp(+V.dairyWeightSetting, -1, 4) ?? 0;
	V.dairyHormonesSetting = Math.clamp(+V.dairyHormonesSetting, -1, 2) ?? 0;
	/* bioreactors */
	V.bioreactorsAnnounced = Math.clamp(+V.bioreactorsAnnounced, 0, 1) ?? 0;
	V.createBioreactors = Math.clamp(+V.createBioreactors, 0, 1) ?? 0;
	V.dairyUpgradeMenials = Math.clamp(+V.dairyUpgradeMenials, 0, 1) ?? 0;
	V.bioreactorsHerm = Math.max(+V.bioreactorsHerm, 0) ?? 0;
	V.bioreactorsXX = Math.max(+V.bioreactorsXX, 0) ?? 0;
	V.bioreactorsXY = Math.max(+V.bioreactorsXY, 0) ?? 0;
	V.bioreactorsBarren = Math.max(+V.bioreactorsBarren, 0) ?? 0;
	/* milkmaid */
	V.MilkmaidID = App.Verify.Utils.findSlaveId(s => s.assignment === Job.MILKMAID);
	V.milkmaidImpregnates = Math.clamp(+V.milkmaidImpregnates, 0, 1) ?? 0;
};

/**
 * @type {App.Verify.Utils.FunctionGameVariables}
 */
App.Verify.I.facilityFarmyard = () => {
	V.farmyard = Math.max(+V.farmyard, 0) ?? 0;
	V.farmyardBreeding = Math.clamp(+V.farmyardBreeding, 0, 1) ?? 0;
	V.farmyardShows = Math.clamp(+V.farmyardShows, 0, 2) ?? 0;
	/* farmer */
	V.FarmerID = App.Verify.Utils.findSlaveId(s => s.assignment === Job.FARMER);
};

/**
 * @type {App.Verify.Utils.FunctionGameVariables}
 */
App.Verify.I.facilityClub = () => {
	/* ads */
	V.clubAdsSpending = Math.clamp(+V.clubAdsSpending, 0, 5000) ?? 0;
	V.clubAdsStacked = Math.clamp(+V.clubAdsStacked, -1, 1) ?? 0;
	V.clubAdsImplanted = Math.clamp(+V.clubAdsImplanted, -1, 1) ?? 0;
	V.clubAdsModded = Math.clamp(+V.clubAdsModded, -1, 1) ?? 0;
	V.clubAdsPreg = Math.clamp(+V.clubAdsPreg, -1, 1) ?? 0;
	V.clubAdsOld = Math.clamp(+V.clubAdsOld, -3, 1) ?? 0;
	V.clubAdsXX = Math.clamp(+V.clubAdsXX, -1, 1) ?? 0;
	/* upgrades */
	V.club = Math.max(+V.club, 0) ?? 0;
	V.clubUpgradePDAs = Math.clamp(+V.clubUpgradePDAs, 0, 1) ?? 0;
	/* madam */
	V.djID = App.Verify.Utils.findSlaveId(s => s.assignment === Job.DJ);
	V.DJignoresFlaws = Math.clamp(+V.DJignoresFlaws, 0, 1) ?? 0;
};

/**
 * @type {App.Verify.Utils.FunctionGameVariables}
 */
App.Verify.I.facilityServantsQuarters = () => {
	/* upgrades */
	V.servantsQuarters.capacity = Math.max(+V.servantsQuarters.capacity, 0) ?? 0;
	V.servantsQuarters.upgrades.monitoring = Math.clamp(+V.servantsQuarters.upgrades.monitoring, 0, 1) ?? 0;
	/* stewardess */
	V.StewardessID = App.Verify.Utils.findSlaveId(s => s.assignment === Job.STEWARD);
	V.servantsQuarters.rules.stewardessImpregnates = Math.clamp(+V.servantsQuarters.rules.stewardessImpregnates, 0, 1) ?? 0;
};

/**
 * @type {App.Verify.Utils.FunctionGameVariables}
 */
App.Verify.I.facilitySchoolroom = () => {
	/* upgrades */
	V.schoolroom = Math.max(+V.schoolroom, 0) ?? 0;
	V.schoolroomUpgradeSkills = Math.clamp(+V.schoolroomUpgradeSkills, 0, 1) ?? 0;
	V.schoolroomUpgradeLanguage = Math.clamp(+V.schoolroomUpgradeLanguage, 0, 1) ?? 0;
	V.schoolroomUpgradeRemedial = Math.clamp(+V.schoolroomUpgradeRemedial, 0, 1) ?? 0;
	/* schoolteacher */
	V.SchoolteacherID = App.Verify.Utils.findSlaveId(s => s.assignment === Job.TEACHER);
};

/**
 * @type {App.Verify.Utils.FunctionGameVariables}
 */
App.Verify.I.facilitySpa = () => {
	/* upgrades */
	V.spa = Math.max(+V.spa, 0) ?? 0;
	V.spaUpgrade = Math.clamp(+V.spaUpgrade, 0, 1) ?? 0;
	/* attendant */
	V.AttendantID = App.Verify.Utils.findSlaveId(s => s.assignment === Job.ATTENDANT);
	V.spaFix = Math.clamp(+V.spaFix, 0, 2) ?? 0;
};

/**
 * @type {App.Verify.Utils.FunctionGameVariables}
 */
App.Verify.I.facilityClinic = () => {
	/* upgrades */
	V.clinic = Math.max(+V.clinic, 0) ?? 0;
	V.clinicUpgradeScanner = Math.clamp(+V.clinicUpgradeScanner, 0, 1) ?? 0;
	V.clinicUpgradeFilters = Math.clamp(+V.clinicUpgradeFilters, 0, 1) ?? 0;
	V.clinicUpgradePurge = Math.clamp(+V.clinicUpgradePurge, 0, 2) ?? 0;
	V.clinicObservePregnancy = Math.clamp(+V.clinicObservePregnancy, 0, 2) ?? 1;
	V.clinicInflateBelly = Math.clamp(+V.clinicInflateBelly, 0, 1) ?? 0;
	V.clinicSpeedGestation = Math.clamp(+V.clinicSpeedGestation, 0, 1) ?? 0;
	/* nurse */
	V.NurseID = App.Verify.Utils.findSlaveId(s => s.assignment === Job.NURSE);
};

/**
 * @type {App.Verify.Utils.FunctionGameVariables}
 */
App.Verify.I.facilityArcade = () => {
	/* upgrades */
	V.arcade = Math.max(+V.arcade, 0) ?? 0;
	V.arcadeUpgradeInjectors = Math.clamp(+V.arcadeUpgradeInjectors, 0, 1) ?? 0;
	V.arcadeUpgradeCollectors = Math.clamp(+V.arcadeUpgradeCollectors, 0, 1.5) ?? 0;
	V.arcadeUpgradeFuckdolls = Math.clamp(+V.arcadeUpgradeFuckdolls, 0, 3) ?? 0;
	V.arcadeUpgradeHealth = Math.clamp(+V.arcadeUpgradeHealth, -1, 2) ?? 0;
};

/**
 * @type {App.Verify.Utils.FunctionGameVariables}
 */
App.Verify.I.facilityCellblock = () => {
	/* upgrades */
	V.cellblock = Math.max(+V.cellblock, 0) ?? 0;
	V.cellblockUpgrade = Math.clamp(+V.cellblockUpgrade, 0, 1) ?? 0;
	/* wardeness */
	V.WardenessID = App.Verify.Utils.findSlaveId(s => s.assignment === Job.WARDEN);
	V.cellblockWardenCumsInside = Math.clamp(+V.cellblockWardenCumsInside, 0, 1) ?? 0;
};

/**
 * @type {App.Verify.Utils.FunctionGameVariables}
 */
App.Verify.I.facilityMasterSuite = () => {
	/* upgrades */
	V.masterSuite = Math.max(+V.masterSuite, 0) ?? 0;
	V.masterSuiteUpgradeLuxury = Math.clamp(+V.masterSuiteUpgradeLuxury, 0, 2) ?? 0;
	V.masterSuiteUpgradePregnancy = Math.clamp(+V.masterSuiteUpgradePregnancy, 0, 1) ?? 0;
	V.masterSuitePregnancyFertilitySupplements = Math.clamp(+V.masterSuitePregnancyFertilitySupplements, 0, 1) ?? 0;
	V.masterSuitePregnancySlaveLuxuries = Math.clamp(+V.masterSuitePregnancySlaveLuxuries, 0, 1) ?? 0;
	V.masterSuitePregnancyFertilityDrugs = Math.clamp(+V.masterSuitePregnancyFertilityDrugs, 0, 1) ?? 0;
	V.masterSuiteHyperPregnancy = Math.clamp(+V.masterSuiteHyperPregnancy, 0, 1) ?? 0;
	/* concubine */
	V.ConcubineID = App.Verify.Utils.findSlaveId(s => s.assignment === Job.CONCUBINE);
};

/**
 * @type {App.Verify.Utils.FunctionGameVariables}
 */
App.Verify.I.facilityHeadGirlSuite = () => {
	/* headgirl */
	V.HeadGirlID = App.Verify.Utils.findSlaveId(s => s.assignment === Job.HEADGIRL);
	V.HGSuiteEquality = Math.clamp(+V.HGSuiteEquality, 0, 1) ?? 0;
	V.HGSuiteSurgery = Math.clamp(+V.HGSuiteSurgery, 0, 1) ?? 0;
	V.HGSuiteDrugs = Math.clamp(+V.HGSuiteDrugs, 0, 1) ?? 0;
	V.HGSuiteAbortion = Math.clamp(+V.HGSuiteAbortion, 0, 1) ?? 0;
	V.HGSuiteHormones = Math.clamp(+V.HGSuiteHormones, 0, 1) ?? 0;
};

/**
 * @type {App.Verify.Utils.FunctionGameVariables}
 */
App.Verify.I.facilityNursery = () => {
	/* matron */
	V.MatronID = App.Verify.Utils.findSlaveId(s => s.assignment === Job.MATRON);
};

/**
 * @type {App.Verify.Utils.FunctionGameVariables}
 */
App.Verify.I.futureSocieties = () => {
	for (const society of App.Data.FutureSociety.playerFSNames) {
		if (!(V.arcologies[0][society] > 0)) {
			FutureSocieties.remove(society);
		}

		if (society !== "FSNull") {
			const decoName = `${society}Decoration`;
			V.arcologies[0][decoName] = Math.clamp(+V.arcologies[0][decoName], 0, 100) ?? 0;
		}

		// reset any FS policies that are set to some value that you can't pick from the UI
		/** @type {PolicySelectorGroup} */
		const FSPolicies = App.Data.Policies.Selection[society];
		if (FSPolicies) {
			for (const [key, settings] of Object.entries(FSPolicies)) {
				const currentValue = _.get(V, key);
				if (currentValue !== 0) { // assume 0 is a valid setting for all policies
					if (!settings.some(s => currentValue === (("enable" in s) ? s.enable : 1))) {
						_.set(V, key, 0);
						console.log("Reset bad policy value", key, currentValue);
					}
				}
			}
		}
	}
};

/**
 * @type {App.Verify.Utils.FunctionGameVariables}
 */
App.Verify.I.arcologies = () => {
	/** validate or reset a diplomatic target
	 * @param {FC.Zeroable<FC.ArcologyDirection>|-1} direction
	 * @returns {FC.Zeroable<FC.ArcologyDirection>|-1}
	 */
	function validTarget(direction) {
		if (!["east", "north", "northeast", "northwest", "south", "southeast", "southwest", "west", 0, -1].includes(direction)) {
			return -1;
		}
		if (!V.arcologies.find(a => a.direction === direction)) {
			return -1;
		}
		return direction;
	}

	for (const arc of V.arcologies) {
		if (typeof arc.government !== 'string') {
			arc.government = "an individual";
		}
		arc.honeymoon = Number(arc.honeymoon) ?? 0;
		arc.prosperity = Number(arc.prosperity) ?? 0;
		arc.ownership = Number(arc.ownership) ?? 0;
		arc.minority = Number(arc.minority) ?? 0;
		arc.PCminority = Number(arc.PCminority) ?? 0;
		arc.demandFactor = Number(arc.demandFactor) ?? 0;

		for (const fs of Object.keys(App.Data.FutureSociety.records)) {
			// @ts-expect-error
			if (arc[fs] === "unset") {
				arc[fs] = null;
			}
		}

		// enforce future society mutual exclusion rules
		for (const group of App.Data.FutureSociety.mutexGroups) {
			for (const fs1 of group) {
				if (arc[fs1] !== null) {
					arc[fs1] = Number(arc[fs1]) ?? null;
				}
				if (arc[fs1] !== null /* check again, may have just changed */) {
					for (const fs2 of group) {
						if (fs1 !== fs2) {
							arc[fs2] = null;
						}
					}
				}
			}
		}
		const raceArray = Array.from(App.Data.misc.filterRaces.keys());
		if (!raceArray.includes(arc.FSSupremacistRace)) {
			if (arc.FSSupremacist !== null) {
				arc.FSSupremacistRace = raceArray.random();
			} else {
				arc.FSSupremacistRace = 0;
			}
		}
		if (!raceArray.includes(arc.FSSubjugationistRace)) {
			if (arc.FSSubjugationist !== null) {
				arc.FSSubjugationistRace = raceArray.random();
			} else {
				arc.FSSubjugationistRace = 0;
			}
		}

		arc.embargo = Math.clamp(+arc.embargo, 1, 3) ?? 1;
		arc.embargoTarget = validTarget(arc.embargoTarget);
		arc.CyberEconomic = Math.clamp(+arc.CyberEconomic, 1, 3) ?? 1;
		arc.CyberEconomicTarget = validTarget(arc.CyberEconomicTarget);
		arc.CyberReputation = Math.clamp(+arc.CyberReputation, 1, 3) ?? 1;
		arc.CyberReputationTarget = validTarget(arc.CyberReputationTarget);
		arc.influenceBonus = Number(arc.influenceBonus) ?? 0;
		arc.influenceTarget = validTarget(arc.influenceTarget);
		arc.rival = Math.clamp(arc.rival, 0, 1) ?? 0;
	}
};

/**
 * @type {App.Verify.Utils.FunctionGameVariables}
 */
App.Verify.I.mods = () => {
	App.Mods.SecExp.Obj.Init();
};

/**
 * @type {App.Verify.Utils.FunctionGameVariables}
 */
App.Verify.I.pit = () => {
	if (V.pit) {
		if (getSlave(V.pit.slaveFightingBodyguard) === undefined) {
			V.pit.slaveFightingBodyguard = null;
		}
	}
};

/**
 * @type {App.Verify.Utils.FunctionGameVariables}
 */
App.Verify.I.jobIDMap = () => {
	V.JobIDMap = makeJobIdMap();
};


code:
/**
 * Runs verification for every Fetus object in the HumanStates womb.
 * @param {string} mothersIdentifier
 * @param {FC.HumanState} mother
 * @param {HTMLDivElement} [div]
 */
App.Verify.womb = (mothersIdentifier, mother, div) => {
	if ("womb" in mother && Array.isArray(mother.womb)) {
		mother.womb.forEach((fetus, i) => {
			const original = _.cloneDeep(fetus);
			try {
				App.Patch.Utils.fetus(`${mothersIdentifier}.womb[${i}]`, fetus, mother);
			} catch (e) {
				console.error(e);
				fetus = original;
				return;
			}
			// add missing props
			App.Utils.assignMissingDefaults(fetus, App.Patch.Utils.fetusTemplate(fetus, mother));
			// verify
			fetus = App.Verify.Utils.verify("fetus", `${mothersIdentifier}.womb[${i}]`, fetus, mother, div);
		});
	}
};

// ***************************************************************************************************** \\
// *************************** Put your verification functions below this line *************************** \\
// ***************************************************************************************************** \\

/**
 * @type {App.Verify.Utils.FunctionHumanState}
 */
App.Verify.I.humanAge = (actor, location) => {
	if (location === "tank slave pool") {
		return actor; // we don't need to process this actor as they are in an incubator tank
	}
	let minAge = V.minimumSlaveAge;
	if (
		location === "infant slave pool" || // Infants in cribs
		location === "PC" || (actor.ID === -1 && isPlayer(actor)) || // The player
		actor.tankBaby !== 0 // Slaves that were born from incubator tanks
	) {
		minAge = 0;
	}
	// TODO:@franklygeorge handle minAge for ChildState when it is implemented
	actor.birthWeek = Math.clamp(+actor.birthWeek, 0, 51) ?? 0;
	actor.actualAge = Math.clamp(+actor.actualAge, minAge, Infinity) ?? 18;
	actor.visualAge = Math.max(+actor.visualAge, 0) ?? actor.actualAge;
	actor.physicalAge = Math.max(+actor.physicalAge, 0) ?? actor.actualAge;
	if (typeof actor.ovaryAge !== "number") { // immortalOvaries intentionally sets ovaryAge to a negative number, so treat it more leniently
		actor.ovaryAge = actor.physicalAge;
	}
	actor.pubertyAgeXX = Math.max(+actor.pubertyAgeXX, 0) ?? V.fertilityAge;
	actor.pubertyAgeXY = Math.max(+actor.pubertyAgeXY, 0) ?? V.potencyAge;
	actor.ageAdjust = Math.clamp(+actor.ageAdjust, -40, 40) ?? 0;
	actor.NCSyouthening = Math.max(+actor.NCSyouthening, 0) ?? 0;
	return actor;
};

/**
 * @type {App.Verify.Utils.FunctionHumanState}
 */
App.Verify.I.humanHealth = (actor, location) => {
	const oldHealth = _.cloneDeep(actor.health);
	actor.health.condition = Math.clamp(actor.health.condition, -100, 100) ?? 0;
	actor.health.shortDamage = Math.max(+actor.health.shortDamage, 0) ?? 0;
	actor.health.longDamage = Math.max(+actor.health.longDamage, 0) ?? 0;
	actor.health.illness = Math.max(+actor.health.illness, 0) ?? 0;
	actor.health.tired = Math.clamp(+actor.health.tired, 0, 100) ?? 0;
	/**
	 * //FIXME: updateHealth(actor) will change the value of actor.health.health each time it is ran.
	 * Even if nothing else has changed anything in actor.health.
	 * The problem seems to be in computeAggregateHealth().
	 * @see computeAggregateHealth
	 * @see updateHealth
	 * To get around this issue we are only running updateHealth(actor) if the values were actually changed by this function
	 */
	if (Serial.stringify(oldHealth) !== Serial.stringify(actor.health)) {
		updateHealth(actor);
	}
	return actor;
};

/**
 * @type {App.Verify.Utils.FunctionHumanState}
 */
App.Verify.I.humanPhysical = (actor, location) => {
	actor.muscles = Math.clamp(+actor.muscles, -100, 100) ?? 0;
	actor.weight = Math.clamp(+actor.weight, -100, 200) ?? 0;
	actor.waist = Math.clamp(+actor.waist, -100, 100) ?? 0;
	actor.height = Math.round(Math.max(+actor.height, 0)) ?? Height.mean(actor);
	// @ts-expect-error Type 'number' is not assignable to type ...
	actor.shoulders = Math.clamp(+actor.shoulders, -2, 2) ?? 0;
	// @ts-expect-error Type 'number' is not assignable to type ...
	actor.hips = Math.clamp(+actor.hips, -2, 3) ?? 0;
	return actor;
};

/**
 * @type {App.Verify.Utils.FunctionHumanState}
 */
App.Verify.I.humanSkin = (actor, location) => {
	if (actor.geneticQuirks.albinism === 2 && !actor.albinismOverride) {
		induceAlbinism(actor, 2);
	}
	return actor;
};

/**
 * @type {App.Verify.Utils.FunctionHumanState}
 */
App.Verify.I.humanFace = (actor, location) => {
	actor.face = Math.clamp(+actor.face, -100, 100) ?? 0;
	actor.lips = Math.clamp(+actor.lips, 0, 100) ?? 15;
	return actor;
};

/**
 * @type {App.Verify.Utils.FunctionHumanState}
 */
App.Verify.I.humanHair = (actor, location) => {
	// @ts-expect-error comparision is intentional
	if (actor.hStyle === "Salon") {
		actor.hStyle = "trimmed";
	}
	actor.hLength = Math.clamp(+actor.hLength, 0, 300) ?? 60;
	actor.haircuts = Math.clamp(+actor.haircuts, 0, 1) ?? 0;
	actor.bald = Math.clamp(+actor.bald, 0, 1) ?? 0;
	return actor;
};

/**
 * @type {App.Verify.Utils.FunctionHumanState}
 */
App.Verify.I.humanBoobs = (actor, location) => {
	actor.boobs = Math.max(+actor.boobs, 100) ?? 200;
	if (actor.boobShape === "spherical" && actor.boobsImplant === 0) {
		actor.boobShape = "normal";
	}
	// @ts-expect-error Type 'number' is not assignable to type ...
	actor.areolae = Math.clamp(+actor.areolae, 0, 4) ?? 0;
	actor.lactation = /** @type {FC.LactationType} */ (Math.clamp(+actor.lactation, 0, 2) ?? 0);
	actor.boobsMilk = Math.max(+actor.boobsMilk, 0) ?? 0;
	if (actor.boobsMilk > 0 && App.Medicine.fleshSize(actor, 'boobs') < 0) {
		// should never get here, but if it does, just immediately abort!
		actor.boobsMilk = 0;
	}
	actor.lactationAdaptation = Math.clamp(+actor.lactationAdaptation, 0, 200) ?? 0;

	actor.boobsImplant = Math.clamp(+actor.boobsImplant, 0, actor.boobs) ?? 0;
	return actor;
};

/**
 * @type {App.Verify.Utils.FunctionHumanState}
 */
App.Verify.I.humanButt = (actor, location) => {
	actor.butt = Math.clamp(+actor.butt, 0, 20) ?? 1;
	actor.anus = /** @type {FC.AnusType} */ (Math.clamp(+actor.anus, 0, 4) ?? 0);
	actor.analArea = Math.max(+actor.analArea, 0) ?? 0;

	actor.buttImplant = Math.clamp(+actor.buttImplant, 0, actor.butt) ?? 0;
	return actor;
};

/**
 * @type {App.Verify.Utils.FunctionHumanState}
 */
App.Verify.I.humanGeneticQuirks = (actor, location) => {
	const quirks = {};
	App.Data.geneticQuirks.forEach((value, q) => quirks[q] = 0);
	// add any new quirks
	App.Utils.assignMissingDefaults(actor.geneticQuirks, quirks);
	return actor;
};

/**
 * @type {App.Verify.Utils.FunctionHumanState}
 */
App.Verify.I.humanPregnancy = (actor, location) => {
	actor.induce = Math.clamp(+actor.induce, 0, 1) ?? 0;
	actor.labor = Math.clamp(+actor.labor, 0, 1) ?? 0;
	actor.prematureBirth = Math.clamp(+actor.prematureBirth, 0, 1) ?? 0;
	actor.ovaries = Math.clamp(+actor.ovaries, 0, 1) ?? 0;
	actor.vasectomy = Math.clamp(+actor.vasectomy, 0, 1) ?? 0;
	actor.mpreg = Math.clamp(+actor.mpreg, 0, 1) ?? 0;
	actor.pregAdaptation = Math.max(+actor.pregAdaptation, 0) ?? 50;
	if (typeof actor.ovaImplant !== "string") {
		actor.ovaImplant = 0; // human.ovaImplant can be a string or 0
	}
	if (actor.pubertyXX === 0 && (actor.ovaries > 0 || actor.mpreg > 0) && actor.preg === -1) {
		actor.preg = 0; // no contraceptives for prepubescent humans
	}
	actor.fertPeak = Math.clamp(+actor.fertPeak, -10, 10) ?? 0;
	actor.fertLate = Math.clamp(+actor.fertLate, -10, 10) ?? 0;
	// @ts-expect-error Type 'number' is not assignable to type ...
	actor.broodmother = Math.clamp(+actor.broodmother, 0, 3) ?? 0;
	actor.broodmotherFetuses = Math.max(+actor.broodmotherFetuses, 0) ?? 0;
	actor.broodmotherOnHold = Math.clamp(+actor.broodmotherOnHold, 0, 1) ?? 0;
	actor.pregSource = +actor.pregSource ?? 0;
	if (location === "gene pool") {
		actor.womb = [];
	} else {
		WombInit(actor);
		WombNormalizePreg(actor);
	}
	return actor;
};

/**
 * @type {App.Verify.Utils.FunctionHumanState}
 */
App.Verify.I.humanBelly = (actor, location) => {
	actor.inflation = Math.clamp(+actor.inflation, 0, 3) ?? 0;
	actor.inflationMethod = Math.clamp(+actor.inflationMethod, 0, 3) ?? 0;
	actor.milkSource = Math.max(+actor.milkSource, 0) ?? 0;
	actor.cumSource = Math.max(+actor.cumSource, 0) ?? 0;
	actor.burst = Math.clamp(+actor.burst, 0, 1) ?? 0;
	actor.bellyImplant = Math.max(+actor.bellyImplant, -1) ?? -1;
	actor.cervixImplant = Math.clamp(+actor.cervixImplant, 0, 3) ?? 0;
	actor.cervixImplantTubing = Math.clamp(+actor.cervixImplantTubing, 0, 1) ?? 0;
	if (actor.cervixImplant) {
		if (
			(
				!["fillable", "advanced fillable", "hyper fillable"].includes(actor.boobsImplantType) &&
				["all", "TnA", "mombod", "boobsImplant"].includes(actor.cervixImplantTarget)
			) ||
			(
				!["fillable", "advanced fillable", "hyper fillable"].includes(actor.buttImplantType) &&
				["all", "TnA", "bottomHeavy", "buttImplant"].includes(actor.cervixImplantTarget)
			)
		) {
			actor.cervixImplantTarget = "bellyImplant";
		}
		if (actor.cervixImplantTarget === "bellyImplant" && actor.bellyImplant < 0) {
			actor.cervixImplantTarget = "none";
		}
	}
	actor.bellySag = Math.max(+actor.bellySag, 0) ?? 0;
	actor.bellySagPreg = Math.max(+actor.bellySagPreg, 0) ?? actor.bellySag;
	actor.bellyPain = Math.clamp(+actor.bellyPain, 0, 2) ?? 0;
	SetBellySize(actor);
	return actor;
};

/**
 * @type {App.Verify.Utils.FunctionHumanState}
 */
App.Verify.I.humanGenitalia = (actor, location) => {
	actor.vagina = Math.clamp(+actor.vagina, -1, 10) ?? 0;
	actor.vaginaLube = /** @type {FC.VaginaLubeType} */ (Math.clamp(+actor.vaginaLube, 0, 2) ?? 0);
	actor.labia = /** @type {FC.LabiaType} */ (Math.clamp(+actor.labia, 0, 3) ?? 0);
	actor.clit = /** @type {FC.ClitType} */ (Math.clamp(+actor.clit, 0, 5) ?? 0);
	actor.foreskin = Math.max(+actor.foreskin, 0) ?? 0;
	actor.dick = Math.max(+actor.dick, 0) ?? 0;
	if (actor.dick) {
		actor.prostate = /** @type {FC.ProstateType} */ (Math.clamp(+actor.prostate, 0, 3) ?? 1);
	}
	actor.balls = Math.max(+actor.balls, 0) ?? 0;
	actor.scrotum = Math.max(+actor.scrotum, 0) ?? actor.balls;

	// @ts-expect-error Type 'number' is not assignable to type ...
	actor.ballsImplant = Math.clamp(+actor.ballsImplant, 0, actor.balls) ?? 0;
	return actor;
};

/**
 * @type {App.Verify.Utils.FunctionHumanState}
 */
App.Verify.I.humanImplants = (actor, location) => {
	actor.ageImplant = Math.clamp(+actor.ageImplant, 0, 1) ?? 0;
	actor.faceImplant = Math.clamp(+actor.faceImplant, 0, 100) ?? 0;
	actor.lipsImplant = Math.clamp(+actor.lipsImplant, 0, 100) ?? 0;
	// @ts-expect-error Type 'number' is not assignable to type '0 | 1 | -1'
	actor.voiceImplant = Math.clamp(+actor.voiceImplant, -1, 1) ?? 0;
	actor.boobsImplant = Math.clamp(+actor.boobsImplant, 0, actor.boobs) ?? 0;
	if (actor.boobsImplant === 0) {
		actor.boobsImplantType = "none";
	} else if (actor.boobsImplant > 0 && actor.boobsImplantType === "none") {
		if (actor.boobsImplant > 10000) {
			actor.boobsImplantType = "hyper fillable";
		} else if (actor.boobsImplant > 2200) {
			actor.boobsImplantType = "advanced fillable";
		} else if (actor.boobsImplant > 1000) {
			actor.boobsImplantType = "fillable";
		} else {
			actor.boobsImplantType = "normal";
		}
	}
	actor.breastMesh = Math.clamp(+actor.breastMesh, 0, 1) ?? 0;
	actor.buttImplant = Math.clamp(+actor.buttImplant, 0, Math.min(actor.butt, 20)) ?? 0;
	actor.heightImplant = /** @type {FC.HeightImplant} */ (Math.clamp(+actor.heightImplant, -10, 10) ?? 0);
	actor.earImplant = Math.clamp(+actor.earImplant, 0, 1) ?? 0;
	// @ts-expect-error Type 'number' is not assignable to type '0 | 1 | -1'
	actor.shouldersImplant = Math.clamp(+actor.shouldersImplant, -10, 10) ?? 0;
	// @ts-expect-error Type 'number' is not assignable to type '0 | 1 | -1'
	actor.hipsImplant = Math.clamp(+actor.hipsImplant, -10, 10) ?? 0;
	return actor;
};

/**
 * @type {App.Verify.Utils.FunctionHumanState}
 */
App.Verify.I.humanPiercings = (actor, location) => {
	actor.piercing = actor.piercing ?? new App.Entity.completePiercingState();
	for (const piercing in App.Data.Piercings) {
		actor.piercing[piercing] = actor.piercing[piercing] ?? new App.Entity.piercingState();
		if (App.Data.Piercings[piercing].smart) {
			actor.piercing[piercing].smart = actor.piercing[piercing].smart ?? false;
		}
	}
	return actor;
};

/**
 * @type {App.Verify.Utils.FunctionHumanState}
 */
App.Verify.I.humanTattoo = (actor, location) => {
	actor.custom.tattoo = actor.custom.tattoo.trim();
	return actor;
};

/**
 * @type {App.Verify.Utils.FunctionHumanState}
 */
App.Verify.I.humanCosmetics = (actor, location) => {
	actor.makeup = Math.clamp(+actor.makeup, 0, 8) ?? 0;
	actor.nails = Math.clamp(+actor.nails, 0, 9) ?? 0;
	actor.chastityAnus = Math.clamp(+actor.chastityAnus, 0, 1) ?? 0;
	actor.chastityPenis = Math.clamp(+actor.chastityPenis, 0, 1) ?? 0;
	actor.chastityVagina = Math.clamp(+actor.chastityVagina, 0, 1) ?? 0;
	if (actor.clothes === "choosing her own clothes") {
		actor.clothes = "no clothing";
	}
	return actor;
};

/**
 * @type {App.Verify.Utils.FunctionHumanState}
 */
App.Verify.I.humanDiet = (actor, location) => {
	actor.hormones = Math.clamp(+actor.hormones, -2, 2) ?? 0;
	actor.hormoneBalance = Math.clamp(+actor.hormoneBalance, -500, 500) ?? 0;
	actor.aphrodisiacs = Math.clamp(+actor.aphrodisiacs, -1, 2) ?? 0;
	actor.curatives = Math.clamp(+actor.curatives, 0, 2) ?? 0;
	return actor;
};

/**
 * @type {App.Verify.Utils.FunctionHumanState}
 */
App.Verify.I.humanMisc = (actor, location) => {
	actor.slaveName = actor.slaveName ?? "Nameless";
	actor.slaveSurname = actor.slaveSurname ?? 0;
	actor.prestige = Math.clamp(+actor.prestige, 0, 3) ?? 0;
	actor.devotion = Math.clamp(+actor.devotion, -100, 100) ?? 0;
	actor.chem = Math.max(+actor.chem, 0) ?? 0;
	actor.addict = Math.max(+actor.addict, 0) ?? 0;
	actor.intelligence = Math.clamp(+actor.intelligence, -100, 100) ?? 0;
	actor.intelligenceImplant = Math.clamp(+actor.intelligenceImplant, -15, 30) ?? 0;
	actor.premature = Math.clamp(+actor.premature, 0, 1) ?? 0;
	actor.tankBaby = /** @type {0|1|2|3} */ (Math.clamp(+actor.tankBaby, 0, 3) ?? 0);
	actor.training = Math.clamp(+actor.training, 0, 150) ?? 0;
	actor.hears = /** @type {FC.Hearing} */ (Math.clamp(+actor.hears, -2, 0) ?? 0);
	// @ts-expect-error Type 'number' is not assignable to type '0 | -1'
	actor.smells = Math.clamp(+actor.smells, -1, 0) ?? 0;
	// @ts-expect-error Type 'number' is not assignable to type '0 | -1'
	actor.tastes = Math.clamp(+actor.tastes, -1, 0) ?? 0;
	actor.heels = Math.clamp(+actor.heels, 0, 1) ?? 0;
	const pLimb = hasAnyProstheticLimbs(actor) ? (getLimbCount(actor, 6) > 0 ? 2 : 1) : 0;
	actor.PLimb = /** @type {0 | 1 | 2 | 3} */ (Math.clamp(+actor.PLimb, 0, 3) ?? pLimb);
	actor.PTail = Math.clamp(+actor.PTail, 0, 1) ?? 0;
	actor.PBack = Math.clamp(+actor.PBack, 0, 1) ?? 0;
	// @ts-expect-error Type 'number' is not assignable to type '0 | 1 | 2 | 3'
	actor.voice = Math.clamp(+actor.voice, 0, 3) ?? 1;
	actor.electrolarynx = Math.clamp(+actor.electrolarynx, 0, 1) ?? 0;
	// @ts-expect-error Type 'number' is not assignable to type '0 | 1 | 2 | 3'
	actor.accent = Math.clamp(+actor.accent, 0, 4) ?? 0;
	actor.origBodyOwnerID = Math.max(+actor.origBodyOwnerID, 0) ?? 0;

	if (actor.drugs.includes("penis") && actor.dick === 0 && actor.vagina >= 0) {
		actor.drugs = /** @type {FC.PCDrug} */ (actor.drugs.replace("penis", "clitoris"));
	}
	return actor;
};


code:
/**
 * Runs verification for all InfantState objects in the infant slave pool
 * @param {HTMLDivElement} [div]
 */
App.Verify.infantStates = (div) => {
	for (const [ID, infant] of getInfants(true)) {
		App.Verify.infantState(`getInfant(${ID}, true)`, infant, div);
	}
};

/**
 * Runs verification for a single InfantState object.
 * @param {string} identifier
 * @param {FC.InfantState} actor
 * @param {HTMLDivElement} [div]
 */
App.Verify.infantState = (identifier, actor, div) => {
	const original = _.cloneDeep(actor);
	try {
		App.Patch.Utils.infantState(identifier, actor);
	} catch (e) {
		console.error(e);
		actor = original;
		return;
	}
	// add missing props
	App.Utils.assignMissingDefaults(actor, App.Patch.Utils.infantTemplate(actor));
	// verify
	actor = App.Verify.Utils.verify("infantState", identifier, actor, undefined, div);
	App.Verify.womb(identifier, actor, div); // shouldn't be needed, but why not
};

// ***************************************************************************************************** \\
// *************************** Put your verification functions below this line *************************** \\
// ***************************************************************************************************** \\

/**
 * @type {App.Verify.Utils.FunctionInfantState}
 */
App.Verify.I.infantAge = (actor) => {
	return /** @type {FC.InfantState} */ (App.Verify.I.slaveAge(actor, "infant slave pool"));
};

/**
 * @type {App.Verify.Utils.FunctionInfantState}
 */
App.Verify.I.infantHealth = (actor) => {
	return /** @type {FC.InfantState} */ (App.Verify.I.slaveHealth(actor, "infant slave pool"));
};

/**
 * @type {App.Verify.Utils.FunctionInfantState}
 */
App.Verify.I.infantPhysical = (actor) => {
	return /** @type {FC.InfantState} */ (App.Verify.I.slavePhysical(actor, "infant slave pool"));
};

/**
 * @type {App.Verify.Utils.FunctionInfantState}
 */
App.Verify.I.infantSkin = (actor) => {
	return /** @type {FC.InfantState} */ (App.Verify.I.slaveSkin(actor, "infant slave pool"));
};

/**
 * @type {App.Verify.Utils.FunctionInfantState}
 */
App.Verify.I.infantFace = (actor) => {
	return /** @type {FC.InfantState} */ (App.Verify.I.slaveFace(actor, "infant slave pool"));
};

/**
 * @type {App.Verify.Utils.FunctionInfantState}
 */
App.Verify.I.infantHair = (actor) => {
	return /** @type {FC.InfantState} */ (App.Verify.I.slaveHair(actor, "infant slave pool"));
};

/**
 * @type {App.Verify.Utils.FunctionInfantState}
 */
App.Verify.I.infantBoobs = (actor) => {
	return /** @type {FC.InfantState} */ (App.Verify.I.slaveBoobs(actor, "infant slave pool"));
};

/**
 * @type {App.Verify.Utils.FunctionInfantState}
 */
App.Verify.I.infantButt = (actor) => {
	return /** @type {FC.InfantState} */ (App.Verify.I.slaveButt(actor, "infant slave pool"));
};

/**
 * @type {App.Verify.Utils.FunctionInfantState}
 */
App.Verify.I.infantGeneticQuirks = (actor) => {
	return /** @type {FC.InfantState} */ (App.Verify.I.slaveGeneticQuirks(actor, "infant slave pool"));
};

/**
 * @type {App.Verify.Utils.FunctionInfantState}
 */
App.Verify.I.infantPregnancy = (actor) => {
	return /** @type {FC.InfantState} */ (App.Verify.I.slavePregnancy(actor, "infant slave pool"));
};

/**
 * @type {App.Verify.Utils.FunctionInfantState}
 */
App.Verify.I.infantBelly = (actor) => {
	return /** @type {FC.InfantState} */ (App.Verify.I.slaveBelly(actor, "infant slave pool"));
};

/**
 * @type {App.Verify.Utils.FunctionInfantState}
 */
App.Verify.I.infantGenitalia = (actor) => {
	return /** @type {FC.InfantState} */ (App.Verify.I.slaveGenitalia(actor, "infant slave pool"));
};

/**
 * @type {App.Verify.Utils.FunctionInfantState}
 */
App.Verify.I.infantImplants = (actor) => {
	return /** @type {FC.InfantState} */ (App.Verify.I.slaveImplants(actor, "infant slave pool"));
};

/**
 * @type {App.Verify.Utils.FunctionInfantState}
 */
App.Verify.I.infantPiercings = (actor) => {
	return /** @type {FC.InfantState} */ (App.Verify.I.slavePiercings(actor, "infant slave pool"));
};

/**
 * @type {App.Verify.Utils.FunctionInfantState}
 */
App.Verify.I.infantTattoo = (actor) => {
	return /** @type {FC.InfantState} */ (App.Verify.I.slaveTattoo(actor, "infant slave pool"));
};

/**
 * @type {App.Verify.Utils.FunctionInfantState}
 */
App.Verify.I.infantCosmetics = (actor) => {
	return /** @type {FC.InfantState} */ (App.Verify.I.slaveCosmetics(actor, "infant slave pool"));
};

/**
 * @type {App.Verify.Utils.FunctionInfantState}
 */
App.Verify.I.infantDiet = (actor) => {
	return /** @type {FC.InfantState} */ (App.Verify.I.slaveDiet(actor, "infant slave pool"));
};

/**
 * @type {App.Verify.Utils.FunctionInfantState}
 */
App.Verify.I.infantPorn = (actor) => {
	return /** @type {FC.InfantState} */ (App.Verify.I.slavePorn(actor, "infant slave pool"));
};

/**
 * @type {App.Verify.Utils.FunctionInfantState}
 */
App.Verify.I.infantRelation = (actor) => {
	return /** @type {FC.InfantState} */ (App.Verify.I.slaveRelation(actor, "infant slave pool"));
};

/**
 * @type {App.Verify.Utils.FunctionInfantState}
 */
App.Verify.I.infantSkill = (actor) => {
	return /** @type {FC.InfantState} */ (App.Verify.I.slaveSkill(actor, "infant slave pool"));
};

/**
 * @type {App.Verify.Utils.FunctionInfantState}
 */
App.Verify.I.infantStat = (actor) => {
	return /** @type {FC.InfantState} */ (App.Verify.I.slaveStat(actor, "infant slave pool"));
};

/**
 * @type {App.Verify.Utils.FunctionInfantState}
 */
App.Verify.I.infantPreferences = (actor) => {
	return /** @type {FC.InfantState} */ (App.Verify.I.slavePreferences(actor, "infant slave pool"));
};

/**
 * @type {App.Verify.Utils.FunctionInfantState}
 */
App.Verify.I.infantRules = (actor) => {
	return /** @type {FC.InfantState} */ (App.Verify.I.slaveRules(actor, "infant slave pool"));
};

/**
 * @type {App.Verify.Utils.FunctionInfantState}
 */
App.Verify.I.infantMisc = (actor) => {
	// TODO: verification for growTime, pregData, and targetLocation
	return /** @type {FC.InfantState} */ (App.Verify.I.slaveMisc(actor, "infant slave pool"));
};


code:
/**
 * Runs verification of the provided PlayerState object (Usually V.PC)
 * @param {FC.PlayerState} actor
 * @param {string} [identifier="PC"]
 * @param {HTMLDivElement} [div]
 * @param {App.Patch.Utils.HumanStateLocation} [location]
 */
App.Verify.playerState = (actor, identifier="PC", div, location="PC") => {
	const original = _.cloneDeep(actor);
	try {
		App.Patch.Utils.playerState(identifier, actor, location);
	} catch (e) {
		console.error(e);
		actor = original;
		return;
	}
	// add missing props
	App.Utils.assignMissingDefaults(actor, basePlayer());
	// verify
	actor = App.Verify.Utils.verify("playerState", identifier, actor, location, div);
	App.Verify.womb(identifier, actor, div);
};

// ***************************************************************************************************** \\
// *************************** Put your verification functions below this line *************************** \\
// ***************************************************************************************************** \\

/**
 * @type {App.Verify.Utils.FunctionPlayerState}
 */
App.Verify.I.playerAge = (actor) => {
	return /** @type {FC.PlayerState} */ (App.Verify.I.humanAge(actor, "PC"));
};

/**
 * @type {App.Verify.Utils.FunctionPlayerState}
 */
App.Verify.I.playerHealth = (actor) => {
	actor.majorInjury = Math.max(+actor.majorInjury, 0) ?? 0;
	actor = /** @type {FC.PlayerState} */ (App.Verify.I.humanHealth(actor, "PC"));
	return actor;
};

/**
 * @type {App.Verify.Utils.FunctionPlayerState}
 */
App.Verify.I.playerPhysical = (actor) => {
	actor.title = Math.clamp(+actor.title, 0, 1) ?? 1;
	actor = /** @type {FC.PlayerState} */ (App.Verify.I.humanPhysical(actor, "PC"));
	return actor;
};

/**
 * @type {App.Verify.Utils.FunctionPlayerState}
 */
App.Verify.I.playerSkin = (actor) => {
	return /** @type {FC.PlayerState} */ (App.Verify.I.humanSkin(actor, "PC"));
};

/**
 * @type {App.Verify.Utils.FunctionPlayerState}
 */
App.Verify.I.playerFace = (actor) => {
	actor = /** @type {FC.PlayerState} */ (App.Verify.I.humanFace(actor, "PC"));
	actor.face = 100; // force actor face to 100 for V.PC
	return actor;
};

/**
 * @type {App.Verify.Utils.FunctionPlayerState}
 */
App.Verify.I.playerHair = (actor) => {
	return /** @type {FC.PlayerState} */ (App.Verify.I.humanHair(actor, "PC"));
};

/**
 * @type {App.Verify.Utils.FunctionPlayerState}
 */
App.Verify.I.playerBoobs = (actor) => {
	return /** @type {FC.PlayerState} */ (App.Verify.I.humanBoobs(actor, "PC"));
};

/**
 * @type {App.Verify.Utils.FunctionPlayerState}
 */
App.Verify.I.playerButt = (actor) => {
	return /** @type {FC.PlayerState} */ (App.Verify.I.humanButt(actor, "PC"));
};

/**
 * @type {App.Verify.Utils.FunctionPlayerState}
 */
App.Verify.I.playerGeneticQuirks = (actor) => {
	return /** @type {FC.PlayerState} */ (App.Verify.I.humanGeneticQuirks(actor, "PC"));
};

/**
 * @type {App.Verify.Utils.FunctionPlayerState}
 */
App.Verify.I.playerPregnancy = (actor) => {
	actor.forcedFertDrugs = Math.max(+actor.forcedFertDrugs, 0) || 0;
	actor = /** @type {FC.PlayerState} */ (App.Verify.I.humanPregnancy(actor, "PC"));
	return actor;
};

/**
 * @type {App.Verify.Utils.FunctionPlayerState}
 */
App.Verify.I.playerBelly = (actor) => {
	return /** @type {FC.PlayerState} */ (App.Verify.I.humanBelly(actor, "PC"));
};

/**
 * @type {App.Verify.Utils.FunctionPlayerState}
 */
App.Verify.I.playerGenitalia = (actor) => {
	actor.newVag = Math.clamp(+actor.newVag, 0, 1) || 0;
	actor = /** @type {FC.PlayerState} */ (App.Verify.I.humanGenitalia(actor, "PC"));
	return actor;
};

/**
 * @type {App.Verify.Utils.FunctionPlayerState}
 */
App.Verify.I.playerImplants = (actor) => {
	return /** @type {FC.PlayerState} */ (App.Verify.I.humanImplants(actor, "PC"));
};

/**
 * @type {App.Verify.Utils.FunctionPlayerState}
 */
App.Verify.I.playerPiercings = (actor) => {
	return /** @type {FC.PlayerState} */ (App.Verify.I.humanPiercings(actor, "PC"));
};

/**
 * @type {App.Verify.Utils.FunctionPlayerState}
 */
App.Verify.I.playerTattoo = (actor) => {
	return /** @type {FC.PlayerState} */ (App.Verify.I.humanTattoo(actor, "PC"));
};

/**
 * @type {App.Verify.Utils.FunctionPlayerState}
 */
App.Verify.I.playerCosmetics = (actor) => {
	return /** @type {FC.PlayerState} */ (App.Verify.I.humanCosmetics(actor, "PC"));
};

/**
 * @type {App.Verify.Utils.FunctionPlayerState}
 */
App.Verify.I.playerDiet = (actor) => {
	actor.refreshmentType = Math.clamp(+actor.refreshmentType, 0, 6) || 0;
	// @ts-ignore this comparision is intentional
	if (actor.drugs === "none") {
		actor.drugs = "no drugs";
	}
	actor = /** @type {FC.PlayerState} */ (App.Verify.I.humanDiet(actor, "PC"));
	return actor;
};

/**
 * @type {App.Verify.Utils.FunctionPlayerState}
 */
App.Verify.I.playerRelation = (actor) => {
	actor.mother = +actor.mother ?? 0;
	actor.father = +actor.father ?? 0;
	return actor;
};

/**
 * @type {App.Verify.Utils.FunctionPlayerState}
 */
App.Verify.I.playerSkill = (actor) => {
	actor.skill.oral = Math.clamp(+actor.skill.oral, 0, 100) ?? 0;
	actor.skill.vaginal = Math.clamp(+actor.skill.vaginal, 0, 100) ?? 0;
	actor.skill.penetrative = Math.clamp(+actor.skill.penetrative, 0, 100) ?? 0;
	actor.skill.anal = Math.clamp(+actor.skill.anal, 0, 100) ?? 0;
	actor.skill.combat = Math.clamp(+actor.skill.combat, 0, 100) ?? 0;

	actor.skill.trading = Math.clamp(+actor.skill.trading, -100, 100) || 0;
	actor.skill.warfare = Math.clamp(+actor.skill.warfare, -100, 100) || 0;
	actor.skill.slaving = Math.clamp(+actor.skill.slaving, -100, 100) || 0;
	actor.skill.engineering = Math.clamp(+actor.skill.engineering, -100, 100) || 0;
	actor.skill.medicine = Math.clamp(+actor.skill.medicine, -100, 100) || 0;
	actor.skill.hacking = Math.clamp(+actor.skill.hacking, -100, 100) || 0;
	actor.skill.combat = Math.clamp(+actor.skill.combat, 0, 100) || 0;
	actor.skill.fighting = Math.clamp(+actor.skill.fighting, -1000, 1000) || 0;
	actor.skill.cumTap = Math.max(+actor.skill.cumTap, 0) || 0;

	// we really aren't supposed to be editing `V` directly from `App.Verify.Utils.FunctionPlayerState` functions
	// but this does need to be handled whenever the player's skills change
	if (V.arcologies && V.arcologies.length !== 0) { // if verification is called before arcologies are intialized we don't want to error out
		V.upgradeMultiplierArcology = upgradeMultiplier('engineering');
		V.upgradeMultiplierMedicine = upgradeMultiplier('medicine');
		V.upgradeMultiplierTrade = upgradeMultiplier('trading');
		V.HackingSkillMultiplier = upgradeMultiplier('hacking');
	}
	return actor;
};

/**
 * @type {App.Verify.Utils.FunctionPlayerState}
 */
App.Verify.I.playerStat = (actor) => {
	actor.counter.oral = Math.max(+actor.counter.oral, 0) ?? 0;
	actor.counter.vaginal = Math.max(+actor.counter.vaginal, 0) ?? 0;
	actor.counter.anal = Math.max(+actor.counter.anal, 0) ?? 0;
	actor.counter.mammary = Math.max(+actor.counter.mammary, 0) ?? 0;
	actor.counter.penetrative = Math.max(+actor.counter.penetrative, 0) ?? 0;
	actor.counter.pitWins = Math.max(+actor.counter.pitWins, 0) ?? 0;
	actor.counter.pitLosses = Math.max(+actor.counter.pitLosses, 0) ?? 0;
	actor.counter.milk = Math.max(+actor.counter.milk, 0) ?? 0;
	actor.counter.cum = Math.max(+actor.counter.cum, 0) ?? 0;
	actor.counter.birthsTotal = Math.max(+actor.counter.birthsTotal, 0) ?? 0;
	actor.counter.birthElite = Math.max(+actor.counter.birthElite, 0) || 0;
	actor.counter.birthMaster = Math.max(+actor.counter.birthMaster, 0) || 0;
	actor.counter.birthDegenerate = Math.max(+actor.counter.birthDegenerate, 0) || 0;
	actor.counter.birthClient = Math.max(+actor.counter.birthClient, 0) || 0;
	actor.counter.birthOther = Math.max(+actor.counter.birthOther, 0) || 0;
	actor.counter.birthArcOwner = Math.max(+actor.counter.birthArcOwner, 0) || 0;
	actor.counter.birthCitizen = Math.max(+actor.counter.birthCitizen, 0) || 0;
	actor.counter.birthSelf = Math.max(+actor.counter.birthSelf, 0) || 0;
	actor.counter.birthLab = Math.max(+actor.counter.birthLab, 0) || 0;
	actor.counter.birthFutaSis = Math.max(+actor.counter.birthFutaSis, 0) || 0;
	actor.counter.miscarriages = Math.max(+actor.counter.miscarriages, 0) ?? 0;
	actor.counter.abortions = Math.max(+actor.counter.abortions, 0) ?? 0;
	actor.counter.laborCount = Math.max(+actor.counter.laborCount, 0) ?? actor.counter.birthsTotal;
	actor.counter.miscarriages = Math.max(+actor.counter.miscarriages, 0) || 0;
	actor.counter.slavesFathered = Math.max(+actor.counter.slavesFathered, 0) || 0;
	actor.counter.slavesKnockedUp = Math.max(+actor.counter.slavesKnockedUp, 0) || 0;
	actor.counter.storedCum = Math.max(+actor.counter.storedCum, 0) || 0;
	actor.counter.reHymen = Math.max(+actor.counter.reHymen, 0) ?? 0;
	actor.counter.raped = Math.max(+actor.counter.raped, 0) || (V.raped && (V.raped > 0) ? 1 : 0);

	actor.counter.moves = Math.max(+actor.counter.moves, 0) || 0;
	actor.counter.quick = Math.max(+actor.counter.quick, 0) || 0;
	actor.counter.crazy = Math.max(+actor.counter.crazy, 0) || 0;
	actor.counter.virgin = Math.max(+actor.counter.virgin, 0) || 0;
	actor.counter.futa = Math.max(+actor.counter.futa, 0) || 0;
	actor.counter.preggo = Math.max(+actor.counter.preggo, 0) || 0;

	actor.bodySwap = Math.max(+actor.bodySwap, 0) ?? 0;
	return actor;
};

/**
 * @type {App.Verify.Utils.FunctionPlayerState}
 */
App.Verify.I.playerPreferences = (actor) => {
	actor.energy = Math.clamp(+actor.energy, 0, 100) ?? 0;
	actor.need = Math.max(+actor.need, 0) ?? 0;
	return actor;
};

/**
 * @type {App.Verify.Utils.FunctionPlayerState}
 */
App.Verify.I.playerRules = (actor) => {
	return actor;
};

/**
 * @type {App.Verify.Utils.FunctionPlayerState}
 */
App.Verify.I.playerMisc = (actor) => {
	actor = /** @type {FC.PlayerState} */ (App.Verify.I.humanMisc(actor, "PC"));
	ibc.recalculate_coeff_id(-1);

	return actor;
};