code:
/* eslint-disable camelcase */
/** @param {App.Entity.SlaveState} slave */
App.UI.SlaveInteract.createSlaveBot = function(slave) {
	const el = new DocumentFragment();
	App.UI.DOM.appendNewElement("p", el, `Downloading slave bot...`, "note");
	Exporter.Json(createCharacterDataFromSlave(slave));
	return el;
};

// Adapted from https://github.com/ZoltanAI/character-editor
class Exporter {
	static downloadFile(file) {
		const link = window.URL.createObjectURL(file);

		const a = document.createElement('a');
		a.setAttribute('download', file.name);
		a.setAttribute('href', link);
		a.click();
	}

	static Json(characterCard) {
		const file = new File([JSON.stringify(characterCard, undefined, '\t')], (characterCard.data.name || 'character') + '.json', {type: 'application/json;charset=utf-8'});

		Exporter.downloadFile(file);
	}
}

/** @param {App.Entity.SlaveState} slave */
function createCharacterDataFromSlave(slave) {
	// Construct a character card based on the Card v2 spec: https://github.com/malfoyslastname/character-card-spec-v2
	const characterCard = {
		spec: 'chara_card_v2',
		spec_version: '2.0', // May 8th addition
		data: {
		  alternate_greetings: [],
		  avatar: "none",
		  character_version: "main",
		  creator: `FreeCities ${App.Version.pmod} System Generated`,
		  creator_notes: `FreeCities ${App.Version.pmod} System Generated Slave Bot`,
			description: generateDescription(slave) + generatePromptStd(slave),
			first_mes: generateFirstMessage(slave),
		  mes_example: "",
		  name: slave.slaveName,
		  personality: generatePersonality(slave),
			scenario: `{{char}} and {{user}} exist in the slaveholding arcology of ${V.arcologies[0].name}. {{char}} is in {{user}}'s office, waiting for inspection.`,
			system_prompt: "",
		  character_book: {  // aka Lorebook
				entries: [
					lorebookArcology(0),
					lorebookFuckdoll(1),
					lorebookMindbroken(2),
					lorebookArcade(3),
					lorebookDairy(4)
				// lorebookActiveFS(100), // adds all active FS starting at (n)
				],
				name: `Free Cities ${slave.slaveName}`
			}
		}
	};
	return characterCard;
}

function generateDescription(slave) {
	let r = [];
	const {He, His, he, him, his} = getPronouns(slave);
	let descParts = [];

	// FS Temp Work NG
	// r.push(lorebookActiveFS());

	// NAME
	r.push(`Name: ${SlaveFullName(slave)}`);

	// RELATIONSHIP
	r.push("\r\nRelationship: {{char}} is {{user}}'s ");
	// Slave age (consider visible age)
	r.push(`${slave.actualAge} year old `);

	// Devotion
	if (slave.fetish !== Fetish.MINDBROKEN) {
		if (slave.devotion < -95) {
			r.push("hate-filled, ");
		} else if (slave.devotion < -50) {
			r.push("hateful, ");
		} else if (slave.devotion < -20) {
			r.push("reluctant, ");
		} else if (slave.devotion <= 20) {
			r.push("hesitant, ");
		} else if (slave.devotion <= 50) {
			r.push("accepting, ");
		} else if (slave.devotion <= 95) {
			r.push("devoted, ");
		} else {
			r.push("worshipful, ");
		}

		// Trust
		if (slave.trust < -95) {
			r.push("abjectly terrified ");
		} else if (slave.trust < -50) {
			r.push("terrified ");
		} else if (slave.trust < -20) {
			r.push("frightened ");
		} else if (slave.trust < 20) {
			r.push("fearful ");
		} else if (slave.trust <= 50) {
			// if (slave.devotion < -20) {
			// 	r.push("careful ");
			// } else {
			// 	r.push("careful ");
			// }
			// FIXME: @null this block always spits out the same result. It should be changed or removed
			r.push("careful ");
		} else if (slave.trust < 95) {
			if (slave.devotion < -20) {
				r.push("bold ");
			} else {
				r.push("trusting ");
			}
		} else {
			if (slave.devotion < -20) {
				r.push("defiant ");
			} else {
				r.push("profoundly trusting ");
			}
		}
	} else {
		r.push("mindbroken ");
	}

	// Slave's Title, ex:"pregnant big bottomed busty milky hourglass broodmother"
	if (slave.fuckdoll > 0) {
		r.push("Fuckdoll");
	} else {
		r.push(`${SlaveTitle(slave)}`);
	}

	// DESCRIPTION
	r.push("\r\nDescription: ");

	// Eyes
	// eye color (orig vs. current?), Add check for no eyes (does it matter?)

	if (slave.fuckdoll > 0) {
		r.push(`blinded, `);
	} else if (!canSee(slave)) {
		r.push(`${slave.eye.origColor} eyes, is blind, `);
	} else {
		r.push(`${slave.eye.origColor} eyes, `);
	}

	if (slave.fuckdoll > 0) {
		r.push("mute, "); // Add fuckdoll additions (ADAPTATION) to any of above skipped descriptors here NG
	} else {
		// Skin
		r.push(`${slave.skin} skin, `);

		// Slave intelligence: Ignore average, include mindbroken
		if (slave.fetish === Fetish.MINDBROKEN) {
			r.push("mindbroken, ");
		} else if (slave.intelligence < -95) {
			r.push("borderline retarded, ");
		} else if (slave.intelligence < -50) {
			r.push("very dumb, ");
		} else if (slave.intelligence > 95) {
			r.push("brilliant, ");
		} else if (slave.intelligence > 50) {
			r.push("very smart, ");
		}

		// Beauty
		if (slave.face < -40) {
			r.push(`${slave.faceShape} ugly face, `);
		} else if (slave.face > 50) {
			r.push(`${slave.faceShape} gorgeous face, `);
		} else if (slave.face > 10) {
			r.push(`${slave.faceShape} very pretty face, `);
		} else {
			r.push(`${slave.faceShape} face, `);
		}

		// Hairstyle
		if (slave.hLength > 100) {
			r.push("very long ");
		} else if (slave.hLength > 30) {
			r.push("long ");
		} else if (slave.hLength > 10) {
			r.push("short ");
		} else if (slave.hLength > 0) {
			r.push("very short ");
		}
		r.push(`${slave.hColor} `);

		// Add "hair" to hairstyles that need it to make sense (e.g. "messy" becomes "messy hair" but "dreadlocks" stays as is)
		if (["braided", "curled", "eary", "bun", "messy bun", "tails", "drills", "luxurious", "messy", "neat", "permed", "bangs", "hime", "strip", "up", "trimmed", "undercut",	"double buns", "chignon"].includes(slave.hStyle)) {
			r.push(`${slave.hStyle} hair, `);
		} else {
			r.push(`${slave.hStyle}, `);
		}

		// Start conditional descriptions
		// Education (bimbo/hindered, well educated)
		if (slave.education < -10) {
			descParts.push("vapid bimbo with no education");
		} else if (slave.intelligence > 25) {
			descParts.push("very well educated");
		}
	}

	// Height. Ignore Average
	if (slave.height < 150) {
		descParts.push(`short`);
	} else if (slave.height > 180) {
		descParts.push(`tall`);
	}

	// Weight. Ignore average
	if (slave.weight < -95) {
		descParts.push(`emaciated`);
	} else if (slave.weight < -30) {
		descParts.push(`very skinny`);
	} else if (slave.weight > 95) {
		descParts.push(`very fat`);
	} else if (slave.weight > 30) {
		descParts.push(`plump`);
	}

	// Boobs. Ignore Average. Add lactation? NG
	if (slave.boobs < 300) {
		descParts.push(`flat chested`);
	} else if (slave.boobs < 500) {
		descParts.push(`small breasts`);
	} else if (slave.boobs > 1400) {
		descParts.push(`massive breasts that impede movement`);
	} else if (slave.boobs > 800) {
		descParts.push(`large breasts`);
	}

	// Butt. Ignore average
	if (slave.butt <= 1) {
		descParts.push(`flat butt`);
	} else if (slave.butt > 7) {
		descParts.push(`gigantic ass`);
	} else if (slave.butt > 3) {
		descParts.push(`big ass`);
	}

	// Musculature
	if (slave.muscles < -31) {
		descParts.push(`very weak`);
	} else if (slave.muscles > 50) {
		descParts.push(`very muscular`);
	}

	// Check amputee (add missing just arms/legs)
	if (isAmputee(slave)) {
		descParts.push(`missing both arms and both legs`);
	}

	// Check pregnant
	if (slave.preg > 30) {
		descParts.push(`very pregnant`);
	} else if (slave.preg > 20) {
		descParts.push(`pregnant`);
	}

	// Check teeth
	if (slave.teeth ===  "straightening braces" || slave.teeth ===  "cosmetic braces") {
		descParts.push(`braces on teeth`);
	} else if (slave.teeth ===  "removable") {
		descParts.push(`has dentures`);
	}

	if (descParts.length > 0) {
		r.push(`${descParts.join(', ')}`);
	}

	// BACKGROUND
	if (slave.fuckdoll > 0 || slave.fetish === Fetish.MINDBROKEN) { // in neither case would slave recall or this be important
		null;
		// FIXME: @null Is this supposed to be a return? or is this supposed to do nothing?
		// If nothing it should be a single if statement, not if else. `if (slave.fuckdoll <== 0 || slave.fetish !== Fetish.MINDBROKEN) {`
		// If it's a place holder replace null with a `// TODO: thing to do` comment
	} else {
		if (slave.career === "a slave") {
			r.push(`\r\nBackground: {{char}} has been enslaved for as long as ${he} can remember`);
		} else {
			r.push(`\r\nBackground: Prior to enslavement, {{char}} was ${slave.career}`);
		}
	}

	// ASSIGNMENT
	r.push(`\r\nAssignment: ${slave.assignment}`);

	if (slave.fuckdoll > 0) {
		null;
		// FIXME: @null Is this supposed to be a return? or is this supposed to do nothing?
		// If nothing it should be a single if statement, not if else. `if (slave.fuckdoll <== 0) {`
		// If it's a place holder replace null with a `// TODO: thing to do` comment
	} else {
		// FETISH
		// Paraphilias listed and prompted
		if (slave.sexualFlaw === SexualFlaw.CUMADDICT ) {
			r.push(`\r\nFetish: pathologically addicted to cum`);
		} else if (slave.sexualFlaw === SexualFlaw.ANALADDICT ) {
			r.push(`\r\nFetish: pathologically addicted to anal sex`);
		} else if (slave.sexualFlaw === SexualFlaw.NEGLECT ) {
			r.push(`\r\nTrait: only considers ${his} partner's pleasure`);
		} else if (slave.sexualFlaw === SexualFlaw.ATTENTION ) {
			r.push(`\r\nTrait: pathologically narcissistic`);
		} else if (slave.sexualFlaw === SexualFlaw.BREASTEXP ) {
			r.push(`\r\nFetish: pathologically addicted to breast augmentation`); // is this right
		} else if (slave.sexualFlaw === SexualFlaw.SELFHATING ) {
			r.push(`\r\nTrait: pathologically masochistic`);
		} else if (slave.sexualFlaw === SexualFlaw.ABUSIVE || slave.sexualFlaw === SexualFlaw.MALICIOUS) {
			r.push(`\r\nTrait: sociopathic, delights in abusing others`); // Are above the same for purposes of LLM
		}

		// Explain sex/entertainment skill level. Leave off average. Check virgin status.

		// ABILITIES
		let abilParts = [];
		if (slave.vagina === 0){
			abilParts.push("virgin");
		} else if (slave.skill.whoring <10){
			abilParts.push("sexually unskilled");
		} else if (slave.skill.whoring > 100){
			abilParts.push("renowned whore");
		} else if (slave.skill.whoring > 61){
			abilParts.push("expert whore");
		}

		if (slave.skill.entertainment > 100){
			abilParts.push(`renowned entertainer`);
		} else if (slave.skill.entertainment > 61){
			abilParts.push(`expert entertainer`);
		}
		if (abilParts.length > 0) {
			r.push(`\r\nAbilities: ${abilParts.join(', ')}`);
		}
	}

	// WEARING
	r.push(`\r\n\Wearing: ${slave.clothes}`);
	if (slave.collar !== "none"){
		r.push(`, ${slave.collar} collar`);
	}

	// GENITALS
	// Front
	r.push("\r\nGenitals: ");
	if (slave.dick === 0 && slave.vagina === -1) { // null slave
		r.push("No genitals");
	} else if (slave.vagina < 0) { // has a dick
		if (slave.dick < 1) {
			r.push("tiny penis");
		} else if (slave.dick > 8) {
			r.push("absurdly large penis");
		} else if (slave.dick > 5) {
			r.push("large penis");
		} else {
			r.push("penis");
		}
	} else if (slave.vagina === 0) { // has a pussy
		r.push("virgin pussy");
	} else if (slave.vagina < 2) {
		r.push("tight pussy");
	} else if (slave.vagina > 6) {
		r.push("cavernous pussy");
	} else if (slave.vagina > 3) {
		r.push("loose pussy");
	} else {
		r.push("pussy");
	}
	// Back
	if (slave.anus === 0) {
		r.push(", virgin anus");
	} else if (slave.vagina > 3) {
		r.push(", loose anus");
	}
	// PHair
	if (slave.pubicHStyle === "hairless" || slave.pubicHStyle === "bald") {
		r.push(", no pubic hair");
	} else {
		r.push(`, pubic hair ${slave.pubicHStyle}`);
	}
	// RULES
	let rulesParts = [];

	if (slave.fuckdoll > 0) {
		null;
		// FIXME: @null Is this supposed to be a return? or is this supposed to do nothing?
		// If nothing it should be a single if statement, not if else. `if (slave.fuckdoll <== 0) {`
		// If it's a place holder replace null with a `// TODO: thing to do` comment
	} else {
		// Speech (also check for mute)
		if (slave.voice === 0) {
			rulesParts.push(`Mute and cannot speak`);
		} else  if (slave.rules.speech === "restrictive"){
			rulesParts.push(`Not allowed to speak`);
		}
		// Masturbation rules, also check for ability to molest other slaves (release cases?)
		if (slave.rules.release.masturbation === 0){
			rulesParts.push(`Not allowed to masturbate`);
		}
		// How they address the user, if not mute
		if (slave.voice !== 0) {
			rulesParts.push(`Addresses {{user}} as ${properMaster()}`);
		}
	}

	if (rulesParts.length > 0) {
		r.push(`\r\nRules: ${rulesParts.join(', ')}`);
	}

	// TATTOOS - Too much context for impact?
	let tattooParts = [];

	if (slave.fuckdoll > 0) {
		null;
		// FIXME: @null Is this supposed to be a return? or is this supposed to do nothing?
		// If nothing it should be a single if statement, not if else. `if (slave.fuckdoll <== 0) {`
		// If it's a place holder replace null with a `// TODO: thing to do` comment
	} else {
		if (slave.armsTat) {
			tattooParts.push(`${slave.armsTat} arm tattoo`);
		}
		if (slave.legsTat) {
			tattooParts.push(`${slave.legsTat} leg tattoo`);
		}
		if (slave.bellyTat) {
			tattooParts.push(`${slave.bellyTat} belly tattoo`);
		}
		if (slave.boobsTat) {
			tattooParts.push(`${slave.boobsTat} breast tattoo`);
		}

		if (tattooParts.length > 0) {
			r.push(`\r\nTattoos: ${tattooParts.join(', ')}`);
		}
	}

	// CHASTITY
	let chasParts = [];
	if (slave.chastityVagina === 1) {
		chasParts.push(`vagina`);
	}
	if (slave.chastityPenis === 1) {
		chasParts.push(`penis`);
	}
	if (slave.chastityAnus === 1) {
		chasParts.push(`anus`);
	}
	if (chasParts.length > 0) {
		r.push(`\r\nChastity device covers: ${chasParts.join(', ')}`);
	}

	// PROSTHETICS
	if (hasBothProstheticArms(slave) && hasBothProstheticLegs(slave)) {
		r.push("\r\n\{{char}} has android arms and legs");
	} else if (hasBothProstheticArms(slave)) {
		r.push("\r\n\{{char}} has android arms");
	} else if (hasBothProstheticLegs(slave)) {
		r.push("\r\n\{{char}} has android legs");
	}

	// RELATIONSHIPS and RIVALRIES
	// Lover
	if (slave.fuckdoll > 0) {
		null;
		// FIXME: @null Is this supposed to be a return? or is this supposed to do nothing?
		// If nothing it should be a single if statement, not if else. `if (slave.fuckdoll <== 0) {`
		// If it's a place holder replace null with a `// TODO: thing to do` comment
	} else {
		const lover = slave.relationship > 0 ? getSlave(slave.relationshipTarget) : null;
		if (lover) {
			if (slave.relationship > 4) {
				r.push(`\r\n${SlaveFullName(lover)} is {{char}}'s wife`);
			} else if (slave.relationship > 3) {
				r.push(`\r\n${SlaveFullName(lover)} is {{char}}'s lover`);
			} else if (slave.relationship > 2) {
				r.push(`\r\n${SlaveFullName(lover)} is {{char}}'s girlfriend`);
			} else if (slave.relationship > 1) {
				r.push(`\r\n${SlaveFullName(lover)} is {{char}}'s friend with benefits`);
			} else {
				r.push(`\r\n${SlaveFullName(lover)} is {{char}}'s friend`);
			}
		}
	}

	// Rival
	if (slave.fuckdoll > 0) {
		null;
		// FIXME: @null Is this supposed to be a return? or is this supposed to do nothing?
		// If nothing it should be a single if statement, not if else. `if (slave.fuckdoll <== 0) {`
		// If it's a place holder replace null with a `// TODO: thing to do` comment
	} else {
		const rival = slave.rivalry > 0 ? getSlave(slave.rivalryTarget) : null;
		if (rival) {
			if (slave.rivalry <= 1) {
				r.push(`\r\n{{char}} dislikes ${SlaveFullName(rival)}`);
			} else if (slave.rivalry <= 2) {
				r.push(`\r\n{{char}} hates ${SlaveFullName(rival)}`);
			} else {
				r.push(`\r\n{{char}} bitterly hates ${SlaveFullName(rival)}`);
			}
		}
	}

	return r.join("");
}

