code:
/**
 * @param {App.Entity.SlaveState} slave
 * @returns {string}
 */
App.Desc.ears = function(slave) {
	const r = [];
	const {
		he, him, his, hers, himself, boy, He, His
	} = getPronouns(slave);
	// ear shape description here
	if (slave.earShape === "none") {
		if (slave.earImplant === 1) {
			if (slave.earT !== "none") {
				r.push(`${He} has smooth skin where ${his} ears should be as ${his} hearing has been cybernetically rerouted to ${his} secondary ears.`);
			} else {
				r.push(`${He} has nothing but small, perforated metal disks where ${his} ears should be.`);
			}
		} else if (slave.earwear === "none") {
			r.push(`${He} has small unsightly holes on the sides of ${his} head.`); // That can't be sanitary.
		} else {
			r.push(`The sides of ${his} head are smooth where ${his} ears should be, but upon closer inspection it is revealed that`);
			if (slave.earwear === "hearing aids") {
				r.push(`${his} ear canals are fitted with hearing aids capped with a skin-matching sheet to obscure the hole.`);
			} else {
				r.push(`${his} ear canals are filled with plugs with skin-matching caps.`);
			}
		}
	} else if (slave.earShape === "damaged") {
		r.push(`${His} outer ears have been severely damaged.`);
	} else if (slave.earShape === "normal" && slave.earT !== "none") {
		// Ears are expected, so lets only mention them if we have two sets
		r.push(`${He} has perfectly ordinary ears.`);
	} else if (slave.earShape === "robot") {
		r.push(`${He} has high tech cyber-ears that could be mistaken for headphones.`);// not yet implemented
	} else if (slave.earShape === "pointy") {
		r.push(`${His} small, ${either("elfin", "leaf-shaped", "pointed")} ears are quite cute and give ${him} an exotic appearance.`);
	} else if (slave.earShape === "elven") {
		r.push(`${He} has long, thin elven ears that ${either(`tend to droop when ${he} is relaxed or sad`, `tend to waggle up and down when ${he} is excited`, `twitch at the slightest touch`)}.`);
	} else if (slave.earShape === "ushi") {
		r.push(`${He} has long, floppy cow ears.`); // that ${either(`tend to droop when ${he} is relaxed or sad`, `tend waggle up and down when ${he} is excited`, `twitch at the slightest touch`)}. These don't make sense for the most part.
	}

	if (slave.earT === "neko") {
		r.push(`${He} has cute, ${slave.earTColor} cat ears on ${his} head; they`);
		if (slave.earImplant === 1) {
			r.push(`perk up at`);
			if (slave.devotion > 20) {
				r.push(`the sound of your voice and`);
			} else {
				r.push(`sudden noises and`);
			}
		}
		r.push(`${either(`tend to droop when ${he} is relaxed or sad`, `twitch at the slightest touch`)}.`);
	} else if (slave.earT === "inu") {
		r.push(`${He} has cute, ${slave.earTColor} dog ears on ${his} head; they`);
		if (slave.earImplant === 1) {
			r.push(`perk up at`);
			if (slave.devotion > 50) {
				r.push(`the sound of your voice and`);
			} else {
				r.push(`sudden noises and`);
			}
		}
		r.push(`${either(`tend to droop when ${he} is relaxed or sad`, `twitch at the slightest touch`)}.`);
	} else if (slave.earT === "kit") {
		r.push(`${He} has elegant, ${slave.earTColor} fox ears on ${his} head; they`);
		if (slave.earImplant === 1) {
			r.push(`perk up at`);
			if (slave.devotion > 50) {
				r.push(`the sound of your voice and`);
			} else {
				r.push(`sudden noises and`);
			}
		}
		r.push(`${either(`tend to droop when ${he} is relaxed or sad`, `twitch at the slightest touch`)}.`);
	} else if (slave.earT === "tanuki") {
		r.push(`${He} has adorable, ${slave.earTColor}, round tanuki ears on ${his} head; they`);
		if (slave.earImplant === 1) {
			r.push(`perk up at`);
			if (slave.devotion > 50) {
				r.push(`the sound of your voice and`);
			} else {
				r.push(`sudden noises and`);
			}
		}
		r.push(`${either(`tend to droop when ${he} is relaxed or sad`, `twitch at the slightest touch`)}.`);
	} else if (slave.earT === "usagi") {
		r.push(`${He} has long, ${slave.earTColor}, fluffy rabbit ears on ${his} head; they`);
		if (slave.earImplant === 1) {
			r.push(`perk up at`);
			if (slave.devotion > 50) {
				r.push(`the sound of your voice and`);
			} else {
				r.push(`sudden noises and`);
			}
		}
		r.push(`${either(`tend to droop when ${he} is relaxed or sad`, `twitch at the slightest touch`)}.`);
	} else if (slave.earT === "normal") {
		r.push(`${He} has`);
		if (slave.earShape !== "none") {
			r.push(`a second set`);
		} else {
			r.push(`a pair`);
		}
		r.push(`of non-functioning ears grafted to the top of ${his} head.`);
	}

	if (slave.hears < 0) {
		if (slave.hears === -1) {
			r.push(`${His} hearing is noticeably impaired,`);
		} else if (slave.hears < -1) {
			r.push(`${He} is completely deaf,`);
		}
		if (slave.hears < -1 && slave.earShape === "none") {
			r.push(`which is fitting due to ${his} lack of ears.`);
		} else {
			r.push(`but this isn't obvious just by looking at ${his} ears.`);
		}
	}
	return r.join(" ");
};


code:
/**
 * returned string fits in a sentence like this:
 * She has {return}.
 *
 * @param {App.Entity.SlaveState} slave
 * @returns {string}
 */
App.Desc.eyesType = function(slave) {
	let r = "";
	if (hasBothEyes(slave)) {
		if (getLeftEyeType(slave) !== getRightEyeType(slave)) {
			r = `a ${App.Desc.eyeTypeToString(getRightEyeType(slave))} and a ${App.Desc.eyeTypeToString(getLeftEyeType(slave))} eye`;
		} else {
			r = `${App.Desc.eyeTypeToString(getRightEyeType(slave))} eyes`;
		}
	} else if (hasAnyEyes(slave)) {
		if (hasRightEye(slave)) {
			r = `a ${App.Desc.eyeTypeToString(getRightEyeType(slave))} eye`;
		} else if (hasLeftEye(slave)) {
			r = `a ${App.Desc.eyeTypeToString(getLeftEyeType(slave))} eye`;
		}
	} else {
		r = "no eyes";
	}

	return r;
};

/**
 * converts an eye type to a string
 * @param {number} type
 * @returns {string}
 */
App.Desc.eyeTypeToString = function(type) {
	switch (type) {
		case 1:
			return "natural";
		case 2:
			return "glass";
		case 3:
			return "artificial";
		default:
			return "unknown eye type: " + type;
	}
};

/**
 * Fits in a sentence like this:
 * She has {return}.
 *
 * Intentionally leaves out some information to make it flow nicer and have less grammatical restrictions.
 * Use App.Desc.eyesColorLong for a complete eye description.
 *
 * @param {App.Entity.SlaveState} slave
 * @param {string} [adj]
 * @param {string} [eye]
 * @param {string} [eyes]
 * @returns {string}
 */
App.Desc.eyesColor = function(slave, adj = "", eye = "eye", eyes = "eyes", a = true) {
	let r = "";
	if (hasBothEyes(slave)) {
		const leftEye = slave.eye.left;
		const rightEye = slave.eye.right;
		if (unusualPupil(leftEye) && leftEye.pupil === rightEye.pupil) {
			r += `${leftEye.pupil} `;
		}
		r += leftEye.iris;
		if (leftEye.iris !== rightEye.iris) {
			r += ` and ${rightEye.iris}`;
		}
		r += ` ${adj} ${eyes}`;
		if (unusualSclera(leftEye) && leftEye.sclera === rightEye.sclera) {
			r += ` with ${leftEye.sclera} sclerae`;
		}
	} else if (hasAnyEyes(slave)) {
		const slaveEye = hasLeftEye(slave) ? slave.eye.left : slave.eye.right;
		if (a) {
			r += "a ";
		}
		if (unusualPupil(slaveEye)) {
			r += `${slaveEye.pupil} `;
		}
		r += `${slaveEye.iris} ${adj ? `${adj} ` : ""}${eye}`;
		if (unusualSclera(slaveEye)) {
			r += ` with ${a ? "a " : ""}${slaveEye.sclera} sclera`;
		}
	} else {
		r = "no eyes";
	}

	return r;

	// TODO deduplicate with eyesColorLong()
	function unusualPupil(eye) { return eye.pupil !== "circular"; }

	function unusualSclera(eye) { return eye.sclera !== "white"; }
};

/**
 * Returns a description of a given slave's eyes.
 * WARNING: Expects .,:;!? after the string, otherwise there might be grammatical errors!
 *
 * @param {App.Entity.SlaveState} slave
 * @returns {string}
 */
App.Desc.eyesColorLong = function(slave) {
	let r = "";
	if (hasBothEyes(slave)) {
		const leftEye = slave.eye.left;
		const rightEye = slave.eye.right;
		if (leftEye.iris === rightEye.iris) {
			if (leftEye.pupil === rightEye.pupil && unusualPupil(leftEye)) {
				r += `${leftEye.pupil} `;
			}
			// This line is the only guaranteed line, every other line has a space on the side TOWARDS this line.
			r += `${leftEye.iris} eyes`;
			let comma = false;
			let left = "";
			let right = "";
			if (leftEye.pupil !== rightEye.pupil) {
				if (unusualPupil(leftEye)) {
					left = ` ${leftEye.pupil}`;
				}
				if (unusualPupil(rightEye)) {
					right = ` ${rightEye.pupil}`;
				}
			}
			if (leftEye.sclera !== rightEye.sclera) {
				if (unusualSclera(leftEye)) {
					left += ` with a ${leftEye.sclera} sclera`;
				}
				if (unusualSclera(rightEye)) {
					right += ` with a ${rightEye.sclera} sclera`;
				}
			}
			if (left !== "") {
				r += `; one${left}`;
				if (right !== "") {
					r += ` and the other${right}`;
				}
				comma = true;
			} else if (right !== "") {
				r += `; one${right}`;
				comma = true;
			}
			if (leftEye.sclera === rightEye.sclera && unusualSclera(leftEye)) {
				r += `${comma ? "," : ""} with ${leftEye.sclera} sclerae`;
			}
		} else {
			const splitPupil = leftEye.pupil !== rightEye.pupil;
			const splitSclerae = leftEye.sclera !== rightEye.sclera;
			if (splitPupil || splitSclerae) {
				r += "a";
				if (splitPupil && unusualPupil(leftEye)) {
					r += ` ${leftEye.pupil}`;
				}
				r += ` ${leftEye.iris} eye`;
				if (splitSclerae && unusualSclera(leftEye)) {
					r += ` with a ${leftEye.sclera} sclera`;
				}
				r += " and a";
				if (splitPupil && unusualPupil(rightEye)) {
					r += ` ${rightEye.pupil}`;
				}
				r += ` ${rightEye.iris} eye`;
				if (splitSclerae && unusualSclera(rightEye)) {
					r += ` with a ${rightEye.sclera} sclera`;
				}
				if (!splitPupil && unusualPupil(leftEye)) {
					r += `; both ${leftEye.pupil}`;
				} else if (!splitSclerae && unusualSclera(leftEye)) {
					r += `; both with ${leftEye.sclera} sclerae`;
				}
			} else {
				if (unusualPupil(leftEye)) {
					r += `${leftEye.pupil} `;
				}
				r += `${leftEye.iris} and ${rightEye.iris} eyes`;
				if (unusualSclera(leftEye)) {
					r += ` with ${leftEye.sclera} sclerae`;
				}
			}
		}
	} else if (hasAnyEyes(slave)) {
		const eye = hasLeftEye(slave) ? slave.eye.left : slave.eye.right;
		r += "a";
		if (unusualPupil(eye)) {
			r += ` ${eye.pupil}`;
		}
		r += ` ${eye.iris} eye`;
		if (unusualSclera(eye)) {
			r += ` with a ${eye.sclera} sclera`;
		}
	} else {
		r = "no eyes";
	}
	return r;

	function unusualPupil(eye) { return eye.pupil !== "circular"; }

	function unusualSclera(eye) { return eye.sclera !== "white"; }
};

