code:
App.UI.walkPastAll = function() {
	const node = new DocumentFragment();

	App.UI.DOM.appendNewElement("p", node, `For testing purposes only, this page lists or can produce all of the "walk past" scenes."Vagina" will use ass if they lack one.`, "scene-intro");

	const tabMap = new Map([
		["Lips", "fLips"],
		["Boobs", "fBoobs"],
		["Butt", "fButt"],
		["Anus", "fAnus"],
		["Vagina", "fVagina"],
		["Dick", "fDick"],
		["Rival", "fRival"],
		["Relation", "fRelation"],
	]);

	App.UI.DOM.appendNewElement("h2", node, `Choose target`);

	const tabBar = new App.UI.Tabs.TabBar("Walkpastlist");
	for (const [title, category] of tabMap) {
		tabBar.addTab(title, title.toLowerCase(), walkPastCategory(category));
	}
	node.append(tabBar.render());

	return node;

	/** generate the specified walkPast vignette for all slaves (for debugging purposes)
	 * @param {string} fixedTarget
	 * @returns {DocumentFragment}
	 */
	function walkPastCategory(fixedTarget) {
		return App.UI.DOM.combineNodes(...V.slaves.map(
			(s) => App.UI.DOM.makeElement("p", walkPast(s, fixedTarget))
		));
	}
};

repo_name: Fekenol/fc

license: mit


