code:
App.Data.Pronouns = {};

/**
 * @readonly
 * @enum {number}
 */
App.Data.Pronouns.Kind = Object.freeze({
	female: 0,
	male: 1,
	neutral: 2,
	toy: 2,
	epicene: 3,
	plural: 3,
	ai: 4,
	custom: 1000
});

/**
 * @param {string} pronoun
 * @param {string} possessive
 * @param {string} possessivePronoun
 * @param {string} object
 * @param {string} objectReflexive
 * @param {string} noun
 * @returns {App.Data.Pronouns.Definition}
 */
App.Data.Pronouns.makePronounsData = function(
	pronoun, possessive, possessivePronoun, object, objectReflexive, noun) {
	return {
		pronoun: pronoun,
		possessive: possessive,
		possessivePronoun: possessivePronoun,
		object: object,
		objectReflexive: objectReflexive,
		noun: noun
	};
};

App.Data.Pronouns.Std = {
	0: App.Data.Pronouns.makePronounsData("she", "her", "hers", "her", "herself", "girl"),
	1: App.Data.Pronouns.makePronounsData("he", "his", "his", "him", "himself", "boy"),
	2: App.Data.Pronouns.makePronounsData("it", "its", "its", "it", "itself", "toy"),
	3: App.Data.Pronouns.makePronounsData("they", "their", "theirs", "them", "themselves", "toys"),
	4: App.Data.Pronouns.makePronounsData("it", "its", "its", "it", "itself", "program"),
};

repo_name: IchthysdeKilt/fc

license: mit


code:
/* eslint-disable no-empty */
/* eslint-disable no-var */
/*
Given an object, this will return an array where for each property of the original object, we include the object
{variable: property, oldVal: _oldDiff.property, newVal: _newDiff.property}
*/
window.generateDiffArray = function generateDiffArray(obj) {
	let diffArray = Object.keys(obj).map(function(key) {
		return {variable: key, oldVal: State.temporary.oldDiff[key], newVal: State.temporary.newDiff[key]};
	});
	return diffArray;
};

/*
Shamelessly copied from https://codereview.stackexchange.com/a/11580
Finds and returns the difference between two objects. Potentially will have arbitrary nestings of objects.
*/
window.difference = function difference(o1, o2) {
	let k, kDiff, diff = {};
	for (k in o1) {
		if (!o1.hasOwnProperty(k)) {
		} else if (typeof o1[k] !== 'object' || typeof o2[k] !== 'object') {
			if (!(k in o2) || o1[k] !== o2[k]) {
				diff[k] = o2[k];
			}
		} else {
			kDiff = difference(o1[k], o2[k]);
			if (kDiff) {
				diff[k] = kDiff;
			}
		}
	}
	for (k in o2) {
		if (o2.hasOwnProperty(k) && !(k in o1)) {
			diff[k] = o2[k];
		}
	}
	for (k in diff) {
		if (diff.hasOwnProperty(k)) {
			return diff;
		}
	}
	return false;
};

/*
Shamelessly copied from https://stackoverflow.com/a/19101235
Flattens an object while concatenating property names.
For example {id: {number: 4, name: "A"}} --> {id.number: 4, id.name: "A"}
*/
window.diffFlatten = function diffFlatten(data) {
	let result = {};
	function recurse(cur, prop) {
		if (Object(cur) !== cur) {
			result[prop] = cur;
		} else if (Array.isArray(cur)) {
			// eslint-disable-next-line no-var
			for (var i=0, l=cur.length; i<l; i++) { // don't change the "var" to "let"
				recurse(cur[i], prop + "[" + i + "]");
			}
			// eslint-disable-next-line block-scoped-var
			if (l === 0) { result[prop] = []; }
		} else {
			let isEmpty = true;
			for (let p in cur) {
				isEmpty = false;
				recurse(cur[p], prop ? prop+"."+p : p);
			}
			if (isEmpty && prop) { result[prop] = {}; }
		}
	}
	recurse(data, "");
	return result;
};

/*
Finds all NaN values anywhere in the State.variables object. Returns an array with the names of the NaNed variables.
*/
window.findNaN = function findNan() {
	const flatV = diffFlatten(State.variables);
	let result = [];
	for (let key in flatV) {
		if (Number.isNaN(flatV[key])) {
			result.push('$$'+key); /* double dollar signs to escape sugarcube markup */
		}
	}
	return result;
};

