code:
// cSpell:ignore glowy

App.UI.Player = {};

App.UI.Player.appearance = function(options, summary = false) {
	let option;

	options.addOption("Your nationality is", "nationality", V.PC).showTextBox()
		.addValueList(App.Data.misc.baseNationalities)
		.addComment("For best result capitalize it.").pulldown();

	options.addOption(`Your current ethnicity is`, "race", V.PC).showTextBox()
		.addValueList(Array.from(App.Data.misc.filterRacesBase, (k => [k[1], k[0]])));

	if (V.cheatMode) {
		options.addOption("Your original ethnicity is", "origRace", V.PC).showTextBox()
			.addValueList(Array.from(App.Data.misc.filterRacesBase, (k => [k[1], k[0]])));
	}

	options.addOption("You are genetically", "genes", V.PC)
		.addValue("XY").addValue("XX");

	if (V.PC.physicalAge >= 20) {
		options.addOption(`You are`, "height", V.PC.natural).showTextBox({unit: "cm"})
			.addRange(145, 150, "<", "Petite")
			.addRange(155, 160, "<", "Short")
			.addRange(165, 170, "<", "Average")
			.addRange(180, 185, "<", "Tall")
			.addRange(190, 185, ">=", "Very tall")
			.addComment(`Average height is ${heightToEitherUnit(Height.mean(V.PC))}`)
			.addGlobalCallback(() => V.PC.height = V.PC.natural.height);
	} else {
		options.addOption(`When full-grown, you will be`, "height", V.PC.natural).showTextBox({unit: "cm"})
			.addRange(145, 150, "<", "Petite")
			.addRange(155, 160, "<", "Short")
			.addRange(165, 170, "<", "Average")
			.addRange(180, 185, "<", "Tall")
			.addRange(190, 185, ">=", "Very tall");
	}
	option = options.addCustomOption()
		.addButton(
			"Make average",
			() => resyncSlaveHeight(V.PC),
			""
		);
	if (V.PC.physicalAge < 20) {
		options.addOption(`But right now, you are`, "height", V.PC).showTextBox({unit: "cm"})
			.addRange(Height.forAge(145, V.PC), Height.forAge(150, V.PC), "<", "Petite for your age")
			.addRange(Height.forAge(155, V.PC), Height.forAge(160, V.PC), "<", "Short for your age")
			.addRange(Height.forAge(165, V.PC), Height.forAge(170, V.PC), "<", "Average for your age")
			.addRange(Height.forAge(180, V.PC), Height.forAge(185, V.PC), "<", "Tall for your age")
			.addRange(Height.forAge(190, V.PC), Height.forAge(185, V.PC), ">=", "Very tall for your age")
			.addComment(`Average height for a ${V.PC.physicalAge} year old is ${heightToEitherUnit(Height.mean(V.PC))}`);
		option = options.addCustomOption()
			.addButton(
				"Scale for age from adult height",
				() => V.PC.height = Height.forAge(V.PC.natural.height, V.PC),
				""
			);
	}

	options.addOption("Your skin tone is", "skin", V.PC).showTextBox()
		.addValueList(makeAList(V.PC.race === "catgirl" ? App.Medicine.Modification.catgirlNaturalSkins : App.Medicine.Modification.naturalSkins));

	if (V.cheatMode) {
		options.addOption("Your genetic skin tone is", "origSkin", V.PC).showTextBox()
			.addValueList(makeAList(V.PC.race === "catgirl" ? App.Medicine.Modification.catgirlNaturalSkins : App.Medicine.Modification.naturalSkins));
	}

	if (V.cheatMode) {
		options.addOption("Your original hair is", "origHColor", V.PC).showTextBox()
			.addValueList(makeAList(App.Medicine.Modification.Color.Primary.map(color => color.value)));
	} else {
		options.addOption("Your hair is", "hColor", V.PC).showTextBox()
			.addValueList(makeAList(App.Medicine.Modification.Color.Primary.map(color => color.value)));
	}

	options.addOption("Your genetic eye color is", "origColor", V.PC.eye).showTextBox()
		.addValueList(makeAList(App.Medicine.Modification.eyeColor.map(color => color.value)));

	options.addOption("You", "vision", V.PC.eye.right)
		.addValueList([
			["Need glasses to see properly", 1, () => { V.PC.eye.right.vision = 1; V.PC.eye.left.vision = 1; }],
			["have normal vision", 2, () => { V.PC.eye.right.vision = 2; V.PC.eye.left.vision = 2; }]
		]);

	options.addOption("Your face is", "faceShape", V.PC)
		.addValueList([
			["Normal", "normal"],
			["Androgynous", "androgynous"],
			["Masculine", "masculine"],
			["Cute", "cute"],
			["Sensual", "sensual"],
			["Exotic", "exotic"]
		]);

	options.addOption("Your body", "markings", V.PC)
		.addValueList([["Is clear of blemishes", "none"], ["Has light freckling", "freckles"], ["Has heavy freckling", "heavily freckled"]]);

	if (V.PC.geneticQuirks.albinism === 2 || V.PC.skin === "pure white" || V.PC.eye.origColor === "red" || V.PC.hColor === "white") {
		options.addOption("You are", "albinism", V.PC.geneticQuirks)
			.addValueList([
				["An albino", 2],
				["Not an albino", 0],
			])
			.addGlobalCallback((val) => induceAlbinism(V.PC, val))
			.addComment("Once this is set, you may change your hair/eyes/skin without worry. You will generate as an albino.");
	}

	options.addOption("You have", "lips", V.PC).addValue("Thin lips", 5)
		.addValueList([
			["Normal lips", 15],
			["Kissable lips", 25],
			["Plush lips", 45],
		]);

	options.addOption("You are", "weight", V.PC)
		.addValueList([
			["Thin", -20],
			["Healthy", 0],
			["Curvy", 20],
			["Chubby", 60],
		]);

	option = options.addOption("Your shoulders are", "shoulders", V.PC).addValue("Very narrow", -2);
	option.addValueList([
		["Narrow", -1],
		["Average", 0],
	]);
	if (V.PC.physicalAge > 13) {
		option.addValue("Broad", 1);
	}
	if (V.PC.physicalAge > 18) {
		option.addValue("Very broad", 2);
	}

	options.addOption(`Your armpits`, "underArmHStyle", V.PC)
		.addValueList([["Are naturally hairless", "hairless"], ["Can grow hair", "neat"]]);

	if (V.PC.boobs >= 200 || summary) {
		if (V.PC.title === 1 && V.PC.boobs <= 200) {
			option = options.addOption("Your chest is", "boobs", V.PC).addValue("Manly", 200, () => { V.PC.boobsImplant = 0; V.PC.boobsImplantType = "none"; });
		} else {
			option = options.addOption("Your breasts are", "boobs", V.PC).addValue("Non-existent", 200, () => { V.PC.boobsImplant = 0; V.PC.boobsImplantType = "none"; });
		}
		option.addValueList([
			["A-cups", 300],
			["B-cups", 400],
			["C-cups", 500],
		]);
		if (V.PC.physicalAge <= 13) {
			option.addValueList([
				["hefty D-cups", 650],
				["heavy DD-cups", 900],
			]);
		} else {
			option.addValueList([
				["D-cups", 650],
				["DD-cups", 900],
				["F-cups", 1100],
				["G-cups", 1300]
			]);
		}
		option.showTextBox({unit: "CCs"});
		option.addCallback(() => V.PC.natural.boobs = V.PC.boobs);
		if (V.PC.boobs >= 500) {
			options.addOption("Your breasts are", "boobsImplant", V.PC)
				.addValueList([
					["All natural", 0, () => { V.PC.boobsImplant = 0; V.PC.boobsImplantType = "none"; }],
					["Fake", V.PC.boobs / 2, () => V.PC.boobsImplantType = "normal"]
				]);
		}
		if (V.PC.boobsImplant > 0) {
			option = options.addOption("You have", "boobsImplantType", V.PC).addValue("Regular implants", "normal");
			if (V.PC.boobsImplant > 400) {
				option.addValue("string implants", "string");
			}
			if (V.PC.boobsImplant > 600) {
				option.addValue("Fillable implants", "fillable");
			}
		}
		if (V.PC.boobs >= 300) {
			option = options.addOption("Your nipples are", "nipples", V.PC).addValue("Tiny", "tiny");
			option.addValue("Cute", "cute");
			if (V.PC.boobs >= 500) {
				option.addValue("Puffy", "puffy");
				option.addValue("Partially inverted", "partially inverted");
			}
			if (V.PC.boobs >= 1000) {
				option.addValue("Inverted", "inverted");
				option.addValue("Huge", "huge");
			}
		}
		if (V.PC.genes === "XY" && V.PC.boobs <= 200) {
			option = options.addOption("If you were a woman, there is no doubt your breasts would be", "boobs", V.PC.natural);
		} else if (V.PC.physicalAge < 18) {
			option = options.addOption("When you finish growing, you feel like your breasts would be", "boobs", V.PC.natural);
		} else {
			option = options.addOption("If you were to grow up all over again, your breasts would be", "boobs", V.PC.natural);
		}
		option.addValueList([
			["non-existent", 200],
			["A-cups", 300],
			["B-cups", 400],
			["C-cups", 500],
			["D-cups", 650],
			["DD-cups", 900],
			["F-cups", 1100],
			["G-cups", 1300],
			["H-cups", 1500],
			["I-cups", 1700],
			["J-cups", 2000]
		]);
		option.showTextBox({unit: "CCs"});
		option.addComment(`Your genetic breast size`);
	}

	option = options.addOption("Your hips are", "hips", V.PC).addValue("Very narrow", -2);
	option.addValueList([
		["Narrow", -1],
		["Average", 0],
	]);
	if (V.PC.physicalAge > 13) {
		option.addValue("Wide", 1);
	}
	if (V.PC.physicalAge > 18) {
		option.addValue("Very Wide", 2);
	}

	option = options.addOption("Your butt is", "butt", V.PC).addValue("Flat", 0, () => { V.PC.buttImplant = 0; V.PC.buttImplantType = "none"; });
	option.addValueList([
		["Small", 1],
		["Plump", 2],
		["Big", 3],
	]);
	if (V.PC.physicalAge > 13) {
		option.addValue("Huge", 4);
	}
	if (V.PC.physicalAge > 18) {
		option.addValueList([
			["Enormous", 5],
			["Gigantic", 6],
		]);
	}
	if (V.PC.butt >= 3) {
		options.addOption("Your ass is", "buttImplant", V.PC)
			.addValueList([
				["All natural", 0, () => V.PC.buttImplantType = "none"],
				["Fake", Math.round(V.PC.butt / 2), () => V.PC.buttImplantType = "normal"]
			]);
	}
	if (V.PC.buttImplant > 0) {
		option = options.addOption("You have", "buttImplantType", V.PC).addValue("Regular implants", "normal");
		if (V.PC.buttImplant > 1) {
			option.addValue("string implants", "string");
			option.addValue("Fillable implants", "fillable");
		}
	}

	if (V.PC.dick !== 0) {
		option = options.addOption("Your dick is", "dick", V.PC).addValue("Tiny", 1, () => V.PC.foreskin = 2);
		option.addValueList([
			["Small", 2, () => V.PC.foreskin = 3],
			["Average", 3, () => V.PC.foreskin = 3],
			["Big", 4, () => V.PC.foreskin = 4],
		]);
		if (V.PC.physicalAge > 13) {
			option.addValue("Huge", 5, () => V.PC.foreskin = 5);
		}
		if (V.PC.physicalAge > 18) {
			option.addValue("Gigantic", 6, () => V.PC.foreskin = 5);
		}
		options.addOption("You are", "foreskin", V.PC)
			.addValueList([
				["Cut", 0],
				["Uncut", V.PC.dick]
			])
			.showTextBox()
			.addComment("Any value above 0 is uncircumcised. For comfort, keep equal or one greater than dick size.");
	}

	if (V.PC.balls !== 0) {
		option = options.addOption("Your balls are", "balls", V.PC).addValue("Small", 2, () => V.PC.scrotum = 2);
		option.addValueList([
			["Average", 3, () => V.PC.scrotum = 4],
			["Large", 4, () => V.PC.scrotum = 5],
		]);
		if (V.PC.physicalAge > 13) {
			option.addValue("Huge", 5, () => V.PC.scrotum = 5);
		}
		if (V.PC.physicalAge > 18) {
			option.addValue("Massive", 6, () => V.PC.scrotum = 6);
		}
		option.addComment("Small balls may be located internally.");
		if (V.PC.balls <= 2) {
			options.addOption("Your balls are", "scrotum", V.PC)
				.addValueList([
					["Internal", 0],
					["External", V.PC.balls]
				])
				.showTextBox()
				.addComment("Any value above 0 is external. For comfort, keep equal or one greater than ball size.");
		}
		if (V.PC.physicalAge < 14) {
			options.addOption("You are", "pubertyXY", V.PC)
				.addValueList([["Not producing potent sperm yet", 0], ["Producing potent sperm", 1]]);
		}
		if (V.PC.pubertyXY === 1) {
			options.addOption("You went through puberty when you were", "pubertyAgeXY", V.PC).showTextBox({unit: "years old."});
		}
	}

	if (V.PC.vagina !== -1) {
		if (V.PC.dick === 0 && V.PC.physicalAge > 13) {
			options.addOption("Your clit is", "clit", V.PC)
				.addValueList([["Normal", 0, () => V.PC.foreskin = 1],
					["Large", 1, () => V.PC.foreskin = 2],
					["Huge", 2, () => V.PC.foreskin = 3],
					["Enormous", 3, () => V.PC.foreskin = 4]
				]);
			options.addOption("Its hood is", "foreskin", V.PC)
				.addValueList([
					["Exposed by a circumcision", 0],
					["Covered by a hood", V.PC.clit + 1]
				])
				.showTextBox()
				.addComment("Any value above 0 is uncircumcised. A hood that is too large can reduce pleasure, while no hood or one that is too small can lead to overstimulation.");
		}
		if (V.PC.physicalAge <= 18) {
			options.addOption("You are", "vagina", V.PC)
				.addValueList([["A virgin", 0], ["Not a virgin", 1]]);
			if (V.PC.physicalAge < 14 && V.PC.preg <= 0) {
				options.addOption("You have", "pubertyXX", V.PC)
					.addValueList([["Not had your first period", 0], ["Had your first period", 1]]);
			}
		}
		if (V.PC.pubertyXX === 1) {
			options.addOption(`You ${V.PC.pubertyXY === 1 ? "had your first period" : "went through puberty"} when you were`, "pubertyAgeXX", V.PC).showTextBox({unit: "years old."});
		}
	}

	options.addOption(`You`, "pubicHStyle", V.PC)
		.addValueList([["Are naturally hairless", "hairless"], ["Can grow pubic hair", "neat"]]);

	options.addOption(`Your pubic hair ${V.PC.physicalAge >= Math.min(V.PC.pubertyAgeXX, V.PC.pubertyAgeXY) - 2 && !["bald", "hairless", "waxed"].includes(V.PC.pubicHStyle) ? "is" : "would be"}`, "pubicHColor", V.PC).showTextBox()
		.addValueList(makeAList(App.Medicine.Modification.Color.Primary.map(color => color.value)))
		.addValue("Match the drapes", V.PC.hColor);

	options.addOption("You are", "anus", V.PC)
		.addValueList([["An anal virgin", 0], ["Not an anal virgin", 1]]);

	if (V.PC.vagina > -1) {
		options.addOption("You would prefer being penetrated in the", "preferredHole", V.PC)
			.addValueList([["No preference", -1], ["Vagina", 1], ["Anus", 0]]);
	}

	function makeAList(iterable) {
		return Array.from(iterable, (k => [capFirstChar(k), k]));
	}
};

