code:
/* ### Non Random Events ### */

new App.DomPassage("conflictReport",
	() => {
		V.nextButton = "Continue";
		V.nextLink = "Scheduled Event";
		App.UI.StoryCaption.encyclopedia = "Battles";
		return App.Events.conflictReport();
	}, ["end-week, temporary-images"]
);
new App.DomPassage("conflictHandler",
	() => {
		return App.Events.conflictHandler();
	}, ["end-week", "temporary-images"]
);

/* ### Random Events ### */

new App.DomPassage("Random Individual Event",
	() => {
		V.nextButton = "Continue";

		if (V.RIERemaining <= 0) {
			// first event for this week: reset counter
			V.RIERemaining = Math.max(1, Math.min(V.RIEPerWeek, Math.floor(getRieEligibleSlaves().length / 2)));
		}
		if (V.RIERemaining > 1) {
			// return to self if we have more events to play
			V.nextLink = "Random Individual Event";
		} else {
			// last event for this week: out to Next Week
			V.nextLink = "Next Week";
		}
		V.RIERemaining--;

		return App.Events.playRandomIndividualEvent();
	}, ["end-week", "temporary-images"]
);

new App.DomPassage("Random Nonindividual Event",
	() => {
		V.nextButton = "Continue";
		V.nextLink = "Random Individual Event";

		return App.Events.playRandomNonindividualEvent();
	}, ["end-week", "temporary-images"]
);

/* ### Scheduled Events ### */

new App.DomPassage("Scheduled Event",
	() => {
		V.nextButton = "Continue";
		V.nextLink = "Scheduled Event"; // return to self; playNonrandomEvent will forward automatically when necessary

		return App.Events.playNonrandomEvent();
	}, ["end-week", "temporary-images"]
);

/* ### Player Events ### */

new App.DomPassage("Gameover",
	() => {
		return App.Events.Gameover();
	}, ["end-week", "temporary-images"]
);


code:
/* ### Standard Facilities ### */
new App.DomPassage("Arcade", () => { return new App.Facilities.Arcade.arcade().render(); }, ["jump-to-safe", "jump-from-safe"]);

new App.DomPassage("Brothel", () => { return new App.Facilities.Brothel.brothel().render(); }, ["jump-to-safe", "jump-from-safe"]);

new App.DomPassage("Cellblock", () => { return new App.Facilities.Cellblock.cellblock().render(); }, ["jump-to-safe", "jump-from-safe"]);

new App.DomPassage("Clinic", () => { return new App.Facilities.Clinic.clinic().render(); }, ["jump-to-safe", "jump-from-safe"]);

new App.DomPassage("Club", () => { return new App.Facilities.Club.club().render(); }, ["jump-to-safe", "jump-from-safe"]);

new App.DomPassage("Dairy", () => { return new App.Facilities.Dairy.dairy().render(); }, ["jump-to-safe", "jump-from-safe"]);

new App.DomPassage("Farmyard", () => { return new App.Facilities.Farmyard.farmyard().render(); }, ["jump-to-safe", "jump-from-safe"]);

new App.DomPassage("Head Girl Suite", () => { return new App.Facilities.HGSuite.headGirlSuite().render(); }, ["jump-to-safe", "jump-from-safe"]);

new App.DomPassage("Incubator", () => { return App.UI.incubator(); }, ["jump-to-safe", "jump-from-safe"]);

new App.DomPassage("Master Suite", () => { return new App.Facilities.MasterSuite.masterSuite().render(); }, ["jump-to-safe", "jump-from-safe"]);

new App.DomPassage("Nursery", () => { return new App.Facilities.Nursery.nursery().render(); }, ["jump-to-safe", "jump-from-safe"]);

new App.DomPassage("Pit", () => { return App.Facilities.Pit.pit(); }, ["jump-to-safe", "jump-from-safe"]);

new App.DomPassage("Schoolroom", () => { return new App.Facilities.Schoolroom.schoolroom().render(); }, ["jump-to-safe", "jump-from-safe"]);

new App.DomPassage("Servants' Quarters", () => { return new App.Facilities.ServantsQuarters.servantsQuarters().render(); }, ["jump-to-safe", "jump-from-safe"]);

new App.DomPassage("Spa", () => { return new App.Facilities.Spa.spa().render(); }, ["jump-to-safe", "jump-from-safe"]);

new App.DomPassage("Transport Hub", () => { return App.Mods.SecExp.transportHub.GUI(); });
new App.DomPassage("Weapons Manufacturing", () => { return App.Mods.SecExp.weapManu.GUI(); }, ["jump-to-safe", "jump-from-safe"]);
new App.DomPassage("securityHQ", () => { return App.Mods.SecExp.secHub.GUI(); }, ["jump-to-safe", "jump-from-safe"]);
new App.DomPassage("secBarracks", () => { return App.Mods.SecExp.barracks.GUI(); }, ["jump-to-safe", "jump-from-safe"]);
new App.DomPassage("riotControlCenter", () => { return App.Mods.SecExp.riotCenter.GUI(); }, ["jump-to-safe", "jump-from-safe"]);
new App.DomPassage("propagandaHub", () => { return App.Mods.SecExp.propHub.GUI(); }, ["jump-to-safe", "jump-from-safe"]);

/* ### Slave Interact Facilities ### */
new App.DomPassage("Wardrobe",
	() => {
		V.nextButton = "Back";
		V.nextLink = "Manage Penthouse";

		return App.UI.WardrobeShopping();
	}, ["jump-to-safe", "jump-from-safe"]
);

new App.DomPassage("Dressing Room",
	() => {
		V.nextButton = "Back";
		V.nextLink = "Manage Penthouse";

		return App.UI.DressingRoom.render();
	}, ["jump-to-safe", "jump-from-safe"]
);

new App.DomPassage("Salon",
	() => {
		V.nextButton = "Confirm changes";
		V.nextLink = "Slave Interact";
		App.UI.StoryCaption.encyclopedia = "The Auto Salon";

		return App.UI.salon(getSlave(V.AS));
	}, ["jump-from-safe"]
);

new App.DomPassage("Body Modification",
	() => {
		V.nextButton = "Confirm changes";
		V.nextLink = "Slave Interact";
		App.UI.StoryCaption.encyclopedia = "The Studio";

		return App.UI.bodyModification(getSlave(V.AS));
	}, ["jump-from-safe"]
);

/* ### Special Facilities ### */
new App.DomPassage("Agent Select",
	() => {
		V.nextButton = "Back";
		V.nextLink = "Neighbor Interact";
		App.UI.StoryCaption.encyclopedia = "Agents";

		return App.Facilities.AgentSelect();
	}, ["jump-from-safe"]
);

new App.DomPassage("Attendant Select",
	() => {
		V.nextButton = "Back";
		V.nextLink = "Spa";
		App.UI.StoryCaption.encyclopedia = "Attendant";

		return App.Facilities.AttendantSelect();
	}, ["jump-to-safe", "jump-hidden", "jump-from-safe"]
);