/* no-usedOnce*/
/**
 * Dumps game save as a readable JSON to the browser for saving in a file
 */
App.Debug.dumpGameState = function() {
	// helper to download a blob
	// borrowed from stackexchange
	function downloadToFile(content, fileName, contentType) {
		let a = document.createElement("a");
		let file = new Blob([content], {
			type: contentType
		});
		a.href = URL.createObjectURL(file);
		a.download = fileName;
		a.click();
	}

	// we will replace SugarCube onSave handler
	let oldHandler = SugarCube.Config.saves.onSave;
	try {
		SugarCube.Config.saves.onSave = function(save) {
			if (oldHandler) {
				oldHandler(save);
			}
			downloadToFile(JSON.stringify(save, null, 2), save.id + ".json", "text/plain");
		};
		SugarCube.Save.serialize();
	} finally {
		SugarCube.Config.saves.onSave = oldHandler;
	}
};

App.Debug.slaveSummaryText = function(idx) {
	let span = document.createElement("span");
	span.appendChild(SlaveSummaryUncached(State.variables.slaves[idx]));
	return span.outerHTML;
};


code:
:: Debug Widgets [widget nobr]

/*
This widget will find which properties were changed in a specific passage (State.bottom.title to be precise), and display their original values and current values.
It's somewhat awkward if a stored variable changes types to/from an object or array.
The included "Go Back" link is intended to replace the usual NextButton.
*/
<<widget "displayDifferences">>
<<set _oldVariables = clone(State.bottom.variables)>>
<<set _newVariables = clone(State.variables)>>
<<set _newDiff = diffFlatten(difference(_oldVariables,_newVariables))>> /* Returns a flattened object containing the names of the changed variables, and the new values of those variables. */
<<set _oldDiff = diffFlatten(difference(_newVariables,_oldVariables))>> /* Returns a flattened object containing the names of the changed variables, and the old values of those variables. */
<<set _diffArrayFromNew = generateDiffArray(_newDiff)>> /* This function requires the existence of the specific variables _newDiff AND _oldDiff to work. */
<<set _diffArrayFromOld = generateDiffArray(_oldDiff)>>
<<link "Go Back">>
	<>
<</link>>

Differences:
<<if _diffArrayFromNew.length > 0>>
	<<for _i = 0; _i < _diffArrayFromNew.length; _i++>> /* Print variable names, and changed values. Will output the new values correctly, may not output old values correctly */
		<<if _diffArrayFromNew[_i].variable != "nextButton" && _diffArrayFromNew[_i].variable != "nextLink" && _diffArrayFromNew[_i].variable != "args">>

    Variable: <<print _diffArrayFromNew[_i].variable>>, Original Value: <<print _diffArrayFromNew[_i].oldVal>>, New Value: <<print _diffArrayFromNew[_i].newVal>>
		<</if>>
	<</for>>

	<<link "Show more">>
		<<replace "#extraInfo">>
			Alternate display:
			<<for _i = 0; _i < _diffArrayFromOld.length; _i++>> /* Print variable names, and changed values. Will output the old values correctly, may not output new values correctly */
				<<if _diffArrayFromOld[_i].variable != "nextButton" && _diffArrayFromOld[_i].variable != "nextLink" && _diffArrayFromOld[_i].variable != "args">>

    Variable: <<print _diffArrayFromOld[_i].variable>>, Original Value: <<print _diffArrayFromOld[_i].oldVal>>, New Value: <<print _diffArrayFromOld[_i].newVal>>
				<</if>>
			<</for>>
		<</replace>>
	<</link>>// This should only be necessary if a variable changes type to/from an object or array. In that case this will display the correct original value, but incorrect current value.//

<<else>>

    Nothing changed!

<</if>>
<<link "Go Back">>
	<>
<</link>>
<</widget>>


code:
/* This is a terrible hack, but it actually seems to yield decent results, so...yay? */