App.UI.Player.syncAgeBasedParameters = function() {
	V.PC.actualAge = Math.clamp(V.PC.actualAge, 10, 80);
	V.PC.physicalAge = V.PC.actualAge;
	V.PC.visualAge = V.PC.actualAge;
	V.PC.ovaryAge = V.PC.actualAge;
	V.PC.height = Height.forAge(V.PC.natural.height, V.PC);
	if (V.PC.genes === "XY") {
		if (V.PC.physicalAge <= 13) {
			V.PC.hips = -2;
			V.PC.shoulders = -1;
			V.PC.butt = 0;
			V.PC.boobs = 100;
			if (V.PC.vagina !== -1) {
				V.PC.vagina = 0;
				V.PC.vaginaLube = 0;
				if (V.PC.preg <= 0) {
					V.PC.pubertyXX = 0;
				}
			}
			V.PC.pregAdaptation = 10;
			if (V.PC.dick !== 0) {
				V.PC.dick = 2;
				V.PC.balls = 2;
				V.PC.scrotum = V.PC.balls + 1;
				V.PC.foreskin = V.PC.dick;
				V.PC.pubertyXY = 0;
			}
		} else if (V.PC.physicalAge <= 18) {
			V.PC.hips = -2;
			V.PC.shoulders = 0;
			V.PC.butt = 1;
			V.PC.boobs = 200;
			if (V.PC.vagina !== -1) {
				V.PC.vagina = 1;
				V.PC.vaginaLube = 0;
				V.PC.pubertyXX = 1;
			}
			V.PC.pregAdaptation = 15;
			if (V.PC.dick !== 0) {
				V.PC.dick = 3;
				V.PC.balls = 3;
				V.PC.scrotum = V.PC.balls;
				V.PC.foreskin = V.PC.dick;
				V.PC.pubertyXY = 1;
			}
		} else {
			V.PC.hips = -1;
			V.PC.shoulders = 1;
			V.PC.butt = 2;
			V.PC.boobs = 200;
			if (V.PC.vagina !== -1) {
				V.PC.vagina = 1;
				V.PC.vaginaLube = 1;
				V.PC.pubertyXX = 1;
			}
			V.PC.pregAdaptation = 20;
			if (V.PC.dick !== 0) {
				V.PC.dick = 4;
				V.PC.balls = 3;
				V.PC.scrotum = V.PC.balls + 1;
				V.PC.foreskin = V.PC.dick;
				V.PC.pubertyXY = 1;
			}
		}
	} else {
		if (V.PC.physicalAge <= 13) {
			V.PC.hips = -2;
			V.PC.shoulders = -2;
			V.PC.butt = 0;
			V.PC.boobs = 350;
			if (V.PC.vagina !== -1) {
				V.PC.vagina = 0;
				V.PC.vaginaLube = 0;
				if (V.PC.preg <= 0) {
					V.PC.pubertyXX = 0;
				}
			}
			V.PC.pregAdaptation = 30;
			if (V.PC.dick !== 0) {
				V.PC.dick = 2;
				V.PC.balls = 2;
				V.PC.scrotum = V.PC.balls + 1;
				V.PC.foreskin = V.PC.dick;
				V.PC.pubertyXY = 0;
			}
		} else if (V.PC.physicalAge <= 18) {
			V.PC.hips = 0;
			V.PC.shoulders = -1;
			V.PC.butt = 1;
			V.PC.boobs = 600;
			if (V.PC.vagina !== -1) {
				V.PC.vagina = 1;
				V.PC.vaginaLube = 1;
				V.PC.pubertyXX = 1;
			}
			V.PC.pregAdaptation = 50;
			if (V.PC.dick !== 0) {
				V.PC.dick = 3;
				V.PC.balls = 3;
				V.PC.scrotum = V.PC.balls;
				V.PC.foreskin = V.PC.dick;
				V.PC.pubertyXY = 1;
			}
		} else {
			V.PC.hips = 1;
			V.PC.shoulders = 0;
			V.PC.butt = 2;
			V.PC.boobs = 900;
			if (V.PC.vagina !== -1) {
				V.PC.vagina = 1;
				V.PC.vaginaLube = 1;
				V.PC.pubertyXX = 1;
			}
			V.PC.pregAdaptation = 50;
			if (V.PC.dick !== 0) {
				V.PC.dick = 4;
				V.PC.balls = 3;
				V.PC.scrotum = V.PC.balls + 1;
				V.PC.foreskin = V.PC.dick;
				V.PC.pubertyXY = 1;
			}
		}
		if (V.PC.dick === 0 && V.PC.vagina >= 0) {
			V.PC.foreskin = V.PC.clit + 1;
		}
		if (V.PC.boobs < 250) {
			V.PC.nipples = "tiny";
		} else if (V.PC.boobs < 1000) {
			V.PC.nipples = "cute";
		} else {
			V.PC.nipples = "puffy";
		}
	}
};

App.UI.Player.assignCareerByAge = function(selection) {
	let career;

	if (V.disableForcedCareers) {
		career = selection;
	} else {
		for (const data of App.Data.player.career.values()) {
			if (Object.values(data).includes(selection)) {
				if (V.PC.actualAge <= 13) {
					career = data.child;
				} else if (V.PC.actualAge <= 21) {
					career = data.apprentice;
				}
				career = career || data.master;
			}
		}
	}

	V.disableForcedCareers = null;

	return career;
};

/**
 * @param {InstanceType<App.UI.OptionsGroup>} options
 * @param {boolean} blackMarket if true then black market exclusive refreshments will be listed for selection
 */
App.UI.Player.refreshmentChoice = function(options, blackMarket = false) {
	let option = options.addOption("Your preferred refreshment is", "refreshmentType", V.PC);
	for (const [key, value] of App.Data.player.refreshmentType) {
		option.addValue(value.name, key, () => { V.PC.refreshment = value.suggestions.values().next().value; } );
	}
	let comment = `${blackMarket && (V.PC.ovaries === 1 || V.PC.mpreg === 1) ? "" : "Flavor only; no mechanical effect. "}If entering a custom refreshment, please assign proper usage.`;
	if (V.PC.refreshmentType === 0) {
		comment += ` "Smoked" must fit into the following sentence: "I smoked a ${V.PC.refreshment}" to fit events properly.`;
	} else if (V.PC.refreshmentType === 5) {
		comment += ` "Popped" must fit into the following sentence: "I shook the bottle of ${V.PC.refreshment}" to fit events properly.`;
	} else if (V.PC.refreshmentType === 6) {
		comment += ` "Orally Dissolved" must fit into the following sentence: "I placed a tab of ${V.PC.refreshment} under my tongue" to fit events properly.`;
	}
	option.addComment(comment);

	option = options.addOption("Specifically", "refreshment", V.PC).showTextBox().pulldown();
	for (const refreshment of App.Data.player.refreshmentType.get(V.PC.refreshmentType).suggestions) {
		option.addValue(capFirstChar(refreshment), refreshment);
	}
	if (V.PC.refreshmentType === 1 && blackMarket && (V.PC.ovaries === 1 || V.PC.mpreg === 1)) {
		option.addValue("Fertility Syrup", "fertility syrup");
	} else if (V.PC.refreshmentType === 4 && blackMarket && (V.PC.ovaries === 1 || V.PC.mpreg === 1)) {
		option.addValue("Fertility Booster", "fertility booster");
	}
};

/**
 * @param {InstanceType<App.UI.OptionsGroup>} options
 */
App.UI.Player.names = function(options) {
	options.addCustomOption(`Everyone calls you <b>${PlayerName()}.</b>`);
	options.addOption("Your given name is", "slaveName", V.PC).showTextBox();

	if (V.cheatMode) {
		options.addOption("Birth Name", "birthName", V.PC).showTextBox();
	}

	if (V.PC.slaveSurname === 0) {
		options.addOption("And no surname", "slaveSurname", V.PC)
			.addValue("Add a surname", "Anon")
			.addComment("Surnames cannot be changed during the game outside of special circumstances.");
	} else {
		options.addOption("And your surname is", "slaveSurname", V.PC).showTextBox()
			.addValue("Go by a single name", 0)
			.addComment("Surnames cannot be changed during the game outside of special circumstances.");
		if (V.cheatMode) {
			options.addOption("Birth Surname", "birthSurname", V.PC).showTextBox();
		}
	}
};

