code:
/** Update custom slave orders (customSlave/huskSlave).
 * @param {App.Entity.CustomSlaveOrder} customSlaveOrder
 */
App.Update.CustomSlaveOrder = function(customSlaveOrder) {
	if (!customSlaveOrder.hasOwnProperty("leg")) {
		if (jsDef(customSlaveOrder.amp) && customSlaveOrder.amp === 1) {
			customSlaveOrder.leg = {left: null, right: null};
		} else {
			customSlaveOrder.leg = {left: new App.Entity.LimbState(), right: new App.Entity.LimbState()};
		}
	}

	if (!customSlaveOrder.hasOwnProperty("arm")) {
		if (jsDef(customSlaveOrder.amp) && customSlaveOrder.amp === 1) {
			customSlaveOrder.arm = {left: null, right: null};
		} else {
			customSlaveOrder.arm = {left: new App.Entity.LimbState(), right: new App.Entity.LimbState()};
		}
	}

	if (customSlaveOrder.hasOwnProperty("amp")) {
		delete customSlaveOrder.amp;
	}

	if (!customSlaveOrder.hasOwnProperty("skill")) {
		customSlaveOrder.skill = {whore: 15, combat: 0};
	}

	if (customSlaveOrder.hasOwnProperty("whoreSkills")) {
		customSlaveOrder.skill.whore = customSlaveOrder.whoreSkills;
		delete customSlaveOrder.whoreSkills;
	}

	if (customSlaveOrder.hasOwnProperty("combatSkills")) {
		customSlaveOrder.skill.combat = customSlaveOrder.combatSkills;
		delete customSlaveOrder.combatSkills;
	}

	if (V.releaseID < 1059) {
		customSlaveOrder.eye = new App.Entity.EyeState();
		delete customSlaveOrder.eyes;
	}
};

repo_name: KnightBoulegard/fc

license: mit