window.ProfileInclude = (function() {
	let stack = [];
	let tally = new Map();
	let builtinIncludeHandler = null;

	class TallyElement {
		constructor(passage) {
			this.passage = passage;
			this.cumulativeTime = 0.0;
			this.calls = 0;
			this.children = new Map();
		}
		get totalChildTime() {
			return Array.from(this.children.values(), (x) => x.cumulativeTime).reduce((sum, current) => { return sum + current; }, 0.0);
		}
		outputBreakdown() {
			if (this.children.size === 0) {
				// no breakdown needed if there are no children, so return an empty fragment
				return document.createDocumentFragment();
			}

			const selfTime = this.cumulativeTime - this.totalChildTime;
			const selfTimePercent = (selfTime / this.cumulativeTime) * 100;
			const selfTimePerCall = selfTime / this.calls;

			let childList = document.createElement("ul");
			let selfListItem = document.createElement("li");
			childList.appendChild(selfListItem);
			selfListItem.appendChild(document.createTextNode(`<self>: ${selfTime.toFixed(0)}ms (${selfTimePercent.toFixed(2)}%); ${selfTimePerCall.toFixed(0)}ms per call over ${this.calls.toFixed(0)} calls`));
			for (const [, child] of this.children) {
				const childTimePercent = (child.cumulativeTime / this.cumulativeTime) * 100;
				const childTimePerCall = child.cumulativeTime / child.calls;
				let childListItem = document.createElement("li");
				childListItem.appendChild(document.createTextNode(`${child.passage}: ${child.cumulativeTime.toFixed(0)}ms (${childTimePercent.toFixed(2)}%); ${childTimePerCall.toFixed(0)}ms per call over ${child.calls.toFixed(0)} calls`));
				childListItem.appendChild(child.outputBreakdown());
				childList.appendChild(childListItem);
			}
			return childList;
		}
	}

	class StackElement {
		constructor(passage) {
			this.passage = passage;
			this.startTime = Date.now();
		}
	}

	function Push(passage) {
		stack.push(new StackElement(passage));
	}

	function PopAndTally() {
		const el = stack.pop();
		const stopTime = Date.now();

		/* traverse the tally tree, ensuring that it has nodes for every frame still in the stack */
		let parentMap = tally;
		for (const child of stack) {
			if (!parentMap.has(child.passage)) {
				parentMap.set(child.passage, new TallyElement(child.passage));
			}
			parentMap = parentMap.get(child.passage).children;
		}

		/* add the popped stack frame's time to the correct child of the tally tree node corresponding with the top of the stack */
		if (!parentMap.has(el.passage)) {
			parentMap.set(el.passage, new TallyElement(el.passage));
		}
		let tallyEl = parentMap.get(el.passage);
		tallyEl.cumulativeTime += stopTime - el.startTime;
		tallyEl.calls++;
	}

	function IncludeBegins(passage) {
		Push(passage);
	}

	function IncludeEnds() {
		PopAndTally();
	}

	$(document).on(':passagestart', function() {
		if (V.profiler && !ProfileInclude.IsEnabled()) {
			ProfileInclude.Start();
		} else if (!V.profiler && ProfileInclude.IsEnabled()) {
			ProfileInclude.Stop();
		}

		tally.clear();

		if (ProfileInclude.IsEnabled()) {
			Push(passage()); // put the "host" passage at the bottom of the stack
		}
	});

	$(document).on(':passagedisplay', function() {
		if (ProfileInclude.IsEnabled()) {
			PopAndTally(); // tally the "host" passage too

			let header = document.createElement("h2");
			header.appendChild(document.createTextNode("Profile Data:"));
			let rootList = document.createElement("ul");
			let rootItem = document.createElement("li");
			let rootData = tally.values().next().value; // should be just the one item in the map at this level
			rootItem.appendChild(document.createTextNode(`${rootData.passage}: ${rootData.cumulativeTime.toFixed(0)}ms (100.00%)`));
			rootItem.appendChild(rootData.outputBreakdown());
			rootList.appendChild(rootItem);
			$("#passages").append(header).append(rootList);
		}
	});

	return {
		Start: function() {
			/* save off the builtin handler for the include macro, delete said macro, then re-register it with instrumentation */
			if (!this.IsEnabled() && Macro.has('include')) {
				builtinIncludeHandler = Macro.get('include').handler;
				Macro.delete('include');
				Macro.add('include', {handler() {
					IncludeBegins(this.args[0]);
					builtinIncludeHandler.call(this);
					IncludeEnds();
				}});
			}
		},

		IsEnabled: function() {
			return (builtinIncludeHandler !== null);
		},

		Stop: function() {
			/* restore the original include macro handler */
			if (this.IsEnabled())	{
				Macro.delete('include');
				Macro.add('include', {handler: builtinIncludeHandler});
				builtinIncludeHandler = null;
			}
		}
	};
})();