App.UI.Player.design = function() {
	const el = new DocumentFragment();
	let options = new App.UI.OptionsGroup();
	let option;
	let r;
	let linkArray;
	const allowEdits = (V.freshPC === 1 || V.saveImported === 0 || V.cheatMode);

	// Title / age
	if (allowEdits) {
		options.addOption("You are a", "title", V.PC)
			.addValue("Masculine Master", 1, () => V.PC.genes = "XY").addValue("Feminine Mistress", 0, () => V.PC.genes = "XX");

		App.UI.Player.names(options);

		V.PC.physicalAge = V.PC.actualAge;
		V.PC.visualAge = V.PC.actualAge;
		V.PC.ovaryAge = V.PC.actualAge;
		if (V.cheatMode) {
			options.addOption("Actual Age", "actualAge", V.PC).showTextBox();
			options.addOption("Physical Age", "physicalAge", V.PC).showTextBox();
			options.addOption("Visual Age", "visualAge", V.PC).showTextBox();
			options.addOption("Ovary Age", "ovaryAge", V.PC).showTextBox();
			options.addOption("Age Implant", "ageImplant", V.PC).addValue("Age altering surgery", 1).on().addValue("No surgery", 0).off();
		} else {
			options.addOption("You are", "actualAge", V.PC).showTextBox()
				.addRange(25, 35, "<", "Surprisingly young").addRange(40, 50, "<", "Entering middle age")
				.addRange(55, 65, "<", "Well into middle age").addRange(70, 65, ">=", "Old");

			options.addOption(`Your birthday was <strong>${V.PC.birthWeek}</strong> weeks ago.`, "birthWeek", V.PC).showTextBox();
			option = options.addCustomOption("")
				.addButton(
					"Adjust body to match age",
					() => App.UI.Player.syncAgeBasedParameters(),
					""
				);
		}
	} else {
		r = [];
		r.push(`You are a`);
		if (V.PC.title === 1) {
			r.push(`masculine <strong>Master</strong>`);
		} else {
			r.push(`feminine <strong>Mistress</strong>`);
		}
		r.push(`and everyone that matters calls you	${PlayerName()}.`);

		r.push(`You are ${V.PC.actualAge} years old, which is`);
		if (V.PC.actualAge >= 65) {
			r.push(`<strong>old</strong>.`);
		} else if (V.PC.actualAge >= 50) {
			r.push(`<strong>well into middle age</strong>.`);
		} else if (V.PC.actualAge >= 35) {
			r.push(`<strong>entering middle age</strong>.`);
		} else if (V.PC.actualAge >= 22) {
			r.push(`<strong>surprisingly young</strong>.`);
		} else if (V.PC.actualAge >= 14) {
			r.push(`<strong>exceedingly young</strong>.`);
		} else {
			r.push(`<strong>merely a child</strong>.`);
		}
		App.Events.addNode(el, r, "p");
	}

	option = options.addOption("Player aging is", "playerAging")
		.addValue("Enabled", 2).on().addValue("Celebrate birthdays, but don't age.", 1).addValue("Disabled", 0).off();
	if (!V.cheatMode) {
		option.addComment("This option cannot be changed during the game.");
	}

	if (V.PC.customTitle) {
		options.addOption("Custom title", "customTitle", V.PC).showTextBox()
			.addValue("Reset to default", "", () => {
				delete V.PC.customTitle;
				delete V.PC.customTitleLisp;
			});

		options.addOption("Lisped custom title", "customTitleLisp", V.PC).showTextBox()
			.addComment('If using a custom title, select Master or Mistress to set the gender of your title. Make sure to replace your "s"s with "th"s to have working lisps in your lisped title.');
	} else {
		options.addCustomOption("Custom title")
			.addButton("Set custom title", () => {
				V.PC.customTitle = "Master";
				V.PC.customTitleLisp = 'Mather';
			});
	}

	// Appearance
	if (allowEdits) {
		if (V.PC.vagina !== -1 && V.PC.dick !== 0) {
			State.temporary.vaginaPenis = 2;
		} else if (V.PC.vagina !== -1) {
			State.temporary.vaginaPenis = 1;
		} else {
			State.temporary.vaginaPenis = 0;
		}

		option = options.addOption("You have a", "vaginaPenis", State.temporary)
			.addValue("Penis", 0, () => {
				V.PC.preg = 0;
				V.PC.pregType = 0;
				V.PC.dick = 4;
				V.PC.balls = 3;
				V.PC.scrotum = 3;
				V.PC.prostate = 1;
				V.PC.vagina = -1;
				V.PC.ovaries = 0;
			}).addValue("Vagina", 1, () => {
				V.PC.dick = 0;
				V.PC.balls = 0;
				V.PC.scrotum = 0;
				V.PC.prostate = 0;
				V.PC.vagina = 1;
				V.PC.ovaries = 1;
			}).addValue("Penis and Vagina", 2, () => {
				V.PC.dick = 4;
				V.PC.balls = 3;
				V.PC.scrotum = 3;
				V.PC.prostate = 1;
				V.PC.vagina = 1;
				V.PC.ovaries = 1;
			});
		if (State.temporary.vaginaPenis === 0) {
			option.addComment("Standard sex scenes; easiest reputation maintenance.");
		} else if (State.temporary.vaginaPenis === 1) {
			option.addComment("Sex scene variations; most difficult reputation maintenance.");
		} else {
			option.addComment("Sex scene variations; more difficult reputation maintenance; some unique opportunities, especially with breasts.");
		}
		App.UI.Player.appearance(options, true);
	} else {
		r = [];

		r.push(`You are a ${heightToEitherUnit(V.PC.height)} tall ${V.PC.nationality} ${V.PC.race} with`);
		if (V.PC.markings === "heavily freckled") {
			r.push(`heavily freckled`);
		} else if (V.PC.markings === "freckles") {
			r.push(`lightly freckled`);
		} else {
			r.push(`clear`);
		}
		r.push(`${V.PC.skin} skin, ${V.PC.hColor} hair and ${App.Desc.eyesColor(V.PC)}. You have a ${V.PC.faceShape}`);
		if (V.PC.lips > 95) {
			r.push(`face with way too much lip; you can't shut your mouth properly anymore.`);
		} else if (V.PC.lips > 70) {
			r.push(`face with truly massive lips.`);
		} else if (V.PC.lips > 40) {
			r.push(`face with plump lips.`);
		} else if (V.PC.lips > 20) {
			r.push(`face with full lips.`);
		} else {
			r.push(`face.`);
		}
		App.Events.addNode(el, r, "p");
	}

	// Refresh
	App.UI.Player.refreshmentChoice(options);

	// History
	if (allowEdits) {
		option = options.addOption("Before you came to the Free Cities, you were a", "career", V.PC);
		// option.addValue("Test subject", "test subject");
		if (V.PC.career === "arcology owner") {
			option.addValue("Arcology owner", "arcology owner");
		} else {
			option.addValueList([
				["Member of the idle wealthy", "wealth"],
				["Business leader", "capitalist"],
				["Mercenary", "mercenary"],
				["Slaver", "slaver"],
				["Engineer", "engineer"],
				["Doctor", "medicine"],
				["Hacker", "BlackHat"],
				["Minor celebrity", "celebrity"],
				["Escort", "escort"],
				["Servant", "servant"],
				["Gang leader", "gang"]]
			);
			if (PCCareerTier() === "child") {
				option.addValueList([
					["Kid with a lot of money", "rich kid"],
					["Business kid", "business kid"],
					["Child soldier", "child soldier"],
					["Slave tender", "slave tender"],
					["Worksite helper", "worksite helper"],
					["Nurse", "nurse"],
					["Wannabe hacker", "script kiddy"],
					["Child star", "child star"],
					["Child prostitute", "child prostitute"],
					["Child servant", "child servant"],
					["Street urchin", "street urchin"]]
				);
			} else if (PCCareerTier() === "apprentice") {
				option.addValueList([
					["Trust funder", "trust fund"],
					["Entrepreneur", "entrepreneur"],
					["Mercenary recruit", "recruit"],
					["Slaver overseer", "slave overseer"],
					["Construction worker", "construction"],
					["Medical assistant", "medical assistant"],
					["Minor hacker", "hacker"],
					["Rising star", "rising star"],
					["Prostitute", "prostitute"],
					["Handmaiden", "handmaiden"],
					["Hoodlum", "hoodlum"]]
				);
			}
			option = options.addCustomOption("")
				.addButton(
					"Adjust career for age",
					() => V.PC.career = App.UI.Player.assignCareerByAge(V.PC.career),
					""
				);
			if (V.secExpEnabled > 0) {
				if (isPCCareerInCategory("capitalist")) {
					option.addComment(`<div><span class="yellowgreen">The propaganda hub's upgrades will be cheaper.</span></div>`);
				} else if (isPCCareerInCategory("mercenary")) {
					option.addComment(`<div><span class="positive">Easier to maintain security</span> and <span class="yellowgreen">the security HQ's upgrades will be cheaper.</span></div>`);
				} else if (isPCCareerInCategory("slaver")) {
					option.addComment(`<div><span class="positive">Easier to maintain authority</span> and <span class="yellowgreen">the security HQ's upgrades will be cheaper.</span></div>`);
				} else if (isPCCareerInCategory("engineer")) {
					option.addComment(`<div><span class="yellowgreen">construction and upgrade of facilities will be cheaper.</span></div>`);
				} else if (isPCCareerInCategory("medicine")) {
					option.addComment(`<div><span class="yellowgreen">Drug upgrades will be cheaper.</span></div>`);
				} else if (isPCCareerInCategory("celebrity")) {
					option.addComment(`<div><span class="yellowgreen">The propaganda hub's upgrades will be cheaper.</span></div>`);
				} else if (isPCCareerInCategory("escort")) {
					option.addComment(`<div><span class="red">Harder to maintain authority.</span></div>`);
				} else if (isPCCareerInCategory("servant")) {
					option.addComment(`<div><span class="red">Harder to maintain authority.</span></div>`);
				} else if (isPCCareerInCategory("gang")) {
					option.addComment(`<div><span class="positive">Easier to maintain authority</span> and <span class="yellowgreen">the security HQ's upgrades will be cheaper.</span></div>`);
				} else if (isPCCareerInCategory("BlackHat")) {
					option.addComment(`<div><span class="red">Harder to maintain authority.</span></div>`);
				} else {
					option.addComment(`<div><span class="red">Harder to maintain authority,</span> but <span class="yellowgreen">the propaganda hub's upgrades will be cheaper.</span></div>`);
				}
			}
		}

		options.addOption("It is rumored that you acquired your arcology through", "rumor", V.PC)
			.addValueList([
				["Wealth", "wealth"],
				["Hard work", "diligence"],
				["Force", "force"],
				["Social engineering", "social engineering"],
				["Blind luck", "luck"]
			]);

		el.append(options.render());
	} else {
		r = [];
		switch (V.PC.career) {
			case "wealth":
			case "trust fund":
			case "rich kid":
				r.push(`Prior to being an arcology owner, you were a member of the idle wealthy.`);
				break;
			case "capitalist":
				r.push(`Prior to being an arcology owner, you were a business leader.`);
				break;
			case "entrepreneur":
			case "business kid":
				r.push(`Prior to being an arcology owner, you were a cunning entrepreneur.`);
				break;
			case "mercenary":
			case "recruit":
				r.push(`Prior to being an arcology owner, you were a mercenary.`);
				break;
			case "child soldier":
				r.push(`Prior to being an arcology owner, you were a disposable conscript.`);
				break;
			case "slaver":
			case "slave overseer":
				r.push(`Prior to being an arcology owner, you were a slaver.`);
				break;
			case "slave tender":
				r.push(`Prior to being an arcology owner, you were a slaver tasked with looking after fresh captures.`);
				break;
			case "engineer":
				r.push(`Prior to being an arcology owner, you were an engineer.`);
				break;
			case "construction":
			case "worksite helper":
				r.push(`Prior to being an arcology owner, you built buildings.`);
				break;
			case "medicine":
				r.push(`Prior to being an arcology owner, you were a surgeon.`);
				break;
			case "medical assistant":
				r.push(`Prior to being an arcology owner, you assisted with surgeries.`);
				break;
			case "nurse":
				r.push(`Prior to being an arcology owner, you were a nurse.`);
				break;
			case "celebrity":
				r.push(`Prior to being an arcology owner, you were a minor celebrity.`);
				break;
			case "rising star":
				r.push(`Prior to being an arcology owner, you were an up-and-coming star.`);
				break;
			case "child star":
				r.push(`Prior to being an arcology owner, you were a child actor.`);
				break;
			case "BlackHat":
			case "hacker":
			case "script kiddy":
				r.push(`Prior to being an arcology owner, you specialized in cracking databases and making mockeries of cyber security.`);
				break;
			case "arcology owner":
				r.push(`Being an arcology owner defines your life now.`);
				break;
			case "escort":
			case "prostitute":
				r.push(`Prior to being an arcology owner, you knew how to survive off your looks and body.`);
				break;
			case "child prostitute":
				r.push(`Prior to being an arcology owner, you had no choice but to sell your body to survive.`);
				break;
			case "servant":
			case "handmaiden":
			case "child servant":
				r.push(`Prior to being an arcology owner, you served a well-off`);
				if (V.PC.counter.birthMaster >= 8) {
					r.push(`master as his breeder.`);
				} else if (V.PC.counter.birthMaster >= 2) {
					r.push(`master and bore him several children.`);
				} else if (V.PC.counter.birthMaster >= 1) {
					r.push(`master and bore him a child.`);
				} else {
					r.push(`master.`);
				}
				break;
			case "gang":
				r.push(`Prior to being an arcology owner, you were the leader of a ruthless gang.`);
				break;
			case "hoodlum":
				r.push(`Prior to being an arcology owner, you were a troublemaker in a gang.`);
				break;
			case "street urchin":
				r.push(`Prior to being an arcology owner, you lived a hard life on the streets.`);
				break;
		}

		r.push(`Word in the arcology is you acquired it through`);
		switch (V.PC.rumor) {
			case "wealth":
				r.push(`a rather ridiculous amount of money.`);
				break;
			case "diligence":
				r.push(`sheer effort.`);
				break;
			case "force":
				r.push(`brutal force.`);
				break;
			case "social engineering":
				r.push(`clever social manipulation.`);
				break;
			case "luck":
				r.push(`blind luck.`);
				break;
		}
		App.Events.addNode(el, r, "p");
	}

	// Sexuality
	App.UI.DOM.appendNewElement("h2", el, "Sexuality");

	if (allowEdits) {
		options = new App.UI.OptionsGroup();

		// obsolete?
		if (V.cheatMode) {
			options.addOption("Vagina", "vagina", V.PC).showTextBox();
			options.addOption("New vagina", "newVag", V.PC).showTextBox();
			options.addOption("Dick", "dick", V.PC).showTextBox();
			options.addOption("Balls", "balls", V.PC).addValueList([
				["Normal", 3],
				["Big", 5],
				["Huge", 9],
				["Monstrous", 30]
			])
				.showTextBox();
			options.addOption("Balls implant", "ballsImplant", V.PC).showTextBox();
		}

		if (V.PC.vagina !== -1 && V.PC.pubertyXX === 1) {
			option = options.addOption("You are", "preg", V.PC)
				.addValue("Taking contraceptives", -1, () => { V.PC.pregType = 0; V.PC.labor = 0; })
				.addValue("Not taking contraceptives", 0, () => { V.PC.pregType = 0; V.PC.labor = 0; })
				.addRange(16, 37, "<=", "Pregnant").addCallback(() => { V.PC.pregType = 1; V.PC.labor = 0; })
				.addRange(40, 42, "<=", "Ready to drop").addCallback(() => { V.PC.pregType = 1; V.PC.labor = 0; })
				.addRange(43, 42, ">", "Ready to drop with octuplets").addCallback(() => { V.PC.pregType = 8; V.PC.labor = 1; });
			const r =[];
			if (V.cheatMode) {
				option.showTextBox();
				r.push(`How far along your pregnancy is (pregMood kicks in at 24+ weeks) - -2: infertile, -1: contraceptives, 0: not pregnant, 1 - 42: pregnant, 43+: giving birth.`);
			}
			if (V.PC.preg === -1) {
				r.push("You can't get pregnant, however there will be a slight increase to living expenses.");
			}

			if (V.PC.counter.birthsTotal > 0) {
				r.push(`You have given birth to <strong>${V.PC.counter.birthsTotal}</strong> babies.`);
			}
			if (r.length >0) {
				option.addComment(r.join(" "));
			}

			option = options.addOption("Hormone effects", "pregMood", V.PC)
				.addValueList([
					["None", 0],
					["Caring and motherly", 1],
					["Aggressive and domineering", 2]
				]);
			if (V.PC.pregMood === 1) {
				option.addComment("Sex scene alterations; slaves will trust you more, but may try to take advantage of your mercy.");
			} else if (V.PC.pregMood === 2) {
				option.addComment("Sex scene alterations; slaves will fear you more, but will become more submissive to you.");
			}

			if (V.cheatMode) {
				options.addOption("Fetus Count", "pregType", V.PC).showTextBox().addComment(`how many you're having (1-8)`);
				options.addOption("Pregnancy Source", "pregSource", V.PC)
					.addValueList([
						["Unknown", SID.GENERIC],
						["Self-impregnation", SID.PC],
						["Citizen", SID.CITIZEN],
						["Former Master", SID.FORMERMASTER],
						["Male arc owner", SID.ARCOWNER],
						["Client", SID.CLIENT],
						["Societal Elite", SID.ELITE],
						["Designer baby", SID.LAB],
						["Futanari Sister", SID.FUTA],
						["Rapist", SID.RAPIST],
					])
					.showTextBox()
					.pulldown();
			}
		}

		el.append(options.render());
	} else {
		r = [];

		if (V.PC.boobs >= 300) {
			r.push(`You have a`);
			if (V.PC.title > 0) {
				r.push(`masculine`);
			} else {
				r.push(`feminine`);
			}
			r.push(`body with`);
			if (V.PC.shoulders > 1) {
				r.push(`very broad shoulders`);
			} else if (V.PC.shoulders > 0) {
				r.push(`broad shoulders`);
			} else if (V.PC.shoulders < -1) {
				r.push(`very narrow shoulders`);
			} else if (V.PC.shoulders < 0) {
				r.push(`narrow shoulders`);
			}
			if (V.PC.shoulders !== 0) {
				r.push(`and`);
			}
			let breastShapeDesc = "";
			if (V.PC.boobShape === "saggy") {
				breastShapeDesc = ` that sag a bit`;
			} else if (V.PC.boobShape === "spherical") {
				breastShapeDesc = ` that might just be a little too much implant now.`;
			}
			if (V.PC.boobs >= 1400) {
				r.push(`giant${(V.PC.boobsImplant !== 0) ? `, fake` : ``} cow tits${breastShapeDesc}.`);
			} else if (V.PC.boobs >= 1200) {
				r.push(`huge`);
				if (V.PC.boobsImplant !== 0) {
					r.push(`fake`);
				}
				r.push(`breasts${breastShapeDesc}.`);
			} else if (V.PC.boobs >= 1000) {
				r.push(`big`);
				if (V.PC.boobsImplant !== 0) {
					r.push(`fake`);
				}
				r.push(`breasts${breastShapeDesc}.`);
			} else if (V.PC.boobs >= 800) {
				r.push(`noticeable breasts${breastShapeDesc}.`);
			} else if (V.PC.boobs >= 650) {
				r.push(`unremarkable breasts${breastShapeDesc}.`);
			} else if (V.PC.boobs >= 500) {
				r.push(`average breasts${breastShapeDesc}.`);
			} else {
				r.push(`small breasts${breastShapeDesc}.`);
			}
		} else {
			if (V.PC.title > 0) {
				r.push(`You have a manly chest.`);
			} else {
				r.push(`You are flat as a board.`);
			}
		}
		r.push(`Around back, you have`);
		if (V.PC.butt > 10) {
			r.push(`a disproportionate swelling of flesh`);
		} else if (V.PC.butt > 7) {
			r.push(`an absurdly large butt`);
		} else if (V.PC.butt > 6) {
			r.push(`a ridiculous ass`);
		} else if (V.PC.butt > 5) {
			r.push(`a gigantic ass`);
		} else if (V.PC.butt > 4) {
			r.push(`an enormous ass`);
		} else if (V.PC.butt > 3) {
			r.push(`a huge ass`);
		} else if (V.PC.butt > 2) {
			r.push(`a big butt`);
		} else if (V.PC.butt > 1) {
			r.push(`a shapely rear`);
		} else if (V.PC.butt > 0) {
			r.push(`a trim rear`);
		} else {
			r.push(`a flat ass`);
		}
		r.push(`attached to a pair of`);
		if (V.PC.hips > 2) {
			r.push(`inhumanly wide hips.`);
		} else if (V.PC.hips > 1) {
			r.push(`very wide hips.`);
		} else if (V.PC.hips > 0) {
			r.push(`wide hips.`);
		} else if (V.PC.hips > -1) {
			r.push(`average hips.`);
		} else if (V.PC.hips > -2) {
			r.push(`narrow hips.`);
		} else {
			r.push(`very narrow hips.`);
		}

		r.push(`Between your legs, you have`);
		if (V.PC.dick !== 0) {
			if (V.PC.dick === 1) {
				r.push(`a tiny, humiliating`);
			} else if (V.PC.dick === 2) {
				r.push(`a small`);
			} else if (V.PC.dick === 3) {
				r.push(`an average`);
			} else if (V.PC.dick === 4) {
				r.push(`a big`);
			} else if (V.PC.dick === 5) {
				r.push(`a huge`);
			} else if (V.PC.dick === 6) {
				r.push(`a pussy wrecking`);
			} else if (V.PC.dick === 7) {
				r.push(`a massive, unusable`);
			} else if (V.PC.dick === 8) {
				r.push(`an imposing, if unusable,`);
			} else if (V.PC.dick === 9) {
				r.push(`a monster of a`);
			} else if (V.PC.dick === 10) {
				r.push(`an inhuman`);
			} else {
				r.push(`an unusable slab of sensitive flesh you call a`);
			}
			r.push(`dick`);
			if (V.PC.balls !== 0) {
				r.push(r.pop() + ",");
			}
			if (V.PC.balls === 1) {
				if (V.PC.scrotum === 0) {
					r.push(`a tiny pair of internal balls`);
				} else {
					r.push(`vestigial testicles`);
				}
			} else if (V.PC.balls === 2) {
				if (V.PC.scrotum === 0) {
					r.push(`a pair of internal balls`);
				} else {
					r.push(`a pair of small testicles`);
				}
			} else if (V.PC.balls === 3) {
				r.push(`a pair of average testicles`);
			} else if (V.PC.balls === 4) {
				r.push(`a dangling pair of large balls`);
			} else if (V.PC.balls === 5) {
				r.push(`a dangling pair of heavy testicles`);
			} else if (V.PC.balls === 6) {
				r.push(`a heavy pair of huge balls`);
			} else if (V.PC.balls === 7) {
				r.push(`a ponderous set of giant testicles`);
			} else if (V.PC.balls === 8) {
				r.push(`a ponderous set of enormous testicles`);
			} else if (V.PC.balls === 9) {
				r.push(`a monstrous pair of testicles`);
			} else {
				r.push(`an inhuman pair of testicles`);
			}
			if (V.PC.vagina !== -1) {
				r.push(`and`);
			} else {
				r.push(r.pop() + ".");
			}
		}
		if (V.PC.vagina !== -1) {
			if (V.PC.vagina === 0) {
				r.push(`an unsullied`);
			} else if (V.PC.vagina === 1 || V.PC.newVag === 1) {
				// nothing
			} else if (V.PC.vagina === 2) {
				r.push(`an experienced`);
			} else if (V.PC.vagina === 3) {
				r.push(`a loose`);
			} else if (V.PC.vagina === 4) {
				r.push(`a very well-traveled`);
			} else {
				r.push(`a ruined`);
			}
			r.push(`vagina.`);
		}
		if (V.PC.vagina !== -1 && V.PC.mpreg !== 0) {
			r.push(`You are`);
			if (V.PC.pregWeek < 0) {
				r.push(`currently recovering from your last pregnancy.`);
			} else if (V.PC.preg === -2) {
				r.push(`infertile and can't get pregnant.`);
			} else if (V.PC.preg === -1) {
				r.push(`taking contraceptives to avoid potential pregnancy.`);
			} else if (V.PC.preg === 0) {
				if (V.PC.pubertyXX === 0) {
					r.push(`not yet fertile.`);
				} else {
					r.push(`fertile and capable of becoming pregnant.`);
				}
			} else if (V.PC.preg > 37) {
				r.push(`extremely pregnant and likely to give birth soon.`);
			} else if (V.PC.preg > 0) {
				r.push(`currently pregnant.`);
			}

			linkArray = [];
			if (V.PC.preg > 20 || V.PC.counter.birthsTotal > 0) {
				if (V.PC.pregMood === 1) {
					r.push(`You tend to be caring and motherly when you're pregnant.`);
					linkArray.push(noChange(), aggressive());
				} else if (V.PC.pregMood === 0) {
					r.push(`Pregnancy doesn't really affect your mood.`);
					linkArray.push(motherly(), aggressive());
				} else {
					r.push(`You tend to be very demanding and aggressive when you're pregnant.`);
					linkArray.push(noChange(), motherly());
				}
			} else {
				if (V.PC.pregMood === 1) {
					r.push(`You tend to be caring and motherly when you're hormonal.`);
					linkArray.push(noChange(), aggressive());
				} else if (V.PC.pregMood === 0) {
					r.push(`Your mood isn't tied to your hormones.`);
					linkArray.push(motherly(), aggressive());
				} else {
					r.push(`You tend to be very demanding and aggressive when you're hormonal.`);
					linkArray.push(noChange(), motherly());
				}
			}

			r.push(App.UI.DOM.generateLinksStrip(linkArray));
			if (V.PC.counter.birthsTotal > 0) {
				r.push(`You have given birth to ${V.PC.counter.birthsTotal} babies.`);
			}
		}

		App.Events.addNode(el, r, "p");
	}

	if (V.cheatMode) {
		// Skills
		App.UI.DOM.appendNewElement("h2", el, "Skills");
		options = new App.UI.OptionsGroup();
		options.addOption(`Trading: ${tradingDescription()}`, "trading", V.PC.skill).addValueList([
			["Economics master", 100],
			["Economics expert", 90],
			["Skilled in economics", 70],
			["Amateur economist", 50],
			["Economics beginner", 30],
			["Basic trader", 0],
			["Haggler", -10],
			["Shopper", -30],
			["Weak saver", -50],
			["Money sieve", -70],
			["What's a trading?", -90],
		]).showTextBox();

		options.addOption(`Warfare: ${warfareDescription()}`, "warfare", V.PC.skill).addValueList([
			["Warfare master", 100],
			["Warfare expert", 90],
			["Skilled in warfare", 70],
			["Amateur combatant", 50],
			["Combat beginner", 30],
			["Basic fighter", 0],
			["Gun haver", -10],
			["Knife holder", -30],
			["Throat puncher", -50],
			["Groin kicker?", -70],
			["Most likely to be raped", -90]
		]).showTextBox();

		options.addOption(`Slaving: ${slavingDescription()}`, "slaving", V.PC.skill).addValueList([
			["Master slaver", 100],
			["Expert slaver", 90],
			["Skilled in slaving", 70],
			["Amateur slaver", 50],
			["Slaving beginner", 30],
			["Basic slaver", 0],
			["Can't make me a slave", -10],
			["Can read contracts", -30],
			["Careful now", -50],
			["Don't trust that guy", -70],
			["Potential slave", -90]
		]).showTextBox();

		options.addOption(`Engineering: ${engineeringDescription()}`, "engineering", V.PC.skill).addValueList([
			["Master engineer", 100],
			["Expert engineer", 90],
			["Skilled in engineering", 70],
			["Amateur engineer", 50],
			["Engineering beginner", 30],
			["Basic engineer", 0],
			["Gingerbread house", -10],
			// cSpell:disable-next-line
			["Knot tyer", -30],
			["You can use glue", -50],
			["You aren't handy", -70],
			["My hovercraft is full of eels", -90]
		]).showTextBox();

		options.addOption(`Medicine: ${medicineDescription()}`, "medicine", V.PC.skill).addValueList([
			["Master surgeon", 100],
			["Expert surgeon", 90],
			["Skilled in medicine", 70],
			["Amateur surgeon", 50],
			["Medical beginner", 30],
			["Basic medic", 0],
			["Can treat wounds", -10],
			["First-aid kit user", -30],
			["Band-aid applier", -50],
			["MEDIC!", -70],
			["Give me another beer", -90]
		]).showTextBox();

		options.addOption(`Hacking: ${hackingDescription()}`, "hacking", V.PC.skill).addValueList([
			["Master hacker", 100],
			["Expert hacker", 90],
			["Skilled hacker", 70],
			["Amateur hacker", 50],
			["Hacking beginner", 30],
			["Basic hacker", 0],
			["Mouse clicker", -10],
			["You can press Enter", -30],
			[`Where's the "any" key?`, -50],
			["Main screen turn on?", -70],
			["Ooh, cool glowy thingy!", -90]
		]).showTextBox();

		el.append(options.render());

		// Potential
		App.UI.DOM.appendNewElement("h2", el, "Misc");
		options = new App.UI.OptionsGroup();
		options.addOption(`Cum Tap`, "cumTap", V.PC.skill).showTextBox();
		options.addOption(`Stored Cum`, "storedCum", V.PC.counter).showTextBox();
		options.addOption(`Forced Fertility Drugs`, "forcedFertDrugs", V.PC).showTextBox();
		el.append(options.render());
	}

	return el;

	function noChange() {
		return App.UI.DOM.link(
			"Change to no change",
			() => { V.PC.pregMood = 0; },
			[],
			"Intro Summary"
		);
	}

	function motherly() {
		return App.UI.DOM.link(
			"Change to motherly",
			() => { V.PC.pregMood = 1; },
			[],
			"Intro Summary"
		);
	}

	function aggressive() {
		return App.UI.DOM.link(
			"Change to aggressive",
			() => { V.PC.pregMood = 2; },
			[],
			"Intro Summary"
		);
	}

	function tradingDescription() {
		if (V.PC.skill.trading >= 100) {
			return `You are a master at economics and trading.`;
		} else if (V.PC.skill.trading >= 80) {
			return `You are an expert at economics and trading.`;
		} else if (V.PC.skill.trading >= 60) {
			return `You are skilled in economics and trading.`;
		} else if (V.PC.skill.trading >= 40) {
			return `You know some things about economics and trading.`;
		} else if (V.PC.skill.trading >= 20) {
			return `You are a beginner in economics.`;
		} else if (V.PC.skill.trading >= 0) {
			return `You know only the basics of trading.`;
		} else if (V.PC.skill.trading >= -20) {
			return `You know how to haggle a little.`;
		} else if (V.PC.skill.trading >= -40) {
			return `You know how to shop around.`;
		} else if (V.PC.skill.trading >= -60) {
			return `You know not to pay sticker price.`;
		} else if (V.PC.skill.trading >= -80) {
			return 	`People always give you discounts, but you never save any money.`;
		} else {
			return `They said it was a bear market, so where are the bears?`;
		}
	}

	function warfareDescription() {
		if (V.PC.skill.warfare >= 100) {
			return `You are a master of warfare.`;
		} else if (V.PC.skill.warfare >= 80) {
			return `You are an expert at tactics and strategy.`;
		} else if (V.PC.skill.warfare >= 60) {
			return `You are skilled in combat.`;
		} else if (V.PC.skill.warfare >= 40) {
			return `You know some things about combat.`;
		} else if (V.PC.skill.warfare >= 20) {
			return `You are a beginner in tactics and strategy.`;
		} else if (V.PC.skill.warfare >= 0) {
			return `You know only the basics of fighting.`;
		} else if (V.PC.skill.warfare >= -20) {
			return `You know how to hold a gun.`;
		} else if (V.PC.skill.warfare >= -40) {
			return `You know how to stab with a knife.`;
		} else if (V.PC.skill.warfare >= -60) {
			return `Go for the throat?`;
		} else if (V.PC.skill.warfare >= -80) {
			return `Just kick them in the balls, right?`;
		} else {
			return `People like you are usually the first raped in a war.`;
		}
	}

	function slavingDescription() {
		if (V.PC.skill.slaving >= 100) {
			return `You are a master slaver.`;
		} else if (V.PC.skill.slaving >= 80) {
			return `You are an expert at enslaving.`;
		} else if (V.PC.skill.slaving >= 60) {
			return `You are skilled in slaving.`;
		} else if (V.PC.skill.slaving >= 40) {
			return `You know some things about getting slaves.`;
		} else if (V.PC.skill.slaving >= 20) {
			return `You are a beginner in slaving.`;
		} else if (V.PC.skill.slaving >= 0) {
			return `You know only the basics of slaving.`;
		} else if (V.PC.skill.slaving >= -20) {
			return `You know how to avoid becoming a slave.`;
		} else if (V.PC.skill.slaving >= -40) {
			return `You know to read contracts before you sign them.`;
		} else if (V.PC.skill.slaving >= -60) {
			return `You know to be careful.`;
		} else if (V.PC.skill.slaving >= -80) {
			return `You know better than to trust anyone.`;
		} else {
			return `It would be easy to enslave you.`;
		}
	}

	function engineeringDescription() {
		if (V.PC.skill.engineering >= 100) {
			return `You are a master engineer.`;
		} else if (V.PC.skill.engineering >= 80) {
			return `You are an expert at engineering.`;
		} else if (V.PC.skill.engineering >= 60) {
			return `You are skilled in engineering.`;
		} else if (V.PC.skill.engineering >= 40) {
			return `You know some things about engineering.`;
		} else if (V.PC.skill.engineering >= 20) {
			return `You are a beginner in engineering.`;
		} else if (V.PC.skill.engineering >= 0) {
			return `You know only the basics of engineering.`;
		} else if (V.PC.skill.engineering >= -20) {
			return `You can build a gingerbread house that doesn't collapse.`;
		} else if (V.PC.skill.engineering >= -40) {
			return `You can tie a tight knot, does that count?`;
		} else if (V.PC.skill.engineering >= -60) {
			return `Glue is your friend; lots of it.`;
		} else if (V.PC.skill.engineering >= -80) {
			return `You know better than to even try to build something.`;
		} else {
			return `You can cook; that's sort of like building something, right?`;
		}
	}

	function medicineDescription() {
		if (V.PC.skill.medicine >= 100) {
			return `You are a master surgeon.`;
		} else if (V.PC.skill.medicine >= 80) {
			return `You are an expert at medicine and surgery.`;
		} else if (V.PC.skill.medicine >= 60) {
			return `You are skilled in surgery.`;
		} else if (V.PC.skill.medicine >= 40) {
			return `You know some things about medicine.`;
		} else if (V.PC.skill.medicine >= 20) {
			return `You are a beginner in medicine.`;
		} else if (V.PC.skill.medicine >= 0) {
			return `You know the basics of treating injuries.`;
		} else if (V.PC.skill.medicine >= -20) {
			return `You can stop a wound from getting infected.`;
		} else if (V.PC.skill.medicine >= -40) {
			return `Gauze is your friend. Just keep wrapping.`;
		} else if (V.PC.skill.medicine >= -60) {
			return `You know how to apply a band-aid.`;
		} else if (V.PC.skill.medicine >= -80) {
			return `Cure-alls are wonderful. Why aren't they sold in stores, though?`;
		} else {
			return `Alcohol makes pain go away, right?`;
		}
	}

	function hackingDescription() {
		if (V.PC.skill.hacking >= 100) {
			return `You are a master of hacking.`;
		} else if (V.PC.skill.hacking >= 80) {
			return `You are an expert at hacking.`;
		} else if (V.PC.skill.hacking >= 60) {
			return `You are skilled in hacking.`;
		} else if (V.PC.skill.hacking >= 40) {
			return `You know some things about hacking.`;
		} else if (V.PC.skill.hacking >= 20) {
			return `You are a beginner in hacking.`;
		} else if (V.PC.skill.hacking >= 0) {
			return `You know only the basics of hacking.`;
		} else if (V.PC.skill.hacking >= -20) {
			return `You know how to click a mouse.`;
		} else if (V.PC.skill.hacking >= -40) {
			return `Enter does something?`;
		} else if (V.PC.skill.hacking >= -60) {
			return `Where is the "any" key?`;
		} else if (V.PC.skill.hacking >= -80) {
			return `You can push the power button, good job.`;
		} else {
			return `This black box thingy is magical.`;
		}
	}
};