new App.DomPassage("BG Select",
	() => {
		V.nextButton = "Back to Main";
		V.nextLink = "Main";
		App.UI.StoryCaption.encyclopedia = "Bodyguard";

		return App.Facilities.BGSelect();
	}, ["jump-to-safe", "jump-from-safe"]
);

new App.DomPassage("Concubine Select",
	() => {
		V.nextButton = "Back";
		V.nextLink = "Master Suite";
		App.UI.StoryCaption.encyclopedia = "Concubine";

		return App.Facilities.ConcubineSelect();
	}, ["jump-to-safe", "jump-hidden", "jump-from-safe"]
);

new App.DomPassage("DJ Select",
	() => {
		return App.Facilities.DJSelect();
	}, ["jump-to-safe", "jump-hidden", "jump-from-safe"]
);

new App.DomPassage("Head Girl Select",
	() => {
		V.nextButton = "Back to Main";
		V.nextLink = "Main";
		App.UI.StoryCaption.encyclopedia = "Head Girl";

		return App.Facilities.HGSelect();
	}, ["jump-to-safe", "jump-hidden", "jump-from-safe"]
);

new App.DomPassage("Madam Select",
	() => {
		V.nextButton = "Back";
		V.nextLink = "Brothel";
		App.UI.StoryCaption.encyclopedia = "Madam";

		return App.Facilities.MadamSelect();
	}, ["jump-to-safe", "jump-hidden", "jump-from-safe"]
);

new App.DomPassage("Milkmaid Select",
	() => {
		V.nextButton = "Back";
		V.nextLink = "Dairy";
		App.UI.StoryCaption.encyclopedia = "Milkmaid";

		return App.Facilities.MilkmaidSelect();
	}, ["jump-to-safe", "jump-hidden", "jump-from-safe"]
);

new App.DomPassage("Nurse Select",
	() => {
		V.nextButton = "Back";
		V.nextLink = "Clinic";
		App.UI.StoryCaption.encyclopedia = "Nurse";

		return App.Facilities.NurseSelect();
	}, ["jump-to-safe", "jump-hidden", "jump-from-safe"]
);

new App.DomPassage("Recruiter Select",
	() => {
		V.nextButton = "Back to Main";
		V.nextLink = "Main";
		App.UI.StoryCaption.encyclopedia = "Recruiter";

		return App.Facilities.RecruiterSelect();
	}, ["jump-to-safe", "jump-hidden", "jump-from-safe"]
);

new App.DomPassage("Schoolteacher Select",
	() => {
		V.nextButton = "Back";
		V.nextLink = "Schoolroom";
		App.UI.StoryCaption.encyclopedia = "Schoolteacher";

		return App.Facilities.SchoolTeacherSelect();
	}, ["jump-to-safe", "jump-hidden", "jump-from-safe"]
);

new App.DomPassage("Stewardess Select",
	() => {
		V.nextButton = "Back";
		V.nextLink = "Servants' Quarters";
		App.UI.StoryCaption.encyclopedia = "Stewardess";

		return App.Facilities.StewardessSelect();
	}, ["jump-to-safe", "jump-hidden", "jump-from-safe"]
);

new App.DomPassage("Wardeness Select",
	() => {
		V.nextButton = "Back";
		V.nextLink = "Cellblock";
		App.UI.StoryCaption.encyclopedia = "Wardeness";

		return App.Facilities.WardenessSelect();
	}, ["jump-to-safe", "jump-hidden", "jump-from-safe"]
);

new App.DomPassage("Farmer Select",
	() => {
		V.nextButton = "Back";
		V.nextLink = "Farmyard";
		App.UI.StoryCaption.encyclopedia = "Farmer";

		return App.Facilities.FarmerSelect();
	}, ["jump-to-safe", "jump-hidden", "jump-from-safe"]
);

new App.DomPassage("Matron Select",
	() => {
		V.nextButton = "Back";
		V.nextLink = "Nursery";
		App.UI.StoryCaption.encyclopedia = "Matron";

		return App.Facilities.MatronSelect();
	}, ["jump-to-safe", "jump-hidden", "jump-from-safe"]
);

new App.DomPassage("Rules Assistant",
	() => {
		const div = document.createElement("div");
		App.RA.options(div);
		return div;
	}, ["jump-to-safe", "jump-from-safe"]
);

new App.DomPassage("Rules Assistant Summary",
	() => {
		V.nextButton = "Back";
		V.nextLink = "Rules Assistant";
		V.returnTo = "Rules Assistant";
		return App.RA.summary();
	}, ["jump-to-safe", "jump-from-safe"]
);

new App.DomPassage("Toy Shop",
	() => {
		V.nextButton = "Back";
		V.nextLink = "Main";

		return App.UI.toyShop();
	}, ["jump-to-safe", "jump-from-safe"]
);

new App.DomPassage("Dispensary",
	() => {
		V.nextButton = "Back"; V.nextLink = "Manage Penthouse"; App.UI.StoryCaption.encyclopedia = "The Pharmaceutical Fab";

		return App.UI.dispensary();
	}, ["jump-to-safe", "jump-from-safe"]
);

repo_name: Ducati/fc

license: mit


code:
/* ### Central Slave Interact ### */
new App.DomPassage("Slave Interact",
	() => {
		V.nextButton = "Confirm changes";
		V.nextLink = "Main";

		return App.UI.SlaveInteract.mainPage(getSlave(V.AS));
	}, ["jump-from-safe"]
);

/* ### Single Interaction ### */
new App.DomPassage("SlaveOnSlaveFeeding",
	() => {
		V.nextButton = "Back";
		V.nextLink = "Slave Interact";

		return App.UI.SlaveInteract.slaveOnSlaveFeedingSelection(getSlave(V.AS));
	}
);

new App.DomPassage("KillSlave", () => App.UI.SlaveInteract.killSlave(getSlave(V.AS)));

new App.DomPassage(
	"Slave Slave Swap Workaround",
	() => {
		V.nextButton = "Abort Operation";
		V.nextLink = "Main";
		return bodySwapSelection(getSlave(V.AS));
	}
);

new App.DomPassage(
	"Husk Slave Swap Workaround",
	() => {
		V.nextButton = "Abort Operation";
		if (V.activeSlave.tankBaby !== 3) {
			V.nextLink = "Scheduled Event";
			V.returnTo = "Scheduled Event";
		} else {
			V.nextLink = "Main";
			V.returnTo = "Incubator";
		}
		return huskSwapSelection(V.activeSlave);
	}
);

new App.DomPassage(
	"Incubator Retrieval Workaround",
	() => {
		V.returnTo = "Main";
		V.nextLink = "Incubator";
		V.nextButton = "Continue";
		return App.UI.facilityRetrievalWorkaround("Incubator");
	}
);

new App.DomPassage(
	"Nursery Retrieval Workaround",
	() => {
		V.returnTo = "Main";
		V.nextLink = "Nursery";
		V.nextButton = "Continue";
		return App.UI.facilityRetrievalWorkaround("Nursery");
	}
);

