code:
/**
 * Runs verification for all SlaveState objects
 * @see App.Verify.Utils.getAllSlaves
 * @param {HTMLDivElement} [div]
 */
App.Verify.slaveStates = (div) => {
	let i;
	const slaves = getSlaveStates(true);
	for (i in slaves) {
		App.Verify.slaveState(`getSlaveStates(true)[${i}]`, slaves[i], "main slave pool", div);
	}
};

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

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

/**
 * @type {App.Verify.Utils.FunctionSlaveState}
 */
App.Verify.I.slaveAge = (actor, location) => {
	return /** @type {FC.SlaveState} */ (App.Verify.I.humanAge(actor, location));
};

/**
 * @type {App.Verify.Utils.FunctionSlaveState}
 */
App.Verify.I.slaveHealth = (actor, location) => {
	return /** @type {FC.SlaveState} */ (App.Verify.I.humanHealth(actor, location));
};

/**
 * @type {App.Verify.Utils.FunctionSlaveState}
 */
App.Verify.I.slavePhysical = (actor, location) => {
	return /** @type {FC.SlaveState} */ (App.Verify.I.humanPhysical(actor, location));
};

/**
 * @type {App.Verify.Utils.FunctionSlaveState}
 */
App.Verify.I.slaveSkin = (actor, location) => {
	return /** @type {FC.SlaveState} */ (App.Verify.I.humanSkin(actor, location));
};

/**
 * @type {App.Verify.Utils.FunctionSlaveState}
 */
App.Verify.I.slaveFace = (actor, location) => {
	return /** @type {FC.SlaveState} */ (App.Verify.I.humanFace(actor, location));
};

/**
 * @type {App.Verify.Utils.FunctionSlaveState}
 */
App.Verify.I.slaveHair = (actor, location) => {
	return /** @type {FC.SlaveState} */ (App.Verify.I.humanHair(actor, location));
};

/**
 * @type {App.Verify.Utils.FunctionSlaveState}
 */
App.Verify.I.slaveBoobs = (actor, location) => {
	return /** @type {FC.SlaveState} */ (App.Verify.I.humanBoobs(actor, location));
};

/**
 * @type {App.Verify.Utils.FunctionSlaveState}
 */
App.Verify.I.slaveButt = (actor, location) => {
	return /** @type {FC.SlaveState} */ (App.Verify.I.humanButt(actor, location));
};

/**
 * @type {App.Verify.Utils.FunctionSlaveState}
 */
App.Verify.I.slaveGeneticQuirks = (actor, location) => {
	return /** @type {FC.SlaveState} */ (App.Verify.I.humanGeneticQuirks(actor, location));
};

/**
 * @type {App.Verify.Utils.FunctionSlaveState}
 */
App.Verify.I.slavePregnancy = (actor, location) => {
	return /** @type {FC.SlaveState} */ (App.Verify.I.humanPregnancy(actor, location));
};

/**
 * @type {App.Verify.Utils.FunctionSlaveState}
 */
App.Verify.I.slaveBelly = (actor, location) => {
	return /** @type {FC.SlaveState} */ (App.Verify.I.humanBelly(actor, location));
};

/**
 * @type {App.Verify.Utils.FunctionSlaveState}
 */
App.Verify.I.slaveGenitalia = (actor, location) => {
	return /** @type {FC.SlaveState} */ (App.Verify.I.humanGenitalia(actor, location));
};

/**
 * @type {App.Verify.Utils.FunctionSlaveState}
 */
App.Verify.I.slaveImplants = (actor, location) => {
	return /** @type {FC.SlaveState} */ (App.Verify.I.humanImplants(actor, location));
};

/**
 * @type {App.Verify.Utils.FunctionSlaveState}
 */
App.Verify.I.slavePiercings = (actor, location) => {
	return /** @type {FC.SlaveState} */ (App.Verify.I.humanPiercings(actor, location));
};

/**
 * @type {App.Verify.Utils.FunctionSlaveState}
 */
App.Verify.I.slaveTattoo = (actor, location) => {
	return /** @type {FC.SlaveState} */ (App.Verify.I.humanTattoo(actor, location));
};

/**
 * @type {App.Verify.Utils.FunctionSlaveState}
 */
App.Verify.I.slaveCosmetics = (actor, location) => {
	actor.choosesOwnClothes = Math.clamp(+actor.choosesOwnClothes, 0, 1) ?? 0;
	actor = /** @type {FC.SlaveState} */ (App.Verify.I.humanCosmetics(actor, location));
	return actor;
};

/**
 * @type {App.Verify.Utils.FunctionSlaveState}
 */
App.Verify.I.slaveDiet = (actor, location) => {
	actor.dietCum = /** @type {FC.dietCumType} */ (Math.clamp(+actor.dietCum, 0, 2) ?? 0);
	actor.dietMilk = /** @type {FC.dietMilkType} */ (Math.clamp(+actor.dietMilk, 0, 2) ?? 0);
	actor.onDiet = Math.clamp(+actor.onDiet, 0, 1) ?? 0;
	// @ts-ignore this comparision is intentional
	if (actor.drugs === "none") {
		actor.drugs = "no drugs";
	}
	actor = /** @type {FC.SlaveState} */ (App.Verify.I.humanDiet(actor, location));
	return actor;
};

/**
 * @type {App.Verify.Utils.FunctionSlaveState}
 */
App.Verify.I.slavePorn = (actor, location) => {
	actor.porn.feed = Math.clamp(+actor.porn.feed, 0, 1) ?? 0;
	actor.porn.viewerCount = Math.max(+actor.porn.viewerCount, 0) ?? 0;
	actor.porn.spending = Math.clamp(+actor.porn.spending, 0, 5000) ?? 0;
	actor.porn.prestige = Math.clamp(+actor.porn.prestige, 0, 3) ?? 0;
	if (actor.porn.fameType === "none") {
		actor.porn.prestige = 0;
		actor.porn.prestigeDesc = 0;
	}
	for (const genre of App.Porn.getAllGenres()) {
		actor.porn.fame[genre.fameVar] = Math.max(+actor.porn.fame[genre.fameVar], 0) ?? 0;
	}
	return actor;
};

/**
 * @type {App.Verify.Utils.FunctionSlaveState}
 */
App.Verify.I.slaveRelation = (actor, location) => {
	actor.mother = +actor.mother ?? 0;
	actor.father = +actor.father ?? 0;
	actor.canRecruit = Math.clamp(+actor.canRecruit, 0, 1) ?? 0;

	actor.relationship = /** @type {FC.RelationShipKind} */ (Math.clamp(+actor.relationship, -3, 5) ?? 0);
	actor.relationshipTarget = Math.max(+actor.relationshipTarget, 0) ?? 0;

	actor.rivalryTarget = Math.max(+actor.rivalryTarget, 0) ?? 0;
	actor.rivalry = /** @type {FC.RivalryType} */ (Math.clamp(+actor.rivalry, 0, 3) ?? 0);

	actor.cloneID = +actor.cloneID ?? 0;

	if (location === "gene pool") { return actor; }

	if (actor.relationship > 0) {
		if (!getSlave(actor.relationshipTarget)) {
			actor.relationship = 0;
			actor.relationshipTarget = 0;
		}
	}

	if (actor.rivalry !== 0) {
		if (!getSlave(actor.rivalryTarget)) {
			actor.rivalry = 0;
			actor.rivalryTarget = 0;
		}
	}

	return actor;
};

/**
 * @type {App.Verify.Utils.FunctionSlaveState}
 */