code:
App.UI.breederProposal = function() {
	const node = new DocumentFragment();
	V.propOutcome = -1;

	App.UI.DOM.appendNewElement("p", node, `You draft a proposal to the Societal Elite proposing the use of specially tested slaves as breeding stock. If they can pass the tests required to join the Societal Elite; even as slaves, their bodies can prove valuable to furthering eugenics. They will gain no additional rights, but their wombs will be used to birth Elite children; helping stave off potential threats of inbreeding and adding possible missed beneficial traits into the Elite's gene pool.`);

	App.UI.DOM.appendNewElement("div", node, `Within the hour, you are called before${(V.eugenicsFullControl === 1) ? ` what's left of` : ``} the Societal Elite.`);

	const {
		girlP
	} = getPronouns(V.PC).appendSuffix("P");
	if (V.eugenicsFullControl === 1) {
		App.UI.DOM.appendNewElement("div", node, `"That sounds like an acceptable proposal. We'll draft the standards for breeding stock and notify you shortly."`);
		V.propOutcome = 1;
		unlock();
	} else if (V.PC.pregSource === -1 || V.PC.pregSource === -6) {
		App.UI.DOM.appendNewElement("div", node, `"Since you are doing your part and carrying an Elite child, we will gladly reward you by granting your proposal. We shall decide upon the standards for breeding stock and notify you shortly."`);
		V.propOutcome = 1;
		unlock();
	} else if (V.PC.counter.birthElite > 0) {
		App.UI.DOM.appendNewElement("div", node, `"Since you have carried children for our cause, we will gladly reward you by granting your proposal. We shall decide upon the standards for breeding stock and notify you shortly."`);
		V.propOutcome = 1;
		unlock();
	} else if (V.PC.dick === 0) {
		if (V.failedElite > 50) {
			App.UI.DOM.appendNewElement("div", node, `"Do you really think you are in any position to make such a request? You, a fertile woman and member of the Elite, who has not taken even the slightest interest in carrying one of our children, dares to even suggest using slaves as breeding stock? Well?"`);

			const usedAsBreeder = function() {
				V.playerBred = 1;
				V.propOutcome = 1;
				unlock();
				return `"Good choice, ${girlP}. A selection of eligible donors will be provided to you; take your pick and bear our children. Or use a test tube, if that's more to your tastes."`;
			};

			const declineAsBreeder = function() {
				V.failedElite += 275;
				V.propOutcome = -1;
				unlock();
				return `"You'll regret this decision. We'll have our way one way or another."`;
			};

			App.Events.addResponses(node, [
				new App.Events.Result(`Agree to being used as a breeder and save face`, usedAsBreeder),
				new App.Events.Result(`Decline being used as a breeder and face the Societal Elites' wrath`, declineAsBreeder),
			]);
		} else {
			App.UI.DOM.appendNewElement("div", node, `"An interesting proposal, given your lack of equipment, though we would be much more open to listen to your request if you were heavy with one of our children.`);

			const usedAsBreeder = function() {
				V.failedElite -= 50;
				V.playerBred = 1;
				V.propOutcome = 1;
				unlock();
				return `"Good choice, ${girlP}; your proposal is more important than your dignity after all, isn't it? A selection of eligible donors will be provided to you; take your pick and bear our children. Or use a test tube, if that's more to your tastes."`;
			};

			const declineAndLeverage = function() {
				V.propOutcome = 1;
				unlock();
				return `"Yes, you have done much to further our cause. We respect the 'balls' on you, despite your lack of them. Very well, we shall set the standards for what shall qualify as breeding stock and our standards will be delivered to you shortly."`;
			};

			const declineAsBreeder = function() {
				V.propOutcome = -1;
				unlock();
				return `"Then why waste our time? You disappoint us, but don't worry; you'll be laden with child soon enough."`;
			};

			const choices = [];
			choices.push(new App.Events.Result(`Agree to being used as a breeder for the sake of your proposal`, usedAsBreeder));
			if (V.failedElite <= 0) {
				choices.push(new App.Events.Result(`Decline being used as a breeder and leverage your standing`, declineAndLeverage));
			} else {
				choices.push(new App.Events.Result(`Decline being used as a breeder and withdraw your proposal`, declineAsBreeder));
			}
			App.Events.addResponses(node, choices);
		}
	} else {
		if (V.PC.dick !== 0 && V.PC.vagina !== -1 && V.failedElite > 50) {
			App.UI.DOM.appendNewElement("div", node, `"We have read your proposal and propose the following: You are not well liked among us, and your medical records show you are quite capable of carrying a child. If you will carry our children, we will grant your proposal, otherwise, leave and never ask us this again."`);

			const agree = function() {
				V.failedElite -= 50;
				V.playerBred = 1;
				V.propOutcome = 1;
				unlock();
				return `"Your contributions will be appreciated. We shall convene to decide the qualifications for a slave to become a breeder. We will inform you of them when we send the list of eligible donors to breed you. Or send you a test tube, if that's more to your tastes."`;
			};

			const declineWithdraw = function() {
				V.propOutcome = -1;
				unlock();
				return `"Very well, if your masculinity is more valuable to you than your proposal, then so be it."`;
			};

			App.Events.addResponses(node, [
				new App.Events.Result(`Agree to being used as a breeder to complete the deal`, agree),
				new App.Events.Result(`Decline being used as a breeder and withdraw your proposal`, declineWithdraw),
			]);
		} else if (V.PC.dick !== 0 && V.PC.vagina !== -1) {
			App.UI.DOM.appendNewElement("div", node, `"We have read your proposal and agree. However, we shall set the standards for what shall qualify as breeding stock, not you. Our standards will be delivered to you shortly.`);

			App.UI.DOM.appendNewElement("p", node, `Your medical records say you have working female sex organs, and we respect your choice to not use them. But have you considered carrying another member's child? It will be extremely lucrative for you and go a long way to furthering our objectives."`);

			const agree = function() {
				V.playerBred = 1;
				V.propOutcome = 1;
				unlock();
				return `"Your contributions will be appreciated. We shall convene to decide the qualifications for a slave to become a breeder. We will inform you of them when we send the list of eligible donors to breed you. Or send you a test tube, if that's more to your tastes."`;
			};

			const declineAsBreeder = function() {
				V.propOutcome = 1;
				unlock();
				return `"Very well, if you wish to waste your gift, it is your prerogative. We shall convene to decide the qualifications for a slave to become a breeder and inform you upon completion."`;
			};

			App.Events.addResponses(node, [
				new App.Events.Result(`Agree to being used as a breeder`, agree),
				new App.Events.Result(`Decline being used as a breeder`, declineAsBreeder),
			]);
		} else {
			App.UI.DOM.appendNewElement("div", node, `"We have read your proposal and agree. However, we shall set the standards for what shall qualify as breeding stock, not you. Our standards will be delivered to you shortly."`);
			V.propOutcome = 1;
			unlock();
		}
	}
	return node;

	/** unlock Continue button */
	function unlock() {
		V.nextButton = "Continue";
		App.Utils.updateUserButton();
	}
};


code:
App.Arcology.changeLanguage = function() {
	let el = document.createElement("div");
	let revivalist = {language: ""};
	let r = [];

	const options = new App.UI.OptionsGroup();
	const languageList = new Set(["English", "Spanish", "Arabic", "Chinese", "Japanese", V.seed]);

	if (["Australia", "North America", "Western Europe"].includes(V.continent)) {
		languageList.add("English");
	} else if (V.continent === "South America") {
		languageList.add("Spanish");
	} else if (V.continent === "Brazil") {
		languageList.add("Portuguese");
	} else if (V.continent === "Central Europe") {
		languageList.add("German");
	} else if (V.continent === "Eastern Europe") {
		languageList.add("Russian");
	} else if (V.continent === "Southern Europe") {
		languageList.add("Italian");
	} else if (V.continent === "Scandinavia") {
		languageList.add("Norwegian");
	} else if (V.continent === "the Middle East" || V.continent === "Africa") {
		languageList.add("Arabic");
	} else if (V.continent === "Asia") {
		languageList.add("Chinese");
	} else if (V.continent === "Japan") {
		languageList.add("Japanese");
	}

	if (V.arcologies[0].FSRomanRevivalist !== "unset") {
		revivalist = {language: "Latin", name: "Roman"};
	} else if (V.arcologies[0].FSAztecRevivalist !== "unset") {
		revivalist = {language: "Nahuatl", name: "Aztec"};
	} else if (V.arcologies[0].FSEgyptianRevivalist !== "unset") {
		revivalist = {language: "Ancient Egyptian", name: "Egyptian"};
	} else if (V.arcologies[0].FSEdoRevivalist !== "unset") {
		revivalist = {language: "Japanese", name: "Edo"};
	} else if (V.arcologies[0].FSArabianRevivalist !== "unset") {
		revivalist = {language: "Arabic", name: "Arabian"};
	} else if (V.arcologies[0].FSChineseRevivalist !== "unset") {
		revivalist = {language: "Chinese", name: "Chinese"};
	}

	if (revivalist.language !== "") {
		languageList.add(revivalist.language);
	}

	r.push(`The lingua franca of the arcology is <span class='bold'>${V.language}.</span>`);
	App.Events.addParagraph(el, r);

	r = [];
	r.push(`Changing the lingua franca of the arcology is a difficult process.`);
	r.push(`Doing so will slightly reduce the arcology's prosperity due to the difficulty changing languages will impose on businesses.`);
	r.push(`It will also cost ${cashFormat(500)} for each mentally competent slave you own, since it will be necessary to build time into their schedules for them to learn basic commands in the new language.`);
	r.push(`Slaves' starting fluency in the new language will depend on their intelligence, nationality, education, and in some cases, ethnicity.`);
	r.push(`Previous linguistic experience will not be saved, meaning that repeated language changes will wipe out slaves' previous language skills.`);
	App.Events.addParagraph(el, r);

	options.addOption("Select a custom language to be applied: ", "seed").showTextBox().addComment("A single capitalized word is recommended.");
	el.append(options.render());

	for (const targetLanguage of languageList) {
		App.UI.DOM.appendNewElement("div", el, App.UI.DOM.link(targetLanguage, () => {
			V.language = targetLanguage;
			V.arcologies[0].prosperity = Math.trunc(0.9 * V.arcologies[0].prosperity);
			for (const slave of V.slaves) {
				if (slave.fetish !== "mindbroken") {
					nationalityToAccent(slave);
					if (slave.accent >= 3) {
						if ((slave.intelligence + slave.intelligenceImplant + 100) > random(0, 100)) {
							slave.accent--;
						}
					}
					cashX(-500, "capEx");
				}
			}
		},
		[], passage(),
		`${revivalist.language !== "" && V.language !== revivalist.language && targetLanguage === revivalist.language ? ` Failing to take this step will retard adoption of ${revivalist.name} Revivalism.` : ""}`
		));
	}
	return el;
};


code:
App.UI.coursingAssociation = function() {
	const node = new DocumentFragment();

	App.Events.addParagraph(node, [`You are a member of ${V.arcologies[0].name}'s Coursing Association. Coursing is a Free Cities revival of the old sport of hunting rabbits and hares with sighthounds, with the typically Free Cities amendments that the hares are replaced by newly enslaved people, the sighthounds are replaced by trained slaves, and the killing of the hare is replaced by rape. Truly, a sport of gentlemen.`]);

	const r = [];
	r.push(`The chasing slaves are known as lurchers, the term once used for the sighthounds. They require speed most of all, but must also be able to tackle their quarry; lurchers with the ability and willingness to make a spectacle of molesting the hares can improve their owners' reputations.`);

	if (V.LurcherID !== 0) {
		r.push(`${SlaveFullName(slaveStateById(V.LurcherID))} is assigned to compete as your lurcher.`);
	} else {
		r.push(`You have not selected a lurcher, meaning that you will not participate in coursing events.`);
	}

	r.push(App.UI.DOM.makeElement("div", `${properTitle()}, slaves assigned here can continue their usual duties.`, "note"));
	App.Events.addParagraph(node, r);

	if (V.LurcherID !== 0) {
		App.Events.addParagraph(node, [
			App.UI.DOM.makeElement("div", "Fire your Lurcher:", "bold"),
			App.UI.SlaveList.render([V.LurcherID], new Array(),
				(slave) => App.UI.DOM.passageLink(SlaveFullName(slave), 'Coursing Association', () => removeJob(slave, Job.LURCHER)))
		]);
	}

	App.Events.addParagraph(node, [
		App.UI.DOM.makeElement("div", "Select a slave to course as a Lurcher:", "bold"),
		App.UI.SlaveList.slaveSelectionList(
			s => V.LurcherID !== s.ID && canWalk(s) && canHold(s) && (canSee(s) || canHear(s)) && s.fuckdoll === 0 && isSlaveAvailable(s),
			(slave) => App.UI.DOM.link(SlaveFullName(slave), () => assignmentTransition(slave, Job.LURCHER, "Coursing Association"))
		)
	]);

	return node;
};


code:
globalThis.policies = (function() {
	return {
		countEugenicsSMRs: countEugenicsSMRs,
		cost: cost,
	};

	function countEugenicsSMRs() {
		let value = 0;
		if (V.policies.SMR.eugenics.faceSMR === 1) {
			value++;
		}
		if (V.policies.SMR.eugenics.heightSMR === 1) {
			value++;
		}
		if (V.policies.SMR.eugenics.intelligenceSMR === 1) {
			value++;
		}
		return value;
	}

	function cost() { return 5000; }
})();
/**
 * @param {string} category
 * @returns {Array}
 */
globalThis.policy = function(category) {
	const validPolicies = [];
	for (let policyString in App.Data.Policies.Selection[category]) {
		policyElement(policyString);
	}
	return validPolicies;

	/** @param {string} policyString */
	function policyElement(policyString) {
		const policyValue = _.get(V, policyString);
		/** @type {PolicySelector[]} */
		const policies = App.Data.Policies.Selection[category][policyString];
		if (policyValue === 0) {
			// apply
			for (const p of policies) {
				const el = document.createElement("p");
				const enable = p.enable || 1;
				if (
					(p.hasOwnProperty("requirements") && p.requirements === false) || // requirements fail
					(p.hasOwnProperty("hide") && p.hide.ifActivated === 0) // policy cannot be unlocked by hand here, may be done by event.
				) {
					continue;
				}

				// title
				const classArray = ["bold"];
				if (p.hasOwnProperty("titleClass")) {
					classArray.push(p.titleClass);
				}
				App.UI.DOM.appendNewElement("span", el, p.title, classArray).append(`: `);

				// Description text
				el.append(p.text);

				// link
				if (!(p.hasOwnProperty("hide") && p.hide.button === 1)) {
					el.append(` `, implement(p, enable));
				}
				validPolicies.push(el);
			}
		} else if (typeof policyValue === "string" || typeof policyValue === "number") {
			const el = document.createElement("p");
			// repeal
			const p = policies.find(pol => pol.hasOwnProperty("enable") && pol.enable === policyValue) || policies[0];
			if (p.hasOwnProperty("hide") && p.hide.ifActivated === 1) {
				return;
			}

			// title
			const title = (p.hasOwnProperty("activatedTitle")) ? p.activatedTitle : p.title;
			App.UI.DOM.appendNewElement("span", el, title, "bold").append(`: `);

			// Description text
			el.append((p.hasOwnProperty("activatedText")) ? p.activatedText : p.text);

			// link
			el.append(` `, repeal(p));
			validPolicies.push(el);
		} else {
			throw Error(`V.${policyString} not initialized properly, "${policyValue}"`);
		}

		/**
		 * @param {PolicySelector} p The data object that describes the policy being considered.
		 * @returns {Node} Link to repeal.
		 */
		function repeal(p) {
			const frag = new DocumentFragment;
			let check = canAfford();
			let link;
			if (!(p.hasOwnProperty("hide") && p.hide.button === 1)) {
				if (check === true) {
					link = App.UI.DOM.link(
						"Repeal",
						() => {
							if (V.rep >= 1000) {
								_.set(V, policyString, 0);
								applyCost();
								if (p.hasOwnProperty("onRepeal")) {
									p.onRepeal();
								}
							}
							App.UI.reload();
						}
					);
					link.style.color = "yellow";
				} else {
					link = App.UI.DOM.disabledLink("Repeal", [`You do not have enough ${check}`]);
					link.style.color = "red";
				}
				frag.append(link);
			}

			if (p.hasOwnProperty("activatedNote")) {
				frag.append(App.UI.DOM.makeElement("div", p.activatedNote, ["note", "indent"]));
			}
			return frag;
		}
		/**
		 * @param {PolicySelector} p The data object that describes the policy being considered.
		 * @param {number|string} enable value to set the policy to in order to switch it on.
		 * @returns {Node} Link to implement.
		 */
		function implement(p, enable) {
			let check = canAfford();
			const frag = new DocumentFragment;
			const linkArray = [];
			let link;
			if (check === true) {
				link = App.UI.DOM.link(
					"Implement",
					() => {
						if (V.rep >= 1000) {
							_.set(V, policyString, enable);
							applyCost();
							if (p.hasOwnProperty("onImplementation")) {
								p.onImplementation();
							}
						}
						App.UI.reload();
					}
				);
				link.style.color = "green";
			} else {
				link = App.UI.DOM.disabledLink("Implement", [`You do not have enough ${check}`]);
				link.style.color = "red";
			}
			linkArray.push(link);
			if (V.cheatMode) {
				linkArray.push(App.UI.DOM.link(
					"Cheat apply",
					() => {
						_.set(V, policyString, enable);
						if (p.hasOwnProperty("onImplementation")) {
							p.onImplementation();
						}
						App.UI.reload();
					}
				));
			}
			frag.append(App.UI.DOM.generateLinksStrip(linkArray));
			if (p.hasOwnProperty("note")) {
				frag.append(App.UI.DOM.makeElement("div", p.note, ["note", "indent"]));
			}
			return frag;
		}

		function canAfford() {
			if (V.cash < 5000) {
				return "cash";
			} else if (V.rep < 1000 && !["EducationPolicies"].includes(category)) {
				return "reputation";
			}
			return true;
		}

		function applyCost() {
			cashX(-5000, "policies");
			if (!["EducationPolicies"].includes(category)) {
				repX(-1000, "policies");
			}
		}
	}
};

globalThis.checkPolicyGameover = function() {
	const arc = V.arcologies[0];
	if (arc.FSSupremacistLawME && V.PC.race !== arc.FSSupremacistRace) {
		V.nextLink = "Gameover";
		V.gameover = "Idiot Ball 2 The Dumbassening";
	} else if (arc.FSSubjugationistLawME && V.PC.race === arc.FSSubjugationistRace) {
		V.nextLink = "Gameover";
		V.gameover = "Idiot Ball 3 Totally Not Idiot Ball 2 Again";
	} else {
		V.nextLink = "Main";
	}
};


code:
App.UI.policies = function() {
	const node = new DocumentFragment();

	App.UI.DOM.appendNewElement("h1", node, `Policies`);

	const r = [];
	r.push(`Passing any law will cost ${cashFormat(5000)} and`);
	if (V.rep >= 1000) {
		r.push(App.UI.DOM.makeElement("span", `a small amount of reputation.`, `green`));
	} else {
		r.push(App.UI.DOM.makeElement("span", `a better reputation.`, `red`));
	}
	r.push(`More policies will become available as the arcology develops.`);
	App.Events.addNode(node, r, "p", "scene-intro");

	const tabBar = new App.UI.Tabs.TabBar("Policies");

	tabBar.addTab("Slave Market Regulations", "smr", smr());
	tabBar.addTab("Sexual Trendsetting", "st", st());
	tabBar.addTab("Population & Domestic", "population", population());
	tabBar.addTab("Education", "education", education());
	tabBar.addTab("Future Societies", "fs", fs());
	tabBar.addTab("Slave Retirement Planning", "srp", srp());

	node.append(tabBar.render());
	return node;

	function smr() {
		const frag = new DocumentFragment();
		App.UI.DOM.appendNewElement("div", frag, `Slave Market Regulations (SMRs) will affect slaves that come through the official slave markets in your arcology. The markets themselves will bear the cost of these regulations, but the minimum practicable slave prices may rise as a result.`, "scene-intro");
		policy("SMR").forEach(p => frag.append(p));
		return frag;
	}

	function st() {
		const frag = new DocumentFragment();
		policy("SexualTrendsetting").forEach(p => frag.append(p));
		return frag;
	}

	function population() {
		const frag = new DocumentFragment();
		App.UI.DOM.appendNewElement("h1", frag, `Population`);
		policy("PopulationPolicies").forEach(p => frag.append(p));
		App.UI.DOM.appendNewElement("h1", frag, `Domestic`);
		policy("DomesticPolicies").forEach(p => frag.append(p));
		return frag;
	}

	function education() {
		const frag = new DocumentFragment();
		if (V.schoolSuggestion === 0) {
			App.UI.DOM.appendNewElement("p", frag, `You have yet to contact a school about adding a local branch.`);
		}
		policy("EducationPolicies").forEach(p => frag.append(p));
		return frag;
	}

	function srp() {
		const frag = new DocumentFragment();
		if (V.policies.retirement.customAgePolicy === 0 && V.policies.retirement.physicalAgePolicy === 0) {
			const div = App.UI.DOM.appendNewElement("div", frag, `Default Retirement Age:`, "bold");
			div.append(` in the absence of a defined slave retirement age, slaves will not remain sex slaves after reaching age ${V.retirementAge}`);
			App.UI.DOM.appendNewElement("div", frag, `This is a current content and mechanical limitation in FC ${V.ver}, not a universal Free Cities rule.`, ["indent", "note"]);
		}
		policy("RetirementPolicies").forEach(p => frag.append(p));
		App.UI.DOM.appendNewElement("h1", frag, `Menials`);
		policy("MenialRetirementPolicies").forEach(p => frag.append(p));
		return frag;
	}
	function fs() {
		const frag = new DocumentFragment();
		policy("FutureSocietiesTab").forEach(p => frag.append(p));
		for (const FS in App.Data.FutureSociety.records) {
			const policies = policy(FS);
			if (policies.length > 0) {
				App.UI.DOM.appendNewElement("h2", frag, App.Data.FutureSociety.records[FS].adj);
				policies.forEach(p => frag.append(p));
			}
		}
		return frag;
	}
};


code:
/**
 * @param {App.Entity.SlaveState} slave
 */
App.UI.prostheticsConfig = function(slave) {
	/* get all prosthetics that are ready for this slave */
	if (V.adjustProstheticsCompleted > 0) {
		V.adjustProsthetics = V.adjustProsthetics.filter(function(p) {
			if (p.workLeft <= 0 && p.slaveID === slave.ID) {
				addProsthetic(slave, p.id);
				V.adjustProstheticsCompleted--;
				return false;
			}
			return true;
		});
	}

	const {He, his, him} = getPronouns(slave);

	const fragment = document.createDocumentFragment();

	App.UI.DOM.appendNewElement("h1", fragment, "Prosthetic Configuration");
	const introP = App.UI.DOM.makeElement("p", "This room is lined with shelves and cabinets; it could be easily mistaken for a storage room if it were not for the examination table in its center.", "scene-intro");

	if (hasBothLegs(slave)) {
		App.UI.DOM.appendNewElement("div", introP, `${slave.slaveName} is obediently waiting for your instructions.`);
	} else {
		App.UI.DOM.appendNewElement("div", introP, `${slave.slaveName} is lying on the table, waiting for your instructions.`);
	}

	fragment.append(introP);

	fragment.append(eyes());
	fragment.append(ears());
	fragment.append(voice());
	fragment.append(limbs());
	fragment.append(tail());
	fragment.append(appendages());

	fragment.append(buyScreen());

	return fragment;

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

		if (hasAnyCyberneticEyes(slave)) {
			App.UI.DOM.appendNewElement("h2", f, "Eyes");
			const p = document.createElement("p");

			p.append(`${He} has ${hasBothCyberneticEyes(slave) ? "ocular implants" : "an ocular implant"} installed. You can change ${hasBothCyberneticEyes(slave) ? "their" : "its"} settings:`);

			const eyeContainer = document.createElement("div");
			eyeContainer.classList.add("eyeContainer", "choices");

			let on = 0;
			let blur = 0;
			let off = 0;
			if (getLeftEyeType(slave) === 3) {
				App.UI.DOM.appendNewElement("div", eyeContainer, "Left:");
				let div = document.createElement("div");
				if (getLeftEyeVision(slave) !== 2) {
					on++;
					div.append(App.UI.DOM.passageLink("[ON]", "Prosthetics Configuration", () => eyeSurgery(slave, "left", "fix")));
				} else {
					div.append("[ON]");
				}
				eyeContainer.append(div);
				div = document.createElement("div");

				if (getLeftEyeVision(slave) !== 1) {
					blur++;
					div.append(App.UI.DOM.passageLink("[BLUR]", "Prosthetics Configuration", () => eyeSurgery(slave, "left", "blur")));
				} else {
					div.append("[BLUR]");
				}
				eyeContainer.append(div);
				div = document.createElement("div");
				if (getLeftEyeVision(slave) !== 0) {
					off++;
					div.append(App.UI.DOM.passageLink("[OFF]", "Prosthetics Configuration", () => eyeSurgery(slave, "left", "blind")));
				} else {
					div.append("[OFF]");
				}
				eyeContainer.append(div);
			}
			if (getRightEyeType(slave) === 3) {
				App.UI.DOM.appendNewElement("div", eyeContainer, "Right:");
				let div = document.createElement("div");
				if (getRightEyeVision(slave) !== 2) {
					on++;
					div.append(App.UI.DOM.passageLink("[ON]", "Prosthetics Configuration", () => eyeSurgery(slave, "right", "fix")));
				} else {
					div.append("[ON]");
				}
				eyeContainer.append(div);
				div = document.createElement("div");

				if (getRightEyeVision(slave) !== 1) {
					blur++;
					div.append(App.UI.DOM.passageLink("[BLUR]", "Prosthetics Configuration", () => eyeSurgery(slave, "right", "blur")));
				} else {
					div.append("[BLUR]");
				}
				eyeContainer.append(div);
				div = document.createElement("div");
				if (getRightEyeVision(slave) !== 0) {
					off++;
					div.append(App.UI.DOM.passageLink("[OFF]", "Prosthetics Configuration", () => eyeSurgery(slave, "right", "blind")));
				} else {
					div.append("[OFF]");
				}
				eyeContainer.append(div);
			}
			if (hasBothCyberneticEyes(slave)) {
				App.UI.DOM.appendNewElement("div", eyeContainer, "Both:");
				let div = document.createElement("div");
				if (on > 0) {
					div.append(App.UI.DOM.passageLink("[ON]", "Prosthetics Configuration", () => eyeSurgery(slave, "both", "fix")));
				} else {
					div.append("[ON]");
				}
				eyeContainer.append(div);
				div = document.createElement("div");

				if (blur > 0) {
					div.append(App.UI.DOM.passageLink("[BLUR]", "Prosthetics Configuration", () => eyeSurgery(slave, "both", "blur")));
				} else {
					div.append("[BLUR]");
				}
				eyeContainer.append(div);
				div = document.createElement("div");
				if (off > 0) {
					div.append(App.UI.DOM.passageLink("[OFF]", "Prosthetics Configuration", () => eyeSurgery(slave, "both", "blind")));
				} else {
					div.append("[OFF]");
				}
				eyeContainer.append(div);
			}
			p.append(eyeContainer);

			const colorDiv = document.createElement("div");
			colorDiv.append(`${He} has ${App.Desc.eyesColor(slave)}. To change ${his} eye color visit the `, App.UI.DOM.passageLink("auto salon", "Salon"), ".");
			p.append(colorDiv);

			f.append(p);
		}
		return f;
	}

	/**
	 * @returns {DocumentFragment}
	 */
	function ears() {
		const f = document.createDocumentFragment();
		if (slave.earImplant === 1) {
			App.UI.DOM.appendNewElement("h2", f, "Ears");

			const p = document.createElement("p");
			p.append(`${He} has cochlear implants installed.`);
			if (slave.hears === 0) {
				p.append("They are operating normally.");
			} else if (slave.hears === -1) {
				p.append(`They are set to muffle ${his} hearing.`);
			} else {
				p.append("They are turned off.");
			}

			const links = [];
			if (slave.hears !== 0) {
				links.push(App.UI.DOM.passageLink("Restore hearing", "Prosthetics Configuration", () => {
					slave.hears = 0;
					V.prostheticsConfig = "hearing";
				}));
			}
			if (slave.hears !== -1) {
				links.push(App.UI.DOM.passageLink("Muffle hearing", "Prosthetics Configuration", () => {
					slave.hears = -1;
					V.prostheticsConfig = "hearing";
				}));
			}
			if (slave.hears !== -2) {
				links.push(App.UI.DOM.passageLink("Disable", "Prosthetics Configuration", () => {
					slave.hears = -2;
					V.prostheticsConfig = "hearing";
				}));
			}
			App.UI.DOM.appendNewElement("div", p, linkStrip(links), "choices");

			f.append(p);
		}
		return f;
	}

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

		if (slave.electrolarynx === 1) {
			App.UI.DOM.appendNewElement("h2", f, "Voice");
			const p = document.createElement("p");
			p.append(`${He} has an electrolarynx installed. `);
			if (slave.voice === 0) {
				p.append("It is turned off.");
			} else if (slave.voice === 1) {
				p.append(`It is set to its "deep voice" setting.`);
			} else if (slave.voice === 2) {
				p.append(`It is set to its "normal voice" setting.`);
			} else if (slave.voice === 3) {
				p.append(`It is set to its "high voice" setting.`);
			}

			const links = [];
			if (slave.voice !== 0) {
				links.push(App.UI.DOM.passageLink("Disable", "Prosthetics Configuration", () => {
					slave.voice = 0;
					V.prostheticsConfig = "voice";
				}));
			}
			if (slave.voice !== 1) {
				links.push(App.UI.DOM.passageLink("Deep voice setting", "Prosthetics Configuration", () => {
					slave.voice = 1;
					V.prostheticsConfig = "voice";
				}));
			}
			if (slave.voice !== 2) {
				links.push(App.UI.DOM.passageLink("Standard voice setting", "Prosthetics Configuration", () => {
					slave.voice = 2;
					V.prostheticsConfig = "voice";
				}));
			}
			if (slave.voice !== 3) {
				links.push(App.UI.DOM.passageLink("High voice setting", "Prosthetics Configuration", () => {
					slave.voice = 3;
					V.prostheticsConfig = "voice";
				}));
			}
			App.UI.DOM.appendNewElement("div", p, linkStrip(links), "choices");

			f.append(p);
		}
		return f;
	}

	/**
	 * @returns {DocumentFragment}
	 */
	function limbs() {
		const f = document.createDocumentFragment();
		App.UI.DOM.appendNewElement("h2", f, "Limbs");

		App.UI.DOM.appendNewElement("p", f, App.Medicine.Limbs.selector(slave, App.Medicine.Limbs.currentLimbs(slave)));

		return f;
	}

	/**
	 * @returns {DocumentFragment}
	 */
	function tail() {
		const f = document.createDocumentFragment();
		App.UI.DOM.appendNewElement("h2", f, "Tail");

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

		if (slave.PTail === 1) {
			App.UI.DOM.appendNewElement("div", p, `${He} has a neural tail interface installed. You can assign and adjust ${his} tail here.`);

			if (slave.tail === "none") {
				const div = App.UI.DOM.appendNewElement("div", p);
				if (isProstheticAvailable(slave, "modT")) {
					App.Events.addNode(div, [
						"Attach a modular tail designed to look like the tail of a:",
						tailSelect()
					]);
				}
				const links = [];
				if (isProstheticAvailable(slave, "combatT") && slave.fuckdoll === 0) {
					links.push(App.UI.DOM.passageLink("Attach Combat Tail", "Prosthetics Configuration", () => {
						V.prostheticsConfig = "attachTail";
						slave.tail = "combat";
						slave.tailColor = "jet black";
					}));
				}
				if (isProstheticAvailable(slave, "combatT2") && slave.fuckdoll === 0) {
					links.push(App.UI.DOM.passageLink("Attach Combat Tail Scorpio", "Prosthetics Configuration", () => {
						V.prostheticsConfig = "attachTail";
						slave.tail = "stinger";
						slave.tailColor = "purple";
					}));
				}
				if (isProstheticAvailable(slave, "sexT")) {
					links.push(App.UI.DOM.passageLink("Attach Pleasure Tail", "Prosthetics Configuration", () => {
						V.prostheticsConfig = "attachTail";
						slave.tail = "sex";
						slave.tailColor = "pink";
					}));
				}
				App.UI.DOM.appendNewElement("div", div, linkStrip(links), "choices");
			} else {
				App.UI.DOM.appendNewElement("div", p, `${He} currently has a tail attached, if you wish to change it you first need to detach it.`);
				App.UI.DOM.appendNewElement("div", p,
					App.UI.DOM.passageLink("Detach", "Prosthetics Configuration", () => {
						V.prostheticsConfig = "detachTail";
						V.nextButton = "Continue";
						V.nextLink = "Prosthetics Configuration";
					}), "choices");

				if (slave.tail === "mod") {
					App.Events.addNode(p, [
						`${He} currently has a modular tail, styled to look like ${App.Data.modTails.get(slave.tailShape).desc}. Modify ${his} tail's appearance:`,
						tailSelect()
					], "div");
				}
			}
		} else {
			App.UI.DOM.appendNewElement("span", p, `${He} does not have a neural tail interface installed so you cannot attach a tail.`, "note");
		}
		f.append(p);
		return f;

		function tailSelect() {
			const sortedTails = Array.from(App.Data.modTails.keys())
				.sort((a, b) => a > b ? 1 : -1)
				.map(tail => {
					return {key: tail, name: App.Data.modTails.get(tail).animal};
				});
			return App.UI.DOM.makeSelect(sortedTails, slave.tailShape, tail => {
				V.prostheticsConfig = "attachTail";
				slave.tail = "mod";
				slave.tailShape = /** @type {FC.TailShape} */ (tail);
				slave.tailColor = (slave.tailColor === "none") ? slave.hColor : slave.tailColor; // if color not set yet, match hair.
				cashX(forceNeg(V.modCost), "slaveMod", slave);
				App.UI.reload();
			});
		}
	}

	function appendages() {
		const f = document.createDocumentFragment();
		App.UI.DOM.appendNewElement("h2", f, "Back");

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

		if (slave.PBack === 1) {
			App.UI.DOM.appendNewElement("div", p, `${He} has a neural back interface installed. You can assign and adjust ${his} back appendages here.`);

			if (slave.appendages !== "none") {
				App.UI.DOM.appendNewElement("div", p, `${He} currently has back appendages attached, if you wish to change them you first need to detach them.`);
				App.UI.DOM.appendNewElement("div", p,
					App.UI.DOM.passageLink("Detach", "Prosthetics Configuration", () => {
						V.prostheticsConfig = "detachAppendages";
						V.nextButton = "Continue";
						V.nextLink = "Prosthetics Configuration";
					}), "choices");
			} else {
				if (isProstheticAvailable(slave, "modW")) {
					App.UI.DOM.appendNewElement("div", p, "Attach a pair of modular wings designed to look like a:");

					const links = [];
					App.Data.modWings.forEach((value, key) => {
						links.push(App.UI.DOM.passageLink(`${value.animal}'s Wings`, "Prosthetics Configuration",
							() => {
								V.prostheticsConfig = "attachAppendages";
								slave.appendages = "mod";
								slave.wingsShape = key;
								slave.appendagesColor = "white";
							}
						));
					});
					App.UI.DOM.appendNewElement("div", p, linkStrip(links), "choices");
				}
				const links = [];
				if (isProstheticAvailable(slave, "combatW") && slave.fuckdoll === 0) {
					links.push(App.UI.DOM.passageLink("Attach a pair of Combat Wings", "Prosthetics Configuration", () => {
						V.prostheticsConfig = "attachAppendages";
						slave.appendages = "falcon";
						slave.appendagesColor = "jet black";
					}));
				}
				if (isProstheticAvailable(slave, "combatA1") && slave.fuckdoll === 0) {
					links.push(App.UI.DOM.passageLink(`Attach the "Arachnid" Combat Appendages`, "Prosthetics Configuration", () => {
						V.prostheticsConfig = "attachAppendages";
						slave.appendages = "arachnid";
						slave.appendagesColor = "jet black";
					}));
				}
				if (isProstheticAvailable(slave, "combatA2") && slave.fuckdoll === 0) {
					links.push(App.UI.DOM.passageLink(`Attach the "Kraken" Combat Appendages`, "Prosthetics Configuration", () => {
						V.prostheticsConfig = "attachAppendages";
						slave.appendages = "kraken";
						slave.appendagesColor = "jet black";
					}));
				}
				if (isProstheticAvailable(slave, "flightW") && slave.fuckdoll === 0) {
					links.push(App.UI.DOM.passageLink("Attach a pair of Wings capable of flight", "Prosthetics Configuration", () => {
						V.prostheticsConfig = "attachAppendages";
						slave.appendages = "flight";
						slave.appendagesColor = "silver";
					}));
				}
				if (isProstheticAvailable(slave, "sexA")) {
					links.push(App.UI.DOM.passageLink("Attach a set of Pleasure Appendages", "Prosthetics Configuration", () => {
						V.prostheticsConfig = "attachAppendages";
						slave.appendages = "sex";
						slave.appendagesColor = "pink";
					}));
				}
				App.UI.DOM.appendNewElement("div", p, linkStrip(links), "choices");
			}

			if (slave.appendages === "mod") {
				App.UI.DOM.appendNewElement("div", p,
					`${He} currently has a pair of modular wings, styled to look like ${App.Data.modWings.get(slave.wingsShape).desc}. Modify the appearance of ${his} wings:`);

				const links = [];
				App.Data.modWings.forEach((value, key) => {
					links.push(App.UI.DOM.passageLink(value.animal, "Prosthetics Configuration", () => {
						slave.wingsShape = key;
						cashX(forceNeg(V.modCost), "slaveMod", slave);
					}));
				});
				App.UI.DOM.appendNewElement("div", p, linkStrip(links), "choices");
			}
		} else {
			App.UI.DOM.appendNewElement("span", p, `${He} does not have a neural back interface installed so you cannot attach appendages.`, "note");
		}
		f.append(p);
		return f;
	}

	/**
	 * @returns {DocumentFragment}
	 */
	function buyScreen() {
		const f = document.createDocumentFragment();
		App.UI.DOM.appendNewElement("h2", f, "Prosthetics");

		const p = document.createElement("p");
		App.UI.DOM.appendNewElement("span", p, `Fit prosthetics to ${him}:`, "note");

		const gridDiv = document.createElement("div");
		gridDiv.classList.add("buy-prosthetics");

		gridDiv.append(document.createElement("div"));

		App.UI.DOM.appendNewElement("div", gridDiv, "Buy and fit");

		let tooltip;
		if (V.researchLab.level > 0) {
			tooltip = `Depending on lab speed, it might be faster than fitting an existing prosthetic, but should almost always be faster than first building and then fitting it to ${him}.`;
		} else {
			tooltip = "With a lab you could both increase speed and decrease cost.";
		}
		App.UI.DOM.appendNewElement("div", gridDiv, App.UI.DOM.spanWithTooltip("Construct in lab", tooltip));

		tooltip = "";
		if (V.researchLab.speed >= 300) { /* max speed */
			tooltip = "Your lab is so fast that fitting prosthetics to your slave can be done instantly, though you will sacrifice some efficiency.";
		} else if (V.researchLab.level > 0) {
			tooltip = "Your lab is not fast enough to fit prosthetics instantly.";
		}

		const fastDiv = document.createElement("div");
		if (tooltip !== "") {
			fastDiv.append(App.UI.DOM.spanWithTooltip("Fast assembly", tooltip));
			fastDiv.style.textAlign = "right";
		}
		gridDiv.append(fastDiv);

		for (const prostheticID of App.Data.prostheticIDs) {
			addBuyRow(prostheticID, gridDiv);
		}
		p.append(gridDiv);
		f.append(p);

		return f;
	}

	/**
	 * @param {string} prosthetic
	 * @param {HTMLDivElement} container
	 */
	function addBuyRow(prosthetic, container) {
		App.UI.DOM.appendNewElement("div", container, capFirstChar(App.Data.prosthetics[prosthetic].name));

		if (V.adjustProsthetics.findIndex(function(p) { return p.id === prosthetic && p.slaveID === slave.ID; }) !== -1 ||
			V.researchLab.tasks.findIndex(function(p) { return p.type === "craftFit" && p.id === prosthetic && p.slaveID === slave.ID; }) !== -1) {
			App.UI.DOM.appendNewElement("div", container, `Currently being fitted to ${him}.`, ["full", "note"]);
		} else if (App.Data.prosthetics[prosthetic].level > V.prostheticsUpgrade) {
			App.UI.DOM.appendNewElement("div", container, `Better contracts are needed to buy these.`, ["full", "note"]);
		} else if (isProstheticAvailable(slave, prosthetic)) {
			App.UI.DOM.appendNewElement("div", container, `Completed.`, ["full", "note"]);
		} else {
			if (V.prosthetics[prosthetic].amount > 0) {
				App.UI.DOM.appendNewElement("div", container,
					App.UI.DOM.passageLink("From storage", "Prosthetics Configuration", () => {
						V.adjustProsthetics.push({
							id: prosthetic,
							workLeft: App.Data.prosthetics[prosthetic].adjust,
							slaveID: slave.ID
						});
						V.prosthetics[prosthetic].amount -= 1;
					}));
			} else {
				App.UI.DOM.appendNewElement("div", container,
					App.UI.DOM.passageLink(cashFormat(App.Data.prosthetics[prosthetic].costs), "Prosthetics Configuration", () => {
						V.adjustProsthetics.push({
							id: prosthetic,
							workLeft: App.Data.prosthetics[prosthetic].adjust,
							slaveID: slave.ID
						});
						cashX(forceNeg(App.Data.prosthetics[prosthetic].costs), "slaveMod", slave);
					}));
			}

			if (V.prosthetics[prosthetic].research > 0) {
				const craftDiv = document.createElement("div");
				craftDiv.style.textAlign = "center";
				if (V.researchLab.level > 0 && V.prosthetics[prosthetic].research > 0) {
					craftDiv.append(App.UI.DOM.passageLink("Construct", "Prosthetics Configuration", () => {
						V.researchLab.tasks.push({
							type: "craftFit",
							id: prosthetic,
							/* 1.5: longer than adjust, but faster than adjust+craft. */
							workLeft: (App.Data.prosthetics[prosthetic].adjust + App.Data.prosthetics[prosthetic].craft) / 1.5,
							slaveID: slave.ID
						});
					}));
				}
				container.append(craftDiv);

				const instantDiv = document.createElement("div");
				instantDiv.style.textAlign = "right";
				if (V.researchLab.speed >= 300 && V.prosthetics[prosthetic].research > 0) { /* max speed */
					if (V.prosthetics[prosthetic].amount > 0) {
						const cost = App.Data.prosthetics[prosthetic].adjust * 50;
						instantDiv.append(App.UI.DOM.passageLink(`From storage: ${cashFormat(cost)}`,
							"Prosthetics Configuration", () => {
								cashX(-cost, "slaveMod", slave);
								addProsthetic(slave, prosthetic);
							}));
					} else {
						const cost = App.Data.prosthetics[prosthetic].costs * 2 + App.Data.prosthetics[prosthetic].adjust * 50;
						instantDiv.append(App.UI.DOM.passageLink(cashFormat(cost), "Prosthetics Configuration", () => {
							cashX(-cost, "slaveMod", slave);
							addProsthetic(slave, prosthetic);
						}));
					}
				}
				container.append(instantDiv);
			} else if (V.researchLab.level > 0) {
				App.UI.DOM.appendNewElement("div", container, "Not researched.", ["research", "note"]);
			} else {
				App.UI.DOM.appendNewElement("div", container, "You need to construct a lab first.", ["research", "note"]);
			}
		}
	}

	/**
	 * @param {Array<Element>} links
	 * @returns {DocumentFragment}
	 */
	function linkStrip(links) {
		const f = document.createDocumentFragment();
		if (links.length > 0) {
			f.append(links[0]);
			for (let i = 1; i < links.length; i++) {
				f.append(" | ", links[i]);
			}
		}
		return f;
	}
};