code:
:: Variable Difference [nobr]

<<set $nextButton = " ">> /* disable the nextButton. Forces the user to use the "Go Back" option contained in the widget */

<<displayDifferences>>


code:
/* Condition
	The current physical condition of the slave.
	Any health improvements get added here.
	Short term damage reduces it as it degrades.

Short term damage
	Anything that hurts a slave gets transferred into this.
	At the end of the week 25% of it will be removed and turned into condition damage instead.
	Usage of preventatives or curatives reduces the actual condition damage by 50%.

Long term damage
	Once ageing beyond 30 years old there is a chance of long term damage that increases with time. Calculated on birthday.
		Math.floor((slave.physicalAge - 25 + jsRandom(1, 15)) / 20)
	25% of the actual condition damage taken during a week also gets added to the pool (therefore gets reduced by preventatives and curatives if active).
	Nothing can reduce this value.
	Perhaps the effect can still be reduced through surgical implant with high upkeep.

Carcinogens
	Aside from a source of regular short term damage high levels will also increase the chances for severe illnesses
		3d6 rolls for illness
			illness > 8 -- 1 62.5%
			illness > 6 -- 2 21.3%
			illness > 5 -- 3 11.6%
			illness > 4 -- 4 2.8%
			illness = 3 or 4 -- 5 1.8%
		Carcinogens subtract Math.trunc(chem / 150) from the dice rolls for a max of -6 at >= 90
	There should be a natural decay of carcinogens every week of 10% of the level. But at the price of 0.2 short term damage per point of chem.
	Add carcinogen damage to serious medical procedures due to use of potent pharmaceuticals during them.

Illness
	There is always a chance a slave gets sick. Often they will just get better on their own, but sometimes it can be more serious and require a stay in the clinic.
	Sick slaves work at reduced effectiveness and will see their health lowered.

Tiredness
	Depending on various factors (living conditions, assignment, rewards, muscles, health) a slave may become more or less tired.
	Once tiredness reached 50 there will be negative effects for productivity and at 80 they become even more extreme.
	Being tired or exhausted also reduces a slave's ability to resist the player, increasing devotion and fear.

Health
	The aggregate of condition, short term damage and long term damage to provide an indication of the current overal state of the slave. The slave will die once this reached -100.
*/

/* Getting ill depends on the following factors;
	- current condition
	- long term damage (accumulated through getting old(er) and residual from short term damage)
	- short term damage (accumulated through serious illness, chemical carcinogens, damaging surgeries and other health damage sources) 125%
	- chemical carcinogens (more serious illness chance with high carcinogen levels)
	- age (long term damge takes care of older slaves, the young ones need a specific vulnerability)
	- use of curatives
	- assignment (rest, clinic, spa and master suite)
	- random chance
*/

/**
 * @param {App.Entity.SlaveState} slave
 * @returns {void}
 */