/**
 * Fits in a sentence like this:
 * She has {return} eyes.
 * Prefer App.Desc.eyesColor if possible as it works reliably with only one eye.
 * Example where this is better: {return}-eyed gaze
 *
 * @param {App.Entity.SlaveState} slave
 * @returns {string} Slave's eye color
 */
App.Desc.eyeColor = function(slave) {
	"use strict";
	let r;

	if (!hasAnyEyes(slave)) {
		r = "empty";
	} else if (hasBothEyes(slave)) {
		if (hasVisibleHeterochromia(slave)) {
			r = `heterochromatic ${getRightEyeColor(slave)} and ${getLeftEyeColor(slave)}`;
		} else {
			r = getLeftEyeColor(slave);
		}
	} else if (hasLeftEye(slave)) {
		r = getLeftEyeColor(slave);
	} else {
		r = getRightEyeColor(slave);
	}
	return r;
};

/**
 * returned string fits in a sentence like this:
 * She has {return}.
 *
 * @param {App.Entity.SlaveState} slave
 * @returns {string}
 */
App.Desc.eyesVision = function(slave) {
	let r = "";
	if (hasBothEyes(slave)) {
		if (getLeftEyeVision(slave) !== getRightEyeVision(slave)) {
			r = `a ${App.Desc.eyeVisionToString(getRightEyeVision(slave))} and a ${App.Desc.eyeVisionToString(getLeftEyeVision(slave))} eye`;
		} else {
			r = `${App.Desc.eyeVisionToString(getRightEyeVision(slave))} eyes`;
		}
	} else if (hasAnyEyes(slave)) {
		if (hasRightEye(slave)) {
			r = `a ${App.Desc.eyeVisionToString(getRightEyeVision(slave))} eye`;
		} else if (hasLeftEye(slave)) {
			r = `a ${App.Desc.eyeVisionToString(getLeftEyeVision(slave))} eye`;
		}
	} else {
		r = "no eyes";
	}

	return r;
};

/**
 * converts an eye vision to a string
 * @param {number} type
 * @returns {string}
 */
App.Desc.eyeVisionToString = function(type) {
	switch (type) {
		case 0:
			return "blind";
		case 1:
			return "nearsighted";
		case 2:
			return "normal";
		default:
			return "unknown eye vision: " + type;
	}
};

repo_name: KnightBoulegard/fc

license: mit


code:
/**
 * @param {App.Entity.SlaveState} slave
 * @returns {string}
 */
App.Desc.face = function(slave) {
	const r = [];
	const {
		he, him, his, hers, himself, boy, He, His
	} = getPronouns(slave);

	r.push(`${His} <span class="pink">face is`);

	switch (slave.faceShape) {
		case "masculine":
			if (slave.face < -95) {
				r.push(`so ugly and masculine that ${his} designation as a slave girl is a mockery.`);
			} else if (slave.face < -40) {
				r.push(`ugly and masculine, making ${him} a poor slave girl by appearance.`);
			} else if (slave.face < -10) {
				r.push(`unattractively masculine.`);
			} else if ((slave.face <= 10)) {
				r.push(`masculine, but not entirely unappealing.`);
			} else if ((slave.face <= 40)) {
				r.push(`attractively masculine.`);
			} else if ((slave.face <= 95)) {
				r.push(`quite handsome in a masculine way.`);
			} else {
				r.push(`the height of masculine handsomeness.`);
			}
			break;
		case "androgynous":
			if (slave.face < -95) {
				r.push(`disturbingly androgynous and terribly ugly.`);
			} else if (slave.face < -40) {
				r.push(`ugly and androgynous; ${he} has neither masculine nor feminine appeal.`);
			} else if (slave.face < -10) {
				r.push(`strangely androgynous, and rather unattractive.`);
			} else if ((slave.face <= 10)) {
				r.push(`strangely androgynous.`);
			} else if ((slave.face <= 40)) {
				r.push(`androgynous, and attractive enough that this ambiguity is interesting.`);
			} else if ((slave.face <= 95)) {
				r.push(`gorgeously androgynous in a complex way that captures the eye.`);
			} else {
				r.push(`so gorgeously androgynous that ${he} tends to induce sexual confusion.`);
			}
			break;
		case "cute":
			if (slave.face < -95) {
				r.push(`very ugly, yet somehow cute; ${he}'s so unattractive that ${he} inspires pity.`);
			} else if (slave.face < -40) {
				r.push(`ugly, but cute, with a pitiable appeal.`);
			} else if (slave.face < -10) {
				r.push(`not attractive, but is appealingly cute.`);
			} else if ((slave.face <= 10)) {
				r.push(`merely average, but is appealingly cute.`);
			} else if ((slave.face <= 40)) {
				r.push(`both attractive and appealingly cute.`);
			} else if ((slave.face <= 95)) {
				r.push(`beautiful, yet somehow also approachably cute.`);
			} else {
				r.push(`an impossibly perfect combination of beauty and girl-next-door cuteness.`);
			}
			break;
		case "sensual":
			if (slave.face < -95) {
				r.push(`very ugly, yet naturally slutty, promising a decent fuck despite its appearance.`);
			} else if (slave.face < -40) {
				r.push(`ugly, but also slutty, promising a good fuck despite its appearance.`);
			} else if (slave.face < -10) {
				r.push(`not attractive, but it has a certain sensual appeal.`);
			} else if ((slave.face <= 10)) {
				r.push(`merely average, but undeniably sensual.`);
			} else if ((slave.face <= 40)) {
				r.push(`both attractive and naturally sultry.`);
			} else if ((slave.face <= 95)) {
				r.push(`both beautiful and sultry, bringing sex to mind naturally.`);
			} else {
				r.push(`very beautiful in a consummately sexual way.`);
			}
			break;
		case "exotic":
			if (slave.face < -95) {
				r.push(`very ugly and unusual, a real tragedy in flesh.`);
			} else if (slave.face < -40) {
				r.push(`ugly and unusual, a real misfortune.`);
			} else if (slave.face < -10) {
				r.push(`unattractive, and distinctive in its unattractiveness.`);
			} else if ((slave.face <= 10)) {
				r.push(`quite average, but not uninteresting.`);
			} else if ((slave.face <= 40)) {
				r.push(`attractive in an exotic and interesting way.`);
			} else if ((slave.face <= 95)) {
				r.push(`exotic and beautiful, capable of catching the eye and keeping its gaze.`);
			} else {
				r.push(`very beautiful and exotic, almost to the point of alien fascination.`);
			}
			break;
		default:
			if (slave.face < -95) {
				r.push(`very ugly.`);
			} else if (slave.face < -40) {
				r.push(`quite ugly.`);
			} else if (slave.face < -10) {
				r.push(`unattractive.`);
			} else if ((slave.face <= 10)) {
				r.push(`average and conventionally feminine.`);
			} else if ((slave.face <= 40)) {
				r.push(`conventionally attractive.`);
			} else if ((slave.face <= 95)) {
				r.push(`conventionally beautiful.`);
			} else {
				r.push(`the height of conventional feminine beauty.`);
			}
	}
	r.push(`</span>`);
	if (slave.weight > 190) {
		r.push(`${His} face is quite fat with ample excess chins.`);
	} else if (slave.weight > 160) {
		r.push(`${His} face is round and plump with a trio of extra chins.`);
	} else if (slave.weight > 130) {
		r.push(`${His} face is chubby with an obvious second chin.`);
	} else if (slave.weight > 97) {
		r.push(`${His} face is soft with barely a second chin.`);
	}
	if (slave.fuckdoll > 0) {
		r.push(`However,`);
		if (slave.face < -1) {
			r.push(`it's hard to see this past the suit, since its face is padded to make ${him} seem less ugly.`);
		} else if (slave.face < 1) {
			r.push(`it's hard to see this past the suit, since its face is shaped to make ${him} seem prettier.`);
		} else {
			r.push(`the suit obscures all but the shape of ${his} pretty face.`);
		}
	} else {
		if (slave.markings === "beauty mark") {
			if (slave.face < -95) {
				r.push(`It bears a severely disfiguring, discolored mark.`);
			} else if (slave.face < -40) {
				r.push(`It bears a couple of unsightly moles.`);
			} else if (slave.face < -10) {
				r.push(`It bears an ugly mole.`);
			} else if ((slave.face <= 10)) {
				r.push(`${He} has a distinctive beauty mark.`);
			} else if ((slave.face <= 40)) {
				r.push(`${He} has a nice beauty mark.`);
			} else if ((slave.face <= 95)) {
				r.push(`${He} has a beauty mark that adds to ${his} distinctiveness.`);
			} else {
				r.push(`${He} has a beauty mark that makes ${him} really memorable.`);
			}
		}
	}
	if (slave.faceImplant > 5) {
		r.push(`${He}'s`);
		if (slave.faceImplant > 95) {
			r.push(`had so much cosmetic surgery that ${his} face is located at the bottom of the uncanny valley `);
			if (slave.face < -10) {
				r.push(`in addition to its ugliness.`);
			} else if (slave.face <= 10) {
				r.push(`the only thing really distinctive about it.`);
			} else {
				r.push(`its attractiveness notwithstanding.`);
			}
		} else if (slave.faceImplant > 60) {
			r.push(`obviously gotten a lot of facial cosmetic surgery.`);
		} else if (slave.faceImplant > 30) {
			r.push(`noticeably received facial cosmetic surgery.`);
		} else {
			r.push(`had some facial cosmetic surgery, though it's subtle.`);
			if (V.PC.skill.medicine >= 100) {
				r.push(`Someone without your knowledge might miss it entirely.`);
			}
		}
		if (V.arcologies[0].FSBodyPurist !== "unset") {
			if (slave.faceImplant > 30) {
				r.push(`This is considered extremely tragic by a society that values bodily purity.`);
			} else {
				r.push(`Since society values bodily purity, even this subtlety affects ${his} attractiveness when it's noticed.`);
			}
		} else if (V.arcologies[0].FSTransformationFetishist !== "unset") {
			if (slave.faceImplant > 30) {
				r.push(`Your transformationist society doesn't think this `);
				if (slave.face >= -10) {
					r.push(`reduces ${his} attractiveness.`);
				} else {
					r.push(`makes ${him} uglier.`);
				}
			}
		} else {
			if (slave.faceImplant > 30) {
				r.push(`The effect is enough to reduce ${his} attractiveness.`);
			}
		}
	}
	if (slave.smells === -1) {
		r.push(`${He} has no sense of smell, but this isn't immediately obvious just by looking at ${his} nose.`);
	}

	if (V.showBodyMods === 1) {
		if (slave.fuckdoll === 0) {
			r.push(App.Desc.makeup(slave));
		}
	}
	return r.join(" ");
};


