/* eslint-disable no-unused-vars */ // TODO: remove after testing

/** Find the previous and next slaves' IDs based on the current sort order
 * @param {App.Entity.SlaveState} slave
 * @returns {[number, number]} - previous and next slave ID
 */
App.UI.SlaveInteract.placeInLine = function(slave) {
	const useSlave = assignmentVisible(slave) ? ((s) => assignmentVisible(s)) : ((s) => slave.assignment === s.assignment);
	const slaveList = V.slaves.filter(useSlave);
	SlaveSort.slaves(slaveList);
	const curSlaveIndex = slaveList.findIndex((s) => s.ID === slave.ID);

	let nextIndex;
	if (curSlaveIndex + 1 > slaveList.length - 1) {
		nextIndex = 0; // wrap around to first slave
	} else {
		nextIndex = curSlaveIndex + 1;
	}
	let prevIndex;
	if (curSlaveIndex - 1 < 0) {
		prevIndex = slaveList.length - 1; // wrap around to last slave
	} else {
		prevIndex = curSlaveIndex - 1;
	}

	return [slaveList[prevIndex].ID, slaveList[nextIndex].ID];
};

/**
 * @param {App.Entity.SlaveState} slave
 * @returns {Node}
 */
App.UI.SlaveInteract.modify = function(slave) {
	const {he, his} = getPronouns(slave);
	let el = new DocumentFragment();

	const isAgent = [Job.AGENT, Job.AGENTPARTNER].includes(slave.assignment);

	App.UI.DOM.appendNewElement('p', el, isAgent ? "Recall your agent to modify them." : "Take slave to another room.", "scene-intro");

	if (isAgent) {
		return el;
	}

	/**
	 * Create a link with a note to send a  slave to a specific room
	 * @param {Node} c
	 * @param {string} caption
	 * @param {string} passage
	 * @param {string} note
	 * @param {function ():void} handler
	 */
	function makeRoomLink(c, caption, passage, note, handler) {
		const res = document.createElement('div');
		c.appendChild(res);
		res.appendChild(App.UI.DOM.link(caption, handler, [], passage));
		App.UI.DOM.appendNewElement('span', res, note, "note");
		return res;
	}

	makeRoomLink(el, "Auto salon", "Salon", ' Modify hair (color, length, style), nails, and even skin color.',
		() => {
			V.activeSlave = slave;
			V.primaryHairColor = "";
			V.secondaryHairColor = "";
			V.primaryEarColor = "";
			V.secondaryEarColor = "";
			V.primaryTailColor = "";
			V.secondaryTailColor = "";
			V.artificialEyeColor = "";
			V.artificialEyeShape = "";
			V.artificialEyeFill = "";
		}
	);

	makeRoomLink(el, "Body mod studio", "Body Modification", ' Mark your slave with piercings, tattoos, brands or even scars.',
		() => {
			V.activeSlave = slave;
			V.degradation = 0;
			V.tattooChoice = undefined;
		},
	);

	makeRoomLink(el, "Remote surgery", "Remote Surgery", ` Surgically modify your slave with state of the art plastic surgery and more. Alter ${his} senses, skeletal structure, organs, and even more.`,
		() => {
			V.activeSlave = slave;
		}
	);

	// Prosthetics
	if (V.prostheticsUpgrade > 0) {
		makeRoomLink(el, "Configure cybernetics", "Prosthetics Configuration", ` Configure prosthetics, if ${he} has been surgically implanted with interfaces that support it.`,
			() => {
				V.activeSlave = slave;
				V.prostheticsConfig = "main";
			}
		);
	}

	return el;
};

/**
 * @param {App.Entity.SlaveState} slave
 * @returns {HTMLElement}
 */
App.UI.SlaveInteract.fucktoyPref = function(slave) {
	const {his} = getPronouns(slave);
	const el = document.createElement('div');
	let links = [];

	function appendLink(text, toyHole, enabled, disabledText) {
		const link = {text: text};
		if (enabled) {
			link.toyHole = toyHole;
		} else {
			link.disabled = disabledText;
		}
		links.push(link);
	}

	if ((slave.assignment === App.Data.Facilities.penthouse.jobs.fucktoy.assignment) || (slave.assignment === App.Data.Facilities.masterSuite.jobs.fucktoy.assignment) || (slave.assignment === App.Data.Facilities.masterSuite.manager.assignment)) {
		App.UI.DOM.appendNewElement("span", el, "Fucktoy use preference:", "story-label");
		el.append(` `);

		const hole = App.UI.DOM.appendNewElement('span', el, `${slave.toyHole}. `);
		hole.style.fontWeight = "bold";

		appendLink('Mouth', 'mouth', true);
		appendLink('Tits', 'boobs', true);
		if (slave.vagina >= 0) {
			appendLink('Pussy', 'pussy', slave.vagina > 0 && canDoVaginal(slave), `Take ${his} virginity before giving ${his} pussy special attention`);
		}
		appendLink('Ass', 'ass', (slave.anus > 0) && canDoAnal(slave), `Take ${his} anal virginity before giving ${his} ass special attention`);
		if (slave.dick > 0 && canPenetrate(slave)) {
			appendLink('Dick', 'dick', true);
		}
		appendLink('No Preference', "all her holes", true);
	}

	function generateLink(linkDesc) {
		// is it just text?
		if (linkDesc.disabled) { return App.UI.DOM.disabledLink(linkDesc.text, [linkDesc.disabled]); }
		// Are they already on this toyHole?
		if (linkDesc.toyHole === slave.toyHole) { return document.createTextNode(linkDesc.text); }
		// Set up the link
		const link = App.UI.DOM.link(
			linkDesc.text,
			() => {
				slave.toyHole = linkDesc.toyHole;
				jQuery('#fucktoypref').empty().append(App.UI.SlaveInteract.fucktoyPref(slave));
			},
		);

		// add a note node if required
		if (linkDesc.note) {
			App.UI.DOM.appendNewElement("span", link, linkDesc.note, "note");
		}
		return link;
	}

	el.appendChild(App.UI.DOM.generateLinksStrip(links.map(generateLink)));

	return el;
};

/**
 * @param {App.Entity.SlaveState} slave
 * @returns {JQuery<HTMLElement>}
 */
App.UI.SlaveInteract.work = function(slave) {
	let el = new DocumentFragment();
	let p;
	let div;
	let span;
	const links = [];
	const {
		He,
	} = getPronouns(slave);

	function appendLink(text, whoreClass, enabled, disabledText) {
		const link = {text: text};
		if (enabled) {
			link.whoreClass = whoreClass;
		} else {
			link.disabled = disabledText;
		}
		links.push(link);
	}

	p = document.createElement('p');
	if ([Job.AGENT, Job.AGENTPARTNER].includes(slave.assignment)) {
		p.className = "scene-intro";
		p.textContent = `Recall your agent to modify them.`;
	} else {
		div = document.createElement('div');
		div.id = "miniscene";
		p.appendChild(div);

		span = document.createElement('span');
		span.id = "useSlave";
		p.appendChild(span);
		p.appendChild(App.UI.SlaveInteract.useSlaveDisplay(slave));
	}
	el.append(p);

	p = document.createElement('p');
	span = document.createElement('span');
	span.className = "note";
	switch (slave.assignment) {
		case "recover from surgery":
			span.textContent = `${He} is recovering from surgery this week`;
			break;
		case Job.BODYGUARD:
			span.textContent = `${He} is your Bodyguard and is not available for other work`;
			break;
		case Job.MADAM:
			span.textContent = `${He} is the Madam and is not available for other work`;
			break;
		case Job.DJ:
			span.textContent = `${He} is the DJ and is not available for other work`;
			break;
		case Job.MILKMAID:
			span.textContent = `${He} is the Milkmaid and is not available for other work`;
			break;
		case Job.FARMER:
			span.textContent = `${He} is the Farmer and is not available for other work`;
			break;
		case Job.STEWARD:
			span.textContent = `${He} is the Stewardess and is not available for other work`;
			break;
		case Job.HEADGIRL:
			span.textContent = `${He} is your Head Girl and is not available for other work`;
			break;
		case Job.RECRUITER:
			span.textContent = `${He} is recruiting slaves and is not available for other work`;
			break;
		case Job.NURSE:
			span.textContent = `${He} is the Nurse and is not available for other work`;
			break;
		case Job.ATTENDANT:
			span.textContent = `${He} is the Attendant of the spa and is not available for other work`;
			break;
		case Job.MATRON:
			span.textContent = `${He} is the Matron of the nursery and is not available for other work`;
			break;
		case Job.TEACHER:
			span.textContent = `${He} is the Schoolteacher and is not available for other work`;
			break;
		case Job.CONCUBINE:
			span.textContent = `${He} is your Concubine and is not available for other work`;
			break;
		case Job.WARDEN:
			span.textContent = `${He} is the Wardeness and is not available for other work`;
			break;
		default:
			// CAN BE REASSIGNED
			span.classList.remove("note");
			span.id = "assignmentLinks";
			span.appendChild(App.UI.SlaveInteract.assignmentBlock(slave));

			if ((V.brothel + V.club + V.dairy + V.farmyard + V.servantsQuarters + V.masterSuite + V.spa + V.nursery + V.clinic + V.schoolroom + V.cellblock + V.arcade + V.HGSuite) > 0) {
				span.append(`Transfer to: `);
				span.appendChild(App.UI.jobLinks.transfersFragment(slave.ID));
			}

			div = document.createElement('div');
			div.id = "fucktoypref";
			div.append(App.UI.SlaveInteract.fucktoyPref(slave));
			span.appendChild(div);
	}
	el.append(span);

	if (slave.assignment === Job.WHORE || slave.assignment === Job.BROTHEL) {
		div = document.createElement('div');
		div.textContent = `Whoring Target: `;
		span = document.createElement('span');
		span.style.fontWeight = "bold";

		span.id = "whoreClass";
		if (!slave.whoreClass) {
			span.textContent = `auto`;
		} else if (slave.whoreClass === 1) {
			span.textContent = `the lower class`;
		} else if (slave.whoreClass === 2) {
			span.textContent = `the middle class`;
		} else if (slave.whoreClass === 3) {
			span.textContent = `the upper class`;
		} else if (slave.whoreClass === 4) {
			span.textContent = `millionaires`;
		} else {
			span.textContent = `THERE HAS BEEN AN ERROR`;
		}
		div.append(span);
		div.append(`. `);

		div.append(App.UI.DOM.makeElement('span', `This is the highest class they are allowed to service, when eligible `, 'note'));

		appendLink(`Auto`, 0, true);
		appendLink(`Lower Class`, 1, true);
		appendLink(`Middle Class`, 2, true);
		appendLink(`Upper Class`, 3, true);
		appendLink(`Millionaires Class`, 4, true);
		div.appendChild(App.UI.DOM.generateLinksStrip(links.map(generateLink)));

		el.append(div);
	}

	function generateLink(linkDesc) {
		// is it just text?
		if (linkDesc.disabled) { return App.UI.DOM.disabledLink(linkDesc.text, [linkDesc.disabled]); }
		// Are they already on this whoreClass?
		if (linkDesc.whoreClass === slave.whoreClass) { return document.createTextNode(linkDesc.text); }
		// Set up the link
		const link = App.UI.DOM.link(
			linkDesc.text,
			() => {
				slave.whoreClass = linkDesc.whoreClass;
				App.UI.SlaveInteract.work(slave);
			},
		);

		// add a note node if required
		if (linkDesc.note) {
			App.UI.DOM.appendNewElement("span", link, linkDesc.note, "note");
		}
		return link;
	}

	return jQuery('#work').empty().append(el);
};

/**
 * @param {App.Entity.SlaveState} slave
 * @returns {HTMLElement}
 */
App.UI.SlaveInteract.assignmentBlock = function(slave) {
	let el = document.createElement('div');
	let title = document.createElement('div');
	let separator = document.createTextNode(` | `);
	title.append(`Assignment: `);

	let assign = document.createElement('span');
	assign.style.fontWeight = "bold";
	if (slave.sentence) {
		assign.textContent = `${slave.assignment} (${slave.sentence} weeks). `;
	} else {
		assign.textContent = `${slave.assignment}. `;
	}
	title.appendChild(assign);
	if (V.assignmentRecords[slave.ID] && V.assignmentRecords[slave.ID] !== slave.assignment) {
		title.append(`Previously: `);
		assign = document.createElement('span');
		assign.style.fontWeight = "bold";
		assign.textContent = `${V.assignmentRecords[slave.ID]}. `;
		title.appendChild(assign);
	}
	if (slave.assignment === Job.SUBORDINATE) {
		const target = getSlave(slave.subTarget);
		let linkText = ``;
		if (target) {
			title.appendChild(document.createTextNode(`Serving ${target.slaveName} exclusively. `));
			linkText = `Change`;
		} else if (slave.subTarget === -1) {
			title.appendChild(document.createTextNode(`Serving as a Stud. `));
			linkText = `Change role`;
		} else {
			title.appendChild(document.createTextNode(`Serving all your other slaves. `));
			linkText = `Choose a specific slave to submit to`;
		}
		title.appendChild(App.UI.DOM.passageLink(linkText, "Subordinate Targeting", () => { V.returnTo = "Slave Interact"; }));
		title.append(separator);
	}
	if (slave.assignment !== Job.CHOICE) {
		title.appendChild(
			App.UI.DOM.link(
				`Stay on this assignment for another month`,
				() => {
					slave.sentence += 4;
					App.UI.SlaveInteract.work(slave);
				},
			)
		);
	}
	el.appendChild(title);

	let links = document.createElement('div');
	links.className = "choices";
	links.appendChild(
		App.UI.jobLinks.assignmentsFragment(
			slave.ID, passage(),
			(slave, assignment) => {
				assignJob(slave, assignment);
				V.activeSlave = slave;
			}
		)
	);
	el.appendChild(links);
	return el;
};