code:
App.Intro.PCAppearanceIntro = function() {
	const node = new DocumentFragment();

	node.append(
		`Race and appearance are largely irrelevant in the Free Cities; there are only the free and the enslaved.`,
		App.UI.DOM.makeElement("div", `Appearance only, will mostly have a superficial effect (unless you make a big deal out of it).`, ["indent", "note"])
	);

	const options = new App.UI.OptionsGroup();
	App.UI.Player.appearance(options);
	node.append(options.render());

	if (isFertile(V.PC, true)) {
		App.UI.DOM.appendNewElement("div", node, App.UI.DOM.passageLink(
			"Continue player character customization",
			"PC Preg Intro",
			() => resetEyeColor(V.PC)
		));
	} else {
		App.UI.DOM.appendNewElement("div", node, App.UI.DOM.passageLink(
			"Finish player character customization",
			"PC Experience Intro",
			() => resetEyeColor(V.PC)
		));
	}

	return node;
};

repo_name: Nithhogg/fc

license: mit


code:
App.Intro.PCBodyIntro = function() {
	V.PC.actualAge = Math.clamp(V.PC.actualAge, 10, 80);

	const el = new DocumentFragment();
	let r = [];

	r.push(`Most slaveowners in the Free Cities are male. The preexisting power structures of the old world have mostly migrated to the new, and it can often be very hard to be a free woman in the Free Cities. Some manage to make their way, but in many arcologies, men are the owners, and women are the owned. You'll cut a striking figure as the owner and leader of your arcology, but`);
	r.push(App.UI.DOM.makeElement("span", `what's under your business attire?`, ["intro", "question"]));
	App.Events.addNode(el, r, "p");

	el.append(body());
	el.append(age());
	el.append(nameIndulgence());
	el.append(endScene());

	return el;

	function body() {
		const el = document.createElement("p");
		const options = new App.UI.OptionsGroup();
		let r = [];

		// Gender
		r.push(`You are a`);
		if (V.PC.genes === "XX") {
			r.push(`woman`);
		} else {
			r.push(`man`);
		}
		r.push(`with a`);
		if (V.PC.title > 0) {
			r.push(`masculine figure and will be referred to as <strong>Master.</strong>`);
		} else {
			r.push(`feminine figure and will be referred to as <strong>Mistress.</strong>`);
		}
		options.addOption(r.join(" "), "title", V.PC)
			.addValue("Masculine appearance", 1)
			.addValue("Feminine appearance", 0)
			.addComment("This option will affect scenes. Femininity may increase difficulty in the future, but for now only your chest and junk matter.");

		// Chest
		options.addOption("Under my suit jacket", "boobs", V.PC)
			.addValue((V.PC.title > 0) ? `Masculine muscles` : `A flat chest`, 200)
			.addValue("Feminine breasts", 900)
			.addComment("These options will affect scenes. Sporting breasts will increase difficulty");

		// Lower deck
		r = [];
		let option;
		r.push(`Behind the front of my tailored`);
		if (V.PC.dick !== 0) {
			if (V.PC.vagina !== -1) {
				r.push(`slacks, <strong>both a penis and a vagina.</strong>`);
				option = options.addCustomOption(r.join(" "))
					.addButton("Remove the penis", penisRemove)
					.addButton("Remove the vagina", vaginaRemove);
			} else {
				r.push(`slacks, a <strong>penis.</strong>`);
				option = options.addCustomOption(r.join(" "))
					.addButton(
						"Switch to vagina",
						() => {
							penisRemove();
							V.PC.genes = "XX";
							vaginaAdd();
						},
						"PC Body Intro"
					)
					.addButton("Add a vagina", vaginaAdd);
			}
		} else {
			r.push(`skirt, a <strong>vagina.</strong>`);
			option = options.addCustomOption(r.join(" "))
				.addButton(
					"Switch to penis",
					() => {
						penisAdd();
						V.PC.genes = "XY";
						vaginaRemove();
					},
					"PC Body Intro"
				)
				.addButton("Add a penis", penisAdd);
		}
		option.addComment(`These options will affect sex scenes. Feminine options will increase difficulty.`);

		el.append(options.render());

		return el;

		function penisAdd() {
			V.PC.dick = 4;
			V.PC.balls = 3;
			V.PC.scrotum = 3;
			V.PC.prostate = 1;
		}

		function penisRemove() {
			V.PC.dick = 0;
			V.PC.balls = 0;
			V.PC.scrotum = 0;
			V.PC.prostate = 0;
		}

		function vaginaAdd() {
			V.PC.vagina = 1;
			V.PC.ovaries = 1;
		}

		function vaginaRemove() {
			V.PC.vagina = -1;
			V.PC.ovaries = 0;
		}
	}

	function age() {
		const el = document.createElement("p");
		const options = new App.UI.OptionsGroup();

		App.UI.DOM.appendNewElement("div", el, `How old are you?`, ["intro", "question"]);
		const r = [];
		r.push(`You're`);
		if (V.PC.actualAge >= 65) {
			r.push(`getting up in years. You've made a legacy for myself, and not done with life just yet.`);
		} else if (V.PC.actualAge >= 50) {
			r.push(`well into middle age. You've made a name for myself, and still got your groove.`);
		} else if (V.PC.actualAge >= 35) {
			r.push(`entering middle age. You're accomplished, and retain some youthful vigor.`);
		} else if (V.PC.actualAge >= 22) {
			r.push(`surprisingly young. You'll need to prove yourself, but you've got energy to burn.`);
		} else if (V.PC.actualAge >= 14) {
			r.push(`exceedingly young. You're nobody, but you're full of youthful vigor and ready to make the world yours.`);
		} else {
			r.push(`just a child. You may be emancipated, but society won't accept you as a leader.`);
		}
		r.push(`Your age:`);
		options.addOption(r.join(" "), "actualAge", V.PC).showTextBox()
			.addComment(`Older player characters start with more reputation and maintain reputation somewhat more easily, but have slightly less sexual energy. Exceedingly young characters will not be accepted by society, even more so if underage, and will face additional hurdles and complications.`);

		el.append(options.render());

		return el;
	}

	function nameIndulgence() {
		const el = document.createElement("p");
		const options = new App.UI.OptionsGroup();

		App.UI.DOM.appendNewElement("div", el, `What is your name and alternate indulgence?`, ["intro", "question"]);

		App.UI.Player.names(options);
		App.UI.Player.refreshmentChoice(options);

		el.append(options.render());

		return el;
	}

	function endScene() {
		const el = document.createElement("p");
		el.append(
			App.UI.DOM.link(
				"Confirm player character overview",
				() => {
					App.UI.Player.syncAgeBasedParameters();
				},
				[],
				"PC Appearance Intro"
			)
		);

		return el;
	}
};