code:
/**
 * @param {App.Entity.SlaveState} slave
 * @returns {string}
 */
App.Desc.heels = function(slave) {
	const r = [];
	const {
		he, him, his, hers, himself, boy, He, His
	} = getPronouns(slave);
	if (slave.heels === 1 && hasAnyLegs(slave)) {
		r.push(`${His}`);
		if (hasBothLegs(slave)) {
			r.push(`<span class="pink">legs have been altered</span>`);
		} else {
			r.push(`<span class="pink">leg has been altered</span>`);
		}
		r.push(`so that ${he} must wear heels in order to walk.`);
		if (V.showClothing === 1 && V.saleDescription === 0) {
			if (slave.shoes !== "none" && slave.shoes !== "flats") {
				r.push(`${He} is, so ${he} can walk reasonably well.`);
			} else {
				r.push(`Since ${he} is without them, ${he}'s crawling on `);
				if (!hasAllLimbs(slave)) {
					r.push(`the ground.`);
				} else {
					r.push(`all fours.`);
				}
			}
		}
	}
	return r.join(" ");
};


code:
/**
 * @param {App.Entity.SlaveState} slave
 * @returns {string}
 */
App.Desc.heightImplant = function(slave) {
	let r = [];
	const {
		he, him, his, hers, himself, boy, He, His
	} = getPronouns(slave);
	if (slave.heightImplant > 1) {
		r = limbs();
		r.push(`are wrong; it's obvious that they have been artificially lengthened.`);
	} else if (slave.heightImplant > 0) {
		r = limbs();
		r.push(`are odd, as though they have been artificially lengthened.`);
	} else if (slave.heightImplant < -1) {
		r = limbs();
		r.push(`are wrong; it's obvious that they have been artificially shortened.`);
	} else if (slave.heightImplant < 0) {
		r = limbs();
		r.push(`are odd, as though they have been artificially shortened.`);
	}

	function limbs() {
		const r = [];
		r.push(`The proportions of ${his}`);
		if (hasAnyArms(slave)) {
			if (hasBothArms(slave)) {
				r.push(`arms`);
			} else {
				r.push(`arm`);
			}
			if (hasAnyLegs(slave)) {
				r.push(`and`);
			}
		}
		if (hasAnyLegs(slave)) {
			if (hasBothLegs(slave)) {
				r.push(`legs`);
			} else {
				r.push(`leg`);
			}
		}
		return r;
	}
	return r.join(" ");
};


code:
/**
 * @param {App.Entity.SlaveState} slave
 * @returns {string}
 */
App.Desc.hips = function(slave) {
	const r = [];
	const {
		he, him, his, hers, himself, boy, He, His
	} = getPronouns(slave);
	if (slave.hips < -1) {
		if (slave.butt > 2) {
			if (V.arcologies[0].FSHedonisticDecadence !== "unset") {
				r.push(`disproportionately large for ${his} narrow hips, but your hedonistic arcology finds this attractive.`);
			} else if (V.arcologies[0].FSTransformationFetishist !== "unset") {
				r.push(`disproportionately large for ${his} narrow hips, but your transformation fetishist arcology considers this attractive.`);
			} else if (V.arcologies[0].FSAssetExpansionist !== "unset") {
				r.push(`disproportionately large for ${his} narrow hips, but that's just fine to your asset expansionist society.`);
			} else {
				r.push(`<span class="red">disproportionately large</span> for ${his} narrow hips.`);
			}
		} else {
			r.push(`and ${his} hips are very narrow.`);
		}
	} else if (slave.hips < 0) {
		if (slave.butt > 4) {
			if (V.arcologies[0].FSHedonisticDecadence !== "unset") {
				r.push(`disproportionately large for ${his} trim hips, but your hedonistic arcology finds this attractive.`);
			} else if (V.arcologies[0].FSTransformationFetishist !== "unset") {
				r.push(`disproportionately large for ${his} trim hips, but your transformation fetishist arcology considers this attractive.`);
			} else if (V.arcologies[0].FSAssetExpansionist !== "unset") {
				r.push(`disproportionately large for ${his} trim hips, but that's just fine to your asset expansionist society.`);
			} else {
				r.push(`<span class="red">disproportionately large</span> for ${his} trim hips.`);
			}
		} else {
			r.push(`complemented by ${his} trim hips.`);
		}
	} else if (slave.hips > 2) {
		if (slave.butt <= 8) {
			r.push(`<span class="red">disproportionately small</span> for ${his} monstrous hips.`);
		} else {
			r.push(`fitting for ${his} monstrous hips.`);
		}
	} else if (slave.hips > 1) {
		if (slave.butt <= 3 && (V.arcologies[0].FSSlimnessEnthusiast === "unset" || (slave.boobs >= 500))) {
			r.push(`<span class="red">disproportionately small</span> for ${his} very wide`);
			if (V.arcologies[0].FSSlimnessEnthusiast !== "unset") {
				r.push(`hips, which your arcology finds unattractive on busty slaves.`);
			} else {
				r.push(`hips.`);
			}
		} else {
			r.push(`flattered by ${his} very wide hips.`);
		}
	} else if (slave.hips > 0) {
		if (slave.butt > 8) {
			if (V.arcologies[0].FSHedonisticDecadence !== "unset") {
				r.push(`disproportionately large for ${his} broad hips, but your hedonistic arcology finds this attractive.`);
			} else if (V.arcologies[0].FSTransformationFetishist !== "unset") {
				r.push(`disproportionately large for ${his} broad hips, but your transformation fetishist arcology considers this attractive.`);
			} else if (V.arcologies[0].FSAssetExpansionist !== "unset") {
				r.push(`disproportionately large for ${his} broad hips, but that's just fine to your asset expansionist society.`);
			} else {
				r.push(`<span class="red">disproportionately large</span> for ${his} broad hips.`);
			}
		} else if (slave.butt <= 2 && (V.arcologies[0].FSSlimnessEnthusiast === "unset" || (slave.boobs >= 500))) {
			r.push(`<span class="red">disproportionately small</span> for ${his} broad`);
			if (V.arcologies[0].FSSlimnessEnthusiast !== "unset") {
				r.push(`hips, which your arcology finds unattractive on busty slaves.`);
			} else {
				r.push(`hips.`);
			}
		} else {
			r.push(`complemented by ${his} broad hips.`);
		}
	} else {
		if (slave.butt > 6) {
			if (V.arcologies[0].FSHedonisticDecadence !== "unset") {
				r.push(`disproportionately large for ${his} womanly hips, but your hedonistic arcology finds this attractive.`);
			} else if (V.arcologies[0].FSTransformationFetishist !== "unset") {
				r.push(`disproportionately large for ${his} womanly hips, but your transformation fetishist arcology considers this attractive.`);
			} else if (V.arcologies[0].FSAssetExpansionist !== "unset") {
				r.push(`disproportionately large for ${his} womanly hips, but that's just fine to your asset expansionist society.`);
			} else {
				r.push(`<span class="red">disproportionately large</span> for ${his} womanly hips.`);
			}
		} else if (slave.butt <= 1 && (V.arcologies[0].FSSlimnessEnthusiast === "unset" || (slave.boobs >= 500))) {
			r.push(`<span class="red">disproportionately small</span> for ${his} ample`);
			if (V.arcologies[0].FSSlimnessEnthusiast !== "unset") {
				r.push(`hips, which your arcology finds unattractive on busty slaves.`);
			} else {
				r.push(`hips.`);
			}
		} else {
			r.push(`complemented by ${his} ample hips.`);
		}
	}
	return r.join(" ");
};


code:
/**
 * @param {App.Entity.SlaveState} slave
 * @returns {string}
 */
App.Desc.horns = function(slave) {
	const r = [];
	const {
		he, him, his, hers, himself, boy, He, His
	} = getPronouns(slave);
	if (slave.horn === "curved succubus horns") {
		r.push(`${He} has`);
		if (slave.face > 40) {
			r.push(`majestic ${slave.hornColor} horns that jut out from the back of ${his} head and curve forward, looking almost like a crown.`);
		} else if (slave.face > 0) {
			r.push(`imposing ${slave.hornColor} horns that jut out from the back of ${his} head and curve forward nicely.`);
		} else {
			r.push(`terrifying ${slave.hornColor} horns that jut out from the back of ${his} head and curve forward menacingly.`);
		}
	} else if (slave.horn === "backswept horns") {
		if (slave.face > 40) {
			r.push(`majestic ${slave.hornColor} horns that jut out the front of ${his} head and curve backwards, like some royal headdress.`);
		} else if (slave.face > 0) {
			r.push(`imposing ${slave.hornColor} horns that jut out the front of ${his} head and curve backwards nicely.`);
		} else {
			r.push(`terrifying ${slave.hornColor} horns that jut out the front of ${his} head and curve backwards, looking like some twisted helmet.`);
		}
	} else if (slave.horn === "cow horns") {
		r.push(`${He} has two ${slave.hornColor} cow horns on the sides of ${his} head; they curve sharply upward.`);
	} else if (slave.horn === "two long oni horns") {
		r.push(`${He} has two long ${slave.hornColor} horns sticking straight up out of ${his}`);
		if (slave.devotion > 20) {
			r.push(`forehead; ${he} holds them up proudly.`);
		} else {
			r.push(`forehead.`);
		}
	} else if (slave.horn === "one long oni horn") {
		r.push(`${He} has a long ${slave.hornColor} horn sticking straight up out of ${his}`);
		if (slave.devotion > 20) {
			r.push(`forehead; ${he} holds it up proudly`);
		} else {
			r.push(`forehead.`);
		}
	} else if (slave.horn === "small horns") {
		r.push(`${He} has cute, little ${slave.hornColor} horns sticking out of ${his} head.`);
	}
	return r.join(" ");
};