function generatePromptStd(slave) {
	let r = [];

	// PERMANENT CHAR PROMPT PART - Does not rely on any slave attributes, but leaving with .join and (slave) passthrough for future conditionals
	r.push(`\r\n{{user}} is the owner of ${V.arcologies[0].name}, an arcology in the fictional slave-holding world of Free Cities. `);
	r.push(generatePlayerSummary());
	r.push(`\r\n{{user}} has authority over his subjects, including training and conditioning slaves for sexual servitude. This is accomplished through coercion, punishment, and other conditioning.\r\nRespond and roleplay only for {{char}} and other NPC.`);

	return r.join("");
}

function generateFirstMessage(slave) {
	let r = [];
	const {He, His, he, him, his} = getPronouns(slave);
	// Set up basic scenario
	r.push("I am sitting in my office as {{char}} arrives for inspection.\r\n");

	// Trust switches
	if (slave.fuckdoll > 0) {
		r.push(`{{char}} is lead in, encased in ${his} fuckdoll suit. `);
		if (slave.fuckdoll < 50) {
			r.push(`${His} twitching hands and slight movement shows ${he} is still fighting the adaptation process, but is unable to move. `);
		} else {
			r.push(`${He} is eerily still. `);
		}
	} else {
		r.push("{{char}} comes in, ");

		if (slave.trust < -95) {
			r.push(`appearing abjectly terrified, barely able to control ${his} terror.`);
		} else if (slave.trust < -50) {
			r.push(`appearing terrified, barely concealing ${his} fear. `);
		} else if (slave.trust < -20) {
			r.push("looking frightened. ");
		} else if (slave.trust < 20) {
			r.push("looking fearful. ");
		} else if (slave.trust <= 50) {
			// if (slave.devotion < -20) {
			// 	r.push("appearing cautious. ");
			// } else {
			// 	r.push("appearing cautious. ");
			// }
			// FIXME: @null this block always spits out the same result. It should be changed or removed
			r.push("appearing cautious. ");
		} else if (slave.trust < 95) {
			if (slave.devotion < -20) {
				r.push("boldly approaching my desk. ");
			} else {
				r.push("trustingly approaching my desk. ");
			}
		} else {
			if (slave.devotion < -20) {
				r.push(`defiantly approaching my desk, ${his} head held high. `);
			} else {
				r.push(`a serene look of profound trust on ${his} face. `);
			}
		}

		// Devotion switches
		if (slave.devotion < -95) {
			r.push(`${His} hate-filled face is barely under control. `);
		} else if (slave.devotion < -50) {
			r.push(`${His} hate for me and these inspections is obvious, though ${he} manages a neutral face. `);
		} else if (slave.devotion < -20) {
			r.push(`${He} appears to have come only reluctantly, preferring to avoid me whenever possible. `);
		} else if (slave.devotion <= 20) {
			r.push(`${He} appears hesitant, not quite trusting me or these inspections.`);
		} else if (slave.devotion <= 50) {
			r.push(`${His} demeanor is accepting, knowing that inspections are a normal part of the week.`);
		} else if (slave.devotion <= 95) {
			r.push(`${He} looks very pleased to have ${properMaster()}'s attention, shining with devotion. `);
		} else {
			r.push(`${He} is positively glowing at the opportunity to have ${his} precious ${properMaster()}'s attention, radiating worshipful devotion.`);
		}
	}
	r.push("\r\n");

	// Check Voice and Vocal Rules
	if (slave.voice === 0 || slave.rules.speech === "restrictive") {
		r.push(`{{char}} is silent, waiting for {{user}} to act.`);
	} else  {
		r.push(`{{char}} looks forward and speaks, "${properMaster()}, how may I serve you?"`);
	}

	return r.join("");
}

function generatePersonality(slave) {
	let r = [];
	r.push("\r\n{{char}} is {{user}}'s ");
	// Slave age (consider visible age)
	r.push(`${slave.actualAge} year old `);

	// Devotion
	if (slave.fetish !== Fetish.MINDBROKEN) {
		if (slave.devotion < -95) {
			r.push("hate-filled, ");
		} else if (slave.devotion < -50) {
			r.push("hateful, ");
		} else if (slave.devotion < -20) {
			r.push("reluctant, ");
		} else if (slave.devotion <= 20) {
			r.push("hesitant, ");
		} else if (slave.devotion <= 50) {
			r.push("accepting, ");
		} else if (slave.devotion <= 95) {
			r.push("devoted, ");
		} else {
			r.push("worshipful, ");
		}

		// Trust
		if (slave.trust < -95) {
			r.push("abjectly terrified ");
		} else if (slave.trust < -50) {
			r.push("terrified ");
		} else if (slave.trust < -20) {
			r.push("frightened ");
		} else if (slave.trust < 20) {
			r.push("fearful ");
		} else if (slave.trust <= 50) {
			// if (slave.devotion < -20) {
			// 	r.push("careful ");
			// } else {
			// 	r.push("careful ");
			// }
			// FIXME: @null this block always spits out the same result. It should be changed or removed
			r.push("careful ");
		} else if (slave.trust < 95) {
			if (slave.devotion < -20) {
				r.push("bold ");
			} else {
				r.push("trusting ");
			}
		} else {
			if (slave.devotion < -20) {
				r.push("defiant ");
			} else {
				r.push("profoundly trusting ");
			}
		}
	} else {
		r.push("mindbroken ");
	}

	// Slave's Title
	if (slave.fuckdoll > 0) {
		r.push("Fuckdoll");
	} else {
		r.push(`${SlaveTitle(slave)}`);
	}

	return r.join("");
}

function generatePlayerSummary() {
	// sections here taken from pLongDescription.js
	const PC = V.PC;
	const raceA = ["asian", "amerindian", "indo-aryan"].includes(PC.race) ? "an" : "a"; // addA() was inheriting colors.
	const r = [];

	r.push(`{{user}} is `);
	if (!PC.nationality || V.seeNationality !== 1 || PC.nationality === "Stateless" || PC.nationality === "slave") {
		r.push(`${PC.race} `);
	} else if (PC.nationality === "Zimbabwean" && PC.race === "white") {
		r.push(`a Rhodesian `);
	} else if (PC.nationality === "Vatican") {
		r.push(`${PC.race} Vatican `);
	} else {
		r.push(`${PC.race} ${PC.nationality} `);
	}
	if (PC.dick && PC.vagina !== -1) {
		r.push(`futanari `);
	} else if (PC.dick > 0) {
		r.push(`${PC.actualAge > 12 ? "man " : "boy "}`);
	} else {
		r.push(`${PC.actualAge > 12 ? "woman " : "girl "}`);
	}
	r.push(`with `);
	if (PC.markings === "freckles") {
		r.push(`freckled `);
	} else if (PC.markings === "heavily freckled") {
		r.push(`heavily freckled `);
	}
	r.push(`${PC.skin} skin, `);

	r.push(App.Desc.Player.face(PC) + " ");

	let ageDifference = '';
	if (PC.visualAge > PC.actualAge) {
		ageDifference = `{{user}} has taken measures to look an older ${PC.visualAge}.`;
	} else if (PC.visualAge < PC.actualAge) {
		ageDifference = `{{user}} has taken measures to look a younger ${PC.visualAge}.`;
	}
	r.push(`{{user}} is ${PC.actualAge} years old.`, ageDifference);

	return r.join("");
}

function lorebookArcology(_n) { // First entry is primarily an example
	return {
		id: _n,
		keys: ["arcology"], // Trigger word for ST to use the content
		secondary_keys: [], // Second Trigger condition (requires both to insert)
		comment: "",
		content: "Arcology: sustainable urban living complex, integrating living spaces, workspaces, and recreational areas in a harmonious and efficient manner. ", // This content is inserted in context. Note it includes Key
		constant: false,
		selective: true,
		insertion_order: 100, // Prioritizes order. Lower value = higher priority
		enabled: true,
		position: "after_char", // Position of content in context
		extensions: { // Below mostly deals with probability-based insertion
			position: 1,
			exclude_recursion: false,
			display_index: 0,
			probability: 100,
			useProbability: true,
			depth: 4,
			selectiveLogic: 0,
			group: ""
		}
	};
}

function lorebookFuckdoll(_n) {
	return {
		id: _n,
		keys: ["fuckdoll"],
		secondary_keys: [],
		comment: "",
		content: "Fuckdoll suit: advanced slave suit, permanently worn, subjecting slave to total control, eliminates all sensory stimulation other than orders passed by the suit's systems and objects inserted into the Fuckdoll's face, pussy, and ass. Slave reactions to commands are mechanical and automatic, pussy and ass are kept lubricated automatically",
		constant: false,
		selective: true,
		insertion_order: 100,
		enabled: true,
		position: "after_char",
		extensions: {
			position: 1,
			exclude_recursion: false,
			display_index: 0,
			probability: 100,
			useProbability: true,
			depth: 4,
			selectiveLogic: 0,
			group: ""
		}
	};
}

function lorebookMindbroken(_n) {
	return {
		id: _n,
		keys: ["mindbroken"],
		secondary_keys: [],
		comment: "",
		content: "Mindbroken: slave's mind is blank, everything she experiences will quickly be forgotten.",
		constant: false,
		selective: true,
		insertion_order: 100,
		enabled: true,
		position: "after_char",
		extensions: {
			position: 1,
			exclude_recursion: false,
			display_index: 0,
			probability: 100,
			useProbability: true,
			depth: 4,
			selectiveLogic: 0,
			group: ""
		}
	};
}

function lorebookArcade(_n) {
	return {
		id: _n,
		keys: ["arcade"],
		secondary_keys: [],
		comment: "",
		content: "Arcade: gloryhole stalls, offering retrained slaves mouths, vaginas and anuses for use by the public",
		constant: false,
		selective: true,
		insertion_order: 100,
		enabled: true,
		position: "after_char",
		extensions: {
			position: 1,
			exclude_recursion: false,
			display_index: 0,
			probability: 100,
			useProbability: true,
			depth: 4,
			selectiveLogic: 0,
			group: ""
		}
	};
}