code:
App.Intro.PCExperienceIntro = function() {
	const node = new DocumentFragment();

	V.showSecExp = V.showSecExp || 0;

	if (V.PC.career === "arcology owner") {
		setTimeout(() => Engine.play("PC Rumor Intro"), Engine.minDomActionDelay);
	}
	if (!V.disableForcedCareers) {
		V.disableForcedCareers = V.PC.actualAge >= 22 ? 1 : 0;
	}
	App.Events.addParagraph(node, [
		`You're a relative unknown in the Free Cities, but it's clear you're already accomplished. The meek and average cannot aspire to acquire arcologies. You've got all the necessary skills to take over an arcology and succeed as its owner, but you should be able to leverage the skills and experience you retain from your past, too.`,
		App.UI.DOM.makeElement("span", `What career brought you to the Free Cities?`, ["intro", "question"])
	]);

	makeOption(
		"Idle wealth", "wealth",
		App.Events.makeNode([`Start with <span class="cash inc">extra money.</span>${(V.showSecExp === 1) ? ` However, you will find it harder to maintain authority, but propaganda hub upgrades will be cheaper.` : ``} Your starting slaves will have one free level in each of two sex skills available.`])
	);

	makeOption(
		"Venture capitalism", "capitalist",
		App.Events.makeNode([`You will be more <span class="positive">effective at business pursuits.</span>${(V.showSecExp === 1) ? ` In addition, propaganda hub upgrades will be cheaper.` : ``} Your starting slaves will have a free level of prostitution skill available.`])
	);

	makeOption(
		"Private military work", "mercenary",
		App.Events.makeNode([
			`You retain mercenary contacts ${(V.showSecExp === 1) ? `and your security skills will make it easier to keep the arcology safe. Also, security HQ upgrades will be cheaper.` : `and security skills.`}`,
			`Your starting slaves will have <span class="positive">free trust available.</span>`
		])
	);

	makeOption(
		"Slaving", "slaver",
		App.Events.makeNode([
			`Your slave breaking experience will be useful. ${(V.showSecExp === 1) ? `You will find that authority will be easier to maintain and security HQ upgrades will be cheaper.` : ""}`, `Your starting slaves will have free <span class="hotpink">devotion</span> available.`
		])
	);

	makeOption(
		"Engineering", "engineer",
		App.Events.makeNode([`<span class="cash inc">Upgrading the arcology will be cheaper.</span> Also, the arcology will start with <span class="cash inc">basic economic upgrades</span> already installed.`])
	);

	makeOption(
		"Surgery", "medicine",
		App.Events.makeNode([`Surgery will be cheaper and healthier and drug upgrades will be cheaper. Your starting slaves will have free implants available.`])
	);

	makeOption(
		"Minor celebrity", "celebrity",
		App.Events.makeNode([
			`Start with <span class="positive">extra reputation.</span>${(V.showSecExp === 1) ? ` In addition, propaganda hub upgrades will be cheaper.` : ``}`,
			`Your starting slaves will have a free level of <span class="cyan">entertainment skill</span> available.`
		])
	);

	makeOption(
		"Sex industry", "escort",
		App.Events.makeNode([
			`As an ex-whore, you will find it <span class="red">hard to maintain reputation${(V.showSecExp === 1) ? `, in addition to authority` : ``}.`,
			`Your starting slaves will have one free level in each of two <span class="cyan">sex skills</span> available, along with a free level of <span class="cyan">entertainment and prostitution.</span>`])
	);

	makeOption(
		"Servant", "servant",
		App.Events.makeNode([
			`As an ex-servant, you will find it <span class="red">hard to maintain reputation${(V.showSecExp === 1) ? `, in addition to authority` : ``}.`, `You know how to lower your upkeep, but not much else. Your starting slaves will have free trust and devotion.`
		])
	);

	makeOption(
		"Gang affiliation", "gang",
		App.Events.makeNode([
			`As an ex-gang member, you know how to haggle slaves.${(V.showSecExp === 1) ? `In addition, asserting your authority will be easier and security HQ upgrades will be cheaper.` : ``}`, `However, you will find reputation quite hard to maintain. Your starting slaves will be fitter and possess a free level of combat skill.`
		])
	);

	makeOption(
		"Incursion Specialist", "BlackHat",
		App.Events.makeNode([
			`As an ex-hacker, you know how to gain access computer systems and other devices. Certain upgrades will be cheaper and you may find alternative approaches to problems.${(V.showSecExp === 1) ? `However, you will <span class="red">find authority quite hard</span> to maintain.` : ""}`,
			`Your starting slaves will have a free level of <span class="cyan">intelligence.</span>`
		])
	);

	function makeOption(linkTitle, career, desc) {
		App.UI.DOM.appendNewElement("div", node, App.UI.DOM.passageLink(
			linkTitle,
			"PC Rumor Intro",
			() => V.PC.career = App.UI.Player.assignCareerByAge(career)
		));

		App.UI.DOM.appendNewElement("div", node, desc, ["indent", "note"]);
	}

	const options = new App.UI.OptionsGroup();

	options.addOption(`Security Expansion Mod effects`, "showSecExp")
		.addValueList([
			["Hide", 0],
			["Show", 1],
		]);

	const option = options.addOption(`Forced career choices`, "disableForcedCareers")
		.addValue("Disable", 0);
	let r = [];
	if (V.PC.actualAge < 14) {
		r.push(`Due to your young age, you will be given the child variant of your chosen career line.`);
	} else if (V.PC.actualAge < 22) {
		r.push(`Due to your age, you will be given the inexperienced variant of your chosen career line.`);
	}
	r.push(`Over time and with effort, you will be capable of achieving everything of importance in the adult careers.`);
	option.addComment(r.join(" "));
	if (V.PC.actualAge < 22) {
		option.addValue("Enable", 1)
			.addComment("Use age based careers.");
	}
	node.append(options.render());

	return node;
};


