code:
App.UI.SlaveSummaryRenderers = function() {
	const bits = App.UI.SlaveSummaryImpl.bits;
	const helpers = App.UI.SlaveSummaryImpl.helpers;
	const data = App.Data.SlaveSummary;

	const shortRenderers = {
		/**
		 * @param {App.Entity.SlaveState} slave
		 * @param {Node} c
		 */
		devotion: function(slave, c) {
			const makeSpan = helpers.makeSpan;
			if (slave.fetish === "mindbroken") {
				makeSpan(c, "MB", "mindbroken");
			} else {
				helpers.makeRatedStyledSpan(c, data.short.mental.devotion, slave.devotion, 100, true);
				helpers.makeStyledSpan(c, App.Ratings.multiNumeric(data.short.mental.trust, [slave.trust + 100, slave.devotion + 100]),
					slave.trust, true);
			}
		},

		/**
		 * @param {App.Entity.SlaveState} slave
		 * @param {Node} c
		 * @returns {void}
		 */
		rules: function(slave, c) {
			const makeSpan = helpers.makeSpan;
			const styles = "strong";
			switch (slave.rules.living) {
				case "luxurious":
					makeSpan(c, "LS:Lux", styles);
					break;
				case "normal":
					makeSpan(c, "LS:Nor", styles);
					break;
				default:
					makeSpan(c, "LS:Spa", styles);
					break;
			}
			if (canTalk(slave, false)) {
				switch (slave.rules.speech) {
					case "permissive":
						makeSpan(c, "SpR:P", styles);
						break;
					case "accent elimination":
						makeSpan(c, "SpR:NoAcc", styles);
						break;
					case "language lessons":
						makeSpan(c, "SpR:LL", styles);
						break;
					default:
						makeSpan(c, "SpR:R", styles);
						break;
				}
			}
			switch (slave.rules.relationship) {
				case "permissive":
					makeSpan(c, "ReR:P", styles);
					break;
				case "just friends":
					makeSpan(c, "ReR:Fr", styles);
					break;
				default:
					makeSpan(c, "ReR:R", styles);
					break;
			}
			switch (slave.rules.punishment) {
				case "confinement":
					makeSpan(c, "Pun:Conf", styles);
					break;
				case "whipping":
					makeSpan(c, "Pun:Whip", styles);
					break;
				case "chastity":
					makeSpan(c, "Pun:Chas", styles);
					break;
				default:
					makeSpan(c, "Pun:Situ", styles);
					break;
			}
			switch (slave.rules.reward) {
				case "relaxation":
					makeSpan(c, "Rew:Relx", styles);
					break;
				case "drugs":
					makeSpan(c, "Rew:Drug", styles);
					break;
				case "orgasm":
					makeSpan(c, "Rew:Orga", styles);
					break;
				default:
					makeSpan(c, "Rew:Situ", styles);
					break;
			}
			makeSpan(c, "MaR:" + App.Utils.releaseSummaryShort(slave), styles);
		},

		/**
		 * @param {App.Entity.SlaveState} slave
		 * @param {Node} c
		 * @returns {void}
		 */
		health: function(slave, c) {
			const b = bits.short;
			b.health(slave, c);
			b.illness(slave, c);
			b.tired(slave, c);
		},

		/**
		 * @param {App.Entity.SlaveState} slave
		 * @param {Node} c
		 * @returns {void}
		 */
		weight: function(slave, c) {
			helpers.makeStyledSpan(c, App.Ratings.multiNumeric(data.short.body.weight,
				[slave.weight + 100, helpers.FSData.policy.HedonisticDecadence.active, slave.hips + 2]), slave.weight, true);
		},

		/**
		 * @param {App.Entity.SlaveState} slave
		 * @param {Node} c
		 * @returns {void}
		 */
		genitalia: function(slave, c) {
			const makeSpan = helpers.makeSpan;
			if (slave.dick > 0) {
				let dickDesc = slave.balls === 0 ? "Geld" : "";
				const dickBallsDesc = App.Ratings.multiNumeric(data.short.body.genitalia.dickBalls, [slave.dick, slave.balls]);
				if (dickBallsDesc) {
					dickDesc += ` ${dickBallsDesc}`;
				}
				if (dickDesc) {
					helpers.makeSpan(c, dickDesc, "pink");
				}
			}
			if (slave.vagina === 0) {
				makeSpan(c, "VV", "lime");
			} else if ((slave.pregKnown === 1) && canStand(slave) && (App.Data.clothes.get(slave.clothes).exposure >= 3) && (slave.shoes === "none")) {
				makeSpan(c, "NBP", "pink");
			}
			if (slave.anus === 0) {
				makeSpan(c, "AV", "lime");
			}
			const holesDesc = App.Ratings.multiNumeric(data.long.body.genitalia.holes, [slave.vagina, slave.anus]);
			if (holesDesc) {
				makeSpan(c, holesDesc, "pink");
			}
		},

		/**
		 * @param {App.Entity.SlaveState} slave
		 * @param {Node} c
		 * @returns {void}
		 */
		diet: function(slave, c) {
			const style = ["teal", "strong"];
			helpers.makeMappedSpan(c, data.short.diet, slave.diet, style);
			helpers.makeMappedSpan(c, data.short.specialDiet, slave.dietCum + 3 * slave.dietMilk, style);
		},

		/**
		 * @param {App.Entity.SlaveState} slave
		 * @param {Node} c
		 * @returns {void}
		 */
		drugs: function(slave, c) {
			let drugDesc = data.short.drugs[slave.drugs];
			const makeSpan = helpers.makeSpan;
			if (drugDesc) {
				makeSpan(c, "Dr:" + drugDesc, ["tan", "strong"]);
			}
			if (slave.curatives === 2) {
				makeSpan(c, "Cura", ["lightgreen", "strong"]);
			} else if (slave.curatives === 1) {
				makeSpan(c, "Prev", ["lightgreen", "strong"]);
			}
			if (slave.aphrodisiacs !== 0) {
				if (slave.aphrodisiacs === 1) {
					makeSpan(c, "Aph", ["lightblue", "strong"]);
				} else if (slave.aphrodisiacs === 2) {
					makeSpan(c, "Aph++", ["lightblue", "strong"]);
				} else {
					makeSpan(c, "Anaph", ["lightblue", "strong"]);
				}
			}
			if (slave.addict !== 0) {
				makeSpan(c, "Add", "cyan");
			}
			let styles = ["lightsalmon", "strong"];
			if (slave.hormones > 1) {
				makeSpan(c, "Ho:F+", styles);
			} else if (slave.hormones > 0) {
				makeSpan(c, "Ho:F", styles);
			} else if (slave.hormones < -1) {
				makeSpan(c, "Ho:M+", styles);
			} else if (slave.hormones < 0) {
				makeSpan(c, "Ho:M", styles);
			}

			styles = ["mediumseagreen", "strong"];
			if (slave.bellyImplant > -1) {
				makeSpan(c, "Belly Imp", styles);
			} else if (((slave.preg <= -2) || (slave.ovaries === 0)) && (slave.vagina !== -1)) {
				makeSpan(c, "Barr", styles);
			} else if (slave.pubertyXX === 0 && (slave.ovaries === 1 || slave.mpreg === 1)) {
				makeSpan(c, "Prepub", styles);
			} else if (slave.ovaryAge >= 47 && slave.preg <= 0 && (slave.ovaries === 1 || slave.mpreg === 1)) {
				makeSpan(c, "Meno", styles);
			} else if (slave.pregWeek < 0) {
				makeSpan(c, "Postpartum", styles);
			} else if (slave.preg === -1) {
				makeSpan(c, "CC", styles);
			} else if (slave.preg === 0 && (slave.ovaries === 1 || slave.mpreg === 1)) {
				makeSpan(c, "Fert+", styles);
			} else if (((slave.preg < slave.pregData.normalBirth / 10) && (slave.preg > 0) && slave.pregKnown === 0) || slave.pregWeek === 1) {
				makeSpan(c, "Preg?", styles);
			} else if ((slave.preg >= 36) && (slave.broodmother > 0)) {
				makeSpan(c, "Perm preg", styles);
			} else if (slave.pregKnown === 1) {
				makeSpan(c, `${slave.pregWeek} wks preg`, styles);
			}
			if (isInduced(slave)) {
				makeSpan(c, "Early Labor", ["orange", "strong"]);
			}
			if (slave.pubertyXY === 0 && slave.balls > 0) {
				makeSpan(c, "Prepub balls", "strong");
			}
			if (slave.balls > 0 && slave.vasectomy === 1) {
				makeSpan(c, "Vasect", "strong");
			}
			styles = ["springgreen", "strong"];
			if (slave.inflation === 3) {
				makeSpan(c, `8 ltr ${slave.inflationType}`, styles);
			} else if (slave.inflation === 2) {
				makeSpan(c, `4 ltr ${slave.inflationType}`, styles);
			} else if (slave.inflation === 1) {
				makeSpan(c, `2 ltr ${slave.inflationType}`, styles);
			} else if (slave.bellyFluid > 0) {
				makeSpan(c, `${slave.bellyFluid}ccs ${slave.inflationType}`, styles);
			}
		},
		/**
		 * @param {App.Entity.SlaveState} slave
		 * @param {Node} c
		 * @returns {void}
		 */
		physicals: function(slave, c) {
			const b = bits.short;
			b.age(slave, c);
			b.face(slave, c);
			b.eyes(slave, c);
			b.ears(slave, c);
			if (slave.markings !== "none") {
				helpers.makeSpan(c, "Markings");
			}
			b.lips(slave, c);
			b.teeth(slave, c);
			b.muscles(slave, c);
			helpers.addText(c, App.Desc.shortLimbs(slave));
			b.voice(slave, c);
			b.tits_ass(slave, c);
			b.hips(slave, c);
			b.waist(slave, c);
			b.implants(slave, c);
			b.lactation(slave, c);
			b.mods(slave, c);
		},

		/**
		 * @param {App.Entity.SlaveState} slave
		 * @param {Node} c
		 * @returns {void}
		 */
		hormoneBalance: function(slave, c) {
			if (slave.hormoneBalance <= -21) {
				helpers.makeSpan(c, "HB:M", ["deepskyblue", "strong"]);
			} else if (slave.hormoneBalance <= 20) {
				helpers.makeSpan(c, "HB:N", ["pink", "strong"]);
			} else if (slave.hormoneBalance <= 500) {
				helpers.makeSpan(c, "HB:F", ["pink", "strong"]);
			}
		},

		/**
		 * @param {App.Entity.SlaveState} slave
		 * @param {Node} c
		 * @returns {void}
		 */
		mental: function(slave, c) {
			const b = bits.short;
			if (slave.fetish !== "mindbroken") {
				if (slave.fetishKnown === 1) {
					b.fetish(slave, c);
				}
				if (slave.attrKnown === 1) {
					b.attraction(slave, c);
				}
			}
			if (slave.piercing.genitals.smart || dildoVibeLevel(slave) > 1 || slave.dickAccessory === "smart bullet vibrator") {
				b.smart_piercing(slave, c);
			}
			b.behavior_flaw(slave, c);
			b.sex_flaw(slave, c);
			b.behavior_quirk(slave, c);
			b.sex_quirk(slave, c);
		},

		/**
		 * @param {App.Entity.SlaveState} slave
		 * @param {Node} c
		 * @returns {void}
		 */
		skills: function(slave, c) {
			const b = bits.short;
			b.intelligence(slave, c);
			b.skills(slave, c);
			b.prestige(slave, c);
			b.porn_prestige(slave, c);
		},

		/**
		 * @param {App.Entity.SlaveState} slave
		 * @param {Node} c
		 * @returns {void}
		 */
		race: function(slave, c) {
			const s = data.short.race[slave.race];
			helpers.makeSpan(c, s ? s : helpers.firstThreeUc(slave.race), "tan");
		},

		/**
		 * @param {App.Entity.SlaveState} slave
		 * @param {Node} c
		 * @returns {void}
		 */
		nationality: function(slave, c) {
			let tmp = data.short.nationality[slave.nationality];
			if (!tmp && slave.nationality === "Zimbabwean") {
				if (slave.race === "white") {
					tmp = 'Rhodesian.';
				} else {
					tmp = `${slave.nationality}.`;
				}
			}
			helpers.makeSpan(c, tmp ? tmp : slave.nationality.substr(0, 3), "tan");
		},

		/**
		 * @param {App.Entity.SlaveState} slave
		 * @param {Node} c
		 * @returns {void}
		 */
		skin: function(slave, c) {
			const s = data.short.skin[slave.skin];
			helpers.makeSpan(c, s ? s : helpers.firstThreeUc(slave.skin));
		},

		clothes: function() { },

		/**
		 * @param {App.Entity.SlaveState} slave
		 * @param {Node} c
		 * @returns {void}
		 */
		rulesets: function(slave, c) {
			if (slave.useRulesAssistant === 0) {
				helpers.makeSpan(c, "RA-Exmt", "lightgreen");
			}
		},

		origins: function() { },

		/**
		 * @param {App.Entity.SlaveState} slave
		 * @param {Node} c
		 * @returns {void}
		 */
		relations: function(slave, c) {
			const b = bits.short;
			b.family(slave, c);
			b.clone(slave, c);
			b.rival(slave, c);
		}

	};

	const longRenderers = {
		/**
		 * @param {App.Entity.SlaveState} slave
		 * @param {Node} c
		 * @returns {void}
		 */
		devotion: function(slave, c) {
			const makeSpan = helpers.makeSpan;
			if (slave.fetish === "mindbroken") {
				makeSpan(c, "Mindbroken.", "mindbroken");
			} else {
				helpers.makeRatedStyledSpan(c, data.long.mental.devotion, slave.devotion, 100, true);
				helpers.makeStyledSpan(c, App.Ratings.multiNumeric(data.long.mental.trust, [slave.trust + 100, slave.devotion + 100]),
					slave.trust, true);
			}
		},
		/**
		 * @param {App.Entity.SlaveState} slave
		 * @param {Node} c
		 * @returns {void}
		 */
		health: function(slave, c) {
			const b = bits.long;
			b.health(slave, c);
			b.illness(slave, c);
			b.tired(slave, c);
		},

		/**
		 * @param {App.Entity.SlaveState} slave
		 * @param {Node} c
		 * @returns {void}
		 */
		weight: function(slave, c) {
			helpers.makeStyledSpan(c, App.Ratings.multiNumeric(data.long.body.weight,
				[slave.weight + 100, helpers.FSData.policy.HedonisticDecadence.active, slave.hips + 2]), slave.weight, true);
		},

		/**
		 * @param {App.Entity.SlaveState} slave
		 * @param {Node} c
		 * @returns {void}
		 */
		rules: function(slave, c) {
			const addText = helpers.addText;
			addText(c, `Living standard: ${slave.rules.living}. `);
			if (canTalk(slave, false)) {
				addText(c, `Speech rules: ${slave.rules.speech}. `);
			}
			addText(c, `Relationship rules: ${slave.rules.relationship}. `);
			addText(c, `Typical punishment: ${slave.rules.punishment}. `);
			addText(c, `Typical reward: ${slave.rules.reward}. `);
			addText(c, `Release rules: ${App.Utils.releaseSummaryLong(slave)}. `);
		},

		/**
		 * @param {App.Entity.SlaveState} slave
		 * @param {Node} c
		 * @returns {void}
		 */
		genitalia: function(slave, c) {
			if (slave.dick > 0) {
				let dickDesc = slave.balls === 0 ? "Gelded." : "";
				const dickBallsDesc = App.Ratings.multiNumeric(data.long.body.genitalia.dickBalls, [slave.dick, slave.balls]);
				if (dickBallsDesc) {
					dickDesc += ` ${dickBallsDesc}`;
				}
				if (dickDesc) {
					helpers.makeSpan(c, dickDesc, "pink");
				}
			}
			if (slave.vagina === 0) {
				helpers.makeSpan(c, "Virgin.", "lime");
			} else if ((slave.pregKnown === 1) && canStand(slave) && (App.Data.clothes.get(slave.clothes).exposure >= 4) && (slave.shoes === "none")) {
				helpers.makeSpan(c, "Naked, barefoot, and pregnant.", "pink");
			}
			if (slave.anus === 0) {
				helpers.makeSpan(c, "Anal virgin.", "lime");
			}
			const holesDesc = App.Ratings.multiNumeric(data.long.body.genitalia.holes, [slave.vagina, slave.anus]);
			if (holesDesc) {
				helpers.makeSpan(c, holesDesc, "pink");
			}
		},

		/**
		 * @param {App.Entity.SlaveState} slave
		 * @param {Node} c
		 * @returns {void}
		 */
		diet: function(slave, c) {
			helpers.makeMappedSpan(c, data.long.diet, slave.diet, "teal");
			const sd = data.long.specialDiet[slave.dietCum + 3 * slave.dietMilk];
			if (sd) {
				helpers.addText(c, "Diet base: ");
				helpers.makeSpan(c, sd, "cyan");
			}
		},

		/**
		 * @param {App.Entity.SlaveState} slave
		 * @param {Node} c
		 * @returns {void}
		 */
		drugs: function(slave, c) {
			const makeSpan = helpers.makeSpan;
			let swd = WombGetLittersData(slave);
			if (slave.drugs !== "no drugs") {
				makeSpan(c, `On ${slave.drugs}.`, "tan");
			}
			if (slave.curatives === 2) {
				makeSpan(c, "On curatives.", "lightgreen");
			} else if (slave.curatives === 1) {
				makeSpan(c, "On preventatives.", "lightgreen");
			}
			if (slave.aphrodisiacs > 0) {
				makeSpan(c, `On ${slave.aphrodisiacs > 1 ? 'extreme' : ''} aphrodisiacs.`, "lightblue");
			} else if (slave.aphrodisiacs === -1) {
				makeSpan(c, "On anaphrodisiacs.", "lightblue");
			}
			if (slave.addict !== 0) {
				makeSpan(c, "Addict.", "cyan");
			}
			if (slave.hormones > 1) {
				makeSpan(c, "Heavy female hormones.", "lightsalmon");
			} else if (slave.hormones > 0) {
				makeSpan(c, "Female hormones.", "lightsalmon");
			} else if (slave.hormones < -1) {
				makeSpan(c, "Heavy male hormones.", "lightsalmon");
			} else if (slave.hormones < 0) {
				makeSpan(c, "Male hormones.", "lightsalmon");
			}
			let styles = "mediumseagreen";
			if (slave.bellyImplant > -1) {
				makeSpan(c, "Belly Implant.", styles);
			} else if ((slave.preg <= -2) && (slave.ovaries === 1 || slave.mpreg === 1)) {
				makeSpan(c, "Barren.", styles);
			} else if ((slave.ovaries === 0 && slave.mpreg === 0) && (slave.vagina !== -1) && (slave.genes === "XX")) {
				makeSpan(c, "Barren.", styles);
			} else if (slave.pubertyXX === 0 && (slave.ovaries === 1 || slave.mpreg === 1)) {
				makeSpan(c, "Not ovulating yet.", styles);
			} else if (slave.ovaryAge >= 47 && slave.preg <= 0 && (slave.ovaries === 1 || slave.mpreg === 1)) {
				makeSpan(c, "Menopausal.", styles);
			} else if (slave.pregWeek < 0) {
				makeSpan(c, "Postpartum.", styles);
			} else if (slave.preg === -1) {
				makeSpan(c, "On contraceptives.", styles);
			} else if (slave.preg === 0 && (slave.ovaries === 1 || slave.mpreg === 1)) {
				makeSpan(c, "Fertile.", styles);
			} else if ((slave.preg >= 36) && (slave.broodmother > 0)) {
				makeSpan(c, "Permanently pregnant.", styles);
			} else if (swd.litters.length > 1) {
				let pregTxt = `Concurrent pregnancies: (${swd.litters.length} sets).`;
				pregTxt += ` Max:${swd.litters[0]} / Min:${swd.litters[swd.litters.length - 1]} week(s).`;
				makeSpan(c, pregTxt, "lime");
			} else if (((slave.preg < slave.pregData.normalBirth / 10) && (slave.preg > 0) && slave.pregKnown === 0) || slave.pregWeek === 1) {
				makeSpan(c, "May be pregnant.");
			} else if (slave.pregKnown === 1) {
				if (slave.pregType < 2 || slave.broodmother > 0) {
					makeSpan(c, `${Math.trunc(slave.pregWeek)} weeks pregnant.`);
				} else {
					let desc = `${Math.trunc(slave.pregWeek)} weeks pregnant with `;
					if (slave.pregType >= 40) {
						desc += `a tremendous brood of offspring.`;
					} else if (slave.pregType >= 20) {
						desc += `a brood of offspring.`;
					} else if (slave.pregType >= 10) {
						desc += `${slave.pregType} babies.`;
					} else if (slave.pregType === 9) {
						desc += `nonuplets.`;
					} else if (slave.pregType === 8) {
						desc += `octuplets.`;
					} else if (slave.pregType === 7) {
						desc += `septuplets.`;
					} else if (slave.pregType === 6) {
						desc += `sextuplets.`;
					} else if (slave.pregType === 5) {
						desc += `quintuplets.`;
					} else if (slave.pregType === 4) {
						desc += `quadruplets.`;
					} else if (slave.pregType === 3) {
						desc += `triplets.`;
					} else {
						desc += `twins.`;
					}
					makeSpan(c, desc);
				}
				if (slave.preg > slave.pregData.normalBirth && slave.broodmother === 0) {
					makeSpan(c, "(Overdue.)");
				}
			}
			if (isInduced(slave)) {
				makeSpan(c, "Showing signs of early labor.", "orange");
			}
			if (slave.pubertyXY === 0 && slave.balls > 0) {
				makeSpan(c, "Has not had first ejaculation.");
			}
			if (slave.balls > 0 && slave.vasectomy === 1) {
				makeSpan(c, "Vasectomy.");
			}
			if (slave.inflation === 3) {
				makeSpan(c, `Filled with 8 liters of ${slave.inflationType}.`, "springgreen");
			} else if (slave.inflation === 2) {
				makeSpan(c, `Filled with 4 liters of ${slave.inflationType}.`, "springgreen");
			} else if (slave.inflation === 1) {
				makeSpan(c, `Filled with 2 liters of ${slave.inflationType}.`, "springgreen");
			} else if (slave.bellyFluid > 0) {
				makeSpan(c, `Stuffed with ${slave.bellyFluid}ccs of ${slave.inflationType}.`, "springgreen");
			}
		},

		/**
		 * @param {App.Entity.SlaveState} slave
		 * @param {Node} c
		 * @returns {void}
		 */
		physicals: function(slave, c) {
			const b = bits.long;
			b.age(slave, c);
			b.face(slave, c);
			b.eyes(slave, c);
			b.ears(slave, c);
			b.lips(slave, c);
			b.teeth(slave, c);
			b.muscles(slave, c);
			helpers.makeSpan(c, App.Desc.longLimbs(slave));
			b.voice(slave, c);
			b.tits_ass(slave, c);
			b.hips(slave, c);
			b.waist(slave, c);
			b.implants(slave, c);
			b.lactation(slave, c);
			b.milkflavor(slave, c);
			b.mods(slave, c);
			const brands = App.Medicine.Modification.brandRecord(slave);
			if (!jQuery.isEmptyObject(brands)) {
				helpers.makeSpan(c, 'Branded.');
			}
		},

		/**
		 * @param {App.Entity.SlaveState} slave
		 * @param {Node} c
		 * @returns {void}
		 */
		hormoneBalance: function(slave, c) {
			const colorClass = slave.hormoneBalance <= -21 ? "deepskyblue" : "pink";
			const desc = App.Ratings.numeric(data.long.hormoneBalance, slave.hormoneBalance + 500);
			helpers.makeSpan(c, desc + " hormone balance.", colorClass);
		},

		/**
		 * @param {App.Entity.SlaveState} slave
		 * @param {Node} c
		 * @returns {void}
		 */
		skills: function(slave, c) {
			const b = bits.long;
			b.intelligence(slave, c);
			b.skills(slave, c);
			b.prestige(slave, c);
			b.porn_prestige(slave, c);
		},

		/**
		 * @param {App.Entity.SlaveState} slave
		 * @param {Node} c
		 * @returns {void}
		 */
		clothes: function(slave, c) {
			const b = bits.long;
			const dressingBlock = helpers.makeBlock(c);
			if (slave.choosesOwnClothes === 1) {
				helpers.makeSpan(dressingBlock, `Dressing ${getPronouns(slave).himself}.`);
			}
			b.clothes(slave, dressingBlock);
			b.collar(slave, dressingBlock);
			b.mask(slave, dressingBlock);
			b.mouth(slave, dressingBlock);
			b.belly(slave, dressingBlock);
			if (hasAnyArms(slave)) {
				b.arms(slave, dressingBlock);
			}
			if (hasAnyLegs(slave)) {
				b.legs(slave, dressingBlock);
				b.shoes(slave, dressingBlock);
			}
			b.chastity(slave, dressingBlock);
			b.vaginal_acc(slave, dressingBlock);
			b.dick_acc(slave, dressingBlock);
			b.buttplug(slave, dressingBlock);
		},

		/**
		 * @param {App.Entity.SlaveState} slave
		 * @param {Node} c
		 * @returns {void}
		 */
		mental: function(slave, c) {
			const b = bits.long;
			if (slave.fetish !== "mindbroken") {
				if (slave.fetishKnown === 1) {
					b.fetish(slave, c);
				}
				if (slave.attrKnown === 1) {
					b.attraction(slave, c);
				}
			}
			if (slave.piercing.genitals.smart || dildoVibeLevel(slave) > 1 || slave.dickAccessory === "smart bullet vibrator") {
				b.smart_piercing(slave, c);
			}
			b.behavior_flaw(slave, c);
			b.sex_flaw(slave, c);
			b.behavior_quirk(slave, c);
			b.sex_quirk(slave, c);
		},

		/**
		 * @param {App.Entity.SlaveState} slave
		 * @param {Node} c
		 * @returns {void}
		 */
		race: function(slave, c) {
			const raceStr = App.Data.misc.filterRaces.get(slave.race) || capFirstChar(slave.race);
			helpers.makeSpan(c, raceStr + '.', "tan");
		},

		/**
		 * @param {App.Entity.SlaveState} slave
		 * @param {Node} c
		 * @returns {void}
		 */
		nationality: function(slave, c) {
			function descStr(slave) {
				switch (slave.nationality) {
					case "a Cook Islander":
						return `Cook Islander.`;
					case "a Liechtensteiner":
						return `Liechtensteiner.`;
					case "a New Zealander":
						return `New Zealander.`;
					case "a Solomon Islander":
						return `Solomon Islander.`;
					case "Zimbabwean":
						if (slave.race === "white") {
							return `Rhodesian.`;
						} else {
							return `${slave.nationality}.`;
						}
					case "slave":
					case "none":
					case "":
					case "Stateless":
						return `Stateless.`;
					default:
						return `${slave.nationality}.`;
				}
			}

			helpers.makeSpan(c, descStr(slave), "tan");
		},

		/**
		 * @param {App.Entity.SlaveState} slave
		 * @param {Node} c
		 * @returns {void}
		 */
		skin: function(slave, c) {
			helpers.makeSpan(c, `${capFirstChar(slave.skin)} skin.`);
		},

		/**
		 * @param {App.Entity.SlaveState} slave
		 * @param {HTMLElement} c
		 * @returns {void}
		 */
		rulesets: function(slave, c) {
			if (slave.useRulesAssistant === 0) {
				helpers.makeSpan(c, "RA-Exempt", "lightgreen");
			} else if ((slave.currentRules !== undefined) && (slave.currentRules.length > 0)) {
				c.innerHTML = `Rules: ${V.defaultRules.filter(x => ruleApplied(slave, x)).map(x => x.name).join(", ")}`;
			}
		},

		/**
		 * @param {App.Entity.SlaveState} slave
		 * @param {Node} c
		 * @returns {void}
		 */
		origins: function(slave, c) {
			if (slave.origin !== "") {
				const para = helpers.makeParagraph(c);
				para.classList.add("gray");
				para.textContent = pronounsForSlaveProp(slave, slave.origin);
			}
		},

		/**
		 * @param {App.Entity.SlaveState} slave
		 * @param {Node} c
		 * @returns {void}
		 */
		relations: function(slave, c) {
			const b = bits.long;
			b.family(slave, c);
			b.clone(slave, c);
			b.rival(slave, c);
		}
	};

	return {
		short: shortRenderers,
		long: longRenderers,
		empty: function() {}
	};
}();