code:
App.Desc.limbChange = function() {
	return {
		currentLimbs: currentLimbs,
		amputate: amputate,
		prosthetic: prosthetic,
		selector: selector,
		applySelector: applySelector,
		reaction: reaction
	};

	/**
	 * Generates an object usable with the standard limb check functions.
	 * @param {App.Entity.SlaveState} slave
	 * @returns {{}}
	 */
	function currentLimbs(slave) {
		let s = {arm: {left: {type: 1}, right: {type: 1}}, leg: {left: {type: 1}, right: {type: 1}}, PLimb: 0};
		if (hasLeftArm(slave)) {
			s.arm.left.type = getLeftArmID(slave);
		} else {
			s.arm.left = null;
		}
		if (hasRightArm(slave)) {
			s.arm.right.type = getRightArmID(slave);
		} else {
			s.arm.right = null;
		}
		if (hasLeftLeg(slave)) {
			s.leg.left.type = getLeftLegID(slave);
		} else {
			s.leg.left = null;
		}
		if (hasRightLeg(slave)) {
			s.leg.right.type = getRightLegID(slave);
		} else {
			s.leg.right = null;
		}
		s.PLimb = slave.PLimb;
		return s;
	}

	/**
	 * @param {App.Entity.SlaveState} slave
	 * @param {{}} oldLimbs
	 * @param {string} returnTo
	 * @returns {string}
	 */
	function amputate(slave, oldLimbs, returnTo) {
		const {his} = getPronouns(slave);

		/**
		 * @param {number} id
		 */
		function install(id) {
			slave.PLimb = id;
			surgeryDamage(slave, 10);
			App.UI.replace("#amputate", App.Desc.limbChange().prosthetic(slave, oldLimbs, returnTo));
		}

		function noInstall() {
			App.UI.replace("#amputate", App.Desc.limbChange().reaction(slave, oldLimbs, returnTo));
		}

		let implant = false;
		let r = "";

		if (slave.PLimb < 1 && isProstheticAvailable(slave, "interfaceP1")) {
			implant = true;
			r += `<div>${App.UI.link("Install basic interface", install, [1])}</div>`;
		}
		if (slave.PLimb < 2 && isProstheticAvailable(slave, "interfaceP2")) {
			implant = true;
			r += `<div>${App.UI.link("Install advanced interface", install, [2])}</div>`;
		}

		if (implant) {
			return "" +
				`
Since you already have a prosthetic interface prepared for this slave, you can install it during the operation. The procedure will put additional strain on ${his} health but less so than if you were to perform the procedures separately.
` +
				`${r}<div>${App.UI.link("Do not install", noInstall)}</div></span>`;
		}

		if (slave.PLimb > 0) {
			return prosthetic(slave, oldLimbs, returnTo);
		}
		return reaction(slave, oldLimbs, returnTo);
	}

	/**
	 * @param {App.Entity.SlaveState} slave
	 * @param {{}} oldLimbs
	 * @param {string} returnTo
	 * @returns {string}
	 */
	function prosthetic(slave, oldLimbs, returnTo) {
		if (!(isProstheticAvailable(slave, "basicL") || isProstheticAvailable(slave, "sexL")
			|| isProstheticAvailable(slave, "beautyL") || isProstheticAvailable(slave, "combatL")
			|| (isProstheticAvailable(slave, "cyberneticL") && slave.PLimb > 1))) {
			return reaction(slave, oldLimbs, returnTo);
		}
		const {him} = getPronouns(slave);

		let r = `<div>Since you already have limbs prepared for ${him} you might as well attach them while you are working on ${him}:</div>` +
			selector(slave, oldLimbs, returnTo);

		return `<span id="selector">${r}</span>`;
	}

	/**
	 * Displays a selector for prosthetic limbs of getSlave($AS)
	 * @param {App.Entity.SlaveState} slave
	 * @param {{}} oldLimbs
	 * @param {string} [returnTo]
	 * @returns {string}
	 */
	function selector(slave, oldLimbs, returnTo = "") {
		if (hasAllNaturalLimbs((slave))) {
			return "You must amputate your slaves limbs before you can attach prosthetics.";
		}
		if (slave.PLimb < 1) {
			return "You must install a prosthetic interface before you can attach prosthetics.";
		}
		const state = currentState(slave);

		/**
		 * Generates an array with the current limbs of a slave.
		 * @param {App.Entity.SlaveState} slave
		 * @returns {number[]}
		 */
		function currentState(slave) {
			let a = [];
			a[0] = getLeftArmID(slave);
			a[1] = getRightArmID(slave);
			a[2] = getLeftLegID(slave);
			a[3] = getRightLegID(slave);
			return a;
		}

		/**
		 * @param {number} pos
		 * @param {number} id
		 * @returns {string}
		 */
		function entry(pos, id) {
			if (state[pos] === 1) {
				return "

";
			}
			return `<div><<radiobutton "_newState[${pos}]" ${id}${state[pos] === id ? " checked" : ""}>></div>`;
		}

		/**
		 * @param {string} title
		 * @param {number} id
		 * @returns {string}
		 */
		function row(title, id) {
			return `<div>${title}</div>${entry(0, id)}${entry(1, id)}${entry(2, id)}${entry(3, id)}`;
		}

		let r = "

Left Arm

Right Arm

Left Leg

Right Leg
";

		r += row("None", 0);
		if (isProstheticAvailable(slave, "basicL")) {
			r += row("Basic prosthetic", 2);
		}
		if (isProstheticAvailable(slave, "sexL")) {
			r += row("Advanced sex limb", 3);
		}
		if (isProstheticAvailable(slave, "beautyL")) {
			r += row("Advanced beauty limb", 4);
		}
		if (isProstheticAvailable(slave, "combatL")) {
			r += row("Advanced combat limb", 5);
		}
		if (isProstheticAvailable(slave, "cyberneticL")) {
			if (slave.PLimb > 1) {
				r += row("Cybernetic limb", 6);
			} else {
				r += "
Install an advanced interface to attach cybernetic limbs.
";
			}
		}

		return `<<set _newState = [${state}]>><div class='limb-selector'>${r}</div>${apply()}`;

		function apply() {
			let s;
			if (!returnTo) {
				s = `<<set $prostheticsConfig = "limbs", $oldLimbs = ${JSON.stringify(oldLimbs)}>>` +
					'<<goto "Prosthetics Configuration">>';
			} else {
				s = `<<replace "#selector">><<= App.Desc.limbChange().reaction(getSlave(${slave.ID}), ${JSON.stringify(oldLimbs)}, "${returnTo}")>><</replace>>`;
			}

			return `<<link "Apply">><<run App.Desc.limbChange().applySelector(getSlave(${slave.ID}), _newState)>>${s}<</link>>`;
		}
	}

	/**
	 *
	 * @param {App.Entity.SlaveState} slave
	 * @param {number[]} newState
	 */
	function applySelector(slave, newState) {
		if (getLeftArmID(slave) !== newState[0]) {
			if (getLeftArmID(slave) > 1) {
				removeLimbs(slave, "left arm");
			}
			if (newState[0] > 1) {
				attachLimbs(slave, "left arm", newState[0]);
			}
		}
		if (getRightArmID(slave) !== newState[1]) {
			if (getRightArmID(slave) > 1) {
				removeLimbs(slave, "right arm");
			}
			if (newState[1] > 1) {
				attachLimbs(slave, "right arm", newState[1]);
			}
		}
		if (getLeftLegID(slave) !== newState[2]) {
			if (getLeftLegID(slave) > 1) {
				removeLimbs(slave, "left leg");
			}
			if (newState[2] > 1) {
				attachLimbs(slave, "left leg", newState[2]);
			}
		}
		if (getRightLegID(slave) !== newState[3]) {
			if (getRightLegID(slave) > 1) {
				removeLimbs(slave, "right leg");
			}
			if (newState[3] > 1) {
				attachLimbs(slave, "right leg", newState[3]);
			}
		}
	}

	/**
	 * @param {App.Entity.SlaveState} slave
	 * @param {{}} oldLimbs
	 * @param {string} returnTo
	 * @returns {string}
	 */
	function reaction(slave, oldLimbs, returnTo = "") {
		let r = "";
		if (oldLimbs.PLimb !== slave.PLimb) {
			r += `Prosthetic interface was ${oldLimbs.PLimb === 0 ? "none" : "basic"} and is now ${slave.PLimb === 1 ? "basic" : "advanced"}. `;
		}
		if (getLeftArmID(oldLimbs) !== getLeftArmID(slave)) {
			r += `Left arm was ${idToDescription(getLeftArmID(oldLimbs))} and is now ${idToDescription(getLeftArmID(slave))}. `;
		}
		if (getRightArmID(oldLimbs) !== getRightArmID(slave)) {
			r += `Right arm was ${idToDescription(getRightArmID(oldLimbs))} and is now ${idToDescription(getRightArmID(slave))}. `;
		}
		if (getLeftLegID(oldLimbs) !== getLeftLegID(slave)) {
			r += `Left leg was ${idToDescription(getLeftLegID(oldLimbs))} and is now ${idToDescription(getLeftLegID(slave))}. `;
		}
		if (getRightLegID(oldLimbs) !== getRightLegID(slave)) {
			r += `Right leg was ${idToDescription(getRightLegID(oldLimbs))} and is now ${idToDescription(getRightLegID(slave))}. `;
		}

		r += "
";

		r += "Slave's reaction";
		// TODO
		// reaction based on limb change & devotion/trust
		if (returnTo) {
			r = `<br><br>${r}<br>[[Continue|${returnTo}]]`;
		}
		return r;
	}
};


code:
/**
 * @param {App.Entity.SlaveState} slave
 * @returns {string}
 */