window.illness = function illness(slave) {
	const random = jsRandom(1, 100); // high rolls are good
	const H = slave.health;
	let assignBonus = 0; // bonus for healthy assignments
	// On the macro side of things disease could also happen to the acrology's population as the arcology becomes crowded, killing citizens and putting slaves at greater risk of getting ill. Again with upgrades/policies to mitigate the issue made available TODO?
	if (slave.assignment === "live with your agent" || slave.assignment === "be your agent") {
		return;
	} // Let's make sure agents don't get sick and accidentally die, they should take care of themselves
	if (slave.assignment === "rest" || slave.assignment === "rest in the spa" || slave.assignment === "get treatment in the clinic" || slave.assignment === "serve in the master suite" || slave.assignment === "please you") {
		assignBonus += 10;
	}
	if (random < 6) { // There is always a 5% chance of a slave feeling worse
		if (H.illness > 0) {
			H.illness += 1 + Math.trunc((slave.chem / 10 + jsRandom(1, 50) + 15) / 100); // Illness progresses with 1, unless chem > 350, then there's a chance for 2
			if (H.illness > 5) {
				healthDamage(slave, 20 * (H.illness - 5)); // Condition penalty for going over maximum illness, very dangerous
				H.illness = 5;
			}
		} else {
			getIll(slave);
		}
	} else if (random > 95) { // There is always a 5% chance of a slave getting better
		H.illness -= 1;
		if (H.illness < 0) {
			H.illness = 0;
			improveCondition(slave, 5);
		}
	} else if ((Math.min(H.condition - H.longDamage - H.shortDamage * 1.25, 50) + (Math.min(Math.trunc((slave.physicalAge - 18) / 3)), 0)) / 3 + random + assignBonus + nurseEffectiveness(slave) < 30 / Math.min(slave.curatives + 1, 2)) { // Chance of getting ill 30% at complete default, 20% with a favourable assignment, 15% with curatives or preventatives, 10% with both measures active and a small benefit from effective Nurse screening
		if (H.illness > 0) {
			H.illness += 1 + Math.trunc((slave.chem / 10 + jsRandom(1, 50) + 15) / 100);
			if (H.illness > 5) {
				healthDamage(slave, 20 * (H.illness - 5));
				H.illness = 5;
			}
		} else {
			getIll(slave);
		}
	} else if (H.illness > 0) { // When ill, a slave has a 60% chance of getting better the next week at complete default, 70% with a favourable assignment, 80% with curatives, 90% with both measures active and additional benefits depending on the nurse on duty
		if ((H.condition - H.longDamage - H.shortDamage * 1.25 + (Math.min(Math.trunc((slave.physicalAge - 18) / 3)), 0)) / 3 + random + assignBonus + nurseEffectiveness(slave) > 40 / Math.clamp(slave.curatives, 1, 2)) {
			if (nurseEffectiveness(slave) > 30 && jsRandom(1, 2) === 2 && H.illness > 1) { // A particularly effective nurse can improve illness faster
				H.illness -= 2;
			} else {
				H.illness -= 1;
			}
		}
	}
	// Slave .need reduction if ill or tired
	if (slave.energy < 50) {
		slave.need = Math.trunc(slave.need * healthPenalty(slave));
	} else if (H.illness > 0) {
		slave.need = Math.trunc(slave.need * (1 - (100 - Math.min(Math.pow(H.illness, 2) * 5 + 5, 95)) / 100));
	}
};

/**
 * Once a new illness is rolled this determines how bad it is initially, chem levels seriously increase the chances of a higher initial value
 * @param {App.Entity.SlaveState} slave
 * @returns {void}
 */
window.getIll = function getIll(slave) {
	const H = slave.health;
	const illness = jsRandom(1, 6) + jsRandom(1, 6) + jsRandom(1, 6) - Math.trunc(H.chem / 150);
	if (illness < 4) {
		H.illness = 5; // 1.8% chance
	} else if (illness < 5) {
		H.illness = 4; // 2.8% chance
	} else if (illness < 6) {
		H.illness = 3; // 11.6% chance
	} else if (illness < 8) {
		H.illness = 2; // 21.3% chance
	} else {
		H.illness = 1; // 62.5% chance
	}
};

/**
 * A better nurse and/or less slaves/patients to look out for makes for a better chance of curing illness
 * @param {App.Entity.SlaveState} slave
 * @returns {number}
 */