App.UI.SlaveInteract.drugs = function(slave) {
	let el = document.createElement('div');

	const drugLevelOptions = [];
	const lips = [];
	const breasts = [];
	const nipples = [];
	const butt = [];
	const dick = [];
	const balls = [];
	const fertility = [];
	const hormones = [];
	const psych = [];
	const misc = [];

	if (slave.drugs !== "no drugs") {
		drugLevelOptions.push({text: `None`, updateSlave: {drugs: `no drugs`}});
	}
	if (slave.indentureRestrictions < 2) {
		// Psych
		if (slave.intelligence > -100 && slave.indentureRestrictions < 1) {
			psych.push({text: `Psychosuppressants`, updateSlave: {drugs: `psychosuppressants`}});
		} else if (slave.intelligence > -100) {
			psych.push({text: `Psychosuppressants`, disabled: `Cannot suppress indentured slave`});
		} else if (slave.indentureRestrictions < 1) {
			psych.push({text: `Psychosuppressants`, disabled: `Too stupid to suppress`});
		} else {
			psych.push({text: `Psychosuppressants`, disabled: `Too stupid and indentured to suppress`});
		}
		if (V.arcologies[0].FSSlaveProfessionalismResearch === 1) {
			if (canImproveIntelligence(slave)) {
				psych.push({text: `Psychostimulants`, updateSlave: {drugs: `psychostimulants`}});
			} else {
				psych.push({text: `Psychostimulants`, disabled: `Cannot improve intelligence`});
			}
		}

		// Breasts
		if (V.arcologies[0].FSSlimnessEnthusiastResearch === 1) {
			if ((slave.boobs - slave.boobsImplant - slave.boobsMilk) > 100) {
				breasts.push({text: `Reducers`, updateSlave: {drugs: `breast redistributors`}});
			} else {
				breasts.push({text: `Reducers`, disabled: `Boobs are too small`});
			}
		}
		if (slave.boobs < 48000) {
			breasts.push({text: `Enhancement`, updateSlave: {drugs: `breast injections`}});
			breasts.push({text: `Intensive enhancement`, updateSlave: {drugs: `intensive breast injections`}});
		} else {
			breasts.push({text: `Enhancement`, disabled: `Boobs are too large`});
		}
		if (V.arcologies[0].FSAssetExpansionistResearch === 1) {
			if (slave.boobs < 25000) {
				breasts.push({text: `Hyper enhancement`, updateSlave: {drugs: `hyper breast injections`}});
			} else {
				breasts.push({text: `Hyper enhancement`, disabled: `Boobs are too large`});
			}
		}

		// Nipples
		if (V.arcologies[0].FSSlimnessEnthusiastResearch === 1) {
			if (slave.nipples === "huge" || slave.nipples === "puffy" || slave.nipples === "cute") {
				nipples.push({text: `Reducers`, updateSlave: {drugs: `nipple atrophiers`}});
			} else {
				nipples.push({text: `Reducers`, disabled: `Nipples are ${slave.nipples}`});
			}
		}
		if (V.dispensary) {
			if ((["inverted", "partially inverted", "cute", "tiny", "puffy"].includes(slave.nipples))) {
				nipples.push({text: `Enhancement`, updateSlave: {drugs: `nipple enhancers`}});
			} else if (slave.nipples === "huge") {
				nipples.push({text: `Enhancement`, disabled: `Nipples are already huge`});
			} else {
				nipples.push({text: `Enhancement`, disabled: `Has no effect on ${slave.nipples} nipples`});
			}
		}

		// Butt
		if (V.arcologies[0].FSSlimnessEnthusiastResearch === 1) {
			if (slave.butt - slave.buttImplant > 0) {
				butt.push({text: `Reducers`, updateSlave: {drugs: `butt redistributors`}});
			} else {
				butt.push({text: `Reducers`, disabled: `Butt is too small`});
			}
		}
		if (slave.butt < 9) {
			butt.push({text: `Enhancement`, updateSlave: {drugs: `butt injections`}});
			butt.push({text: `Intensive enhancement`, updateSlave: {drugs: `intensive butt injections`}});
		} else {
			butt.push({text: `Enhancement`, disabled: `Butt is too large`});
		}
		if (V.arcologies[0].FSAssetExpansionistResearch === 1) {
			if (slave.butt < 20) {
				butt.push({text: `Hyper enhancement`, updateSlave: {drugs: `hyper butt injections`}});
			} else {
				butt.push({text: `Hyper enhancement`, disabled: `Butt is too large`});
			}
		}

		// Lips
		if (V.arcologies[0].FSSlimnessEnthusiastResearch === 1) {
			if (slave.lips - slave.lipsImplant > 0) {
				lips.push({text: `Reducers`, updateSlave: {drugs: `lip atrophiers`}});
			} else {
				lips.push({text: `Reducers`, disabled: `Lips are too small`});
			}
		}
		if (slave.lips <= 95 || (slave.lips <= 85 && V.seeExtreme !== 1)) {
			lips.push({text: `Enhancement`, updateSlave: {drugs: `lip injections`}});
		} else {
			lips.push({text: `Enhancement`, disabled: `Lips are too large`});
		}

		// Fertility
		fertility.push({text: `Fertility`, updateSlave: {drugs: `fertility drugs`}});
		if (V.seeHyperPreg === 1 && slave.indentureRestrictions < 1 && V.superFertilityDrugs === 1 && (slave.breedingMark !== 1 || V.propOutcome === 0 || V.eugenicsFullControl === 1 || V.arcologies[0].FSRestart === "unset")) {
			fertility.push({text: `Fertility+`, updateSlave: {drugs: `super fertility drugs`}});
		}

		// Dick/clit
		if (V.arcologies[0].FSSlimnessEnthusiastResearch === 1) {
			if (slave.dick > 1) {
				dick.push({text: `Reducers`, updateSlave: {drugs: `penis atrophiers`}});
			} else if (slave.dick === 1) {
				dick.push({text: `Reducers`, disabled: `Dick is already at minimum size`});
			}
			if (slave.clit > 0) {
				dick.push({text: `Reducers`, updateSlave: {drugs: `clitoris atrophiers`}});
			}
		}
		if (slave.dick > 0) {
			if (slave.dick < 10) {
				dick.push({text: `Enhancement`, updateSlave: {drugs: `penis enhancement`}});
				dick.push({text: `Intensive enhancement`, updateSlave: {drugs: `intensive penis enhancement`}});
			} else {
				dick.push({text: `Enhancement`, disabled: `Dick is too large`});
			}
		} else {
			if (slave.clit < 5) {
				dick.push({text: `Enhancement`, updateSlave: {drugs: `penis enhancement`}});
				dick.push({text: `Intensive enhancement`, updateSlave: {drugs: `intensive penis enhancement`}});
			} else {
				dick.push({text: `Enhancement`, disabled: `Clit is too large`});
			}
		}
		if (V.arcologies[0].FSAssetExpansionistResearch === 1) {
			if (slave.dick > 0) {
				if (slave.dick < 31) {
					dick.push({text: `Hyper enhancement`, updateSlave: {drugs: `hyper penis enhancement`}});
				} else {
					dick.push({text: `Hyper enhancement`, disabled: `Dick is too large`});
				}
			} else {
				if (slave.clit < 5) {
					dick.push({text: `Hyper enhancement`, updateSlave: {drugs: `penis enhancement`}});
				} else {
					dick.push({text: `Hyper enhancement`, disabled: `Clit is too large`});
				}
			}
		}
		if (slave.dick > 0 && slave.dick < 11 && !canAchieveErection(slave) && slave.chastityPenis !== 1) {
			dick.push({text: `Erectile dysfunction circumvention`, updateSlave: {drugs: `priapism agents`}});
		}

		// Balls
		if (V.arcologies[0].FSSlimnessEnthusiastResearch === 1) {
			if (slave.balls > 1) {
				balls.push({text: `Reducers`, updateSlave: {drugs: `testicle atrophiers`}});
			} else if (slave.balls === 1) {
				balls.push({text: `Reducers`, disabled: `Balls are already at minimum size`});
			}
		}
		if (slave.balls > 0) {
			balls.push({text: `Enhancement`, updateSlave: {drugs: `testicle enhancement`}});
			balls.push({text: `Intensive enhancement`, updateSlave: {drugs: `intensive testicle enhancement`}});
			if (V.arcologies[0].FSAssetExpansionistResearch === 1) {
				balls.push({text: `Hyper enhancement`, updateSlave: {drugs: `hyper testicle enhancement`}});
			}
		}

		// Hormones
		if (V.precociousPuberty === 1 && V.pubertyHormones === 1 && (slave.breedingMark !== 1 || V.propOutcome === 0 || V.eugenicsFullControl === 1 || V.arcologies[0].FSRestart === "unset")) {
			if ((slave.ovaries === 1 || slave.mpreg === 1) && slave.pubertyXX === 0) {
				hormones.push({text: `Female injections`, updateSlave: {drugs: `female hormone injections`}});
			}
			if (slave.balls > 0 && slave.pubertyXY === 0) {
				hormones.push({text: `Male injections`, updateSlave: {drugs: `male hormone injections`}});
			}
		}
		hormones.push({text: `Blockers`, updateSlave: {drugs: `hormone blockers`}});
		hormones.push({text: `Enhancement`, updateSlave: {drugs: `hormone enhancers`}});

		// Misc
		if (V.arcologies[0].FSSlimnessEnthusiastResearch === 1) {
			if (slave.labia > 0) {
				misc.push({text: `Labia reducers`, updateSlave: {drugs: `labia atrophiers`}});
			}
		}
		if (V.growthStim === 1) {
			if (canImproveHeight(slave)) {
				misc.push({text: `Growth Stimulants`, updateSlave: {drugs: `growth stimulants`}});
			} else {
				misc.push({text: `Growth Stimulants`, disabled: `Cannot increase height further`});
			}
		}
		if (V.arcologies[0].FSSlimnessEnthusiastResearch === 1) {
			if (slave.weight > -95) {
				misc.push({text: `Weight loss pills`, updateSlave: {drugs: `appetite suppressors`}});
			} else {
				misc.push({text: `Weight loss pills`, disabled: `Slave is already at low weight`});
			}
		}
		misc.push({text: `Steroids`, updateSlave: {drugs: `steroids`}});
		if (slave.boobs > 250 && slave.boobShape !== "saggy" && V.purchasedSagBGone === 1) {
			misc.push({text: `Sag-B-Gone breast lifting cream`, updateSlave: {drugs: `sag-B-gone`}});
		}
		if (V.arcologies[0].FSYouthPreferentialistResearch === 1) {
			if (slave.visualAge > 18) {
				misc.push({text: `Anti-aging cream`, updateSlave: {drugs: `anti-aging cream`}});
			} else {
				misc.push({text: `Anti-aging cream`, disabled: `Slave already looks young enough`});
			}
		}
	}

	let title = document.createElement('div');
	title.textContent = `Drugs: `;
	let chosenDrug = document.createElement('span');
	chosenDrug.textContent = `${capFirstChar(slave.drugs)} `;
	chosenDrug.style.fontWeight = "bold";
	title.append(chosenDrug);
	title.appendChild(App.UI.SlaveInteract.generateRows(drugLevelOptions, slave));
	el.append(title);

	App.UI.SlaveInteract.appendLabeledChoiceRow(el, "Lips", lips, slave);
	App.UI.SlaveInteract.appendLabeledChoiceRow(el, "Breasts", breasts, slave);
	App.UI.SlaveInteract.appendLabeledChoiceRow(el, "Nipples", nipples, slave);
	App.UI.SlaveInteract.appendLabeledChoiceRow(el, "Butt", butt, slave);
	App.UI.SlaveInteract.appendLabeledChoiceRow(el, "Dick", dick, slave);
	App.UI.SlaveInteract.appendLabeledChoiceRow(el, "Balls", balls, slave);
	App.UI.SlaveInteract.appendLabeledChoiceRow(el, "Fertility", fertility, slave);
	App.UI.SlaveInteract.appendLabeledChoiceRow(el, "Hormones", hormones, slave);
	App.UI.SlaveInteract.appendLabeledChoiceRow(el, "Psych", psych, slave);
	App.UI.SlaveInteract.appendLabeledChoiceRow(el, "Misc", misc, slave);

	return jQuery('#drugs').empty().append(el);
};

App.UI.SlaveInteract.hormones = function(slave) {
	let el = document.createElement('div');
	const options = [];
	const level = [];

	if (slave.hormones !== 0) {
		level.push({text: `None`, updateSlave: {hormones: 0}});
	}

	if (slave.indentureRestrictions < 2) {
		options.push({text: `Intensive Female`, updateSlave: {hormones: 2}});
	} else {
		options.push({text: `Intensive Female`, disabled: `Cannot use intensive hormones on indentured slaves`});
	}
	options.push({text: `Female`, updateSlave: {hormones: 1}});
	options.push({text: `Male`, updateSlave: {hormones: -1}});
	if (slave.indentureRestrictions < 2) {
		options.push({text: `Intensive Male`, updateSlave: {hormones: -2}});
	} else {
		options.push({text: `Intensive Male`, disabled: `Cannot use intensive hormones on indentured slaves`});
	}

	let title = document.createElement('div');
	title.textContent = `Hormones: `;
	let choice = document.createElement('span');
	choice.style.fontWeight = "bold";
	switch (slave.hormones) {
		case 2: {
			choice.textContent = `intensive female. `;
			break;
		}
		case 1: {
			choice.textContent = `female. `;
			break;
		}
		case 0: {
			choice.textContent = `none. `;
			break;
		}
		case -1: {
			choice.textContent = `male. `;
			break;
		}
		case -2: {
			choice.textContent = `intensive male. `;
			break;
		}
		default: {
			choice.textContent = `Not set. `;
		}
	}

	title.append(choice);
	title.appendChild(App.UI.SlaveInteract.generateRows(level, slave));
	el.append(title);

	let links = document.createElement('div');
	links.appendChild(App.UI.SlaveInteract.generateRows(options, slave));
	links.className = "choices";
	el.append(links);

	return jQuery('#hormones').empty().append(el);
};

