code:
/**
 * @param {App.Entity.SlaveState} slave
 */
globalThis.nationalityToRace = function(slave) {
	slave.race = hashChoice(setup.raceSelector[slave.nationality] || setup.raceSelector[""]);
};

/**
 * @param {App.Entity.SlaveState} slave
 */
globalThis.raceToNationality = function(slave) {
	/* consider this placeholder until raceNationalities gets fixed up */
	slave.nationality = hashChoice(V.nationalities);
	/* Maximum of 100 attempts */
	let i = 0;
	for (; setup.raceSelector[slave.nationality] && !(slave.race in setup.raceSelector[slave.nationality]) && i < 100; i++) {
		slave.nationality = hashChoice(V.nationalities);
	}
	/* No success after 100 attempts, so just randomize according to race */
	if (setup.raceSelector[slave.nationality] && !(slave.race in setup.raceSelector[slave.nationality]) && i === 100) {
		const nationalityKey = `${slave.race.toLowerCase().replace(/[ -]/g, "")}Nationalities`;
		if (nationalityKey in setup) {
			slave.nationality = hashChoice(setup[nationalityKey]);
		} else {
			slave.nationality = "Stateless";
		}
	}
};

/**
 * @param {string | object} nationality
 * @param {string | object} race
 * @param {boolean} male
 * @param {undefined} [filter] Default: allow all
 * @returns {string}
 */
globalThis.generateName = function(nationality, race, male, filter = _.stubTrue) {
	const lookup = (male ? setup.malenamePoolSelector : setup.namePoolSelector);
	const result = jsEither(
		(lookup[`${nationality}.${race}`] || lookup[nationality] ||
			(male ? setup.whiteAmericanMaleNames : setup.whiteAmericanSlaveNames)).filter(filter));
	/* fallback for males without specific male name sets: return female name */
	if (male && !result) {
		return generateName(nationality, race, false);
	}
	return result;
};

/**
 * @param {string | number} nationality
 * @param {any} race
 * @param {any} male
 * @param {Function} [filter] Default: allow all
 * @returns {string}
 */
globalThis.generateSurname = function(nationality, race, male, filter = _.stubTrue) {
	const result = jsEither(
		(setup.surnamePoolSelector[`${nationality}.${race}`] ||
			setup.surnamePoolSelector[nationality] ||
			setup.whiteAmericanSlaveSurnames).filter(filter));
	if (male) {
		/* see if we have male equivalent of that surname, and return that if so */
		const maleLookup = setup.maleSurnamePoolSelector[`${nationality}.${race}`] || setup.maleSurnamePoolSelector[nationality];
		if (maleLookup && maleLookup[result]) {
			return maleLookup[result];
		}
	}
	return result || 0;
};

/**
 * @param {string} name
 * @param {string | object} nationality
 * @param {any} race
 * @returns {boolean}
 */
globalThis.isMaleName = function(name, nationality, race) {
	const names = setup.malenamePoolSelector[`${nationality}.${race}`] ||
		setup.malenamePoolSelector[nationality] ||
		setup.whiteAmericanMaleNames;
	return names && names.includes(name);
};

/**
 * @param {App.Entity.SlaveState} slave
 */
globalThis.nationalityToName = function(slave) {
	const male = (slave.genes === "XY");

	slave.birthName = generateName(slave.nationality, slave.race, male);
	slave.birthSurname = generateSurname(slave.nationality, slave.race, male);
	if (male && isMaleName(slave.birthName, slave.nationality, slave.race) && !V.allowMaleSlaveNames) {
		slave.slaveName = generateName(slave.nationality, slave.race, false);
	} else {
		slave.slaveName = slave.birthName;
	}
	slave.slaveSurname = slave.birthSurname;
	if (V.useFSNames === 1) {
		if (V.FSNamePref === 0) {
			if (V.arcologies[0].FSChattelReligionist > 20) {
				slave.slaveName = jsEither(setup.chattelReligionistSlaveNames);
				slave.slaveSurname = 0;
			} else if (V.arcologies[0].FSRomanRevivalist > 20) {
				slave.slaveName = jsEither(setup.romanSlaveNames);
				slave.slaveSurname = jsEither(setup.romanSlaveSurnames);
			} else if (V.arcologies[0].FSAztecRevivalist > 20) {
				slave.slaveName = jsEither(setup.aztecSlaveNames);
				slave.slaveSurname = 0;
			} else if (V.arcologies[0].FSEgyptianRevivalist > 20) {
				slave.slaveName = jsEither(setup.ancientEgyptianSlaveNames);
				slave.slaveSurname = 0;
			} else if (V.arcologies[0].FSEdoRevivalist > 20) {
				slave.slaveName = jsEither(setup.edoSlaveNames);
				slave.slaveSurname = jsEither(setup.edoSlaveSurnames);
			} else if (V.arcologies[0].FSDegradationist !== "unset") {
				DegradingName(slave);
			}
		} else if (V.FSNamePref === 1) {
			slave.slaveName = jsEither(setup.chattelReligionistSlaveNames);
			slave.slaveSurname = 0;
		} else if (V.FSNamePref === 2) {
			slave.slaveName = jsEither(setup.romanSlaveNames);
			slave.slaveSurname = jsEither(setup.romanSlaveSurnames);
		} else if (V.FSNamePref === 3) {
			slave.slaveName = jsEither(setup.aztecSlaveNames);
			slave.slaveSurname = 0;
		} else if (V.FSNamePref === 4) {
			slave.slaveName = jsEither(setup.ancientEgyptianSlaveNames);
			slave.slaveSurname = 0;
		} else if (V.FSNamePref === 5) {
			slave.slaveName = jsEither(setup.edoSlaveNames);
			slave.slaveSurname = jsEither(setup.edoSlaveSurnames);
		} else if (V.FSNamePref === 6) {
			DegradingName(slave);
		} else if (V.FSNamePref === 7) {
			PaternalistName(slave);
		} else if (V.FSNamePref === 8) {
			slave.slaveName = jsEither(setup.bimboSlaveNames);
			slave.slaveSurname = 0;
		}
	}
};

/**
 * @param {App.Entity.SlaveState} slave
 */