window.nurseEffectiveness = function nurseEffectiveness(slave) {
	const V = State.variables;
	const H = slave.health;
	const clinicUpgrade = 1; // Creating a purchasable upgrade to increase the amount of slaves the nurse can handle -- TODO
	const clinicScreening = 1; // Assumes the clinic is set to screening all slaves to improve their chances of staying healthy. Turning it off would allow the nurse to focus on just her patients in the clinic -- TODO
	if (V.Nurse !== 0) {
		let nurseEffectiveness = Math.trunc((V.Nurse.skill.nurse * clinicUpgrade / Math.max((V.CliniciIDs.length * 10 + (V.slaves.length * 2) * clinicScreening), 1)) * 20);
		if (H.illness > 1 && slave.assignment === "get treatment in the clinic") {
			if (nurseEffectiveness < 20) {
				return nurseEffectiveness;
			} else if (V.Nurse.skill.nurse > 80) {
				return Math.min(nurseEffectiveness, 40);
			} else if (V.Nurse.skill.nurse > 40) {
				return Math.min(nurseEffectiveness, 30);
			} else {
				return 20;
			}
		} else if (H.illness < 2) { // reasonably ill slaves get no benefit from the nurse unless they are in the clinic, otherwise she can provide benefits to prevent illness in the first place and clearing up illnesses of level 1
			nurseEffectiveness = Math.trunc(nurseEffectiveness / 4);
			if (nurseEffectiveness < 5) {
				return nurseEffectiveness;
			} else {
				return 5;
			}
		}
	} else {
		return 0;
	}
};

/**
 * Run at the end of the week to take care of health changes
 * @param {App.Entity.SlaveState} slave
 * @returns {void}
 */
window.endWeekHealthDamage = function endWeekHealthDamage(slave) {
	const H = slave.health;
	let chemToShort = 0;
	let shortToCondition = 0;
	let shortToLong = 0;
	let tiredToCondition = 0;

	// Checking if we are dealing with the player
	// Player does not make use of most things slaves deal with, only short to long term damage
	if (slave.ID !== -1) {
		// dealing with carcinogens
		// They decay naturally at a rate of 10%, but at as they decay cause short term damage
		if (slave.chem > 0) {
			if (slave.chem > 10) {
				chemToShort += Math.max(Math.trunc(slave.chem * 0.1), 1);
			} else if (slave.chem > jsRandom(0, 9)) {
				chemToShort += 1;
			}
			slave.chem -= chemToShort;
			H.shortDamage += Math.max(Math.trunc(chemToShort * 0.1), 2);
		}

		// dealing with illness
		if (H.illness > 0) {
			H.shortDamage += Math.trunc(Math.pow(H.illness, 1.52) * 3 + 2); // 5, 10, 17, 26, 36 points of damage per respective level of illness
		}

		// Reducing condition for tired slaves
		if (H.tired > 100) {
			tiredToCondition += Math.max(Math.trunc((H.tired - 100) * 0.1 + Math.max(normalRandInt(0), 0)), 1);
			H.tired = 100;
		} else {
			H.tired = Math.max(H.tired, 0);
		}
		if (tiredToCondition > 0) {
			H.condition -= tiredToCondition;
		}

		// Long term damage due to age calculated on birthdays only
		if (slave.birthWeek === 0 && slave.physicalAge > 29) {
			H.longDamage += Math.trunc((slave.physicalAge - 25 + jsRandom(1, 15)) / 20);
		}
	} else if (H.condition < 100) { // The player gets an automatic 5 condition recovery each weak up to 100
		H.condition = Math.min(H.condition + 5, 100);
	}

	// recovering and transferring short term damage to condition and long term
	if (H.shortDamage > 0) {
		shortToCondition += Math.max(Math.trunc(H.shortDamage * 0.25), 1); // 25% of short term damage gets transferred
		H.shortDamage -= shortToCondition;
		if (slave.curatives > 0 || slave.ID === -1) { // transferred damage is half if on preventatives/curatives or target is the player
			shortToCondition = Math.trunc(shortToCondition * 0.5);
		}
		H.condition -= shortToCondition;
		shortToLong += Math.trunc(shortToCondition * 0.25); // 25% of transferred damage gets added to long term damage, minimum of 16 short term damage before any long term damage is accumulated
		H.longDamage += shortToLong;
	}

	// Making sure condition doesn't get too high
	if (H.condition > 150) {
		H.condition -= Math.trunc(Math.pow(H.condition - 150, 0.5));
	}

	H.health = H.condition - H.longDamage - H.shortDamage;
};

/**
 * Run at the end of the week to take care of tiredness changes
 * @param {App.Entity.SlaveState} slave
 * @returns {void}
 */
