From adeb6461c25621798c7a481e56de44da7d059ca1 Mon Sep 17 00:00:00 2001
From: Blank_Alt <12406-Blank_Alt@users.noreply.gitgud.io>
Date: Sat, 3 Jul 2021 10:51:01 +0000
Subject: [PATCH] SecExp-Fixes/Cleanup

---
 .../SecExp/buildings/riotControlCenter.tw     |   1 +
 src/Mods/SecExp/edicts.tw                     |   1 +
 src/Mods/SecExp/events/attackReport.js        | 232 ++++++++----------
 src/Mods/SecExp/events/rebellionReport.js     |   2 +-
 src/endWeek/economics/neighborsDevelopment.js |   8 +-
 src/endWeek/economics/persBusiness.js         |   8 +-
 src/events/RE/reArcologyInspection.js         |   2 +-
 src/events/scheduled/sePCBirthday.js          |   8 +-
 src/gui/storyCaption.js                       |   2 +-
 9 files changed, 122 insertions(+), 142 deletions(-)

diff --git a/src/Mods/SecExp/buildings/riotControlCenter.tw b/src/Mods/SecExp/buildings/riotControlCenter.tw
index 6ab03a6c31d..5f875d557e0 100644
--- a/src/Mods/SecExp/buildings/riotControlCenter.tw
+++ b/src/Mods/SecExp/buildings/riotControlCenter.tw
@@ -98,6 +98,7 @@ The riot control center opens its guarded doors to you. The great chamber inside
 		<br><<link "Deploy the unit against slaves rebel leaders">>
 			<<if $SecExp.buildings.riotCenter.upgrades.rapidUnitCost == 0>>
 				<<set $SecExp.core.authority -= 1000 + 50 * $SecExp.buildings.riotCenter.upgrades.rapidUnit>>
+				<<set $SecExp.core.authority = Math.clamp($SecExp.core.authority, 0, 20000)>>
 			<<else>>
 				<<run repX(forceNeg(1000 + 50 * $SecExp.buildings.riotCenter.upgrades.rapidUnit), "war")>>
 			<</if>>
diff --git a/src/Mods/SecExp/edicts.tw b/src/Mods/SecExp/edicts.tw
index 810c785926a..41fcdf1a6da 100644
--- a/src/Mods/SecExp/edicts.tw
+++ b/src/Mods/SecExp/edicts.tw
@@ -9,6 +9,7 @@
 <<if $SecExp.battles.victories + $SecExp.battles.losses > 0 || $SecExp.rebellions.victories + $SecExp.rebellions.losses > 0 || $mercenaries > 0>>
 	<button class="tab-links" onclick="App.UI.tabBar.openTab(event, 'Military')" id="tab Military">Military</button>
 <</if>>
+<<set $SecExp.core.authority = Math.clamp($SecExp.core.authority, 0, 20000)>>
 
 <div id="Society" class="tab-content">
 	<div class="content">