App.UI.SlaveSummary = function() {
	const emptyRenderer = (/** @type {App.Entity.SlaveState} */ slave, /** @type {Node} */ c) => { };
	const delegates = {
		clothes: emptyRenderer,
		devotion: emptyRenderer,
		rules: emptyRenderer,
		height: emptyRenderer,
		diet: emptyRenderer,
		health: emptyRenderer,
		drugs: emptyRenderer,
		race: emptyRenderer,
		nationality: emptyRenderer,
		genitalia: emptyRenderer,
		physicals: emptyRenderer,
		hormoneBalance: emptyRenderer,
		skills: emptyRenderer,
		mental: emptyRenderer,
		weight: emptyRenderer,
		skin: emptyRenderer,
		origins: emptyRenderer,
		rulesets: emptyRenderer,
		relations: emptyRenderer
	};

	/**
	 * @returns {FC.UI.SlaveSummary.State}
	 */
	function makeNewState() {
		return {
			abbreviation: {
				clothes: 2,
				devotion: 2,
				diet: 2,
				drugs: 2,
				genitalia: 2,
				health: 2,
				hormoneBalance: 2,
				mental: 2,
				nationality: 2,
				origins: 2,
				physicals: 2,
				race: 2,
				rules: 2,
				rulesets: 2,
				skills: 2,
			}
		};
	}

	function delegateForSetting(name, setting) {
		switch (setting) {
			case 0:
				return App.UI.SlaveSummaryRenderers.empty;
			case 1:
				return App.UI.SlaveSummaryRenderers.short[name];
			case 2:
				return App.UI.SlaveSummaryRenderers.long[name];
		}
	}

	function initDelegates(settingsObj) {
		try {
			settingsObj = settingsObj || V.UI.slaveSummary;
			/** @type {FC.UI.SlaveSummary.AbbreviationState} */
			const abbrSettings = settingsObj.abbreviation;
			for (const setting in abbrSettings) {
				delegates[setting] = delegateForSetting(setting, abbrSettings[setting]);
			}
			delegates.weight = delegateForSetting("weight", abbrSettings.diet);
			delegates.skin = delegateForSetting("skin", abbrSettings.physicals);
			delegates.relations = delegateForSetting("relations", abbrSettings.mental);
			// special settings
			if (!V.seeRace) {
				delegates.race = emptyRenderer;
			}
		} catch (ex) {
			console.error(ex);
		}
	}

	function settingsChanged(newState = V) {
		try {
			const newStateIsOK = newState && newState.hasOwnProperty("UI") && newState.UI.hasOwnProperty("slaveSummary");
			const settingsObj = newStateIsOK ? newState.UI.slaveSummary : V.UI.slaveSummary;

			initDelegates(settingsObj);
			App.UI.SlaveSummaryImpl.helpers.syncFSData(newState.arcologies[0]);
			App.Art.setDynamicCSS(newState);
		} catch (ex) {
			console.log(`Slave summary settings change handler encountered an error: ${ex}`);
		}
	}

	function societyChanged(arcology) {
		App.UI.SlaveSummaryImpl.helpers.syncFSData(arcology);
	}

	/**
	 * @param {App.Entity.SlaveState} slave
	 * @returns {DocumentFragment}
	 */
	function render(slave) {
		/** @type {FC.UI.SlaveSummary.AbbreviationState} */
		const abbrSettings = V.UI.slaveSummary.abbreviation;
		const helpers = App.UI.SlaveSummaryImpl.helpers;

		const res = document.createDocumentFragment();
		let para = helpers.makeParagraph(res);

		delegates.devotion(slave, para);
		if (!slave.fuckdoll) {
			delegates.rules(slave, para);
		}
		delegates.weight(slave, para);
		delegates.diet(slave, para);
		delegates.health(slave, para);
		delegates.drugs(slave, para);

		para = helpers.makeParagraph(res);
		helpers.makeSpan(para, `${capFirstChar(SlaveTitle(slave))}${abbrSettings.physicals === 2 ? '.' : ''}`, ["coral", "strong"]);
		delegates.race(slave, para);
		delegates.nationality(slave, para);
		delegates.skin(slave, para);
		delegates.genitalia(slave, para);
		delegates.physicals(slave, para);
		delegates.hormoneBalance(slave, para);

		para = helpers.makeParagraph(res);
		delegates.skills(slave, para);
		delegates.mental(slave, para);
		if (slave.custom.label) {
			helpers.makeSpan(res, `${capFirstChar(slave.custom.label)}.`, ["custom-label"]);
		}
		if ((slave.relationship !== 0) || (totalRelatives(slave) > 0) || (abbrSettings.clothes === 2) || (abbrSettings.rulesets === 2)) {
			para = helpers.makeParagraph(res);
		}
		delegates.relations(slave, para);
		if (!slave.fuckdoll) {
			delegates.clothes(slave, para);
		}
		const RABlock = helpers.makeBlock(para);
		delegates.rulesets(slave, RABlock);
		delegates.origins(slave, res);
		return res;
	}

	/**
	 * @param {InstanceType<App.UI.OptionsGroup>} optionsGroup
	 */
	function addOptions(optionsGroup) {
		// SAH is "Summarized, Abbreviated, Hidden"
		const SAHOptions = {
			"Summarized": 2,
			"Abbreviated": 1,
			"Hidden": 0
		};

		function appendOption(name, desc, options) {
			const option = optionsGroup.addOption(desc, name, V.UI.slaveSummary.abbreviation);
			for (const key in options) {
				option.addValue(key, options[key], initDelegates);
			}
		}

		function appendSAHOption(name, desc) {
			appendOption(name, desc, SAHOptions);
		}

		appendSAHOption("devotion", "Mental stats are");
		appendSAHOption("mental", "Mental attributes are");
		appendSAHOption("rules", "Rules are");
		appendSAHOption("health", "Health is");
		appendSAHOption("diet", "Diet and weight are");
		appendSAHOption("drugs", "Drugs and addiction are");
		appendSAHOption("hormoneBalance", "Hormone balance is");
		appendSAHOption("genitalia", "Genitalia are");
		appendSAHOption("physicals", "Physical traits are");
		appendSAHOption("skills", "Skills are");
		appendSAHOption("nationality", "Nationality is");
		appendSAHOption("race", "Race is");
		appendOption("rulesets", "Rules Assistant rulesets are", {
			"Summarized": 2,
			"Abbreviated": 1
		});
		appendOption("clothes", "Clothes are", {
			"Summarized": 2,
			"Hidden": 0
		});
		appendOption("origins", "Origins are", {
			"Summarized": 2,
			"Hidden": 0
		});
	}

	return {
		makeNewState: makeNewState,
		settingsChanged: settingsChanged,
		societyChanged: societyChanged,
		render: render,
		addOptions: addOptions
	};
}();


