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.Tabs = (function() {
	/**
	 * @type {Array<HTMLButtonElement|HTMLAnchorElement>}
	 */
	let active = null;

	/**
	 * TabBar class responsible for collecting the tabs and rendering to one DOM Node.
	 *
	 * Multiple TabBars on one page will work, but hotkeys may behave unexpectedly. (It's also considered bad ui design)
	 */
	class TabBar {
		/**
		 * @typedef tab
		 * @property {string} name
		 * @property {string} id
		 * @property {Node} content
		 * @property {string} [buttonClass]
		 * @property {string} [contentClass]
		 */

		/**
		 * @param {string} id Saved, should stay the same across versions. No spaces allowed
		 */
		constructor(id) {
			/**
			 * @type {string}
			 * @private
			 */
			this._id = id;
			/**
			 * @type {Array<tab>}
			 * @private
			 */
			this._tabs = [];
			/**
			 * @type {Node}
			 * @private
			 */
			this._customNode = null;
		}

		/**
		 * @param {string} name Display name
		 * @param {string} id Saved, should stay the same across versions
		 * @param {Node} content
		 * @param {string} [buttonClass] CSS class applied to the button
		 * @param {string} [contentClass] CSS class applied to the content holder
		 */
		addTab(name, id, content, buttonClass, contentClass) {
			this._tabs.push({
				name: name,
				id: id,
				content: content,
				buttonClass: buttonClass,
				contentClass: contentClass,
			});
		}

		/**
		 * Add a DOM element between the button row and the content. Mostly intended for art.
		 *
		 * @param {Node} node
		 */
		set customNode(node) {
			this._customNode = node;
		}

		/**
		 * @param {boolean} plainLinks
		 * @returns {DocumentFragment}
		 */
		render(plainLinks = false) {
			const f = new DocumentFragment();
			const tabBar = document.createElement("div");
			tabBar.classList.add("tab-bar");
			f.append(tabBar);

			if (this._customNode) {
				f.append(this._customNode);
			}

			/**
			 * @type {Array<HTMLButtonElement|HTMLAnchorElement>}
			 */
			const buttonList = [];
			/**
			 * @type {Array<HTMLDivElement>}
			 */
			const contentList = [];

			let any = false;
			for (const tab of this._tabs) {
				// check if selected.
				const selected = V.tabChoice.hasOwnProperty(this._id) && V.tabChoice[this._id] === tab.id;

				/** @type {HTMLButtonElement|HTMLAnchorElement} */
				let button;
				if (!plainLinks) {
					button = App.UI.DOM.appendNewElement("button", tabBar, tab.name, tab.buttonClass);
				} else {
					button = App.UI.DOM.makeElement("a", tab.name, tab.buttonClass);
				}
				buttonList.push(button);

				const contentHolder = App.UI.DOM.appendNewElement("div", f, tab.content, "tab-content");
				if (tab.contentClass) {
					contentHolder.classList.add(tab.contentClass);
				}
				contentList.push(contentHolder);

				button.onclick = () => {
					buttonList.forEach(c => { c.classList.remove("active"); });
					contentList.forEach(c => { c.classList.remove("active"); });
					button.classList.add("active");
					contentHolder.classList.add("active");
					V.tabChoice[this._id] = tab.id;
				};

				if (selected) {
					contentHolder.classList.add("active");
					button.classList.add("active");
					any = true;
				}
			}

			if (plainLinks) {
				tabBar.append(App.UI.DOM.generateLinksStrip(buttonList));
			}

			// if no tabs are open, open the first one.
			if (!any) {
				buttonList[0].click();
			}

			active = buttonList;

			return f;
		}
	}

	/**
	 * Call on passageinit to clear the button cache.
	 * Shouldn't matter, but we don't need to have huge DOM trees floating around.
	 */
	function clear() {
		active = null;
	}

	/**
	 * Open the tab to the left of the currently open tab. If there is none, don't do anything.
	 */
	function openLeft() {
		if (active) {
			const index = currentIndex();
			if (index - 1 >= 0) {
				active[index - 1].click();
			}
		}
	}

	/**
	 * Open the tab to the right of the currently open tab. If there is none, don't do anything.
	 */
	function openRight() {
		if (active) {
			const index = currentIndex();
			if (index > -1 && index + 1 < active.length) {
				active[index + 1].click();
			}
		}
	}

	/**
	 * @returns {number} Index of the currently open tab
	 */
	function currentIndex() {
		for (let i = 0; i < active.length; i++) {
			if (active[i].classList.contains("active")) {
				return i;
			}
		}
		return -1;
	}

	return {
		TabBar: TabBar,
		clear: clear,
		left: openLeft,
		right: openRight
	};
})();