code:
App.Intro.PCPregIntro = function() {
	const node = new DocumentFragment();

	App.Events.addParagraph(node, [
		`You have a working female reproductive system, and thus, a menstrual cycle. Women already face hardships as slaveowners and arcology owners, and being with child, coupled with a strong societal aversion to the dominant being penetrated, leads most pregnant owners to find themselves in dire situations. As an added precaution, since nothing stops an owner from hooking up with another owner, contraceptives are common amongst free women.`,
		App.UI.DOM.makeElement("span", `What's your stance on taking contraceptives?`, ["intro", "question"])
	]);

	// <p>
	let r = [];
	let linkArray = [];
	r.push(`You're`);
	if (V.PC.preg === -1) {
		r.push(`not interested in being a mother.`);
		linkArray.push(App.UI.DOM.passageLink(
			"Risky",
			"PC Preg Intro",
			() => V.PC.preg = 0
		));
	} else if (V.PC.preg === 0) {
		r.push(`fertile and you know the risks.`);
		linkArray.push(App.UI.DOM.passageLink(
			"Safe",
			"PC Preg Intro",
			() => V.PC.preg = -1
		));
		linkArray.push(App.UI.DOM.passageLink(
			"About that...",
			"PC Preg Intro",
			() => V.PC.preg = 10
		));
	} else if (V.PC.preg > 42) {
		r.push(`proudly pregnant with octuplets and they are coming right now.`);
		linkArray.push(App.UI.DOM.passageLink(
			"Not pregnant",
			"PC Preg Intro",
			() => {
				V.PC.preg = 0;
				V.PC.pregType = 0;
				V.PC.labor = 0;
			}
		));
	} else if (V.PC.preg > 37) {
		r.push(`${V.PC.preg} weeks pregnant and very close to being due.`);
		linkArray.push(App.UI.DOM.passageLink(
			"Not pregnant",
			"PC Preg Intro",
			() => {
				V.PC.preg = 0;
				V.PC.pregType = 0;
			}
		));
		linkArray.push(App.UI.DOM.passageLink(
			"Overdid the fertility agents",
			"PC Preg Intro",
			() => {
				V.PC.preg = 43;
				V.PC.pregType = 8;
				V.PC.labor = 1;
			}
		));
	} else {
		r.push(`already pregnant.`);
		linkArray.push(App.UI.DOM.passageLink(
			"Fertile",
			"PC Preg Intro",
			() => V.PC.preg = 0
		));
		linkArray.push(App.UI.DOM.passageLink(
			"Make that very pregnant",
			"PC Preg Intro",
			() => V.PC.preg = 40
		));
	}
	App.Events.addParagraph(node, [
		r.join(" "),
		App.UI.DOM.generateLinksStrip(linkArray),
		App.UI.DOM.makeElement("span", `Being or becoming pregnant will raise difficulty.`, "note")
	]);

	App.UI.DOM.appendNewElement("p", node, `Hormones can be an interesting thing, but how do they affect you?`, ["intro", "question"]);

	r = [];
	r.push(`You're`);
	if (V.PC.pregMood === 1) {
		r.push(`gentle and motherly when you're hormonal.`);
	} else if (V.PC.pregMood === 0) {
		r.push(`in complete control of yourself.`);
	} else {
		r.push(`aggressive and domineering when you're hormonal.`);
	}
	const options = new App.UI.OptionsGroup();
	options.addOption(r.join(" "), "pregMood", V.PC)
		.addValueMap(new Map([
			["Normal", 0],
			["Motherly", 1],
			["Aggressive", 2]
		]))
		.addComment(`Hormone induced moods may alter some sex scenes, as well as change slaves' reactions to you.`);
	node.append(options.render());

	App.UI.DOM.appendNewElement("p", node, App.UI.DOM.passageLink(
		"Confirm player character customization",
		"PC Experience Intro"
	));
	return node;
};


code:
App.Intro.PCRumorIntro = function() {
	const node = new DocumentFragment();

	delete V.showSecExp;

	App.Events.addParagraph(node, [
		`Who you are is something that you will have to define for yourself through your actions. Once you own an arcology, no one will be in a position to apply moral scorekeeping to you. In the brave new world of the Free Cities, you will be free to define yourself as the sum of your actions, rather than as the product of your past. The first decision that will define who you are as an arcology owner is your choice of method in acquiring one.`,
		App.UI.DOM.makeElement("span", `What approach will you take?`, ["intro", "question"])
	]);

	makeOption(
		"A judicious application of funds", "wealth",
		App.Events.makeNode([`Start with <span class="cash inc">extra money,</span> since you were wealthy enough to buy an arcology.`])
	);

	makeOption(
		"Hard work and diligence", "diligence",
		App.Events.makeNode([`New slaves will <span class="hotpink">hate you less,</span> since it will be known that you worked hard to earn your position.`])
	);

	makeOption(
		"The remorseless use of force", "force",
		App.Events.makeNode([`New slaves <span class="gold">will fear you more,</span> since rumors about your violent takeover will inevitably circulate.`])
	);

	makeOption(
		"Clever social engineering", "social engineering",
		App.Events.makeNode([`Start with the <span class="positive">first societal option unlocked,</span> since you manipulated the arcology's citizens.`])
	);

	makeOption(
		"Blind luck", "luck",
		App.Events.makeNode([`Start with a <span class="positive">good reputation,</span> since the story of your unlikely accession will be famous.`])
	);

	return node;

	function makeOption(linkTitle, rumor, desc) {
		App.UI.DOM.appendNewElement("div", node, App.UI.DOM.passageLink(
			linkTitle,
			"Takeover Target",
			() => V.PC.rumor = rumor
		));

		App.UI.DOM.appendNewElement("div", node, desc, ["indent", "note"]);
	}
};


code:
App.Intro.slaveAgeIntro = function() {
	const node = new DocumentFragment();

	App.UI.DOM.appendNewElement("p", node, `Do you want to see content involving girls younger than 18 in this game?`, ["intro", "question"]);

	makeOption([
		App.UI.DOM.passageLink("No.", "PC Body Intro", () => {
			V.minimumSlaveAge = 18;
			V.pedoMode = 0;
		}),
		App.UI.DOM.makeElement("span", `All slaves will be at least 18 years old, and slavery of children will be illegal in the Free Cities.`, "note")
	]);

	makeOption([
		`Yes, I wish to see girls as young as`,
		App.UI.DOM.makeTextBox(V.minimumSlaveAge, (v) => V.minimumSlaveAge = v, true),
		App.UI.DOM.passageLink("Continue.", "PC Body Intro", () => {
			V.pedoMode = 0;
		}),
	]);

	makeOption([
		`Yes, and`,
		App.UI.DOM.passageLink("I just want lots of lolis.", "PC Body Intro", () => {
			V.minimumSlaveAge = 3;
			V.pedoMode = 1;
		}),
		App.UI.DOM.makeElement("span", `Nearly all randomly generated slaves will be under the age of 18, although custom slaves and slaves related to specific events may be older.`, "note")
	]);

	makeOption([
		`Yes,`,
		App.UI.DOM.passageLink("I wish to see them grow up and become fertile.", "PC Body Intro", () => {
			V.minimumSlaveAge = 3;
			V.pedoMode = 0;
			V.precociousPuberty = 1;
			V.loliGrow = 1;
			V.fertilityAge = 10;
			V.potencyAge = 12;
			V.seeAge = 1;
		}),
		App.UI.DOM.makeElement("span", `Preset. Slaves' age will be random from minimal possible age. They can be made fertile younger than normal puberty age (10) in some cases, and grow up naturally.`, "note")
	]);

	return node;

	function makeOption(array) {
		App.UI.DOM.appendNewElement("div", node, App.Events.makeNode(array));
	}
};


code:
// cSpell:ignore maskirovka