App.Desc.mind = function(slave) {
	const r = [];
	const {
		he, him, his, hers, himself, boy, He, His
	} = getPronouns(slave);
	if (slave.fuckdoll === 0) {
		r.push(App.Desc.eyes(slave));

		if (slave.fetish === "mindbroken") {
			r.push(`However, <span class="coral">${his} mind is fundamentally broken;</span> everything ${he} experiences will quickly be forgotten.`);
		}

		r.push(behavioralFlaws());
		r.push(behavioralQuirks());
		r.push(sexualFlaws());
		r.push(sexualQuirk());
		r.push(fetish());
		r.push(attraction());
	} else {
		r.push(`It's impossible to tell what intelligence or inclinations a Fuckdoll might have by looking at it, but the most recent records indicate that this one is`);
		if (slave.intelligence + slave.intelligenceImplant > 95) {
			r.push(`<span class="deepskyblue">brilliant</span>`);
		} else if (slave.intelligence + slave.intelligenceImplant > 50) {
			r.push(`<span class="deepskyblue">highly intelligent</span>`);
		} else if (slave.intelligence + slave.intelligenceImplant > 15) {
			r.push(`of <span class="deepskyblue">above average intelligence</span>`);
		} else if (slave.intelligence + slave.intelligenceImplant >= -15) {
			r.push(`of average intelligence`);
		} else if (slave.intelligence + slave.intelligenceImplant >= -50) {
			r.push(`of <span class="orangered">below average intelligence</span>`);
		} else if (slave.intelligence + slave.intelligenceImplant >= -95) {
			r.push(`<span class="orangered">very stupid</span>`);
		} else if (slave.intelligence + slave.intelligenceImplant >= -100) {
			r.push(`<span class="orangered">a moron</span>`);
		}
		if (slave.intelligence >= -15) {
			if (slave.intelligenceImplant < 15) {
				r.push(`but is uneducated.`);
			} else if (slave.intelligenceImplant >= 30) {
				r.push(`and is well educated.`);
			} else {
				r.push(`and is educated.`);
			}
		} else {
			if (slave.intelligenceImplant < 15) {
				r.push(`and is uneducated.`);
			} else if (slave.intelligenceImplant >= 30) {
				r.push(`but is well educated.`);
			} else {
				r.push(`but is educated.`);
			}
		}

		if (slave.behavioralFlaw !== "none" || slave.sexualFlaw !== "none" || slave.behavioralQuirk !== "none" || slave.sexualQuirk !== "none") {
			if (slave.behavioralFlaw !== "none" || slave.sexualFlaw !== "none") {
				r.push(`${His} remaining mental flaws are likewise impossible to discern, but if ${he} was allowed out of ${his} suit, this Fuckdoll would probably still`);
				if (slave.behavioralFlaw !== "none") {
					switch (slave.behavioralFlaw) {
						case "hates men":
							r.push(`<span class="red">hate men</span>`);
							break;
						case "hates women":
							r.push(`<span class="red">hate women</span>`);
							break;
						case "gluttonous":
							r.push(`be a <span class="red">stress eater</span>`);
							break;
						default:
							r.push(`be <span class="red">${slave.behavioralFlaw}</span>`);
					}
				} if (slave.sexualFlaw !== "none") {
					if (slave.behavioralFlaw !== "none") {
						r.push(`and would still`);
					}
					switch (slave.sexualFlaw) {
						case "hates oral":
							r.push(`<span class="red">hate oral.</span>`);
							break;
						case "hates anal":
							r.push(`<span class="red">hate anal.</span>`);
							break;
						case "hates penetration":
							r.push(`<span class="red">hate penetration.</span>`);
							break;
						case "cum addict":
							r.push(`a <span class="yellow">cum addict.</span>`);
							break;
						case "anal addict":
							r.push(`an <span class="yellow">anal addict.</span>`);
							break;
						case "attention whore":
							r.push(`an <span class="yellow">attention whore.</span>`);
							break;
						case "breast growth":
							r.push(`have a <span class="yellow">breast obsession.</span>`);
							break;
						case "breeder":
							r.push(`have a <span class="yellow">breeding obsession.</span>`);
							break;
						default:
							r.push(`be <span class="red">${slave.sexualFlaw}.</span>`);
					}
				} else {
					r.push(`to an unacceptable degree.`);
				}
			}
			if (slave.behavioralQuirk !== "none" || slave.sexualQuirk !== "none") {
				r.push(`${His} character seems to retain some distinctiveness; ${he} may still`);
				if (slave.behavioralQuirk !== "none") {
					r.push(`be`);
					switch (slave.behavioralQuirk) {
						case "fitness":
							r.push(`<span class="green">a fitness fanatic</span>`);
							break;
						case "adores women":
							r.push(`<span class="green">adore women</span>`);
							break;
						case "adores men":
							r.push(`<span class="green">adore men</span>`);
							break;
						case "advocate":
							r.push(`<span class="green">an advocate</span>`);
							break;
						default:
							r.push(`<span class="green">${slave.behavioralQuirk}</span>`);
					}
				}
				if (slave.sexualQuirk !== "none") {
					if (slave.behavioralQuirk !== "none") {
						r.push(`and still`);
					}
					r.push(`be`);
					switch (slave.sexualQuirk) {
						case "romantic":
						case "perverted":
						case "caring":
						case "unflinching":
							r.push(`<span class="green"> ${slave.sexualQuirk}.</span>`);
							break;
						default:
							r.push(`a <span class="green"> ${slave.sexualQuirk}.</span>`);
					}
				} else {
					r.push(`in there.`);
				}
			}
		}

		if (slave.fetishKnown === 1) {
			r.push(`${His} recent biomedical readings indicate that ${he} still prefers`);
			switch (slave.fetish) {
				case "submissive":
					r.push(`<span class="lightcoral">being forced to submit</span>`);
					break;
				case "cumslut":
					r.push(`<span class="lightcoral">being facefucked</span>`);
					break;
				case "humiliation":
					r.push(`<span class="lightcoral">being fucked in public</span>`);
					break;
				case "buttslut":
					r.push(`<span class="lightcoral">being sodomized</span>`);
					break;
				case "boobs":
					r.push(`<span class="lightcoral">having ${his} breasts handled</span>`);
					break;
				case "pregnancy":
					r.push(`<span class="lightcoral">being inseminated</span>`);
					break;
				case "dom":
				case "sadist":
					r.push(`<span class="lightcoral">being on top</span>`);
					break;
				case "masochist":
					r.push(`<span class="lightcoral">being hurt</span>`);
					break;
				default:
					r.push(`<span class="lightcoral">having vanilla sex</span>`);
			}
			r.push(`to any other kind of use.`);
		}

		if (slave.attrKnown === 1) {
			r.push(`${His} suit usually forces ${him} to become aroused for use, but when ${he}'s allowed to react naturally, ${he} has`);
			if (slave.energy > 95) {
				r.push(`a <span class="green">nymphomaniac</span> need`);
			} else if (slave.energy > 80) {
				r.push(`a <span class="green">pathological need</span>`);
			} else if (slave.energy > 60) {
				r.push(`a <span class="green">powerful need</span>`);
			} else if (slave.energy > 40) {
				r.push(`a <span class="yellow">healthy need</span>`);
			} else if (slave.energy > 20) {
				r.push(`a <span class="red">weak need</span>`);
			} else {
				r.push(`<span class="red">no need</span>`);
			}
			r.push(`for external sexual stimulation, acts`);
			if (slave.attrXX <= 15) {
				r.push(`<span class="red">revolted</span>`);
			} else if (slave.attrXX <= 35) {
				r.push(`<span class="red">unhappy</span>`);
			} else if (slave.attrXX <= 65) {
				r.push(`indifferent`);
			} else if (slave.attrXX <= 85) {
				r.push(`<span class="green">eager</span>`);
			} else {
				r.push(`<span class="green">very enthusiastic</span>`);
			}
			r.push(`when a pussy is pressed against ${his} face hole, and`);
			if (slave.attrXY <= 15) {
				r.push(`<span class="red">struggles</span>`);
			} else if (slave.attrXY <= 35) {
				r.push(`<span class="red">does not react positively</span>`);
			} else if (slave.attrXY <= 65) {
				r.push(`does not react significantly`);
			} else if (slave.attrXY <= 85) {
				r.push(`<span class="green">seems pleased</span>`);
			} else {
				r.push(`is <span class="green">very happy</span>`);
			}
			r.push(`when a dick is pushed inside`);
			if (canDoVaginal(slave) && slave.vagina > 0 && canDoAnal(slave) && slave.anus > 0) {
				r.push(`either of ${his} lower holes.`);
			} else if (canDoVaginal(slave) && slave.vagina > 0) {
				r.push(`${his} front hole.`);
			} else if (canDoAnal(slave) && slave.anus > 0) {
				r.push(`${his} rear hole.`);
			} else {
				r.push(`it.`);
			}
		}
	}

	function behavioralFlaws() {
		const r = [];
		if (slave.behavioralFlaw === "arrogant") {
			if (slave.devotion < -20 && slave.trust >= -20) {
				r.push(`${He} is <span class="red">arrogant</span> and clings to ${his} dignity.`);
			} else if (slave.devotion < -20) {
				r.push(`${He} is still <span class="red">arrogant</span> at heart, but does ${his} best to hide it out of fear.`);
			} else if (slave.devotion <= 20) {
				r.push(`${He} is <span class="red">arrogant</span> and seems to think slavery beneath ${him}.`);
			} else {
				r.push(`Despite being well broken, ${he} seems to retain hints of <span class="red">arrogance.</span>`);
			}
		} else if (slave.behavioralFlaw === "bitchy") {
			if (slave.devotion < -20 && slave.trust >= -20) {
				r.push(`${He} is <span class="red">bitchy</span> and insults you every chance ${he} gets.`);
			} else if (slave.devotion < -20) {
				r.push(`${He} is still <span class="red">bitchy</span> at times, but does ${his} best to keep quiet out of fear.`);
			} else if (slave.devotion <= 20) {
				r.push(`${He} is <span class="red">bitchy</span> and constantly tries to get a word in edgewise.`);
			} else {
				r.push(`Since ${he} is well broken, ${he} tries to confine ${his} <span class="red">bitchy</span> remarks to your other slaves.`);
			}
		} else if (slave.behavioralFlaw === "liberated") {
			if (slave.devotion < -20 && slave.trust >= -20) {
				r.push(`${He} strongly believes that <span class="red">slavery is wrong,</span> and resists it as best ${he} can.`);
			} else if (slave.devotion < -20) {
				r.push(`${He} strongly believes that <span class="red">slavery is wrong,</span> but usually keeps quiet out of fear.`);
			} else if (slave.devotion <= 20) {
				r.push(`${He} strongly believes that <span class="red">slavery is wrong,</span> and rarely misses a chance to complain about it.`);
			} else {
				r.push(`${He} strongly believes that <span class="red">slavery is wrong,</span> but has learned to keep it to ${him}self.`);
			}
		} else if (slave.behavioralFlaw === "devout") {
			r.push(`${He} is <span class="red">devoutly religious,</span>`);
			if (slave.devotion < -20 && slave.trust >= -20) {
				r.push(`and uses ${his} faith as a wellspring of resistance.`);
			} else if (slave.devotion < -20) {
				r.push(`and uses ${his} faith as a place of refuge.`);
			} else if (slave.devotion <= 20) {
				r.push(`and uses ${his} faith as a private place within ${him}self.`);
			} else {
				r.push(`but has learned to keep ${his} faith private.`);
			}
		} else if (slave.behavioralFlaw === "odd") {
			r.push(`${He} behaves <span class="red">oddly,</span> saying and doing random things.`);
		} else if (slave.behavioralFlaw === "hates men") {
			r.push(`${He} <span class="red">strongly dislikes</span> being around`);
			if (slave.attrXY >= 85 && slave.energy >= 40) {
				r.push(`men; since ${he} loves the dick, ${he}'s forced to put up with them.`);
			} else if (slave.attrXY >= 65 && slave.energy >= 40) {
				r.push(`men; since ${he} likes the dick, ${he} reluctantly puts up with them.`);
			} else {
				r.push(`men.`);
			}
		} else if (slave.behavioralFlaw === "hates women") {
			r.push(`${He} <span class="red">strongly dislikes</span> being around`);
			if (slave.attrXX >= 85 && slave.energy >= 40) {
				r.push(`women; since ${he} loves pussy, ${he}'s forced to put up with them.`);
			} else if ((slave.attrXX >= 65) && (slave.energy >= 40)) {
				r.push(`women; since ${he} likes pussy, ${he} reluctantly puts up with them.`);
			} else {
				r.push(`women.`);
			}
		} else if (slave.behavioralFlaw === "anorexic") {
			r.push(`${He} suffers from <span class="red">anorexia.</span>`);
		} else if (slave.behavioralFlaw === "gluttonous") {
			r.push(`${He} <span class="red">tends to overeat</span> whenever ${he} can, reacting to the rigors of sexual slavery with overeating.`);
		}
		return r.join(" ");
	}
	function behavioralQuirks() {
		const r = [];
		switch (slave.behavioralQuirk) {
			case "confident":
				r.push(`${He}'s <span class="green">confident,</span> and believes that ${he} has something of value to offer, even as a slave.`);
				break;
			case "cutting":
				r.push(`${He} often has a witty or <span class="green">cutting</span> remark ready, but knows when to keep them to ${him}self.`);
				break;
			case "funny":
				r.push(`${He}'s <span class="green">funny,</span> often providing a little comic relief.`);
				break;
			case "adores women":
				r.push(`${He} <span class="green">adores women,</span> and loves spending time with them.`);
				break;
			case "adores men":
				r.push(`${He} <span class="green">adores men,</span> and loves spending time with them.`);
				break;
			case "fitness":
				r.push(`${He}'s a <span class="green">fitness fanatic,</span> and almost gets off to a hard workout.`);
				break;
			case "insecure":
				r.push(`${He}'s <span class="green">insecure,</span> defining ${his} self worth by how much others want to fuck ${him}.`);
				break;
			case "sinful":
				if (V.arcologies[0].FSChattelReligionist === "unset") {
					r.push(`${He}'s delightfully <span class="green">sinful,</span> taking real pleasure in breaking cultural mores.`);
				} else {
					r.push(`${He}'s a devout Chattel Religionist, and is aggressively sinful against old world faiths. ${He} is enthusiastic about slutty religious clothing, and excited by intentional sacrilege like openly using old world religious icons as sex toys or having orgies on altars.`);
				}
				break;
			case "advocate":
				r.push(`${He}'s an <span class="green">advocate</span> for slavery, and can articulate what it's done for ${him}.`);
		}
		return r.join(" ");
	}

	function sexualFlaws() {
		const r = [];
		switch (slave.sexualFlaw) {
			case "hates oral":
				r.push(`${He} <span class="red">hates</span> oral sex and tries to avoid it.`);
				break;
			case "hates anal":
				r.push(`${He} <span class="red">hates</span> anal sex and tries to avoid it.`);
				break;
			case "hates penetration":
				r.push(`${He} <span class="red">hates</span> penetration and tries to avoid it.`);
				break;
			case "repressed":
				r.push(`${He} is <span class="red">sexually repressed,</span> retaining a fundamental distaste for sex from ${his} upbringing.`);
				break;
			case "idealistic":
				r.push(`${He} is <span class="red">sexually idealistic,</span> retaining a belief that sex should be based on love and consent.`);
				break;
			case "shamefast":
				r.push(`${He} is <span class="red">shamefast,</span> suffering crippling anxiety when naked.`);
				break;
			case "apathetic":
				r.push(`${He} is <span class="red">sexually apathetic,</span> often slipping into inertness during sex.`);
				break;
			case "crude":
				r.push(`${He} is <span class="red">sexually crude,</span> and has little sense of what partners find disgusting during sex.`);
				break;
			case "judgemental":
				r.push(`${He} is <span class="red">sexually judgemental,</span> and often denigrates ${his} sexual partners' performance.`);
				break;
			case "cum addict":
				r.push(`${He}'s a <span class="yellow">cum addict:</span> ${he} has a deep psychological addiction to`);
				if (V.PC.balls > 0) {
					r.push(`semen`);
					if (V.PC.vagina !== -1) {
						r.push(`and`);
					}
				}
				if (V.PC.vagina !== -1) {
					r.push(`pussyjuice`);
				}
				r.push(`and becomes anxious if ${he} goes for a few hours without drinking any.`);
				break;
			case "anal addict":
				r.push(`${He}'s an <span class="yellow">anal addict:</span> ${he} has a deep psychological need to be fucked in the ass and becomes anxious if ${he} goes for a few hours without anal.`);
				break;
			case "attention whore":
				r.push(`${He}'s an <span class="yellow">attention whore:</span> shocking and titillating spectators is more important to ${him} than the actual pleasure of sex.`);
				break;
			case "breast growth":
				r.push(`${He} has a <span class="yellow">breast growth obsession:</span> ${he}'s nearly incapable of believing that ${his} breasts are big enough.`);
				break;
			case "abusive":
				r.push(`${He}'s sexually <span class="yellow">abusive:</span> ${he} prefers taking sexual pleasure by force to having it offered to ${him}.`);
				break;
			case "malicious":
				r.push(`${He}'s sexually <span class="yellow">malicious:</span> ${he} gets off on others' anguish.`);
				break;
			case "self hating":
				r.push(`${He}'s filled with <span class="yellow">self hatred,</span> and is disturbingly willing to comply with things that might hurt ${him}.`);
				break;
			case "neglectful":
				r.push(`${He}'s sexually <span class="yellow">self neglectful,</span> and often shows no interest in getting off ${him}self.`);
				break;
			case "breeder":
				r.push(`${He}'s <span class="yellow">obsessed with being bred</span> to the point of fetishizing pregnancy itself as much as any act that leads to it.`);
		}
		return r.join(" ");
	}

	function sexualQuirk() {
		const r = [];
		switch (slave.sexualQuirk) {
			case "gagfuck queen":
				r.push(`${He}'s a <span class="green">gagfuck queen:</span> ${he}'s able to safely take a rough facefuck.`);
				break;
			case "painal queen":
				r.push(`${He}'s a <span class="green">painal queen:</span> ${he} knows exactly how much ${he} can take without getting hurt.`);
				break;
			case "strugglefuck queen":
				r.push(`${He}'s a <span class="green">strugglefuck queen:</span> ${he} can gauge exactly how much resistance ${his} partners want.`);
				break;
			case "tease":
				r.push(`${He}'s a <span class="green">tease,</span> and often displays a little flash of ${him}self followed by a blush.`);
				break;
			case "romantic":
				r.push(`${He}'s a <span class="green">romantic,</span> and persists in innocent pleasure in the closeness of sex.`);
				break;
			case "perverted":
				r.push(`${He}'s <span class="green">perverted,</span> and enjoys breaking sexual boundaries.`);
				break;
			case "caring":
				r.push(`${He}'s <span class="green">caring,</span> and enjoys bringing partners pleasure more than getting off ${him}self.`);
				break;
			case "unflinching":
				r.push(`${He}'s <span class="green">unflinching,</span> willing to do anything, even by the standards of sex slaves.`);
				break;
			case "size queen":
				r.push(`${He}'s <span class="green">a size queen;</span> preferring big cock is almost ${his} trademark.`);
		}
		return r.join(" ");
	}

	function fetish() {
		const r = [];
		if (slave.fetishKnown === 1) {
			switch (slave.fetish) {
				case "submissive":
					if (slave.sexualFlaw === "apathetic") {
						r.push(`This sexual apathy plays into ${his} preference for <span class="lightcoral">submission.</span>`);
					} else if (slave.behavioralFlaw === "arrogant") {
						r.push(`${His} arrogance is really just a thin shell to protect ${his} true need to <span class="lightcoral">submit.</span>`);
					} else if (slave.fetishStrength > 95) {
						r.push(`${He}'s an extreme <span class="lightcoral">submissive,</span> and relishes the strictures of slavery.`);
					} else if (slave.fetishStrength > 60) {
						r.push(`${He}'s a confirmed <span class="lightcoral">submissive,</span> and enjoys the strictures of slavery.`);
					} else {
						r.push(`${He} has <span class="lightcoral">submissive</span> tendencies, and likes the strictures of slavery.`);
					}
					break;
				case "cumslut":
					if (slave.sexualFlaw === "hates oral") {
						r.push(`${He}'s torn between ${his} <span class="lightcoral">love of semen</span> and ${his} dislike of having cocks in ${his} mouth.`);
					} else if (slave.fetishStrength > 95) {
						r.push(`${He}'s a <span class="lightcoral">cumslut,</span> and loves giving blowjobs and receiving facials.`);
					} else if (slave.fetishStrength > 60) {
						r.push(`${He} <span class="lightcoral">prefers giving oral,</span> and enjoys sucking dick and receiving facials.`);
					} else {
						r.push(`${He} has an <span class="lightcoral">oral fixation,</span> and likes giving blowjobs and receiving facials.`);
					}
					break;
				case "humiliation":
					if (slave.behavioralFlaw === "bitchy") {
						r.push(`${His} bitchiness is really just an expression of ${his} deep need to be <span class="lightcoral">humiliated</span> ${him}self.`);
					} else if (slave.sexualFlaw === "shamefast") {
						r.push(`${His} shame is genuine, and it is with real self-loathing that ${he} <span class="lightcoral">gets off on humiliation.</span>`);
					} else if (slave.fetishStrength > 95) {
						r.push(`${He}'s a slut for <span class="lightcoral">humiliation,</span> and gets off on having others see ${his} enslavement.`);
					} else if (slave.fetishStrength > 60) {
						r.push(`${He} enjoys <span class="lightcoral">humiliating</span> sex.`);
					} else {
						r.push(`${He} likes <span class="lightcoral">embarrassing</span> sex.`);
					}
					break;
				case "buttslut":
					if (slave.sexualFlaw === "hates anal") {
						r.push(`${His} hatred is just pretense to cover ${his} shame about how much ${he} really loves getting <span class="lightcoral">fucked in the butt.</span>`);
					} else if (slave.fetishStrength > 95) {
						r.push(`${He}'s a <span class="lightcoral">buttslut,</span> happy to have anyone put anything up ${his} ass.`);
					} else if (slave.fetishStrength > 60) {
						r.push(`${He} <span class="lightcoral">prefers anal.</span>`);
					} else {
						r.push(`${He} has an <span class="lightcoral">anal fixation.</span>`);
					}
					break;
				case "boobs":
					if (slave.fetishStrength > 95) {
						r.push(`${He} <span class="lightcoral">prefers mammary intercourse</span> to any other kind of sex, and readily climaxes to nipple stimulation.`);
					} else if (slave.fetishStrength > 60) {
						r.push(`${He} enjoys <span class="lightcoral">breast play,</span> and is rapidly aroused by nipple stimulation.`);
					} else {
						r.push(`${He} <span class="lightcoral">really likes boobs,</span> ${he}rs and others.`);
					}
					break;
				case "pregnancy":
					if (slave.fetishStrength > 95) {
						r.push(`${He} has a <span class="lightcoral">pregnancy fetish,</span> and finds anything related to reproduction sexy.`);
					} else if (slave.fetishStrength > 60) {
						r.push(`${He} has an <span class="lightcoral">impregnation fantasy,</span> and enjoys bareback sex.`);
					} else {
						r.push(`${He} has a recurring <span class="lightcoral">impregnation fantasy.</span>`);
					}
					break;
				case "dom":
					if (slave.sexualFlaw === "apathetic") {
						r.push(`${He} is at war with ${him}self, since ${his} habitual apathy during sex barely masks a desire to <span class="lightcoral">dominate.</span>`);
					} else if (slave.fetishStrength > 95) {
						r.push(`${He} is a <span class="lightcoral">complete dom;</span> with other slaves this is expressed as a strong preference to top, and with ${his} betters ${he}'s an almost competitive lover.`);
					} else if (slave.fetishStrength > 60) {
						r.push(`${He} likes to take an active, powerful role in sex; with other slaves this is expressed as <span class="lightcoral">dominance,</span> and with ${his} betters ${he}'s a very energetic lover.`);
					} else {
						r.push(`${He} prefers to take a <span class="lightcoral">dominant</span> sexual role.`);
					}
					break;
				case "sadist":
					if (slave.fetishStrength > 95) {
						r.push(`${He} is an <span class="lightcoral">aggressive sadist;</span> ${he}'s constantly plotting to control, abuse, and hurt other slaves.`);
					} else if (slave.fetishStrength > 60) {
						r.push(`${He} has <span class="lightcoral">sadistic tendencies;</span> ${he} enjoys watching other slaves in pain, but truly loves causing pain ${him}self.`);
					} else {
						r.push(`${He} gets off on <span class="lightcoral">the suffering of others;</span> ${he} enjoys watching other slaves in pain.`);
					}
					break;
				case "masochist":
					if (slave.fetishStrength > 95) {
						r.push(`${He} is a <span class="lightcoral">pain slut.</span>`);
					} else if (slave.fetishStrength > 60) {
						r.push(`${He} <span class="lightcoral">gets off on pain,</span> showing strong masochistic tendencies.`);
					} else {
						r.push(`${He} <span class="lightcoral">doesn't mind pain,</span> and shows some masochistic tendencies.`);
					}
					break;
				default:
					r.push(`${His} sexual tastes are <span class="pink">quite normal.</span>`);
			}
		} else {
			r.push(`${His} fetishes, if any, are not known to you.`);
		}
		return r.join(" ");
	}

	function attraction() {
		const r = [];
		if (slave.attrKnown === 1) {
			if (slave.energy > 95) {
				r.push(`${He}'s a <span class="green">nymphomaniac.</span>`);
			} else if (slave.energy > 80) {
				r.push(`${He}'s a <span class="green">sex addict.</span>`);
			} else if (slave.energy > 60) {
				r.push(`${He} has a <span class="green">powerful appetite for sex.</span>`);
			} else if (slave.energy > 40) {
				r.push(`${He} has a <span class="yellow">healthy sex drive.</span>`);
			} else if (slave.energy > 20) {
				r.push(`${He} has a <span class="red">weak sex drive.</span>`);
			} else {
				r.push(`${He}'s <span class="red">frigid,</span> with little interest in sex.`);
			}
			if (slave.attrXY <= 5) {
				if (slave.attrXX <= 5) {
					r.push(`${He} <span class="red">finds both men's and women's intimate areas quite repulsive,</span> an unfortunate state of affairs!`);
				} else {
					r.push(`${He} <span class="red">finds men sexually disgusting,</span>`);
					if (slave.attrXX <= 15) {
						r.push(`and <span class="red">has almost as much trouble with women.</span>`);
					} else if (slave.attrXX <= 35) {
						r.push(`and is <span class="red">unenthusiastic about women.</span>`);
					} else if (slave.attrXX <= 65) {
						r.push(`and ${he} is indifferent to women.`);
					} else if (slave.attrXX <= 85) {
						r.push(`but <span class="green">is attracted to women.</span>`);
					} else if (slave.attrXX <= 95) {
						r.push(`<span class="green">strongly preferring women.</span>`);
					} else {
						r.push(`but is <span class="green">passionate about women.</span>`);
					}
				}
			} else if (slave.attrXY <= 15) {
				r.push(`${He} <span class="red">considers men's bodies a turnoff,</span>`);
				if (slave.attrXX <= 5) {
					r.push(`and is <span class="red">actually disgusted by women's.</span>`);
				} else if (slave.attrXX <= 15) {
					r.push(`and <span class="red">feels the same about women's.</span>`);
				} else if (slave.attrXX <= 35) {
					r.push(`and is <span class="red">unenthusiastic about women.</span>`);
				} else if (slave.attrXX <= 65) {
					r.push(`and ${he} is indifferent to women.`);
				} else if (slave.attrXX <= 85) {
					r.push(`but <span class="green">is attracted to women.</span>`);
				} else if (slave.attrXX <= 95) {
					r.push(`<span class="green">strongly preferring women.</span>`);
				} else {
					r.push(`but is <span class="green">passionate about women.</span>`);
				}
			} else if (slave.attrXY <= 35) {
				r.push(`${He} <span class="red">finds most men unattractive,</span>`);
				if (slave.attrXX <= 5) {
					r.push(`and is <span class="red">disgusted by the idea of intimacy with a woman.</span>`);
				} else if (slave.attrXX <= 15) {
					r.push(`and is <span class="red">turned off by women.</span>`);
				} else if (slave.attrXX <= 35) {
					r.push(`and is <span class="red">unenthusiastic about women.</span>`);
				} else if (slave.attrXX <= 65) {
					r.push(`and ${he} is indifferent to women.`);
				} else if (slave.attrXX <= 85) {
					r.push(`but <span class="green">is attracted to women.</span>`);
				} else if (slave.attrXX <= 95) {
					r.push(`<span class="green">strongly preferring women.</span>`);
				} else {
					r.push(`but is <span class="green">passionate about women.</span>`);
				}
			} else if (slave.attrXY <= 65) {
				r.push(`${He}'s indifferent to sex with men,`);
				if (slave.attrXX <= 5) {
					r.push(`but is <span class="red">disgusted by the idea of intimacy with a woman.</span>`);
				} else if (slave.attrXX <= 15) {
					r.push(`but is <span class="red">turned off by women.</span>`);
				} else if (slave.attrXX <= 35) {
					r.push(`but is <span class="red">actually unenthusiastic about women.</span>`);
				} else if (slave.attrXX <= 65) {
					r.push(`and feels the same about women, too.`);
				} else if (slave.attrXX <= 85) {
					r.push(`but <span class="green">is attracted to women.</span>`);
				} else if (slave.attrXX <= 95) {
					r.push(`<span class="green">strongly preferring women.</span>`);
				} else {
					r.push(`but is <span class="green">passionate about women.</span>`);
				}
			} else if (slave.attrXY <= 85) {
				r.push(`${He} <span class="green">finds men attractive,</span>`);
				if (slave.attrXX <= 5) {
					r.push(`but is <span class="red">disgusted by the idea of intimacy with a woman.</span>`);
				} else if (slave.attrXX <= 15) {
					r.push(`but is <span class="red">turned off by women.</span>`);
				} else if (slave.attrXX <= 35) {
					r.push(`but is <span class="red">unenthusiastic about women.</span>`);
				} else if (slave.attrXX <= 65) {
					r.push(`but is indifferent to women.`);
				} else if (slave.attrXX <= 85) {
					r.push(`and <span class="green">likes women too.</span>`);
				} else if (slave.attrXX <= 95) {
					r.push(`but <span class="green">likes women even more.</span>`);
				} else {
					r.push(`but is really <span class="green">passionate about women.</span>`);
				}
			} else if (slave.attrXY <= 95) {
				r.push(`${He} <span class="green">is aroused by most men,</span>`);
				if (slave.attrXX <= 5) {
					r.push(`but is <span class="red">disgusted by the idea of intimacy with a woman.</span>`);
				} else if (slave.attrXX <= 15) {
					r.push(`but is <span class="red">turned off by women.</span>`);
				} else if (slave.attrXX <= 35) {
					r.push(`but is <span class="red">unenthusiastic about women.</span>`);
				} else if (slave.attrXX <= 65) {
					r.push(`but is indifferent to women.`);
				} else if (slave.attrXX <= 85) {
					r.push(`and <span class="green">likes women too.</span>`);
				} else if (slave.attrXX <= 95) {
					r.push(`and <span class="green">thinks most women are hot,</span> too.`);
				} else {
					r.push(`but is most <span class="green">passionate about women.</span>`);
				}
			} else {
				if (slave.attrXX > 95) {
					r.push(`${He}'s <span class="green">omnisexual,</span> and is passionately attracted to nearly everyone.`);
				} else {
					r.push(`${He}'s <span class="green">passionate about men,</span>`);
					if (slave.attrXX <= 5) {
						r.push(`but is <span class="red">disgusted by the idea of intimacy with a woman.</span>`);
					} else if (slave.attrXX <= 15) {
						r.push(`but is <span class="red">turned off by women.</span>`);
					} else if (slave.attrXX <= 35) {
						r.push(`but is <span class="red">unenthusiastic about women.</span>`);
					} else if (slave.attrXX <= 65) {
						r.push(`but is indifferent to women.`);
					} else if (slave.attrXX <= 85) {
						r.push(`but also <span class="green">likes women,</span> too.`);
					} else {
						r.push(`but also <span class="green">thinks most women are hot,</span> too.`);
					}
				}
			}
		} else {
			r.push(`You do not understand ${his} sexuality very well.`);
		}
		return r.join(" ");
	}

	return r.join(" ");
};