globalThis.nationalityToAccent = function(slave) {
	const naturalAccent = jsEither([0, 1, 1, 2, 2, 2, 3, 3, 3, 3]);

	switch (slave.nationality) {
		case "Afghan":
			if (V.language === "Pashto") {
				slave.accent = jsEither([0, 0, 0, 0, 0, 0, 1]);
			} else if (V.language === "Dari") {
				slave.accent = jsEither([0, 0, 0, 0, 0, 0, 1]);
			} else if (V.language === "Persian") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Albanian":
			slave.accent = naturalAccent;
			break;
		case "Algerian":
			slave.accent = (V.language === "Arabic") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "American":
			if (V.language === "English") {
				slave.accent = jsEither([0, 0, 0, 0, 0, 0, 1]);
			} else if (V.language === "Spanish" && slave.race === "latina") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else if (V.language === "Chinese" && slave.race === "asian") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else if (V.language === "Arabic" && slave.race === "middle eastern") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Andorran":
			slave.accent = (V.language === "Catalan") ? jsEither([0, 0, 0, 0, 0, 0, 1]) : naturalAccent;
			break;
		case "Angolan":
			slave.accent = (V.language === "Portuguese") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Antiguan":
			slave.accent = (V.language === "English") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Argentinian":
			slave.accent = (V.language === "Spanish") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Armenian":
			if (V.language === "Russian") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else if (V.language === "English") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Aruban":
			if (V.language === "Dutch") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "English") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else if (V.language === "Portuguese") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Australian":
			slave.accent = (V.language === "English") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Austrian":
			if (V.language === "German") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "Serbian") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else if (V.language === "Slovene") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else if (V.language === "Turkish" && slave.race === "indo-aryan") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Azerbaijani":
			slave.accent = naturalAccent;
			break;
		case "Bahamian":
			slave.accent = (V.language === "English") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Bahraini":
			slave.accent = (V.language === "Arabic") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Bangladeshi":
			if (V.language === "Bengali") {
				slave.accent = jsEither([0, 0, 0, 0, 0, 0, 1]);
			} else if (V.language === "Hindi") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Barbadian":
			slave.accent = (V.language === "English") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Belarusian":
			slave.accent = (V.language === "Russian") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Belgian":
			if (V.language === "Dutch") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "French") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "German") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Belizean":
			slave.accent = (V.language === "English") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Beninese":
			slave.accent = (V.language === "French") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Bermudian":
			slave.accent = (V.language === "English") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Bhutanese":
			slave.accent = (V.language === "Dzongkha") ? jsEither([0, 0, 0, 0, 0, 0, 1]) : naturalAccent;
			break;
		case "Bissau-Guinean":
			slave.accent = (V.language === "Portuguese") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Bolivian":
			slave.accent = (V.language === "Spanish") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Bosnian":
			if (V.language === "Croatian") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "Serbian") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Brazilian":
			if (V.language === "Portuguese") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "Spanish") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "British":
			if (V.language === "English") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "Hindi" && slave.race === "indo-aryan") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Bruneian":
			if (V.language === "Malay") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "English") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Bulgarian":
			slave.accent = naturalAccent;
			break;
		case "Burkinabé":
			slave.accent = (V.language === "French") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Burmese":
			slave.accent = naturalAccent;
			break;
		case "Burundian":
			slave.accent = (V.language === "Kirundi") ? jsEither([0, 0, 0, 0, 0, 0, 1]) : naturalAccent;
			break;
		case "Cambodian":
			if (V.language === "Khmer") {
				slave.accent = jsEither([0, 0, 0, 0, 0, 0, 1]);
			} else if (V.language === "Cham") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "French") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Cameroonian":
			slave.accent = (V.language === "French") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Canadian":
			if (V.language === "English") {
				slave.accent = jsEither([0, 0, 0, 0, 0, 0, 1]);
			} else if (V.language === "French") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Cape Verdean":
			slave.accent = (V.language === "Portuguese") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Catalan":
			slave.accent = (V.language === "Spanish") ? jsEither([0, 0, 0, 0, 0, 0, 1]) : naturalAccent;
			break;
		case "Central African":
			slave.accent = (V.language === "French") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Chadian":
			slave.accent = (V.language === "Arabic") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Chilean":
			slave.accent = (V.language === "Spanish") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Chinese":
			if (V.language === "Tibetan") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else if (V.language === "Korean") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else if (V.language === "Kazakh") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Colombian":
			slave.accent = (V.language === "Spanish") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Comorian":
			if (V.language === "Arabic") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else if (V.language === "French") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Congolese":
			if (V.language === "Lingala") {
				slave.accent = jsEither([0, 0, 0, 0, 0, 0, 1]);
			} else if (V.language === "Kikongo") {
				slave.accent = jsEither([0, 0, 0, 0, 0, 0, 1]);
			} else if (V.language === "French") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "a Cook Islander":
			if (V.language === "Cook Islands Māori") {
				slave.accent = jsEither([0, 0, 0, 0, 0, 0, 1]);
			} else if (V.language === "English") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Costa Rican":
			slave.accent = (V.language === "Spanish") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Croatian":
			if (V.language === "Bosnian") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "Serbian") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Cuban":
			slave.accent = (V.language === "Spanish") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Curaçaoan":
			if (V.language === "Dutch") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "English") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else if (V.language === "Portuguese") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Cypriot":
			if (V.language === "Greek") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "Turkish") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "English") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Czech":
			if (V.language === "Slovak") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "Vietnamese" && slave.race === "asian") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Danish":
			slave.accent = naturalAccent;
			break;
		case "Djiboutian":
			slave.accent = (V.language === "French") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Dominican":
			slave.accent = (V.language === "Spanish") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Dominiquais":
			slave.accent = (V.language === "English") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Dutch":
			slave.accent = naturalAccent;
			break;
		case "East Timorese":
			if (V.language === "Tetum") {
				slave.accent = jsEither([0, 0, 0, 0, 0, 0, 1]);
			} else if (V.language === "Portuguese") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "Malay") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Ecuadorian":
			slave.accent = (V.language === "Spanish") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Egyptian":
			slave.accent = (V.language === "Arabic") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Emirati":
			slave.accent = (V.language === "Arabic") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Equatoguinean":
			slave.accent = (V.language === "Spanish") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Eritrean":
			if (V.language === "Tigrinya") {
				slave.accent = jsEither([0, 0, 0, 0, 0, 0, 1]);
			} else if (V.language === "Arabic") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "English") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Estonian":
			slave.accent = (V.language === "Russian") ? jsEither([0, 1, 2, 2, 2, 3, 3]) : naturalAccent;
			break;
		case "Ethiopian":
			if (V.language === "Amharic") {
				slave.accent = jsEither([0, 0, 0, 0, 0, 0, 1]);
			} else if (V.language === "Arabic") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else if (V.language === "Tigrinya") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Fijian":
			slave.accent = (V.language === "English") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Filipina":
			if (V.language === "Filipino") {
				slave.accent = jsEither([0, 0, 0, 0, 0, 0, 1]);
			} else if (V.language === "English") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "Tagalog") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "Arabic") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else if (V.language === "Spanish") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Finnish":
			slave.accent = (V.language === "Swedish") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "French":
			if (V.language === "English") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else if (V.language === "Spanish") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else if (V.language === "German") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else if (V.language === "Arabic" && slave.race === "middle eastern") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else if (V.language === "Catalan") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "French Guianan":
			slave.accent = (V.language === "French") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "French Polynesian":
			if (V.language === "Tahitian") {
				slave.accent = jsEither([0, 0, 0, 0, 0, 0, 1]);
			} else if (V.language === "French") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "Chinese") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Gabonese":
			slave.accent = (V.language === "French") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Gambian":
			slave.accent = (V.language === "English") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Georgian":
			slave.accent = (V.language === "Abkhaz") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "German":
			if (V.language === "English") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else if (V.language === "French") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else if (V.language === "Turkish" && slave.race === "indo-aryan") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Ghanan":
			if (V.language === "English") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "Hausa") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else if (V.language === "Arabic") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Greek":
			slave.accent = naturalAccent;
			break;
		case "Greenlandic":
			slave.accent = (V.language === "Danish") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Grenadian":
			slave.accent = (V.language === "English") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Guamanian":
			slave.accent = (V.language === "English") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Guatemalan":
			slave.accent = (V.language === "Spanish") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Guinean":
			slave.accent = (V.language === "French") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Guyanese":
			slave.accent = (V.language === "English") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Haitian":
			slave.accent = (V.language === "French") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Honduran":
			slave.accent = (V.language === "Spanish") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Hungarian":
			if (V.language === "German") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else if (V.language === "English") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "I-Kiribati":
			if (V.language === "Gilbertese") {
				slave.accent = jsEither([0, 0, 0, 0, 0, 0, 1]);
			} else if (V.language === "English") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Icelandic":
			if (V.language === "Danish") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else if (V.language === "English") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Indian":
			if (V.language === "Hindi") {
				slave.accent = jsEither([0, 0, 0, 0, 0, 0, 1]);
			} else if (V.language === "English") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Indonesian":
			if (V.language === "Javanese") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "Malay") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Iranian":
			if (V.language === "Persian") {
				slave.accent = jsEither([0, 0, 0, 0, 0, 0, 1]);
			} else if (V.language === "Arabic") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Iraqi":
			if (V.language === "Arabic") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "Kurdish") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Irish":
			slave.accent = (V.language === "English") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Israeli":
			if (V.language === "Hebrew") {
				slave.accent = jsEither([0, 0, 0, 0, 0, 0, 1]);
			} else if (V.language === "Arabic") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "Yiddish") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Italian":
			slave.accent = naturalAccent;
			break;
		case "Ivorian":
			slave.accent = (V.language === "French") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Jamaican":
			slave.accent = (V.language === "English") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Japanese":
			slave.accent = naturalAccent;
			break;
		case "Jordanian":
			slave.accent = (V.language === "Arabic") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Kazakh":
			slave.accent = (V.language === "Russian") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Kenyan":
			slave.accent = (V.language === "Arabic") ? jsEither([0, 1, 2, 2, 2, 3, 3]) : naturalAccent;
			break;
		case "Kittitian":
			slave.accent = (V.language === "English") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Korean":
			slave.accent = (V.language === "Chinese") ? jsEither([0, 1, 2, 2, 2, 3, 3]) : naturalAccent;
			break;
		case "Kosovan":
			if (V.language === "Albanian") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "Serbian") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Kurdish":
			if (V.language === "Arabic") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else if (V.language === "Turkish") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Kuwaiti":
			slave.accent = (V.language === "Arabic") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Kyrgyz":
			slave.accent = (V.language === "Russian") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Laotian":
			if (V.language === "Lao") {
				slave.accent = jsEither([0, 0, 0, 0, 0, 0, 1]);
			} else if (V.language === "Khmu") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "French") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Latvian":
			slave.accent = naturalAccent;
			break;
		case "Lebanese":
			slave.accent = (V.language === "Arabic") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Liberian":
			slave.accent = (V.language === "English") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Libyan":
			slave.accent = (V.language === "Arabic") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "a Liechtensteiner":
			slave.accent = (V.language === "German") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Lithuanian":
			slave.accent = naturalAccent;
			break;
		case "Luxembourgian":
			if (V.language === "Luxembourgish") {
				slave.accent = jsEither([0, 0, 0, 0, 0, 0, 1]);
			} else if (V.language === "German") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "French") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Macedonian":
			slave.accent = naturalAccent;
			break;
		case "Malagasy":
			if (V.language === "French") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "English") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Malawian":
			if (V.language === "English") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "Chichewa") {
				slave.accent = jsEither([0, 0, 0, 0, 0, 0, 1]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Malaysian":
			slave.accent = (V.language === "Malay") ? jsEither([0, 0, 0, 0, 0, 0, 1]) : naturalAccent;
			break;
		case "Maldivian":
			slave.accent = naturalAccent;
			break;
		case "Malian":
			slave.accent = (V.language === "Arabic") ? jsEither([0, 1, 2, 2, 2, 3, 3]) : naturalAccent;
			break;
		case "Maltese":
			if (V.language === "English") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "Italian") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Marshallese":
			slave.accent = (V.language === "English") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Mauritanian":
			slave.accent = (V.language === "Arabic") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Mauritian":
			if (V.language === "English") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "French") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Mexican":
			if (V.language === "Spanish") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "Nahuatl" && slave.race === "amerindian") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Micronesian":
			slave.accent = (V.language === "English") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Moldovan":
			slave.accent = (V.language === "Russian") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Monégasque":
			slave.accent = (V.language === "French") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Mongolian":
			slave.accent = naturalAccent;
			break;
		case "Montenegrin":
			slave.accent = (V.language === "Serbian") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Moroccan":
			slave.accent = (V.language === "Arabic") ? jsEither([0, 1, 2, 2, 2, 3, 3]) : naturalAccent;
			break;
		case "Mosotho":
			slave.accent = (V.language === "Sesotho") ? jsEither([0, 0, 0, 0, 0, 0, 1]) : naturalAccent;
			break;
		case "Motswana":
			slave.accent = (V.language === "English") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Mozambican":
			if (V.language === "Portuguese") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "Makhuwa") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "Sena") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "Swahili") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Namibian":
			if (V.language === "English") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "Afrikaans") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Nauruan":
			slave.accent = (V.language === "English") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Nepalese":
			if (V.language === "Nepali") {
				slave.accent = jsEither([0, 0, 0, 0, 0, 0, 1]);
			} else if (V.language === "Maithili") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "Bhojpuri") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "English") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "New Caledonian":
			if (V.language === "French") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "Nengone") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "Drehu") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "a New Zealander":
			if (V.language === "English") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "Māori" && slave.race === "pacific islander") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Ni-Vanuatu":
			if (V.language === "French") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "English") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Nicaraguan":
			slave.accent = (V.language === "Spanish") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Nigerian":
			if (V.language === "English") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "Hausa") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "Arabic") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Nigerien":
			if (V.language === "French") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "Hausa") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "Arabic") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Niuean":
			slave.accent = (V.language === "English") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Norwegian":
			slave.accent = naturalAccent;
			break;
		case "Omani":
			slave.accent = (V.language === "Arabic") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Pakistani":
			slave.accent = (V.language === "Arabic") ? jsEither([0, 1, 2, 2, 2, 3, 3]) : naturalAccent;
			break;
		case "Palauan":
			if (V.language === "English") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "Sonsorolese") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "Tobian") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "Japanese") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Palestinian":
			slave.accent = (V.language === "Arabic") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Panamanian":
			slave.accent = (V.language === "Spanish") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Papua New Guinean":
			slave.accent = naturalAccent;
			break;
		case "Paraguayan":
			slave.accent = (V.language === "Spanish") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Peruvian":
			slave.accent = (V.language === "Spanish") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Polish":
			slave.accent = naturalAccent;
			break;
		case "Portuguese":
			slave.accent = (V.language === "Spanish") ? jsEither([0, 1, 2, 2, 2, 3, 3]) : naturalAccent;
			break;
		case "Puerto Rican":
			if (V.language === "Spanish") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "English") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Qatari":
			slave.accent = (V.language === "Arabic") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Romanian":
			slave.accent = naturalAccent;
			break;
		case "Russian":
			slave.accent = naturalAccent;
			break;
		case "Rwandan":
			slave.accent = (V.language === "Kinyarwanda") ? jsEither([0, 0, 0, 0, 0, 0, 1]) : naturalAccent;
			break;
		case "Sahrawi":
			slave.accent = (V.language === "Arabic") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Saint Lucian":
			slave.accent = (V.language === "English") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Salvadoran":
			slave.accent = (V.language === "Spanish") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Sammarinese":
			slave.accent = (V.language === "Italian") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Samoan":
			slave.accent = (V.language === "English") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "São Toméan":
			slave.accent = (V.language === "Portuguese") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Saudi":
			slave.accent = (V.language === "Arabic") ? jsEither([0, 0, 0, 0, 0, 0, 1]) : naturalAccent;
			break;
		case "Scottish":
			slave.accent = (V.language === "English") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Senegalese":
			slave.accent = (V.language === "French") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Serbian":
			if (V.language === "Bosnian") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "Serbian") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Seychellois":
			if (V.language === "French") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "English") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Sierra Leonean":
			slave.accent = (V.language === "English") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Singaporean":
			if (V.language === "English") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "Chinese") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "Malay") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "Tamil") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Slovak":
			slave.accent = (V.language === "Czech") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Slovene":
			slave.accent = naturalAccent;
			break;
		case "a Solomon Islander":
			slave.accent = (V.language === "English") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Somali":
			slave.accent = (V.language === "Arabic") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "South African":
			if (V.language === "English") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "Afrikaans") {
				slave.accent = jsEither([0, 0, 0, 0, 0, 0, 1]);
			} else if (V.language === "Dutch") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "South Sudanese":
			slave.accent = (V.language === "English") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Spanish":
			slave.accent = (V.language === "Catalan") ? jsEither([0, 1, 2, 2, 2, 3, 3]) : naturalAccent;
			break;
		case "Sri Lankan":
			if (V.language === "Sinhalese") {
				slave.accent = jsEither([0, 0, 0, 0, 0, 0, 1]);
			} else if (V.language === "Tamil") {
				slave.accent = jsEither([0, 0, 0, 0, 0, 0, 1]);
			} else if (V.language === "English") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Sudanese":
			slave.accent = (V.language === "Arabic") ? jsEither([0, 1, 2, 2, 2, 3, 3]) : naturalAccent;
			break;
		case "Surinamese":
			slave.accent = (V.language === "Dutch") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Swazi":
			if (V.language === "English") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "Afrikaans") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else if (V.language === "Tsonga") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else if (V.language === "Zulu") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Swedish":
			if (V.language === "English") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else if (V.language === "Finnish") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else if (V.language === "Norwegian") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else if (V.language === "German") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Swiss":
			if (V.language === "German") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "French") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "Italian") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Syrian":
			slave.accent = (V.language === "Arabic") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Taiwanese":
			slave.accent = (V.language === "Chinese") ? jsEither([0, 0, 0, 0, 0, 0, 1]) : naturalAccent;
			break;
		case "Tajik":
			slave.accent = (V.language === "Russian") ? jsEither([0, 1, 2, 2, 2, 3, 3]) : naturalAccent;
			break;
		case "Tanzanian":
			slave.accent = (V.language === "Swahili") ? jsEither([0, 0, 0, 0, 0, 0, 1]) : naturalAccent;
			break;
		case "Thai":
			if (V.language === "Chinese") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else if (V.language === "Malay") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else if (V.language === "Khmer") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Tibetan":
			slave.accent = (V.language === "Chinese") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Togolese":
			slave.accent = (V.language === "French") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Tongan":
			slave.accent = (V.language === "English") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Trinidadian":
			slave.accent = (V.language === "English") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Tunisian":
			slave.accent = (V.language === "Arabic") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Turkish":
			if (V.language === "Arabic") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else if (V.language === "Kurdish") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else if (V.language === "Zaza") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Turkmen":
			slave.accent = (V.language === "Russian") ? jsEither([0, 1, 2, 2, 2, 3, 3]) : naturalAccent;
			break;
		case "Tuvaluan":
			slave.accent = (V.language === "English") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Ugandan":
			slave.accent = (V.language === "Swahili") ? jsEither([0, 0, 0, 0, 0, 0, 1]) : naturalAccent;
			break;
		case "Ukrainian":
			slave.accent = (V.language === "Russian") ? jsEither([0, 1, 2, 2, 2, 3, 3]) : naturalAccent;
			break;
		case "Uruguayan":
			slave.accent = (V.language === "Spanish") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Uzbek":
			slave.accent = (V.language === "Russian") ? jsEither([0, 1, 2, 2, 2, 3, 3]) : naturalAccent;
			break;
		case "Vatican":
			if (V.language === "Italian") {
				slave.accent = jsEither([0, 0, 0, 0, 0, 0, 1]);
			} else if (V.language === "Latin") {
				slave.accent = jsEither([0, 0, 0, 0, 0, 0, 1]);
			} else if (V.language === "French") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else if (V.language === "German") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Venezuelan":
			slave.accent = (V.language === "Spanish") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Vietnamese":
			if (V.language === "Chinese") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else if (V.language === "English") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else if (V.language === "French") {
				slave.accent = jsEither([0, 1, 2, 2, 2, 3, 3]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Vincentian":
			slave.accent = (V.language === "English") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Yemeni":
			slave.accent = (V.language === "Arabic") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Zairian":
			if (V.language === "Lingala") {
				slave.accent = jsEither([0, 0, 0, 0, 0, 0, 1]);
			} else if (V.language === "Kikongo") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "Swahili") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "French") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Zambian":
			slave.accent = (V.language === "English") ? jsEither([0, 1, 1, 1, 1, 2]) : naturalAccent;
			break;
		case "Zimbabwean":
			if (V.language === "Shona") {
				slave.accent = jsEither([0, 0, 0, 0, 0, 0, 1]);
			} else if (V.language === "Ndebele") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "Chewa") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else if (V.language === "English") {
				slave.accent = jsEither([0, 1, 1, 1, 1, 2]);
			} else {
				slave.accent = naturalAccent;
			}
			break;
		case "Ancient Chinese Revivalist":
			slave.accent = (V.language === "Chinese") ? jsEither([0, 0, 0, 0, 0, 0, 1]) : naturalAccent;
			break;
		case "Ancient Egyptian Revivalist":
			slave.accent = (V.language === "Ancient Egyptian") ? jsEither([0, 0, 0, 0, 0, 0, 1]) : naturalAccent;
			break;
		case "Arabian Revivalist":
			slave.accent = (V.language === "Arabic") ? jsEither([0, 0, 0, 0, 0, 0, 1]) : naturalAccent;
			break;
		case "Aztec Revivalist":
			slave.accent = (V.language === "Nahuatl") ? jsEither([0, 0, 0, 0, 0, 0, 1]) : naturalAccent;
			break;
		case "Edo Revivalist":
			slave.accent = (V.language === "Japanese") ? jsEither([0, 0, 0, 0, 0, 0, 1]) : naturalAccent;
			break;
		case "Roman Revivalist":
			slave.accent = (V.language === "Latin") ? jsEither([0, 0, 0, 0, 0, 0, 1]) : naturalAccent;
			break;
		default:
			slave.accent = naturalAccent;
			break;
	}

	if (slave.nationality === V.language) {
		slave.accent = jsEither([0, 0, 0, 0, 0, 0, 1]);
	}
};