App.Intro.takeoverTarget = function() {
	const slavesImported = getSlaves().filter(s => s.newGamePlus === 1);
	if (slavesImported.size > 0) {
		V.retirementAge = Math.max(V.retirementAge, _.max(slavesImported.map(s => s.actualAge)) + 2);
		const highestActualAge = _.max(slavesImported.map(s => s.actualAge));
		const highestPhysicalAge = _.max(slavesImported.map(s => s.physicalAge));
		if (highestActualAge > 65) {
			V.retirementAge = highestPhysicalAge + 2;
			V.policies.retirement.physicalAgePolicy = 1;
			if (highestPhysicalAge > 65) {
				V.retirementAge = highestActualAge + 2;
				V.policies.retirement.physicalAgePolicy = 0;
			}
		}
	}
	const node = new DocumentFragment();
	const r = [];

	r.push(`Before you deploy the`);
	if (V.PC.rumor === "wealth") {
		r.push(`financial reserves that`);
	} else if (V.PC.rumor === "diligence") {
		r.push(`carefully constructed plan that`);
	} else if (V.PC.rumor === "force") {
		r.push(`mercenaries and`);
		if (V.continent === "Eastern Europe") {
			r.push(`maskirovka`);
		} else {
			r.push(`cover plan`);
		}
		r.push(`that`);
	} else if (V.PC.rumor === "social engineering") {
		r.push(`clever social manipulation that`);
	} else {
		r.push(`optimistic plan you hope`);
	}
	r.push(`will allow you to take over an arcology, you need to select a target. There are a number of vulnerable arcologies that you could`);
	if (V.PC.rumor === "wealth") {
		r.push(`attempt a hostile takeover of`);
	} else if (V.PC.rumor === "diligence") {
		r.push(`work to take over`);
	} else if (V.PC.rumor === "force") {
		r.push(`attack`);
	} else if (V.PC.rumor === "social engineering") {
		r.push(`infiltrate`);
	} else {
		r.push(`aspire to take over`);
	}
	r.push(`with a reasonable chance of success. Free Cities are volatile places, even compared to the troubled state of the rest of the world. There are always arcologies whose owners are on the brink of failure, and you could target one of them.`);
	if (V.PC.career === "arcology owner") {
		r.push(`(Since you've <span class="springgreen">owned an arcology before,</span> you identify more potential target arcologies than a novice might.)`);
	}
	r.push(`Alternatively, arcologies are being built every day, and their owners' control is often uncertain.`);

	r.push(App.UI.DOM.makeElement("div", `Which arcology will you target?`, ["intro", "question"]));
	App.Events.addParagraph(node, r);

	const arcologiesNode = App.UI.DOM.appendNewElement('div', node);

	const generateArcologiesSet = (container) => {
		const card = App.UI.DOM.appendNewElement("div", container, null, ["card"]);
		card.append(App.UI.DOM.passageLink(
			"A newly constructed arcology",
			"Terrain Intro",
			() => V.targetArcology.fs = "New"
		));
		App.UI.DOM.appendNewElement("div", card, `With many new arcologies being constructed, you will be able to select which area of the world and type of Free City you'd like your target arcology to be located in.`, ["indent", "note"]);
		App.UI.DOM.appendNewElement("div", card, `Recommended for new players.`, ["indent", "note"]);

		container.append(App.Intro.generateEstablishedArcologies(true));
	};

	generateArcologiesSet(arcologiesNode);

	node.append(App.UI.DOM.link('Consider more options', () => {
		arcologiesNode.innerHTML = '';
		generateArcologiesSet(arcologiesNode);
	}));

	return node;
};


code:
/**
 * @typedef FC.ArcologyLocation
 *
 * @property {string} name
 * @property {string} value
 * @property {string} [market]
 * @property {string} [commerce]
 * @property {string} [refugees]
 * @property {string} [culture]
 * @property {string[]} [notes]
 */

App.Intro.terrainIntro = function() {
	const node = new DocumentFragment();

	App.Events.addParagraph(node, [`The Free Cities are located wherever the rule of law is weak enough or permissive enough to allow a small area to secede, and where founders can afford to buy an area on which to build.`]);

	App.Events.addParagraph(node, [`Many Free Cities are therefore located in marginal, rural terrain. Founding a Free City in such an area is easy, and can usually be accomplished with the indifference or even connivance of the old country from which it secedes. After all, the potential commercial benefits are great, and the loss of underused land is only significant in the moral sense.`]);

	App.Events.addParagraph(node, [`Some Free Cities are located on water. Though some areas of shallow sea over the continental shelves hide valuable resources, others are neglected. Arcologies are such massive structures that it is very possible to design them to float anchored to the seabed.`]);

	App.Events.addParagraph(node, [`Finally, a few Free Cities have been carved out from old world cities. Urban decay has left the hearts of many cities ripe for this. Many old world countries resist this kind of secession, but this rarest, smallest, and densest kind of Free City can offer its surrounding nation a great deal of economic advantage.`]);

	App.UI.DOM.appendNewElement("p", node, `Which kind of Free City hosts your arcology?`, ["intro", "question"]);

	/** @type {FC.ArcologyLocation[]} */
	const locations = [
		{
			name: `Urban`,
			value: "urban",
			market: `<span class="noteworthy">Low</span> minimum slave value and initial <span class="noteworthy">bear market</span> for slaves.`,
			commerce: `<span class="positive">High</span> ease of commerce with the old world.`,
			refugees: `<span class="positive">High</span> access to refugees and other desperate people.`,
			culture: `<span class="warning">Low</span> cultural independence.`,
			notes: [
				`Unusually compact arcology with few manufacturing sectors.`,
			],
		},
		{
			name: `Rural`,
			value: "rural",
			market: `<span class="noteworthy">High</span> minimum slave value and initial <span class="noteworthy">bull market</span> for slaves.`,
			commerce: `Moderate ease of commerce with the old world.`,
			refugees: `Moderate access to refugees and other desperate people.`,
			culture: `Moderate cultural independence.`,
			notes: [
				`Widespread arcology with many manufacturing sectors.`,
			],
		},
		{
			name: `Ravine`,
			value: "ravine",
			market: `<span class="noteworthy">High</span> minimum slave value and initial <span class="noteworthy">bull market</span> for slaves.`,
			commerce: `<span class="warning">Low</span> ease of commerce with the old world.`,
			refugees: `<span class="warning">Very low</span> access to refugees and other desperate people.`,
			culture: `<span class="positive">High</span> cultural independence.`,
			notes: [
				`The arcology mostly being hidden inside the ravine leads to an unusual layout.`,
			],
		},
		{
			name: `Marine`,
			value: "marine",
			market: `Moderate minimum slave value and initially balanced market for slaves.`,
			commerce: `Moderate ease of commerce with the old world.`,
			refugees: `<span class="warning">Low</span> access to refugees and other desperate people.`,
			culture: `<span class="positive">High</span> cultural independence.`,
			notes: [
				`Large amount of markets and an extra shop sector.`,
			],
		},
		{
			name: `Oceanic`,
			value: "oceanic",
			market: `<span class="noteworthy">High</span> minimum slave value and initial <span class="noteworthy">bull market</span> for slaves.`,
			commerce: `Moderate ease of commerce with the old world.`,
			refugees: `<span class="warning">Very low</span> access to refugees and other desperate people.`,
			culture: `<span class="positive">Very high</span> cultural independence.`,
			notes: [
				`This unique location attracts the wealthy leading to initial luxury apartments.`,
				`Ensures access to slaves from all over the world and will not associate the arcology with a continent.`,
				V.showSecExp ? `Oceanic arcologies will not be subjects of attacks.` : ``
			],
		},
	];

	const div = document.createElement("div");

	for (const location of locations) {
		div.append(locationCard(location));
	}

	node.append(div);

	/** @param {FC.ArcologyLocation} location */
	function locationCard(location) {
		const div = App.UI.DOM.makeElement("div", null, ['card']);
		const keys = ["market", "commerce", "refugees", "culture", "notes"];

		App.UI.DOM.appendNewElement("div", div, App.UI.DOM.passageLink(
			location.name,
			"Location Intro",
			() => V.terrain = location.value,
		));

		keys
			.filter(key => location.hasOwnProperty(key))
			.forEach(key => {
				const innerDiv = App.UI.DOM.makeElement("div", null, ['indent']);
				const item = location[key];

				if (Array.isArray(item)) {
					item.forEach(n => {
						innerDiv.append(App.UI.DOM.makeElement("div", n, ['note']));
					});
				} else {
					innerDiv.innerHTML = item;
				}

				div.append(innerDiv);
			});

		return div;
	}

	if (V.showSecExp === 1) {
		App.UI.DOM.link(
			"Hide Security Expansion Mod effects",
			() => {
				V.showSecExp = 0;
				App.UI.reload();
			}
		);
	} else {
		App.UI.DOM.link(
			"Show Security Expansion Mod effects",
			() => {
				V.showSecExp = 1;
				App.UI.reload();
			}
		);
	}

	return node;
};


code:
App.Intro.tradeIntro = function() {
	const node = new DocumentFragment();

	App.Events.addParagraph(node, [
		`Most of the Free Cities are run on radically libertarian or even anarcho-capitalist principles. The first Free Cities experimented with indentured servitude, and this rapidly developed into widespread slavery. By now, the Free Cities collectively are a fundamentally slaveowning society and maintain a thriving slave trade that feeds off the terrible conditions in parts of the old world.`,
		App.UI.DOM.makeElement("span", `What attitude have the old world countries generally taken towards this trade?`, ["intro", "question"])
	]);

	App.Events.addResponses(node, [
		new App.Events.Result(`They've turned a blind eye, or even been complicit.`, complicit, `Slaves from all across the world will appear.`),
		new App.Events.Result(`They've done what little they can to stop it.`, little, `Almost all slaves will be from the continent where the arcology is located.`),
		new App.Events.Result(`Reactions have been mixed.`, mixed, `This will bypass arcology location restrictions, allowing you to adjust the nationalities encountered when buying non-customizable slaves.`),
	]);

	return node;

	function complicit() {
		V.internationalTrade = 1;
		V.customVariety = 0;
		Engine.play("Extreme Intro");
		return ``;
	}

	function little() {
		V.internationalTrade = 0;
		V.customVariety = 0;
		Engine.play("Extreme Intro");
		return ``;
	}

	function mixed() {
		V.internationalTrade = 0;
		V.customVariety = 1;
		V.customWA = 1;
		Engine.play("Customize Slave Trade");
		return ``;
	}
};


code:
App.Events.PArcologyNaming = class PArcologyNaming extends App.Events.BaseEvent {
	constructor(actors, params) {
		super(actors, params);
	}

	eventPrerequisites() {
		return [
			() => V.arcologies[0].name.indexOf("Arcology ") !== -1,
		];
	}

	get eventName() {
		return "Arcology Naming";
	}

	execute(node) {
		let r = [];
		r.push(`As a society free of the encumbrance of governmental oversight, the arcologies of the Free Cities are places where societal evolution and corporate expansion can occur rapidly. Even so, the incredible speed with which the arcology has improved under your tenure as compared to that of your predecessor, after you obtained ownership through`);
		if (V.PC.rumor === "wealth") {
			r.push(`a leveraged buyout,`);
		} else if (V.PC.rumor === "diligence") {
			r.push(`hard work and competence,`);
		} else if (V.PC.rumor === "force") {
			r.push(`some episodes of violence,`);
		} else if (V.PC.rumor === "social engineering") {
			r.push(`the creative use of psychology,`);
		} else if (V.PC.rumor === "luck") {
			r.push(`an incredible opportunity,`);
		}
		r.push(`is nothing short of astonishing. Other arcologies have taken many years to develop along anything but strictly conservative lines, and you are not the only arcology owner with a background`);
		if (isPCCareerInCategory("wealth")) {
			r.push(`of substantial wealth.`);
		} else if (isPCCareerInCategory("capitalist")) {
			r.push(`in business.`);
		} else if (V.PC.career === "mercenary") {
			r.push(`in the world of private contracting.`);
		} else if (V.PC.career === "recruit" || V.PC.career === "child soldier") {
			r.push(`in the military.`);
		} else if (V.PC.career === "slaver") {
			r.push(`as a slavebreaker.`);
		} else if (V.PC.career === "slave overseer") {
			r.push(`as a slave trainer.`);
		} else if (V.PC.career === "slave tender") {
			r.push(`as a slave tamer.`);
		} else if (V.PC.career === "engineer") {
			r.push(`in arcology engineering.`);
		} else if (V.PC.career === "construction" || V.PC.career === "worksite helper") {
			r.push(`in construction.`);
		} else if (V.PC.career === "medicine" || V.PC.career === "medical assistant") {
			r.push(`in medicine and surgery.`);
		} else if (V.PC.career === "nurse") {
			r.push(`in medicine.`);
		} else if (V.PC.career === "celebrity" || V.PC.career === "rising star" || V.PC.career === "child star") {
			r.push(`in the public sphere.`);
		} else if (V.PC.career === "escort" || V.PC.career === "prostitute" || V.PC.career === "child prostitute") {
			r.push(`involving many personal contacts.`);
		} else if (V.PC.career === "servant" || V.PC.career === "handmaiden" || V.PC.career === "child servant") {
			r.push(`involving the rich and powerful.`);
		} else if (V.PC.career === "gang" || V.PC.career === "hoodlum" || V.PC.career === "street urchin") {
			r.push(`involving gangs.`);
		} else if (V.PC.career === "BlackHat" || V.PC.career === "hacker" || V.PC.career === "script kiddy") {
			r.push(`involving mysterious data breaches.`);
		} else {
			r.push(`in the Free Cities.`);
		}
		r.push(`It occurs to you that the arcology's growing role as a place where those with the means to do so can live in the society you have created, enjoying themselves and their lives to the fullest while subjugating others, should be commemorated.`);
		App.Events.addParagraph(node, r);
		r = [];
		r.push(`As any slaveowner with half a mind in the Free Cities knows, there is power in words, and in what they represent. Master. Slave. These two words alone, more than any others, define the arcologies that have taken up the mantle of leading the world forward in these dark times. The time has come to add a new term to the lexicon of the Free Cities — a name. No longer will your arcology be known only as ${V.arcologies[0].name}, a bland and uninspiring name if ever there was one.`);
		App.Events.addParagraph(node, r);
		r = [];
		r.push(`From this day forward, the arcology shall be known as:`);
		App.Events.addParagraph(node, r);
		App.UI.DOM.appendNewElement("div", node, App.UI.DOM.makeTextBox(V.arcologies[0].name, (v) => V.arcologies[0].name = v));

		const responses = [];
		responses.push(new App.Events.Result(`Hurrah!`, hurrah, `Click to confirm your arcology's name, for now and ever! Or until you decide to change it again while managing your arcology.`));

		App.Events.addResponses(node, responses);

		function hurrah() {
			V.nextButton = "Continue";
			App.Utils.scheduleSidebarRefresh();
			return `Your citizens enthusiastically respond to your naming of the arcology as ${V.arcologies[0].name}. They are proud to finally have a real name for their home. Other Free Cities take note, and some take up their own names as well, another indication of the Cities' collective ascension over the old world.`;
		}
	}
};


