From b09636cdefc1a3cc42a0e70d7e5d908a34ac0785 Mon Sep 17 00:00:00 2001
From: Arkerthan <arkerthan@gmail.com>
Date: Sat, 5 Jun 2021 11:55:30 +0200
Subject: [PATCH] Convert Costs Budget to DOM

---
 devTools/types/FC/gameState.d.ts              |   4 +-
 js/002-config/fc-js-init.js                   |   1 +
 js/003-data/gameVariableData.js               |  12 +-
 src/005-passages/budgetPassages.js            |   7 ++
 src/{uncategorized => budget}/budget.js       |   5 +-
 src/budget/costsBudget.js                     | 114 ++++++++++++++++++
 .../backwardsCompatibility.js                 |  11 ++
 src/js/economyJS.js                           |   8 +-
 src/uncategorized/costsBudget.tw              | 102 ----------------
 src/uncategorized/repBudget.tw                |   2 +-
 10 files changed, 152 insertions(+), 114 deletions(-)
 create mode 100644 src/005-passages/budgetPassages.js
 rename src/{uncategorized => budget}/budget.js (99%)
 create mode 100644 src/budget/costsBudget.js
 delete mode 100644 src/uncategorized/costsBudget.tw

diff --git a/devTools/types/FC/gameState.d.ts b/devTools/types/FC/gameState.d.ts
index b28d13f53d1..1615f94cbd8 100644
--- a/devTools/types/FC/gameState.d.ts
+++ b/devTools/types/FC/gameState.d.ts
@@ -118,7 +118,7 @@ declare namespace FC {
 		activeArcologyIdx?: number;
 
 		passageSwitchHandler?: () => void;
-		showAllEntries?: {
+		showAllEntries: {
 			costsBudget: number;
 			repBudget: number;
 		};
@@ -132,7 +132,7 @@ declare namespace FC {
 		clubSlavesGettingHelp?: number;
 
 		lastWeeksRepErrors?: string;
-		lastWeeksCashErrors?: string;
+		lastWeeksCashErrors: Array<string>;
 
 		arcadeDemandDegResult?: 1 | 2 | 3 | 4 | 5;
 
diff --git a/js/002-config/fc-js-init.js b/js/002-config/fc-js-init.js
index d48f8a99ba7..f96e54356f9 100644
--- a/js/002-config/fc-js-init.js
+++ b/js/002-config/fc-js-init.js
@@ -15,6 +15,7 @@ var App = { }; // eslint-disable-line no-redeclare
 App.Arcology = {};
 App.Arcology.Cell = {};
 App.Art = {};
+App.Budget = {};
 App.Corporate = {};
 App.Data = {};
 App.Data.clothes = new Map();
diff --git a/js/003-data/gameVariableData.js b/js/003-data/gameVariableData.js
index 4f2b71a0163..4a3bb1a7c1e 100644
--- a/js/003-data/gameVariableData.js
+++ b/js/003-data/gameVariableData.js
@@ -413,10 +413,6 @@ App.Data.resetOnNGPlus = {
 
 	reminderEntry: "",
 	reminderWeek: "",
-	lastWeeksCashIncome: {},
-	lastWeeksCashExpenses: {},
-	lastWeeksRepIncome: {},
-	lastWeeksRepExpenses: {},
 	currentRule: {},
 	costs: 0,
 	seeBuilding: 0,
@@ -433,6 +429,14 @@ App.Data.resetOnNGPlus = {
 	DJignoresFlaws: 0,
 	DJnoSex: 0,
 
+	// Budget
+	lastWeeksCashIncome: {},
+	lastWeeksCashExpenses: {},
+	lastWeeksRepIncome: {},
+	lastWeeksRepExpenses: {},
+	showAllEntries: {costsBudget: 0, repBudget: 0},
+	lastWeeksCashErrors: [],
+
 	localEcon: 0,
 	econRate: 0,
 	drugsCost: 0,
diff --git a/src/005-passages/budgetPassages.js b/src/005-passages/budgetPassages.js
new file mode 100644
index 00000000000..09ea968153b
--- /dev/null
+++ b/src/005-passages/budgetPassages.js
@@ -0,0 +1,7 @@
+new App.DomPassage("Costs Budget",
+	() => {
+		V.nextButton = "Back";
+		V.nextLink = "Main";
+		return App.Budget.costs();
+	}, ["jump-to-safe", "jump-from-safe"]
+);
diff --git a/src/uncategorized/budget.js b/src/budget/budget.js
similarity index 99%
rename from src/uncategorized/budget.js
rename to src/budget/budget.js
index 4e5bfcfc018..abd3ede0152 100644
--- a/src/uncategorized/budget.js
+++ b/src/budget/budget.js
@@ -3,7 +3,7 @@
  * @param {"cash"|"rep"} budgetType
  * @returns {HTMLTableElement}
  */
-App.UI.budget = function(budgetType) {
+App.Budget.table = function(budgetType) {
 	let coloredRow = true;
 
 	// Set up object to track calculated displays
@@ -395,10 +395,13 @@ App.UI.budget = function(budgetType) {
 		cell.append("Tracked totals");
 
 		cell = row.insertCell();
+		// Make the total 0 first, otherwise it gets counted as part of the new total
+		V[income].Total = 0;
 		V[income].Total = hashSum(V[income]);
 		cell.append(formatColorDOM(Math.trunc(V[income].Total)));
 
 		cell = row.insertCell();
+		V[expenses].Total = 0;
 		V[expenses].Total = hashSum(V[expenses]);
 		cell.append(formatColorDOM(Math.trunc(V[expenses].Total)));
 
diff --git a/src/budget/costsBudget.js b/src/budget/costsBudget.js
new file mode 100644
index 00000000000..f8a7b4a6720
--- /dev/null
+++ b/src/budget/costsBudget.js
@@ -0,0 +1,114 @@
+/**
+ * Costs Budget Passage
+ * @returns {DocumentFragment}
+ */
+App.Budget.costs = function() {
+	const f = new DocumentFragment();
+
+	App.UI.DOM.appendNewElement("h1", f, "Costs Budget");
+	f.append(intro());
+	if (V.difficultySwitch === 1) {
+		f.append(economy());
+	}
+	f.append(settings());
+
+	// Table of Totals
+	if (!V.lastWeeksCashIncome) {
+		App.UI.DOM.appendNewElement("p", f, "Financial data currently unavailable.");
+	} else {
+		App.UI.DOM.appendNewElement("p", f, App.Budget.table("cash"));
+	}
+
+	errors(f);
+	return f;
+
+	/**
+	 * @returns {HTMLParagraphElement}
+	 */
+	function intro() {
+		return App.UI.DOM.makeElement("p", `Here you can view many of the financial details of your arcology, ${properTitle()}. The detailed list of slaves and their costs (food, hormones) that you may remember can now be found at slave maintenance. Other links will allow you to directly control areas of your arcology to adjust spending to suit your tastes.`, "scene-intro");
+	}
+
+	/**
+	 * @returns {HTMLParagraphElement}
+	 */
+	function economy() {
+		const p = document.createElement("p");
+		App.UI.DOM.appendNewElement("div", p, `The Local Economy score effects some prices in your ecology. The lower the score, the higher the prices. The base score is 100.`, "scene-intro");
+
+		const grid = document.createElement("div");
+		grid.className = "grid-2columns-auto";
+
+		App.UI.DOM.appendNewElement("div", grid, "Global Economy", "cash");
+		if ((V.cheatMode) && (V.cheatModeM)) {
+			const div = document.createElement("div");
+			div.append(App.UI.DOM.makeTextBox(V.economy, v => {
+				V.economy = v;
+				V.cheater = 1;
+			}, true));
+			grid.append(div);
+		} else {
+			App.UI.DOM.appendNewElement("div", grid, String(V.economy));
+		}
+
+		App.UI.DOM.appendNewElement("div", grid, "Local Economy", "cash");
+		if ((V.cheatMode) && (V.cheatModeM)) {
+			const div = document.createElement("div");
+			div.append(App.UI.DOM.makeTextBox(V.localEcon, v => {
+				V.localEcon = v;
+				V.cheater = 1;
+			}, true));
+			grid.append(div);
+		} else {
+			App.UI.DOM.appendNewElement("div", grid, String(V.localEcon));
+		}
+
+		p.append(grid);
+
+		const r = [];
+		r.push("The current score is");
+		if (V.localEcon > 100) {
+			let _econPercent = Math.trunc(1000 - 100000 / V.localEcon) / 10;
+			r.push(`reducing prices by <span class="cash inc">${_econPercent}%.</span>`);
+		} else if (V.localEcon === 100) {
+			r.push("equal to the base score. There are no price modifications.");
+		} else {
+			let _econPercent = Math.trunc(100000 / V.localEcon - 1000) / 10;
+			r.push(`increasing prices by <span class="cash dec">${_econPercent}%.</span>`);
+		}
+		$(p).append(...App.Events.spaceSentences(r));
+
+		return p;
+	}
+
+	/**
+	 * @returns {HTMLParagraphElement}
+	 */
+	function settings() {
+		const p = document.createElement("p");
+		App.UI.DOM.appendNewElement("div", p, "Your weekly costs are as follows:", "detail");
+
+		let _options = new App.UI.OptionsGroup();
+		_options.addOption("", "costsBudget", V.showAllEntries)
+			.addValue("Normal", 0).on().addValue("Show Empty Entries", 1);
+		p.append(_options.render());
+
+		return p;
+	}
+
+	/**
+	 * @param {DocumentFragment} container
+	 */
+	function errors(container) {
+		if (V.lastWeeksCashErrors.length > 0) {
+			const p = document.createElement("p");
+			p.append(App.UI.DOM.passageLink("Reset", "Costs Budget",
+				() => { V.lastWeeksCashErrors = []; }));
+			App.UI.DOM.appendNewElement("div", p, "Errors:", "error");
+			for (const error of V.lastWeeksCashErrors) {
+				App.UI.DOM.appendNewElement("div", p, error, "error");
+			}
+			container.append(p);
+		}
+	}
+};
diff --git a/src/data/backwardsCompatibility/backwardsCompatibility.js b/src/data/backwardsCompatibility/backwardsCompatibility.js
index 5973ea3894d..0eb51c3bffd 100644
--- a/src/data/backwardsCompatibility/backwardsCompatibility.js
+++ b/src/data/backwardsCompatibility/backwardsCompatibility.js
@@ -1308,6 +1308,17 @@ App.Update.globalVariables = function(node) {
 	V.experimental.raGrowthExpr = V.experimental.raGrowthExpr || 0;
 	V.experimental.reportMissingClothing = V.experimental.reportMissingClothing || 0;
 
+	// Budget
+	V.showAllEntries.costsBudget = V.showAllEntries.costsBudget || 0;
+	V.showAllEntries.repBudget = V.showAllEntries.repBudget || 0;
+	if (typeof V.lastWeeksCashErrors === "string") {
+		if (V.lastWeeksCashErrors === "Errors: ") {
+			V.lastWeeksCashErrors = [];
+		} else {
+			V.lastWeeksCashErrors = [V.lastWeeksCashErrors];
+		}
+	}
+
 	node.append(`Done!`);
 };
 
diff --git a/src/js/economyJS.js b/src/js/economyJS.js
index 85e2b2de7de..70ad99e44a1 100644
--- a/src/js/economyJS.js
+++ b/src/js/economyJS.js
@@ -2224,7 +2224,7 @@ The third category, the "slave slot" is completely optional. Sometimes you just
  */
 globalThis.cashX = function(cost, what, who) {
 	if (!Number.isFinite(cost)) {
-		V.lastWeeksCashErrors += `Expected a finite number for ${what}, but got ${cost}<br>`;
+		V.lastWeeksCashErrors.push(`Expected a finite number for ${what}, but got ${cost}`);
 		return 0;
 	}
 
@@ -2240,7 +2240,7 @@ globalThis.cashX = function(cost, what, who) {
 		if (typeof V.lastWeeksCashIncome[what] !== 'undefined') {
 			V.lastWeeksCashIncome[what] += cost;
 		} else {
-			V.lastWeeksCashErrors += `Unknown place "${what}" gained you ${cost}<br>`;
+			V.lastWeeksCashErrors.push(`Unknown place "${what}" gained you ${cost}`);
 		}
 
 		// record the slave, if available
@@ -2256,7 +2256,7 @@ globalThis.cashX = function(cost, what, who) {
 		if (typeof V.lastWeeksCashExpenses[what] !== 'undefined') {
 			V.lastWeeksCashExpenses[what] += cost;
 		} else {
-			V.lastWeeksCashErrors += `Unknown place "${what}" charged you ${cost}<br>`;
+			V.lastWeeksCashErrors.push(`Unknown place "${what}" charged you ${cost}`);
 		}
 
 		// record the slave, if available
@@ -2551,7 +2551,7 @@ globalThis.ownershipReport = function(short) {
 globalThis.setupLastWeeksCash = function() {
 	V.lastWeeksCashIncome = new App.Data.Records.LastWeeksCash();
 	V.lastWeeksCashExpenses = new App.Data.Records.LastWeeksCash();
-	V.lastWeeksCashErrors = "Errors: ";
+	V.lastWeeksCashErrors = [];
 };
 
 globalThis.setupLastWeeksRep = function() {
diff --git a/src/uncategorized/costsBudget.tw b/src/uncategorized/costsBudget.tw
deleted file mode 100644
index 79ea4f02fc5..00000000000
--- a/src/uncategorized/costsBudget.tw
+++ /dev/null
@@ -1,102 +0,0 @@
-:: Costs Budget [nobr jump-to-safe jump-from-safe]
-
-<<set $nextButton = "Back to Main", $nextLink = "Main", _arcologyCosts = 0>>
-
-<<if def $lastWeeksCashIncome>>
-	<<set $lastWeeksCashIncome.Total = 0>>
-	<<set $lastWeeksCashExpenses.Total = 0>>
-<</if>>
-
-<<if ndef $showAllEntries>>
-	<<set $showAllEntries = {costsBudget: 0, repBudget: 0}>>
-<</if>>
-
-<div class="scene-intro">
-	Here you can view many of the financial details of your arcology, <<= properTitle()>>. The detailed list of slaves and their costs (food, hormones) that you may remember can now be found in the "Slave maintenance" link. Other links will allow you to directly control areas of your arcology to adjust spending to suit your tastes.
-</div>
-
-<br style="clear:both"><<if $lineSeparations == 0>><br><<else>><hr style="margin:0"><</if>>
-
-<<if $difficultySwitch == 1>>
-	<div class="scene-intro">
-		The Local Economy score effects some prices in your ecology. The lower the score, the higher the prices. The base score is ''100''.
-	</div>
-	<span id="economy">
-		<span class="yellowgreen">
-			Global Economy
-		</span>
-	| <<print $economy>>
-	</span>
-	<<if ($cheatMode) && ($cheatModeM)>>
-		<<set _tEconomy = $economy>>
-		<<textbox "_tEconomy" _tEconomy>>
-		<<link "Apply">>
-		<<set $economy = Math.trunc(Number(_tEconomy)) || $economy, $cheater = 1>>
-		<<replace "#economy">>
-		<span class="yellowgreen">
-			Global Economy
-		</span>
-		| <<print $economy>>
-		<</replace>>
-		<</link>>
-	<</if>>
-	<br>
-
-	<span id="localEcon">
-		<span class="yellowgreen">
-			Local Economy
-		</span>
-	| <<print $localEcon>>
-	</span>
-	<<if ($cheatMode) && ($cheatModeM)>>
-		<<set _tLocalEcon = $localEcon>>
-		<<textbox "_tLocalEcon" _tLocalEcon>>
-		<<link "Apply">>
-		<<set $localEcon = Math.trunc(Number(_tLocalEcon)) || $localEcon, $cheater = 1>>
-		<<replace "#localEcon">>
-		<span class="yellowgreen">
-			Local Economy
-		</span>
-		| <<print $localEcon>>
-		<</replace>>
-		<</link>>
-	<</if>>
-
-	<br> The current score is
-	<<if $localEcon > 100>>
-		<<set _econPercent = Math.trunc(1000-100000/$localEcon)/10>>
-		reducing prices by <span class="cash inc">''<<print _econPercent>>%.''</span>
-	<<elseif $localEcon == 100>>
-		equal to the base score. There are no price modifications.
-	<<else>>
-		<<set _econPercent = Math.trunc(100000/$localEcon-1000)/10>>
-		increasing prices by <span class="cash dec">''<<print _econPercent>>%.''</span>
-	<</if>>
-	<br style="clear:both"><<if $lineSeparations == 0>><br><br><<else>><hr style="margin:0"><</if>>
-<</if>>
-
-<span class="detail">Your weekly costs are as follows:</span>
-<<set _options = new App.UI.OptionsGroup()>>
-<<run _options.addOption("", "costsBudget", $showAllEntries)
-.addValue("Normal", 0).on().addValue("Show Empty Entries", 1)>>
-<<includeDOM _options.render()>>
-
-/* Table of Totals */
-<p>
-	<<if ndef $lastWeeksCashIncome>>
-		Financial data currently unavailable.
-	<<else>>
-		<<includeDOM App.UI.budget("cash")>>
-	<</if>>
-</p>
-
-<<if ndef $lastWeeksCashErrors>>
-	<<set $lastWeeksCashErrors = "Errors: ">>
-<</if>>
-
-<<if $lastWeeksCashErrors !== "Errors: ">>
-	<<link "Reset">>
-		<<set $lastWeeksCashErrors = "Errors: ">>
-	<</link>><br>
-	@@.red;<<print $lastWeeksCashErrors>>@@
-<</if>>
diff --git a/src/uncategorized/repBudget.tw b/src/uncategorized/repBudget.tw
index 263a7b79e98..40aa3cde368 100644
--- a/src/uncategorized/repBudget.tw
+++ b/src/uncategorized/repBudget.tw
@@ -26,7 +26,7 @@
 <<if ndef $lastWeeksRepIncome>>
 	Financial data currently unavailable.
 <<else>>
-	<<includeDOM App.UI.budget("rep")>>
+	<<includeDOM App.Budget.table("rep")>>
 <</if>>
 
 <<if ndef $lastWeeksRepErrors>>
-- 
GitLab