repo_name: Fanatey/fc

license: mit


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.
	/** @type {HTMLLinkElement} */
	let currentThemeElement = null;

	return {
		selector: selector,
		loadTheme: loadTheme,
		init: loadFromStorage,
	};

	/**
	 * @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("Load", () => {
			if (selector.files.length > 0) {
				const themeFile = selector.files[0];
				loadTheme(themeFile.name);
			}
		}), " ", App.UI.DOM.link("Unload", unloadTheme));

		return div;
	}

	/**
	 * @param {string} [filename] or filepath relative to the HTML file.
	 */
	function loadTheme(filename) {
		unloadTheme();
		if (filename) {
			SugarCube.storage.set("theme", filename);
			currentThemeElement = document.createElement("link");
			currentThemeElement.setAttribute("rel", "stylesheet");
			currentThemeElement.setAttribute("type", "text/css");
			currentThemeElement.setAttribute("href", `./${filename}`);
			// make it unique to force reloading instead of using the cached version
			currentThemeElement.href += `?id=${Date.now()}`;
			document.head.appendChild(currentThemeElement);
		}
	}

	/**
	 * Unloads the current theme
	 */
	function unloadTheme() {
		if (currentThemeElement !== null) {
			document.head.removeChild(currentThemeElement);
			currentThemeElement = null;
			SugarCube.storage.delete("theme");
		}
	}

	/**
	 * Loads a saved theme from browser storage
	 */
	function loadFromStorage() {
		const file = SugarCube.storage.get("theme");
		if (file !== null) {
			loadTheme(file);
		}
	}
})();


code:
App.UI.GlobalTooltips = (function() {
	let instances = [];

	const tooltips = new Map([
		[".exampleTooltip", "I am a helpful tooltip. We are very rare because we are still in development."],

		[".devotion", "Devotion is a measure of a slave's love for you."],
		[".trust", "Trust is a measure of a slave's expectations of you and confidence to perform well."],
		[".defiant", "Defiant slaves will actively work against you."],

		[".flaw", "Flaws impend your slaves performance. Try removing or converting them into quirks."],
		[".intelligent", "More intelligent slaves tend to perform better."],
		[".stupid", "More intelligent slaves tend to perform better."],
		[".health", "The healthier your slaves, the better they perform."],
		[".libido", "Sex drive has various effects, generally higher is better."],
		[".positive", "This is good."],
		[".warning", "This is very bad. Try removing the cause for this."],

		[".error", "Something just broke. Please report this."],

		[".cash", "Money. Always useful."],
		[".reputation", "Your reputation as a slave owner. The higher, the better."],
	]);

	function enable() {
		disable();
		instances = [];
		for (const [identifier, tooltip] of tooltips.entries()) {
			instances.push(tippy.delegate(document.body, {
				target: identifier,
				content: tooltip
			}));
		}
	}

	function disable() {
		for (const instance of instances) {
			instance.destroy();
		}
		instances = [];
	}

	function update() {
		if (V.tooltipsEnabled === 1) {
			enable();
		} else {
			disable();
		}
	}

	return {update: update};
})();