/*
Backup and then apply gingering modifiers to $activeSlave - no changes should be made to $activeSlave until after calling removeGingering() to restore the backup
Called from lawCompliance
*/
globalThis.checkForGingering = function() {
	let r = "";
	const {he, him, his, He, His} = getPronouns(V.activeSlave);

	/* reset in case gingered slaves were viewed but not purchased (no newSlaveIntro) */
	V.gingering = 0;
	V.gingeringDetected = 0;
	V.gingeringDetection = 0;
	V.toSearch = V.activeSlave.origin;
	if (V.applyLaw === 1 && V.policies.SMR.honestySMR === 1) {
		/* SMR prohibits gingering and is enforced for this slave - do nothing */
	} else if (V.activeSlave.indenture > 0) {
		/* indentured servants cannot be gingered */
	} else if (V.toSearch !== 0 && V.toSearch.includes("Shelter")) {
		/* is a Shelter slave - do nothing */
	} else {
		V.beforeGingering = clone(V.activeSlave);
		if (V.activeSlave.trust < -20 && jsRandom(1, 3) === 1) {
			V.gingering = "antidepressant";
			V.activeSlave.trust += jsRandom(10, 40);
		} else if (V.activeSlave.devotion < -20 && jsRandom(1, 3) === 1) {
			V.gingering = "depressant";
			V.activeSlave.devotion += jsRandom(10, 40);
		} else if (V.activeSlave.health.condition < 60 && jsRandom(1, 3) === 1) {
			V.gingering = "stimulant";
			improveCondition(V.activeSlave, jsRandom(20, 40));
		} else if (V.activeSlave.balls > 0 && V.activeSlave.dick > 2 && jsRandom(1, 3) === 1) {
			V.gingering = "vasoconstrictor";
			V.activeSlave.dick -= jsRandom(1, 2);
		} else if (V.activeSlave.balls > 0 && V.activeSlave.dick < 5 && jsRandom(1, 3) === 1) {
			V.gingering = "vasodilator";
			V.activeSlave.dick += jsRandom(1, 2);
		} else if (V.activeSlave.attrKnown === 0 && jsRandom(1, 3) === 1) {
			V.gingering = "aphrodisiac";
			V.activeSlave.aphrodisiacs = 2;
			V.activeSlave.attrKnown = 1;
			V.activeSlave.attrXX = jsRandom(60, 90);
			V.activeSlave.attrXY = jsRandom(60, 90);
			V.activeSlave.energy = jsRandom(50, 90);
		} else if (V.activeSlave.anus > 0 && V.activeSlave.fetishKnown === 0 && jsRandom(1, 3) === 1) {
			V.gingering = "ginger";
			V.activeSlave.fetish = "buttslut";
			V.activeSlave.fetishKnown = 1;
			V.activeSlave.fetishStrength = 65;
		}
	}
	if (V.gingering !== 0) {
		if (V.PC.skill.slaving >= 100) {
			V.gingeringDetected = 1;
			V.gingeringDetection = "slaver";
			switch (V.gingering) {
				case "antidepressant":
					r += `${He} is acting dazed and unfocused. ${He}'s obviously been given antidepressants to make ${him} appear less fearful, and will be considerably less trusting than ${he} seems.`;
					break;
				case "depressant":
					r += `${He} is acting languid and drugged. ${He}'s obviously been given a depressant to make ${him} appear less hateful, and will be considerably less accepting of slavery than ${he} seems.`;
					break;
				case "stimulant":
					r += `${He} is acting twitchy and hyperactive. ${He}'s obviously been given a stimulant to make ${him} seem healthier and more energetic, and is a lot less healthy than ${he} looks.`;
					break;
				case "vasoconstrictor":
					r += `${His} lips have the slightest blue tinge, making it obvious ${he}'s been given a vasoconstrictor. ${His} cock is a lot less of a girldick than it looks right now.`;
					break;
				case "vasodilator":
					r += `${He} has an impressive erection, but it seems quite unconnected to the rest of ${him}, as if ${he} doesn't know what to do with it. ${He}'s obviously been given a vasodilator to make ${his} dick seem a little less pathetic.`;
					break;
				case "aphrodisiac":
					r += `${His} pupils are slightly dilated, ${his} breath comes in fast pants, and ${his} skin is flushed. ${He}'s obviously been given as big of a dose of aphrodisiacs as ${he} can handle without a heart attack.`;
					break;
				default:
					r += `${He} is acting oddly, presenting ${his} ass in an awkward way and acting uncomfortable. ${He}'s obviously had an irritant shoved up ${his} butt to make ${him} act like an anal whore.`;
			}
			r += ` It's a trick you're very familiar with, given your <span class="springgreen">training as a slaver.</span>`;
		} else {
			/* not slaver */
			if (V.PC.skill.warfare >= 100 && jsRandom(1, 2) === 1) {
				V.gingeringDetected = 1;
				V.gingeringDetection = "mercenary";
			} else if (V.PC.rumor === "force" && jsRandom(1, 2) === 1) {
				V.gingeringDetected = 1;
				V.gingeringDetection = "force";
			} else if (jsRandom(1, 3) === 1) {
				V.gingeringDetected = 1;
			}
			if (V.gingeringDetected === 1) {
				switch (V.gingering) {
					case "antidepressant":
						r += `${He} is acting dazed and unfocused, like ${he}'s been given antidepressants to make ${him} appear less fearful. ${He} may be considerably less trusting than ${he} seems. `;
						break;
					case "depressant":
						r += `${He} is acting languid and drugged, like ${he}'s been given a depressant to make ${him} appear less hateful. ${He} may be considerably less accepting of slavery than ${he} seems. `;
						break;
					case "stimulant":
						r += `${He} is acting twitchy and hyperactive, like ${he}'s been given a stimulant to make ${him} seem healthier and more energetic. ${He} may be considerably less vital than ${he} seems. `;
						break;
					case "vasoconstrictor":
						r += `${His} lips have the slightest blue tinge, suggesting that ${he} may have been given a vasoconstrictor. If ${he} has, ${his} cock may be considerably less feminine and demure than it now seems. `;
						break;
					case "vasodilator":
						r += `${He} has an impressive erection, but it seems quite unconnected to the rest of ${him}, as if ${he} doesn't know what to do with it. ${He} may have been given a vasodilator. If ${he} has, ${his} cock may be considerably less impressive than it now seems. `;
						break;
					case "aphrodisiac":
						r += `${His} pupils are slightly dilated, ${his} breath comes in fast pants, and ${his} skin is flushed. These are the characteristic symptoms of a dose of aphrodisiacs limited only by a desire to avoid giving ${him} a heart attack. `;
						break;
					default:
						r += `${He} is acting oddly, presenting ${his} ass in an awkward way and acting uncomfortable. ${He} may be considerably less interested in anal sex than ${he} seems. `;
				}
				if (V.gingeringDetection === "mercenary") {
					r += `The nervous seller confirms this in response to a direct inquiry. Your intimidating reputation from your <span class="springgreen">extensive combat training</span> has its uses.`;
				} else if (V.gingeringDetection === "force") {
					r += `The nervous seller confirms this in response to a direct inquiry. Your reputation as a <span class="springgreen"> ${V.PCTitle ? "man" : "woman"} of blood</span> has its uses.`;
				}
			}
		} /* gingering detected */
	} /* gingering !== 0 */
	return r;
};