/**
 * @param {App.Entity.SlaveState} slave
 */
App.UI.prostheticsConfigPassage = function(slave) {
	const node = new DocumentFragment();
	const r = [];
	const {
		He,
		he, his, him, himself
	} = getPronouns(slave);

	switch (V.prostheticsConfig) {
		case "main":
			V.nextButton = "Confirm changes";
			V.nextLink = "Slave Interact";
			node.append(App.UI.prostheticsConfig(slave));
			break;
		case "hearing":
			V.prostheticsConfig = "main";
			V.nextButton = "Continue";
			V.nextLink = "Prosthetics Configuration";
			if (slave.hears === 0) {
				r.push(`${He} tilts ${his} head as ${his} hearing returns.`);
			} else if (slave.hears === -1) {
				r.push(`${He} shakes ${his} head as ${his} hearing becomes muffled.`);
			} else {
				r.push(`${He} has a panicked expression when ${his} hearing is suddenly silenced.`);
			}
			break;
		case "voice":
			V.prostheticsConfig = "main";
			V.nextButton = "Continue";
			V.nextLink = "Prosthetics Configuration";
			if (slave.voice === 0) {
				r.push(`${He} tries testing out ${his} new voice, only to discover ${he}'s been made mute.`);
			} else if (slave.voice === 1) {
				r.push(`${He} tests out the`);
				if (canHear(slave)) {
					r.push(`sound`);
				} else {
					r.push(`feeling`);
				}
				r.push(`of ${his} new, deep voice.`);
			} else if (slave.voice === 2) {
				r.push(`${He} tests out the`);
				if (canHear(slave)) {
					r.push(`sound`);
				} else {
					r.push(`feeling`);
				}
				r.push(`of ${his} new, normal voice.`);
			} else if (slave.voice === 3) {
				r.push(`${He} tests out the`);
				if (canHear(slave)) {
					r.push(`sound`);
				} else {
					r.push(`feeling`);
				}
				r.push(`of ${his} new, high voice.`);
			}
			break;
		case "detachTail":
			V.prostheticsConfig = "main";
			V.nextButton = "Continue";
			V.nextLink = "Prosthetics Configuration";
			slave.tail = "none";
			slave.tailShape = "none";
			slave.tailColor = "none";
			r.push(`You send the release signal and the mechanical lock disengages allowing the artificial tail to pop right off.`);
			break;
		case "attachTail":
			V.prostheticsConfig = "main";
			V.nextButton = "Continue";
			V.nextLink = "Prosthetics Configuration";
			App.UI.DOM.appendNewElement("div", node, `Attaching ${his} tail is a simple procedure, you simply push the connector into a socket, right where ${his} tailbone ends, until the lock engages.`);
			r.push(`When you are done, ${he} looks back and`);
			if (["cat", "leopard", "tiger", "jaguar", "lion"].includes(slave.tailShape)) {
				r.push(`sways ${his} tail side to side enigmatically.`);
			} else if (["dog", "wolf", "jackal"].includes(slave.tailShape)) {
				r.push(`wags ${his} tail side to side energetically.`);
			} else if (slave.tailShape === "fox") {
				r.push(`slowly sways ${his} tail feeling the soft fur brush against ${his} skin.`);
			} else if (slave.tailShape === "kitsune") {
				r.push(`slowly sways ${his} tails luxuriating in the incredibly soft, fluffy fur brushing against ${his} skin.`);
			} else if (slave.tailShape === "raccoon") {
				r.push(`admires ${his} long, thick fluffy tail.`);
			} else if (slave.tailShape === "rabbit") {
				r.push(`wiggles ${his} little tail a bit.`);
			} else if (slave.tailShape === "squirrel") {
				r.push(`admires ${his} the size of ${his} fluffy tail.`);
			} else if (slave.tailShape === "horse") {
				r.push(`sways ${his} tail back and forth.`);
			} else if (slave.tailShape === "bird") {
				r.push(`ruffles ${his} tail feathers.`);
			} else if (slave.tailShape === "phoenix") {
				r.push(`ruffles ${his} tail feathers.`);
			} else if (slave.tailShape === "peacock") {
				r.push(`ruffles ${his} tail feathers.`);
			} else if (slave.tailShape === "raven") {
				r.push(`ruffles ${his} tail feathers.`);
			} else if (slave.tailShape === "swan") {
				r.push(`wiggles ${his} small bundle of tail feathers.`);
			} else if (slave.tailShape === "sheep") {
				r.push(`wiggles ${his} short, wooly tail.`);
			} else if (slave.tailShape === "cow") {
				r.push(`swats ${himself} playfully.`);
			} else if (slave.tailShape === "gazelle") {
				r.push(`wiggles ${his} little tail a bit.`);
			} else if (slave.tailShape === "deer") {
				r.push(`wiggles ${his} little tail a bit.`);
			} else if (slave.tailShape === "succubus") {
				r.push(`waves ${his} tail at you, showing considerable dexterity with it.`);
			} else if (slave.tailShape === "dragon") {
				r.push(`strokes the scales of ${his} tail.`);
			} else if (slave.tail === "combat") {
				r.push(`experimentally whips the long tail side to side then takes aim at a prepared fruit, lashes out with blinding speed and smiles as it explodes into chunks.`);
			} else if (slave.tail === "stinger") {
				r.push(`experimentally whips the long, segmented tail side to side and tries lashing out with it. ${He} then aims its stinger towards a prepared fruit, fires a projectile and smiles as it accurately strikes ${his} target.`);
			} else if (slave.tail === "sex") {
				r.push(`accidentally engages the vibrating and lube functions, startling ${him} and making quite a mess.`);
			} else {
				r.push(`admires ${his} new tail.`);
			}
			break;
		case "detachAppendages":
			V.prostheticsConfig = "main";
			V.nextButton = "Continue";
			V.nextLink = "Prosthetics Configuration";
			slave.appendages = "none";
			slave.wingsShape = "none";
			slave.appendagesColor = "none";
			r.push(`You send the release signal and the mechanical locks disengage allowing the dorsal appendages to be removed.`);
			break;
		case "attachAppendages":
			V.prostheticsConfig = "main";
			V.nextButton = "Continue";
			V.nextLink = "Prosthetics Configuration";
			App.UI.DOM.appendNewElement("div", node, `Attaching ${his} dorsal appendages is a simple procedure, you simply push the connectors into their socket, on either side of the vertebrae located between ${his} shoulder blades, until they lock in place.`);
			r.push(`When you are done, ${he} looks back and`);
			if (slave.wingsShape === "angel") {
				r.push(`tries flapping ${his} wings.`);
			} else if (slave.wingsShape === "seraph") {
				r.push(`tries flapping ${his} six wings.`);
			} else if (slave.wingsShape === "demon") {
				r.push(`tries flapping ${his} wings.`);
			} else if (slave.wingsShape === "dragon") {
				r.push(`tries beating ${his} wings.`);
			} else if (slave.wingsShape === "phoenix") {
				r.push(`tries flapping ${his} wings.`);
			} else if (slave.wingsShape === "bird") {
				r.push(`tries flapping ${his} wings.`);
			} else if (slave.wingsShape === "fairy") {
				r.push(`flutters ${his} wings.`);
			} else if (slave.wingsShape === "butterfly") {
				r.push(`flutters ${his} wings.`);
			} else if (slave.wingsShape === "moth") {
				r.push(`flutters ${his} wings.`);
			} else if (slave.wingsShape === "insect") {
				r.push(`flutters ${his} wings.`);
			} else if (slave.wingsShape === "evil") {
				r.push(`tries beating ${his} wings.`);
			} else if (slave.appendages === "falcon") {
				r.push(`admires ${his} sleek, sharp-edged wings.`);
			} else if (slave.appendages === "arachnid") {
				r.push(`tries extending and contracting ${his} menacing spider legs. Eventually ${he} manages to lift ${himself} off the ground and moves around using the legs.`);
			} else if (slave.appendages === "kraken") {
				r.push(`tries extending and contracting ${his} new tentacles. Eventually ${he} manages to lift ${himself} off the ground and moves around using the tentacles.`);
			} else if (slave.appendages === "sex") {
				r.push(`tries examining the many different tips of ${his} new pleasure appendages before accidentally engaging their vibrating and lube functions, startling ${him} and making a huge mess.`);
			} else if (slave.appendages === "flight") {
				r.push(`tries engaging ${his} wing thrusters. Feeling ${his} feet leave the ground, ${he} hurriedly disengages the thrusters before flying too high.`);
			} else {
				r.push(`admires ${his} new dorsal appendages.`);
			}
			break;
	}
	App.Utils.scheduleSidebarRefresh();
	if (r.length > 0) {
		App.Events.addParagraph(node, r);
	}
	return node;
};