code:
App.Intro.init = function() {
	/*
	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. */
	App.Update.setNonexistentProperties(V, App.Data.defaultGameStateVariables);

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

	V.ver = App.Version.base;
	V.pmodVer = App.Version.pmod;
	V.releaseID = App.Version.release;

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

	if (V.saveImported === 0) { // new game (not NG+)
		V.PC = basePlayer();
		WombInit(V.PC);
		cashX(10000, "personalBusiness");
		initRules();
	} else { // imported save (NG+)
		App.Data.NewGamePlus();
	}

	/* Porn star counts (prestige 1) and ID's (prestige 3) */
	V.pornStars = {};
	for (const genre of App.Porn.getAllGenres()) {
		V.pornStars[genre.fameVar] = {p1count: 0, p3ID: 0};
	}

	assistant.object();
	repX(1000, "event");
	App.Data.prostheticIDs.forEach(function(id) {
		V.prosthetics[id] = {amount: 0, research: 0};
	});
	V.JobIDMap = makeJobIdMap();

	App.Mods.SF.Init();

	V.weatherToday = App.Data.Weather.hotNice.random();
	V.weatherLastWeek = 1;
	V.weatherType = 1;
	V.weatherRemaining = 6;
	V.prisonCircuitIndex = random(0, V.prisonCircuit.length-1);
	if (V.prisonCircuit[V.prisonCircuitIndex] === "juvenile detention" && (V.minimumSlaveAge >= 16 || V.pedo_mode === 1)) {
		V.prisonCircuitIndex = 0; // skip juvenile detention if juvenile slaves are not allowed, or we're in pedo mode (where all prisoners are juvenile)
	}

	/* I am not a slave object! Do not treat me like one! */
	V.customSlave = new App.Entity.CustomSlaveOrder();
	V.huskSlave = new App.Entity.CustomSlaveOrder();
};


code:
/**
 * @returns {DocumentFragment}
 */
App.UI.SlaveInteract.artificialInsemination = function() {
	const f = new DocumentFragment();
	let r;

	App.UI.DOM.appendNewElement("p", f, `${getSlave(V.AS).slaveName} is prepped for fertilization; now you must select a target to harvest sperm from.`, "scene-intro");

	App.UI.DOM.appendNewElement("h2", f, "Select an eligible slave to serve as the semen donatrix");

	r = [];
	let any = false;
	for (const slave of V.slaves) {
		if (slave.balls > 0 && slave.pubertyXY === 1 && canBreed(getSlave(V.AS), slave)) {
			const {his} = getPronouns(slave);

			const name = App.UI.DOM.makeElement("span", SlaveFullName(slave), "has-tooltip");
			tippy(name, {
				content: App.UI.DOM.slaveDescriptionDialog(slave, "Pop-up", {noArt: true}),
				interactive: true,
			});
			r.push(App.UI.DOM.makeElement("div", name));

			r.push(App.UI.DOM.makeElement("div",
				App.Medicine.Surgery.makeLink(
					new App.Medicine.Surgery.Procedures.Insemination(getSlave(V.AS), `Use ${his} sperm.`, slave),
					exit, false)
			));

			any = true;
		}
	}
	if (r.length > 0) {
		App.Events.addNode(f, r, "div", "grid-2columns-auto");
	} else {
		App.UI.DOM.appendNewElement("p", f, "You have no slaves with potent sperm.", "note");
	}

	if (V.incubator.tanks.length > 0 && V.incubator.setting.reproduction === 2) {
		App.UI.DOM.appendNewElement("h2", f, "Select an eligible incubatee to milk for semen");
		App.UI.DOM.appendNewElement("p", f, "Incubator settings are resulting in large-scale fluid secretion.", "scene-intro");
		r = [];
		any = false;
		for (const tank of V.incubator.tanks) {
			if (tank.balls > 0 && tank.dick > 0 && canBreed(getSlave(V.AS), tank)) {
				r.push(App.UI.DOM.makeElement("div",
					App.Medicine.Surgery.makeLink(
						new App.Medicine.Surgery.Procedures.Insemination(getSlave(V.AS), `Use ${tank.slaveName}'s sperm.`, tank),
						exit, false)
				));
				any = true;
			}
		}

		if (any) {
			App.Events.addParagraph(f, r);
		} else {
			App.UI.DOM.appendNewElement("p", f, "You have no growing slaves producing sperm.", "note");
		}
	}

	if (V.PC.balls !== 0) {
		App.UI.DOM.appendNewElement("p", f,
			App.Medicine.Surgery.makeLink(
				new App.Medicine.Surgery.Procedures.Insemination(getSlave(V.AS), "Use your own", V.PC),
				exit, false)
		);
	} else if (V.PC.counter.storedCum > 0) {
		r = [];
		r.push(App.Medicine.Surgery.makeLink(
			new App.Medicine.Surgery.Procedures.InseminationFromStored(getSlave(V.AS), "Use a vial of your own", V.PC),
			exit, false));
		r.push(`<span class="detail">You have enough sperm stored away to inseminate ${V.PC.counter.storedCum} more ${V.PC.counter.storedCum > 1 ? "slaves" : "slave"}.</span>`);
		App.Events.addParagraph(f, r);
	}

	function exit() {
		Engine.play("Remote Surgery");
	}

	return f;
};


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.slaveAssignmentBabyFactory = 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.specialForcesCap = 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.PCdiet = 0;
	this.PCdrugs = 0;
	this.PCmedical = 0;
	this.PCcosmetics = 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.loan = 0;

	// Policies
	this.food = 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.shows = 0;	// e.g. Farmyard shows, etc
	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; // decay (from reputation.js)
	this.overflow = 0; // loss above 20k cap
	this.curve = 0; // loss due to income curving
	this.cheating = 0;

	this.total = 0;
};