new App.DomPassage(
	"Agent Company",
	() => {
		V.nextButton = "Continue";
		V.nextLink = "Neighbor Interact";
		return App.UI.SlaveInteract.agentCompany(getSlave(V.AS));
	}, ["jump-from-safe"]
);

new App.DomPassage(
	"Surgery Degradation",
	() => {
		V.nextButton = "Continue";
		V.nextLink = "Remote Surgery";
		return App.UI.SlaveInteract.surgeryDegradation(getSlave(V.AS));
	}, ["jump-from-safe"]
);

new App.DomPassage(
	"Remote Surgery",
	() => {
		V.nextButton = "Confirm changes";
		V.nextLink = "Slave Interact";
		return App.UI.SlaveInteract.remoteSurgery(getSlave(V.AS));
	}, ["jump-from-safe"]
);

new App.DomPassage(
	"Import Slave",
	() => {
		V.nextButton = "Continue";
		V.nextLink = "Slave Interact";
		return App.UI.SlaveInteract.importSlave();
	}, ["jump-from-safe"]
);

new App.DomPassage(
	"Export Slave",
	() => {
		V.nextButton = "Continue";
		V.nextLink = "Slave Interact";
		return App.UI.SlaveInteract.exportSlave(getSlave(V.AS));
	}, ["jump-from-safe"]
);

new App.DomPassage(
	"Create Slave Bot",
	() => {
		V.nextButton = "Continue";
		V.nextLink = "Slave Interact";
		const el = new DocumentFragment();
		App.UI.DOM.appendNewElement("p", el, `Exporting slave...`);
		App.UI.SlaveInteract.createSlaveBot(getSlave(V.AS));
		return el;
	}, ["jump-from-safe"]
);

new App.DomPassage(
	"Cheat Edit JS",
	() => {
		V.nextButton = " ";
		return App.UI.SlaveInteract.cheatEditSlave(getSlave(V.AS));
	}
);

new App.DomPassage(
	"Cheat Edit JS Apply",
	() => {
		V.nextButton = "Continue";
		V.nextLink = "Slave Interact";
		const el = new DocumentFragment();
		App.UI.DOM.appendNewElement("p", el, `You perform the dark rituals, pray to the dark gods, and sell your soul for the power to change and mold slaves to your will.`);
		App.UI.DOM.appendNewElement("p", el, `This slave has been changed forever and you have lost a bit of your soul, YOU CHEATER!`);
		return el;
	}, ["jump-from-safe"]
);

new App.DomPassage("MpregSelf",
	() => {
		V.nextButton = "Back";
		V.nextLink = "Main";

		return MpregSelf();
	}
);

new App.DomPassage("BreedingTest",
	() => {
		V.nextButton = "Confirm changes";
		V.nextLink = "Slave Interact";
		return App.UI.DOM.makeElement("div", App.Interact.eliteBreedingExam(getSlave(V.AS)));
	}
);

new App.DomPassage("Aztec Slave Sacrifice Penance",
	() => {
		V.nextButton = "Back to Main";
		V.nextLink = "Main";
		App.UI.StoryCaption.encyclopedia = "Aztec Revivalism";
		return App.UI.SlaveInteract.aztecSlaveSacrificePenance(getSlave(V.AS));
	}
);

new App.DomPassage("Aztec Slave Sacrifice Life",
	() => {
		V.nextButton = "Back to Main";
		V.nextLink = "Main";
		App.UI.StoryCaption.encyclopedia = "Aztec Revivalism";
		return App.UI.SlaveInteract.aztecSlaveSacrificeLife(getSlave(V.AS));
	}
);

new App.DomPassage("Abort",
	() => {
		V.nextButton = "Back";
		V.nextLink = "Slave Interact";
		return App.Interact.abort(getSlave(V.AS));
	}
);

new App.DomPassage("FAnimalImpreg",
	() => {
		V.nextButton = "Back";
		V.nextLink = "Slave Interact";
		return App.Interact.fAnimalImpreg(getSlave(V.AS));
	}
);

new App.DomPassage("Matchmaking",
	() => {
		if (lastVisited("Child Interact") === 1) {
			V.nextLink = "Incubator";
		} else {
			V.nextLink = "Slave Interact";
		}
		V.nextButton = "Continue";
		return App.Interact.matchmaking(getSlave(V.AS));
	}
);

new App.DomPassage("FSelf",
	() => {
		V.nextLink = "Main";
		V.nextButton = "Back";
		return App.Interact.fSelf();
	}
);

new App.DomPassage("FMarry",
	() => {
		V.nextButton = "Back";
		V.nextLink = "Slave Interact";
		return App.Interact.fMarry(getSlave(V.AS));
	}
);

new App.DomPassage("Discard Confirm",
	() => {
		V.nextButton = "Back";
		V.nextLink = "Slave Interact";
		return App.Interact.discard(getSlave(V.AS));
	}
);

new App.DomPassage("Sell Slave",
	() => {
		V.nextButton = "Back";
		V.nextLink = "Slave Interact";
		return App.Interact.sellSlave(getSlave(V.AS));
	}
);

new App.DomPassage("Brothel Assignment Scene",
	() => {
		App.UI.StoryCaption.encyclopedia = "Brothel";
		return App.Facilities.Brothel.assignmentScene(getSlave(V.AS));
	}
);

new App.DomPassage("Industrial Dairy Assignment Scene",
	() => {
		App.UI.StoryCaption.encyclopedia = "Dairy";
		return App.Facilities.Dairy.industrialAssignmentScene(getSlave(V.AS));
	}
);

new App.DomPassage("Free Range Dairy Assignment Scene",
	() => {
		App.UI.StoryCaption.encyclopedia = "Dairy";
		return App.Facilities.Dairy.freeRangeAssignmentScene(getSlave(V.AS));
	}
);

new App.DomPassage("Personal Attention Select",
	() => {
		return App.UI.Player.personalAttention();
	}, ["jump-to-safe", "jump-from-safe"]
);

new App.DomPassage("Agent Assignment Scene",
	() => {
		V.nextButton = "Continue";
		V.nextLink = "Neighbor Interact";
		App.UI.StoryCaption.encyclopedia = "Agents";
		return App.UI.SlaveInteract.agentAssignmentScene();
	}
);

new App.DomPassage("Artificial Insemination",
	() => {
		V.nextButton = "Back";
		V.nextLink = "Remote Surgery";
		return App.UI.SlaveInteract.artificialInsemination();
	}
);

new App.DomPassage("Neighbor Interact",
	() => {
		V.nextLink = "Main";
		V.nextButton = "Back";
		return App.Neighbor.Interact();
	}, ["jump-to-safe", "jump-from-safe"]
);

new App.DomPassage("Slave Slave Swap",
	() => {
		V.nextButton = "Continue";
		return App.UI.SlaveInteract.slaveSlaveSwap();
	}
);

new App.DomPassage("Husk Slave Swap",
	() => {
		V.nextButton = "Continue";
		return App.UI.SlaveInteract.huskSlaveSwap();
	}
);

new App.DomPassage("Prosthetics Configuration",
	() => {
		const slave = getSlave(V.AS);
		return App.UI.prostheticsConfigPassage(slave);
	}
);