/*
Retrieve original $activeSlave without gingering modifiers
Call as removeGingering()
Called from newSlaveIntro, bulkSlaveGenerate
*/
globalThis.removeGingering = function() {
	if (V.gingering !== 0 && V.beforeGingering !== 0 && V.activeSlave !== 0 && V.beforeGingering.ID === V.activeSlave.ID) {
		/* extra checks to ensure gingering state is not left over from a different slave that was inspected but not purchased */
		V.activeSlave = V.beforeGingering;
		V.beforeGingering = 0;
	} else {
		/* clear left over state from a different slave without modifying activeSlave
		 */
		V.gingering = 0;
		V.beforeGingering = 0;
	}
};

/**
 * @param {App.Entity.SlaveState} slave*/
globalThis.randomizeAttraction = function(slave) {
	const sexuality = jsRandom(0, 100);
	let attraction = Math.clamp(slave.energy * 2, 60, 180);

	if (slave.balls > 0) {
		if (sexuality > 90) {
			slave.attrXY = Math.clamp(attraction, 0, 100);
			attraction -= slave.attrXY;
			slave.attrXX = Math.clamp(attraction, 0, 100);
		} else if (sexuality > 70) {
			slave.attrXY = Math.clamp(attraction + jsRandom(-5, 5), 0, 100);
			slave.attrXX = Math.clamp(attraction + jsRandom(-5, 5), 0, 100);
		} else {
			slave.attrXX = Math.clamp(attraction, 0, 100);
			attraction -= slave.attrXX;
			slave.attrXY = Math.clamp(attraction, 0, 100);
		}
	} else {
		if (sexuality > 90) {
			slave.attrXX = Math.clamp(attraction, 0, 100);
			attraction -= slave.attrXX;
			slave.attrXY = Math.clamp(attraction, 0, 100);
		} else if (sexuality > 60) {
			slave.attrXY = Math.clamp(attraction + jsRandom(-5, 5), 0, 100);
			slave.attrXX = Math.clamp(attraction + jsRandom(-5, 5), 0, 100);
		} else {
			slave.attrXY = Math.clamp(attraction, 0, 100);
			attraction -= slave.attrXY;
			slave.attrXX = Math.clamp(attraction, 0, 100);
		}
	}
	slave.attrXX = Math.clamp(slave.attrXX + jsRandom(-5, 5), 0, 100);
	slave.attrXY = Math.clamp(slave.attrXY + jsRandom(-5, 5), 0, 100);
};

globalThis.BaseSlave = function() {
	return new App.Entity.SlaveState();
};

/**
 * @param {App.Entity.SlaveState} slave*/
globalThis.generatePronouns = function(slave) {
	if (slave.fuckdoll > 0) {
		slave.pronoun = App.Data.Pronouns.Kind.toy;
	} else if (slave.dick > 0 && slave.vagina === -1 && V.diversePronouns === 1) {
		slave.pronoun = App.Data.Pronouns.Kind.male;
	} else {
		slave.pronoun = App.Data.Pronouns.Kind.female;
	}
};

/**
 * @param {App.Entity.SlaveState} slave
 */
globalThis.generatePuberty = function(slave) {
	if ((slave.ovaries === 1 || slave.mpreg === 1) && slave.physicalAge >= slave.pubertyAgeXX) {
		slave.pubertyXX = 1;
	} else {
		if (slave.preg >= -1) {
			slave.preg = 0;
		}
		slave.pubertyXX = 0;
	}
	if (slave.balls > 0 && slave.ballType !== "sterile" && slave.physicalAge >= slave.pubertyAgeXY) {
		slave.pubertyXY = 1;
	} else {
		slave.pubertyXY = 0;
	}
};

/**
 * Apply the effects of an age lift (make them appear younger than they do currently)
 * @param {App.Entity.SlaveState} slave
 */
globalThis.applyAgeImplant = function(slave) {
	if (slave.visualAge >= 25) {
		slave.ageImplant = 1;
		/* roughly: 25 -> 19, 35 -> 25, 50 -> 32, 80 -> 40, 130 -> 50 */
		slave.visualAge = Math.round(18.5 * Math.log(slave.visualAge) - 40);
	}
};

/**
 * Makes someone appear older than they do currently
 * @param {App.Entity.SlaveState} slave
 */
globalThis.applyAgeImplantOlder = function(slave) {
	if (slave.visualAge < 80) {
		// doesn't currently set ageImplant
		/* roughly: 5 -> 20, 35 -> 45, 50 -> 56, 60 -> 64, 79 -> 80 */
		slave.visualAge = Math.round(0.8 * slave.visualAge + 16);
	}
};

repo_name: KnightBoulegard/fc

license: mit


code:
/**
 * @file Functions for rendering lists of slave summaries for various purposes. This includes
 * lists for the penthouse/facilities, selecting a slaves, facility leaders.
 *
 * For documentation see devNotes/slaveListing.md
 */

App.UI.SlaveList = {};

/**
 * @callback slaveTextGenerator
 * @param {App.Entity.SlaveState} slave
 * @returns {string}
 */

/**
 * @callback slaveToElement
 * @param {App.Entity.SlaveState} slave
 * @returns {HTMLElement|DocumentFragment}
 */