code:
/**
 * @returns {string}
 */

globalThis.getProstheticsStockpile = function() {
	return `<div>Prosthetics interfaces: ${num(V.prosthetics.interfaceP1.amount + V.prosthetics.interfaceP2.amount + V.prosthetics.interfaceP3.amount)}</div>` +
		`<div class="choices">Basic: ${V.prosthetics.interfaceP1.amount}</div>` +
		`<div class="choices">Advanced: ${V.prosthetics.interfaceP2.amount}</div>` +
		`<div class="choices">Quadruped: ${V.prosthetics.interfaceP3.amount}</div>` +
		`
Limbs: ${num(V.prosthetics.basicL.amount + V.prosthetics.sexL.amount + V.prosthetics.beautyL.amount +
			V.prosthetics.combatL.amount + V.prosthetics.cyberneticL.amount + V.prosthetics.felidaeL.amount + V.prosthetics.canidaeL.amount + V.prosthetics.felidaeCL.amount + V.prosthetics.canidaeCL.amount)}
` +
		`<div class="choices">Basic: ${V.prosthetics.basicL.amount}</div>` +
		`<div class="choices">Sex: ${V.prosthetics.sexL.amount}</div>` +
		`<div class="choices">Beauty: ${V.prosthetics.beautyL.amount}</div>` +
		`<div class="choices">Combat: ${V.prosthetics.combatL.amount}</div>` +
		`<div class="choices">Cybernetic: ${V.prosthetics.cyberneticL.amount}</div>` +
		`<div class="choices">Feline: ${V.prosthetics.felidaeL.amount}</div>` +
		`<div class="choices">Canine: ${V.prosthetics.canidaeL.amount}</div>` +
		`<div class="choices">Feline Combat: ${V.prosthetics.felidaeCL.amount}</div>` +
		`<div class="choices">Canine Combat: ${V.prosthetics.canidaeCL.amount}</div>` +
		`<div>Implants: ${num(V.prosthetics.ocular.amount + V.prosthetics.cochlear.amount + V.prosthetics.electrolarynx.amount)}</div>` +
		`<div class="choices">Ocular: ${V.prosthetics.ocular.amount}</div>` +
		`<div class="choices">Cochlear: ${V.prosthetics.cochlear.amount}</div>` +
		/* `<div class="choices">Erectile: ${V.prosthetics.erectile.amount}</div>` + */
		`<div class="choices">Electrolarynx: ${V.prosthetics.electrolarynx.amount}</div>` +
		`<div>Tail interface: ${V.prosthetics.interfaceTail.amount}</div>` +
		`<div>Tails: ${num(V.prosthetics.modT.amount + V.prosthetics.sexT.amount + V.prosthetics.combatT.amount + V.prosthetics.combatT2.amount)}</div>` +
		`<div class="choices">Modular: ${V.prosthetics.modT.amount}</div>` +
		`<div class="choices">Pleasure: ${V.prosthetics.sexT.amount}</div>` +
		`<div class="choices">Combat: ${V.prosthetics.combatT.amount}</div>` +
		`<div class="choices">Combat Tail, "Stinger": ${V.prosthetics.combatT2.amount}</div>` +
		`<div>Back interface: ${V.prosthetics.interfaceBack.amount}</div>` +
		`
Appendages: ${num(V.prosthetics.modW.amount + V.prosthetics.flightW.amount + V.prosthetics.sexA.amount + V.prosthetics.combatW.amount + V.prosthetics.combatA1.amount + V.prosthetics.combatA2.amount)}
` +
		`<div class="choices">Modular Wings: ${V.prosthetics.modW.amount}</div>` +
		`<div class="choices">Aerial Wings: ${V.prosthetics.flightW.amount}</div>` +
		`<div class="choices">Pleasure Appendages: ${V.prosthetics.sexA.amount}</div>` +
		`<div class="choices">Combat Wings, "Falcon": ${V.prosthetics.combatW.amount}</div>` +
		`<div class="choices">Combat Appendages, "Arachnid": ${V.prosthetics.combatA1.amount}</div>` +
		`<div class="choices">Combat Appendages, "Kraken": ${V.prosthetics.combatA2.amount}</div>`;
};