new App.DomPassage("Multiple Organ Implant",
	() => {
		return App.UI.multipleOrganImplant();
	}
);

new App.DomPassage("Surgery Death",
	() => {
		V.nextLink = "Main";

		const slave = getSlave(V.AS);
		removeSlave(slave);

		const f = new DocumentFragment();
		App.Events.addParagraph(f, [`${slave.slaveName} <span class="health dec">has died from complications of surgery.</span>`]);
		return f;
	}, ["jump-from-safe"]
);

new App.DomPassage("Walk Past List",
	() => {
		V.nextButton = "Back to Main";
		V.nextLink = "Main";
		return App.UI.walkPastAll();
	}
);
new App.DomPassage("Find Slave",
	() => {
		V.nextButton = "Back to Main";
		V.nextLink = "Main";
		return App.UI.findSlave();
	}, ["jump-to-safe", "jump-from-safe"]
);

new App.DomPassage("Analyze Pregnancy",
	() => {
		V.nextButton = "Continue";
		return App.UI.analyzePregnancy();
	}, ["jump-from-safe"]
);

new App.DomPassage("Analyze PC Pregnancy",
	() => {
		V.nextButton = "Continue";
		return App.UI.analyzePCPregnancy();
	}, ["jump-from-safe"]
);

new App.DomPassage("Surrogacy Workaround",
	() => {
		V.nextButton = "Cancel";
		return App.UI.surrogacyWorkaround();
	}
);

new App.DomPassage("Pit Workaround",
	() => {
		V.nextButton = V.pit.slavesFighting === null || V.pit.slavesFighting.length !== 2 ? "Cancel" : "Finish";
		V.nextLink = "Pit";
		return App.Facilities.Pit.workaround();
	}
);

new App.DomPassage("Cloning Workaround",
	() => {
		V.nextButton = "Cancel";
		V.nextLink = "Gene Lab";
		return App.UI.cloningWorkaround();
	}
);

new App.DomPassage("Ova Transplant Workaround",
	() => {
		V.nextButton = "Cancel";
		return App.UI.ovaTransplantWorkaround();
	}
);

new App.DomPassage("Bulk Ova Transplant Workaround",
	() => {
		V.nextButton = "Cancel";
		return App.UI.ovaTransplantWorkaround(true);
	}
);

new App.DomPassage("Subordinate Targeting",
	() => {
		V.nextButton = "Back";
		V.nextLink = V.returnTo;
		return App.UI.subordinateTargeting();
	}
);

new App.DomPassage("Dinner Party Preparations",
	() => {
		V.nextButton = "Cancel The Event";
		V.nextLink = "Main";
		return App.Mods.DinnerParty.Prep();
	}
);

new App.DomPassage("Dinner Party Execution",
	() => {
		V.nextButton = "Back to Main";
		V.nextLink = "Main";
		App.UI.StoryCaption.encyclopedia = "Personal Assistant";
		return App.Mods.DinnerParty.Execution();
	}
);

new App.DomPassage("Inspect Tank Settings",
	() => {
		V.nextButton = "Continue";
		V.nextLink = "Incubator";
		return App.UI.inspectTankSettings(false);
	}, ["jump-from-safe"]
);

new App.DomPassage("Inspect Fetus Tank Settings",
	() => {
		V.nextButton = "Continue";
		V.nextLink = "Analyze Pregnancy";
		return App.UI.inspectTankSettings(true);
	}, ["jump-from-safe"]
);

new App.DomPassage("Inspect PC Fetus Tank Settings",
	() => {
		V.nextButton = "Continue";
		V.nextLink = "Analyze PC Pregnancy";
		return App.UI.inspectTankSettings(true, true);
	}, ["jump-from-safe"]
);


code:
new App.DomPassage("Alpha disclaimer", () => { return App.Intro.alphaDisclaimer(); });

new App.DomPassage("New Game Plus", () => { return App.Intro.newGamePlus(); });

new App.DomPassage("PC Body Intro", () => { return App.Intro.PCBodyIntro(); });

new App.DomPassage("Intro Summary", () => { return App.Intro.summary(); });

new App.DomPassage("Acquisition",
	() => {
		App.UI.StoryCaption.encyclopedia = "How to Play";
		return App.Intro.acquisition();
	}
);

new App.DomPassage("Customize Slave Trade", () => { return App.Intro.CustomSlaveTrade(); });

new App.DomPassage("Starting Girls", () => { return App.StartingGirls.passage(); });

new App.DomPassage("Gender Intro", () => { return App.Intro.genderIntro(); });

new App.DomPassage("PC Experience Intro", () => { return App.Intro.PCExperienceIntro(); });

new App.DomPassage("Economy Intro", () => { return App.Intro.economyIntro(); });

new App.DomPassage("Trade Intro", () => { return App.Intro.tradeIntro(); });

new App.DomPassage("Extreme Intro", () => { return App.Intro.extremeIntro(); });

new App.DomPassage("Slave Age Intro", () => { return App.Intro.slaveAgeIntro(); });

new App.DomPassage("PC Rumor Intro", () => { return App.Intro.PCRumorIntro(); });

new App.DomPassage("Terrain Intro", () => { return App.Intro.terrainIntro(); });

new App.DomPassage("Takeover Target", () => { return App.Intro.takeoverTarget(); });

new App.DomPassage("PC Preg Intro", () => { return App.Intro.PCPregIntro(); });

new App.DomPassage("PC Appearance Intro", () => { return App.Intro.PCAppearanceIntro(); });

new App.DomPassage("Location Intro", () => { return App.Intro.locationIntro(); });


code:
new App.DomPassage("Main",
	() => {
		V.nextButton = "END WEEK";
		V.nextLink = "End Week";
		App.UI.StoryCaption.encyclopedia = "How to Play";

		return App.MainView.full();
	}, ["jump-to-safe", "jump-from-safe"]
);

new App.DomPassage("Future Society", () => App.UI.fsPassage(), ["jump-to-safe", "jump-from-safe"]);

new App.DomPassage("Slave FS Conformance", () => App.FSConformance.slaveConformancePassage(), ["jump-to-safe", "jump-from-safe"]);

new App.DomPassage("Manage Penthouse",
	() => {
		V.nextButton = "Back";
		V.nextLink = "Main";
		App.UI.StoryCaption.encyclopedia = "What the Upgrades Do";
		return App.UI.managePenthouse();
	}, ["jump-to-safe", "jump-from-safe"]
);

new App.DomPassage("Manage Personal Affairs",
	() => {
		V.nextButton = "Back";
		V.nextLink = "Main";
		App.UI.StoryCaption.encyclopedia = "Being in Charge";
		return App.UI.managePersonalAffairs();
	}, ["jump-to-safe", "jump-from-safe"]
);

new App.DomPassage("pDildoVagina",
	() => {
		V.nextButton = "Back";
		V.nextLink = "Manage Personal Affairs";
		return App.UI.pDildoVagina();
	}, ["jump-from-safe"]
);