App.UI.SlaveList.render = function() {
	const facilityPassages = new Set(
		["Main", "Head Girl Suite", "Spa", "Brothel", "Club", "Arcade", "Clinic", "Schoolroom", "Dairy", "Farmyard", "Servants' Quarters", "Master Suite", "Cellblock"]);

	/** @type {string} */
	let passageName;

	// potentially can be a problem if played long enough to reach Number.MAX_SAFE_INTEGER
	let listID = Number.MIN_SAFE_INTEGER;

	/** @type {Object.<number, Node>}*/
	const readyResults = {

	};

	/** @type {App.Art.SlaveArtBatch?} */
	let batchRenderer = null;

	return {
		listMarkup: listMarkup,
		listDOM: listDOM
	};

	/**
	 * @param {number[]} IDs
	 * @param {Array.<{id: number, rejects: string[]}>} rejectedSlaves
	 * @param {slaveToElement} interactionLink
	 * @param {slaveToElement} [postNote]
	 * @returns {DocumentFragment}
	 */
	function listDOM(IDs, rejectedSlaves, interactionLink, postNote) {
		passageName = passage();

		let res = document.createDocumentFragment();

		if ((V.seeImages === 1) && (V.seeSummaryImages === 1)) {
			batchRenderer = new App.Art.SlaveArtBatch(IDs, 1);
			res.appendChild(batchRenderer.writePreamble());
		}

		if (V.useSlaveListInPageJSNavigation === 1) {
			res.appendChild(createQuickList(IDs));
		}

		const fcs = App.Entity.facilities;
		const penthouse = fcs.penthouse;

		let anyFacilityExists = false;

		for (const f of Object.values(fcs)) {
			if (f !== penthouse && f.established) {
				anyFacilityExists = true;
				break;
			}
		}

		let showTransfers = false;
		if (anyFacilityExists) {
			if (facilityPassages.has(passageName)) {
				V.returnTo = passageName;
				showTransfers = true;
			}
		}

		for (const _sid of IDs) {
			let ss = renderSlave(_sid, interactionLink, showTransfers, postNote);
			let slaveDiv = document.createElement("div");
			slaveDiv.id = `slave-${_sid}`;
			slaveDiv.classList.add("slaveSummary");
			if (V.slavePanelStyle === 2) {
				slaveDiv.classList.add("card");
			}
			slaveDiv.appendChild(ss);
			res.appendChild(slaveDiv);
		}

		for (const rs of rejectedSlaves) {
			const slave = slaveStateById(rs.id);
			const rejects = rs.rejects;
			const slaveName = SlaveFullName(slave);
			let slaveDiv = document.createElement("div");
			slaveDiv.id = `slave-${slave.ID}`;
			slaveDiv.style.setProperty("clear", "both");
			if (rejects.length === 1) {
				slaveDiv.innerHTML = rejects[0];
			} else {
				slaveDiv.appendChild(document.createTextNode(`${slaveName}: `));
				let ul = document.createElement("ul");
				for (const rr of rejects) {
					const li = document.createElement("li");
					li.innerHTML = rr;
					ul.appendChild(li);
				}
				slaveDiv.appendChild(ul);
			}
			res.appendChild(slaveDiv);
		}

		batchRenderer = null; // release reference

		return res;
	}

	/**
	 * @param {number[]} IDs
	 * @param {Array.<{id: number, rejects: string[]}>} rejectedSlaves
	 * @param {slaveToElement} interactionLink
	 * @param {slaveToElement} [postNote]
	 * @returns {string}
	 */
	function listMarkup(IDs, rejectedSlaves, interactionLink, postNote) {
		const listIDStr = `slave-list-${listID}`;
		readyResults[listID] = listDOM(IDs, rejectedSlaves, interactionLink, postNote);

		$(document).one(':passagedisplay', function() {
			for (const e of document.querySelectorAll('[id^=slave-list]')) {
				const id = e.getAttribute("data-listId");
				if (readyResults.hasOwnProperty(id)) {
					if (e.childNodes.length > 0) {
						e.innerHTML = '';
					}
					e.appendChild(readyResults[id]);
					delete readyResults[id];
				}
			}
		});

		let resMarkup = `<div id="${listIDStr}" data-listId="${listID}"></div>`;
		++listID;

		return resMarkup;
	}

	/**
	 * @param {number} id
	 * @param {slaveToElement} interactionLink
	 * @param {boolean} showTransfers
	 * @param {slaveToElement} [postNote]
	 * @returns {DocumentFragment}
	 */
	function renderSlave(id, interactionLink, showTransfers, postNote) {
		let res = document.createDocumentFragment();
		if (V.slavePanelStyle === 0) {
			res.appendChild(document.createElement("br"));
		} else if (V.slavePanelStyle === 1) {
			const hr = document.createElement("hr");
			hr.style.margin = "0";
			res.appendChild(hr);
		}
		const slave = slaveStateById(id);

		if (batchRenderer) {
			let imgDiv = document.createElement("div");
			imgDiv.classList.add("imageRef");
			imgDiv.classList.add("smlImg");
			imgDiv.appendChild(batchRenderer.render(slave));
			res.appendChild(imgDiv);
		}
		// res.push(dividerAndImage(slave));
		res.appendChild(interactionLink(slave));

		if ((slave.choosesOwnClothes === 1) && (slave.clothes === "choosing her own clothes")) {
			const _oldDevotion = slave.devotion;
			App.SlaveAssignment.choosesOwnClothes(slave);
			slave.devotion = _oldDevotion; /* restore devotion value so repeatedly changing clothes isn't an exploit */
		}

		SlaveStatClamp(slave);
		slave.trust = Math.trunc(slave.trust);
		slave.devotion = Math.trunc(slave.devotion);
		slave.health.condition = Math.trunc(slave.health.condition);
		slave.health.shortDamage = Math.trunc(slave.health.shortDamage);
		slave.health.longDamage = Math.trunc(slave.health.longDamage);
		slave.health.illness = Math.trunc(slave.health.illness);
		slave.health.tired = Math.trunc(slave.health.tired);
		slave.health.health = Math.trunc(slave.health.health);
		res.appendChild(document.createTextNode(' will '));
		const assignment = document.createElement("span");
		if ((slave.assignment === Job.REST) && (slave.health.condition >= -20)) {
			assignment.className = "freeAssignment";
			assignment.innerText = slave.assignment;
		} else if ((slave.assignment === Job.CONFINEMENT) && ((slave.devotion > 20) || ((slave.trust < -20) && (slave.devotion >= -20)) || ((slave.trust < -50) && (slave.devotion >= -50)))) {
			assignment.innerText = slave.assignment;
			if (slave.sentence > 0) {
				assignment.innerText += ` (${slave.sentence} weeks)`;
			}
		} else if (slave.choosesOwnAssignment === 1) {
			assignment.innerText = `choose ${getPronouns(slave).possessive} own job`;
		} else {
			assignment.innerText = slave.assignment;
			if (slave.sentence > 0) {
				assignment.innerText += ` ${slave.sentence} weeks`;
			}
		}
		if (slave.assignment === Job.CLINIC) {
			let list = [];
			let i;
			if (slave.health.condition <= 40) {
				list.push(`poor health`);
			}
			if (slave.health.shortDamage >= 10) {
				list.push(`injuries`);
			}
			if (V.Nurse !== 0) {
				if ((slave.chem > 15) && (V.clinicUpgradeFilters === 1)) {
					list.push(`unhealthy chemicals`);
				}
				if ((V.clinicInflateBelly > 0) && (slave.bellyImplant >= 0) && (slave.bellyImplant <= (V.arcologies[0].FSTransformationFetishistResearch ? 800000 : 130000))) {
					list.push(`implant filling`);
				}
				if ((slave.pregKnown === 1) && (V.clinicSpeedGestation > 0 || slave.pregControl === "speed up") && ((slave.pregAdaptation*1000 < slave.bellyPreg || slave.preg > slave.pregData.normalBirth/1.33))) {
					list.push(`observation of accelerated pregnancy`);
				} else if ((slave.pregKnown === 1) && (V.clinicSpeedGestation > 0 || slave.pregControl === "speed up")) {
					list.push(`safely hurrying pregnancy along`);
				} else if ((slave.pregAdaptation*1000 < slave.bellyPreg || slave.preg > slave.pregData.normalBirth/1.33)) {
					list.push(`observation during pregnancy`);
				}
			}
			if (list.length > 0) {
				assignment.innerText += " for ";
			} else {
				assignment.innerText += ", preparing to check out";
			}
			for (i = 0; i < list.length; i++) {
				assignment.innerText += list[i];
				if (i === (list.length - 2)) {
					assignment.innerText += " and ";
				} else if (i < (list.length - 2)) {
					assignment.innerText += ", ";
				}
			}
		} else if (slave.assignment === Job.SPA) {
			let list = [];
			let i;
			if (slave.fetish === "mindbroken") {
				assignment.innerText += `, mindbroken`;
			} else {
				if ((slave.sexualFlaw !== "none") || (slave.behavioralFlaw !== "none")) {
					list.push(`overcoming flaws`);
				}
				if ((slave.trust < 60) || (slave.devotion < 60)) {
					list.push(`learning to accept life as a slave`);
				}
				if (slave.health.condition < 20) {
					list.push(`improving in health`);
				}
				if (list.length > 0) {
					assignment.innerText += ", ";
				} else {
					assignment.innerText += ", preparing to move out";
				}
				for (i = 0; i < list.length; i++) {
					assignment.innerText += list[i];
					if (i === (list.length - 2)) {
						assignment.innerText += " and ";
					} else if (i < (list.length - 2)) {
						assignment.innerText += ", ";
					}
				}
			}
		} else if (slave.assignment === Job.SCHOOL) {
			let lessons = [];
			let i;
			if ((V.schoolroomRemodelBimbo === 1 && slave.intelligenceImplant > -15) || (V.schoolroomRemodelBimbo === 0 && slave.intelligenceImplant < 30)) {
				lessons.push("general");
			}
			if (!((slave.voice === 0) || (slave.accent <= 1) || ((V.schoolroomUpgradeLanguage === 0) && (slave.accent <= 2)))) {
				lessons.push("speech");
			}
			if (!((slave.skill.oral > 30) || ((V.schoolroomUpgradeSkills === 0) && (slave.skill.oral > 10)))) {
				lessons.push("oral");
			}
			if (!((slave.skill.whoring > 30) || ((V.schoolroomUpgradeSkills === 0) && (slave.skill.whoring > 10)))) {
				lessons.push("whoring");
			}
			if (!((slave.skill.entertainment > 30) || ((V.schoolroomUpgradeSkills === 0) && (slave.skill.entertainment > 10)))) {
				lessons.push("entertainment");
			}
			if (!((slave.skill.anal > 30) || ((V.schoolroomUpgradeSkills === 0) && (slave.skill.anal > 10)))) {
				lessons.push("anal");
			}
			if (!((slave.skill.vaginal > 30) || ((V.schoolroomUpgradeSkills === 0) && (slave.skill.vaginal > 10)) || (slave.vagina < 0))) {
				lessons.push("vaginal");
			}
			if (lessons.length > 0) {
				assignment.innerText += ", practicing: ";
			} else {
				assignment.innerText += ", studying for finals";
			}
			for (i = 0; i < lessons.length; i++) {
				assignment.innerText += lessons[i];
				if (i === (lessons.length - 2)) {
					assignment.innerText += " and ";
				} else if (i < (lessons.length - 2)) {
					assignment.innerText += ", ";
				}
			}
		} else if (slave.assignment === Job.SUBORDINATE) {
			if (slave.subTarget === -1) {
				assignment.innerText += ", serving as your Stud";
			} else if (slave.subTarget !== 0) {
				const domSlave = getSlave(slave.subTarget);
				if (domSlave) {
					assignment.innerText += ", serving " + SlaveFullName(domSlave) + " exclusively";
				} else {
					slave.subTarget = 0;
				}
			}
		} else if (slave.assignment === Job.AGENT) {
			const arc = V.arcologies.find((a) => a.leaderID === slave.ID);
			if (arc) {
				assignment.innerText += `, leading `;
				if (passageName === "Neighbor Interact") {
					assignment.appendChild(App.UI.DOM.makeElement("span", arc.name, "name"));
				} else {
					assignment.appendChild(App.UI.DOM.passageLink(arc.name, "Neighbor Interact"));
				}
			}
		} else if (slave.assignment === Job.AGENTPARTNER) {
			const arc = V.arcologies.find((a) => a.leaderID === slave.relationshipTarget);
			if (arc) {
				assignment.innerText += ` in `;
				if (passageName === "Neighbor Interact") {
					assignment.appendChild(App.UI.DOM.makeElement("span", arc.name, "name"));
				} else {
					assignment.appendChild(App.UI.DOM.passageLink(arc.name, "Neighbor Interact"));
				}
			}
		}
		assignment.appendChild(document.createTextNode('.'));
		res.appendChild(assignment);
		if (V.assignmentRecords[slave.ID]) {
			res.appendChild(document.createTextNode(` Last assigned to ${V.assignmentRecords[slave.ID]}.`));
		}
		res.appendChild(document.createTextNode(' '));

		if ((V.displayAssignments === 1) && (passageName === "Main") && (slave.ID !== V.HeadGirl.ID) && (slave.ID !== V.Recruiter.ID) && (slave.ID !== V.Bodyguard.ID)) {
			res.appendChild(App.UI.jobLinks.assignmentsFragment(slave.ID, "Main", (slave, assignment) => { App.UI.SlaveList.ScrollPosition.record(); assignJob(slave, assignment); }));
		}
		if (showTransfers) {
			res.appendChild(document.createElement("br"));
			res.appendChild(document.createTextNode('Transfer to: '));
			res.appendChild(App.UI.jobLinks.transfersFragment(slave.ID, (slave, assignment) => { App.UI.SlaveList.ScrollPosition.record(); assignJob(slave, assignment); }));
		}

		res.appendChild(App.UI.SlaveSummary.render(slave));

		if (postNote) {
			const pn = postNote(slave);
			if (pn) {
				let r = document.createElement("p");
				r.classList.add("si");
				r.appendChild(pn);
				res.appendChild(r);
			}
		}

		return res;
	}

	/**
	 * @param {number[]} IDs
	 * @returns {DocumentFragment}
	 */
	function createQuickList(IDs) {
		/**
		 *
		 * @param {Node} container
		 * @param {string} tagName
		 * @param {string} [content]
		 * @param {string|string[]} [classNames]
		 * @param {Object.<string, any>} [attributes]
		 * @returns {HTMLElement}
		 */
		function makeElement(container, tagName, content, classNames, attributes) {
			let res = document.createElement(tagName);
			container.appendChild(res);
			if (content) {
				res.textContent = content;
			}
			if (Array.isArray(classNames)) {
				for (const c of classNames) {
					res.classList.add(c);
				}
			} else if (classNames !== undefined) {
				res.classList.add(classNames);
			}

			if (attributes) {
				for (const [k, v] of Object.entries(attributes)) {
					res.setAttribute(k, v);
				}
			}
			return res;
		}

		const res = document.createDocumentFragment();

		/* Useful for finding weird combinations — usages of this passage that don't yet generate the quick index.
		*	<<print 'pass/count/indexed/flag::[' + passageName + '/' + _Count + '/' + _indexed + '/' + $SlaveSummaryFiler + ']'>>
		*/

		if (IDs.length > 1 && passageName === "Main") {
			const _buttons = [];
			let _offset = -50;
			if (/Select/i.test(passageName)) {
				_offset = -25;
			}
			res.appendChild(document.createElement("br"));
			/*
			 * we want <button data-quick-index="<<= listID>>">...
			 */
			const quickIndexBtn = document.createElement("button");
			res.appendChild(quickIndexBtn);
			quickIndexBtn.id = `quick-list-toggle${listID}`;
			quickIndexBtn.setAttribute('data-quick-index', listID.toString());
			quickIndexBtn.onclick = function(ev) {
				let which = ev.target.attributes["data-quick-index"].value;
				let quick = $("div#list_index" + which);
				quick.toggleClass("hidden");
			};
			/*
			 * we want <div id="list_index3" class=" hidden">...
			 */
			const listIndex = makeElement(res, "div", undefined, "hidden");
			listIndex.id = `list_index${listID}`;

			for (const sID of IDs) {
				const _IndexSlave = slaveStateById(sID);
				const _indexSlaveName = SlaveFullName(_IndexSlave);
				const _devotionClass = getSlaveDevotionClass(_IndexSlave);
				const _trustClass = getSlaveTrustClass(_IndexSlave);
				_buttons.push({
					"data-name": _indexSlaveName,
					"data-scroll-to": `#slave-${_IndexSlave.ID}`,
					"data-scroll-offset": _offset,
					"data-devotion": _IndexSlave.devotion,
					"data-trust": _IndexSlave.trust,
					"class": `${_devotionClass} ${_trustClass}`
				});
			}
			if (_buttons.length > 0) {
				V.sortQuickList = V.sortQuickList || 'Devotion';
				makeElement(listIndex, "em", "Sorting: ");
				const qlSort = makeElement(listIndex, "span", V.sortQuickList, "strong");
				qlSort.id = "qlSort";
				listIndex.appendChild(document.createTextNode(". "));
				const linkSortByDevotion = makeElement(listIndex, "a", "Sort by Devotion");
				linkSortByDevotion.onclick = (ev) => {
					ev.preventDefault();
					V.sortQuickList = "Devotion";
					$("#qlSort").text(V.sortQuickList);
					$("#qlWrapper").removeClass("trust").addClass("devotion");
					sortButtonsByDevotion();
				};
				const linkSortByTrust = makeElement(listIndex, "a", "Sort by Trust");
				linkSortByTrust.onclick = (ev) => {
					ev.preventDefault();
					V.sortQuickList = "Trust";
					$("#qlSort").text(V.sortQuickList);
					$("#qlWrapper").removeClass("devotion").addClass("trust");
					sortButtonsByTrust();
				};
				makeElement(listIndex, "br");
				const qlWrapper = makeElement(listIndex, "div", undefined, ["quicklist", "devotion"]);
				qlWrapper.id = "qlWrapper";
				for (const _button of _buttons) {
					const btn = makeElement(listIndex, 'button', _button['data-name'], undefined, _button);
					btn.onclick = App.UI.quickBtnScrollToHandler;
				}
			}
		}
		return res;
	}
}();