App.UI.SlaveInteract.diet = function(slave) {
	const {
		// eslint-disable-next-line no-unused-vars
		he,
		him,
		his,
		hers,
		himself,
		boy,
		He,
		His
	} = getPronouns(slave);
	let el = document.createElement('div');

	let title = document.createElement('div');
	title.textContent = `Diet: `;
	let choice = document.createElement('span');
	choice.style.fontWeight = "bold";
	choice.textContent = `${capFirstChar(slave.diet)}. `;

	title.append(choice);
	el.append(title);

	const health = [];
	health.push({text: `Healthy`, updateSlave: {diet: "healthy"}});
	if (V.dietCleanse === 1) {
		if (slave.health.condition < 90 || slave.chem >= 10) {
			health.push({text: `Cleanse`, updateSlave: {diet: "cleansing"}});
		} else {
			health.push({text: `Cleanse`, disabled: `${He} is already healthy`});
		}
	}

	const weight = [];
	if (slave.weight >= -95) {
		weight.push({text: `Lose weight`, updateSlave: {diet: "restricted"}});
	} else {
		weight.push({text: `Lose weight`, disabled: `${He} is already underweight`});
	}
	if (slave.fuckdoll === 0 && slave.fetish !== "mindbroken" && V.feeder === 1) {
		if (slave.weight > 10 || slave.weight < -10) {
			weight.push({text: `Correct weight`, updateSlave: {diet: "corrective"}});
		} else {
			weight.push({text: `Correct weight`, disabled: `${He} is already a healthy weight`});
		}
	}
	if (slave.weight <= 200) {
		weight.push({text: `Fatten`, updateSlave: {diet: "fattening"}});
	} else {
		weight.push({text: `Fatten`, disabled: `${He} is already overweight`});
	}

	const muscle = [];
	if (slave.muscles <= 95 && !isAmputee(slave)) {
		muscle.push({text: `Build muscle`, updateSlave: {diet: "muscle building"}});
	} else if (!isAmputee(slave)) {
		muscle.push({text: `Build muscle`, disabled: `${He} is maintaining ${his} enormous musculature`});
	} else {
		muscle.push({text: `Build muscle`, disabled: `${He} has no limbs and thus can't effectively build muscle`});
	}

	if ((slave.muscles > 5 || slave.fuckdoll === 0) && canWalk(slave)) {
		muscle.push({text: `Slim down`, updateSlave: {diet: "slimming"}});
	} else if (!canWalk(slave)) {
		muscle.push({text: `Slim down`, disabled: `${He} can't move and thus can't trim down`});
	} else if (slave.fuckdoll > 0) {
		muscle.push({text: `Slim down`, disabled: `${He} has no muscles left to lose`});
	}

	const production = [];
	if (slave.balls > 0 && V.cumProDiet === 1) {
		production.push({text: `Cum production`, updateSlave: {diet: "cum production"}});
	}
	if (isFertile(slave) && slave.preg === 0 && V.dietFertility === 1) {
		production.push({text: `Fertility`, updateSlave: {diet: "fertility"}});
	}

	const hormone = [];
	if (V.feeder === 1) {
		hormone.push({text: `Estrogen enriched`, updateSlave: {diet: "XX"}});
		hormone.push({text: `Testosterone enriched`, updateSlave: {diet: "XY"}});
		if (V.dietXXY === 1 && slave.balls > 0 && (slave.ovaries === 1 || slave.mpreg === 1)) {
			hormone.push({text: `Herm hormone blend`, updateSlave: {diet: "XXY"}});
		}
	}

	App.UI.SlaveInteract.appendLabeledChoiceRow(el, "Health", health, slave);
	App.UI.SlaveInteract.appendLabeledChoiceRow(el, "Weight", weight, slave);
	App.UI.SlaveInteract.appendLabeledChoiceRow(el, "Muscle", muscle, slave);
	App.UI.SlaveInteract.appendLabeledChoiceRow(el, "Production", production, slave);
	App.UI.SlaveInteract.appendLabeledChoiceRow(el, "Hormone", hormone, slave);

	return jQuery('#diet').empty().append(el);
};

App.UI.SlaveInteract.dietBase = function(slave) {
	let el = document.createElement('div');
	const milk = [];
	const cum = [];

	// Milk
	if (slave.dietCum < 2) {
		milk.push({text: `Milk added`, updateSlave: {dietMilk: 1}});
		if (slave.dietCum < 2) {
			milk.push({text: `Milk based`, updateSlave: {dietMilk: 2, dietCum: 0}});
		}
		if (slave.dietMilk) {
			milk.push({text: `Remove milk`, updateSlave: {dietMilk: 0}});
		}
	} else {
		milk.push({text: `Milk`, disabled: `Diet is based entirely on cum`});
	}

	// Cum
	if (slave.dietMilk < 2) {
		cum.push({text: `Cum added`, updateSlave: {dietCum: 1}});
		cum.push({text: `Cum based`, updateSlave: {dietCum: 2, dietMilk: 0}});
		if (slave.dietCum) {
			cum.push({text: `Remove cum`, updateSlave: {dietCum: 0}});
		}
	} else {
		cum.push({text: `Cum`, disabled: `Diet is based entirely on milk`});
	}

	let title = document.createElement('div');
	title.textContent = `Diet base: `;
	let choice = document.createElement('span');
	choice.style.fontWeight = "bold";
	if (slave.dietCum === 2) {
		choice.textContent = `cum based. `;
	} else if (slave.dietCum === 1 && slave.dietMilk === 0) {
		choice.textContent = `cum added. `;
	} else if (slave.dietCum === 1 && slave.dietMilk === 1) {
		choice.textContent = `cum and milk added. `;
	} else if (slave.dietMilk === 1 && slave.dietCum === 0) {
		choice.textContent = `milk added. `;
	} else if (slave.dietMilk === 2) {
		choice.textContent = `milk based. `;
	} else if (slave.dietCum === 0 && slave.dietMilk === 0) {
		choice.textContent = `normal. `;
	} else {
		choice.textContent = `THERE HAS BEEN AN ERROR.`;
	}

	title.append(choice);
	el.append(title);

	App.UI.SlaveInteract.appendLabeledChoiceRow(el, "Milk", milk, slave);
	App.UI.SlaveInteract.appendLabeledChoiceRow(el, "Cum", cum, slave);

	return jQuery('#dietBase').empty().append(el);
};

App.UI.SlaveInteract.snacks = function(slave) {
	const {
		// eslint-disable-next-line no-unused-vars
		he,
		him,
		his,
		hers,
		himself,
		boy,
		He,
		His
	} = getPronouns(slave);
	let el = document.createElement('div');
	let options = [];

	if (V.arcologies[0].FSHedonisticDecadenceResearch === 1) {
		let title = document.createElement('div');
		title.textContent = `Solid Slave Food Access: `;
		let choice = document.createElement('span');
		choice.style.fontWeight = "bold";
		if (slave.onDiet === 0) {
			choice.textContent = `Free to stuff ${himself}.`;
		} else {
			choice.textContent = `On a strict diet.`;
		}
		title.append(choice);
		el.append(title);

		options.push({text: `No access`, updateSlave: {onDiet: 1}});
		options.push({text: `Full access`, updateSlave: {onDiet: 0}});

		let links = document.createElement('div');
		links.appendChild(App.UI.SlaveInteract.generateRows(options, slave));
		links.className = "choices";
		el.append(links);
	}

	return jQuery('#snacks').empty().append(el);
};