code:
App.UI.prostheticLab = function() {
	const node = new DocumentFragment();

	let r = [];
	V.researchLab.speed = ((V.researchLab.hired * 3) + V.researchLab.menials) * V.researchLab.aiModule;
	const staff = V.researchLab.hired + V.researchLab.menials;
	let linkArray = [];

	App.UI.DOM.appendNewElement("h1", node, "The Prosthetics Lab");

	if (V.adjustProsthetics.length > V.adjustProstheticsCompleted) {
		App.UI.DOM.appendNewElement("h2", node, "External contract workers");
		App.UI.DOM.appendNewElement("div", node, `Currently adjusting these prosthetics:`);
		for (const p of V.adjustProsthetics) {
			if (p.workLeft > 0) {
				App.UI.DOM.appendNewElement("div", node,
					`${capFirstChar(App.Data.prosthetics[p.id].name)} for ${SlaveFullName(V.slaves[V.slaveIndices[p.slaveID]])}`,
					"indent"
				);
			}
		}
	}

	if (V.researchLab.level > 0) {
		App.UI.DOM.appendNewElement("h2", node, "Research Department");
		App.UI.DOM.appendNewElement("div", node, "You switch one of the screens in your office to the feed from your research facility.", "note");

		App.UI.DOM.appendNewElement("h3", node, "Personnel");

		if (V.researchLab.aiModule === 1) {
			App.UI.DOM.appendNewElement("div", node, App.UI.DOM.link(
				"Buy and install research module",
				() => {
					cashX(forceNeg(35000 * V.upgradeMultiplierArcology), "capEx");
					V.researchLab.aiModule = 2;
					App.UI.reload();
				},
				[], '',
				`This module enables your personal assistant to assist staff and direct menials assigned to your research facility, increasing efficiency. It costs ${cashFormat(35000 * V.upgradeMultiplierArcology)}`
			));
		}
		if ((staff) === 0) {
			App.UI.DOM.appendNewElement("div", node, "Facility is currently unstaffed.", "note");
		} else {
			r.push(`You have`);
			if (V.researchLab.hired === 1) {
				r.push(`one scientist`);
			} else if (V.researchLab.hired > 1) {
				r.push(`${V.researchLab.hired} scientists`);
			}
			if (V.researchLab.hired > 0 && V.researchLab.menials > 0) {
				r.push(`and`);
			}
			if (V.researchLab.menials === 1) {
				r.push(`one menial slave`);
			} else if (V.researchLab.menials > 1) {
				r.push(`${V.researchLab.menials} menials`);
			}
			r.push(`working in your lab.`);
			const {
				heA
			} = getPronouns(assistant.pronouns().main).appendSuffix("A");
			if (V.researchLab.aiModule > 1) {
				r.push(`Occasionally you hear the voice of your assistant as ${heA} helps direct and organize work.`);
			}
			App.Events.addNode(node, r, "div", "note");
			r = [];
		}

		let div = App.UI.DOM.appendNewElement("div", node, null, "indent");
		div.append(`Currently, this facility can employ ${V.researchLab.maxSpace} people.`);
		if (V.researchLab.maxSpace >= 5) { // Exists
			if (V.researchLab.maxSpace < 50) { // Can upgrade
				const maxSpace = [5, 10, 20, 30, 40, 50];
				const upgrade = maxSpace[maxSpace.indexOf(V.researchLab.maxSpace) + 1];
				App.UI.DOM.appendNewElement("div", div, App.UI.DOM.link(
					"Expand facility",
					() => {
						cashX(forceNeg(Math.trunc(500 * upgrade * V.upgradeMultiplierArcology)), "capEx");
						V.researchLab.maxSpace = upgrade;
						App.UI.reload();
					}
				));
			} else {
				App.Events.addNode(div, r, "div", "note");
				r = [];
			}
		}

		//

		if (staff < V.researchLab.maxSpace) {
			const space = (V.researchLab.maxSpace - (staff));
			linkArray = [];
			for (const num of [1, 5, 10]) {
				if (space >= num) {
					linkArray.push(App.UI.DOM.link(
						`${num}x`,
						() => {
							V.researchLab.hired += num;
							cashX(forceNeg(2000 * num), "labScientistsTransfer");
							App.UI.reload();
						}
					));
				} else {
					break;
				}
			}
			App.Events.addNode(node, ["Hire", App.UI.DOM.generateLinksStrip(linkArray), "scientists."]);
			App.UI.DOM.appendNewElement("div", node, `Each scientist will require an initial fee of ${cashFormat(2000)} and incur ${cashFormat(300)} weekly.`, ["indent", "note"]);

			if (V.menials > 0) {
				const space = (V.researchLab.maxSpace - (staff));
				linkArray = [];
				for (const num of [1, 5, 10]) {
					if (space >= num && V.menials >= num) {
						linkArray.push(App.UI.DOM.link(
							`${num}x`,
							() => {
								V.researchLab.menials += num;
								V.menials -= num;
								App.UI.reload();
							}
						));
					} else {
						break;
					}
				}
				App.Events.addNode(node, ["Assign", App.UI.DOM.generateLinksStrip(linkArray), "menial slaves to lab."]);
			} else {
				App.UI.DOM.appendNewElement("div", node, `You do not own any${` unassigned`} menial slaves.`);
			}
			App.UI.DOM.appendNewElement("div", node, `Using menial slaves is much cheaper than hiring scientists, but they are less effective. ${cashFormat(100)} per slave each week.`, ["indent", "note"]);
		} else {
			App.UI.DOM.appendNewElement("div", node, "Facility is fully staffed.", ["indent", "note"]);
		}

		if (V.researchLab.hired > 0) {
			linkArray = [];
			for (const num of [1, 5, 10]) {
				if (V.researchLab.hired >= num) {
					linkArray.push(App.UI.DOM.link(
						`${num}x`,
						() => {
							V.researchLab.hired -= num;
							App.UI.reload();
						}
					));
				} else {
					break;
				}
			}
			App.Events.addNode(node, ["Fire", App.UI.DOM.generateLinksStrip(linkArray), "scientists."]);
		}
		if (V.researchLab.menials > 0) {
			linkArray = [];
			for (const num of [1, 5, 10]) {
				if (V.researchLab.menials >= num) {
					linkArray.push(App.UI.DOM.link(
						`${num}x`,
						() => {
							V.researchLab.menials -= num;
							V.menials += num;
							App.UI.reload();
						}
					));
				} else {
					break;
				}
			}
			App.Events.addNode(node, ["Dismiss", App.UI.DOM.generateLinksStrip(linkArray), "menial slaves."]);
		}

		App.UI.DOM.appendNewElement("h3", node, "Tasks");

		if (V.researchLab.tasks.length > 0) {
			let j = 0;
			if (V.researchLab.speed > 0) {
				App.UI.DOM.appendNewElement("div", node, `The following tasks are queued:`);
			} else {
				App.UI.DOM.appendNewElement("div", node, `You have projects planned but without researchers you won't be able to work on them:`);
			}

			for (let i = 0; i < V.researchLab.tasks.length; i++) {
				const r = [];
				switch (V.researchLab.tasks[i].type) {
					case "research":
						r.push(`You`);
						if (i === 0) {
							r.push(`are researching`);
						} else {
							r.push(`plan to research`);
						}
						break;
					case "craft":
						r.push(`You`);
						if (i === 0) {
							r.push(`are constructing`);
						} else {
							r.push(`plan to construct`);
						}
						break;
					case "craftFit":
						r.push(`For <span class="noteworthy">${SlaveFullName(V.slaves[V.slaveIndices[V.researchLab.tasks[i].slaveID]])}</span> you`);
						if (i === 0) {
							r.push(`are constructing`);
						} else {
							r.push(`plan to construct`);
						}
						break;
					default:
						r.push(`<span class="error">Error: Unknown V.researchLab.tasks[_i].type: ${V.researchLab.tasks[i].type}</span > `);
				}
				j += V.researchLab.tasks[i].workLeft;
				r.push(`<span class="noteworthy">${capFirstChar(App.Data.prosthetics[V.researchLab.tasks[i].id].name)}.</span>`);
				if (V.researchLab.speed > 0) {
					r.push(`Finished in approximately ${(Math.floor(j / V.researchLab.speed) + 1)} week(s).`);
				}

				r.push(App.UI.DOM.link(
					(V.researchLab.tasks[i].type === "research") ? "Cancel: Will not return investments." : "Cancel",
					() => {
						if (V.researchLab.tasks[i].type === "research") {
							V.prosthetics[V.researchLab.tasks[i].id].research = 0;
						}
						V.researchLab.tasks.deleteAt(i);
						App.UI.reload();
					}
				));
				App.Events.addNode(node, r, "div", "indent");
			}
		} else {
			App.UI.DOM.appendNewElement("div", node, `Currently the research lab has no tasks planned.`, "note");
		}

		App.UI.DOM.appendNewElement("h3", node, "Research");

		App.UI.DOM.appendNewElement("div", node, `Available research projects:`);
		for (let p of App.Data.prostheticIDs) {
			if (V.prosthetics[p].research === 0) {
				//

				if (App.Data.prosthetics[p].level <= V.prostheticsUpgrade) {
					App.UI.DOM.appendNewElement("div", node, App.UI.DOM.link(
						`Reverse engineer ${addA(App.Data.prosthetics[p].name)}`,
						() => {
							cashX(forceNeg(App.Data.prosthetics[p].costs), "labResearch");
							V.prosthetics[p].research = -1;
							V.researchLab.tasks.push({type: "research", id: p, workLeft: App.Data.prosthetics[p].research});
							App.UI.reload();
						}, [], "",
						`Costs ${cashFormat(App.Data.prosthetics[p].costs)} of initial investment.`
					), "indent");
				} else {
					App.UI.DOM.appendNewElement("div", node, `You need better contracts to get the required research material for reverse engineering ${addA(App.Data.prosthetics[p].name)}.`, "note");
				}
			}
		}

		App.UI.DOM.appendNewElement("h3", node, "Manufacture");

		r.push(`Available building projects:`);
		for (let p of App.Data.prostheticIDs) {
			if (V.prosthetics[p].research === 1) {
				App.UI.DOM.appendNewElement("div", node, App.UI.DOM.link(
					`Build ${addA(App.Data.prosthetics[p].name)}`,
					() => {
						V.researchLab.tasks.push({type: "craft", id: p, workLeft: App.Data.prosthetics[p].craft});
						App.UI.reload();
					}
				), "indent");
			}
		}

		App.UI.DOM.appendNewElement("h3", node, "Prepared prosthetics");
		App.Events.addParagraph(node, [getProstheticsStockpile()]);
	}

	return node;
};