App.UI.SlaveList.Decoration = {};
/**
 * returns "HG", "BG", "PA", and "RC" prefixes
 * @param {App.Entity.SlaveState} slave
 * @returns {HTMLElement}
 */
App.UI.SlaveList.Decoration.penthousePositions = function(slave) {
	const fcs = App.Entity.facilities;
	if (fcs.headGirlSuite.manager.isEmployed(slave)) {
		return App.UI.DOM.makeElement("span", 'HG', ['lightcoral', 'strong']);
	}
	if (fcs.penthouse.manager.isEmployed(slave)) {
		return App.UI.DOM.makeElement("span", 'RC', ['lightcoral', 'strong']);
	}
	if (fcs.armory.manager.isEmployed(slave)) {
		return App.UI.DOM.makeElement("span", 'BG', ['lightcoral', 'strong']);
	}
	if (Array.isArray(V.personalAttention) && V.personalAttention.some(s => s.ID === slave.ID)) {
		return App.UI.DOM.makeElement("span", 'PA', ['lightcoral', 'strong']);
	}
	return null;
};

App.UI.SlaveList.ScrollPosition = (function() {
	let lastPassage = null;
	let position = 0;

	return {
		reset : function() {
			lastPassage = null;
			position = 0;
		},

		record : function() {
			lastPassage = passage();
			position = window.pageYOffset;
		},

		restore : function() {
			$(document).one(':passageend', () => {
				if (lastPassage === passage()) {
					window.scrollTo(0, position);
				}
				this.reset();
			});
		}
	};
})();

App.UI.SlaveList.SlaveInteract = {};

/**
 * @param {App.Entity.SlaveState} slave
 * @returns {HTMLElement}
 */
App.UI.SlaveList.SlaveInteract.stdInteract = function(slave) {
	return App.UI.DOM.passageLink(SlaveFullName(slave), 'Slave Interact', () => {
		App.UI.SlaveList.ScrollPosition.record();
		V.activeSlave = slave;
	});
};

/**
 * @param {App.Entity.SlaveState} slave
 * @returns {DocumentFragment|HTMLElement}
 */
App.UI.SlaveList.SlaveInteract.penthouseInteract = function(slave) {
	let decoration = App.UI.SlaveList.Decoration.penthousePositions(slave);
	let stdLink = App.UI.SlaveList.SlaveInteract.stdInteract(slave);
	if (decoration) {
		let fr = document.createDocumentFragment();
		fr.appendChild(decoration);
		fr.appendChild(document.createTextNode(' '));
		fr.appendChild(stdLink);
		return fr;
	}
	return stdLink;
};

/**
 * @param {App.Entity.SlaveState} slave
 * @returns {HTMLElement}
 */
App.UI.SlaveList.SlaveInteract.personalAttention = (slave) =>
	App.UI.DOM.passageLink(SlaveFullName(slave), undefined,
		() => { App.UI.selectSlaveForPersonalAttention(slave.ID); });

/**
 * Adds/removes a slave with the given id to/from the personal attention array
 * @param {number} id slave id
 */
App.UI.selectSlaveForPersonalAttention = function(id) {
	if (!Array.isArray(V.personalAttention)) {
		/* first PA target */
		V.personalAttention = [{
			ID: id,
			trainingRegimen: "undecided"
		}];
	} else {
		const _pai = V.personalAttention.findIndex(s => s.ID === id);
		if (_pai === -1) {
			/* not already a PA target; add */
			V.activeSlave = getSlave(id);
			V.personalAttention.push({
				ID: id,
				trainingRegimen: "undecided"
			});
		} else {
			/* already a PA target; remove */
			V.personalAttention.deleteAt(_pai);
			if (V.personalAttention.length === 0) {
				V.personalAttention = "sex";
			}
		}
	}
	SugarCube.Engine.play("Personal Attention Select");
};

/**
 * Generates fragment with sorting options, that link to the given passage
 * @param {string} passage The passage to link to
 * @returns {string}
 */
App.UI.SlaveList.sortingLinks = function(passage) {
	let r = '    Sort by: ';
	let textify = (cc) => capFirstChar(cc.replace(/([A-Z])/g, " $1"));
	r += ["devotion", "name", "assignment", "seniority", "actualAge", "visualAge", "physicalAge", "weeklyIncome"]
		.map(so => V.sortSlavesBy !== so ?
			App.UI.passageLink(textify(so), passage, `$sortSlavesBy = "${so}"`) : textify(so))
		.join(" | ");

	r += '    Sort: ';
	r += ["descending", "ascending"].map(so => V.sortSlavesOrder !== so ?
		App.UI.passageLink(capFirstChar(so), passage, `$sortSlavesOrder = "${so}"`) : capFirstChar(so))
		.join(" | ");
	return r;
};

/**
 * @param {string} passage
 * @returns {*}
 */
App.UI.SlaveList.sortingLinksDOM = function(passage) {
	const outerDiv = document.createElement("div");
	outerDiv.classList.add("flex-container");
	const textify = string => capFirstChar(string.replace(/([A-Z])/g, " $1"));

	let innerDiv = App.UI.DOM.makeElement("div", "Sort by: ", "indent");
	let order = ["devotion", "name", "assignment", "seniority", "actualAge", "visualAge", "physicalAge", "weeklyIncome"]
		.map(so => V.sortSlavesBy !== so ?
			App.UI.DOM.passageLink(textify(so), passage, () => { V.sortSlavesBy = so; }) : textify(so));
	innerDiv.append(App.UI.DOM.arrayToList(order, " | ", " | "));
	outerDiv.append(innerDiv);

	innerDiv = App.UI.DOM.makeElement("div", "Sort: ", "indent");
	order = ["descending", "ascending"].map(so => V.sortSlavesOrder !== so ?
		App.UI.DOM.passageLink(textify(so), passage, () => { V.sortSlavesOrder = so; }) : textify(so));
	innerDiv.append(App.UI.DOM.arrayToList(order, " | ", " | "));
	outerDiv.append(innerDiv);

	return outerDiv;
};

/**
 * Standard tabs for a facility with a single job (SJ)
 * @param {App.Entity.Facilities.Facility} facility
 * @param {string} [facilityPassage]
 * @param {boolean} [showTransfersTab=false]
 * @param {{assign: string, remove: string, transfer: (string| undefined)}} [tabCaptions]
 * @returns {string}
 */