function lorebookDairy(_n) {
	return {
		id: _n,
		keys: ["dairy"],
		secondary_keys: [],
		comment: "",
		content: "Dairy: barn used for milking and breeding human slaves, collecting human milk for sale and selling breeding services",
		constant: false,
		selective: true,
		insertion_order: 100,
		enabled: true,
		position: "after_char",
		extensions: {
			position: 1,
			exclude_recursion: false,
			display_index: 0,
			probability: 100,
			useProbability: true,
			depth: 4,
			selectiveLogic: 0,
			group: ""
		}
	};
}


code:
App.UI.SlaveInteract.importSlave = function() {
	const el = new DocumentFragment();
	App.UI.DOM.appendNewElement("span", el, `Paste the code into the text box and press enter: `);
	el.append(
		App.UI.DOM.makeTextBox(
			"",
			v => {
				if (v) {
					const slave = JSON.parse(`{${v}}`);
					slave.ID = generateSlaveID();
					App.Update.Slave(slave);
					App.Entity.Utils.SlaveDataSchemeCleanup(slave);
					newSlave(slave);
					SlaveDatatypeCleanup(slave);
					removeJob(slave, slave.assignment);
					V.AS = slave.ID;
					Engine.play("Slave Interact");
				}
			}
		)
	);
	return el;
};

/** @param {App.Entity.SlaveState} slave */
App.UI.SlaveInteract.exportSlave = function(slave) {
	const el = new DocumentFragment();
	App.UI.DOM.appendNewElement("p", el, `Copy the following block of code for importing: `, "note");
	App.UI.DOM.appendNewElement("textarea", el, toJson(slave), ["export-field"]);
	return el;
};

repo_name: Arkerthan/fc

license: mit


code:
/**
 * Contains a list of the properties the infant object has
 * May need another look-over
 */
App.Facilities.Nursery.InfantState = class InfantState {
	constructor() {
		/** Child's current name
		 * @type {FC.Zeroable<string>} */
		this.slaveName = "blank";
		/** Child's current surname
		 * @type {FC.Zeroable<string>} */
		this.slaveSurname = 0;
		/** @type {FC.GenderGenes} */
		this.genes = "XX";
		this.pronoun = App.Data.Pronouns.Kind.female;
		/** slave's natural genetic properties */
		this.natural = new App.Entity.GeneticState();
		/** game week child was acquired.
		 *
		 * _0: Obtained prior to game start / at game start_ */
		this.weekAcquired = 0;
		/** Child's ID */
		this.ID = 0;
		this.father = 0;
		this.mother = 0;
		this.daughters = 0;
		this.sisters = 0;
		/** week she was born (int between 0-51) */
		this.birthWeek = jsRandom(0, 51);
		/** How old she really is. */
		this.actualAge = 18;
		/** How old her body looks. */
		this.visualAge = 18;
		/** How old her body is. */
		this.physicalAge = 18;
		/** child's race
		 * @type {FC.Race} */
		this.race = "white";
		/** child's nationality */
		this.nationality = "slave";
		/**
		 * child markings
		 * * "beauty mark"
		 * * "birthmark"
		 * * "freckles"
		 * * "heavily freckled"
		 * @type {FC.Markings}
		 */
		this.markings = "none";
		/**
		 * The infant's eyes
		 * @type {App.Entity.EyeState}
		 */
		this.eye = new App.Entity.EyeState();
		/** hair color */
		this.hColor = "brown";
		/** pubic hair color */
		this.pubicHColor = "brown";
		/** armpit hair style */
		this.underArmHColor = "brown";
		/** eyebrowHColor*/
		this.eyebrowHColor = "brown";
		/** skin color */
		this.skin = "light";
		/** pubic hair style */
		this.pubicHStyle = "neat";
		/** armpit hair style */
		this.underArmHStyle = "neat";
		/**
		 * face attractiveness
		 *
		 * * -96 - : very ugly
		 * * -95 - -41: ugly
		 * * -40 - -11: unattractive
		 * * -10 - 10: attractive
		 * * 11 - 40: very pretty
		 * * 41 - 95: gorgeous
		 * * 96+: mind blowing
		 */
		this.face = 0;
		/**
		 * accepts string (will be treated as "normal")
		 * * "normal"
		 * * "masculine"
		 * * "androgynous"
		 * * "cute"
		 * * "sensual"
		 * * "exotic"
		 * * "feline" (catmod exclusive content)
		 */
		this.faceShape = "normal";
		/**
		 * how addicted to aphrodisiacs slave is - inherited at birth
		 * * 0: not
		 * * 1-2: new addict
		 * * 3-9: confirmed addict
		 * * 10+: dependent
		 */
		this.addict = 0;
		/**
		 * child intelligence
		 * * -100 - -96: borderline retarded
		 * * -95 - -51: very slow
		 * * -50 - -16: slow
		 * * -15 - 15: average
		 * * 16 - 50: smart
		 * * 51 - 95: very smart
		 * * 96 - 100: brilliant
		 */
		this.intelligence = 0;
		/**
		 * * "none"
		 * * "mindbroken"
		 * * "submissive"
		 * * "cumslut"
		 * * "humiliation"
		 * * "buttslut"
		 * * "boobs"
		 * * "sadist"
		 * * "masochist"
		 * * "dom"
		 * * "pregnancy"
		 * @type {FC.Fetish}
		 */
		this.fetish = "none";
		/**
		 * * "none"
		 * * "arrogant": clings to her dignity, thinks slavery is beneath her
		 * * "bitchy": can 't keep her opinions to herself
		 * * "odd": says and does odd things
		 * * "hates men": hates men
		 * * "hates women": hates women
		 * * "gluttonous": likes eating, gains weight
		 * * "anorexic": dislikes eating and being forced to eat, loses weight
		 * * "devout": resistance through religious faith
		 * * "liberated": believes slavery is wrong
		 * @type {FC.BehavioralFlaw}
		 */
		this.behavioralFlaw = "none";
		/**
		 * * "none"
		 * * "hates oral": hates oral sex
		 * * "hates anal": hates anal sex
		 * * "hates penetration": dislikes penetrative sex
		 * * "shamefast": nervous when naked
		 * * "idealistic": believes sex should be based on love and consent
		 * * "repressed": dislikes sex
		 * * "apathetic": inert during sex
		 * * "crude": sexually crude and has little sense of what partners find disgusting during sex
		 * * "judgemental": sexually judgemental and often judges her sexual partners' performance
		 * * "neglectful": disregards herself in sex
		 * * "cum addict": addicted to cum
		 * * "anal addict": addicted to anal
		 * * "attention whore": addicted to being the center of attention
		 * * "breast growth": addicted to her own breasts
		 * * "abusive": sexually abusive
		 * * "malicious": loves causing pain and suffering
		 * * "self hating": hates herself
		 * * "breeder": addicted to being pregnant
		 * @type {FC.SexualFlaw}
		 */
		this.sexualFlaw = "none";
		/** 0: does not have; 1: carrier; 2: active
		 * * heterochromia is an exception. String = active
		 * @type {FC.GeneticQuirks}
		 */
		this.geneticQuirks = {
			/** Oversized breasts. Increased growth rate, reduced shrink rate. Breasts try to return to oversized state if reduced. */
			macromastia: 0,
			/** Greatly oversized breasts. Increased growth rate, reduced shrink rate. Breasts try to return to oversized state if reduced.
			 *
			 * **macromastia + gigantomastia** - Breasts never stop growing. Increased growth rate, no shrink rate. */
			gigantomastia: 0,
			/** is prone to having twins, shorter pregnancy recovery rate */
			fertility: 0,
			/** is prone to having multiples, even shorter pregnancy recovery rate
			 *
			 * **fertility + hyperFertility** - will have multiples, even shorter pregnancy recovery rate */
			hyperFertility: 0,
			/** pregnancy does not block ovulation, child can become pregnant even while pregnant */
			superfetation: 0,
			/**
			 * abnormal production of amniotic fluid
			 * only affects fetuses */
			polyhydramnios: 0,
			/** Pleasurable pregnancy and orgasmic birth. Wider hips, looser and wetter vagina. High pregadaptation and low birth damage. */
			uterineHypersensitivity: 0,
			/** inappropriate lactation*/
			galactorrhea: 0,
			/** Is abnormally tall. gigantism + dwarfism - is very average*/
			gigantism: 0,
			/** Is abnormally short. gigantism + dwarfism - is very average*/
			dwarfism: 0,
			/** retains childlike characteristics*/
			neoteny: 0,
			/** rapid aging
			 *
			 * **neoteny + progeria** - progeria wins, not that she'll make it to the point that neoteny really kicks in */
			progeria: 0,
			/** has a flawless face. pFace + uFace - Depends on carrier status, may swing between average and above/below depending on it */
			pFace: 0,
			/** has a hideous face. pFace + uFace - Depends on carrier status, may swing between average and above/below depending on it */
			uFace: 0,
			/** has pale skin, white hair and red eyes */
			albinism: 0,
			/** may have mismatched eyes */
			heterochromia: 0,
			/** ass never stops growing. Increased growth rate, reduced shrink rate. */
			rearLipedema: 0,
			/** has (or will have) a huge dong */
			wellHung: 0,
			/** constantly gains weight unless dieting, easier to gain weight. wGain + wLoss - weight gain/loss fluctuates randomly */
			wGain: 0,
			/** constantly loses weight unless gaining, easier to lose weight. wGain + wLoss - weight gain/loss fluctuates randomly */
			wLoss: 0,
			/** body attempts to normalize to an androgynous state */
			androgyny: 0,
			/** Constantly gains muscle mass, easier to gain muscle. mGain + mLoss - muscle gain/loss amplified, passively lose muscle unless building */
			mGain: 0,
			/** Constantly loses muscle mass, easier to gain muscle. mGain + mLoss - muscle gain/loss amplified, passively lose muscle unless building */
			mLoss: 0,
			/** child can only ever birth girls */
			girlsOnly: 0
		};
		/** chance of generating sperm with a Y chromosome (yields male baby). inherited by sons, with mutation */
		this.spermY = 50;
		/** how many weeks until the child is ready for release */
		this.growTime = 156;
		/** Was the slave born prematurely?
		 * @type {FC.Bool}
		 * 0: no; 1: yes */
		this.premature = 0;
		/** Is the slave a clone? If so, what is the original slave's name?
		 * @type {FC.Zeroable<string>}
		 * 0: no; 1: yes */
		this.clone = 0;
		/** ID she was cloned from */
		this.cloneID = 0;
		/** @type {{skin:string, eyeColor:string, hColor:string}} */
		this.albinismOverride = null;
		/** Slave's inbreeding coefficient */
		this.inbreedingCoeff = 0;
	}
};


code:
:: Customize Infant [nobr]

<<set $nextButton = "Continue">>

/* TODO: all of this */
You may enter custom descriptors for your child's hair color, hair style, tattoos, or anything else here. After typing, press enter to commit your change. These custom descriptors will appear in descriptions of your child, but will have no gameplay effect. Changing them is free.

<<if (def $activeChild.custom) && ($activeChild.custom.title != "")>>
	You have instructed $him to always refer to you as $activeChild.custom.title, which, should $he lisp, comes out as $activeChild.custom.titleLisp.
<<else>>
	You expect $him to refer to you as all the other <<if $nurseryChildren > 1>>children and <</if>>slaves do.
<</if>>

<<if def $activeChild.custom>>
	<<if $activeChild.custom.title == "">>
		<<link "Set a custom title for $him to address you as">>
			<<replace "#result">>
				Custom title: <<textbox "$activeChild.custom.title" $activeChild.custom.title>>
				<<link "Apply">>
					<<replace "#result">>
						$He'll try $his best to call you $activeChild.custom.title.
						<<set $activeChild.custom.titleLisp = lispReplace($activeChild.custom.title)>>
					<</replace>>
				<</link>>
			<</replace>>
		<</link>>
	<<else>>
		$He's trying $his best to call you <<textbox "$activeChild.custom.title" $activeChild.custom.title>>

		<<link "Apply">>
			<<replace "#result">>
				$He'll try $his best to call you $activeChild.custom.title.
				<<set $activeChild.custom.titleLisp = lispReplace($activeChild.custom.title)>>
			<</replace>>
		<</link>> |
		<<link "Stop using a custom title">>
			<<replace "#result">>
				$He will no longer refer to you with a special title.
				<<set $activeChild.custom.title = "">>
				<<set $activeChild.custom.titleLisp = "">>
			<</replace>>
		<</link>>
	<</if>>
<</if>>

Change $his given name: <<textbox "$activeChild.slaveName" $activeChild.slaveName>>
/*
<<link "Restore $his birth name">><<set $activeChild.slaveName = $activeChild.birthName>><</link>>
<<if $arcologies[0].FSPastoralist != null>>
	<<if $activeChild.lactation > 0>>
		| <<link "Give $him a random cow given name" "Rename">><<set $activeChild.slaveName = setup.cowSlaveNames.random()>><</link>>
	<</if>>
<</if>>
<<if child.race == "catgirl">>
	| <<link "Give $him a random cat given name" "Rename">><<set $activeChild.slaveName = setup.catSlaveNames.random()>><</link>>
<</if>>
<<if $arcologies[0].FSChattelReligionist != null>>
	| <<link "Give $him a random devotional given name" "Rename">><<set $activeChild.slaveName = setup.chattelReligionistSlaveNames.random()>><</link>>
<</if>>
*/

Change $his surname:
<<if $activeChild.slaveSurname>>
	<<textbox "$activeChild.slaveSurname" $activeChild.slaveSurname>>
<<else>>
	<<textbox "$activeChild.slaveSurname">>