App.UI.SlaveInteract.useSlaveDisplay = function(slave) {
	// Goal: Be able to write the entire "use her" block with only dom fragments.
	let el = document.createElement('div');

	const {
		// eslint-disable-next-line no-unused-vars
		he,
		him,
		his,
		hers,
		himself,
		boy,
		He,
		His
	} = getPronouns(slave);

	/*
	Array of objects. Each object follows the form: {
		text: "Link text",
		scene: "scene to include",
		goto: if another passage is needed
		updateSlave: update slave itself if needed, like {trust: 2},
		update: updates V.,
		note: if a note must appear after the link
	}
	*/
	const sexOptions = [];
	const fillFaceOptions = [];
	const fillAssOptions = [];
	// if no scene, it's just text, no link. Italicize it.

	if (slave.fuckdoll === 0) {
		if (slave.vagina > -1) {
			if (canDoVaginal(slave)) {
				sexOptions.push({text: `Fuck ${him}`, scene: `FVagina`});
				if (canDoAnal(slave)) {
					sexOptions.push({text: `Use ${his} holes`, scene: `FButt`});
				}
			} else {
				sexOptions.push({text: `Fuck ${him}`, disabled: `Remove ${his} chastity belt if you wish to fuck ${him}`});
			}
		}
		if (slave.bellyPreg >= 300000) {
			if (canDoVaginal(slave) || canDoAnal(slave)) {
				sexOptions.push({text: `Fuck ${him} on ${his} belly`, scene: `FBellyFuck`});
				if (V.pregInventions >= 1) {
					sexOptions.push({text: `Fuck ${him} in ${his} maternity swing`, scene: `FMaternitySwing`});
					sexOptions.push({text: `Fuck ${him} with the help of ${his} assistants`, scene: `FAssistedSex`});
					sexOptions.push({text: `Fuck ${him} in your goo pool`, scene: `FPoolSex`});
				}
			}
		}

		if (canDoAnal(slave)) {
			sexOptions.push({text: `Fuck ${his} ass`, scene: `FAnus`});
		} else {
			sexOptions.push({text: `Fuck ${his} ass`, disabled: `Remove ${his} chastity belt if you wish to fuck ${his} ass`});
		}
		sexOptions.push({text: `Use ${his} mouth`, scene: `FLips`});
		sexOptions.push({text: `Kiss ${him}`, scene: `FKiss`});
		if (hasAnyLegs(slave)) {
			sexOptions.push({text: `Have ${him} dance for you`, scene: `FDance`});
		}

		sexOptions.push({text: `Play with ${his} tits`, scene: `FBoobs`});

		sexOptions.push({text: `Caress ${him}`, scene: `FCaress`});

		sexOptions.push({text: `Give ${him} a hug`, scene: `FEmbrace`});
		if (V.cheatMode === 1) {
			sexOptions.push({text: `Pat ${his} head`, scene: `FPat`});
		}

		sexOptions.push({text: `Grope ${his} boobs`, scene: `FondleBoobs`});
		if (slave.nipples === "fuckable" && V.PC.dick > 0) {
			sexOptions.push({text: `Fuck ${his} nipples`, scene: `FNippleFuck`});
		}
		if (slave.lactation > 0 && slave.boobs >= 2000 && slave.belly < 60000 && hasAnyArms(slave)) {
			sexOptions.push({text: `Drink ${his} milk`, scene: `FSuckle`});
		}

		if (canDoAnal(slave)) {
			sexOptions.push({text: `Grope ${his} butt`, scene: `FondleButt`});
		}


		if (slave.vagina > -1) {
			if (canDoVaginal(slave)) {
				sexOptions.push({text: `Grope ${his} pussy`, scene: `FondleVagina`});
				sexOptions.push({text: `Eat ${him} out`, scene: `FLickPussy`});
			}
		}

		if (slave.dick > 0) {
			if (!(slave.chastityPenis)) {
				sexOptions.push({text: `Grope ${his} dick`, scene: `FondleDick`});
				if (canPenetrate(slave)) {
					if (V.policies.sexualOpeness === 1 || slave.toyHole === "dick") {
						sexOptions.push({text: `Ride ${his} dick`, scene: `FDick`});
					}
				}
			} else {
				sexOptions.push({text: `Use ${his} dick`, disabled: `Remove ${his} dick chastity belt if you wish to play with ${his} cock`});
			}
		}

		if (hasAnyLegs(slave) && V.PC.dick > 0) {
			sexOptions.push({text: `Get a footjob`, scene: `FFeet`});
		}

		if (canGetPregnant(slave) && (slave.geneticQuirks.superfetation !== 2 || V.geneticMappingUpgrade !== 0) && (slave.fuckdoll === 0) && V.seePreg !== 0) {
			if (canImpreg(slave, V.PC)) {
				sexOptions.push({text: `Impregnate ${him} yourself`, scene: `FPCImpreg`});
			}
			if (canImpreg(slave, slave)) {
				sexOptions.push({text: `Use ${his} own seed to impregnate ${him}`, scene: `FSlaveSelfImpreg`});
			}
			sexOptions.push({text: `Use another slave to impregnate ${him}`, scene: `FSlaveImpreg`});
		}
		if (slave.assignment !== Job.DAIRY && slave.assignment !== Job.ARCADE && slave.assignment !== Job.CELLBLOCK) {
			if (V.dairyPiping === 1) {
				if ((V.milkPipeline > 88 && V.milkPipeline !== 0) || V.arcologies[0].FSPastoralistLaw === 1) {
					if ((slave.inflation < 3 && slave.pregKnown === 0 && slave.bellyImplant < 1500) || slave.inflation < 1) {
						if (slave.inflationType === "milk" || slave.inflationType === "none") {
							fillAssOptions.push({text: `Milk`, scene: `FillUpButt`, updateSlave: {inflationType: "milk", inflationMethod: 2}});
							fillFaceOptions.push({text: `Milk`, scene: `FillUpFace`, updateSlave: {inflationType: "milk", inflationMethod: 1}});
						}
					}
				} else {
					fillAssOptions.push({text: `Milk`, disabled: `${V.dairyNameCaps} is not producing enough milk to pump through the pipes`});
					fillFaceOptions.push({text: `Milk`, disabled: `${V.dairyNameCaps} is not producing enough milk to pump through the pipes`});
				}
				if ((V.cumPipeline > 88 && V.cumPipeline !== 0) || V.arcologies[0].FSPastoralistLaw === 1) {
					if ((slave.inflation < 3 && slave.pregKnown === 0 && slave.bellyImplant < 1500) || slave.inflation < 1) {
						if (slave.inflationType === "cum" || slave.inflationType === "none") {
							fillAssOptions.push({text: `Cum`, scene: `FillUpButt`, updateSlave: {inflationType: "cum", inflationMethod: 2}});
							fillFaceOptions.push({text: `Cum`, scene: `FillUpFace`, updateSlave: {inflationType: "cum", inflationMethod: 1}});
						}
					}
				} else {
					fillAssOptions.push({text: `Cum`, disabled: `${V.dairyNameCaps} is not producing enough cum to pump through the pipes`});
					fillFaceOptions.push({text: `Cum`, disabled: `${V.dairyNameCaps} is not producing enough cum to pump through the pipes`});
				}
			} /* dairyPiping === 1 */
			if (V.enema === 1) {
				if ((slave.inflation < 3 && slave.pregKnown === 0 && slave.bellyImplant < 1500) || slave.inflation < 1) {
					if (slave.inflationType === "water" || slave.inflationType === "none") {
						fillAssOptions.push({text: `Water`, scene: `FillUpButt`, updateSlave: {inflationType: "water", inflationMethod: 2}});
					}
					if (V.medicalEnema === 1) {
						if (slave.inflationType === "aphrodisiac" || slave.inflationType === "none") {
							fillAssOptions.push({text: `Aphrodisiacs`, scene: `FillUpButt`, updateSlave: {inflationType: "aphrodisiac", inflationMethod: 2}});
						}
						if (slave.inflationType === "curative" || slave.inflationType === "none") {
							fillAssOptions.push({text: `Curatives`, scene: `FillUpButt`, updateSlave: {inflationType: "curative", inflationMethod: 2}});
						}
						if (slave.inflationType === "tightener" || slave.inflationType === "none") {
							fillAssOptions.push({text: `Rectal tighteners`, scene: `FillUpButt`, updateSlave: {inflationType: "tightener", inflationMethod: 2}});
						}
					}
				} /* inflation < 3 */
			} /* enema === 1 */
			if (V.wcPiping === 1) {
				if ((slave.inflation < 3 && slave.pregKnown === 0 && slave.bellyImplant < 1500) || slave.inflation < 1) {
					if (slave.inflationType === "urine" || slave.inflationType === "none") {
						fillAssOptions.push({text: `Urine`, scene: `FillUpButt`, updateSlave: {inflationType: "urine", inflationMethod: 2}});
					}
				}
			} /* wcPiping === 1 */
		} /* assigned to dairy or arcade */
		if (slave.inflation === 0 && slave.bellyImplant < 1500) {
			if (slave.assignment !== Job.DAIRY && slave.assignment !== Job.ARCADE && slave.assignment !== Job.CELLBLOCK) {
				if (V.buckets === 1) {
					fillFaceOptions.push({text: `Two liters of slave food`, scene: `forceFeeding`, updateSlave: {inflation: 1, inflationType: "food", inflationMethod: 1}});
					if (slave.pregKnown === 0) {
						fillFaceOptions.push({text: `A gallon of slave food`, scene: `forceFeeding`, updateSlave: {inflation: 2, inflationType: "food", inflationMethod: 1}});
						fillFaceOptions.push({text: `Two gallons of slave food`, scene: `forceFeeding`, updateSlave: {inflation: 3, inflationType: "food", inflationMethod: 1}});
					}
				}
				fillFaceOptions.push({text: `Get another slave to do it`, goto: `SlaveOnSlaveFeedingWorkAround`});
			}
		}
		if (canDoVaginal(slave)) {
			sexOptions.push({text: `Have another slave fuck ${his} pussy`, scene: `FSlaveSlaveVag`});
		}
		if (canPenetrate(slave)) {
			sexOptions.push({text: `Have another slave ride ${his} cock`, scene: `FSlaveSlaveDick`});
		} else if (slave.clit >= 4) {
			sexOptions.push({text: `Have another slave ride ${his} clit-dick`, scene: `FSlaveSlaveDick`});
		}
		if (V.seeBestiality && V.cheatMode === 1) {
			if (V.farmyardKennels > 0 && V.activeCanine !== 0) {
				sexOptions.push({text: `Have a ${V.activeCanine.species} mount ${him}`, scene: `BeastFucked`, update: {animalType: "canine"}});
			}
			if (V.farmyardStable > 0 && V.activeHooved !== 0) {
				sexOptions.push({text: `Let a ${V.activeHooved.species} mount ${him}`, scene: `BeastFucked`, update: {animalType: "hooved"}});
			}
			if (V.farmyardCages > 0 && V.activeFeline !== 0) {
				sexOptions.push({text: `Have a ${V.activeFeline.species} mount ${him}`, scene: `BeastFucked`, update: {animalType: "feline"}});
			}
		}
		sexOptions.push({text: `Abuse ${him}`, scene: `FAbuse`});
		if (V.seeIncest === 1) {
			const availRelatives = availableRelatives(slave);
			if (availRelatives.mother) {
				sexOptions.push({text: `Fuck ${him} with ${his} mother`, scene: `FRelation`, update: {partner: "mother"}});
			} else if (availRelatives.motherName !== null) {
				sexOptions.push({text: `${His} mother, ${availRelatives.motherName}, is unavailable`});
			}
			/*
			if (availRelatives.father) {
				sexOptions.push({text: `Fuck ${him} with ${his} father`, scene: `FRelation`, update: {partner: "father"}});
			} else if (availRelatives.fatherName !== null) {
				sexOptions.push({text: `${His} father, ${availRelatives.motherName}, is unavailable`});
			}
			*/
			if (slave.daughters > 0) {
				if (availRelatives.daughters === 0) {
					if (slave.daughters === 1) {
						sexOptions.push({text: `Fuck ${him} with ${his} daughter`, disabled: `${His} ${availRelatives.oneDaughterRel} is unavailable`});
					} else {
						sexOptions.push({text: `Fuck ${him} with one of ${his} daughters`, disabled: `${His} daughters are unavailable`});
					}
				} else {
					if (slave.daughters === 1) {
						sexOptions.push({text: `Fuck ${him} with ${his} ${availRelatives.oneDaughterRel}`, scene: `FRelation`, update: {partner: "daughter"}});
					} else {
						sexOptions.push({text: `Fuck ${him} with one of ${his} daughters`, scene: `FRelation`, update: {partner: "daughter"}});
					}
					/*
					if (availRelatives.daughters > 1) {
						sexOptions.push({text: `Fuck ${him} with ${his} daughters`, scene: `FRelation`, update: {partner: "daughter"}});
					}
					*/
				}
			}
			if (slave.sisters > 0) {
				if (availRelatives.sisters === 0) {
					if (slave.sisters === 1) {
						sexOptions.push({text: `Fuck ${him} with ${his} sister`, disabled: `${His} ${availRelatives.oneSisterRel} is unavailable`});
					} else {
						sexOptions.push({text: `Fuck ${him} with one of ${his} sisters`, disabled: `${His} sisters are unavailable`});
					}
				} else {
					if (slave.sisters === 1) {
						sexOptions.push({text: `Fuck ${him} with ${his} ${availRelatives.oneSisterRel}`, scene: `FRelation`, update: {partner: "sister"}});
					} else {
						sexOptions.push({text: `Fuck ${him} with one of ${his} sisters`, scene: `FRelation`, update: {partner: "sister"}});
					}
					/*
					if (availRelatives.sisters > 1) {
						sexOptions.push({text: `Fuck ${him} with ${his} sisters`, scene: `FRelation`, update: {partner: "sisters}});
					}
					*/
				}
			}
		}
		if (slave.relationship > 0) {
			const lover = getSlave(slave.relationshipTarget);
			if (isSlaveAvailable(lover)) {
				sexOptions.push({text: `Fuck ${him} with ${his} ${relationshipTermShort(slave)} ${SlaveFullName(lover)}`, scene: `FRelation`, update: {partner: "relationship"}});
			} else {
				if (lover.assignment === Job.AGENT) {
					if (slave.broodmother < 2) {
						sexOptions.push({text: `Send ${him} to live with your agent ${SlaveFullName(lover)}`, goto: `Agent Company`, update: {subSlave: lover}});
					} else {
						sexOptions.push({text: `A hyper-broodmother cannot be sent to live with your agent`});
					}
				} else {
					sexOptions.push({text: `${SlaveFullName(lover)} is unavailable`});
				}
			}
		}
		if (slave.rivalryTarget !== 0 && hasAllLimbs(slave)) {
			const rival = getSlave(slave.relationshipTarget);
			if (isSlaveAvailable(rival) && hasAnyLegs(rival)) {
				sexOptions.push({text: `Abuse ${his} rival with ${him}`, scene: `FRival`});
			}
		}
		if (slave.fetish !== "mindbroken" && (canTalk(slave) || hasAnyArms(slave))) {
			sexOptions.push({text: `Ask ${him} about ${his} feelings`, scene: `FFeelings`});
			if (V.PC.dick > 0) {
				sexOptions.push({text: `Make ${him} beg`, scene: `FBeg`});
			}
		}
		if (slave.devotion >= 100 && slave.relationship < 0 && slave.relationship > -3) {
			sexOptions.push({text: `Talk to ${him} about relationships`, goto: `Matchmaking`, update: {subSlave: 0}});
		}
		let ML = V.marrying.length;
		if ((V.policies.mixedMarriage === 1 || V.cheatMode === 1) && slave.relationship !== 5 && slave.relationship !== -3) {
			if (V.marrying.includes(slave.ID)) {
				sexOptions.push({text: `Marry ${him}`, disabled: `You are already marrying ${him} this weekend`});
			} else {
				if (ML < 2) {
					if (V.cheatMode === 1 || ML === 0) {
						sexOptions.push({text: `Marry ${him}`, goto: "FMarry"});
					} else {
						sexOptions.push({text: `Marry ${him}`, disabled: `You already have a wedding planned for this weekend`});
					}
				} else {
					sexOptions.push({text: `Marry ${him}`, disabled: `You can only marry up to two slaves per week`});
				}
			}
		}
		if (V.cheatMode === 1) {
			sexOptions.push({text: `Check ${his} stats`, scene: `Slave Stats`});
		}
	} else {
		/* IS A FUCKDOLL */
		sexOptions.push({text: `Fuck ${his} face hole`, scene: `FFuckdollOral`});
		if (canDoVaginal(slave)) {
			sexOptions.push({text: `Fuck ${his} front hole`, scene: `FFuckdollVaginal`});
		}
		if (canGetPregnant(slave) && (slave.geneticQuirks.superfetation !== 2 || V.geneticMappingUpgrade !== 0) && V.seePreg !== 0) {
			if (canImpreg(slave, V.PC)) {
				sexOptions.push({text: `Put a baby in ${him}`, scene: `FFuckdollImpreg`});
			}
		}
		if (canDoAnal(slave)) {
			sexOptions.push({text: `Fuck ${his} rear hole`, scene: `FFuckdollAnal`});
		}
	}
	let activeSlaveRepSacrifice = repGainSacrifice(slave, V.arcologies[0]);
	if (activeSlaveRepSacrifice > 0 && V.arcologies[0].FSPaternalist === "unset" && (slave.breedingMark === 0 || V.propOutcome === 0 || V.eugenicsFullControl === 1 || V.arcologies[0].FSRestart === "unset")) {
		sexOptions.push({
			text: `Sacrifice ${him} on the altar`,
			goto: `Aztec Slave Sacrifice`,
			note: `This will kill ${him} and gain you ${activeSlaveRepSacrifice} reputation`,
			update: {sacrificeType: "life"}
		});
	}
	el.append(`Use ${him}: `);
	el.appendChild(generateRows(sexOptions));
	if (!jQuery.isEmptyObject(fillFaceOptions)) {
		let fill = document.createElement('div');
		fill.appendChild(document.createTextNode(` Fill ${his} mouth with: `));
		fill.appendChild(generateRows(fillFaceOptions));
		el.appendChild(fill);
	}
	if (!jQuery.isEmptyObject(fillAssOptions)) {
		let fill = document.createElement('div');
		fill.appendChild(document.createTextNode(` Fill ${his} ass with: `));
		fill.appendChild(generateRows(fillAssOptions));
		el.appendChild(fill);
	}

	function generateRows(sexArray) {
		let row = document.createElement('span');
		for (let i = 0; i < sexArray.length; i++) {
			let link;
			const separator = document.createTextNode(` | `);
			const keys = Object.keys(sexArray[i]);

			// Test to see if there was a problem with the key
			for (let j = 0; j < keys.length; j++) {
				if (!["disabled", "goto", "note", "scene", "text", "update", "updateSlave"].includes(keys[j])) {
					sexArray[i].text += " ERROR, THIS SCENE WAS NOT ENTERED CORRECTLY";
					console.log("Trash found while generateRows() was running: " + keys[j] + ": " + sexArray[i][keys[j]]);
					break;
				}
			}
			// is it just text?
			if (sexArray[i].disabled) {
				link = App.UI.DOM.disabledLink(sexArray[i].text, [sexArray[i].disabled]);
			} else {
				let passage = "";
				if (sexArray[i].goto) {
					passage = sexArray[i].goto;
				}

				// Set up the link
				link = App.UI.DOM.link(
					sexArray[i].text,
					() => { click(sexArray[i]); },
					[],
					passage
				);

				// add a note node if required
				if (sexArray[i].note) {
					link.appendChild(App.UI.DOM.makeElement('span', sexArray[i].note, 'note'));
				}
			}
			row.appendChild(link);
			if (i < sexArray.length - 1) {
				row.appendChild(separator);
			}
		}

		return row;

		function click(sexOption) {
			if (sexOption.updateSlave) {
				Object.assign(slave, sexOption.updateSlave);
			}
			if (sexOption.update) {
				Object.assign(V, sexOption.update);
			}

			if (sexOption.goto) {
				// just play the passage, no need to refresh anything here
				Engine.play(sexOption.goto);
			} else if (sexOption.scene) {
				// Run scene and store render results temporarily
				let frag = App.UI.DOM.renderPassage(sexOption.scene);

				// Refresh (clears scene display)
				App.UI.SlaveInteract.refreshAll(V.slaves[V.slaveIndices[V.activeSlave.ID]]);

				// Display scene render results
				$("#miniscene").append(frag);
			} else {
				// just refresh
				App.UI.SlaveInteract.refreshAll(V.slaves[V.slaveIndices[V.activeSlave.ID]]);
			}
		}
	}
	return el;
};