window.tired = function tired(slave) {
	const V = State.variables;
	const H = slave.health;
	let livingRules = 0;
	let assignment = 0;
	let reward = 0;
	let muscles;
	let health;
	let tiredChange;

	// Assignment
	if (slave.assignment === "rest" || slave.assignment === "get treatment in the clinic") {
		assignment -= normalRandInt(15, 2); // Reduces tired by an average of 15 points while on a relaxing assignment
	} else if (slave.assignment === "serve in the master suite" || slave.assignment === "please you" || slave.assignment === "guard you" || slave.assignment === "be the Attendant" || slave.assignment === "be the Matron" || slave.assignment === "be the Stewardess" || slave.assignment === "be the Milkmaid" || slave.assignment === "be the Farmer" || slave.assignment === "be the DJ" || slave.assignment === "be your Concubine" || slave.assignment === "be the Madam" || slave.assignment === "be the Schoolteacher" || slave.assignment === "be the Wardeness" || slave.assignment === "be the Nurse" || slave.assignment === "be your Head Girl" || slave.assignment === "recruit girls" || slave.assignment === "learn in the schoolroom" || slave.assignment === "take classes" || slave.assignment === "live with your Head Girl") {
		assignment -= normalRandInt(5); // Reduces tired by an average of 5 points while on a relatively easy assignment/in a leadership position
	} else if (slave.assignment === "rest in the spa") {
		assignment -= normalRandInt(20, 1.5) * (V.spaUpgrade + 1); // Reduces tired by an average of 20 points while in the spa, 40 points with the upgraded spa
		if (V.Attendant !== 0) {
			assignment -= Math.trunc(V.Attendant.skill.attendant / 10); // Maximum of 10 extra points of negative tiredness due to attendant skill
		}
	} else if (slave.assignment === "whore" || slave.assignment === "work in the brothel" || slave.assignment === "work a glory hole" || slave.assignment === "be confined in the arcade" || slave.assignment === "serve the public" || slave.assignment === "serve in the club") {
		assignment += 0; // These assignments get their tiredness increase from tiredFucks()
	} else if (slave.assignment === "be confined in the arcade") {
		assignment += normalRandInt(5); // Arcade is a terrible place
	} else if (slave.assignment === "work as a farmhand") {
		assignment += normalRandInt(10, 2); // Increases tired by an average of 10 points while on a very demanding assignment
	} else if (slave.assignment === "be a servant") {
		assignment += normalRandInt(5); // Increases tired by an average of 5 points while on a demanding assignment
	} else if (slave.assignment === "be your agent" || slave.assignment === "live with your agent") {
		assignment -= normalRandInt(15, 2); // Making sure agents don't get exhausted, surely they can afford to do some relaxing
	} else if (slave.assignment === "work in the dairy") {
		if (V.dairyRestraintsSetting > 1) {
			assignment += normalRandInt(15, 2); // Full industrial Dairy is exhausting
		} else if (V.dairyRestraintsSetting > 0) {
			assignment += normalRandInt(5); // Restraining while milking is a little stressful
		} else {
			assignment -= normalRandInt(5); // Being a free range cow is relatively relaxing
		}
	} else if (slave.assignment === "get milked") {
		assignment -= normalRandInt(2); // Feels good to lighten the load
	}

	// Living Conditions
	// PM - What they return to after a day of work. Do this after the assignment to allow kinder facility decorations.
	if (slave.rules.living === "spare") {
		livingRules -= 1; // Barely reduce tiredness while sleeping in spare conditions
		if (V.dormitory < V.dormitoryPopulation) {
			livingRules += 5; // Overcrowding penalty
		}
	} else if (slave.rules.living === "luxurious") {
		livingRules -= normalRandInt(10); // Reduces tired by an average of 10 points while sleeping under luxurious conditions
		// PM - Luxury rooms are rooms and not subject to overcrowding penalties.
	} else {
		livingRules -= normalRandInt(5); // Reduces tired by an average of 5 points while sleeping under normal conditions
		if (V.dormitory < V.dormitoryPopulation) {
			livingRules += 5; // Overcrowding penalty
		}
	}

	// Rewards
	if (V.spaSpots > 0 && (slave.rules.reward === "relaxation" || slave.rules.reward === "situational")) {
		if (slave.rules.reward === "relaxation") { // Considering the strength of the reward
			reward += 2;
		} else {
			reward += 1;
		}
		if (slave.devotion > 50) { // Considering how often the slave gets rewarded
			reward *= 3;
		} else if (slave.devotion > 20) {
			reward *= 2;
		} else if (slave.devotion < -20 || slave.trust >= -20) {
			reward = 0;
		}
		V.spaSpots -= reward; // Reducing the available space in the spa depending on how often the slave can be found there
		if (reward > 0) {
			reward = -(Math.max(normalRandInt(reward), 0) * (V.spaUpgrade + 1));
		}
	}

	// Muscles
	if (slave.muscles < 0) {
		muscles = -Math.trunc((slave.muscles / 10) * (1 + normalRandInt(0, 5) / 100)); // Being weak increases tiredness, building muscles eventually reduces tiredness
	} else {
		muscles = -Math.trunc(5 * (1 + normalRandInt(0, 5) / 100)); // Muscle benefits max out at 50
	}

	// Health
	health = Math.trunc((H.shortDamage / 2 - H.condition / 10) * (1 + normalRandInt(0, 5) / 100)); // Current condition reduces tiredness, health damage increases tiredness

	tiredChange = livingRules + assignment + reward + muscles + health;
	H.tired += tiredChange;
	if (H.tired < 0) {
		H.tired = 0;
	}
};