App.Verify.I.slaveSkill = (actor, location) => {
	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.whoring = Math.clamp(+actor.skill.whoring, 0, 100) ?? 0;
	actor.skill.entertainment = Math.clamp(+actor.skill.entertainment, 0, 100) ?? 0;
	actor.skill.combat = Math.clamp(+actor.skill.combat, 0, 100) ?? 0;
	actor.skill.headGirl = Math.clamp(+actor.skill.headGirl, 0, 200) ?? 0;
	actor.skill.recruiter = Math.clamp(+actor.skill.recruiter, 0, 200) ?? 0;
	actor.skill.bodyguard = Math.clamp(+actor.skill.bodyguard, 0, 200) ?? 0;
	actor.skill.madam = Math.clamp(+actor.skill.madam, 0, 200) ?? 0;
	actor.skill.DJ = Math.clamp(+actor.skill.DJ, 0, 200) ?? 0;
	actor.skill.nurse = Math.clamp(+actor.skill.nurse, 0, 200) ?? 0;
	actor.skill.teacher = Math.clamp(+actor.skill.teacher, 0, 200) ?? 0;
	actor.skill.attendant = Math.clamp(+actor.skill.attendant, 0, 200) ?? 0;
	actor.skill.matron = Math.clamp(+actor.skill.matron, 0, 200) ?? 0;
	actor.skill.stewardess = Math.clamp(+actor.skill.stewardess, 0, 200) ?? 0;
	actor.skill.milkmaid = Math.clamp(+actor.skill.milkmaid, 0, 200) ?? 0;
	actor.skill.farmer = Math.clamp(+actor.skill.farmer, 0, 200) ?? 0;
	actor.skill.wardeness = Math.clamp(+actor.skill.wardeness, 0, 200) ?? 0;
	actor.skill.servant = Math.clamp(+actor.skill.servant, 0, 200) ?? 0;
	actor.skill.entertainer = Math.clamp(+actor.skill.entertainer, 0, 200) ?? 0;
	actor.skill.whore = Math.clamp(+actor.skill.whore, 0, 200) ?? 0;
	return actor;
};

/**
 * @type {App.Verify.Utils.FunctionSlaveState}
 */