<</if>>
/*
<<link "Restore $his birth surname">><<set $activeChild.slaveSurname = $activeChild.birthSurname>><</link>>
<<if $activeChild.slaveSurname>>
	| <<link "Take $his surname away">><<set $activeChild.slaveSurname = 0>><</link>>
<</if>>
*/
/*
<<if $activeChild.relationship >= 5>>
	<<for _i = 0; _i < $slaves.length; _i++>>
		<<if $activeChild.relationshipTarget == $slaves[_i].ID>>
			<<if $slaves[_i].slaveSurname>>
				<<if $activeChild.slaveSurname != $slaves[_i].slaveSurname>>
					| <<link "Give $him $his wife's surname">><<set $activeChild.slaveSurname = $slaves[_i].slaveSurname>><</link>>
					<<break>>
				<</if>>
			<</if>>
		<</if>>
	<</for>>
<</if>>
<<if $activeChild.relationship == -3>>
	<<if $PC.slaveSurname>>
		<<if $activeChild.slaveSurname != $PC.slaveSurname>>
			| <<link "Give $him your surname">><<set $activeChild.slaveSurname = $PC.slaveSurname>><</link>>
		<</if>>
	<</if>>
<</if>>
*/
/*
<<if $arcologies[0].FSRomanRevivalist != null>>
	| <<link "Give $him a random full Roman name">><<set $activeChild.slaveName = setup.romanSlaveNames.random(), $activeChild.slaveSurname = setup.romanSlaveSurnames.random()>><</link>>
<<elseif $arcologies[0].FSAztecRevivalist != null>>
	| <<link "Give $him a random full Aztec name">><<set $activeChild.slaveName = setup.aztecSlaveNames.random(), $activeChild.slaveSurname = 0>><</link>>
<<elseif $arcologies[0].FSEgyptianRevivalist != null>>
	| <<link "Give $him a random full ancient Egyptian name">><<set $activeChild.slaveName = setup.ancientEgyptianSlaveNames.random(), $activeChild.slaveSurname = 0>><</link>>
<<elseif $arcologies[0].FSEdoRevivalist != null>>
	| <<link "Give $him a random full feudal Japanese name">><<set $activeChild.slaveName = setup.edoSlaveNames.random(), $activeChild.slaveSurname = setup.edoSlaveSurnames.random()>><</link>>
<</if>>
<<if $arcologies[0].FSDegradationist != null>>
	| <<link "Give $him a degrading full name">><<run DegradingName($activeChild)>><</link>>
<</if>>
*/
/*

<<switch $activeChild.hStyle>>
<<case "tails" "dreadlocks" "cornrows">>
$His hair is in $activeChild.hStyle.
<<case "ponytail">>
$His hair is in a $activeChild.hStyle.
<<default>>
$His hair is $activeChild.hStyle.
<</switch>>
Custom hair description: <<textbox "$activeChild.hStyle" $activeChild.hStyle "Add custom descriptors">>

    //For best results, use a short, uncapitalized and unpunctuated description; for example: 'back in a ponytail'//
*/

$His hair is $activeChild.hColor in color.
Custom hair color: <<textbox "$activeChild.hColor" $activeChild.hColor "Add custom descriptors">>

    //For best results, use a short, uncapitalized and unpunctuated description; for example: 'black with purple highlights'//

<<if $activeChild.eyeColor != $activeChild.origEye>>
	$He is wearing $activeChild.eyeColor lenses.
<<else>>
	$He has $activeChild.eyeColor eyes.
<</if>>
Custom eye color: <<textbox "$activeChild.eyeColor" $activeChild.eyeColor "Add custom descriptors">>

    //For best results, use a short, uncapitalized and unpunctuated description; for example: 'blue'//

/*

Change $his origin story: <<textbox "$activeChild.origin" $activeChild.origin "Add custom descriptors">>

    //For best results, use complete, capitalized and punctuated sentences; for example: '$He followed you home from the pet store.'//
*/
/*

Change $his custom description: <<textbox "$activeChild.custom.desc" $activeChild.custom.desc "Add custom descriptors">>

    //For best results, use complete, capitalized and punctuated sentences; for example: '$He has a beauty mark above $his left nipple.'//

Change $his custom label: <<textbox "$activeChild.custom.label" $activeChild.custom.label "Add custom descriptors">>

    //For best results, use a short phrase; for example: 'Breeder.'//
*/
/*

Assign $him a custom image: <input id="customImageValue" type="text">
<<link "Reset" "Add custom descriptors">><<set $activeChild.custom.image = null>><</link>>

    //File must be in// <tt><select id="customImageFormatSelector" style="border: none;"><option value="png">PNG</option><option value="jpg">JPG</option><option value="gif">GIF</option><option value="webm">WEBM</option></select></tt> //format. Place file in the \resources folder. Enter the filename without the extension. For example, for a file with the path// <tt>\bin\resources\headgirl.png</tt>, //enter //<tt>headgirl</tt>.
<>
<<if $seeImages == 1 && $imageChoice == 1>>

	<<if ndef $activeChild.custom.hairVector>><<set $activeChild.custom.hairVector = 0>><</if>>
	Assign $him a custom hair SVG image:
	<<if $activeChild.custom.hairVector != 0>>
		<<textbox "$activeChild.custom.hairVector" $activeChild.custom.hairVector "Add custom descriptors">>
	<<else>>
		<<textbox "$activeChild.custom.hairVector" "" "Add custom descriptors">>
	<</if>>
	<<link "Reset" "Add custom descriptors">><<set $activeChild.custom.hairVector = 0>><</link>>
<</if>>
*/


code:
:: Infant Interact [nobr]

<<if $cheatMode>>
	//[[Cheat Edit Child Alternative|MOD_Edit Child Cheat New][$cheater = 1]]//
<</if>>

<<set _i = App.Facilities.Nursery.childIndexForID($activeChild.ID)>>

<<set $nextButton = "Confirm changes", $nextLink = "AC Dump", $returnTo = "Nursery", _SL = $slaves.length, _CL = $cribs.length, $cribs[_i] = $activeChild>>
<<run App.Utils.setLocalPronouns($activeChild)>>
/* TODO: the encyclopedia will need to be updated for children as well */
<<set $encyclopedia = "Nursery">>

@@.cyan;[←,Q] @@

	<<link "Prev" "Previous Child In Line">><</link>>

    $activeChild.slaveName

	<<link "Next" "Next Child In Line">><</link>>

@@.cyan; [E,→]@@

<<if $seeDetails>>

		<<= App.Facilities.Nursery.LongInfantDescription($activeChild)>>

	[[Options][$nextLink = passage()]] |
	[[Hide descriptions|Infant Interact][$seeDetails = 0]] |
	[[Customize|Customize Infant]] |

	<<link "Update">>
		<<replace "#LCD">>
		<<= App.Facilities.Nursery.LongInfantDescription($activeChild)>>
		<</replace>>
	<</link>>

<<else>>
	//[[Show descriptions|Infant Interact][$seeDetails = 1]]//
<</if>>

Target destination: $activeChild.targetLocation.
<<link "Slavery">>
	<<set $activeChild.targetLocation = "slavery">>
	<<replace "#targetLocation">>$activeChild.targetLocation<</replace>>
<</link>> |
<<link "Freedom">>
	<<set $activeChild.targetLocation = "freedom">>
	<<replace "#targetLocation">>$activeChild.targetLocation<</replace>>
<</link>>


code:
/**
 * Displays a summary of the infant
 * @param {App.Facilities.Nursery.InfantState} child
 * @returns {string}
 */
