code:
/* Stylesheet for WardrobeUse.tw */

#passage-wardrobe-use .optionMacro {
	display: inline;
}

#passage-wardrobe-use .optionDescription {
	width: unset;
	text-align: left;
}

#passage-wardrobe-use .optionValue {
	display: flex;
}

#passage-wardrobe-use .optionMacroOption {
	flex-grow: 1;
}

/* Last line shouldn't grow */
#passage-wardrobe-use .optionValue::after {
	flex-grow: 1000000000;
	content: '';
}


code:
:: Alpha disclaimer [nobr]

Warning

	This is a text-based game that includes descriptions of sexual activity, graphic violence, drug use, and other subjects not suitable for persons under the age of 18. This is a work of fiction; any resemblance to actual persons, places, or events is unintended.

Welcome!

	An in-game encyclopedia is available from the sidebar, with answers to most basic gameplay questions.

		Important info for new players, and a reminder for FC veterans:

	when the player is offered a set of choices, the

		Continue

	button at the top of the left sidebar will almost always remain available. Using this to decline all the options presented is not cheating. If it's available, it's supposed to be available. It represents the player character politely bowing out of the situation.

Please note

		This is an alpha.

	That means the game is missing content, is full of bugs, is imbalanced, and is generally in an incomplete state. The game will keep a start of turn autosave. If you encounter a bug, we strongly recommend you reload your start of turn autosave immediately. Please submit your feedback and bug reports at https://gitgud.io/pregmodfan/fc-pregmod/issues/. Consider attaching a save file and screenshot of the problem.

	 	Pregmod is a modification of the original Free Cities created by FCdev, which can be seen at https://freecitiesblog.blogspot.com/.

		version: $ver, mod version: $pmodVer, build: $releaseID

		<<link "More version info">>
			<<replace "#version">>

						Mod: expanded age ranges and other tweaks 2016-08-30

						+SV

						Mod: extra preg content and other crap

					Saves from versions prior to 0.6 are not compatible.

			<</replace>>
		<</link>>

	[[I am 18 or more years of age, I understand, and I wish to continue|Economy Intro]]

repo_name: KnightBoulegard/fc

license: mit


code:
App.UI.MultipleInspect = (function() {
	function _LSDDOM(slave, saleDescription, applyLaw) {
		// LSD will reset these globals, so we have to set them every single time
		V.saleDescription = (saleDescription ? 1 : 0);
		V.applyLaw = (applyLaw ? 1 : 0);

		const oldAS = V.activeSlave;
		V.activeSlave = slave;
		const frag = App.UI.DOM.renderPassage("Long Slave Description");
		V.activeSlave = oldAS;
		return frag;
	}

	/**
	 * Provide a mechanism to inspect multiple slaves at once (for example, for Household Liquidators and recETS).
	 * Intended for use from DOM passages.
	 * @param {Array<App.Entity.SlaveState>} slaves
	 * @param {boolean} showFamilyTree
	 * @param {boolean} saleDescription
	 * @param {boolean} applyLaw
	 * @returns {DocumentFragment}
	 */
	function MultipleInspectDOM(slaves, showFamilyTree, saleDescription, applyLaw) {
		const frag = document.createDocumentFragment();
		const tabbar = App.UI.DOM.appendNewElement("div", frag, "", "tabbar");

		for (const slave of slaves) {
			tabbar.append(App.UI.tabbar.tabButtonDOM(`slave${slave.ID}`, slave.slaveName));
			frag.append(App.UI.tabbar.makeTabDOM(`slave${slave.ID}`, _LSDDOM(slave, saleDescription, applyLaw)));
		}

		if (slaves.length > 1 && showFamilyTree) {
			const button = App.UI.tabbar.tabButtonDOM(`familyTreeTab`, "Family Tree");
			button.addEventListener('click', event => {
				renderFamilyTree(slaves, slaves[0].ID);
			});
			tabbar.append(button);
			const ftTarget = document.createElement("div");
			ftTarget.setAttribute("id", "familyTree");
			frag.append(App.UI.tabbar.makeTabDOM(`familyTreeTab`, ftTarget));
		}

		App.UI.tabbar.handlePreSelectedTab(`slave${slaves[0].ID}`);

		return frag;
	}

	return MultipleInspectDOM;
})();


code:
App.UI.OptionsGroup = (function() {
	/**
	 * @typedef value
	 * @property {*} value
	 * @property {string} [name]
	 * @property {string} mode
	 * @property {number} [compareValue]
	 * @property {string} [descAppend] can be SC markup
	 * @property {boolean} [on]
	 * @property {boolean} [off]
	 * @property {boolean} [neutral]
	 * @property {Function} [callback]
	 */

	const Option = class {
		/**
		 * @param {string} description can be SC markup
		 * @param {string} property
		 * @param {object} [object=V]
		 */
		constructor(description, property, object = V) {
			this.description = description;
			this.property = property;
			this.object = object;
			/**
			 * @type {Array<value>}
			 */
			this.valuePairs = [];
		}

		/**
		 * @param {*} name
		 * @param {*} [value=name]
		 * @param {Function} [callback]
		 * @returns {Option}
		 */
		addValue(name, value = name, callback = undefined) {
			this.valuePairs.push({
				name: name, value: value, mode: "=", callback: callback
			});
			return this;
		}

		/**
		 * @param {Array<*|Array>} values
		 * @returns {Option}
		 */
		addValueList(values) {
			for (const value of values) {
				if (Array.isArray(value)) {
					this.addValue(value[0], value[1]);
				} else {
					this.addValue(value);
				}
			}
			return this;
		}

		/**
		 * @param {*} value
		 * @param {number} compareValue
		 * @param {string} mode on of: "<", "<=", ">", ">="
		 * @param {string} [name=value]
		 */
		addRange(value, compareValue, mode, name = value) {
			this.valuePairs.push({
				name: name, value: value, mode: mode, compareValue: compareValue
			});
			return this;
		}

		/**
		 * @param {string} [unit]
		 * @returns {Option}
		 */
		showTextBox(unit) {
			this.textbox = true;
			if (unit) {
				this.unit = unit;
			}
			return this;
		}

		/**
		 * @param {string} comment can be SC markup
		 * @returns {Option}
		 */
		addComment(comment) {
			this.comment = comment;
			return this;
		}

		/**
		 * Adds a button that executes the callback when clicked AND reloads the passage
		 *
		 * @param {string} name
		 * @param {Function} callback
		 * @param {string} passage
		 */
		customButton(name, callback, passage) {
			this.valuePairs.push({
				name: name, value: passage, callback: callback, mode: "custom"
			});
			return this;
		}

		/**
		 * @param {string} element can be SC markup
		 * @returns {Option}
		 */
		addCustomElement(element) {
			this.valuePairs.push({
				value: element, mode: "plain"
			});
			return this;
		}

		/* modify last added option */

		/**
		 * Added to the description if last added value is selected.
		 * example use: addValue(...).customDescription(...).addValue(...).customDescription(...)
		 * @param {string} description can be SC markup
		 */
		customDescription(description) {
			this.valuePairs.last().descAppend = description;
			return this;
		}

		/**
		 * @param {Function} callback gets executed on every button click. Selected value is given as argument.
		 */
		addCallback(callback) {
			this.valuePairs.last().callback = callback;
			return this;
		}

		/**
		 * Mark option as on to style differently.
		 * @returns {Option}
		 */
		on() {
			this.valuePairs.last().on = true;
			return this;
		}

		/**
		 * Mark option as off to style differently.
		 * @returns {Option}
		 */
		off() {
			this.valuePairs.last().off = true;
			return this;
		}

		/**
		 * Mark option as neutral to style differently.
		 * @returns {Option}
		 */
		neutral() {
			this.valuePairs.last().neutral = true;
			return this;
		}
	};

	return class {
		constructor() {
			/**
			 * @type {Array<Option>}
			 */
			this.options = [];
		}

		/**
		 * @returns {App.UI.OptionsGroup}
		 */
		enableDoubleColumn() {
			this.doubleColumn = true;
			return this;
		}

		addOption(name, property, object = V) {
			const option = new Option(name, property, object);
			this.options.push(option);
			return option;
		}

		render() {
			const container = document.createElement("div");
			container.className = "options-group";
			if (this.doubleColumn) {
				container.classList.add("double");
			}
			for (/** @type {Option} */ const option of this.options) {
				/* left side */
				const desc = document.createElement("div");
				desc.className = "description";
				$(desc).wiki(option.description);
				container.append(desc);

				/* right side */
				const currentValue = option.object[option.property];
				let anySelected = false;

				const buttonGroup = document.createElement("div");
				buttonGroup.classList.add("button-group");
				for (const value of option.valuePairs) {
					if (value.mode === "plain") {
						/* insert custom SC markup and go to next element */
						$(buttonGroup).wiki(value.value);
						continue;
					}
					const button = document.createElement("button");
					button.append(value.name);
					if (value.on) {
						button.classList.add("on");
					} else if (value.off) {
						button.classList.add("off");
					} else if (value.neutral) {
						button.classList.add("neutral");
					}
					if (value.mode === "custom") {
						button.onclick = () => {
							value.callback();
							if (value.value) {
								Engine.play(value.value);
							} else {
								App.UI.reload();
							}
						};
					} else {
						if (value.mode === "=" && currentValue === value.value) {
							button.classList.add("selected", "disabled");
							anySelected = true;
							if (value.descAppend !== undefined) {
								desc.append(" ");
								$(desc).wiki(value.descAppend);
							}
						} else if (!anySelected && inRange(value.mode, value.compareValue, currentValue)) {
							button.classList.add("selected");
							// disable the button if clicking it won't change the variable value
							if (currentValue === value.value) {
								button.classList.add("disabled");
							}
							anySelected = true;
							if (value.descAppend !== undefined) {
								desc.append(" ");
								$(desc).wiki(value.descAppend);
							}
						}
						button.onclick = () => {
							option.object[option.property] = value.value;
							if (value.callback) {
								value.callback();
							}
							App.UI.reload();
						};
					}
					buttonGroup.append(button);
				}
				if (option.textbox) {
					const isNumber = typeof currentValue === "number";
					const textbox = App.UI.DOM.makeTextBox(currentValue, input => {
						option.object[option.property] = input;
						App.UI.reload();
					}, isNumber);
					if (isNumber) {
						textbox.classList.add("number");
					}
					buttonGroup.append(textbox);
					if (option.unit) {
						buttonGroup.append(" ", option.unit);
					}
				}
				if (option.comment) {
					const comment = document.createElement("span");
					comment.classList.add("comment");
					$(comment).wiki(option.comment);
					buttonGroup.append(comment);
				}
				container.append(buttonGroup);
			}

			return container;

			function inRange(mode, compareValue, value) {
				if (mode === "<") {
					return value < compareValue;
				} else if (mode === "<=") {
					return value <= compareValue;
				} else if (mode === ">") {
					return value > compareValue;
				} else if (mode === ">=") {
					return value >= compareValue;
				}
				return false;
			}
		}
	};
})();


code:
/**
 * Generate a list of links the player can safely jump to.
 *
 * Tags are:
 * jump-to-safe: Allow to jump to this passage from anywhere
 * jump-from-safe: Allow to jump from this passage to anywhere
 * hidden: Allows to jump to via history, but does not show up in the quick links menu; use together with jump-to-safe
 * no-history: Do not record history for this passage, act as if was never opened; mutually exclusive with the jump tags
 *
 * @returns {DocumentFragment}
 */
