code:
App.UI.Player.personalAttention = function() {
	const frag = new DocumentFragment();

	App.UI.DOM.appendNewElement("h1", frag, `Personal Attention`);

	V.nextButton = "Back to Main";
	V.nextLink = "Main";

	// set up div for refreshing options
	const refreshDiv = document.createElement("div");
	refreshDiv.append(content());
	frag.append(
		refreshDiv,
		slaves(),
	);

	return frag;

	function content() {
		const frag = new DocumentFragment();

		frag.append(
			focus(),
			proclamations(),
			skills(),
			attention(),
		);

		return frag;
	}

	function focus() {
		const div = App.UI.DOM.makeElement("div", null, ['margin-bottom']);
		const text = [];
		const links = [];

		App.UI.DOM.appendNewElement("h2", div, `Focus`);

		text.push(`This week you are`);

		if (typeof V.personalAttention === "object") {
			// really hacky way to make sure text doesn't show as "undecided"
			V.personalAttention[0].trainingRegimen = V.personalAttention[0].trainingRegimen
				|| getRegimen(getSlave(V.personalAttention[0].ID));

			if (V.personalAttention.length > 1) {
				V.personalAttention[1].trainingRegimen = V.personalAttention[1].trainingRegimen
					|| getRegimen(getSlave(V.personalAttention[0].ID));
			}

			text.push(`training <span class="slave-name">${SlaveFullName(getSlave(V.personalAttention[0].ID))}</span> to <span class="bold">${V.personalAttention[0].trainingRegimen}${V.personalAttention.length > 1? ` and ${SlaveFullName(getSlave(V.personalAttention[1].ID))} to ${V.personalAttention[1].trainingRegimen}` : ``}.`);
		} else {
			switch (V.personalAttention) {
				case PersonalAttention.WHORING:
					text.push(`using your body to make some side cash.`);
					break;
				case PersonalAttention.MAID:
					text.push(`using your housekeeping skills to help reduce upkeep costs on your home.`);
					break;
				case PersonalAttention.BUSINESS:
					text.push(`using your business acumen to earn some extra money.`);
					break;
				case PersonalAttention.SMUGGLING:
					text.push(`going to try to make some easy (but dirty) money.`);
					break;
				case PersonalAttention.SURVEY:
					text.push(`surveying ${V.arcologies[0].name}'s defenses in person.`);
					break;
				case PersonalAttention.DEVELOPMENT:
					text.push(`going to try to help raise ${V.arcologies[0].name}'s prosperity by contributing your talents to a local development project.`);
					break;
				case PersonalAttention.TECH:
					text.push(`contracting out your hacking skills to earn some extra cash.`);
					break;
				case PersonalAttention.SUPPORTHG:
					text.push(`going to support ${S.HeadGirl.slaveName} in ${getPronouns(S.HeadGirl).his} day-to-day routine.`);
					break;
				case PersonalAttention.SEX:
					text.push(`going to have as much sex as you possibly can.`);
					break;
				case PersonalAttention.TRADE:
					text.push(`training in trade.`);
					break;
				case PersonalAttention.WAR:
					text.push(`training in warfare.`);
					break;
				case PersonalAttention.SLAVING:
					text.push(`training in slaving.`);
					break;
				case PersonalAttention.ENGINEERING:
					text.push(`training in engineering.`);
					break;
				case PersonalAttention.MEDICINE:
					text.push(`training in medicine.`);
					break;
				case PersonalAttention.HACKING:
					text.push(`training in hacking.`);
					break;
				case PersonalAttention.PROCLAMATION:
					if (V.SecExp.proclamation.type === "security") {
						text.push(`issuing a new proclamation about the security of ${V.arcologies[0].name}.`);
					} else {
						text.push(`issuing a new proclamation about crime in ${V.arcologies[0].name}.`);
					}
					break;
				default:
					throw new Error(`Unknown V.personalAttention value of '${V.personalAttention}'. Please report this.`);
			}
		}

		App.Events.addNode(div, text);

		addFocusLinks(links,
			{
				link: `Focus on "connecting"`,
				value: PersonalAttention.WHORING,
				prereqs: [
					() => ["escort", "prostitute", "child prostitute"].includes(V.PC.career),
				],
			},
			{
				link: `Maintain your home`,
				value: PersonalAttention.MAID,
				prereqs: [
					() => ["servant", "handmaiden", "child servant"].includes(V.PC.career),
				],
			},
			{
				link: `Focus on business`,
				value: PersonalAttention.BUSINESS,
			},
			{
				link: `Help people "pass" things around`,
				value: PersonalAttention.SMUGGLING,
				prereqs: [
					() => ["gang", "hoodlum", "street urchin"].includes(V.PC.career),
				],
			},
			{
				link: `Survey your arcology's defenses in person`,
				value: PersonalAttention.SURVEY,
				prereqs: [
					() => V.PC.skill.warfare > 25,
				],
			},
			{
				link: `Contribute to a local development project`,
				value: PersonalAttention.DEVELOPMENT,
				prereqs: [
					() => V.PC.skill.engineering > 25,
					() => V.arcologies[0].prosperity + 1 * (1 + Math.ceil(V.PC.skill.engineering / 100)) < V.AProsperityCap,
				],
			},
			{
				link: `Sell your intrusion services `,
				value: PersonalAttention.TECH,
				prereqs: [
					() => V.PC.skill.hacking > 25,
				],
			},
			{
				link: `Support your Head Girl`,
				value: PersonalAttention.SUPPORTHG,
				prereqs: [
					() => !!S.HeadGirl,
				],
			},
			{
				link: `Focus on sex`,
				value: PersonalAttention.SEX,
			},
		);

		App.UI.DOM.appendNewElement("div", div, App.UI.DOM.generateLinksStrip(links), ['margin-left']);

		return div;

		/**
		 * @param {Array<HTMLSpanElement|HTMLAnchorElement>} links
		 * @param {{link: string, value: PersonalAttention, prereqs?: Array<function():boolean>}[]} args
		 */
		function addFocusLinks(links, ...args) {
			args.forEach(link => {
				if (!link.prereqs || link.prereqs.every(prereq => prereq())) {
					if (V.personalAttention === link.value) {
						links.push(App.UI.DOM.disabledLink(link.link, [
							`Currently selected.`,
						]));
					} else {
						links.push(App.UI.DOM.link(link.link, () => {
							V.personalAttention = link.value;

							refresh();
						}));
					}
				}
			});
		}
	}

	function proclamations() {
		const div = App.UI.DOM.makeElement("div", null, ['margin-bottom']);

		const links = [];

		if (V.secExpEnabled) {
			App.UI.DOM.appendNewElement("h2", div, `Proclamations`);

			if (V.SecExp.proclamation.cooldown === 0) {
				const options = new App.UI.OptionsGroup();
				const option = options.addOption(null, "currency", V.SecExp.proclamation);

				App.UI.DOM.appendNewElement("div", div, `You can dedicate the week to issuing a proclamation, a powerful tool that will have an immediate noticeable effect on the arcology.`, ['note']);

				App.Events.addNode(div, [
					`You are using <span class="bold">${V.SecExp.proclamation.currency || 'cash'}.</span>`,
				]);

				if (V.SecExp.core.authority >= 2000) {
					option.addValue(`Authority`, 'authority');
				}

				if (V.rep >= 4000) {
					option.addValue(`Reputation`, 'reputation');
				}

				option.addValue(`Cash`, 'cash');

				App.UI.DOM.appendNewElement("div", div, options.render(), ['margin-left']);

				if (V.SecExp.core.authority >= 2000
					|| V.rep >= 4000
					|| V.cash >= 8000) {
					if (V.personalAttention === PersonalAttention.PROCLAMATION) {
						if (V.SecExp.proclamation.type === "security") {
							links.push(
								App.UI.DOM.disabledLink(`Issue a proclamation about security`, [
									`Currently selected.`
								]),

								App.UI.DOM.link(`Issue a proclamation about crime`, () => {
									V.personalAttention = PersonalAttention.PROCLAMATION;
									V.SecExp.proclamation.type = "crime";

									refresh();
								},
								null, ``, `You will use your ${V.SecExp.proclamation.currency === "authority"
									? `control over the arcology`
									: V.SecExp.proclamation.currency === "reputation"
										? `great influence`
										: `vast financial means`
								} to force the arrest of suspected citizens without passing through the normal legal procedures.`),
							);
						} else {
							links.push(
								App.UI.DOM.link(`Issue a proclamation about security`, () => {
									V.personalAttention = PersonalAttention.PROCLAMATION;
									V.SecExp.proclamation.type = "security";

									refresh();
								},
								null, ``, `You will use your ${V.SecExp.proclamation.currency === "authority"
									? `control over the arcology`
									: V.SecExp.proclamation.currency === "reputation"
										? `great influence`
										: `vast financial means`
								} to force citizens to give up on sensitive information for the good of the arcology.`),

								App.UI.DOM.disabledLink(`Issue a proclamation about crime`, [
									`Currently selected.`
								]),
							);
						}
					} else {
						links.push(
							App.UI.DOM.link(`Issue a proclamation about security`, () => {
								V.personalAttention = PersonalAttention.PROCLAMATION;
								V.SecExp.proclamation.type = "security";

								refresh();
							},
							null, ``, `You will use your ${V.SecExp.proclamation.currency === "authority"
								? `control over the arcology`
								: V.SecExp.proclamation.currency === "reputation"
									? `great influence`
									: `vast financial means`
							} to force citizens to give up on sensitive information for the good of the arcology.`),

							App.UI.DOM.link(`Issue a proclamation about crime`, () => {
								V.personalAttention = PersonalAttention.PROCLAMATION;
								V.SecExp.proclamation.type = "crime";

								refresh();
							},
							null, ``, `You will use your ${V.SecExp.proclamation.currency === "authority"
								? `control over the arcology`
								: V.SecExp.proclamation.currency === "reputation"
									? `great influence`
									: `vast financial means`
							} to force the arrest of suspected citizens without passing through the normal legal procedures.`),
						);
					}

					App.UI.DOM.appendNewElement("div", div, App.UI.DOM.generateLinksStrip(links), ['margin-left']);
				}
			} else {
				App.UI.DOM.appendNewElement("div", div, `It's too early to issue another proclamation. Another will be available in ${numberWithPluralOne(V.SecExp.proclamation.cooldown, "week")}.`, ['note']);
			}
		}

		return div;
	}

	function skills() {
		const div = App.UI.DOM.makeElement("div", null, ['margin-bottom']);
		const cost = 10000 * V.AgeEffectOnTrainerPricingPC;

		App.UI.DOM.appendNewElement("h2", div, `Skills`);

		div.append(addSkills(
			{
				skill: "trading",
				text: `trader`,
				link: {
					value: PersonalAttention.TRADE,
					text: `Hire a merchant to train you in commerce`,
				},
			},
			{
				skill: "warfare",
				text: `tactician`,
				link: {
					value: PersonalAttention.WAR,
					text: `Hire a mercenary to train you in warfare`,
				},
			},
			{
				skill: "slaving",
				text: `slaver`,
				link: {
					value: PersonalAttention.SLAVING,
					text: `Hire a slaver to train you in slaving`,
				},
			},
			{
				skill: "engineering",
				text: `arcology engineer`,
				link: {
					value: PersonalAttention.ENGINEERING,
					text: `Hire an engineer to train you in arcology engineering`,
				},
			},
			{
				skill: "medicine",
				text: `surgeon`,
				link: {
					value: PersonalAttention.MEDICINE,
					text: `Hire a doctor to train you in medicine`,
				},
			},
			{
				skill: "hacking",
				text: `hacker`,
				link: {
					value: PersonalAttention.HACKING,
					text: `Hire a specialist to train you in hacking`,
				},
			},
		));

		return div;

		/**
		 * @param {Array<{skill: string, text: string, link: {value: PersonalAttention, text: string}}>} args
		 * @returns {HTMLDivElement}
		 */
		function addSkills(...args) {
			const div = document.createElement("div");

			args.forEach(skill => {
				div.append(
					addSkill(skill.skill, skill.text, {
						value: skill.link.value,
						text: skill.link.text,
					}),
				);
			});

			return div;

			/**
			 * @param {string} skill
			 * @param {string} text
			 * @param {{value: PersonalAttention, text: string}} link
			 */
			function addSkill(skill, text, link) {
				const div = document.createElement("div");

				const an = ['a', 'e', 'i', 'o', 'u'].includes(text[0]) ? `an` : `a`;

				if (V.PC.skill[skill] >= 100) {
					div.append(`You are a master ${text}.`);
				} else {
					if (V.PC.skill[skill] > 60) {
						div.append(`You are an expert ${text}.`);
					} else if (V.PC.skill.hacking > 30) {
						div.append(`You have some skill as ${an} ${text}.`);
					} else if (V.PC.skill.hacking > 10) {
						div.append(`You have basic knowledge as ${an} ${text}.`);
					} else {
						div.append(`You have no knowledge as ${an} ${text}.`);
					}

					addLink(div, {
						value: link.value,
						link: link.text,
					});
				}

				return div;

				/**
				 * @param {HTMLDivElement} div
				 * @param {{value: PersonalAttention, link: string}} skill
				 */
				function addLink(div, skill) {
					if (V.personalAttention === skill.value) {
						App.UI.DOM.appendNewElement("div", div, App.UI.DOM.disabledLink(skill.link, [
							`Currently selected.`,
						]), ['margin-left']);
					} else {
						App.UI.DOM.appendNewElement("div", div, App.UI.DOM.link(skill.link, () => {
							V.personalAttention = skill.value;

							refresh();
						}, null, '', `Training will cost ${cashFormat(cost)} per week.`), ['margin-left']);
					}
				}
			}
		}
	}

	function attention() {
		/**
		 * @param {number} index
		 * @param {string} text
		 * @param {string} target
		 * @returns {HTMLSpanElement|HTMLAnchorElement}
		 */
		function attentionLink(index, text, target) {
			// @ts-ignore TODO: change V.personalAttention into either an object or multiple variables
			if (V.personalAttention[index].trainingRegimen === target) {
				return App.UI.DOM.disabledLink(text, [
					'Currently selected.'
				]);
			} else {
				return App.UI.DOM.link(text, () => {
					// @ts-ignore
					V.personalAttention[index].trainingRegimen = target;
					refresh();
				});
			}
		}

		const frag = new DocumentFragment();

		App.UI.DOM.appendNewElement("h2", frag, `Slaves`);

		if (typeof V.personalAttention !== "object" || V.personalAttention.length === 0) {
			frag.append(`You have not selected a slave for your personal attention.`);
		} else {
			if (V.personalAttention.length > (V.PC.skill.slaving >= 100 ? 2 : 1)) {	// ensure only the maximum number of slaves are being trained
				V.personalAttention.shift();
			}

			for (let i = 0; i < V.personalAttention.length; i++) {
				const div = App.UI.DOM.appendNewElement("div", frag, null, ['margin-bottom', 'card']);

				div.id = "attention";	// used for scrolling back up after selecting a slave

				const slave = getSlave(V.personalAttention[i].ID);
				const regimen = V.personalAttention[i].trainingRegimen;

				if (V.personalAttention.map(s => { return s.ID; }).count(slave.ID) > 1) {
					V.personalAttention.deleteAt(i);
					V.personalAttention.deleteAt(i);
					i--;
					continue;
				}

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

				let text = [];
				let links = [];

				V.personalAttention[i].trainingRegimen = V.personalAttention[i].trainingRegimen || getRegimen(slave, regimen);

				text.push(
					`You will give`,
					App.UI.DOM.referenceSlaveWithPreview(slave, SlaveFullName(slave)),
					`your personal attention this week to ${V.personalAttention[i].trainingRegimen}.`,
					App.UI.DOM.link(`Stop`, () => {
						if (V.personalAttention.length === 1) {
							V.personalAttention = PersonalAttention.SEX;
						} else {
							// @ts-ignore
							V.personalAttention.deleteAt(s => s.ID === slave.ID);
						}

						refresh();
					}),
				);

				App.Events.addNode(div, text);

				App.UI.DOM.appendNewElement("h3", div, `Change training objective`);

				// Devotion

				App.Events.addNode(div, [
					`Current devotion: `,
					devotionText(slave),
				]);

				if (slave.devotion <= 20 && slave.trust >= -20) {
					links.push(
						attentionLink(i, `Break ${his} will`, "break her will"),
						attentionLink(i, `Use enhanced breaking techniques`, "harshly break her will"),
					);
				} else {
					links.push(attentionLink(i, `Build`, "build her devotion"));
				}

				// Sexuality

				if (!slave.fetishKnown || !slave.attrKnown) {
					links.push(attentionLink(i, `Explore ${his} sexuality and fetishes`, "explore her sexuality"));
				} else {
					links.push(App.UI.DOM.disabledLink(`Explore ${his} sexuality and fetishes`, [
						`You already understand ${his} sexuality.`
					]));
				}

				// Education

				if (slave.devotion <= 20 && slave.trust >= -20) {
					links.push(App.UI.DOM.disabledLink(`Teach ${him}`, [
						`${He}'s too disobedient to learn sex skills`
					]));
				} else if (slave.skill.anal >= 100
					&& slave.skill.oral >= 100
					&& slave.skill.whoring >= 30
					&& slave.skill.entertainment >= 30) {
					if (slave.skill.vaginal >= 100) {
						links.push(App.UI.DOM.disabledLink(`Teach ${him}`, [
							`${He} knows all the skills you can teach`
						]));
					} else if (slave.dick === 0 && slave.scrotum === 0 && slave.vagina === -1) {
						links.push(App.UI.DOM.disabledLink(`Teach ${him}`, [
							`${He} knows all the skills you can teach a null slave`
						]));
					} else if (slave.dick > 0 && slave.balls === 0) {
						links.push(App.UI.DOM.disabledLink(`Teach ${him}`, [
							`${He} knows all the skills you can teach a gelded slave`
						]));
					} else if (slave.dick > 0 && slave.boobs > 300 && slave.vagina === -1) {
						links.push(App.UI.DOM.disabledLink(`Teach ${him}`, [
							`${He} knows all the skills you can teach a shemale slave`
						]));
					} else {
						links.push(attentionLink(i, `Teach ${him}`, "learn skills"));
					}
				}

				// Health

				App.Events.addNode(div, [
					`Current health: `,
					healthText(slave),
				]);

				links.push(attentionLink(i, `Care for ${him}`, "look after her health"));

				// Flaws

				if (slave.behavioralFlaw !== "none") {
					App.Events.addNode(div, [
						`Current behavioral flaw: <span class="red">${slave.behavioralFlaw}.</span> `,
					]);

					links.push(attentionLink(i, `Remove ${his} behavioral flaw`, "fix her behavioral flaw"));

					if (slave.devotion < -20) {
						links.push(App.UI.DOM.disabledLink(`Soften ${his} behavioral flaw`, [
							`${He} must be broken before ${his} flaws can be softened`
						]));
					} else {
						links.push(attentionLink(i, `Soften ${his} behavioral flaw`, "soften her behavioral flaw"));
					}
				}

				if (slave.sexualFlaw !== "none") {
					App.Events.addNode(div, [
						`Current sexual flaw: <span class="red">${capFirstChar(slave.sexualFlaw)}.</span>`,
					]);

					links.push(attentionLink(i, `Remove ${his} sexual flaw`, "fix her sexual flaw"));

					if (slave.devotion < -20) {
						links.push(App.UI.DOM.disabledLink(`Soften ${his} sexual flaw`, [
							`${He} must be broken before ${his} flaws can be softened`
						]));
					} else if (App.Data.misc.paraphiliaList.includes(slave.sexualFlaw)) {
						links.push(App.UI.DOM.disabledLink(`Soften ${his} sexual flaw`, [
							`Paraphilias cannot be softened`
						]));
					} else {
						links.push(attentionLink(i, `Soften ${his} sexual flaw`, "soften her sexual flaw"));
					}
				}

				App.UI.DOM.appendNewElement("div", div, App.UI.DOM.generateLinksStrip(links), ['margin-left']);
				links = [];

				div.append(
					App.UI.DOM.makeElement("h3", `Induce a behavioral flaw`),
				);

				// Behavioral Flaws

				addLinks(
					slave,
					regimen,
					links,
					i,
					{
						quirk: "confident",
						flaw: "arrogant",
						type: "induce arrogance",
						link: `Arrogance`,
					},
					{
						quirk: "cutting",
						flaw: "bitchy",
						type: "induce bitchiness",
						link: `Bitchiness`,
					},
					{
						quirk: "funny",
						flaw: "odd",
						type: "induce odd behavior",
						link: `Odd behavior`,
					},
					{
						quirk: "adores women",
						flaw: "hates men",
						type: "induce hatred of men",
						link: `Hatred of men`,
					},
					{
						quirk: "adores men",
						flaw: "hates women",
						type: "induce hatred of women",
						link: `Hatred of women`,
					},
					{
						quirk: "fitness",
						flaw: "gluttonous",
						type: "induce gluttony",
						link: `Gluttony`,
					},
					{
						quirk: "insecure",
						flaw: "anorexic",
						type: "induce anorexia",
						link: `Anorexia`,
					},
					{
						quirk: "sinful",
						flaw: "devout",
						type: "induce religious devotion",
						link: `Religious devotion`,
					},
					{
						quirk: "advocate",
						flaw: "liberated",
						type: "induce liberation",
						link: `Liberation`,
					},
				);

				App.UI.DOM.appendNewElement("div", div, App.UI.DOM.generateLinksStrip(links), ['margin-left']);
				links = [];

				// Sexual Flaws

				div.append(
					App.UI.DOM.makeElement("h3", `Induce a sexual flaw`),
				);

				addLinks(
					slave,
					regimen,
					links,
					i,
					{
						quirk: "gagfuck queen",
						flaw: "hates oral",
						type: "induce hatred of oral",
						link: `Hatred of oral`,
					},
					{
						quirk: "painal queen",
						flaw: "hates anal",
						type: "induce hatred of anal",
						link: `Hatred of anal`,
					},
					{
						quirk: "strugglefuck queen",
						flaw: "hates penetration",
						type: "induce hatred of penetration",
						link: `Hatred of penetration`,
					},
					{
						quirk: "tease",
						flaw: "shamefast",
						type: "induce shame",
						link: `Shame`,
					},
					{
						quirk: "romantic",
						flaw: "idealistic",
						type: "induce sexual idealism",
						link: `Sexual idealism`,
					},
					{
						quirk: "perverted",
						flaw: "repressed",
						type: "induce sexual repression",
						link: `Sexual repression`,
					},
					{
						quirk: "caring",
						flaw: "apathetic",
						type: "induce sexual apathy",
						link: `Sexual apathy`,
					},
					{
						quirk: "unflinching",
						flaw: "crude",
						type: "induce crudity",
						link: `Crudity`,
					},
					{
						quirk: "size queen",
						flaw: "judgemental",
						type: "induce judgement",
						link: `Judgment`,
					},
				);

				App.UI.DOM.appendNewElement("div", div, App.UI.DOM.generateLinksStrip(links), ['margin-left']);
				links = [];

				// Paraphilias

				App.UI.DOM.appendNewElement("h3", div, `Induce a paraphilia`);

				if (slave.fetishStrength >= 95) {
					addLinks(
						slave,
						regimen,
						links,
						i,
						{
							fetish: "cumslut",
							flaw: "cum addict",
							type: "induce cum addiction",
							link: `Cum addiction`,
						},
						{
							fetish: "buttslut",
							flaw: "anal addict",
							type: "induce anal addiction",
							link: `Anal addiction`,
						},
						{
							fetish: "humiliation",
							flaw: "attention whore",
							type: "induce attention whoring",
							link: `Attention whoring`,
						},
						{
							fetish: "boobs",
							flaw: "breast growth",
							type: "induce breast growth obsession",
							link: `Breast growth obsession`,
						},
						{
							fetish: "dom",
							flaw: "abusive",
							type: "induce abusiveness",
							link: `Abusiveness`,
						},
						{
							fetish: "sadist",
							flaw: "malicious",
							type: "induce maliciousness",
							link: `Maliciousness`,
						},
						{
							fetish: "masochist",
							flaw: "self hating",
							type: "induce self hatred",
							link: `Self hatred`,
						},
						{
							fetish: "submissive",
							flaw: "neglectful",
							type: "induce sexual self neglect",
							link: `Sexual self neglect`,
						},
						{
							fetish: "pregnancy",
							flaw: "breeder",
							type: "induce breeding obsession",
							link: `Breeding obsession`,
						},
					);

					App.UI.DOM.appendNewElement("div", div, App.UI.DOM.generateLinksStrip(links), ['margin-left']);
					links = [];
				} else {
					App.UI.DOM.appendNewElement("div", div, `Paraphilias can be induced from a strong fetish.`, ['note', 'margin-left']);
				}
			}
		}

		return frag;

		/**
		 * @typedef Link
		 *
		 * @property {FC.SexualFlaw|FC.BehavioralFlaw} flaw
		 * @property {FC.SexualQuirk|FC.BehavioralQuirk} [quirk]
		 * @property {FC.Fetish} [fetish]
		 * @property {string} type
		 * @property {string} link
		 */

		/**
		 *
		 * @param {App.Entity.SlaveState} slave
		 * @param {string} regimen
		 * @param {Array<HTMLAnchorElement|HTMLSpanElement>} links
		 * @param {number} index
		 * @param  {Link[]} args
		 */
		function addLinks(slave, regimen, links, index, ...args) {
			args.forEach(link => {
				if (slave.sexualFlaw !== link.flaw
					&& slave.behavioralFlaw !== link.flaw
					&& (!link.quirk || slave.sexualQuirk !== link.quirk
						&& slave.behavioralQuirk !== link.quirk)
					&& (!link.fetish || slave.fetish === link.fetish)) {
					if (regimen === link.type) {
						links.push(App.UI.DOM.disabledLink(link.link, [
							`Currently selected.`,
						]));
					} else {
						links.push(App.UI.DOM.link(link.link, () => {
							// @ts-ignore
							V.personalAttention[index].trainingRegimen = link.type;

							refresh();
						}, null, '', `Inducing flaws is difficult and bad for slaves' obedience.`));
					}
				}
			});
		}
	}

	function slaves() {
		const div = App.UI.DOM.makeElement("div", null, ['margin-bottom']);

		App.UI.DOM.appendNewElement("h3", div, `Select a slave to train`);

		if (V.PC.skill.slaving >= 100) {
			App.Events.addNode(div, [
				`Your <span class="skill player">slaving experience</span> allows you to divide your attention between more than one slave each week, with slightly reduced efficiency.`
			], "div", ['note', 'margin-left']);
		}

		div.append(App.UI.SlaveList.slaveSelectionList(
			s => assignmentVisible(s) && s.fuckdoll === 0,
			s => App.UI.DOM.link(SlaveFullName(s),
				(id) => { selectSlave(id); }, [s.ID]
			),
		));

		return div;
	}

	function refresh() {
		App.UI.DOM.replace(refreshDiv, content());
	}

	function selectSlave(id) {
		if (!Array.isArray(V.personalAttention)) { // first PA target
			V.personalAttention = [{
				ID: id,
				trainingRegimen: null,
			}];
		} else {
			const paIndex = V.personalAttention.findIndex(s => s.ID === id);
			if (paIndex === -1) { // not already a PA target; add
				V.personalAttention.push({
					ID: id,
					trainingRegimen: null,
				});
			} else { // already a PA target; remove
				V.personalAttention.deleteAt(paIndex);
				if (V.personalAttention.length === 0) {
					V.personalAttention = "sex";
				}
			}
		}

		refresh();

		document.getElementById("attention").scrollIntoView();
	}

	/**
	 * @param {App.Entity.SlaveState} slave
	 * @returns {DocumentFragment}
	 */
	 function devotionText(slave) {
		const frag = new DocumentFragment();

		App.UI.SlaveSummaryImpl.helpers.makeRatedStyledSpan(frag, App.Data.SlaveSummary.long.mental.devotion, slave.devotion, 100, true);

		return frag;
	}

	/**
	 * @param {App.Entity.SlaveState} slave
	 * @returns {DocumentFragment}
	 */
	function healthText(slave) {
		const frag = new DocumentFragment();

		App.UI.SlaveSummaryImpl.helpers.makeRatedStyledSpan(frag, App.Data.SlaveSummary.long.health.health, slave.health.condition, 100, true);

		return frag;
	}

	/**
	 * @param {App.Entity.SlaveState} slave
	 * @param {string} regimen
	 * @returns {string}
	 */
	function getRegimen(slave, regimen = null) {
		const {his} = getPronouns(slave);

		if (!regimen) {
			if (slave.health.condition < -20) {
				return `look after ${his} health`;
			} else if (slave.behavioralFlaw !== "none") {
				if (slave.devotion < -20) {
					return `fix ${his} behavioral flaw`;
				} else {
					return `soften ${his} behavioral flaw`;
				}
			} else if (slave.sexualFlaw !== "none") {
				if (slave.devotion < -20) {
					return `fix ${his} sexual flaw`;
				} else {
					return `soften ${his} sexual flaw`;
				}
			} else if (!slave.fetishKnown) {
				return `explore ${his} sexuality`;
			} else if (slave.devotion <= 20 && slave.trust >= -20) {
				return `break ${his} will`;
			}
		}

		return `build ${his} devotion`;
	}
};