code:
App.Update.Slave = function(slave, genepool = false) {
	slave.geneticQuirks = Object.assign({
		macromastia: 0, gigantomastia: 0, fertility: 0, hyperFertility: 0, superfetation: 0, gigantism: 0, dwarfism: 0, pFace: 0, uFace: 0, albinism: 0, heterochromia: 0, rearLipedema: 0, wellHung: 0, wGain: 0, wLoss: 0, mGain: 0, mLoss: 0, androgyny: 0, girlsOnly: 0
	}, slave.geneticQuirks);

	if (slave.earShape === undefined) { slave.earShape = "normal"; }
	if (slave.earT === undefined) { slave.earT = "none"; }
	if (slave.earTColor === undefined) { slave.earTColor = "hairless"; }
	if (slave.horn === undefined) { slave.horn = "none"; }
	if (slave.hornColor === undefined) { slave.hornColor = "none"; }
	if (slave.tail === undefined) { slave.tail = "none"; }
	if (slave.tailShape === undefined) { slave.tailShape = "none"; }
	if (slave.tailColor === undefined) { slave.tailColor = "none"; }
	if (slave.prostateImplant !== undefined) {
		if (slave.prostateImplant === 1) {
			slave.prostate = 3;
		}
		delete slave.prostateImplant;
	}
	if (slave.daughters === undefined) { slave.daughters = 0; }
	if (slave.sisters === undefined) { slave.sisters = 0; }
	if (slave.pregGenerator !== undefined) { delete slave.pregGenerator; }
	if (slave.pregAdaptation === undefined) {
		if (slave.physicalAge <= 3) {
			slave.pregAdaptation = 10;
		} else if (slave.physicalAge <= 12 || slave.genes === "XY") {
			slave.pregAdaptation = 20;
		} else if (slave.physicalAge <= 17) {
			slave.pregAdaptation = 30;
		} else {
			slave.pregAdaptation = 50;
		}
	}
	if (slave.pregKnown === undefined) {
		if (slave.preg > 0) {
			slave.pregKnown = 1;
		} else {
			slave.pregKnown = 0;
		}
	}
	if (slave.pregWeek === undefined) {
		if (slave.preg > 0) {
			slave.pregWeek = slave.preg;
		} else {
			slave.pregWeek = 0;
		}
	}
	if (slave.pubertyXX === undefined) {
		if (slave.physicalAge >= slave.pubertyAgeXX) {
			slave.pubertyXX = 1;
			slave.fertKnown = 1;
		} else {
			slave.pubertyXX = 0;
			slave.fertKnown = 0;
		}
	}
	if (slave.pubertyXY === undefined) {
		if (slave.physicalAge >= slave.pubertyAgeXY) {
			slave.pubertyXY = 1;
		} else {
			slave.pubertyXY = 0;
		}
	}
	if (slave.genetics === undefined) { slave.genetics = {}; }
	if (slave.geneMods === undefined) { slave.geneMods = {NCS: 0, rapidCellGrowth: 0}; }
	if (slave.inducedNCS !== undefined) {
		slave.geneMods.NCS = slave.inducedNCS;
		delete slave.inducedNCS;
	}
	if (slave.PCSlutContacts !== undefined) { delete slave.PCSlutContacts; }
	if (slave.wombImplant === undefined) { slave.wombImplant = "none"; }
	if (slave.superfetation !== undefined) { delete slave.superfetation; }
	if (slave.lactationDuration === undefined) {
		if (slave.lactation === 0) {
			slave.lactationDuration = 0;
		} else {
			slave.lactationDuration = 2;
		}
	}
	if (slave.induceLactation === undefined) { slave.induceLactation = 0; }
	if (slave.weightDirection === undefined) { slave.weightDirection = 0; }
	if (slave.clone === undefined) { slave.clone = 0; }
	if (slave.abortionTat === undefined) { slave.abortionTat = -1; }
	if (slave.birthsTat === undefined) { slave.birthsTat = -1; }
	if (slave.reservedChildren !== undefined) { delete slave.reservedChildren; }
	if (slave.origin !== undefined && slave.origin !== 0) { slave.origin = pronounReplacer(slave.origin); }
	if (slave.custom !== undefined) {
		if (slave.custom.desc !== undefined && slave.custom.desc !== "") {
			slave.custom.desc = pronounReplacer(slave.custom.desc);
		}
		if (slave.custom.tattoo !== undefined && slave.custom.tattoo !== "") {
			slave.custom.tattoo = pronounReplacer(slave.custom.tattoo);
		}
	}
	if (slave.prestigeDesc !== undefined && slave.prestigeDesc !== 0) { slave.prestigeDesc = pronounReplacer(slave.prestigeDesc); }
	if (slave.pornPrestigeDesc !== undefined && slave.pornPrestigeDesc !== 0) { // This must be defined first, hence the previous line.
		if (V.releaseID < 1050 && slave.prestigeDesc !== undefined && slave.prestigeDesc !== 0) { /* BC absolutely FUCKED this */
			const genre = App.Porn.getGenreByFameName(slave.pornFameType);
			if (jsDef(genre)) {
				if (slave.pornPrestige === 1) {
					slave.pornPrestigeDesc = `$He has a following in slave pornography. ${genre.prestigeDesc1}.`;
				} else if (slave.pornPrestige === 2) {
					slave.pornPrestigeDesc = `$He is well known from $his career in slave pornography. ${genre.prestigeDesc2}.`;
				} else if (slave.pornPrestige === 3) {
					slave.pornPrestigeDesc = `$He is world famous for $his career in slave pornography. ${genre.prestigeDesc3}.`;
				} else {
					slave.pornPrestigeDesc = 0;
				}
			} else {
				slave.pornPrestigeDesc = 0;
			}
		} else {
			slave.pornPrestigeDesc = pronounReplacer(slave.pornPrestigeDesc);
		}
	}

	if (slave.amp !== undefined) {
		if (slave.amp === 1) {
			slave.arm = {left: null, right: null};
			slave.leg = {left: null, right: null};
		} else {
			const newID = ((slave.amp * -1) + 1);
			slave.arm = {
				left: new App.Entity.LimbState(),
				right: new App.Entity.LimbState()
			};
			slave.leg = {
				left: new App.Entity.LimbState(),
				right: new App.Entity.LimbState()
			};
			slave.arm.left.type = newID;
			slave.arm.right.type = newID;
			slave.leg.left.type = newID;
			slave.leg.right.type = newID;
			/* no need to check partial amputation, since it is not possible to create prior to this */
		}
		delete slave.amp;
		delete slave.missingLegs;
		delete slave.missingArms;
	} else if (slave.arm === undefined) {
		slave.arm = {
			left: new App.Entity.LimbState(),
			right: new App.Entity.LimbState()
		};
		slave.leg = {
			left: new App.Entity.LimbState(),
			right: new App.Entity.LimbState()
		};
	}

	if (hasAnyProstheticLimbs(slave)) {
		slave.PLimb = 1;
		if (getLimbCount(slave, 6) > 0) {
			slave.PLimb = 2;
		}
	}

	if (slave.eyeball !== undefined) { delete slave.eyeball; }

	if (slave.auricle !== undefined) { delete slave.auricle; }

	if (slave.readyProsthetics === undefined) { slave.readyProsthetics = []; }
	if (slave.readyLimbs !== undefined) {
		for (let k = 0; k < slave.readyLimbs.length; k++) {
			switch (slave.readyLimbs[k].type) {
				case -1:
					addProsthetic(slave, "basicL");
					break;
				case -2:
					addProsthetic(slave, "sexL");
					break;
				case -3:
					addProsthetic(slave, "beautyL");
					break;
				case -4:
					addProsthetic(slave, "combatL");
					break;
				case -5:
					addProsthetic(slave, "cyberneticL");
					break;
			}
		}
	}

	if (V.releaseID < 1052) {
		const prosthetics = slave.readyProsthetics;
		slave.readyProsthetics = [];
		for (const p of prosthetics) {
			addProsthetic(slave, p.id);
		}
	}

	if (V.releaseID < 1058) {
		if (slave.albinism === 2) {
			let temp;
			temp = slave.origSkin;
			slave.origSkin = slave.albinismOverride.skin;
			slave.albinismOverride.skin = temp;
			temp = slave.origEye;
			slave.origEye = slave.albinismOverride.eyeColor;
			slave.albinismOverride.eyeColor = temp;
			temp = slave.origHColor;
			slave.origHColor = slave.albinismOverride.hColor;
			slave.albinismOverride.hColor = temp;
		}
	}

	if (V.releaseID < 1059 || !(slave.eye)) {
		slave.eye = new App.Entity.EyeState();
		const origEye = slave.origEye || "brown";
		setGeneticEyeColor(slave, origEye);
		if (slave.eyes === -4) {
			eyeSurgery(slave, "both", "remove");
		} else {
			if (slave.eyesImplant === 1) {
				eyeSurgery(slave, "both", "cybernetic");
			}
			if (slave.eyes === -3) {
				eyeSurgery(slave, "both", "glass");
			} else if (slave.eyes === -2) {
				eyeSurgery(slave, "both", "blind");
			} else if (slave.eyes === -1) {
				eyeSurgery(slave, "both", "blur");
			}
			const eyeColor = slave.eyeColor || "brown";
			const pupil = slave.pupil || "circular";
			const sclerae = slave.sclerae || "white";
			setEyeColorFull(slave, eyeColor, pupil, sclerae, "both");
			if (typeof slave.geneticQuirks.heterochromia === "string") {
				setEyeColor(slave.geneticQuirks.heterochromia, "left");
			}
		}
	}

	if (slave.eyes !== undefined) { delete slave.eyes; }
	if (slave.eyeColor !== undefined) { delete slave.eyeColor; }
	if (slave.eyesImplant !== undefined) { delete slave.eyesImplant; }
	if (slave.origEye !== undefined) { delete slave.origEye; }
	if (slave.pupil !== undefined) { delete slave.pupil; }
	if (slave.sclerae !== undefined) { delete slave.sclerae; }

	if (slave.origin === "Shortly after birth, $he was sealed in an aging tank until $he was of age. $He knows only of the terror that awaits $him should $he not obey $his master.") {
		slave.tankBaby = 2;
	} else if (slave.origin === "Shortly after birth, $he was sealed in an aging tank until $he was of age. $He knows nothing of the world outside of what the tank imprinted $him with.") {
		slave.tankBaby = 1;
	} else if (slave.tankBaby === undefined) {
		slave.tankBaby = 0;
	}

	if (slave.origin === "$He sold $himself into slavery to feed $himself and $his growing brood.") {
		if (slave.pregAdaptation < 750) {
			slave.pregAdaptation = 750;
		}
	}

	let backwardsCompatibility;
	if (slave.rivalry !== 0) {
		backwardsCompatibility = V.slaveIndices[slave.rivalryTarget];
		if (backwardsCompatibility === undefined) {
			slave.rivalry = 0;
			slave.rivalryTarget = 0;
		}
	}
	if (slave.relationship > 0) {
		backwardsCompatibility = V.slaveIndices[slave.relationshipTarget];
		if (backwardsCompatibility === undefined) {
			slave.relationship = 0;
			slave.relationshipTarget = 0;
		}
	}

	if (slave.race === "surgically altered to look amerindian") {
		slave.race = "amerindian";
	} else if (slave.race === "surgically altered to look asian") {
		slave.race = "asian";
	} else if (slave.race === "surgically altered to look black") {
		slave.race = "black";
	} else if (slave.race === "surgically altered to look indo-aryan") {
		slave.race = "indo-aryan";
	} else if (slave.race === "surgically altered to look latina") {
		slave.race = "latina";
	} else if (slave.race === "surgically altered to look malay") {
		slave.race = "malay";
	} else if (slave.race === "surgically altered to look middle eastern") {
		slave.race = "middle eastern";
	} else if (slave.race === "surgically altered to look mixed race" || slave.race === "surgically altered to look mixed") {
		slave.race = "mixed race";
	} else if (slave.race === "surgically altered to look pacific islander" || slave.race === "surgically altered to look like a pacific islander") {
		slave.race = "pacific islander";
	} else if (slave.race === "surgically altered to look southern european" || slave.race === "southern European") {
		slave.race = "southern european";
	} else if (slave.race === "surgically altered to look semitic") {
		slave.race = "semitic";
	} else if (slave.race === "surgically altered to look white") {
		slave.race = "white";
	}

	/* eslint-disable camelcase */
	if (slave.override_Race === undefined) { slave.override_Race = 0; }
	if (slave.override_Skin === undefined) { slave.override_Skin = 0; }
	if (slave.override_Eye_Color === undefined) { slave.override_Eye_Color = 0; }
	if (slave.override_H_Color === undefined) { slave.override_H_Color = 0; }
	if (slave.override_Pubic_H_Color === undefined) { slave.override_Pubic_H_Color = 0; }
	if (slave.override_Arm_H_Color === undefined) { slave.override_Arm_H_Color = 0; }
	/* eslint-enable camelcase */

	switch (slave.skin) {
		case "red dyed":
			slave.skin = "dyed red";
			break;
		case "green dyed":
			slave.skin = "dyed green";
			break;
		case "blue dyed":
			slave.skin = "dyed blue";
			break;
		case "tanned":
			slave.skin = "tan";
			break;
		case "lightened":
			slave.skin = "light";
			break;
		case "bronzed":
			slave.skin = "bronze";
			break;
		case "darkened":
			slave.skin = "dark";
			break;
	}

	switch (slave.origSkin) {
		case "blue dyed":
		case "dyed blue":
		case "dyed green":
		case "dyed red":
		case "green dyed":
		case "red dyed":
			slave.origSkin = randomRaceSkin(slave.origRace);
			break;
		case "tanned":
			slave.origSkin = "tan";
			break;
		case "lightened":
			slave.origSkin = "light";
			break;
		case "bronzed":
			slave.origSkin = "bronze";
			break;
		case "darkened":
			slave.origSkin = "dark";
			break;
	}

	if (slave.markings === "heavily") {
		slave.markings = "heavily freckled";
	} else if (slave.markings === "beauty") {
		slave.markings = "beauty mark";
	}

	if (slave.underArmHColor === "slave.hColor") {
		slave.underArmHColor === slave.hColor;
	}
	if (slave.eyebrowHColor === "slave.hColor") {
		slave.eyebrowHColor === slave.hColor;
	}

	if (slave.genes === undefined) {
		if (slave.ovaries === 1) {
			slave.genes = "XX";
		} else {
			slave.genes = "XY";
		}
	}

	if (V.releaseID < 1000) {
		if (slave.face === -3) {
			slave.face = -100;
		} else if (slave.face === -2) {
			slave.face = -50;
		} else if (slave.face === -1) {
			slave.face = -20;
		} else if (slave.face === 0) {
			slave.face = 0;
		} else if (slave.face === 1) {
			slave.face = 20;
		} else if (slave.face === 2) {
			slave.face = 50;
		} else {
			slave.face = 100;
		}
	}
	if (V.releaseID < 1031) {
		if (slave.intelligence === -3) {
			slave.intelligence = -100;
		} else if (slave.intelligence === -2) {
			slave.intelligence = -60;
		} else if (slave.intelligence === -1) {
			slave.intelligence = -30;
		} else if (slave.intelligence === 0) {
			slave.intelligence = 0;
		} else if (slave.intelligence === 1) {
			slave.intelligence = 30;
		} else if (slave.intelligence === 2) {
			slave.intelligence = 60;
		} else {
			slave.intelligence = 99;
		}
		if (slave.intelligenceImplant === 1) {
			slave.intelligenceImplant = 30;
		}
	}

	if (slave.teeth === 0) {
		slave.teeth = "normal";
	} else if (slave.teeth === "straightening") {
		slave.teeth = "straightening braces";
	} else if (slave.teeth === "cosmetic") {
		slave.teeth = "cosmetic braces";
	}

	if (slave.areolaeShape === undefined) {
		if (slave.areolae === 4) {
			slave.areolaeShape = "heart";
			slave.areolae = 3;
		} else if (slave.areolae === 5) {
			slave.areolaeShape = "star";
			slave.areolae = 3;
		} else {
			slave.areolaeShape = "circle";
		}
	}

	if (V.releaseID < 1061) {
		if (slave.boobsImplantType === 1) {
			slave.boobsImplantType = "string";
		} else if (slave.boobsImplant >= 10000) {
			slave.boobsImplantType = "hyper fillable";
		} else if (slave.boobsImplant >= 2000) {
			slave.boobsImplantType = "advanced fillable";
		} else if (slave.boobsImplant >= 800) {
			slave.boobsImplantType = "fillable";
		} else if (slave.boobsImplant > 0) {
			slave.boobsImplantType = "normal";
		} else {
			slave.boobsImplantType = "none";
		}
		if (slave.buttImplantType === 1) {
			slave.buttImplantType = "string";
		} else if (slave.buttImplant > 7) {
			slave.buttImplantType = "hyper fillable";
		} else if (slave.buttImplant >= 5) {
			slave.buttImplantType = "advanced fillable";
		} else if (slave.buttImplant >= 3) {
			slave.buttImplantType = "fillable";
		} else if (slave.buttImplant > 0) {
			slave.buttImplantType = "normal";
		} else {
			slave.buttImplantType = "none";
		}
	}

	if (V.releaseID < 1059) {
		if (slave.eyeColor === undefined) {
			slave.eyeColor = slave.eyes;
			slave.eyes = 1;
			if (slave.eyeColor === undefined) {
				slave.eyeColor = "brown";
			}
		}

		if (slave.pupil === undefined) {
			if (slave.eyeColor === "catlike") {
				slave.pupil = "catlike";
				slave.eyeColor = "brown";
			} else if (slave.eyeColor === "serpent-like") {
				slave.pupil = "serpent-like";
				slave.eyeColor = "brown";
			} else if (slave.eyeColor === "devilish") {
				slave.pupil = "devilish";
				slave.eyeColor = "brown";
			} else if (slave.eyeColor === "demonic") {
				slave.pupil = "demonic";
				slave.eyeColor = "brown";
			} else if (slave.eyeColor === "hypnotic") {
				slave.pupil = "hypnotic";
				slave.eyeColor = "brown";
			} else if (slave.eyeColor === "heart-shaped") {
				slave.pupil = "heart-shaped";
				slave.eyeColor = "brown";
			} else if (slave.eyeColor === "wide-eyed") {
				slave.pupil = "wide-eyed";
				slave.eyeColor = "brown";
			} else if (slave.eyeColor === "almond-shaped") {
				slave.pupil = "almond-shaped";
				slave.eyeColor = "brown";
			} else if (slave.eyeColor === "bright") {
				slave.pupil = "bright";
				slave.eyeColor = "brown";
			} else if (slave.eyeColor === "teary") {
				slave.pupil = "teary";
				slave.eyeColor = "brown";
			} else if (slave.eyeColor === "vacant") {
				slave.pupil = "vacant";
				slave.eyeColor = "brown";
			} else {
				slave.pupil = "circular";
			}
		}
	}

	if (slave.pitkills !== undefined) { delete slave.pitkills; }
	if (slave.penetrationCount !== undefined) { delete slave.penetrationCount; }
	if (slave.oralTotal !== undefined) { delete slave.oralTotal; }
	if (slave.vaginaCount !== undefined) { delete slave.vaginaCount; }

	if (((V.ver.startsWith("0.6") && !V.ver.startsWith("10.6")) || (V.ver.startsWith("0.7")) || (V.ver.startsWith("0.8"))) && (!V.ver.startsWith("0.8.9")) && (!V.ver.startsWith("0.8.10")) && (!V.ver.startsWith("0.8.11")) && (!V.ver.startsWith("0.8.12"))) {
		if (slave.attrXX === 2) {
			slave.attrXX = 90;
		} else if (slave.attrXX === 1) {
			slave.attrXX = 70;
		} else if (slave.attrXX === 0) {
			slave.attrXX = 50;
		} else if (slave.attrXX === -1) {
			slave.attrXX = 30;
		} else {
			slave.attrXX = 10;
		}
		if (slave.attrXY === 2) {
			slave.attrXY = 90;
		} else if (slave.attrXY === 1) {
			slave.attrXY = 70;
		} else if (slave.attrXY === 0) {
			slave.attrXY = 50;
		} else if (slave.attrXY === -1) {
			slave.attrXY = 30;
		} else {
			slave.attrXY = 10;
		}
	}

	if ((V.ver.startsWith("0.6") && !V.ver.startsWith("10.6")) || (V.ver.startsWith("0.7")) || (V.ver.startsWith("0.8"))) {
		if (slave.health <= -9) {
			slave.health = -90;
		} else if (slave.health <= -7) {
			slave.health = jsRandom(-89, -70);
		} else if (slave.health <= -5) {
			slave.health = jsRandom(-69, -50);
		} else if (slave.health <= -3) {
			slave.health = jsRandom(-49, -30);
		} else if (slave.health <= -1) {
			slave.health = jsRandom(-29, -10);
		} else if (slave.health <= 1) {
			slave.health = jsRandom(-9, 10);
		} else if (slave.health <= 3) {
			slave.health = jsRandom(11, 30);
		} else if (slave.health <= 5) {
			slave.health = jsRandom(31, 50);
		} else if (slave.health <= 7) {
			slave.health = jsRandom(51, 70);
		} else if (slave.health <= 8) {
			slave.health = jsRandom(71, 80);
		} else if (slave.health <= 9) {
			slave.health = jsRandom(81, 90);
		} else if (slave.health <= 10) {
			slave.health = jsRandom(91, 100);
		} else if (slave.health <= 15) {
			slave.health = jsRandom(101, 150);
		} else if (slave.health <= 20) {
			slave.health = jsRandom(151, 200);
		} else if (slave.health >= 50) {
			slave.health = 500;
		} else if (slave.health > 20) {
			slave.health = 205;
		}

		slave.devotion = (slave.devotion * 5);
		slave.oldDevotion = (slave.oldDevotion * 5);
		slave.trust = (slave.trust * 5);
		slave.oldTrust = (slave.oldTrust * 5);

		if (slave.fetishStrength === 0) {
			slave.fetishStrength = jsRandom(0, 60);
		} else if (slave.fetishStrength === 1) {
			slave.fetishStrength = jsRandom(61, 80);
		} else if (slave.fetishStrength === 2) {
			slave.fetishStrength = jsRandom(96, 100);
		}

		if (slave.weight === -3) {
			slave.weight = jsRandom(-110, -96);
		} else if (slave.weight === -2) {
			slave.weight = jsRandom(-95, -31);
		} else if (slave.weight === -1) {
			slave.weight = jsRandom(-30, -11);
		} else if (slave.weight === 0) {
			slave.weight = jsRandom(-11, 10);
		} else if (slave.weight === 1) {
			slave.weight = jsRandom(11, 30);
		} else if (slave.weight === 2) {
			slave.weight = jsRandom(31, 95);
		} else if (slave.weight === 3) {
			slave.weight = jsRandom(96, 110);
		}

		if (slave.lips !== 0) {
			if (slave.lips === 3) {
				slave.lips = 85;
			} else if (slave.lips === 2) {
				slave.lips = 55;
			} else if (slave.lips === 1) {
				slave.lips = 35;
			}
		}
	}

	if (((V.ver.startsWith("0.6") && !V.ver.startsWith("10.6")) || (V.ver.startsWith("0.7")) || (V.ver.startsWith("0.8")) || (V.ver.startsWith("0.9"))) && (!V.ver.startsWith("0.9.5")) && (!V.ver.startsWith("0.9.6")) && (!V.ver.startsWith("0.9.7")) && (!V.ver.startsWith("0.9.8")) && (!V.ver.startsWith("0.9.9")) && (!V.ver.startsWith("0.9.10"))) {
		if (slave.skill === undefined) {
			if (slave.oralSkill > 0) {
				if (slave.oralSkill === 3) {
					slave.oralSkill = 100;
				} else if (slave.oralSkill === 2) {
					slave.oralSkill = 65;
				} else {
					slave.oralSkill = 35;
				}
			}
			if (slave.vaginalSkill > 0) {
				if (slave.vaginalSkill === 3) {
					slave.vaginalSkill = 100;
				} else if (slave.vaginalSkill === 2) {
					slave.vaginalSkill = 65;
				} else {
					slave.vaginalSkill = 35;
				}
			}
			if (slave.analSkill > 0) {
				if (slave.analSkill === 3) {
					slave.analSkill = 100;
				} else if (slave.analSkill === 2) {
					slave.analSkill = 65;
				} else {
					slave.analSkill = 35;
				}
			}
			if (slave.whoreSkill > 0) {
				if (slave.whoreSkill === 3) {
					slave.whoreSkill = 100;
				} else if (slave.whoreSkill === 2) {
					slave.whoreSkill = 65;
				} else {
					slave.whoreSkill = 35;
				}
			}
			if (slave.entertainSkill > 0) {
				if (slave.entertainSkill === 3) {
					slave.entertainSkill = 100;
				} else if (slave.entertainSkill === 2) {
					slave.entertainSkill = 65;
				} else {
					slave.entertainSkill = 35;
				}
			}
		}

		if (V.ver !== "0.9.4") {
			slave.aphrodisiacs = 0;
			if (slave.drugs === "curatives") {
				slave.curatives = 2;
				slave.drugs = "no drugs";
			} else if (slave.drugs === "preventatives") {
				slave.curatives = 1;
				slave.drugs = "no drugs";
			} else if (slave.drugs === "aphrodisiacs") {
				slave.aphrodisiacs = 1;
				slave.drugs = "no drugs";
			} else if (slave.drugs === "extreme aphrodisiacs") {
				slave.aphrodisiacs = 2;
				slave.drugs = "no drugs";
			}
			if (slave.muscles >= 3) {
				slave.muscles = 100;
			} else if (slave.muscles >= 2) {
				slave.muscles = 50;
			} else if (slave.muscles >= 1) {
				slave.muscles = 20;
			} else {
				slave.muscles = 0;
			}
		}
	}

	switch (slave.boobsTat) {
		case "floral designs":
			slave.boobsTat = "flowers";
			break;
		case "demeaning inscriptions":
			slave.boobsTat = "rude words";
			break;
		case "lewd scenes":
			slave.boobsTat = "scenes";
			break;
		case "degrading language":
			slave.boobsTat = "degradation";
			break;
		case "slutty advertisements":
			slave.boobsTat = "advertisements";
			break;
	}

	switch (slave.buttTat) {
		case "floral designs":
			slave.buttTat = "flowers";
			break;
		case "demeaning inscriptions":
			slave.buttTat = "rude words";
			break;
		case "lewd scenes":
			slave.buttTat = "scenes";
			break;
		case "degrading language":
			slave.buttTat = "degradation";
			break;
		case "slutty advertisements":
			slave.buttTat = "advertisements";
			break;
	}

	switch (slave.vaginaTat) {
		case "floral designs":
			slave.vaginaTat = "flowers";
			break;
		case "demeaning inscriptions":
			slave.vaginaTat = "rude words";
			break;
		case "lewd scenes":
			slave.vaginaTat = "scenes";
			break;
		case "degrading language":
			slave.vaginaTat = "degradation";
			break;
		case "slutty advertisements":
			slave.vaginaTat = "advertisements";
			break;
	}

	switch (slave.dickTat) {
		case "floral designs":
			slave.dickTat = "flowers";
			break;
		case "demeaning inscriptions":
			slave.dickTat = "rude words";
			break;
		case "lewd scenes":
			slave.dickTat = "scenes";
			break;
		case "degrading language":
			slave.dickTat = "degradation";
			break;
		case "slutty advertisements":
			slave.dickTat = "advertisements";
			break;
	}

	switch (slave.anusTat) {
		case "floral designs":
			slave.anusTat = "flowers";
			break;
		case "demeaning inscriptions":
			slave.anusTat = "rude words";
			break;
		case "lewd scenes":
			slave.anusTat = "scenes";
			break;
		case "degrading language":
			slave.anusTat = "degradation";
			break;
		case "slutty advertisements":
			slave.anusTat = "advertisements";
			break;
	}

	switch (slave.backTat) {
		case "floral designs":
			slave.backTat = "flowers";
			break;
		case "demeaning inscriptions":
			slave.backTat = "rude words";
			break;
		case "lewd scenes":
			slave.backTat = "scenes";
			break;
		case "degrading language":
			slave.backTat = "degradation";
			break;
		case "slutty advertisements":
			slave.backTat = "advertisements";
			break;
	}

	switch (slave.shouldersTat) {
		case "floral designs":
			slave.shouldersTat = "flowers";
			break;
		case "demeaning inscriptions":
			slave.shouldersTat = "rude words";
			break;
		case "lewd scenes":
			slave.shouldersTat = "scenes";
			break;
		case "degrading language":
			slave.shouldersTat = "degradation";
			break;
		case "slutty advertisements":
			slave.shouldersTat = "advertisements";
			break;
	}

	switch (slave.armsTat) {
		case "floral designs":
			slave.armsTat = "flowers";
			break;
		case "demeaning inscriptions":
			slave.armsTat = "rude words";
			break;
		case "lewd scenes":
			slave.armsTat = "scenes";
			break;
		case "degrading language":
			slave.armsTat = "degradation";
			break;
		case "slutty advertisements":
			slave.armsTat = "advertisements";
			break;
	}

	switch (slave.legsTat) {
		case "floral designs":
			slave.legsTat = "flowers";
			break;
		case "demeaning inscriptions":
			slave.legsTat = "rude words";
			break;
		case "lewd scenes":
			slave.legsTat = "scenes";
			break;
		case "degrading language":
			slave.legsTat = "degradation";
			break;
		case "slutty advertisements":
			slave.legsTat = "advertisements";
			break;
	}

	switch (slave.stampTat) {
		case "floral designs":
			slave.stampTat = "flowers";
			break;
		case "demeaning inscriptions":
			slave.stampTat = "rude words";
			break;
		case "lewd scenes":
			slave.stampTat = "scenes";
			break;
		case "degrading language":
			slave.stampTat = "degradation";
			break;
		case "slutty advertisements":
			slave.stampTat = "advertisements";
			break;
	}

	switch (slave.lipsTat) {
		case "floral designs":
			slave.lipsTat = "flowers";
			break;
		case "demeaning inscriptions":
			slave.lipsTat = "rude words";
			break;
		case "lewd scenes":
			slave.lipsTat = "scenes";
			break;
		case "degrading language":
			slave.lipsTat = "degradation";
			break;
		case "slutty advertisements":
			slave.lipsTat = "advertisements";
			break;
	}

	if (slave.currentRules === undefined || slave.currentRules.length < 1) {
		slave.currentRules = [];
	}

	if (slave.height < -1) {
		slave.height = jsRandom(140, 149);
	} else if (slave.height < 0) {
		slave.height = jsRandom(150, 159);
	} else if (slave.height < 1) {
		slave.height = jsRandom(160, 169);
	} else if (slave.height < 2) {
		slave.height = jsRandom(170, 184);
	} else if (slave.height <= 3) {
		slave.height = jsRandom(185, 200);
	}

	if (V.releaseID < 1059) {
		if (slave.eyeColor === "no default value") {
			slave.eyeColor = slave.origEye;
		}
	}

	if (slave.birthSurname === undefined) { slave.birthSurname = 0; }
	if (slave.slaveSurname === undefined) { slave.slaveSurname = 0; }

	if (slave.faceImplant === 1) {
		slave.faceImplant = 15;
	} else if (slave.faceImplant === 2) {
		slave.faceImplant = 65;
	}

	if (slave.areoleaPiercing !== undefined) { delete slave.areoleaPiercing; }
	if (slave.pregControl === undefined) { slave.pregControl = "none"; }
	if (slave.pregControl === "labor supressors") {
		slave.pregControl = "labor suppressors";
	}

	if (slave.chastityAnus === undefined || slave.chastityPenis === undefined || slave.chastityVagina === undefined) {
		if (slave.dickAccessory === "combined chastity") {
			slave.chastityAnus = 1;
			slave.chastityPenis = 1;
			slave.dickAccessory = "none";
		} else if (slave.vaginalAccessory === "combined chastity") {
			slave.chastityAnus = 1;
			slave.chastityVagina = 1;
			slave.vaginalAccessory = "none";
		} else if (slave.dickAccessory === "anal chastity" || slave.vaginalAccessory === "anal chastity") {
			slave.chastityAnus = 1;
			slave.dickAccessory = "none";
			slave.vaginalAccessory = "none";
		} else if (slave.dickAccessory === "chastity") {
			slave.chastityPenis = 1;
			slave.dickAccessory = "none";
		} else if (slave.vaginalAccessory === "chastity belt") {
			slave.chastityVagina = 1;
			slave.vaginalAccessory = "none";
		} else {
			slave.chastityAnus = 0;
			slave.chastityPenis = 0;
			slave.chastityVagina = 0;
		}
	}

	if (slave.rules !== undefined && slave.rules.rest === undefined) {
		slave.rules.rest = "restrictive";
	}

	// migrate to extended family mode if we detected it was needed
	if (V.relationLinks !== undefined) {
		let link = V.relationLinks[slave.ID];
		if (link) {
			// we already know who your parents are
			slave.mother = link.mother;
			slave.father = link.father;
		} else {
			if (slave.relationTarget > 0) {
				switch (slave.relation) {
					case "sister":
					case "twin": {
						const otherLink = V.relationLinks[slave.relationTarget];
						if (otherLink) {
							// we don't know your parents, but we DO know your sister's parents
							// this shouldn't happen but might sometimes, and there's an obviously correct thing to do - use your sister's parents for you too
							slave.mother = otherLink.mother;
							slave.father = otherLink.father;
						} else {
							// don't know your parents, generate new IDs for them
							setMissingParents(slave);
						}
						V.relationLinks[slave.ID] = {mother: slave.mother, father: slave.father};
						// your sister's parents are the same as your parents
						if (!V.relationLinks[slave.relationTarget]) {
							V.relationLinks[slave.relationTarget] = V.relationLinks[slave.ID];
						}
						break;
					}
					case "mother":
						// we know your mother. that's easy.
						slave.mother = slave.relationTarget;
						V.relationLinks[slave.ID] = {mother: slave.mother, father: 0};
						break;
					case "daughter":
						// we know you are your daughter's mother. keep track of that in case she's forgotten somehow.
						if (!V.relationLinks[slave.relationTarget]) {
							V.relationLinks[slave.relationTarget] = {mother: slave.ID, father: 0};
						}
						break;
					default:
						throw `Unrecognized relation for ${SlaveFullName(slave)}.`;
				}
			}
		}

		// if the slave still had a valid recruitment target, allow her to recruit, otherwise don't
		slave.canRecruit = (slave.recruiter === 0) ? 0 : 1;
	}
	delete slave.relation;
	delete slave.relationTarget;
	delete slave.recruiter;

	if (slave.geneticQuirks.albinism === 2 && !slave.albinismOverride) {
		induceAlbinism(slave, 2);
	}
	if (genepool) {
		slave.womb = [];
	} else {
		WombInit(slave);
	}
	if (V.releaseID < 1036) {
		for (let pmw = 0; pmw < slave.womb.length; pmw++) {
			if (slave.womb[pmw].genetics.mother !== slave.womb[pmw].motherID || slave.womb[pmw].genetics.father !== slave.womb[pmw].fatherID) {
				slave.womb[pmw].genetics = generateGenetics(slave, slave.womb[pmw].fatherID, pmw);
			}
		}
	}
};