App.UI.SlaveInteract.bloating = function(slave) {
	const {
		// eslint-disable-next-line no-unused-vars
		he,
		him,
		his,
		hers,
		himself,
		boy,
		He,
		His
	} = getPronouns(slave);
	let bloating = document.createElement('div');
	if (slave.inflation > 0) {
		let intro = document.createElement('span');
		intro.textContent = "Required Bloating";
		intro.style.textDecoration = "underline";
		bloating.append(intro);

		bloating.append(": ");

		let requirement = document.createElement('span');
		requirement.style.fontWeight = "bold";
		requirement.id = "inflate";
		if (slave.inflation === 3) {
			requirement.textContent = `${He} is required to keep 2 gallons of ${slave.inflationType} in ${him} at all times`;
		} else if (slave.inflation === 2) {
			requirement.textContent = `${He} is required to keep 4 liters of ${slave.inflationType} in ${him} at all times`;
		} else if (slave.inflation === 1) {
			requirement.textContent = `${He} is required to keep 2 liters of ${slave.inflationType} in ${him} at all times`;
		}
		bloating.append(requirement);
		bloating.append(". ");

		if (slave.inflationMethod === 3) {
			let link = App.UI.DOM.link(
				`Let ${him} deflate`,
				() => {
					slave.inflation = 0;
					slave.inflationType = "none";
					slave.inflationMethod = 0;
					slave.cumSource = 0;
					slave.milkSource = 0;
					SetBellySize(slave);
					App.UI.SlaveInteract.refreshAll(slave);
				},
			);
			bloating.append(link);
		} else {
			let link = App.UI.DOM.link(
				`Let ${him} deflate`,
				() => {
					slave.inflation = 0;
					slave.inflationType = "none";
					slave.inflationMethod = 0;
					SetBellySize(slave);
					App.UI.SlaveInteract.refreshAll(slave);
				},
			);
			bloating.append(link);
		}
	}
	// make sure it updates itself after run
	return jQuery('#bloating').empty().append(bloating);
};

App.UI.SlaveInteract.fertility = function(slave) {
	const {
		// eslint-disable-next-line no-unused-vars
		he,
		him,
		his,
		hers,
		himself,
		boy,
		He,
		His
	} = getPronouns(slave);
	const separator = document.createTextNode(` | `);
	let fertilityblock = document.createElement('span');
	let link = document.createElement('div');
	link.className = "choices";
	if (V.pregnancyMonitoringUpgrade === 1) {
		fertilityblock.append(document.createElement('br'));
		let fullscantext = document.createElement('span');
		fullscantext.textContent = "Full scan of abdomen and reproductive organs";
		fullscantext.style.textDecoration = "underline";
		let spacetext = document.createElement('span');
		spacetext.textContent = ": ";
		fertilityblock.append(fullscantext);
		fertilityblock.append(spacetext);
		fertilityblock.append(App.UI.DOM.passageLink("Do it", "Analyze Pregnancy"));
		fertilityblock.append(document.createElement('br'));
	}
	if (slave.ovaries === 1 || slave.mpreg === 1 || slave.preg > 0) {
		let note = document.createTextNode(``);
		note.className = "note";
		if (slave.preg < -1) {
			note.textContent += `${He} is sterile`;
		} else if (slave.pubertyXX === 0 && slave.preg < 1) {
			note.textContent += `${He} is not yet fertile`;
		} else if (slave.ovaryAge >= 47 && slave.preg < 1) {
			note.textContent += `${He} is too old to become pregnant`;
			if (slave.preg === -1) {
				slave.preg = 0;
				SetBellySize(slave);
			}
		} else if (slave.broodmotherOnHold === 1) {
			note.textContent += `${His} pregnancy implant is turned off`;
			if (slave.broodmotherCountDown > 0) {
				note.textContent += `${he} is expected to be completely emptied of ${his} remaining brood in ${slave.broodmotherCountDown} week`;
				if (slave.broodmotherCountDown > 1) {
					note.textContent += `s`;
				}
				note.textContent += `.`;
				link.append(App.UI.DOM.link(
					`Turn on implant`,
					() => {
						slave.broodmotherOnHold = 0;
						slave.broodmotherCountDown = 0;
					},
					[],
					"Slave Interact"
				));
				fertilityblock.append(link);
			}
		} else if (slave.preg >= -1) {
			fertilityblock.append("Contraception and fertility: ");
			let fertility = document.createElement('span');
			// fertility.id = "fertility";
			fertility.style.fontWeight = "bold";
			if (slave.preg === -1) {
				fertility.textContent = "using contraceptives";
			} else if (slave.pregWeek < 0) {
				fertility.textContent = "postpartum";
			} else if (slave.preg === 0) {
				fertility.textContent = "fertile";
			} else if (slave.preg < 4) {
				fertility.textContent = "may be pregnant";
			} else {
				fertility.textContent = `${Math.trunc(slave.preg * 1000) / 1000} weeks pregnant`; // * and / needed to avoid seeing something like 20.1000000008 in some cases.
			}
			fertility.textContent += ". ";
			fertilityblock.appendChild(fertility);

			if (slave.preg === 0) {
				link.appendChild(App.UI.DOM.link(
					`Use contraceptives`,
					() => {
						slave.preg = -1;
						App.UI.SlaveInteract.refreshAll(slave);
					},
				));
				fertilityblock.append(link);
			} else if (slave.preg === -1) {
				link.appendChild(App.UI.DOM.link(
					`Let ${him} get pregnant`,
					() => {
						slave.preg = 0;
						App.UI.SlaveInteract.refreshAll(slave);
					},
				));
				fertilityblock.append(link);
			} else if (slave.induce === 1) {
				note.textContent += `Hormones are being slipped into ${his} food; ${he} will give birth suddenly and rapidly this week`;
			} else if (slave.preg > slave.pregData.normalBirth - 2 && slave.preg > slave.pregData.minLiveBirth && slave.broodmother === 0 && slave.labor === 0) {
				link.appendChild(App.UI.DOM.link(
					`Induce labor`,
					() => {
						slave.labor = 1;
						slave.induce = 1;
						V.birthee = 1;
					},
					[],
					"Slave Interact"
				));
				fertilityblock.append(link);
				fertilityblock.appendChild(separator);
				fertilityblock.append(App.UI.DOM.passageLink(`Give ${him} a cesarean section`, "csec"));
			} else if (slave.broodmother > 0) {
				if (slave.broodmotherOnHold !== 1) {
					link.appendChild(App.UI.DOM.link(
						`Turn off implant`,
						() => {
							slave.broodmotherOnHold = 1;
							slave.broodmotherCountDown = 38 - WombMinPreg(slave);
						},
					));
					fertilityblock.append(link);
				}
				if (slave.broodmotherOnHold !== 1 && slave.preg >= 36) {
					fertilityblock.appendChild(separator);
				}
				fertilityblock.appendChild(separator);
				if (slave.preg > 37) {
					fertilityblock.append(App.UI.DOM.passageLink(`Induce mass childbirth`, "BirthStorm"));
				}
			} else if (slave.preg > slave.pregData.minLiveBirth) {
				link.appendChild(App.UI.DOM.link(
					`Give ${him} a cesarean section`,
					() => {
						slave.broodmotherOnHold = 0;
						slave.broodmotherCountDown = 0;
					},
					[],
					"csec"
				));
				fertilityblock.append(link);
			} else if (slave.preg > 0 && slave.breedingMark === 1 && V.propOutcome === 1 && V.arcologies[0].FSRestart !== "unset" && V.eugenicsFullControl !== 1 && (slave.pregSource === -1 || slave.pregSource === -6)) {
				note.textContent += "You are forbidden from aborting an Elite child";
			} else if (slave.preg > 0) {
				link.appendChild(App.UI.DOM.link(
					`Abort ${his} pregnancy`,
					() => {
						slave.broodmotherOnHold = 0;
						slave.broodmotherCountDown = 0;
					},
					[],
					"Abort"
				));
				fertilityblock.append(link);
			}
			fertilityblock.append(note);
		}
	}
	if (
		(slave.pregKnown === 1) &&
		(V.pregSpeedControl === 1) &&
		(
			slave.breedingMark !== 1 ||
			V.propOutcome === 0 ||
			V.eugenicsFullControl === 1 ||
			V.arcologies[0].FSRestart === "unset"
		) &&
		(slave.indentureRestrictions < 1) &&
		(slave.broodmother === 0) &&
		V.seePreg !== 0
	) {
		let title = document.createElement('div');
		let underline = document.createElement('span');
		link = document.createElement('div');
		link.className = "choices";

		underline.style.textDecoration = "underline";
		underline.textContent = "Pregnancy control";
		title.appendChild(underline);
		title.append(": ");

		if (slave.pregControl === "labor suppressors") {
			title.append("Labor is suppressed. ");
		} else if (slave.pregControl === "slow gestation") {
			title.append("Slowed gestation speed. ");
		} else if (slave.pregControl === "speed up") {
			title.append("Faster gestation speed, staffed clinic recommended. ");
		} else {
			title.append("Normal gestation and birth. ");
		}
		fertilityblock.appendChild(title);
		if (slave.preg >= slave.pregData.minLiveBirth) {
			if (slave.pregControl === "labor suppressors") {
				link.appendChild(App.UI.DOM.link(
					`Normal Birth`,
					() => {
						slave.pregControl = "none";
						App.UI.SlaveInteract.fertility(slave);
					},
				));
			} else {
				link.appendChild(App.UI.DOM.link(
					`Suppress Labor`,
					() => {
						slave.pregControl = "labor suppressors";
						App.UI.SlaveInteract.fertility(slave);
					},
				));
			}
		} else if (slave.preg < slave.pregData.normalBirth) {
			if (slave.pregControl !== "none") {
				link.appendChild(App.UI.DOM.link(
					`Normal Gestation`,
					() => {
						slave.pregControl = "none";
						App.UI.SlaveInteract.fertility(slave);
					},
				));
			}
			if (slave.pregControl !== "slow gestation") {
				link.append(separator);
				link.appendChild(App.UI.DOM.link(
					`Slow Gestation`,
					() => {
						slave.pregControl = "slow gestation";
						App.UI.SlaveInteract.fertility(slave);
					},
				));
			}
			if (slave.pregControl !== "speed up") {
				link.append(separator);
				link.appendChild(App.UI.DOM.link(
					`Fast Gestation`,
					() => {
						slave.pregControl = "speed up";
						App.UI.SlaveInteract.fertility(slave);
					},
				));
			}
		}
		fertilityblock.appendChild(link);
	}
	return jQuery('#fertilityblock').empty().append(fertilityblock);
};

App.UI.SlaveInteract.curatives = function(slave) {
	const curativeOptions = [];

	curativeOptions.push({text: `None`, updateSlave: {curatives: 0}});
	curativeOptions.push({text: `Preventatives`, updateSlave: {curatives: 1}});
	curativeOptions.push({text: `Curatives`, updateSlave: {curatives: 2}});

	let el = document.createElement('div');
	let title = document.createElement('div');
	title.append(`Health: `);
	let chosenOption = document.createElement('span');
	chosenOption.style.fontWeight = "bold";
	if (slave.curatives > 1) {
		chosenOption.textContent = `curatives`;
	} else if (slave.curatives > 0) {
		chosenOption.textContent = `preventatives`;
	} else {
		chosenOption.textContent = `none`;
	}
	title.appendChild(chosenOption);
	title.append(`.`);
	let link = document.createElement('div');
	link.className = "choices";
	link.appendChild(App.UI.SlaveInteract.generateRows(curativeOptions, slave));
	el.append(title);
	el.append(link);
	return jQuery('#curatives').empty().append(el);
};

App.UI.SlaveInteract.aphrodisiacs = function(slave) {
	const aphrodisiacOptions = [];

	aphrodisiacOptions.push({text: `None`, updateSlave: {aphrodisiacs: 0}});
	aphrodisiacOptions.push({text: `Aphrodisiacs`, updateSlave: {aphrodisiacs: 1}});
	aphrodisiacOptions.push({text: `Extreme aphrodisiacs`, updateSlave: {aphrodisiacs: 2}});
	aphrodisiacOptions.push({text: `Anaphrodisiacs`, updateSlave: {aphrodisiacs: -1}, note: `Supresses libido`});


	let el = document.createElement('div');
	let title = document.createElement('div');
	title.append(`Aphrodisiacs: `);
	let chosenOption = document.createElement('span');
	chosenOption.style.fontWeight = "bold";
	if (slave.aphrodisiacs > 1) {
		chosenOption.textContent = `extreme`;
	} else if (slave.aphrodisiacs > 0) {
		chosenOption.textContent = `applied`;
	} else if (slave.aphrodisiacs === -1) {
		chosenOption.textContent = `anaphrodisiacs`;
	} else {
		chosenOption.textContent = `none`;
	}
	title.appendChild(chosenOption);
	title.append(`.`);
	let link = document.createElement('div');
	link.className = "choices";
	link.appendChild(App.UI.SlaveInteract.generateRows(aphrodisiacOptions, slave));
	el.append(title);
	el.append(link);
	return jQuery('#aphrodisiacs').empty().append(el);
};