code:
/** Get all the enunciations used by a particular slave as a destructurable object.
 * @param {App.Entity.SlaveState} slave
 * @returns {FC.Enunciation}
 */
globalThis.getEnunciation = function(slave) {
	let ret = {};

	if (SlaveStatsChecker.checkForLisp(slave)) {
		if (V.PC.customTitleLisp !== undefined) {
			ret.title = V.PC.customTitleLisp;
		} else if (V.PC.title !== 0) {
			ret.title = "Mathter";
		} else {
			ret.title = "Mithtreth";
		}
		if (V.allowFamilyTitles === 1) {
			const soleParentPrefersMom = slave.father === -1 && slave.mother === -1 && V.PC.title === 0;
			if (slave.father === -1 && !soleParentPrefersMom) {
				if (slave.actualAge < 4 && slave.physicalAge < 4) {
					ret.title = "Dadda";
				} else if (slave.actualAge < 9) {
					ret.title = "Daddy";
				} else {
					ret.title = "Dad";
				}
			} else if (slave.mother === -1) {
				if (slave.actualAge < 4 && slave.physicalAge < 4) {
					ret.title = "Mama";
				} else if (slave.actualAge < 9) {
					ret.title = "Mommy";
				} else {
					ret.title = "Mom";
				}
			} else if (V.PC.mother === slave.ID || V.PC.father === slave.ID) {
				if (V.PC.title === 1) {
					ret.title = "Thon";
				} else if (V.PC.title === 0) {
					ret.title = "Daughter";
				}
			} else if (areSisters(slave, V.PC) > 0) {
				if (V.PC.title === 1) {
					if (slave.actualAge < 18) {
						ret.title = "Bro";
					} else {
						ret.title = "Brother";
					}
				} else if (V.PC.title === 0) {
					if (slave.actualAge < 18) {
						ret.title = "Thith";
					} else {
						ret.title = "Thithter";
					}
				}
			}
		}
		if (slave.custom.titleLisp !== undefined && slave.custom.titleLisp !== "") {
			if (slave.rudeTitle === 1) {
				if (slave.trust > 20) {
					ret.title = slave.custom.titleLisp;
				}
			} else {
				ret.title = slave.custom.titleLisp;
			}
		}
		ret.say = "lisp";
	} else {
		if (V.PC.customTitle !== undefined) {
			ret.title = V.PC.customTitle;
		} else if (V.PC.title !== 0) {
			ret.title = "Master";
		} else {
			ret.title = "Mistress";
		}
		if (V.allowFamilyTitles === 1) {
			const soleParentPrefersMom = slave.father === -1 && slave.mother === -1 && V.PC.title === 0;
			if (slave.father === -1 && !soleParentPrefersMom) {
				if (slave.actualAge < 4 && slave.physicalAge < 4) {
					ret.title = "Dadda";
				} else if (slave.actualAge < 9) {
					ret.title = "Daddy";
				} else {
					ret.title = "Dad";
				}
			} else if (slave.mother === -1) {
				if (slave.actualAge < 4 && slave.physicalAge < 4) {
					ret.title = "Mama";
				} else if (slave.actualAge < 9) {
					ret.title = "Mommy";
				} else {
					ret.title = "Mom";
				}
			} else if (V.PC.mother === slave.ID || V.PC.father === slave.ID) {
				if (V.PC.title === 1) {
					ret.title = "Son";
				} else if (V.PC.title === 0) {
					ret.title = "Daughter";
				}
			} else if (areSisters(slave, V.PC) > 0) {
				if (V.PC.title === 1) {
					if (slave.actualAge < 18) {
						ret.title = "Bro";
					} else {
						ret.title = "Brother";
					}
				} else if (V.PC.title === 0) {
					if (slave.actualAge < 18) {
						ret.title = "Sis";
					} else {
						ret.title = "Sister";
					}
				}
			}
		}
		if (slave.custom.title !== undefined && slave.custom.title !== "") {
			if (slave.rudeTitle === 1) {
				if (slave.trust > 20) {
					ret.title = slave.custom.title;
				}
			} else {
				ret.title = slave.custom.title;
			}
		}
		ret.say = "say";
	}

	return ret;
};

/**
 * Returns speech with lisp if slave lisps
 * @param {App.Entity.SlaveState} slave
 * @param {string} speech
 * @returns {string}
 */
globalThis.Spoken = function(slave, speech) {
	if (SlaveStatsChecker.checkForLisp(slave)) {
		return lispReplace(speech);
	} else {
		return speech;
	}
};

license: mit


code:
globalThis.burstCheck = function(slave) {
	return (slave.burst);
	// return V.burstIDs.includes(slave.ID);
};

globalThis.burst = function(slave) {
	slave.burst = 1;
	/*
	if (typeof slave === "number") {
		V.burstIDs.push(slave);
	} else {
		V.burstIDs.push(slave.ID);
	}
	*/
};


code:
/**
 * True if slave has at least one eye
 *
 * @param {FC.HumanState} slave
 * @returns {boolean}
 */
globalThis.hasAnyEyes = function(slave) {
	return !!slave.eye.right || !!slave.eye.left;
};

/**
 * True if slave has at least one eye that is natural
 *
 * @param {FC.HumanState} slave
 * @returns {boolean}
 */
globalThis.hasAnyNaturalEyes = function(slave) {
	return getLeftEyeType(slave) === 1 || getRightEyeType(slave) === 1;
};

/**
 * True if slave has at least one eye that is prosthetic (cyber or glass)
 *
 * @param {FC.HumanState} slave
 * @returns {boolean}
 */
globalThis.hasAnyProstheticEyes = function(slave) {
	return getLeftEyeType(slave) > 1 || getRightEyeType(slave) > 1;
};

/**
 * True if slave has at least one eye that is cybernetic
 *
 * @param {FC.HumanState} slave
 * @returns {boolean}
 */
globalThis.hasAnyCyberneticEyes = function(slave) {
	return getLeftEyeType(slave) === 3 || getRightEyeType(slave) === 3;
};

/**
 * True if slave has both eyes
 *
 * @param {FC.HumanState} slave
 * @returns {boolean}
 */
globalThis.hasBothEyes = function(slave) {
	return !!slave.eye.right && !!slave.eye.left;
};

/**
 * True if slave has both eyes and they are natural
 *
 * @param {FC.HumanState} slave
 * @returns {boolean}
 */
globalThis.hasBothNaturalEyes = function(slave) {
	return getLeftEyeType(slave) === 1 && getRightEyeType(slave) === 1;
};

/**
 * True if slave has both eyes and they are prosthetic (cyber or glass)
 *
 * @param {FC.HumanState} slave
 * @returns {boolean}
 */
globalThis.hasBothProstheticEyes = function(slave) {
	return getLeftEyeType(slave) > 1 && getRightEyeType(slave) > 1;
};

/**
 * True if slave has both eyes and they are cybernetic
 *
 * @param {FC.HumanState} slave
 * @returns {boolean}
 */
globalThis.hasBothCyberneticEyes = function(slave) {
	return getLeftEyeType(slave) === 3 && getRightEyeType(slave) === 3;
};

/**
 * True if slave has left eye
 *
 * @param {FC.HumanState} slave
 * @returns {boolean}
 */
globalThis.hasLeftEye = function(slave) {
	return !!slave.eye.left;
};
/**
 * True if slave has right eye
 *
 * @param {FC.HumanState} slave
 * @returns {boolean}
 */
globalThis.hasRightEye = function(slave) {
	return !!slave.eye.right;
};

/**
 * Returns type of the left eye.
 *
 * @param {FC.HumanState} slave
 * @returns {number}
 */
globalThis.getLeftEyeType = function(slave) {
	if (hasLeftEye(slave)) {
		return slave.eye.left.type;
	} else {
		return 0;
	}
};

/**
 * Returns type of the right eye.
 *
 * @param {FC.HumanState} slave
 * @returns {number}
 */
globalThis.getRightEyeType = function(slave) {
	if (hasRightEye(slave)) {
		return slave.eye.right.type;
	} else {
		return 0;
	}
};

/**
 * @param {FC.HumanState} slave
 * @returns {number}
 */
globalThis.getLeftEyeVision = function(slave) {
	if (hasLeftEye(slave)) {
		return slave.eye.left.vision;
	} else {
		return 0;
	}
};

/**
 * @param {FC.HumanState} slave
 * @returns {number}
 */
globalThis.getRightEyeVision = function(slave) {
	if (hasRightEye(slave)) {
		return slave.eye.right.vision;
	} else {
		return 0;
	}
};

/**
 * @param {FC.HumanState} slave
 * @returns {number}
 */
globalThis.getBestVision = function(slave) {
	return Math.max(getRightEyeVision(slave), getLeftEyeVision(slave));
};

/**
 * @param {FC.HumanState} slave
 * @returns {number}
 */
globalThis.getWorstVision = function(slave) {
	return Math.min(getRightEyeVision(slave), getLeftEyeVision(slave));
};

/**
 *
 * @param {FC.HumanState} slave
 * @param {number} vision
 * @returns {boolean}
 */
globalThis.anyVisionEquals = function(slave, vision) {
	return getRightEyeVision(slave) === vision || getLeftEyeVision(slave) === vision;
};