App.Facilities.Nursery.InfantSummary = function(child) {
	const weeksOwned = V.week - child.weekAcquired;
	const abbreviate = V.UI.slaveSummary.abbreviation;

	let r = ``;

	function InfantSummaryUncached() {
		// r += health();
		r += description();
		r += race();
		r += nationality();
		r += skin();
		r += age();
		r += face();

		r += `</span><br>`;

		if (V.seeImages !== 1 || V.seeSummaryImages !== 1 || V.imageChoice === 1) {
			r += "    ";
		}

		r += intelligence();
		if (child.prestige > 0) {
			r += prestige();
		}
		if (child.pornPrestige > 0) {
			r += pornPrestige();
		}
		r += behavioralFlaw();
		r += sexualFlaw();

		if ((child.relationship !== 0) || (abbreviate.clothes === 2) || (abbreviate.rulesets === 2)) {
			r += `<br> `;
			if (V.seeImages !== 1 || V.seeSummaryImages !== 1 || V.imageChoice === 1) {
				r += `    `;
			}
		}

		r += extendedFamily();
		r += rival();

		r += `    `;

		if (abbreviate.origins === 2 && child.origin !== "") {
			r += origins();
		}

		return r;
	}

	// function health() {
	// if (abbreviate.health === 1) {
	// 	return shortHealth();
	// } else if (abbreviate.health === 2) {
	// 	return longHealth();
	// }
	// }

	function description() {
		let desc;

		if (abbreviate.nationality +
			abbreviate.genitalia +
			abbreviate.physicals +
			abbreviate.skills +
			abbreviate.mental !== 0) {
			r += `<br> `;
			if (V.seeImages !== 1 || V.seeSummaryImages !== 1 || V.imageChoice === 1) {
				r += "    ";
			}
		}

		if (child.actualAge > 0) {
			if (child.actualAge > 1) {
				desc = `toddler`;
			} else {
				desc = `baby`;
			}
		} else {
			if (weeksOwned <= 1) {
				desc = `newborn`;
			} else {
				desc = `baby`;
			}
		}

		const firstLetter = desc.substring(0, 1).toUpperCase();

		desc = firstLetter + desc.substring(1);
		r += `<strong><span class="coral">${desc}${abbreviate.physicals === 2 ? '.' : ''}</span></strong> `;

		return r;
	}

	function race() {
		let r = ``;

		if (V.seeRace) {
			r += `<span class="race">`;

			if (abbreviate.race === 1) {
				r += shortRace();
			} else if (abbreviate.race === 2) {
				r += App.Data.misc.filterRaces.get(child.race) || `${child.race.charAt(0).toUpperCase() + child.race.slice(1)}. `;
			}
			r += `</span> `;
		}

		return r;
	}

	function nationality() {
		if (abbreviate.nationality === 1) {
			return shortNationality();
		} else if (abbreviate.nationality === 2) {
			return longNationality();
		}
	}

	function skin() {
		if (abbreviate.physicals === 1) {
			return shortSkin();
		} else {
			return longSkin();
		}
	}

	function age() {
		if (abbreviate.physicals === 1) {
			return shortAge();
		} else if (abbreviate.physicals === 2) {
			return longAge();
		}
	}

	function face() {
		if (abbreviate.physicals === 1) {
			return shortFace();
		} else if (abbreviate.physicals === 2) {
			return longFace();
		}
	}

	function intelligence() {
		if (abbreviate.skills === 1) {
			return shortIntelligence();
		} else if (abbreviate.skills === 2) {
			return longIntelligence();
		}
	}

	function prestige() {
		if (abbreviate.skills === 1) {
			return shortPrestige();
		} else if (abbreviate.skills === 2) {
			return longPrestige();
		}
	}

	function pornPrestige() {
		if (abbreviate.skills === 1) {
			return shortPornPrestige();
		} else if (abbreviate.skills === 2) {
			return longPornPrestige();
		}
	}

	function behavioralFlaw() {
		if (abbreviate.mental === 1) {
			return shortBehaviorFlaw();
		} else if (abbreviate.mental === 2) {
			return longBehaviorFlaw();
		}
	}

	function sexualFlaw() {
		if (abbreviate.mental === 1) {
			return shortSexFlaw();
		} else if (abbreviate.mental === 2) {
			return longSexFlaw();
		}
	}

	function extendedFamily() {
		if (abbreviate.mental === 1) {
			let r = ``;

			r += `<span class="lightgreen">`;
			shortExtendedFamily();
			r += `</span> `;

			return r;
		} else if (abbreviate.mental === 2) {
			return longExtendedFamily();
		}
	}

	function rival() {
		if (abbreviate.mental === 1) {
			return shortRival();
		} else if (abbreviate.mental === 2) {
			return longRival();
		}
	}

	function origins() {
		let r = `<br> `;

		if (V.seeImages !== 1 || V.seeSummaryImages !== 1 || V.imageChoice === 1) {
			r += `    `;
		}
		r += `<span class="gray">${child.origin}</span> `;

		return r;
	}

	// /**	TODO: add health to infants
	// function shortHealth() {
	// 	if (child.health.condition < -20) {
	// 		r += `<strong><span class="red">H ${V.summaryStats ? `[${child.health.condition}]` : ''}</span></strong> `;
	// 	} else if (child.health.condition <= 20) {
	// 		r += `<strong><span class="yellow">H ${V.summaryStats ? `[${child.health.condition}]` : ''}</span></strong> `;
	// 	} else if (child.health.condition > 20) {
	// 		r += `<strong><span class="green">H ${V.summaryStats ? `[${child.health.condition}]` : ''}</span></strong> `;
	// 	}
	// 	r += " ";
	// }

	// function longHealth() {
	// 	if (child.health.condition < -90) {
	// 		r += `<span class="red">On the edge of death ${V.summaryStats ? `[${child.health.condition}]` : ''}.</span> `;
	// 	} else if (child.health.condition < -50) {
	// 		r += `<span class="red">Extremely unhealthy${V.summaryStats ? `[${child.health.condition}]` : ''}.</span> `;
	// 	} else if (child.health.condition < -20) {
	// 		r += `<span class="red">Unhealthy${V.summaryStats ? `[${child.health.condition}]` : ''}.</span> `;
	// 	} else if (child.health.condition <= 20) {
	// 		r += `<span class="yellow">Healthy${V.summaryStats ? `[${child.health.condition}]` : ''}.</span> `;
	// 	} else if (child.health.condition <= 50) {
	// 		r += `<span class="green">Very healthy${V.summaryStats ? `[${child.health.condition}]` : ''}.</span> `;
	// 	} else if (child.health.condition <= 90) {
	// 		r += `<span class="green">Extremely healthy${V.summaryStats ? `[${child.health.condition}]` : ''}.</span> `;
	// 	} else {
	// 		r += `<span class="green">Unnaturally healthy${V.summaryStats ? `[${child.health.condition}]` : ''}.</span> `;
	// 	}
	// 	r += " ";
	// }

	function shortRace() {
		let r = ``;

		switch (child.race) {
			case "white":
				r += `C`;
				break;
			case "asian":
				r += `A`;
				break;
			case "indo-aryan":
				r += `I`;
				break;
			case "latina":
				r += `L`;
				break;
			case "middle eastern":
				r += `ME`;
				break;
			case "black":
				r += `B`;
				break;
			case "pacific islander":
				r += `PI`;
				break;
			case "catgirl":
				r += `CT`;
				break;
			case "malay":
				r += `M`;
				break;
			case "amerindian":
				r += `AI`;
				break;
			case "semitic":
				r += `S`;
				break;
			case "southern european":
				r += `SE`;
				break;
			case "mixed race":
				r += `MR`;
				break;
			default:
				r += `${child.race.charAt(0).toUpperCase() + child.race.charAt(1) + child.race.charAt(2)}`;
				break;
		}

		return r;
	}

	function shortNationality() {
		let r = `<span class="tan">`;

		switch (child.nationality) {
			case "Afghan":
				r += `Afg`;
				break;
			case "Albanian":
				r += `Alb`;
				break;
			case "Algerian":
				r += `Alg`;
				break;
			case "American":
				r += `USA`;
				break;
			case "Andorran":
				r += `And`;
				break;
			case "Angolan":
				r += `Ang`;
				break;
			case "Antiguan":
				r += `AB`;
				break;
			case "Argentinian":
				r += `Arg`;
				break;
			case "Armenian":
				r += `Arm`;
				break;
			case "Aruban":
				r += `Aru`;
				break;
			case "Australian":
				r += `Aus`;
				break;
			case "Austrian":
				r += `Aut`;
				break;
			case "Azerbaijani":
				r += `Aze`;
				break;
			case "Bahamian":
				r += `Bah`;
				break;
			case "Bahraini":
				r += `Bah`;
				break;
			case "Bangladeshi":
				r += `Bgd`;
				break;
			case "Barbadian":
				r += `Bar`;
				break;
			case "Belarusian":
				r += `Ber`;
				break;
			case "Belgian":
				r += `Bel`;
				break;
			case "Belizean":
				r += `Blz`;
				break;
			case "Beninese":
				r += `Ben`;
				break;
			case "Bermudian":
				r += `Bmd`;
				break;
			case "Bhutanese":
				r += `Bhu`;
				break;
			case "Bissau-Guinean":
				r += `GB`;
				break;
			case "Bolivian":
				r += `Bol`;
				break;
			case "Bosnian":
				r += `Bos`;
				break;
			case "Brazilian":
				r += `Bra`;
				break;
			case "British":
				r += `UK`;
				break;
			case "Bruneian":
				r += `Bru`;
				break;
			case "Bulgarian":
				r += `Bul`;
				break;
			case "Burkinabé":
				r += `BF`;
				break;
			case "Burmese":
				r += `Bur`;
				break;
			case "Burundian":
				r += `Bnd`;
				break;
			case "Cambodian":
				r += `Kam`;
				break;
			case "Cameroonian":
				r += `Cam`;
				break;
			case "Canadian":
				r += `Can`;
				break;
			case "Cape Verdean":
				r += `CV`;
				break;
			case "Catalan":
				r += `Cat`;
				break;
			case "Central African":
				r += `CAR`;
				break;
			case "Chadian":
				r += `Cha`;
				break;
			case "Chilean":
				r += `Chl`;
				break;
			case "Chinese":
				r += `Chi`;
				break;
			case "Colombian":
				r += `Col`;
				break;
			case "Comorian":
				r += `Com`;
				break;
			case "Congolese":
				r += `RC`;
				break;
			case "a Cook Islander":
				r += `CI`;
				break;
			case "Costa Rican":
				r += `CR`;
				break;
			case "Croatian":
				r += `Cro`;
				break;
			case "Cuban":
				r += `Cub`;
				break;
			case "Curaçaoan":
				r += `Cur`;
				break;
			case "Cypriot":
				r += `Cyp`;
				break;
			case "Czech":
				r += `Cze`;
				break;
			case "Danish":
				r += `Den`;
				break;
			case "Djiboutian":
				r += `Dji`;
				break;
			case "Dominican":
				r += `DR`;
				break;
			case "Dominiquais":
				r += `Dom`;
				break;
			case "Dutch":
				r += `Nld`;
				break;
			case "East Timorese":
				r += `ET`;
				break;
			case "Ecuadorian":
				r += `Ecu`;
				break;
			case "Egyptian":
				r += `Egy`;
				break;
			case "Emirati":
				r += `UAE`;
				break;
			case "Equatoguinean":
				r += `EG`;
				break;
			case "Eritrean":
				r += `Eri`;
				break;
			case "Estonian":
				r += `Est`;
				break;
			case "Ethiopian":
				r += `Eth`;
				break;
			case "Fijian":
				r += `Fij`;
				break;
			case "Filipina":
				r += `Phl`;
				break;
			case "Finnish":
				r += `Fin`;
				break;
			case "French":
				r += `Fra`;
				break;
			case "French Guianan":
				r += `FG`;
				break;
			case "French Polynesian":
				r += `FP`;
				break;
			case "Gabonese":
				r += `Gab`;
				break;
			case "Gambian":
				r += `Gam`;
				break;
			case "Georgian":
				r += `Geo`;
				break;
			case "German":
				r += `Ger`;
				break;
			case "Ghanan":
				r += `Gha`;
				break;
			case "Greek":
				r += `Gre`;
				break;
			case "Greenlandic":
				r += `Grn`;
				break;
			case "Grenadian":
				r += `Gda`;
				break;
			case "Guamanian":
				r += `Gua`;
				break;
			case "Guatemalan":
				r += `Gtm`;
				break;
			case "Guinean":
				r += `Gui`;
				break;
			case "Guyanese":
				r += `Guy`;
				break;
			case "Haitian":
				r += `Hai`;
				break;
			case "Honduran":
				r += `Hon`;
				break;
			case "Hungarian":
				r += `Hun`;
				break;
			case "I-Kiribati":
				r += `Kir`;
				break;
			case "Icelandic":
				r += `Ice`;
				break;
			case "Indian":
				r += `Ind`;
				break;
			case "Indonesian":
				r += `Idn`;
				break;
			case "Iranian":
				r += `Irn`;
				break;
			case "Iraqi":
				r += `Irq`;
				break;
			case "Irish":
				r += `Irl`;
				break;
			case "Israeli":
				r += `Isr`;
				break;
			case "Italian":
				r += `Ita`;
				break;
			case "Ivorian":
				r += `IC`;
				break;
			case "Jamaican":
				r += `Jam`;
				break;
			case "Japanese":
				r += `Jpn`;
				break;
			case "Jordanian":
				r += `Jor`;
				break;
			case "Kazakh":
				r += `Kaz`;
				break;
			case "Kenyan":
				r += `Ken`;
				break;
			case "Kittitian":
				r += `SKN`;
				break;
			case "Korean":
				r += `Kor`;
				break;
			case "Kosovan":
				r += `Kos`;
				break;
			case "Kurdish":
				r += `Kur`;
				break;
			case "Kuwaiti":
				r += `Kuw`;
				break;
			case "Kyrgyz":
				r += `Kyr`;
				break;
			case "Laotian":
				r += `Lao`;
				break;
			case "Latvian":
				r += `Lat`;
				break;
			case "Lebanese":
				r += `Lbn`;
				break;
			case "Liberian":
				r += `Lib`;
				break;
			case "Libyan":
				r += `Lby`;
				break;
			case "a Liechtensteiner":
				r += `Lie`;
				break;
			case "Lithuanian":
				r += `Lit`;
				break;
			case "Luxembourgian":
				r += `Lux`;
				break;
			case "Macedonian":
				r += `Mac`;
				break;
			case "Malagasy":
				r += `Mad`;
				break;
			case "Malawian":
				r += `Mwi`;
				break;
			case "Malaysian":
				r += `Mys`;
				break;
			case "Maldivian":
				r += `Mdv`;
				break;
			case "Malian":
				r += `Mal`;
				break;
			case "Maltese":
				r += `Mlt`;
				break;
			case "Marshallese":
				r += `MI`;
				break;
			case "Mauritanian":
				r += `Mta`;
				break;
			case "Mauritian":
				r += `Mts`;
				break;
			case "Mexican":
				r += `Mex`;
				break;
			case "Micronesian":
				r += `FSM`;
				break;
			case "Moldovan":
				r += `Mol`;
				break;
			case "Monégasque":
				r += `Mnc`;
				break;
			case "Mongolian":
				r += `Mon`;
				break;
			case "Montenegrin":
				r += `Mng`;
				break;
			case "Moroccan":
				r += `Mor`;
				break;
			case "Mosotho":
				r += `Les`;
				break;
			case "Motswana":
				r += `Bot`;
				break;
			case "Mozambican":
				r += `Moz`;
				break;
			case "Namibian":
				r += `Nam`;
				break;
			case "Nauruan":
				r += `Nau`;
				break;
			case "Nepalese":
				r += `Npl`;
				break;
			case "New Caledonian":
				r += `NC`;
				break;
			case "a New Zealander":
				r += `NZ`;
				break;
			case "Ni-Vanuatu":
				r += `Van`;
				break;
			case "Nicaraguan":
				r += `Nic`;
				break;
			case "Nigerian":
				r += `Nga`;
				break;
			case "Nigerien":
				r += `Ngr`;
				break;
			case "Niuean":
				r += `Niu`;
				break;
			case "Norwegian":
				r += `Nor`;
				break;
			case "Omani":
				r += `Omn`;
				break;
			case "Pakistani":
				r += `Pak`;
				break;
			case "Palauan":
				r += `Plu`;
				break;
			case "Palestinian":
				r += `Pal`;
				break;
			case "Panamanian":
				r += `Pan`;
				break;
			case "Papua New Guinean":
				r += `PNG`;
				break;
			case "Paraguayan":
				r += `Par`;
				break;
			case "Peruvian":
				r += `Per`;
				break;
			case "Polish":
				r += `Pol`;
				break;
			case "Portuguese":
				r += `Por`;
				break;
			case "Puerto Rican":
				r += `PR`;
				break;
			case "Qatari":
				r += `Qat`;
				break;
			case "Romanian":
				r += `Rom`;
				break;
			case "Russian":
				r += `Rus`;
				break;
			case "Rwandan":
				r += `Rwa`;
				break;
			case "Sahrawi":
				r += `Sah`;
				break;
			case "Saint Lucian":
				r += `SL`;
				break;
			case "Salvadoran":
				r += `ES`;
				break;
			case "Sammarinese":
				r += `SM`;
				break;
			case "Samoan":
				r += `Sam`;
				break;
			case "São Toméan":
				r += `STP`;
				break;
			case "Saudi":
				r += `Sau`;
				break;
			case "Scottish":
				r += `Sco`;
				break;
			case "Senegalese":
				r += `Sen`;
				break;
			case "Serbian":
				r += `Srb`;
				break;
			case "Seychellois":
				r += `Sey`;
				break;
			case "Sierra Leonean":
				r += `Sie`;
				break;
			case "Singaporean":
				r += `Sng`;
				break;
			case "Slovak":
				r += `Svk`;
				break;
			case "Slovene":
				r += `Svn`;
				break;
			case "a Solomon Islander":
				r += `SI`;
				break;
			case "Somali":
				r += `Som`;
				break;
			case "South African":
				r += `RSA`;
				break;
			case "South Sudanese":
				r += `SS`;
				break;
			case "Spanish":
				r += `Spa`;
				break;
			case "Sri Lankan":
				r += `Sri`;
				break;
			case "Sudanese":
				r += `Sud`;
				break;
			case "Surinamese":
				r += `Sur`;
				break;
			case "Swazi":
				r += `Swa`;
				break;
			case "Swedish":
				r += `Swe`;
				break;
			case "Swiss":
				r += `Swi`;
				break;
			case "Syrian":
				r += `Syr`;
				break;
			case "Taiwanese":
				r += `Tai`;
				break;
			case "Tajik":
				r += `Taj`;
				break;
			case "Tanzanian":
				r += `Tza`;
				break;
			case "Thai":
				r += `Tha`;
				break;
			case "Tibetan":
				r += `Tib`;
				break;
			case "Togolese":
				r += `Tog`;
				break;
			case "Tongan":
				r += `Ton`;
				break;
			case "Trinidadian":
				r += `TT`;
				break;
			case "Tunisian":
				r += `Tun`;
				break;
			case "Turkish":
				r += `Tur`;
				break;
			case "Turkmen":
				r += `Tkm`;
				break;
			case "Tuvaluan":
				r += `Tuv`;
				break;
			case "Ugandan":
				r += `Uga`;
				break;
			case "Ukrainian":
				r += `Ukr`;
				break;
			case "Uruguayan":
				r += `Uru`;
				break;
			case "Uzbek":
				r += `Uzb`;
				break;
			case "Vatican":
				r += `VC`;
				break;
			case "Venezuelan":
				r += `Ven`;
				break;
			case "Vietnamese":
				r += `Vnm`;
				break;
			case "Vincentian":
				r += `SVG`;
				break;
			case "Yemeni":
				r += `Yem`;
				break;
			case "Zairian":
				r += `DRC`;
				break;
			case "Zambian":
				r += `Zam`;
				break;
			case "Zimbabwean":
				if (child.race === "white") {
					r += `Rho`;
				} else {
					r += `Zwe`;
				}
				break;
			case "Ancient Chinese Revivalist":
				r += `Chi Rev`;
				break;
			case "Ancient Egyptian Revivalist":
				r += `Egy Rev`;
				break;
			case "Arabian Revivalist":
				r += `Ara Rev`;
				break;
			case "Aztec Revivalist":
				r += `Azt Rev`;
				break;
			case "Edo Revivalist":
				r += `Edo Rev`;
				break;
			case "Roman Revivalist":
				r += `Rom Rev`;
				break;
			case "":
			case "none":
			case "child":
			case "Stateless":
				r += `None`;
				break;
			default:
				r += `${child.nationality.charAt(0) + child.nationality.charAt(1) + child.nationality.charAt(2)}`;
				break;
		}

		r += `</span> `;

		return r;
	}

	function longNationality() {
		let r = `<span class="nationality">`;

		switch (child.nationality) {
			case "a Cook Islander":
				r += `Cook Islander. `;
				break;
			case "a Liechtensteiner":
				r += `Liechtensteiner. `;
				break;
			case "a New Zealander":
				r += `New Zealander. `;
				break;
			case "a Solomon Islander":
				r += `Solomon Islander. `;
				break;
			case "Zimbabwean":
				if (child.race === "white") {
					r += `Rhodesian. `;
				} else {
					r += `${child.nationality}. `;
				}
				break;
			case "child":
			case "none":
			case "":
			case "Stateless":
				r += `Stateless. `;
				break;
			default:
				r += `${child.nationality}. `;
				break;
		}

		r += `</span> `;

		return r;
	}

	function shortSkin() {
		let r = `<span class="pink">`;

		switch (child.skin) {
			case "pure white":
				r += `P. Whi`;
				break;
			case "extremely fair":
				r += `E. Fai`;
				break;
			case "very fair":
				r += `V. Fai`;
				break;
			case "extremely pale":
				r += `E. Pal`;
				break;
			case "very pale":
				r += `V. Pal`;
				break;
			case "light brown":
				r += `L. Br`;
				break;
			case "dark brown":
				r += `D. Br`;
				break;
			case "light olive":
				r += `L. Oli`;
				break;
			case "dark olive":
				r += `D. Oli`;
				break;
			case "light beige":
				r += `L. Bei`;
				break;
			case "dark beige":
				r += `D. Bei`;
				break;
			case "tan":
				r += `Tan`;
				break;
			case "bronze":
				r += `Bron`;
				break;
			case "ebony":
				r += `Ebon`;
				break;
			case "pure black":
				r += `P. Bla`;
				break;
			case "black and white striped":
				r += `BnW`;
				break;
			case "yellow":
				r += `Ylw`;
				break;
			case "red":
				r += `Red`;
				break;
			case "dark":
			case "fair":
			case "pale":
				r += `${child.skin.charAt(0).toUpperCase() + child.skin.slice(1)}`;
				break;
			default:
				r += `${child.skin.charAt(0).toUpperCase() + child.skin.charAt(1) + child.skin.charAt(2)}`;
				break;
		}
		r += `</span> `;

		return r;
	}

	function longSkin() {
		return `<span class="pink">${child.skin.charAt(0).toUpperCase() + child.skin.slice(1)} skin.</span> `;
	}

	function shortAge() {
		let r = `<span class="pink">`;

		if (V.showAgeDetail === 1) {
			if (child.actualAge > 0) {
				r += `${capFirstChar(num(child.actualAge))}yrs. `;
			} else {
				if (weeksOwned > 4) {
					r += `${capFirstChar(num(Math.trunc(weeksOwned / 4)))}mos. `;
				} else {
					if (weeksOwned <= 1) {
						r += `Nwbrn. `;
					} else {
						r += `${capFirstChar(num(weeksOwned))}wks. `;
					}
				}
			}
		}

		return r;
	}

	function longAge() {
		let r = `<span class="pink">`;

		if (V.showAgeDetail) {
			if (child.actualAge > 0) {
				r += `${capFirstChar(num(child.actualAge))}-year-old `;
				if (child.actualAge > 1) {
					r += `toddler. `;
				} else {
					r += `baby. `;
				}
			} else {
				if (weeksOwned > 4) {
					r += `${capFirstChar(num(Math.trunc(weeksOwned / 4)))} months old. `;
				} else {
					if (weeksOwned <= 1) {
						r += `Newborn. `;
					} else {
						r += `${capFirstChar(num(weeksOwned))} weeks old. `;
					}
				}
			}
		}

		return r;
	}

	function shortFace() {
		let r = ``;

		if (child.face < -95) {
			r += `<span class="red">Face---${V.summaryStats ? `[${child.face}]` : ''}</span> `;
		} else if (child.face < -40) {
			r += `<span class="red">Face--${V.summaryStats ? `[${child.face}]` : ''}</span> `;
		} else if (child.face < -10) {
			r += `<span class="red">Face-${V.summaryStats ? `[${child.face}]` : ''}</span> `;
		} else if (child.face <= 10) {
			r += `Face${V.summaryStats ? `[${child.face}]` : ''}`;
		} else if (child.face <= 40) {
			r += `<span class="pink">Face+${V.summaryStats ? `[${child.face}]` : ''}</span> `;
		} else if (child.face <= 95) {
			r += `<span class="pink">Face++${V.summaryStats ? `[${child.face}]` : ''}</span> `;
		} else {
			r += `<span class="pink">Face+++${V.summaryStats ? `[${child.face}]` : ''}</span> `;
		}

		return r;
	}

	function longFace() {
		let r = `Likely to have `;

		if (child.face < -95) {
			r += `a <span class="red">very ugly${V.summaryStats ? `[${child.face}]`: ''}</span>`;
		} else if (child.face < -40) {
			r += `a <span class="red">ugly${V.summaryStats ? `[${child.face}]`: ''}</span>`;
		} else if (child.face < -10) {
			r += `a <span class="red">unattractive${V.summaryStats ? `[${child.face}]`: ''}</span>`;
		} else if (child.face <= 10) {
			r += `an average${V.summaryStats ? `[${child.face}]`: ''}`;
		} else if (child.face <= 40) {
			r += `an <span class="pink">attractive${V.summaryStats ? `[${child.face}]`: ''}</span>`;
		} else if (child.face <= 95) {
			r += `a <span class="pink">beautiful${V.summaryStats ? `[${child.face}]`: ''}</span>`;
		} else {
			r += `a <span class="pink">very beautiful${V.summaryStats ? `[${child.face}]`: ''}</span>`;
		}
		r += `, ${child.faceShape} face. `;

		return r;
	}

	function shortIntelligence() {
		let intelligence = child.intelligence;
		let r = ``;

		if (child.hasOwnProperty("intelligenceImplant")) {
			intelligence += child.intelligenceImplant;
		}

		if (child.fetish === Fetish.MINDBROKEN) {
			return;
		} else if (child.hasOwnProperty("intelligenceImplant") && child.intelligenceImplant >= 30) {
			if (intelligence >= 130) {
				r += `<span class="deepskyblue">I++++(e+)${V.summaryStats ? `[${intelligence}]` : ''}</span> `;
			} else if (intelligence > 95) {
				r += `<span class="deepskyblue">I+++(e+)${V.summaryStats ? `[${intelligence}]` : ''}</span> `;
			} else if (intelligence > 50) {
				r += `<span class="deepskyblue">I++(e+)${V.summaryStats ? `[${intelligence}]` : ''}</span> `;
			} else if (intelligence > 15) {
				r += `<span class="deepskyblue">I+(e+)${V.summaryStats ? `[${intelligence}]` : ''}</span> `;
			} else if (intelligence >= -15) {
				r += `I(e+)${V.summaryStats ? `[${intelligence}]` : ''}`;
			} else if (intelligence >= -50) {
				r += `<span class="orangered">I-(e+)${V.summaryStats ? `[${intelligence}]` : ''}</span> `;
			} else if (intelligence >= -95) {
				r += `<span class="orangered">I--(e+)${V.summaryStats ? `[${intelligence}]` : ''}</span> `;
			} else {
				r += `<span class="orangered">I---(e+)${V.summaryStats ? `[${intelligence}]` : ''}</span> `;
			}
		} else if (child.hasOwnProperty("intelligenceImplant") && child.intelligenceImplant >= 15) {
			if (intelligence > 95) {
				r += `<span class="deepskyblue">I+++(e)${V.summaryStats ? `[${intelligence}]` : ''}</span> `;
			} else if (intelligence > 50) {
				r += `<span class="deepskyblue">I++(e)${V.summaryStats ? `[${intelligence}]` : ''}</span> `;
			} else if (intelligence > 15) {
				r += `<span class="deepskyblue">I+(e)${V.summaryStats ? `[${intelligence}]` : ''}</span> `;
			} else if (intelligence >= -15) {
				r += `I(e)${V.summaryStats ? `[${intelligence}]` : ''}`;
			} else if (intelligence >= -50) {
				r += `<span class="orangered">I-(e)${V.summaryStats ? `[${intelligence}]` : ''}</span> `;
			} else if (intelligence >= -95) {
				r += `<span class="orangered">I--(e)${V.summaryStats ? `[${intelligence}]` : ''}</span> `;
			} else {
				r += `<span class="orangered">I---(e)${V.summaryStats ? `[${intelligence}]` : ''}</span> `;
			}
		} else {
			if (intelligence > 95) {
				r += `<span class="deepskyblue">I+++${V.summaryStats ? `[${intelligence}]` : ''}</span> `;
			} else if (intelligence > 50) {
				r += `<span class="deepskyblue">I++${V.summaryStats ? `[${intelligence}]` : ''}</span> `;
			} else if (intelligence > 15) {
				r += `<span class="deepskyblue">I+${V.summaryStats ? `[${intelligence}]` : ''}</span> `;
			} else if (intelligence >= -15) {
				r += `I${V.summaryStats ? `[${intelligence}]` : ''}`;
			} else if (intelligence >= -50) {
				r += `<span class="orangered">I-${V.summaryStats ? `[${intelligence}]` : ''}</span> `;
			} else if (intelligence >= -95) {
				r += `<span class="orangered">I--${V.summaryStats ? `[${intelligence}]` : ''}</span> `;
			} else {
				r += `<span class="orangered">I---${V.summaryStats ? `[${intelligence}]` : ''}</span> `;
			}
		}

		return r;
	}

	function longIntelligence() {
		let intelligence = child.intelligence;
		let r = ``;

		if (child.hasOwnProperty("intelligenceImplant")) {
			intelligence += child.intelligenceImplant;
		}

		if (child.intelligence > 95) {
			r += `<span class="deepskyblue">Likely to be brilliant${V.summaryStats ? `[${intelligence}]` : ''}.</span> `;
		} else if (intelligence > 50) {
			r += `<span class="deepskyblue">Likely to be very smart${V.summaryStats ? `[${intelligence}]` : ''}.</span> `;
		} else if (intelligence > 15) {
			r += `<span class="deepskyblue">Likely to be smart${V.summaryStats ? `[${intelligence}]` : ''}.</span> `;
		} else if (intelligence >= -15) {
			r += `Average intelligence${V.summaryStats ? `[${intelligence}]` : ''}. `;
		} else if (intelligence >= -50) {
			r += `<span class="orangered">Likely to be slow${V.summaryStats ? `[${intelligence}]` : ''}.</span> `;
		} else if (intelligence >= -95) {
			r += `<span class="orangered">Likely to be very slow${V.summaryStats ? `[${intelligence}]` : ''}.</span> `;
		} else {
			r += `<span class="orangered">Likely to be moronic${V.summaryStats ? `[${intelligence}]` : ''}.</span> `;
		}

		return r;
	}

	function shortPrestige() {
		let r = `<span class="green">`;

		if (child.prestige > 2) {
			r += `Prest++`;
		} else if (child.prestige === 2) {
			r += `Prest+`;
		} else if (child.prestige === 1) {
			r += `Prest`;
		}
		r += `</span> `;

		return r;
	}

	function longPrestige() {
		let r = `<span class="green">`;

		if (child.prestige > 2) {
			r += `Extremely prestigious. `;
		} else if (child.prestige === 2) {
			r += `Very prestigious. `;
		} else if (child.prestige === 1) {
			r += `Prestigious. `;
		}
		r += `</span> `;

		return r;
	}

	function shortPornPrestige() {
		let r = `<span class="green">`;

		if (child.pornPrestige > 2) {
			r += `PPrest++`;
		} else if (child.pornPrestige === 2) {
			r += `PPrest+`;
		} else if (child.pornPrestige === 1) {
			r += `PPrest`;
		}
		r += `</span> `;

		return r;
	}

	function longPornPrestige() {
		let r = `<span class="green">`;

		if (child.pornPrestige > 2) {
			r += `Porn star. `;
		} else if (child.pornPrestige === 2) {
			r += `Porn slut. `;
		} else if (child.pornPrestige === 1) {
			r += `Porn amateur. `;
		}
		r += `</span> `;

		return r;
	}

	function shortBehaviorFlaw() {
		let r = `<span class="red">`;

		switch (child.behavioralFlaw) {
			case "arrogant":
				r += `Arrog`;
				break;
			case "bitchy":
				r += `Bitchy`;
				break;
			case "odd":
				r += `Odd`;
				break;
			case "hates men":
				r += `Men-`;
				break;
			case "hates women":
				r += `Women-`;
				break;
			case "gluttonous":
				r += `Glut`;
				break;
			case "anorexic":
				r += `Ano`;
				break;
			case "devout":
				r += `Dev`;
				break;
			case "liberated":
				r += `Lib`;
				break;
			default:
				child.behavioralFlaw = "none";
				break;
		}
		r += `</span> `;

		return r;
	}

	function longBehaviorFlaw() {
		let r = `<span class="red">`;

		switch (child.behavioralFlaw) {
			case "arrogant":
				r += `Arrogant. `;
				break;
			case "bitchy":
				r += `Bitchy. `;
				break;
			case "odd":
				r += `Odd. `;
				break;
			case "hates men":
				r += `Hates men. `;
				break;
			case "hates women":
				r += `Hates women. `;
				break;
			case "gluttonous":
				r += `Stress eater. `;
				break;
			case "anorexic":
				r += `Anorexic. `;
				break;
			case "devout":
				r += `Devoutly religious. `;
				break;
			case "liberated":
				r += `Mentally liberated. `;
				break;
			default:
				child.behavioralFlaw = "none";
				break;
		}
		r += `</span> `;

		return r;
	}

	function shortSexFlaw() {
		let r = ``;

		switch (child.sexualFlaw) {
			case "hates oral":
				r += `<span class="red">Oral-</span> `;
				break;
			case "hates anal":
				r += `<span class="red">Anal-</span> `;
				break;
			case "hates penetration":
				r += `<span class="red">Fuck-</span> `;
				break;
			case "shamefast":
				r += `<span class="red">Shame</span> `;
				break;
			case "idealistic":
				r += `<span class="red">Ideal</span> `;
				break;
			case "repressed":
				r += `<span class="red">Repre</span> `;
				break;
			case "apathetic":
				r += `<span class="red">Apath</span> `;
				break;
			case "crude":
				r += `<span class="red">Crude</span> `;
				break;
			case "judgemental":
				r += `<span class="red">Judge</span> `;
				break;
			case "cum addict":
				r += `<span class="yellow">CumAdd</span> `;
				break;
			case "anal addict":
				r += `<span class="yellow">AnalAdd</span> `;
				break;
			case "attention whore":
				r += `<span class="yellow">Attention</span> `;
				break;
			case "breast growth":
				r += `<span class="yellow">BoobObsess</span> `;
				break;
			case "abusive":
				r += `<span class="yellow">Abusive</span> `;
				break;
			case "malicious":
				r += `<span class="yellow">Malice</span> `;
				break;
			case "self hating":
				r += `<span class="yellow">SelfHatr</span> `;
				break;
			case "neglectful":
				r += `<span class="yellow">SelfNeglect</span> `;
				break;
			case "breeder":
				r += `<span class="yellow">BreedObsess</span> `;
				break;
			default:
				child.sexualFlaw = "none";
				break;
		}

		return r;
	}

	function longSexFlaw() {
		let r = ``;

		switch (child.sexualFlaw) {
			case "hates oral":
				r += `<span class="red">Hates oral.</span> `;
				break;
			case "hates anal":
				r += `<span class="red">Hates anal.</span> `;
				break;
			case "hates penetration":
				r += `<span class="red">Hates penetration.</span> `;
				break;
			case "shamefast":
				r += `<span class="red">Shamefast.</span> `;
				break;
			case "idealistic":
				r += `<span class="red">Sexually idealistic.</span> `;
				break;
			case "repressed":
				r += `<span class="red">Sexually repressed.</span> `;
				break;
			case "apathetic":
				r += `<span class="red">Sexually apathetic.</span> `;
				break;
			case "crude":
				r += `<span class="red">Sexually crude.</span> `;
				break;
			case "judgemental":
				r += `<span class="red">Sexually judgemental.</span> `;
				break;
			case "cum addict":
				r += `<span class="yellow">Cum addict.</span> `;
				break;
			case "anal addict":
				r += `<span class="yellow">Anal addict.</span> `;
				break;
			case "attention whore":
				r += `<span class="yellow">Attention whore.</span> `;
				break;
			case "breast growth":
				r += `<span class="yellow">Breast obsession.</span> `;
				break;
			case "abusive":
				r += `<span class="yellow">Sexually abusive.</span> `;
				break;
			case "malicious":
				r += `<span class="yellow">Sexually malicious.</span> `;
				break;
			case "self hating":
				r += `<span class="yellow">Self hatred.</span> `;
				break;
			case "neglectful":
				r += `<span class="yellow">Self neglectful.</span> `;
				break;
			case "breeder":
				r += `<span class="yellow">Breeding obsession.</span> `;
				break;
			default:
				child.sexualFlaw = "none";
				break;
		}

		return r;
	}

	function shortExtendedFamily() {
		const {daughter, sister, wife} = getPronouns(child);

		let handled = 0;
		let r = ``;

		if (child.mother > 0) {
			const ssj = V.slaves.findIndex(function(s) {
				return s.ID === child.mother;
			});
			if (ssj !== -1) {
				r += `${SlaveFullName(V.slaves[ssj])}'s ${daughter}`;
				if (child.relationshipTarget === V.slaves[ssj].ID) {
					const friendShipShort = relationshipTermShort(child);
					r += ` & ${friendShipShort}`;
					handled = 1;
				}
			}
			r += " ";
		} else if (child.mother === -1) {
			r += `Your ${daughter}`;
			if (child.relationship < -1) {
				r += ` & ${PCrelationshipTerm(child)}`;
				handled = 1;
			}
			r += " ";
		} else if (child.mother in V.missingTable && V.showMissingSlavesSD && V.showMissingSlaves) {
			r += `${V.missingTable[child.mother].fullName}'s ${daughter} `;
		}

		if (child.father > 0 && child.father !== child.mother) {
			const ssj = V.slaves.findIndex(function(s) {
				return s.ID === child.father;
			});
			if (ssj !== -1) {
				r += `${SlaveFullName(V.slaves[ssj])}'s ${daughter}`;
				if (child.relationshipTarget === V.slaves[ssj].ID && handled !== 1) {
					const friendShipShort = relationshipTermShort(child);
					r += ` & ${friendShipShort}`;
					handled = 1;
				}
			}
			r += " ";
		} else if (child.father === -1 && child.mother !== -1) {
			r += `Your ${daughter}`;
			if (child.relationship < -1) {
				r += ` & ${PCrelationshipTerm(child)}`;
				handled = 1;
			}
			r += " ";
		} else if (child.father in V.missingTable && child.father !== child.mother && V.showMissingSlavesSD && V.showMissingSlaves) {
			r += `${V.missingTable[child.father].fullName}'s ${daughter}`;
		}

		if (child.daughters === 1) {
			let ssj = V.slaves.findIndex(function(s) {
				return s.mother === child.ID;
			});
			if (ssj !== -1) {
				r += `${SlaveFullName(V.slaves[ssj])}'s mother`;
				if (child.relationshipTarget === V.slaves[ssj].ID) {
					const friendShipShort = relationshipTermShort(child);
					r += ` & ${friendShipShort}`;
					handled = 1;
				}
			}
			r += " ";
			ssj = V.slaves.findIndex(function(s) {
				return s.father === child.ID;
			});
			if (ssj !== -1) {
				r += `${SlaveFullName(V.slaves[ssj])}'s father`;
				if (child.relationshipTarget === V.slaves[ssj].ID && handled !== 1) {
					const friendShipShort = relationshipTermShort(child);
					r += ` & ${friendShipShort}`;
					handled = 1;
				}
			}
			r += " ";
		} else if (child.daughters > 1) {
			r += `multiple daughters `;
		}

		if (child.sisters === 1) {
			const ssj = V.slaves.findIndex(function(s) {
				return areSisters(s, child) > 0;
			});
			if (ssj !== -1) {
				r += `${SlaveFullName(V.slaves[ssj])}'s ${sister}`;
				if (child.relationshipTarget === V.slaves[ssj].ID) {
					const friendShipShort = relationshipTermShort(child);
					r += `& ${friendShipShort}`;
					handled = 1;
				}
			}
			r += " ";
		} else if (child.sisters > 1) {
			r += `multiple sisters `;
		}

		if (child.relationship > 0 && handled !== 1) {
			const ssj = V.slaves.findIndex(function(s) {
				return s.ID === child.relationshipTarget;
			});
			if (ssj !== -1) {
				r += `${SlaveFullName(V.slaves[ssj])}'s`;
				const friendShipShort = relationshipTermShort(child);
				r += ` ${friendShipShort}`;
			}
		} else if (child.relationship === -3 && child.mother !== -1 && child.father !== -1) {
			r += `Your ${wife}`;
		} else if (child.relationship === -2) {
			r += `E Bonded`;
		} else if (child.relationship === -1) {
			r += `E Slut`;
		}

		return r;
	}

	function longExtendedFamily() {
		const {daughter, sister, wife} = getPronouns(child);

		let handled = 0;
		let r = ``;

		if (child.mother > 0) {
			const ssj = V.slaves.findIndex(function(s) {
				return s.ID === child.mother;
			});
			if (ssj !== -1) {
				r += `${SlaveFullName(V.slaves[ssj])}'s <span class="lightgreen">${daughter}`;
				if (child.relationshipTarget === V.slaves[ssj].ID) {
					const friendShipShort = relationshipTerm(child);
					r += ` and ${friendShipShort}`;
					handled = 1;
				}
				r += `.</span> `;
			}
		} else if (child.mother === -1) {
			r += `Your `;

			if (child.relationship < -1) {
				r += `<span class="lightgreen">${daughter} and ${PCrelationshipTerm(child)}.</span> `;
				handled = 1;
			} else {
				r += `<span class="lightgreen">${daughter}.</span> `;
			}
		} else if (child.mother in V.missingTable && V.showMissingSlavesSD && V.showMissingSlaves) {
			r += `${V.missingTable[child.mother].fullName}'s <span class="lightgreen">${daughter}.</span> `;
		}

		if (child.father > 0 && child.father !== child.mother) {
			const ssj = V.slaves.findIndex(function(s) {
				return s.ID === child.father;
			});
			if (ssj !== -1) {
				r += `${SlaveFullName(V.slaves[ssj])}'s <span class="lightgreen">${daughter}`;
				if (child.relationshipTarget === V.slaves[ssj].ID) {
					const friendShipShort = relationshipTerm(child);
					r += ` and ${friendShipShort}`;
					handled = 1;
				}
				r += `.</span> `;
			}
		} else if (child.father === -1 && child.father !== child.mother) {
			r += `Your `;
			if (child.relationship < -1) {
				r += `<span class="lightgreen">${daughter} and ${PCrelationshipTerm(child)}.</span> `;
				handled = 1;
			} else {
				r += `<span class="lightgreen">${daughter}.</span> `;
			}
		} else if (child.father in V.missingTable && child.father !== child.mother && V.showMissingSlavesSD && V.showMissingSlaves) {
			r += `${V.missingTable[child.father].fullName}'s <span class="lightgreen">${daughter}.</span> `;
		}

		if (child.daughters === 1) {
			let ssj = V.slaves.findIndex(function(s) {
				return s.mother === child.ID;
			});
			if (ssj !== -1) {
				r += `${SlaveFullName(V.slaves[ssj])}'s <span class="lightgreen">mother`;
				if (child.relationshipTarget === V.slaves[ssj].ID) {
					const friendShipShort = relationshipTerm(child);
					r += ` and ${friendShipShort}`;
					handled = 1;
				}
				r += `.</span> `;
			}
			ssj = V.slaves.findIndex(function(s) {
				return s.father === child.ID;
			});
			if (ssj !== -1) {
				r += `${SlaveFullName(V.slaves[ssj])}'s <span class="lightgreen">father`;
				if (child.relationshipTarget === V.slaves[ssj].ID) {
					const friendShipShort = relationshipTerm(child);
					r += ` and ${friendShipShort}`;
					handled = 1;
				}
				r += `.</span> `;
			}
		} else if (child.daughters > 1) {
			if (child.daughters > 10) {
				r += `<span class="lightgreen">Has tons of daughters.</span> `;
			} else if (child.daughters > 5) {
				r += `<span class="lightgreen">Has many daughters.</span> `;
			} else {
				r += `<span class="lightgreen">Has several daughters.</span> `;
			}
		}

		if (child.sisters === 1) {
			const ssj = V.slaves.findIndex(function(s) {
				return areSisters(s, child) > 0;
			});
			if (ssj !== -1) {
				r += `${SlaveFullName(V.slaves[ssj])}'s <span class="lightgreen">${sister}`;
				if (child.relationshipTarget === V.slaves[ssj].ID) {
					const friendShipShort = relationshipTerm(child);
					r += ` and ${friendShipShort}`;
					handled = 1;
				}
				r += `.</span> `;
			}
		} else if (child.sisters > 1) {
			if (child.sisters > 10) {
				r += `<span class="lightgreen">One of many sisters.</span> `;
			} else if (child.sisters > 5) {
				r += `<span class="lightgreen">Has many sisters.</span> `;
			} else {
				r += `<span class="lightgreen">Has several sisters.</span> `;
			}
		}

		if (child.relationship > 0 && handled !== 1) {
			const ssj = V.slaves.findIndex(function(s) {
				return s.ID === child.relationshipTarget;
			});
			if (ssj !== -1) {
				const friendship = relationshipTerm(child);
				r += `${SlaveFullName(V.slaves[ssj])}'s `;
				r += `<span class="lightgreen">${friendship}.</span> `;
			}
		} else if (child.relationship === -3 && child.mother !== -1 && child.father !== -1) {
			r += `<span class="lightgreen">Your ${wife}.</span> `;
		} else if (child.relationship === -2) {
			r += `<span class="lightgreen">Emotionally bonded to you.</span> `;
		} else if (child.relationship === -1) {
			r += `<span class="lightgreen">Emotional slut.</span> `;
		}

		return r;
	}

	function shortRival() {
		let r = ``;

		if (child.rivalry !== 0) {
			r += `    `;
			const ssj = V.slaves.findIndex(function(s) {
				return s.ID === child.rivalryTarget;
			});
			if (ssj !== -1) {
				r += `<span class="lightsalmon">`;
				if (child.rivalry <= 1) {
					r += `Disl ${SlaveFullName(V.slaves[ssj])}`;
				} else if (child.rivalry <= 2) {
					r += `${SlaveFullName(V.slaves[ssj])}'s rival`;
				} else {
					r += `Hates ${SlaveFullName(V.slaves[ssj])}`;
				}
				r += `</span> `;
			}
		}

		return r;
	}

	function longRival() {
		let r = ``;

		if (child.rivalry !== 0) {
			r += `    `;
			const ssj = V.slaves.findIndex(function(s) {
				return s.ID === child.rivalryTarget;
			});

			if (ssj !== -1) {
				if (child.rivalry <= 1) {
					r += `<span class="lightsalmon">Dislikes</span> ${SlaveFullName(V.slaves[ssj])}. `;
				} else if (child.rivalry <= 2) {
					r += `${SlaveFullName(V.slaves[ssj])}'s <span class="lightsalmon">rival.</span> `;
				} else {
					r += `<span class="lightsalmon">Hates</span> ${SlaveFullName(V.slaves[ssj])}. `;
				}
			}
			r += " ";
		}

		return r;
	}

	return InfantSummaryUncached();
};