App.UI.SlaveInteract.incubator = function(slave) {
	const {
		// eslint-disable-next-line no-unused-vars
		he,
		him,
		his,
		hers,
		himself,
		boy,
		He,
		His
	} = getPronouns(slave);
	V.reservedChildren = FetusGlobalReserveCount("incubator");
	let _reservedIncubator = WombReserveCount(slave, "incubator");
	let _reservedNursery = WombReserveCount(slave, "nursery");
	let _WL = slave.womb.length;
	let el = document.createElement('div');

	if (V.incubator > 0) {
		if (slave.preg > 0 && slave.broodmother === 0 && slave.pregKnown === 1 && slave.eggType === "human") {
			if ((slave.assignment === Job.DAIRY && V.dairyPregSetting > 0) || (slave.assignment === Job.FARMYARD && V.farmyardBreeding > 0)) {} else {
				let title = document.createElement('div');
				let link = document.createElement('div');
				link.className = "choices";
				if (_WL - _reservedNursery === 0) {
					title.textContent = `${His} children are already reserved for ${V.nurseryName}`;
					title.style.fontStyle = "italic";
				} else {
					const freeTanks = (V.incubator - V.tanks.length);
					if (_reservedIncubator > 0) {
						if (_WL === 1) {
							title.textContent = `${His} child will be placed in ${V.incubatorName}. `;
						} else if (_reservedIncubator < _WL) {
							title.textContent = `${_reservedIncubator} of ${his} children will be placed in ${V.incubatorName}.`;
						} else if (_WL === 2) {
							title.textContent = `Both of ${his} children will be placed in ${V.incubatorName}. `;
						} else {
							title.textContent = `All ${_reservedIncubator} of ${his} children will be placed in ${V.incubatorName}. `;
						}
						if ((_reservedIncubator + _reservedNursery < _WL) && (V.reservedChildren < freeTanks)) {
							link.appendChild(
								App.UI.DOM.link(
									`Keep another child`,
									() => {
										WombAddToGenericReserve(slave, "incubator", 1);
										V.slaves[V.slaveIndices[slave.ID]] = slave;
										V.reservedChildren = FetusGlobalReserveCount("incubator");
										App.UI.SlaveInteract.refreshAll(slave);
									}
								)
							);
							if (_reservedIncubator > 0) {
								link.append(` | `);
								link.appendChild(
									App.UI.DOM.link(
										`Keep one less child`,
										() => {
											WombCleanGenericReserve(slave, "incubator", 1);
											V.slaves[V.slaveIndices[slave.ID]] = slave;
											V.reservedChildren = FetusGlobalReserveCount("incubator");
											App.UI.SlaveInteract.refreshAll(slave);
										}
									)
								);
							}
							if (_reservedIncubator > 1) {
								link.append(` | `);
								link.appendChild(
									App.UI.DOM.link(
										`Keep none of ${his} children`,
										() => {
											WombCleanGenericReserve(slave, "incubator", 9999);
											V.slaves[V.slaveIndices[slave.ID]] = slave;
											V.reservedChildren = FetusGlobalReserveCount("incubator");
											App.UI.SlaveInteract.refreshAll(slave);
										}
									)
								);
							}
							if ((V.reservedChildren + _WL - _reservedIncubator) <= freeTanks) {
								link.append(` | `);
								link.appendChild(
									App.UI.DOM.link(
										`Keep the rest of ${his} children`,
										() => {
											WombAddToGenericReserve(slave, "incubator", 9999);
											V.slaves[V.slaveIndices[slave.ID]] = slave;
											V.reservedChildren = FetusGlobalReserveCount("incubator");
											App.UI.SlaveInteract.refreshAll(slave);
										}
									)
								);
							}
						} else if ((_reservedIncubator === _WL) || (V.reservedChildren === freeTanks) || (_reservedIncubator - _reservedNursery >= 0)) {
							link.appendChild(
								App.UI.DOM.link(
									`Keep one less child`,
									() => {
										WombCleanGenericReserve(slave, "incubator", 1);
										V.slaves[V.slaveIndices[slave.ID]] = slave;
										V.reservedChildren = FetusGlobalReserveCount("incubator");
										App.UI.SlaveInteract.refreshAll(slave);
									}
								)
							);
							if (_reservedIncubator > 1) {
								link.append(` | `);
								link.appendChild(
									App.UI.DOM.link(
										`Keep none of ${his} children`,
										() => {
											WombCleanGenericReserve(slave, "incubator", 9999);
											V.slaves[V.slaveIndices[slave.ID]] = slave;
											V.reservedChildren = FetusGlobalReserveCount("incubator");
											App.UI.SlaveInteract.refreshAll(slave);
										}
									)
								);
							}
						}
					} else if (V.reservedChildren < freeTanks) {
						title.textContent = `${He} is pregnant and you have `;
						if (freeTanks === 1) {
							title.textContent += `an `;
						}
						let tank = document.createElement('span');
						tank.className = "lime";
						tank.textContent = `available aging tank`;
						if (freeTanks > 1) {
							tank.textContent += `s`;
						}
						tank.textContent += `.`;
						let _cCount = (_WL > 1 ? "a" : "the");
						link.appendChild(
							App.UI.DOM.link(
								`Keep ${_cCount} child`,
								() => {
									WombAddToGenericReserve(slave, "incubator", 1);
									V.slaves[V.slaveIndices[slave.ID]] = slave;
									V.reservedChildren = FetusGlobalReserveCount("incubator");
									App.UI.SlaveInteract.refreshAll(slave);
								}
							)
						);
						title.appendChild(tank);
						if ((_WL > 1) && (V.reservedChildren + _WL) <= freeTanks) {
							link.append(` | `);
							link.appendChild(
								App.UI.DOM.link(
									`Keep all of ${his} children`,
									() => {
										WombAddToGenericReserve(slave, "incubator", 9999);
										V.slaves[V.slaveIndices[slave.ID]] = slave;
										V.reservedChildren = FetusGlobalReserveCount("incubator");
										App.UI.SlaveInteract.refreshAll(slave);
									}
								)
							);
						}
					} else if (V.reservedChildren === freeTanks) {
						title.textContent = `You have no available tanks for ${his} children. `;
					}
				}
				el.append(title);
				el.append(link);
			}
		}
	}
	return jQuery('#incubator').empty().append(el);
};

App.UI.SlaveInteract.nursery = function(slave) {
	const {
		// eslint-disable-next-line no-unused-vars
		he,
		him,
		his,
		hers,
		himself,
		boy,
		He,
		His
	} = getPronouns(slave);
	let el = document.createElement('div');
	if (V.nursery > 0) {
		V.reservedChildrenNursery = FetusGlobalReserveCount("nursery");
		let _reservedIncubator = WombReserveCount(slave, "incubator");
		let _reservedNursery = WombReserveCount(slave, "nursery");
		let _WL = slave.womb.length;
		if (slave.preg > 0 && slave.broodmother === 0 && slave.pregKnown === 1 && slave.eggType === "human") {
			if ((slave.assignment === Job.DAIRY && V.dairyPregSetting > 0) || (slave.assignment === Job.FARMYARD && V.farmyardBreeding > 0)) {} else {
				let title = document.createElement('div');
				let link = document.createElement('div');
				link.className = "choices";
				if (_WL - _reservedIncubator === 0) {
					V.reservedChildren = 0;
					title.textContent = `${His} children are already reserved for ${V.incubatorName}`;
					title.style.fontStyle = "italic";
				} else {
					const freeCribs = (V.nursery - V.cribs.length);
					if (_reservedNursery > 0) {
						if (_WL === 1) {
							title.textContent = `${His} child will be placed in ${V.nurseryName}. `;
						} else if (_reservedNursery < _WL) {
							title.textContent = `_reservedNursery of ${his} children will be placed in ${V.nurseryName}.`;
						} else if (_WL === 2) {
							title.textContent = `Both of ${his} children will be placed in ${V.nurseryName}. `;
						} else {
							title.textContent = `All ${_reservedNursery} of ${his} children will be placed in ${V.nurseryName}. `;
						}
						if ((_reservedIncubator + _reservedNursery < _WL) && (V.reservedChildrenNursery < freeCribs)) {
							link.appendChild(
								App.UI.DOM.link(
									`Keep another child`,
									() => {
										WombAddToGenericReserve(slave, "nursery", 1);
										V.slaves[V.slaveIndices[slave.ID]] = slave;
										V.reservedChildren = FetusGlobalReserveCount("nursery");
										App.UI.SlaveInteract.refreshAll(slave);
									}
								)
							);

							if (_reservedNursery > 0) {
								link.append(` | `);
								link.appendChild(
									App.UI.DOM.link(
										`Keep one less child`,
										() => {
											WombCleanGenericReserve(slave, "nursery", 1);
											V.slaves[V.slaveIndices[slave.ID]] = slave;
											V.reservedChildren = FetusGlobalReserveCount("nursery");
											App.UI.SlaveInteract.refreshAll(slave);
										}
									)
								);
							}
							if (_reservedNursery > 1) {
								link.append(` | `);
								link.appendChild(
									App.UI.DOM.link(
										`Keep none of ${his} children`,
										() => {
											WombCleanGenericReserve(slave, "nursery", 9999);
											App.UI.SlaveInteract.refreshAll(slave);
											// TODO: Copying this from the SC, but it's not three lines like the others? -LCD
										}
									)
								);
							}
							if ((V.reservedChildrenNursery + _WL - _reservedNursery) <= freeCribs) {
								link.append(` | `);
								link.appendChild(
									App.UI.DOM.link(
										`Keep the rest of ${his} children`,
										() => {
											WombAddToGenericReserve(slave, "nursery", 9999);
											App.UI.SlaveInteract.refreshAll(slave);
											// TODO: Copying this from the SC, but it's not three lines like the others? -LCD
										}
									)
								);
							}
						} else if ((_reservedNursery === _WL) || (V.reservedChildrenNursery === freeCribs) || (_reservedNursery - _reservedIncubator >= 0)) {
							link.appendChild(
								App.UI.DOM.link(
									`Keep one less child`,
									() => {
										WombCleanGenericReserve(slave, "nursery", 1);
										V.slaves[V.slaveIndices[slave.ID]] = slave;
										V.reservedChildren = FetusGlobalReserveCount("nursery");
										App.UI.SlaveInteract.refreshAll(slave);
									}
								)
							);

							if (_reservedNursery > 1) {
								link.append(` | `);
								link.appendChild(
									App.UI.DOM.link(
										`Keep none of ${his} children`,
										() => {
											WombCleanGenericReserve(slave, "nursery", 9999);
											V.slaves[V.slaveIndices[slave.ID]] = slave;
											V.reservedChildren = FetusGlobalReserveCount("nursery");
											App.UI.SlaveInteract.refreshAll(slave);
										}
									)
								);
							}
						}
					} else if (V.reservedChildrenNursery < freeCribs) {
						title.textContent = `${He} is pregnant and you have `;
						if (freeCribs === 1) {
							title.textContent += `an `;
						}
						let crib = document.createElement('span');
						crib.className = "lime";
						crib.textContent = `available room`;
						if (freeCribs > 1) {
							crib.textContent += `s`;
						}
						crib.textContent += `.`;
						let _cCount = (_WL > 1 ? "a" : "the");
						link.appendChild(
							App.UI.DOM.link(
								`Keep ${_cCount} child`,
								() => {
									WombAddToGenericReserve(slave, "nursery", 1);
									V.slaves[V.slaveIndices[slave.ID]] = slave;
									V.reservedChildren = FetusGlobalReserveCount("nursery");
									App.UI.SlaveInteract.refreshAll(slave);
								}
							)
						);
						title.appendChild(crib);
						if ((_WL > 1) && (V.reservedChildrenNursery + _WL) <= freeCribs) {
							link.append(` | `);
							link.appendChild(
								App.UI.DOM.link(
									`Keep all of ${his} children`,
									() => {
										WombAddToGenericReserve(slave, "nursery", 9999);
										V.slaves[V.slaveIndices[slave.ID]] = slave;
										V.reservedChildren = FetusGlobalReserveCount("nursery");
										App.UI.SlaveInteract.refreshAll(slave);
									}
								)
							);
						}
					} else if (V.reservedChildrenNursery === freeCribs) {
						title.textContent = `You have no available rooms for ${his} children. `;
					}
				}
				el.append(title);
				el.append(link);
			}
		}
	}
	return jQuery('#nursery').empty().append(el);
};

App.UI.SlaveInteract.smartSettings = function(slave) {
	let el = document.createElement('div');

	const {
		// eslint-disable-next-line no-unused-vars
		he,
		him,
		his,
		hers,
		himself,
		boy,
		He,
		His
	} = getPronouns(slave);
	const bodyPart = [];
	const BDSM = [];
	const gender = [];
	const level = [];

	if (slave.clitPiercing === 3 || slave.vaginalAccessory === "smart bullet vibrator") {
		// Level
		level.push({text: `No sex`, updateSlave: {clitSetting: `none`}});
		level.push({text: `All sex`, updateSlave: {clitSetting: `all`}});

		// Body part
		bodyPart.push({text: `Vanilla`, updateSlave: {clitSetting: `vanilla`}});
		bodyPart.push({text: `Oral`, updateSlave: {clitSetting: `oral`}});
		bodyPart.push({text: `Anal`, updateSlave: {clitSetting: `anal`}});
		bodyPart.push({text: `Boobs`, updateSlave: {clitSetting: `boobs`}});
		if (V.seePreg !== 0) {
			bodyPart.push({text: `Preg`, updateSlave: {clitSetting: `pregnancy`}});
		}
		// BDSM
		BDSM.push({text: `Sub`, updateSlave: {clitSetting: `submissive`}});
		BDSM.push({text: `Dom`, updateSlave: {clitSetting: `dom`}});
		BDSM.push({text: `Masochism`, updateSlave: {clitSetting: `masochist`}});
		BDSM.push({text: `Sadism`, updateSlave: {clitSetting: `sadist`}});
		BDSM.push({text: `Humiliation`, updateSlave: {clitSetting: `humiliation`}});

		// Gender
		gender.push({text: `Men`, updateSlave: {clitSetting: `men`}});
		gender.push({text: `Women`, updateSlave: {clitSetting: `women`}});
		gender.push({text: `Anti-men`, updateSlave: {clitSetting: `anti-men`}});
		gender.push({text: `Anti-women`, updateSlave: {clitSetting: `anti-women`}});
	}

	let title = document.createElement('div');
	if (slave.clitPiercing === 3) {
		if (slave.dick < 1) {
			title.textContent = `${His} smart clit piercing `;
			if (slave.vaginalAccessory === "smart bullet vibrator") {
				title.textContent += `and smart bullet vibrator are `;
			} else {
				title.textContent += `is `;
			}
			title.textContent += `set to: `;
		} else {
			title.textContent = `${His} smart frenulum piercing `;
			if (slave.vaginalAccessory === "smart bullet vibrator") {
				title.textContent += `and smart bullet vibrator are `;
			} else {
				title.textContent += `is `;
			}
			title.textContent += `set to: `;
		}
	} else {
		title.textContent = `${His} smart bullet vibe is set to: `;
	}
	let selected = document.createElement('span');
	selected.style.fontWeight = "bold";
	selected.textContent = `${slave.clitSetting}. `;
	title.append(selected);
	el.append(title);

	App.UI.SlaveInteract.appendLabeledChoiceRow(el, "Level", level, slave);
	App.UI.SlaveInteract.appendLabeledChoiceRow(el, "Body part", bodyPart, slave);
	App.UI.SlaveInteract.appendLabeledChoiceRow(el, "BDSM", BDSM, slave);
	App.UI.SlaveInteract.appendLabeledChoiceRow(el, "Gender", gender, slave);

	return jQuery('#smartSettings').empty().append(el);
};