code:
App.Events.PBombing = class PBombing extends App.Events.BaseEvent {
	eventPrerequisites() {
		return [
			() => getTraitor().actor === 0
		];
	}

	execute(node) {
		let r = [];
		let gameover = false;
		const {
			His, He,
			his, he, him
		} = getPronouns(S.Bodyguard ? S.Bodyguard : {pronoun: App.Data.Pronouns.Kind.neutral});

		V.nextButton = "Continue";

		if (S.Bodyguard) {
			App.Events.drawEventArt(node, S.Bodyguard);
		}
		V.fcnn.push("...new report on advances in security screening designed to sniff out small explosives like...");

		r.push(`One fine day, you're strolling down the main promenade, making your usual combined inspection and public rounds. These walks are triply useful, since they allow you to keep a finger on the pulse of your demesne, identify any problems, and display yourself to the population.`);
		if (S.Bodyguard) {
			r.push(`${S.Bodyguard.slaveName} is walking with you, of course; ${his} presence protection, intimidation, and ostentation, all in one.`);
		}

		r.push(`${capFirstChar(V.assistant.name)} suddenly relays a silent alarm, highlighting a scruffy individual twenty`);
		if (V.showInches === 2) {
			r.push(`yards`);
		} else {
			r.push(`meters`);
		}
		r.push(`ahead. This person is hooded and acting oddly. You catch a glance from beneath the hood in your direction, and the figure obviously decides that it can get no closer to you. Your assistant's scanning detects the presence of an implanted bomb moments before the suicide bomber detonates themself.`);

		r.push(`The implant is small, and went off too far ahead to do anything more than stun.`);
		if (S.Bodyguard) {
			r.push(`Since ${he} was behind you, ${S.Bodyguard.slaveName} is in much better shape than you, ${his} ears clear and ${his} senses sharp.`);
		}
		r.push(`Three other assailants run at you from where they were waiting in the confusion, wielding improvised weapons that apparently did not set off the security scanners.`);

		App.Events.addParagraph(node, r);
		r = [];

		if (S.Bodyguard) {
			if (getLimbCount(S.Bodyguard, 105) === 4) {
				r.push(`${S.Bodyguard.slaveName} glances at you to check that ${his} principal is alive, ${his} P-Limbs already entering combat mode. ${He} becomes more inhuman by the moment,`);
				if ((S.Bodyguard.muscles + S.Bodyguard.height - 100) / 25 > 5) {
					r.push(`${his} calves unfolding to reveal stabilizers that slide down and broaden ${his} ground contact. The targeting programs in ${his} arms allow ${him} to fire ${his} light machine gun from the hip, and ${he} places a precise burst in each target.`);
				} else if ((S.Bodyguard.muscles + S.Bodyguard.height - 100) / 25 > 4) {
					r.push(`the arm that holds ${his} battle rifle extending to lock to its foregrip and stock. With it fully attached to ${him}, ${he} raises that arm at each attacker in turn.`);
				} else if ((S.Bodyguard.muscles + S.Bodyguard.height - 100) / 25 > 3) {
					r.push(`stabilizers extending from the forearm that holds ${his} submachine gun. ${He} fires it one-handed, the weapons barely moving at all as ${he} flicks from target to target.`);
				} else {
					r.push(`${his} hand extending fully around ${his} machine pistol even as ${he} brings it up to meet the first target. When its first magazine is expended, ${his} forearm unfolds to reveal another.`);
				}
				r.push(`Your bodyguard's enhancements are not exactly secret, however, and the attackers are numerous, sent to overwhelm ${him}. ${He} drops ${his} weapon, ammunition expended, producing a scream of triumph from one of the few remaining assailants. Nothing daunted, ${he} swings ${his} dominant hand down to ${his} thigh on that side. It unfolds and places a pistol in ${his} grip, which ${he} brings up and uses to end the engagement.`);
				App.Events.addParagraph(node, r);
				r = [];
				r.push(`After reloading ${his} pistol from the ammunition compartment in ${his} other thigh, ${he} strides mechanically from body to body, a few of which are still moving. Stopping by each one, ${he} places ${his} palm against the base of each head with obscene care before extending the blade in ${his} forearm through it. This done, ${he} scans for further threats. There are none, and due to ${his} terrifying display there is an increase in confidence in the security of ${V.arcologies[0].name} and public applause for your skill in training ${him}. There is some minor damage to repair, but no lasting harm.`);
				cashX(-1000, "event", S.Bodyguard);
				repX(2500, "event", S.Bodyguard);
				V.arcologies[0].prosperity += 1;
			} else if (S.Bodyguard.skill.combat > 30) {
				r.push(`${S.Bodyguard.slaveName} glances at you to check that ${his} principal is alive for the moment, already`);
				if ((S.Bodyguard.muscles + S.Bodyguard.height - 100) / 25 > 5) {
					r.push(`getting ${his} light machine gun into action. Since they're so close, ${he} doesn't even bother to shoulder the weapon, simply using ${his} weapon's firepower to dump rounds into each attacker.`);
				} else if ((S.Bodyguard.muscles + S.Bodyguard.height - 100) / 25 > 4) {
					r.push(`bringing ${his} battle rifle up. ${He} swivels from target to target, placing a single round in each, using the weapon's devastating stopping power to good effect.`);
				} else if ((S.Bodyguard.muscles + S.Bodyguard.height - 100) / 25 > 3) {
					r.push(`bringing ${his} submachine gun up. ${He} cradles the compact weapon in`);
					if (hasBothArms(S.Bodyguard)) {
						r.push(`both hands`);
					} else {
						r.push(`${his} hand`);
					}
					r.push(`and swivels from target to target, placing a long burst of fire in each, using the weapon's large ammunition capacity to good effect.`);
				} else {
					r.push(`drawing ${his} machine pistol. ${He} cradles the diminutive weapon in`);
					if (hasBothArms(S.Bodyguard)) {
						r.push(`both hands`);
					} else {
						r.push(`${his} hand`);
					}
					r.push(`and swivels from target to target, placing a disciplined burst of fire in each at its center of mass.`);
				}
				r.push(`None of them gets within five`);
				if (V.showInches === 2) {
					r.push(`yards.`);
				} else {
					r.push(`meters.`);
				}
				r.push(`${S.Bodyguard.slaveName} dispassionately places a round in the head of each body, reloads, and takes up a position from which ${he} can watch for further threats. There are none, and due to ${his} sterling conduct there is an increase in confidence in the security of ${V.arcologies[0].name} and public applause for your skill in training ${him}. There is some minor damage to repair, but no lasting harm.`);
				cashX(-1000, "event", S.Bodyguard);
				repX(2500, "event", S.Bodyguard);
				V.arcologies[0].prosperity += 1;
			} else if (S.Bodyguard.muscles > 5) {
				r.push(`${S.Bodyguard.slaveName} hurriedly`);
				if ((S.Bodyguard.muscles + S.Bodyguard.height - 100) / 25 > 5) {
					r.push(`gets ${his} light machine gun firing. The weapon's high rate of fire tears through the first enemy but rapidly pulls ${his} aim off target.`);
				} else if ((S.Bodyguard.muscles + S.Bodyguard.height - 100) / 25 > 4) {
					r.push(`brings up ${his} battle rifle. ${He} hits the nearest assailant with ${his} first round and then fires again, but the weapon's powerful recoil sends it over ${his} target.`);
				} else if ((S.Bodyguard.muscles + S.Bodyguard.height - 100) / 25 > 3) {
					r.push(`brings up ${his} submachine gun. ${He} points it at the nearest assailant and pulls the trigger; the weapon's high rate of fire tears through the enemy but rapidly pulls ${his} aim off target.`);
				} else {
					r.push(`draws ${his} machine pistol. ${He} points it one-handed at the nearest assailant and pulls the trigger; the weapon's high rate of fire tears through the enemy but rapidly pulls ${his} aim off target.`);
				}
				r.push(`By the time ${he} pulls the muzzle back down the others are on ${him}. Fortunately, ${he} is equal to the challenge, getting ${his} sword into gory action. ${He} is physically powerful but unskilled, so the result is simple, unlovely butcher's work. This spectacle produces an increase in confidence in the security of ${V.arcologies[0].name}. There is some minor property damage to repair, but no lasting harm.`);
				cashX(-1000, "event", S.Bodyguard);
				V.arcologies[0].prosperity += 1;
			} else {
				r.push(`${S.Bodyguard.slaveName} hurriedly`);
				if ((S.Bodyguard.muscles + S.Bodyguard.height - 100) / 25 > 5) {
					r.push(`gets ${his} light machine gun firing. The weapon's high rate of fire tears through the first enemy but rapidly pulls ${his} aim off target.`);
				} else if ((S.Bodyguard.muscles + S.Bodyguard.height - 100) / 25 > 4) {
					r.push(`brings up ${his} battle rifle. ${He} hits the nearest assailant with ${his} first round and then fires again, but the weapon's powerful recoil sends it over ${his} target.`);
				} else if ((S.Bodyguard.muscles + S.Bodyguard.height - 100) / 25 > 3) {
					r.push(`brings up ${his} submachine gun. ${He} points it at the nearest assailant and pulls the trigger; the weapon's high rate of fire tears through the enemy but rapidly pulls ${his} aim off target.`);
				} else {
					r.push(`draws ${his} machine pistol. ${He} points it one-handed at the nearest assailant and pulls the trigger; the weapon's high rate of fire tears through the enemy but rapidly pulls ${his} aim off target.`);
				}
				r.push(`By the time ${he} pulls the muzzle back down the others are on ${him}. ${His} sword is very sharp, but so are ${his} assailants' weapons, and everyone is unskilled. As often happens with bladed fights between unarmored novices, everyone dies, and so fast that you cannot intervene. ${S.Bodyguard.slaveName} expires gasping in agony over multiple deep wounds to ${his} abdomen, but still manages to lock eyes with you as ${he} does. There is some minor property damage to repair, but no lasting harm.`);
				cashX(-1000, "event", S.Bodyguard);
				healthDamage(S.Bodyguard, 1000);
				removeSlave(S.Bodyguard, {dead: true});
			}
		} else if (V.personalArms > 0) {
			r.push(`You are prepared for this exigency. Your handsome clothing conceals a small but very powerful pistol. This fact comes slowly back to you as your ears and mind shake off the blast effects. There is something important about this fact, but you are having trouble thinking of it. Eventually, you remember, and groggily draw and level the weapon. Its rocket-assisted slugs detonate on contact, assisting your impaired marksmanship by causing fatal damage regardless of shot placement. You are alive, and not permanently damaged, which is more than can be said for your assailants, who have, with the assistance of your weapon, redecorated this area of the club in red tones. There is some minor property damage to repair, but no lasting harm.`);
			cashX(-1000, "event");
		} else if (V.mercenaries > 0) {
			r.push(`As you collect your senses, you are dimly aware of a massive form rushing to stand over you. In its hands is a chunky object that emits three long tongues of flame and makes three barking sounds that break through the ringing in your ears. You manage to get yourself together enough to understand the mercenary as he slings his shotgun and kneels to check you over. His experienced hands disclose no injury, and he says, "You'll be OK, ${properTitle()}. Come on, up and at 'em. Let's get you checked out." There is some minor property damage to repair, but no lasting harm.`);
			cashX(-1000, "event");
		} else if (V.arcologyUpgrade.drones === 1) {
			r.push(`Two security drones reach the scene at the very last second. They drop the assailants with taser rounds, the demonic little munitions' sharp heads burying themselves in muscle before applying voltage. There is some minor property damage to repair, but no lasting harm.`);
			cashX(-1000, "event");
		} else {
			r.push(`Without a bodyguard, decent security systems, or any other means of personal defense, you meet your ignominious death at the end of a bludgeon fashioned from a maintenance tool.`);
			gameover = true;
		}
		App.Events.addParagraph(node, r);
		if (gameover) {
			App.Events.LocalGameOver(node);
		} else {
			App.Events.addParagraph(node, [`Hours later, you receive another message from the Daughters of Liberty. This one is even briefer than the first. "Next time, '${properMaster()},'" it reads.`]);
		}
	}
};