diff --git a/src/Mods/SecExp/events/attackReport.js b/src/Mods/SecExp/events/attackReport.js
index ef06c5fb6fa..659aa823efe 100644
--- a/src/Mods/SecExp/events/attackReport.js
+++ b/src/Mods/SecExp/events/attackReport.js
@@ -2,6 +2,67 @@ App.Events.attackReport = function() {
 	V.nextButton = "Continue";
 	V.nextLink = "Scheduled Event";
 	V.encyclopedia = "Battles";
+	const casualtiesReport = function(type, loss, squad=null) {
+		const isSpecial = squad && App.SecExp.unit.list().slice(1).includes(type);
+		let r = [];
+		if (loss <= 0) {
+			r.push(`No`);
+		} else if (loss <= (isSpecial ? (squad.troops * 0.2) : 10)) {
+			r.push(`Light`);
+		} else if (loss <= (isSpecial ? (squad.troops * 0.4) : 30)) {
+			r.push(`Moderate`);
+		} else if (loss <= (isSpecial ? (squad.troops * 0.6) : 60)) {
+			r.push(`Heavy`);
+		} else {
+			r.push(`Catastrophic`);
+		}
+		r.push(`casualties suffered.`);
+		if (App.SecExp.unit.list().includes(type)) {
+			if (squad.troops <= 0) {
+				squad.active = 0;
+				r.push(`Unfortunately the losses they took were simply too great, their effective combatants are in so small number you can no longer call them a deployable unit.`);
+				if (type === "bots") {
+					r.push(`It will take quite the investment to rebuild them.`);
+				} else {
+					r.push(`The remnants will be sent home honored as veterans or reorganized in a new unit.`);
+				}
+			} else if (squad.troops <= 10) {
+				r.push(`The unit has very few operatives left, it risks complete annihilation if deployed again.`);
+			}
+		}
+		return r.join(" ");
+	};
+	function loopThroughUnits(units, type) {
+		for (const unit of units) {
+			if (App.SecExp.unit.isDeployed(unit)) {
+				if (V.SecExp.war.losses > 0) {
+					loss = lossesList.pluck();
+					loss = Math.clamp(loss, 0, unit.troops);
+				}
+
+				const r = [`${type !== "bots" ? `${unit.platoonName}` : `Security drones`}: ${casualtiesReport(type, loss, unit)}`];
+				if (type !== "bots") {
+					unit.battlesFought++;
+					if (loss > 0) {
+						const med = Math.round(Math.clamp(loss * unit.medics * 0.25, 1, loss));
+						if (unit.medics === 1) {
+							r.push(`Some men were saved by their medics.`);
+						}
+						unit.troops -= Math.trunc(Math.clamp(loss - med, 0, unit.maxTroops));
+						V.SecExp.units[type].dead += Math.trunc(loss - med);
+					}
+					if (unit.training < 100 && random(1, 100) > 60) {
+						r.push(`Experience has increased.`);
+						unit.training += random(5, 15) + (majorBattle ? 1 : 0) * random(5, 15);
+					}
+				} else if (type === "bots" && loss > 0) {
+					unit.troops -= loss;
+				}
+				App.Events.addNode(node, r, "div");
+			}
+		}
+	}
+
 	const node = new DocumentFragment();
 	let r = [];
 
@@ -12,6 +73,7 @@ App.Events.attackReport = function() {
 	V.SecExp.core.totalKills += V.SecExp.war.attacker.losses;
 	V.SecExp.war.losses = Math.trunc(V.SecExp.war.losses);
 	let loot = 0;
+	let loss = 0;
 	let captives;
 	const lossesList = [];
 
@@ -477,6 +539,7 @@ App.Events.attackReport = function() {
 			repX(forceNeg(800 * majorBattleMod), "war");
 			V.SecExp.core.authority -= 800 * majorBattleMod;
 		}
+		V.SecExp.core.authority = Math.clamp(V.SecExp.core.authority, 0, 20000);
 		App.Events.addParagraph(node, r);
 		r = [];
 		r.push(`Fortunately the arcology survives <span class="yellow">mostly intact,</span> however reports of <span class="red">mass looting and killing of citizens</span> flood your office for a few days.`);
@@ -923,93 +986,54 @@ App.Events.attackReport = function() {
 
 	function unitsBattleReport() {
 		const el = document.createElement("div");
-		if (V.SecExp.war.losses === 0) {
-			if (V.SF.Toggle && V.SF.Active >= 1 && V.SecExp.war.deploySF) {
-				App.UI.DOM.appendNewElement("div", el, `${num(V.SF.ArmySize)} soldiers from ${V.SF.Lower} joined the battle: no casualties suffered`);
-			}
-			const noCasualties = function(units, type) {
-				for (const unit of units) {
-					if (App.SecExp.unit.isDeployed(unit)) {
-						const r = [`${type !== 'bots' ? `${unit.platoonName}` : `Security Drones`} : no casualties.`];
-						if (type !== "bots") {
-							unit.battlesFought++;
-							if (unit.training < 100) {
-								if (random(1, 100) > 60) {
-									r.push(`Experience has increased.`);
-									unit.training += random(5, 15) + (majorBattle ? 1 : 0) * random(5, 15);
-								}
-							}
-						}
-						App.Events.addNode(el, r, "div");
-					}
-				}
-			};
-			for (const unitClass of App.SecExp.unit.list()) {
-				if (App.SecExp.battle.deployedUnits(unitClass) >= 1) {
-					if (unitClass !== 'bots') {
-						noCasualties(V.SecExp.units[unitClass].squads, unitClass);
+		if (V.SecExp.war.losses >= 0) {
+			if (V.SecExp.war.losses > 0) {
+				// if the losses are more than zero
+				// generates a list of randomized losses, from which each unit picks one at random
+				let losses = V.SecExp.war.losses;
+				const averageLosses = Math.trunc(losses / App.SecExp.battle.deployedUnits());
+				let assignedLosses;
+				for (let i = 0; i < App.SecExp.battle.deployedUnits(); i++) {
+					assignedLosses = Math.trunc(Math.clamp(averageLosses + random(-5, 5), 0, 100));
+					if (assignedLosses > losses) {
+						assignedLosses = losses;
+						losses = 0;
 					} else {
-						noCasualties([V.SecExp.units.bots], unitClass);
+						losses -= assignedLosses;
 					}
+					lossesList.push(assignedLosses);
 				}
-			}
-		} else if (V.SecExp.war.losses > 0) {
-			// if the losses are more than zero
-			// generates a list of randomized losses, from which each unit picks one at random
-			let losses = V.SecExp.war.losses;
-			const averageLosses = Math.trunc(losses / App.SecExp.battle.deployedUnits());
-			let assignedLosses;
-			for (let i = 0; i < App.SecExp.battle.deployedUnits(); i++) {
-				assignedLosses = Math.trunc(Math.clamp(averageLosses + random(-5, 5), 0, 100));
-				if (assignedLosses > losses) {
-					assignedLosses = losses;
-					losses = 0;
-				} else {
-					losses -= assignedLosses;
+				if (losses > 0) {
+					lossesList[random(lossesList.length - 1)] += losses;
 				}
-				lossesList.push(assignedLosses);
-			}
-			if (losses > 0) {
-				lossesList[random(lossesList.length - 1)] += losses;
-			}
-			lossesList.shuffle();
+				lossesList.shuffle();
 
-			// sanity check for losses
-			let count = 0;
-			for (let i = 0; i < lossesList.length; i++) {
-				if (!Number.isInteger(lossesList[i])) {
-					lossesList[i] = 0;
+				// sanity check for losses
+				let count = 0;
+				for (let i = 0; i < lossesList.length; i++) {
+					if (!Number.isInteger(lossesList[i])) {
+						lossesList[i] = 0;
+					}
+					count += lossesList[i];
+				}
+				if (count < V.SecExp.war.losses) {
+					const rand = random(lossesList.length - 1);
+					lossesList[rand] += V.SecExp.war.losses - count;
+				} else if (count > V.SecExp.war.losses) {
+					const diff = count - V.SecExp.war.losses;
+					const rand = random(lossesList.length - 1);
+					lossesList[rand] = Math.clamp(lossesList[rand] - diff, 0, 100);
 				}
-				count += lossesList[i];
-			}
-			if (count < V.SecExp.war.losses) {
-				const rand = random(lossesList.length - 1);
-				lossesList[rand] += V.SecExp.war.losses - count;
-			} else if (count > V.SecExp.war.losses) {
-				const diff = count - V.SecExp.war.losses;
-				const rand = random(lossesList.length - 1);
-				lossesList[rand] = Math.clamp(lossesList[rand] - diff, 0, 100);
 			}
 
-			// assigns the losses and notify the player
 			if (V.SF.Toggle && V.SF.Active >= 1 && V.SecExp.war.deploySF) {
-				let loss = lossesList.pluck();
-				loss = Math.clamp(loss, 0, V.SF.ArmySize);
-				const r = [`${num(V.SF.ArmySize)} soldiers from ${V.SF.Lower} joined the battle:`];
-				if (loss <= 0) {
-					r.push(`no casualties`);
-				} else if (loss <= 10) {
-					r.push(`light casualties`);
-				} else if (loss <= 30) {
-					r.push(`moderate casualties`);
-				} else if (loss <= 60) {
-					r.push(`heavy casualties`);
-				} else {
-					r.push(`catastrophic casualties`);
+				if (V.SecExp.war.losses > 0) {
+					loss = lossesList.pluck();
+					loss = Math.clamp(loss, 0, V.SF.ArmySize);
+					V.SF.ArmySize -= loss;
 				}
-				r.push(`suffered.`);
-				V.SF.ArmySize -= loss;
-				App.Events.addNode(el, r, "div");
+				App.UI.DOM.appendNewElement("div", el, `${num(V.SF.ArmySize)} soldiers from ${V.SF.Lower} joined the battle: casualtiesReport(type, loss)`);
+
 			}
 			for (const unitClass of App.SecExp.unit.list()) {
 				if (App.SecExp.battle.deployedUnits(unitClass) >= 1) {
@@ -1018,62 +1042,12 @@ App.Events.attackReport = function() {
 					} else {
 						loopThroughUnits([V.SecExp.units.bots], unitClass);
 					}
-			}
+				}
 			}
 		} else {
 			App.UI.DOM.appendNewElement("div", el, `Error: losses are a negative number or NaN`, "red");
 		}// closes check for more than zero casualties
-		return el;
-
-		function loopThroughUnits(units, type) {
-			for (const unit of units) {
-				if (App.SecExp.unit.isDeployed(unit)) {
-					let loss = lossesList.pluck();
-					loss = Math.clamp(loss, 0, unit.troops);
-					if (type !== "bots") {
-						unit.battlesFought++;
-					} else {
-						unit.troops -= loss;
-					}
 
-					const r = [`${type !== "bots" ? `${unit.platoonName}` : `Security drones`}:`];
-					if (loss <= 0) {
-						r.push(`no casualties`);
-					} else if (loss <= unit.troops * 0.2) {
-						r.push(`light casualties`);
-					} else if (loss <= unit.troops * 0.4) {
-						r.push(`moderate casualties`);
-					} else if (loss <= unit.troops * 0.6) {
-						r.push(`heavy casualties`);
-					} else {
-						r.push(`catastrophic casualties`);
-					}
-					r.push(`suffered.`);
-					if (type !== "bots") {
-					const med = Math.round(Math.clamp(loss * unit.medics * 0.25, 1, loss));
-					if (unit.medics === 1 && loss > 0) {
-						r.push(`Some men were saved by their medics.`);
-					}
-					unit.troops -= Math.trunc(Math.clamp(loss - med, 0, unit.maxTroops));
-					V.SecExp.units.militia.dead += Math.trunc(loss - med);
-						if (unit.training < 100 && random(1, 100) > 60) {
-							r.push(`Experience has increased.`);
-							unit.training += random(5, 15) + (majorBattle ? 1 : 0) * random(5, 15);
-						}
-					}
-					App.Events.addNode(el, r, "div");
-					if (unit.troops <= 5) {
-						unit.active = 0;
-						if (type !== "bots") {
-							App.UI.DOM.appendNewElement("div", el, `Unfortunately the losses they took were simply too great, their effective combatants are in so small number you can no longer call them a deployable unit. The remnants will be sent home honored as veterans or reorganized in a new unit.`);
-						} else {
-							App.UI.DOM.appendNewElement("div", el, `Unfortunately the losses they took were simply too great, their effective combatants are in so small number you can no longer call them a deployable unit. It will take quite the investment to rebuild them.`);
-						}
-					} else if (unit.troops <= 10) {
-						App.UI.DOM.appendNewElement("div", el, `The unit has very few operatives left, it risks complete annihilation if deployed again.`);
-					}
-				}
-			}
-		}
+		return el;
 	}
 };
diff --git a/src/Mods/SecExp/events/rebellionReport.js b/src/Mods/SecExp/events/rebellionReport.js
index 14e108cc7c0..663cea8f7f5 100644
--- a/src/Mods/SecExp/events/rebellionReport.js
+++ b/src/Mods/SecExp/events/rebellionReport.js
@@ -196,7 +196,6 @@ App.Events.rebellionReport = function() {
 		const node = new DocumentFragment();
 		const r = [];
 		const rebels = {ID: [], names: []};
-		console.log('rebels:', rebels);
 
 		const Dissolve = function() {
 			App.SecExp.unit.unitFree(unit).add(manpower);
@@ -394,6 +393,7 @@ App.Events.rebellionReport = function() {
 		lostSlaves = Math.trunc((V.SecExp.war.attacker.troops - V.SecExp.war.attacker.losses) * 0.8);
 		App.SecExp.slavesDamaged(lostSlaves);
 	}
+	V.SecExp.core.authority = Math.clamp(V.SecExp.core.authority, 0, 20000);
 
 	if (result !== -1) {
 		if (V.SecExp.war.engageRule === 0) {
diff --git a/src/endWeek/economics/neighborsDevelopment.js b/src/endWeek/economics/neighborsDevelopment.js
index ccbc6650db4..e7c56e72c9d 100644
--- a/src/endWeek/economics/neighborsDevelopment.js
+++ b/src/endWeek/economics/neighborsDevelopment.js
@@ -330,8 +330,8 @@ App.EndWeek.neighborsDevelopment = function() {
 					redHanded = 1;
 					repX(forceNeg(random(100, 200)), "war");
 					if (V.secExpEnabled > 0) {
-						V.SecExp.core.authority -= random(100, 500) * V.arcologies[0].CyberEconomic;
-						V.SecExp.core.crimeLow += random(10, 25);
+						V.SecExp.core.authority = Math.clamp(V.SecExp.core.authority - random(100, 500) * V.arcologies[0].CyberEconomic, 0, 20000);
+						V.SecExp.core.crimeLow = Math.clamp(V.SecExp.core.crimeLow + random(10, 25), 0, 100);
 					}
 					V.arcologies[0].prosperity = Math.clamp(V.arcologies[0].prosperity, 1, V.AProsperityCap);
 				}
@@ -377,8 +377,8 @@ App.EndWeek.neighborsDevelopment = function() {
 					redHanded = 1;
 					repX(forceNeg(random(100, 200)), "war");
 					if (V.secExpEnabled > 0) {
-						V.SecExp.core.authority -= random(100, 500) * V.arcologies[0].CyberReputation;
-						V.SecExp.core.crimeLow += random(10, 25);
+						V.SecExp.core.authority = Math.clamp(V.SecExp.core.authority - random(100, 500) * V.arcologies[0].CyberReputation, 0, 20000);
+						V.SecExp.core.crimeLow = Math.clamp(V.SecExp.core.crimeLow + random(10, 25), 0, 100);
 					}
 					V.arcologies[0].prosperity = Math.clamp(V.arcologies[0].prosperity, 1, 300);
 				}
diff --git a/src/endWeek/economics/persBusiness.js b/src/endWeek/economics/persBusiness.js
index 7adcd0e5f95..cd8e407a987 100644
--- a/src/endWeek/economics/persBusiness.js
+++ b/src/endWeek/economics/persBusiness.js
@@ -784,9 +784,9 @@ App.EndWeek.personalBusiness = function() {
 					if (V.secExpEnabled > 0) {
 						X = 1;
 						r.push(`<span class="red">authority,</span>`);
-						V.SecExp.core.authority -= random(100, 500);
+						V.SecExp.core.authority = Math.clamp(V.SecExp.core.authority - random(100, 500), 0, 20000);
 						r.push(`<span class="red">crime rate</span>`);
-						V.SecExp.core.crimeLow += random(10, 25);
+						V.SecExp.core.crimeLow = Math.clamp(V.SecExp.core.crimeLow + random(10, 25), 0, 100);
 						r.push(`and`);
 					}
 					r.push(`<span class="red">reputation</span>`);
@@ -876,14 +876,14 @@ App.EndWeek.personalBusiness = function() {
 				r.push(`You are selling the data collected by your security department, which earns a discreet sum of <span class="yellowgreen">${cashFormat(dataGain)}.</span>`);
 				cashX(dataGain, "personalBusiness");
 				r.push(`Many of your citizens are not enthusiastic of this however, <span class="red">damaging your authority.</span>`);
-				V.SecExp.core.authority -= 50;
+				V.SecExp.core.authority = Math.clamp(V.SecExp.core.authority - 50, 0, 20000);
 			}
 		}
 
 		if (V.SecExp.buildings.propHub && V.SecExp.buildings.propHub.upgrades.marketInfiltration > 0) {
 			const blackMarket = random(7000, 8000);
 			r.push(`Your secret service makes use of black markets and illegal streams of goods to make a profit, making you <span class="yellowgreen">${cashFormat(blackMarket)}.</span> This however allows <span class="red">crime to flourish</span> in the underbelly of the arcology.`);
-			V.SecExp.core.crimeLow += random(1, 3);
+			V.SecExp.core.crimeLow = Math.clamp(V.SecExp.core.crimeLow + random(1, 3), 0, 100);
 			cashX(blackMarket, "personalBusiness");
 		}
 
diff --git a/src/events/RE/reArcologyInspection.js b/src/events/RE/reArcologyInspection.js
index 1f87c964457..66907be72d6 100644
--- a/src/events/RE/reArcologyInspection.js
+++ b/src/events/RE/reArcologyInspection.js
@@ -461,7 +461,7 @@ App.Events.REArcologyInspection = class REArcologyInspection extends App.Events.
 			t.push(`Every arcology has its share of dissidents; it's one of the inevitable results of the semi-anarchic nature of the Free Cities. You have an opportunity to build your own <span class="reputation inc">reputation</span> and also help secure ${agent ? `${agent.slaveName}'s` : `your indirect`} <span class="darkviolet">authority</span> over ${arcology.name}, and it would be a shame to let it pass unheeded. You spend the rest of the day helping pass judgement, and even administer a few whippings and assfuckings yourself, just for good measure.`);
 			repX(100, "event");
 			if (V.secExpEnabled) {
-				V.SecExp.core.authority += 250;
+				V.SecExp.core.authority = Math.clamp(V.SecExp.core.authority + 250, 0, 20000);
 			}
 			return t;
 		}
diff --git a/src/events/scheduled/sePCBirthday.js b/src/events/scheduled/sePCBirthday.js
index 4e4231efde9..5af0807e12d 100644
--- a/src/events/scheduled/sePCBirthday.js
+++ b/src/events/scheduled/sePCBirthday.js
@@ -245,11 +245,15 @@ App.Events.pcBirthday = (function(events) {
 			switch (eventData.attire) {
 				case "formal":
 					repX(100, "event");
-					if (V.secExpEnabled) { V.SecExp.core.authority += 300; }
+					if (V.secExpEnabled) {
+						V.SecExp.core.authority = Math.clamp(V.SecExp.core.authority + 300, 0, 20000);
+					}
 					break;
 				case "casual":
 					repX(300, "event");
-					if (V.secExpEnabled) { V.SecExp.core.authority += 100; }
+					if (V.secExpEnabled) {
+						V.SecExp.core.authority = Math.clamp(V.SecExp.core.authority + 100, 0, 20000);
+					}
 					break;
 			}
 		},
diff --git a/src/gui/storyCaption.js b/src/gui/storyCaption.js
index a2ff2f9a9a8..0b39cf7d009 100644
--- a/src/gui/storyCaption.js
+++ b/src/gui/storyCaption.js
@@ -410,6 +410,7 @@ App.UI.storyCaption = function() {
 	}
 
 	function crime() {
+		V.SecExp.core.crimeLow = Math.clamp(Math.trunc(crime), 0, 100);
 		const div = document.createElement("div");
 		App.UI.DOM.appendNewElement("span", div, "Crime", "orangered");
 		div.append(" | ");
@@ -417,7 +418,6 @@ App.UI.storyCaption = function() {
 
 		if (showCheats()) {
 			div.append(App.UI.DOM.makeTextBox(V.SecExp.core.crimeLow, crime => {
-				V.SecExp.core.crimeLow = Math.clamp(Math.trunc(crime), 0, 100);
 				V.cheater = 1;
 				App.Utils.scheduleSidebarRefresh();
 			}, true));
-- 
GitLab