App.UI.SlaveInteract.orgasm = function(slave) {
	let el = document.createElement('div');

	let title = document.createElement('div');
	title.textContent = `Non-assignment orgasm rules: `;
	el.append(title);

	makeLinks("Masturbation", "rules.release.masturbation");
	makeLinks("Partner", "rules.release.partner");
	makeLinks("Family", "rules.release.family");
	makeLinks("Other slaves", "rules.release.slaves");
	makeLinks("Master", "rules.release.master", true);

	function makeLinks(text, setting, master=false) {
		const options =
			[{text: master ? `Grant` : `Allow`, updateSlave: {[setting]: 1}},
			 {text: master ? `Deny` : `Forbid`, updateSlave: {[setting]: 0}}];

		let links = document.createElement('div');
		links.append(`${text}: `);
		links.append(status(_.get(slave, setting), master));
		links.appendChild(App.UI.SlaveInteract.generateRows(options, slave));
		links.className = "choices";
		el.append(links);
	}

	function status(setting, master) {
		let selected = document.createElement('span');
		selected.style.fontWeight = "bold";
		let text;
		if (master) {
			text = setting ? "granted" : "denied";
		} else {
			text = setting ? "allowed" : "denied";
		}
		selected.textContent = `${text}. `;
		return selected;
	}

	return jQuery('#orgasm').empty().append(el);
};

App.UI.SlaveInteract.custom = (function() {
	let el;
	let label;
	let result;
	let textbox;
	return custom;

	function custom(slave) {
		let title;
		el = document.createElement('div');

		el.appendChild(intro(slave));

		title = document.createElement('h3');
		title.textContent = `Art`;
		el.appendChild(title);
		el.appendChild(customSlaveImage(slave));
		el.appendChild(customHairImage(slave));

		title = document.createElement('h3');
		title.textContent = `Names`;
		el.appendChild(title);
		el.appendChild(playerTitle(slave));
		el.appendChild(slaveFullName(slave));

		title = document.createElement('h3');
		title.textContent = `Description`;
		el.appendChild(title);
		el.appendChild(hair(slave));
		el.appendChild(eyeColor(slave));
		el.appendChild(customTattoo(slave));
		el.appendChild(customOriginStory(slave));
		el.appendChild(customDescription(slave));
		el.appendChild(customLabel(slave));

		return jQuery('#custom').empty().append(el);
	}

	function intro(slave) {
		V.oldName = slave.slaveName;
		V.oldSurname = slave.slaveSurname;
		let intro = document.createElement('p');
		intro.className = "scene-intro";
		intro.append(`You may enter custom descriptors for your slave's hair color, hair style, tattoos, or anything else here. After typing, press `);
		intro.appendChild(App.UI.DOM.makeElement("kbd", "enter"));
		intro.append(` to commit your change. These custom descriptors will appear in descriptions of your slave, but will have no gameplay effect. Changing them is free.`);
		return intro;
	}

	function playerTitle(slave) {
		const {
			// eslint-disable-next-line no-unused-vars
			he,
			him,
			his,
			hers,
			himself,
			boy,
			He,
			His
		} = getPronouns(slave);
		let playerTitle = document.createElement('p');
		label = document.createElement('div');
		if (slave.devotion >= -50) {
			if (slave.custom.title !== "") {
				label.textContent = `You have instructed ${him} to always refer to you as ${slave.custom.title}, which, should ${he} lisp, comes out as ${slave.custom.titleLisp}.`;
			} else {
				label.textContent = `You expect ${him} to refer to you as all your other slaves do.`;
			}
			result = document.createElement('div');
			result.id = "result";
			result.className = "choices";

			let hiddenTextBox = document.createElement('span');
			let shownTextBox = document.createElement('span');
			if (slave.custom.title === "") {
				hiddenTextBox.appendChild(
					App.UI.DOM.link(
						`Set a custom title for ${him} to address you as`,
						() => {
							jQuery('#result').empty().append(shownTextBox);
						}
					)
				);
				result.appendChild(hiddenTextBox);
				shownTextBox.textContent = `Custom title: `;
				textbox = App.UI.DOM.makeTextBox(
					"",
					v => {
						slave.custom.title = v;
						jQuery('#result').empty().append(
							document.createTextNode(`${He}'ll try ${his} best to call you ${slave.custom.title}.`)
						);
						slave.custom.titleLisp = lispReplace(slave.custom.title);
					});
				shownTextBox.appendChild(textbox);
			} else {
				result.append(`${He}'s trying ${his} best to call you `);
				textbox = App.UI.DOM.makeTextBox(
					slave.custom.title,
					v => {
						slave.custom.title = v;
						jQuery('#result').empty().append(
							document.createTextNode(`${He}'ll try ${his} best to call you ${slave.custom.title}.`)
						);
						slave.custom.titleLisp = lispReplace(slave.custom.title);
					});
				result.appendChild(textbox);
				result.appendChild(
					App.UI.DOM.link(
						` Stop using a custom title`,
						() => {
							jQuery('#result').empty().append(
								document.createTextNode(`${He} will no longer refer to you with a special title.`)
							);
							slave.custom.title = "";
							slave.custom.titleLisp = "";
						}
					)
				);
			}
			label.appendChild(result);
		} else {
			label.textContent = `You must break ${his} will further before ${he} will refer to you by a new title. `;
			if (SlaveStatsChecker.checkForLisp(slave)) {
				if (slave.custom.titleLisp && slave.custom.titleLisp !== "") {
					label.textContent += `For now, ${he} intends to keep calling you "${slave.custom.titleLisp}."`;
				}
			} else {
				if (slave.custom.title && slave.custom.title !== "") {
					label.textContent += `For now, ${he} intends to keep calling you "${slave.custom.title}."`;
				}
			}
		}
		playerTitle.appendChild(label);
		return playerTitle;
	}

	function slaveFullName(slave) {
		const {
			// eslint-disable-next-line no-unused-vars
			he,
			him,
			his,
			hers,
			himself,
			boy,
			He,
			His
		} = getPronouns(slave);
		let slaveFullNameNode = document.createElement('span');
		if (
			((slave.devotion >= -50 || slave.trust < -20) && (slave.birthName !== slave.slaveName)) ||
			(slave.devotion > 20 || slave.trust < -20)
		) {
			slaveFullNameNode.appendChild(slaveName());
			slaveFullNameNode.appendChild(slaveSurname());
		} else {
			slaveFullNameNode.textContent = `You must break ${his} will further before you can successfully force a new name on ${him}.`;
			slaveFullNameNode.className = "note";
		}

		return slaveFullNameNode;

		function slaveName() {
			// Slave Name
			let slaveNameNode = document.createElement('p');
			label = document.createElement('div');
			result = document.createElement('div');
			result.id = "result";
			result.className = "choices";

			label.append(`Change ${his} given name`);
			if (slave.birthName !== slave.slaveName) {
				label.append(` (${his} birth name was ${slave.birthName})`);
			}
			label.append(`: `);

			textbox = App.UI.DOM.makeTextBox(
				slave.slaveName,
				v => {
					slave.slaveName = v;
				},
				false,
				"Rename");
			label.appendChild(textbox);

			slaveNameNode.appendChild(label);

			result.appendChild(App.UI.DOM.link(
				` Restore ${his} birth name`,
				() => {
					slave.slaveName = slave.birthName;
				},
				false,
				"Rename"
			));

			if (V.arcologies[0].FSPastoralist !== "unset") {
				if (slave.lactation > 0) {
					result.append(` | `);
					result.appendChild(App.UI.DOM.link(
						`Give ${him} a random cow given name`,
						() => {
							slave.slaveName = setup.cowSlaveNames.random();
						},
						false,
						"Rename"
					));
				}
			}
			if (V.arcologies[0].FSIntellectualDependency !== "unset") {
				if (slave.intelligence + slave.intelligenceImplant < -10) {
					result.append(` | `);
					result.appendChild(App.UI.DOM.link(
						`Give ${him} a random stipper given name`,
						() => {
							slave.slaveName = setup.bimboSlaveNames.random();
						},
						false,
						"Rename"
					));
				}
			}
			if (V.arcologies[0].FSChattelReligionist !== "unset") {
				result.append(` | `);
				result.appendChild(App.UI.DOM.link(
					`Give ${him} a random devotional given name`,
					() => {
						slave.slaveName = setup.chattelReligionistSlaveNames.random();
					},
					false,
					"Rename"
				));
			}
			slaveNameNode.appendChild(result);
			return slaveNameNode;
		}

		function slaveSurname() {
			// Slave Surname
			let slaveSurnameNode = document.createElement('p');
			label = document.createElement('div');
			result = document.createElement('div');
			result.id = "result";
			result.className = "choices";

			label.append(`Change ${his} surname`);
			if (slave.birthSurname !== slave.slaveSurname) {
				label.append(` (${his} birth surname was ${slave.birthSurname})`);
			}
			label.append(`: `);

			textbox = App.UI.DOM.makeTextBox(
				slave.slaveSurname,
				v => {
					slave.slaveSurname = textbox.value;
				},
				false,
				"Rename"
			);
			label.appendChild(textbox);

			slaveSurnameNode.appendChild(label);

			result.appendChild(App.UI.DOM.link(
				` Restore ${his} birth surname`,
				() => {
					slave.slaveSurname = slave.birthSurname;
				},
				false,
				"Rename"
			));

			if (slave.slaveSurname) {
				result.append(` | `);
				result.appendChild(App.UI.DOM.link(
					`Take ${his} surname away`,
					() => {
						slave.slaveSurname = 0;
					},
					false,
					"Rename"
				));
			}
			if (slave.relationship >= 5) {
				for (let _i = 0; _i < V.slaves.length; _i++) {
					if (slave.relationshipTarget === V.slaves[_i].ID) {
						if (V.slaves[_i].slaveSurname) {
							if (slave.slaveSurname !== V.slaves[_i].slaveSurname) {
								result.append(` | `);
								const wifePronouns = getPronouns(V.slaves[_i]);
								result.appendChild(App.UI.DOM.link(
									`Give ${him} ${his} ${wifePronouns.wife}'s surname`,
									() => {
										slave.slaveSurname = V.slaves[_i].slaveSurname;
									},
									false,
									"Rename"
								));
								break;
							}
						}
					}
				}
			}
			if (slave.relationship === -3) {
				if (V.PC.slaveSurname) {
					if (slave.slaveSurname !== V.PC.slaveSurname) {
						result.append(` | `);
						result.appendChild(App.UI.DOM.link(
							`Give ${him} your surname`,
							() => {
								slave.slaveSurname = V.PC.slaveSurname;
							},
							false,
							"Rename"
						));
					}
				}
			}
			if (V.arcologies[0].FSRomanRevivalist !== "unset") {
				result.append(` | `);
				result.appendChild(App.UI.DOM.link(
					`Give ${him} a random full Roman name`,
					() => {
						slave.slaveName = setup.romanSlaveNames.random();
						slave.slaveSurname = setup.romanSlaveSurnames.random();
					},
					false,
					"Rename"
				));
			} else if (V.arcologies[0].FSAztecRevivalist !== "unset") {
				result.append(` | `);
				result.appendChild(App.UI.DOM.link(
					`Give ${him} a random full Aztec name`,
					() => {
						slave.slaveName = setup.aztecSlaveNames.random();
						slave.slaveSurname = 0;
					},
					false,
					"Rename"
				));
			} else if (V.arcologies[0].FSEgyptianRevivalist !== "unset") {
				result.append(` | `);
				result.appendChild(App.UI.DOM.link(
					`Give ${him} a random full ancient Egyptian name`,
					() => {
						slave.slaveName = setup.ancientEgyptianSlaveNames.random();
						slave.slaveSurname = 0;
					},
					false,
					"Rename"
				));
			} else if (V.arcologies[0].FSEdoRevivalist !== "unset") {
				result.append(` | `);
				result.appendChild(App.UI.DOM.link(
					`Give ${him} a random full feudal Japanese name`,
					() => {
						slave.slaveName = setup.edoSlaveNames.random();
						slave.slaveSurname = setup.edoSlaveSurnames.random();
					},
					false,
					"Rename"
				));
			}
			if (V.arcologies[0].FSDegradationist > -1) {
				result.append(` | `);
				result.appendChild(App.UI.DOM.link(
					`Give ${him} a degrading full name`,
					() => {
						DegradingName(slave);
					},
					false,
					"Rename"
				));
			}
			slaveSurnameNode.appendChild(result);
			return slaveSurnameNode;
		}
	}

	function hair(slave) {
		let hairNode = new DocumentFragment();
		const {
			// eslint-disable-next-line no-unused-vars
			he,
			him,
			his,
			hers,
			himself,
			boy,
			He,
			His
		} = getPronouns(slave);

		hairNode.appendChild(hairStyle());
		hairNode.appendChild(hairColor());
		return hairNode;

		function hairStyle() {
			let hairStyleNode = document.createElement('p');
			let label = document.createElement('div');
			label.append(`Custom hair description: `);

			let textbox = App.UI.DOM.makeTextBox(
				slave.hStyle,
				v => {
					slave.hStyle = v;
					App.UI.SlaveInteract.custom(slave);
				});
			label.appendChild(textbox);

			switch (slave.hStyle) {
				case "tails":
				case "dreadlocks":
				case "cornrows":
					label.append(` "${His} hair is in ${slave.hStyle}."`);
					break;
				case "ponytail":
					label.append(` "${His} hair is in a ${slave.hStyle}."`);
					break;
				default:
					label.append(` "${His} hair is ${slave.hStyle}."`);
					break;
			}
			hairStyleNode.appendChild(label);

			let choices = document.createElement('div');
			choices.className = "choices";
			choices.appendChild(App.UI.DOM.makeElement('span', ` For best results, use a short, uncapitalized and unpunctuated description; for example: 'back in a ponytail'`, 'note'));
			hairStyleNode.appendChild(choices);
			return hairStyleNode;
		}

		function hairColor() {
			let hairStyleNode = document.createElement('p');
			let label = document.createElement('div');
			label.append(`Custom hair color: `);

			let textbox = App.UI.DOM.makeTextBox(
				slave.hColor,
				v => {
					slave.hColor = v;
					App.UI.SlaveInteract.custom(slave);
				});
			label.appendChild(textbox);
			label.append(` "${His} hair is ${slave.hColor}."`);
			hairStyleNode.appendChild(label);

			let choices = document.createElement('div');
			choices.className = "choices";
			choices.appendChild(App.UI.DOM.makeElement('span', ` For best results, use a short, uncapitalized and unpunctuated description; for example: 'black with purple highlights'`, 'note'));
			hairStyleNode.appendChild(choices);
			return hairStyleNode;
		}
	}



	function eyeColor(slave) {
		let eyeColorNode = document.createElement('p');
		const {
			// eslint-disable-next-line no-unused-vars
			he,
			him,
			his,
			hers,
			himself,
			boy,
			He,
			His
		} = getPronouns(slave);
		let label = document.createElement('div');
		if (getLenseCount(slave) > 0) {
			label.textContent = `${He} is wearing ${App.Desc.eyesColor(slave, "", "lense", "lenses")}.`;
		} else {
			label.textContent = `${He} has ${App.Desc.eyesColor(slave)}.`;
		}
		eyeColorNode.appendChild(label);

		let choices = document.createElement('div');
		choices.className = "choices";

		let eye;
		let textbox;

		if (hasLeftEye(slave)) {
			eye = document.createElement('div');
			eye.append(`Custom left eye color: `);
			textbox = App.UI.DOM.makeTextBox(
				slave.eye.left.iris,
				v => {
					slave.eye.left.iris = v;
					App.UI.SlaveInteract.custom(slave);
				});
			eye.appendChild(textbox);
			choices.appendChild(eye);
		}
		if (hasRightEye(slave)) {
			eye = document.createElement('div');
			eye.append(`Custom right eye color: `);
			textbox = App.UI.DOM.makeTextBox(
				slave.eye.right.iris,
				v => {
					slave.eye.right.iris = v;
					App.UI.SlaveInteract.custom(slave);
				});
			eye.appendChild(textbox);
			choices.appendChild(eye);
		}
		choices.appendChild(App.UI.DOM.makeElement('span', `For best results, use a short, uncapitalized and unpunctuated description; for example: 'blue'`, 'note'));
		eyeColorNode.appendChild(choices);
		return eyeColorNode;
	}

	function customTattoo(slave) {
		let el = document.createElement('p');
		const {
			// eslint-disable-next-line no-unused-vars
			he,
			him,
			his,
			hers,
			himself,
			boy,
			He,
			His
		} = getPronouns(slave);
		el.append(`Change ${his} custom tattoo: `);
		el.appendChild(App.UI.DOM.makeTextBox(
			slave.custom.tattoo,
			v => {
				slave.custom.tattoo = v;
				App.UI.SlaveInteract.custom(slave);
			}));

		let choices = document.createElement('div');
		choices.className = "choices";
		choices.appendChild(App.UI.DOM.makeElement('span', `For best results, use complete sentences; for example: '${He} has blue stars tattooed along ${his} cheekbones.'`, 'note'));
		el.appendChild(choices);

		return el;
	}

	function customOriginStory(slave) {
		let el = document.createElement('p');
		const {
			// eslint-disable-next-line no-unused-vars
			he,
			him,
			his,
			hers,
			himself,
			boy,
			He,
			His
		} = getPronouns(slave);

		el.append(`Change ${his} origin story: `);
		el.appendChild(App.UI.DOM.makeTextBox(
			slave.origin,
			v => {
				slave.origin = v;
				App.UI.SlaveInteract.custom(slave);
			}));

		let choices = document.createElement('div');
		choices.className = "choices";
		choices.appendChild(App.UI.DOM.makeElement('span', ` For best results, use complete, capitalized and punctuated sentences; for example: '${He} followed you home from the pet store.'`, 'note'));
		el.appendChild(choices);

		return el;
	}

	function customDescription(slave) {
		let el = document.createElement('p');
		const {
			// eslint-disable-next-line no-unused-vars
			he,
			him,
			his,
			hers,
			himself,
			boy,
			He,
			His
		} = getPronouns(slave);

		el.append(`Change ${his} custom description: `);
		el.appendChild(App.UI.DOM.makeTextBox(
			slave.custom.desc,
			v => {
				slave.custom.desc = v;
				App.UI.SlaveInteract.custom(slave);
			}));

		let choices = document.createElement('div');
		choices.className = "choices";
		choices.appendChild(App.UI.DOM.makeElement('span', ` For best results, use complete, capitalized and punctuated sentences; for example: '${He} has a beauty mark above ${his} left nipple.'`, 'note'));
		el.appendChild(choices);

		return el;
	}

	function customLabel(slave) {
		let el = document.createElement('p');
		const {
			// eslint-disable-next-line no-unused-vars
			he,
			him,
			his,
			hers,
			himself,
			boy,
			He,
			His
		} = getPronouns(slave);

		el.append(`Change ${his} custom label: `);
		el.appendChild(App.UI.DOM.makeTextBox(
			slave.custom.label,
			v => {
				slave.custom.label = v;
				App.UI.SlaveInteract.custom(slave);
			}));

		let choices = document.createElement('div');
		choices.className = "choices";
		choices.appendChild(App.UI.DOM.makeElement('span', ` For best results, use a short phrase; for example: 'Breeder.'`, 'note'));
		el.appendChild(choices);

		return el;
	}


	function customSlaveImage(slave) {
		let el = document.createElement('p');
		const {
			// eslint-disable-next-line no-unused-vars
			he,
			him,
			his,
			hers,
			himself,
			boy,
			He,
			His
		} = getPronouns(slave);
		el.append(`Assign ${him} a custom image: `);

		let textbox = document.createElement("INPUT");
		textbox.id = "customImageValue";
		el.appendChild(textbox);


		let kbd = document.createElement('kbd');
		let select = document.createElement('SELECT');
		select.id = "customImageFormatSelector";
		select.style.border = "none";

		let filetypes = [
			"png",
			"jpg",
			"gif",
			"webm",
			"webp",
			"mp4"
		];

		filetypes.forEach((fileType) => {
			let el = document.createElement('OPTION');
			el.value = fileType;
			el.text = fileType.toUpperCase();
			select.add(el);
		});
		kbd.append(`.`);
		kbd.appendChild(select);
		el.appendChild(kbd);

		el.appendChild(
			App.UI.DOM.link(
				` Reset`,
				() => {
					slave.custom.image = null;
					App.UI.SlaveInteract.custom(slave);
					App.Art.refreshSlaveArt(slave, 3, "artFrame");
				},
			)
		);

		let choices = document.createElement('div');
		choices.className = "choices";
		let note = document.createElement('span');
		note.className = "note";
		note.append(`Place file in the `);

		kbd = document.createElement('kbd');
		kbd.append(`resources`);
		note.appendChild(kbd);

		note.append(` folder. Choose the extension from the menu first, then enter the filename in the space and press enter. For example, for a file with the path `);

		kbd = document.createElement('kbd');
		kbd.textContent = `\\bin\\resources\\headgirl.`;
		let filetypeDesc = document.createElement('span');
		filetypeDesc.id = "customImageFormatValue";
		filetypeDesc.textContent = "png";
		kbd.appendChild(filetypeDesc);
		note.appendChild(kbd);

		note.append(`, choose `);

		kbd = document.createElement('kbd');
		kbd.textContent = `PNG`;
		note.appendChild(kbd);

		note.append(` then enter `);

		kbd = document.createElement('kbd');
		kbd.textContent = `headgirl`;
		note.appendChild(kbd);

		note.append(`.`);
		choices.appendChild(note);
		el.appendChild(choices);

		jQuery(function() {
			function activeSlave() {
				return slave;
			}

			jQuery("#customImageFormatValue").text(activeSlave().custom.image === null ? "png" : activeSlave().custom.image.format);
			jQuery("#customImageValue")
				.val(activeSlave().custom.image === null ?
					"" : activeSlave().custom.image.filename)
				.on("change", function(e) {
					const c = activeSlave().custom;
					if (this.value.length === 0) {
						c.image = null;
					} else {
						if (c.image === null) {
							c.image = {
								filename: this.value,
								format: jQuery("#customImageFormatSelector").val()
							};
							App.Art.refreshSlaveArt(slave, 3, "artFrame");
						} else {
							c.image.filename = this.value;
							App.Art.refreshSlaveArt(slave, 3, "artFrame");
						}
					}
				});
			jQuery("#customImageFormatSelector")
				.val(activeSlave().custom.image ? activeSlave().custom.image.format : "png")
				.on("change", function(e) {
					if (activeSlave().custom.image !== null) {
						activeSlave().custom.image.format = this.value;
					}
					jQuery("#customImageFormatValue").text(this.value);
				});
		});
		return el;
	}

	function customHairImage(slave) {
		let el = document.createElement('p');
		const {
			// eslint-disable-next-line no-unused-vars
			he,
			him,
			his,
			hers,
			himself,
			boy,
			He,
			His
		} = getPronouns(slave);

		if (V.seeImages === 1 && V.imageChoice === 1) {
			if (!slave.custom.hairVector) {
				slave.custom.hairVector = 0;
			}
			el.append(`Assign ${him} a custom hair SVG image: `);

			el.appendChild(App.UI.DOM.makeTextBox(
				slave.custom.hairVector,
				v => {
					slave.custom.hairVector = v;
					App.UI.SlaveInteract.custom(slave);
				}));

			el.appendChild(
				App.UI.DOM.link(
					` Reset`,
					() => {
						slave.custom.hairVector = 0;
						App.UI.SlaveInteract.custom(slave);
						App.Art.refreshSlaveArt(slave, 3, "artFrame");
					},
				)
			);
		}

		return el;
	}
})();