code:
/**
 * @param {App.Entity.SlaveState} slave
 * @returns {string}
 */
App.Desc.mouth = function(slave) {
	const r = [];
	const {
		he, him, his, hers, himself, boy, He, His, woman, girl
	} = getPronouns(slave);

	r.push(`${He} has`);
	if (slave.lips <= 10) {
		r.push(`thin, unattractive lips.`);
	} else if (slave.lips <= 20) {
		r.push(`normal lips.`);
	} else if (slave.lips <= 40) {
		r.push(`full, attractive lips.`);
	} else if (slave.lips <= 70) {
		r.push(`plump, beestung lips.`);
	} else if (slave.lips <= 95) {
		r.push(`huge, obviously augmented lips.`);
	} else {
		r.push(`a facepussy: ${his} lips are so huge that they're always a bit parted in the middle, forming a moist, inviting`);
		if (V.PC.dick !== 0) {
			r.push(`hole for cock.`);
		} else {
			r.push(`hole.`);
		}
	}

	if (V.showImplantEffects === 1) {
		if (V.arcologies[0].FSTransformationFetishist !== "unset") {
			if (slave.lipsImplant > 0) {
				r.push(`They are about ${Math.floor((slave.lipsImplant / slave.lips) * 100)}% implant.`);
			}
			if (V.arcologies[0].FSTransformationFetishist > 20) {
				if (slave.lips > 70) {
					if (slave.lipsImplant / slave.lips < .50) {
						r.push(`${His} lips are huge and <span class="red">disgustingly natural</span> for their size.`);
					} else {
						r.push(`${His} lips are huge and <span class="green">obviously implants,</span> as they should be.`);
					}
				}
			}
		}
	}

	if (slave.fuckdoll > 0) {
		r.push(`${His} mouth is held open by the suit's oral insert.`);
	} else if ((slave.assignment === "work in the dairy") && (V.dairyRestraintsSetting > 1)) {
		if (V.dairyFeedersSetting > 1) {
			r.push(`${His} milking machine has a phallus a long way down ${his} throat to rehydrate ${him}.`);
		} else {
			r.push(`${He}'s got ${his} mouth wrapped around a phallus provided by ${his} milking machine, and is sucking it off for hydration.`);
		}
	} else {
		if (canTalk(slave)) {
			if (slave.lips > 70) {
				r.push(`${He} can barely enunciate past ${his} dick-sucking lips; '${WrittenMaster(slave)}' comes out as '${lispReplace(WrittenMaster(slave))}.'`);
			}
		}
		if (slave.teeth !== "normal") {
			if (slave.teeth === "crooked") {
				r.push(`${His} teeth are <span class="yellow">crooked,</span> detracting from ${his} beauty whenever ${he} opens ${his} mouth.`);
			} else if (slave.teeth === "straightening braces") {
				r.push(`${He} has braces,`);
				if (slave.visualAge > 35) {
					r.push(`an amusing sight on such a mature ${woman}.`);
				} else if ((slave.visualAge > 14) && (slave.visualAge < 18)) {
					r.push(`a cute look on such a young ${girl}.`);
				} else {
					r.push(`and occasionally looks preoccupied with discomfort as they straighten ${his} teeth.`);
				}
			} else if (slave.teeth === "cosmetic braces") {
				r.push(`${He}'s wearing braces despite ${his} straight teeth,`);
				if (slave.visualAge > 35) {
					r.push(`an amusing sight on such a mature ${woman}.`);
				} else if ((slave.visualAge > 14) && (slave.visualAge < 18)) {
					r.push(`a cute look on such a young ${girl}.`);
				} else {
					r.push(`just for appearances.`);
				}
			} else if (slave.teeth === "gapped") {
				r.push(`${He} has a prominent gap between ${his} front`);
				if (slave.faceShape === "cute") {
					r.push(`teeth that suits ${his} cute face surprisingly well.`);
				} else {
					r.push(`teeth.`);
				}
				if (canTalk(slave)) {
					r.push(`It also leaves ${him} with a slight lisp.`);
				}
			} else if (slave.teeth === "removable") {
				r.push(`${His} teeth have been removed and replaced with high-quality dentures. It's difficult to tell anything's unusual until you take them out for gummy oral sex.`);
			} else if (slave.teeth === "pointy") {
				r.push(`${His} teeth have been replaced with realistic implants that mimic the dentition of a carnivore. ${His} smiles are frightening, and ${he} can bare them to become truly terrifying.`);
			} else if (slave.teeth === "fangs") {
				r.push(`${His} upper canine teeth have been replaced with realistic implants that mimic fangs. ${His} smiles are frightening, and ${he} can bare them to become truly terrifying.`);
			} else if (slave.teeth === "fang") {
				r.push(`One of ${his} upper canine teeth has been replaced with realistic implant shaped like a fang.`);
				if (slave.faceShape === "cute") {
					r.push(`It gives ${his} cute face an added`);
					if (slave.lips <= 50) {
						r.push(`charm, especially when it rests over ${his} lower lip.`);
					} else {
						r.push(`charm.`);
					}
				} else {
					if (slave.lips <= 50) {
						r.push(`It stands out when it rests over ${his} lower lip.`);
					} else {
						r.push(`It doesn't fit well in ${his} mouth and frequently prods ${his} fat lower lip.`);
					}
				}
			} else if (slave.teeth === "baby") {
				r.push(`${He} still has ${his} baby teeth.`);
			} else if (slave.teeth === "mixed") {
				r.push(`${He} is in the process of replacing ${his} baby teeth.`);
			}
		}
	}
	if (slave.tastes === -1) {
		r.push(`${He} has no sense of taste, but this isn't immediately obvious just by looking at ${his} tongue.`);
	}

	r.push(App.Desc.mods(slave, "lips"));
	r.push(App.Desc.mods(slave, "tongue"));

	if (slave.fuckdoll > 0) {
		if (V.PC.dick !== 0) {
			r.push(`Sticking a dick`);
		} else {
			r.push(`Sliding a dildo`);
		}
		r.push(`into ${his}`);
		if (slave.lips > 95) {
			r.push(`facepussy`);
		} else {
			r.push(`mouth insert`);
		}
		if (slave.fuckdoll <= 45) {
			r.push(`mostly results in gagging.`);
		} else {
			r.push(`offers its user a selection of delightful face hole massage options.`);
		}
	} else {
		r.push(`${He} is`);
		if (slave.skill.oral >= 100) {
			r.push(`an <span class="aquamarine">oral sex master.</span>`);
		} else if (slave.skill.oral > 60) {
			r.push(`an <span class="aquamarine">expert at oral.</span>`);
		} else if (slave.skill.oral > 30) {
			r.push(`<span class="aquamarine">orally skilled.</span>`);
		} else if (slave.skill.oral > 10) {
			r.push(`<span class="aquamarine">capable of basic oral sex.</span>`);
		} else {
			r.push(`unskilled at oral sex.`);
		}
	}
	return r.join(" ");
};