/**
 * @param {FC.HumanState} slave
 * @returns {string}
 */
globalThis.getLeftEyeColor = function(slave) {
	if (hasLeftEye(slave)) {
		return slave.eye.left.iris;
	} else {
		return "empty";
	}
};

/**
 * @param {FC.HumanState} slave
 * @returns {string}
 */
globalThis.getRightEyeColor = function(slave) {
	if (hasRightEye(slave)) {
		return slave.eye.right.iris;
	} else {
		return "empty";
	}
};

/**
 * @param {FC.HumanState} slave
 * @returns {string}
 */
globalThis.getLeftEyePupil = function(slave) {
	if (hasLeftEye(slave)) {
		return slave.eye.left.pupil;
	} else {
		return "circular";
	}
};

/**
 * @param {FC.HumanState} slave
 * @returns {string}
 */
globalThis.getRightEyePupil = function(slave) {
	if (hasRightEye(slave)) {
		return slave.eye.right.pupil;
	} else {
		return "circular";
	}
};

/**
 * @param {FC.HumanState} slave
 * @returns {string}
 */
globalThis.getLeftEyeSclera = function(slave) {
	if (hasLeftEye(slave)) {
		return slave.eye.left.sclera;
	} else {
		return "empty";
	}
};

/**
 * @param {FC.HumanState} slave
 * @returns {string}
 */
globalThis.getRightEyeSclera = function(slave) {
	if (hasRightEye(slave)) {
		return slave.eye.right.sclera;
	} else {
		return "empty";
	}
};

/**
 * @param {FC.HumanState} slave
 * @returns {boolean}
 */
globalThis.hasVisibleHeterochromia = function(slave) {
	return hasAnyEyes(slave) && getLeftEyeColor(slave) !== getRightEyeColor(slave);
};

/**
 * Gives the genetic color of the specified eye part.
 *
 * @param {FC.HumanState} playerOrSlave
 * @param {FC.BodySide} side
 * @param {"iris"|"pupil"|"sclera"} [eyePart="iris"]
 * @returns {string}
 */
globalThis.getGeneticEyeColor = function(playerOrSlave, side, eyePart = "iris") {
	if (eyePart === "iris") {
		if (playerOrSlave.geneticQuirks.albinism === 2 && "albinismOverride" in playerOrSlave) {
			return playerOrSlave.albinismOverride.eyeColor;
		} else {
			if (side === "left" && typeof playerOrSlave.geneticQuirks.heterochromia === "string") {
				return playerOrSlave.geneticQuirks.heterochromia;
			} else {
				return playerOrSlave.eye.origColor;
			}
		}
	} else if (eyePart === "pupil") {
		return playerOrSlave.race === "catgirl" ? "catlike" : "circular";
	} else {
		return "white";
	}
};

/**
 * Counts the number of eyes that are not the genetic color
 *
 * @param {FC.HumanState} slave
 * @returns {number}
 */
globalThis.getLenseCount = function(slave) {
	let count = 0;

	if (hasRightEye(slave) &&
		(getRightEyeColor(slave) !== getGeneticEyeColor(slave, "right", "iris")) ||
		(getRightEyePupil(slave) !== getGeneticEyeColor(slave, "right", "pupil")) ||
		(getRightEyeSclera(slave) !== getGeneticEyeColor(slave, "right", "sclera"))) {
		count++;
	}

	if (hasLeftEye(slave) &&
		getLeftEyeColor(slave) !== getGeneticEyeColor(slave, "left", "iris") ||
		(getLeftEyePupil(slave) !== getGeneticEyeColor(slave, "left", "pupil")) ||
		(getLeftEyeSclera(slave) !== getGeneticEyeColor(slave, "left", "sclera"))) {
		count++;
	}

	return count;
};


code:
/* limb checker */

/**
 * True if slave has no limbs, neither natural nor prosthetic
 *
 * @param {FC.LimbsState} slave
 * @returns {boolean}
 */
globalThis.isAmputee = function(slave) {
	return !(slave.leg.right || slave.leg.left || slave.arm.right || slave.arm.left);
};

/**
 * True if slave has at least one natural limb
 *
 * @param {FC.LimbsState} slave
 * @returns {boolean}
 */
globalThis.hasAnyNaturalLimbs = function(slave) {
	return getLeftArmID(slave) === 1 || getRightArmID(slave) === 1 || getLeftLegID(slave) === 1 || getRightLegID(slave) === 1;
};

/**
 * True if slave has at least one prosthetic limb
 *
 * @param {FC.LimbsState} slave
 * @returns {boolean}
 */
globalThis.hasAnyProstheticLimbs = function(slave) {
	return getLeftArmID(slave) > 1 || getRightArmID(slave) > 1 || getLeftLegID(slave) > 1 || getRightLegID(slave) > 1;
};

/**
 * True if slave has at least one quadrupedal limb
 *
 * @param {FC.LimbsState} slave
 * @returns {boolean}
 */
globalThis.hasAnyQuadrupedLimbs = function(slave) {
	return getLeftArmID(slave) > 6 || getRightArmID(slave) > 6 || getLeftLegID(slave) > 6 || getRightLegID(slave) > 6;
};

/**
 * True if slave has at least one leg
 *
 * @param {FC.LimbsState} slave
 * @returns {boolean}
 */
globalThis.hasAnyLegs = function(slave) {
	return !!slave.leg.right || !!slave.leg.left;
};

/**
 * True if slave has at least one arm
 *
 * @param {FC.LimbsState} slave
 * @returns {boolean}
 */
globalThis.hasAnyArms = function(slave) {
	return !!slave.arm.right || !!slave.arm.left;
};

/**
 * True if slave has at least one leg that is natural
 *
 * @param {FC.LimbsState} slave
 * @returns {boolean}
 */
globalThis.hasAnyNaturalLegs = function(slave) {
	return getLeftLegID(slave) === 1 || getRightLegID(slave) === 1;
};

/**
 * True if slave has at least one arm that is natural
 *
 * @param {FC.LimbsState} slave
 * @returns {boolean}
 */
globalThis.hasAnyNaturalArms = function(slave) {
	return getLeftArmID(slave) === 1 || getRightArmID(slave) === 1;
};

/**
 * True if slave has at least one leg that is prosthetic
 *
 * @param {FC.LimbsState} slave
 * @returns {boolean}
 */
globalThis.hasAnyProstheticLegs = function(slave) {
	return getLeftLegID(slave) > 1 || getRightLegID(slave) > 1;
};

/**
 * True if slave has at least one leg that is quadrupedal
 *
 * @param {FC.LimbsState} slave
 * @returns {boolean}
 */
globalThis.hasAnyQuadrupedLegs = function(slave) {
	return getLeftLegID(slave) > 6 || getRightLegID(slave) > 6;
};

/**
 * True if slave has at least one arm that is prosthetic
 *
 * @param {FC.LimbsState} slave
 * @returns {boolean}
 */
globalThis.hasAnyProstheticArms = function(slave) {
	return getLeftArmID(slave) > 1 || getRightArmID(slave) > 1;
};

/**
 * True if slave has at least one arm that is quadrupedal
 *
 * @param {FC.LimbsState} slave
 * @returns {boolean}
 */
globalThis.hasAnyQuadrupedArms = function(slave) {
	return getLeftArmID(slave) > 6 || getRightArmID(slave) > 6;
};

/**
 * True if slave has both legs
 *
 * @param {FC.LimbsState} slave
 * @returns {boolean}
 */
globalThis.hasBothLegs = function(slave) {
	return !!slave.leg.right && !!slave.leg.left;
};

/**
 * True if slave has both arms
 *
 * @param {FC.LimbsState} slave
 * @returns {boolean}
 */
globalThis.hasBothArms = function(slave) {
	return !!slave.arm.right && !!slave.arm.left;
};

/**
 * True if slave has both legs and they are natural
 *
 * @param {FC.LimbsState} slave
 * @returns {boolean}
 */
globalThis.hasBothNaturalLegs = function(slave) {
	return getLeftLegID(slave) === 1 && getRightLegID(slave) === 1;
};

/**
 * True if slave has both arms and they are natural
 *
 * @param {FC.LimbsState} slave
 * @returns {boolean}
 */
globalThis.hasBothNaturalArms = function(slave) {
	return getLeftArmID(slave) === 1 && getRightArmID(slave) === 1;
};

/**
 * True if slave has both arms and they are artificial
 *
 * @param {FC.LimbsState} slave
 * @returns {boolean}
 */
globalThis.hasBothProstheticArms = function(slave) {
	return getLeftArmID(slave) > 1 && getRightArmID(slave) > 1;
};

/**
 * True if slave has both arms and they are quadrupedal
 *
 * @param {FC.LimbsState} slave
 * @returns {boolean}
 */
globalThis.hasBothQuadrupedArms = function(slave) {
	return getLeftArmID(slave) > 6 && getRightArmID(slave) > 6;
};

/**
 * True if slave has both legs and they are artificial
 *
 * @param {FC.LimbsState} slave
 * @returns {boolean}
 */
globalThis.hasBothProstheticLegs = function(slave) {
	return getLeftLegID(slave) > 1 && getRightLegID(slave) > 1;
};

/**
 * True if slave has both legs and they are quadrupedal
 *
 * @param {FC.LimbsState} slave
 * @returns {boolean}
 */
globalThis.hasBothQuadrupedLegs = function(slave) {
	return getLeftLegID(slave) > 6 && getRightLegID(slave) > 6;
};

/**
 * True if slave has any limbs
 *
 * @param {FC.LimbsState} slave
 * @returns {boolean}
 */
globalThis.hasAnyLimbs = function(slave) {
	return hasAnyArms(slave) || hasAnyLegs(slave);
};

/**
 * True if slave has all limbs
 *
 * @param {FC.LimbsState} slave
 * @returns {boolean}
 */
globalThis.hasAllLimbs = function(slave) {
	return hasBothLegs(slave) && hasBothArms(slave);
};

/**
 * True if slave has all limbs and all are natural
 *
 * @param {FC.LimbsState} slave
 * @returns {boolean}
 */
globalThis.hasAllNaturalLimbs = function(slave) {
	return hasBothNaturalLegs(slave) && hasBothNaturalArms(slave);
};

/**
 * True if slave has all limbs and all are quadrupedal
 *
 * @param {FC.LimbsState} slave
 * @returns {boolean}
 */
globalThis.isQuadrupedal = function(slave) {
	return hasBothQuadrupedLegs(slave) && hasBothQuadrupedArms(slave);
};

/**
 * True if slave has left arm
 *
 * @param {FC.LimbsState} slave
 * @returns {boolean}
 */
globalThis.hasLeftArm = function(slave) {
	return !!slave.arm.left;
};

/**
 * True if slave has right arm
 *
 * @param {FC.LimbsState} slave
 * @returns {boolean}
 */
globalThis.hasRightArm = function(slave) {
	return !!slave.arm.right;
};

/**
 * True if slave has left leg
 *
 * @param {FC.LimbsState} slave
 * @returns {boolean}
 */
globalThis.hasLeftLeg = function(slave) {
	return !!slave.leg.left;
};

/**
 * True if slave has right leg
 *
 * @param {FC.LimbsState} slave
 * @returns {boolean}
 */
globalThis.hasRightLeg = function(slave) {
	return !!slave.leg.right;
};

/**
 * Returns limb ID of the left arm. Uses new IDs.
 *
 * @param {FC.LimbsState} slave
 * @returns {number}
 */
globalThis.getLeftArmID = function(slave) {
	if (hasLeftArm(slave)) {
		return slave.arm.left.type;
	} else {
		return 0;
	}
};

/**
 * Returns limb ID of the right arm. Uses new IDs.
 *
 * @param {FC.LimbsState} slave
 * @returns {number}
 */
globalThis.getRightArmID = function(slave) {
	if (hasRightArm(slave)) {
		return slave.arm.right.type;
	} else {
		return 0;
	}
};

/**
 * Returns limb ID of the left leg. Uses new IDs.
 *
 * @param {FC.LimbsState} slave
 * @returns {number}
 */
globalThis.getLeftLegID = function(slave) {
	if (hasLeftLeg(slave)) {
		return slave.leg.left.type;
	} else {
		return 0;
	}
};

/**
 * Returns limb ID of the right leg. Uses new IDs.
 *
 * @param {FC.LimbsState} slave
 * @returns {number}
 */
globalThis.getRightLegID = function(slave) {
	if (hasRightLeg(slave)) {
		return slave.leg.right.type;
	} else {
		return 0;
	}
};

/**
 * Returns a very short description of the specified limb ID. Uses new limb IDs.
 *
 * @param {number} id
 * @returns {string}
 */
globalThis.idToDescription = function(id) {
	switch (id) {
		case 0:
			return "amputated";
		case 1:
			return "healthy";
		case 2:
			return "modern prosthetic";
		case 3:
			return "advanced, sex-focused prosthetic";
		case 4:
			return "advanced, beauty-focused prosthetic";
		case 5:
			return "advanced, combat-focused prosthetic";
		case 6:
			return "highly advanced cybernetic";
		case 7:
			return "feline prosthetic";
		case 8:
			return "canine prosthetic";
		case 9:
			return "feline combat-adapted prosthetic";
		case 10:
			return "canine combat-adapted prosthetic";
		default:
			return "unknown id: " + id;
	}
};

/**
 * Returns count of specified limb type. Uses new limb IDs:
 * * 0: no limb
 * * 1: natural
 * * 2: basic
 * * 3: sex
 * * 4: beauty
 * * 5: combat
 * * 6: cybernetic
 * * 7: felidaeL
 * * 8: canidaeL
 * * 9: felidaeCL
 * * 10: canidaeCL
 *
 * Can also be used to check for groups:
 * * 101: any limbs, that are not amputated
 * * 102: prosthetic limbs off all kind
 * * 103: sex-prosthetic
 * * 104: beauty-prosthetic
 * * 105: combat-prosthetic
 * * 106: quadruped
 * * 107: feline
 * * 108: canine
 * *
 *
 * !!! I think I adjusted this right but i didn't spend too long on it so it might be wrong. !!!
 *
 * @param {FC.LimbsState} slave
 * @param {number} [id] Defaults to all limb types if unspecified (id = 101)
 * @returns {number}
 */