App.UI.SlaveList.listSJFacilitySlaves = function(facility, facilityPassage, showTransfersTab = false, tabCaptions = undefined) {
	const job = facility.job();

	facilityPassage = facilityPassage || passage();
	tabCaptions = tabCaptions || {
		assign: 'Assign a slave',
		remove: 'Remove a slave',
		transfer: 'Transfer from Facility'
	};
	let r = '';
	if (V.sortSlavesMain) {
		r += this.sortingLinks(facilityPassage) + '<br>';
	}
	r += '<div class="tabbar">' +
		App.UI.tabbar.tabButton('assign', tabCaptions.assign) +
		App.UI.tabbar.tabButton('remove', tabCaptions.remove) +
		(showTransfersTab ? App.UI.tabbar.tabButton('transfer', tabCaptions.transfer) : '') +
		'</div>';

	const facilitySlaves = [...job.employeesIDs()];
	if (facilitySlaves.length > 0) {
		SlaveSort.IDs(facilitySlaves);
		r += App.UI.tabbar.makeTab("remove", App.UI.SlaveList.render.listMarkup(facilitySlaves, [],
			App.UI.SlaveList.SlaveInteract.stdInteract,
			(slave) => App.UI.DOM.link(`Retrieve ${getPronouns(slave).object} from ${facility.name}`, () => removeJob(slave, job.desc.assignment), [], facilityPassage)
		));
	} else {
		r += App.UI.tabbar.makeTab("remove", `<em>${capFirstChar(facility.name)} is empty for the moment</em>`);
	}

	/**
	 * @param {number[]} slaveIDs
	 * @returns {string}
	 */
	function assignableTabContent(slaveIDs) {
		SlaveSort.IDs(slaveIDs);
		let rejectedSlaves = [];
		let passedSlaves = [];
		slaveIDs.forEach((id) => {
			const rejects = facility.canHostSlave(slaveStateById(id));
			if (rejects.length > 0) {
				rejectedSlaves.push({id: id, rejects: rejects});
			} else {
				passedSlaves.push(id);
			}
		}, []);
		return App.UI.SlaveList.render.listMarkup(passedSlaves, rejectedSlaves,
			App.UI.SlaveList.SlaveInteract.stdInteract,
			(slave) => App.UI.DOM.link(`Send ${getPronouns(slave).object} to ${facility.name}`, () => { assignmentTransition(slave, job.desc.assignment, facilityPassage); }));
	}
	if (facility.hasFreeSpace) {
		const assignableSlaveIDs = job.desc.partTime ?
			V.slaves.map(slave => slave.ID) : // all slaves can work here
			[...App.Entity.facilities.penthouse.employeesIDs()]; // only slaves from the penthouse can be transferred here
		r += App.UI.tabbar.makeTab("assign", assignableTabContent(assignableSlaveIDs));
	} else {
		r += App.UI.tabbar.makeTab("assign", `<strong>${capFirstChar(facility.name)} is full and cannot hold any more slaves</strong>`);
	}

	if (showTransfersTab) {
		if (facility.hasFreeSpace) {
			// slaves from other facilities can be transferred here
			const transferableIDs = V.slaves.reduce((acc, slave) => {
				if (!assignmentVisible(slave) && !facility.isHosted(slave)) {
					acc.push(slave.ID);
				}
				return acc;
			}, []);
			r += App.UI.tabbar.makeTab("transfer", assignableTabContent(transferableIDs));
		} else {
			r += App.UI.tabbar.makeTab("transfer", `<strong>${capFirstChar(facility.name)} is full and cannot hold any more slaves</strong>`);
		}
	}
	App.UI.tabbar.handlePreSelectedTab();

	return r;
};

/**
 * @param {string[]} classNames
 */
App.UI.SlaveList.makeNameDecorator = function(classNames) {
	return (slave) => {
		const r = document.createElement("span");
		for (const c of classNames) {
			r.classList.add(c);
		}
		r.textContent = SlaveFullName(slave);
		return r;
	};
};

/**
 * @returns {string}
 */
App.UI.SlaveList.listNGPSlaves = function() {
	const thisPassage = 'New Game Plus';
	let r = this.sortingLinks(thisPassage) + '<br>';

	r += '<div class="tabbar">' +
		App.UI.tabbar.tabButton('assign', 'Import a slave') +
		App.UI.tabbar.tabButton('remove', 'Remove from import') +
		'</div>';

	const NGPassignment = Job.IMPORTED;
	/** @type {App.Entity.SlaveState[]} */
	const slaves = V.slaves;

	/* handle the legacy assignment string */
	for (const slave of slaves) {
		if (slave.assignment === "be imported") {
			slave.assignment = Job.IMPORTED;
		}
	}

	if (V.slavesToImport > 0) {
		const importedSlavesIDs = slaves.reduce((acc, s) => {
			if (s.assignment === NGPassignment) {
				acc.push(s.ID);
			}
			return acc;
		}, []);
		SlaveSort.IDs(importedSlavesIDs);
		r += App.UI.tabbar.makeTab("remove", App.UI.SlaveList.render.listMarkup(importedSlavesIDs, [],
			App.UI.SlaveList.makeNameDecorator(["emphasizedSlave", "pink"]),
			(s) => App.UI.DOM.passageLink('Remove from import list', thisPassage,
				() => { V.slavesToImport -= 1; removeJob(s, NGPassignment); }
			)));
	} else {
		r += App.UI.tabbar.makeTab("remove", `<em>No slaves will go with you to the new game</em>`);
	}

	if (V.slavesToImport < V.slavesToImportMax) {
		const slavesToImportIDs = slaves.reduce((acc, s) => { if (s.assignment !== NGPassignment) { acc.push(s.ID); } return acc; }, []);
		SlaveSort.IDs(slavesToImportIDs);
		r += App.UI.tabbar.makeTab("assign", App.UI.SlaveList.render.listMarkup(slavesToImportIDs, [],
			App.UI.SlaveList.makeNameDecorator(["emphasizedSlave", "pink"]),
			(s) => App.UI.DOM.passageLink('Add to import list', thisPassage,
				() => { V.slavesToImport += 1; assignJob(s, NGPassignment); }
			)));
	} else {
		r += App.UI.tabbar.makeTab("assign", `<strong>Slave import limit reached</strong>`);
	}

	App.UI.tabbar.handlePreSelectedTab();
	return r;
};

/**
 * Renders facility manager summary or a note with a link to select one
 * @param {App.Entity.Facilities.Facility} facility
 * @param {string} [selectionPassage] passage name for manager selection. "${Manager} Select" if omitted
 * @returns {string}
 */
App.UI.SlaveList.displayManager = function(facility, selectionPassage) {
	const managerCapName = capFirstChar(facility.desc.manager.position);
	selectionPassage = selectionPassage || `${managerCapName} Select`;
	const manager = facility.manager.currentEmployee;
	if (manager) {
		return this.render.listMarkup([manager.ID], [],
			App.UI.SlaveList.SlaveInteract.stdInteract,
			() => App.UI.DOM.passageLink(`Change or remove ${managerCapName}`, selectionPassage));
	} else {
		return `You do not have a slave serving as a ${managerCapName}. ${App.UI.passageLink(`Appoint one`, selectionPassage, "")}`;
	}
};

/**
 * Displays standard facility page with manager and list of workers
 * @param {App.Entity.Facilities.Facility} facility
 * @param {boolean} [showTransfersPage]
 * @returns {string}
 */
App.UI.SlaveList.stdFacilityPage = function(facility, showTransfersPage) {
	return this.displayManager(facility) + '<br><br>' + this.listSJFacilitySlaves(facility, passage(), showTransfersPage);
};