App.Verify.I.slaveStat = (actor, location) => {
	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.publicUse = Math.max(+actor.counter.publicUse, 0) ?? 0;
	actor.counter.mammary = Math.max(+actor.counter.mammary, 0) ?? 0;
	actor.counter.penetrative = Math.max(+actor.counter.penetrative, 0) ?? 0;
	actor.counter.pitKills = Math.max(+actor.counter.pitKills, 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.births = Math.max(+actor.counter.births, 0) ?? 0;
	actor.counter.birthsTotal = Math.max(+actor.counter.birthsTotal, 0) ?? actor.counter.births;
	actor.counter.abortions = Math.max(+actor.counter.abortions, 0) ?? 0;
	actor.counter.miscarriages = Math.max(+actor.counter.miscarriages, 0) ?? 0;
	actor.counter.laborCount = Math.max(+actor.counter.laborCount, 0) ?? actor.counter.birthsTotal;
	actor.counter.slavesFathered = Math.max(+actor.counter.slavesFathered, 0) ?? 0;
	actor.counter.PCChildrenFathered = Math.max(+actor.counter.PCChildrenFathered, 0) ?? 0;
	actor.counter.slavesKnockedUp = Math.max(+actor.counter.slavesKnockedUp, 0) ?? 0;
	actor.counter.PCKnockedUp = Math.max(+actor.counter.PCKnockedUp, 0) ?? 0;
	actor.counter.bestiality = Math.max(+actor.counter.bestiality, 0) ?? 0;
	actor.counter.PCChildrenBeared = Math.max(+actor.counter.PCChildrenBeared, 0) ?? 0;
	actor.counter.timesBred = Math.max(+actor.counter.timesBred, 0) ?? 0;
	actor.counter.reHymen = Math.max(+actor.counter.reHymen, 0) ?? 0;
	actor.counter.events = Math.max(+actor.counter.events, 0) ?? 0;
	actor.bodySwap = Math.max(+actor.bodySwap, 0) ?? 0;
	return actor;
};

/**
 * @type {App.Verify.Utils.FunctionSlaveState}
 */
App.Verify.I.slavePreferences = (actor, location) => {
	actor.energy = Math.clamp(+actor.energy, 0, 100) ?? 0;
	actor.need = Math.max(+actor.need, 0) ?? 0;
	actor.attrXY = Math.clamp(+actor.attrXY, 0, 100) ?? 0;
	actor.attrXX = Math.clamp(+actor.attrXX, 0, 100) ?? 0;
	actor.attrKnown = Math.clamp(+actor.attrKnown, 0, 1) ?? 0;
	actor.fetishStrength = Math.clamp(+actor.fetishStrength, 0, 100) ?? 0;
	actor.fetishKnown = Math.clamp(+actor.fetishKnown, 0, 1) ?? 0;
	return actor;
};

/**
 * @type {App.Verify.Utils.FunctionSlaveState}
 */
App.Verify.I.slaveRules = (actor, location) => {
	if (actor.useRulesAssistant !== 0) {
		actor.useRulesAssistant = 1;
	}
	actor.choosesOwnAssignment = Math.clamp(+actor.choosesOwnAssignment, 0, 1) ?? 0;
	actor.PCExclude = Math.clamp(+actor.PCExclude, 0, 1) ?? 0;
	actor.HGExclude = Math.clamp(+actor.HGExclude, 0, 1) ?? 0;
	actor.StudExclude = Math.clamp(+actor.StudExclude, 0, 1) ?? 0;
	actor.choosesOwnChastity = Math.clamp(+actor.choosesOwnChastity, 0, 1) ?? 0;
	actor.breedingMark = Math.clamp(+actor.breedingMark, 0, 1) ?? 0;
	actor.rudeTitle = Math.clamp(+actor.rudeTitle, 0, 1) ?? 0;
	return actor;
};

/**
 * @type {App.Verify.Utils.FunctionSlaveState}
 */
App.Verify.I.slaveMisc = (actor, location) => {
	actor.career = actor.career ?? "a slave";
	actor.origin = actor.origin ?? "";
	actor.weekAcquired = +actor.weekAcquired ?? 0;
	actor.newGamePlus = Math.clamp(+actor.newGamePlus, 0, 1) ?? 0;
	actor.oldDevotion = Math.clamp(+actor.oldDevotion, -100, 100) ?? 0;
	actor.trust = Math.clamp(+actor.trust, -100, 100) ?? 0;
	actor.oldTrust = Math.clamp(+actor.oldTrust, -100, 100) ?? 0;
	actor.fuckdoll = Math.clamp(+actor.fuckdoll, 0, 100) ?? 0;
	actor.subTarget = Math.max(+actor.subTarget, -1) ?? 0;
	actor.sentence = Math.max(+actor.sentence, 0) ?? 0;
	actor.indenture = Math.max(+actor.indenture, -1) ?? -1;
	actor.indentureRestrictions = /** @type {FC.IndentureType} */ (Math.clamp(+actor.indentureRestrictions, 0, 2) ?? 0);
	actor.slaveCost = Math.min(+actor.slaveCost, 1) ?? 1;
	actor.lifetimeCashExpenses = Math.min(+actor.lifetimeCashExpenses, 0) ?? 0;
	actor.lifetimeCashIncome = Math.max(+actor.lifetimeCashIncome, 0) ?? 0;
	actor.lastWeeksCashIncome = Math.max(+actor.lastWeeksCashIncome, 0) ?? 0;
	actor.lifetimeRepExpenses = Math.min(+actor.lifetimeRepExpenses, 0) ?? 0;
	actor.lifetimeRepIncome = Math.max(+actor.lifetimeRepIncome, 0) ?? 0;
	actor.lastWeeksRepExpenses = Math.min(+actor.lastWeeksRepExpenses, 0) ?? 0;
	actor.lastWeeksRepIncome = Math.max(+actor.lastWeeksRepIncome, 0) ?? 0;
	actor.sexAmount = Math.max(+actor.sexAmount, 0) ?? 0;
	actor.sexQuality = Math.max(+actor.sexQuality, 0) ?? 0;
	actor.whoreClass = Math.max(+actor.whoreClass, 0) ?? 0;
	if (!Object.values(Job).includes(actor.assignment)) {
		actor.assignment = Job.REST;
		App.Verify.I.jobIDMap();
	}
	actor = /** @type {FC.SlaveState} */ (App.Verify.I.humanMisc(actor, location));
	return actor;
};


code:
/**
 * Runs verification for all TankSlaveState objects in the tank slave pool
 * @param {HTMLDivElement} [div]
 */
App.Verify.tankSlaveStates = (div) => {
	for (const [ID, tankSlave] of getTankSlaves(true)) {
		App.Verify.tankSlaveState(`getTankSlave(${ID}, true)`, tankSlave, div);
	}
};

/**
 * Runs verification for a single TankSlaveState object.
 * @param {string} identifier
 * @param {FC.TankSlaveState} actor
 * @param {HTMLDivElement} [div]
 */
App.Verify.tankSlaveState = (identifier, actor, div) => {
	const original = _.cloneDeep(actor);
	try {
		App.Patch.Utils.tankSlaveState(identifier, actor);
	} catch (e) {
		console.error(e);
		actor = original;
		return;
	}
	// add missing props
	App.Utils.assignMissingDefaults(
		actor,
		App.Patch.Utils.slaveTemplate(actor),
		App.Entity.TankSlaveState.toTank(App.Patch.Utils.slaveTemplate(actor))
	);
	// verify
	actor = App.Verify.Utils.verify("tankSlaveState", 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.FunctionTankSlaveState}
 */
App.Verify.I.tankSlaveIncubatorSettings = (actor) => {
	/*
	TODO:@franklygeorge verification for:
		actor.incubatorSettings.imprint
		actor.incubatorSettings.weight
		actor.incubatorSettings.muscles
		actor.incubatorSettings.growthStims
		actor.incubatorSettings.reproduction
		actor.incubatorSettings.growTime
		actor.incubatorSettings.pregAdaptation
		actor.incubatorSettings.pregAdaptationPower
		actor.incubatorSettings.pregAdaptationInWeek
	While your at it these values could use defined types

	*/
	return actor;
};

/**
 * @type {App.Verify.Utils.FunctionTankSlaveState}
 */
App.Verify.I.tankSlaveAge = (actor) => {
	return /** @type {FC.TankSlaveState} */ (App.Verify.I.slaveAge(actor, "tank slave pool"));
};

/**
 * @type {App.Verify.Utils.FunctionTankSlaveState}
 */
App.Verify.I.tankSlaveHealth = (actor) => {
	return /** @type {FC.TankSlaveState} */ (App.Verify.I.slaveHealth(actor, "tank slave pool"));
};

/**
 * @type {App.Verify.Utils.FunctionTankSlaveState}
 */
App.Verify.I.tankSlavePhysical = (actor) => {
	return /** @type {FC.TankSlaveState} */ (App.Verify.I.slavePhysical(actor, "tank slave pool"));
};

/**
 * @type {App.Verify.Utils.FunctionTankSlaveState}
 */
App.Verify.I.tankSlaveSkin = (actor) => {
	return /** @type {FC.TankSlaveState} */ (App.Verify.I.slaveSkin(actor, "tank slave pool"));
};

/**
 * @type {App.Verify.Utils.FunctionTankSlaveState}
 */
App.Verify.I.tankSlaveFace = (actor) => {
	return /** @type {FC.TankSlaveState} */ (App.Verify.I.slaveFace(actor, "tank slave pool"));
};

/**
 * @type {App.Verify.Utils.FunctionTankSlaveState}
 */
App.Verify.I.tankSlaveHair = (actor) => {
	return /** @type {FC.TankSlaveState} */ (App.Verify.I.slaveHair(actor, "tank slave pool"));
};

/**
 * @type {App.Verify.Utils.FunctionTankSlaveState}
 */
App.Verify.I.tankSlaveBoobs = (actor) => {
	return /** @type {FC.TankSlaveState} */ (App.Verify.I.slaveBoobs(actor, "tank slave pool"));
};

/**
 * @type {App.Verify.Utils.FunctionTankSlaveState}
 */
App.Verify.I.tankSlaveButt = (actor) => {
	return /** @type {FC.TankSlaveState} */ (App.Verify.I.slaveButt(actor, "tank slave pool"));
};

/**
 * @type {App.Verify.Utils.FunctionTankSlaveState}
 */
App.Verify.I.tankSlaveGeneticQuirks = (actor) => {
	return /** @type {FC.TankSlaveState} */ (App.Verify.I.slaveGeneticQuirks(actor, "tank slave pool"));
};

/**
 * @type {App.Verify.Utils.FunctionTankSlaveState}
 */
App.Verify.I.tankSlavePregnancy = (actor) => {
	return /** @type {FC.TankSlaveState} */ (App.Verify.I.slavePregnancy(actor, "tank slave pool"));
};

/**
 * @type {App.Verify.Utils.FunctionTankSlaveState}
 */
App.Verify.I.tankSlaveBelly = (actor) => {
	return /** @type {FC.TankSlaveState} */ (App.Verify.I.slaveBelly(actor, "tank slave pool"));
};

/**
 * @type {App.Verify.Utils.FunctionTankSlaveState}
 */
App.Verify.I.tankSlaveGenitalia = (actor) => {
	return /** @type {FC.TankSlaveState} */ (App.Verify.I.slaveGenitalia(actor, "tank slave pool"));
};

/**
 * @type {App.Verify.Utils.FunctionTankSlaveState}
 */
App.Verify.I.tankSlaveImplants = (actor) => {
	return /** @type {FC.TankSlaveState} */ (App.Verify.I.slaveImplants(actor, "tank slave pool"));
};

/**
 * @type {App.Verify.Utils.FunctionTankSlaveState}
 */
App.Verify.I.tankSlavePiercings = (actor) => {
	return /** @type {FC.TankSlaveState} */ (App.Verify.I.slavePiercings(actor, "tank slave pool"));
};

/**
 * @type {App.Verify.Utils.FunctionTankSlaveState}
 */
App.Verify.I.tankSlaveTattoo = (actor) => {
	return /** @type {FC.TankSlaveState} */ (App.Verify.I.slaveTattoo(actor, "tank slave pool"));
};

/**
 * @type {App.Verify.Utils.FunctionTankSlaveState}
 */
App.Verify.I.tankSlaveCosmetics = (actor) => {
	return /** @type {FC.TankSlaveState} */ (App.Verify.I.slaveCosmetics(actor, "tank slave pool"));
};

/**
 * @type {App.Verify.Utils.FunctionTankSlaveState}
 */
App.Verify.I.tankSlaveDiet = (actor) => {
	return /** @type {FC.TankSlaveState} */ (App.Verify.I.slaveDiet(actor, "tank slave pool"));
};

/**
 * @type {App.Verify.Utils.FunctionTankSlaveState}
 */
App.Verify.I.tankSlavePorn = (actor) => {
	return /** @type {FC.TankSlaveState} */ (App.Verify.I.slavePorn(actor, "tank slave pool"));
};

/**
 * @type {App.Verify.Utils.FunctionTankSlaveState}
 */
App.Verify.I.tankSlaveRelation = (actor) => {
	return /** @type {FC.TankSlaveState} */ (App.Verify.I.slaveRelation(actor, "tank slave pool"));
};

/**
 * @type {App.Verify.Utils.FunctionTankSlaveState}
 */
App.Verify.I.tankSlaveSkill = (actor) => {
	return /** @type {FC.TankSlaveState} */ (App.Verify.I.slaveSkill(actor, "tank slave pool"));
};

/**
 * @type {App.Verify.Utils.FunctionTankSlaveState}
 */
App.Verify.I.tankSlaveStat = (actor) => {
	return /** @type {FC.TankSlaveState} */ (App.Verify.I.slaveStat(actor, "tank slave pool"));
};

/**
 * @type {App.Verify.Utils.FunctionTankSlaveState}
 */
App.Verify.I.tankSlavePreferences = (actor) => {
	return /** @type {FC.TankSlaveState} */ (App.Verify.I.slavePreferences(actor, "tank slave pool"));
};

/**
 * @type {App.Verify.Utils.FunctionTankSlaveState}
 */
App.Verify.I.tankSlaveRules = (actor) => {
	return /** @type {FC.TankSlaveState} */ (App.Verify.I.slaveRules(actor, "tank slave pool"));
};

/**
 * @type {App.Verify.Utils.FunctionTankSlaveState}
 */
App.Verify.I.tankSlaveMisc = (actor) => {
	return /** @type {FC.TankSlaveState} */ (App.Verify.I.slaveMisc(actor, "tank slave pool"));
};

repo_name: Nithhogg/fc

license: mit


code:
/**
 * @param {function(FC.SlaveState):boolean} predicate
 * @returns {FC.HumanID} ID of the first matched slave or 0 if no match was found
 */
App.Verify.Utils.findSlaveId = (predicate) => {
	const s = getSlaves().find(predicate);
	return s ? s.key : 0;
};

/**
 * @typedef {object} App.Verify.Utils.ChangeRecord
 * @property {"key"|"value"|"type"} type
 * @property {string} description
 * @property {string} path
 * @property {*} [origVal]
 * @property {*} [newVal]
 */

/**
 * Takes two objects and compares their changes, returning an array with the differences
 * @param {object} obj1 the original object
 * @param {object} obj2 the potentially different object
 * @param {keyof App.Verify.Utils.InstructionSet} setKey
 * @param {string} instructionID
 * @param {string} identifier
 * @returns {App.Verify.Utils.ChangeRecord[]|undefined}
 */
App.Verify.Utils.changeSummary = (obj1, obj2, setKey, instructionID, identifier) => {
	const temp = `When executing 'App.Verify.instructions.${setKey}.${instructionID}': '{path}' {change}`;

	if (Serial.stringify(obj1) === Serial.stringify(obj2)) { return undefined; } // Quick and dirty comparision, don't do work when it isn't needed

	// something has changed, deep comparision needed
	/**
	 * @param {object} obj1
	 * @param {object} obj2
	 * @param {string} [path=""]
	 * @returns {App.Verify.Utils.ChangeRecord[]}
	 */
	const compare = (obj1, obj2, path="") => {
		/** @type {App.Verify.Utils.ChangeRecord[]} */
		let changes = [];

		if (Serial.stringify(obj1) === Serial.stringify(obj2)) { return changes; } // don't do work when it isn't needed

		let keys1 = Object.keys(obj1);
		let keys2 = Object.keys(obj2);

		if (JSON.stringify(keys1) !== JSON.stringify(keys2)) {
			// a key was added or deleted
			// check for deletion
			keys1.forEach((key) => {
				if (!(key in obj2)) {
					// the key was deleted
					changes.push({
						type: "key",
						description: temp.replace("{path}", `${path}.${key}`).replace("{change}", `<${typeof obj1[key]}(${obj1[key]})> was deleted`),
						path: `${path}.${key}`,
					});
				}
			});
			// check for addition
			keys2.forEach((key) => {
				if (!(key in obj1)) {
					changes.push({
						type: "key",
						description: temp.replace("{path}", `${path}.${key}`).replace("{change}", `<${typeof obj2[key]}(${obj2[key]})> was created`),
						path: `${path}.${key}`,
					});
				}
			});
		}

		const sharedKeys = [...new Set(Object.keys(obj1).concat(Object.keys(obj2)).filter((value) => {
			return (value in obj1) && (value in obj2);
		}))];

		// check types and values
		sharedKeys.forEach((key) => {
			if (obj1[key] && typeof obj1[key] !== typeof obj2[key]) {
				// type changed
				changes.push({
					type: "type",
					description: temp.replace("{path}", `${path}.${key}`).replace("{change}", `was changed from type '${typeof obj1[key]}' with value of '${obj1[key]}' to type '${typeof obj2[key]}' with value of '${obj2[key]}'`),
					path: `${path}.${key}`,
				});
			} else if ((!obj1[key] || typeof obj1[key] !== "object") && obj1[key] !== obj2[key]) {
				// value changed
				changes.push({
					type: "value",
					description: temp.replace("{path}", `${path}.${key}`).replace("{change}", `changed from ${obj1[key]} to ${obj2[key]}`),
					path: `${path}.${key}`,
					origVal: obj1[key],
					newVal: obj2[key],
				});
			} else if (typeof obj1[key] === "object") {
				// recurse object
				changes = changes.concat(compare(obj1[key], obj2[key], `${path}.${key}`));
			}
		});
		return changes;
	};

	let changes = compare(obj1, obj2, identifier);

	return (changes.length === 0) ? undefined : changes;
};

/**
 * Used by `App.Patch.applyAll` to detect when an error occurs during verification.
 * Set to false by `App.Patch.applyAll`.
 * set to true by `App.Verify.Utils.verify` on error.
 * @see App.Patch.applyAll
 * @see App.Verify.Utils.verify
 * @type {boolean}
 */
App.Verify.Utils.verificationError = false;

/**
 * verifies the given object using the given set and returns it.
 * if extra === "V" then it does verification on V and returns nothing.
 * if a verification instruction fails an error will be logged to the console and the object will be returned with all of the changes up to the failed verification instruction
 * @param {keyof App.Verify.Utils.InstructionSet} setKey
 * @param {string} identifier a string that helps identify where an object came from
 * @param {object|undefined} [obj] the object to be verified, this object will not be modified
 * @param {*|undefined} [extra] extra data to pass to the verification instruction
 * @param {HTMLDivElement} [div] if provided info will be appended to this div
 * @returns {object|undefined} the verified (and potential changed) object, you should overwrite the original with this
 */
App.Verify.Utils.verify = (setKey, identifier, obj, extra, div) => {
	const DEBUG = false;
	const isV = (extra === "V");
	if (isV && setKey !== "gameVariables") {
		throw new Error(`setKey '${setKey}' cannot be used without an object`);
	} else if (!isV && setKey === "gameVariables") {
		throw new Error(`setKey '${setKey}' can only be used with V`);
	}

	obj = isV ? V : obj;

	const setName = `App.Verify.instructions.${setKey}`;
	if (DEBUG) {
		console.log(`\\/ \\/ \\/ Verification started for '${setName}' on '${identifier}'  \\/ \\/ \\/`);
	}
	let original;
	for (const instructionID in App.Verify.instructions[setKey]) {
		if (DEBUG) {
			console.log(`Executing '${setName}.${instructionID}' on '${identifier}'  \\/ \\/ \\/`);
		}
		// create a copy for change detection and/or rollback
		original = _.cloneDeep(isV ? V : obj);
		try {
			// apply the verification instruction
			if (isV) {
				// @ts-ignore expects 2 arguments, even thought that is wrong
				App.Verify.instructions[setKey][instructionID]();
				// we don't assign to V as it is a constant
			} else if (extra) {
				obj = App.Verify.instructions[setKey][instructionID](obj, extra);
			} else {
				// @ts-ignore expects 2 arguments, even thought that may be wrong
				obj = App.Verify.instructions[setKey][instructionID](obj);
			}
			if (obj.releaseID !== original.releaseID) {
				throw new Error(`releaseID was changed from '${original.releaseID}' to '${obj.releaseID}'. This is not allowed!`);
			}
		} catch (e) {
			App.Verify.Utils.verificationError = true;
			const message = `Verification failed when executing '${setName}.${instructionID}' on '${identifier}'`;
			if (div) {
				$(div).append(`<div><span class="error">${message}</span></div>`);
				div.append(App.UI.DOM.formatException(e));
			} else {
				console.error(`/\\ /\\ /\\ ${message} /\\ /\\ /\\`, e);
			}
			if (isV) {
				// rollback the game state
				State.restore();
				// and return nothing
				return;
			} else {
				// rollback to the last successful verification and return it
				return original;
			}
		}

		// report changes if V.logVerificationChanges is set to 1
		if (getProp(V, "reportVerificationChanges", 0) === 1) {
			const changes = App.Verify.Utils.changeSummary(original, obj, setKey, instructionID, identifier);
			if (changes) {
				changes.forEach((record) => {
					if (record.path === "V.IDNumber") {
						return;
					}
					let level = "error";
					if (
						record.description.includes(`'App.Verify.instructions.gameVariables.genePool'`) ||
						record.type === "key"
					) {
						// The genePool verification is one of the (maybe the only) times that properties should actually be deleted/created
						level = "orange";
					}
					if (["key", "type"].includes(record.type)) {
						if (div) {
							$(div).append(`<div><span class="${level}">${record.description}</span></div>`);
						} else if (level === "error") {
							console.error(record.description);
						} else {
							console.warn(record.description);
						}
					} else {
						if (div) {
							$(div).append(`<div><span>${record.description}</span></div>`);
						} else {
							console.warn(record.description);
							console.warn("original val:", record.origVal, "new val:", record.newVal);
						}
					}
				});
			}
		}
	}
	if (DEBUG) {
		console.log(`/\\ /\\ /\\ Verification finished for '${setName}' on '${identifier}' /\\ /\\ /\\`);
	}
	if (isV) {
		return;
	} else {
		return obj;
	}
};

/**
 * @typedef {(actor: FC.HumanState, location: App.Patch.Utils.HumanStateLocation) => FC.HumanState} App.Verify.Utils.FunctionHumanState
 */

/**
 * @typedef {() => void} App.Verify.Utils.FunctionGameVariables
 */

/**
 * @typedef {{[key: string]: App.Verify.Utils.FunctionGameVariables}} App.Verify.Utils.GameVariablesInstructionDef
 */

/**
 * @typedef {(actor: FC.SlaveState, location: App.Patch.Utils.HumanStateLocation) => FC.SlaveState} App.Verify.Utils.FunctionSlaveState
 */

/**
 * @typedef {{[key: string]: App.Verify.Utils.FunctionSlaveState}} App.Verify.Utils.SlaveStateInstructionDef
 */

/**
 * @typedef {(actor: FC.TankSlaveState) => FC.TankSlaveState} App.Verify.Utils.FunctionTankSlaveState
 */

/**
 * @typedef {{[key: string]: App.Verify.Utils.FunctionTankSlaveState}} App.Verify.Utils.TankSlaveStateInstructionDef
 */

/**
 * @typedef {(actor: FC.PlayerState) => FC.PlayerState} App.Verify.Utils.FunctionPlayerState
 */

/**
 * @typedef {{[key: string]: App.Verify.Utils.FunctionPlayerState}} App.Verify.Utils.PlayerStateInstructionDef
 */

/**
 * @typedef {(actor: FC.ChildState) => FC.ChildState} App.Verify.Utils.FunctionChildState
 */

/**
 * @typedef {{[key: string]: App.Verify.Utils.FunctionChildState}} App.Verify.Utils.ChildStateInstructionDef
 */

/**
 * @typedef {(actor: FC.InfantState) => FC.InfantState} App.Verify.Utils.FunctionInfantState
 */

/**
 * @typedef {{[key: string]: App.Verify.Utils.FunctionInfantState}} App.Verify.Utils.InfantStateInstructionDef
 */

/**
 * @typedef {(actor: FC.Fetus, mother: FC.HumanState) => FC.Fetus} App.Verify.Utils.FunctionFetus
 */

/**
 * @typedef {{[key: string]: App.Verify.Utils.FunctionFetus}} App.Verify.Utils.FetusInstructionDef
 */

/**
 * @typedef {(order: FC.CustomSlaveOrder) => FC.CustomSlaveOrder} App.Verify.Utils.FunctionCustomSlaveOrder
 */

/**
 * @typedef {{[key: string]: App.Verify.Utils.FunctionCustomSlaveOrder}} App.Verify.Utils.CustomSlaveOrderInstructionDef
 */

/**
 * This and App.Patch.Utils.PatchRecord should have the same keys.
 * With the exception of the `humanState`, `pre`, and `post` keys which should be missing from here
 * and the `gameVariables` key which should be missing from `App.Patch.Utils.PatchRecord`.
 * @see App.Patch.Utils.PatchRecord
 *
 * @typedef {object} App.Verify.Utils.InstructionSet
 * @property {App.Verify.Utils.GameVariablesInstructionDef} gameVariables
 * @property {App.Verify.Utils.SlaveStateInstructionDef} slaveState
 * @property {App.Verify.Utils.TankSlaveStateInstructionDef} tankSlaveState
 * @property {App.Verify.Utils.PlayerStateInstructionDef} playerState
 * @property {App.Verify.Utils.ChildStateInstructionDef} childState
 * @property {App.Verify.Utils.InfantStateInstructionDef} infantState
 * @property {App.Verify.Utils.FetusInstructionDef} fetus
 * @property {App.Verify.Utils.CustomSlaveOrderInstructionDef} customSlaveOrder
 */


code:
/**
 * This file must stay below all the other verification files
 * Need to know how to use verification? Go to the line with `====== How to use verification ======`.
 * Need to update/add verification? Then keep reading.
 *
 * Some things to note:
 *  - Verification should not delete, rename, or add any properties.
 *  - Verification should not change the type of a property.
 *  - Verification can and should change the value of a property if needed.
 *  - If a property needs deleted, renamed, added, or to have its type changed that needs to be handled in a patch.
 *  - Instructions for creating a patch can be found at the top of `/src/data/patches/patch.js`.
 *
 * Each data structure type defined in `App.Verify.instructions` has its own file in `/src/data/verification/`.
 * Most likely the thing you want to verify already has a verification function defined in `App.Verify.instructions`.
 * These verification functions are defined in the data structure type's file below the line with `Put your verification functions below this line`.
 * @see App.Verify.instructions
 * If so then just make your changes to the existing verification function.
 * If not then continue below.
 *
 * The easiest way to create a new verification function is to copy an existing one and rename it.
 * Lets say we named our new function `App.Verify.I.slaveBrain`.
 * Now we want to add our function to the correct instruction set.
 * In this example it would be `App.Verify.instructions.slaveState`.
 * The key is not very important, but in most cases it should match the last part of the function.
 * So in this case `slaveBrain` would be our key.
 * So we would add a line that had the contents `slaveBrain: App.Verify.I.slaveBrain` to `App.Verify.instructions.slaveState`.
 * The location we add this line to is potentally important as every function before ours in the set will be executed first.
 * For example: `App.Verify.I.slaveBelly()` has a call to `SetBellySize()` which checks pregnancy and as such should and does run after `App.Verify.I.slavePregnancy()`
 *
 * If you are verifying a data structure that extends another data structure then you also call the verification functions for the extended data structure.
 * For example:
 * TankSlaveState extends SlaveState which extends HumanState.
 * If you look at `App.Verify.I.tankSlaveAge()` you will see that it doesn't do anything but call `App.Verify.I.slaveAge()`.
 * And the last thing `App.Verify.I.slaveAge()` does is call `App.Verify.I.humanAge()` which does most of the heavy lifting.
 * @see App.Verify.I.tankSlaveAge
 * @see App.Verify.I.slaveAge
 * @see App.Verify.I.humanAge
 *
 * ====== How to use verification ======
 * In most cases you can run full verification for your object.
 * Each of the functions listed below catches any errors thrown by the validation instructions and returns the object in the state it was right before the error.
 * The validation instructions themselves are stored under `App.Verify.I` and can be called directly, but they will not catch their own errors.
 * Which functions you use will depend on what you want to accomplish.
 *
 * FC.GameVariables (V): call `App.Verify.everything()` or (ideally) use a specific function under `App.Verify.I`.
 * `App.Verify.everything()` will also apply all of the other verification functions listed below.
 * You should only use `App.Verify.everything()` if you truly need to verify everything as it is resource intensive.
 * @see App.Verify.everything
 *
 * FC.PlayerState: call `App.Verify.playerState()`.
 * @see App.Verify.playerState
 *
 * FC.SlaveState: call `App.Verify.slaveState()`.
 * @see App.Verify.slaveState
 *
 * FC.TankSlaveState: call `App.Verify.tankSlaveState()`.
 * @see App.Verify.tankSlaveState
 *
 * FC.ChildState: call `App.Verify.childState()`.
 * @see App.Verify.childState
 *
 * FC.InfantState: call `App.Verify.infantState()`.
 * @see App.Verify.infantState
 *
 * FC.HumanState.womb: call `App.Verify.womb()`
 * The functions above call this as needed
 * @see App.Verify.womb
 *
 * FC.Fetus: call `App.Verify.fetus()`.
 * `App.Verify.womb()` calls this
 * @see App.Verify.fetus
 */

/**
 * Instruction sets may be executed independent of each other. Do not depend on `gameVariables` being executed before `humanState` for example.
 * The instructions in each set will be executed in the order they are listed.
 * Each instruction set has its own file in `/src/data/verification/` that you should store its functions in.
 * @type {App.Verify.Utils.InstructionSet}
 */
App.Verify.instructions = {
	gameVariables: {
		genePool: App.Verify.I.genePool,
		economy: App.Verify.I.economy,
		facilityNames: App.Verify.I.facilityNames,
		facilityBrothel: App.Verify.I.facilityBrothel,
		facilityDairy: App.Verify.I.facilityDairy,
		facilityFarmyard: App.Verify.I.facilityFarmyard,
		facilityClub: App.Verify.I.facilityClub,
		facilityServantsQuarters: App.Verify.I.facilityServantsQuarters,
		facilitySchoolroom: App.Verify.I.facilitySchoolroom,
		facilitySpa: App.Verify.I.facilitySpa,
		facilityClinic: App.Verify.I.facilityClinic,
		facilityArcade: App.Verify.I.facilityArcade,
		facilityCellblock: App.Verify.I.facilityCellblock,
		facilityMasterSuite: App.Verify.I.facilityMasterSuite,
		facilityHeadGirlSuite: App.Verify.I.facilityHeadGirlSuite,
		facilityNursery: App.Verify.I.facilityNursery,
		futureSocieties: App.Verify.I.futureSocieties,
		arcologies: App.Verify.I.arcologies,
		mods: App.Verify.I.mods,
		pit: App.Verify.I.pit,
		jobIDMap: App.Verify.I.jobIDMap,
	},
	slaveState: {
		slaveAge: App.Verify.I.slaveAge,
		slaveHealth: App.Verify.I.slaveHealth,
		slavePhysical: App.Verify.I.slavePhysical,
		slaveSkin: App.Verify.I.slaveSkin,
		slaveFace: App.Verify.I.slaveFace,
		slaveHair: App.Verify.I.slaveHair,
		slaveBoobs: App.Verify.I.slaveBoobs,
		slaveButt: App.Verify.I.slaveButt,
		slaveGeneticQuirks: App.Verify.I.slaveGeneticQuirks,
		slavePregnancy: App.Verify.I.slavePregnancy,
		slaveBelly: App.Verify.I.slaveBelly,
		slaveGenitalia: App.Verify.I.slaveGenitalia,
		slaveImplants: App.Verify.I.slaveImplants,
		slavePiercings: App.Verify.I.slavePiercings,
		slaveTattoo: App.Verify.I.slaveTattoo,
		slaveCosmetics: App.Verify.I.slaveCosmetics,
		slaveDiet: App.Verify.I.slaveDiet,
		slavePorn: App.Verify.I.slavePorn,
		slaveRelation: App.Verify.I.slaveRelation,
		slaveSkill: App.Verify.I.slaveSkill,
		slaveStat: App.Verify.I.slaveStat,
		slavePreferences: App.Verify.I.slavePreferences,
		slaveRules: App.Verify.I.slaveRules,
		slaveMisc: App.Verify.I.slaveMisc,
	},
	tankSlaveState: {
		tankSlaveIncubatorSettings: App.Verify.I.tankSlaveIncubatorSettings,
		tankSlaveAge: App.Verify.I.tankSlaveAge,
		tankSlaveHealth: App.Verify.I.tankSlaveHealth,
		tankSlavePhysical: App.Verify.I.tankSlavePhysical,
		tankSlaveSkin: App.Verify.I.tankSlaveSkin,
		tankSlaveFace: App.Verify.I.tankSlaveFace,
		tankSlaveHair: App.Verify.I.tankSlaveHair,
		tankSlaveBoobs: App.Verify.I.tankSlaveBoobs,
		tankSlaveButt: App.Verify.I.tankSlaveButt,
		tankSlaveGeneticQuirks: App.Verify.I.tankSlaveGeneticQuirks,
		tankSlavePregnancy: App.Verify.I.tankSlavePregnancy,
		tankSlaveBelly: App.Verify.I.tankSlaveBelly,
		tankSlaveGenitalia: App.Verify.I.tankSlaveGenitalia,
		tankSlaveImplants: App.Verify.I.tankSlaveImplants,
		tankSlavePiercings: App.Verify.I.tankSlavePiercings,
		tankSlaveTattoo: App.Verify.I.tankSlaveTattoo,
		tankSlaveCosmetics: App.Verify.I.tankSlaveCosmetics,
		tankSlaveDiet: App.Verify.I.tankSlaveDiet,
		tankSlavePorn: App.Verify.I.tankSlavePorn,
		tankSlaveRelation: App.Verify.I.tankSlaveRelation,
		tankSlaveSkill: App.Verify.I.tankSlaveSkill,
		tankSlaveStat: App.Verify.I.tankSlaveStat,
		tankSlavePreferences: App.Verify.I.tankSlavePreferences,
		tankSlaveRules: App.Verify.I.tankSlaveRules,
		tankSlaveMisc: App.Verify.I.tankSlaveMisc,
	},
	playerState: {
		playerAge: App.Verify.I.playerAge,
		playerHealth: App.Verify.I.playerHealth,
		playerPhysical: App.Verify.I.playerPhysical,
		playerSkin: App.Verify.I.playerSkin,
		playerFace: App.Verify.I.playerFace,
		playerHair: App.Verify.I.playerHair,
		playerBoobs: App.Verify.I.playerBoobs,
		playerButt: App.Verify.I.playerButt,
		playerGeneticQuirks: App.Verify.I.playerGeneticQuirks,
		playerPregnancy: App.Verify.I.playerPregnancy,
		playerBelly: App.Verify.I.playerBelly,
		playerGenitalia: App.Verify.I.playerGenitalia,
		playerImplants: App.Verify.I.playerImplants,
		playerPiercings: App.Verify.I.playerPiercings,
		playerTattoo: App.Verify.I.playerTattoo,
		playerCosmetics: App.Verify.I.playerCosmetics,
		playerDiet: App.Verify.I.playerDiet,
		playerRelation: App.Verify.I.playerRelation,
		playerSkill: App.Verify.I.playerSkill,
		playerStat: App.Verify.I.playerStat,
		playerPreferences: App.Verify.I.playerPreferences,
		playerRules: App.Verify.I.playerRules,
		playerMisc: App.Verify.I.playerMisc,
	},
	childState: {
		childAge: App.Verify.I.childAge,
		childHealth: App.Verify.I.childHealth,
		childPhysical: App.Verify.I.childPhysical,
		childSkin: App.Verify.I.childSkin,
		childFace: App.Verify.I.childFace,
		childHair: App.Verify.I.childHair,
		childBoobs: App.Verify.I.childBoobs,
		childButt: App.Verify.I.childButt,
		childGeneticQuirks: App.Verify.I.childGeneticQuirks,
		childPregnancy: App.Verify.I.childPregnancy,
		childBelly: App.Verify.I.childBelly,
		childGenitalia: App.Verify.I.childGenitalia,
		childImplants: App.Verify.I.childImplants,
		childPiercings: App.Verify.I.childPiercings,
		childTattoo: App.Verify.I.childTattoo,
		childCosmetics: App.Verify.I.childCosmetics,
		childDiet: App.Verify.I.childDiet,
		childPorn: App.Verify.I.childPorn,
		childRelation: App.Verify.I.childRelation,
		childSkill: App.Verify.I.childSkill,
		childStat: App.Verify.I.childStat,
		childPreferences: App.Verify.I.childPreferences,
		childRules: App.Verify.I.childRules,
		childMisc: App.Verify.I.childMisc,
	},
	infantState: {
		infantAge: App.Verify.I.infantAge,
		infantHealth: App.Verify.I.infantHealth,
		infantPhysical: App.Verify.I.infantPhysical,
		infantSkin: App.Verify.I.infantSkin,
		infantFace: App.Verify.I.infantFace,
		infantHair: App.Verify.I.infantHair,
		infantBoobs: App.Verify.I.infantBoobs,
		infantButt: App.Verify.I.infantButt,
		infantGeneticQuirks: App.Verify.I.infantGeneticQuirks,
		infantPregnancy: App.Verify.I.infantPregnancy,
		infantBelly: App.Verify.I.infantBelly,
		infantGenitalia: App.Verify.I.infantGenitalia,
		infantImplants: App.Verify.I.infantImplants,
		infantPiercings: App.Verify.I.infantPiercings,
		infantTattoo: App.Verify.I.infantTattoo,
		infantCosmetics: App.Verify.I.infantCosmetics,
		infantDiet: App.Verify.I.infantDiet,
		infantPorn: App.Verify.I.infantPorn,
		infantRelation: App.Verify.I.infantRelation,
		infantSkill: App.Verify.I.infantSkill,
		infantStat: App.Verify.I.infantStat,
		infantPreferences: App.Verify.I.infantPreferences,
		infantRules: App.Verify.I.infantRules,
		infantMisc: App.Verify.I.infantMisc,
	},
	fetus: {
		fetusGeneticQuirks: App.Verify.I.fetusGeneticQuirks,
	},
	customSlaveOrder: {
		customSlaveOrderMisc: App.Verify.I.customSlaveOrderMisc,
	}
};

/**
 * Runs verification for everything.
 * This is resource intensive. If you only need to verify a single data structure, use its verification function(s) instead.
 * @param {HTMLDivElement} [div] if provided then info may be appended to this div
 * @param {Function} [callback] Called when verification finishes if provided
 */
App.Verify.everything = (div, callback) => {
	// a list of verifications to run
	const funcs = [
		{func: App.Verify.slaveStates, params: [div], message: "Verifying SlaveStates..."},
		{func: App.Verify.tankSlaveStates, params: [div], message: "Verifying TankSlaveStates..."},
		{func: App.Verify.childStates, params: [div], message: "Verifying ChildStates..."},
		{func: App.Verify.infantStates, params: [div], message: "Verifying InfantStates..."},
		{func: App.Verify.fetuses, params: [div], message: `Verifying ${V.seePreg === 1 ? 'Fetuses' : 'FStates'}...`},
		{func: App.Verify.playerState, params: [V.PC, "PC", div], message: "Verifying PlayerState..."},
		{func: App.Verify.customSlaveOrders, params: [div], message: "Verifying CustomSlaveOrders..."},
		{func: App.Verify.gameVariables, params: [div], message: "Verifying GameVariables (V)..."}, // game variables should always be verified last
	].reverse();

	// attempt to get the element, use a dummy if it doesn't exist
	const message = document.getElementById("patch-message") ?? App.UI.DOM.makeElement("div");

	const verify = () => {
		// get the verification to run
		const func = funcs.pop();
		// run the verification
		try {
			func.func.apply(null, func.params);
		} catch (e) {
			if (div) {
				// attempt to get the element, use a dummy if it doesn't exist
				const header = document.getElementById("patch-header") ?? App.UI.DOM.makeElement("div");
				header.innerHTML += `<span class="error">Verification failed!</span>`;
				console.error("Verification failed!");
				if (App.Patch.Utils.current.div) {
					App.Patch.Utils.current.div.append(App.UI.DOM.formatException(e));
				} else {
					div.append(App.UI.DOM.formatException(e));
				}
				header.innerHTML += `<br><span>See below for details</span><hr>`;
			} else {
				console.error(e);
				console.error("Verification failed!");
			}
			State.restore(); // restore the state to before patching
			// close the loading screen (if it is open) so that the user can see the error
			App.Patch.Utils.loadingScreen.end();
			return;
		}

		// check if there are more to run
		if (funcs.length !== 0) {
			// set the message the player sees
			message.innerHTML = funcs.last().message;
			// wait for the DOM to update then call verify
			setTimeout(verify, 0);
		} else if (callback) {
			callback();
		}
	};
	if (funcs.length !== 0) {
		// set the message the player sees
		message.innerHTML = funcs.last().message;
		// wait for the DOM to update then call verify
		setTimeout(verify, 0);
	}
};


code:
/* eslint-disable no-empty */
/* eslint-disable no-var */
/*
Given an object, this will return an array where for each property of the original object, we include the object
{variable: property, oldVal: oldDiff.property, newVal: newDiff.property}
*/
globalThis.generateDiffArray = function(obj) {
	let diffArray = Object.keys(obj).map(function(key) {
		return {variable: key, oldVal: State.temporary.oldDiff[key], newVal: State.temporary.newDiff[key]};
	});
	return diffArray;
};

/*
Shamelessly copied from https://codereview.stackexchange.com/a/11580
Finds and returns the difference between two objects. Potentially will have arbitrary nestings of objects.
*/
globalThis.difference = function(o1, o2) {
	let k;
	let kDiff;
	let diff = {};
	for (k in o1) {
		if (!o1.hasOwnProperty(k)) {
		} else if (typeof o1[k] !== 'object' || typeof o2[k] !== 'object') {
			if (!(k in o2) || o1[k] !== o2[k]) {
				diff[k] = o2[k];
			}
		} else {
			kDiff = difference(o1[k], o2[k]);
			if (kDiff) {
				diff[k] = kDiff;
			}
		}
	}
	for (k in o2) {
		if (o2.hasOwnProperty(k) && !(k in o1)) {
			diff[k] = o2[k];
		}
	}
	for (k in diff) {
		if (diff.hasOwnProperty(k)) {
			return diff;
		}
	}
	return false;
};

/*
Shamelessly copied from https://stackoverflow.com/a/19101235
Flattens an object while concatenating property names.
For example {id: {number: 4, name: "A"}} --> {id.number: 4, id.name: "A"}
*/
globalThis.diffFlatten = function(data) {
	let result = {};
	function recurse(cur, prop) {
		if (Object(cur) !== cur) {
			result[prop] = cur;
		} else if (Array.isArray(cur)) {
			// eslint-disable-next-line no-var
			for (var i=0, l=cur.length; i<l; i++) { // don't change the "var" to "let"
				recurse(cur[i], prop + "[" + i + "]");
			}
			// eslint-disable-next-line block-scoped-var
			if (l === 0) { result[prop] = []; }
		} else {
			let isEmpty = true;
			for (let p in cur) {
				isEmpty = false;
				recurse(cur[p], prop ? prop+"."+p : p);
			}
			if (isEmpty && prop) { result[prop] = {}; }
		}
	}
	recurse(data, "");
	return result;
};

/*
Finds all NaN values anywhere in the State.variables object. Returns an array with the names of the NaNed variables.
*/
globalThis.findNaN = function() {
	const flatV = diffFlatten(State.variables);
	let result = [];
	for (let key in flatV) {
		if (Number.isNaN(flatV[key])) {
			result.push('$$'+key); /* double dollar signs to escape SugarCube markup */
		}
	}
	return result;
};

/* no-usedOnce*/
/**
 * Dumps game save as a readable JSON to the browser for saving in a file
 */
App.Debug.dumpGameState = function() {
	// helper to download a blob
	// borrowed from stackexchange
	function downloadToFile(content, fileName, contentType) {
		let a = document.createElement("a");
		let file = new Blob([content], {
			type: contentType
		});
		a.href = URL.createObjectURL(file);
		a.download = fileName;
		a.click();
	}

	const handler = (save) => {
		let name = save.id + ".json";
		try {
			name = App.Utils.getSaveFilename(undefined, "json");
		} catch (ex) {
			console.error(ex);
		}
		downloadToFile(Serial.stringify(save, null, 2), name, "text/plain");
	};

	Save.onSave.add(handler);
	try {
		Save.base64.save();
	} finally {
		Save.onSave.delete(handler);
	}
};


code:
globalThis.profileEvents = (function() {
	let passageinit = 0;
	let passagestart = 0;
	let passagerender = 0;
	let passagedisplay = 0;
	let passageend = 0;

	/**
	 * @param {HTMLElement} container
	 */
	function render(container) {
		App.UI.DOM.appendNewElement("h2", container, "Passage Events Profiler");
		const p = document.createElement("p");
		p.classList.add("profile-events");
		row(p, ":passageinit", ":passagestart", "Copy State", passagestart - passageinit);
		row(p, ":passagestart", ":passagerender", "Render", passagerender - passagestart);
		row(p, ":passagerender", ":passagedisplay", "Display", passagedisplay - passagerender);
		row(p, ":passagedisplay", ":passageend", "Cleanup / Auto Save", passageend - passagedisplay);
		container.append(p);
	}

	function row(container, start, stop, desc, value) {
		App.UI.DOM.appendNewElement("div", container, `From ${start}`);
		App.UI.DOM.appendNewElement("div", container, `To ${stop}`);
		App.UI.DOM.appendNewElement("div", container, `(${desc})`);
		App.UI.DOM.appendNewElement("div", container, `${value}ms`);
	}

	return {
		passageinit: () => {
			if (V.profiler) {
				passageinit = performance.now();
			}
		},
		passagestart: () => {
			if (V.profiler) {
				passagestart = performance.now();
			}
		},
		passagerender: () => {
			if (V.profiler) {
				passagerender = performance.now();
			}
		},
		passagedisplay: () => {
			if (V.profiler) {
				passagedisplay = performance.now();
			}
		},
		/**
		 * @param {HTMLElement} content
		 */
		passageend: (content) => {
			if (V.profiler) {
				passageend = performance.now();
				render(content);
			}
		},
	};
})();


code:
/* This is a terrible hack, but it actually seems to yield decent results, so...yay? */

globalThis.ProfileInclude = (function() {
	let stack = [];
	let tally = new Map();
	let builtinIncludeHandler = null;

	class TallyElement {
		constructor(passage) {
			this.passage = passage;
			this.cumulativeTime = 0.0;
			this.calls = 0;
			this.children = new Map();
		}
		get totalChildTime() {
			return Array.from(this.children.values(), (x) => x.cumulativeTime).reduce((sum, current) => { return sum + current; }, 0.0);
		}
		outputBreakdown() {
			if (this.children.size === 0) {
				// no breakdown needed if there are no children, so return an empty fragment
				return document.createDocumentFragment();
			}

			const selfTime = this.cumulativeTime - this.totalChildTime;
			const selfTimePercent = (selfTime / this.cumulativeTime) * 100;
			const selfTimePerCall = selfTime / this.calls;

			let childList = document.createElement("ul");
			let selfListItem = document.createElement("li");
			childList.appendChild(selfListItem);
			selfListItem.appendChild(document.createTextNode(`<self>: ${selfTime.toFixed(0)}ms (${selfTimePercent.toFixed(2)}%); ${selfTimePerCall.toFixed(0)}ms per call over ${this.calls.toFixed(0)} calls`));
			for (const [, child] of this.children) {
				const childTimePercent = (child.cumulativeTime / this.cumulativeTime) * 100;
				const childTimePerCall = child.cumulativeTime / child.calls;
				let childListItem = document.createElement("li");
				childListItem.appendChild(document.createTextNode(`${child.passage}: ${child.cumulativeTime.toFixed(0)}ms (${childTimePercent.toFixed(2)}%); ${childTimePerCall.toFixed(0)}ms per call over ${child.calls.toFixed(0)} calls`));
				childListItem.appendChild(child.outputBreakdown());
				childList.appendChild(childListItem);
			}
			return childList;
		}
	}

	class StackElement {
		constructor(passage) {
			this.passage = passage;
			this.startTime = Date.now();
		}
	}

	function Push(passage) {
		stack.push(new StackElement(passage));
	}

	function PopAndTally() {
		const el = stack.pop();
		const stopTime = Date.now();

		/* traverse the tally tree, ensuring that it has nodes for every frame still in the stack */
		let parentMap = tally;
		for (const child of stack) {
			if (!parentMap.has(child.passage)) {
				parentMap.set(child.passage, new TallyElement(child.passage));
			}
			parentMap = parentMap.get(child.passage).children;
		}

		/* add the popped stack frame's time to the correct child of the tally tree node corresponding with the top of the stack */
		if (!parentMap.has(el.passage)) {
			parentMap.set(el.passage, new TallyElement(el.passage));
		}
		let tallyEl = parentMap.get(el.passage);
		tallyEl.cumulativeTime += stopTime - el.startTime;
		tallyEl.calls++;
	}

	function IncludeBegins(passage) {
		Push(passage);
	}

	function IncludeEnds() {
		PopAndTally();
	}

	$(document).on(':passagestart', function() {
		if (V.profiler && !ProfileInclude.IsEnabled()) {
			ProfileInclude.Start();
		} else if (!V.profiler && ProfileInclude.IsEnabled()) {
			ProfileInclude.Stop();
		}

		tally.clear();

		if (ProfileInclude.IsEnabled()) {
			Push(passage()); // put the "host" passage at the bottom of the stack
		}
	});

	$(document).on(':passagedisplay', function() {
		if (ProfileInclude.IsEnabled()) {
			PopAndTally(); // tally the "host" passage too

			let header = document.createElement("h2");
			header.appendChild(document.createTextNode("Profile Data:"));
			let rootList = document.createElement("ul");
			let rootItem = document.createElement("li");
			let rootData = tally.values().next().value; // should be just the one item in the map at this level
			rootItem.appendChild(document.createTextNode(`${rootData.passage}: ${rootData.cumulativeTime.toFixed(0)}ms (100.00%)`));
			rootItem.appendChild(rootData.outputBreakdown());
			rootList.appendChild(rootItem);
			$("#passages").append(header).append(rootList);
		}
	});

	return {
		Start: function() {
			/* save off the builtin handler for the include macro, delete said macro, then re-register it with instrumentation */
			if (!this.IsEnabled() && Macro.has('include')) {
				builtinIncludeHandler = Macro.get('include').handler;
				Macro.delete('include');
				Macro.add('include', {handler() {
					IncludeBegins(this.args[0]);
					builtinIncludeHandler.call(this);
					IncludeEnds();
				}});
			}
		},

		IsEnabled: function() {
			return (builtinIncludeHandler !== null);
		},

		Stop: function() {
			/* restore the original include macro handler */
			if (this.IsEnabled())	{
				Macro.delete('include');
				Macro.add('include', {handler: builtinIncludeHandler});
				builtinIncludeHandler = null;
			}
		},

		IncludeBegins: IncludeBegins,
		IncludeEnds: IncludeEnds
	};
})();


code:
/**
 * This widget will find which properties were changed in a specific passage (State.bottom.title to be precise), and display their original values and current values.
 * It's somewhat awkward if a stored variable changes types to/from an object or array.
 * The included "Go Back" link is intended to replace the usual NextButton.
 * @returns {DocumentFragment}
 */
App.UI.variableDifference = function() {
	const node = new DocumentFragment();
	const T = State.temporary;
	T.oldVariables = clone(State.bottom.variables);
	T.newVariables = clone(State.variables);
	T.newDiff = diffFlatten(difference(T.oldVariables, T.newVariables)); // Returns a flattened object containing the names of the changed variables, and the new values of those variables.
	T.oldDiff = diffFlatten(difference(T.newVariables, T.oldVariables)); // Returns a flattened object containing the names of the changed variables, and the old values of those variables.
	T.diffArrayFromNew = generateDiffArray(T.newDiff); // This function requires the existence of the specific variables T.newDiff AND T.oldDiff to work.
	T.diffArrayFromOld = generateDiffArray(T.oldDiff);
	App.UI.DOM.appendNewElement("div", node, App.UI.DOM.link(
		"Go Back",
		() => {
			Engine.backward(); // Moves the save state one state backwards.
			State.history.splice(-1, 1); // Removes the latest save state. Necessary to allow the option of displaying the changed variables again immediately without screwing everything up.
			Config.history.maxStates = 1; // Reset the max number of states so as not to explode save file sizes.
		}
	));

	const testSet = new Set(["nextButton", "nextLink", "args"]);

	App.UI.DOM.appendNewElement("div", node, `Differences:`);
	if (T.diffArrayFromNew.length > 0) {
		for (const dif of T.diffArrayFromNew) { // Print variable names, and changed values. Will output the new values correctly, may not output old values correctly
			if (!testSet.has(dif.variable)) {
				App.UI.DOM.appendNewElement("div", node, `Variable: ${dif.variable}, Original Value: ${dif.oldVal}, New Value: ${dif.newVal}`, "indent");
			}
		}

		App.Events.addResponses(node, [
			new App.Events.Result(`Show more`, showMore, `This should only be necessary if a variable changes type to/from an object or array. In that case this will display the correct original value, but incorrect current value.`)
		]);
	} else {
		App.UI.DOM.appendNewElement("div", node, `Nothing changed!`, "indent");
	}
	return node;

	function showMore() {
		const frag = new DocumentFragment();
		App.UI.DOM.appendNewElement("div", frag, `Alternate display:`);
		for (const dif of T.diffArrayFromOld) { // Print variable names, and changed values. Will output the old values correctly, may not output new values correctly
			if (!testSet.has(dif.variable)) {
				App.UI.DOM.appendNewElement("div", frag, `Variable: ${dif.variable}, Original Value: ${dif.oldVal}, New Value: ${dif.newVal}`, "indent");
			}
		}
		return frag;
	}
};