code:
/**
 * @param {App.Entity.SlaveState} slave
 * @param {object} params
 * @param {string} [params.oldName] Their name before you renamed them by calling this function
 * @param {string} [params.oldSurname] Their surname before you renamed them by calling this function
 */
App.UI.SlaveInteract.rename = function(slave, {oldName = "", oldSurname = ""} = {}) {
	const r = [];
	const {
		He, he, His, his, him, girl, himself, wife
	} = getPronouns(slave);
	if (slave.slaveName === oldName && slave.slaveSurname === oldSurname) {
		return;
	} else {
		/* First time renaming reaction */
		if (slave.slaveName === "" || !(slave.hasOwnProperty("slaveName"))) {
			r.push(`${oldName} needs to be called something on the records, so ${oldName} ${he} stays.`);
			slave.slaveName = oldName;
		} else if (slave.fetish === "mindbroken") {
			r.push(`${slave.slaveName} doesn't even recognize that ${he}'s been renamed. It simply does not register.`);
		} else if ((oldName !== slave.birthName && slave.slaveName === slave.birthName) || (oldSurname !== slave.birthSurname && slave.slaveSurname === slave.birthSurname)) {
			r.push(`${slave.slaveName}`);
			if (slave.devotion > 50) {
				r.push(`cheerfully accepts ${his} old name back.`);
			} else if (slave.devotion >= -20) {
				r.push(`obediently accepts ${his} old name back.`);
			} else if (slave.devotion >= -50) {
				r.push(`happily accepts ${his} original name.`);
			} else if (slave.trust > 20) {
				r.push(`scoffs at the notion that ${he} was ever anything other than ${SlaveFullBirthName(slave)}, <span class="orangered">weakening your control over ${him}.</span>`);
				slave.trust += 5;
			} else {
				r.push(`cautiously accepts the return of ${his} name.`);
			}
		} else if ((oldName === slave.birthName && slave.slaveName !== slave.birthName) || (oldSurname === slave.birthSurname && slave.slaveSurname !== slave.birthSurname)) {
			r.push(`${slave.slaveName}`);
			if (slave.devotion > 50) {
				r.push(`accepts ${his} new name cheerfully. This attachment to you <span class="devotion inc">increases ${his} devotion.</span>`);
				slave.devotion += 4;
			} else if (slave.devotion >= -20) {
				r.push(`obediently accepts ${his} new name. This surrender to you <span class="devotion inc">increases ${his} obedience.</span>`);
				slave.devotion += 4;
			} else if (slave.devotion >= -50) {
				r.push(`reluctantly accepts ${his} new name.`);
			} else {
				r.push(`angrily tries to resist ${his} new name, insisting that ${his} name is ${SlaveFullBirthName(slave)}. This resistance <span class="devotion dec">increases ${his} rebelliousness.</span>`);
				slave.devotion -= 5;
			}
		} else {
			r.push(`Since ${slave.slaveName} has already had a new name`);
			if (slave.devotion > 20) {
				r.push(`given to`);
			} else {
				r.push(`forced on`);
			}
			r.push(`${him} before, the mere fact of having it changed again doesn't really affect ${him}.`);
		}

		let insultingName = false;
		let toSearch;
		if (oldName !== slave.slaveName && typeof slave.slaveName === "string") {
			toSearch = slave.slaveName.toLowerCase();
			insultingName = App.Data.misc.badWords.some((s) => toSearch.includes(s));
		}
		if (!insultingName) {
			if (oldSurname !== slave.slaveSurname && typeof slave.slaveSurname === "string") {
				toSearch = slave.slaveSurname.toLowerCase();
				insultingName = App.Data.misc.badWords.some((s) => toSearch.includes(s));
			}
		}
		if (insultingName) {
			if (slave.fetish === "mindbroken") {
				r.push(`${His} new name would be insulting to a normal ${girl}, but ${he} dully accepts that it is an accurate description and goes about ${his} duties.`);
			} else if (slave.devotion < -50) {
				r.push(`Being given such a degrading name <span class="devotion dec">further increases ${his} hatred</span> of you.`);
				slave.devotion -= 5;
			} else if (slave.devotion <= 50) {
				r.push(`Being given such a degrading name <span class="trust dec">terrifies ${him},</span> since ${he} thinks it's fair warning for what ${he}'ll suffer in the future.`);
				slave.trust -= 5;
			}
		}

		/* Wife's surname reaction */
		if (oldSurname !== slave.slaveSurname) {
			if (slave.relationship >= 5) {
				const slaveWife = getSlave(slave.relationshipTarget);
				if (!slaveWife) {
					r.push(`<span class="red">Error, relationshipTarget not found.</span>`);
				} else {
					if (slaveWife.slaveSurname) {
						const {wife2} = getPronouns(slaveWife).appendSuffix('2');
						if (slave.slaveSurname === slaveWife.slaveSurname) {
							r.push(`${He}'s touched that ${he} now shares a surname with ${his} ${wife2} ${slaveWife.slaveName} ${slaveWife.slaveSurname}, and is more confident than ever that you intend to keep them together in marital bliss.`);
							slave.trust += 5;
						} else if (oldSurname === slaveWife.slaveSurname) {
							r.push(`${He}'s concerned that ${he} no longer shares a surname with ${his} ${wife2} ${slaveWife.slaveName} ${slaveWife.slaveSurname}, and is very worried that you might be considering splitting them up.`);
							slave.trust -= 5;
						}
					}
				}
			}
		}

		/* PC's surname reaction */
		if (oldSurname !== slave.slaveSurname) {
			if (slave.relationship === -3) {
				if (V.PC.slaveSurname) {
					if (slave.fetish === "mindbroken") {
						r.push(`Names are meaningless to ${him} and it is unlikely ${he}'ll remember it.`);
					} else if (slave.devotion + slave.trust >= 175) {
						if (slave.slaveSurname === V.PC.slaveSurname) {
							r.push(`When you tell ${him} that ${he}'s to be known as ${slave.slaveName} ${slave.slaveSurname} now, ${he} starts to cry. ${He} tries to get ${himself} under control and thank you as best ${he} can,`);
							if (hasAnyArms(slave)) {
								r.push(`wiping at the tears running down ${his} ${slave.skin} cheeks,`);
							}
							r.push(`but ${he} can't seem to stop weeping as ${he} thanks you over and over. ${He}'s a sex slave, your property, and it's understandable that some doubts about the permanence of ${his} place as your slave ${wife}. This has helped reassure ${him}, and explains the strength of ${his} emotional reaction. The next time you make love to ${him}, ${he} presses ${himself} as close to you as ${he} can, eager to drink in as much of your presence as ${he} can get.`);
							slave.devotion += 5;
							slave.trust += 5;
						} else if (oldSurname === V.PC.slaveSurname) {
							r.push(`${He}'s devastated that you'd rename ${him} something other than your name. ${He}'s terrified that you intend to discard ${him} as your slave ${wife}, and saddened that you would take away something that was precious to ${him}.`);
							slave.devotion -= 5;
							slave.trust -= 5;
						}
					} else if (slave.devotion < -20 && slave.trust > 20) {
						if (slave.slaveSurname === V.PC.slaveSurname) {
							r.push(`When you tell ${him} that ${he}'s to be known as ${slave.slaveName} ${slave.slaveSurname} now, ${he} shows no reaction. You already took everything from ${him}, what's a name at this point?`);
						} else if (oldSurname === V.PC.slaveSurname) {
							r.push(`${He} doesn't care about losing your surname at first, but it quickly sets in that ${he} may have <span class="trust dec">pushed ${his} position too far.</span>`);
							slave.trust -= 20;
						}
					} else if (slave.devotion < -20) {
						if (slave.slaveSurname === V.PC.slaveSurname) {
							r.push(`When you tell ${him} that ${he}'s to be known as ${slave.slaveName} ${slave.slaveSurname} now, ${he} pleads with you not to steal ${his} name. It matters little to you, and ${he} is forced to accept your will.`);
							slave.devotion += 5;
						} else if (oldSurname === V.PC.slaveSurname) {
							r.push(`${He}'s happy to no longer have to share a name with ${his} tormentor, but the bliss doesn't last long as dread quickly sets in about what this may mean.`);
							slave.devotion += 5;
							slave.trust -= 10;
						}
					} else {
						if (slave.slaveSurname === V.PC.slaveSurname) {
							r.push(`When you tell ${him} that ${he}'s to be known as ${slave.slaveName} ${slave.slaveSurname} now, ${he} nods with approval feeling that ${he} may hold at least some value in your eyes.`);
							slave.trust += 5;
						} else if (oldSurname === V.PC.slaveSurname) {
							r.push(`${He} accepts that you'd rename ${him} something other than your name. ${He}'s a little scared that you intend to discard ${him} as your slave ${wife}, or worse, but realizes this was not only a possibility, but likely.`);
							slave.trust -= 5;
						}
					}
				}
			}
		}
	}
	return jQuery("#rename").empty().append(r.join(" "));
};