code:
/*
receiver - What is the state of the network box.
	-1: PC has not been given a welcome packet. (default)
	0: PC has been given a welcome packet but has not installed it.
	1+: box is installed.
channel[numberAsString] - how many times it has been viewed.
channel.last - program viewed last week.
pcViewership.count - How many weeks since the PC last watched FCTV.
pcViewership.frequency - How often should the PC watch FCTV.
	-1: Never.
	1: at least once a week.
	2: least every two weeks.
	4: at least once a month. (default)
remote - Does the PC have a FCTV branded remote (also used to trigger a slave acquisition event).
weekEnabled - The week FCTV was installed.
*/
globalThis.FCTV = (function() {
	return {
		channels: channels,
		initChannels: initChannels,
		channelCount: channelCount,
		FinalTouches: FinalTouches,
	};

	/** Produce an array of numbers representing all available channels in App.Data.FCTV.channels
	 * @returns {number[]}
	 */
	function channels() {
		return Object.keys(App.Data.FCTV.channels).map(chStr => parseInt(chStr, 10));
	}

	function initChannels() {
		const channelList = FCTV.channels();
		for (let i = 0; i < channelList.length; i++) {
			V.FCTV.channel[num(channelList[i], true)] = 0;
		}
	}

	/**
	 * checks value of selected channel in relation to an arbitrary number. Have we watched it more/less/same than i.
	 * @param {number} selectedChannel
	 * @param {number} i
	 * @param {string} operation eq/gt/lt: equals/greater than/less than.
	 * @returns {boolean}
	 */
	function channelCount(selectedChannel, i, operation = 'eq') {
		if (operation === 'eq') {
			if (V.FCTV.channel[num(selectedChannel, true)] === i) {
				return true;
			}
		} else if (operation === 'gt') {
			if (V.FCTV.channel[num(selectedChannel, true)] > i) {
				return true;
			}
		} else if (operation === 'lt') {
			if (V.FCTV.channel[num(selectedChannel, true)] < i) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Applies some universal changes to FCTV slaves
	 * @param {App.Entity.SlaveState} slave
	 * @returns {App.Entity.SlaveState}
	 */
	function FinalTouches(slave) {
		slave.pubertyXX = 1;
		slave.career = "a slave";
		slave.origin = "You purchased $him from FCTV's Home Slave Shopping stream channel.";
		setHealth(slave, 80, 0, 0, 0, 0);
		return slave;
	}
})();

repo_name: Elohiem/fc

license: mit


code:
// @ts-nocheck
App.Update.FCTV = function() {
	function convert(a, b) {
		return V.FCTVreceiver ? a : b;
	}

	function initChannels() {
		const channelList = FCTV.channels();
		for (let i = 0; i < channelList.length; i++) {
			const channel = num(channelList[i], true);
			const currentChannel = 'show' + capFirstChar(channel);
			V.FCTV.channel[channel] = convert(V[currentChannel], V.FCTV.channel[channel]) || 0;
		}
	}

	V.FCTV = V.FCTV || {};
	V.FCTV.receiver = V.FCTV.receiver > -1 ? V.FCTV.receiver : -1;
	if (convert && V.receiverAvailable) {
		V.FCTV.receiver = V.FCTVreceiver > 0 ? V.FCTVreceiver : 0;
	}
	V.FCTV.pcViewership = V.FCTV.pcViewership || {};
	V.FCTV.channel = V.FCTV.channel || {};
	initChannels();
	V.FCTV.channel.last = convert(V.lastShow, V.FCTV.channel.last) || -1;

	V.FCTV.pcViewership.count = convert(V.FCTVcount, V.FCTV.pcViewership.count) || 0;
	V.FCTV.pcViewership.frequency = convert(V.FCTVrate, V.FCTV.pcViewership.frequency) || 4;
	V.FCTV.remote = convert(V.FCTVremote, V.FCTV.remote) || 0;

	if (V.FCTVreceiver && !V.FCTV.weekEnabled) {
		V.FCTV.weekEnabled = V.receiverAvailable > 1 ? V.receiverAvailable : 0;
	}

	if (V.FCTV.channel.hasOwnProperty("selected")) {
		delete V.FCTV.channel.selected;
	}
};


code:
button.selectedslave { background-color: #b84; }
table.slave-genetic-details { width: 100%; margin: 0 10px; border: 1px solid white; table-layout: fixed; font-size: 85%; border-collapse: separate; padding: 2px; }
table.slave-genetic-details tr { padding: 0 3px; }
table.slave-genetic-details td { vertical-align: top; border: 1px solid black; padding: 0 3px; }
table.slave-genetic-details th { vertical-align: top; background-color: rgba(127, 127, 127, 0.2); padding: 0 3px; }
table.slave-genetic-details td.editor { border: 1px solid #777; }
table.slave-genetic-details td.editor:hover { border: 1px solid #8cf; transition-duration: 0.3s; }
table.slave-genetic-details td.editor input { width: calc(100% + 8px); height: 100%; min-width: 0; margin: 0 -4px; padding: 4px 5px; box-sizing: border-box; }
table.slave-genetic-details td.editor select { width: calc(100% + 8px); height: 100%; min-width: 0; margin: 0 -4px; padding: 3px 5px; box-sizing: border-box; border: 1px solid #777; }
table.slave-genetic-details td.editor select:hover { border: 1px solid #8cf; transition-duration: 0.3s; }


code:
App.UI.editGenetics = function() {
	const node = new DocumentFragment();
	const cupCat = new Categorizer(
		[0, 'flat'],
		[300, 'A-cup'],
		[400, 'B-cup'],
		[500, 'C-cup'],
		[650, 'D-cup'],
		[800, 'DD-cup'],
		[1000, 'F-cup'],
		[1200, 'G-cup'],
		[1400, 'H-cup'],
		[1600, 'I-cup'],
		[1800, 'J-cup'],
		[2050, 'K-cup'],
		[2300, 'L-cup'],
		[2600, 'M-cup'],
		[2900, 'N-cup'],
		[3250, 'O-cup'],
		[3600, 'P-cup'],
		[3950, 'Q-cup'],
		[4300, 'R-cup'],
		[4700, 'S-cup'],
		[5100, 'massive']
	);
	const faceCat = new Categorizer(
		[-Infinity, 'very ugly'],
		[-95, 'ugly'],
		[-40, 'unattractive'],
		[-10, 'attractive'],
		[11, 'very pretty'],
		[41, 'gorgeous'],
		[96, 'mind blowing']
	);
	const lipsCat = new Categorizer(
		[0, 'thin'],
		[11, 'normal'],
		[21, 'pretty'],
		[41, 'plush'],
		[71, 'huge'],
		[96, 'facepussy']
	);
	const hormonalCat = new Categorizer(
		[-Infinity, 'heavy male'],
		[-49, 'male'],
		[-24, 'natural'],
		[25, 'female'],
		[50, 'heavy female']
	);
	const intelligenceCat = new Categorizer(
		[-Infinity, 'borderline retarded'],
		[-95, 'very slow'],
		[-50, 'slow'],
		[-15, 'average'],
		[16, 'smart'],
		[51, 'very smart'],
		[96, 'brilliant']
	);
	function birthFullName(s) {
		let r = [];
		if (V.surnameOrder !== 1) {
			if (["Cambodian", "Chinese", "Hungarian", "Japanese", "Korean", "Mongolian", "Taiwanese", "Vietnamese"].includes(s.nationality)) {
				if (s.birthSurname || s.surname) {
					r.push(s.birthSurname || s.surname);
				}
				r.push(App.UI.DOM.makeElement("span", (s.birthName || s.name), ["bold", "underline"]));
			} else {
				r.push(App.UI.DOM.makeElement("span", (s.birthName || s.name), ["bold", "underline"]));
				if (s.birthSurname || s.surname) {
					r.push(s.birthSurname || s.surname);
				}
			}
		} else {
			r.push(App.UI.DOM.makeElement("span", (s.birthName || s.name), ["bold", "underline"]));
			if (s.birthSurname || s.surname) {
				r.push(s.birthSurname || s.surname);
			}
		}
		return App.Events.makeNode(r);
	}
	function currentFullName(s) {
		let r = [];
		if (V.surnameOrder !== 1) {
			if (["Cambodian", "Chinese", "Hungarian", "Japanese", "Korean", "Mongolian", "Taiwanese", "Vietnamese"].includes(s.nationality)) {
				if (s.slaveSurname) {
					r.push(s.slaveSurname);
				}
				r.push(App.UI.DOM.makeElement("span", s.slaveName, ["bold", "underline"]));
			} else {
				r.push(App.UI.DOM.makeElement("span", s.slaveName, ["bold", "underline"]));
				if (s.slaveSurname) {
					r.push(s.slaveSurname);
				}
			}
		} else {
			r.push(App.UI.DOM.makeElement("span", s.slaveName, ["bold", "underline"]));
			if (s.slaveSurname) {
				r.push(s.slaveSurname);
			}
		}
		return App.Events.makeNode(r);
	}
	function nameButton(s) {
		const button = App.UI.DOM.makeElement("button", birthFullName(s), "slavepicker");
		button.setAttribute("data-slave", s.ID);
		button.style.width = "100%";
		return button;
	}
	function geneDetailsFunction(s) {
		function makeRow() {
			row = App.UI.DOM.appendNewElement("tr", tbody);
		}
		function makeCell(text, colSpan) {
			let cell = App.UI.DOM.appendNewElement("td", row, text);
			if (colSpan) {
				cell.colSpan = colSpan;
			}
		}
		function makeHeader(text) {
			App.UI.DOM.appendNewElement("th", row, text);
		}

		function makeHr() {
			makeRow();
			row.append(document.createElement("td"));
			cell = App.UI.DOM.appendNewElement("td", row, App.UI.DOM.makeElement("hr"));
			cell.colSpan = 6;
			row.append(document.createElement("td"));
		}

		const table = document.createElement("table");
		table.classList.add("slave-genetic-details");
		const tbody = App.UI.DOM.appendNewElement("tbody", table);

		let row = document.createElement("tr");
		let cell;
		makeRow();

		makeHeader(`Given name(s)`);
		makeCell(s.birthName, 2);

		makeHeader(`Family name`);
		makeCell(s.birthSurname || '', 2);

		makeHeader(`Karyotype`);
		cell = App.UI.DOM.appendNewElement("td", row, `${s.genes} (${toSex(s.genes)})`, ["editor", "choice-editor"]);
		cell.setAttribute("data-param", "genes");
		cell.setAttribute("data-choices", "XX, XY");

		makeRow();

		makeHeader(`Nationality`);
		cell = App.UI.DOM.appendNewElement("td", row, s.nationality, ["editor", "string-editor"]);
		cell.colSpan = 2;
		cell.setAttribute("data-param", "nationality");

		makeHeader(`Ethnicity`);
		cell = App.UI.DOM.appendNewElement("td", row, s.origRace, ["editor", "choice-editor"]);
		cell.colSpan = 2;
		cell.setAttribute("data-param", "origRace");
		cell.setAttribute("data-choices", "amerindian, asian, black, indo-aryan, latina, malay, middle eastern, mixed race, pacific islander, semitic, southern european, catgirl, white");

		makeHeader(`Career`);
		cell = App.UI.DOM.appendNewElement("td", row, s.career ? s.career : '', ["note"]);

		makeRow();

		makeHeader(`Father`);
		makeCell(parentFullName(s.father), 2);

		makeHeader(`Mother`);
		makeCell(parentFullName(s.mother), 2);

		makeHeader(`Age`);
		makeCell(ageDesc(s));

		if (s.birthName !== s.slaveName || s.birthSurname !== s.slaveSurname) {
			makeRow();

			cell = App.UI.DOM.appendNewElement("td", row, App.Events.makeNode([`Currently called`, currentFullName(s)]), ["note"]);
			cell.colSpan = 8;
		}

		makeHr();

		makeRow();

		makeHeader(`Skin`);
		cell = App.UI.DOM.appendNewElement("td", row, s.origSkin, ["editor", "string-editor"]);
		cell.setAttribute("data-param", "origSkin");

		makeHeader(`Skin markings`);
		cell = App.UI.DOM.appendNewElement("td", row, s.origSkin, ["editor", "choice-editor"]);
		cell.setAttribute("data-param", "markings");
		cell.setAttribute("data-choices", "none, beauty mark, birthmark, freckles, heavily freckled");

		makeHeader(`Eyes`);
		cell = App.UI.DOM.appendNewElement("td", row, s.eye.origColor, ["editor", "string-editor"]);
		cell.setAttribute("data-param", "eye.origColor");

		makeHeader(`Hair`);
		cell = App.UI.DOM.appendNewElement("td", row, s.origHColor, ["editor", "string-editor"]);
		cell.setAttribute("data-param", "origHColor");

		makeRow();

		makeHeader(`Height`);
		cell = App.UI.DOM.appendNewElement("td", row, `${s.height} cm (${toFeet(s.height)})`, ["editor", "number-editor"]);
		cell.setAttribute("data-param", "height");
		cell.setAttribute("data-min", "50");
		cell.setAttribute("data-max", "250");

		makeHeader(`Weight`);
		cell = App.UI.DOM.appendNewElement("td", row, percent(s.weight), ["editor", "number-editor"]);
		cell.setAttribute("data-param", "weight");

		makeHeader(`Muscles`);
		cell = App.UI.DOM.appendNewElement("td", row, percent(s.muscles), ["editor", "number-editor"]);
		cell.setAttribute("data-param", "muscles");

		cell = App.UI.DOM.appendNewElement("td", row);
		cell.colSpan = 2;

		makeRow();

		makeHeader(`Shoulders`);
		cell = App.UI.DOM.appendNewElement("td", row, shouldersDesc(s.shoulders), ["editor", "number-editor"]);
		cell.setAttribute("data-param", "shoulders");
		cell.setAttribute("data-min", "-2");
		cell.setAttribute("data-max", "2");

		makeHeader(`Waist`);
		cell = App.UI.DOM.appendNewElement("td", row, percent(s.waist), ["editor", "number-editor"]);
		cell.setAttribute("data-param", "waist");

		makeHeader(`Hips`);
		cell = App.UI.DOM.appendNewElement("td", row, hipsDesc(s.hips), ["editor", "number-editor"]);
		cell.setAttribute("data-param", "hips");
		cell.setAttribute("data-min", "-2");
		cell.setAttribute("data-max", "3");

		makeHeader(`Rear`);
		cell = App.UI.DOM.appendNewElement("td", row, buttDesc(s.butt), ["editor", "number-editor"]);
		cell.setAttribute("data-param", "butt");
		cell.setAttribute("data-min", "0");
		cell.setAttribute("data-max", "20");

		makeRow();

		makeHeader(`Breasts`);
		cell = App.UI.DOM.appendNewElement("td", row, `${s.boobs} cc (${cupCat.cat(s.boobs)})`, ["editor", "number-editor"]);
		cell.setAttribute("data-param", "boobs");
		cell.setAttribute("data-min", "0");
		cell.setAttribute("data-max", "100000");

		makeHeader(`Breast shape`);
		cell = App.UI.DOM.appendNewElement("td", row, s.boobShape, ["editor", "choice-editor"]);
		cell.setAttribute("data-param", "boobShape");
		cell.setAttribute("data-choices", "normal, perky, saggy, torpedo-shaped, downward-facing, wide-set");

		makeHeader(`Nipples`);
		cell = App.UI.DOM.appendNewElement("td", row, s.nipples, ["editor", "choice-editor"]);
		cell.setAttribute("data-param", "nipples");
		cell.setAttribute("data-choices", "huge, puffy, inverted, tiny, cute, partially inverted");

		makeHeader(`Areolae`);
		cell = App.UI.DOM.appendNewElement("td", row, areolaeDesc(s.areolae), ["editor", "number-editor"]);
		cell.setAttribute("data-param", "areolae");
		cell.setAttribute("data-min", "0");
		cell.setAttribute("data-max", "5");

		makeRow();

		makeHeader(`Face`);
		cell = App.UI.DOM.appendNewElement("td", row, `${faceCat.cat(s.face)} (${percent(s.face)})`, ["editor", "number-editor"]);
		cell.setAttribute("data-param", "face");

		makeHeader(`Lips`);
		cell = App.UI.DOM.appendNewElement("td", row, `${lipsCat.cat(s.lips)} (${s.lips})`, ["editor", "number-editor"]);
		cell.setAttribute("data-param", "lips");
		cell.setAttribute("data-min", "0");
		cell.setAttribute("data-max", "100");

		makeHeader(`Teeth`);
		cell = App.UI.DOM.appendNewElement("td", row, s.teeth, ["editor", "choice-editor"]);
		cell.setAttribute("data-param", "teeth");
		cell.setAttribute("data-choices", "normal, crooked, straightening braces, cosmetic braces, removable, pointy, baby, mixed");

		makeHeader(`Voice`);
		cell = App.UI.DOM.appendNewElement("td", row, voiceDesc(s.voice), ["editor", "number-editor"]);
		cell.setAttribute("data-param", "voice");
		cell.setAttribute("data-min", "0");
		cell.setAttribute("data-max", "3");

		makeHr();

		if (s.vagina >= 0) {
			makeRow();

			makeHeader(`Labia`);
			cell = App.UI.DOM.appendNewElement("td", row, labiaDesc(s.labia), ["editor", "number-editor"]);
			cell.setAttribute("data-param", "labia");
			cell.setAttribute("data-min", "0");
			cell.setAttribute("data-max", "3");

			makeHeader(`Clitoris`);
			cell = App.UI.DOM.appendNewElement("td", row, clitDesc(s.clit), ["editor", "number-editor"]);
			cell.setAttribute("data-param", "clit");
			cell.setAttribute("data-min", "0");
			cell.setAttribute("data-max", "5");

			makeHeader(`Ovaries`);
			cell = App.UI.DOM.appendNewElement("td", row,
				s.ovaries ?
					App.Events.makeNode(['working', App.UI.DOM.makeElement("span", "\u2714", "green")]) :
					App.Events.makeNode(['broken', App.UI.DOM.makeElement("span", "\u2718", "red")])
			);
			cell.setAttribute("data-param", "ovaries");
			cell.setAttribute("data-min", "0");
			cell.setAttribute("data-max", "5");

			makeHeader(`Ovum type`);
			makeCell(s.eggType);
		}
		if (s.dick > 0 || s.balls > 0) {
			makeRow();

			makeHeader(`Penis`);
			cell = App.UI.DOM.appendNewElement("td", row, penisDesc(s.dick), ["editor", "number-editor"]);
			cell.setAttribute("data-param", "dick");
			cell.setAttribute("data-min", "0");
			cell.setAttribute("data-max", "11");

			makeHeader(`Testes`);
			cell = App.UI.DOM.appendNewElement("td", row, testesDesc(s.balls), ["editor", "number-editor"]);
			cell.setAttribute("data-param", "balls");
			cell.setAttribute("data-min", "0");
			cell.setAttribute("data-max", "10");

			makeHeader(`Prostate`);
			cell = App.UI.DOM.appendNewElement("td", row,
				s.prostate ?
					App.Events.makeNode(['working', App.UI.DOM.makeElement("span", "\u2714", "green")]) :
					App.Events.makeNode(['broken', App.UI.DOM.makeElement("span", "\u2718", "red")]),
			);
			cell.setAttribute("data-param", "prostate");

			makeHeader(`Spermium type`);
			makeCell(s.ballType);
		}

		makeRow();

		makeHeader(`Hormonal`);
		cell = App.UI.DOM.appendNewElement("td", row, `${hormonalCat.cat(s.hormoneBalance)} (${s.hormoneBalance})`, ["editor", "number-editor"]);
		cell.setAttribute("data-param", "hormoneBalance");

		makeHeader(`Anal area`);
		cell = App.UI.DOM.appendNewElement("td", row, s.analArea, ["editor", "number-editor"]);
		cell.setAttribute("data-param", "analArea");
		cell.setAttribute("data-min", "0");
		cell.setAttribute("data-max", "3");

		cell = App.UI.DOM.appendNewElement("td", row);
		cell.colSpan = 4;

		makeHr();

		makeRow();

		makeHeader(`Intelligence`);
		cell = App.UI.DOM.appendNewElement("td", row, intelligenceCat.cat(s.intelligence), ["editor", "number-editor"]);
		cell.setAttribute("data-param", "intelligence");
		cell.setAttribute("data-min", "-100");
		cell.setAttribute("data-max", "100");

		makeHeader(`Behavioral`);
		cell = App.UI.DOM.appendNewElement("td", row, s.behavioralFlaw !== 'none' ? s.behavioralFlaw : s.behavioralQuirk);

		makeHeader(`Sexual`);
		cell = App.UI.DOM.appendNewElement("td", row, s.sexualFlaw !== 'none' ? s.sexualFlaw : s.sexualQuirk);

		cell = App.UI.DOM.appendNewElement("td", row);
		cell.colSpan = 2;

		if (s.chem > 0 || s.addict > 0) {
			makeRow();
			makeCell(chemicalsDesc(s), 8);
		}
		return table;
	}
	function percent(n) {
		if (n > 0) { return '+' + Number(n).toFixed(0) + '%'; }
		if (n === 0) { return '\u00B10%'; }
		return Number(n).toFixed(0) + '%';
	}
	function toFeet(n) {
		let realFeet = Number(n) / 30.48;
		let feet = Math.floor(realFeet);
		let inches = Math.round((realFeet - feet) * 12);
		return feet + "\u2032" + inches + '\u2033';
	}
	function toSex(karyotype) {
		return {
			XX: 'female',
			XY: 'male',
			X: 'Turner syndrome female',
			X0: 'Turner syndrome female',
			XYY: 'XYY syndrome male',
			XXY: 'Klinefelter syndrome male',
			XXX: 'triple X syndrome female'
		}[String(karyotype).toUpperCase()] || 'unknown/not viable';
	}
	function ageDesc(slave) {
		let age = slave.actualAge + (slave.birthWeek + V.week - slave.weekAcquired) / 52.0;
		let years = Math.floor(age);
		let months = Math.floor((age - years) * 12);
		return (years > 0 ? years + 'y ' + months + 'm' : months + 'm');
	}
	function shouldersDesc(s) {
		return ({
			'-2': 'very narrow',
			'-1': 'narrow',
			'0': 'feminine',
			'1': 'broad',
			'2': 'very broad'
		}[s] || 'unknown') + ' (' + Number(s) + ')';
	}
	function hipsDesc(s) {
		return ({
			'-2': 'very narrow',
			'-1': 'narrow',
			'0': 'normal',
			'1': 'wide',
			'2': 'very wide',
			'3': 'inhumanly wide'
		}[s] || 'unknown') + ' (' + Number(s) + ')';
	}
	function buttDesc(s) {
		return ({
			'0': 'flat',
			'1': 'small',
			'2': 'plump',
			'3': 'big',
			'4': 'huge',
			'5': 'enormous',
			'6': 'gigantic',
			'7': 'ridiculous',
			'8': 'immense',
			'9': 'immense',
			'10': 'immense',
			'11': 'inhuman',
			'12': 'inhuman',
			'13': 'inhuman',
			'14': 'inhuman',
			'15': 'inhuman',
			'16': 'inhuman',
			'17': 'inhuman',
			'18': 'inhuman',
			'19': 'inhuman',
			'20': 'inhuman'
		}[s] || 'unknown') + ' (' + Number(s) + ')';
	}
	function areolaeDesc(s) {
		return ({
			'0': 'normal',
			'1': 'large',
			'2': 'unusually wide',
			'3': 'huge',
			'4': 'heart shaped',
			'5': 'star shaped'
		}[s] || 'unknown') + ' (' + Number(s) + ')';
	}
	function voiceDesc(s) {
		return ({
			'0': 'mute',
			'1': 'deep',
			'2': 'feminine',
			'3': 'high'
		}[s] || 'unknown') + ' (' + Number(s) + ')';
	}
	function labiaDesc(s) {
		return ({
			'0': 'minimal',
			'1': 'big',
			'2': 'huge',
			'3': 'huge dangling'
		}[s] || 'unknown') + ' (' + Number(s) + ')';
	}
	function clitDesc(s) {
		return ({
			'0': 'normal',
			'1': 'large',
			'2': 'huge',
			'3': 'enormous',
			'4': 'penis-like',
			'5': 'like a massive penis'
		}[s] || 'unknown') + ' (' + Number(s) + ')';
	}
	function penisDesc(s) {
		return ({
			'0': 'none',
			'1': 'tiny',
			'2': 'little',
			'3': 'normal',
			'4': 'big',
			'5': 'huge',
			'6': 'gigantic',
			'7': 'gigantic',
			'8': 'titanic',
			'9': 'absurd',
			'10': 'inhuman',
			'11': 'hypertrophied'
		}[s] || 'unknown') + ' (' + Number(s) + ')';
	}
	function testesDesc(s) {
		return ({
			'0': 'none',
			'1': 'vestigial',
			'2': 'small',
			'3': 'average',
			'4': 'big',
			'5': 'huge',
			'6': 'enormous',
			'7': 'hypertrophied', '8': 'hypertrophied', '9': 'hypertrophied', '10': 'hypertrophied'
		}[s] || 'unknown') + ' (' + Number(s) + ')';
	}
	function chemicalsDesc(slave) {
		let res = [];
		if (slave.chem > 0) {
			res.push('Immune system damaged (' + slave.chem.toFixed(0) + ').');
		}
		if (slave.addict > 0) {
			res.push('Addicted to aphrodisiacs (' + slave.addict.toFixed(0) + ').');
		}
		return res.join(' ');
	}
	function parentFullName(id) {
		if (id === 0) {
			return App.UI.DOM.makeElement("span", "unknown", "note");
		}
		if (id === -1) {
			/* The PC */
			return birthFullName(V.PC) + ' (PC)';
		} else {
			let parent = V.genePool.find(function(s) {
				return s.ID === id;
			});
			return parent ?
				birthFullName(parent) :
				App.Events.makeNode([
					App.UI.DOM.makeElement("span", "missing", "note"),
					`(${id})`
				]);
		}
	}

	let outerDiv = App.UI.DOM.appendNewElement("div", node);
	outerDiv.style.position = "relative";
	outerDiv.style.width = "100%";
	outerDiv.style.height = "100%";

	let innerDiv = App.UI.DOM.appendNewElement("div", outerDiv);
	innerDiv.id = "slaveList";
	innerDiv.style.width = "20em";
	innerDiv.style.float = "left";

	innerDiv = App.UI.DOM.appendNewElement("div", outerDiv);
	innerDiv.id = "geneDetails";
	innerDiv.style.position = "fixed";
	innerDiv.style.left = "0";
	innerDiv.style.right = "0";
	innerDiv.style.top = "0";
	innerDiv.style.bottom = "0";
	innerDiv.style.marginLeft = "40em";
	innerDiv.style.marginRight = "2.5em";
	innerDiv.style.marginTop = "2.5em";

	jQuery(function() {
		let slaveList = jQuery('#slaveList');
		_(V.genePool).sortBy(['birthName', 'birthSurname']).forEach(function(s) {
			slaveList.append(nameButton(s));
		});
		jQuery(document).off('.edit-genetics');
		jQuery(document).on('keyup.edit-genetics', function(e) {
			if (slaveList.is(':visible')) {
				let currentSelection = jQuery('button.selectedslave');
				switch (e.keyCode) {
					case 38: /* ArrowUp */
						if (currentSelection.length === 0) {
							slaveList.find('button').last().trigger('click');
						} else {
							currentSelection.prev().trigger('click');
						}
						break;
					case 40: /* ArrowDown */
						if (currentSelection.length === 0) {
							slaveList.find('button').first().trigger('click');
						} else {
							currentSelection.next().trigger('click');
						}
						break;
					default:
						/* do nothing */
						break;
				}
			}
		});
		let geneDetails = jQuery('#geneDetails');
		jQuery('.slavepicker').each(function() {
			let el = jQuery(this);
			let id = Number(el.attr('data-slave'));
			el.on('click', function() {
				jQuery('button.selectedslave').removeClass('selectedslave');
				el.addClass('selectedslave');
				let slave = V.genePool.find(function(s) { return s.ID === id; });
				geneDetails.html(geneDetailsFunction(slave));

				let numberEditorOpen = function() {
					let td = jQuery(this);
					td.off('click');
					let attr = String(td.attr('data-param'));
					let min = Number(td.attr('data-min')) || -100;
					let max = Number(td.attr('data-max')) || 100;
					let editableText = jQuery('<input type="number" min="' + min + '" max="' + max + '" />');
					editableText.val(Number(slave[attr]));
					editableText.keyup(function(e) {
						if (e.keyCode === 13) {
							slave[attr] = Number(jQuery(this).val());
							el.trigger('click');
						}
					});
					editableText.blur(function() {
						slave[attr] = Number(jQuery(this).val());
						el.trigger('click');
					});
					td.empty().append(editableText);
					editableText.focus();
				};
				geneDetails.find('td.number-editor').click(numberEditorOpen);
				let stringEditorOpen = function() {
					let td = jQuery(this);
					td.off('click');
					let attr = String(td.attr('data-param'));
					let editableText = jQuery('<input type="text" />');
					editableText.val(String(slave[attr]));
					editableText.keyup(function(e) {
						if (e.keyCode === 13) {
							slave[attr] = String(jQuery(this).val());
							el.trigger('click');
						}
					});
					editableText.blur(function() {
						slave[attr] = String(jQuery(this).val());
						el.trigger('click');
					});
					td.empty().append(editableText);
					editableText.focus();
				};
				geneDetails.find('td.string-editor').click(stringEditorOpen);
				let choiceEditorOpen = function() {
					let td = jQuery(this);
					td.off('click');
					let attr = String(td.attr('data-param'));
					let choices = String(td.attr('data-choices')).split(/,\s*/);
					let editableText = jQuery('<select>' + choices.map(ch => '<option>' + ch + '</option>').join('') + '</select>');
					editableText.val(String(slave[attr]));
					editableText.keyup(function(e) {
						if (e.keyCode === 13) {
							slave[attr] = String(jQuery(this).val());
						}
					});
					editableText.blur(function() {
						slave[attr] = String(jQuery(this).val());
					});
					td.empty().append(editableText);
					editableText.focus();
				};
				geneDetails.find('td.choice-editor').each(choiceEditorOpen);
			});
		});
	});

	return node;
};


code:
globalThis.eliteBreedingExam = function(slave = null) {
	const frag = new DocumentFragment();
	const eliteAngered = V.failedElite > 100;
	const SlaveProfessionalismEstablished = V.arcologies[0].FSSlaveProfessionalism > 20;
	const preferYounger = V.arcologies[0].FSYouthPreferentialist > 20;
	const partyAnimal = V.arcologies[0].FSHedonisticDecadence > 20;
	const implantsExpected = V.arcologies[0].FSTransformationFetishist > 20;
	const cost = 5000;
	let r = [];
	let test;
	let passing = 0;
	let fixable;

	const result = function() {
		if (slave) {
			if (test === true) {
				return App.UI.DOM.makeElement("span", "PASSED", "lime");
			} else if (fixable) {
				return App.UI.DOM.makeElement("span", "Within fixable range.", "yellow");
			} else {
				passing--;
				return App.UI.DOM.makeElement("span", "FAILED", "red");
			}
		}
		return ``;
	};

	r.push(`For each slave be to examined, a fee of <span class='yellowgreen'>${cashFormat(cost)}</span> will be deducted from your account.`);
	r.push(`Updates will be posted periodically. It is your responsibility to keep up to date.`);
	r.push(`Failures will be sterilized. Please make sure your slave fits the following criteria before submitting them for testing. We shall not be held responsible for time wasted.`);
	if (!slave) {
		r.push(`Must be not; already be marked as a breeding slave, inside a fuckdoll suit, infertile and already with child.`);
	} else {
		r.push(`${slave.slaveName} is up for review:`);
	}
	App.Events.addNode(frag, r, "div");
	r = [];
	const list = App.UI.DOM.appendNewElement("ul", frag);
	if (slave) {
		test = slave.intelligence + slave.intelligenceImplant >= (eliteAngered ? 95 : 50);
	}
	App.UI.DOM.appendNewElement("li", list, `Must be ${eliteAngered ? 'highly intelligent' : 'very smart'}. `).append(result());

	if (slave) {
		test = Beauty(slave) >= (eliteAngered ? 120 : 100);
	}
	App.UI.DOM.appendNewElement("li", list, `Must be ${eliteAngered ? 'unbelievably' : ''} beautiful. `).append(result());

	if (slave) {
		test = slave.face >= (eliteAngered ? 95 : 40);
	}
	App.UI.DOM.appendNewElement("li", list, `Their face must be ${eliteAngered ? 'breathtaking' : 'gorgeous'}. `).append(result());

	if (slave) {
		test = slave.chem <= 20;
	}
	App.UI.DOM.appendNewElement("li", list, `Their health must not be overly impacted by drug use. `).append(result());

	if (slave) {
		test = slave.addict === 0;
	}
	App.UI.DOM.appendNewElement("li", list, `They must not be addicted to drugs. `).append(result());

	if (V.arcologies[0].FSMaturityPreferentialist > 20 || V.arcologies[0].FSYouthPreferentialist > 20) {
		const targetAge = 24;
		if (slave) {
			if (V.arcologies[0].FSMaturityPreferentialist > 20) {
				test = slave.physicalAge > targetAge;
			} else {
				test = slave.physicalAge < targetAge;
			}
		}
		App.UI.DOM.appendNewElement("li", list, `Must be ${preferYounger ? 'younger' : 'older'} than ${targetAge}. `).append(result());
	}

	if (V.arcologies[0].FSIntellectualDependency > 20) {
		if (slave) {
			test = slave.energy >= 96;
		}
		App.UI.DOM.appendNewElement("li", list, `Must be sexually receptive at all times. `).append(result());
	} else if (V.arcologies[0].FSSlaveProfessionalism > 20) {
		if (slave) {
			test = slave.skill.vaginal + slave.skill.anal + slave.skill.oral + slave.skill.entertainment + slave.skill.whoring >= (eliteAngered ? 500 : 400);
		}
		App.UI.DOM.appendNewElement("li", list, `Must be capable of mastering skills. `).append(result());
		if (slave) {
			test = slave.accent <= 1;
		}
		App.UI.DOM.appendNewElement("li", list, `Must be capable of eloquent and accurate speech. `).append(result());
	}
	if (V.arcologies[0].FSPetiteAdmiration > 20 || V.arcologies[0].FSStatuesqueGlorification > 20) {
		const targetHeight = V.arcologies[0].FSPetiteAdmiration > 20 ? 150 : 186;
		if (V.arcologies[0].FSPetiteAdmiration > 20) {
			if (slave) {
				test = (slave.height - (slave.heightImplant * 10)) < targetHeight;
			}
			App.UI.DOM.appendNewElement("li", list, `Must naturally be very short. (${targetHeight} cm) `).append(result());
			if (slave) {
				test = (slave.height - (slave.heightImplant * 10)) < Height.mean(slave) - 15;
			}
			App.UI.DOM.appendNewElement("li", list, `Must naturally be very short for their age. `).append(result());
		} else if (V.arcologies[0].FSStatuesqueGlorification > 20) {
			if (slave) {
				test = (slave.height - (slave.heightImplant * 10)) > targetHeight;
			}
			App.UI.DOM.appendNewElement("li", list, `Must naturally be very tall. (${targetHeight} cm) `).append(result());
		}
	}

	if (V.arcologies[0].FSSupremacist > 20 || V.arcologies[0].FSSubjugationist > 20) {
		const targetRace = V.arcologies[0].FSSupremacist > 20 ? V.arcologies[0].FSSupremacistRace : V.arcologies[0].FSSubjugationistRace;
		if (V.arcologies[0].FSSupremacist > 20) {
			if (slave) {
				test = slave.race === targetRace;
			}
			App.UI.DOM.appendNewElement("li", list, `Must be ${targetRace}. `).append(result());
		} else {
			if (slave) {
				test = slave.race !== targetRace;
			}
			App.UI.DOM.appendNewElement("li", list, `Must not be ${targetRace}. `).append(result());
		}
	}

	if (V.arcologies[0].FSGenderRadicalist > 20) {
		if (slave) {
			test = slave.dick >= 1 && slave.balls >= 1;
		}
		App.UI.DOM.appendNewElement("li", list, `Must have a functional penis. `).append(result());
	} else if (V.arcologies[0].FSGenderFundamentalist > 20) {
		if (slave) {
			test = slave.dick === 0 && slave.balls === 0;
		}
		App.UI.DOM.appendNewElement("li", list, `Must be physically female. `).append(result());
	}
	if (V.arcologies[0].FSPaternalist > 20) {
		if (slave) {
			test = slave.intelligenceImplant >= (SlaveProfessionalismEstablished ? 30 : 15);
		}
		App.UI.DOM.appendNewElement("li", list, `Must be ${SlaveProfessionalismEstablished ? 'well' : ''} educated. `).append(result());
		if (slave) {
			test = slave.health.condition >= 60;
		}
		App.UI.DOM.appendNewElement("li", list, `Must be in good health. `).append(result());
	// } else if (V.arcologies[0].FSDegradationist > 20) {
	}

	if (SlaveProfessionalismEstablished) {
		if (slave) {
			test = slave.intelligenceImplant >= 15;
		}
		App.UI.DOM.appendNewElement("li", list, `Must be well educated. `).append(result());
	}

	if (V.arcologies[0].FSBodyPurist > 20) {
		if (slave) {
			test = slave.chem <= (eliteAngered ? 0 : 15);
		}
		App.UI.DOM.appendNewElement("li", list, `Must have low carcinogen levels. `).append(result());
		if (slave) {
			test = slave.boobsImplant + slave.buttImplant + slave.lipsImplant + slave.hipsImplant + slave.shouldersImplant === 0 && slave.faceImplant <= 5 && slave.bellyImplant === -1;
		}
		App.UI.DOM.appendNewElement("li", list, `Must be implant free. `).append(result());
	// } else if (V.arcologies[0].FSTransformationFetishist > 20) {
	//	idealSlave.chem = 20;
	}
	if (V.arcologies[0].FSSlimnessEnthusiast > 20) {
		if (slave) {
			test = slave.weight <= (partyAnimal ? 30 : 0);
		}
		if (partyAnimal) {
			App.UI.DOM.appendNewElement("li", list, `May be no larger than "plush". `).append(result());
		} else {
			App.UI.DOM.appendNewElement("li", list, `Must be thin. `).append(result());
		}
		if (slave) {
			test = slave.butt <= 2;
		}
		App.UI.DOM.appendNewElement("li", list, `Must have a trim rear. `).append(result());
		if (slave) {
			test = slave.boobs <= 400;
		}
		App.UI.DOM.appendNewElement("li", list, `Must have a sleek chest. `).append(result());
	} else if (V.arcologies[0].FSAssetExpansionist > 20) {
		if (slave) {
			test = (implantsExpected ? (slave.butt) : (slave.butt - slave.buttImplant)) >= 6;
		}
		App.UI.DOM.appendNewElement("li", list, `Must have a ${implantsExpected ? '' : 'naturally'} gigantic rear. `).append(result());
		if (slave) {
			test = (implantsExpected ? (slave.boobs) : (slave.boobs - slave.boobsImplant)) >= 1000;
		}
		App.UI.DOM.appendNewElement("li", list, `Must be ${implantsExpected ? '' : 'naturally'} busty. `).append(result());
	}
	if (V.arcologies[0].FSPastoralist > 20) {
		if (slave) {
			test = slave.lactation === 1;
		}
		App.UI.DOM.appendNewElement("li", list, `Must be lactating naturally. `).append(result());
	}
	if (V.arcologies[0].FSPhysicalIdealist > 20) {
		if (V.arcologies[0].FSPhysicalIdealistLaw === 1) {
			const musclesMin = 20;
			const musclesMax = 50;
			if (slave) {
				test = slave.weight <= 30;
			}
			App.UI.DOM.appendNewElement("li", list, `Must not be overweight. `).append(result());
			if (slave) {
				test = slave.muscles.isBetween(musclesMin, musclesMax);
				fixable = (slave.muscles <= musclesMin && slave.muscles > musclesMax - musclesMin) || (slave.muscles >= musclesMax && slave.muscles < musclesMin + 30);
				if (fixable && passing === 0) {
					passing = 2;
				}
			}
			App.UI.DOM.appendNewElement("li", list, `Must be fit, but not too muscular. `).append(result());
		} else {
			if (slave) {
				test = slave.muscles > 95;
			}
			App.UI.DOM.appendNewElement("li", list, `Must be extremely muscular. `).append(result());
		}
	} else if (V.arcologies[0].FSHedonisticDecadence > 20) {
		const targetWeight = V.arcologies[0].FSSlimnessEnthusiast > 20 ? 10 : 95;
		if (slave) {
			test = slave.weight > targetWeight;
		}
		if (V.arcologies[0].FSSlimnessEnthusiast > 20) {
			App.UI.DOM.appendNewElement("li", list, `Must be more than "trim". `).append(result());
		} else {
			App.UI.DOM.appendNewElement("li", list, `Must be big, soft and fat. `).append(result());
		}
	}

	if (slave) {
		if (passing === 2) {
			r.push(`The aforementioned slave has been deemed currently unsuitable for breeding, but may be resubmitted after corrections are applied. We understand the subjective nature of "fit" and do not wish to preclude the use a viable breeder over such a trivial fix. A contraceptive routine has been applied to protect against "accidents" in the interim.`);
			slave.preg = -1;
		} else if (passing !== 0) {
			r.push(`The aforementioned slave has been deemed <span class="red">unsuitable</span> for breeding.`);
			slave.preg = -3;
		} else {
			r.push(`The aforementioned slave has been deemed <span class="green">worthy</span> of being used for breeding and has been marked as such. Please note the increased restrictions on breeding slaves.`);
			r.push(`They are not permitted for public use or anything that may harm their growing child. The child within them is considered a member of the Elite class and as such, any harm that comes to them will result in severe penalties to the breeder's owner. Development of the child will be closely monitored; should the fetus be identified as not of the owner's blood (or any other member of the Elite class), said owner shall face severe fines.`);
			slave.breedingMark = 1;
			slave.pregControl = "none";
			removeJob(slave, slave.assignment);
		}
		cashX(-cost, "capEx");
	}
	App.Events.addNode(frag, r, "div");
	return frag;
};


code:
App.UI.surrogacy = function() {
	const node = new DocumentFragment();
	const receptrix = getReceptrix();
	const donatrix = getDonatrix();
	const impregnatrix = getImpregnatrix();
	const wombIndex = V.wombIndex;
	const slave = getSlave(V.AS);
	let r = [];

	function getReceptrix() {
		if (V.receptrix && V.receptrix.ID) {
			return V.receptrix.ID === -1 ? V.PC : getSlave(V.receptrix.ID);
		}
	}

	function getDonatrix() {
		if (V.donatrix && V.donatrix.ID) {
			return V.donatrix.ID === -1 ? V.PC : getSlave(V.donatrix.ID);
		}
	}

	function getImpregnatrix() {
		if (V.impregnatrix && V.impregnatrix.ID) {
			if (V.impregnatrix.ID === -1) {
				return V.PC;
			} else {
				return getSlave(V.impregnatrix.ID) || V.incubator.tanks.find(t => t.ID === V.impregnatrix.ID);
			}
		}
	}

	switch (V.surgeryType) {
		case "surrogacy":
			if (receptrix.ID === -1) {
				r.push(`Since the surgery required only a local anesthetic, you remain fully aware of the procedure as the autosurgery carries it out. You slowly rise to your feet, a hand to your lower belly, appreciating the new life growing within you.`);
				V.PC.pregKnown = 1;
				WombSurrogate(V.PC, 1, donatrix, impregnatrix.ID, 1);
				WombNormalizePreg(V.PC);
			} else {
				receptrix.pregKnown = 1;
				WombSurrogate(receptrix, 1, donatrix, impregnatrix.ID, 1);
				WombNormalizePreg(receptrix);
				const {
					He,
					he, his, him
				} = getPronouns(receptrix);
				if (receptrix.fetish === "mindbroken") {
					r.push(`${receptrix.slaveName} leaves the surgery with a certain warmth in ${his} lower abdomen, ${he} knows that ${he} has been impregnated.`);
				} else if (receptrix.fetish === "pregnancy" && receptrix.fetishStrength > 60 && receptrix.fetishKnown === 1) {
					if (canSee(receptrix)) {
						r.push(`Since the surgery required only a local anesthetic, ${receptrix.slaveName} remained fully aware throughout the procedure. ${He} was overjoyed at the sight of the syringe containing ${his} future child emptying into ${his} womb.`);
					} else {
						r.push(`${receptrix.slaveName} leaves the surgery with a certain warmth in ${his} lower abdomen, ${he} knows that ${he} has been impregnated.`);
					}
					r.push(`${He} is  filled with joy over the life settling into ${his} womb and can't wait to see the result. ${He}'s so pleased that ${he} now trusts your plans for ${his} body.`);
					receptrix.trust += 4;
					receptrix.devotion += 10;
				} else if (receptrix.devotion > 50) {
					if (canSee(receptrix)) {
						r.push(`Since the surgery required only a local anesthetic, ${receptrix.slaveName} remained fully aware throughout the procedure. ${He} watched the syringe containing ${his} new pregnancy empty into ${his} womb with rapt attention.`);
					} else {
						r.push(`${receptrix.slaveName} leaves the surgery with a certain warmth in ${his} lower abdomen, ${he} knows that ${he} has been impregnated.`);
					}
					r.push(`${He}'s <span class="devotion inc">grateful</span> that you think ${him} worthy of carrying this child, and a little nervous about how ${he}'ll perform as a surrogate.`);
					receptrix.devotion += 4;
				} else if (receptrix.devotion >= -20) {
					if (canSee(receptrix)) {
						r.push(`Since the surgery required only a local anesthetic, ${receptrix.slaveName} remained fully aware throughout the procedure. From the syringe making contact with ${his} skin, to the egg's delivery into ${his} womb and ${his} subsequent impregnation, ${he} couldn't look away.`);
					} else {
						r.push(`${receptrix.slaveName} leaves the surgery with a certain warmth in ${his} lower abdomen, ${he} knows that ${he} has been impregnated.`);
					}
					r.push(`${He} understands the realities of ${his} life as a slave, so it isn't much of a shock. ${He} is <span class="trust dec">sensibly fearful</span> of your total power over ${his} body.`);
					receptrix.trust -= 10;
				} else {
					if (canSee(receptrix)) {
						r.push(`Since the surgery required only a local anesthetic, ${receptrix.slaveName} remained fully aware throughout the procedure. The moment ${he} realized what was happening, ${he} shut ${his} eyes tight, only opening them again as ${he} feels the slight tingle of the injector exiting ${his} lower abdomen.`);
					} else {
						r.push(`${receptrix.slaveName} leaves the surgery with a certain warmth in ${his} lower abdomen, ${he} knows that ${he} has been impregnated.`);
					}
					r.push(`${He} does not understand the realities of ${his} life as a slave at a core level, so ${he}'s terrified and angry that you have forced ${him} to bear this child, even more so as ${he} realizes ${he} doesn't know who the father is. ${He} is sensibly fearful of your total power over ${his} body and the future of the life ${he} now harbors within ${him}.`);
					receptrix.trust -= 15;
					receptrix.devotion -= 15;
				}
			}
			V.receptrix = 0;
			V.impregnatrix = 0;
			V.donatrix = 0;
			break;
		case "transplant":
			if (receptrix.ID === -1) {
				r.push(`Since the surgery required only a local anesthetic, you are very aware that you are now carrying ${slave.slaveName}'s child. You slowly rise to your feet, a hand to your lower belly, appreciating the new life growing within you.`);
				const fetus = WombRemoveFetus(slave, wombIndex);
				WombAddFetus(V.PC, fetus);
				V.PC.pregKnown = 1;
				V.PC.preg = WombMaxPreg(V.PC);
				slave.preg = WombMaxPreg(slave);
				WombNormalizePreg(V.PC);
				WombNormalizePreg(slave);
			} else {
				const fetus = WombRemoveFetus(donatrix, wombIndex);
				WombAddFetus(receptrix, fetus);
				receptrix.pregKnown = 1;
				receptrix.preg = WombMaxPreg(receptrix);
				donatrix.preg = WombMaxPreg(donatrix);
				WombNormalizePreg(receptrix);
				WombNormalizePreg(donatrix);
				if (donatrix.ID === -1) {
					V.PC = donatrix;
				}
				const {
					He,
					he, his, him
				} = getPronouns(receptrix);
				if (receptrix.fetish === "mindbroken") {
					r.push(`${receptrix.slaveName} leaves the surgery with a certain warmth in ${his} lower abdomen, ${he} knows that ${he} has been impregnated.`);
				} else if ((receptrix.fetish === "pregnancy") && (receptrix.fetishStrength > 60) && (receptrix.fetishKnown === 1)) {
					if (canSee(receptrix)) {
						r.push(`Since the surgery required only a local anesthetic, ${receptrix.slaveName} remained fully aware throughout the procedure. ${He} was overjoyed at the sight of the syringe containing ${his} future child emptying into ${his} womb.`);
					} else {
						r.push(`${receptrix.slaveName} leaves the surgery with a certain warmth in ${his} lower abdomen, ${he} knows that ${he} has been impregnated.`);
					}
					r.push(`${He} is  filled with joy over the life resettling into ${his} womb and can't wait to see the result. ${He}'s so pleased that ${he} now trusts your plans for ${his} body.`);
					receptrix.trust += 4;
					receptrix.devotion += 10;
				} else if (receptrix.devotion > 50) {
					if (canSee(receptrix)) {
						r.push(`Since the surgery required only a local anesthetic, ${receptrix.slaveName} remained fully aware throughout the procedure. ${He} watched the syringe containing ${his} new pregnancy empty into ${his} womb with rapt attention.`);
					} else {
						r.push(`${receptrix.slaveName} leaves the surgery with a certain warmth in ${his} lower abdomen, ${he} knows that ${he} has been impregnated.`);
					}
					r.push(`${He}'s <span class="devotion inc">grateful</span> that you think ${him} worthy of carrying this child in`);
					if (donatrix.ID === -1) {
						r.push(`your stead,`);
					} else {
						r.push(`lieu of ${donatrix.slaveName},`);
					}
					r.push(`and a little nervous about how ${he}'ll perform as a surrogate.`);
					receptrix.devotion += 4;
				} else if (receptrix.devotion >= -20) {
					if (canSee(receptrix)) {
						r.push(`Since the surgery required only a local anesthetic, ${receptrix.slaveName} remained fully aware throughout the procedure. From the syringe making contact with ${his} skin, to the egg's delivery into ${his} womb and ${his} subsequent impregnation, ${he} couldn't look away.`);
					} else {
						r.push(`${receptrix.slaveName} leaves the surgery with a certain warmth in ${his} lower abdomen, ${he} knows that ${he} has been impregnated.`);
					}
					r.push(`${He} understands the realities of ${his} life as a slave, so it isn't much of a shock. ${He} is sensibly fearful of your total power over ${his} body and how you are forcing`);
					if (donatrix.ID === -1) {
						r.push(`your pregnancy upon ${him}.`);
					} else {
						r.push(`${him} to carry what should have been ${donatrix.slaveName}'s burden.`);
					}
					receptrix.trust -= 10;
				} else {
					if (canSee(receptrix)) {
						r.push(`Since the surgery required only a local anesthetic, ${receptrix.slaveName} remained fully aware throughout the procedure. The moment ${he} realized what was happening, ${he} shut ${his} eyes tight, only opening them again as ${he} feels the slight tingle of the injector exiting ${his} lower abdomen.`);
					} else {
						r.push(`${receptrix.slaveName} leaves the surgery with a certain warmth in ${his} lower abdomen, ${he} knows that ${he} has been impregnated.`);
					}
					r.push(`${He} does not understand the realities of ${his} life as a slave at a core level, so ${he}'s <span class="devotion dec">terrified and angry</span> that you have forced ${him} to`);
					if (donatrix.ID === -1) {
						r.push(`carry your child instead of doing so yourself.`);
					} else {
						r.push(`bear this child in lieu of ${donatrix.slaveName}.`);
					}
					r.push(`${He} is <span class="trust dec">sensibly fearful</span> of your total power over ${his} body and who else's troubles may be levied upon ${him}.`);
					receptrix.trust -= 15;
					receptrix.devotion -= 15;
				}
			}
			V.receptrix = 0;
			V.donatrix = 0;
			V.wombIndex = 0;
			break;
		case "clone":
			if (receptrix.ID === -1) {
				r.push(`Since the surgery required only a local anesthetic, you remain fully aware of the procedure as the autosurgery carries it out. You slowly rise to your feet, a hand to your lower belly, appreciating the clone growing within you.`);
				V.PC.pregKnown = 1;
				WombImpregnateClone(V.PC, 1, donatrix, 1);
				WombNormalizePreg(V.PC);
			} else {
				receptrix.pregKnown = 1;
				WombImpregnateClone(receptrix, 1, donatrix, 1);
				WombNormalizePreg(receptrix);
				const {
					He,
					he, his, him
				} = getPronouns(receptrix);
				if (receptrix.fetish === "mindbroken") {
					r.push(`${receptrix.slaveName} leaves the surgery with a certain warmth in ${his} lower abdomen,`);
					if (receptrix.ID === donatrix.ID) {
						r.push(`but has no idea ${he} carries ${his} own clone.`);
					} else {
						r.push(`${he} knows that ${he} has been impregnated.`);
					}
				} else if (receptrix.fetish === "pregnancy" && receptrix.fetishStrength > 60 && receptrix.fetishKnown === 1) {
					if (canSee(receptrix)) {
						r.push(`Since the surgery required only a local anesthetic, ${receptrix.slaveName} remained fully aware throughout the procedure. ${He} was overjoyed at the sight of the syringe containing ${his}`);
						if (receptrix.ID === donatrix.ID) {
							r.push(`clone`);
						} else {
							r.push(`future child`);
						}
						r.push(`emptying into ${his} womb.`);
					} else {
						r.push(`${receptrix.slaveName} leaves the surgery with a certain warmth in ${his} lower abdomen, ${he} knows that ${he} has been impregnated${(receptrix.ID === donatrix.ID) ? `, but not that ${he} now bears ${his} own clone` : ''}.`);
					}
					r.push(`${He} is  filled with joy over the life settling into ${his} womb and can't wait to see the result. ${He}'s so pleased that ${he} now trusts your plans for ${his} body.`);
					receptrix.trust += 4;
					receptrix.devotion += 10;
				} else if (receptrix.devotion > 50) {
					if (canSee(receptrix)) {
						r.push(`Since the surgery required only a local anesthetic, ${receptrix.slaveName} remained fully aware throughout the procedure. ${He} watched the syringe containing ${his} new pregnancy empty into ${his} womb with rapt attention.`);
					} else {
						r.push(`${receptrix.slaveName} leaves the surgery with a certain warmth in ${his} lower abdomen, ${he} knows that ${he} has been impregnated${(receptrix.ID === donatrix.ID) ? `, but not that ${he} now bears ${his} own clone` : ''}.`);
					}
					r.push(`${He}'s <span class="devotion inc">grateful</span> that you think ${him} worthy of carrying this child, and a little nervous about how ${he}'ll perform as a surrogate.`);
					receptrix.devotion += 4;
				} else if (receptrix.devotion >= -20) {
					if (canSee(receptrix)) {
						r.push(`Since the surgery required only a local anesthetic, ${receptrix.slaveName} remained fully aware throughout the procedure. From the syringe making contact with ${his} skin, to the egg's delivery into ${his} womb and ${his} subsequent impregnation, ${he} couldn't look away.`);
					} else {
						r.push(`${receptrix.slaveName} leaves the surgery with a certain warmth in ${his} lower abdomen, ${he} knows that ${he} has been impregnated${(receptrix.ID === donatrix.ID) ? `, but not that ${he} now bears ${his} own clone` : ''}.`);
					}
					r.push(`${He} understands the realities of ${his} life as a slave, so it isn't much of a shock. ${He} is <span class="trust dec">sensibly fearful</span> of your total power over ${his} body.`);
					receptrix.trust -= 10;
				} else {
					if (canSee(receptrix)) {
						r.push(`Since the surgery required only a local anesthetic, ${receptrix.slaveName} remained fully aware throughout the procedure. The moment ${he} realized what was happening, ${he} shut ${his} eyes tight, only opening them again as ${he} feels the slight tingle of the injector exiting ${his} lower abdomen.`);
					} else {
						r.push(`${receptrix.slaveName} leaves the surgery with a certain warmth in ${his} lower abdomen, ${he} knows that ${he} has been impregnated.`);
					}
					r.push(`${He} does not understand the realities of ${his} life as a slave at a core level, so ${he}'s <span class="devotion dec">terrified and angry</span> that you have forced ${him} to bear`);
					if (receptrix.ID === donatrix.ID) {
						r.push(`${his} own clone and potential replacement.`);
					} else {
						r.push(`this child, even more so as ${he} realizes ${he} doesn't know who the father is.`);
					}
					r.push(`${He} is <span class="trust dec">sensibly fearful</span> of your total power over ${his} body and the future of the life ${he} now harbors within ${him}.`);
					receptrix.trust -= 15;
					receptrix.devotion -= 15;
				}
			}
			V.receptrix = 0;
			V.donatrix = 0;
	}
	App.Events.addParagraph(node, r);
	return node;
};


code:
:: pregmod widgets [nobr widget]

<<widget "setLocalPronouns">>
/*
	"He has a camera. The camera belongs to him. It is his camera. The camera is his."
	"She has a diamond ring. The diamond ring belongs to her. It is her diamond ring. The diamond ring is hers."

	Pronouns are tricky to get right, and this widget should make it easier to use variables naturally in writing. There are two problems in English pronouns that matter here:

	1. Her/her vs his/him. To solve this ambiguity, variables use male pronouns throughout, although players will of course see the contents of the variable. "$He glared" can be printed as "She glared."

	2. Her/hers vs his/his. Here, the opposite problem. In this one case and this alone, where you would use "hers," (the possessive pronoun) the variable is female, since the distinction is only important there. I am very sorry for English.
*/
	<<run App.Utils.setLocalPronouns($args[0], $args[1])>>
<</widget>>

<<widget "setNonlocalPronouns">>
	/* a fake slave object, we need the .pronoun attribute only */
	<<set _fSlave = {pronoun: App.Data.Pronouns.Kind.female}>>
	/* Used for generic slaves, citizens, security, etc. */
	<<if $diversePronouns == 1>>
		<<if $args[0] === 100>>
			<<set _fSlave.pronoun = App.Data.Pronouns.Kind.male>>
		<<elseif ($args[0] > 0) && (random(1,100) <= $args[0])>>
			<<set _fSlave.pronoun = App.Data.Pronouns.Kind.male>>
		<</if>>
	<</if>>
	<<run App.Utils.setLocalPronouns(_fSlave, 'U')>>
<</widget>>

<<widget "setPlayerPronouns">>
	<<set _pl = ["he", "his", "hers", "him", "himself", "woman", "women", "loli", "girl", "daughter", "sister", "wife", "wives",
		"He", "His", "Hers", "Him", "Himself", "Woman", "Women", "Loli", "Girl", "Daughter", "Sister", "Wife", "Wives"]>>
	<<run App.Utils.setLocalPronouns($PC, 'P', _pl)>>
	<<unset _pl>>
<</widget>>

<<widget "setAssistantPronouns">>
	<<set _apl = ["he", "his", "hers", "him", "himself", "woman", "women", "loli", "girl", "daughter", "sister", "wife", "wives",
		"He", "His", "Hers", "Him", "Himself", "Woman", "Women", "Loli", "Girl", "Daughter", "Sister", "Wife", "Wives"]>>

	<<run App.Utils.setLocalPronouns(assistant.pronouns().main, 'A', _apl)>>
	<<if $assistant.market>>
		<<run App.Utils.setLocalPronouns(assistant.pronouns().market, 'M', _apl)>>
	<</if>>
	<<unset _apl>>
<</widget>>

/*
	Should be called with two slave objects as arguments, the primary and secondary. E.g. <<setSpokenLocalPronouns $activeSlave $subSlave>>
	_primarySlaveLisp and _secondarySlaveLisp only exist so that we don't have to rely on using the exact variables $activeSlave and $subSlave
*/
<<widget "setSpokenLocalPronouns">>
	<<set _primarySlaveLisp = SlaveStatsChecker.checkForLisp($args[0])>>

	<<set _heLisp = lispReplace($he)>>
	<<set _hisLisp = lispReplace($his)>>
	<<set _hersLisp = lispReplace($hers)>>
	<<set _himselfLisp = lispReplace($himself)>>
	<<set _daughterLisp = lispReplace($daughter)>>
	<<set _sisterLisp = lispReplace($sister)>>
	<<set _loliLisp = lispReplace($loli)>>
	<<set _wifeLisp = lispReplace($wife)>>
	<<set _wivesLisp = lispReplace($wives)>>

	<<set _HeLisp = lispReplace($He)>>
	<<set _HisLisp = lispReplace($His)>>
	<<set _HersLisp = lispReplace($Hers)>>
	<<set _HimselfLisp = lispReplace($Himself)>>
	<<set _DaughterLisp = lispReplace($Daughter)>>
	<<set _SisterLisp = lispReplace($Sister)>>
	<<set _LoliLisp = lispReplace($Loli)>>
	<<set _WifeLisp = lispReplace($Wife)>>
	<<set _WivesLisp = lispReplace($Wives)>>

	<<if def _he2>>
		<<set _secondarySlaveLisp = SlaveStatsChecker.checkForLisp($args[1])>>

		<<set _he2Lisp = lispReplace(_he2)>>
		<<set _his2Lisp = lispReplace(_his2)>>
		<<set _hers2Lisp = lispReplace(_hers2)>>
		<<set _himself2Lisp = lispReplace(_himself2)>>
		<<set _daughter2Lisp = lispReplace(_daughter2)>>
		<<set _sister2Lisp = lispReplace(_sister2)>>
		<<set _loli2Lisp = lispReplace(_loli2)>>
		<<set _wife2Lisp = lispReplace(_wife2)>>
		<<set _wives2Lisp = lispReplace(_wives2)>>

		<<set _He2Lisp = lispReplace(_He2)>>
		<<set _His2Lisp = lispReplace(_His2)>>
		<<set _Hers2Lisp = lispReplace(_Hers2)>>
		<<set _Himself2Lisp = lispReplace(_Himself2)>>
		<<set _Daughter2Lisp = lispReplace(_Daughter2)>>
		<<set _Sister2Lisp = lispReplace(_Sister2)>>
		<<set _Loli2Lisp = lispReplace(_Loli2)>>
		<<set _Wife2Lisp = lispReplace(_Wife2)>>
		<<set _Wives2Lisp = lispReplace(_Wives2)>>
	<</if>>
<</widget>>

<<widget "setSpokenPlayerPronouns">>
	<<set _playerSlaveLisp = SlaveStatsChecker.checkForLisp($args[0])>>

	<<set _hePLisp = lispReplace(_heP)>>
	<<set _hisPLisp = lispReplace(_hisP)>>
	<<set _hersPLisp = lispReplace(_hersP)>>
	<<set _himselfPLisp = lispReplace(_himselfP)>>
	<<set _daughterPLisp = lispReplace(_daughterP)>>
	<<set _sisterPLisp = lispReplace(_sisterP)>>
	<<set _loliPLisp = lispReplace(_loliP)>>
	<<set _wifePLisp = lispReplace(_wifeP)>>
	<<set _wivesPLisp = lispReplace(_wivesP)>>

	<<set _HePLisp = lispReplace(_HeP)>>
	<<set _HisPLisp = lispReplace(_HisP)>>
	<<set _HersPLisp = lispReplace(_HersP)>>
	<<set _HimselfPLisp = lispReplace(_HimselfP)>>
	<<set _DaughterPLisp = lispReplace(_DaughterP)>>
	<<set _SisterPLisp = lispReplace(_SisterP)>>
	<<set _LoliPLisp = lispReplace(_LoliP)>>
	<<set _WifePLisp = lispReplace(_WifeP)>>
	<<set _WivesPLisp = lispReplace(_WivesP)>>
<</widget>>

<<widget "setSpokenAssistantPronouns">>
	<<set _assistantSlaveLisp = SlaveStatsChecker.checkForLisp($args[0])>>

	<<set _heALisp = lispReplace(_heA)>>
	<<set _hisALisp = lispReplace(_hisA)>>
	<<set _hersALisp = lispReplace(_hersA)>>
	<<set _himselfALisp = lispReplace(_himselfA)>>
	<<set _daughterALisp = lispReplace(_daughterA)>>
	<<set _sisterALisp = lispReplace(_sisterA)>>
	<<set _loliALisp = lispReplace(_loliA)>>
	<<set _wifeALisp = lispReplace(_wifeA)>>
	<<set _wivesALisp = lispReplace(_wivesA)>>

	<<set _HeALisp = lispReplace(_HeA)>>
	<<set _HisALisp = lispReplace(_HisA)>>
	<<set _HersALisp = lispReplace(_HersA)>>
	<<set _HimselfALisp = lispReplace(_HimselfA)>>
	<<set _DaughterALisp = lispReplace(_DaughterA)>>
	<<set _SisterALisp = lispReplace(_SisterA)>>
	<<set _LoliALisp = lispReplace(_LoliA)>>
	<<set _WifeALisp = lispReplace(_WifeA)>>
	<<set _WivesALisp = lispReplace(_WivesA)>>
<</widget>>