From 6df73d6078765ea148bdc6355cfc17ac78ec5f2d Mon Sep 17 00:00:00 2001
From: Svornost <11434-svornost@users.noreply.gitgud.io>
Date: Mon, 12 Oct 2020 20:00:16 -0700
Subject: [PATCH] Rework facility leader sex with employees. 1. Add a new
 release rule to control whether development facility leaders (Nurse,
 Attendant, Matron, Warden, and Schoolteacher) are allowed to have sex with
 the slave.  Does not affect production facility leaders. 2. Determine
 employee-leader sexual contact before running slave reports at all, and
 adjust need and counters based only on ACTUAL sex, not hypotheticals.  Fixes
 bugs for Cellblock and Clinic even without no rules changes, but also
 accounts for the new rule.  Counters for both slaves are always adjusted on
 the *employee* end. 3. Friends may satisfy sexual needs in the clinic, if
 they're allowed to under general release rules; if relationship rules are
 *also* permissive, their relationship will advance automatically to FWB. 4.
 Fix many instances of counter mismatch throughout these scenarios.  Use seX()
 where it makes sense to do so. 5. Add hints to Slave Interact to describe the
 release rules in more detail.  Doesn't look great but should get the message
 across.  Someone's welcome to reformat these if they want.

---
 devTools/types/FC/RA.d.ts                     |   1 +
 js/003-data/miscData.js                       |   4 +
 .../backwardsCompatibility/datatypeCleanup.js |   7 +
 src/endWeek/clinicReport.js                   |   1 +
 src/endWeek/facilityLeaderSex.js              |  55 +++++
 src/endWeek/reports/nurseryReport.js          |   2 +
 src/endWeek/schoolroomReport.js               |   1 +
 src/endWeek/slaveAssignmentReport.js          |   1 +
 src/interaction/siRules.js                    |  32 +--
 src/js/DefaultRules.js                        |   1 +
 src/js/SlaveState.js                          |   5 +-
 src/js/releaseRules.js                        |  28 ++-
 src/js/rulesAssistant.js                      |   1 +
 src/js/rulesAssistantOptions.js               |  13 ++
 src/npc/surgery/surgery.js                    |   1 +
 src/player/js/PlayerState.js                  |   2 +
 src/uncategorized/cellblockReport.tw          |   1 +
 src/uncategorized/saRules.tw                  | 201 +++++++++---------
 src/uncategorized/spaReport.tw                |   1 +
 19 files changed, 238 insertions(+), 120 deletions(-)
 create mode 100644 src/endWeek/facilityLeaderSex.js