code:
/** @param {App.Entity.SlaveState} slave */
App.Interact.Sale.separationReactions = function(slave) {
	const {sister, daughter, father, mother} = getPronouns(slave);
	const resultNode = document.createDocumentFragment();
	for (const s of V.slaves) {
		const {his2} = getPronouns(s).appendSuffix("2");
		if (slave.mother === s.ID) {
			App.Events.addNode(resultNode, [`${s.slaveName} is <span class="devotion dec">grieved</span> that you are selling ${his2} ${daughter}.`], "div");
			s.devotion -= 20;
		}
		if (slave.father === s.ID) {
			App.Events.addNode(resultNode, [`${s.slaveName} is <span class="devotion dec">disappointed</span> that you are selling ${his2} ${daughter}.`], "div");
			s.devotion -= 10;
		}
		if (slave.ID === s.father) {
			App.Events.addNode(resultNode, [`${s.slaveName} is <span class="devotion dec">saddened</span> that you are selling ${his2} ${father}.`], "div");
			s.devotion -= 10;
		}
		if (slave.ID === s.mother) {
			App.Events.addNode(resultNode, [`${s.slaveName} is <span class="devotion dec">grieved</span> that you are selling ${his2} ${mother}.`], "div");
			s.devotion -= 20;
		}
		switch (areSisters(slave, s)) {
			case 1:
				App.Events.addNode(resultNode, [`${s.slaveName} is <span class="devotion dec">devastated</span> that you are selling ${his2} twin.`], "div");
				s.devotion -= 30;
				break;
			case 2:
				App.Events.addNode(resultNode, [`${s.slaveName} is <span class="devotion dec">grieved</span> that you are selling ${his2} ${sister}.`], "div");
				s.devotion -= 20;
				break;
			case 3:
				App.Events.addNode(resultNode, [`${s.slaveName} is <span class="devotion dec">disheartened</span> that you are selling ${his2} half-${sister}.`], "div");
				s.devotion -= 10;
				break;
		}
	}
	if (slave.relationship > 0) {
		const rel = getSlave(slave.relationshipTarget);
		if (rel && rel.fetish !== "mindbroken") {
			const {his2} = getPronouns(rel).appendSuffix("2");
			App.Events.addParagraph(resultNode, [`${rel.slaveName} is <span class="devotion dec">grieved</span> that you are selling ${his2} best source of comfort and companionship in a life of bondage.`]);
			rel.devotion -= rel.relationship*10;
		}
	} else if (slave.relationship === -3) {
		App.Events.addParagraph(resultNode, [`Selling one of your slave wives is socially unacceptable. In addition, your other devoted slaves are worried that you may not respect their status.`]);
		repX(-200, "slaveTransfer");
		for (const s of V.slaves) {
			if (s.devotion > 50) {
				s.trust -= 5;
			}
		}
	}
	if (slave.rivalry !== 0) {
		const riv = getSlave(slave.rivalryTarget);
		if (riv && riv.fetish !== "mindbroken") {
			const {he2, his2} = getPronouns(riv).appendSuffix("2");
			App.Events.addParagraph(resultNode, [`${riv.slaveName} is <span class="devotion inc">pleased</span> that ${he2} won't have to see ${his2} rival any more.`]);
			riv.devotion += riv.rivalry*3;
		}
	}
	if (isShelterSlave(slave)) {
		V.shelterAbuse += 1;
	}
	return resultNode;
};


