code:
App.Data.JobDesc = class {
	constructor() {
		this.position = "";
		this.assignment = "";
		this.publicSexUse = false;
		this.fuckdollAccepted = false;
		/** @type {boolean|undefined} */
		this.broodmotherAccepted = false;
		/** workers can take part time jobs in addition to their main (full-time) one */
		this.partTime = false;
	}
};

App.Data.ManagerJobDesc = class extends App.Data.JobDesc {
	constructor() {
		super();
		this.shouldWalk = true;
		this.shouldHold = true;
		this.shouldSee = true;
		this.shouldHear = true;
		this.shouldTalk = true;
		this.shouldThink = true;
		this.requiredDevotion = 50;
		/**
		 * Applicable careers
		 * @type {string[]} */
		this.careers = [];
		/**
		 * Applicable skill name
		 * @type {string} */
		this.skill = null;
	}
};

App.Data.FacilityDesc = class {
	constructor() {
		/** Base name for state variables */
		this.baseName = "";
		/** Generic name for UI (Brothel, Club, etc.)
		 * If null, baseName is used instead
		*/
		this.genericName = "";
		/** @type {Object.<string, App.Data.JobDesc>} */
		this.jobs = {};
		this.defaultJob = "";
		/** @type {App.Data.ManagerJobDesc} */
		this.manager = null;
	}
};

App.Data.Facilities = {};
App.Entity.Facilities = {};

App.Entity.Facilities.Job = class {
	constructor() {
		/** @type {App.Data.JobDesc} */
		this.desc = null;
		/** @type {App.Entity.Facilities.Facility} */
		this.facility = null;
	}

	/**
	 * Does slave meet the requirements for this job
	 * @param {App.Entity.SlaveState} slave
	 * @returns {string[]}
	 */
	checkRequirements(slave) {
		let r = [];
		if (this.desc.publicSexUse &&
			(slave.breedingMark === 1 && State.variables.propOutcome === 1 && State.variables.eugenicsFullControl !== 1 && State.variables.arcologies[0].FSRestart !== "unset")) {
			r.push(`${slave.slaveName} is for private use only.`);
		}
		if (!this.desc.fuckdollAccepted && slave.fuckdoll > 0) {
			r.push(`Fuckdolls can't ${this.desc.assignment} at ${this.facility.name}.`);
		}
		if (!this.desc.broodmotherAccepted && slave.preg > 37 && slave.broodmother === 2) {
			r.push(`Birthing broodmothers can't ${this.desc.assignment}.`);
		}
		return r;
	}

	/**
	 * Can slave be employed at this position
	 * @param {App.Entity.SlaveState} slave
	 * @returns {string[]}
	 */
	canEmploy(slave) {
		if (this.isEmployed(slave)) {
			return [`${slave.slaveName} is already assigned to ${this.desc.assignment} at ${this.facility.name}.`];
		}
		if (!this._facilityHasFreeSpace) {
			return [`Capacity of ${this.facility.name} exceeded.`];
		}
		if (slave.assignment === this.desc.assignment) {
			return [`${slave.slaveName} is already assigned to ${this.desc.assignment}.`];
		}

		return this.checkRequirements(slave);
	}

	/**
	 * Is slave already assigned to this job
	 * @param {App.Entity.SlaveState} slave
	 * @returns {boolean}
	 */
	isEmployed(slave) {
		return slave.assignment === this.desc.assignment;
	}

	/**
	 * @callback linkCallback
	 * @param {string} assignment new assignment
	 * @returns {string} code to include into the <<link>><</link>>
	 */

	/**
	 * @callback assignmentCallback
	 * @param {App.Entity.SlaveState} slave the slave whose assignment changes
	 * @param {string} assignment new assignment
	 * @returns {void}
	 */

	/**
	 * Returns link text for the penthouse assignment
	 * @param {number} i slave index
	 * @param {string} [passage] passage to go to
	 * @param {linkCallback} [callback]
	 * @param {string} [linkText]
	 * @returns {string}
	 */
	assignmentLink(i, passage, callback, linkText) {
		linkText = linkText || this.desc.position;
		const linkAction = callback !== undefined ? callback(this.desc.assignment) : '';
		return `<<link "${linkText}"${passage !== undefined ? ' "' + passage + '"' : ''}>><<= assignJob(${App.Utils.slaveRefString(i)}, "${this.desc.assignment}")>>${linkAction}<</link>>`;
	}

	/**
	 * Returns link text for the penthouse assignment
	 * @param {number} i slave index
	 * @param {string} [passage] passage to go to
	 * @param {assignmentCallback} [callback]
	 * @param {string} [linkText]
	 * @returns {HTMLAnchorElement}
	 */
	assignmentLinkElement(i, passage, callback, linkText) {
		linkText = linkText || this.desc.position;
		return App.UI.DOM.assignmentLink(State.variables.slaves[i], this.desc.assignment, passage, callback, linkText);
	}

	/**
	 * all slaves that are employed at this job
	 * @returns {App.Entity.SlaveState[]}
	 */
	employees() {
		return State.variables.slaves.filter( s => s.assignment === this.desc.assignment);
	}

	/**
	 * Indices in the slaves array for all slaves that are employed at this job
	 * @returns {number[]}
	 */
	employeesIndices() {
		return State.variables.slaves.reduce(
			(acc, cur, idx) => { if (cur.assignment === this.desc.assignment) { acc.push(idx); } return acc; }, []);
	}

	/**
	 * IDs for all slaves that are employed at this job
	 * @returns {number[]}
	 */
	employeesIds() {
		return this.employees().map(s => s.ID);
	}

	/**
	 * Tests if slave is broken enough
	 * @protected
	 * @param {App.Entity.SlaveState} slave
	 * @param {number} [pureDevotion=50] Minimal devotion level to pass test with any trust
	 * @param {number} [devotion=-50] Minimal devotion for slaves with enough fear
	 * @param {number} [trust=-21] Maximal trust (i.e. minimal fear) for the less devotional (see above)
	 * @param {number} [pureFear=-51] Maximal low trust to pass test with any devotion (because of the fear)
	 * @param {number} [pureTrust=101] Minimal high trust level to pass test without devotion
	 * @returns {boolean}
	 */
	static _isBrokenEnough(slave, pureDevotion, devotion, trust, pureFear, pureTrust) {
		if ((slave.devotion < (pureDevotion || 50)) &&
			(slave.trust < (pureTrust || 101)) && (slave.trust > (pureFear || -51)) &&
			((slave.devotion <= (devotion || -51)) || (slave.trust >= (trust || -21)))) {
			return false;
		}
		return true;
	}

	/**
	 * @protected
	 * Standard message that slave is not broken enough
	 * @param {App.Entity.SlaveState} slave
	 * @returns {string}
	 */
	static _stdBreakageMessage(slave) {
		return `${slave.slaveName} must be either more fearful of you or devoted to you.`;
	}

	/** @private */
	get _facilityHasFreeSpace() {
		return this.facility.hasFreeSpace;
	}
};

App.Entity.Facilities.ManagingJob = class extends App.Entity.Facilities.Job {
	constructor() {
		super();
		/** @type {App.Data.ManagerJobDesc} */
		this.desc = null;
	}

	/**
	 * Can slave be employed at this position
	 * @param {App.Entity.SlaveState} slave
	 * @returns {string[]}
	 */
	checkRequirements(slave) {
		let r = super.checkRequirements(slave);
		if (slave.devotion < this.desc.requiredDevotion) {
			r.push(`${slave.slaveName} must be more devoted to you.`);
		}
		if (this.desc.shouldWalk && !canWalk(slave)) {
			r.push(`${slave.slaveName} must be able to walk.`);
		}
		if (this.desc.shouldHold && !canHold(slave)) {
			r.push(`${slave.slaveName} must be able to hold on to objects.`);
		}
		if (this.desc.shouldSee && !canSee(slave)) {
			r.push(`${slave.slaveName} must have working eyes.`);
		}
		if (this.desc.shouldHear && !canHear(slave)) {
			r.push(`${slave.slaveName} must be able to hear.`);
		}
		if (this.desc.shouldTalk && !canTalk(slave)) {
			r.push(`${slave.slaveName} must be able to talk.`);
		}
		if (this.desc.shouldThink && slave.fetish === "mindbroken") {
			r.push(`${slave.slaveName} must possess cognition.`);
		}
		return r;
	}
	/**
	 * Returns true if slave has enough applicable skill or career
	 * @param {App.Entity.SlaveState} slave
	 * @returns {boolean}
	 */
	slaveHasExperience(slave) {
		return (this.desc.skill !== null && slave.skill[this.desc.skill] >= State.variables.masteredXP) ||
			(typeof slave.career === 'string' && this.desc.careers.includes(slave.career));
	}

	/** @returns {App.Entity.SlaveState} */
	get currentEmployee() {
		const obj = State.variables[capFirstChar(this.desc.position)];
		return obj === undefined || obj === 0 ? null : obj;
	}

	/** @private */
	get _facilityHasFreeSpace() {
		return true;
	}
};