/**
 * @param {App.Entity.SlaveState} slave
 * @returns {void}
 */
window.tiredFucks = function tiredFucks(slave) {
	slave.health.tired += Math.trunc(slave.sexAmount * (1 + normalRandInt(0, 5) / 100) / 25);
};


code:
/**
 * @param {App.Entity.SlaveState} slave
 * @returns {string}
 */
window.minorInjuryResponse = function minorInjuryResponse(slave) {
	const arcology = State.variables.arcologies[0];
	const arcologyUpgrade = State.variables.arcologyUpgrade;
	const {he, himself, He} = getPronouns(slave);
	let r = "";

	if (arcology.FSDegradationist > 20) {
		r += ` ${He} carries on, knowing that in your degradationist arcology, this is accepted behavior.`;
	} else if (arcology.FSPaternalist > 20) {
		if (arcologyUpgrade.drones === 1 && jsRandom(1, 100) > 50) {
			r += ` The security drones successfully detain him and oblige him to pay significant <span class="yellowgreen">compensation,</span> since abusing slaves is discouraged here.`;
			cashX(50, "slaveUpkeep", slave);
		} else if (arcologyUpgrade.grid === 1 && jsRandom(1, 100) > 50) {
			r += ` The computer systems successfully detain him and require him to pay significant <span class="yellowgreen">compensation,</span> since abusing slaves is discouraged here.`;
			cashX(50, "slaveUpkeep", slave);
		} else if (slave.skill.whoring > jsRandom(1, 100)) {
			r += ` ${slave.slaveName} is used to this sort of thing, and convinces him to pay significant <span class="yellowgreen">compensation,</span> since abusing slaves is discouraged here.`;
			cashX(50, "slaveUpkeep", slave);
		} else if (slave.skill.combat > 0) {
			r += ` ${slave.slaveName} can take care of ${himself}, and ${he} successfully forces him to pay significant <span class="yellowgreen">compensation,</span> since abusing slaves is discouraged here.`;
			cashX(50, "slaveUpkeep", slave);
		}
	} else {
		if (arcologyUpgrade.drones === 1 && jsRandom(1, 100) > 50) {
			r += ` The security drones successfully detain him and oblige him to pay minor <span class="yellowgreen">compensation.</span>`;
			cashX(10, "slaveUpkeep", slave);
		} else if (arcologyUpgrade.grid === 1 && jsRandom(1, 100) > 50) {
			r += ` The computer systems successfully detain him and require him to pay minor <span class="yellowgreen">compensation.</span>`;
			cashX(10, "slaveUpkeep", slave);
		} else if (slave.skill.whoring > jsRandom(1, 100)) {
			r += ` ${slave.slaveName} is used to this sort of thing, and convinces him to pay minor <span class="yellowgreen">compensation.</span>`;
			cashX(10, "slaveUpkeep", slave);
		} else if (slave.skill.combat > 0) {
			r += ` ${slave.slaveName} can take care of ${himself}, and ${he} successfully forces him to pay minor <span class="yellowgreen">compensation.</span>`;
			cashX(10, "slaveUpkeep", slave);
		}
	}

	return r;
};