globalThis.getLimbCount = function(slave, id = 101) {
	if (id < 100) {
		let n = 0;
		if (getLeftArmID(slave) === id) {
			n++;
		}
		if (getRightArmID(slave) === id) {
			n++;
		}
		if (getLeftLegID(slave) === id) {
			n++;
		}
		if (getRightLegID(slave) === id) {
			n++;
		}
		return n;
	}
	switch (id) {
		case 101:
			return getLimbCount(slave, 1) + getLimbCount(slave, 102);
		case 102:
			return getLimbCount(slave, 2) + getLimbCount(slave, 3) + getLimbCount(slave, 4) + getLimbCount(slave, 5) + getLimbCount(slave, 6) + getLimbCount(slave, 7) + getLimbCount(slave, 8) + getLimbCount(slave, 9) + getLimbCount(slave, 10);
		case 103:
			return getLimbCount(slave, 3) + getLimbCount(slave, 6);
		case 104:
			return getLimbCount(slave, 4) + getLimbCount(slave, 6);
		case 105:
			return getLimbCount(slave, 5) + getLimbCount(slave, 6) + getLimbCount(slave, 9) + getLimbCount(slave, 10);
		case 106:
			return getLimbCount(slave, 7) + getLimbCount(slave, 8) + getLimbCount(slave, 9) + getLimbCount(slave, 10);
		case 107:
			return getLimbCount(slave, 7) + getLimbCount(slave, 9);
		case 108:
			return getLimbCount(slave, 8) + getLimbCount(slave, 10);
	}
	// unknown id defaults to 0
	return 0;
};

/**
 * Returns count of specified leg type. Uses new limb IDs.
 *
 * @param {FC.LimbsState} slave
 * @param {number} id
 * @returns {number}
 */
globalThis.getLegCount = function(slave, id) {
	let n = 0;

	if (getLeftLegID(slave) === id) {
		n++;
	}
	if (getRightLegID(slave) === id) {
		n++;
	}

	return n;
};

/**
 * Returns count of specified arm type. Uses new limb IDs.
 *
 * @param {FC.LimbsState} slave
 * @param {number} id
 * @returns {number}
 */
globalThis.getArmCount = function(slave, id) {
	let n = 0;

	if (getLeftArmID(slave) === id) {
		n++;
	}
	if (getRightArmID(slave) === id) {
		n++;
	}

	return n;
};

/**
 * Returns a string depending on the limbs a slave has.
 * By default a variation of "arms and legs", but this can be changed via parameters.
 * Expects the slave to at least have one limb.
 *
 * @param {FC.LimbsState} slave
 * @param {string} [arms]
 * @param {string} [arm]
 * @param {string} [legs]
 * @param {string} [leg]
 */
globalThis.armsAndLegs = function(slave, arms = "arms", arm = "arm", legs = "legs", leg = "leg") {
	let r = "";
	if (hasAnyArms(slave)) {
		if (hasBothArms(slave)) {
			r += arms;
		} else {
			r += arm;
		}
		if (hasAnyLegs(slave)) {
			r += " and ";
		}
	}

	if (hasBothLegs(slave)) {
		r += legs;
	} else if (hasAnyLegs(slave)) {
		r += leg;
	}

	return r;
};


code:
globalThis.isInLabor = function(slave) {
	return (slave.labor);
	// return V.birthIDs.includes(slave.ID);
};

globalThis.startLabor = function(slave) {
	slave.labor = 1;
	/*
	if (typeof slave === "number") {
		V.birthIDs.push(slave);
	} else {
		V.birthIDs.push(slave.ID);
	}
	*/
};

globalThis.isInduced = function(slave) {
	return (slave.induce);
	// return V.induceIDs.includes(slave.ID);
};

globalThis.induce = function(slave) {
	startLabor(slave);
	slave.induce = 1;
	/*
	if (typeof slave === "number") {
		V.induceIDs.push(slave);
	} else {
		V.induceIDs.push(slave.ID);
	}
	*/
};


code:
/**
 * @param {App.Entity.SlaveState} slave
 * @returns {boolean}
 */
globalThis.isSexuallyPure = function(slave) {
	if (!slave) {
		return null;
	}
	return (slave.vagina < 1 && slave.anus < 1 && !slave.counter.anal && !slave.counter.vaginal && !slave.counter.oral && !slave.counter.mammary && !slave.counter.penetrative && !slave.counter.publicUse);
};

/**
 * @param {App.Entity.SlaveState} slave
 * @returns {boolean}
 */
globalThis.isSlaveAvailable = function(slave) {
	if (!slave) {
		return null;
	} else if (slave.assignment === Job.AGENT) {
		return false;
	} else if (slave.assignment === Job.AGENTPARTNER) {
		return false;
	} else if (slave.assignment === Job.ARCADE) {
		return false;
	} else if (slave.assignment === Job.DAIRY && V.dairyRestraintsSetting >= 2) {
		return false;
	}
	return true;
};

/**
 * @param {App.Entity.SlaveState} slave
 * @returns {boolean}
 */
globalThis.isFaceVisible = function(slave) {
	const obscuringClothes = ["a Fuckdoll suit", "restrictive latex", "a burqa", "a niqab and abaya", "a klan robe", "a slutty klan robe"].includes(slave.clothes); // Debatable whether "a cybersuit", "battlearmor", "Imperial Plate", should be added. Helmets could be off in short periods.
	const obscuringFaceAccessory = ["porcelain mask"].includes(slave.faceAccessory);
	return (!obscuringClothes && !obscuringFaceAccessory);
};

globalThis.SlaveStatsChecker = (function() {
	return {
		checkForLisp: hasLisp,
		isModded: isModded,
		isUnmodded: isUnmodded,
		modScore: modScore
	};

	/* call as SlaveStatsChecker.checkForLisp() */
	function hasLisp(slave) {
		if (V.disableLisping === 1 || !canTalk(slave)) {
			return false;
		}
		return (slave.lips > 70 || (slave.piercing.lips.weight + slave.piercing.tongue.weight > 2) || slave.teeth === "gapped");
	}

	/** call as SlaveStatsChecker.modScore()
	 * @param {App.Entity.SlaveState} slave
	 * @returns {{piercing: number, tat: number, brand: number, scar: number, total: number}}
	 */
	function modScore(slave) {
		const piercing = piercingScore(slave);
		const tat = tatScore(slave);
		const brand = brandScore(slave);
		const scar = scarScore(slave);
		return {
			piercing,
			tat,
			brand,
			scar,
			total: piercing + tat + brand + scar
		};
	}

	/**
	 * helper function, not callable
	 * @param {App.Entity.SlaveState} slave
	 * @returns {number}
	 */
	function piercingScore(slave) {
		let score = 0;

		for (const piercing in slave.piercing) {
			const impact = (["lips", "tongue", "nipple", "genitals", "vagina", "dick", "anus"].includes(piercing)) ? 0.25 : 0.5; // Piercings in sexual areas count more for piercing score.
			if (piercing === "genitals" && slave.piercing.genitals.smart) {
				score += 1.25;
			} else if (slave.piercing[piercing].weight > 0) {
				score += slave.piercing[piercing].weight * 0.75 - impact;
				if (["areola", "corset"].includes(piercing)) {
					score += 1; // bonus score for corset and areola piercings (which are actually many piercings each)
				}
			}
		}

		return score;
	}

	/**
	 * helper function, not callable
	 * @param {App.Entity.SlaveState} slave
	 * @returns {number}
	 */
	function tatScore(slave) {
		let score = 0;

		if (slave.boobsTat !== 0) {
			score += 1.25;
		}
		if (slave.buttTat !== 0) {
			score += 1.25;
		}
		if (slave.lipsTat !== 0) {
			score += 1.25;
		}
		if (slave.shouldersTat !== 0) {
			score += 1;
		}
		if (slave.backTat !== 0) {
			score += 1.25;
		}
		if (slave.armsTat !== 0) {
			score += 1;
		}
		if (slave.legsTat !== 0) {
			score += 1;
		}
		if (slave.stampTat !== 0) {
			score += 1;
		}
		if (slave.vaginaTat !== 0) {
			score += 1;
		}
		if (slave.dickTat !== 0) {
			score += 1;
		}
		if (slave.bellyTat !== 0) {
			if ((slave.preg > slave.pregData.normalBirth / 1.33 && slave.pregType >= 20) || slave.belly >= 300000) {
				score += 0.75;
			} else if ((slave.preg > slave.pregData.normalBirth / 2 && slave.pregType >= 20) || (slave.preg > slave.pregData.normalBirth / 1.33 && slave.pregType >= 10) || slave.belly >= 150000) {
				score += 1;
			} else if (slave.belly >= 10000 || slave.bellyImplant >= 8000) {
				score += 1;
			} else if ((slave.preg >= slave.pregData.normalBirth / 4 && slave.pregType >= 20) || (slave.preg > slave.pregData.normalBirth / 4 && slave.pregType >= 10) || slave.belly >= 5000) {
				score += 0.5;
			} else if (slave.belly >= 1500) {
				score += 0.25;
			} else {
				score += 0.1;
			}
		}
		if (slave.anusTat === "bleached") {
			score += 0.5;
		} else if (slave.anusTat !== 0) {
			score += 1.25;
		}
		if (slave.abortionTat > 0 || (slave.abortionTat === 0 && slave.pregKnown === 1)) {
			score += 1;
		}
		if (slave.birthsTat > 0 || (slave.birthsTat === 0 && slave.pregKnown === 1)) {
			score += 1;
		}
		return score;
	}

	/**
	 * helper function, not callable
	 * @param {App.Entity.SlaveState} slave
	 * @returns {number}
	 */
	function brandScore(slave) {
		let score = 0;
		const brands = App.Medicine.Modification.brandRecord(slave);
		score += Object.getOwnPropertyNames(brands).length;
		return score;
	}

	/**
	 * helper function, not callable
	 * @param {App.Entity.SlaveState} slave
	 * @returns {number}
	 */
	function scarScore(slave) {
		let score = 0;
		let scars;
		if (slave.hasOwnProperty("scar")) { /* For very old saves this may not be defined yet and blocks the save from loading. */
			scars = App.Medicine.Modification.scarRecord(slave);
			for (const bodypart of Object.keys(scars)) {
				for (const kind of Object.keys(scars[bodypart])) {
					score += scars[bodypart][kind];
				}
			}
		}
		return score;
	}

	/**
	 * call as SlaveStatsChecker.isModded()
	 * @param {App.Entity.SlaveState} slave
	 * @returns {boolean}
	 */
	function isModded(slave) {
		const tattoos = tatScore(slave);
		const piercings = piercingScore(slave);
		const brands = brandScore(slave);
		const scars = scarScore(slave);
		const mods = piercings + tattoos + scars;

		return (mods > 15 || (piercings > 8 && tattoos > 5) || brands > 5);
	}

	/**
	 * call as SlaveStatsChecker.isUnmodded()
	 * @param {App.Entity.SlaveState} slave
	 * @returns {boolean}
	 */
	function isUnmodded(slave) {
		const tattoos = tatScore(slave);
		const piercings = piercingScore(slave);
		const brands = brandScore(slave);
		const scars = scarScore(slave);

		return (!isModded(slave) && slave.piercing.corset.weight === 0 && piercings < 3 && tattoos < 2 && brands < 2 && scars <= 1);
	}
}());

/**
 * Returns if slave is considered slim or not by arcology standards.
 * @param {App.Entity.SlaveState} slave
 * @returns {boolean}
 */
globalThis.isSlim = function(slave) {
	let slim = false;
	const arcology = V.arcologies[0];

	if (arcology.FSSlimnessEnthusiastLaw === 1) {
		return (slimLawPass(slave) === 1);
	}
	if ((slave.boobs < 500) && (slave.butt < 3)) {
		if ((slave.muscles <= 30) && (arcology.FSPhysicalIdealist === "unset") && (slave.weight <= 10) && (arcology.FSHedonisticDecadence === "unset")) {
			slim = true;
		} else if (arcology.FSPhysicalIdealist !== "unset") {
			if (((arcology.FSPhysicalIdealistStrongFat === 1) && (slave.weight <= 30)) || slave.weight <= 10) {
				slim = true;
			}
		} else if ((arcology.FSHedonisticDecadence !== "unset") && (slave.weight <= 30)) {
			if (arcology.FSHedonisticDecadenceStrongFat === 1 || slave.muscles <= 30) {
				slim = true;
			}
		}
	}
	return slim;
};

/**
 * Returns if slave is considered a fashionable body shape or not by arcology laws.
 * @param {App.Entity.SlaveState} slave
 * @returns {number} 1: yes, 0: no
 */
globalThis.genderLawPass = function(slave) {
	let genderLawPass = 1;

	const arcology = V.arcologies[0];

	if (arcology.FSPhysicalIdealist === "unset" && arcology.FSHedonisticDecadenceStrongFat === 0 && slave.muscles > 30) {
		/* muscle check */
		genderLawPass = 0;
	}
	if (arcology.FSHedonisticDecadence !== "unset" || arcology.FSPhysicalIdealistStrongFat === 1) {
		/* weight check */
		if (slave.weight > 130 || slave.weight <= -30) {
			genderLawPass = 0;
		}
	} else if (Math.abs(slave.weight) > 30) {
		genderLawPass = 0;
	}
	if (arcology.FSAssetExpansionist !== "unset") {
		if (slave.boobs < 1600 || slave.boobs > 4300 || slave.butt < 4 || slave.butt > 8) {
			genderLawPass = 0;
		}
	} else if (slave.boobs < 500 || slave.boobs > 800 || slave.butt < 3 || slave.butt > 4) {
		genderLawPass = 0;
	}

	return genderLawPass;
};

/**
 * Returns if slave is considered slim or not by Slimness Enthusiast Law.
 * @param {App.Entity.SlaveState} slave
 * @returns {number} 1: yes, 0: no
 */
globalThis.slimLawPass = function(slave) {
	let slimLawPass = 0;
	const arcology = V.arcologies[0];

	if (arcology.FSSlimnessEnthusiastLaw === 1) {
		if ((slave.boobs < 300) && (slave.butt <= 1) && (slave.waist <= 10)) {
			if ((arcology.FSPhysicalIdealist === "unset") && (arcology.FSHedonisticDecadenceStrongFat === 0) && (slave.muscles > 30)) {
				/* muscle check*/
				slimLawPass = 0;
			} else if ((arcology.FSHedonisticDecadence !== "unset") || (arcology.FSPhysicalIdealistStrongFat === 1)) {
				slimLawPass = 1;
				/* weight check*/
				if (slave.weight > 30) {
					slimLawPass = 0;
				}
			} else if (slave.weight > 10) {
				slimLawPass = 0;
			} else {
				slimLawPass = 1;
			}
		}
	}

	return slimLawPass;
};

/**
 * Returns if slave is considered an acceptable height by arcology standards.
 * @param {App.Entity.SlaveState} slave
 * @returns {boolean}
 */
globalThis.heightPass = function(slave) {
	const arcology = V.arcologies[0];

	if (arcology.FSPetiteAdmiration !== "unset") {
		if (arcology.FSPetiteAdmirationLaw2 === 1) {
			if (slave.height < Height.mean(slave) - 5) {
				return true;
			}
		} else {
			if (slave.height < 160) {
				return true;
			}
		}
	} else if (arcology.FSStatuesqueGlorification !== "unset") {
		if (arcology.FSStatuesqueGlorificationLaw2 === 1) {
			if (slave.height + shoeHeight(slave) > Height.mean(slave) + 5) {
				return true;
			}
		} else {
			if (slave.height + shoeHeight(slave) >= 170) {
				return true;
			}
		}
	}
	return false;
};