code:
App.Data.NewGamePlus = (function() {
	const NGPOffset = 1200000;

	function ngpSlaveID(id, preserveSpecial=false) {
		const minID = preserveSpecial ? -20 : -1;
		if (id > 0) {
			id += NGPOffset;
		} else if (V.freshPC === 1 && id === -1) {
			id = -NGPOffset;
		} else if (id < minID) {
			id -= NGPOffset;
		}
		return id;
	}

	function slaveOrZero(id) {
		if (id > 0 && !getSlave(id)) {
			return 0;
		}
		return id;
	}

	function PCInit() {
		if (V.freshPC === 0) {
			cashX(V.ngpParams.prosperity, "personalBusiness");
			const oldCash = V.cash;
			V.cash = 0;
			V.cashLastWeek = 0;
			cashX((Math.clamp(1000*Math.trunc(oldCash/100000), 5000, 1000000)), "personalBusiness");
			if (V.retainCareer === 0) {
				V.PC.career = "arcology owner";
				V.PC.skill.trading = 100;
				V.PC.skill.warfare = 100;
				V.PC.skill.hacking = 100;
				V.PC.skill.slaving = 100;
				V.PC.skill.engineering = 100;
				V.PC.skill.medicine = 100;
			}
			V.PC.mother = ngpSlaveID(V.PC.mother);
			V.PC.father = ngpSlaveID(V.PC.father);
			V.PC.pregSource = slaveOrZero(ngpSlaveID(V.PC.pregSource, true));
			for (let fetus of V.PC.womb) {
				fetus.fatherID = ngpSlaveID(fetus.fatherID, true);
				fetus.genetics.father = ngpSlaveID(fetus.genetics.father, true);
				fetus.genetics.mother = ngpSlaveID(fetus.genetics.mother, true);
			}
		} else {
			V.PC = basePlayer();
			WombInit(V.PC);
			V.cheater = 0;
			V.cash = 0;
			cashX(10000, "personalBusiness");
		}
	}

	function slaveLoopInit() {
		const ngUpdateGenePool = function(genePool = []) {
			const transferredSlaveIds = (V.slaves || [])
				.filter(s => s.ID >= NGPOffset)
				.map(s => s.ID - NGPOffset);
			return genePool
				.filter(s => (transferredSlaveIds.includes(s.ID)))
				.map(function(s) {
					const result = jQuery.extend(true, {}, s);
					result.ID += NGPOffset;
					return result;
				});
		};

		const ngUpdateMissingTable = function(missingTable) {
			const newTable = {};

			(V.slaves || [])
				.forEach(s => ([s.pregSource + NGPOffset, s.mother + NGPOffset, s.father + NGPOffset]
					.filter(i => (i in missingTable))
					.forEach(i => {
						newTable[i - NGPOffset] = missingTable[i];
						newTable[i - NGPOffset].ID -= NGPOffset;
					})));

			return newTable;
		};

		V.slaves.deleteWith((s) => s.assignment !== Job.IMPORTED);

		for (let slave of V.slaves) {
			slave.ID += NGPOffset;
			slave.assignment = Job.REST;
			slave.weekAcquired = 0;
			slave.newGamePlus = 1;
			slave.mother = ngpSlaveID(slave.mother);
			slave.father = ngpSlaveID(slave.father);
			slave.canRecruit = 0;
			slave.breedingMark = 0;
			if (typeof V.ngpParams.nationality === 'string') {
				slave.nationality = V.ngpParams.nationality;
			}
			slave.relationshipTarget = ngpSlaveID(slave.relationshipTarget);
			slave.cloneID = ngpSlaveID(slave.cloneID);
			slave.pregSource = ngpSlaveID(slave.pregSource, true);
			for (let fetus of slave.womb) {
				fetus.fatherID = ngpSlaveID(fetus.fatherID, true);
				fetus.genetics.father = ngpSlaveID(fetus.genetics.father, true);
				fetus.genetics.mother = ngpSlaveID(fetus.genetics.mother, true);
			}
			slave.rivalry = 0, slave.rivalryTarget = 0, slave.subTarget = 0;
			slave.drugs = "no drugs";
			slave.porn.spending = 0;
			slave.rules.living = "spare";
			slave.diet = "healthy";
			slave.pregControl = "none";
		}
		V.slaveIndices = slaves2indices();
		for (let slave of V.slaves) {
			slave.pregSource = slaveOrZero(slave.pregSource);
			slave.cloneID = slaveOrZero(slave.cloneID);
			slave.relationshipTarget = slaveOrZero(slave.relationshipTarget);
		}
		V.genePool = ngUpdateGenePool(V.genePool);
		if (typeof V.missingTable === undefined || V.showMissingSlaves === false) {
			V.missingTable = {};
		} else {
			V.missingTable = ngUpdateMissingTable(V.missingTable);
		}
		let validRelationship = (s) => (s.relationshipTarget !== 0 && getSlave(s.relationshipTarget).relationshipTarget === s.ID);
		for (let slave of V.slaves) {
			if ((slave.relationship < 0 && V.freshPC === 1) || (slave.relationship > 0 && !validRelationship(slave))) {
				slave.relationship = 0;
				slave.relationshipTarget = 0;
			}
			slave.counter.milk = 0;
			slave.counter.cum = 0;
			slave.counter.births = 0;
			slave.counter.mammary = 0;
			slave.counter.penetrative = 0;
			slave.counter.oral = 0;
			slave.counter.anal = 0;
			slave.counter.vaginal = 0;
			slave.lifetimeCashExpenses = 0;
			slave.lifetimeCashIncome = 0;
			slave.lastWeeksCashIncome = 0;
			slave.lifetimeRepExpenses = 0;
			slave.lifetimeRepIncome = 0;
			slave.lastWeeksRepExpenses = 0;
			slave.lastWeeksRepIncome = 0;
		}
	}

	function doNGPSetup() {
		slaveLoopInit();
		PCInit();
		resetFamilyCounters();
		V.ngpParams = {};
	}

	return doNGPSetup;
})();