/** @typedef RowItem
 * @type {object}
 * @property {string} [FS] - FS requirement, if any
 * @property {string} [text] - link text
 * @property {object} [updateSlave] - properties to be merged onto the slave
 * @property {object} [update] - properties to be merged into global state
 * @property {string} [disabled] - text indicating why the option is unavailable
 * @property {string} [note]
 */

/** Append a simple row of choices with a label to a container, if there are choices to be made.
 * @param {Node} parent
 * @param {string} label
 * @param {RowItem[]} array
 * @param {App.Entity.SlaveState} slave
 */
App.UI.SlaveInteract.appendLabeledChoiceRow = function(parent, label, array, slave) {
	if (array.length > 0) {
		let links = document.createElement('div');
		links.append(`${label}: `);
		links.appendChild(App.UI.SlaveInteract.generateRows(array, slave));
		links.className = "choices";
		return parent.appendChild(links);
	}
};

/** Generate a row of choices
 * @param {RowItem[]} array
 * @param {App.Entity.SlaveState} slave
 * @param {string} [category] - should be in the form of slave.category, the thing we want to update.
 * @param {boolean} [accessCheck=false]
 * @returns {HTMLSpanElement}
 */
App.UI.SlaveInteract.generateRows = function(array, slave, category, accessCheck = false) {
	let row = document.createElement('span');
	let useSep = false;
	for (let i = 0; i < array.length; i++) {
		let link;
		const separator = document.createTextNode(` | `);
		const keys = Object.keys(array[i]);

		// Test to see if there was a problem with the key
		for (let j = 0; j < keys.length; j++) {
			if (["FS", "text", "updateSlave", "update", "note", "disabled"].includes(keys[j])) {
				continue;
			} else {
				array[i].text += " ERROR, THIS SCENE WAS NOT ENTERED CORRECTLY";
				console.log("Trash found while generateRows() was running: " + keys[j] + ": " + array[i][keys[j]]);
				break;
			}
		}
		// Some items will never be in App.data.misc, especially "none" if it falls in between harsh and nice data sets. Trying to look it up would cause an error, which is what access check works around.
		let unlocked = false;
		if (accessCheck === true) {
			if (category === "chastity") {
				let text = array[i].text.toLowerCase(); // Yucky. Category name does not match for chastity (since it sets multiple kinds of chastity at once). Compare using a lowercased name instead.
				unlocked = isItemAccessible.entry(text, category, slave);
			} else {
				unlocked = isItemAccessible.entry(array[i].updateSlave[category], category, slave);
			}
		}
		if (accessCheck === false || unlocked) {
			if (i < array.length && i !== 0 && useSep === true) { // start with separator (after first loop); can't after since the last loop may not have any text.
				row.appendChild(separator);
			}
			useSep = true; // First item may not appear and if it doesn't we don't want the second to start with a '|'
			// is it just text?
			if (array[i].disabled) {
				link = App.UI.DOM.disabledLink(array[i].text, [array[i].disabled]);
			} else if (typeof unlocked === 'string') {
				link = App.UI.DOM.disabledLink(array[i].text, [unlocked]);
			} else {
				link = document.createElement('span');

				// Set up the link
				link.appendChild(
					App.UI.DOM.link(
						`${array[i].text} `,
						() => { click(array[i]); },
					)
				);

				if (array[i].FS) {
					let FS = array[i].FS.substring(2); // Given "FSEdoRevivalist", cut off the first two letters to start a user friendly tooltip
					FS = FS.replace(/([A-Z])/g, ` $1`); // Given "EdoRevivalist", find every capital letter and put a space in front of it
					FS = App.UI.DOM.disabledLink(`FS`, [FS]); // Tooltip should read "Edo Revivalist"
					FS.style.fontStyle = "italic";
					link.appendChild(FS);
				}

				// add a note node if required
				if (array[i].note) {
					link.appendChild(App.UI.DOM.makeElement('span', ` ${array[i].note}`, 'note'));
				}
			}
			row.appendChild(link);
		}
	}

	return row;

	/** @param {RowItem} arrayOption */
	function click(arrayOption) {
		if (arrayOption.updateSlave) {
			for (const slaveProperty in arrayOption.updateSlave) {
				_.set(slave, slaveProperty, arrayOption.updateSlave[slaveProperty]);
			}
		}
		if (arrayOption.update) {
			Object.assign(V, arrayOption.update);
		}
		App.UI.Wardrobe.refreshAll(slave);
		App.UI.SlaveInteract.refreshAll(slave);
		return;
	}
};

App.UI.SlaveInteract.refreshAll = function(slave) {
	App.UI.SlaveInteract.drugs(slave);
	App.UI.SlaveInteract.bloating(slave);
	App.UI.SlaveInteract.fertility(slave);
	App.UI.SlaveInteract.curatives(slave);
	App.UI.SlaveInteract.aphrodisiacs(slave);
	App.UI.SlaveInteract.incubator(slave);
	App.UI.SlaveInteract.nursery(slave);
	App.UI.SlaveInteract.custom(slave);
	App.UI.SlaveInteract.hormones(slave);
	App.UI.SlaveInteract.diet(slave);
	App.UI.SlaveInteract.dietBase(slave);
	App.UI.SlaveInteract.snacks(slave);
	App.UI.SlaveInteract.smartSettings(slave);
	App.UI.SlaveInteract.orgasm(slave);
	App.UI.SlaveInteract.work(slave);
};

src/interaction/slaveInteract.js