code:
App.Interact.BaseChoosePartnerRenderer = class {
	/** Set up selection of a second slave for an interaction (i.e. Slave/Slave or threesome with PC)
	 * @param {App.Entity.SlaveState} slave
	 */
	constructor(slave) {
		this.slave = slave;
		this.intro = "";
		this.instructions = "Select an eligible slave";
		this.noneEligible = "You have no slaves capable of this act.";
		this.execute = (/** @type {App.Entity.SlaveState} */ slave, /** @type {App.Entity.SlaveState} */ partner) => new DocumentFragment();
	}

	/** Determines whether a particular candidate is eligible to join slave for this interaction
	 * @param {App.Entity.SlaveState} candidate
	 * @returns {boolean}
	 */
	eligible(candidate) {
		return true;
	}

	/** Details to render for a particular candidate entry, which will be relevant for the player's decision
	 * @param {App.Entity.SlaveState} candidate
	 * @param {ParentNode} container
	 */
	renderDetail(candidate, container) {
	}
};

/** Allows the player to choose a second slave to participate in a slave interaction.
 * @param {App.Interact.BaseChoosePartnerRenderer} renderer
 * @returns {HTMLDivElement}
 */
App.Interact.choosePartner = function(renderer) {
	const target = document.createElement("div");

	Dialog.setup(renderer.instructions);
	Dialog.open();
	Dialog.append(renderChoices());

	function renderChoices() {
		const node = new DocumentFragment();
		App.UI.DOM.appendNewElement("div", node, renderer.intro);
		const slave = renderer.slave;

		const eligibles = V.slaves.filter((s) => (s.ID !== slave.ID) && renderer.eligible(s));
		for (const eligible of eligibles) {
			const div = App.UI.DOM.appendNewElement("div", node);
			div.append(App.UI.DOM.link(
				SlaveFullName(eligible),
				() => {
					Dialog.close();
					App.UI.DOM.replace(target, renderer.execute(slave, eligible));
				}
			));
			if (eligible.custom.label) {
				App.UI.DOM.appendNewElement("span", div, ` ${eligible.custom.label}`, ["custom-label"]);
			}
			if (totalRelatives(slave) > 0) {
				const relTerm = relativeTerm(slave, eligible);
				if (relTerm !== null) {
					App.UI.DOM.appendNewElement("span", div, ` ${capFirstChar(relTerm)}`, ["relationship"]);
				}
			}
			renderer.renderDetail(eligible, div);
		}

		if (eligibles.length === 0) {
			App.UI.DOM.appendNewElement("div", node, renderer.noneEligible, ["note"]);
		}

		return node;
	}

	return target;
};