App.UI.quickMenu = (function() {
	// setup passage lists
	const jumpFrom = Story.lookup("tags", "jump-from-safe").map(passage => passage.title);
	const jumpTo = Story.lookup("tags", "jump-to-safe").map(passage => passage.title);
	const hidden = Story.lookup("tags", "jump-hidden").map(passage => passage.title);
	const noHistory = Story.lookup("tags", "no-history").map(passage => passage.title);

	// if property name is a passage name, then it's a link, otherwise only text.
	// category titles  are never links to passages
	// Only two values are allowed: true or an object following the same rules
	const layout = addOtherCategory({
		Main: true,
		Manage: {
			"Manage Penthouse": true,
			"Manage Arcology": true,
			"Manage Personal Affairs": true,
			"Manage Corporation": true,
			"Personal assistant options":true,
			Firebase: true,
			propagandaHub: true,
			securityHQ: true,
			secBarracks: true,
			riotControlCenter: true,
		},
		Social: {
			"Future Society": true,
			Policies: true,
			edicts: true,
			"Neighbor Interact": true,
		},
		Facilities: {
			"Head Girl Suite": true,
			"BG Select": true,
			Brothel: true,
			Club: true,
			Arcade: true,
			Dairy: true,
			Farmyard: true,
			"Servants' Quarters": true,
			"Master Suite": true,
			Schoolroom: true,
			Spa: true,
			Nursery: true,
			Clinic: true,
			Cellblock: true,
			Incubator: true,
			Pit: true,
		},
		Locations: {
			"Gene Lab": true,
			Dispensary: true,
			"Organ Farm": true,
			"Implant Manufactory": true,
			"Prosthetic Lab": true,
			Wardrobe: true,
			"The Black Market": true,
		},
		Tools: {
			"Find Slave": true,
			"Rules Assistant":true,
			"Personal Attention Select":true,
		},
		Options: {
			Options: true,
			"Summary Options": true,
			"Description Options": true,
			"Universal Rules": true,
		}
	});

	// true means hidden
	const hiddenPassages = cleanPassageMapping({
		Arcade: () => !V.arcade,
		"BG Select": () => V.dojo <= 1,
		Brothel: () => !V.brothel,
		Cellblock: () => !V.cellblock,
		Clinic: () => !V.clinic,
		Club: () => !V.club,
		Dairy: () => !V.dairy,
		Dispensary: () => !V.dispensary,
		edicts: () => V.secExpEnabled <= 0,
		Farmyard: () => !V.farmyard,
		Firebase: () => !V.SF.Toggle || V.SF.Toggle < 1 || V.SF.Active < 1,
		"Future Society": () => !V.FSAnnounced,
		"Gene Lab": () => !V.geneticMappingUpgrade,
		"Head Girl Suite": () => !V.HGSuite,
		"Implant Manufactory": () => !V.ImplantProductionUpgrade,
		Incubator: () => !V.incubator,
		"Manage Corporation": () => V.corp.Announced !== 1,
		"Master Suite": () => !V.masterSuite,
		"Neighbor Interact": () => V.arcologies.length === 0,
		Nursery: () => !V.nursery,
		"Organ Farm": () => !V.organFarmUpgrade,
		Pit: () => !V.pit,
		propagandaHub: () => V.secExpEnabled !== 1 || V.SecExp.buildings.propHub.active <= 0,
		"Prosthetic Lab": () => V.researchLab.level === 0,
		riotControlCenter: () => V.secExpEnabled !== 1 || V.riotCenter <= 0,
		Schoolroom: () => !V.schoolroom,
		secBarracks: () => V.secExpEnabled !== 1 || V.SecExp.buildings.barracks.active <= 0,
		securityHQ: () => V.secExpEnabled !== 1 || V.secHQ <= 0,
		"Servants' Quarters": () => !V.servantsQuarters,
		Spa: () => !V.spa,
		"The Black Market": () => V.rep < 10000,
	});

	// show different names than the actual passage name, can be a function
	const uiNames = cleanPassageMapping({
		Arcade: () => App.Entity.facilities.arcade.UIName,
		"BG Select": "Armory",
		Brothel: () => App.Entity.facilities.brothel.UIName,
		Cellblock: () => App.Entity.facilities.cellblock.UIName,
		Clinic: () => App.Entity.facilities.clinic.UIName,
		Club: () => App.Entity.facilities.club.UIName,
		Dairy: () => App.Entity.facilities.dairy.UIName,
		Dispensary: "Pharmaceutical Fabricator",
		edicts: "Edicts",
		Farmyard: () => App.Entity.facilities.farmyard.UIName,
		"Find Slave": "Locate Slave",
		Firebase: () => `${App.SF.Caps()}'s Firebase`,
		"Future Society": "Future Societies",
		"Head Girl Suite": () => App.Entity.facilities.headGirlSuite.UIName,
		Incubator: () => App.Entity.facilities.incubator.UIName,
		"Master Suite": () => App.Entity.facilities.masterSuite.UIName,
		"Neighbor Interact": "Diplomacy",
		Nursery: () => App.Entity.facilities.nursery.UIName,
		Options: "Game Options",
		"Personal assistant options": "Personal Assistant",
		"Personal Attention Select":"Personal Attention",
		Pit: () => App.Entity.facilities.pit.UIName,
		propagandaHub: "Manage PR",
		riotControlCenter: "Manage Rebels",
		Schoolroom: () => App.Entity.facilities.schoolroom.UIName,
		secBarracks: "Manage Military",
		securityHQ: "Manage Security",
		"Servants' Quarters": () => App.Entity.facilities.servantsQuarters.UIName,
		Spa: () => App.Entity.facilities.spa.UIName,
		"The Black Market": "Black Market",
	});

	// extra information behind the link, is a function
	const f = App.Entity.facilities;
	const extraInfo = cleanPassageMapping({
		Arcade: () => occupancy(f.arcade),
		"BG Select": () => occupancy(f.armory),
		Brothel: () => occupancy(f.brothel),
		Cellblock: () => occupancy(f.cellblock),
		Clinic: () => occupancy(f.clinic),
		Club: () => occupancy(f.club),
		Dairy: () => occupancy(f.dairy),
		Farmyard: () => occupancy(f.farmyard),
		"Head Girl Suite": () => occupancy(f.headGirlSuite),
		Incubator: () => occupancy(f.incubator),
		"Master Suite": () => occupancy(f.masterSuite),
		Nursery: () => occupancy(f.nursery),
		Pit: () => occupancy(f.pit),
		Schoolroom: () => occupancy(f.schoolroom),
		"Servants' Quarters": () => occupancy(f.servantsQuarters),
		Spa: () => occupancy(f.spa),
	});

	// true shows a notification symbol
	const notifications = cleanPassageMapping({
		"Future Society": () => V.FSCredits > 0 || V.FSReminder,
		Incubator: () => V.readySlaves > 0,
		"Manage Corporation": () => V.corp.SpecToken > 0 && V.corp.SpecTimer === 0,
	});

	// setup hotkeys list, upper/lower case is important!
	// Due to limitation to the key capture library keys cannot be used when they are already used in
	// src/002-config/mousetrapConfig.js
	const hotkeys = cleanPassageMapping({
		"BG Select": "b",
		"Buy Slaves": "s",
		edicts: "E",
		Firebase: "z",
		"Future Society": "f",
		Main: "m",
		"Manage Arcology": "c",
		"Manage Corporation":"C",
		"Manage Penthouse": "p",
		"Manage Personal Affairs": "x",
		"Neighbor Interact": "d",
		Options: "o",
		"Personal assistant options": "t",
		"Personal Attention Select": "a",
		Policies: "y",
		propagandaHub: "H",
		"Recruiter Select": "u",
		riotControlCenter: "R",
		"Rules Assistant": "r",
		secBarracks: "A",
		securityHQ: "S",
		"Universal Rules": "v",
		// Facilities
		Brothel: "1",
		Club: "2",
		Arcade: "3",
		Dairy: "4",
		Farmyard: "5",
		"Servants' Quarters": "6",
		"Master Suite": "7",
		Schoolroom: "8",
		Spa: "9",
		Nursery: "0",
		Clinic: "shift+1",
		Cellblock: "shift+2",
		Incubator: "shift+3",
		Pit: "shift+4",
	});

	/**
	 * The DOM element of name of the currently played passage or any of it's parents. Used during generation to
	 * uncollapse the category with the current passage.
	 * @type {HTMLElement}
	 */
	let currentPassage;
	/**
	 * DOM elements that have a notification. Used to traverse upwards to give the categories notifications too.
	 * @type {Array<HTMLElement>}
	 */
	let notificationPassages;

	let hotkeysEnabled = false;

	// register hotkeys
	for (const passage in hotkeys) {
		Mousetrap.bind(hotkeys[passage], () => {
			if (hotkeysEnabled
				// the passage is accessible
				&& !(hiddenPassages[passage] && hiddenPassages[passage]())) {
				Engine.play(passage);
			}
		});
	}

	// setup history
	let history = [];
	let historyNavigation = false;
	$(document).on(':passageinit', event => {
		// if navigated here normally, add passage to history, otherwise remove last entry from history
		if (!historyNavigation) {
			if (State.passage === event.passage.title // reloaded
				|| noHistory.includes(State.passage) // came from a no-history passage
				|| noHistory.includes(event.passage.title) // no history for this passage
			) {
				return;
			}
			// if last passage can be jumped to add passage to history, otherwise clear history
			if (jumpTo.includes(State.passage)) {
				history.unshift(State.passage);
			} else {
				history = [];
			}
		} else {
			historyNavigation = false;
			history.shift();
		}
	});
	Mousetrap.bind("backspace", () => {
		// jump back in history
		goBack();
	});

	/**
	 * Goes back in history if possible.
	 */
	function goBack() {
		if (history.length > 0 && jumpFrom.includes(State.passage)) {
			historyNavigation = true;
			Engine.play(history[0]);
		}
	}

	/**
	 * Add a "Return" link at second position if there is a history.
	 * @param {Array<HTMLElement>} linkList
	 */
	function addBackLink(linkList) {
		if (history.length > 0) {
			const div = document.createElement("div");
			div.classList.add("menu-link");
			const a = document.createElement("a");
			a.append("Return");
			a.onclick = goBack;
			div.append(a, " ", App.UI.DOM.makeElement("span", "[backspace]", "hotkey"));
			// insert at second position
			linkList.splice(1, 0, div);
		}
	}

	/**
	 * Generate a quick menu
	 * @returns {string|HTMLDivElement}
	 */
	function generateMenu() {
		if (!jumpFrom.includes(State.passage)) {
			hotkeysEnabled = false;
			return "";
		}
		hotkeysEnabled = true;
		currentPassage = null;
		notificationPassages = [];

		const div = document.createElement("div");
		div.classList.add("quick-links");

		try {
			// quick menu
			const links = generateLinkList(layout);

			if (V.debugMode) {
				addCategory(links, "Hidden", hidden.reduce((acc, cur) => {
					acc[cur] = true;
					return acc;
				}, {}));
			}

			addBackLink(links);
			div.append(...links);

			// traverse from current passage up to uncollapse.
			if (currentPassage !== null) {
				while (!currentPassage.classList.contains("quick-links")) {
					currentPassage.classList.remove("collapsed");
					currentPassage = currentPassage.parentElement;
				}
			}

			// traverse from notifications up to add icon
			for (let i = 0; i < notificationPassages.length; i++) {
				while (!notificationPassages[i].classList.contains("quick-links")) {
					if (notificationPassages[i].classList.contains("category")) {
						notificationPassages[i].classList.add("notification");
					}
					notificationPassages[i] = notificationPassages[i].parentElement;
				}
			}
		} catch (e) {
			console.error(e);
			div.append("Quick Links generation failed. Please run ", App.UI.DOM.passageLink("Backwards Compatibility", "Backwards Compatibility"));
		}

		return div;
	}

	/**
	 * Create a list of links and/or categories based on the given category
	 *
	 * @param {object} category
	 * @returns {Array<HTMLElement>}
	 */
	function generateLinkList(category) {
		/** @type {Array<HTMLElement>} */
		const links = [];
		for (const passage in category) {
			if (category[passage] === true) {
				addMenuLink(links, passage);
			} else {
				addCategory(links, passage, category[passage]);
			}
		}
		return links;
	}

	/**
	 * Add a category to a link list
	 *
	 * @param {Array<HTMLElement>} outerLinkList
	 * @param {string} name
	 * @param {object} category
	 */
	function addCategory(outerLinkList, name, category) {
		const innerLinkList = generateLinkList(category);
		if (innerLinkList.length === 0) {
			// category is empty
			return;
		}
		const toggle = document.createElement("div");
		toggle.classList.add("toggle");
		toggle.append(name);
		const content = document.createElement("div");
		content.classList.add("content");
		content.append(...innerLinkList);

		// wrap everything in one div, so we can control collapsing by changing only one class
		const wrapper = document.createElement("div");
		wrapper.classList.add("category", "collapsed");
		toggle.onclick = () => {
			wrapper.classList.toggle("collapsed");
		};
		wrapper.append(toggle, content);
		outerLinkList.push(wrapper);
	}

	/**
	 * Create a link to the given passage and add it to the link list if link is accessible.
	 *
	 * @param {Array<HTMLElement>} linkList
	 * @param {string} passage
	 */
	function addMenuLink(linkList, passage) {
		// passage is hidden
		if (hiddenPassages.hasOwnProperty(passage) && hiddenPassages[passage]()) {
			return;
		}
		// we show some kind of text/link
		const div = document.createElement("div");
		linkList.push(div);
		div.classList.add("menu-link");
		// this passage has a notification
		if (notifications[passage] && notifications[passage]()) {
			notificationPassages.push(div);
			div.classList.add("notification");
		}
		// there is additional non-link info
		if (extraInfo[passage]) {
			div.append(" ", extraInfo[passage]());
		}
		// we are already on this passage
		if (State.passage === passage) {
			div.prepend(uiName(passage));
			currentPassage = div;
			return;
		}
		// That is not a passage we can safely jump to, it's a deco element
		if (!jumpTo.includes(passage)) {
			div.prepend(uiName(passage));
			return;
		}
		// Create link
		const a = document.createElement("a");
		a.append(uiName(passage));
		a.onclick = () => {
			Engine.play(passage);
		};
		div.prepend(a);
		if (hotkeys[passage]) {
			div.append(" ", App.UI.DOM.makeElement("span", `[${hotkeys[passage]}]`, "hotkey"));
		}
	}

	/**
	 * Gives back the proper name for the passage or the passage title itself if there is none
	 *
	 * @param {string} passage
	 * @returns {string}
	 */
	function uiName(passage) {
		if (uiNames[passage]) {
			if (typeof uiNames[passage] === "function") {
				return uiNames[passage]();
			}
			return uiNames[passage];
		}
		return passage;
	}

	/**
	 * Adds an "Other" category to a given layout consisting of passages that can be jumped to not sorted into the
	 * existing layout.
	 *
	 * @param {object} layout
	 * @returns {object}
	 */
	function addOtherCategory(layout) {
		const passages = jumpTo.slice();

		// filter out passages used in layout
		filterPassages(passages, layout);

		// filter out hidden passages
		for (let i = 0; i < hidden.length; i++) {
			const index = passages.indexOf(hidden[i]);
			if (index > -1) {
				passages.splice(index, 1);
			}
		}

		// Other category
		if (passages.length > 0) {
			const other = {};
			for (let i = 0; i < passages.length; i++) {
				other[passages[i]] = true;
			}
			layout.Other = other;
		}
		return layout;
	}

	/**
	 * Remove all entries from a given list that exist as keys in a given layout.
	 *
	 * @param {Array<string>} passages
	 * @param {object} layout
	 */
	function filterPassages(passages, layout) {
		for (const category in layout) {
			if (layout[category] === true) {
				const index = passages.indexOf(category);
				if (index > -1) {
					passages.splice(index, 1);
				}
			} else {
				filterPassages(passages, layout[category]);
			}
		}
	}

	/**
	 * @param {App.Entity.Facilities.Facility} facility
	 * @returns {string}
	 */
	function occupancy(facility) {
		const desc = facility.occupancyReport(V.abbreviateSidebar === 2);
		if (desc === "") {
			return "";
		}
		return `(${desc})`;
	}

	/**
	 * Cleans out all mappings that are not contained in jumpTo and therefore not considered safe.
	 *
	 * @param {object} keys
	 * @returns {object}
	 */
	function cleanPassageMapping(keys) {
		const result = {};
		for (const key in keys) {
			if (jumpTo.includes(key)) {
				result[key] = keys[key];
			} else {
				console.log("Found mapping to unsafe passage: " + key + " to " + keys[key]);
			}
		}
		return result;
	}

	return generateMenu;
})();