code:
/**
 * @param {App.Entity.SlaveState} slave
 * @returns {string}
 */
App.Desc.shoulders = function(slave) {
	const r = [];
	const {
		he, him, his, hers, himself, boy, He, His
	} = getPronouns(slave);
	if (slave.fuckdoll === 0) {
		if (slave.shoulders < -1) {
			r.push(`${His} shoulders and chest are very narrow and`);
			if (slave.boobs > 2000) {
				r.push(`feminine, forcing ${his} pressed-together tits to spread far beyond ${his} sides.`);
			} else {
				r.push(`feminine.`);
			}
		} else if (slave.shoulders < 0) {
			r.push(`${His} shoulders and chest are quite`);
			if (slave.boobs > 1200) {
				r.push(`feminine, causing a lot of cleavage and pressing ${his} boobs outward beyond ${his} sides.`);
			} else {
				r.push(`feminine.`);
			}
		} else if (slave.shoulders > 1) {
			r.push(`${His} shoulders and chest are very`);
			if (slave.boobs > 600) {
				r.push(`broad, making ${his} boobs look a lot smaller than they actually are.`);
			} else {
				r.push(`broad.`);
			}
		} else if (slave.shoulders > 0) {
			r.push(`${His} shoulders and chest are fairly`);
			if (slave.boobs > 600) {
				r.push(`broad, making ${his} tits look smaller than they actually are.`);
			} else {
				r.push(`broad.`);
			}
		} else {
			r.push(`${His} shoulders and chest are`);
			if (slave.boobs > 800) {
				r.push(`feminine, flattering ${his} breasts.`);
			} else {
				r.push(`feminine.`);
			}
		}
		if (slave.shoulders > slave.hips) {
			r.push(`They're wider than ${his} hips,`);
			if (slave.boobs > 2000 * (slave.shoulders - slave.hips)) {
				r.push(`but ${his} massive breasts make it hard to discern.`);
			} else if (V.arcologies[0].FSGenderRadicalist !== "unset") {
				r.push(`giving ${him} a somewhat mannish appearance.`);
			} else if (V.arcologies[0].FSGenderFundamentalist !== "unset") {
				r.push(`giving ${him} an <span class="red">ugly, mannish appearance.</span>`);
			} else {
				r.push(`giving ${him} an <span class="red">unattractive, somewhat mannish, appearance.</span>`);
			}
		}
	}
	return r.join(" ");
};