/**
 * Returns slave bimbo body degree (FSIntellectualDependencyLawBeauty).
 * @param {App.Entity.SlaveState} slave
 * @returns {number}
 */
globalThis.bimboScore = function(slave) {
	let degree = 0;
	const modScore = SlaveStatsChecker.modScore(slave);

	if (slave.lips > 70) {
		degree++;
	}
	if ((slave.lipsImplant / slave.lips) >= 0.60) {
		degree++;
	}
	if (slave.boobs >= 2000) {
		degree++;
		if (slave.boobs >= 10000) {
			degree++;
		}
	}
	if ((slave.boobsImplant / slave.boobs) >= 0.60) {
		degree++;
	}
	if (slave.butt > 4) {
		degree++;
		if (slave.butt > 10) {
			degree++;
		}
	}
	if ((slave.buttImplant / slave.butt) >= 0.60) {
		degree++;
	}
	if (slave.belly >= 1500) {
		degree++;
		if (slave.belly >= 20000) {
			degree++;
		}
	}
	if (slave.waist <= -60) {
		degree++;
	}
	if (slave.hips > 1) {
		degree++;
		if (slave.hips > 2) {
			degree++;
		}
	}
	if (slave.dick > 3 && canPenetrate(slave)) {
		degree++;
		if (slave.balls > 5) {
			degree++;
		}
	}
	if (slave.hLength >= 100) {
		degree++;
	}
	if (slave.makeup > 1 && slave.nails > 1) {
		degree++;
	}
	if (modScore.total >= 10) {
		degree++;
	}
	if (modScore.piercing > 5) {
		degree++;
	}
	if (modScore.tat > 3) {
		degree++;
	}
	degree += shoeHeelCategory(slave);
	if (slave.skin === "sun tanned" || slave.skin === "spray tanned") {
		degree++;
	}
	if (getExposure(slave) === 2) {
		degree++;
	}

	// degree can go far higher than the max to allow various FS combinations to take advantage.
	return Math.clamp(degree, 0, 6);
};

/**
 * Returns if slave is considered stacked (big T&A) or not.
 * @param {App.Entity.SlaveState} slave
 * @returns {boolean}
 */
globalThis.isStacked = function(slave) {
	return (slave.butt > 4) && (slave.boobs > 800);
};

/**
 * @param {App.Entity.SlaveState} slave
 * @returns {boolean}
 */
globalThis.isXY = function(slave) {
	return (slave.dick > 0);
};

/**
 * @param {App.Entity.SlaveState} slave
 * @returns {boolean}
 */
globalThis.isYoung = function(slave) {
	return (slave.visualAge < 30);
};

/**
 * @param {App.Entity.SlaveState} slave
 * @returns {boolean}
 */
globalThis.isPreg = function(slave) {
	return ((slave.bellyPreg >= 5000) || (slave.bellyImplant >= 5000));
};

/**
 * @param {App.Entity.SlaveState} slave
 * @returns {boolean}
 */
globalThis.isNotPreg = function(slave) {
	return (!isPreg(slave) && (slave.belly < 100) && (slave.weight < 30) && !App.Data.misc.fakeBellies.includes(slave.bellyAccessory));
};

/**
 * @param {App.Entity.SlaveState} slave
 * @returns {boolean}
 */
globalThis.isPure = function(slave) {
	return ((slave.boobsImplant === 0) && (slave.buttImplant === 0) && (slave.waist >= -95) && (slave.lipsImplant === 0) && (slave.faceImplant < 30) && (slave.bellyImplant === -1) && (Math.abs(slave.shouldersImplant) < 2) && (Math.abs(slave.hipsImplant) < 2));
};

/**
 * @param {App.Entity.SlaveState} slave
 * @returns {boolean}
 */
globalThis.isSurgicallyImproved = function(slave) {
	return ((slave.boobsImplant > 0) && (slave.buttImplant > 0) && (slave.waist < -10) && (slave.lipsImplant > 0));
};

/**
 * @param {FC.HumanState} slave
 * @returns {boolean}
 */
globalThis.isFullyPotent = function(slave) {
	if (!slave) {
		return null;
	} else if (slave.dick > 0 && slave.balls > 0 && slave.ballType !== "sterile" && slave.hormoneBalance < 100 && slave.drugs !== "hormone blockers" && slave.pubertyXY === 1) {
		return true;
	}
	return false;
};

/**
 * @param {App.Entity.SlaveState} slave
 * @returns {boolean} Whether the slave is considered at least smart
 */
globalThis.isSmart = function(slave) {
	if (!slave) {
		return null;
	} else if (slave.intelligence > 15) {
		return true;
	}
	return false;
};

/**
 * @param {App.Entity.SlaveState} slave
 * @returns {boolean} Whether the slave is considered at least slow
 */
globalThis.isStupid = function(slave) {
	if (!slave) {
		return null;
	} else if (slave.intelligence < -15) {
		return true;
	}
	return false;
};

/**
 * @param {App.Entity.SlaveState} slave
 * @returns {boolean} Whether the slave is a part of the "superior" race
 */
globalThis.isSuperiorRace = function(slave) {
	const arcology = V.arcologies[0];

	if (!slave || arcology.FSSupremacist === "unset") {
		return null;
	} else {
		if (slave.race === arcology.FSSupremacistRace) {
			return true;
		}
	}
	return false;
};

/**
 * @param {App.Entity.SlaveState} slave
 * @returns {boolean} Whether the slave is a part of the "inferior" race
 */
globalThis.isInferiorRace = function(slave) {
	const arcology = V.arcologies[0];

	if (!slave || arcology.FSSubjugationist === "unset") {
		return null;
	} else {
		if (slave.race === arcology.FSSubjugationistRace) {
			return true;
		}
	}
	return false;
};

/**
 * @param {App.Entity.SlaveState} slave
 * @returns {number} Whether the slave is a breeder for the Elites
 */
globalThis.isEliteBreeder = function(slave) {
	return slave.breedingMark;
};

/**
 * @param {FC.HumanState} slave
 * @returns {boolean}
 */
globalThis.canGetPregnant = function(slave) {
	if (!slave) {
		return null;
	} else if (slave.preg === -1) { /* contraceptives check */
		return false;
	} else if (!isFertile(slave)) { /* check other fertility factors */
		return false;
	} else if ((slave.ovaries === 1) && (canDoVaginal(slave))) {
		return true;
	} else if ((slave.mpreg === 1) && (canDoAnal(slave))) {
		/* pregmod */
		return true;
	}
	return false;
};

/** contraceptives (.preg === -1) do not negate this function
 * @param {FC.HumanState} slave
 * @returns {boolean}
 */
globalThis.isFertile = function(slave) {
	if (!slave) {
		return null;
	}

	if (slave.womb.length > 0 && slave.geneticQuirks.superfetation < 2) {
		/* currently pregnant without superfetation */
		return false;
	} else if (slave.broodmother > 0) {
		/* currently broodmother */
		return false;
	} else if (slave.preg < -1) {
		/* sterile */
		return false;
	} else if (slave.pregWeek < 0) {
		/* postpartum */
		return false;
	} else if (slave.pubertyXX === 0) {
		/* pregmod start */
		return false;
	} else if (slave.ovaryAge >= 47) {
		return false;
	} else if (slave.inflation > 2) {
		return false;
	} else if (slave.bellyImplant !== -1) {
		return false;
	} else if (slave.mpreg === 1 || slave.ovaries === 1) {
		if (slave.womb.length > 0) { // superfetation route
			if (slave.fertPeak !== 0) {
				return false;
			}
		}
		return true;
	}
	return false;
};

/**
 * @param {FC.HumanState} slave
 * @returns {boolean}
 */
globalThis.canAchieveErection = function(slave) {
	if (!slave) {
		return null;
	} else if (slave.dick <= 0) {
		return false;
	} else if (slave.dick < 11 && slave.drugs === "priapism agents") {
		return true;
	} else if (slave.dick > 6) {
		return false;
	} else if (slave.aphrodisiacs > 1 || (slave.inflationType === "aphrodisiac" && slave.inflation >= 2)) {
		return true;
	} else if (slave.ballType === "sterile") {
		return false;
	} else if ((slave.balls > 0 ? slave.hormoneBalance < 100 : slave.hormoneBalance <= -100) && slave.drugs !== "hormone blockers") {
		return true;
	} else if (slave.aphrodisiacs > 0 || slave.inflationType === "aphrodisiac") {
		return true;
	}
	return false;
};

/**
 * @param {FC.HumanState} slave
 * @returns {boolean}
 */
globalThis.canPenetrate = function(slave) {
	if (!slave) {
		return null;
	} else if (!canAchieveErection(slave)) {
		return false;
	} else if (slave.chastityPenis === 1) {
		return false;
	} else if (slave.dick > 7) {
		return false;
	/*
	} else if (slave.belly >= 300000) {
		return false;
	*/
	}
	return true;
};

/**
 * @param {FC.HumanState} slave
 * @returns {boolean}
 */
globalThis.canSee = function(slave) {
	if (!slave) {
		return null;
	}
	return (getBestVision(slave)) > 0;
};

/**
 *
 * @param {FC.HumanState} slave
 * @returns {boolean}
 */
globalThis.canSeePerfectly = function(slave) {
	if (!canSee(slave)) {
		return false;
	}

	if (getBestVision(slave) === 2 && (slave.eyewear === "blurring glasses") || (slave.eyewear === "blurring contacts")) {
		// could see perfectly, but being blurred
		return false;
	} else if (getBestVision(slave) < 2 && !(slave.eyewear === "corrective glasses" || slave.eyewear === "corrective contacts")) {
		// can't see perfectly and not corrected
		return false;
	}
	return true;
};

/**
 * @param {FC.HumanState} slave
 * @returns {boolean}
 */
globalThis.canHear = function(slave) {
	if (!slave) {
		return null;
	}
	return ((slave.hears > -2) && (slave.earwear !== "deafening ear plugs"));
};

/**
 * @param {FC.HumanState} slave
 * @returns {boolean}
 */
globalThis.canSmell = function(slave) {
	if (!slave) {
		return null;
	}
	return (slave.smells > -1);
};

/**
 * @param {FC.HumanState} slave
 * @returns {boolean}
 */
globalThis.canTaste = function(slave) {
	if (!slave) {
		return null;
	}
	return (slave.tastes > -1);
};

/**
 * @param {FC.HumanState} slave
 * @returns {boolean}
 */
globalThis.canHold = function(slave) {
	if (!slave) {
		return null;
	} else if (hasAnyQuadrupedArms(slave)) {
		return false;
	}
	return hasAnyArms(slave);
};

/** If a slave can walk, she can move and stand.
 * @param {FC.HumanState} slave
 * @returns {boolean}
 */
globalThis.canWalk = function(slave) {
	if (!slave) {
		return null;
	} else if (!hasBothLegs(slave)) {
		return false;
	} else if (!isQuadrupedal(slave) && hasAnyQuadrupedLegs(slave)) {
		return false;
	} else if (tooFatSlave(slave)) {
		return false;
	} else if (tooBigBreasts(slave)) {
		return false;
	} else if (tooBigDick(slave)) {
		return false;
	} else if (tooBigBalls(slave)) {
		return false;
	} else if (tooBigButt(slave)) {
		return false;
	} else if (tooBigBelly(slave)) {
		return false;
	} else if (slave.heels === 1 && shoeHeelCategory(slave) === 0) {
		return false;
	}
	return true;
};

/** If a slave can stand, she can move, but not necessarily walk.
 * @param {FC.HumanState} slave
 * @returns {boolean}
 */
globalThis.canStand = function(slave) {
	if (!slave) {
		return null;
	/*
	} else if (!canMove(slave)) {
		return false;
	*/
	} else if (!hasAnyLegs(slave)) {
		return false;
	} else if (hasAnyQuadrupedLegs(slave)) {
		return false;
	} else if (!hasBothLegs(slave) && shoeHeelCategory(slave) > 1) {
		return false; // Can't stand on one leg while in heels
	} else if (slave.heels === 1 && shoeHeelCategory(slave) === 0) {
		return false;
	} else if (tooFatSlave(slave)) {
		return false;
	} else if (tooBigBreasts(slave)) {
		return false;
	} else if (tooBigBelly(slave)) {
		return false;
	}
	return true;
};

/** can the actor move at all?
 * @param {FC.HumanState} slave
 * @returns {boolean}
 */
globalThis.canMove = function(slave) {
	if (!slave) {
		return false;
	} else if (canWalk(slave)) {
		return true; // If she can walk, no point to even check anything after
	} else if (!hasBothLegs(slave)) {
		return false;
	} else if (!hasBothQuadrupedLegs(slave) && hasAnyQuadrupedLegs(slave)) {
		return false;
	} else if (tooFatSlave(slave)) {
		return false;
	} else if (slave.physicalAge <= 3) {
		if (slave.boobs > 25000 + (slave.muscles * 20)) {
			return false;
		}
		if (slave.belly >= 150000) {
			return false;
		}
		if (slave.balls >= 30 + (slave.muscles * 0.3)) {
			return false;
		}
	} else if (slave.physicalAge <= 12) {
		if (slave.boobs > 100000 + (slave.muscles * 50)) {
			return false;
		}
		if (slave.belly >= 300000) {
			return false;
		}
		if (slave.balls >= 60 + (slave.muscles * 0.5)) {
			return false;
		}
	} else if (slave.physicalAge < 18) {
		if (slave.boobs > 250000 + (slave.muscles * 100)) {
			return false;
		}
		if (slave.belly >= 600000) {
			return false;
		}
		if (slave.balls >= 90 + (slave.muscles * 0.7)) {
			return false;
		}
	} else if (slave.physicalAge >= 18) {
		if (slave.boobs > 500000 + (slave.muscles * 200)) {
			return false;
		}
		if (slave.belly >= 1000000) {
			return false;
		}
		if (slave.balls >= 90 + (slave.muscles * 0.7)) {
			return false;
		}
	}
	return true;
};

/** Assuming the actor can walk, is their movement hindered by anything? Intended for the player, but could be applied to slaves.
 * @param {FC.HumanState} slave
 * @returns {boolean}
 */