App.UI.SlaveList.penthousePage = function() {
	const ph = App.Entity.facilities.penthouse;

	function overviewTabContent() {
		const fragment = document.createDocumentFragment();
		const A = V.arcologies[0];

		let slaveWrapper = document.createElement("div"); // first is a div so we have no space between slave and buttons
		if (V.HeadGirl) {
			/** @type {App.Entity.SlaveState} */
			const HG = V.HeadGirl;
			slaveWrapper.append(App.UI.DOM.makeElement("span", SlaveFullName(HG), "slave-name"),
				" is serving as your Head Girl");
			if (A.FSEgyptianRevivalistLaw === 1) {
				slaveWrapper.append(" and Consort");
			}
			slaveWrapper.append(". ");
			const link = App.UI.DOM.makeElement("span", App.UI.DOM.passageLink("Manage Head Girl", "HG Select"), "major-link");
			link.id = "manageHG";
			slaveWrapper.append(link, " ", App.UI.DOM.makeElement("span", "[H]", "hotkey"));
			slaveWrapper.append(App.UI.SlaveList.render.listDOM([HG.ID], [],
				App.UI.SlaveList.SlaveInteract.penthouseInteract));
		} else {
			if (V.slaves.length > 1) {
				slaveWrapper.append("You have ", App.UI.DOM.makeElement("span", "not", "warning"), " selected a Head Girl");
				if (A.FSEgyptianRevivalistLaw === 1) {
					slaveWrapper.append(" and Consort");
				}
				slaveWrapper.append(". ", App.UI.DOM.makeElement("span", App.UI.DOM.passageLink("Select One", "HG Select"), "major-link"),
					" ", App.UI.DOM.makeElement("span", "[H]", "hotkey"));
				slaveWrapper.id = "manageHG";
				if (V.slavePanelStyle === 2) {
					slaveWrapper.classList.add("slaveSummary", "card");
				}
			} else {
				slaveWrapper.append("You do not have enough slaves to keep a Head Girl");
				slaveWrapper.classList.add("note");
			}
		}
		fragment.append(slaveWrapper);

		slaveWrapper = document.createElement("p");
		if (V.Recruiter) {
			/** @type {App.Entity.SlaveState} */
			const RC = V.Recruiter;
			const {he} = getPronouns(RC);
			slaveWrapper.append(App.UI.DOM.makeElement("span", SlaveFullName(RC), "slave-name"),
				" is working");
			if (V.recruiterTarget !== "other arcologies") {
				slaveWrapper.append(" to recruit girls");
			} else {
				slaveWrapper.append(" as a Sexual Ambassador");
				if (A.influenceTarget === -1) {
					slaveWrapper.append(", but ", App.UI.DOM.makeElement("span", `${he} has no target to influence.`, "warning"));
				} else {
					const targetName = V.arcologies.find(a => a.direction === A.influenceTarget).name;
					slaveWrapper.append(` to ${targetName}.`);
				}
			}
			slaveWrapper.append(". ");
			const link = App.UI.DOM.makeElement("span", App.UI.DOM.passageLink("Manage Recruiter", "Recruiter Select"), "major-link");
			link.id = "manageRecruiter";
			slaveWrapper.append(link, " ", App.UI.DOM.makeElement("span", "[U]", "hotkey"));
			slaveWrapper.append(App.UI.SlaveList.render.listDOM([RC.ID], [],
				App.UI.SlaveList.SlaveInteract.penthouseInteract));
		} else {
			slaveWrapper.append("You have ", App.UI.DOM.makeElement("span", "not", "warning"), " selected a Recruiter. ",
				App.UI.DOM.makeElement("span", App.UI.DOM.passageLink("Select one", "Recruiter Select"), "major-link"),
				" ", App.UI.DOM.makeElement("span", "[U]", "hotkey"));
			slaveWrapper.id = "manageRecruiter";
			if (V.slavePanelStyle === 2) {
				slaveWrapper.classList.add("slaveSummary", "card");
			}
		}
		fragment.append(slaveWrapper);

		if (V.dojo) {
			slaveWrapper = document.createElement("p");
			/** @type {App.Entity.SlaveState} */
			const BG = V.Bodyguard;
			if (BG) {
				slaveWrapper.append(App.UI.DOM.makeElement("span", SlaveFullName(BG), "slave-name"),
					" is serving as your bodyguard. ");
				const link = App.UI.DOM.makeElement("span", App.UI.DOM.passageLink("Manage Bodyguard", "BG Select"), "major-link");
				link.id = "manageBG";
				slaveWrapper.append(link, " ", App.UI.DOM.makeElement("span", "[B]", "hotkey"));
				slaveWrapper.append(App.UI.SlaveList.render.listDOM([BG.ID], [],
					App.UI.SlaveList.SlaveInteract.penthouseInteract));
				slaveWrapper.append(App.MainView.useGuard());
			} else {
				slaveWrapper.append("You have ", App.UI.DOM.makeElement("span", "not", "warning"), " selected a Bodyguard. ",
					App.UI.DOM.makeElement("span", App.UI.DOM.passageLink("Select one", "BG Select"), "major-link"),
					" ", App.UI.DOM.makeElement("span", "[B]", "hotkey"));
				slaveWrapper.id = "manageBG";
				if (V.slavePanelStyle === 2) {
					slaveWrapper.classList.add("slaveSummary", "card");
				}
			}

			fragment.append(slaveWrapper);
		}
		return fragment;
	}

	/**
	 * @param {string} job
	 * @returns {{n: number, dom: DocumentFragment}}
	 */
	function _slavesForJob(job) {
		const employeesIDs = job === 'all' ? [...ph.employeesIDs()] : [...ph.job(job).employeesIDs()];
		if (employeesIDs.length === 0) {
			return {n: 0, dom: document.createDocumentFragment()};
		}

		SlaveSort.IDs(employeesIDs);
		return {
			n: employeesIDs.length,
			dom: App.UI.SlaveList.render.listDOM(employeesIDs, [], App.UI.SlaveList.SlaveInteract.penthouseInteract,
				V.fucktoyInteractionsPosition === 1 && job === "fucktoy" ? App.MainView.useFucktoy : null)
		};
	}

	/**
	 * @typedef tabDesc
	 * @property {string} tabName
	 * @property {string} caption
	 * @property {Node} content
	 */

	/**
	 * @param {string} tabName
	 * @param {string} caption
	 * @param {Node} content
	 * @returns {tabDesc}
	 */
	function makeTabDesc(tabName, caption, content) {
		return {
			tabName: tabName,
			caption: caption,
			content: content
		};
	}

	/**
	 * Displays encyclopedia entries for occupations at the top of the tab, if enabled
	 * @returns {HTMLSpanElement}
	 */
	function encycTips(jn) {
		const span = document.createElement("span");
		span.classList.add("note");
		if (V.showTipsFromEncy) {
			switch (jn) {
				case "rest":
					span.append(App.Encyclopedia.Entries.rest());
					break;
				case "chooseOwn":
					break; /* no entry written for choose own */
				case "fucktoy":
					span.append(App.Encyclopedia.Entries.fucktoy());
					break;
				case "classes":
					span.append(App.Encyclopedia.Entries.attendingClasses());
					break;
				case "houseServant":
					span.append(App.Encyclopedia.Entries.servitude());
					break;
				case Job.WHORE:
					span.append(App.Encyclopedia.Entries.whoring());
					break;
				case "publicServant":
					span.append(App.Encyclopedia.Entries.publicService());
					break;
				case "subordinateSlave":
					span.append(App.Encyclopedia.Entries.sexualServitude());
					break;
				case "cow":
					span.append(App.Encyclopedia.Entries.milking());
					break;
				case "gloryhole":
					span.append(App.Encyclopedia.Entries.gloryHole());
					break;
				case "confinement":
					span.append(App.Encyclopedia.Entries.confinement());
					break;
				default:
					span.append(App.UI.DOM.makeElement("span", "missing tip for this tab", "error"));
					break;
			}
		}
		return span;
	}

	function allTab() {
		const penthouseSlavesIDs = [...ph.employeesIDs()];
		if (S.HeadGirl) {
			penthouseSlavesIDs.push(S.HeadGirl.ID);
		}
		if (S.Recruiter) {
			penthouseSlavesIDs.push(S.Recruiter.ID);
		}
		if (S.Bodyguard) {
			penthouseSlavesIDs.push(S.Bodyguard.ID);
		}
		SlaveSort.IDs(penthouseSlavesIDs);
		return makeTabDesc('all', `All${V.useSlaveSummaryTabs > 0 ? ` (${penthouseSlavesIDs.length})` : ""}`,
			App.UI.SlaveList.render.listDOM(penthouseSlavesIDs, [], App.UI.SlaveList.SlaveInteract.penthouseInteract));
	}

	let fragment = document.createDocumentFragment();

	if (V.positionMainLinks >= 0) {
		fragment.append(App.UI.DOM.makeElement("div", App.UI.View.mainLinks(), "center"));
	}

	if (V.sortSlavesMain) {
		fragment.append(App.UI.SlaveList.sortingLinksDOM("Main"));
	}

	/** @type {tabDesc[]} */
	let tabs = [];

	// Overview tab
	if (V.useSlaveSummaryOverviewTab) {
		tabs.push(makeTabDesc('overview', "Special Roles", overviewTabContent()));
	}

	if (V.useSlaveSummaryTabs === 0) {
		tabs.push(allTab());
	}

	// tabs for each assignment
	for (const jn of ph.jobsNames) {
		const slaves = _slavesForJob(jn);
		if (slaves.n > 0) {
			tabs.push(makeTabDesc(jn, `${ph.desc.jobs[jn].position}${V.useSlaveSummaryTabs > 0 ? ` (${slaves.n})` : ""}`, App.UI.DOM.combineNodes(encycTips(jn), slaves.dom)));
		} else if (V.useSlaveSummaryTabs === 0) {
			tabs.push(makeTabDesc(jn, ph.desc.jobs[jn].position, encycTips(jn)));
		}
	}

	if (V.useSlaveSummaryTabs > 0) {
		tabs.push(allTab());
	}

	const div = document.createElement("div");
	div.classList.add("tabbar");
	if (V.useSlaveSummaryTabs === 0) {
		const links = [];
		for (const tab of tabs) {
			links.push(App.UI.tabbar.tabButtonDOM(tab.tabName, tab.caption, true));
		}
		div.append(App.UI.DOM.arrayToList(links, " | ", " | "));
	} else {
		for (const tab of tabs) {
			const button = App.UI.tabbar.tabButtonDOM(tab.tabName, tab.caption);
			if (V.useSlaveSummaryTabs === 2) {
				button.classList.add("card");
			}
			div.append(button);
		}
	}
	fragment.append(div);

	for (const tab of tabs) {
		const div = App.UI.tabbar.makeTabDOM(tab.tabName, tab.content);
		if (V.useSlaveSummaryTabs === 0) {
			div.classList.add("noFade");
		} else if (V.useSlaveSummaryTabs === 2) {
			div.classList.add("card");
		}
		fragment.append(div);
	}

	if (V.positionMainLinks <= 0) {
		fragment.append(App.UI.DOM.makeElement("div", App.UI.View.mainLinks(), "center"));
	}

	App.UI.tabbar.handlePreSelectedTab();
	return fragment;
};

/**
 * @callback assignmentFilterGenerateCallback
 * @param {string} value
 * @returns {string}
 */

/**
 * @callback slaveFilterCallbackReasoned
 * @param {App.Entity.SlaveState} slave
 * @returns {string[]}
 */

/**
 * @callback slaveFilterCallbackSimple
 * @param {App.Entity.SlaveState} slave
 * @returns {boolean}
 */

App.UI.SlaveList.slaveSelectionList = function() {
	const selectionElementId = "slaveSelectionList";

	return selection;

	/**
	 * @typedef ListOptions
	 * @property {slaveFilterCallbackReasoned|slaveFilterCallbackSimple} filter
	 * @property {slaveTestCallback} [expCheck]
	 * @property {slaveToElement} interactionLink
	 * @property {slaveToElement} [postNote]
	 */

	/**
	 * @param {slaveFilterCallbackReasoned|slaveFilterCallbackSimple} filter
	 * @param {slaveToElement} interactionLink
	 * @param {slaveTestCallback} [experianceChecker]
	 * @param {slaveToElement} [postNote]
	 * @returns {string}
	 */
	function selection(filter, interactionLink, experianceChecker, postNote) {
		if (experianceChecker === null) { experianceChecker = undefined; }
		State.temporary.slaveSelection = {
			filter: filter,
			expCheck: experianceChecker,
			interactionLink: interactionLink,
			postNote: postNote,
			update: _updateList
		};

		$(document).one(':passagedisplay', () => { _updateList('all'); });
		return `<div>${_assignmentFilter(s => `<<run _slaveSelection.update('${s}')>>`, experianceChecker !== undefined)} <div id=${selectionElementId}></div></div>`;
	}

	function _updateList(assignment) {
		const e = document.getElementById(selectionElementId);
		e.innerHTML = '';
		e.appendChild(_listSlaves(assignment, State.temporary.slaveSelection));
	}
	/**
	 * Displays assignment filter links, whose action are generated by the callback
	 * @param {assignmentFilterGenerateCallback} callback
	 * @param {boolean} includeExperienced
	 * @returns {string}
	 */
	function _assignmentFilter(callback, includeExperienced) {
		let filters = {
			all: "All"
		};
		let fNames = Object.keys(App.Entity.facilities);
		fNames.sort();
		for (const fn of fNames) {
			/** @type {App.Entity.Facilities.Facility} */
			const f = App.Entity.facilities[fn];
			if (f.established && f.hasEmployees) {
				filters[fn] = f.name;
			}
		}
		let links = [];
		/* seems like SC2 does not process data-setter when data-passage is not set
		for (const f in filters) {
			links.push(App.UI.passageLink(filters[f], passage, callback(f)));
		}
		if (includeExperienced) {
			links.push(`<span class="lime">${App.UI.passageLink('Experienced', passage, callback('experienced'))}</span>`);
		}*/
		for (const f in filters) {
			links.push(`<<link "${filters[f]}">>${callback(f)}<</link>>`);
		}
		if (includeExperienced) {
			links.push(`<span class="lime"><<link "Experienced">>${callback('experienced')}<</link>></span>`);
		}

		return links.join(' | ');
	}

	/**
	 *
	 * @param {string} assignmentStr
	 * @param {ListOptions} options
	 * @returns {DocumentFragment}
	 */
	function _listSlaves(assignmentStr, options) {
		const slaves = V.slaves;
		let unfilteredIDs = [];
		switch (assignmentStr) {
			case 'all':
				unfilteredIDs = slaves.map(s => s.ID);
				break;
			case 'experienced':
				unfilteredIDs = slaves.reduce((acc, s, idx) => {
					if (options.expCheck(s)) {
						acc.push(s.ID);
					}
					return acc;
				}, []);
				break;
			default:
				unfilteredIDs = [...App.Entity.facilities[assignmentStr].employeesIDs()]; // set to array
				break;
		}
		SlaveSort.IDs(unfilteredIDs);
		let passingIDs = [];
		let rejects = [];

		unfilteredIDs.forEach(id => {
			const fr = options.filter(slaveStateById(id));
			if (fr === true || (Array.isArray(fr) && fr.length === 0)) {
				passingIDs.push(id);
			} else {
				if (Array.isArray(fr)) { rejects.push({id: id, rejects: fr}); }
			}
		});

		// clamsi fragment to create a function which combines results of two optional tests
		// done this way to test for tests presence only once
		const listPostNote = options.expCheck ?
			(options.postNote ?
				s => options.expCheck(s) ? App.UI.DOM.combineNodes(
					App.UI.DOM.makeElement("span", "Has applicable career experience.", "lime"),
					document.createElement("br"),
					options.postNote(s)
				) : options.postNote(s) :
				s => options.expCheck(s) ? App.UI.DOM.makeElement("span", "Has applicable career experience.", "lime") : null) :
			options.postNote ?
				s => options.postNote(s) :
				() => null;

		return App.UI.SlaveList.render.listDOM(passingIDs, rejects, options.interactionLink, listPostNote);
	}
}();

/**
 * @param {App.Entity.Facilities.Facility} facility
 * @param {string} passage go here after the new facility manager is selected
 * @returns {string}
 */
App.UI.SlaveList.facilityManagerSelection = function(facility, passage) {
	return this.slaveSelectionList(slave => facility.manager.canEmploy(slave),
		(slave) => App.UI.DOM.passageLink(SlaveFullName(slave), passage,
			() => { assignJob(slave, facility.manager.desc.assignment); }),
		slave => facility.manager.slaveHasExperience(slave));
};