code:
App.Data.Pronouns = {};

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

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

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


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

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

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

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

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

	// we will replace SugarCube onSave handler
	let oldHandler = SugarCube.Config.saves.onSave;
	try {
		SugarCube.Config.saves.onSave = function(save) {
			if (oldHandler) {
				oldHandler(save);
			}
			downloadToFile(JSON.stringify(save, null, 2), save.id + ".json", "text/plain");
		};
		SugarCube.Save.serialize();
	} finally {
		SugarCube.Config.saves.onSave = oldHandler;
	}
};

App.Debug.slaveSummaryText = function(idx) {
	let span = document.createElement("span");
	span.appendChild(App.UI.SlaveSummary.render(V.slaves[idx]));
	return span.outerHTML;
};


code:
:: Debug Widgets [widget nobr]

/*
This widget will find which properties were changed in a specific passage (State.bottom.title to be precise), and display their original values and current values.
It's somewhat awkward if a stored variable changes types to/from an object or array.
The included "Go Back" link is intended to replace the usual NextButton.
*/
<<widget "displayDifferences">>
<<set _oldVariables = clone(State.bottom.variables)>>
<<set _newVariables = clone(State.variables)>>
<<set _newDiff = diffFlatten(difference(_oldVariables,_newVariables))>> /* Returns a flattened object containing the names of the changed variables, and the new values of those variables. */
<<set _oldDiff = diffFlatten(difference(_newVariables,_oldVariables))>> /* Returns a flattened object containing the names of the changed variables, and the old values of those variables. */
<<set _diffArrayFromNew = generateDiffArray(_newDiff)>> /* This function requires the existence of the specific variables _newDiff AND _oldDiff to work. */
<<set _diffArrayFromOld = generateDiffArray(_oldDiff)>>
<<link "Go Back">>
	<>
<</link>>

Differences:
<<if _diffArrayFromNew.length > 0>>
	<<for _i = 0; _i < _diffArrayFromNew.length; _i++>> /* Print variable names, and changed values. Will output the new values correctly, may not output old values correctly */
		<<if _diffArrayFromNew[_i].variable != "nextButton" && _diffArrayFromNew[_i].variable != "nextLink" && _diffArrayFromNew[_i].variable != "args">>

    Variable: <<print _diffArrayFromNew[_i].variable>>, Original Value: <<print _diffArrayFromNew[_i].oldVal>>, New Value: <<print _diffArrayFromNew[_i].newVal>>
		<</if>>
	<</for>>

	<<link "Show more">>
		<<replace "#extraInfo">>
			Alternate display:
			<<for _i = 0; _i < _diffArrayFromOld.length; _i++>> /* Print variable names, and changed values. Will output the old values correctly, may not output new values correctly */
				<<if _diffArrayFromOld[_i].variable != "nextButton" && _diffArrayFromOld[_i].variable != "nextLink" && _diffArrayFromOld[_i].variable != "args">>

    Variable: <<print _diffArrayFromOld[_i].variable>>, Original Value: <<print _diffArrayFromOld[_i].oldVal>>, New Value: <<print _diffArrayFromOld[_i].newVal>>
				<</if>>
			<</for>>
		<</replace>>
	<</link>>// This should only be necessary if a variable changes type to/from an object or array. In that case this will display the correct original value, but incorrect current value.//

<<else>>

    Nothing changed!

<</if>>
<<link "Go Back">>
	<>
<</link>>
<</widget>>


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

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

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

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

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

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

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

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

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

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

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

	function IncludeEnds() {
		PopAndTally();
	}

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

		tally.clear();

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

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

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

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

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

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

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


code:
:: Variable Difference [nobr]

<<set $nextButton = " ">> /* disable the nextButton. Forces the user to use the "Go Back" option contained in the widget */

<<displayDifferences>>


code:
/**
 * @param {Node} [lastElement] will go on the same line as the last line
 * @returns {DocumentFragment}
 */