new App.DomPassage("pDildoAss",
	() => {
		V.nextButton = "Back";
		V.nextLink = "Manage Personal Affairs";
		return App.UI.pDildoAss();
	}, ["jump-from-safe"]
);

new App.DomPassage("retire",
	() => {
		V.nextButton = "Back";
		V.nextLink = "Main";
		return App.Events.retire(getSlave(V.AS));
	}
);

new App.DomPassage("Change Language",
	() => {
		V.nextButton = "Confirm changes";
		V.nextLink = "Main";
		return App.Arcology.changeLanguage();
	}, ["jump-from-safe"]
);

new App.DomPassage("Neighbor Arcology Cheat",
	() => {
		V.nextButton = "Continue";
		V.nextLink = "MOD_Edit Neighbor Arcology Cheat Datatype Cleanup";
		return App.UI.Cheat.neighbors();
	}
);

new App.DomPassage("Manage Corporation",
	() => {
		V.nextButton = "Back";
		V.nextLink = "Main";
		App.UI.StoryCaption.encyclopedia = "The Corporation";
		return App.Corporate.manage();
	}, ["jump-to-safe", "jump-from-safe"]
);

new App.DomPassage("Manage Arcology",
	() => {
		return App.UI.manageArcology();
	}, ["jump-to-safe", "jump-from-safe"]
);

new App.DomPassage("Policies",
	() => {
		V.nextButton = "Back";
		V.nextLink = "Main";
		App.UI.StoryCaption.encyclopedia = "Future Societies";
		return App.UI.policies();
	}, ["jump-to-safe", "jump-from-safe"]
);

new App.DomPassage("Universal Rules",
	() => {
		V.nextButton = "Back";
		V.nextLink = "Manage Penthouse";
		return App.UI.universalRules();
	}, ["jump-to-safe", "jump-from-safe"]
);

new App.DomPassage("Farmyard Animals",
	() => {
		return App.Facilities.Farmyard.animals();
	}
);

new App.DomPassage("Doctor Consultation",
	() => {
		return App.UI.doctorConsultation();
	}
);

new App.DomPassage("Elective Surgery",
	() => {
		return App.UI.electiveSurgery();
	}
);

new App.DomPassage("Personal Appearance",
	() => {
		V.nextButton = "Back";
		V.nextLink = "Manage Personal Affairs";
		return App.UI.playerSalon(V.PC);
	}, ["jump-to-safe", "jump-from-safe"]
);

new App.DomPassage("Brothel Advertisement",
	() => {
		return App.Facilities.Brothel.ads();
	}, ["jump-from-safe"]
);

new App.DomPassage("Club Advertisement",
	() => {
		return App.Facilities.Club.ads();
	}, ["jump-from-safe"]
);

new App.DomPassage("Coursing Association",
	() => {
		V.nextButton = "Back to Main";
		V.nextLink = "Main";
		return App.UI.coursingAssociation();
	}, ["jump-from-safe"]
);

new App.DomPassage("Personal assistant options",
	() => {
		V.nextButton = "Back";
		V.nextLink = "Main";
		return App.UI.personalAssistantOptions();
	}, ["jump-to-safe", "jump-from-safe"]
);

new App.DomPassage("Assistant Appearance Pack Two",
	() => {
		V.nextButton = "Back";
		V.nextLink = "Personal assistant options";
		return App.UI.assistantAppearancePackTwo();
	}
);

new App.DomPassage("The Black Market",
	() => {
		V.nextButton = "Back";
		V.nextLink = "Main";
		V.returnTo = "Main";
		App.UI.StoryCaption.encyclopedia = "The Black Market";
		return App.UI.blackMarket();
	}, ["jump-to-safe", "jump-from-safe"]
);

new App.DomPassage("FirebaseTour",
	() => {
		V.nextButton = "Return to Operations";
		V.nextLink = "Firebase";
		return App.UI.firebaseTour();
	}
);

new App.DomPassage("Prosthetic Lab",
	() => {
		V.nextButton = "Back";
		V.nextLink = "Manage Penthouse";
		return App.UI.prostheticLab();
	}, ["jump-to-safe", "jump-from-safe"]
);

new App.DomPassage("Gene Lab",
	() => {
		V.nextButton = "Back";
		V.nextLink = "Manage Penthouse";
		App.UI.StoryCaption.encyclopedia = "The Pharmaceutical Fab";
		return App.UI.geneLab();
	}, ["jump-to-safe", "jump-from-safe"]
);

new App.DomPassage("Breeder Proposal",
	() => {
		V.nextButton = " ";
		V.nextLink = "Main";
		return App.UI.breederProposal();
	}
);

new App.DomPassage("Implant Manufactory",
	() => {
		V.nextButton = "Back";
		V.nextLink = "Manage Penthouse";
		App.UI.StoryCaption.encyclopedia = "The Pharmaceutical Fab";
		return App.UI.implantManufactory();
	}, ["jump-to-safe", "jump-from-safe"]
);

new App.DomPassage("Organ Farm",
	() => {
		V.nextButton = "Back";
		V.nextLink = "Manage Penthouse";
		App.UI.StoryCaption.encyclopedia = "The Pharmaceutical Fab";
		return App.UI.organFarm();
	}, ["jump-to-safe", "jump-from-safe"]
);

new App.DomPassage("Barracks",
	() => {
		V.nextButton = "Back";
		V.nextLink = "Main";
		return App.UI.barracks();
	}
);

new App.DomPassage("editSF",
	() => {
		V.nextButton = `Back to ${V.SF.Lower}'s Firebase`;
		V.nextLink = "Firebase";
		return App.UI.editSF();
	}
);

new App.DomPassage("Firebase",
	() => {
		V.nextButton = "Back";
		V.nextLink = "Main";
		App.UI.StoryCaption.encyclopedia = "Special Force";
		return App.UI.FireBase();
	}, ["jump-to-safe", "jump-from-safe"]
);

new App.DomPassage("edicts",
	() => {
		V.nextButton = "Back";
		V.nextLink = "Main";
		return App.Mods.SecExp.edicts();
	}, ["jump-to-safe", "jump-from-safe"]
);

new App.DomPassage("Media Studio",
	() => {
		V.nextButton = "Back";
		V.nextLink = "Manage Penthouse";
		App.UI.StoryCaption.encyclopedia = "Media Hub";
		return App.UI.mediaStudio();
	}, ["jump-to-safe", "jump-from-safe"]
);


code:
new App.DomPassage("Options",
	() => {
		if (lastVisited("Slave Interact") === 1) {
			V.storedLink = "Slave Interact";
		} else {
			V.storedLink = "Main";
		}

		V.nextButton = "Back";
		V.nextLink = V.storedLink;
		App.UI.StoryCaption.encyclopedia = "How to Play";

		return App.UI.optionsPassage();
	}, ["jump-to-safe", "jump-from-safe", "temporary-images"]
);

new App.DomPassage("Description Options",
	() => {
		V.nextButton = "Back";
		if (V.storedLink !== "Slave Interact") {
			V.storedLink = lastVisited("Slave Interact") === 1 ? "Slave Interact" : "Options";
		}
		V.nextLink = V.storedLink;

		return App.UI.descriptionOptions();
	}, ["jump-to-safe", "jump-from-safe"]
);

