Skip to content
Snippets Groups Projects
assignJS.js 26.2 KiB
Newer Older
  • Learn to ignore specific revisions
  • brickode's avatar
    brickode committed
    /* eslint-disable no-case-declarations */
    
    Skriv's avatar
    Skriv committed
    
    /**
     * @param {App.Entity.SlaveState} slave
    
     * @param {FC.Assignment} job
    
    Skriv's avatar
    Skriv committed
     * @returns {string}
     */
    
    globalThis.assignJob = function(slave, job) {
    
    vas's avatar
    vas committed
    	"use strict";
    
    vas's avatar
    vas committed
    	let r = "";
    
    	const oldJob = slave.assignment;
    
    vas's avatar
    vas committed
    
    
    	// handle non-exclusive pseudo-assignments as special cases
    
    	if (job === Job.PIT) {
    
    		if (!V.pit.fighterIDs.includes(slave.ID)) {
    			V.pit.fighterIDs.push(slave.ID);
    
    		V.JobIDMap[Job.PIT].add(slave.ID);
    
    	} else if (job === Job.LURCHER) {
    
    		V.LurcherID = slave.ID;
    
    		V.JobIDMap[Job.LURCHER].clear();
    		V.JobIDMap[Job.LURCHER].add(slave.ID);
    
    klorpa's avatar
    klorpa committed
    
    
    lowercasedonkey's avatar
    lowercasedonkey committed
    	removeJob(slave, slave.assignment, true);
    
    ezsh's avatar
    ezsh committed
    	const restingAssignment = slave.assignment; // not necessary Job.REST, but the assignment chosen by removeJob() for her case
    
    vas's avatar
    vas committed
    	const idx = V.slaveIndices[slave.ID];
    
    vas's avatar
    vas committed
    
    
    lowercasedonkey's avatar
    lowercasedonkey committed
    	 * this helper makes sure global references global IDs (V.HeadGirlID, V.AttendantID, etc) are set correctly
    
    	 * @param {string} propName
    	 */
    	function uniqueJob(propName) {
    
    lowercasedonkey's avatar
    lowercasedonkey committed
    		if (App.Data.resetOnNGPlus.hasOwnProperty(propName)) {
    
    			// TODO remove this branch when all special slaves migrate to IDs
    			if (V[propName] !== 0 && V[propName].ID !== slave.ID) {
    				removeJob(V[propName], job, true);
    			}
    			V[propName] = slave;
    		} else {
    			const specialIDProp = `${propName}ID`;
    			const prevAssigneeID = V[specialIDProp];
    			if (prevAssigneeID !== slave.ID) {
    				removeJob(slaveStateById(prevAssigneeID), job, true);
    			}
    			V[specialIDProp] = slave.ID;
    
    lowercasedonkey's avatar
    lowercasedonkey committed
    	/* Tracking for the following cases: */
    
    	if (oldJob !== job && V.assignmentRecords[slave.ID] !== job && oldJob !== Job.REST) { // Check that there is a real change happening. Sometimes when you send someone to a classroom or something, this fires twice.
    
    lowercasedonkey's avatar
    lowercasedonkey committed
    		switch (job.toLowerCase()) {
    
    			case Job.CELLBLOCK:
    
    lowercasedonkey's avatar
    lowercasedonkey committed
    			case "cellblock":
    
    				if (oldJob !== Job.CELLBLOCK) { // Due to the way assignJob fires twice on assigning to a building, we have to make sure that we are keeping the original record.
    
    lowercasedonkey's avatar
    lowercasedonkey committed
    					V.assignmentRecords[slave.ID] = oldJob;
    				}
    				break;
    
    			case Job.CLINIC:
    
    lowercasedonkey's avatar
    lowercasedonkey committed
    			case "clinic":
    
    				if (oldJob !== Job.CLINIC) {
    
    lowercasedonkey's avatar
    lowercasedonkey committed
    					V.assignmentRecords[slave.ID] = oldJob;
    				}
    				break;
    
    			case Job.SCHOOL.toLowerCase():
    
    lowercasedonkey's avatar
    lowercasedonkey committed
    			case "schoolroom":
    
    				if (oldJob !== Job.SCHOOL) {
    
    lowercasedonkey's avatar
    lowercasedonkey committed
    					V.assignmentRecords[slave.ID] = oldJob;
    				}
    				break;
    
    			case Job.SPA.toLowerCase():
    
    lowercasedonkey's avatar
    lowercasedonkey committed
    			case "spa":
    
    				if (oldJob !== Job.SPA) {
    
    lowercasedonkey's avatar
    lowercasedonkey committed
    					V.assignmentRecords[slave.ID] = oldJob;
    				}
    				break;
    
    lowercasedonkey's avatar
    lowercasedonkey committed
    	}
    
    vas's avatar
    vas committed
    	/* use .toLowerCase() to get rid of a few dupe conditions. */
    	switch (job.toLowerCase()) {
    
    		case Job.ARCADE.toLowerCase():
    
    vas's avatar
    vas committed
    		case "arcade":
    
    			slave.assignment = Job.ARCADE;
    
    			if (slave.clothes !== "a fuckdoll suit") {
    				slave.clothes = "no clothing";
    			}
    
    vas's avatar
    vas committed
    			slave.shoes = "none";
    			slave.collar = "none";
    
    			slave.rules.living = LivingRule.SPARE;
    
    vas's avatar
    vas committed
    			break;
    
    vas's avatar
    vas committed
    
    
    		case Job.BROTHEL.toLowerCase():
    
    vas's avatar
    vas committed
    		case "brothel":
    
    			slave.assignment = Job.BROTHEL;
    
    vas's avatar
    vas committed
    			switch (V.brothelDecoration) {
    				case "Degradationist":
    				case "standard":
    
    					slave.rules.living = LivingRule.SPARE;
    
    vas's avatar
    vas committed
    					break;
    
    vas's avatar
    vas committed
    				default:
    
    					slave.rules.living = LivingRule.NORMAL;
    
    vas's avatar
    vas committed
    					break;
    
    vas's avatar
    vas committed
    			}
    
    vas's avatar
    vas committed
    			break;
    
    vas's avatar
    vas committed
    
    
    		case Job.CELLBLOCK.toLowerCase():
    
    vas's avatar
    vas committed
    		case "cellblock":
    
    			slave.assignment = Job.CELLBLOCK;
    
    Skriv's avatar
    Skriv committed
    			switch (V.cellblockDecoration) {
    				case "Paternalist":
    
    					slave.rules.living = LivingRule.NORMAL;
    
    Skriv's avatar
    Skriv committed
    					break;
    				default:
    
    					slave.rules.living = LivingRule.SPARE;
    
    Skriv's avatar
    Skriv committed
    					break;
    			}
    
    vas's avatar
    vas committed
    			break;
    
    vas's avatar
    vas committed
    
    
    		case Job.CLINIC.toLowerCase():
    
    vas's avatar
    vas committed
    		case "clinic":
    
    			slave.assignment = Job.CLINIC;
    
    vas's avatar
    vas committed
    			switch (V.clinicDecoration) {
    				case "Repopulation Focus":
    				case "Eugenics":
    				case "Gender Radicalist":
    				case "Gender Fundamentalist":
    				case "Paternalist":
    				case "Maturity Preferentialist":
    				case "Youth Preferentialist":
    				case "Slimness Enthusiast":
    				case "Hedonistic":
    
    Pregmodder's avatar
    Pregmodder committed
    				case "Intellectual Dependency":
    				case "Petite Admiration":
    				case "Statuesque Glorification":
    
    					slave.rules.living = LivingRule.LUXURIOUS;
    
    vas's avatar
    vas committed
    					break;
    
    klorpa's avatar
    klorpa committed
    
    
    vas's avatar
    vas committed
    				case "Roman Revivalist":
    				case "Aztec Revivalist":
    				case "Egyptian Revivalist":
    				case "Arabian Revivalist":
    				case "Chinese Revivalist":
    				case "Chattel Religionist":
    				case "Edo Revivalist":
    
    					slave.rules.living = LivingRule.NORMAL;
    
    vas's avatar
    vas committed
    					break;
    
    klorpa's avatar
    klorpa committed
    
    
    vas's avatar
    vas committed
    				default:
    
    					slave.rules.living = LivingRule.SPARE;
    
    vas's avatar
    vas committed
    					break;
    
    vas's avatar
    vas committed
    			}
    
    vas's avatar
    vas committed
    			break;
    
    vas's avatar
    vas committed
    
    
    		case Job.CLUB.toLowerCase():
    
    vas's avatar
    vas committed
    		case "club":
    
    			slave.assignment = Job.CLUB;
    
    			slave.rules.living = LivingRule.NORMAL;
    
    vas's avatar
    vas committed
    			break;
    
    vas's avatar
    vas committed
    
    
    		case Job.DAIRY.toLowerCase():
    
    vas's avatar
    vas committed
    		case "dairy":
    
    			slave.assignment = Job.DAIRY;
    
    vas's avatar
    vas committed
    			switch (V.dairyDecoration) {
    				case "Roman Revivalist":
    
    vas's avatar
    vas committed
    				case "Aztec Revivalist":
    				case "Chinese Revivalist":
    				case "Chattel Religionist":
    				case "Edo Revivalist":
    				case "Arabian Revivalist":
    				case "Egyptian Revivalist":
    				case "Supremacist":
    				case "Subjugationist":
    				case "Degradationist":
    
    					slave.rules.living = LivingRule.SPARE;
    
    vas's avatar
    vas committed
    					break;
    
    vas's avatar
    vas committed
    				default:
    
    					slave.rules.living = LivingRule.NORMAL;
    
    vas's avatar
    vas committed
    					break;
    
    vas's avatar
    vas committed
    			}
    
    vas's avatar
    vas committed
    			break;
    
    vas's avatar
    vas committed
    
    
    		case Job.FARMYARD.toLowerCase():
    
    		case "farmyard":
    
    			slave.assignment = Job.FARMYARD;
    
    kopareigns's avatar
    kopareigns committed
    			switch (V.farmyardDecoration) {
    
    				case "Aztec Revivalist":
    				case "Chinese Revivalist":
    				case "Chattel Religionist":
    				case "Edo Revivalist":
    				case "Arabian Revivalist":
    				case "Egyptian Revivalist":
    				case "Supremacist":
    				case "Subjugationist":
    				case "Degradationist":
    
    					slave.rules.living = LivingRule.SPARE;
    
    					break;
    				case "Roman Revivalist":
    
    					slave.rules.living = LivingRule.LUXURIOUS;
    
    					break;
    				default:
    
    					slave.rules.living = LivingRule.SPARE;
    
    		case Job.HEADGIRLSUITE.toLowerCase():
    
    vas's avatar
    vas committed
    		case "head girl suite":
    		case "hgsuite":
    
    			slave.assignment = Job.HEADGIRLSUITE;
    
    			slave.rules.living = LivingRule.LUXURIOUS;
    
    vas's avatar
    vas committed
    			break;
    
    vas's avatar
    vas committed
    
    
    		case Job.MASTERSUITE.toLowerCase():
    
    vas's avatar
    vas committed
    		case "master suite":
    		case "mastersuite":
    
    			slave.assignment = Job.MASTERSUITE;
    
    DCoded's avatar
    DCoded committed
    			if (V.masterSuiteUpgradeLuxury > 0) {
    
    				slave.rules.living = LivingRule.LUXURIOUS;
    
    DCoded's avatar
    DCoded committed
    			} else {
    
    				slave.rules.living = LivingRule.NORMAL;
    
    DCoded's avatar
    DCoded committed
    			}
    
    vas's avatar
    vas committed
    			break;
    
    vas's avatar
    vas committed
    
    
    		case Job.SCHOOL.toLowerCase():
    
    vas's avatar
    vas committed
    		case "schoolroom":
    
    			slave.assignment = Job.SCHOOL;
    
    			slave.rules.living = LivingRule.NORMAL;
    
    vas's avatar
    vas committed
    			break;
    
    vas's avatar
    vas committed
    
    
    		case Job.QUARTER.toLowerCase():
    
    vas's avatar
    vas committed
    		case "servants' quarters":
    		case "servantsquarters":
    
    			slave.assignment = Job.QUARTER;
    
    vas's avatar
    vas committed
    			switch (V.servantsQuartersDecoration) {
    				case "Roman Revivalist":
    
    vas's avatar
    vas committed
    				case "Aztec Revivalist":
    				case "Chinese Revivalist":
    				case "Chattel Religionist":
    				case "Edo Revivalist":
    				case "Supremacist":
    				case "Subjugationist":
    				case "Degradationist":
    				case "Arabian Revivalist":
    				case "Egyptian Revivalist":
    
    					slave.rules.living = LivingRule.SPARE;
    
    vas's avatar
    vas committed
    					break;
    
    Pregmodder's avatar
    Pregmodder committed
    				case "Slave Professionalism":
    					if (slave.intelligence + slave.intelligenceImplant > 15) {
    
    						slave.rules.living = LivingRule.NORMAL;
    
    Pregmodder's avatar
    Pregmodder committed
    					} else {
    
    						slave.rules.living = LivingRule.SPARE;
    
    Pregmodder's avatar
    Pregmodder committed
    					}
    					break;
    				case "Petite Admiration":
    				case "Statuesque Glorification":
    					if (heightPass(slave)) {
    
    						slave.rules.living = LivingRule.NORMAL;
    
    Pregmodder's avatar
    Pregmodder committed
    					} else {
    
    						slave.rules.living = LivingRule.SPARE;
    
    Pregmodder's avatar
    Pregmodder committed
    					}
    					break;
    
    vas's avatar
    vas committed
    				default:
    
    					slave.rules.living = LivingRule.NORMAL;
    
    vas's avatar
    vas committed
    					break;
    
    vas's avatar
    vas committed
    			}
    
    vas's avatar
    vas committed
    			break;
    
    vas's avatar
    vas committed
    
    
    		case Job.SPA.toLowerCase():
    
    vas's avatar
    vas committed
    		case "spa":
    
    			slave.assignment = Job.SPA;
    
    vas's avatar
    vas committed
    			switch (V.spaDecoration) {
    				case "Chattel Religionist":
    				case "Chinese Revivalist":
    
    					slave.rules.living = LivingRule.NORMAL;
    
    vas's avatar
    vas committed
    					break;
    
    vas's avatar
    vas committed
    				case "Degradationist":
    
    					slave.rules.living = LivingRule.SPARE;
    
    vas's avatar
    vas committed
    					break;
    
    vas's avatar
    vas committed
    				default:
    
    					slave.rules.living = LivingRule.LUXURIOUS;
    
    vas's avatar
    vas committed
    					break;
    
    vas's avatar
    vas committed
    			}
    
    vas's avatar
    vas committed
    			break;
    
    klorpa's avatar
    klorpa committed
    
    
    		case Job.NURSERY.toLowerCase():
    
    brickode's avatar
    brickode committed
    		case "nursery":
    
    			slave.assignment = Job.NURSERY;
    
    			slave.rules.living = LivingRule.NORMAL;
    
    brickode's avatar
    brickode committed
    			break;
    
    vas's avatar
    vas committed
    
    
    		case Job.ATTENDANT.toLowerCase():
    
    			slave.assignment = Job.ATTENDANT;
    
    			slave.rules.living = LivingRule.LUXURIOUS;
    
    		case Job.MATRON.toLowerCase():
    
    			slave.assignment = Job.MATRON;
    
    			slave.rules.living = LivingRule.LUXURIOUS;
    
    			slave.rules.rest = RestRule.MID;
    
    		case Job.DJ.toLowerCase():
    
    svornost's avatar
    svornost committed
    			uniqueJob("dj");
    
    			slave.assignment = Job.DJ;
    
    			slave.rules.living = LivingRule.LUXURIOUS;
    
    			slave.rules.rest = RestRule.MID;
    
    		case Job.MADAM.toLowerCase():
    
    			slave.assignment = Job.MADAM;
    
    			slave.rules.living = LivingRule.LUXURIOUS;
    
    			slave.rules.rest = RestRule.MID;
    
    		case Job.MILKMAID.toLowerCase():
    
    			slave.assignment = Job.MILKMAID;
    
    			slave.rules.living = LivingRule.LUXURIOUS;
    
    			slave.rules.rest = RestRule.MID;
    
    		case Job.FARMER.toLowerCase():
    
    			slave.assignment = Job.FARMER;
    
    			slave.rules.living = LivingRule.LUXURIOUS;
    
    			slave.rules.rest = RestRule.MID;
    
    		case Job.NURSE.toLowerCase():
    
    			slave.assignment = Job.NURSE;
    
    			slave.rules.living = LivingRule.LUXURIOUS;
    
    			slave.rules.rest = RestRule.MID;
    
    		case Job.TEACHER.toLowerCase():
    
    			slave.assignment = Job.TEACHER;
    
    			slave.rules.living = LivingRule.LUXURIOUS;
    
    			slave.rules.rest = RestRule.MID;
    
    		case Job.STEWARD.toLowerCase():
    
    			slave.assignment = Job.STEWARD;
    
    			slave.rules.living = LivingRule.LUXURIOUS;
    
    			slave.rules.rest = RestRule.MID;
    
    		case Job.WARDEN.toLowerCase():
    
    			slave.assignment = Job.WARDEN;
    
    			slave.rules.living = LivingRule.LUXURIOUS;
    
    			slave.rules.rest = RestRule.MID;
    
    vas's avatar
    vas committed
    			break;
    
    vas's avatar
    vas committed
    
    
    		case Job.RECRUITER:
    
    			uniqueJob("Recruiter");
    
    			slave.assignment = Job.RECRUITER;
    
    		case Job.CONCUBINE.toLowerCase():
    
    			slave.assignment = Job.CONCUBINE;
    
    			slave.rules.living = V.masterSuiteUpgradeLuxury > 0 ? LivingRule.LUXURIOUS : LivingRule.NORMAL;
    
    vas's avatar
    vas committed
    			break;
    
    vas's avatar
    vas committed
    
    
    		case Job.HEADGIRL.toLowerCase():
    
    			slave.assignment = Job.HEADGIRL;
    
    vas's avatar
    vas committed
    			if (V.HGSuite === 1) {
    
    				slave.rules.living = LivingRule.LUXURIOUS;
    
    vas's avatar
    vas committed
    			}
    
    Pregmodder's avatar
    Pregmodder committed
    			slave.rules.rest = RestRule.MID;
    
    vas's avatar
    vas committed
    			break;
    
    vas's avatar
    vas committed
    
    
    		case Job.BODYGUARD.toLowerCase():
    
    			slave.assignment = Job.BODYGUARD;
    
    vas's avatar
    vas committed
    			if (V.dojo > 1) {
    
    				slave.rules.living = LivingRule.LUXURIOUS;
    
    vas's avatar
    vas committed
    			}
    
    brickode's avatar
    brickode committed
    			if (V.pit && V.pit.bodyguardFights && V.pit.fighterIDs.includes(slave.ID)) { V.pit.fighterIDs.delete(slave.ID); }
    
    vas's avatar
    vas committed
    			break;
    
    vas's avatar
    vas committed
    
    
    		case Job.AGENT.toLowerCase():
    		case Job.AGENTPARTNER.toLowerCase():
    
    vas's avatar
    vas committed
    			slave.assignment = job;
    			slave.useRulesAssistant = 0; /* non-visible roles exempt from Rules Assistant */
    
    			WombCleanGenericReserve(slave, 'incubator', 9999);
    			WombCleanGenericReserve(slave, 'nursery', 9999);
    
    			if (job === Job.AGENT) {
    
    				if (App.activeArcology().direction !== 0) { // never assign an agent to the player's arcology
    					if (App.activeArcology().leaderID !== 0) {
    						const oldAgent = getSlave(App.activeArcology().leaderID);
    						if (oldAgent && oldAgent.assignment === Job.AGENT) {
    							// this is an error...you should never be able to assign an agent over the top of another
    							// but in order to prevent game state corruption, we are going to remove the old agent
    							removeJob(oldAgent, oldAgent.assignment, false);
    						}
    					}
    					App.activeArcology().leaderID = slave.ID;
    					App.activeArcology().government = "your agent";
    				}
    
    vas's avatar
    vas committed
    			}
    
    vas's avatar
    vas committed
    			break;
    
    vas's avatar
    vas committed
    
    
    		case Job.CHOICE.toLowerCase():
    
    vas's avatar
    vas committed
    			slave.assignment = job;
    
    vas's avatar
    vas committed
    			break;
    
    vas's avatar
    vas committed
    
    		default:
    
    			slave.assignment = job; /* removeJob already set choosesOwnAssignment = 0 */
    
    vas's avatar
    vas committed
    			break;
    
    vas's avatar
    vas committed
    	}
    
    
    ezsh's avatar
    ezsh committed
    	V.JobIDMap[restingAssignment].delete(slave.ID);
    
    	V.JobIDMap[slave.assignment].add(slave.ID);
    
    
    	if (!assignmentVisible(slave) && Array.isArray(V.personalAttention)) {
    
    		if (V.personalAttention.deleteWith(s => s.ID === slave.ID).length > 0) {
    			if (V.personalAttention.length === 0) {
    
    Arkerthan's avatar
    Arkerthan committed
    				resetPersonalAttention();
    
    				r += `${slave.slaveName} no longer has your personal attention; you plan to focus on ${V.personalAttention}.`;
    
    DCoded's avatar
    DCoded committed
    			} else {
    
    				r += `${slave.slaveName} no longer has your personal attention.`;
    
    DCoded's avatar
    DCoded committed
    			}
    
    vas's avatar
    vas committed
    		}
    	}
    
    	// maybe recreate it each time? V.JobIDArray = makeJobIdMap();
    
    DCoded's avatar
    DCoded committed
    	if (idx >= 0) { V.slaves[idx] = slave; }
    
    kopareigns's avatar
    kopareigns committed
    
    
    vas's avatar
    vas committed
    	return r;
    };
    
    vas's avatar
    vas committed
    
    
    Skriv's avatar
    Skriv committed
    /**
     * @param {App.Entity.SlaveState} slave
    
     * @param {FC.Assignment} assignmentStr
    
    Skriv's avatar
    Skriv committed
     */
    
    globalThis.assignJobSafely = function(slave, assignmentStr) {
    
    	if (V.assignmentRecords[slave.ID] === Job.CHOICE) {
    		assignJob(slave, Job.REST);
    
    lowercasedonkey's avatar
    lowercasedonkey committed
    		slave.choosesOwnAssignment = 1;
    
    	} else if ([Job.AGENT, Job.AGENTPARTNER].includes(V.assignmentRecords[slave.ID])) { // it is NEVER safe to auto-reassign agents (we don't know which arcology they came from)
    		assignJob(slave, Job.REST);
    
    lowercasedonkey's avatar
    lowercasedonkey committed
    	} else if (!App.Utils.jobForAssignment(assignmentStr).canEmploy(slave).length) {  // If nothing complains about job requirements not being met
    
    		assignJob(slave, assignmentStr);
    	} else {
    
    		assignJob(slave, Job.REST);
    
    lowercasedonkey's avatar
    lowercasedonkey committed
    	// Whether they manage to go back or they default to rest, we don't need their record:
    
    	if (V.assignmentRecords[slave.ID]) {
    		delete V.assignmentRecords[slave.ID];
    	}
    
    Skriv's avatar
    Skriv committed
    /**
     * @param {App.Entity.SlaveState} slave
    
     * @param {FC.Assignment} assignment
    
     * @param {boolean} [saveRecord=false]
    
    Skriv's avatar
    Skriv committed
     * @returns {string}
     */
    
    globalThis.removeJob = function(slave, assignment, saveRecord = false) {
    	if (!slave) {
    
    		// it is well-formed, but does nothing, to remove an assignment from nobody.
    
    		// this lets us call <<run removeJob(_S.HeadGirl, Job.HEADGIRL)>> and similar,
    		// without first checking to see whether a slave is really assigned to _S.HeadGirl or not.
    
    lowercasedonkey's avatar
    lowercasedonkey committed
    	if (V.assignmentRecords[slave.ID] && (saveRecord === false)) {
    
    		delete V.assignmentRecords[slave.ID];
    	}
    
    vas's avatar
    vas committed
    	const idx = V.slaveIndices[slave.ID];
    
    vas's avatar
    vas committed
    
    
    	if (assignment === Job.PIT) {
    
    brickode's avatar
    brickode committed
    		if (V.pit) {
    			V.pit.fighterIDs.delete(slave.ID);
    			V.JobIDMap[Job.PIT].delete(slave.ID);
    		} else {
    			return;	// TODO: should this return or just continue?
    		}
    
    	} else if (assignment === Job.LURCHER) {
    
    		if (V.LurcherID === slave.ID) {
    			V.LurcherID = 0;
    		}
    
    		V.JobIDMap[Job.LURCHER].delete(slave.ID);
    
    DCoded's avatar
    DCoded committed
    	} else {
    
    		if (V.JobIDMap.hasOwnProperty(assignment)) {
    			V.JobIDMap[assignment].delete(slave.ID);
    		} else {
    			V.JobIDMap[slave.assignment].delete(slave.ID);
    		}
    
    
    		if (slave.ID === V.HeadGirlID) {
    			V.HeadGirlID = 0;
    		} else if (slave.ID === V.RecruiterID) {
    			V.RecruiterID = 0;
    
    Pregmodder's avatar
    Pregmodder committed
    			V.recruiterIOUs = 0;
    
    		} else if (slave.ID === V.BodyguardID) {
    			V.BodyguardID = 0;
    		} else if (slave.ID === V.MadamID) {
    			V.MadamID = 0;
    		} else if (slave.ID === V.djID) {
    
    Blank_Alt's avatar
    Blank_Alt committed
    			V.djID = 0;
    
    		} else if (slave.ID === V.MilkmaidID) {
    			V.MilkmaidID = 0;
    		} else if (slave.ID === V.FarmerID) {
    			V.FarmerID = 0;
    		} else if (slave.ID === V.SchoolteacherID) {
    
    			V.SchoolteacherID = 0;
    
    		} else if (V.AttendantID === slave.ID) {
    			V.AttendantID = 0;
    
    		} else if (slave.ID === V.MatronID) {
    			V.MatronID = 0;
    		} else if (slave.ID === V.NurseID) {
    
    			V.NurseID = 0;
    
    		} else if (slave.ID === V.StewardessID) {
    
    		} else if (slave.ID === V.WardenessID) {
    
    Blank_Alt's avatar
    Blank_Alt committed
    			V.WardenessID = 0;
    
    		} else if (slave.ID === V.ConcubineID) {
    			V.ConcubineID = 0;
    
    ezsh's avatar
    ezsh committed
    		} else if (slave.ID === V.StudID) {
    			V.StudID = 0;
    
    DCoded's avatar
    DCoded committed
    		}
    
    vas's avatar
    vas committed
    
    		/* use .toLowerCase() to get rid of a few dupe conditions. */
    		switch (assignment.toLowerCase()) {
    
    			case Job.ARCADE.toLowerCase():
    
    Skriv's avatar
    Skriv committed
    			case "arcade":
    
    				slave.assignment = Job.GLORYHOLE;
    
    Skriv's avatar
    Skriv committed
    				break;
    
    
    			case Job.BROTHEL.toLowerCase():
    
    Skriv's avatar
    Skriv committed
    			case "brothel":
    
    				slave.assignment = Job.WHORE;
    
    Skriv's avatar
    Skriv committed
    				break;
    
    
    			case Job.CELLBLOCK.toLowerCase():
    
    Skriv's avatar
    Skriv committed
    			case "cellblock":
    
    				slave.assignment = Job.REST;
    
    Skriv's avatar
    Skriv committed
    				if (slave.inflation > 0) {
    
    vas's avatar
    vas committed
    				}
    
    Skriv's avatar
    Skriv committed
    				break;
    
    
    			case Job.CLINIC.toLowerCase():
    
    Skriv's avatar
    Skriv committed
    			case "clinic":
    
    				slave.assignment = Job.REST;
    
    Skriv's avatar
    Skriv committed
    				break;
    
    
    			case Job.CLUB.toLowerCase():
    
    Skriv's avatar
    Skriv committed
    			case "club":
    
    				slave.assignment = Job.PUBLIC;
    
    Skriv's avatar
    Skriv committed
    				break;
    
    
    			case Job.DAIRY.toLowerCase():
    
    Skriv's avatar
    Skriv committed
    			case "dairy":
    
    				slave.assignment = Job.MILKED;
    
    Pregmodder's avatar
    Pregmodder committed
    				if (V.dairyRestraintsSetting > 1) {
    					slave.health.tired = 100;
    				}
    
    Pregmodder's avatar
    Pregmodder committed
    				if (V.dairyFeedersSetting > 0) {
    					slave.diet = "healthy";
    				}
    				if (V.dairyHormonesSetting > 0) {
    					slave.hormones = 0;
    				}
    
    Skriv's avatar
    Skriv committed
    				break;
    
    
    			case Job.FARMYARD.toLowerCase():
    
    Skriv's avatar
    Skriv committed
    			case "farmyard":
    
    				slave.assignment = Job.REST;
    
    Skriv's avatar
    Skriv committed
    				break;
    
    
    			case Job.SCHOOL.toLowerCase():
    
    Skriv's avatar
    Skriv committed
    			case "schoolroom":
    
    				slave.assignment = Job.REST;
    
    Skriv's avatar
    Skriv committed
    				break;
    
    
    			case Job.SPA.toLowerCase():
    
    Skriv's avatar
    Skriv committed
    			case "spa":
    
    				slave.assignment = Job.REST;
    
    Skriv's avatar
    Skriv committed
    				break;
    
    
    			case Job.QUARTER.toLowerCase():
    
    Skriv's avatar
    Skriv committed
    			case "servants' quarters":
    			case "servantsquarters":
    
    				slave.assignment = Job.HOUSE;
    
    Skriv's avatar
    Skriv committed
    				break;
    
    
    			case Job.MASTERSUITE.toLowerCase():
    
    Skriv's avatar
    Skriv committed
    			case "master suite":
    			case "mastersuite":
    
    				slave.assignment = Job.FUCKTOY;
    
    Skriv's avatar
    Skriv committed
    				break;
    
    			case "live with your head girl":
    			case "head girl suite":
    			case "hgsuite":
    
    				slave.assignment = Job.REST;
    
    Skriv's avatar
    Skriv committed
    				break;
    
    
    			case Job.NURSERY:
    
    Skriv's avatar
    Skriv committed
    			case "nursery":
    
    				slave.assignment = Job.REST;
    
    Skriv's avatar
    Skriv committed
    				break;
    
    			case "be your head girl":
    
    Pregmodder's avatar
    Pregmodder committed
    				let attentionCheck = 1;
    
    				slave.assignment = Job.REST;
    
    				const HGSlave = V.slaves.find(s => s.assignment === Job.HEADGIRLSUITE);
    				if (HGSlave) {
    					removeJob(HGSlave, Job.HEADGIRLSUITE);
    					if (V.HGSuiteEquality === 1 && HGSlave.devotion > 50) {
    						assignJob(HGSlave, Job.HEADGIRL);
    						V.HeadGirlID = HGSlave.ID;
    						HGSlave.diet = "healthy";
    						attentionCheck = 0;
    
    Pregmodder's avatar
    Pregmodder committed
    					}
    				}
    				if (V.personalAttention === "HG" && attentionCheck === 1) {
    
    Arkerthan's avatar
    Arkerthan committed
    					resetPersonalAttention();
    
    Skriv's avatar
    Skriv committed
    					r += `You no longer have a slave assigned to be your Head Girl, so you turn your personal attention to focus on ${V.personalAttention}.`;
    				}
    
    Skriv's avatar
    Skriv committed
    				break;
    
    
    			case Job.AGENT:
    			case Job.AGENTPARTNER:
    				if (slave.assignment === Job.AGENT) {
    
    					const arc = V.arcologies.find((a) => a.leaderID === slave.ID);
    
    					if (arc) {
    						arc.leaderID = 0;
    						arc.government = "your trustees";
    					}
    
    Blank's avatar
    Blank committed
    				}
    
    				slave.assignment = Job.REST;
    
    brickode's avatar
    brickode committed
    				if (slave.relationshipTarget > 0) {
    					/* following code assumes there can be at most one companion */
    
    					const lover = V.slaves.find(s => haveRelationshipP(s, slave) && s.assignment === Job.AGENTPARTNER);
    					if (lover) {
    						removeJob(lover, Job.AGENTPARTNER, saveRecord);
    
    Skriv's avatar
    Skriv committed
    					}
    				}
    				break;
    
    klorpa's avatar
    klorpa committed
    
    
    Skriv's avatar
    Skriv committed
    			default:
    
    				slave.assignment = Job.REST;
    
    Skriv's avatar
    Skriv committed
    				break;
    
    vas's avatar
    vas committed
    		}
    
    
    		V.JobIDMap[slave.assignment].add(slave.ID);
    
    
    		if (slave.rules.living === LivingRule.LUXURIOUS && !assignmentVisible(slave)) {
    			slave.rules.living = LivingRule.NORMAL;
    
    DCoded's avatar
    DCoded committed
    		}
    
    klorpa's avatar
    klorpa committed
    
    
    vas's avatar
    vas committed
    		slave.choosesOwnAssignment = 0;
    		slave.sentence = 0;
    
    promethium's avatar
    promethium committed
    		slave.subTarget = 0;
    
    vas's avatar
    vas committed
    	}
    
    	// reset it each time? V.JobIDArray = makeJobIdMap();
    
    DCoded's avatar
    DCoded committed
    	if (idx >= 0) {
    		V.slaves[idx] = slave;
    	}
    
    kopareigns's avatar
    kopareigns committed
    
    
    vas's avatar
    vas committed
    	return r;
    
    ezsh's avatar
    ezsh committed
    };
    
    /**
     * Indicate whether a slave's current assignment is shown in Main
     * Often used as a proxy for "penthouse slave"
     * @param {App.Entity.SlaveState} slave
    
    globalThis.assignmentVisible = function(slave) {
    
    	switch (slave.assignment) {
    		/* normal out-of-penthouse jobs */
    
    Skriv's avatar
    Skriv committed
    		case Job.ARCADE:
    
    		case Job.BROTHEL:
    		case Job.CELLBLOCK:
    		case Job.CLINIC:
    		case Job.CLUB:
    		case Job.DAIRY:
    		case Job.FARMYARD:
    		case Job.HEADGIRLSUITE:
    		case Job.MASTERSUITE:
    		case Job.SCHOOL:
    		case Job.QUARTER:
    		case Job.SPA:
    		case Job.NURSERY:
    
    		case Job.ATTENDANT:
    		case Job.MATRON:
    		case Job.DJ:
    		case Job.MADAM:
    		case Job.MILKMAID:
    		case Job.FARMER:
    		case Job.NURSE:
    		case Job.TEACHER:
    		case Job.STEWARD:
    		case Job.WARDEN:
    		case Job.CONCUBINE:
    
    
    		/* agents are not in the arcology at all */
    
    		case Job.AGENT:
    		case Job.AGENTPARTNER:
    
    		case Job.CHOICE:
    
    	}
    
    	/* all other jobs are shown in penthouse */
    
     * @returns {Record<FC.Assignment, Set<number>>} dictionary assignment -> slave IDs
    
    globalThis.makeJobIdMap = function() {
    
    	/** @type {Object.<FC.Assignment, Set<number>>} */
    
    	for (const jn of Object.values(Job)) {
    
    	for (const slave of V.slaves) {
    
    		res[slave.assignment].add(slave.ID);
    
    brickode's avatar
    brickode committed
    	if (V.pit) {
    		res[Job.PIT] = new Set(V.pit.fighterIDs);
    	}
    
    	res[Job.LURCHER].add(V.LurcherID);
    
    
    /**
     * Generates string with links for changing slave assignment
     */
    
    App.UI.jobLinks = function() {
    
    	"use strict";
    	const facilitiesOrder = [
    
    		/* sorted by improvement before work, within improvement in order of progress, within work alphabetical for facilities*/
    
    		App.Entity.facilities.penthouse,
    
    		App.Entity.facilities.cellblock,
    
    		App.Entity.facilities.nursery,
    
    		App.Entity.facilities.schoolroom,
    
    		App.Entity.facilities.clinic,
    		App.Entity.facilities.spa,
    		App.Entity.facilities.arcade,
    
    		App.Entity.facilities.brothel,
    		App.Entity.facilities.club,
    		App.Entity.facilities.dairy,
    
    		App.Entity.facilities.farmyard,
    		App.Entity.facilities.masterSuite,
    		App.Entity.facilities.servantsQuarters
    
    	return {
    		assignmentsFragment: assignmentsFragment,
    		transfersFragment: transfersFragment
    	};
    
    Skriv's avatar
    Skriv committed
    	 * Generates assignment links
    
    	 * @param {string} passage
    	 * @param {assignmentCallback} [callback]
    	 * @returns {DocumentFragment}
    	 */
    
    	function assignmentsFragment(ID, passage, callback) {
    		let penthouseJobs = App.Entity.facilities.penthouse.assignmentLinkElements(ID, undefined, passage, callback);
    		const slave = slaveStateById(ID);
    
    		const sp = getPronouns(slave);
    
    		if (slave.fuckdoll === 0) {
    
    			const assignment = Job.CHOICE;
    
    			if (slave.assignment !== assignment) {
    
    				penthouseJobs.push(App.UI.DOM.assignmentLink(slave, assignment, passage, callback, `Let ${sp.object} choose`));
    
    			}
    		} else {
    			penthouseJobs.push(App.UI.DOM.disabledLink(`Let ${sp.object} choose`, ["Fuckdolls can't choose their job"]));
    		}
    		let res = document.createDocumentFragment();
    		// there is always at least one job
    		res.appendChild(penthouseJobs[0]);
    		for (let i = 1; i < penthouseJobs.length; ++i) {
    			res.appendChild(document.createTextNode(" | "));
    			res.appendChild(penthouseJobs[i]);
    		}
    		return res;
    	}
    
    
    svornost's avatar
    svornost committed
    	/**
    
    svornost's avatar
    svornost committed
    	 * @param {number} ID
    	 * @param {assignmentCallback} [callback]
    	 * @returns {DocumentFragment}
    	 */
    
    	function transfersFragment(ID, callback) {
    
    		/** @type {HTMLElement[]} */
    		const transfers = [];
    
    
    		for (const f of facilitiesOrder) {
    
    			if (!f.established || f.jobs.length === 0) { continue; }
    
    			const rejects = f.canHostSlave(slave);
    			if (rejects.length === 0) {
    
    				transfers.push(f.transferLinkElement(ID, undefined, passage(), callback));
    
    			} else {
    				transfers.push(App.UI.DOM.disabledLink(f.genericName, rejects));
    			}
    		}
    
    		let res = document.createDocumentFragment();
    		// there is always at least one job
    		res.appendChild(transfers[0]);
    		for (let i = 1; i < transfers.length; ++i) {
    			res.appendChild(document.createTextNode(" | "));
    			res.appendChild(transfers[i]);
    		}
    		return res;
    	}
    
    Blank's avatar
    Blank committed
    App.activeArcology = function() {
    	return V.arcologies[V.activeArcologyIdx];
    
    Skriv's avatar
    Skriv committed
    };
    
    
    svornost's avatar
    svornost committed
    App.currentAgent = function(arcology) {
    
    Skriv's avatar
    Skriv committed
    };
    
    
    /**
     * Remove all workers from the facility changing their assignments
     * @param {App.Entity.Facilities.Facility} facility
    
     * @param {FC.Assignment} [managerAssignment="rest"] new assignment for the facility manager
     * @param {FC.Assignment} [workerAssignment="rest"] new assignment for the facility workers
    
    App.Utils.moveFacilityWorkers = function(facility, managerAssignment = Job.REST, workerAssignment = Job.REST) {
    
    	if (facility.manager && facility.manager.currentEmployee) {
    		assignJob(facility.manager.currentEmployee, managerAssignment);
    	}
    
    	for (const w of facility.employees()) {
    		assignJob(w, workerAssignment);
    	}
    };
    
    
    App.Utils.jobForAssignment = function() {
    	const map = new Map();
    
    	function fillMap() {
    		/**
    		 * @param {Map} m
    		 * @param {App.Entity.Facilities.Facility} f
    		 */
    		function addFacility(m, f) {
    			if (f.manager) {
    				m.set(f.desc.manager.assignment, f.manager);
    			}
    			for (const j of f.jobsNames) {
    				m.set(f.desc.jobs[j].assignment, f.job(j));
    			}
    		}
    
    		for (const f in App.Entity.facilities) {
    			if (f.length > 0) {
    				addFacility(map, App.Entity.facilities[f]);
    			}
    		}
    	}
    
    	/**
    
    	 * @param {FC.Assignment} assignment
    
    Skriv's avatar
    Skriv committed
    	 * @returns {App.Entity.Facilities.Job}
    	 */
    
    	function getJob(assignment) {
    		if (map.size === 0) {
    			fillMap();
    		}
    
    lowercasedonkey's avatar
    lowercasedonkey committed
    		const res = map.get(assignment);
    		if (!res) {
    
    lowercasedonkey's avatar
    lowercasedonkey committed
    			throw Error(`Can't find job object for assignment '${assignment}'`);
    		}
    
    lowercasedonkey's avatar
    lowercasedonkey committed
    		return res;
    
    
    /** Assign a slave, play the appropriate assignment scene if necessary, and redirect to a destination passage.
     * @param {App.Entity.SlaveState} slave
    
     * @param {FC.Assignment} assignTo
    
     * @param {string} passage
     */
    globalThis.assignmentTransition = function(slave, assignTo, passage) {
    	/** @param {string} scene */
    	function detourThroughScene(scene) {
    		V.returnTo = passage;
    		Engine.play(scene);
    	}
    
    
    lowercasedonkey's avatar
    lowercasedonkey committed
    	V.AS = slave.ID;
    
    	assignJob(slave, assignTo);
    	if (V.showAssignToScenes === 1 && slave.fetish !== "mindbroken") {
    
    		if (assignTo === Job.DAIRY && V.dairyRestraintsSetting >= 2 && ((V.dairyStimulatorsSetting >= 2) || (V.dairyFeedersSetting >= 2) || (V.dairyPregSetting >= 2))) {
    
    			detourThroughScene("Industrial Dairy Assignment Scene");
    
    		} else if (assignTo === Job.DAIRY && (V.dairyRestraintsSetting === 0 && slave.devotion > 0)) {
    
    			detourThroughScene("Free Range Dairy Assignment Scene");
    
    		} else if (assignTo === Job.BROTHEL) {
    
    			detourThroughScene("Brothel Assignment Scene");
    		} else {
    
    	} else {
    		Engine.play(passage);
    
    
    /**
     * Creates a link that will remove all slaves from a facility, including manager.
     * @param {string} facilityName
    
     * @param {FC.Assignment} [managerAssignment]
     * @param {FC.Assignment} [workerAssignment]
    
    ezsh's avatar
    ezsh committed
     * @returns {DocumentFragment}
    
     */
    globalThis.removeFacilityWorkers = function(facilityName, managerAssignment, workerAssignment) {
    	const facility = App.Entity.facilities[facilityName];
    	const count = facility.totalEmployeesCount;
    
    lowercasedonkey's avatar
    lowercasedonkey committed
    	const frag = new DocumentFragment();
    
    	if (count > 0) {
    
    lowercasedonkey's avatar
    lowercasedonkey committed
    		frag.append(
    			App.UI.DOM.link(
    
    				"Remove all slaves",
    				() => {
    					App.Utils.moveFacilityWorkers(facility, managerAssignment, workerAssignment);
    					penthouseCensus();
    				},
    				[],
    				passage(),
    			)
    		);
    		if ((count + V.dormitoryPopulation) > V.dormitory) {
    
    lowercasedonkey's avatar
    lowercasedonkey committed
    			App.UI.DOM.appendNewElement("span", frag, ` Dormitory capacity will be exceeded.`, "red");
    
    lowercasedonkey's avatar
    lowercasedonkey committed
    	return frag;