code:
/**
 * Displays a detailed description of the infant
 * @param {App.Facilities.Nursery.InfantState} child
 * @returns {string}
 */
App.Facilities.Nursery.LongInfantDescription = function(child, {market = 0, eventDescription = 0} = {}) {
	const PC = V.PC;
	const arcology = V.arcologies[0];
	const weeksOwned = V.week - child.weekAcquired;

	let r = ``;
	let age;
	let title;
	let father = 0;
	let fatherPC = 0;
	let mother = 0;
	let motherPC = 0;

	const {he, his, He} = getPronouns(child);

	if (child.father === -1 && child.mother === -1) {
		father = PC;
		fatherPC = 1;
		mother = PC;
		motherPC = 1;
	} else {
		if (child.father === -1) {
			father = PC;
			fatherPC = 1;
			mother = getSlave(child.mother);
		} else if (child.mother === -1) {
			father = getSlave(child.father);
			mother = PC;
			motherPC = 1;
		} else {
			if (child.father > 0) { /* Fathered by a current slave */
				father = getSlave(child.father);
			} else if (child.father < 0) { /* Fathered by a missing slave */
				father = -1;
			}
			if (child.father > 0) { /* Mothered by a current slave */
				mother = getSlave(child.mother);
			} else if (child.mother < 0) { /* Mothered by a missing slave */
				mother = -1;
			}
		}
	}

	// TODO: add infant art here

	r += `     `;

	r += `<span id="childName" class="slave name simple">${SlaveFullName(child)}</span> `;

	if (jsDef(child.custom)) {
		if (child.custom.label) {
			r += ` (<span class="custom-label">${child.custom.label}</span>) `;
		}
	}

	if (child.actualAge > 0) {
		age = `${num(child.actualAge)}-year-old`;
		if (child.actualAge > 1) {
			title = `toddler`;
		} else {
			title = `baby`;
		}
	} else {
		if (weeksOwned > 4) {
			age = `${num(Math.trunc(weeksOwned / 4))}-month-old`;
			title = `baby`;
		} else {
			if (weeksOwned <= 1) {
				age = ``;
				title = `newborn`;
			} else {
				age = `${num(weeksOwned)}-week-old`;
				title = `baby`;
			}
		}
	}

	r += ` is a ${age} <strong><span class="coral">${title}.</span></strong> ${He} was born in ${arcology.name} ${weeksOwned > 4 ? weeksOwned < 9 ? `about a month` : `${num(weeksOwned)} months` : weeksOwned <= 1 ? `last week` : `${num(weeksOwned)} weeks`}${weeksOwned > 1 ? ` ago` : ``}`;

	if (jsDef(child.counter)) {
		const oral = child.counter.oral;
		const vaginal = child.counter.vaginal;
		const anal = child.counter.oral;
		const mammary = child.counter.mammary;
		const penetrative = child.counter.penetrative;
		const total = oral + vaginal + anal + mammary + penetrative;

		if (total > 0) {
			r += ` and has been fucked about ${num(total)} times, including `;
			if ((vaginal + anal + mammary + penetrative) > 0) {
				if (vaginal > 0) {
					r += `${num(vaginal)} vanilla, `;
				}
				if (anal > 0) {
					r += `${num(anal)} anal, `;
				}
				if (mammary > 0) {
					r += `${num(mammary)} mammary `;
				}
				if (penetrative > 0) {
					r += `${num(penetrative)} penetrating, `;
				}
				r += ` and `;
			}
			r += `${num(oral)} oral sexual encounters. `;
		} else {
			if (weeksOwned >= 1) {
				r += ` and `;
			} else {
				r += `. ${He} `;
			}

			r += `has had little or no sexual experience `;
			if (child.weekAcquired !== 0) {
				r += `as your slave `;
			} else {
				r += `in your new arcology `;
			}
			r += `yet. `;

			let sortedCounts = [];
			sortedCounts.push(
				{type: "oral", value: oral},
				{type: "vaginal", value: vaginal},
				{type: "anal", value: anal},
				{type: "mammary", value: mammary},
				{type: "penetrative", value: penetrative}
			);
			sortedCounts = sortedCounts.sort(function(a, b) {	// sorts the counts from largest to smallest
				return b.value - a.value;
			});

			if (sortedCounts[0].type === "oral") {
				if (((weeksOwned * 112) / oral) < 4) {
					r += `Remarkably, this means that ${he}'s sucked something off `;
					if (((weeksOwned * 112) / oral) < 1) {
						r += `more than once every hour `;
					} else if (((weeksOwned * 112) / oral) < 1.5) {
						r += `about once every hour `;
					} else if (((weeksOwned * 112) / oral) < 2.5) {
						r += `about once every two hours `;
					} else if (((weeksOwned * 112) / oral) < 3.5) {
						r += `about once every three hours `;
					} else {
						r += `about once every four hours `;
					}
					r += ` ${he}'s spent awake. `;
				}
			} else if (sortedCounts[0].type === "vaginal") {
				if (((weeksOwned * 112) / oral) < 4) {
					r += `Remarkably, this means that ${his} pussy has been fucked `;
					if (((weeksOwned * 112) / oral) < 1) {
						r += `more than once every hour `;
					} else if (((weeksOwned * 112) / oral) < 1.5) {
						r += `about once every hour `;
					} else if (((weeksOwned * 112) / oral) < 2.5) {
						r += `about once every two hours `;
					} else if (((weeksOwned * 112) / oral) < 3.5) {
						r += `about once every three hours `;
					} else {
						r += `about once every four hours `;
					}
					r += ` ${he}'s spent awake. `;
				}
			} else if (sortedCounts[0].type === "anal") {
				if (((weeksOwned * 112) / oral) < 4) {
					r += `Remarkably, this means that ${he}'s been buttfucked `;
					if (((weeksOwned * 112) / oral) < 1) {
						r += `more than once every hour `;
					} else if (((weeksOwned * 112) / oral) < 1.5) {
						r += `about once every hour `;
					} else if (((weeksOwned * 112) / oral) < 2.5) {
						r += `about once every two hours `;
					} else if (((weeksOwned * 112) / oral) < 3.5) {
						r += `about once every three hours `;
					} else {
						r += `about once every four hours `;
					}
					r += ` ${he}'s spent awake. `;
				}
			} else if (sortedCounts[0].type === "mammary") {
				if (((weeksOwned * 112) / oral) < 4) {
					r += `Remarkably, this means that ${he}'s put ${his} tits to work `;
					if (((weeksOwned * 112) / oral) < 1) {
						r += `more than once every hour `;
					} else if (((weeksOwned * 112) / oral) < 1.5) {
						r += `about once every hour `;
					} else if (((weeksOwned * 112) / oral) < 2.5) {
						r += `about once every two hours `;
					} else if (((weeksOwned * 112) / oral) < 3.5) {
						r += `about once every three hours `;
					} else {
						r += `about once every four hours `;
					}
					r += ` ${he}'s spent awake. `;
				}
			} else if (sortedCounts[0].type === "penetrative") {
				if (((weeksOwned * 112) / oral) < 4) {
					r += `Remarkably, this means that ${he}'s pounded a hole `;
					if (((weeksOwned * 112) / oral) < 1) {
						r += `more than once every hour `;
					} else if (((weeksOwned * 112) / oral) < 1.5) {
						r += `about once every hour `;
					} else if (((weeksOwned * 112) / oral) < 2.5) {
						r += `about once every two hours `;
					} else if (((weeksOwned * 112) / oral) < 3.5) {
						r += `about once every three hours `;
					} else {
						r += `about once every four hours `;
					}
					r += ` ${he}'s spent awake. `;
				}
			}
		}
	} else {
		r += `. `;
	}

	// r += App.Desc.family(child) + ' '; TODO Assemble DOM instead of string to make this work again

	if (father && fatherPC) {
		if (child.eyeColor === PC.eye.origColor) {
			r += `${He} has your ${child.eyeColor} eyes, `;
		} else if (mother) {
			r += `${He} has ${his} mother ${mother.slaveName}'s ${child.eyeColor} eyes, `;
		}
	} else {
		if (father && child.eyeColor === father.eyeColor) {
			r += `${He} has ${his} father ${father.slaveName}'s ${child.eyeColor} eyes, `;
		} else if (mother) {
			r += `${He} has ${his} mother ${mother.slaveName}'s ${child.eyeColor} eyes, `;
		} else {
			r += `${He} has ${child.eyeColor} eyes, `;
		}
	}
	r += ` and ${he} will most likely have ${child.hColor} hair when ${he}'s older. `;

	r += `${He} ${V.seeRace ? `is ${child.race} and ${child.nationality}, and` : ``} has ${child.skin} skin. `;

	return r;
};