new App.DomPassage("Summary Options",
	() => {
		V.nextButton = "Back";
		if (V.storedLink !== "Slave Interact" && V.storedLink !== "Main") {
			V.storedLink = lastVisited("Main") === 1 ? "Main" : "Options";
		}
		V.nextLink = V.storedLink;
		App.Utils.PassageSwitchHandler.set(App.EventHandlers.optionsChanged);

		return App.UI.summaryOptions();
	}, ["jump-to-safe", "jump-from-safe"]
);

new App.DomPassage("Hotkey Settings",
	() => {
		V.nextButton = "Back";
		V.nextLink = "Main";

		return App.UI.Hotkeys.settings();
	}, ["jump-to-safe", "jump-from-safe"]
);

new App.DomPassage("Edit Genetics",
	() => {
		return App.UI.editGenetics();
	}, ["jump-from-safe"]
);

new App.DomPassage("Variable Difference",
	() => {
		V.nextButton = " "; /* disable the nextButton. Forces the user to use the "Go Back" option contained in the function */
		return App.UI.variableDifference();
	}
);

new App.DomPassage("MOD_Edit Arcology Cheat",
	() => {
		V.nextButton = "Continue";
		V.nextLink = "MOD_Edit Arcology Cheat Datatype Cleanup";
		return App.UI.Cheat.arcologyPassage();
	}
);

new App.DomPassage("MOD_Edit Arcology Cheat Datatype Cleanup",
	() => {
		V.nextButton = "Continue";
		V.nextLink = "Main";
		return App.UI.Cheat.arcologyCheatDatatypeCleanup();
	}
);

new App.DomPassage("MOD_Edit Neighbor Arcology Cheat Datatype Cleanup",
	() => {
		V.nextButton = "Continue";
		V.nextLink = "Main";
		return App.UI.Cheat.neighborArcologyCheatDatatypeCleanup();
	}
);

new App.DomPassage("PCCheatMenu",
	() => {
		V.nextButton = "Apply";
		V.nextLink = "PCCheatMenuCheatDatatypeCleanup";
		App.UI.StoryCaption.encyclopedia = "Design Your Master";
		return App.UI.Cheat.PCCheatMenu();
	}
);

new App.DomPassage("PCCheatMenuCheatDatatypeCleanup",
	() => {
		V.nextButton = "Continue";
		V.nextLink = "Manage Personal Affairs";
		return App.UI.Cheat.PCCheatMenuCheatDatatypeCleanup();
	}
);


code:
new App.DomPassage("Market",
	() => {
		const span = App.UI.DOM.makeElement("span", App.Markets[V.market.slaveMarket]());
		span.id = "slave-markets";
		return span;
	}, ["temporary-images"]
);

new App.DomPassage("Buy Slaves",
	() => {
		V.nextButton = "Back";
		V.nextLink = "Main";
		App.UI.StoryCaption.encyclopedia = "Obtaining Slaves";

		return App.UI.market();
	}, ["jump-from-safe", "jump-to-safe", "temporary-images"]
);

new App.DomPassage("Bulk Slave Intro", () => App.Markets.bulkSlaveIntro());

new App.DomPassage("Underperforming Slaves",
	() => {
		V.nextButton = "Back";
		V.nextLink = "Buy Slaves";
		return App.Underperformers.passage();
	}, ["jump-from-safe", "jump-to-safe", "temporary-images"]
);


code:
App.Corporate.Shared.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;
	}
};