globalThis.isHindered = function(slave) {
	const averageHeight = Height.mean(slave);
	if (!slave) {
		return null;
	} else if (!canWalk(slave) && canMove(slave)) {
		return true;
	} else if (!isQuadrupedal(slave) && hasAnyQuadrupedLegs(slave)) {
		return true;
	} else if (hasBothQuadrupedLegs(slave) && !hasBothQuadrupedArms(slave)) {
		return true;
	} else if (!hasBothQuadrupedLegs(slave) && hasAnyQuadrupedLegs(slave) && !hasBothQuadrupedArms(slave) && hasAnyQuadrupedArms(slave)) {
		return true;
	} else if (slave.weight >= 130 || (slave.weight >= 95 + ((slave.physicalAge - 9) * 5))) {
		return true;
	} else if (slave.muscles > 95 && slave.height <= (averageHeight + 10)) {
		return true;
	} else if (slave.belly >= 60000 || slave.belly >= 60000 / (1 + Math.pow(Math.E, -0.4 * (slave.physicalAge - 14))) || slave.belly >= Math.max(10000, ((12500 / 19) * slave.height) - (1172500 / 19))) {
		return true;
	} else if (slave.boobs > 5000) {
		return true;
	} else if (slave.balls >= 14) {
		return true;
	} else if (slave.hips > 2) {
		return true;
	} else if (slave.butt > 6) {
		return true;
	} else if (slave.muscles < -30) {
		return true;
	}
	// player exclusives
	if (slave.ID === -1) {
		if (onBedRest(slave)) {
			return true;
		} else if (slave.criticalDamage !== 0) {
			return true;
		}
	}
	return false;
};

/**
 * @param {App.Entity.SlaveState} slave
 * @param {boolean} checkLanguage Does a bad accent count as being unable to speak?
 * @returns {boolean}
 */
globalThis.canTalk = function(slave, checkLanguage = true) {
	if (!slave) {
		return null;
	} else if (checkLanguage && slave.accent > 2) {
		return false;
	} else if (slave.voice === 0) {
		return false;
	} else if (slave.lips > 95) {
		return false;
	} else if (slave.mouthAccessory === "dildo gag") {
		return false;
	} else if (slave.mouthAccessory === "massive dildo gag") {
		return false;
	} else if (slave.mouthAccessory === "ball gag") {
		return false;
	} else if (slave.mouthAccessory === "bit gag") {
		return false;
	} else if (slave.mouthAccessory === "ring gag") {
		return false;
	}
	return true;
};

/**
 * @param {FC.HumanState} slave
 * @returns {boolean}
 */
globalThis.canDoAnal = function(slave) {
	if (!slave) {
		return null;
	} else if (slave.chastityAnus === 1) {
		return false;
	}
	return true;
};

/**
 * @param {FC.HumanState} slave
 * @returns {boolean}
 */
globalThis.canDoVaginal = function(slave) {
	if (!slave) {
		return null;
	} else if (slave.vagina < 0) {
		return false;
	} else if (slave.chastityVagina === 1) {
		return false;
	}
	return true;
};

/**
 * @param {FC.HumanState} slave
 * @returns {boolean}
 */
globalThis.tooFatSlave = function(slave) {
	if (!slave) {
		return null;
	} else if (slave.weight > 130 + (slave.muscles / 20) && slave.physicalAge <= 3) {
		return true;
	} else if (slave.weight > 160 + (slave.muscles / 15) && slave.physicalAge <= 12) {
		return true;
	} else if (slave.weight > 185 + (slave.muscles / 10) && slave.physicalAge < 18) {
		return true;
	} else if (slave.weight > 190 + (slave.muscles / 5)) {
		return true;
	}
	return false;
};

/**
 * @param {FC.HumanState} slave
 * @returns {boolean}
 */
globalThis.tooBigBreasts = function(slave) {
	if (!slave) {
		return null;
	} else if (slave.boobs > 5000 + (slave.muscles * 20) && slave.physicalAge <= 3) {
		return true;
	} else if (slave.boobs > 10000 + (slave.muscles * 50) && slave.physicalAge <= 12) {
		return true;
	} else if (slave.boobs > 25000 + (slave.muscles * 100) && slave.physicalAge < 18) {
		return true;
	} else if (slave.boobs > 40000 + (slave.muscles * 200)) {
		return true;
	}
	return false;
};

/**
 * @param {FC.HumanState} slave
 * @returns {boolean}
 */
globalThis.tooBigBelly = function(slave) {
	if (!slave) {
		return null;
	} else if (slave.belly >= 120000 + (slave.muscles * 500) && slave.physicalAge <= 3) { // 70k - 170k
		return true;
	} else if (slave.belly >= 150000 + (slave.muscles * 800) && slave.physicalAge <= 12) { // 70k - 230k
		return true;
	} else if (slave.belly >= 350000 + (slave.muscles * 1000) && slave.physicalAge < 18) { // 250k - 450k
		return true;
	} else if (slave.belly >= 450000 + (slave.muscles * 2000)) { // 250k - 650k
		return true;
	}
	return false;
};

/**
 * @param {FC.HumanState} slave
 * @returns {boolean}
 */
globalThis.tooBigBalls = function(slave) {
	if (!slave) {
		return null;
	} else if (slave.balls >= 30 + (slave.muscles * 0.3) && slave.physicalAge <= 3) {
		return true;
	} else if (slave.balls >= 60 + (slave.muscles * 0.5) && slave.physicalAge <= 12) {
		return true;
	} else if (slave.balls >= 90 + (slave.muscles * 0.7)) {
		return true;
	}
	return false;
};

/**
 * @param {FC.HumanState} slave
 * @returns {boolean}
 */
globalThis.tooBigDick = function(slave) {
	if (!slave) {
		return null;
	} else if (slave.dick >= 20 + (slave.muscles * 0.1) && slave.physicalAge <= 3 && slave.dick !== 0) {
		return true;
	} else if (slave.dick >= 45 + (slave.muscles * 0.3) && slave.physicalAge <= 12) {
		return true;
	} else if (slave.dick >= 68 + (slave.muscles * 0.4)) {
		return true;
	}
	return false;
};

/**
 * @param {FC.HumanState} slave
 * @returns {boolean}
 */
globalThis.tooBigButt = function(slave) {
	if (!slave) {
		return null;
	} else if (slave.butt > 10 && slave.physicalAge <= 3) {
		return true;
	} else if (slave.butt > 14 && slave.physicalAge <= 12) {
		return true;
	}
	return false;
};

/**
 * @param {App.Entity.SlaveState} slave
 * @returns {boolean}
 */
globalThis.isVegetable = function(slave) {
	if (!slave) {
		return false;
	}
	return (slave.fetish === "mindbroken");
};

/**
 * Returns the hair color the slave was (or would be) born with.
 *
 * @param {App.Entity.SlaveState} slave
 * @returns {string}
 */
globalThis.getGeneticHairColor = function(slave) {
	if (slave.geneticQuirks.albinism === 2) {
		return slave.albinismOverride.hColor;
	}
	return slave.origHColor;
};

/**
 * Returns the skin color the slave was (or would be) born with.
 *
 * @param {App.Entity.SlaveState} slave
 * @returns {string}
 */
globalThis.getGeneticSkinColor = function(slave) {
	if (slave.geneticQuirks.albinism === 2) {
		return slave.albinismOverride.skin;
	}
	return slave.origSkin;
};

/**
 *
 * @param {App.Entity.SlaveState} slave
 * @returns {boolean}
 */
globalThis.canBeReceptrix = function(slave) {
	return (
		(slave.ovaries > 0 || slave.mpreg > 0) &&
		isSlaveAvailable(slave) &&
		slave.preg >= 0 &&
		slave.preg < 4 &&
		slave.pregWeek >= 0 &&
		slave.pubertyXX === 1 &&
		slave.pregType < 12 &&
		slave.bellyImplant === -1 &&
		slave.broodmother === 0 &&
		slave.inflation <= 2 &&
		slave.physicalAge < 70
	);
};

/**
 * @param {FC.HumanState} slave
 * @returns {string}
 */
globalThis.milkFlavor = function(slave) {
	if (slave.milkFlavor === "none") {
		return ``;
	}
	return `${slave.milkFlavor} flavored `;
};

globalThis.canBeDeflowered = function(slave) {
	return (slave.vagina === 0 && canDoVaginal(slave)) || (slave.anus === 0 && canDoAnal(slave));
};


code:
/* config.history.tracking = false;*/
// State.expired.disable;

globalThis.peacekeepersCanBeEstablished = function() {
	return V.invasionVictory > 0 && App.Events.effectiveWeek() > 48;
};

/**
 * @param {number} x
 * @param {number} minValue
 * @param {number} maxValue
 * @param {number} [defaultValue=0]
 * @returns {number}
 */
globalThis.variableAsNumber = function(x, minValue, maxValue, defaultValue = 0) {
	x = Number(x);
	if (isNaN(x)) {
		return defaultValue;
	}
	if (x < minValue) { // Works even if minValue is undefined.
		return minValue;
	}
	if (x > maxValue) { // Works even if maxValue is undefined.
		return maxValue;
	}
	return x;
};

if (typeof interpolate === "undefined") {
	globalThis.interpolate = function(x0, y0, x1, y1, x) {
		if (x <= x0) {
			return y0;
		} else if (x >= x1) {
			return y1;
		} else {
			return (x - x0) * ((y1 - y0) / (x1 - x0)) + y0;
		}
	};
}

/**
 * @param {any[]} arr
 * @param {any} val
 * @returns {any[]}
 */
globalThis.removeFromArray = function(arr, val) {
	for (let i = 0; i < arr.length; i++) {
		if (val === arr[i]) {
			return arr.splice(i, 1);
		}
	}
	return null;
};

/**
 * @param {any[]} arr
 * @param {any} callback
 * @param {any} thisArg
 * @returns {Array}
 */
globalThis.filterInPlace = function(arr, callback, thisArg) {
	let j = 0;

	arr.forEach(function(e, i) {
		if (callback.call(thisArg, e, i, arr)) {
			arr[j++] = e;
		}
	});

	arr.length = j;
	return arr;
};

/** pregmod: are slave2's sperm compatible with slave1's eggs?
 * @param {FC.HumanState} slave1
 * @param {FC.HumanState|FC.AnimalState} slave2
 * @returns {boolean}
 */
globalThis.canBreed = function(slave1, slave2) {
	if (!slave1 || !slave2) {
		return null;
	}
	return (slave1.eggType === slave2.ballType);
};

/** assuming slave1 is fertile, could slave2 impregnate slave1?
 * slave2 must have dick and balls with compatible sperm;
 * both slaves must not be in chastity; slave2 need not achieve erection
 * @param {FC.HumanState} slave1
 * @param {FC.HumanState} slave2
 * @returns {boolean}
 */
globalThis.canImpreg = function(slave1, slave2) {
	if (!slave1 || !slave2) {
		return null;
	} else if (slave2.dick < 1) {
		return false;
	} else if (slave2.balls < 1) {
		return false;
	} else if (slave2.chastityPenis === 1) {
		return false;
	} else if (slave2.pubertyXY === 0) {
		/* pregmod start */
		return false;
	} else if (slave2.vasectomy === 1) {
		return false;
	} else if (!canBreed(slave1, slave2)) {
		return false; /* pregmod end */
	} else if (!canGetPregnant(slave1)) {
		/* includes chastity checks */
		return false;
	} else {
		return true;
	}
};

/** assuming slave1 is fertile, could slave2 impregnate slave1?
 * slave2 must have balls with compatible sperm;
 * a special function intended for tribbing scenes;
 * @param {FC.HumanState} slave1
 * @param {FC.HumanState} slave2
 * @returns {boolean}
 */
globalThis.canFemImpreg = function(slave1, slave2) {
	if (!slave1 || !slave2) {
		return null;
	} else if (slave2.balls < 1) {
		return false;
	} else if (slave2.chastityVagina === 1) {
		return false;
	} else if (slave2.pubertyXY === 0) {
		/* pregmod start */
		return false;
	} else if (slave2.vasectomy === 1) {
		return false;
	} else if (!canBreed(slave1, slave2)) {
		return false; /* pregmod end */
	} else if (!canGetPregnant(slave1)) {
		/* includes chastity checks */
		return false;
	} else {
		return true;
	}
};

/**
 * @param {FC.HumanState} slave
 * @returns {number}
 */
globalThis.milkAmount = function(slave) {
	let milk;
	let calcs = slave.boobs - slave.boobsImplant - slave.boobsMilk;
	if (calcs > 40000) {
		milk = (158 + ((calcs - 40000) / 600));
	} else if (calcs > 25000) {
		milk = (128 + ((calcs - 25000) / 500));
	} else if (calcs > 10000) {
		milk = (78 + ((calcs - 10000) / 300));
	} else if (calcs > 5000) {
		milk = (53 + ((calcs - 5000) / 200));
	} else if (calcs > 2000) {
		milk = (29 + ((calcs - 2000) / 125));
	} else if (calcs > 800) {
		milk = (16 + ((calcs - 800) / 80));
	} else {
		milk = (8 + ((calcs - 400) / 50));
	}
	if (slave.boobsImplant > 0) {
		milk *= Math.max(0.25, (1 - (slave.boobsImplant / slave.boobs)));
	}
	if (slave.lactation === 2) {
		milk *= 1.2;
	}
	milk += (milk * ((slave.devotion - 50) / 200));
	calcs = (slave.hormoneBalance / 50);
	if (slave.balls !== 0 && calcs > -2) {
		calcs -= 1;
	} else if (slave.ovaries !== 1 && calcs < 2) {
		calcs += 1;
	}
	milk *= (1 + (calcs * 0.1));
	milk *= (1 + (slave.preg / 100));
	milk *= (1 + (slave.health.condition / 50));
	milk *= (1 + (slave.weight / 500));
	milk *= (1 + (slave.lactationAdaptation / 500));
	milk += (slave.boobsMilk / 100);
	milk *= healthPenalty(slave);
	milk = Math.trunc(milk);
	milk = Math.clamp(milk, 1, 1000000000000000000);
	return milk;
};

/**
 * The amount of cum a slave produces in deciliters
 * @param {FC.HumanState} slave
 * @returns {number}
 */
globalThis.cumAmount = function(slave) {
	let cum = 0;
	if (slave.drugs === "testicle enhancement") {
		cum = ((slave.balls * 3.5) + 1);
	} else if (slave.drugs === "hyper testicle enhancement") {
		cum = ((slave.balls * 5) + 1);
	} else {
		cum = ((slave.balls * 2.5) + 1);
	}
	if (slave.ballType === "sterile") {
		cum *= 0.8;
	}
	if (slave.diet === "cum production") {
		cum *= 1.2;
	}
	const calcs = (slave.hormoneBalance / 50);
	cum *= (1 - (calcs * 0.1));
	if (slave.scrotum === 0) {
		cum *= 0.8;
	}
	if (slave.prostate === 0) {
		cum *= 0.2; // being generous here
	} else if (slave.prostate === 2) {
		cum *= 1.2;
	} else if (slave.prostate === 3) {
		cum *= 1.5;
	}
	if (slave.devotion > 50) {
		cum += (cum * (slave.devotion / 100));
	} else if (slave.devotion < -50) {
		cum += (cum * (slave.devotion / 100));
	}
	if (slave.health.condition > 50) {
		cum += (cum * (slave.health.condition / 50));
	} else if (slave.health.condition < -50) {
		cum += (cum * (slave.health.condition / 50));
	}
	cum *= healthPenalty(slave);
	cum = Math.trunc(cum);
	cum = Math.clamp(cum, 1, 1_000_000_000_000_000);
	return cum;
};