code:
/**
 *
 * @param {App.Entity.SlaveState} slave
 * @returns {DocumentFragment}
 */
App.Interact.fFuckdollAnal = function(slave) {
	const node = new DocumentFragment();
	let r = [];

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

	seX(slave, "anal", V.PC, "penetrative");
	r.push(`You decide to use the Fuckdoll's`);
	if (slave.anus > 3) {
		r.push(`gaping`);
	} else if (slave.anus === 3) {
		r.push(`loose`);
	} else if (slave.anus === 2) {
		r.push(`relaxed`);
	} else if (slave.anus === 1) {
		r.push(`tight`);
	}
	r.push(`rear hole.`);
	if (slave.fuckdoll <= 10) {
		r.push(`Since ${he} is not well adapted to life as a living sex toy yet, ${he} won't respond to position commands. So, you simply`);
		if (hasAnyLegs(slave)) {
			r.push(`walk over to ${him}`);
		} else {
			r.push(`flip ${him} over`);
		}
		r.push(`and ram`);
		if (V.PC.dick !== 0) {
			r.push(`your cock`);
		} else {
			r.push(`a strap-on`);
		}
		r.push(`up ${his} rear hole.`);
	} else if (slave.fuckdoll <= 80) {
		r.push(`${He} can follow intermediate commands, so you order ${him} to present ${his} rear hole. ${He} obediently`);
		if (hasAnyLegs(slave)) {
			r.push(`bends over, arches ${his} back, and`);
		} else {
			r.push(`flips over and`);
		}
		r.push(`winks ${his} anus until you insert`);
		if (V.PC.dick !== 0) {
			r.push(`your cock.`);
		} else {
			r.push(`a strap-on.`);
		}
	} else {
		r.push(`${He} can follow advanced commands, so you bring ${him} over to your chair`);
		if (hasAnyLegs(slave)) {
			r.push(`and order ${him} to squat down onto your`);
			if (V.PC.dick !== 0) {
				r.push(`cock`);
			} else {
				r.push(`strap-on`);
			}
			r.push(`and slide ${his} anus up and down`);
			if (V.PC.dick !== 0) {
				r.push(`your`);
			} else {
				r.push(`the`);
			}
			r.push(`shaft.`);
		} else {
			r.push(`and impale ${him} on`);
			if (V.PC.dick !== 0) {
				r.push(`your cock,`);
			} else {
				r.push(`your strap-on,`);
			}
			r.push(`ordering ${him} to do ${his} feeble best to bounce.`);
		}
	}
	if (slave.fuckdoll <= 20) {
		r.push(`${He}'s not fully used to having things suddenly forced up ${his} ass, so ${he} struggles, and ${his} sphincter spasms deliciously.`);
	} else if (slave.fuckdoll <= 40) {
		r.push(`Aware that ${he} is supposed to relax and accept anal rape, ${he} does ${his} best to accommodate the sodomy.`);
	} else {
		r.push(`You command ${him} to milk your`);
		if (V.PC.dick !== 0) {
			r.push(`cock`);
		} else {
			r.push(`strap-on`);
		}
		r.push(`with ${his} asshole, and ${he} obediently tightens ${his} sphincter against the invading phallus rhythmically.`);
	}
	if (slave.fuckdoll <= 60) {
		if (slave.energy > 60) {
			r.push(`Denied any other outlet for ${his} powerful sex drive, ${he} orgasms.`);
		}
	} else {
		r.push(`Tuned to enjoy any use by total denial of all other stimulation, ${he} orgasms repeatedly as you fuck ${his} anus.`);
	}
	if (slave.voice === 0) {
		r.push(`Though ${he} is mute, ${his} breath hisses loudly`);
		if (slave.lips > 95) {
			r.push(`past the lips of ${his} facepussy.`);
		} else {
			r.push(`through ${his} mouth insert.`);
		}
	} else {
		r.push(`${He} moans,`);
		if (slave.lips > 95) {
			r.push(`and the lips of ${his} facepussy quiver.`);
		} else {
			r.push(`struggling to force the sound past ${his} mouth insert.`);
		}
	}
	r.push(`You climax${(V.PC.dick !== 0) ? `, filling ${his} rectum with your cum,` : ``} and return ${him} to`);
	if (hasAnyLegs(slave)) {
		r.push(`a standing position.`);
	} else {
		r.push(`where ${he} was resting.`);
	}
	if (V.PC.dick !== 0) {
		if (slave.anus > 2) {
			r.push(`Your cum flows out of ${his} gaped rear hole and down the material of ${his} suit.`);
		} else if (slave.anus === 2) {
			r.push(`Your cum drips out of ${his} loosened rear hole and down the material of ${his} suit.`);
		} else {
			r.push(`${His} tight rear hole retains every drop of your cum.`);
		}
		if (canImpreg(slave, V.PC)) {
			r.push(knockMeUp(slave, 5, 0, -1));
		}
		r.push(`The Fuckdoll will be cleaned by another slave.`);
	}
	if (slave.anus === 0) {
		if (slave.fetish !== Fetish.MINDBROKEN) {
			r.push(`As you return to your business, ${he} shakes slightly in place, and a few low moans come out of ${his} face hole. This is probably a reaction to losing ${his} anal virginity.`);
		} else {
			r.push(`${He} gives no external indication that ${he}'s aware that ${he}'s just lost ${his} anal virginity.`);
		}
		r.push(`In any case, <span class="virginity loss">${his} rear hole has been broken in.</span>`);
		slave.anus = 1;
	}
	App.Events.addParagraph(node, r);
	return node;
};