App.Entity.Facilities.Facility = class {
	/**
	 * @param {App.Data.FacilityDesc} desc defines state variable for this facility
	 * @param {Object.<string, App.Entity.Facilities.Job>} [jobs] job object that are not default
	 * @param {App.Entity.Facilities.ManagingJob} [manager]
	 */
	constructor(desc, jobs, manager) {
		this.desc = desc;
		jobs = jobs || {};
		/** @private @type {Object.<string, App.Entity.Facilities.Job>} */
		this._jobs = {};

		for (const jn in this.desc.jobs) {
			if (jobs[jn] !== undefined) {
				this._jobs[jn] = jobs[jn];
			} else {
				this._jobs[jn] = this._createJob(jn);
			}
			this._jobs[jn].facility = this;
			this._jobs[jn].desc = desc.jobs[jn];
		}

		if (manager === undefined) {
			// default manager job implementation
			manager = (this.desc.manager !== null) ? new App.Entity.Facilities.ManagingJob() : null;
		}
		/** @private */
		this._manager = manager;
		if (this._manager !== null) {
			this._manager.facility = this;
			this._manager.desc = this.desc.manager;
		}
	}

	/** Facility display name
	 * @returns {string} */
	get name() {
		const res = State.variables[this.desc.baseName + "Name"];
		return res !== undefined ? res : 'the ' + this.genericName;
	}

	/** Facility generic name ("Brothel", "Schoolroom", etc.)
	 * @returns {string} */
	get genericName() {
		return this.desc.genericName !== null ? this.desc.genericName : capFirstChar(this.desc.baseName);
	}

	/** All jobs at this facility
	 * @returns {string[]}
	 */
	get jobsNames() {
		return Object.keys(this.desc.jobs);
	}

	/**
	 * Returns job description
	 * @param {string} [name] job name; the default job will be used if omitted
	 * @returns {App.Entity.Facilities.Job}
	 */
	job(name) {
		return this._jobs[name || this.desc.defaultJob];
	}

	get manager() {
		return this._manager;
	}

	/** Facility slave capacity
	 * @returns {number} */
	get capacity() {
		return State.variables[this.desc.baseName];
	}

	get established() {
		return this.capacity > 0;
	}

	/** Number of already hosted slaves
	 * @returns {number} */
	get hostedSlaves() {
		const job = this.job();
		return job ? job.employeesIds().length : 0;
	}

	get hasFreeSpace() {
		return this.capacity > this.hostedSlaves;
	}

	/**
	 * @param {string} name
	 * @returns {number}
	 */
	option(name) {
		return State.variables[this.desc.baseName + name];
	}

	/**
	 * @param {string} name
	 * @returns {number}
	 */
	upgrade(name) {
		return this.option("Upgrade" + name);
	}

	/**
	 * Can this facility host the given slave
	 * @param {App.Entity.SlaveState} slave
	 * @param {string} [job]
	 * @returns {string[]} array with rejection reasons. Slave can be hosted if this is empty.
	 */
	canHostSlave(slave, job) {
		job = job || this.desc.defaultJob;
		const j = this.job(job);
		if (j === undefined) {
			console.log(`Can't find job ${job} at ${this.name}.`); // eslint-disable-line no-console
		}
		// if there are more than one jobs at this facility, test them too
		if (Object.keys(this.desc.jobs).length > 1 && this.isHosted(slave)) {
			return [`${slave.slaveName} is already assigned to ${slave.assignment} at ${this.name}.`];
		}
		return j.canEmploy(slave);
	}

	/**
	 * Does the given slave work at this facility
	 * @param {App.Entity.SlaveState} slave
	 * @returns {boolean}
	 */
	isHosted(slave) {
		for (const j in this._jobs) {
			if (this._jobs[j].isEmployed(slave)) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Returns link text for the job assignments
	 * @param {number} i slave index
	 * @param {string} [job] generate link only for this job
	 * @param {string} [passage]
	 * @param {linkCallback} callback
	 * @returns {string[]}
	 */
	assignmentLinks(i, job, passage, callback) {
		/** @type {App.Entity.SlaveState} */
		const slave = App.Utils.slaveByIndex(i);
		const jobs = job === undefined ? this._jobs : {job: this._jobs[job]};

		let res = [];
		for (const jn in jobs) {
			const j = jobs[jn];
			let rejects = j.canEmploy(slave);
			if (rejects.length === 0) {
				res.push(j.assignmentLink(i, passage, callback));
			} else {
				res.push(App.UI.disabledLink(j.desc.position, rejects));
			}
		}
		return res;
	}

	/**
	 * Returns link text for the job assignments
	 * @param {number} i slave index
	 * @param {string} [job] generate link only for this job
	 * @param {string} [passage]
	 * @param {assignmentCallback} callback
	 * @returns {HTMLElement[]}
	 */
	assignmentLinkElements(i, job, passage, callback) {
		/** @type {App.Entity.SlaveState} */
		const slave = App.Utils.slaveByIndex(i);
		const jobs = job === undefined ? this._jobs : {job: this._jobs[job]};

		let res = [];
		for (const jn in jobs) {
			const j = jobs[jn];
			let rejects = j.canEmploy(slave);
			if (rejects.length === 0) {
				res.push(j.assignmentLinkElement(i, passage, callback));
			} else {
				res.push(App.UI.DOM.disabledLink(j.desc.position, rejects));
			}
		}
		return res;
	}

	/**
	 * Returns link text for the facility transfer
	 * @param {number} i slave index
	 * @param {string} [job] transfer to this job (uses default job if this is undefined)
	 * @param {string} [passage]
	 * @param {linkCallback} [callback]
	 * @returns {string}
	 */
	transferLink(i, job, passage, callback) {
		job = job || this.desc.defaultJob;
		return this._jobs[job].assignmentLink(i, passage, callback, this.genericName);
	}

	/**
	 * Returns link text for the facility transfer
	 * @param {number} i slave index
	 * @param {string} [job] transfer to this job (uses default job if this is undefined)
	 * @param {string} [passage]
	 * @param {linkCallback} [callback]
	 * @returns {HTMLElement}
	 */
	transferLinkElement(i, job, passage, callback) {
		job = job || this.desc.defaultJob;
		return this._jobs[job].assignmentLinkElement(i, passage, callback, this.genericName);
	}

	/**
	 * all slaves that are employed at this job
	 * @returns {App.Entity.SlaveState[]}
	 */
	employees() {
		if (Object.keys(this._jobs).length === 1) {
			return this.job().employees();
		}
		/** @type {App.Entity.Facilities.Job[]} */
		let jobArray = [];
		for (const jn in this._jobs) {
			jobArray.push(this._jobs[jn]);
		}
		return State.variables.slaves.filter(s => jobArray.some(j => j.isEmployed(s)));
	}

	/**
	 * Indices in the slaves array for all slaves that are employed at this job
	 * @returns {number[]}
	 */
	employeesIndices() {
		if (Object.keys(this._jobs).length === 1) {
			return this.job().employeesIndices();
		}
		/** @type {App.Entity.Facilities.Job[]} */
		let jobArray = [];
		for (const jn in this._jobs) {
			jobArray.push(this._jobs[jn]);
		}
		return State.variables.slaves.reduce(
			(acc, cur, idx) => { if (jobArray.some(j => j.isEmployed(cur))) { acc.push(idx); } }, []);
	}

	/**
	 * @protected
	 * @param {string} jobName
	 * @returns {App.Entity.Facilities.Job}
	 */
	_createJob(jobName) { /* eslint-disable-line no-unused-vars*/
		return new App.Entity.Facilities.Job();
	}
};

/**
 * Job for a facility with a single job option
 */
App.Entity.Facilities.FacilitySingleJob = class extends App.Entity.Facilities.Job {
	/**
	 * Returns link text for the penthouse assignment
	 * @param {number} i slave index
	 * @param {string} [passage] passage to go to
	 * @param {linkCallback} [callback]
	 * @param {string} [linkText]
	 * @returns {string}
	 */
	assignmentLink(i, passage, callback, linkText) {
		linkText = linkText || this.facility.genericName;
		const linkAction = callback !== undefined ? callback(this.desc.assignment) : '';
		const psg = passage === undefined ? '' : `, $returnTo = "${passage}"`;
		return `<<link "${linkText}" "Assign">><<set $assignTo = "${this.facility.genericName}", $i = ${i}${psg}>>${linkAction}<</link>>`;
	}

	/**
	 * @param {number} i slave index
	 * @param {string} [targetPassage] passage to go to
	 * @param {assignmentCallback} [callback]
	 * @param {string} [linkText]
	 * @returns {HTMLAnchorElement}
	 */
	assignmentLinkElement(i, targetPassage, callback, linkText) {
		linkText = linkText || this.desc.position;
		return App.UI.DOM.assignmentLink(State.variables.slaves[i], this.desc.assignment, "Assign",
			(slave, assignment) => {
				if (callback) {
					callback(slave, assignment);
				}
				// Set up environment for the "Assign" passage
				V.i = V.slaveIndices[slave.ID];
				V.activeSlave = slave;
				V.assignTo = this.facility.genericName;
				V.returnTo = targetPassage ? targetPassage : passage();
			}, linkText);
	}

	/** @returns {number[]} */
	employeesIndices() {
		const si = V.slaveIndices;
		const ids = V[this._employeeIDsVariableName]; // updated by assignJob()/removeJob()
		return ids.map(id => si[id]);
	}

	employeesIds() {
		return State.variables[this._employeeIDsVariableName]; // updated by assignJob()/removeJob();
	}

	/** @returns {App.Entity.SlaveState[]} */
	employees() {
		/** @type {App.Entity.SlaveState[]} */
		const slaves = State.variables.slaves;
		return this.employeesIndices().map(ind => slaves[ind]);
	}

	/** @private */
	get _employeeIDsVariableName() {
		return this.facility.genericName + "iIDs";
	}
};

App.Entity.Facilities.SingleJobFacility = class extends App.Entity.Facilities.Facility {
	/**
	 * @override
	 * @protected
	 * @returns {App.Entity.Facilities.FacilitySingleJob}
	 */
	_createJob() {
		return new App.Entity.Facilities.FacilitySingleJob();
	}
};

/** Instances of all facility objects */
App.Entity.facilities = {};


code:
App.Medicine.OrganFarm.Organ = class {
	/**
	 * @param {string} type - unique type of organ, used as key
	 * @param {string} name - display name
	 * @param {string} tooltip - full sentence, uncapitalized and unpunctuated
	 * @param {number} cost - how much it costs to grow the organ
	 * @param {number} time - how long it takes to grow the organ (without upgrades)
	 * @param {function(App.Entity.SlaveState):boolean} canGrow
	 * @param {string[]} dependencies - organs that are implanted first if possible, use type of other organs as values
	 * @param {App.Medicine.OrganFarm.OrganImplantAction[]} actions
	 */
	constructor({type: type, name, tooltip= "", cost, time, canGrow = () => true, dependencies = [], actions= []} = {}) {
		this.type = type;
		this.name = name;
		this.tooltip = tooltip;
		this.cost = cost;
		this.time = time;
		/** @type {function(App.Entity.SlaveState):boolean} */
		this.canGrow = canGrow;
		/** @type {string[]} */
		this.dependencies = dependencies;
		/** @type {App.Medicine.OrganFarm.OrganImplantAction[]} */
		this.implantActions = actions;

		App.Medicine.OrganFarm.Organs[type] = this;
	}
};

App.Medicine.OrganFarm.OrganImplantAction = class {
	/**
	 * @param {string} name - display name
	 * @param {string} tooltip - full sentence, uncapitalized and unpunctuated
	 * @param {number} healthImpact
	 * @param {string} surgeryType - for use with surgery degradation
	 * @param {boolean} autoImplant
	 * @param {function(App.Entity.SlaveState):boolean} canImplant
	 * @param {function(App.Entity.SlaveState):string} implantError - message to show if this action cannot be used
	 * @param {function(App.Entity.SlaveState)} implant
	 */
	constructor({name, tooltip = "", healthImpact, surgeryType, autoImplant = true, canImplant, implantError, implant} = {}) {
		this.name = name;
		this.tooltip = tooltip;
		this.healthImpact = healthImpact;
		this.surgeryType = surgeryType;
		this.autoImplant = autoImplant;
		/**
		 * True if this action can implant the organ
		 * @type {function(App.Entity.SlaveState):boolean}
		 */
		this.canImplant = canImplant;
		/**
		 * Error message if the organ cannot be implanted.
		 * @type {function(App.Entity.SlaveState):string}
		 */
		this.implantError = implantError;
		/**
		 * Implant the organ
		 * @type {function(App.Entity.SlaveState)}
		 */
		this.implant = implant;
	}
};

license: mit


code:
(function() {
	const readOnlySymbol = Symbol("readonly proxy");
	window.createReadonlyProxy = function(target) {
		if (target == null) return target; //intentionally ==
		if (target[readOnlySymbol]) { return target; }
		if (_.isArray(target)) {
			return new Proxy(target, {
				get:function(o, prop) {
					if(prop === readOnlySymbol) { return true; }
					const val = o[prop];
					if (typeof val === 'function') {
						if (['push', 'unshift', 'pop'].includes(prop)) {
							return function () {
								throw Error("Cannot modify a readonly array");
							}
						}
						return val.bind(target);
					}
					return createReadonlyProxy(val);
				},
				set:function(o, prop, value) {
					return true;
				},
				deleteProperty:function(o, prop) {
					return true;
				}
			});
		}
		if (_.isObject(target)) {
			return new Proxy(target, {
				get:function(o, prop) {
					if(prop === readOnlySymbol) { return true; }
					return createReadonlyProxy(o[prop]);
				},
				set:function(o, prop, value) {
					return true;
				},
				deleteProperty:function(o, prop) {
					return true;
				}
			});
		}
		return target;
	};
	const cheaterSymbol = Symbol("cheating proxy");
	window.createCheatProxy = function(target) {
		if (target == null) return target; //intentionally ==
		if (target[cheaterSymbol]) { return target; }
		if (_.isArray(target)) {
			return new Proxy(target, {
				get:function(o, prop) {
					if(prop === cheaterSymbol) { return true; }
					const val = o[prop];
					if (typeof val === 'function') {
						if (['push', 'unshift', 'pop'].includes(prop)) {
							return function (el) {
								const retval = Array.prototype[prop].apply(o, arguments);
								//Make sure we set cheater after calling the function
								State.variables.cheater = 1;//Can't use `V` because it probably points to a proxy.
								return retval;
							}
						}
						return val.bind(target);
					}
					return createCheatProxy(val);
				},
				set:function(o, prop, value) {
					o[prop] = value;
					State.variables.cheater = 1;//Can't use `V` because it probably points to a proxy.
					return true;
				},
				deleteProperty:function(o, prop) {
					delete o[prop];
					State.variables.cheater = 1;//Can't use `V` because it probably points to a proxy.
					return true;
				}
			});
		}
		if (_.isObject(target)) {
			return new Proxy(target, {
				get:function(o, prop) {
					if(prop === cheaterSymbol) { return true; }
					return createCheatProxy(o[prop]);
				},
				set:function(o, prop, value) {
					o[prop] = value;
					State.variables.cheater = 1;
					return true;
				},
				deleteProperty:function(o, prop) {
					delete o[prop];
					State.variables.cheater = 1;
					return true;
				}
			});
		}
		return target;
	};
})();
Object.defineProperty(window, "V", {
	get: function() {
		if (window.storyProxy != null) { return window.storyProxy; }
		return State.variables;
	}
});
//This should be used if the user might use V under normal, non-cheating circumstances but shouldn't be punished for accidentally setting the value. The attempt to make the change will simply be disregarded.
window.runWithReadonlyProxy = function(callback) {
	window.storyProxy = createReadonlyProxy(State.variables);
	try {
		return callback();
	} finally {
		window.storyProxy = null;
	}
};
// This should be used if setting values would constitute cheating. For example, a debug view that shows all variables in an editable form; showing isn't cheating, but making a change would be.
window.runWithCheatProxy = function(callback) {
	window.storyProxy = createCheatProxy(State.variables);
	try {
		return callback();
	} finally {
		window.storyProxy = null;
	}
};


code:
App.Corporate.InitConstants = function() {
	App.Corporate.payoutMinimumCash = 1000000;
	App.Corporate.payoutCorpValueMultiplier = 0.05;
	App.Corporate.dividendOptions = [0.75, 0.5, 0.25, 0.1, 0.05];
	App.Corporate.stockSplits = [
		{
			'newStocks': 3,
			'oldStocks': 2,
			'weeks': 1,
			'cost': 7.5
		},
		{
			'newStocks': 2,
			'weeks': 1,
			'cost': 10
		},
		{
			'newStocks': 3,
			'weeks': 2,
			'cost': 12.5
		},
		{
			'newStocks': 4,
			'weeks': 3,
			'cost': 15
		},
		{
			'oldStocks': 4,
			'weeks': 1,
			'cost': 15
		},
		{
			'oldStocks': 10,
			'weeks': 2,
			'cost': 25
		},
	];
	/*
	center/range object: The center value is the average or center value of the property, while range is the value range (centered on center). So if center is 1 and range is 0.5, the value can be anything from 0.75 to 1.25.

	== Base ==
	id: The variable ID for identifying the department.
	name: The display name of the department.
	focusDescription: The descriptive phrase of what the department does:
	The department focuses on {breaking slaves}.

	sizeCost: How much a single development of the department costs in thousands.
	maintenance: (object) Defines the amount paid each week.
		linear: How much a single development costs to maintain each week in thousands.
		quadratic: Development maintenance increases quadratically; this is the multiplier on the squared development component.
		category: Which division maintenance category this division belongs to. Multiple divisions in a category increase overhead costs exponentially.
	founding: (object) Defines information regarding the founding of the division.
	corporateCash: Defines how much it costs the player to *found the corporation* with this department with a 2-to-1 split, in thousands. This will be the corporation's starting cash, from which the development and slaves will be purchased.
	startingSize: How developed the department is upon founding, including how many active slaves the department starts with (Note that acquiring divisions don't start with slaves)
	merger: A list of companies that can be purchased during the merger event. The event will collect all of these company objects associated with owned divisions and pick some at random.
		name: The generic name of the company that is being purchased. This will have "the" put in front of it, so it must fit with the sentance "The {name} will be bought."
		cost: How much, in thousands, it will cost to purchase this company. Default: 50.
		text: An object with the flavor text of the event.
			trouble: A description of the company and its troubles. Will be in the sentence "The first company is {trouble}"
			acquire: The text that will be shown when the player chooses to acquire the company. Will be in the sentence "You acquire the {name}{acquire}".
		result: The functional result of acquiring this company.
			development: The number of developments that will be added to this division.
			slaves: the number of activeSlaves that will be added to this division. If the division doesn't have enough room to store them you will have to purchase more development by the end of the week or they will be sold off. *WARNING* Do not provide this property if the division cannot have activeSlave set (Acquiring divisions, for example)

	== Acquiring ==
	An acquiring division generates new slaves. It can always sell to the market but cannot buy from the market.
	slaveValue: How much value is added to a slave when it becomes a Held slave.
	acquiring: (center/range) The percentage of development that can acquire a slave each week.
	mercenaryHelp: (object) If present, defines how and when your mercenaries (not SF) assist this division in acquiring slaves.
	level: What level the mercenaries must be above in order to assist.
	cost: How much each level of mercenary above level costs the department for this assistance, in thousands.
	nextDivision: (list) The ids of the divisions that acquired slaves can be sent directly to. Note that if any of these divisions can send slaves to a listed division, that division will block this division from sending to it. For example, if A->[B,C] and B->C, then A->C can only happen if B *has not* been founded. Once B is founded, slaves from A must proceed A->B->C and cannot skip to C.

	==Processing==
	A processing division enhances the value of slaves. It can always buy from the market and sell to the market.
	addedValue: How much value is added to a slave when it becomes a Held slave.
	processing: (center/range) The percentage of active slaves that become held slaves each week.
	slaveProcessType: (text object) The verb used for the completion of the processing done on slaves. It will be used to explain when a slave moves from processed to held.
	present: The department can {break} 5 slaves.
	past:The department {broke} 5 slaves.
	slaveProcessDescription: The descriptive phrase for what sort of processing is being done to the division's slaves.
	present: the department is {breaking} 5 slaves.
	future: the department will {break} 5 slaves.
	past: Over the past week, the department {broke} 5 slaves.
	market: The department bought {fresh slaves} from the market.
	nextDivision: (list) The ids of the divisions that improved slaves can be sent directly to. Note that if any of these divisions can send slaves to a listed division, that division will block this division from sending to it. For example, if A->[B,C] and B->C, then A->C can only happen if B *has not* been founded. Once B is founded, slaves from A must proceed A->B->C and cannot skip to C.

	==Working==
	A working division produces revenue by working slaves. It can always buy from the market but cannot sell to the market.
	attrition: (center/range) The percentage of active slaves that become no longer workable
	revenue: (center/range) The amount of money generated by a slave.
	slaveProcessDescription: The descriptive phrase for what sort of work is being done by the division's slaves and how they are removed from the workforce.
	present: the department is {exploiting} 5 slaves.
	future: the department will {exploit} 5 slaves.
	past: Over the past week, the department {wore out} 5 slaves.
	market: The department bought {menials} from the market.

	*/
	App.Corporate.divisionList = [
		new App.Corporate.Division.Acquiring( {
			'id':'Extra',
			'name':'Extralegal Enslavement',
			'focusDescription':'capturing and enslaving prisoners',
			'sizeCost': 20,
			'slaveValue':10,
			'acquiring': {
				'center':1,
				'range': 0.8,
			},
			'maintenance':{
				'linear': 7.5,
				'quadratic': 5,
				'category':'acquision',
			},
			'founding':{
				'corporateCash':100,
			},
			'mercenaryHelp': {
				'level': 2,
				'cost': 0.05
			},
			'nextDivision': [
				"Break",
				"Arcade"
			],
			'merger':[
				{
					'name': 'mercenary company',
					'cost': 50,
					'text': {
						'trouble':'a troubled mercenary company. After a spate of failed operations, the rank and file deposed their management and are now looking to merge with a better-run organization. The old leadership did not engage in enslavement activities, but the mercenaries have decided to turn their talents towards slaving for profit in conflict zones.',
						'acquire':' and ease their transition to conflict zone slaving.'
					},
					'result': {
						'development': 5
					}
				}
			]
		}),
		new App.Corporate.Division.Acquiring( {
			'id': 'Legal',
			'name': 'Legal Enslavement',
			'focusDescription': 'legal enslavement',
			'sizeCost': 25,
			'slaveValue':15,
			'acquiring': {
				'center':1,
				'range': 0.5,
			},
			'maintenance':{
				'linear': 12,
				'quadratic': 6,
				'category':'acquision',
			},
			'founding':{
				'corporateCash':125,
			},
			'nextDivision': [
				"Train",
				"Menial",
				"Surgery"
			],
			'merger':[
				{
					'name':'telemarketing firm',
					'cost': 50,
					'text': {
						'trouble':'an old world telemarketing firm. In an attempt to survive in the changing economy, it turned its focus towards convincing desperate, ignorant, or stupid people from the old world to accept voluntary enslavement, through a variety of entrapment procedures. It failed to make the transition successfully, but you could certainly continue its operations under your aegis.',
						'acquire':' and clean out the deadwood, getting the business in Free Cities shape.'
					},
					'result': {
						'development':5
					}
				}
			]
		}),
		new App.Corporate.Division.Processing( {
			'id': 'Break',
			'name': 'Slave Breaking',
			'focusDescription': 'breaking slaves',
			'sizeCost': 10,
			'addedValue': 5,
			'processing': {
				'center':0.3,
				'range':0.3,
			},
			'maintenance':{
				'linear': 0.45,
				'quadratic': 0.6,
				'category':'processing',
			},
			'founding':{
				'corporateCash':25,
			},
			'slaveProcessType':{
				'present':'break',
				'past':'broke'
			},
			'slaveProcessDescription':{
				'present':'attempting to break',
				'future':'break',
				'past':'broken',
				'market': {
					single: 'slave that needs breaking',
					plural: 'slaves that need breaking'
				}
			},
			'nextDivision': [
				"Train",
				"Menial",
				"Surgery"
			],
			'merger':[
				{
					'name': 'slave breaking facility',
					'cost': 50,
					'text': {
						'trouble':'a small slave breaking firm. Despite a great need for obedient slaves the owner of this little establishment was unable to turn a profit. The facility has everything a respecting slave breaking could ever need, it seems the owner simply lacked the right character to apply effective breaking techniques. Your corporation knows perfectly well what it takes to achieve obedience and the assets can be put to great use.',
						'acquire':' and staff it with qualified personnel to make use of the new assets.'
					},
					'result': {
						'development':5,
						'slaves':5
					}
				}
			]
		}),
		new App.Corporate.Division.Processing( {
			'id': 'Surgery',
			'name': 'Slave Modifications',
			'focusDescription': 'physical slave modifications',
			'sizeCost': 20,
			'addedValue':8,
			'processing': {
				'center':0.5,
				'range':0.2,
			},
			'maintenance':{
				'linear': 2.4,
				'quadratic': 1,
				'category':'processing',
			},
			'founding':{
				'corporateCash':55,
			},
			'slaveProcessType':{
				'present':'modify',
				'past':'finished working on'
			},
			'slaveProcessDescription':{
				'present':'working on',
				'future':'work on',
				'past':'improved',
				'market': {
					single: 'slave that could use some work done to their body',
					plural: 'slaves that could use some work done to their bodies'
				}
			},
			'nextDivision': [
				"Train",
				"Dairy"
			],
			'merger':[
				{
					'name': 'medical clinic',
					'cost': 50,
					'text': {
						'trouble':"a small medical clinic. Medical malpractice continues to be a major source of trouble for surgeons in the old world. A plastic surgeon has just suffered defeat in a major legal case over implant ruptures. He's looking to make the transition to a more enlightened area, along with his inventory and staff.",
						'acquire':" and the surgeon decides to retire on the proceeds of the sale, wishing your corporation well. It was a smart decision; he can retire to the Free Cities on that sum of money, and remote surgery is killing surgeons' wages."
					},
					'result': {
						'development': 3,
						'slaves':3
					}
				}
			]
		}),
		new App.Corporate.Division.Processing( {
			'id': 'Train',
			'name': 'Slave Training',
			'focusDescription': 'training slaves',
			'sizeCost': 25,
			'addedValue': 11,
			'processing': {
				'center':0.2,
				'range':0.3,
			},
			'maintenance':{
				'linear': 0.7,
				'quadratic': 1,
				'category':'processing',
			},
			'founding':{
				'corporateCash':50,
			},
			'slaveProcessType':{
				'present':'train',
				'past':'trained'
			},
			'slaveProcessDescription':{
				'present':'training',
				'future':'train',
				'past':'trained',
				'market': {
					single: 'slave that needs slave training',
					plural: 'slaves that need slave training'
				}
			},
			'nextDivision': [
				"Whore"
			],
			'merger':[
				{
					'name': 'slave training firm',
					'cost': 50,
					'text': {
						'trouble':"a small slave training firm. Slave training is a notoriously difficult business, since the maturation times on the merchandise can be extremely long, and the improvement in price can be marginal if the training does not prove highly successful. There's nothing inherently wrong with the business, they've simply hit a cash flow bottleneck and need to merge with a cash-rich organization like yours.",
						'acquire':". The previous owners are happy they'll be able to continue operations under the aegis of a better-run, richer corporation."
					},
					'result': {
						'development': 3,
						'slaves':3
					}
				}
			]
		}),
		new App.Corporate.Division.Working( {
			'id': 'Arcade',
			'name': 'Sex Arcades',
			'focusDescription': 'operating sex arcades',
			'sizeCost': 5,
			'attrition': {
				'center':0.05,
				'range':0.2,
			},
			'maintenance':{
				'linear': 0.2,
				'quadratic': 0.125,
				'category':'working',
			},
			'revenue':{
				'center':1000,
				'range':0.1
			},
			'founding':{
				'corporateCash':15,
				'startingSize':20,
			},
			'slaveWorkDescription':{
				'present':'exploiting',
				'future':'exploit',
				'past':'wore out',
				'market': 'fresh slave'
			},
			'merger':[
				{
					'name': 'café',
					'cost': 50,
					'text': {
						'trouble':"a quaint maid café. The prime location together with a popular concept should make for a bustling business and yet the owner is looking to get out of the business hoping to salvage some of his savings. Sadly there was a lack of capital to employ attractive servant slave maids. While someone might very well be more than willing to come in and invest in some better slaves, the place is so cheap you could easily replace the furniture and use the current assets to create a new arcade location for your corporation instead.",
						'acquire':". The old owner wishes his slaves luck under the new management, not knowing your plans for the place. A new sex arcade under your corporate umbrella will open shortly."
					},
					'result': {
						'development':5,
						'slaves':5
					}
				}
			]
		}),
		new App.Corporate.Division.Working( {
			'id': 'Menial',
			'name': 'Menial Services',
			'focusDescription': 'offering menial services',
			'sizeCost': 6.5,
			'attrition': {
				'center':0.05,
				'range':0.2,
			},
			'maintenance':{
				'linear': 0.2,
				'quadratic': 0.1,
				'category':'working',
			},
			'revenue':{
				'center':1250,
				'range':0.2
			},
			'founding':{
				'corporateCash':20,
				'startingSize':20,
			},
			'slaveWorkDescription':{
				'present':'exploiting',
				'future':'exploit',
				'past':'wore out',
				'market':'menial slave'
			},
			'merger':[
				{
					'name': 'slaveholding firm',
					'cost': 50,
					'text': {
						'trouble':"a minor slaveholding company. A major deal with a troubled government just fell through, and they had depended on it going through to an unwise degree. They need to merge with a larger slaving concern immediately, but there's nothing at all wrong with their significant inventory of slaves.",
						'acquire':", cash out its hapless staff, and absorb its stock into your corporation."
					},
					'result': {
						'development':5,
						'slaves':5
					}
				}
			]
		}),
		new App.Corporate.Division.Working( {
			'id': 'Dairy',
			'name': 'Dairy',
			'focusDescription': 'milking slaves',
			'sizeCost': 15,
			'attrition': {
				'center':0.05,
				'range':0.2,
			},
			'maintenance':{
				'linear': 0.85,
				'quadratic': 0.4,
				'category':'working',
			},
			'revenue':{
				'center':3000,
				'range':0.2
			},
			'founding':{
				'corporateCash':25,
			},
			'slaveWorkDescription':{
				'present':'milking',
				'future':'milk',
				'past':'ran dry',
				'market': 'cow'
			},
			'merger':[
				{
					'name': 'farm',
					'cost': 50,
					'text': {
						'trouble':"a dairy farm. Why a conventional dairy farm popped up as target confused you for a moment, but it quickly became clear the entire family was so deep in debt it would be a no brainer to buy the farm and start milking the large-uddered farmer's daughters as a great addition to your corporate dairy.",
						'acquire':" and your new slaves for the corporation. The family is not happy with your plans, but their approval is not required, you care only for their fluids."
					},
					'result': {
						'development':3,
						'slaves':3
					}
				}
			]
		}),
		new App.Corporate.Division.Working( {
			'id': 'Whore',
			'name': 'Escort Service',
			'focusDescription': 'whoring out slaves',
			'sizeCost': 20,
			'attrition': {
				'center':0.05,
				'range':0.2,
			},
			'maintenance':{
				'linear': 0.7,
				'quadratic': 0.25,
				'category':'working',
			},
			'revenue':{
				'center':3000,
				'range':0.1
			},
			'founding':{
				'corporateCash':50,
			},
			'slaveWorkDescription':{
				'present':'whoring out',
				'future':'whore out',
				'past':'lost their appeal',
				'market': 'trained whore'
			},
			'merger':[
				{
					'name': 'brothel',
					'cost': 50,
					'text': {
						'trouble':"a little brothel. With slave whores becoming the dominant force in sexual services the current madam lost her passion for the business. She's getting up there in age and has run a tight ship for many years so she deemed it the right time to bow out. All you need to do to add a new brothel location for your corporation is sign at the dotted line before anyone else has a chance to bite.",
						'acquire':" before anyone else can make an offer. The madam is surprised by your speed, but happily signs over the brothel."
					},
					'result': {
						'development':3,
						'slaves':3
					}
				}
			]
		})
	];
	// divisionCategoriesList: Division maintenance categories are used to calculate the overhead that occurs from having multiple divisions within the same category.
	//  id: The identifier that will be used in division.maintenance.category
	//  freeDivisions: How many divisions must appear in this category before the overhead cost comes into play. For example, if this is 1, then the second division will cost divisionCost, and the third will bring the overhead up to four times divisionCost.
	//  divisionCost: How much to multiply the overhead by in cash. The first division past the free divisions will cost this much, but the value will increase exponentially.
	//  freeDevelopment: How many developments, totaled across all divisions in the category, are ignored by this category before overhead comes into play. Note: freeDivisions does *not* affect this; if freeDevelopment is 100 and freeDivisions is 1, if you only have a single division with 110 developments, you will be charged for the 10 developments over.
	//  developmentCost: How much to multiply the squared developments by, in cash. The first development past the ignored value will cost this much, but the cost increases exponentially.
	// corporate: The corporate-wide overhead for divisions, beyond the individual categories.
	//  freeDivisions: How many total divisions, across all categories, are ignored before charging corporate level overhead.
	//  divisionCost: How much to multiply the square of counted divisions in cash. The first counted division will cost this much, but the value increases exponentially.
	// Equations:
	// divisionBaseOverhead = (divisions - freeDivisions) ^ 2 * divisionCost
	// divisionOperationOverhead = (sum(development) - freeDevelopment) ^ 2 * developmentCost
	// corpOverhead = (divisions - 2) ^ 2 * 10000
	App.Corporate.maintenance = {
		divisionCategoriesList: [
			{
				id:'acquision',
				freeDivisions: 0,
				divisionCost: 25000,
				freeDevelopment: 100,
				developmentCost: 2
			},
			{
				id:'processing',
				freeDivisions: 0,
				divisionCost: 25000,
				freeDevelopment: 200,
				developmentCost: 1
			},
			{
				id:'working',
				freeDivisions: 1,
				divisionCost: 15000,
				freeDevelopment: 800,
				developmentCost: 0.1
			},
		],
		corporate: {
			freeDivisions: 2,
			divisionCost: 10000
		}
	};
};


code:
App.Corporate.Init_DivisionAcquiring = function(shared) {
	App.Corporate.Division.Acquiring = class extends App.Corporate.Division.Base {
		constructor({slaveValue, acquiring, mercenaryHelp, nextDivision}) {
			super(arguments[0]);
			this._const.slaveValue = slaveValue;
			this._const.acquiring = new averageRange(acquiring);
			this._const.nextDivisions = nextDivision;
			if(mercenaryHelp != null) {
				this._const.mercenaryHelp = {
					level: mercenaryHelp.level,
					cost: mercenaryHelp.cost
				};
			}
		}
		// abstract virtual definitions
		get fromMarket()		  { return false; }
		get toMarket()			{ return true; }
		get heldSlaves()		  { return this.getStored("Slaves"); }
		set heldSlaves(value)	 { this.setStored("Slaves", Math.trunc(value)); }
		get activeSlaves()		{ return 0; }
		set activeSlaves(value)   { throw "Cannot set active slaves for acquiring divisions"; }
		get processRate()		 { return this._const.acquiring.center; }
		get purchasedSlaveValue() { return null; }
		get initialSlaveValue()   { return this.soldSlaveValue; }
		get soldSlaveValue()	  { return this._const.slaveValue; }
		get nounFinishedSlave()   { return "slave"; }
		get nounSlaveFromMarket() { return "slave"; }
		messageSlaveCount() {
			return `It averages <span class="green">${numberWithPluralNonZero(this.developmentCount, "new slave")}</span> each week.`;
		}
		messageSlaveOutput() {
			return shared.MessageSlaveToMarket(this);
		}
		message_endWeek_Slaves(divLedger) {
			let newSlaves = divLedger.slaves.value;
			if(newSlaves > 0) {
				return `<span class="green">acquired ${numberWithPlural(newSlaves, "slave")}</span>`
					 + (this.hadMercenaryHelp ? " with the help of your mercenaries" : "");
			} else {
				return `<span class="red">failed to acquire any slaves</span>`
					 + (this.hadMercenaryHelp ? " even with the help of your mercenaries" : "");
			}
		}
		endWeek_Slaves(divLedger) {
			let slaves = shared.EndWeekProcessing_Slaves(this.developmentCount, this._const.acquiring);
			this.heldSlaves += slaves.value;
			return divLedger.slaves.apply(slaves);
		}
		// virtual overrides
		get purchasedSlaveValue() {
			return null;
		}
		get nextDivisions()	   { return this._const.nextDivisions; }
		dissolve() {
			App.Corporate.sellSlaves(this, this.heldSlaves);
			super.dissolve();
			delete V[`${this._const.corpId}Slaves`];
		}
		getAutoBuyFromMarket() {
			return false;
		}
		setAutoBuyFromMarket(value) {
			throw "Acquiring divisions cannot acquire from the market";
		}

		get availableRoom() { return 0; }
		get maintenanceSlaves() { return this.developmentCount; }
		get maintenanceCost() {
			// If it makes sense to have mercenaries help with other types of divisions, this code and the mercenaryHelp property will need to be moved into the super class.
			let baseCost = super.maintenanceCost;

			if(this.hadMercenaryHelp) {
				const mercHelpCost = Math.trunc((V.mercenaries - this.mercenaryHelp.level) * this.mercenaryHelp.cost * 1000);
				baseCost += mercHelpCost;
			}
			return baseCost;
		}

		create() {
			super.create();
			this.heldSlaves = 0;
			shared.FoundingSetupAutoSell(this);
		}

		get hadMercenaryHelp() { return this.mercenaryHelp != null && V.mercenaries > this.mercenaryHelp.level; }
	};
};


code:
App.Corporate.Init_DivisionBase = function(shared) {
	App.Corporate.Division.Base = class {
		constructor({id, name, focusDescription, sizeCost, maintenance, founding, merger}) {
			this._const = {};
			this._const.id = id;
			this._const.corpId = `corpDiv${capFirstChar(id)}`;
			this._const.cost = sizeCost;
			this._const.name = name;
			this._const.focusDescription = focusDescription;
			this._const.maintenance = {
				quadratic: maintenance.quadratic,
				linear: maintenance.linear,
				category: maintenance.category
			};
			if(founding != null) {
				this._const.founding = new shared.FoundingType(this, founding);
			}
			if(merger != null && _.isObject(merger) || (Array.isArray(merger) && merger.length > 0)) {
				if(!Array.isArray(merger)) {
					merger = [merger];
				}
				this._const.merger = merger;
			}
			this.relatedDivisions = new shared.RelatedDivisionType();
		}
		// initialized data
		get id()				  { return this._const.id; }
		get sizeCost()			{ return this._const.cost; }
		get name()				{ return this._const.name; }
		get focusDescription()	{ return this._const.focusDescription; }
		get founding()			{ return this._const.founding; }
		get nextDivisions()	   { return null; }
		get hasMergers()		  { return this._const.merger != null; }
		get mergerChoices()	   { return this._const.merger; }
		get maintenanceCategory() { return this._const.maintenance.category; }

		// stored variables
		get founded()			   { return this.getStored('') == 1; }
		get foundedDate()		   { return this.getStored('Founded'); }
		get developmentCount()	  { return this.getStored("Dev"); }
		set developmentCount(value) {
			if(value < 0) { throw "Cannot set development count to less than 0"; }
			// dissolve is the only function that sets founded to false.
			if(value == 0 && this.founded) { throw "Cannot set development count to 0; use dissolve instead."; }
			this.setStored("Dev", value);
		}

		// calculated
		get availableRoom()	   { return Math.max(0, this.developmentCount - this.activeSlaves); }
		get maintenanceCost() {
			return Math.trunc( this._const.maintenance.linear	* 1000 * this.developmentCount
							 + this._const.maintenance.quadratic * Math.pow(this.activeSlaves, 2));
		}
		get foundingCostDivision() { return this._const.founding.size * this.sizeCost; }
		get foundingCost()		 { return this.foundingCostDivision; }
		get canFoundCorporation()  { return this._const.founding != null; }
		get foundingCash() {
			if(!this.canFoundCorporation) { throw `${this.name} is not set up found a corporation`; }
			return 1000 * this._const.founding.startingPrice;
		}
		get value() {
			const developmentValue = this.developmentCount * this.sizeCost * 800;
			let slaveProcValue = 0;
			let slaveHeldValue = 0;

			if(this.activeSlaves > 0) { slaveProcValue = this.activeSlaves * this.purchasedSlaveValue * 1000; }
			if(this.heldSlaves > 0) { slaveHeldValue = this.heldSlaves * this.soldSlaveValue * 1000; }

			return developmentValue + slaveProcValue + slaveHeldValue;
		}

		// abstract virtual
		get fromMarket()					 { throw "Must be defined"; }
		get toMarket()					   { throw "Must be defined"; }
		get heldSlaves()					 { throw "Must be defined"; }
		set heldSlaves(value)				{ throw "Must be defined"; }
		get activeSlaves()				   { throw "Must be defined"; }
		set activeSlaves(value)			  { throw "Must be defined"; }
		get processRate()					{ throw "Must be defined"; }
		get initialSlaveValue()			  { throw "Must be defined"; }
		get soldSlaveValue()				 { throw "Must be defined"; }
		get slaveAction()					{ throw "Must be defined"; }
		get nounFinishedSlave()			  { throw "Must be defined"; }
		get nounSlaveFromMarket()			{ throw "Must be defined"; }
		messageSlaveCount()				  { throw "Must be defined"; }
		messageSlaveOutput()				 { throw "Must be defined"; }
		message_endWeek_Slaves(divLedger)	{ throw "Must be defined"; }
		endWeek_Slaves(ledger, divLedger)	{ throw "Must be defined"; }

		// The value of a slave ignoring enhancements from founded divisions
		// The actual value of a slave after all improvements
		get purchasedSlaveValue() {
			let cheapest  = {'value':Number.MAX_VALUE, 'div': null};
			let expensive = {'value': 0, 'div': null};
			for(let fromDiv of this.relatedDivisions.from) {
				let initialValue = fromDiv.initialSlaveValue;
				if(initialValue < cheapest.value) {
					cheapest.value = initialValue;
					cheapest.div = fromDiv;
				}
				if(fromDiv.founded) {
					let soldValue = fromDiv.soldSlaveValue;
					if(soldValue > expensive.value) {
						expensive.value = soldValue;
						expensive.div = fromDiv;
					}
				}
			}
			if(expensive.div != null && expensive.value != cheapest.value) {
				// The added value of an owned intermediary takes time to work its way through this division to the next
				let expensiveDiv = expensive.div;
				let valueDiff = expensive.value - cheapest.value;
				let weeksSinceFounding = V.week - (expensiveDiv.foundedDate || 0);
				let weeksToProcess = 10 * expensiveDiv.processRate;
				let multiplier = Math.min(weeksSinceFounding / weeksToProcess, 1);
				let finalAddedValue = valueDiff * multiplier;
				return cheapest.value + finalAddedValue;
			} else if(cheapest.div != null) {
				return cheapest.value;
			}
			throw "No route to acquisition found.";
		}
		get maintenanceSlaves() { return this.activeSlaves * this.processRate; }
		getDisplayMaintenanceCost() {
			const cost	= this.maintenanceCost;
			const processedCount = this.maintenanceSlaves;

			return {cost, perUnit: cost / processedCount};
		}
		getAutoSendToDivision(division) {
			return !App.Corporate.ownsIntermediaryDivision(this, division)
				&& this.getStored(`To${division.id}`) == 1;
		}
		setAutoSendToDivision(division, value) {
			this.setStored(`To${division.id}`, value ? 1 : 0);
		}
		getAutoSendToMarket() {
			return this.getStored("ToMarket") == 1;
		}
		setAutoSendToMarket(value) {
			this.setStored("ToMarket", value ? 1 : 0);
		}
		getAutoBuyFromMarket() {
			return this.getStored("FromMarket");
		}
		setAutoBuyFromMarket(value) {
			this.setStored("FromMarket", value ? 1 : 0);
		}
		endweek_Revenue(divLedger) {
			// Unless otherwise specified, divisions don't produce revenue directly.
		}
		endWeek_Transfer(divLedger) {
			let transferRetval = {total:0};
			let divisions = [];
			for(let otherDiv of this.relatedDivisions.to.filter(div=> div.founded && this.getAutoSendToDivision(div))) {
				const otherLedger = divLedger.weekLedger.getDivision(otherDiv);

				const room = otherDiv.availableRoom - otherLedger.transfer.in;
				if(room == 0) { continue; }
				divisions.push({division: otherDiv, room});
			}
			const fillDivisions = evenFillArray(divisions, this.heldSlaves, pair=>pair.room);
			for(const filled of fillDivisions) {
				const division = filled.item.division;
				const value = filled.value;
				divLedger.transfer.addDivision(division, value);
			}
		}
		endWeek_Market(divLedger) {
			if(this.getAutoSendToMarket()) {
				divLedger.market.sell = this.heldSlaves - divLedger.transfer.total;
			}
			if(this.getAutoBuyFromMarket()) {
				divLedger.market.buy = this.availableRoom - divLedger.transfer.in;
			}
			shared.SellUnhousedSlaves(this, divLedger, this.processRate);
		}

		create() {
			if(this.founded) { throw `${this.name} has already been founded.`; }

			App.Corporate.expandedDivision();
			App.Corporate.chargeAsset(this.foundingCostDivision * 1000, "development");
			this.setStored('', 1);
			this.developmentCount = this._const.founding.size;
			this.setStored('Founded', V.week);
		}
		dissolve() {
			this.setStored('', 0);
			App.Corporate.sellDevelopment(this);
			App.Corporate.dissolvedDivision();
			this.relatedDivisions.to.forEach(nextDep => delete V[`${this._const.corpId}To${nextDep.id}`]);
		}

		// private helpers
		getStored(key	   ) { return V[this._const.corpId + key];  }
		setStored(key, value) { V[this._const.corpId + key] = value; }
	};
};


code:
App.Corporate.Init_DivisionProcessing = function(shared) {
	App.Corporate.Division.Processing = class extends App.Corporate.Division.Base {
		constructor({founding, addedValue, processing, nextDivision, slaveProcessType, slaveProcessDescription}) {
			super(arguments[0]);
			this._const.addedValue = addedValue;
			this._const.processing = new averageRange(processing);
			this._const.nextDivisions = nextDivision;
			this._const.slaveProcessType = slaveProcessType;
			this._const.slaveProcessDescription = slaveProcessDescription;
		}

		// abstract virtual definitions
		get fromMarket()		{ return true; }
		get toMarket()		  { return true; }
		get heldSlaves()		{ return this.getStored("Slaves2"	   ); }
		set heldSlaves(value)   {		this.setStored("Slaves2", Math.trunc(value)); }
		get activeSlaves()	  { return this.getStored("Slaves"		); }
		set activeSlaves(value) {		this.setStored("Slaves", Math.trunc(value)); }
		get processRate()	   { return this._const.processing.center; }
		get soldSlaveValue() {
		  // TODO: find a way to cache this.
		  return this.purchasedSlaveValue + this._const.addedValue;
		}
		get slaveAction() {
			return this._const.slaveProcessDescription;
		}
		get nounFinishedSlave()   { return `${this._const.slaveProcessDescription.past} slave`; }
		get nounSlaveFromMarket() { return this._const.slaveProcessDescription.market; }
		messageSlaveCount() {
			return shared.MessageProcessedSlaves(this, `can ${this._const.slaveProcessType.present}`, 'green');
		}
		messageSlaveOutput() {
			return shared.MessageSlaveToMarket(this);
		}
		message_endWeek_Slaves(divLedger) {
			let newSlaves = divLedger.slaves.value;
			// The division
			let retval = this._const.slaveProcessType.past; // exploited
			if(newSlaves <= 0) {
				retval += " none of its slaves.";
			} else {
				retval += ` <span class="green">${numberWithPlural(newSlaves, 'slave')}.</span>`;
			}
			retval += " The division ";
			if(this.activeSlaves) {
				retval += `is still ${this._const.slaveProcessDescription.present} ${numberWithPlural(this.activeSlaves, "slave")}`;
			} else {
				retval += `doesn't have any slaves to ${this._const.slaveProcessDescription.future}`;
			}
			return retval;
		}
		endWeek_Slaves(divLedger) {
			let slaves = shared.EndWeekProcessing_Slaves(this.activeSlaves, this._const.processing);
			this.activeSlaves -= slaves.value;
			this.heldSlaves += slaves.value;
			return divLedger.slaves.apply(slaves);
		}
		get initialSlaveValue() {
			const values = this.relatedDivisions.from
												.map(fromDiv => fromDiv.initialSlaveValue);
			if(values.length == 0) { throw "No route to acquisition found."; }
			return Math.min(...values) + this._const.addedValue;
		}
		// virtual override
		get nextDivisions() { return this._const.nextDivisions; }
		get developmentCount() { return super.developmentCount; }
		set developmentCount(value) {
			super.developmentCount = value;
			shared.SellOverflowSlaves(this);
		}
		dissolve() {
			App.Corporate.sellSlaves(this, this.heldSlaves);
			super.dissolve();
			delete V[this._const.corpId + "Slaves"];
			delete V[this._const.corpId + "Slaves2"];
		}

		get foundingCostSlaves() { return this._const.founding.size * this.purchasedSlaveValue; }
		get foundingCost()	   { return this.foundingCostDivision + this.foundingCostSlaves; }
		create() {
			super.create();
			App.Corporate.chargeAsset(this.foundingCostSlaves * 1000, "slaves");
			this.activeSlaves = this._const.founding.size;
			this.heldSlaves = 0;
			shared.FoundingSetupAutoBuy(this);
			shared.FoundingSetupAutoSell(this);
		}
	};
};


code:
App.Corporate.Init_DivisionWorking = function(shared) {
	App.Corporate.Division.Working = class extends App.Corporate.Division.Base {
		constructor({founding, attrition, revenue, slaveWorkDescription}) {
			super(arguments[0]);
			this._const.attrition = new averageRange(attrition);
			this._const.revenue = new averageRange(revenue);
			this._const.slaveWorkDescription = slaveWorkDescription;
		}

		// abstract virtual definitions
		get fromMarket() { return true; }
		get toMarket() { return false; }
		get heldSlaves() { return 0; }
		set heldSlaves(value) { throw "Cannot set held slaves of working division"; }
		get activeSlaves() { return this.getStored("Slaves"); }
		set activeSlaves(value) { this.setStored("Slaves", Math.trunc(value)); }
		get processRate() { return this._const.attrition.center; }
		get initialSlaveValue()   { return null; }
		get soldSlaveValue() { return null; }
		get slaveAction() {
			return this._const.slaveWorkDescription;
		}
		get nounFinishedSlave()   { throw "Cannot get finished slave in working division, since they don't produce finished slaves."; }
		get nounSlaveFromMarket() { return this._const.slaveWorkDescription.market; }
		messageSlaveCount() {
			return shared.MessageProcessedSlaves(this, `has to replace`, 'red');
		}
		messageSlaveOutput() {
			/* TODO: originally some divisions had a slight description for what the work was; ie, "the escorts generate" */
			return `The division generates <span class="yellowgreen">${cashFormat(this.slaveRevenue)}</span> per slave on average.`;
		}
		endWeek_Slaves(divLedger) {
			let slaves = shared.EndWeekProcessing_Slaves(this.activeSlaves, this._const.attrition);
			this.activeSlaves -= slaves.value;
			return divLedger.slaves.apply(slaves);
		}
		message_endWeek_Slaves(divLedger) {
			let lostSlaves = divLedger.slaves.value;
			let retval = '';// The division
			if(this.activeSlaves <= 0) {
				retval += `has <span class="red">no slaves</span> to ${this._const.slaveWorkDescription.future}`;
			} else {
				retval += `is ${this._const.slaveWorkDescription.present} <span class="green">${numberWithPlural(this.activeSlaves, 'slave')}.</span> `;
			}
			if(lostSlaves > 0) {
				retval += `During operations <span class="red">${numberWithPlural(lostSlaves, 'slave')}</span> ${this._const.slaveWorkDescription.past}`;
			}
			return retval;
		}

		// virtual override
		get developmentCount() { return super.developmentCount; }
		set developmentCount(value) {
			super.developmentCount = value;
			shared.SellOverflowSlaves(this);
		}
		dissolve() {
			super.dissolve();
			delete V[this._const.corpId + "Slaves"];
		}
		getAutoSendToMarket() {
			return false;
		}
		setAutoSendToMarket(value) {
			throw "Working divisions cannot sell to market";
		}
		endweek_Revenue(divLedger) {
			let {roll, value} = this._const.revenue.roll();
			let revenue = Math.trunc(this.activeSlaves * value);
			divLedger.revenue.apply({value: revenue, efficiency: roll});
		}
		endWeek_Transfer(divLedger) {
			// Working divisions don't do transfers
		}

		get slaveRevenue() {
			return this._const.revenue.center;
		}
		get maintenanceSlaves() {
			// maintenance is paid on working slaves, not worked slaves.
			return this.activeSlaves;
		}

		get foundingCostSlaves() { return this._const.founding.size * this.purchasedSlaveValue; }
		get foundingCost()	   { return this.foundingCostDivision + this.foundingCostSlaves; }
		create() {
			super.create();
			App.Corporate.chargeAsset(this.foundingCostSlaves * 1000, "slaves");
			this.activeSlaves = this._const.founding.size;
			shared.FoundingSetupAutoBuy(this);
		}
	};
};


code:
window.averageRange = class {
	constructor({center, range}) {
		this._const = {
			center,
			range
		};
	}
	get center() { return this._const.center; }
	get range() { return this._const.range; }
	roll() {
		let roll = Math.clamp(gaussianPair(0, 0.2)[0], -0.5, 0.5);
		return {roll, value: (roll * this.range) + this.center};
	}
	rollInt() {
		let retval = this.roll();
		retval.value = Math.trunc(retval.value);
		return retval;
	}
};
window.evenFillArray = function(array, amount, lookupAmount) {
	let perItem, changed;
	let retval = [];
	do {
		let newArray = [];
		changed = false;
		perItem = Math.trunc(amount / array.length);
		for(let item of array) {
			let itemValue = lookupAmount(item);
			if(itemValue >= perItem) {
				newArray.push(item);
				continue;
			}

			amount -= itemValue;
			retval.push({item, value:itemValue});
			changed = true;
		}
		array = newArray;
	} while (changed);
	let remainder = amount % array.length;
	for(let item of array) {
		let extra = 0;
		if(remainder > 0) {
			remainder--;
			extra = 1;
		}
		retval.push({item, value: perItem + extra});
	}
	return retval;
};
window.typeHiddenMembers = class {
	constructor() {
		this._const = {};
		this._var   = {};
		this._cache = {};
	}
};
App.Corporate.Init = function() {
	const Ledger = class {
		constructor(corp, suffix="") {
			this.corp = corp;
			this.suffix = suffix;
			this.clear();
		}
		get operations(	 )	 { return this.getStored('OpCost'			  ); }
		set operations(value)	 { return this.setStored('OpCost', value); }

		get localRevenue(	 )   { return this.getStored('Rev'				 ); }
		set localRevenue(value)   { return this.setStored('Rev', value); }

		get development(	 )	{ return this.getStored('AssetsDev'		   ); }
		set development(value)	{ return this.setStored('AssetsDev', value); }

		get slaves(	 )		 { return this.getStored('AssetsSlave'		 ); }
		set slaves(value)		 { return this.setStored('AssetsSlave', value); }

		get overhead(	 )	   { return this.getStored('Overhead'			); }
		set overhead(value)	   { return this.setStored('Overhead', value); }

		get economicBoost(	 )  { return this.getStored('EconBonus'		   ); }
		set economicBoost(value)  { return this.setStored('EconBonus', value); }

		get economy(	 )		{ return this.getStored('Econ'		   ); }
		set economy(value)		{ return this.setStored('Econ', value); }

		get foreignRevenue(	 ) { return this.getStored('NeighborBonus'	   ); }
		set foreignRevenue(value) { return this.setStored('NeighborBonus', value); }

		copy(ledger) {
			this.operations	 = ledger.operations;
			this.localRevenue   = ledger.localRevenue;
			this.foreignRevenue = ledger.foreignRevenue;
			this.development	= ledger.development;
			this.slaves		 = ledger.slaves;
			this.overhead	   = ledger.overhead;
			this.economicBoost  = ledger.economicBoost;
			this.economy		= ledger.economy;
		}
		clear() {
			this.operations	 = 0;
			this.localRevenue   = 0;
			this.foreignRevenue = 0;
			this.development	= 0;
			this.slaves		 = 0;
			this.overhead	   = 0;
			this.economicBoost  = 0;
			this.economy		= 0;
		}
		release() {
			this.deleteStored('OpCost');
			this.deleteStored('Rev');
			this.deleteStored('AssetsDev');
			this.deleteStored('AssetsSlave');
			this.deleteStored('Overhead');
			this.deleteStored('EconBonus');
			this.deleteStored('Econ');
			this.deleteStored('NeighborBonus');
		}
		get profit() {
			return this.revenue + this.economicBoost
				 - this.development - this.slaves - this.overhead - this.operations;
		}
		get revenue() {
			return this.localRevenue + this.foreignRevenue;
		}
		setEconomy(economy) {
			this.economy = economy;

			// NOTE: Set economicBoost to 0 so it doesn't affect this.profit!
			this.economicBoost = 0; // <-- DO NOT delete
			this.economicBoost = Math.trunc(this.profit * (economy - 100) / 100);
		}
		// private access
		getStored(key) {
			return this.corp.getStored(key + this.suffix);
		}
		setStored(key, value) {
			return this.corp.setStored(key + this.suffix, value);
		}
		deleteStored(key){
			this.corp.deleteStored(key + this.suffix);
		}
	};
	const WeekProcessingEffeciencyLine = class {
		constructor() {
			this.value = 0;
			this.efficiency = 0;
		}
		apply(pair) {
			this.value = pair.value;
			this.efficiency = pair.efficiency;
		}
	};
	const WeekProcessingTransfer = class extends typeHiddenMembers {
		constructor(divLedger) {
			super();
			this._const.divLedger = divLedger;
			this._var.divisions = [];
			this._var.in = 0;
			this._var.out = 0;
		}
		addDivision(division, fill) {
			const otherLedger = this._const.divLedger.weekLedger.getDivision(division);

			this._var.out += fill;
			otherLedger.transfer.in += fill;

			this._var.divisions.push({division, fill});
		}
		get in() {
			return this._var.in;
		}
		set in(value) {
			if(value == this._var.in) { return; }
			if(this._const.divLedger.market.canBuy) {
				this._const.divLedger.market.buy -= value - this._var.in;
			}
			this._var.in = value;
		}
		get total() {
			return this._var.out;
		}
		get divisions() {
			return this._var.divisions;
		}
	};
	const WeekProcessingMarket = class extends typeHiddenMembers {
		constructor(divLedger) {
			super();

			this._const.divLedger = divLedger;
			this._var.canBuy = false;
			this._var.buy = 0;
			this._var.sell = 0;
		}
		get buy() {
			return this._var.buy;
		}
		set buy(value) {
			// Note: canBuy merely means we've set buy to some value, even 0.
			// Setting to 0 after setting to another value happens when we tried to buy some number, but couldn't afford it.
			this._var.canBuy = true;
			this._var.buy = value;
			this._cache.buyValue = null;
		}
		get sell() {
			return this._var.sell;
		}
		set sell(value) {
			this._var.sell = value;
			this._cache.sellValue = null;
		}
		get sellValue() {
			if(this._cache.sellValue == null) { this._cache.sellValue = App.Corporate.slaveMarketSellValue(this.division, this.sell); }
			return this._cache.sellValue;
		}
		get buyValue() {
			if(this._cache.buyValue == null) { this._cache.buyValue = App.Corporate.slaveMarketPurchaseValue(this.division, this.buy); }
			return this._cache.buyValue;
		}
		get canBuy() {
			return this._var.canBuy;
		}

		get divisionLedger() {
			return this._const.divLedger;
		}
		get division() {
			return this.divisionLedger.division;
		}
	};
	const WeekProcessingDivision = class extends typeHiddenMembers {
		constructor(division, weekLedger) {
			super();

			this._const.division = division;
			this._const.ledger = weekLedger;
			this._var.slaves   = new WeekProcessingEffeciencyLine();
			this._var.revenue  = new WeekProcessingEffeciencyLine();
			this._var.transfer = new WeekProcessingTransfer(this);
			this._var.market   = new WeekProcessingMarket(this);
		}
		get slaves() {
			return this._var.slaves;
		}
		get revenue() {
			return this._var.revenue;
		}
		get transfer() {
			return this._var.transfer;
		}
		get market() {
			return this._var.market;
		}
		get division() {
			return this._const.division;
		}
		get weekLedger() {
			return this._const.ledger;
		}
	};
	const WeekProcessingOverheadCategory = class extends typeHiddenMembers {
		constructor(categoryId) {
			super();

			this._const.category = App.Corporate.maintenance.divisionCategories[categoryId];
			if(this._const.category == null) { debugger; throw new "Invalid category id: " + categoryId; }
			this._var.divisions = [];
		}
		addDivision(division) {
			this._var.divisions.push(division);
		}
		get cost() {
			const category = this._const.category;
			const ownedDivisionCount = this._var.divisions.length - category.freeDivisions;
			const developmentCount   = this._var.divisions.reduce((r, div) => r + div.developmentCount, 0) - category.freeDevelopment;

			const divisionCost	= Math.trunc(Math.pow(Math.max(ownedDivisionCount, 0), 2) * category.divisionCost   );
			const developmentCost = Math.trunc(Math.pow(Math.max(developmentCount, 0), 2) * category.developmentCost);

			return divisionCost + developmentCost;
		}
	};
	const WeekProcessingLedger = class extends typeHiddenMembers {
		constructor() {
			super();

			this._var.divisions = {};
			this._var.maintenanceCategories = {};
			this._var.operatingCost = 0;
			this._var.canExpandNow = false;
			this._var.canSpecializeNow = false;
			this._var.dividend = 0;
			this._var.payout = 0;
		}

		getDivision(division) {
			if(!(division.id in this._var.divisions)) {
				this._var.divisions[division.id] = new WeekProcessingDivision(division, this);
			}
			return this._var.divisions[division.id];
		}
		get divisionLedgers() {
			return this._var.divisions;
		}

		registerMaintenanceForDivision(division) {
			let categoryId = division.maintenanceCategory;
			if(!(categoryId in this._var.maintenanceCategories)) {
				this._var.maintenanceCategories[categoryId] = new WeekProcessingOverheadCategory(categoryId);
			}
			let category = this._var.maintenanceCategories[categoryId];
			category.addDivision(division);
		}
		get maintenanceCategories() {
			return this._var.maintenanceCategories;
		}

		get operatingCost() {
			return this._var.operatingCost;
		}
		set operatingCost(value) {
			if(!Number.isFinite(value)) { debugger; throw "Operating cost wasn't finite "; }
			this._var.operatingCost = Math.trunc(value);
		}
		get overhead() {
			const divCount = App.Corporate.numDivisions;
			if(divCount <= 1) { return 0; }

			const divisionOverhead = Object.values(this.maintenanceCategories).reduce((r, categoryLedger) => r + categoryLedger.cost, 0);
			const corpMaintInfo = App.Corporate.maintenance.corporate;
			let corpOverhead = Math.pow(Math.max(divCount - corpMaintInfo.freeDivisions, 0), 2) * corpMaintInfo.divisionCost;
			let retval = (divisionOverhead + corpOverhead) * 1 - (5 - State.variables.baseDifficulty) / 8;
			return Math.trunc(retval);
		}
		get canExpandNow() { return this._var.canExpandNow; }
		set canExpandNow(value) { this._var.canExpandNow = value; }

		get hasDividend() { return this._var.dividend > 0; }
		get dividend() { return this._var.dividend; }
		set dividend(value) { this._var.dividend = value; }

		get hasPayout() { return this._var.payout > 0; }
		get payout() { return this._var.payout; }
		set payout(value) { this._var.payout = value; }

		get canSpecializeNow() { return this._var.canSpecializeNow; }
		set canSpecializeNow(value) { this._var.canSpecializeNow = value; }
	};
	App.Corporate.Division = {};
	const shared = {
		RelatedDivisionType: class {
			constructor() {
				this._var = {
					to:   [],
					from: [],
					all:  []
				};
			}
			get to()   { return this._var.to; }
			get from() { return this._var.from; }
			get all()  { return this._var.all; }
			addTo(value) {
				this._var.to.push(value);
				this._var.all.push(value);
			}
			addFrom(value) {
				this._var.from.push(value);
				this._var.all.push(value);
			}
			get anyFounded() { return this.all.some(div=>div.founded); }
		},
		FoundingType: class {
			constructor(division, {corporateCash, startingSize = 10}) {
				this._const = {
					division,
					cash: corporateCash,
					size: startingSize
				};
			}
			get cash() { return this._const.cash; }
			get size() { return this._const.size; }
			get startingPrice() {
				let div = this._const.division;
				return this._const.cash + div.foundingCost;
			}
		},
		SellOverflowSlaves: function(division) {
			const slavesToSell  = division.activeSlaves - division.developmentCount;
			if(slavesToSell > 0) {
				const slaveProcCost = Math.trunc(App.Corporate.slaveMarketPurchaseValue(division, -slavesToSell));
				App.Corporate.chargeAsset(slaveProcCost, "slaves");
				division.activeSlaves -= slavesToSell;
				V.menialDemandFactor -= slavesToSell;
			}
		},
		SellUnhousedSlaves: function(division, divLedger, rate) {
			if(divLedger.market.sell != 0) { return; }

			let housing = 2 * rate * division.developmentCount;
			let unhoused = division.heldSlaves - housing;
			if(unhoused <= 0) { return; }

			divLedger.market.sell = unhoused;
		},
		MessageProcessedSlaves: function(division, verbPhrase, color) {
			let procCount = Math.trunc(division.developmentCount * division.processRate);
			let slaveCountedNoun = numberWithPluralNonZero(procCount, "slave");

			return `It ${verbPhrase} approximately <span class="${color}">${slaveCountedNoun}</span> each week when operating at capacity (${division.developmentCount})`;
		},
		MessageSlaveToMarket: function(division) {
			return `The slaves from this division can be sold for <span class='yellowgreen'>${cashFormat(Math.trunc(division.soldSlaveValue * menialSlaveCost()))}</span> each.`;
		},
		EndWeekProcessing_Slaves: function(processingCount, rate) {
			const perDevPair = rate.roll();
			let slaveIncrease = perDevPair.value * processingCount;
			if(slaveIncrease < 1) {
				slaveIncrease = (slaveIncrease > Math.random() ? 1 : 0);
			}
			return {efficiency: perDevPair.roll, value: Math.trunc(slaveIncrease)};
		},
		FoundingSetupAutoBuy: function(division) {
			let foundedFrom = division.relatedDivisions.from.filter(div=>div.founded);
			if(foundedFrom.length == 0) {
				division.setAutoBuyFromMarket(true);
			} else {
				for(let otherDiv of foundedFrom) {
					if(otherDiv.getAutoSendToMarket()) {
						otherDiv.setAutoSendToDivision(division, true);
					}
				}
			}
		},
		FoundingSetupAutoSell: function(division) {
			let foundedTo = division.relatedDivisions.to.filter(div=>div.founded);
			if(foundedTo.length == 0) {
				division.setAutoSendToMarket(true);
			} else {
				for(let otherDiv of foundedTo) {
					if(otherDiv.getAutoBuyFromMarket()) {
						division.setAutoSendToDivision(otherDiv, true);
					}
				}
			}
		}
	};
	App.Corporate.Init_DivisionBase(shared);
	App.Corporate.Init_DivisionAcquiring(shared);
	App.Corporate.Init_DivisionProcessing(shared);
	App.Corporate.Init_DivisionWorking(shared);

	App.Corporate.InitConstants();
	let divisions = App.Corporate.divisions = mapIdList(App.Corporate.divisionList);
	App.Corporate.maintenance.divisionCategories = mapIdList(App.Corporate.maintenance.divisionCategoriesList);

	for(const divInfo of App.Corporate.divisionList.filter(div=>div.nextDivisions != null)) {
		const div = divisions[divInfo.id];
		for(const nextDepId of divInfo.nextDivisions) {
			let nextDiv = divisions[nextDepId];
			div.relatedDivisions.addTo(nextDiv);
			nextDiv.relatedDivisions.addFrom(div);
		}
	}
	let asDivision = function(division) {
		if(_.isObject(division)) { return division; }
		return App.Corporate.divisions[division];
	};
	App.Corporate.getStored	= function(key	   ) { return V[`corp${key}`];  };
	App.Corporate.setStored	= function(key, value) { V[`corp${key}`] = value; };
	App.Corporate.deleteStored = function(key	   ) { delete V[`corp${key}`];  };

	// Integer properties starting with corp
	const propertyToStoryInt = {
		cash: "Cash",
		numDivisions: 'Div',
		foundedDate: 'Founded',
		dividend: "Dividend",
		specializations: "Spec",
		specializationTokens: "SpecToken",
		specializationTimer: "SpecTimer",
	};
	for(const property in propertyToStoryInt) {
		const key = propertyToStoryInt[property];
		Object.defineProperty(App.Corporate, property, {
		get: function(	 ) { return this.getStored(key); },
		set: function(value) {
			if(!Number.isFinite(value)) { throw "Unreal number " + key; }
			this.setStored(key, Math.trunc(value));
		}
		});
	}

	// Boolean properties starting with corp (true == 1, false == 0)
	const propertyToStoryBool = {
		founded: "Incorporated",
		hasMarket: "Market",
		payoutCash: "CashDividend",
		canExpand: 'ExpandToken',
	};
	for(const property in propertyToStoryBool) {
		const key = propertyToStoryBool[property];
		Object.defineProperty(App.Corporate, property, {
			get: function(	 ) { return this.getStored(key) === 1; },
			set: function(value) { this.setStored(key, value == true ? 1 : 0); }
		});
	}

	// Abnormal properties
	Object.defineProperty(App.Corporate, "value", {
		get: function() {
			if(!this.founded) { return 0; }
			let corpAssets = App.Corporate.divisionList
										  .filter(div=>div.founded)
										  .reduce((v, div)=>v + div.value, 0);
			return corpAssets + this.dividend + this.cash;
		}
	});
	Object.defineProperty(App.Corporate, "dividendRatio", {
		get: function(	 ) { return V.dividendRatio;		},
		set: function(value) {		V.dividendRatio = value; }
	});
	Object.defineProperty(App.Corporate, "dividendTimer", {
		get: function(	 ) { return V.dividendTimer;		},
		set: function(value) {		V.dividendTimer = value; }
	});
	Object.defineProperty(App.Corporate, "payoutAfterCash", {
		get: function() {
			return Math.max(Math.trunc(this.payoutCorpValueMultiplier * this.value), this.payoutMinimumCash);
		}
	});

	const SharesType = class {
		get personal()	  { return V.personalShares;		}
		set personal(value) {		V.personalShares = value; }
		get public()		{ return V.publicShares;		}
		set public(value)   {		V.publicShares   = value; }
		get total()		 { return this.personal + this.public; }
	};
	App.Corporate.shares = new SharesType();
	App.Corporate.ledger = {
		current: new Ledger(App.Corporate),
		old:	 new Ledger(App.Corporate, "Old"),
		swap: function() {
			this.old.copy(this.current);
			this.current.clear();
		},
		clear: function() {
			this.old.clear();
			this.current.clear();
		},
		release: function() {
			this.old.release();
			this.current.release();
		}
	};
	App.Corporate.foundingCostToPlayer = function(division, personalShares, publicShares) {
		division = asDivision(division);
		let costToPlayer = Math.trunc((division.foundingCash / (personalShares + publicShares)) * personalShares);
		return costToPlayer;
	};
	App.Corporate.create = function(division, personalShares, publicShares) {
		this.shares.personal = personalShares;
		this.shares.public   = publicShares;
		V.dividendTimer = 13;
		this.founded = true;
		this.foundedDate = V.week;
		this.dividend = 0;
		this.dividendRatio = 0;
		this.specializationTimer = 4;

		this.ledger.clear();

		// this will be updated by division.create
		this.numDivisions = 0;
		this.expansionTokens = 1;

		division = asDivision(division);
		cashX(forceNeg(App.Corporate.foundingCostToPlayer(division, personalShares, publicShares)), 'stocksTraded');
		this.cash = division.foundingCash;

		division.create(this);
		App.Corporate.ledger.swap();
	};
	App.Corporate.dissolve = function() {
		for(let division of this.divisionList.filter(x=>x.founded)) {
			division.dissolve();
		}
		this.founded = false;
		this.numDivisions = 0;
		this.expansionTokens = 0;
		this.setStored("Expand", 0);
		this.specializations = 0;
		this.specializationTokens = 0;
		this.setStored("SpecRaces", []);
		this.ledger.release();

		// Some of these will need to be refactored into App.Corporate.Specialization
		const toDelete = [
			"corpCash",
			"personalShares",
			"publicShares",
			"corpDividend",
			"dividendTimer",
			"corpSpecAccent",
			"corpSpecAge",
			"corpSpecAmputee",
			"corpSpecBalls",
			"corpSpecDevotion",
			"corpSpecDick",
			"corpSpecEducation",
			"corpSpecGender",
			"corpSpecGenitalia",
			"corpSpecWeight",
			"corpSpecHeight",
			"corpSpecHormones",
			"corpSpecImplants",
			"corpSpecInjection",
			"corpSpecIntelligence",
			"corpSpecMilk",
			"corpSpecMuscle",
			"corpSpecPussy",
			"corpSpecSexEd",
			"corpSpecTrust",
			"corpSpecVirgin"
		];
		toDelete.forEach(id => delete V[id]);

		if(this.hasMarket) {
			let corporateMarketIndex = V.sectors.findIndex(sector => sector.type === "CorporateMarket");
			V.sectors[corporateMarketIndex].type = "Markets";
			this.hasMarket = false;
		}
	};
	App.Corporate.expandedDivision = function() {
		this.numDivisions += 1;
		this.canExpand = false;
	};
	App.Corporate.dissolvedDivision = function() {
		this.numDivisions -= 1;
	};
	App.Corporate.chargeAsset = function(cost, type) {
		if(!Number.isFinite(cost)) { throw "The cost provided was not a real number"; }
		cost = Math.trunc(cost);
		if(!(type in this.ledger.current)) { throw `Ledger doesn't record '${type}' category.`; }
		if(cost == 0) { return; }

		this.ledger.current[type] += cost;
		this.cash -= cost;
	};
	App.Corporate.earnRevenue = function(cost, locality) {
		if(!Number.isFinite(cost)) { throw "The cost provided was not real"; }
		cost = Math.trunc(cost);
		let current = this.ledger.current;
		let key = `${locality}Revenue`;
		if(!(key in current)) { throw `Unknown locality '${locality}'`; }
		current[key] += cost;
		this.cash += cost;
	};
	App.Corporate.chargeDividend = function(cost, weekLedger) {
		if(!Number.isFinite(cost)) { throw "The cost provided was not real"; }
		cost = Math.trunc(cost);
		if(weekLedger == null) {
			debugger;
			throw "No weekLedger provided";
		}
		this.dividend += cost;
		this.cash -= cost;
		weekLedger.dividend += cost;
	};
	App.Corporate.creditEconomy = function() {
		this.ledger.current.setEconomy(V.localEcon);
		this.cash += this.ledger.current.economicBoost;
	};
	/* Need to prevent skipping intermediaries if they exist, ie break->surgery->train, you can skip surgery only if you don't have it.*/
	App.Corporate.ownsIntermediaryDivision = function(fromDivision, toDivision) {
		for(let intermediateDiv of toDivision.relatedDivisions
											 .from
											 .filter(iDep => iDep.id != fromDivision.id
														  && fromDivision.relatedDivisions.to.includes(iDep))) {
			if(intermediateDiv.founded) { return true; }
		}
		return false;
	};
	App.Corporate.slaveMarketPurchaseValue = function(division, count) {
		division = asDivision(division);
		let slaveValue = division.purchasedSlaveValue;
		let totalValue = slaveValue * count * menialSlaveCost(count);
		return Math.trunc(totalValue);
	};
	App.Corporate.slaveMarketSellValue = function(division, count) {
		division = asDivision(division);
		let slaveValue = division.soldSlaveValue;
		let totalValue = slaveValue * count * menialSlaveCost(count);
		return Math.trunc(totalValue);
	};
	App.Corporate.buySlaves = function(division, count) {
		if(count <= 0) { return 0; }

		division = asDivision(division);
		let purchasePrice = this.slaveMarketPurchaseValue(division, count);
		if(this.cash < purchasePrice) {
			throw "Attempted purchase without enough money";
		}

		this.chargeAsset(purchasePrice, "slaves");
		division.activeSlaves += count;
		V.menialSupplyFactor  -= count;
		return purchasePrice;
	};
	App.Corporate.sellSlaves = function(division, count) {
		if(count <= 0) { return 0; }

		division = asDivision(division);
		if(division.heldSlaves < count) { throw "Attempted to sell more slaves than held."; }

		let sellPrice = this.slaveMarketSellValue(division, count);
		this.earnRevenue(sellPrice, "local");
		division.heldSlaves -= count;
		V.menialDemandFactor -= count;
		return sellPrice;
	};
	App.Corporate.transferSlaves = function(fromDivision, toDivision, count) {
		fromDivision = asDivision(fromDivision);
		toDivision = asDivision(toDivision);
		// TODO: validate the from and to departments are directly connected.

		if(fromDivision.heldSlaves < count) { throw `Tried to move ${count} slaves out of ${fromDivision.name}, but it only had ${fromDivision.heldSlaves}`; }

		fromDivision.heldSlaves -= count;
		toDivision.activeSlaves += count;
	};

	App.Corporate.buyDevelopment = function(division, count) {
		division = asDivision(division);

		let cost = Math.trunc(division.sizeCost * count * 1000);

		this.chargeAsset(cost, "development");
		division.developmentCount += count;
	};
	App.Corporate.sellDevelopment = function(division, count) {
		division = asDivision(division);

		const devCount = division.developmentCount;
		count = count || devCount;
		if(count > devCount) { throw `Attempted to sell more of a division ${division.id} than exists (${count} of ${devCount})`; }
		const developmentCost = Math.trunc(count * division.sizeCost * 800);
		this.chargeAsset(-developmentCost, "development");
		division.developmentCount -= count;
	};
	App.Corporate.setAutoSendToDivision = function(fromDivision, toDivision, value) {
		fromDivision = asDivision(fromDivision);
		toDivision = asDivision(toDivision);

		fromDivision.setAutoSendToDivision(toDivision, value);
	};
	App.Corporate.setAutoSendToMarket = function(division, value) {
		division = asDivision(division);
		division.setAutoSendToMarket(value);
	};
	App.Corporate.setAutoBuyFromMarket = function(division, value) {
		division = asDivision(division);
		division.setAutoBuyFromMarket(value);
	};
	App.Corporate.calculateDividend = function(weekLedger) {
		let profit = this.ledger.current.profit;
		if(this.dividendRatio > 0 && profit > 0) {
			this.chargeDividend(profit * this.dividendRatio, weekLedger);
		}
		// Payout leftover cash should be the last thing the corporation does
		// in a week so that its cash will be at the payout amount.
		if(this.payoutCash) {
			let payoutAfter = this.payoutAfterCash;
			if(this.cash > payoutAfter) {
				this.chargeDividend(this.cash - payoutAfter, weekLedger);
			}
		}

		if(this.dividendTimer <= 1) {
			weekLedger.payout = Math.trunc(this.dividend * this.shares.personal / this.shares.total);
			cashX(weekLedger.payout, "stocks");
			this.dividendTimer = 14;// 13 for each quarter, but +1 because we're subtracting one below.
			this.dividend = 0;
		}

		this.dividendTimer--;
	};
	App.Corporate.endWeek = function() {
		let ledger = new WeekProcessingLedger();
		// Prepare requests
		for(let div of this.divisionList.filter(div=>div.founded)) {
			let divLedger = ledger.getDivision(div);

			ledger.operatingCost += div.maintenanceCost;
			ledger.registerMaintenanceForDivision(div);

			div.endweek_Revenue(divLedger);
			div.endWeek_Slaves(divLedger);
		}
		for(let divLedger of Object.values(ledger.divisionLedgers)) {
			let div = divLedger.division;
			div.endWeek_Transfer(divLedger);
			div.endWeek_Market(divLedger);
		}
		this.chargeAsset(ledger.operatingCost, "operations");
		this.chargeAsset(ledger.overhead, "overhead");
		// Execute sales requests, transfers, and earned revenue
		for(let divLedger of Object.values(ledger.divisionLedgers)) {
			let div = divLedger.division;
			this.earnRevenue(divLedger.revenue.value, "local");
			if(div.activeSlaves > 0) {
				shared.SellOverflowSlaves(div);
			}

			for(let otherDivPair of divLedger.transfer.divisions) {
				let otherDiv = otherDivPair.division;
				let count = otherDivPair.fill;
				if(count == 0) { continue; }

				this.transferSlaves(div, otherDiv, count);
			}
			if(divLedger.market.sell > 0) {
				divLedger.market.finalSale = this.sellSlaves(div, divLedger.market.sell);
			}
		}

		// Execute purchase requests
		// todo: Make a switch to allow the user to control purchasing behavior.
		// todo: Expensive first
		// todo: Cheapest first
		// Even purchase requsts:
		let purchaseValues = evenFillArray(Object.values(ledger.divisionLedgers)
												 .filter(divLedger=>divLedger.market.buy > 0)
										  , this.cash
										  , divLedger=>divLedger.market.buyValue);
		for(let divLedgerPair of purchaseValues) {
			let divLedger = divLedgerPair.item;
			let purchaseCost = divLedgerPair.value;
			let div = divLedger.division;

			if(divLedger.market.buyValue > purchaseCost) {
				// Estimate how many slaves we can afford within the purchase cost
				let perSlaveEstimate = Math.trunc(divLedger.market.buyValue / divLedger.market.buy);
				let numSlavesEstimate = Math.trunc(purchaseCost / perSlaveEstimate);

				if(numSlavesEstimate < 1) {
					divLedger.market.originalBuy = divLedger.market.buy;
					divLedger.market.buyShortMoney = divLedger.market.buyValue;
					divLedger.market.buyShortSlaves = divLedger.market.buy;
					divLedger.market.buy = 0;
				} else {
					divLedger.market.originalBuy = divLedger.market.buy;
					divLedger.market.buyShortMoney = divLedger.market.buyValue - purchaseCost;
					divLedger.market.buyShortSlaves = divLedger.market.buy - numSlavesEstimate;
					divLedger.market.buy = numSlavesEstimate;
				}
			}
			divLedger.market.finalPurchase = this.buySlaves(div, divLedger.market.buy);
		}
		this.creditEconomy();

		if(this.numDivisions < this.divisionList.length && !this.canExpand) {
			let expansionValue = Math.trunc(Math.pow(this.numDivisions, 1.5) + (5 * this.numDivisions + 2) / 4);
			if(this.value > expansionValue * 1000000) {
				ledger.canExpandNow = true;
				this.canExpand = true;
			}
		}
		let specializationExpansion = 1.6 * Math.pow(1.25, this.specializations) - 1.2;
		if(this.value > specializationExpansion * 1000000){
			this.specializationTokens++;
			this.specializations++;
			ledger.canSpecializeNow = true;
		}
		if(this.specializationTimer > 0) {
			this.specializationTimer--;
		}
		this.calculateDividend(ledger);

		return ledger;
	};
	App.Corporate.cheatCash = function(userCash) {
		userCash = Math.trunc(Number(userCash));
		if(Number.isFinite(userCash)) {
			this.cash = userCash;
			V.cheater = 1;
		}
	};

	App.Corporate.Backcompat = function(){
		// current foreignRevenue used to be used for old foreignRevenue
		let c = this.ledger.current;
		App.Corporate.ledger.old.foreignRevenue = c.foreignRevenue;
		if(c.operations === undefined) {
			c.operations = 0;
			c.overhead = 0;
			c.economicBoost = 0;
		}
	};

	if(V.corpDivTrainSurgeryTimer !== undefined) {
		let timer = V.corpDivTrainSurgeryTimer;
		// Note: originally the timer was capped at 20, so the founding week isn't guaranteed to be correct.
		V.corpDivSurgeryFounded = V.week - timer;
		delete V.corpDivTrainSurgeryTimer;
	}
};

// Corporation Share Price
// A positive q means adding shares to the market, negative means removing them

window.corpSharePrice = function(q = 0, personalShares = null, publicShares = null) {
	if (V.corpIncorporated === 0) {
		return 0;
	}
	personalShares = personalShares || V.personalShares;
	publicShares   = publicShares   || V.publicShares;
	return Math.trunc(1000 * (App.Corporate.value / (personalShares + publicShares + q)));
};

// Corporation race blacklisting/whitelisting
// race is the lowercase string representing the race, 'blacklist' is either 1 or 0. 1 means we are blacklisting and 0 means we are whitelisting said race
window.corpBlacklistRace = function(race, blacklist) {
	let raceArray = State.variables.corpSpecRaces;
	if (raceArray.length > 0 && blacklist === 1) {
		raceArray.delete(race);
	} else if (blacklist === 1) {
		raceArray = setup.filterRacesLowercase.filter(x => x !== race);
	} else {
		raceArray.push(race);
	}
	return raceArray;
};


code:
:: Corporate Market [nobr]

<<set $nextButton = "Back", $nextLink = "Main">>
/* Multi-Purchase Support */
<<if ndef $newSlaves>><<set $newSlaves = []>><</if>>
<<if $newSlaves.length > 0>>
	<<set $nextButton = "Continue", $nextLink = "Bulk Slave Intro", $slaveMarket = "corporate", $returnTo = "Main", $newSlaveIndex = 0>>
<</if>>

<<if $corpSpecGender == 2>>
	<<setNonlocalPronouns 100>>
<<elseif $corpSpecGender == 1>>
	<<setNonlocalPronouns 0>>
<<else>>
	<<setNonlocalPronouns $seeDicks>>
<</if>>

You're in your corporation's flagship slave market.

The corporation pays you only nominal rent for this space, but will offer you a discount on slaves here based on your shares in it. Additionally, having the corporation's premiere products sold here in $arcologies[0].name will associate them with you, allowing your reputation to improve the corporate brand. A pretty
<<if $corpSpecDick == 1 && $corpSpecPussy == 1>>futanari<<elseif $corpSpecGender == 2>><<if $corpSpecBalls == -1>>clipped<<else>>shemale<</if>><</if>>
sales<<if $corpSpecAge == 3>>_womanU<<else>>_girlU<</if>> in a slutty suit greets you properly. _HeU's a corporate slave chosen for loyalty and intelligence, and to physically represent the brand.
<<if $corpSpecHeight == 5>>
	Even before _heU engaged with you _hisU towering height made _himU stand out and caught your eye, from up close _heU's even more impressive.
<<elseif $corpSpecHeight == 1>>
	_HeU's so short you have to physically look down on _himU.
<</if>>
<<if $corpSpecInjection == 4>>
	_HeU has awe-inspiring breasts which _hisU suit jacket helps support; even so, their bottoms reach past _hisU navel.
<<elseif $corpSpecInjection == 5>>
	_HeU has gigantic, milk-swollen breasts almost spilling out of _hisU suit.
<<elseif $corpSpecImplants == 2>>
	_HeU has enormous fake tits that ride inhumanly high on _hisU chest.
<<elseif $corpSpecInjection == 3>>
	_HeU has huge tits and a nice broad butt which _hisU tight skirt hugs closely.
<<elseif $corpSpecImplants == 1>>
	_HeU has nice fake bimbo tits and a silicone bubble butt.
<<elseif $corpSpecInjection == 2>>
	_HeU's nice and curvy, with nice breasts and a lovely ass.
<<elseif $corpSpecInjection == 1>>
	_HeU has cute little breasts and a petite rump.
<</if>>
<<if $corpSpecCosmetics == 1>>_HisU face is flawless.<</if>>
<<if ($corpSpecRaces.length == 1)>>
	<<set _corpSpecRace = $corpSpecRaces.random()>>
	_HeU's <<print _corpSpecRace>>, of course<<if $corpSpecAccent == 1>>, and has a cute accent<</if>>.
<</if>>
<<if $corpSpecHormones == 1>>
	_HisU voice is soft and feminine, and _heU bats _hisU eyes at you.
<<elseif $corpSpecHormones == 2>>
	_HeU has a deep voice, and speaks assertively.
<</if>>
<<if $corpSpecMuscle == 4>>
	_HisU hot body is perfectly toned under _hisU suit.
<<elseif $corpSpecMuscle == 5>>
	_HisU suit is specially tailored to flatter _hisU visible musculature.
<</if>>
Of course, you know all about the corporation, but sales<<if $corpSpecAge == 3>>_womenU<<else>><<= _girlU>>s<</if>> here have standing orders to treat you like any other customer, so you can review the market properly. The slave turns to <<if $corpSpecAmputee != 1>>a multimedia presentation on<<else>>a rack of<</if>> your corporation's slaves.

_HeU explains that the corporation captures many people, so it only retains and trains those that fit its product lines.

<<= generateMarketSlave("corporate")>>

_HisU presentation done, the
<<if $corpSpecDick == 1 && $corpSpecPussy == 1>>futanari<<elseif $corpSpecGender == 2>><<if $corpSpecBalls == -1>>clipped<<else>>shemale<</if>><</if>>
sales<<if $corpSpecAge == 3>>_womanU<<else>>_girlU<</if>> directs you to the inventory interface that will allow you to peruse the corporation's slaves, and moves over to stand next to it, ready to answer any questions.
<<if $PC.dick != 0>>
	_HeU positions _himselfU facing away from you and hikes up _hisU skirt to bare _hisU
	<<if $corpSpecInjection > 4>>gigantic ass<<elseif $corpSpecImplants > 0>>fake ass<<elseif $corpSpecInjection == 3>>big butt<<elseif $corpSpecInjection == 2>>cute butt<<else>>little behind<</if>>
	and cocks _hisU hips<<if $corpSpecBalls == -1>>, keeping _hisU soft cock demurely out of sight<</if>>, if you feel like <<if $corpSpecGender == 2 && $corpSpecPussy != 1>>sodomizing<<else>>fucking<</if>> _himU
<<else>>
	_HeU gets down on _hisU knees right there, making _hisU mouth available if you feel like riding _hisU face
<</if>>
while you browse.

<<link "Use _himU">>
	<<replace "#result">>
		<<if $PC.dick != 0>>
			_HisU <<if $corpSpecGender == 2 && $corpSpecPussy != 1>>asshole is thoughtfully pre-lubed, and your cock slides easily up it<<else>>pussy is nice and wet, and your cock slides inside _himU easily<</if>> as you consider the slaves on offer. _HeU's well trained, and uses _hisU <<if $corpSpecGender == 2 && $corpSpecPussy != 1>>sphincter<<else>>womanly muscles<</if>> to please your member expertly as _heU ruts _himselfU gently back against you, doing almost all the work. When you orgasm, _heU retains the load carefully, keeping your cum inside _himU and away from _hisU nice clothes.
		<<else>>
			_HeU's very well trained, and does all the work. Once you move in and straddle _himU, _heU gets right down to it, or in this case, up to it. _HisU clever tongue runs gently along your labia to build anticipation before starting a delightful dance around and then atop your clitoris. You orgasm in no time at all, a climax which _heU carefully prolongs by kissing you full on the pussy in just the right way, only breaking the oral embrace when you've ridden it as far as you'll go.
		<</if>>
	<</replace>>
<</link>>

<<set _slaveCost = slaveCost($activeSlave)>>
<<if $slavesSeen > $slaveMarketLimit>><<set _slaveCost += _slaveCost*(($slavesSeen-$slaveMarketLimit)*0.1)>><</if>>

<<if $corpMarket == 1>>
	Your own local franchise of your corporation allows you to enjoy a discount.
	<<set _slaveCost *= 0.9>>
<</if>>

<<set _slaveCost = 500*Math.trunc(_slaveCost/500)>>
The offered price is <<print cashFormat(_slaveCost)>>.<<if $slavesSeen > $slaveMarketLimit>> You have cast such a wide net for slaves this week that it is becoming more expensive to find more for sale. Your reputation helps determine your reach within the slave market.<</if>>

<<setLocalPronouns $activeSlave>>
<<buyingFromMarketControls>>

<<link "End corporate slave sales here and return this sector to standard markets">>
	<<set $corpMarket = 0>>
	<<for _i = 0; _i < $sectors.length; _i++>>
		<<if $sectors[_i].type == "CorporateMarket">><<set $sectors[_i].type = "Markets">><<break>><</if>>
	<</for>>
	<<goto "Main">>
<</link>>