code:
/** @param {App.Entity.SlaveState} slave */
App.Interact.discard = function(slave) {
	const frag = document.createDocumentFragment();

	const {him} = getPronouns(slave);

	let cost = 0;
	try {
		cost = 50 * Math.trunc(slaveCost(slave) / 100); // slightly less than you'd get from Sell Slave
	} catch {
		// just use the minimum price
	}
	cost = Math.max(cost, minimumSlaveCost(false));

	const result = document.createElement("span");

	App.UI.DOM.appendNewElement("p", frag, `This will permanently remove ${SlaveFullName(slave)} from the game, and you will receive a minimal reimbursement of ${cashFormat(cost)} for ${him}. This is primarily intended as a way to rid yourself of bugged slaves. Are you certain?`);

	App.UI.DOM.appendNewElement("div", result, App.UI.DOM.link(`Discard ${him}`, () => {
		$(result).empty();
		try {
			result.append(App.Interact.Sale.separationReactions(slave));
		} catch {
			// oh well
		}

		App.UI.DOM.appendNewElement("div", result, `${slave.slaveName} has been removed from the game.`);

		cashX(cost, "slaveTransfer");
		removeSlave(slave);
		V.nextButton = "Back to Main";
		V.nextLink = "Main";
		App.Utils.scheduleSidebarRefresh();
	}));

	App.UI.DOM.appendNewElement("div", result, App.UI.DOM.passageLink(`Put ${him} up for sale instead`, "Sell Slave"));
	App.UI.DOM.appendNewElement("div", result, App.UI.DOM.passageLink("Cancel", V.nextLink));

	frag.append(result);

	return frag;
};