code:
/**
 * @param {App.Entity.SlaveState} slave
 * @returns {string}
 */
App.Desc.skin = function(slave) {
	let r = [];
	const {
		he, him, his, hers, himself, boy, He, His
	} = getPronouns(slave);

	if (slave.fuckdoll > 0) {
		r.push(`The small areas of visible skin are`);
		r = r.concat(freckles());
		r = r.concat(tanned());
	} else if (V.seeNationality === 1) {
		if (slave.nationality === 0) {
			r.push(`Ethnically, ${he}'s`);
		} else if (slave.nationality === "slave") {
			r.push(`${He}'s been chattel long enough that slavery is effectively ${his} nationality; ethnically, ${he}'s`);
		} else if (slave.nationality === "Stateless") {
			r.push(`${He} has spent so much time in the Free Cities that their statelessness is effectively ${his} nationality; ethnically, ${he}'s`);
		} else if (slave.nationality === "Zimbabwean" && slave.race === "white") {
			r.push(`${He}'s originally <span class="tan">Rhodesian;</span> ethnically, ${he}'s`);
		} else if (slave.nationality === "Vatican") {
			r.push(`${He}'s originally <span class="tan">from Vatican City;</span> ethnically, ${he}'s`);
		} else {
			r.push(`${He}'s originally <span class="tan">${slave.nationality};</span> ethnically, ${he}'s`);
		}
		r.push(`<span class="tan">${slave.race},</span> and ${his} skin is`);
		r = r.concat(freckles());
		r = r.concat(tanned());
		if (slave.race !== slave.origRace && V.saleDescription !== 1) {
			r.push(`${He} has received plastic surgery to appear ${slave.race}; ${he} is originally ${slave.origRace}.`);
		} else if (slave.race !== slave.origRace && V.saleDescription === 1) {
			if (V.PC.skill.medicine >= 100) {
				r.push(`Thanks to your medical experience, you can easily tell that ${he} was originally ${slave.origRace}, but surgically modified.`);
			} else if (V.PC.skill.medicine >= 50) {
				r.push(`Oddly enough, ${he} appears to have a number of ${slave.origRace} features.`);
			} else if (V.PC.skill.slaving >= 50) {
				r.push(`Thanks to your experience in buying and selling slaves, you can easily tell that ${he} is not naturally ${slave.race}.`);
			} else if (jsRandom(0, 100) < V.PC.skill.medicine) {
				r.push(`${His} features seem slightly off.`);
			}
		}
	} else if ((V.seeRace === 1)) {
		r.push(`Ethnically, ${he}'s <span class="tan">${slave.race},</span> and ${his} skin is`);
		r = r.concat(freckles());
		r = r.concat(tanned());
		if (slave.race !== slave.origRace && V.saleDescription !== 1) {
			r.push(`${He} has received plastic surgery to appear ${slave.race}; ${he} is originally ${slave.origRace}`);
		} else if (slave.race !== slave.origRace && V.saleDescription === 1) {
			if (V.PC.skill.medicine >= 100) {
				r.push(`Thanks to your medical experience, you can easily tell that ${he} was originally ${slave.origRace}, but surgically modified.`);
			} else if (V.PC.skill.medicine >= 50) {
				r.push(`Oddly enough, ${he} appears to have a number of ${slave.origRace} features.`);
			} else if (V.PC.skill.slaving >= 50) {
				r.push(`Thanks to your experience in buying and selling slaves, you can easily tell that ${he} is not naturally ${slave.race}.`);
			} else if (jsRandom(0, 100) < V.PC.skill.medicine) {
				r.push(`${His} features seem slightly off.`);
			}
		}
	} else {
		r.push(`${His} skin is`);
		r = r.concat(freckles());
		r = r.concat(tanned());
	}

	return r.join(" ");

	function freckles() {
		const r = [];
		if (slave.markings === "freckles") {
			r.push(`${slave.skin} and lightly `);
			if ((skinToneLevel(slave.skin) > 5) && (skinToneLevel(slave.skin) < 10) && (slave.hColor === "red")) {
				r.push(`freckled, an attractive combination.`);
			} else {
				r.push(`freckled.`);
			}
		} else if (slave.markings === "heavily freckled") {
			r.push(`${slave.skin} and heavily`);
			if ((skinToneLevel(slave.skin) > 5) && (skinToneLevel(slave.skin) < 10) && (slave.hColor === "red")) {
				r.push(`freckled, an attractive combination.`);
			} else {
				r.push(`freckled.`);
			}
		} else {
			r.push(`${slave.skin}.`);
		}
		return r;
	}

	function tanned() {
		const r = [];
		if (slave.skin === "sun tanned" || slave.skin === "spray tanned") {
			r.push(`${His} skin has been`);
			if (slave.skin === "sun tanned") {
				r.push(`tanned to a natural, healthy-looking tone.`);
			} else if (slave.skin === "spray tanned") {
				r.push(`tanned with obvious, garish-looking makeup.`);
			} else {
				r.push(`tanned.`);
			}
		}
		return r;
	}
};


code:
/**
 * @param {App.Entity.SlaveState} slave
 * @returns {string}
 */
App.Desc.armwear = function(slave) {
	const r = [];
	const {
		his, He
	} = getPronouns(slave);
	// TODO: check clothing descriptions for glove references
	if (hasAnyArms(slave)) {
		switch (slave.armAccessory) {
			case "hand gloves":
				r.push(`${He} is wearing a pair of simple gloves that covers ${his}`);
				if (hasBothArms(slave)) {
					r.push(`hands`);
				} else {
					r.push(`hand`);
				}
				r.push(`up to ${his}`);
				if (hasBothArms(slave)) {
					r.push(`wrists.`);
				} else {
					r.push(`wrist.`);
				}
				break;
			case "elbow gloves":
				r.push(`${He} is wearing a pair of long gloves that covers ${his}`);
				if (hasBothArms(slave)) {
					r.push(`arms`);
				} else {
					r.push(`arm`);
				}
				r.push(`until just past ${his}`);
				if (hasBothArms(slave)) {
					r.push(`elbows.`);
				} else {
					r.push(`elbow.`);
				}
		}
	}

	return r.join(" ");
};