/**
 * The amount of fem cum a slave produces in deciliters
 * @param {FC.HumanState} slave
 * @returns {number}
 */
globalThis.girlCumAmount = function(slave) {
	let fluid = ((slave.prostate * (slave.energy / 5)) + 1);
	if (slave.vagina >= 0) {
		if (slave.vaginaLube === 2) {
			fluid *= 1.3;
		} else if (slave.vaginaLube === 1) {
			fluid *= 1.1;
		}
	}
	if (slave.energy > 10) {
		if (slave.health.condition > 50) {
			fluid *= (slave.health.condition / 50);
		} else if (slave.health.condition < -50) {
			fluid *= (1 + (slave.health.condition / 50));
		}
	}
	fluid = Math.trunc(fluid);
	if (fluid < 1) {
		fluid = 1;
	}
	return fluid;
};

/**
 * @param {string} text
 * @returns {string}
 */
globalThis.lispReplace = function(text) {
	text = text.replace(/Sh/g, "Th");
	text = text.replace(/SS/g, "Th");
	text = text.replace(/Ss/g, "Th");
	text = text.replace(/sS/g, "Th");
	text = text.replace(/S/g, "Th");
	text = text.replace(/aX/g, "aKTH");
	text = text.replace(/eX/g, "eKTH");
	text = text.replace(/iX/g, "iKTH");
	text = text.replace(/oX/g, "oKTH");
	text = text.replace(/uX/g, "uKTH");
	text = text.replace(/yX/g, "yKTH");
	text = text.replace(/AX/g, "AKTH");
	text = text.replace(/EX/g, "EKTH");
	text = text.replace(/IX/g, "IKTH");
	text = text.replace(/OX/g, "OKTH");
	text = text.replace(/UX/g, "UKTH");
	text = text.replace(/YX/g, "YKTH");
	text = text.replace(/Xa/g, "Tha");
	text = text.replace(/Xe/g, "The");
	text = text.replace(/Xi/g, "Thi");
	text = text.replace(/Xo/g, "Tho");
	text = text.replace(/Xu/g, "Thu");
	text = text.replace(/Xy/g, "Thy");
	text = text.replace(/XA/g, "THA");
	text = text.replace(/XE/g, "THE");
	text = text.replace(/XI/g, "THI");
	text = text.replace(/XO/g, "THO");
	text = text.replace(/XU/g, "THU");
	text = text.replace(/XY/g, "THY");
	text = text.replace(/X/g, "EKTH");
	text = text.replace(/zZ/g, "Th");
	text = text.replace(/Zz/g, "Th");
	text = text.replace(/ZZ/g, "TH");
	text = text.replace(/Z/g, "Th");
	text = text.replace(/Cia/g, "Tha");
	text = text.replace(/Ci/g, "Thi");
	text = text.replace(/Ce/g, "The");
	text = text.replace(/Cy/g, "Thy");
	text = text.replace(/CIA/g, "THA");
	text = text.replace(/CI/g, "THI");
	text = text.replace(/CE/g, "THE");
	text = text.replace(/CY/g, "THY");
	text = text.replace(/ss/g, "th");
	text = text.replace(/sh/g, "th");
	text = text.replace(/s/g, "th");
	text = text.replace(/zz/g, "th");
	text = text.replace(/z/g, "th");
	text = text.replace(/ax/g, "akth");
	text = text.replace(/ex/g, "ekth");
	text = text.replace(/ix/g, "ikth");
	text = text.replace(/ox/g, "okth");
	text = text.replace(/ux/g, "ukth");
	text = text.replace(/yx/g, "ykth");
	text = text.replace(/Ax/g, "Akth");
	text = text.replace(/Ex/g, "Ekth");
	text = text.replace(/Ix/g, "Ikth");
	text = text.replace(/Ox/g, "Okth");
	text = text.replace(/Ux/g, "Ukth");
	text = text.replace(/Yx/g, "Ykth");
	text = text.replace(/xa/g, "tha");
	text = text.replace(/xe/g, "the");
	text = text.replace(/xi/g, "thi");
	text = text.replace(/xo/g, "tho");
	text = text.replace(/xu/g, "thu");
	text = text.replace(/xy/g, "thy");
	text = text.replace(/xA/g, "thA");
	text = text.replace(/xE/g, "thE");
	text = text.replace(/xI/g, "thI");
	text = text.replace(/xO/g, "thO");
	text = text.replace(/xU/g, "thU");
	text = text.replace(/xY/g, "thY");
	text = text.replace(/x/g, "ekth");
	text = text.replace(/cia/g, "tha");
	text = text.replace(/ci/g, "thi");
	text = text.replace(/ce/g, "the");
	text = text.replace(/cy/g, "thy");
	text = text.replace(/cI/g, "thI");
	text = text.replace(/cE/g, "thE");
	text = text.replace(/cY/g, "thY");
	return text;
};

/**
 * @param {App.Entity.SlaveState} slave
 * @param {object} arcology
 * @returns {number}
 */
globalThis.repGainSacrifice = function(slave, arcology) {
	if (!slave || !arcology || arcology.FSAztecRevivalist === "unset" || arcology.FSAztecRevivalist <= 0) {
		return 0;
	}
	return Math.ceil(
		(Math.min(100, Math.pow(1.0926, V.week - slave.weekAcquired)) + slave.prestige * 30) * arcology.FSAztecRevivalist / 100 / ((V.slavesSacrificedThisWeek || 0) + 1));
};

/**
 * @param {App.Entity.SlaveState} slave
 * @returns {boolean}
 */
globalThis.bodyguardSuccessorEligible = function(slave) {
	if (!slave) {
		return false;
	}
	return (slave.devotion > 50 && slave.muscles >= 0 && slave.weight < 130 && slave.boobs < 8000 && slave.butt < 10 && slave.belly < 5000 && slave.balls < 10 && slave.dick < 10 && slave.preg < 20 && slave.fuckdoll === 0 && slave.fetish !== "mindbroken" && canWalk(slave) && canHold(slave) && canSee(slave) && canHear(slave));
};

/**
 * @param {any} obj
 * @returns {string}
 */
globalThis.toJson = function(obj) {
	let jsontext = JSON.stringify(obj);
	jsontext = jsontext.replace(/^{/, "");
	jsontext = jsontext.replace(/}$/, "");
	return jsontext;
};

/**
 * @param {App.Entity.SlaveState} slave
 * @returns {string}
 */
globalThis.nippleColor = function(slave) {
	if (skinToneLevel(slave.skin) < 8) {
		if (slave.preg > slave.pregData.normalBirth / 4 || (slave.counter.birthsTotal > 0 && slave.lactation > 0)) {
			return "brown";
		} else {
			return "pink";
		}
	} else if (skinToneLevel(slave.skin) < 14) {
		if (slave.preg > slave.pregData.normalBirth / 4 || (slave.counter.birthsTotal > 0 && slave.lactation > 0)) {
			return "dark brown";
		} else {
			return "pink";
		}
	} else if (skinToneLevel(slave.skin) > 20) {
		if (slave.preg > slave.pregData.normalBirth / 4 || (slave.counter.birthsTotal > 0 && slave.lactation > 0)) {
			return "black";
		} else {
			return "dark brown";
		}
	} else {
		if (slave.preg > slave.pregData.normalBirth / 4 || (slave.counter.birthsTotal > 0 && slave.lactation > 0)) {
			return "dark brown";
		} else {
			return "brown";
		}
	}
};

/**
 * @param {App.Entity.SlaveState} slave
 * @param {App.Entity.PlayerState} PC
 * @returns {number}
 */
globalThis.overpowerCheck = function(slave, PC) {
	let strength;

	if (V.arcologies[0].FSPhysicalIdealist !== "unset") {
		if (PC.title === 1) {
			strength = 130;
		} else {
			strength = 100;
		}
	} else if (PC.title === 1) {
		strength = 50;
	} else {
		strength = 30;
	}
	strength += (185 - slave.height);
	strength -= (PC.belly / 1000);
	strength += (PC.skill.warfare / 3);
	strength -= (PC.health.shortDamage);

	return strength;
};

/**
 * returns if arg2 can pick up and carry arg1
 * @param {FC.HumanState} liftee
 * @param {FC.HumanState} lifter
 * @returns {boolean}
 */
globalThis.canLift = function(liftee, lifter) {
	let sumWeight;

	sumWeight = liftee.height - lifter.height;
	sumWeight += liftee.weight / 2;
	sumWeight += liftee.boobs / 500;
	sumWeight += liftee.belly / 1000;
	sumWeight += liftee.butt / 2;
	if (liftee.hips > 0) {
		sumWeight += Math.pow(liftee.hips, 2);
	}
	if (liftee.dick >= 20) {
		sumWeight += Math.trunc(liftee.dick / 10);
	}
	if (liftee.balls >= 10) {
		sumWeight += Math.pow(Math.trunc(liftee.balls / 10), 3);
	}

	return 20 + lifter.muscles >= sumWeight && hasBothArms(lifter);
};

/**
 * returns array of IDs of all characters who impregnated slave
 * @param {FC.HumanState} slave
 * @param {boolean} [genepool=false]
 * @returns {number[]}
 */
globalThis.impregnatedBy = function(slave, genepool = false) {
	const IDArray = [];
	if (!Array.isArray(slave.womb)) {
		if (genepool) {
			slave.womb = [];
		} else {
			WombInit(slave);
		}
	}
	for (let i = 0; i < slave.womb.length; i++) {
		IDArray.push(slave.womb[i].fatherID);
	}
	return IDArray;
};

/**
 * returns true if mother was impregnated by father
 * @param {FC.HumanState} mother
 * @param {FC.HumanState} father
 * @param {boolean} [genepool=false]
 * @returns {boolean}
 */
globalThis.isImpregnatedBy = function(mother, father, genepool = false) {
	return impregnatedBy(mother, genepool).includes(father.ID);
};

/**
 * @param {App.Entity.SlaveState} slave
 */
globalThis.SoftenBehavioralFlaw = function(slave) {
	switch (slave.behavioralFlaw) {
		case "arrogant":
			slave.behavioralQuirk = "confident";
			break;
		case "bitchy":
			slave.behavioralQuirk = "cutting";
			break;
		case "odd":
			slave.behavioralQuirk = "funny";
			break;
		case "hates men":
			slave.behavioralQuirk = "adores women";
			break;
		case "hates women":
			slave.behavioralQuirk = "adores men";
			break;
		case "gluttonous":
			slave.behavioralQuirk = "fitness";
			break;
		case "anorexic":
			slave.behavioralQuirk = "insecure";
			break;
		case "devout":
			slave.behavioralQuirk = "sinful";
			break;
		case "liberated":
			slave.behavioralQuirk = "advocate";
			break;
	}
	slave.behavioralFlaw = "none";
};

/**
 * @param {App.Entity.SlaveState} slave
 */
globalThis.SoftenSexualFlaw = function(slave) {
	switch (slave.sexualFlaw) {
		case "hates oral":
			slave.sexualQuirk = "gagfuck queen";
			break;
		case "hates anal":
			slave.sexualQuirk = "painal queen";
			break;
		case "hates penetration":
			slave.sexualQuirk = "strugglefuck queen";
			break;
		case "shamefast":
			slave.sexualQuirk = "tease";
			break;
		case "idealistic":
			slave.sexualQuirk = "romantic";
			break;
		case "repressed":
			slave.sexualQuirk = "perverted";
			break;
		case "apathetic":
			slave.sexualQuirk = "caring";
			break;
		case "crude":
			slave.sexualQuirk = "unflinching";
			break;
		case "judgemental":
			slave.sexualQuirk = "size queen";
			break;
	}
	slave.sexualFlaw = "none";
};

/**
 * @param {object} PC
 */
globalThis.generatePlayerPronouns = function(PC) {
	if (PC.title === 0) {
		PC.pronoun = App.Data.Pronouns.Kind.female;
	} else {
		PC.pronoun = App.Data.Pronouns.Kind.male;
	}
};

/**
 * @param {number} nmbr1
 * @param {number} nmbr2
 */
globalThis.pregNumberName = function(nmbr1, nmbr2) {
	let pt = "";
	let p1 = nmbr1 % 10;
	let p2 = ((nmbr1 % 100) - (nmbr1 % 10)) / 10;
	let p3 = ((nmbr1 % 1000) - (nmbr1 % 100)) / 100;
	switch (nmbr1) {
		case 1:
			pt += "bab";
			break;
		case 2:
			pt += "twin";
			break;
		default:
			switch (nmbr1) {
				case 3:
					pt += "tri";
					break;
				case 4:
					pt += "quadru";
					break;
				case 5:
					pt += "quintu";
					break;
				case 6:
					pt += "sextu";
					break;
				case 7:
					pt += "septu";
					break;
				case 8:
					pt += "octu";
					break;
				case 9:
					pt += "nonu";
					break;
				default:
					switch (p1) {
						case 1:
							pt += "un";
							break;
						case 2:
							pt += "duo";
							break;
						case 3:
							pt += "tre";
							break;
						case 4:
							pt += "quattuor";
							break;
						case 5:
							pt += "quin";
							break;
						case 6:
							pt += "sex";
							break;
						case 7:
							pt += "septen";
							break;
						case 8:
							pt += "octo";
							break;
						case 9:
							pt += "novem";
							break;
					}
					switch (p2) {
						case 1:
							pt += "dec";
							break;
						case 2:
							pt += "vigint";
							break;
						case 3:
							pt += "trigint";
							break;
						case 4:
							pt += "quadragint";
							break;
						case 5:
							pt += "quinquagint";
							break;
						case 6:
							pt += "sexagint";
							break;
						case 7:
							pt += "septuagint";
							break;
						case 8:
							pt += "octogint";
							break;
						case 9:
							pt += "nonagint";
							break;
					}
					if (nmbr1 >= 100) {
						if (p2 !== 0) {
							pt += "i";
						}
						switch (p3) {
							case 1:
								pt += "centu";
								break;
							case 2:
								pt += "bicentu";
								break;
							case 3:
								pt += "tricentu";
								break;
							case 4:
								pt += "quadricentu";
								break;
							case 5:
								pt += "quincentu";
								break;
							case 6:
								pt += "sexcentu";
								break;
							case 7:
								pt += "septemcentu";
								break;
							case 8:
								pt += "octocentu";
								break;
							case 9:
								pt += "novemcentu";
								break;
						}
					} else {
						pt += "u";
					}
			}
			pt += "plet";
	}
	if (nmbr2 === 2) {
		if (nmbr1 === 1) {
			pt += "ie";
		}
		pt += "s";
	} else {
		if (nmbr1 === 1) {
			pt += "y";
		}
	}
	return pt;
};