diff --git a/devTools/types/FC/RA.d.ts b/devTools/types/FC/RA.d.ts
index 05fa0941fab..5c9869eebf4 100644
--- a/devTools/types/FC/RA.d.ts
+++ b/devTools/types/FC/RA.d.ts
@@ -57,6 +57,7 @@ declare namespace FC {
 		interface RuleReleaseSetters {
 			masturbation: number;
 			partner: number;
+			facilityLeader: number;
 			family: number;
 			slaves: number;
 			master: number;
diff --git a/js/003-data/miscData.js b/js/003-data/miscData.js
index 42f090ec991..fcbf269784f 100644
--- a/js/003-data/miscData.js
+++ b/js/003-data/miscData.js
@@ -1539,6 +1539,10 @@ App.Data.misc = {
 
 	servantMilkersJobs: [Job.HOUSE, Job.SUBORDINATE, Job.FUCKTOY, Job.RECRUITER, Job.REST, Job.CONFINEMENT, Job.CLASSES, Job.QUARTER],
 
+	// only these specific "development" facility leaders will do anything to satisfy their employees' sexual needs.
+	// "production" facilities need their employees to stay horny to be more effective workers.
+	sexFromDevelopmentLeaders: [Job.NURSE, Job.ATTENDANT, Job.MATRON, Job.WARDEN, Job.TEACHER],
+
 	pettyCriminalPool: ["armed robbery", "arson", "assault", "battery", "blackmail", "burglary", "cat burglar", "child abuse", "child molestation", "domestic abuse", "illegal immigrant", "manslaughter", "mule", "murder", "petty theft", "pickpocketing", "rape", "robbery", "tax evasion", "theft"],
 
 	gangCriminalPool: ["arms smuggler", "assassin", "attempted murder", "drug peddler", "drug smuggler", "fence", "gang assaulter", "gang bruiser", "gang murderer", "gang thief", "hitman", "manslaughter", "mule", "murder", "smuggler"],
diff --git a/src/data/backwardsCompatibility/datatypeCleanup.js b/src/data/backwardsCompatibility/datatypeCleanup.js
index 6d4ec8ba5cf..6ae2a51ef83 100644
--- a/src/data/backwardsCompatibility/datatypeCleanup.js
+++ b/src/data/backwardsCompatibility/datatypeCleanup.js
@@ -83,6 +83,7 @@ App.Entity.Utils.SlaveDataSchemeCleanup = (function() {
 				case "chastity":
 					newRule.masturbation = 0;
 					newRule.partner = 0;
+					newRule.facilityLeader = 0;
 					newRule.family = 0;
 					newRule.slaves = 0;
 					newRule.master = 0;
@@ -90,6 +91,7 @@ App.Entity.Utils.SlaveDataSchemeCleanup = (function() {
 				case "restrictive":
 					newRule.masturbation = 0;
 					newRule.partner = 1;
+					newRule.facilityLeader = 1;
 					newRule.family = 0;
 					newRule.slaves = 0;
 					newRule.master = 1;
@@ -97,6 +99,7 @@ App.Entity.Utils.SlaveDataSchemeCleanup = (function() {
 				case "masturbation":
 					newRule.masturbation = 1;
 					newRule.partner = 0;
+					newRule.facilityLeader = 1;
 					newRule.family = 0;
 					newRule.slaves = 0;
 					newRule.master = 1;
@@ -104,6 +107,7 @@ App.Entity.Utils.SlaveDataSchemeCleanup = (function() {
 				case "sapphic":
 					newRule.masturbation = 0;
 					newRule.partner = 1;
+					newRule.facilityLeader = 1;
 					newRule.family = 1;
 					newRule.slaves = 1;
 					newRule.master = 1;
@@ -111,6 +115,7 @@ App.Entity.Utils.SlaveDataSchemeCleanup = (function() {
 				case "permissive":
 					newRule.masturbation = 1;
 					newRule.partner = 1;
+					newRule.facilityLeader = 1;
 					newRule.family = 1;
 					newRule.slaves = 1;
 					newRule.master = 1;
@@ -119,6 +124,8 @@ App.Entity.Utils.SlaveDataSchemeCleanup = (function() {
 			rulestate.release = newRule;
 		} else if (typeof rulestate.release !== "object" || rulestate.release === null) {
 			rulestate.release = new App.Entity.ReleaseRulesState();
+		} else if (typeof rulestate.release.facilityLeader !== "number") {
+			rulestate.release.facilityLeader = 1;
 		}
 	}
 
diff --git a/src/endWeek/clinicReport.js b/src/endWeek/clinicReport.js
index a098ec4070d..87ffbc9daf2 100644
--- a/src/endWeek/clinicReport.js
+++ b/src/endWeek/clinicReport.js
@@ -4,6 +4,7 @@ App.EndWeek.clinicReport = function() {
 	const slaves = App.Utils.sortedEmployees(App.Entity.facilities.clinic);
 	let devBonus = (V.clinicDecoration !== "standard") ? 1 : 0;
 	let healthBonus = 0;
+	V.flSex = App.EndWeek.getFLSex(App.Entity.facilities.clinic); // FIXME: should be local, passed as a parameter to saRules
 
 	function nurseText() {
 		let r = [];
diff --git a/src/endWeek/facilityLeaderSex.js b/src/endWeek/facilityLeaderSex.js
new file mode 100644
index 00000000000..656c4e81d73
--- /dev/null
+++ b/src/endWeek/facilityLeaderSex.js
@@ -0,0 +1,55 @@
+/** Get the slave (or player) who's taking care of this patient's sexual needs this week.
+ * @param {App.Entity.SlaveState} slave
+ * @returns {{type: "player" | "lover" | "friend" | "family" | "nurse" | null, slave?: App.Entity.SlaveState}}
+ */
+App.EndWeek.getClinicPartner = function(slave) {
+	const validVisitingPartner = (/** @type {App.Entity.SlaveState} */ s) => s && canMove(s) && isSlaveAvailable(s) && App.Utils.sexAllowed(slave, s);
+	if (slave.relationship === -3) { // player visits wife
+		return {type: "player"};
+	}
+	if (slave.relationship > 0) {
+		const partner = getSlave(slave.relationshipTarget);
+		if (validVisitingPartner(partner)) {
+			if (slave.relationship > 2) { // lover
+				return {type: "lover", slave: partner};
+			} else { // friend
+				return {type: "friend", slave: partner};
+			}
+		}
+	}
+	if (V.seeIncest === 1) { // close family member
+		const partner = randomRelatedSlave(slave, validVisitingPartner);
+		if (partner) {
+			return {type: "family", slave: partner};
+		}
+	}
+	if (S.Nurse && App.Utils.sexAllowed(S.Nurse, slave)) {
+		return {type: "nurse"};
+	}
+	return {type: null};
+};
+
+/** Determines which employees a given facility leader is having sex with this week
+ * @param {App.Entity.Facilities.Facility} facility
+ * @returns {Set<number>}
+ */
+App.EndWeek.getFLSex = function(facility) {
+	/** @type {Set<number>} */
+	const employeeSex = new Set();
+	const fl = facility.manager ? facility.manager.currentEmployee : null;
+	if (fl && App.Data.misc.sexFromDevelopmentLeaders.includes[fl.assignment]) {
+		const hornyEmployees = facility.employees().filter((s) => s.devotion >= -50 /* not unhappy */ && s.energy > 20 /* not frigid */);
+		for (const emp of hornyEmployees) {
+			if (fl.assignment === Job.NURSE && App.EndWeek.getClinicPartner(emp).type !== "nurse") {
+				continue; // nurse is busy, will not have sex with patients who are satisfied by someone else
+			}
+			if (fl.assignment === Job.WARDEN && emp.relationship === -3) {
+				continue; // wardeness will never molest the PC's spouse
+			}
+			if (App.Utils.sexAllowed(emp, fl)) { // no sex with a slave you've forbidden them to fuck
+				employeeSex.add(emp.ID);
+			}
+		}
+	}
+	return employeeSex;
+};
diff --git a/src/endWeek/reports/nurseryReport.js b/src/endWeek/reports/nurseryReport.js
index 9d9bfdb6df6..7b6c1618125 100644
--- a/src/endWeek/reports/nurseryReport.js
+++ b/src/endWeek/reports/nurseryReport.js
@@ -14,6 +14,8 @@ App.Facilities.Nursery.nurseryReport = function nurseryReport() {
 	let
 		matronBonus = 0;
 
+	V.flSex = App.EndWeek.getFLSex(App.Entity.facilities.nursery); // FIXME: should be local, passed as a parameter to saRules
+
 	function matronChanges() {
 		if (S.Matron) {
 			S.Matron.devotion += devBonus;
diff --git a/src/endWeek/schoolroomReport.js b/src/endWeek/schoolroomReport.js
index c42cf4e6009..1695ad78953 100644
--- a/src/endWeek/schoolroomReport.js
+++ b/src/endWeek/schoolroomReport.js
@@ -3,6 +3,7 @@ App.EndWeek.schoolroomReport = function() {
 
 	const slaves = App.Utils.sortedEmployees(App.Entity.facilities.schoolroom);
 	const devBonus = (V.schoolroomDecoration !== "standard") ? 1 : 0;
+	V.flSex = App.EndWeek.getFLSex(App.Entity.facilities.schoolroom); // FIXME: should be local, passed as a parameter to saRules
 
 	function schoolteacherText() {
 		let r = [];
diff --git a/src/endWeek/slaveAssignmentReport.js b/src/endWeek/slaveAssignmentReport.js
index 4a34bc39245..9f0ef46ffda 100644
--- a/src/endWeek/slaveAssignmentReport.js
+++ b/src/endWeek/slaveAssignmentReport.js
@@ -407,6 +407,7 @@ App.EndWeek.slaveAssignmentReport = function() {
 
 	/* Clean up global SA variables */
 	App.EndWeek.saVars = null;
+	delete V.flSex; // FIXME: remove, once this is passed as a parameter to saRules
 
 	return res;
 
diff --git a/src/interaction/siRules.js b/src/interaction/siRules.js
index f67710899e4..83d24881c12 100644
--- a/src/interaction/siRules.js
+++ b/src/interaction/siRules.js
@@ -12,7 +12,7 @@ App.UI.SlaveInteract.rules = function(slave) {
 		let choices;
 		const {
 			He, His,
-			he, him, his
+			he, him, his, himself
 		} = getPronouns(slave);
 
 		if (V.seePreg !== 0) {
@@ -132,7 +132,7 @@ App.UI.SlaveInteract.rules = function(slave) {
 				});
 			}
 
-			p.append(listChoices(choices, "living"));
+			p.append(' ', listChoices(choices, "living"));
 		}
 
 		// Rest
@@ -147,7 +147,7 @@ App.UI.SlaveInteract.rules = function(slave) {
 				{value: "permissive"},
 				{value: "mandatory"},
 			];
-			div.append(listChoices(choices, "rest"));
+			div.append(' ', listChoices(choices, "rest"));
 			p.append(div);
 		}
 
@@ -162,7 +162,7 @@ App.UI.SlaveInteract.rules = function(slave) {
 				{value: "restrictive"},
 				{value: "permissive"},
 			];
-			div.append(listChoices(choices, "mobility"));
+			div.append(' ', listChoices(choices, "mobility"));
 			p.append(div);
 		}
 
@@ -176,7 +176,7 @@ App.UI.SlaveInteract.rules = function(slave) {
 			{value: "chastity"},
 			{value: "situational"},
 		];
-		div.append(listChoices(choices, "punishment"));
+		div.append(' ', listChoices(choices, "punishment"));
 		p.append(div);
 
 		// Reward
@@ -189,7 +189,7 @@ App.UI.SlaveInteract.rules = function(slave) {
 			{value: "orgasm"},
 			{value: "situational"},
 		];
-		div.append(listChoices(choices, "reward"));
+		div.append(' ', listChoices(choices, "reward"));
 		p.append(div);
 
 		// Lactation
@@ -219,7 +219,7 @@ App.UI.SlaveInteract.rules = function(slave) {
 					}
 				);
 			}
-			div.append(listChoices(choices, "lactation"));
+			div.append(' ', listChoices(choices, "lactation"));
 			p.append(div);
 		}
 
@@ -242,7 +242,7 @@ App.UI.SlaveInteract.rules = function(slave) {
 					{value: "language lessons"},
 				);
 			}
-			div.append(listChoices(choices, "speech"));
+			div.append(' ', listChoices(choices, "speech"));
 			p.append(div);
 		}
 
@@ -254,7 +254,7 @@ App.UI.SlaveInteract.rules = function(slave) {
 			{value: "just friends"},
 			{value: "permissive"},
 		];
-		div.append(listChoices(choices, "relationship"));
+		div.append(' ', listChoices(choices, "relationship"));
 		p.append(div);
 
 		p.append(smartSettings(slave));
@@ -292,13 +292,14 @@ App.UI.SlaveInteract.rules = function(slave) {
 			title.textContent = `Non-assignment orgasm rules: `;
 			el.append(title);
 
-			makeLinks("Masturbation", "rules.release.masturbation");
-			makeLinks("Partner", "rules.release.partner");
-			makeLinks("Family", "rules.release.family");
-			makeLinks("Other slaves", "rules.release.slaves");
-			makeLinks("Master", "rules.release.master", true);
+			makeLinks("Masturbation", "rules.release.masturbation", `Controls whether ${he} is allowed to pleasure ${himself}, should ${he} feel the need`);
+			makeLinks("Partner", "rules.release.partner", `Controls whether ${he} is allowed sexual contact with ${his} romantic partner, should you permit ${him} to have one`);
+			makeLinks("Facility Leader", "rules.release.facilityLeader", `Controls whether development facility leaders (Nurse, Attendant, etc) are allowed to satisfy ${his} sexual needs while ${he} is assigned to their facility; does not apply to production facilities`);
+			makeLinks("Family", "rules.release.family", `Controls whether ${he} is allowed sexual contact with close family members`);
+			makeLinks("Other slaves", "rules.release.slaves", `Controls whether ${he} is allowed sexual contact with your other slaves that do not fit any of the above categories`);
+			makeLinks("Master", "rules.release.master", `Controls whether you will fuck ${him} personally when ${he} needs it`, true);
 
-			function makeLinks(text, setting, master = false) {
+			function makeLinks(text, setting, note, master = false) {
 				const options =
 					[{text: master ? `Grant` : `Allow`, updateSlave: {[setting]: 1}},
 						{text: master ? `Deny` : `Forbid`, updateSlave: {[setting]: 0}}];
@@ -307,6 +308,7 @@ App.UI.SlaveInteract.rules = function(slave) {
 				links.append(`${text}: `);
 				links.append(status(_.get(slave, setting), master));
 				links.appendChild(App.UI.SlaveInteract.generateRows(options, slave, "", false, refresh));
+				App.UI.DOM.appendNewElement('span', links, ' ' + note, "note");
 				links.className = "choices";
 				el.append(links);
 			}
diff --git a/src/js/DefaultRules.js b/src/js/DefaultRules.js
index e297af1c02f..071fa076479 100644
--- a/src/js/DefaultRules.js
+++ b/src/js/DefaultRules.js
@@ -1943,6 +1943,7 @@ globalThis.DefaultRules = (function() {
 			};
 			changed = processReleaseProp('masturbation') || false;
 			changed = processReleaseProp('partner') || false;
+			changed = processReleaseProp('facilityLeader') || false;
 			changed = processReleaseProp('family') || false;
 			changed = processReleaseProp('slaves') || false;
 			changed = processReleaseProp('master') || false;
diff --git a/src/js/SlaveState.js b/src/js/SlaveState.js
index 1851c144838..971a9635cc3 100644
--- a/src/js/SlaveState.js
+++ b/src/js/SlaveState.js
@@ -15,7 +15,10 @@ App.Entity.ReleaseRulesState = class ReleaseRulesState {
 		this.masturbation = 0;
 		/** Can the slave fuck her romantic partner (relationship = FWB or higher)?
 		 * @type {FC.Bool} */
-		this.partner = 0;
+		this.partner = 1;
+		/** Can the slave's development facility leader (Nurse, Attendant, etc) fuck her if she needs it?
+		 * @type {FC.Bool} */
+		this.facilityLeader = 1;
 		/** Can the slave fuck her close family members (siblings/parents/children)?
 		 * @type {FC.Bool} */
 		this.family = 0;
diff --git a/src/js/releaseRules.js b/src/js/releaseRules.js
index 68d708532c9..fd443122021 100644
--- a/src/js/releaseRules.js
+++ b/src/js/releaseRules.js
@@ -5,13 +5,17 @@
  * @returns {boolean}
  */
 App.Utils.sexAllowed = function sexAllowed(slaveA, slaveB) {
-	/* check most specific to least specific - master, partner, family, slaves */
+	/* check most specific to least specific - master, partner, leader, family, slaves */
 	if (slaveA === V.PC) {
 		return slaveB.rules.release.master === 1;
 	} else if (slaveB === V.PC) {
 		return slaveA.rules.release.master === 1;
 	} else if (haveRelationshipP(slaveA, slaveB)) {
 		return (slaveA.rules.release.partner === 1) && (slaveB.rules.release.partner === 1);
+	} else if (App.Utils.isDevelopmentFacilityLeader(slaveA, slaveB)) {
+		return slaveA.rules.release.facilityLeader === 1;
+	} else if (App.Utils.isDevelopmentFacilityLeader(slaveB, slaveA)) {
+		return slaveB.rules.release.facilityLeader === 1;
 	} else if (areRelated(slaveA, slaveB)) {
 		return V.seeIncest && (slaveA.rules.release.family === 1) && (slaveB.rules.release.family === 1);
 	} else {
@@ -59,6 +63,23 @@ App.Utils.releaseRestricted = function releaseRestricted(slave) {
 	return (slave.rules.release.slaves === 0) || (slave.rules.release.family === 0) || (slave.rules.release.masturbation === 0) || (slave.rules.release.partner === 0);
 };
 
+/**
+ * Returns true if employee is an employee of a development facility, and leader manages that same facility. Non-commutative!
+ * @param {App.Entity.SlaveState} employee
+ * @param {App.Entity.SlaveState} leader
+ * @returns {boolean}
+ */
+App.Utils.isDevelopmentFacilityLeader = function(employee, leader) {
+	if (!App.Data.misc.sexFromDevelopmentLeaders.includes(leader.assignment)) {
+		return false;
+	}
+	try {
+		return (leader.ID === App.Utils.jobForAssignment(employee.assignment).facility.manager.currentEmployee.ID);
+	} catch {
+		return false; // could mean no job for assignment, or facility does not have a manager assigned...doesn't matter, we're only interested in matches
+	}
+};
+
 /**
  * Returns a short summary of the slave's release rules
  * @param {App.Entity.SlaveState} slave
@@ -152,7 +173,7 @@ App.Utils.releaseSummaryLong = function releaseSummaryLong(slave) {
 App.Desc.releaseDesc = function releaseDesc(slave) {
 	const rel = slave.rules.release;
 	const includeFamily = (rel.family === 1) && (V.seeIncest === 1);
-	const {He, he, his} = getPronouns(slave);
+	const {He, he, his, him} = getPronouns(slave);
 	let r = "and ";
 	let appendFrequency = false;
 	if (rel.masturbation === 0 && rel.partner === 0 && !includeFamily && rel.slaves === 0 && rel.master === 0) {
@@ -230,6 +251,9 @@ App.Desc.releaseDesc = function releaseDesc(slave) {
 			r += ` which ${he} is rarely willing to do.`;
 		}
 	}
+	if (rel.facilityLeader === 0) {
+		r += ` Your slave leaders have been told not to touch ${him}, no matter how much ${he} begs.`;
+	} // else case is "default state", no text
 	return r;
 };
 
diff --git a/src/js/rulesAssistant.js b/src/js/rulesAssistant.js
index ac88e556abc..febf9cf4b3a 100644
--- a/src/js/rulesAssistant.js
+++ b/src/js/rulesAssistant.js
@@ -312,6 +312,7 @@ App.RA.newRule = function() {
 		return {
 			masturbation: null,
 			partner: null,
+			facilityLeader: null,
 			family: null,
 			slaves: null,
 			master: null
diff --git a/src/js/rulesAssistantOptions.js b/src/js/rulesAssistantOptions.js
index 0791f92114b..fb79af53893 100644
--- a/src/js/rulesAssistantOptions.js
+++ b/src/js/rulesAssistantOptions.js
@@ -1647,6 +1647,7 @@ globalThis.rulesAssistantOptions = (function() {
 			this.appendChild(new RewardList());
 			this.appendChild(new ReleaseMasturbationSwitch());
 			this.appendChild(new ReleasePartnerSwitch());
+			this.appendChild(new ReleaseFacilityLeaderSwitch());
 			this.appendChild(new ReleaseFamilySwitch());
 			this.appendChild(new ReleaseSlavesSwitch());
 			this.appendChild(new ReleaseMasterSwitch());
@@ -2667,6 +2668,18 @@ globalThis.rulesAssistantOptions = (function() {
 		}
 	}
 
+	class ReleaseFacilityLeaderSwitch extends RadioSelector {
+		constructor() {
+			const pairs = [
+				["Allowed", 1],
+				["Forbidden", 0],
+			];
+			super("Sex with development facility leaders", pairs);
+			this.setValue(current_rule.set.releaseRules.facilityLeader);
+			this.onchange = (value) => current_rule.set.releaseRules.facilityLeader = value;
+		}
+	}
+
 	class ReleaseFamilySwitch extends RadioSelector {
 		constructor() {
 			const pairs = [
diff --git a/src/npc/surgery/surgery.js b/src/npc/surgery/surgery.js
index 43be494f1dd..7b13dac8462 100644
--- a/src/npc/surgery/surgery.js
+++ b/src/npc/surgery/surgery.js
@@ -1094,6 +1094,7 @@ globalThis.beginFuckdoll = function(slave) {
 	slave.rules.living = "spare";
 	slave.rules.speech = "restrictive";
 	slave.rules.release.masturbation = 0;
+	slave.rules.release.facilityLeader = 0;
 	slave.rules.release.partner = 0;
 	slave.rules.release.family = 0;
 	slave.rules.release.slaves = 0;
diff --git a/src/player/js/PlayerState.js b/src/player/js/PlayerState.js
index fa6785ecd5f..c41c21ccbf7 100644
--- a/src/player/js/PlayerState.js
+++ b/src/player/js/PlayerState.js
@@ -39,6 +39,8 @@ App.Entity.PlayerReleaseRulesState = class {
 		this.masturbation = 1;
 		/** Can you fuck your romantic partner (relationship = FWB or higher)? */
 		this.partner = 1;
+		/** Can a developemnt facility leader (Nurse, Attendant, etc) fuck you if you need it? */
+		this.facilityLeader = 1;
 		/** Can you fuck your close family members (siblings/parents/children)? */
 		this.family = 1;
 		/** Can you fuck the general slave population? */
diff --git a/src/uncategorized/cellblockReport.tw b/src/uncategorized/cellblockReport.tw
index fb2f6af05f8..7c0555e8fce 100644
--- a/src/uncategorized/cellblockReport.tw
+++ b/src/uncategorized/cellblockReport.tw
@@ -9,6 +9,7 @@
 	<<set _devBonus = 0>>
 <</if>>
 <<set _cellblockNameCaps = capFirstChar($cellblockName)>>
+<<set $flSex = App.EndWeek.getFLSex(App.Entity.facilities.cellblock)>> /* FIXME: should be local, passed as a parameter to saRules */
 
 <<if (_S.Wardeness)>>
 	<<setLocalPronouns _S.Wardeness>>
diff --git a/src/uncategorized/saRules.tw b/src/uncategorized/saRules.tw
index 21365a00a28..fc59461007d 100644
--- a/src/uncategorized/saRules.tw
+++ b/src/uncategorized/saRules.tw
@@ -589,7 +589,7 @@
 
 			<<= App.SlaveAssignment.rewardAndPunishment($slaves[$i])>>
 		<<case "be the Nurse">>
-			<<set $slaves[$i].need -= (App.Entity.facilities.clinic.employeesIDs().size*3)>>
+			<<set $slaves[$i].need -= ($flSex.size*3)>>
 			<<if $slaves[$i].energy <= 20>>
 				is frigid and has little interest in getting off<<if App.Utils.releaseRestricted($slaves[$i])>>, making the rule restricting $his sexual outlets superfluous<</if>>.
 				<<set $slaves[$i].need = 0>>
@@ -670,78 +670,83 @@
 
 			<<= App.SlaveAssignment.rewardAndPunishment($slaves[$i])>>
 		<<case "get treatment in the clinic">>
-			<<if $slaves[$i].relationship > 2>>
-				<<set _lover = $slaveIndices[$slaves[$i].relationshipTarget]>>
-				<<if !canMove($slaves[_lover]) || !isSlaveAvailable($slaves[_lover])>>
-					<<set _lover = 0>>
-				<</if>>
-			<<else>>
-				<<set _lover = 0>>
-			<</if>>
-			<<if _lover == 0 && $seeIncest == 1>>
-				<<set _famVisitor = randomRelatedSlave($slaves[$i], (s) => { return canMove(s) && isSlaveAvailable(s) && App.Utils.sexAllowed($slaves[$i], s); } )>>
-			<</if>>
 			<<if $slaves[$i].devotion < -50>>
 				is so unhappy that $he has little interest in getting off<<if App.Utils.releaseRestricted($slaves[$i])>>, making the rule restricting $his sexual outlets superfluous<</if>>.
 				<<set $slaves[$i].need = 0>>
 			<<elseif $slaves[$i].energy <= 20>>
 				is frigid and has little interest in getting off<<if App.Utils.releaseRestricted($slaves[$i])>>, making the rule restricting $his sexual outlets superfluous<</if>>.
 				<<set $slaves[$i].need = 0>>
-			<<elseif $slaves[$i].relationship == -3>>
-				is well taken care of during $his stay in $clinicName; you make sure your $wife's every sexual need is handled personally.
-				<<set $slaves[$i].need = 0>>
-				<<if canImpreg($slaves[$i], $PC) && (($slaves[$i].vagina > 0 && $slaves[$i].ovaries == 1)||($slaves[$i].anus != 0 && $slaves[$i].mpreg == 1))>>
-					<<= knockMeUp($slaves[$i], 10, 0, -1, 1)>>
-					<<if ($slaves[$i].vagina > 0 && $slaves[$i].ovaries == 1)>>
-						<<set $slaves[$i].counter.vaginal += 7, $vaginalTotal += 7>>
-					<<else>>
-						<<set $slaves[$i].counter.anal += 7, $analTotal += 7>>
-					<</if>>
-					<<if $slaves[$i].preg > 0>>
-						It comes as little surprise when routine health checks start to show @@.lime;$he's pregnant!@@
-					<</if>>
-				<</if>>
-			<<elseif _lover != 0>>
-				<<setLocalPronouns $slaves[_lover] 2>>
-				<<set $slaves[$i].need = 0>>
-				is well taken care of during $his stay in $clinicName; $his <<if $slaves[$i].relationship == 3>>friend with benefits<<elseif $slaves[$i].relationship == 4>>sweetheart<<else>>_wife2<</if>> frequently stops by when $he gets the chance to make sure $his sexual needs are properly handled.
-				<<set $slaves[_lover].counter.oral += 14, $oralTotal += 14>>
-			<<elseif (def _famVisitor)>>
-				is well-loved by $his family; this week, $his <<= relativeTerm($slaves[$i], _famVisitor)>> @@.lightgreen;_famVisitor.slaveName@@ pays special attention to $him, making sure $his sexual needs are met.
-				<<set $slaves[$i].need = 0>>
-				<<set _famVisitor.counter.oral += 7, $oralTotal += 7>>
-			<<elseif _S.Nurse>>
-				is routinely brought to orgasm by _S.Nurse.slaveName as part of $his duties.
-				<<if canPenetrate($slaves[$i]) && _S.Nurse.boobs >= 500>>
-					<<run actX(_S.Nurse, "mammary", 14)>>
-				<<else>>
-					<<run actX(_S.Nurse, "oral", 14)>>
-					/* possible cumflation code here */
-				<</if>>
-				<<set $slaves[$i].need -= 60>>
-			<<elseif _release.masturbation === 1>>
-				<<if ($slaves[$i].devotion <= 20) && ($slaves[$i].trust >= -20)>>
-					takes solace in $his permission to masturbate rather than being forced to seek other means of release, @@.mediumaquamarine;reducing $his fear@@ of you.
-					<<set $slaves[$i].trust += 2, $slaves[$i].need = 0>>
-				<<elseif ($slaves[$i].devotion <= 20)>>
-					enjoys being allowed to masturbate rather than having to seek other means of release, @@.mediumaquamarine;slightly reducing $his fear@@ of you but @@.mediumorchid;allowing $him to remain in control of $him sexuality.@@
-					<<set $slaves[$i].trust += 1, $slaves[$i].devotion -= 1, $slaves[$i].need = 0>>
-				<<elseif ($slaves[$i].devotion <= 50)>>
-					accepts having to relieve $himself solely through masturbation.
-					<<set $slaves[$i].need = 0>>
-				<<else>>
-					is a little disappointed that $he's limited to $his <<if !hasAnyArms($slaves[$i])>>imagination<<else>>hand<<if hasBothArms($slaves[$i])>>s<</if>><</if>> and toys, but @@.mediumaquamarine;understands you care about $his current health.@@
-					<<set $slaves[$i].trust += 1, $slaves[$i].need = 0>>
-				<</if>>
-				<<if $slaves[$i].devotion > 20>>
-					When $he does play with $himself, $he
-					<<includeDOM App.EndWeek.Rules.masturbationFetishPlay($slaves[$i])>>
-					<<includeDOM App.EndWeek.Rules.masturbationDiscoversFetish($slaves[$i])>>
-				<</if>>
-				<<includeDOM App.EndWeek.Rules.masturbationDrugEffects($slaves[$i])>>
 			<<else>>
-				eventually gives in to $his urges and is @@.gold;punished@@ for illicit masturbation.
-				<<set $slaves[$i].trust -= 2, $slaves[$i].need -= 10>>
+				<<set _partner = App.EndWeek.getClinicPartner($slaves[$i])>>
+				<<switch _partner.type>>
+					<<case "player">>
+						is well taken care of during $his stay in $clinicName; you make sure your $wife's every sexual need is handled personally.
+						<<set $slaves[$i].need = 0>>
+						<<if canImpreg($slaves[$i], $PC) && (($slaves[$i].vagina > 0 && $slaves[$i].ovaries == 1)||($slaves[$i].anus != 0 && $slaves[$i].mpreg == 1))>>
+							<<= knockMeUp($slaves[$i], 10, 0, -1, 1)>>
+							<<if ($slaves[$i].vagina > 0 && $slaves[$i].ovaries == 1)>>
+								<<set $slaves[$i].counter.vaginal += 7, $vaginalTotal += 7>>
+							<<else>>
+								<<set $slaves[$i].counter.anal += 7, $analTotal += 7>>
+							<</if>>
+							<<if $slaves[$i].preg > 0>>
+								It comes as little surprise when routine health checks start to show @@.lime;$he's pregnant!@@
+							<</if>>
+						<</if>>
+					<<case "lover">>
+						<<setLocalPronouns _partner.slave 2>>
+						<<set $slaves[$i].need = 0>>
+						is well taken care of during $his stay in $clinicName; $his <<if $slaves[$i].relationship == 3>>friend with benefits<<elseif $slaves[$i].relationship == 4>>sweetheart<<else>>_wife2<</if>> frequently stops by when $he gets the chance to make sure $his sexual needs are properly handled.
+						<<set _partner.slave.counter.oral += 14, $oralTotal += 14>>
+					<<case "family">>
+						is well-loved by $his family; this week, $his <<= relativeTerm($slaves[$i], _partner.slave)>> @@.lightgreen;_partner.slave.slaveName@@ pays special attention to $him, making sure $his sexual needs are met.
+						<<set $slaves[$i].need = 0>>
+						<<set _partner.slave.counter.oral += 7, $oralTotal += 7>>
+					<<case "friend">>
+						is friends with @@.lightgreen;_partner.slave.slaveName,@@ who comes to visit $him regularly. $His sexual frustration from being confined to the clinic shows, and _partner.slave.slaveName often winds up helping $him get relief.
+						<<if _partner.slave.rules.relationship === "permissive" && $slaves[$i].rules.relationship === "permissive">>
+							They have @@.lightgreen;become lovers.@@
+							<<set $slaves[$i].relationship = 3, _partner.slave.relationship = 3>>
+						<<else>>
+							They know it your rules prevent them from becoming anything more, but they enjoy themselves anyway.
+						<</if>>
+						<<set $slaves[$i].need = 0>>
+						<<set _partner.slave.counter.oral += 7, $oralTotal += 7>>
+					<<case "nurse">>
+						is routinely brought to orgasm by _S.Nurse.slaveName as part of $his duties.
+						<<if canPenetrate($slaves[$i]) && _S.Nurse.boobs >= 500>>
+							<<run seX(_S.Nurse, "mammary", $slaves[$i], "penetrative", 14)>>
+						<<else>>
+							<<run actX(_S.Nurse, "oral", 14)>>
+							/* possible cumflation code here */
+						<</if>>
+						<<set $slaves[$i].need -= 60>>
+					<<default>>
+						<<if _release.masturbation === 1>>
+							<<if ($slaves[$i].devotion <= 20) && ($slaves[$i].trust >= -20)>>
+								takes solace in $his permission to masturbate rather than being forced to seek other means of release, @@.mediumaquamarine;reducing $his fear@@ of you.
+								<<set $slaves[$i].trust += 2, $slaves[$i].need = 0>>
+							<<elseif ($slaves[$i].devotion <= 20)>>
+								enjoys being allowed to masturbate rather than having to seek other means of release, @@.mediumaquamarine;slightly reducing $his fear@@ of you but @@.mediumorchid;allowing $him to remain in control of $him sexuality.@@
+								<<set $slaves[$i].trust += 1, $slaves[$i].devotion -= 1, $slaves[$i].need = 0>>
+							<<elseif ($slaves[$i].devotion <= 50)>>
+								accepts having to relieve $himself solely through masturbation.
+								<<set $slaves[$i].need = 0>>
+							<<else>>
+								is a little disappointed that $he's limited to $his <<if !hasAnyArms($slaves[$i])>>imagination<<else>>hand<<if hasBothArms($slaves[$i])>>s<</if>><</if>> and toys, but @@.mediumaquamarine;understands you care about $his current health.@@
+								<<set $slaves[$i].trust += 1, $slaves[$i].need = 0>>
+							<</if>>
+							<<if $slaves[$i].devotion > 20>>
+								When $he does play with $himself, $he
+								<<includeDOM App.EndWeek.Rules.masturbationFetishPlay($slaves[$i])>>
+								<<includeDOM App.EndWeek.Rules.masturbationDiscoversFetish($slaves[$i])>>
+							<</if>>
+							<<includeDOM App.EndWeek.Rules.masturbationDrugEffects($slaves[$i])>>
+						<<else>>
+							eventually gives in to $his urges and is @@.gold;punished@@ for illicit masturbation.
+							<<set $slaves[$i].trust -= 2, $slaves[$i].need -= 10>>
+						<</if>>
+				<</switch>>
 			<</if>>
 
 			<<= App.EndWeek.Rules.speechRules($slaves[$i])>>
@@ -870,7 +875,11 @@
 
 			<<= App.SlaveAssignment.rewardAndPunishment($slaves[$i])>>
 		<<case "be the Wardeness">>
-			<<set $slaves[$i].need -= (_L.cellblock*5)>>
+			<<if _S.Wardeness.fetish == "mindbroken">>
+				<<set $slaves[$i].need -= (_L.cellblock*5)>>
+			<<else>>
+				<<set $slaves[$i].need -= ($flSex.size*5)>>
+			<</if>>
 			<<if $slaves[$i].energy <= 20>>
 				is frigid and has little interest in getting off<<if App.Utils.releaseRestricted($slaves[$i])>>, making the rule restricting $his sexual outlets superfluous<</if>>.
 				<<set $slaves[$i].need = 0>>
@@ -916,7 +925,8 @@
 
 			<<= App.SlaveAssignment.rewardAndPunishment($slaves[$i])>>
 		<<case "be confined in the cellblock">>
-			<<if _S.Wardeness && canPenetrate(_S.Wardeness)>>
+			<<set _wardenFunTimes = 0>>
+			<<if $flSex.has($slaves[$i].ID) || _S.Wardeness.fetish == "mindbroken">>
 				<<set _wardenFunTimes = random(0,5)>>
 				<<set $slaves[$i].need -= (10*_wardenFunTimes)>>
 			<</if>>
@@ -929,32 +939,27 @@
 			<<elseif $slaves[$i].relationship == -3>>
 				You make sure your troublesome $wife's sexual needs are handled, openly, in the middle of $cellblockName, where everyone can see, hear, and smell your dominance.
 				<<set $slaves[$i].need = 0>>
+				<<if canDoVaginal($slaves[$i]) && $slaves[$i].vagina > 0>>
+					<<run seX($PC, "penetrative", $slaves[$i], "vaginal", 7)>>
+				<<elseif canDoAnal($slaves[$i]) && $slaves[$i].anus > 0>>
+					<<run seX($PC, "penetrative", $slaves[$i], "anal", 7)>>
+				<<else>>
+					<<run seX($PC, "penetrative", $slaves[$i], "oral", 7)>>
+				<</if>>
 				<<if canImpreg($slaves[$i], $PC) && (($slaves[$i].vagina > 0 && $slaves[$i].ovaries == 1)||($slaves[$i].anus != 0 && $slaves[$i].mpreg == 1))>>
 					<<= knockMeUp($slaves[$i], 10, 0, -1, 1)>>
-					<<if ($slaves[$i].vagina > 0 && $slaves[$i].ovaries == 1)>>
-						<<set $slaves[$i].counter.vaginal += 7, $vaginalTotal += 7>>
-					<<else>>
-						<<set $slaves[$i].counter.anal += 7, $analTotal += 7>>
-					<</if>>
 					<<if $slaves[$i].preg > 0>>
 						As an added show, you @@.lime;proudly display $his positive pregnancy@@ test for all to see.
 					<</if>>
-				<<elseif canDoVaginal($slaves[$i]) && $slaves[$i].vagina > 0>>
-					<<set $slaves[$i].counter.vaginal += 7, $vaginalTotal += 7>>
-				<<elseif canDoAnal($slaves[$i]) && $slaves[$i].anus > 0>>
-					<<set $slaves[$i].counter.anal += 7, $analTotal += 7>>
 				<</if>>
 			<<else>>
-				<<if _S.Wardeness && canPenetrate(_S.Wardeness)>>
-					<<run SimpleSexAct.Slave($slaves[$i], _wardenFunTimes)>>
-					<<run actX(_S.Wardeness, "penetrative", _wardenFunTimes)>>
+				<<if _wardenFunTimes > 0>>
+					<<run SimpleSexAct.Slaves($slaves[$i], _S.Wardeness, _wardenFunTimes)>>
 					<<if _wardenFunTimes > 0 && canImpreg($slaves[$i], _S.Wardeness) && ($cellblockWardenCumsInside == 1 || _S.Wardeness.fetish == "mindbroken")>>
-						<<if ($slaves[$i].vagina > 0 && $slaves[$i].ovaries == 1)>>
+						<<if (canDoVaginal($slaves[$i]) && $slaves[$i].vagina > 0 && $slaves[$i].ovaries == 1)>>
 							<<= knockMeUp($slaves[$i], 10, 0, $WardenessID, 1)>>
-							<<run seX($slaves[$i], "vaginal", _S.Wardeness, 1)>>
-						<<elseif ($slaves[$i].anus > 0 && $slaves[$i].mpreg == 1)>>
+						<<elseif (canDoAnal($slaves[$i]) && $slaves[$i].anus > 0 && $slaves[$i].mpreg == 1)>>
 							<<= knockMeUp($slaves[$i], 10, 1, $WardenessID, 1)>>
-							<<run seX($slaves[$i], "anal", _S.Wardeness, 1)>>
 						<</if>>
 					<</if>>
 				<</if>>
@@ -1037,7 +1042,7 @@
 
 			<<= App.SlaveAssignment.rewardAndPunishment($slaves[$i])>>
 		<<case "be the Attendant">>
-			<<set $slaves[$i].need -= (App.Entity.facilities.spa.employeesIDs().size*3)>>
+			<<set $slaves[$i].need -= ($flSex.size*3)>>
 			<<if $slaves[$i].energy <= 20>>
 				is frigid and has little interest in getting off<<if App.Utils.releaseRestricted($slaves[$i])>>, making the rule restricting $his sexual outlets superfluous<</if>>.
 				<<set $slaves[$i].need = 0>>
@@ -1119,7 +1124,7 @@
 			<<elseif $slaves[$i].energy <= 20>>
 				is frigid and has little interest in getting off.
 				<<set $slaves[$i].need = 0>>
-			<<elseif _S.Attendant>>
+			<<elseif $flSex.has($slaves[$i].ID)>>
 				is routinely relieved of any built up tension by _S.Attendant.slaveName and $his
 				<<if canPenetrate($slaves[$i]) && _S.Attendant.boobs >= 500>>
 					luscious breasts.
@@ -1282,7 +1287,7 @@
 
 			<<= App.SlaveAssignment.rewardAndPunishment($slaves[$i])>>
 		<<case "be the Matron">>
-			<<set $slaves[$i].need -= (_L.nursery*3)>>
+			<<set $slaves[$i].need -= ($flSex.size*3)>>
 			<<if $slaves[$i].energy <= 20>>
 				is frigid and has little interest in getting off<<if App.Utils.releaseRestricted($slaves[$i])>>, making the rule restricting $his sexual outlets superfluous<</if>>.
 				<<set $slaves[$i].need = 0>>
@@ -1361,7 +1366,7 @@
 			<<elseif $slaves[$i].energy <= 20>>
 				is frigid and has little interest in getting off.
 				<<set $slaves[$i].need = 0>>
-			<<elseif _S.Matron>>
+			<<elseif $flSex.has($slaves[$i].ID)>>
 				is routinely relieved of any built up tension by _S.Matron.slaveName and $his
 				<<if canPenetrate($slaves[$i]) && _S.Matron.boobs >= 500>>
 					luscious breasts.
@@ -1521,18 +1526,7 @@
 
 			<<= App.SlaveAssignment.rewardAndPunishment($slaves[$i])>>
 		<<case "be the Schoolteacher">>
-			<<set $slaves[$i].need -= _L.schoolroom*10>>
-			<<set _sexLessons = _L.schoolroom*2>>
-			<<if canDoVaginal($slaves[$i]) && $slaves[$i].vagina != 0>>
-				<<set $slaves[$i].counter.vaginal += _sexLessons, $vaginalTotal += _sexLessons>>
-			<</if>>
-			<<if canDoAnal($slaves[$i]) && $slaves[$i].anus != 0>>
-				<<set $slaves[$i].counter.anal += _sexLessons, $analTotal += _sexLessons>>
-			<</if>>
-			<<if canPenetrate($slaves[$i])>>
-				<<set $slaves[$i].counter.penetrative += _sexLessons, $penetrativeTotal += _sexLessons>>
-			<</if>>
-			<<set $slaves[$i].counter.oral += _sexLessons, $oralTotal += _sexLessons>>
+			<<set $slaves[$i].need -= ($flSex.size*10)>>
 			<<if $slaves[$i].energy <= 20>>
 				is frigid and has little interest in getting off<<if App.Utils.releaseRestricted($slaves[$i])>>, making the rule restricting $his sexual outlets superfluous<</if>>.
 				<<set $slaves[$i].need = 0>>
@@ -1611,9 +1605,12 @@
 
 			<<= App.SlaveAssignment.rewardAndPunishment($slaves[$i])>>
 		<<case "learn in the schoolroom">>
-			<<if _S.Schoolteacher>>
+			<<if $flSex.has($slaves[$i].ID)>>
 				<<set $slaves[$i].need -= 30>>
-				<<run actX($slaves[$i], "oral", 7), actX($slaves[$i], "mammary", 7)>>
+				<<run seX($slaves[$i], "oral", _S.Schoolteacher, "oral", 7)>>
+				<<if canPenetrate(_S.Schoolteacher) && $slaves[$i].boobs > 500>>
+					<<run seX($slaves[$i], "mammary", _S.Schoolteacher, "penetrative", 7)>>
+				<</if>>
 				<<if canDoVaginal($slaves[$i])>>
 					<<if $slaves[$i].vagina != 0>>
 						<<run seX(_S.Schoolteacher, "penetrative", $slaves[$i], "vaginal", 7)>>
diff --git a/src/uncategorized/spaReport.tw b/src/uncategorized/spaReport.tw
index 0344e2cfc87..19d64cce398 100644
--- a/src/uncategorized/spaReport.tw
+++ b/src/uncategorized/spaReport.tw
@@ -8,6 +8,7 @@
 <<else>>
 	<<set _devBonus = 0>>
 <</if>>
+<<set $flSex = App.EndWeek.getFLSex(App.Entity.facilities.spa)>> /* FIXME: should be local, passed as a parameter to saRules */
 
 <<if $AttendantID != 0>>
 	<<if (_S.Attendant.health.condition < 100)>>
-- 
GitLab