App.Desc.playerArcology = function(lastElement) {
	const A = V.arcologies[0];
	const fragment = document.createDocumentFragment();

	/**
	 * @returns {DocumentFragment}
	 */
	function location() {
		const fragment = document.createDocumentFragment();

		fragment.append(App.UI.DOM.makeElement("span", A.name, "name"),
			`, your arcology, is located in a Free City in ${V.terrain === "oceanic" ? "the middle of the ocean" : V.continent}. It is a huge structure whose${V.arcologyUpgrade.apron === 1 ? " solar-paneled" : ""} skin gleams in the sunshine${V.arcologyUpgrade.hydro === 1 ? ", while verdant exterior hydroponics bays lend it an air of growth" : ""}. `);

		let buffer = [];
		if (V.weatherCladding === 1) {
			buffer.push(`Much of its beautiful exterior is now hidden behind dull panels of weather cladding${V.arcologyUpgrade.spire === 1 ? ", though its highest point is capped by a tall, elegant spire" : ""}.`);
		} else if (V.weatherCladding === 2) {
			buffer.push(`The entirety of its once dull exterior has been retrofitted with breathtaking golden sheets in eye catching designs${V.arcologyUpgrade.spire === 1 ? ", its highest point capped by a tall, magnificent spire" : ""}.`);
		}
		buffer.push(`Its${V.weatherCladding === 2 ? " glorious" : V.weatherCladding > 0 ? " dull" : V.arcologyUpgrade.apron === 1 ? " shining" : ""} bulk`);
		if (V.terrain === "urban") {
			buffer.push(`towers above the city around it; it is part of a cluster of arcologies that form a Free City in the midst of an old world urban area.`);
		} else if (V.terrain === "rural") {
			buffer.push(`rises above the buildings around it; it is part of a cluster of arcologies and smaller structures that form a Free City in the middle of a barren wilderness.`);
		} else if (V.terrain === "ravine") {
			buffer.push(`just barely peaks above the cliffs of its home valley; it is part of a cluster of arcologies and smaller structures that form a Free City in the depths of a deep ravine.`);
		} else if (V.terrain === "marine") {
			buffer.push(`rises above the shallow water all around it; it is part of a cluster of arcologies that form a Free City near the seashore.`);
		} else {
			buffer.push(`rises above the blue water all around it; it is part of a cluster of arcologies that form a Free City anchored over a seamount.`);
		}
		fragment.append(buffer.join(" "));

		return fragment;
	}

	let openDiv = App.UI.DOM.makeElement("div", location(), "indent");

	function deco100() {
		if (FutureSocieties.HighestDecoration() < 100) {
			return "";
		}

		let buffer = [];

		if (A.FSChattelReligionistDecoration >= 100) {
			buffer.push(`The uppermost point on the arcology is capped by a massive religious icon.`);
		}
		if (A.FSRomanRevivalistDecoration >= 100) {
			buffer.push(`The entrances of the arcology are built of white stone, with graceful columns bringing Rome immediately to mind.`);
		} else if (A.FSAztecRevivalistDecoration >= 100) {
			buffer.push(`The entrances of the arcology are built of obsidian, giving them a nice sheen as they absorb the sun's rays.`);
		} else if (A.FSEgyptianRevivalistDecoration >= 100) {
			buffer.push(`The entrances of the arcology are built of sandstone, with richly carved columns bringing ancient Egypt immediately to mind.`);
		} else if (A.FSEdoRevivalistDecoration >= 100) {
			buffer.push(`The entrances of the arcology are built of close fitted stone topped with the white walls of a feudal Japanese castle.`);
		} else if (A.FSArabianRevivalistDecoration >= 100) {
			buffer.push(`The entrances of the arcology are built of warm stone and crowded with the busy commerce of an Arabian revival.`);
		} else if (A.FSChineseRevivalistDecoration >= 100) {
			buffer.push(`The entrances of the arcology are guarded by bronze statues of traditional Chinese guardian spirits.`);
		}

		const {he: heP, his: hisP} = getPronouns(V.PC);
		let statue;
		if (V.PC.dick !== 0) {
			if (V.PC.vagina !== -1) {
				statue = "futanari";
			} else if (V.PC.boobs >= 300) {
				statue = "shemale";
			} else {
				statue = "male";
			}
		} else {
			statue = "female";
		}

		buffer.push(`Outside the main entrance, there is an enormous statue of an idealized figure with several statues of nude slaves at its feet.`);
		if (A.FSStatuesqueGlorification === 100) {
			buffer.push(`The central ${statue} figure towers over the area.`);
		}
		if (A.FSSupremacistDecoration === 100) {
			buffer.push(`The central ${statue} figure has distinctly ${A.FSSupremacistRace} features.`);
		}
		if (A.FSSubjugationistDecoration === 100) {
			buffer.push(`The slaves have exaggerated ${A.FSSubjugationistRace} features.`);
		}
		if (A.FSGenderRadicalistDecoration === 100) {
			buffer.push(`The slaves are all hermaphrodites.`);
		}
		if (A.FSGenderFundamentalistDecoration === 100) {
			buffer.push(`The slaves are all perfectly female.`);
		}
		if (A.FSPaternalistDecoration === 100) {
			buffer.push(`The slaves are gazing adoringly up at the central ${statue} figure.`);
		}
		if (A.FSDegradationistDecoration === 100) {
			buffer.push(`The slaves are cowering, and are wearing chains.`);
		}
		if (A.FSBodyPuristDecoration === 100) {
			buffer.push(`The slaves' bodies are idealized, too.`);
		}
		if (A.FSTransformationFetishistDecoration === 100) {
			buffer.push(`The slaves have unnaturally narrow waists and big breasts.`);
		}
		if (A.FSYouthPreferentialistDecoration === 100) {
			if (V.minimumSlaveAge < 13) {
				buffer.push(`The slaves are cute lolis with round, innocent faces.`);
			} else {
				buffer.push(`The slaves are young, with smooth, innocent faces.`);
			}
		}
		if (A.FSMaturityPreferentialistDecoration === 100) {
			buffer.push(`The slaves are mature, with motherly bodies.`);
		}
		if (A.FSSlimnessEnthusiastDecoration === 100) {
			buffer.push(`The slaves have pretty, girlish figures.`);
		}
		if (A.FSPetiteAdmirationDecoration === 100) {
			buffer.push(`The slaves are quite short.`);
		}
		if (A.FSAssetExpansionistDecoration === 100) {
			buffer.push(`The slaves' breasts are unrealistically huge, almost as large as the rest of their bodies.`);
		}
		if (A.FSRepopulationFocusDecoration === 100) {
			buffer.push(`The slaves are heavily pregnant${V.PC.vagina !== -1 ? `, as is the central${V.PC.dick !== 0 ? " futanari" : " female"}` : ""} figure.`);
		}
		if (A.FSRestartDecoration === 100) {
			buffer.push(`The central`);
			if (V.PC.dick > 0) {
				if (V.PC.vagina >= 0) {
					buffer.push(`futanari figure's balls are oversized and ${hisP} stomach slightly rounded,`);
				} else if (V.PC.boobs >= 300) {
					buffer.push(`shemale figure's balls are swollen with virile seed,`);
				} else {
					buffer.push(`male figure's balls are swollen with virile seed,`);
				}
			} else {
				buffer.push(`female figure's stomach is slightly rounded,`);
			}
			buffer.push(`announcing its fertility, while the slaves wear chastity devices.`);
		}
		if (A.FSPastoralistDecoration === 100) {
			buffer.push(`The statuary is located in the middle of a fountain; the slaves' nipples pour water into the pool beneath.`);
		}
		if (A.FSPhysicalIdealistDecoration === 100) {
			buffer.push(`Both the central ${statue} figure and the slaves are ${A.FSPhysicalIdealistLaw === 1 ? "quite fit" : "very muscular"}.`);
		}
		if (A.FSHedonisticDecadenceDecoration === 100) {
			buffer.push(`The central ${statue} figure has a distinct softness to its body, while the slaves are delightfully rotund.`);
		}
		if (A.FSIntellectualDependencyDecoration === 100) {
			buffer.push(`The slaves are visibly aroused.`);
		}
		if (A.FSSlaveProfessionalismDecoration === 100) {
			buffer.push(`The slaves have flawless posture.`);
		}
		if (A.FSChattelReligionistDecoration === 100) {
			buffer.push(`The central ${statue} figure has a halo behind ${hisP} head, and the slaves are in attitudes of worship.`);
		}
		if (A.FSRomanRevivalistDecoration === 100) {
			buffer.push(`The central ${statue} figure is wearing a toga; ${heP} has a laurel wreath about ${hisP} brow.`);
		}
		if (A.FSAztecRevivalistDecoration === 100) {
			buffer.push(`The central ${statue} figure is wearing a feathered cloak, ornamented with jewels; ${heP} has a giant headdress on ${hisP} head.`);
		}
		if (A.FSEgyptianRevivalistDecoration === 100) {
			buffer.push(`The central ${statue} figure is wearing a pharaoh's crown.`);
		}
		if (A.FSEdoRevivalistDecoration === 100) {
			buffer.push(`The central ${statue} figure has a hand resting confidently on the hilts of the sheathed katana and wakizashi ${heP} has thrust through the sash about ${hisP} waist.`);
		}
		if (A.FSArabianRevivalistDecoration === 100) {
			buffer.push(`The central ${statue} figure is wearing a turban and flowing robes.`);
		}
		if (A.FSChineseRevivalistDecoration === 100) {
			buffer.push(`The central ${statue} figure is wearing the robes of an ancient Chinese ${V.PC.title === 1 ? "Emperor" : "Empress"}.`);
		}

		return buffer.join(" ");
	}

	openDiv.append(deco100());

	/**
	 * @returns {string}
	 */
	function deco40() {
		if (FutureSocieties.HighestDecoration() < 40) {
			return "";
		}

		let buffer = [];
		buffer.push(`The entryway features the first of ${A.name}'s many banks of public screens.`);
		if (A.FSSupremacistDecoration >= 40) {
			buffer.push(`Some are showing talk shows and documentaries supporting ${A.FSSupremacistRace} supremacy.`);
		}
		if (A.FSSubjugationistDecoration >= 40) {
			buffer.push(`Some are showing long pseudoscientific programs explaining ${A.FSSubjugationistRace} degeneracy.`);
		}
		if (A.FSDegradationistDecoration >= 40) {
			buffer.push(`Some are showing abusive pornography involving slaves being beaten, sodomized, and modified against their will.`);
		}
		if (A.FSIntellectualDependencyDecoration >= 40) {
			buffer.push(`Some are showing simple, colorful, and very sexual programs made to excite slow slaves.`);
		}
		if (A.FSSlaveProfessionalismDecoration >= 40) {
			buffer.push(`Some are showing self-improvement shows on how to be a better slave for one's owner.`);
		}
		if (A.FSRepopulationFocusDecoration >= 40) {
			buffer.push(`Some are showing medical programs about pregnancy, interspersed with pornography starring slaves with big pregnant bellies.`);
		}
		if (A.FSRestartDecoration >= 40) {
			buffer.push(`Some are showing propaganda discouraging slave pregnancy, while encouraging the Elite to interbreed. Others urge free citizens to join the Societal Elite today.`);
		}
		if (A.FSGenderFundamentalistDecoration >= 40) {
			buffer.push(`Some are showing pornography starring women with perfect tits, tight asses and nice pussies.`);
		}
		if (A.FSPaternalistDecoration >= 40) {
			buffer.push(`Some are showing educational programs for the edification of slaves, and news programs featuring slave accomplishments.`);
		}
		if (A.FSDegradationistDecoration >= 40) {
			buffer.push(`Some are showing abusive pornography involving slaves being beaten, sodomized, and modified against their will.`);
		}
		if (A.FSBodyPuristDecoration >= 40) {
			buffer.push(`Some are showing sports programs, the latest games, and self-improvement videos.`);
		}
		if (A.FSTransformationFetishistDecoration >= 40) {
			buffer.push(`Some are showing medical programs about surgical transformation, interspersed with pornography starring slaves with fake tits.`);
		}
		if (A.FSYouthPreferentialist >= 40) {
			buffer.push(`Most of the girls featured are just over the age of majority.`);
		}
		if (A.FSMaturityPreferentialist >= 40) {
			buffer.push(`Most of the ladies featured are nice and mature.`);
		}
		if (A.FSStatuesqueGlorificationDecoration >= 40) {
			buffer.push(`Some are showing infomercials on how to appear taller, interspersed with pornography starring tall slaves.`);
		}
		if (A.FSPetiteAdmirationDecoration >= 40) {
			buffer.push(`Some are showing infomercials for accommodating short slaves, interspersed with pornography focusing on the size differences between owners and slaves.`);
		}
		if (A.FSSlimnessEnthusiastDecoration >= 40) {
			buffer.push(`Some are showing workout videos interspersed with pornography starring slim slaves.`);
		}
		if (A.FSAssetExpansionistDecoration >= 40) {
			buffer.push(`Some are playing pornography starring slaves with huge assets, interspersed with pharmaceutical advertisements.`);
		}
		if (A.FSPastoralistDecoration >= 40) {
			buffer.push(`Some are showing informational agricultural programs interspersed with pornography starring slaves who are lactating heavily${V.seePreg === 1 ? ", hugely pregnant, or often both" : ""}.`);
		}
		if (A.FSPhysicalIdealistDecoration >= 40) {
			buffer.push(`Some are showing`);
			if (A.FSPhysicalIdealistLaw === 1) {
				buffer.push(`athletic competitions, advertisements for supplements, and pornography starring physically fit slaves.`);
			} else {
				buffer.push(`bodybuilding competitions, advertisements for supplements, and pornography starring hugely muscular slaves.`);
			}
		}
		if (A.FSHedonisticDecadenceDecoration >= 40) {
			buffer.push(`Some are showing pampering spas, advertisements for various eateries, and pornography starring corpulent slaves and food.`);
		}
		if (A.FSChattelReligionistDecoration >= 40) {
			buffer.push(`Some are showing religious services interspersed with serious programs on the compatibility of sexual slavery with the faith.`);
		}
		if (A.FSRomanRevivalistDecoration >= 40) {
			buffer.push(`Some are showing announcements on ${A.name}'s progress prepared by the state media for the edification of citizens.`);
		} else if (A.FSAztecRevivalistDecoration >= 40) {
			buffer.push(`Some are glorifying the Five Suns and the role of sacrifice and bloodletting.`);
		} else if (A.FSEgyptianRevivalistDecoration >= 40) {
			buffer.push(`Some are showing educational and scientific programs interspersed with multicultural features.`);
		} else if (A.FSEdoRevivalistDecoration >= 40) {
			buffer.push(`Some are showing historical dramas set in Edo Japan.`);
		} else if (A.FSArabianRevivalistDecoration >= 40) {
			buffer.push(`Some are showing dramatic retellings of traditional Bedouin fables.`);
		} else if (A.FSChineseRevivalistDecoration >= 40) {
			buffer.push(`Some are showing ancient Chinese operas.`);
		}
		if (V.arcologyUpgrade.grid === 1) {
			buffer.push(`The programming is constantly updated to suit the individual viewer.`);
			if (V.brothelAdsSpending > 0) {
				buffer.push(`Pornographic advertisements for the brothel are interspersed with the programming.`);
				if (V.brothelAdsOld === 1) {
					buffer.push(`The featured slave actresses are all MILFs.`);
				} else if (V.brothelAdsOld === -1) {
					buffer.push(`The featured slave actresses are all nice and young.`);
				} else if (V.brothelAdsOld === -2) {
					buffer.push(`The featured slave actresses are all teenagers.`);
				} else if (V.brothelAdsOld === -3) {
					buffer.push(`The featured slave actresses are all lolis.`);
				} else {
					buffer.push(`The featured slave actresses vary in age.`);
				}
				if (V.brothelAdsStacked === 1) {
					buffer.push(`Lots of bouncing breasts and butts`);
				} else if (V.brothelAdsStacked === -1) {
					buffer.push(`Lots of trim breasts and shapely butts`);
				} else {
					buffer.push(`A variety of breast and butt sizes and shapes`);
				}
				buffer.push(`are on display, and`);
				if (V.brothelAdsImplanted === 1) {
					buffer.push(`most of these are augmented by implants.`);
				} else if (V.brothelAdsImplanted === -1) {
					buffer.push(`they're all natural.`);
				} else {
					buffer.push(`some are augmented by implants.`);
				}
				if (V.seePreg === 1) {
					if (V.brothelAdsPreg === 1) {
						buffer.push(`Most of slaves have firm, rounded bellies.`);
					} else if (V.brothelAdsPreg === -1) {
						buffer.push(`Most of the slaves have firm, flat bellies.`);
					} else {
						buffer.push(`Some of the slaves are pregnant.`);
					}
				}
				if (V.brothelAdsModded === 1) {
					buffer.push(`Everything is heavily pierced and tattooed.`);
				} else if (V.brothelAdsModded === -1) {
					buffer.push(`Everything is free of tattoos and piercings.`);
				} else {
					buffer.push(`Some of these assets are tattooed and pierced, and some aren't.`);
				}
				buffer.push(`The slaves in the ads are`);
				if (V.brothelAdsXX === 1) {
					buffer.push(`sucking dick and taking cock in their pussies and asses.`);
				} else if (V.brothelAdsXX === -1) {
					buffer.push(`sucking cock and being assfucked while their dicks flop around.`);
				} else {
					buffer.push(`sucking dick and taking anal, and the ones that have pussies are being fucked there, too.`);
				}
				const t = `As a result, ${V.brothelName} is known as the place to go if you want to rent`;
				if (V.brothelAdsXX === 1) {
					if (V.brothelAdsImplanted === 1 && V.brothelAdsStacked === 1) {
						buffer.push(`${t} a${V.brothelAdsPreg === 1 ? " pregnant" : ""} bimbo's pussy.`);
					} else if (V.brothelAdsOld === 1) {
						buffer.push(`${t} a${V.brothelAdsPreg === 1 ? " pregnant" : ""} MILF's tits.`);
					} else if (V.brothelAdsOld === -3) {
						buffer.push(`${t} ${addA(`${V.brothelAdsPreg === 1 ? "pregnant " : ""}${V.brothelAdsStacked === 1 ? "oppai " : ""}loli.`)}`);
					} else if (V.brothelAdsPreg === 1) {
						buffer.push(`${t} a pregnant girl.`);
					}
				} else if (V.brothelAdsXX === -1) {
					if (V.brothelAdsStacked === 1) {
						buffer.push(`${t} a shemale's asspussy.`);
					} else if (V.brothelAdsStacked === -1) {
						buffer.push(`${t} a trap's tight ass.`);
					}
				}
			}
			if (V.clubAdsSpending > 0) {
				buffer.push(`Music videos set in ${V.clubName} are also shown frequently.`);
				if (V.clubAdsOld === 1) {
					buffer.push(`The featured strippers are all MILFs.`);
				} else if (V.clubAdsOld === -1) {
					buffer.push(`The featured strippers are all nice and young.`);
				} else if (V.clubAdsOld === -2) {
					buffer.push(`The featured strippers are all teenagers.`);
				} else if (V.clubAdsOld === -3) {
					buffer.push(`The featured strippers are all lolis.`);
				} else {
					buffer.push(`The featured strippers vary in age.`);
				}
				if (V.clubAdsStacked === 1) {
					buffer.push(`Lots of bouncing breasts and butts`);
				} else if (V.clubAdsStacked === -1) {
					buffer.push(`Lots of trim breasts and shapely butts`);
				} else {
					buffer.push(`A variety of breast and butt sizes and shapes`);
				}
				buffer.push(`are on display, and`);
				if (V.clubAdsImplanted === 1) {
					buffer.push(`most of these are augmented by implants.`);
				} else if (V.clubAdsImplanted === -1) {
					buffer.push(`they're all natural.`);
				} else {
					buffer.push(`some are augmented by implants.`);
				}
				if (V.seePreg === 1) {
					if (V.clubAdsPreg === 1) {
						buffer.push(`Most of strippers have firm, rounded bellies.`);
					} else if (V.clubAdsPreg === -1) {
						buffer.push(`Most of the strippers have firm, flat bellies.`);
					} else {
						buffer.push(`Some of the strippers are pregnant.`);
					}
				}
				if (V.clubAdsModded === 1) {
					buffer.push(`Everything is heavily pierced and tattooed.`);
				} else if (V.clubAdsModded === -1) {
					buffer.push(`Everything is free of tattoos and piercings.`);
				} else {
					buffer.push(`Some of these assets are tattooed and pierced, and some aren't.`);
				}
				buffer.push(`The strippers get naked quickly, and show off`);
				if (V.clubAdsXX === 1) {
					buffer.push(`their pussies and assholes.`);
				} else if (V.clubAdsXX === -1) {
					buffer.push(`their assholes.`);
				} else {
					buffer.push(`their holes.`);
				}
				buffer.push(`The music videos are very popular.`);
				const t = `As a result, ${V.clubName} is known as the place to spend a night partying with`;
				if (V.clubAdsXX === 1) {
					if (V.clubAdsImplanted === 1 && V.clubAdsStacked === 1) {
						buffer.push(`${t}${V.clubAdsPreg === 1 ? " pregnant" : ""} bimbos.`);
					} else if (V.clubAdsOld === 1) {
						buffer.push(`${t}${V.clubAdsPreg === 1 ? " pregnant" : ""} MILFs.`);
					} else if (V.clubAdsOld === -3) {
						buffer.push(`${t}${V.clubAdsPreg === 1 ? " pregnant" : ""}${V.brothelAdsStacked === 1 ? " oppai" : ""} lolis.`);
					} else if (V.clubAdsPreg === 1) {
						buffer.push(`${t} with pregnant sluts.`);
					}
				} else if (V.clubAdsXX === -1) {
					if (V.clubAdsStacked === 1) {
						buffer.push(`${t} with shemales.`);
					} else if (V.clubAdsStacked === -1) {
						buffer.push(`${t} traps.`);
					}
				}
			}
		}
		return buffer.join(" ");
	}

	const d40 = deco40();
	if (d40 !== "") {
		fragment.append(openDiv);
		// we have to reuse the variable because of scoping
		openDiv = App.UI.DOM.makeElement("div", d40, "indent");
	}

	/**
	 * @returns {string}
	 */
	function secExp() {
		if (V.secExpEnabled !== 1) {
			return "";
		}

		let buffer = [];
		if (V.SecExp.buildings.propHub.active === 1) {
			buffer.push(`A small street hides a surprisingly inconspicuous building, whose task is to manage your public image, protection and population control.`);
		}
		if (V.secHQ === 1) {
			buffer.push(`In a secure corner of the penthouse, the Security HQ silently works to build a safe and prosperous arcology.`);
		}
		if (V.SecExp.buildings.barracks.active === 1) {
			buffer.push(`At the center of the structure the barracks can be found filling the halls with the noise of ballistic weapons and training troops.`);
		}
		if (V.weapManu === 1) {
			buffer.push(`Down in the lower levels of the arcology the weapons manufacturing facility dominates the environment; there, armaments of all kind are produced and shipped away.`);
		}
		if (V.riotCenter === 1) {
			buffer.push(`Near the penthouse the riot control center can be found. Here dissidents and dangerous political forces of ${A.name} are carefully monitored and managed.`);
		}
		if (V.transportHub === 1) {
			buffer.push(`The transport hub, in the commercial section of the arcology, deals with new arrivals to ${A.name} via ${V.terrain === "oceanic" || V.terrain === "marine" ? "sea" : "land"} in addition to air.`);
		}
		return buffer.join(" ");
	}

	const se = secExp();
	if (se !== "") {
		fragment.append(openDiv);
		// we have to reuse the variable because of scoping
		openDiv = App.UI.DOM.makeElement("div", se, "indent");
	}

	function deco60() {
		if (FutureSocieties.HighestDecoration() < 60) {
			return "";
		}

		let buffer = [];
		buffer.push(`The central plaza is a large open atrium lined with banners.`);
		if (A.FSSupremacistDecoration >= 60) {
			buffer.push(`Some depict great achievements in ${A.FSSupremacistRace} history.`);
		}
		if (A.FSSubjugationistDecoration >= 60) {
			buffer.push(`Some depict crushing defeats in ${A.FSSubjugationistRace} history.`);
		}
		if (A.FSGenderRadicalistDecoration >= 60) {
			buffer.push(`Some are split horizontally in three: one part depicts penetration of a woman's pussy, another depicts penetration of a shemale's anus, and the third depicts simultaneous penetration of a hermaphrodite's pussy and anus. The order in which they appear, top to bottom, varies from banner to banner.`);
		}
		if (A.FSGenderFundamentalistDecoration >= 60) {
			buffer.push(`Some lovingly depict close-ups of pussies, a different one on each banner, in all their beauty and variation.`);
		}
		if (A.FSPaternalistDecoration >= 60) {
			buffer.push(`Some feature respectful portraits of some of the arcology's most talented slaves doing what they're best at.`);
		}
		if (A.FSRepopulationFocusDecoration >= 60) {
			buffer.push(`Some feature respectful portraits of happy pregnant women.`);
		}
		if (A.FSRestartDecoration >= 60) {
			buffer.push(`Some feature respectful portraits of society's finest.`);
		}
		if (A.FSBodyPuristDecoration >= 60) {
			buffer.push(`Some feature portraits of pristine nude bodies without a blemish or marking.`);
		}
		if (A.FSTransformationFetishistDecoration >= 60) {
			buffer.push(`Some depict sex featuring some of the arcology's most heavily implanted slaves.`);
		}
		if (A.FSYouthPreferentialistDecoration >= 60) {
			buffer.push(`Some depict idealized outlines of youthful bodies, with trim hips and pure faces.`);
		}
		if (A.FSMaturityPreferentialistDecoration >= 60) {
			buffer.push(`Some depict idealized outlines of mature bodies, with motherly hips and knowing faces.`);
		}
		if (A.FSPetiteAdmirationDecoration >= 60) {
			buffer.push(`Some depict sex between bodies of vastly different sizes.`);
		}
		if (A.FSStatuesqueGlorificationDecoration >= 60) {
			buffer.push(`Some depict idealized outlines of statuesque bodies.`);
		}
		if (A.FSSlimnessEnthusiastDecoration >= 60) {
			buffer.push(`Some feature artistic depictions of slender girls, running, dancing, and in the midst of the act of love.`);
		}
		if (A.FSAssetExpansionistDecoration >= 60) {
			buffer.push(`Some feature lewd close-ups of enormous breasts and giant asses.`);
		}
		if (A.FSPastoralistDecoration >= 60) {
			buffer.push(`Some feature prizewinning cows.`);
		}
		if (A.FSPhysicalIdealistDecoration >= 60) {
			buffer.push(`Some feature`);
			if (A.FSPhysicalIdealistLaw === 1) {
				buffer.push(`athletic portraits of the arcology's fastest, strongest, and healthiest girls.`);
			} else {
				buffer.push(`some of the arcology's most swole ladies.`);
			}
		}
		if (A.FSHedonisticDecadenceDecoration >= 60) {
			buffer.push(`Many feature the most popular entertainment options your arcology has to offer.`);
		}
		if (A.FSChattelReligionistDecoration >= 60) {
			buffer.push(`Many include religious iconography.`);
		}
		if (A.FSSlaveProfessionalismDecoration >= 60) {
			buffer.push(`Most were handcrafted by the local chattel.`);
		}
		if (A.FSDegradationistDecoration >= 60) {
			buffer.push(`There are cages hanging from the ceiling of the central plaza; a naked slave occupies each one.`);
		}
		if (A.FSRomanRevivalistDecoration >= 60) {
			buffer.push(`The central plaza is decorated to look like a thriving Roman forum on market day.`);
		} else if (A.FSAztecRevivalistDecoration >= 60) {
			buffer.push(`The central plaza is decorated to look like a tribute to the gods, with huge statues, all looking in awe at the central temple. Many merchants peddle the goods of the new millennium.`);
		} else if (A.FSEgyptianRevivalistDecoration >= 60) {
			buffer.push(`The central plaza is decorated to look like a thriving ancient Egyptian market on a festival day.`);
		} else if (A.FSEdoRevivalistDecoration >= 60) {
			buffer.push(`The central plaza is decorated to look like a vision of urban Edo Japan, with strings of lanterns crisscrossing the space and the petals from cherry blossoms floating gently through the air.`);
		} else if (A.FSArabianRevivalistDecoration >= 60) {
			buffer.push(`The central plaza is decorated to look like a thriving Arabian slave market.`);
		} else if (A.FSChineseRevivalistDecoration >= 60) {
			buffer.push(`The central plaza is constructed as a Chinese water market, with stalls set up to serve customers passing by on boats riding on shallow canals.`);
		}
		return buffer.join(" ");
	}

	const d60 = deco60();
	if (d60 !== "") {
		fragment.append(openDiv);
		// we have to reuse the variable because of scoping
		openDiv = App.UI.DOM.makeElement("div", d60, "indent");
		if (V.arcologyUpgrade.drones === 1) {
			openDiv.append(`A security drone occasionally flies across the open space.`);
		}
	} else if (V.arcologyUpgrade.drones === 1) {
		openDiv.append(` The central plaza is a large atrium; a security drone occasionally flies across the open space.`);
	}

	function deco80part1() {
		if (FutureSocieties.HighestDecoration() < 80) {
			return "";
		}

		let buffer = [];

		buffer.push(`There are numerous slaves stationed down on the plaza to greet visitors.`);
		if (A.FSSlaveProfessionalismDecoration >= 80) {
			buffer.push(`They're intelligent and articulate.`);
		}
		if (A.FSPaternalistDecoration >= 80) {
			buffer.push(`All are healthy and happy.`);
		}
		if (A.FSBodyPuristDecoration >= 80) {
			buffer.push(`Most are pretty and unspoiled.`);
		}
		if (A.FSStatuesqueGlorificationDecoration >= 80) {
			buffer.push(`Plenty are tall for their age.`);
		}
		if (A.FSTransformationFetishistDecoration >= 80) {
			if (A.FSTransformationFetishistResearch === 1) {
				buffer.push(`Most have absolutely enormous fake tits, lips and asses. Some even have breasts and butts larger than their entire bodies.`);
			} else {
				buffer.push(`Most have enormous fake tits, lips and asses.`);
			}
		}
		if (A.FSYouthPreferentialistDecoration >= 80) {
			buffer.push(`Most are quite young, and a fair amount of energetic giggling can be heard.`);
		}
		if (A.FSMaturityPreferentialistDecoration >= 80) {
			buffer.push(`Most are quite mature, and very experienced; they see nothing unusual about offering visitors public sex as a way to welcome them to ${A.name}.`);
		}
		if (A.FSSlimnessEnthusiastDecoration >= 80) {
			buffer.push(`Many are girlish and energetic.`);
		}
		if (A.FSIntellectualDependencyDecoration >= 80) {
			buffer.push(`More than few are getting a little hands-on with the visitors.`);
		}
		if (A.FSAssetExpansionistDecoration >= 80) {
			if (A.FSAssetExpansionistResearch === 1) {
				buffer.push(`Many are using wheeled stands to permit them to stand despite their titanic breasts, massive asses, trunk-like cocks and boulder-sized balls.`);
			} else {
				buffer.push(`Many are wearing custom bras to manage their enormous breasts.`);
			}
		}
		if (A.FSRepopulationFocusDecoration >= 80) {
			buffer.push(`Most have large pregnant bellies.`);
		}
		if (A.FSRestartDecoration >= 80) {
			buffer.push(`All are either wearing chastity or are infertile.`);
		}
		if (A.FSPetiteAdmirationDecoration >= 80) {
			buffer.push(`Most are making use of raised platforms to give guests a better view of their short figures.`);
		}
		if (A.FSPastoralistDecoration >= 80) {
			buffer.push(`Many of them can offer visitors a sample of ${A.name}'s pride and joy, straight from the nipple.`);
		}
		if (A.FSPhysicalIdealistDecoration >= 80) {
			if (A.FSPhysicalIdealistLaw === 1) {
				buffer.push(`Their bodies are uniformly fit and healthy${A.FSRomanRevivalist > 0 ? " in the Hellenistic tradition" : ""}.`);
			} else {
				buffer.push(`Their musculature is uniformly stunning. Swole acceptance is high here.`);
			}
		}
		if (A.FSHedonisticDecadenceDecoration >= 80) {
			buffer.push(`Most are quite plush and often carry trays for visitors to sample the local pleasures.`);
		}
		if (A.FSRomanRevivalistDecoration >= 80) {
			buffer.push(`Many are wearing authentic ancient Roman stolas and maintaining composed and dignified attitudes.`);
		} else if (A.FSAztecRevivalistDecoration >= 80) {
			buffer.push(`Many are wearing authentic Aztec huipils, woven specifically to draw the attention of the visitors.`);
		} else if (A.FSEgyptianRevivalistDecoration >= 80) {
			buffer.push(`Many are wearing authentic ancient Egyptian dresses, and are dancing and making merry when they aren't assisting people.`);
		} else if (A.FSEdoRevivalistDecoration >= 80) {
			buffer.push(`Many are wearing daring kimonos, and politely greet visitors by their proper titles.`);
		} else if (A.FSArabianRevivalistDecoration >= 80) {
			buffer.push(`Many are wearing beguiling harem girl outfits, and dance to set the little bells attached to the fabric jingling.`);
		} else if (A.FSChineseRevivalistDecoration >= 80) {
			buffer.push(`Many are wearing qipaos cut very short, and jostle and chatter in the throng that packs the plaza.`);
		}
		if (A.FSGenderRadicalistDecoration >= 80) {
			if (A.FSGenderRadicalistResearch === 1) {
				buffer.push(`On several raised stands around the plaza, girls with dicks are stripping for the pleasure of passersby. Some of them are noticeably pregnant, despite lacking vaginas.`);
			} else {
				buffer.push(`On several raised stands around the plaza, girls with dicks are stripping for the pleasure of passersby.`);
			}
		}
		if (A.FSGenderFundamentalistDecoration >= 80) {
			buffer.push(`On several raised stands around the plaza, female slaves are stripping for the pleasure of passersby.`);
		}
		if (A.FSChattelReligionistDecoration >= 80) {
			buffer.push(`A handful of slaves are in religious attire, praying. They help if asked, and will even lead brief religious services if requested.`);
		}
		if (A.FSDegradationistDecoration >= 80) {
			buffer.push(`Numerous downtrodden slaves are working on the plaza at menial tasks. Some are even carrying citizens in sedan chairs.`);
		}
		if (A.FSSupremacistDecoration >= 80) {
			buffer.push(`Here and there around the plaza, slaves of every race besides ${A.FSSupremacistRace} people can be seen carrying things, cleaning, and even shining shoes.`);
		}
		if (A.FSSubjugationistDecoration >= 80) {
			buffer.push(`In the center of the plaza, an auction of a large group of frightened ${A.FSSubjugationistRace} slaves is taking place.`);
		}
		if (V.assistant.power > 0) {
			buffer.push(`The plaza is packed with citizens doing business.`);
		}

		return buffer.join(" ");
	}

	openDiv.append(deco80part1());

	fragment.append(openDiv);

	const citizenDiv = document.createElement("div");
	citizenDiv.classList.add("indent");

	function deco80part2() {
		if (FutureSocieties.HighestDecoration() < 80) {
			return "";
		}

		let buffer = [];
		if (A.FSSupremacistDecoration >= 80) {
			buffer.push(`The chanted sounds of indoctrination can faintly be heard in the plaza.`);
		}
		if (A.FSSubjugationistDecoration >= 80) {
			buffer.push(`Judging by the sound of leather on flesh, a whipping is happening somewhere close by the plaza.`);
		}
		if (A.FSGenderRadicalistDecoration >= 80) {
			buffer.push(`A slave is being sodomized in a parlor off the plaza, to go by the pained moaning.`);
		}
		if (A.FSGenderFundamentalistDecoration >= 80) {
			buffer.push(`A slave is being fucked in a parlor off the plaza, to go by the gasps of pleasure.`);
		}
		if (A.FSPaternalistDecoration >= 80) {
			buffer.push(`The sounds of orgasm are drifting out of a hallway off the plaza.`);
		}
		if (A.FSDegradationistDecoration >= 80) {
			buffer.push(`Agonized screaming is drifting out of a hallway off the plaza.`);
		}
		if (A.FSPetiteAdmirationDecoration >= 80) {
			buffer.push(`The squeals of a slave being whisked off their feet somewhere off the plaza.`);
		}
		if (A.FSBodyPuristDecoration >= 80) {
			buffer.push(`Splashing water in a swimming pool can be heard from below the plaza.`);
		}
		if (A.FSRepopulationFocusDecoration >= 80) {
			buffer.push(`The sound of a woman in labor can be heard from somewhere off the plaza.`);
		}
		if (A.FSTransformationFetishistDecoration >= 80) {
			buffer.push(`The heavy beat of club music can be heard on the plaza.`);
		}
		if (A.FSSlaveProfessionalismDecoration >= 80) {
			buffer.push(`A deep discussion over sexual technique can be heard from a nearby balcony.`);
		}
		if (A.FSIntellectualDependencyDecoration >= 80) {
			buffer.push(`Giggles and moans are drifting out of a hallway off the plaza.`);
		}
		if (A.FSRestartDecoration >= 80) {
			buffer.push(`The sound of a fertile slave being viciously beaten can be heard from a side hall.`);
		}
		if (A.FSYouthPreferentialistDecoration >= 80) {
			buffer.push(`The squeals of a young slave taking cock in a tight hole are coming from somewhere off the plaza.`);
		}
		if (A.FSMaturityPreferentialistDecoration >= 80) {
			buffer.push(`The throaty laughter of an amused older woman is coming from somewhere off the plaza.`);
		}
		if (A.FSSlimnessEnthusiastDecoration >= 80) {
			buffer.push(`The quiet murmur of feet on a running track can be heard from the club above the plaza.`);
		}
		if (A.FSAssetExpansionistDecoration >= 80) {
			buffer.push(`Stereotypical bimbo giggling can be heard from the club above the plaza.`);
		}
		if (A.FSStatuesqueGlorification >= 80) {
			buffer.push(`The sound of a short slave being bullied can be heard from somewhere off the plaza.`);
		}
		if (A.FSPastoralistDecoration >= 80) {
			buffer.push(`An indistinct gushing noise is coming from somewhere below the plaza.`);
		}
		if (A.FSPhysicalIdealistDecoration >= 80) {
			buffer.push(`The clash of a deadlift bar being racked can be heard from a nearby gym.`);
		}
		if (A.FSHedonisticDecadenceDecoration >= 80) {
			buffer.push(`The sound of a rather heavy orgy can be heard from somewhere off the plaza.`);
		}
		if (A.FSChattelReligionistDecoration >= 80) {
			buffer.push(`A lovely hymn is drifting through the air.`);
		}
		if (A.FSRomanRevivalistDecoration >= 80 && A.FSRomanRevivalistSMR === 1) {
			buffer.push(`Faintly, the sound of cheering can be heard as`);
			if (A.FSPaternalist !== "unset") {
				buffer.push(`a slave achieves sexual victory over their partner`);
			} else if (V.pitKillsTotal > 0) {
				buffer.push(`a slave dies`);
			} else {
				buffer.push(`a slave successfully rapes`);
			}
			buffer.push(`in gladiatorial combat.`);
		} else if (A.FSAztecRevivalistDecoration >= 80) {
			buffer.push(`The sound of prayer and chanting echoes across the space, briefly accompanied by a sacrifice's ${A.FSPaternalist !== "unset" ? "moaning" : "last scream"} as the rite completes.`);
		} else if (A.FSEgyptianRevivalistDecoration >= 80) {
			buffer.push(`To the side of the plaza is a huge stone entryway heading down: the entrance to the tomb you have prepared for yourself.`);
		} else if (A.FSEdoRevivalistDecoration >= 80) {
			buffer.push(`Everyone maintains a certain air of decorum, and whenever two citizens meet they perform a polite ceremony of respectful greeting.`);
		} else if (A.FSArabianRevivalistDecoration >= 80) {
			buffer.push(`The throng packing the busy plaza must periodically give way before a new train of fresh slaves chained together at the neck, being led into the market by one of the arcology's many prosperous slavers.`);
		} else if (A.FSChineseRevivalistDecoration >= 80) {
			buffer.push(`The noise in the thriving open space is almost oppressive, with the sounds of drunken merriment, traditional music, and distant intercourse forming an omnipresent hum.`);
		}

		return buffer.join(" ");
	}

	citizenDiv.append(deco80part2());

	citizenDiv.append(` ${num(V.ACitizens)} citizens and ${num(V.ASlaves + V.slaves.length)} slaves live in ${A.name}.`);

	function fsLaws() {
		let buffer = [];
		if (A.FSSupremacistLawME === 1) {
			buffer.push(`The citizenry is entirely ${A.FSSupremacistRace}.`);
		}
		if (A.FSRomanRevivalistLaw === 1) {
			buffer.push(`Every citizen has military responsibilities, which are a point of pride to many, with most opting to wear utilitarian clothing even when off duty.`);
		}
		if (A.FSAztecRevivalistLaw === 1) {
			buffer.push(`Most citizens wear satin loincloths and cloaks, distinguished from those of slaves by the richness of their adornments.`);
		}
		if (A.FSMaturityPreferentialistLaw === 1) {
			buffer.push(`Most citizens are at least middle-aged, and graying hair on a citizen is considered attractive here.`);
		} else if (A.FSYouthPreferentialistLaw === 1) {
			buffer.push(`Most citizens shine with youth and enthusiasm.`);
		}
		if (A.FSGenderRadicalistDecoration === 100) {
			buffer.push(`Every single one of the slaves is female by virtue of her fuckable asshole.`);
		} else if (A.FSGenderFundamentalistSMR === 1) {
			buffer.push(`Almost every citizen is an upstanding man, while the slave population is almost completely female.`);
		}
		if (A.FSEgyptianRevivalistLaw === 1) {
			buffer.push(`Close relationships between citizens and slaves, especially slave siblings, are common.`);
		} else if (A.FSEgyptianRevivalistIncestPolicy === 1) {
			buffer.push(`Close relationships between citizens, slaves and siblings are common.`);
		}
		if (A.FSSubjugationistLawME === 1) {
			buffer.push(`${capFirstChar(A.FSSubjugationistRace)} subhumans form a majority of the slaves.`);
		}
		if (A.FSChattelReligionistLaw === 1) {
			buffer.push(`The slave population as a whole is unusually accepting of its station.`);
		}
		if (A.FSPaternalistLaw === 1) {
			buffer.push(`The slaves are well cared for, and it can sometimes be difficult to tell slaves from citizens.`);
		} else if (A.FSDegradationistLaw === 1) {
			buffer.push(`Most of the slaves are recent captures, since the vicious society that's taken root here uses people up quickly.`);
		}
		if (A.FSBodyPuristLaw === 1) {
			buffer.push(`The average slave is quite healthy.`);
		} else if (A.FSTransformationFetishistSMR === 1) {
			if (A.FSTransformationFetishistResearch === 1) {
				buffer.push(`Breast implants are almost universal; ${A.FSSlimnessEnthusiast === "unset" ? "an M-cup bust is below average among the slave population" : "even the most lithe slave sports a pair of overly round chest balloons"}.`);
			} else {
				buffer.push(`Breast implants are almost universal; ${A.FSSlimnessEnthusiast === "unset" ? "a D-cup bust is below average among the slave population" : "even the most lithe slave sports a pair of overly round chest balloons"}.`);
			}
		}
		if (A.FSIntellectualDependencySMR === 1) {
			buffer.push(`The average slave is entirely dependent on its master.`);
		} else if (A.FSSlaveProfessionalismSMR === 1) {
			buffer.push(`The average slave is entirely capable of acting on its master's behalf.`);
		}
		if (A.FSSlimnessEnthusiastSMR === 1) {
			buffer.push(`Most of the slave population is quite slim and physically fit.`);
		} else if (A.FSAssetExpansionistSMR === 1) {
			buffer.push(`${A.name}'s consumption of pharmaceuticals is impressive, since slave growth hormones are nearly ubiquitous.`);
		}
		if (A.FSPetiteAdmirationSMR === 1) {
			buffer.push(`Slaves are both easy to identify, but hard to find in a crowd given their short stature.`);
		} else if (A.FSStatuesqueGlorificationSMR === 1) {
			buffer.push(`${A.name}'s ${A.FSStatuesqueGlorificationLaw === 1 ? "entire" : "slave"} population stands taller than most visitors.`);
		}
		if (A.FSRepopulationFocusSMR === 1) {
			buffer.push(`Most of the slaves in the arcology are sporting pregnant bellies.`);
		} else if (A.FSRestartSMR === 1) {
			buffer.push(`Most of the slaves in the arcology are infertile.`);
		}
		if (A.FSRestartLaw === 1) {
			if (V.ACitizens < 600) {
				buffer.push(`Nearly every free woman is a member of the Elite and some degree pregnant.`);
			} else {
				buffer.push(`It isn't uncommon to see a free woman pregnant and proudly bearing the insignia of the Elite.`);
			}
		}
		if (A.FSRepopulationFocusLaw === 1) {
			buffer.push(`It isn't uncommon to see free women proudly sporting waist sizes over 100cm either.`);
		}
		if (A.FSPastoralistLaw === 1) {
			buffer.push(`Much of the menial slave labor force works to service ${A.name}'s hundreds of human cattle.`);
		}
		if (A.FSPhysicalIdealistSMR === 1) {
			buffer.push(`${A.name} must import a very large quantity of nutritive protein to nourish its slaves.`);
		}
		if (A.FSHedonisticDecadenceSMR === 1) {
			buffer.push(`${A.name} must import a very large quantity of fattening food to plump up its slaves.`);
		}

		return buffer.join(" ");
	}

	citizenDiv.append(fsLaws());

	citizenDiv.append(` Its lingua franca is ${V.language}.`);
	fragment.append(citizenDiv);

	/**
	 * @returns {HTMLDivElement}
	 */
	function peacekeepers() {
		const div = document.createElement("div");
		div.classList.add("indent");

		if (V.plot) {
			if (V.peacekeepers === 0) {
				if (V.invasionVictory) {
					div.append(`The area previously occupied by the little old world country whose collapse led to a failed invasion of the Free City is a lawless wilderness.`);
				} else if (V.week > 29) {
					div.append(`A small old world country near the arcology is in the process of collapse${V.nationHate ? ", and your opportunistic behavior towards it has caused hatred there" : ""}.`);
				} else {
					div.append(`There is nothing notable about the decaying old world countries ${V.terrain === "oceanic" ? "on the shoreline nearest" : "near"} the arcology.`);
				}
			} else {
				if (V.peacekeepers.strength >= 50) {
					div.append(`General ${V.peacekeepers.generalName} now governs an area near the Free City as a warlord, using the men and women of his former peacekeeping forces to rule.`);
					if (V.peacekeepers.attitude >= 100) {
						div.append(` The area is a de facto client state of your arcology.`);
					} else {
						div.append(` He considers himself indebted to you, and delivers periodic tributes of menial slaves.`);
					}
				} else {
					// TODO create an actual framework for this behavior (only refreshing a specific element)
					const undermine = function undermine() {
						const fragment = document.createDocumentFragment();

						fragment.append(`There's a peacekeeping force led by General ${V.peacekeepers.generalName} in the troubled area near the Free City.`);

						if (V.peacekeepers.undermining > 0) {
							fragment.append(` You are spending ¤`,
								App.UI.DOM.makeTextBox(V.peacekeepers.undermining, v => {
									V.peacekeepers.undermining = Math.clamp(Math.ceil(v / 1000) * 1000, 0, 10000);
									refresh();
								}, true),
								` each week to promote misinformation in the old world that undermines the peacekeepers.`);
						} else {
							fragment.append(`You are not undermining the peacekeepers. `,
								App.UI.DOM.link("Start a misinformation campaign", () => {
									V.peacekeepers.undermining = 1000;
									refresh();
								}));
						}

						return fragment;

						function refresh() {
							span.innerHTML = "";
							span.append(undermine());
						}
					};

					const span = document.createElement("span");
					span.append(undermine());
					div.append(span);
				}
			}
		}
		return div;
	}

	const peace = peacekeepers();
	if (lastElement !== undefined) {
		peace.append(" ", lastElement);
	}
	fragment.append(peace);

	return fragment;
};