code:
App.FCNN = (function() {
	const starting = [
		"...coming up at the top of the hour: Catgirl slaves, science fact or science fiction...",
		"...coming up at the top of the hour: Malnockestivi Smith, Free Cities' first MtFtMtFtH transgendered person...",
		"...new arcology construction up 23% worldwide this year, according to...",
		"...United States Congress spends 1,264th consecutive day gridlocked over post office...",
		"...coming up at the top of the hour: Arcology owners: oversexed oligarchs or attractive, oversexed oligarchs?...",
		"...coming up at the top of the hour: Anal sex: not just for sex any more...",
		"...coming up at the top of the hour: Oral sex: the new hello...",
		"...new book by prominent feminist suggests that women should not be used as sexual appliances...",
		"...just ahead, interview with Desha Moore, prominent advocate for compulsory female enslavement...",
		"...just ahead, Slave Market Trends: will the pierced anus be in again this year...",
		"...just ahead, Slave Market Trends: upstart slave trainers avoid implants...",
		"...implant technology firm BusTech notches successful initial public offering...",
		"...the upcoming shortage of authentic leather and what it means for the whip industry...",
		"...dairy conglomerate Creem Inc. denies allegations of adulterating breast milk with...",
		"...two-time award-winning actress Linda Loveless debuted new implants on the red carpet this...",
		"...dick size: are your slaves lying to you when they tell you you're too big...",
		"...just ahead, slave expert's opinion on best shemale slaves to use for double penetration...",
		"...Free Cities social conservatives criticize marriage, say your slaves should be enough...",
		"...councilman Taggart suggested in a public address that involuntary enslavement...",
		"...councilman Taggart denies allegations that he has remained faithful to his wife...",
		"...councilman Taggart presented evidence that regulation of the sex slave market would...",
		"...after a word from our sponsors. Creem Inc.: for all your dairy needs...",
		"...after a word from our sponsors. Horstmann Ltd, Free Cities' finest whipmakers...",
		"...after a word from our sponsors. Coming soon to theaters, Quintuple, the musical...",
		"...critical of low-end slave training corporation Wallerson & Sons for practices that they say...",
		"...training corporation Wallerson & Sons called a study on slave illness rates 'ludicrous,' but...",
		"...our tech correspondent: the possibilities of virally-administered gene therapy...",
		"...our tech correspondent: breakthrough in in-vitro drug treatments that promise to...",
		"...our tech correspondent: next year to see release of two competing aphrodisiacs...",
		"...our tech correspondent: the coming permanent aphrodisiac implants, and what they mean...",
		"...Sex Slaves in Space: what it takes to keep a mining crew happy for an 18-month contract...",
		"...the implant-drug balance: how much tissue growth is necessary to support larger...",
		"...the actress stated that the cut to full nudity in the script violated contractual...",
		"...doping scandal as Slave Games watchdog alleges champion used internal reservoir of lube...",
		"...next on Extreme Surgery: the mouthpussy experimenters and what they...",
		"...'A hole's a hole,' said CEO of upstart budget glory hole franchise...",
		"...underground slave pit fights step into the light this evening as...",
		"...underground slave pit fight champion, freed yesterday, sells herself back into...",
		"...with the lead designer of the MP17, the new machine pistol marketed specifically for bodyguards...",
		"...the new Aegis drone series: because your arcology's security is your most important possession...",
		"...the BAe Goshawk: because you deserve to travel at twice the speed of sound in the finest style...",
		"...this year's Goat.cx award for outstanding orifice innovation goes to...",
		"...public controversy over cannibalism. Decadence taken too far or an acceptable next step...",
		"...sixth day of street cleaners' strike. Spokesman for the strikers: 'It's getting too nasty...",
		"...debuts new book, 'So Long, And Thanks For All The Dicks', in which the recently retired sex slaves tell-all about...",
		"...cure for lactose intolerance, for which he was awarded the International Association of Pastoralist...",
		"...from the Free Cities have become increasingly common clientele for the black market...",
		"...a risky gamble on the three-hour-long hardcore sex scene, but the box office figures for just...",
		"...claiming that even tourists to the Free Cities were at risk of sexual enslavement...",
	];

	const week30 = [
		"...is where I'll be when the bombs hit. Survival is my first...",
		"...time to prepare for major shortages is now, since there's no likelihood...",
		"...construction of shelters accelerated last quarter, due to fears...",
		"...major drought across the entire hemisphere shows no signs of...",
		"...medical organization offered no explanation, stating that...",
		"...just ahead, an FCNN Special Report: Immortality and You — Breakthroughs in 'Bodyswapping' technology and what they mean for...",
		"...wildfires have now cost an estimated...",
		"...widespread demonstrations against the government's friendly relations with the Free Cities...",
		"...of the small island nation, declared that his homeland may be completely underwater by...",
		"...claimed the sanctions constituted an act of war...",
		"...nuclear weapons development program, drawing harsh criticism from international observers...",
		"...simply don't have the money to adequately respond to a natural disaster of this...",
		"...representatives announced their withdrawal from the international organization...",
		"...special report on the depopulation and abandonment of rural areas worldwide, as natural...",
	];

	const week50 = [
		"...tested a thermonuclear device of some five megatons last night...",
		"...heavy fighting for the third week in the capital city of...",
		"...economic recovery looking increasingly unlikely in the event of...",
		"...complete exhaustion of safe water reserves across...",
		"...climate refugees streamed across the border despite...",
		"...released a statement today, explaining their refusal to intervene against the pillaging of several Free Cities in northern...",
		"...denies allegations of ordering last week's combined arms assault on the arcology known as...",
		"...Coming up, an FCNN Special Report: Ghost Cities — The truth about recent surge of depopulated metropolitan areas in...",
		"...was assassinated in his home in the nation's capital, alongside his entire...",
		"...forces opened fire on crowds of protesters, killing and wounding...",
		"...declared the insurgency officially defeated, despite reports of widespread violence...",
		"...suspended national elections until further notice, in a move that is seen as yet another authoritarian...",
	];

	const week70 = [
		"...second low-order nuclear detonation in the city of...",
		"...total societal collapse induced by rising sea levels declared today in...",
		"...likelihood of containing Influenza-M was downgraded to a lower...",
		"...threatened immediate use of chemical weapons should those forces not withdraw...",
		"...relief organizations unable to deal with casualties from nuclear exchange between...",
		"...guerrilla fighting intensifies in escalating proxy war between 'Paternalist' and 'Degradationist' Free Cities in the region of...",
		"...Coming up, an FCNN Special Report about the unprecedented scale of slave insurrections across...",
		"...invasive plant growth epidemic contested by persistent droughts in eastern...",
		"...Daughters of Liberty claimed responsibility for the attack...",
		"...mass suicide of the cult members, who believed that current events were prophesied...",
		"...genocide is a strong term, but there's simply no other way to describe...",
	];

	const week90 = [
		"...have enslaved the entire legislature, their leader personally sexually assaulting...",
		"...confirmed that the arcologies were subject to a nuclear detonation, but...",
		"...conflict can now be said to encompass the entire continent...",
		"...government has effectively fallen, with much of the capital destroyed...",
		"...deadliest in the nation's history, was allegedly committed by the Daughters of Liberty...",
		"...famine so devastating that confirmed reports of cannibalism are now...",
		"...dozens upon dozens of mass graves in what were once city parks...",
		"...evidence of a planned slave uprising, executed all of their arcology's several thousand...",
	];

	const bodyswap = [
		"...transferring consciousness from one body to another is morally wrong, but financially..."
	];

	const TFSCompromise = [
		"...Ladies: Do you want dick so much that you want to HAVE a dick? If yes, join the Futanari Sisters today! Learn more at..."
	];

	const corpAnnounced = [
		"...and Free Cities have definitely shaken up the stock market in recent..."
	];

	const catgirlsPublic = [
		"...Catgirl slaves: Fad, fluke, or fashion? Tune in for an exclusive FCNN interview with Doctor Radomir Nieskowitz later tonight..."
	];

	const projectNComplete = [
		"...Sons of Sekhmet claim responsibility for bombing campaign in...",
		"...was killed this week in a surprise ambush by Sons of Sekhmet assassins...",
		"...terrorist organizations in the Free Cities: An exclusive report, coming up next...",
		"...large scale rioting continues for the ninety-sixth consecutive day over genetic engineering concerns...",
	];

	const textGroups = [
		// dynamic headlines based on conditions
		{valid: () => true, text: () => starting},
		{valid: () => V.week >= 30, text: () => week30},
		{valid: () => V.week >= 50, text: () => week50},
		{valid: () => V.week >= 70, text: () => week70},
		{valid: () => V.week >= 90, text: () => week90},
		{valid: () => V.bodyswapAnnounced === 1, text: () => bodyswap},
		{valid: () => V.TFS.schoolUpgrade === 3, text: () => TFSCompromise},
		{valid: () => V.FCNNstation === 1, text: () => [`...is FCNN, broadcasting live from its international headquarters within ${V.arcologies[0].name}...`]},
		{valid: () => V.corp.Announced === 1, text: () => corpAnnounced},
		{valid: () => V.seeCats === 1 && V.projectN.public === 1, text: () => catgirlsPublic},
		{valid: () => V.seeCats === 1 && V.projectN.status >= 5, text: () => projectNComplete},
		// custom headlines can be stored in game state by events
		{valid: (/** @type {boolean} */ includeCustom) => includeCustom, text: () => V.fcnn}
	];

	return {
		/** Get all currently-valid news ticker strings for FCNN
		 * @param {boolean} [seeCustom=true] indicates whether custom headlines (from game state) are included
		 * @returns {string[]}
		 */
		getAllValidText: function(seeCustom = true) {
			return textGroups.filter(g => g.valid(seeCustom)).flatMap(g => g.text());
		},

		/** Get a random news ticker string for FCNN
		 * @returns {string}
		 */
		getText: function() {
			if (V.FCNNstation !== 1 && V.week >= 90) {
				return "FCNN: FCNN service has been temporarily suspended. Please stand by. ";
			}
			return this.getAllValidText().random() + " ";
		}
	};
})();