App.Corporate.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); }
};
App.Corporate.Shared.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;
	}
};
App.Corporate.Shared.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;
	}
};
App.Corporate.Shared.SellUnhousedSlaves = function(division, divLedger, rate) {
	if (divLedger.market.sell != 0) {
		return;
	}

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

	divLedger.market.sell = unhoused;
};
App.Corporate.Shared.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})`;
};
App.Corporate.Shared.MessageSlaveToMarket = function(division) {
	return `The slaves from this division can be sold for <span class='yellowgreen'>${cashFormat(Math.trunc(division.soldSlaveValue * menialSlaveCost()))}</span> each.`;
};
App.Corporate.Shared.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)};
};
App.Corporate.Shared.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);
			}
		}
	}
};
App.Corporate.Shared.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.Division.Base = class {
	constructor({id, name, focusDescription, sizeCost, maintenance, founding, merger}) {
		this._const = {};
		this._const.id = id;
		this._const.corpId = `Div${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 App.Corporate.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 App.Corporate.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 Error("Cannot set development count to less than 0");
		}
		// dissolve is the only function that sets founded to false.
		if (value === 0 && this.founded) {
			throw Error("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 Error(`${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
	 * @returns {boolean}
	 */
	get fromMarket() { throw Error("Must be defined"); }

	/**
	 * @abstract
	 * @returns {boolean}
	 */
	get toMarket() { throw Error("Must be defined"); }

	/**
	 * @abstract
	 * @returns {number}
	 */
	get heldSlaves() { throw Error("Must be defined"); }

	/**
	 * @abstract
	 * @param {number} value
	 */
	set heldSlaves(value) { throw Error("Must be defined"); }

	/**
	 * @abstract
	 * @returns {number}
	 */
	get activeSlaves() { throw Error("Must be defined"); }

	/**
	 * @abstract
	 * @param {number} value
	 */
	set activeSlaves(value) { throw Error("Must be defined"); }

	/**
	 * @abstract
	 * @returns {number}
	 */
	get processRate() { throw Error("Must be defined"); }

	/**
	 * @abstract
	 * @returns {number}
	 */
	get initialSlaveValue() { throw Error("Must be defined"); }

	/**
	 * @abstract
	 * @returns {number}
	 */
	get soldSlaveValue() { throw Error("Must be defined"); }

	/**
	 * @abstract
	 * @returns {string}
	 */
	get slaveAction() { throw Error("Must be defined"); }

	/**
	 * @abstract
	 * @returns {string}
	 */
	get nounFinishedSlave() { throw Error("Must be defined"); }

	/**
	 * @abstract
	 * @returns {string}
	 */
	get nounSlaveFromMarket() { throw Error("Must be defined"); }

	/**
	 * @abstract
	 * @returns {string}
	 */
	messageSlaveCount() { throw Error("Must be defined"); }

	/**
	 * @abstract
	 * @returns {string}
	 */
	messageSlaveOutput() { throw Error("Must be defined"); }

	/**
	 * @abstract
	 * @param {object} divLedger
	 * @returns {string}
	 */
	message_endWeek_Slaves(divLedger) { throw Error("Must be defined"); }

	/**
	 * @abstract
	 * @param {object} ledger
	 * @param {object} divLedger
	 * @returns {string}
	 */
	endWeek_Slaves(ledger, divLedger) { throw Error("Must be defined"); }

	/**
	 * The value of a slave ignoring enhancements from founded divisions
	 * The actual value of a slave after all improvements
	 * @returns {number}
	 */
	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 Error("No route to acquisition found.");
	}

	/**
	 * @returns {number}
	 */
	get maintenanceSlaves() { return this.activeSlaves * this.processRate; }

	/**
	 * @returns {{cost: number, perUnit: number}}
	 */
	getDisplayMaintenanceCost() {
		const cost = this.maintenanceCost;
		const processedCount = this.maintenanceSlaves;

		return {cost, perUnit: cost / processedCount};
	}

	/**
	 * @param {App.Corporate.Division.Base} division
	 * @returns {boolean}
	 */
	getAutoSendToDivision(division) {
		return (!App.Corporate.ownsIntermediaryDivision(this, division) && this.getStored(`To${division.id}`) === 1);
	}

	/**
	 * @param {App.Corporate.Division.Base} division
	 * @param {boolean} value
	 */
	setAutoSendToDivision(division, value) {
		this.setStored(`To${division.id}`, value ? 1 : 0);
	}

	/**
	 * @returns {boolean}
	 */
	getAutoSendToMarket() {
		return !!this.getStored("ToMarket");
	}

	/**
	 * @param {boolean} value
	 */
	setAutoSendToMarket(value) {
		this.setStored("ToMarket", value ? 1 : 0);
	}

	/**
	 * @returns {boolean}
	 */
	getAutoBuyFromMarket() {
		return !!this.getStored("FromMarket");
	}

	/**
	 * @param {boolean} value
	 */
	setAutoBuyFromMarket(value) {
		this.setStored("FromMarket", value ? 1 : 0);
	}

	/**
	 * @param {object} divLedger
	 */
	endweek_Revenue(divLedger) {
		// Unless otherwise specified, divisions don't produce revenue directly.
	}

	/**
	 * @param {object} divLedger
	 */
	endWeek_Transfer(divLedger) {
		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);
		}
	}

	/**
	 * @param {object} divLedger
	 */
	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;
		}
		App.Corporate.Shared.SellUnhousedSlaves(this, divLedger, this.processRate);
	}

	create() {
		if (this.founded) {
			throw Error(`${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.corp[`${this._const.corpId}To${nextDep.id}`]);
	}

	// private helpers
	/**
	 * @param {string} key
	 * @returns {*}
	 */
	getStored(key) { return V.corp[this._const.corpId + key]; }

	/**
	 * @param {string} key
	 * @param {*} value
	 */
	setStored(key, value) { V.corp[this._const.corpId + key] = value; }
};


code:
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 App.Corporate.Shared.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 Error("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="positive">${numberWithPluralNonZero(this.developmentCount, "new slave")}</span> each week.`;
	}

	messageSlaveOutput() {
		return App.Corporate.Shared.MessageSlaveToMarket(this);
	}

	message_endWeek_Slaves(divLedger) {
		let newSlaves = divLedger.slaves.value;
		if (newSlaves > 0) {
			return `<span class="positive">acquired ${numberWithPlural(newSlaves, "slave")}` +
				(this.hadMercenaryHelp ? " with the help of your mercenaries." : ".");
		} else {
			return `<span class="red">failed to acquire any slaves` +
				(this.hadMercenaryHelp ? " even with the help of your mercenaries." : ".");
		}
	}

	endWeek_Slaves(divLedger) {
		let slaves = App.Corporate.Shared.EndWeekProcessing_Slaves(this.developmentCount, this._const.acquiring);
		this.heldSlaves += slaves.value;
		return divLedger.slaves.apply(slaves);
	}

	// virtual overrides
	get nextDivisions() { return this._const.nextDivisions; }

	dissolve() {
		App.Corporate.sellSlaves(this, this.heldSlaves);
		super.dissolve();
		delete V.corp[`${this._const.corpId}Slaves`];
	}

	getAutoBuyFromMarket() {
		return false;
	}

	setAutoBuyFromMarket(value) {
		throw Error("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._const.mercenaryHelp.level) * this._const.mercenaryHelp.cost * 1000);
			baseCost += mercHelpCost;
		}
		return baseCost;
	}

	create() {
		super.create();
		this.heldSlaves = 0;
		App.Corporate.Shared.FoundingSetupAutoSell(this);
	}

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


code:
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 App.Corporate.Shared.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 App.Corporate.Shared.MessageProcessedSlaves(this, `can ${this._const.slaveProcessType.present}`, 'green');
	}

	messageSlaveOutput() {
		return App.Corporate.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="positive">${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 = App.Corporate.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 Error("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;
		App.Corporate.Shared.SellOverflowSlaves(this);
	}

	dissolve() {
		App.Corporate.sellSlaves(this, this.heldSlaves);
		super.dissolve();
		delete V.corp[this._const.corpId + "Slaves"];
		delete V.corp[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;
		App.Corporate.Shared.FoundingSetupAutoBuy(this);
		App.Corporate.Shared.FoundingSetupAutoSell(this);
	}
};


code:
App.Corporate.Division.Working = class extends App.Corporate.Division.Base {
	constructor({founding, attrition, revenue, slaveWorkDescription}) {
		super(arguments[0]);
		this._const.attrition = new App.Corporate.Shared.averageRange(attrition);
		this._const.revenue = new App.Corporate.Shared.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 Error("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;
	}

	// @ts-ignore
	get nounFinishedSlave() { throw Error("Cannot get finished slave in working division, since they don't produce finished slaves."); }

	get nounSlaveFromMarket() { return this._const.slaveWorkDescription.market; }

	messageSlaveCount() {
		return App.Corporate.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 = App.Corporate.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="positive">${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;
		App.Corporate.Shared.SellOverflowSlaves(this);
	}

	dissolve() {
		super.dissolve();
		delete V.corp[this._const.corpId + "Slaves"];
	}

	getAutoSendToMarket() {
		return false;
	}

	setAutoSendToMarket(value) {
		throw Error("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;
		App.Corporate.Shared.FoundingSetupAutoBuy(this);
	}
};


code:
// cSpell:ignore no-brainer, large-uddered

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 sentence "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': 'acquisition',
			},
			'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': 'acquisition',
			},
			'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 self-respecting slave-breaker 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 enterprise, 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 farmer's large-uddered 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: 'acquisition',
				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 = function() {
	const Ledger = class {
		constructor(corp, suffix = "") {
			this.corp = corp;
			this.suffix = suffix;
		}
		get operations() { return this.getStored('OpCost'); }
		set operations(value) { this.setStored('OpCost', value); }

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

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

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

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

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

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

		get foreignRevenue() { return this.getStored('NeighborBonus'); }
		set foreignRevenue(value) { 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 typeHiddenMembers = class {
		constructor() {
			this._const = {};
			this._var = {};
			this._cache = {};
		}
	};
	const WeekProcessingEfficiencyLine = 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 WeekProcessingEfficiencyLine();
			this._var.revenue = new WeekProcessingEfficiencyLine();
			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) { throw Error("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)) { throw Error("Operating cost wasn't finite "); }
			this._var.operatingCost = Math.trunc(value);
		}
		get overhead() {
			const divCount = App.Corporate.getStored("Div");
			if (divCount <= 1 || V.corp.disableOverhead) { return 0; }

			const divisionOverhead = Object.values(this.maintenanceCategories).reduce((r, categoryLedger) => r + categoryLedger.cost, 0);
			const maintenanceInfo = App.Corporate.maintenance.corporate;
			let corpOverhead = Math.pow(Math.max(divCount - maintenanceInfo.freeDivisions, 0), 2) * maintenanceInfo.divisionCost;
			let retval = (divisionOverhead + corpOverhead) * 1 - (5 - V.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.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);
		}
	}

	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.endWeek = function() {
		let ledger = new WeekProcessingLedger();
		// Prepare requests
		for (let div of App.Corporate.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);
		}
		App.Corporate.chargeAsset(ledger.operatingCost, "operations");
		App.Corporate.chargeAsset(ledger.overhead, "overhead");
		// Execute sales requests, transfers, and earned revenue
		for (let divLedger of Object.values(ledger.divisionLedgers)) {
			let div = divLedger.division;
			App.Corporate.earnRevenue(divLedger.revenue.value, "local");
			if (div.activeSlaves > 0) {
				App.Corporate.Shared.SellOverflowSlaves(div);
			}

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

				App.Corporate.transferSlaves(div, otherDiv, count);
			}
			if (divLedger.market.sell > 0) {
				divLedger.market.finalSale = App.Corporate.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 requests:
		let purchaseValues = evenFillArray(Object.values(ledger.divisionLedgers)
			.filter(divLedger=>divLedger.market.buy > 0)
		, App.Corporate.getStored("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 = App.Corporate.buySlaves(div, divLedger.market.buy);
		}
		App.Corporate.creditEconomy();

		if (App.Corporate.getStored("Div") < App.Corporate.divisionList.length && App.Corporate.getStored("ExpandToken") === 0) {
			let expansionValue = Math.trunc(Math.pow(App.Corporate.getStored("Div"), 1.5) + (5 * App.Corporate.getStored("Div") + 2) / 4);
			if (App.Corporate.calculateValue() > expansionValue * 1000000) {
				ledger.canExpandNow = true;
				App.Corporate.setStored("ExpandToken", 1);
			}
		}
		let specializationExpansion = 1.6 * Math.pow(1.25, App.Corporate.getStored("Spec")) - 1.2;
		if (App.Corporate.calculateValue() > specializationExpansion * 1000000) {
			App.Corporate.setStored("SpecToken", App.Corporate.getStored("SpecToken") + 1);
			App.Corporate.setStored("Spec", App.Corporate.getStored("Spec") + 1);
			ledger.canSpecializeNow = true;
		}
		if (App.Corporate.getStored("SpecTimer") > 0) {
			App.Corporate.setStored("SpecTimer", App.Corporate.getStored("SpecTimer") - 1);
		}
		App.Corporate.calculateDividend(ledger);

		return ledger;
	};
};


code:
/** Create the DOM ledger table
 * @param {object} ledger - One of the members of App.Corporate.ledger, such as .current or .old
 * @param {number} week - Week number (usually V.week, for the current ledger, or V.week-1, for the old one)
 * @returns {HTMLTableElement}
 */
App.Corporate.writeLedger = function(ledger, week) {
	/** Add a row to the ledger
	 * @param {HTMLElement} table
	 * @param {"th" | "td"} cellType
	 * @param {string} caption
	 * @param {number} [value]
	 * @param {string|Node} [note]
	 */
	function createRow(table, cellType, caption, value, note) {
		const row = App.UI.DOM.appendNewElement("tr", table);
		/** @type {HTMLTableCellElement} */
		const leftCell = App.UI.DOM.appendNewElement(cellType, row, caption);
		if (jsDef(value)) {
			const rightCell = App.UI.DOM.appendNewElement(cellType, row, App.UI.DOM.makeElement("div", formatCash(value)));
			if (note) {
				App.UI.DOM.appendNewElement("div", rightCell, note, "minor-note");
			}
		} else {
			leftCell.colSpan = 2;
		}
	}

	/** Format cash with color, DOM style (probably should be a shared function)
	 * @param {number} cash
	 * @returns {HTMLSpanElement}
	 */
	function formatCash(cash) {
		let span = App.UI.DOM.makeElement('span', cashFormat(cash));
		if (cash === 0) {
			span.classList.add("gray");
		} else {
			span.classList.add("cash");
			if (cash < 0) {
				span.classList.add("dec");
			} else if (cash > 0) {
				span.classList.add("inc");
			}
		}
		return span;
	}

	/** Build the cheat textbox for liquidity
	 * @param {number} cash - initial value
	 * @returns {HTMLInputElement}
	 */
	function makeCheatTextbox(cash) {
		return App.UI.DOM.makeTextBox(cash, (v) => {
			App.Corporate.cheatCash(v);
			updateLedgerTable();
		}, true);
	}

	function buildLedgerTable() {
		const table = document.createElement('table');
		table.className = "corporate";
		table.id = "corporate-ledger";

		const thead = App.UI.DOM.appendNewElement('thead', table);
		createRow(thead, "th", `Ledger for ${asDateString(week)} - ${asDateString(week + 1)}`);
		const tbody = App.UI.DOM.appendNewElement('tbody', table);
		createRow(tbody, "td", "Revenue", ledger.revenue);
		if (V.cheatMode && ledger.foreignRevenue > 0) {
			createRow(tbody, "td", "Including Neighbor Bonus", ledger.foreignRevenue);
		}
		createRow(tbody, "td", "Operating Expenses", forceNeg(ledger.operations));
		createRow(tbody, "td", "Slave Expenses", forceNeg(ledger.slaves));
		createRow(tbody, "td", "Asset Expenses", forceNeg(ledger.development));
		if (V.cheatMode) {
			createRow(tbody, "td", `Economic ${ledger.economicBoost < 0 ? "Expenses" : "Windfall"}`, ledger.economicBoost);
		}
		createRow(tbody, "td", "Overhead", forceNeg(ledger.overhead));
		let econNote = '';
		if (V.difficultySwitch > 0) {
			if (ledger.economy > 100) {
				econNote = "* Profits benefited from a strong economy.";
			} else if (ledger.economy > 60) {
				econNote = "* Profits were lowered by the weak economy.";
			} else {
				econNote = "* Profits were severely depressed by the failing economy.";
			}
		}
		createRow(tbody, "td", "Profit", ledger.profit, econNote);

		createRow(tbody, "th", "Totals");
		createRow(tbody, "td", "Liquidity", App.Corporate.getStored("Cash"), V.cheatMode ? makeCheatTextbox(App.Corporate.getStored("Cash")) : undefined);
		createRow(tbody, "td", "Corporate Value", App.Corporate.calculateValue());
		createRow(tbody, "td", "Dividend for Payout", App.Corporate.getStored("Dividend"), `Pays out on ${asDateString(V.week + V.dividendTimer, -1)},
			${V.dividendTimer === 1	? `the end of this week` : `in ${V.dividendTimer} weeks`}
		`);

		return table;
	}

	function updateLedgerTable() {
		$('#corporate-ledger').replaceWith(buildLedgerTable());
	}

	return buildLedgerTable();
};