code:
:: StoryCaption [nobr]

<<set _Pass = passage()>>

<<if $ui != "start">>
<<userButton>>
<</if>>

<<if $ui != "start">>
	<<if $cheatMode || $debugMode>>

_Pass

		<<if _Pass == "JS Random Event">>

<<= $event.eventName>>

		<</if>>
	<</if>>

		''Week $week''
		<<if $week > 52>> (<<= years($week)>>) <</if>>

 Week of <<= asDateString($week)>>

	<<if _Pass == "Main" && $cheatMode && $cheatModeM>>
		<<set _TWeek = $week>>
		<<textbox "$week" $week>>
		<<link "Apply">>
			<<set $week = Math.trunc(Number($week) || _TWeek)>>
			<<if $week < 1>><<set $week = 1>><</if>>
			<<replace "#week">>''Week $week''<</replace>>
		<</link>>
	<</if>>

 <<if $weatherToday.severity == 1>>
		@@.cyan; $weatherToday.name@@
 <<elseif $weatherToday.severity == 2>>
		@@.yellow; $weatherToday.name@@
 <<elseif $weatherToday.severity == 3>>
		@@.orange; $weatherToday.name@@
 <<else>>
		@@.red; $weatherToday.name@@
 <</if>>

	<<if $sideBarOptions.Cash > 0>>
		<<set $cash = Math.trunc($cash)>>
		<<if $cash > 0>>
			@@.yellowgreen;Cash@@ | @@.yellowgreen;<<= cashFormat($cash)>>@@
		<<else>>
			__@@.red;Cash@@__ | @@.red;<<= cashFormat($cash)>>@@
		<</if>>

		<<if _Pass == "Main">>

			<<set _TCash2 = ($cash-$cashLastWeek)>>
			<<if _TCash2 > 0>> (@@.green;<<= cashFormat(_TCash2)>>@@
			<<else>> (@@.red;<<= cashFormat(_TCash2)>>@@
			<</if>>
			since last week)
			<<if $cheatMode && $cheatModeM>>
				<<set _TCash1 = $cash>>
				<<textbox "$cash" $cash>>
				<<link "Apply">>
					<<set $cash = Math.trunc(Number($cash) || _TCash1), $cheater = 1>>
				<</link>>
			<</if>>

 <</if>>
	<</if>>

	<<if $foodMarket > 0>>

		<<set $food = Math.trunc($food)>>
		<<set $foodConsumption = Math.trunc(($lowerClass*$foodRate.lower) + ($middleClass*$foodRate.middle) + ($upperClass*$foodRate.upper) + ($topClass*$foodRate.top))>>
		<<if $food > $foodConsumption>> /* if there is enough food for the next week */
			@@.chocolate;Food@@
		<<else>>
			@@.red;Food@@
		<</if>>
		<<if $food < 0>> <<set $food = 0>> <</if>>
		| @@.chocolate;<<= massFormat($food)>>@@

		<<if _Pass == "Main">>

			<<set _TFood2 = ($food-$foodLastWeek)>>
			<<if _TFood2 < 0>> (@@.red;<<= massFormat(_TFood2)>>@@
			<<else>> (@@.chocolate;+<<= massFormat(_TFood2)>>@@
			<</if>>
			since last week)
			<<if $cheatMode && $cheatModeM>>
				<<set _TFood1 = $food>>
				<<textbox "$food" $food>>
				<<link "Apply">>
					<<if $food < 0>>
						<<set $food = 0>>
					<<else>>
						<<set $food = Math.trunc(Number($food) || _TFood1), $cheater = 1>>
					<</if>>
				<</link>>
			<</if>>
		<</if>>
	<</if>>

	<<if $sideBarOptions.Upkeep > 0>>

			<<if _Pass == "Main">> [[Upkeep|Costs Budget]] |
			<<else>> Upkeep |
			<</if>>
			@@.red;<<= cashFormat($costs)>>@@

	<</if>>

	<<if $sideBarOptions.SexSlaveCount > 0>>

@@.pink;Total Sex Slaves@@ | <<= $slaves.length>>

	<</if>>
	<<if $sideBarOptions.roomPop > 0>>

@@.pink;Penthouse Beds@@ | <<if $dormitoryPopulation+$roomsPopulation > ($dormitory+$rooms)>>@@.red;<<= $dormitoryPopulation+$roomsPopulation>>@@<<else>><<= $dormitoryPopulation+$roomsPopulation>><</if>>/<<= ($dormitory+$rooms)>>

@@.pink;Dormitory Beds@@ | <<if $dormitoryPopulation > $dormitory>>@@.red;<<= $dormitoryPopulation>>@@<<else>><<= $dormitoryPopulation>><</if>>/<<= $dormitory>>

@@.pink;Luxury Rooms@@ | <<if $roomsPopulation > $rooms>>@@.red;<<= $roomsPopulation>>@@<<else>><<= $roomsPopulation>><</if>>/<<= $rooms>>

	<</if>>

	<<if _Pass != "Manage Penthouse">>
		<<if $sideBarOptions.GSP > 0 && _Pass != "Neighbor Interact">>

			@@.yellowgreen;GSP@@ |
			<<= num(Math.trunc(0.1*$arcologies[0].prosperity))>><<if $showNumbers == 2 || ($showNumbers == 1 && Math.trunc(0.1*$arcologies[0].prosperity) > $showNumbersMax)>>m<<else>> million<</if>>
			<<= ownershipReport({sidebar: true})>>

		<</if>>

		<<if $sideBarOptions.Rep > 0>>

			<<if _Pass == "Main">> [[Rep|Rep Budget]] |
			<<else>> @@.green;Rep@@ |
			<</if>>
			<<if $rep > 19000>>
				@@color:rgb(0,145,0);worshipped@@
			<<elseif $rep > 18000>>
				@@color:rgb(0,150,0);great@@
			<<elseif $rep > 17000>>
				@@color:rgb(0,155,0);exalted@@
			<<elseif $rep > 16000>>
				@@color:rgb(0,160,0);illustrious@@
			<<elseif $rep > 15000>>
				@@color:rgb(0,165,0);prestigious@@
			<<elseif $rep > 14000>>
				@@color:rgb(0,170,0);renowned@@
			<<elseif $rep > 13000>>
				@@color:rgb(0,175,0);famed@@
			<<elseif $rep > 12000>>
				@@color:rgb(0,180,0);celebrated@@
			<<elseif $rep > 11000>>
				@@color:rgb(0,185,0);honored@@
			<<elseif $rep > 10000>>
				@@color:rgb(0,190,0);acclaimed@@
			<<elseif $rep > 9000>>
				@@color:rgb(0,195,0);eminent@@
			<<elseif $rep > 8250>>
				@@color:rgb(0,200,0);prominent@@
			<<elseif $rep > 7500>>
				@@color:rgb(0,205,0);distinguished@@
			<<elseif $rep > 6750>>
				@@color:rgb(0,210,0);admired@@
			<<elseif $rep > 6000>>
				@@color:rgb(0,215,0);esteemed@@
			<<elseif $rep > 5250>>
				@@color:rgb(0,220,0);respected@@
			<<elseif $rep > 4500>>
				@@color:rgb(0,225,0);known@@
			<<elseif $rep > 3750>>
				@@color:rgb(0,230,0);recognized@@
			<<elseif $rep > 3000>>
				@@color:rgb(0,235,0);rumored@@
			<<elseif $rep > 2250>>
				@@color:rgb(0,240,0);envied@@
			<<elseif $rep > 1500>>
				@@color:rgb(0,245,0);resented@@
			<<elseif $rep > 750>>
				@@color:rgb(0,250,0);disliked@@
			<<else>>
				@@color:rgb(0,255,0);unknown@@
			<</if>>
			(<<= num($rep)>>)
			<<if _Pass == "Main" && $cheatMode && $cheatModeM>>
				<<set _TRep = $rep>>
				<<textbox "$rep" $rep>>
				<<link "Apply">>
					<<set $rep = Math.clamp(Math.trunc(Number($rep) || _TRep), 0, 20000), $cheater = 1>>
				<</link>>
			<</if>>

		<</if>>

		<<if $secExpEnabled > 0 && ($sideBarOptions.Authority + $sideBarOptions.Security + $sideBarOptions.Crime > 0)>>
			<<if $sideBarOptions.Authority > 0>>

				@@.darkviolet;Auth@@ |
				<<set $SecExp.core.authority = Math.clamp(Math.trunc($SecExp.core.authority), 0, 20000)>>
				<<if $SecExp.core.authority > 19500>>
					@@color:rgb(148, 0, 211);divine will@@
				<<elseif $SecExp.core.authority > 19000>>
					@@color:rgb(148, 0, 211);sovereign@@
				<<elseif $SecExp.core.authority > 18000>>
					@@color:rgb(148, 0, 211);monarch@@
				<<elseif $SecExp.core.authority > 17000>>
					@@color:rgb(148, 0, 211);tyrant@@
				<<elseif $SecExp.core.authority > 15000>>
					@@color:rgb(148, 0, 211);dictator@@
				<<elseif $SecExp.core.authority > 14000>>
					@@color:rgb(148, 0, 211);prince@@
				<<elseif $SecExp.core.authority > 13000>>
					@@color:rgb(183, 0, 211);master@@
				<<elseif $SecExp.core.authority > 12000>>
					@@color:rgb(183, 0, 211);leader@@
				<<elseif $SecExp.core.authority > 11000>>
					@@color:rgb(183, 0, 211);director@@
				<<elseif $SecExp.core.authority > 10000>>
					@@color:rgb(183, 0, 211);overseer@@
				<<elseif $SecExp.core.authority > 9000>>
					@@color:rgb(183, 0, 211);chief@@
				<<elseif $SecExp.core.authority > 8000>>
					@@color:rgb(183, 0, 211);manager@@
				<<elseif $SecExp.core.authority > 7000>>
					@@color:rgb(211,0,204);principal@@
				<<elseif $SecExp.core.authority > 6000>>
					@@color:rgb(211,0,204);auxiliary@@
				<<elseif $SecExp.core.authority > 5000>>
					@@color:rgb(211,0,204);subordinate@@
				<<elseif $SecExp.core.authority > 4000>>
					@@color:rgb(211,0,204);follower@@
				<<elseif $SecExp.core.authority > 3000>>
					@@color:rgb(211,0,204);powerless@@
				<<elseif $SecExp.core.authority > 2000>>
					@@color:rgb(211,0,204);toothless@@
				<<elseif $SecExp.core.authority > 1000>>
					@@color:rgb(211,0,204);mostly harmless@@
				<<else>>
					@@color:rgb(211,0,204);harmless@@
				<</if>>
				(<<= num($SecExp.core.authority)>>)
				<<if _Pass == "Main" && $cheatMode && $cheatModeM>>
					<<set _TAuth = $SecExp.core.authority>>
					<<textbox "$SecExp.core.authority" $SecExp.core.authority>>
					<<link "Apply">>
						<<set $SecExp.core.authority = Math.clamp(Math.trunc(Number($SecExp.core.authority) || _TAuth), 0, 20000), $cheater = 1>>
					<</link>>
				<</if>>

			<</if>>

			<<if $sideBarOptions.Security > 0>>

				@@.deepskyblue;Security@@ | @@.deepskyblue;<<= Math.trunc($SecExp.security.cap)>>%@@
				<<if _Pass == "Main" && $cheatMode && $cheatModeM>>
					<<set _TSec = $SecExp.security.cap>>
					<<textbox "$SecExp.security.cap" $SecExp.security.cap>>
					<<link "Apply">>
						<<set $SecExp.security.cap = Math.clamp(Math.trunc(Number($SecExp.security.cap) || _TSec), 0, 100), $cheater = 1>>
					<</link>>
				<</if>>

			<</if>>

			<<if $sideBarOptions.Crime > 0>>

				@@.orangered;Crime@@ | @@.orangered;<<= Math.trunc($SecExp.core.crimeLow)>>%@@
				<<if _Pass == "Main" && $cheatMode && $cheatModeM>>
					<<set _TCrime = $SecExp.core.crimeLow>>
					<<textbox "$SecExp.core.crimeLow" $SecExp.core.crimeLow>>
					<<link "Apply">>
						<<set $SecExp.core.crimeLow = Math.clamp(Math.trunc(Number($SecExp.core.crimeLow) || _TCrime), 0, 100), $cheater = 1>>
					<</link>>
				<</if>>

			<</if>>

		<</if>> /* Closes secExp check and associated sideBarOptions options */
	<</if>> /* Closes Pass != "Manage Penthouse""*/

		<<includeDOM App.UI.quickMenu()>>

	<<if $nextButton != "Continue" && $nextButton != " ">>
		<<if _Pass == "Rules Assistant">>

[[Rules Assistant Summary]]

		<</if>> /* Closes Pass state check */

		<<if $debugMode > 0>>

				Debugging Tools

<<link "Display Variables">><<checkvars>><</link>>

					<<link "Display Changed Variables">>
						<<set Config.history.maxStates = 2>> /* makes sure we store the current state so we can return to it */
						<<goto "Variable Difference">>
					<</link>>

<<link "Bug Report">><<bugreport>><</link>>

				<<if $debugModeCustomFunction > 0>>

						<<textarea "_customEvalCode" "">>
						<<link "Run Custom Function">>
							<<if _customEvalCode>>
								<<if _customEvalCode.charAt(0) != "(" || $nextLink == ")">> /* second condition is only there for sanityCheck */
									<<set _customEvalCode = "(" + _customEvalCode + ")">>
								<</if>>
								<<if typeof eval(_customEvalCode) === "function">>
									<<run (eval(_customEvalCode))()>>
								<</if>>
							<</if>>
							<<goto _Pass>>
						<</link>>

				<</if>>

<<link "Dump Game State">><<run App.Debug.dumpGameState()>><</link>>

		<</if>>
	<</if>> /* Closes nextButton != "Continue" && nextButton != " " */
<<elseif _Pass == "Starting Girls">>
	<<set _slaveCost = startingSlaveCost($activeSlave)>>

		<<if _slaveCost > $cash>>
			@@.red;This slave will cost ''<<print cashFormat(_slaveCost)>>.''

			You only have: <<print cashFormat($cash)>>@@
		<<else>>
			This slave will cost @@.yellowgreen;''<<print cashFormat(_slaveCost)>>.''@@

			You have <<print cashFormat($cash)>>.
		<</if>>

<</if>>

	//''FCE'':// <<= App.Encyclopedia.Dialog.linkSC($encyclopedia, $encyclopedia)>>


code:
:: StoryCaptionWidgets [widget nobr]

<<widget "userButton">>
	<<set _Pass = passage()>>
	/* Must use link so spacebar shortcut will work. */
	/* Don't remove these spans, it will break things. */
	<<if _Pass != "End Week">>
		<<if _Pass == "Main">>

<<link "END WEEK">><<run endWeek()>><</link>> @@.cyan;[Ent]@@

			<<if $rulesAssistantAuto == 1 && DefaultRulesError()>>

@@.yellow;WARNING: Rules Assistant has rules with errors!@@

			<</if>>
		<<else>>

			<<if $nextButton != " ">>
				<<link "$nextButton">>
					<<goto $nextLink>>
				<</link>> @@.cyan;[Space]@@
			<</if>>

		<</if>>
	<</if>>
<</widget>>


code:
:: SVG filters

<svg class="defs-only" style="width: 0; height: 0; position: absolute;">
	<filter id="skin-black" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="0.15 0 0 0 0 0 0.1 0 0 0 0 0 0.05 0 0 0 0 0 1 0"/>
	</filter>
	<filter id="skin-bronzed" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="0.8 0 0.1 0 0 0 0.6 0 0 0 0 0 0.3 0 0 0 0 0 1 0"/>
	</filter>
	<filter id="skin-brown" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="0.8 0 0 0 0 0 0.5 0 0 0 0 0 0.15 0 0 0 0 0 1 0"/>
	</filter>
	<filter id="skin-dark" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="0.5 0 0 0 0 0 0.2 0 0 0 0 0 0.1 0 0 0 0 0 1 0 "/>
	</filter>
	<filter id="skin-ebony" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="0.1 0 0 0 0 0 0.05 0 0 0 0 0 0.05 0 0 0 0 0 1 0"/>
	</filter>
	<filter id="skin-extremely-pale" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="1.05 0 0 0 0 0 1.05 0 0 0 0 0 1.1 0 0 0 0 0 1 0"/>
	</filter>
	<filter id="skin-fair" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="0.8 0 0 0 0.2 0 0.8 0 0 0.2 0 0 0.8 0 0.2 0 0 0 1 0"/>
	</filter>
	<filter id="skin-light" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="0.5 0 0 0 0.5 0 0.5 0 0 0.5 0 0 0.5 0 0.5 0 0 0 1 0"/>
	</filter>
	<filter id="skin-light-brown" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="0.8 0 0 0 0 0 0.7 0 0 0 0 0 0.5 0 0 0 0 0 1 0"/>
	</filter>
	<filter id="skin-olive" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="0.9 0 0 0 0 0 0.8 0 0 0 0 0 0.6 0 0 0 0 0 1 0"/>
	</filter>
	<filter id="skin-pale" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="0.6 0 0 0 0.4 0 0.6 0 0 0.4 0 0 1 0 0 0 0 0 1 0"/>
	</filter>
	<filter id="skin-tanned" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="0.8 0 0 0 0 0 0.65 0 0 0 0 0 0.5 0 0 0 0 0 1 0"/>
	</filter>
	<filter id="skin-white" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0"/>
	</filter>
	<filter id="skin-dyed-red" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="0.6 0 0 0 0.4 0 0.25 0 0 0.2 0 0 0 0 0.3 0 0 0 1 0 "/>
	</filter>
	<filter id="skin-dyed-green" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="0.7 0 0 0 0 0.2 0.65 0.15 0 0 0 0 0.5 0 0 0 0 0 1 0"/>
	</filter>
	<filter id="skin-dyed-blue" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="0.3 0 0 0 0 0 0.5 0 0 0 0.4 0.2 0.25 0 0.1 0 0 0 1 0"/>
	</filter>

	<filter id="hair-ashen" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="0.7 0 0 0 0.1 0 0.7 0 0 0.1 0 0 0.7 0 0.1 0 0 0 1 0"/>
	</filter>
	<filter id="hair-auburn" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="0.8 0 0 0 0.2 0 0.5 0 0 0 0 0 0.15 0 0 0 0 0 1 0"/>
	</filter>
	<filter id="hair-black" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="0.2 0 0 0 0 0 0.15 0 0 0 0 0 0.1 0 0 0 0 0 1 0"/>
	</filter>
	<filter id="hair-blazing-red" color-interpolation-filters="sRGB">
		<feComponentTransfer>
			<feFuncR type="gamma" exponent="0.10" />
			<feFuncG type="gamma" exponent="3.00" />
			<feFuncB type="gamma" exponent="7.50" />
		</feComponentTransfer>
	</filter>
	<filter id="hair-blonde" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="1.4 0 0 0 0 0 1.3 0 0 0 0 0 0.7 0 0 0 0 0 1 0"/>
	</filter>
	<filter id="hair-blue" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="0.3 0 0 0 0 0 0.5 0 0 0 0.4 0.2 0.25 0 0.1 0 0 0 1 0"/>
	</filter>
	<filter id="hair-bright-red" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="1 0 0 0 0.2 0 0.4 0 0 0 0 0 0.1 0 0.1 0 0 0 1 0"/>
	</filter>
	<filter id="hair-brown" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="0.8 0 0 0 0 0 0.5 0 0 0 0 0 0.15 0 0 0 0 0 1 0"/>
	</filter>
	<filter id="hair-brunette" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="0.8 0 0 0 0 0 0.5 0 0 0 0 0 0.15 0 0 0 0 0 1 0"/>
	</filter>
	<filter id="hair-burgundy" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="0.45 0 0 0 0.25 0 0.2 0 0 0.1 0 0 0.3 0 0.2 0 0 0 1 0"/>
	</filter>
	<filter id="hair-chestnut" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="0.9 0 0 0 0.2 0 0.7 0 0 0 0 0 0.1 0 0.1 0 0 0 1 0"/>
	</filter>
	<filter id="hair-chocolate-brown" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="0.6 0 0 0 0 0 0.2 0 0 0 0 0 0.1 0 0 0 0 0 1 0"/>
	</filter>
	<filter id="hair-copper" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="1.5 0 0 0 -0.2 0 0.8 0 0 -0.2 0 0 0.1 0 0 0 0 0 1 0"/>
	</filter>
	<filter id="hair-dark" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="0.5 0 0 0 0 0 0.2 0 0 0 0 0 0.1 0 0 0 0 0 1 0"/>
	</filter>
	<filter id="hair-dark-blonde" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="1.1 0 0 0 0 0 0.9 0 0 0 0 0 0.4 0 0 0 0 0 1 0"/>
	</filter>
	<filter id="hair-dark-brown" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="0.6 0 0 0 0 0 0.35 0 0 0 0 0 0.1 0 0 0 0 0 1 0"/>
	</filter>
	<filter id="hair-dark-red" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="0.7 0 0 0 0.2 0 0.15 0 0 0.1 0 0 0 0 0 0 0 0 1 0"/>
	</filter>
	<filter id="hair-deep-red" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="1.5 0 0 0 -0.3 0 0.3 0 0 -0.1 0 0 0 0 0 0 0 0 1 0"/>
	</filter>
	<filter id="hair-dirty-blonde" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="0.8 0 0 0 0.3 0 0.7 0 0 0.3 0 0 0.2 0 0.5 0 0 0 1 0"/>
	</filter>
	<filter id="hair-fair-blonde" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="1 0 0 0 0.5 0 0.8 0 0 0.4 0 0 0.6 0 0.2 0 0 0 1 0"/>
	</filter>
	<filter id="hair-ginger" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="1.2 0 0 0 0.1 0 0.9 0 0 0 0 0 0.1 0 0.3 0 0 0 1 0"/>
	</filter>
	<filter id="hair-golden" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="1.7 0 0 0 -0.1 0 1.5 0 0 -0.1 0 0 0.1 0 0.3 0 0 0 1 0"/>
	</filter>
	<filter id="hair-golden-blonde" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="1.8 0 0 0 -0.3 0 1.6 0 0 -0.4 0 0 0.3 0 0.1 0 0 0 1 0"/>
	</filter>
	<filter id="hair-green" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="0.7 0 0 0 0 0.2 0.65 0.15 0 0 0 0 0.5 0 0 0 0 0 1 0"/>
	</filter>
	<filter id="hair-gray" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0"/>
	</filter>
	<filter id="hair-grey" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0"/>
	</filter>
	<filter id="hair-hazel" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="0.8 0 0 0 0.2 0 0.5 0 0 0.2 0 0 0.15 0 0.15 0 0 0 1 0"/>
	</filter>
	<filter id="hair-light-brown" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="0.8 0 0 0 0 0 0.7 0 0 0 0 0 0.5 0 0 0 0 0 1 0"/>
	</filter>
	<filter id="hair-light-purple" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="0.7 0 0 0 0.4 0 0.1 0 0 0.2 0 0 0.7 0 0.3 0 0 0 1 0"/>
	</filter>
	<filter id="hair-neon-blue" color-interpolation-filters="sRGB">
		<feComponentTransfer>
			<feFuncR type="gamma" exponent="3.75" />
			<feFuncG type="gamma" exponent="0.75" />
			<feFuncB type="gamma" exponent="0.25" />
		</feComponentTransfer>
	</filter>
	<filter id="hair-neon-green" color-interpolation-filters="sRGB">
		<feComponentTransfer>
			<feFuncR type="gamma" exponent="3.00" />
			<feFuncG type="gamma" exponent="0.25" />
			<feFuncB type="gamma" exponent="0.98" />
		</feComponentTransfer>
	</filter>
	<filter id="hair-neon-pink" color-interpolation-filters="sRGB">
		<feComponentTransfer>
			<feFuncR type="gamma" exponent="0.10" />
			<feFuncG type="gamma" exponent="1.00" />
			<feFuncB type="gamma" exponent="0.50" />
		</feComponentTransfer>
	</filter>
	<filter id="hair-olive" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="0.7 0 0 0 0 0 0.6 0 0 0 0 0 0.15 0 0 0 0 0 1 0"/>
	</filter>
	<filter id="hair-onyx-black" color-interpolation-filters="sRGB">
		<feComponentTransfer>
			<feFuncR type="gamma" exponent="5.50" />
			<feFuncG type="gamma" exponent="6.00" />
			<feFuncB type="gamma" exponent="7.00" />
		</feComponentTransfer>
	</filter>
	<filter id="hair-pale-blonde" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="0.45 0 0 0 0.7 0 0.4 0 0 0.7 0 0 0.6 0 0.4 0 0 0 1 0"/>
	</filter>
	<filter id="hair-peachy" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="1.45 0 0 0 0 0 1.4 -0.1 0 -0.1 0 0 0.85 0 0 0 0 0 1 0"/>
	</filter>
	<filter id="hair-pink" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="1.5 0 0 0 0 0 1.5 -0.2 0 -0.2 0 0 1 0 0 0 0 0 1 0"/>
	</filter>
	<filter id="hair-platinum-blonde" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="0.6 0 0 0 0.4 0 0.6 0 0 0.4 0 0 1 0 0 0 0 0 1 0"/>
	</filter>
	<filter id="hair-purple" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="0.9 0 0 0 0 0 0.2 0 0 0 0 0 1 0 0 0 0 0 1 0"/>
	</filter>
	<filter id="hair-raven-black" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="0.2 0 0 0 0 0 0.2 0 0 0 0 0 0.3 0 0 0 0 0 1 0"/>
	</filter>
	<filter id="hair-red" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="0.6 0 0 0 0.4 0 0.25 0 0 0.2 0 0 0 0 0.3 0 0 0 1 0"/>
	</filter>
	<filter id="hair-red-blonde" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="1 0 0 0 0.25 0 0.8 0 0 0.1 0 0 0.35 0 0.15 0 0 0 1 0"/>
	</filter>
	<filter id="hair-silver" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="0.8 0 0 0 0.3 0 0.8 0 0 0.3 0 0 0.8 0 0.3 0 0 0 1 0"/>
	</filter>
	<filter id="hair-strawberry-blonde" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="1.2 0 0 0 0.2 0 1.2 0 0 0 0 0 0.7 0 0 0 0 0 1 0"/>
	</filter>
	<filter id="hair-violet" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="2 -1 0 0 0 0 2 -1 0 0 -1 0 2 0 0 0 0 0 1 0"/>
	</filter>
	<filter id="hair-white" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="0.6 0 0 0 0.5 0 0.6 0 0 0.5 0 0 0.6 0 0.5 0 0 0 1 0"/>
	</filter>
	<filter id="hair-white-blonde" color-interpolation-filters="sRGB">
		<feColorMatrix type="matrix" values="0.7 0 0 0 0.5 0 0.7 0 0 0.5 0 0 0.5 0 0.4 0 0 0 1 0"/>
	</filter>
</svg>


code:
App.UI.Theme = (function() {
	// NOTE: Due to browser limitations it is not possible to retrieve the path of selected files, only the filename.
	// We therefore expect all files to be located in the same directory as the HTML file. Selected files from somewhere
	// else will simply not be loaded or if a file in the correct place has the same name, it will be loaded instead.
	let currentThemeElement = null;
	let devTheme = null;

	// reload theme on page reload
	$(document).on(":passagestart", () => {
		if (currentThemeElement === null && V.theme !== 0 && V.theme !== undefined) {
			load(V.theme);
		}
	});

	return {
		selector: selector,
		devTheme: reloadDevTheme,
		onLoad: onLoad
	};

	/**
	 * @returns {HTMLDivElement}
	 */
	function selector() {
		const div = document.createElement("div");

		const selector = document.createElement("input");
		selector.type = "file";
		selector.accept = ".css";
		div.append(selector);

		div.append(App.UI.DOM.link("Apply", () => {
			unload();
			if (selector.files.length > 0) {
				const themeFile = selector.files[0];
				load(themeFile.name);
			}
		}), " ", App.UI.DOM.link("Unload", unload));

		return div;
	}

	/**
	 * @param {string} filename or filepath relative to the HTML file.
	 */
	function load(filename) {
		V.theme = filename;

		currentThemeElement = newTheme(filename);

		document.head.appendChild(currentThemeElement);
	}

	function unload() {
		if (currentThemeElement !== null) {
			document.head.removeChild(currentThemeElement);
			currentThemeElement = null;
			V.theme = 0;
		}
	}

	/**
	 * Unloads current dev theme and loads new theme if specified.
	 * @param {string} [filename]
	 */
	function reloadDevTheme(filename) {
		if (devTheme !== null) {
			document.head.removeChild(devTheme);
			devTheme = null;
		}

		if (filename !== undefined) {
			devTheme = newTheme(filename);

			document.head.appendChild(devTheme);
		}
	}

	function newTheme(filename) {
		const theme = document.createElement("link");
		theme.setAttribute("rel", "stylesheet");
		theme.setAttribute("type", "text/css");
		theme.setAttribute("href", `./${filename}`);
		return theme;
	}

	/**
	 * @param {object} V
	 */
	function onLoad(V) {
		if (V.theme !== 0) {
			load(V.theme);
		}
	}
})();


code:
:: setupVars [widget nobr]

/*
* This passage is tagged as widget, so it will be loaded whenever any passage is loaded. The global setup object seems to reset itself to {} (empty object): until we figure out why, this is where we set properties for it.
*/
<<for _k,_v range App.Data.misc>>
	<<set setup[_k] = _v>>
<</for>>
<<run App.Art.cacheArtData()>>
<<run App.Corporate.Init()>>


code:
:: init [nobr]

/*
FREE CITIES
a text-based slave management game
Copyright (C) 2017 freecitiesdev

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

/* Set up the game as politely as possible. If values are already set, they are preserved. */
<<run App.Update.setNonexistantProperties(V, App.Data.defaultGameStateVariables)>>

/* These variables must be created AND set to default values, NG+ or not */
<<run App.Update.setExistantProperties(V, App.Data.resetOnNGPlus)>>

<<set $ver = App.Version.base, $pmodVer = App.Version.pmod, $releaseID = App.Version.release>>

/* 0 out the record books as we start a new game */
<<run setupLastWeeksCash()>>
<<run setupLastWeeksRep()>>

<<if $saveImported == 0>> /* new game (not NG+) */
	<<set $PC = basePlayer()>>
	<<run WombInit($PC)>>
	<<run cashX(10000, "personalBusiness")>>
	<<include "Init Rules">>
<<else>> /* imported save (NG+) */
	<<run App.Data.NewGamePlus()>>
<</if>>

/* Porn star counts (prestige 1) and ID's (prestige 3) */
<<set $pornStars = {}>>
<<for _genre range App.Porn.getAllGenres()>>
	<<set $pornStars[_genre.fameVar] = { p1count: 0, p3ID: 0 }>>
<</for>>

<<run assistant.object()>>
<<run FCTV.manage()>>
<<run repX(1000, "event")>>
<<run setup.prostheticIDs.forEach(function(id) {
	$prosthetics[id] = {amount: 0, research: 0};
})>>
<<set $JobIDMap = makeJobIdMap()>>

/*SFVAR*/ <<set $SF = Object.assign({}, $SF, {Toggle:0, Active: -1}), $SF.Facility = Object.assign({}, $SF.Facility, {Toggle:0, Active:0})>>

<<set $foodConsumption = (($lowerClass*$foodRate.lower) + ($middleClass*$foodRate.middle) + ($upperClass*$foodRate.upper) + ($topClass*$foodRate.top))>>	/* total amount food consumed per week */

<<set $weatherToday = App.Data.weather.nice.random()>>
<<set $prisonCircuitIndex = random(0,$prisonCircuit.length-1)>>

/* I am not a slave object! Do not treat me like one! */
<<set $customSlave = new App.Entity.CustomSlaveOrder()>>
<<set $huskSlave = new App.Entity.CustomSlaveOrder()>>

<<= App.SF.Init()>>

<<goto "Alpha disclaimer">>


code:
App.Data.Records = {};

App.Data.Records.LastWeeksCash = function() {
	// Slaves in general
	this.slaveMod = 0,
	this.slaveSurgery = 0,
	this.birth = 0,
	this.porn = 0,
	// Slave Income and Expenses per assignment
	this.slaveAssignmentRest = 0,
	this.slaveAssignmentRestVign = 0,
	this.slaveAssignmentFucktoy = 0,
	this.slaveAssignmentClasses = 0,
	this.slaveAssignmentHouse = 0,
	this.slaveAssignmentHouseVign = 0,
	this.slaveAssignmentWhore = 0,
	this.slaveAssignmentWhoreVign = 0,
	this.slaveAssignmentPublic = 0,
	this.slaveAssignmentPublicVign = 0,
	this.slaveAssignmentSubordinate = 0,
	this.slaveAssignmentMilked = 0,
	this.slaveAssignmentMilkedVign = 0,
	this.slaveAssignmentExtraMilk = 0,
	this.slaveAssignmentExtraMilkVign = 0,
	this.slaveAssignmentGloryhole = 0,
	this.slaveAssignmentConfinement = 0,
	this.slaveAssignmentBodyguard = 0,
	this.slaveAssignmentHeadgirl = 0,
	this.slaveAssignmentHeadgirlsuite = 0,
	this.slaveAssignmentRecruiter = 0,
	this.slaveAssignmentConcubine = 0,
	this.slaveAssignmentMastersuite = 0,
	this.slaveAssignmentAgent = 0,
	this.slaveAssignmentAgentPartner = 0,
	this.slaveAssignmentArcade = 0,
	this.slaveAssignmentMadam = 0,
	this.slaveAssignmentMadamVign = 0,
	this.slaveAssignmentBrothel = 0,
	this.slaveAssignmentBrothelVign = 0,
	this.slaveAssignmentWarden = 0,
	this.slaveAssignmentCellblock = 0,
	this.slaveAssignmentDj = 0,
	this.slaveAssignmentDjVign = 0,
	this.slaveAssignmentClub = 0,
	this.slaveAssignmentClubVign = 0,
	this.slaveAssignmentNurse = 0,
	this.slaveAssignmentClinic = 0,
	this.slaveAssignmentMilkmaid = 0,
	this.slaveAssignmentDairy = 0,
	this.slaveAssignmentDairyVign = 0,
	this.slaveAssignmentFarmer = 0,
	this.slaveAssignmentFarmyard = 0,
	this.slaveAssignmentFarmyardVign = 0,
	this.slaveAssignmentMatron = 0,
	this.slaveAssignmentNursery = 0,
	this.slaveAssignmentNurseryVign = 0,
	this.slaveAssignmentTeacher = 0,
	this.slaveAssignmentSchool = 0,
	this.slaveAssignmentSteward = 0,
	this.slaveAssignmentQuarter = 0,
	this.slaveAssignmentQuarterVign = 0,
	this.slaveAssignmentAttendant = 0,
	this.slaveAssignmentSpa = 0,
	this.slaveAssignmentBaby_factory = 0,
	this.slaveAssignmentChoice = 0,
	// Menial Slaves
	this.menialTrades = 0,
	this.fuckdolls = 0,
	this.menialBioreactors = 0,
	// Misc Slaves
	this.labScientists = 0,
	this.labMenials = 0,
	this.slaveTransfer = 0,
	this.menialTransfer = 0,
	this.fuckdollsTransfer = 0,
	this.menialBioreactorsTransfer = 0,
	this.menialTransferA = 0,
	this.fuckdollsTransferA = 0,
	this.menialBioreactorsTransferA = 0,
	this.labScientistsTransfer = 0,
	this.babyTransfer = 0,
	this.menialRetirement = 0,

	// Buildings
	this.masterSuite = 0,
	this.arcade = 0,
	this.brothel = 0,
	this.brothelAds = 0,
	this.cellblock = 0,
	this.club = 0,
	this.clubAds = 0,
	this.clinic = 0,
	this.dairy = 0,
	this.farmyard = 0,
	this.incubator = 0,
	this.incubatorSlaves = 0,
	this.nursery = 0,
	this.pit = 0,
	this.lab = 0,
	this.labResearch = 0,
	this.school = 0,
	this.servantsQuarters = 0,
	this.spa = 0,

	// Arcology
	this.environment = 0,
	this.weather = 0,

	this.mercenaries = 0,
	this.peacekeepers = 0,
	this.specialForces = 0,
	this.securityExpansion = 0,

	this.citizenOrphanage = 0,
	this.privateOrphanage = 0,

	this.capEx = 0,
	this.futureSocieties = 0,
	this.schoolBacking = 0,
	this.policies = 0,
	this.subsidiesAndBarriers = 0,
	this.edicts = 0,

	// Personal Finance
	this.personalBusiness = 0,
	this.personalLivingExpenses = 0,
	this.PCtraining = 0,
	this.PCmedical = 0,
	this.PCskills = 0,
	this.stocksTraded = 0, // trading
	this.stocks = 0, // share growth
	this.fines = 0,
	this.event = 0, // poker night etc. Try to file things elsewhere if you can.
	this.war = 0,

	this.rents = 0,

	this.cheating = 0,
	this.total = 0;
};

App.Data.Records.LastWeeksRep = function() {
	// Slave Jobs
	this.fucktoy = 0,
	this.publicServant = 0,
	this.gloryholeArcade = 0,
	this.gloryhole = 0,
	this.disobedience = 0,
	this.concubine = 0,
	this.headGirl = 0,
	this.bodyguard = 0,
	this.recruiter = 0,

	// Slaves in general
	this.fuckdolls = 0,
	this.slaveTransfer = 0,
	this.babyTransfer = 0,
	this.birth = 0,
	this.retirement = 0,
	this.slavesViewOfPC = 0,
	this.prestigiousSlave = 0,
	this.vignette = 0,
	this.porn = 0,

	// Buildings
	this.arcade = 0,
	this.brothel = 0,
	this.club = 0,
	this.publicServantClub = 0,
	this.clubAds = 0,
	this.pit = 0,
	this.servantsQuarters = 0,
	this.spa = 0,

	this.architecture = 0,

	// Policies
	this.capEx = 0,
	this.futureSocieties = 0,
	this.policies = 0,
	this.subsidiesAndBarriers = 0,
	this.edicts = 0,
	this.war = 0,
	this.food = 0,

	// Forces
	this.peacekeepers = 0,
	this.specialForces = 0,
	this.securityExpansion = 0,

	// Personal Finance
	this.personalBusiness = 0,
	this.PCappearance = 0,
	this.PCactions = 0, // actions involving your body = becoming pregnant, etc.
	this.PCRelationships = 0,
	this.SlaveRelationships = 0,
	this.event = 0, // poker night etc. Try to file things elsewhere if you can.
	this.war = 0,

	// special
	this.multiplier = 0,
	this.overflow = 0,
	this.cheating = 0,

	this.total = 0;
};


code:
/**
 * @returns {DocumentFragment}
 */
App.UI.View.mainLinks = function() {
	"use strict";
	const PA = Array.isArray(V.personalAttention) ? V.personalAttention.map(x => getSlave(x.ID)) : [];
	let fragment = document.createDocumentFragment();

	if (V.PC.health.shortDamage >= 30) {
		fragment.append(`The injuries received in the recent battle prevent you from undertaking tiring efforts.`);
	} else {
		switch (V.personalAttention) {
			case "business":
				fragment.append(`You plan to focus on business this week.`);
				break;
			case "whoring":
				fragment.append(`You plan to focus on earning extra money this week.`);
				break;
			case "upkeep":
				fragment.append(`You plan to focus on cleaning the penthouse this week.`);
				break;
			case "defensive survey":
				fragment.append(`You plan to survey ${V.arcologies[0].name}'s defenses in person this week.`);
				break;
			case "development project":
				fragment.append(`You plan on contributing to a local development project this week.`);
				break;
			case "smuggling":
				fragment.append(`You plan to make some easy (but dirty) money this week.`);
				break;
			case "HG":
				fragment.append(`You plan to support your Head Girl this week, `);
				if (V.HeadGirl) {
					const {he, his} = getPronouns(V.HeadGirl);
					fragment.append(`so ${he} can give more slaves ${his} attention.`);
				} else {
					fragment.append(`should you assign one.`);
				}
				break;
			case "sex":
				fragment.append(`You plan to have as much sex with your slaves as possible this week.`);
				break;
			case "trading":
				fragment.append(`This week you will learn trading.`);
				break;
			case "warfare":
				fragment.append(`This week you will learn modern combat tactics.`);
				break;
			case "slaving":
				fragment.append(`This week you will learn slaving.`);
				break;
			case "engineering":
				fragment.append(`This week you will learn engineering.`);
				break;
			case "medicine":
				fragment.append(`This week you will learn medicine.`);
				break;
			case "hacking":
				fragment.append(`This week you will learn hacking.`);
				break;
			case "proclamation":
				fragment.append(`This week you plan to issue a proclamation about ${V.SecExp.proclamation.type}.`);
				break;
			case "technical accidents":
				fragment.append(`This week you plan to sell your technical skills to the highest bidder.`);
				break;
			default:
				if (PA.length > 0) {
					fragment.append(`You plan to train `);

					const trainees = [];
					PA.forEach((trainee, i) => {
							trainees.push(App.UI.DOM.combineNodes(App.UI.DOM.makeElement("span", SlaveFullName(trainee), "slave-name"),
								` to ${V.personalAttention[i].trainingRegimen}`));
						}
					);
					fragment.append(App.UI.DOM.arrayToList(trainees));

					fragment.append(` this week.`);
				}
				break;
		}
	}

	if (V.PC.health.shortDamage < 30) {
		const link = App.UI.DOM.makeElement("span", App.UI.DOM.passageLink("Change plans", "Personal Attention Select"), "major-link");
		link.id = "managePA";
		fragment.append(" ", link, " ", App.UI.DOM.makeElement("span", "[A]", "hotkey"));
	}

	if (V.useSlaveSummaryOverviewTab === 0) {
		let div = document.createElement("div");
		if (typeof V.slaveIndices[V.HeadGirl.ID] !== 'undefined') {
			div.append(App.UI.DOM.makeElement("span", SlaveFullName(V.HeadGirl), "slave-name"), " is serving as your Head Girl");
			if (V.arcologies[0].FSEgyptianRevivalistLaw === 1) {
				div.append(` and Consort`);
			}
			div.append(". ",
				App.UI.DOM.makeElement("span", App.UI.DOM.passageLink("Manage Head Girl", "HG Select"), "major-link"),
				" ", App.UI.DOM.makeElement("span", "[H]", "hotkey"));
			div.id = "manageHG";
		} else if (V.slaves.length > 1) {
			div.append(`You have not selected a Head Girl`);
			if (V.arcologies[0].FSEgyptianRevivalistLaw === 1) {
				div.append(` and Consort`);
			}
			div.append(". ",
				App.UI.DOM.makeElement("span", App.UI.DOM.passageLink("Select One", "HG Select"), "major-link"),
				" ", App.UI.DOM.makeElement("span", "[H]", "hotkey"));
			div.id = "manageHG";
		} else {
			div.append(App.UI.DOM.makeElement("span", "You do not have enough slaves to keep a Head Girl", "note"));
		}
		fragment.append(div);

		div = document.createElement("div");
		if (typeof V.slaveIndices[V.Recruiter.ID] !== 'undefined') {
			div.append(App.UI.DOM.makeElement("span", SlaveFullName(V.Recruiter), "slave-name"), " is working to recruit girls. ",
				App.UI.DOM.makeElement("span", App.UI.DOM.passageLink("Manage Recruiter", "Recruiter Select"), "major-link"));
		} else {
			div.append("You have not selected a Recruiter. ",
				App.UI.DOM.makeElement("span", App.UI.DOM.passageLink("Select one", "Recruiter Select"), "major-link"));
		}
		div.append(" ", App.UI.DOM.makeElement("span", "[U]", "hotkey"));
		div.id = "manageRecruiter";
		fragment.append(div);

		if (V.dojo) {
			div = document.createElement("div");
			if (typeof V.slaveIndices[V.Bodyguard.ID] !== 'undefined') {
				div.append(App.UI.DOM.makeElement("span", SlaveFullName(V.Bodyguard), "slave-name"), " is serving as your bodyguard. ",
					App.UI.DOM.makeElement("span", App.UI.DOM.passageLink("Manage Bodyguard", "BG Select"), "major-link"));
			} else {
				div.append("You have not selected a Bodyguard. ",
					App.UI.DOM.makeElement("span", App.UI.DOM.passageLink("Select one", "BG Select"), "major-link"));
			}
			div.append(" ", App.UI.DOM.makeElement("span", "[B]", "hotkey"));
			div.id = "manageBG";
			fragment.append(div);
		}
	}

	if (V.completedOrgans.length > 0) {
		/* first remove any organs with no corresponding slave */
		V.completedOrgans = V.completedOrgans.filter(organ => {
			return (typeof organ === 'object' && getSlave(organ.ID) !== undefined);
		});
		/* cycle through slaves, for each slave cycle through completed organs and track how many are of the interrogated slave (and if organs have a slaves to be implanted on) */
		for (let i = 0; i < V.slaves.length; i++) {
			const slaveOrgans = V.completedOrgans.reduce((acc, organ) => organ.ID === V.slaves[i].ID ? acc + 1 : acc, 0);
			/* if the interrogated slave has one or more organs ready: */
			if (slaveOrgans > 0) {
				const div = document.createElement("div");
				div.classList.add("yellow");
				div.append("The fabricator has completed ");
				if (slaveOrgans > 1) {
					div.append(`${slaveOrgans} organs`);
				} else {
					div.append('an organ');
				}
				div.append(" for ",
					App.UI.DOM.makeElement("span", App.UI.DOM.link(V.slaves[i].slaveName, () => { V.activeSlave = V.slaves[i], V.tabChoice.RemoteSurgery = "Structural"; }, [], "Remote Surgery"), "clear-formatting"),
					" which ");
				if (slaveOrgans > 1) {
					div.append('are');
				} else {
					div.append('is');
				}
				div.append(' ready to be implanted.');
				fragment.append(div);
			}
		}
	}

	if (V.adjustProstheticsCompleted > 0) {
		for (let j = 0; j < V.adjustProsthetics.length; j++) {
			if (getSlave(V.adjustProsthetics[j].slaveID) !== undefined) {
				const i = V.slaveIndices[V.adjustProsthetics[j].slaveID];
				if (V.adjustProsthetics[j].workLeft <= 0) {
					const div = document.createElement("div");
					div.classList.add("yellow");
					div.append(`The lab has completed ${addA(setup.prosthetics[V.adjustProsthetics[j].id].name)} for `,
						App.UI.DOM.makeElement("span", App.UI.DOM.link(SlaveFullName(V.slaves[i]), () => { V.activeSlave = V.slaves[i]; }, [], "Slave Interact"), "clear-formatting"),
						" which is ready to be attached.");
					fragment.append(div);
				}
			} else {
				V.adjustProsthetics.splice(j, 1);
				j--;
			}
		}
	}

	if (V.completedOrgans.length > 0 && V.adjustProstheticsCompleted > 0) {
		const div = document.createElement("div");
		div.append(App.UI.DOM.passageLink("Implant and Attach", "Multiple Organ Implant"),
			App.UI.DOM.makeElement("span", " all organs and prosthetics that are ready.", "yellow"));
		fragment.append(div);
	} else if (V.completedOrgans.length > 1) {
		const div = document.createElement("div");
		div.append(App.UI.DOM.passageLink("Implant", "Multiple Organ Implant"),
			App.UI.DOM.makeElement("span", " all organs that are ready for implantation.", "yellow"));
		fragment.append(div);
	} else if (V.adjustProstheticsCompleted > 1) {
		const div = document.createElement("div");
		div.append(App.UI.DOM.passageLink("Attach", "Multiple Organ Implant"),
			App.UI.DOM.makeElement("span", " all prosthetics that are ready to be attached.", "yellow"));
		fragment.append(div);
	}

	const div = document.createElement("div");
	if (V.slaveCostFactor === 1) {
		div.append("The slave market is stable; the price of slaves is average.");
	} else {
		let r;
		if (V.slaveCostFactor > 1) {
			if (V.slaveCostFactor > 1.05) {
				r = "There is a bull market for slaves; the price of slaves is very high.";
			} else {
				r = "The slave market is bullish; the price of slaves is high.";
			}
		} else {
			if (V.slaveCostFactor < 0.95) {
				r = "There is a bear market for slaves; the price of slaves is very low.";
			} else {
				r = "The slave market is bearish; the price of slaves is low.";
			}
		}
		div.append(App.UI.DOM.makeElement("span", r, "yellow"));
	}

	const buySlaves = App.UI.DOM.makeElement("span", App.UI.DOM.passageLink("Buy Slaves", "Buy Slaves"), "major-link");
	buySlaves.id = "buySlaves";
	div.append(" ", buySlaves, " ", App.UI.DOM.makeElement("span", "[S]", "hotkey"));

	fragment.append(div);

	/**
	 * @param {string} school
	 */
	function schoolSale(school) {
		const div = document.createElement("div");
		div.append(App.UI.DOM.makeElement("span", "For your first purchase, ", "yellow"),
			App.UI.DOM.passageLink(school, school, () => { V.slavesSeen += 1; }),
			App.UI.DOM.makeElement("span", " will sell at half price this week.", "yellow"));
		fragment.append(div);
	}

	if (V.seeDicks !== 100) {
		if (V.TSS.schoolSale !== 0) {
			schoolSale("The Slavegirl School");
		}
		if (V.GRI.schoolSale !== 0) {
			schoolSale("Growth Research Institute");
		}
		if (V.SCP.schoolSale !== 0) {
			schoolSale("St. Claver Preparatory");
		}
		if (V.TCR.schoolSale !== 0) {
			schoolSale("The Cattle Ranch");
		}
		if (V.HA.schoolSale !== 0) {
			schoolSale("The Hippolyta Academy");
		}
	}
	if (V.seeDicks !== 0) {
		if (V.LDE.schoolSale !== 0) {
			schoolSale("L'École des Enculées");
		}
		if (V.TGA.schoolSale !== 0) {
			schoolSale("The Gymnasium-Academy");
		}
		if (V.TFS.schoolSale !== 0) {
			schoolSale("The Futanari Sisters");
		}
	}
	if (V.NUL.schoolSale !== 0) {
		schoolSale("Nueva Universidad de Libertad");
	}
	return fragment;
};


code:
/* OPEN TOYCHEST */
App.Interact.ToyChest = function(slave) {
	"use strict";
	let r = ``;
	/* eslint-disable no-unused-vars*/
	const {
		he, him, his, hers, himself, boy, He, His
	} = getPronouns(slave);
	/* eslint-enable */

	r = `${slave.slaveName} `;
	if (slave.fuckdoll) {
		r += `is waiting for use nearby. `;
	} else if (slave.fetish === "mindbroken") {
		if (canSmell(slave) && slave.career === "a breeding bull" && isFertile(V.PC) && V.PC.preg === 0 && canPenetrate(slave)) {
			r += `is rock hard and sniffing the air. `;
		} else {
			r += `is waiting dumbly nearby. `;
		}
	} else if (slave.devotion > 50) {
		if (slave.toyHole === "mouth") {
			r += `has positioned ${himself} nearby with ${his} mouth conveniently `;
			if (V.PC.dick) {
				r += `at cock level. `;
			} else {
				r += `level with your pussy. `;
			}
		} else if (slave.toyHole === "boobs") {
			r += `is kneeling nearby with ${his} chest thrust out as far as it will go. `;
		} else if (slave.toyHole === "pussy" && hasBothLegs(slave)) {
			r += `is kneeling on the couch with legs apart to present ${his} pussy. `;
		} else if (slave.toyHole === "pussy") {
			r += `is lying on the couch with ${his} pussy ready for you. `;
		} else if (slave.toyHole === "ass" && hasBothLegs(slave)) {
			r += `is lying on the couch with ${his} legs up and back to present ${his} butthole. `;
		} else if (slave.toyHole === "ass") {
			r += `is lying on the couch with ${his} butt ready for you. `;
		} else if (slave.toyHole === "dick") {
			r += `is lying on the couch with ${his} dick at the ready for you. `;
		} else {
			r += `is nearby, eagerly presenting ${himself}. `;
		}
	} else if (slave.trust < -20) {
		r += `is waiting nearby in terror, hoping obedience will save ${him} from punishment. `;
	} else if (slave.devotion < -90) {
		r += `is hogtied nearby for sexual abuse. `;
	} else if (slave.devotion < -50) {
		r += `is restrained nearby for sexual use. `;
	} else if (slave.devotion < -20) {
		r += `is unhappily waiting nearby for sexual use. `;
	} else if (slave.devotion <= 20) {
		r += `is obediently waiting nearby for sexual use. `;
	} else if (slave.devotion <= 50) {
		r += `has positioned ${himself} nearby to offer you ${his} body. `;
	}
	let pose = jsRandom(1, 100);
	if (slave.fuckdoll) {
		r += `${His} holes are, as always, available. `;
	} else if (slave.fetish === "mindbroken") {
		r += `${He} ignores ${his} clothing, regarding it as an impediment to ${his} purpose. `;
	} else {
		switch (slave.clothes) {
			case "shibari ropes":
				r += `You've tied ${his} shibari bindings so that ${he}'s forced to thrust out ${his} chest. `;
				break;
			case "uncomfortable straps":
				if (pose > 75) {
					r += `${His} leather straps are forcing ${him} to thrust out ${his} tits. `;
				} else if (pose > 50) {
					r += `${His} leather straps are uncomfortably pressing ${his} breasts and asscrack. `;
				} else if (pose > 25) {
					r += `The rings in ${his} leather straps over each nipple and hole make ${him} look like a perfect fucktoy. `;
				} else {
					r += `${His} uncomfortable straps force ${him} to constantly present ${his} holes. `;
				}
				break;
			case "restrictive latex":
			case "a latex catsuit":
				r += `${His} complete suit of latex makes ${him} a nice, artistic display, a plastic work of art in the shape of a female form.`;
				break;
			case "a military uniform":
				r += `${His} uniformed presence lends your office the air of a military command center.`;
				break;
			case "a schutzstaffel uniform":
				r += `${His} uniformed presence lends your office the air of a military command center.`;
				break;
			case "a slutty schutzstaffel uniform":
				r += `${His} uniformed presence lends your office the air of a military command center.`;
				break;
			case "a red army uniform":
				r += `${His} uniformed presence lends your office the air of a military command center.`;
				break;
			case "a long qipao":
				r += `${His} elegant qipao lends your office an air of traditional opulence and prestige.`;
				break;
			case "battlearmor":
				r += `${His} armored presence lends your office the air of a battlefield command center.`;
				break;
			case "a mounty outfit":
				r += `${His} uniformed presence lends your office the air of a police command center.`;
				break;
			case "a dirndl":
				r += `${His} dirndl lends a traditional atmosphere to your office.`;
				break;
			case "lederhosen":
				r += `${His} lederhosen lends a traditional atmosphere to your office.`;
				break;
			case "a biyelgee costume":
				r += `${His} biyelgee costume lends a festive atmosphere to your office.`;
				break;
			case "a mini skirt":
				r += `${His} flattering mini dress makes ${him} the perfect office ornament for the modern captain of industry.`;
				break;
			case "a nice nurse outfit":
				r += `${His} proper nurse's outfit gives the office a clinical air.`;
				break;
			case "a fallen nuns habit":
				r += `${His} latex parody of a nuns habit gives the office a sacrilegious air.`;
				break;
			case "a chattel habit":
				r += `${His} chattel habit makes the office look like what it is: the inner sanctum of a new and deeply sensual faith.`;
				break;
			case "a penitent nuns habit":
				r += `${His} sackcloth habit gives the office a somber air.`;
				break;
			case "attractive lingerie":
				r += `${He}'s wearing classy lingerie, making ${him} an unusually refined ornament to the office.`;
				break;
			case "kitty lingerie":
				r += `${His} cat-themed lingerie gives the office a bizarrely innocent air of perversion.`;
				break;
			case "a succubus outfit":
				r += `${He}'s dressed to look like a succubus. There is a demon`;
				if (V.PC.title === 0) {
					r += `ess`;
				}
				r += ` in this office, and ${he} serves `;
				if (V.PC.title === 0) {
					r += `her.`;
				} else {
					r += `him.`;
				}
				break;
			case "spats and a tank top":
				r += `${He}'s wearing spats and a tank top, giving the office the active air of a gym.`;
				break;
			case "a string bikini":
				r += `${He}'s wearing a string bikini, making ${him} a sexy, enticing office ornament.`;
				break;
			case "a scalemail bikini":
				r += `${His} scalemail bikini gives the office a medieval air.`;
				break;
			case "striped panties":
			case "a striped bra":
			case "striped underwear":
				r += `${He}'s wearing cute underwear, making ${him} a sexy, innocent-looking office ornament.`;
				break;
			case "attractive lingerie for a pregnant woman":
				r += `${His} breasts gently spill out of ${his} slightly too small top.`;
				if (slave.lactation) {
					r += `${His} leaking nipples have rendered ${his} top see-through.`;
				}
				break;
			case "a maternity dress":
				r += `${His} low cut dress reveals ample cleavage.`;
				break;
			case "stretch pants and a crop-top":
				r += `${His} comfortable clothes give your office a laid back air and are easy to slip off when the mood strikes.`;
				break;
			case "a cheerleader outfit":
				r += `${He}'s wearing a slutty cheerleader outfit, making it look like ${he}'s here to fuck before team practice.`;
				break;
			case "clubslut netting":
				r += `${He}'s wearing slutty netting and headphones so ${he} can dance to music without annoying visitors.`;
				break;
			case "cutoffs and a t-shirt":
				r += `${He}'s wearing cutoffs and a t-shirt, making ${him} a fun, clean office ornament.`;
				break;
			case "a slutty nurse outfit":
				r += `${He}'s wearing a slutty nurse outfit, and looks more than ready to minister to any patient's needs.`;
				break;
			case "a schoolgirl outfit":
				r += `${He}'s wearing a slutty schoolgirl outfit, and looks ready to do whatever it takes to improve ${his} grades.`;
				break;
			case "a kimono":
				r += `${He}'s wearing a kimono, lending your office an air of elegance, though ${he} lacks some of the air of the true yamato nadeshiko.`;
				break;
			case "a burkini":
				r += `${He}'s wearing a colorful burkini, lending your office a vague air of conservatism.`;
				break;
			case "a hijab and blouse":
				r += `${He}'s wearing a modest hijab and blouse, lending your office a certain air of conservatism.`;
				break;
			case "a hijab and abaya":
				r += `${He}'s wearing a modest hijab and abaya, lending your office a certain air of conservatism.`;
				break;
			case "a niqab and abaya":
				r += `${He}'s wearing a niqab and abaya, lending your office an air of conservatism.`;
				break;
			case "a klan robe":
			case "a slutty klan robe":
				r += `${He}'s wearing a ku klux klan robe, lending your office an air of conservatism.`;
				break;
			case "a burqa":
				r += `${He}'s wearing an all-concealing burqa, lending your office an intense air of conservatism.`;
				break;
			case "battledress":
				r += `${He}'s wearing skimpy battledress, making your office seem a little like the ideal bunker for a survivalist.`;
				break;
			case "a slutty outfit":
				if (slave.actualAge < 21) {
					r += `${He}'s wearing a schoolgirl uniform and sucking on hard candy, giving ${him} a delectably youthful appearance.`;
				} else if (slave.actualAge < 30) {
					r += `${He}'s chosen to wear a slutty nurse outfit, and looks more than ready to minister to any patient's needs.`;
				} else if (slave.actualAge < 40) {
					r += `${He}'s wearing a schoolgirl uniform and sucking on hard candy, a delightfully perverse outfit for a slave in ${his} thirties.`;
				} else {
					r += `${He}'s wearing a wifely apron that covers ${his} front, but leaves ${his} backside bare, an appropriate outfit for a slave in ${his} forties.`;
				}
				break;
			case "a halter top dress":
				r += `The gorgeous halter top dress ${he}'s wearing is almost a work of art.`;
				break;
			case "a ball gown":
				r += `The fabulous silken ball gown ${he}'s wearing lifts the entire atmosphere.`;
				break;
			case "a slave gown":
				r += `The gorgeous gown ${he}'s wearing lends an air of class to the office.`;
				break;
			case "slutty business attire":
				r += `The suit ${he}'s wearing would make it look like ${he}'s here to do business, if not for the extreme shortness of the skirt and ${his} acre of cleavage.`;
				break;
			case "nice business attire":
				r += `The suit ${he}'s wearing makes it look like ${he}'s here to do business, not fuck.`;
				break;
			case "a comfortable bodysuit":
				r += `The bodysuit ${he}'s wearing displays ${his} every fuckable curve.`;
				break;
			case "a leotard":
				r += `The leotard ${he}'s wearing is tight enough to advertise every detail.`;
				break;
			case "a monokini":
				r += `${His} topless swimsuit gives the office a perverted yet cultured aura.`;
				break;
			case "an apron":
				r += `${His} apron makes the office an intimate air, especially since ${he}'s nude underneath it.`;
				break;
			case "a cybersuit":
				r += `${His} cybersuit lends to an impersonal but sexual atmosphere, as ${his} delicious curves are prominently displayed.`;
				break;
			case "a bunny outfit":
				r += `The bunny outfit ${he}'s wearing makes ${him} look ready to serve drinks and suck dick.`;
				break;
			case "a slutty maid outfit":
				r += `${His} maid outfit makes ${him} look useful and sexually easy.`;
				break;
			case "a nice maid outfit":
				r += `${His} maid outfit makes ${his} servitude obvious while not looking too lewd.`;
				break;
			case "harem gauze":
				r += `${His} harem girl outfit lends ${his} corner of your office an Eastern opulence.`;
				break;
			case "slutty jewelry":
				r += `The bangles ${he}'s wearing make little noises every time ${he} moves, serving as a constant reminder of ${his} sexual availability.`;
				break;
			case "conservative clothing":
				r += `${His} clothes make it look like ${he}'s here for some other purpose than sexual slavery.`;
				break;
			case "chains":
				r += `${His} chains make it obvious that ${he}'s here as an office sex toy.`;
				break;
			case "Western clothing":
				r += `${His} Western clothing is comically out of place in a modern office.`;
				break;
			case "overalls":
				r += `${His} worker's overalls make it clear that you're the boss and ${he} is your subordinate.`;
				break;
			case "body oil":
				r += `${His} body oil makes ${his} muscles a lovely ornament to the office, and makes all ${his} holes nice and inviting.`;
				break;
			case "a toga":
			case "a hanbok":
				r += `${His} toga lends an air of antiquity to the office.`;
				break;
			case "a huipil":
				r += `${His} revealing huipil is a delightfully exotic novelty in your office.`;
				break;
			case "a slutty qipao":
				r += `${His} lovely qipao is a delicious contradiction of conservative silk patterns and scandalously high cuts.`;
				break;
			case "a bimbo outfit":
				r += `The way ${his} underwear is revealed by ${his} clothing just exudes sluttiness.`;
				break;
			case "a courtesan dress":
				r += `The layered dress ${he}'s wearing, and the posture forced by its corset, gives your office an air of elegance.`;
				break;
			case "a bra":
			case "a button-up shirt":
			case "a button-up shirt and panties":
			case "a sweater":
			case "a sweater and cutoffs":
			case "a sweater and panties":
			case "a t-shirt":
			case "a t-shirt and jeans":
			case "a t-shirt and panties":
			case "a t-shirt and thong":
			case "a tank-top":
			case "a tank-top and panties":
			case "a thong":
			case "a tube top":
			case "a tube top and thong":
			case "an oversized t-shirt":
			case "an oversized t-shirt and boyshorts":
			case "boyshorts":
			case "cutoffs":
			case "jeans":
			case "leather pants":
			case "leather pants and a tube top":
			case "leather pants and pasties":
			case "panties":
			case "panties and pasties":
			case "pasties":
				r += `${His} purpose as your office sex toy is prominently displayed by ${his} revealing outfit.`;
				break;
			case "a gothic lolita dress":
				r += `${His} gothic lolita dress lends a cute but dark old world charm to your office.`;
				break;
			case "a police uniform":
				r += `${His} police uniform lends an air of security and authority to your office.`;
				break;
			case "a one-piece swimsuit":
				r += `${His} one-piece swimsuit lends your office a more casual atmosphere.`;
				break;
			case "a nice pony outfit":
			case "a slutty pony outfit":
				r += `${His} bondage pony outfit reinforces the atmosphere of submission to your will.`;
				break;
			case "a sports bra":
			case "sport shorts and a t-shirt":
			case "sport shorts and a sports bra":
			case "sport shorts":
				r += `${His} athletic clothing gives the office a more youthful vibe.`;
				break;
			case "a skimpy loincloth":
				r += `${His} skimpy loincloth gives the office a more barbaric and raw atmosphere.`;
				break;
			case "a Santa dress":
				r += `${His} skimpy dress somehow makes you seem more charitable and jovial than you really are.`;
				break;
			default:
				if (slave.chastityVagina && slave.chastityAnus) {
					r += `${His} chastity belt covers and protects both ${his} vagina and anus, but leaves ${his} breasts naked and ${his} mouth available.`;
				} else if (slave.chastityVagina) {
					r += `${His} chastity belt covers and protects ${his} pussy, but leaves ${his} breasts naked, ${his} mouth available, and ${his} asshole vulnerable.`;
				} else if (slave.chastityAnus) {
					r += `${His} chastity belt covers and protects ${his} anus, but leaves ${his} breasts naked, ${his} mouth available, and ${his} pussy vulnerable.`;
				} else {
					r += `${He}'s nude and ready to be fucked.`;
				}
				break;
		}
	}
	return r;
};


code:
/**
 * @param slave
 * @return {string}
 */
App.Interact.guardPose = function(slave) {
	"use strict";
	let r = ``;
	/* eslint-disable no-unused-vars*/
	const {
		he, him, his, hers, himself, boy,
		He, His
	} = getPronouns(slave);
	let man = (boy === "girl" ? "woman" : "man");
	/* eslint-enable */

	r = `${slave.slaveName} is standing behind your left shoulder, guarding your person. `;
	switch (slave.clothes) {
		case "attractive lingerie":
			r += `${His} lacy lingerie contrasts with ${his} deadly weaponry.`;
			break;
		case "a succubus outfit":
			r += `${His} succubus outfit and impressive weapons make ${him} look like a character from an overwrought graphic novel.`;
			break;
		case "uncomfortable straps":
			r += `${His} uncomfortable straps make ${him} look like a classical slave warrior.`;
			break;
		case "shibari ropes":
			r += `${His} shibari ropes make ${him} look like an insane slave fighter.`;
			break;
		case "restrictive latex":
		case "a latex catsuit":
			r += `${His} complete suit of latex gives ${him} a beautiful yet intimidating appearance.`;
			break;
		case "a nice nurse outfit":
			r += `${His} practical nurse's outfit make ${his} weapons look like surgical instruments of death.`;
			break;
		case "a military uniform":
			r += `${His} military uniform is most befitting of an honor guard.`;
			break;
		case "a schutzstaffel uniform":
			r += `${His} military uniform is most befitting of an honor guard.`;
			break;
		case "a slutty schutzstaffel uniform":
			r += `${His} military uniform is most befitting of an honor guard and your personal whore.`;
			break;
		case "a red army uniform":
			r += `${His} military uniform is most befitting of an honor guard.`;
			break;
		case "a long qipao":
			r += `${His} elegant qipao makes ${him} seem less deadly than ${he} actually is.`;
			break;
		case "battlearmor":
			r += `${His} battlearmor means ${he}'s always ready for combat.`;
			break;
		case "a mounty outfit":
			r += `${His} mounty outfit only enhances ${his} authority as your personal guard.`;
			break;
		case "a police uniform":
			r += `${His} police uniform only enhances ${his} authority as your personal guard.`;
			break;
		case "a gothic lolita dress":
			r += `${His} elegant dress makes ${him} seem less deadly than ${he} actually is.`;
			break;
		case "a dirndl":
			r += `${His} dirndl allows ${him} to hide all sorts of useful weapons against ${his} bare thighs.`;
			break;
		case "lederhosen":
			r += `${His} weapon hangs from a shoulder sling over ${his} lederhosen.`;
			break;
		case "a biyelgee costume":
			r += `${His} biyelgee costume makes ${him} appear as an athletic specimen, ready to defend you in hand-to-hand combat.`;
			break;
		case "a mini dress":
			r += `${His} revealing mini dress and elegant weapons make ${him} look sexy, yet deadly.`;
			break;
		case "a monokini":
			r += `${His} monokini's unrepressed appearance clashes amusingly with ${his} deadly weapons.`;
			break;
		case "an apron":
			r += `${He}'s nude, aside from an apron and the holsters for ${his} numerous weapons.`;
			break;
		case "overalls":
			r += `${His} overalls and armaments make ${him} look like ${he}'s ready to shoot some varmints.`;
			break;
		case "a cybersuit":
			r += `${His} cybersuit makes ${him} look sleek, sexy, and deadly. A perfect femme fatale.`;
			break;
		case "clubslut netting":
			r += `${His} club netting's slutty appearance clashes amusingly with ${his} deadly weapons.`;
			break;
		case "a string bikini":
			r += `${His} string bikini's flirty appearance clashes amusingly with ${his} deadly weapons.`;
			break;
		case "a scalemail bikini":
			r += `${His} scalemail bikini's barbaric appearance meshes well with ${his} sword, but clashes with ${his} firearms.`;
			break;
		case "striped panties":
			r += `${He}'s nude, aside from some cute panties and the holsters for ${his} numerous weapons.`;
			break;
		case "a slutty outfit":
			if (slave.actualAge < 21) {
				r += `${He}'s wearing a schoolgirl uniform and sucking on hard candy, making ${him} look like a character from a kung-fu film.`;
			} else if (slave.actualAge < 30) {
				r += `${He}'s chosen to wear a catsuit, and looks like a fictional heroine.`;
			} else if (slave.actualAge < 40) {
				// TODO: should this one be the same as the one above?
				r += `${He}'s wearing a schoolgirl uniform and sucking on hard candy, making ${him} look like a character from a kung-fu film.`;
			} else {
				r += `${He}'s wearing a wifely dress, creating a discordant effect with ${his} weapons.`;
			}
			break;
		case "a cheerleader outfit":
			r += `${He}'s wearing a cheerleader uniform, creating an amusing effect with ${his} weapons.`;
			break;
		case "attractive lingerie for a pregnant woman":
			r += `${His} silky lingerie contrasts with ${his} deadly weaponry.`;
			break;
		case "kitty lingerie":
			r += `${His} cutesy lingerie contrasts with ${his} deadly weaponry.`;
			break;
		case "a maternity dress":
			r += `${His} loose dress gives ${him} plenty of places to conceal weaponry.`;
			break;
		case "stretch pants and a crop-top":
			r += `${His} relaxed outfit clashes amusingly with ${his} serious weapons.`;
			break;
		case "a slave gown":
			r += `${His} gorgeous gown and elegant weapons make ${him} look refined, yet deadly.`;
			break;
		case "a halter top dress":
			r += `${His} beautiful halter top dress and elegant weapons make ${him} look refined, yet deadly.`;
			break;
		case "a ball gown":
			r += `${His} fabulous silken ball gown and elegant weapons make ${him} look refined, yet deadly.`;
			break;
		case "a nice maid outfit":
			r += `${His} pretty maid outfit and weapons make ${him} look like a character from an animated movie.`;
			break;
		case "a nice pony outfit":
		case "a slutty pony outfit":
			r += `${His} leather outfit make ${him} look like an insane slave fighter.`;
			break;
		case "a hanbok":
			r += `${His} pretty hanbok and weapons make ${him} look like a character from a history novel.`;
			break;
		case "spats and a tank top":
			r += `${His} exercise outfit makes ${him} look like ${he} has the fitness to back up ${his} weapons.`;
			break;
		case "a slutty maid outfit":
			r += `${His} slutty maid outfit and weapons make ${him} look like a character from an exploitation film.`;
			break;
		case "cutoffs and a t-shirt":
			r += `${His} relaxed clothing clashes with ${his} weaponry, making ${him} look like ${he}'s planning to kill someone and then have a relaxing day off.`;
			break;
		case "harem gauze":
			r += `${His} weapons pin ${his} sheer gauze down over ${his} body, putting everything on lewd display.`;
			break;
		case "a fallen nuns habit":
			r += `${His} sacrilegious getup and weapons make ${him} look like a sinner's fantasy.`;
			break;
		case "a chattel habit":
			r += `${His} chattel habit and weapons make ${his} role as a defender of the new faith instantly obvious.`;
			break;
		case "a penitent nuns habit":
			r += `${His} somber habit and weapons make ${him} look like a member of a militant order.`;
			break;
		case "slutty business attire":
			r += `${His} slutty business suit and weapons give ${him} the look a supervillain's sidekick.`;
			break;
		case "nice business attire":
			r += `${His} business suit and weapons give ${him} the look of a finely honed corporate instrument.`;
			break;
		case "conservative clothing":
			r += `${His} casual, conservative clothes and weapons make ${him} look like a normal old world security contractrix.`;
			break;
		case "a hijab and blouse":
			r += `${His} modest, conservative clothes and weapons make ${him} look like an old world security contractrix.`;
			break;
		case "a comfortable bodysuit":
			r += `${His} bodysuit and weapons make ${him} look like an action movie heroine.`;
			break;
		case "a burkini":
			r += `${His} form-fitting swimsuit and weapons make ${him} look like an action movie heroine.`;
			break;
		case "a Santa dress":
			r += `${His} skimpy holiday dress seems to be for those on the nice list, but ${his} weaponry is for those on the naughty list.`;
			break;
		case "a leotard":
			r += `${His} leotard and weapons make ${him} look like the heroine of an old exploitation film.`;
			break;
		case "a bunny outfit":
			r += `${His} bunny outfit and weapons make ${him} look like the hench${man} of a villain from a cheesy vintage spy film.`;
			break;
		case "a slutty nurse outfit":
			r += `Between ${his} nurse outfit and ${his} weapons ${he} looks prepared to do harm, and then to heal it.`;
			break;
		case "a schoolgirl outfit":
			r += `This school${boy} is well armed, bringing to mind more than one farcical action film.`;
			break;
		case "a kimono":
			r += `${His} kimono has discreet cuts that allow ${him} to fight reasonably well in it if necessary, and are almost invisible when ${he} stands straight.`;
			break;
		case "a slutty qipao":
			r += `${His} qipao and weapons make ${him} look like a character from a fighting game.`;
			break;
		case "a toga":
			r += `${His} toga and weapons make a stark contrast, making ${him} stand out a lot.`;
			break;
		case "a huipil":
			r += `${His} huipil hides most of the weapons ${he} carries, but carrying them on naked skin is a slight discomfort.`;
			break;
		case "a hijab and abaya":
		case "a niqab and abaya":
			r += `${His} weapons are strapped to the outside of ${his} abaya, a juxtaposition that would be more ludicrous had not many women in the Middle East recently been seen fighting dressed in just this way.`;
			break;
		case "a burqa":
			r += `${His} weapons are strapped to the outside of ${his} burqa, a juxtaposition that would be more ludicrous had not some women in the Middle East recently been seen fighting dressed in just this way.`;
			break;
		case "a klan robe":
		case "a slutty klan robe":
			r += `${His} weapons are strapped to the outside of ${his} robe, all the essentials are present, mainly rope.`;
			break;
		case "battledress":
			r += `Though ${his} battledress top is just a tank top, ${he} still skirts the edge of looking more like a Free Cities mercenary than a Free Cities sex slave.`;
			break;
		case "slutty jewelry":
			r += `The bangles ${he}'s wearing are complemented by the straps that mount ${his} weapons to ${his} otherwise naked body.`;
			break;
		case "a courtesan dress":
			r += `${He} may look unarmed, but ${his} layered skirt and hanging sleeves perfectly conceal ${his} arsenal.`;
			break;
		case "a bimbo outfit":
			r += `${His} lacy lingerie and barely there clothing contrasts with ${his} deadly weaponry.`;
			break;
		case "a tube top and thong":
		case "a button-up shirt and panties":
		case "a bra":
		case "a button-up shirt":
		case "a sweater":
		case "a tank-top":
		case "a thong":
		case "a tube top":
		case "a striped bra":
		case "a skimpy loincloth":
		case "a sports bra":
			r += `Due to the skimpiness of ${his} outfit, ${he} appears to be wearing more weapons than clothing.`;
			break;
		case "a sweater and panties":
		case "a sweater and cutoffs":
		case "a t-shirt and jeans":
		case "a t-shirt and panties":
		case "a t-shirt and thong":
		case "a t-shirt":
		case "a tank-top and panties":
		case "an oversized t-shirt and boyshorts":
		case "an oversized t-shirt":
		case "boyshorts":
		case "cutoffs":
		case "jeans":
		case "leather pants and a tube top":
		case "leather pants and pasties":
		case "leather pants":
		case "panties and pasties":
		case "panties":
		case "pasties":
		case "sport shorts and a sports bra":
		case "sport shorts and a t-shirt":
		case "sport shorts":
		case "striped underwear":
			r += `${His} weapons only accentuate the nakedness of ${his} body.`;
			break;
		default:
			if (slave.chastityVagina || slave.chastityAnus) {
				r += `${His} chastity belt covers and protects ${him}, just as ${his} weapons cover and protect you.`;
			} else if (slave.chastityPenis === 1) {
				r += `${His} chastity cage covers and protects ${him}, just as ${his} weapons cover and protect you.`;
			} else {
				r += `${He} is nude except for ${his} armament.`;
			}
			break;
	}
	return r;
};