code:
/**
 * @returns {DocumentFragment}
 */
App.UI.View.mainLinks = function() {
	"use strict";
	const PA = V.personalAttention.task === PersonalAttention.TRAINING ? V.personalAttention.slaves.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.task) {
			case PersonalAttention.BUSINESS:
				fragment.append(`You plan to focus on business this week.`);
				break;
			case PersonalAttention.WHORING:
				fragment.append(`You plan to focus on earning extra money this week.`);
				break;
			case PersonalAttention.MAID:
				fragment.append(`You plan to focus on cleaning the penthouse this week.`);
				break;
			case PersonalAttention.SURVEY:
				fragment.append(`You plan to survey ${V.arcologies[0].name}'s defenses in person this week.`);
				break;
			case PersonalAttention.DEVELOPMENT:
				fragment.append(`You plan on contributing to a local development project this week.`);
				break;
			case PersonalAttention.SMUGGLING:
				fragment.append(`You plan to make some easy (but dirty) money this week.`);
				break;
			case PersonalAttention.SUPPORTHG:
				fragment.append(`You plan to support your Head Girl this week, `);
				if (S.HeadGirl) {
					const {he, his} = getPronouns(S.HeadGirl);
					fragment.append(`so ${he} can give more slaves ${his} attention.`);
				} else {
					fragment.append(`should you assign one.`);
				}
				break;
			case PersonalAttention.SEX:
				fragment.append(`You plan to have as much sex with your slaves as possible this week.`);
				break;
			case PersonalAttention.TRADE:
				fragment.append(`This week you will learn trading.`);
				break;
			case PersonalAttention.WAR:
				fragment.append(`This week you will learn modern combat tactics.`);
				break;
			case PersonalAttention.SLAVING:
				fragment.append(`This week you will learn slaving.`);
				break;
			case PersonalAttention.ENGINEERING:
				fragment.append(`This week you will learn engineering.`);
				break;
			case PersonalAttention.MEDICINE:
				fragment.append(`This week you will learn medicine.`);
				break;
			case PersonalAttention.HACKING:
				fragment.append(`This week you will learn hacking.`);
				break;
			case PersonalAttention.PROCLAMATION:
				fragment.append(`This week you plan to issue a proclamation about ${V.SecExp.proclamation.type}.`);
				break;
			case PersonalAttention.TECH:
				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 ${App.PersonalAttention.getText(V.personalAttention.slaves[i].objective, trainee)}`));
					});
					fragment.append(App.UI.DOM.toSentence(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", App.UI.Hotkeys.hotkeys("Personal Attention Select"), "hotkey"));
	}

	if (V.useSlaveSummaryOverviewTab === 0) {
		let div = document.createElement("div");
		if (S.HeadGirl) {
			div.append(App.UI.DOM.makeElement("span", SlaveFullName(S.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", "Head Girl Select"), "major-link"),
				" ", App.UI.DOM.makeElement("span", App.UI.Hotkeys.hotkeys("Head Girl Select"), "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", "Head Girl Select"), "major-link"),
				" ", App.UI.DOM.makeElement("span", App.UI.Hotkeys.hotkeys("Head Girl Select"), "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 (V.RecruiterID) {
			div.append(App.UI.DOM.makeElement("span", SlaveFullName(S.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", App.UI.Hotkeys.hotkeys("Recruiter Select"), "hotkey"));
		div.id = "manageRecruiter";
		fragment.append(div);

		if (V.dojo) {
			div = document.createElement("div");
			if (V.BodyguardID !== 0) {
				div.append(App.UI.DOM.makeElement("span", SlaveFullName(S.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", App.UI.Hotkeys.hotkeys("BG Select"), "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.AS = V.slaves[i].ID;
								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++) {
			const slave = getSlave(V.adjustProsthetics[j].slaveID);
			if (slave) {
				if (V.adjustProsthetics[j].workLeft <= 0) {
					const div = document.createElement("div");
					div.classList.add("yellow");
					div.append(`The lab has completed ${addA(App.Data.prosthetics[V.adjustProsthetics[j].id].name)} for `,
						App.UI.DOM.makeElement("span", App.UI.DOM.link(SlaveFullName(slave), () => { V.AS = slave.ID; }, [], "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 = App.UI.DOM.makeElement("div", null, ['margin-bottom']);
	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);

	for (const [SCH, schObj] of App.Data.misc.schools) {
		if (!schObj.requirements || V[SCH].schoolSale === 0) {
			continue;
		}
		const div = document.createElement("div");
		div.append(App.UI.DOM.makeElement("span", "For your first purchase, ", "yellow"),
			App.UI.DOM.passageLink(
				schObj.title,
				"Market",
				() => {
					V.market = new App.Markets.Global(SCH);
					V.nextButton = "Back to Main";
					V.nextLink = "Main";
					V.returnTo = "Main";
				}),
			App.UI.DOM.makeElement("span", " will sell at half price this week.", "yellow"));
		fragment.append(div);
	}
	return fragment;
};


code:
/* OPEN TOYCHEST */
App.Interact.ToyChest = function(slave) {
	"use strict";
	const {
		he, him, his, himself, He, His
	} = getPronouns(slave);

	let 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 "Imperial Plate":
				r += `${His} heavily armored and crest-marked presence lends your office an air of violent nobility.`;
				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 tight Imperial bodysuit":
				r += `The bodysuit ${he}'s wearing displays ${his} every fuckable curve, your crest proudly displayed on the swell of ${his} chest.`;
				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 {App.Entity.SlaveState} slave
 * @returns {string}
 */
App.Interact.guardPose = function(slave) {
	"use strict";
	const {
		he, him, his, boy, woman,
		He, His
	} = getPronouns(slave);

	let 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 "Imperial Plate":
			r += `${His} heavy Imperial armor makes ${him} look like a walking tank, the dynamic plating shifting against itself as ${he} moves.`;
			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 tight Imperial bodysuit":
			r += `${His} form-fitting bodysuit and weapons make ${him} look like an action movie heroine, an elegant mesh of pulsating cybernetics and tight nanomesh that rides up tightly against ${his} every curve.`;
			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${woman} 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;
};