From 42af1c44dd697069af55427f0f2660c3dbdc7ac4 Mon Sep 17 00:00:00 2001
From: Svornost <11434-svornost@users.noreply.gitgud.io>
Date: Sun, 28 Jun 2020 16:02:09 -0700
Subject: [PATCH] 1. Give SF intervention forces a real SecExp unit object like
 all the others 2. Remove battleWidgets.js and the SFatk, SFdef, SFhp, and
 carriableSoldiers globals (those values are now accessed from the unit
 interface) 3. Reduce code duplication a bit in the SecExp battle reports 4.
 Fix some cases of fractional soldiers fighting in rebellions.

---
 js/003-data/gameVariableData.js               |   4 -
 .../SecExp/SecExpBackwardCompatibility.tw     |  12 -
 src/Mods/SecExp/attackHandler.tw              |  11 +-
 src/Mods/SecExp/attackReport.tw               |  81 +--
 src/Mods/SecExp/js/Unit.js                    |  38 +-
 src/Mods/SecExp/js/secExp.js                  |   4 +-
 src/Mods/SecExp/rebellionGenerator.tw         |   8 +-
 src/Mods/SecExp/rebellionHandler.tw           |  11 +-
 src/Mods/SecExp/unitsRebellionReport.tw       | 569 ++++++------------
 src/Mods/SecExp/widgets/battleWidgets.js      |  38 --
 10 files changed, 266 insertions(+), 510 deletions(-)
 delete mode 100644 src/Mods/SecExp/widgets/battleWidgets.js

diff --git a/js/003-data/gameVariableData.js b/js/003-data/gameVariableData.js
index 5abaac8fa43..2e263adb019 100644
--- a/js/003-data/gameVariableData.js
+++ b/js/003-data/gameVariableData.js
@@ -516,10 +516,6 @@ App.Data.resetOnNGPlus = {
 	expectedEquip: 0,
 	estimatedMen: 0,
 	SFIntervention: 0,
-	carriableSoldiers: 0,
-	SFatk: 0,
-	SFdef: 0,
-	SFhp: 0,
 	rebellingID: [],
 	saveValid: 0,
 	lastSelection: [],
diff --git a/src/Mods/SecExp/SecExpBackwardCompatibility.tw b/src/Mods/SecExp/SecExpBackwardCompatibility.tw
index 9629e43a9c7..08918c7a8be 100644
--- a/src/Mods/SecExp/SecExpBackwardCompatibility.tw
+++ b/src/Mods/SecExp/SecExpBackwardCompatibility.tw
@@ -590,18 +590,6 @@
 <<if ndef $SFIntervention>>
 	<<set $SFIntervention = 0>>
 <</if>>
-<<if ndef $carriableSoldiers>>
-	<<set $carriableSoldiers = 0>>
-<</if>>
-<<if ndef $SFatk>>
-	<<set $SFatk = 0>>
-<</if>>
-<<if ndef $SFdef>>
-	<<set $SFdef = 0>>
-<</if>>
-<<if ndef $SFhp>>
-	<<set $SFhp = 0>>
-<</if>>
 <<if ndef $rebellingID>>
 	<<set $rebellingID = []>>
 <</if>>
diff --git a/src/Mods/SecExp/attackHandler.tw b/src/Mods/SecExp/attackHandler.tw
index 10c846cb6b6..3b5a7102d83 100644
--- a/src/Mods/SecExp/attackHandler.tw
+++ b/src/Mods/SecExp/attackHandler.tw
@@ -903,13 +903,10 @@
 	<</for>>
 
 	<<if $SF.Toggle && $SF.Active >= 1 && $SFIntervention>>
-		<<set $SFatk = 0>>
-		<<set $SFdef = 0>>
-		<<set $SFhp = 0>>
-		<<run calcSFStatistics()>>
-		<<set _attack += $SFatk>>
-		<<set _defense += $SFdef>>
-		<<set _hp += $SFhp>>
+		<<set _unit = App.SecExp.getUnit("SF")>>
+		<<set _attack += _unit.attack>>
+		<<set _defense += _unit.defense>>
+		<<set _hp += _unit.hp>>
 	<</if>>
 
 	/* morale and baseHp calculation */
diff --git a/src/Mods/SecExp/attackReport.tw b/src/Mods/SecExp/attackReport.tw
index af5af2564a5..b89024af670 100644
--- a/src/Mods/SecExp/attackReport.tw
+++ b/src/Mods/SecExp/attackReport.tw
@@ -14,65 +14,38 @@
 <<set _loot = 0>>
 
 /* result */
+<<set $battlesCount++>>
 <<if $majorBattle == 0>>
-	<<set $battlesCount++>>
 	<<set _majorBattleMod = 1>>
-	<<if $battleResult == 3>>
-		<strong>Victory!</strong>
-		<<set $PClossStreak = 0>>
-		<<set $PCvictoryStreak += 1>>
-		<<set $PCvictories++>>
-	<<elseif $battleResult == -3>>
-		<strong>Defeat!</strong>
-		<<set $PClossStreak += 1>>
-		<<set $PCvictoryStreak = 0>>
-		<<set $PClosses++>>
-	<<elseif $battleResult == 2>>
-		<strong>Partial victory!</strong>
-		<<set $PCvictories++>>
-	<<elseif $battleResult == -2>>
-		<strong>Partial defeat!</strong>
-		<<set $PClosses++>>
-	<<elseif $battleResult == -1>>
-		<strong>We surrendered</strong>
-		<<set $PClosses++>>
-	<<elseif $battleResult == 0>>
-		<strong>Failed bribery!</strong>
-		<<set $PClosses++>>
-	<<elseif $battleResult == 1>>
-		<strong>Successful bribery!</strong>
-		<<set $PCvictories++>>
-	<</if>>
 <<else>>
 	<<set _majorBattleMod = 2>>
-	<<set $battlesCount++>>
 	<<set $majorBattlesCount++>>
-	<<if $battleResult == 3>>
-		<strong>Major victory!</strong>
-		<<set $PClossStreak = 0>>
-		<<set $PCvictoryStreak += 1>>
-		<<set $PCvictories++>>
-	<<elseif $battleResult == -3>>
-		<strong>Major Defeat!</strong>
-		<<set $PClossStreak += 1>>
-		<<set $PCvictoryStreak = 0>>
-		<<set $PClosses++>>
-	<<elseif $battleResult == 2>>
-		<strong>Partial victory!</strong>
-		<<set $PCvictories++>>
-	<<elseif $battleResult == -2>>
-		<strong>Partial defeat!</strong>
-		<<set $PClosses++>>
-	<<elseif $battleResult == -1>>
-		<strong>We surrendered</strong>
-		<<set $PClosses++>>
-	<<elseif $battleResult == 0>>
-		<strong>Failed bribery!</strong>
-		<<set $PClosses++>>
-	<<elseif $battleResult == 1>>
-		<strong>Successful bribery!</strong>
-		<<set $PCvictories++>>
-	<</if>>
+<</if>>
+<<if $battleResult == 3>>
+	<strong>Victory!</strong>
+	<<set $PClossStreak = 0>>
+	<<set $PCvictoryStreak += 1>>
+	<<set $PCvictories++>>
+<<elseif $battleResult == -3>>
+	<strong>Defeat!</strong>
+	<<set $PClossStreak += 1>>
+	<<set $PCvictoryStreak = 0>>
+	<<set $PClosses++>>
+<<elseif $battleResult == 2>>
+	<strong>Partial victory!</strong>
+	<<set $PCvictories++>>
+<<elseif $battleResult == -2>>
+	<strong>Partial defeat!</strong>
+	<<set $PClosses++>>
+<<elseif $battleResult == -1>>
+	<strong>We surrendered</strong>
+	<<set $PClosses++>>
+<<elseif $battleResult == 0>>
+	<strong>Failed bribery!</strong>
+	<<set $PClosses++>>
+<<elseif $battleResult == 1>>
+	<strong>Successful bribery!</strong>
+	<<set $PCvictories++>>
 <</if>>
 <hr>
 
diff --git a/src/Mods/SecExp/js/Unit.js b/src/Mods/SecExp/js/Unit.js
index cd6bdfa02dc..db2a025f91a 100644
--- a/src/Mods/SecExp/js/Unit.js
+++ b/src/Mods/SecExp/js/Unit.js
@@ -10,13 +10,15 @@ App.SecExp.generateUnitID = function() {
 };
 
 /** Player unit factory - get a unit based on its type and index
- * @param {string} type - "Bots", "Militia", "Slaves", or "Mercs"
+ * @param {string} type - "Bots", "Militia", "Slaves", "Mercs", or "SF"
  * @param {number} [index] - must be supplied if type is not "Bots"
  * @returns {App.SecExp.Unit}
  */
 App.SecExp.getUnit = function(type, index) {
 	if (type === "Bots") {
 		return new App.SecExp.DroneUnit(V.secBots, App.SecExp.BaseDroneUnit);
+	} else if (type === "SF") {
+		return new App.SecExp.SFUnit();
 	} else if (typeof index !== "number") {
 		throw `Bad index for unit type ${type}: ${index}`;
 	}
@@ -467,6 +469,40 @@ App.SecExp.HumanUnit = class SecExpHumanUnit extends App.SecExp.Unit {
 	}
 };
 
+App.SecExp.troopsFromSF = function() {
+	if (V.slaveRebellion !== 1 || V.citizenRebellion !== 1) {
+		// attack: how many troops can we actually carry?
+		const transportMax = Math.trunc(125 * (V.SF.Squad.GunS + ((V.SF.Squad.AV + V.SF.Squad.TV)/2)));
+		return Math.min(transportMax, V.SF.ArmySize);
+	} else {
+		// rebellion: transport capabilities are irrelevant
+		return V.SF.ArmySize;
+	}
+};
+
+App.SecExp.SFUnit = class SFUnit extends App.SecExp.Unit {
+	constructor() {
+		super(null, App.SecExp.BaseSpecialForcesUnit);
+		this._distancePenalty = (V.slaveRebellion !== 1 || V.citizenRebellion !== 1) ? 0.10 : 0.0;
+	}
+
+	get attack() {
+		// ignores base attack? weird.
+		const attackUpgrades = V.SF.Squad.Armoury + V.SF.Squad.Drugs + V.SF.Squad.AA + V.SF.Squad.AV;
+		return (0.75 - this._distancePenalty) * attackUpgrades;
+	}
+
+	get defense() {
+		// ignores base defense? weird.
+		const defenseUpgrades = V.SF.Squad.Armoury + V.SF.Squad.Drugs + (V.SF.Squad.AA + V.SF.Squad.TA) / 2 + (V.SF.Squad.AV + V.SF.Squad.TV) / 2;
+		return (0.5 - this._distancePenalty) * defenseUpgrades;
+	}
+
+	get hp() {
+		return this._baseUnit.hp * App.SecExp.troopsFromSF();
+	}
+};
+
 App.SecExp.EnemyUnit = class SecExpEnemyUnit extends App.SecExp.Unit {
 	/** @param {FC.SecExp.UnitData} data
 	 * @param {BaseUnit} baseUnit
diff --git a/src/Mods/SecExp/js/secExp.js b/src/Mods/SecExp/js/secExp.js
index 9469924406d..25d6d41a695 100644
--- a/src/Mods/SecExp/js/secExp.js
+++ b/src/Mods/SecExp/js/secExp.js
@@ -211,7 +211,7 @@ App.SecExp.conflict = (function() {
 			}
 			countHumanTroops((u) => u.isDeployed === 1);
 			if (V.SF.Toggle && V.SF.Active >= 1 && V.SFIntervention) {
-				troops += V.carriableSoldiers;
+				troops += App.SecExp.troopsFromSF();
 			}
 		} else {
 			if (V.irregulars > 0) {
@@ -222,7 +222,7 @@ App.SecExp.conflict = (function() {
 			}
 			countHumanTroops((u) => u.active === 1 && V.loyalID.includes(u.ID));
 			if (V.SF.Toggle && V.SF.Active >= 1) {
-				troops += V.carriableSoldiers;
+				troops += App.SecExp.troopsFromSF();
 			}
 		}
 		return troops;
diff --git a/src/Mods/SecExp/rebellionGenerator.tw b/src/Mods/SecExp/rebellionGenerator.tw
index 715923a49dc..cb17b7b6498 100644
--- a/src/Mods/SecExp/rebellionGenerator.tw
+++ b/src/Mods/SecExp/rebellionGenerator.tw
@@ -306,8 +306,8 @@
 	<<set _repFactor = Math.clamp($rep / 20000,0.4,0.6)>>
 	<<set _rebelPercent = 0.3 * _authFactor>>
 	<<set _irregularPercent = 0.2 * _repFactor>>
-	<<set $attackTroops = Math.clamp(Math.trunc($ASlaves * _rebelPercent + random(-100,100)) * _weekMod,50,$ASlaves)>>
-	<<set $irregulars = Math.clamp(Math.trunc($ACitizens * _irregularPercent + random(-100,100)) * _weekMod,50,$ACitizens)>>
+	<<set $attackTroops = Math.clamp(Math.trunc($ASlaves * _rebelPercent * _weekMod) + random(-100,100),50,$ASlaves)>>
+	<<set $irregulars = Math.clamp(Math.trunc($ACitizens * _irregularPercent * _weekMod) + random(-100,100),50,$ACitizens)>>
 	/* calc if units rebel */
 	<<for _i = 0; _i < $slaveUnits.length; _i++>>
 		<<if $slaveUnits[_i].loyalty < 10>>
@@ -376,8 +376,8 @@
 	<<set _repFactor = Math.clamp($rep / 20000,0.4,0.6)>>
 	<<set _rebelPercent = 0.3 * _authFactor>>
 	<<set _irregularPercent = 0.2 * _repFactor>>
-	<<set $attackTroops = Math.clamp(Math.trunc($ACitizens * _rebelPercent + random(-100,100)) * _weekMod,50,$ACitizens)>>
-	<<set $irregulars = Math.clamp(Math.trunc($ACitizens * _irregularPercent + random(-100,100)) * _weekMod,50,$ACitizens)>>
+	<<set $attackTroops = Math.clamp(Math.trunc($ACitizens * _rebelPercent * _weekMod) + random(-100,100),50,$ACitizens)>>
+	<<set $irregulars = Math.clamp(Math.trunc($ACitizens * _irregularPercent * _weekMod) + random(-100,100),50,$ACitizens)>>
 	/* calc if units rebel */
 	<<for _i = 0; _i < $militiaUnits.length; _i++>>
 		<<if $militiaUnits[_i].loyalty < 10>>
diff --git a/src/Mods/SecExp/rebellionHandler.tw b/src/Mods/SecExp/rebellionHandler.tw
index 20e41d978aa..18e30661da3 100644
--- a/src/Mods/SecExp/rebellionHandler.tw
+++ b/src/Mods/SecExp/rebellionHandler.tw
@@ -84,13 +84,10 @@
 <</for>>
 
 <<if $SF.Toggle && $SF.Active >= 1>>
-	<<set $SFatk = 0>>
-	<<set $SFdef = 0>>
-	<<set $SFhp = 0>>
-	<<run calcSFStatistics()>>
-	<<set _attack += $SFatk>>
-	<<set _defense += $SFdef>>
-	<<set _hp += $SFhp>>
+	<<set _unit = App.SecExp.getUnit("SF")>>
+	<<set _attack += _unit.attack>>
+	<<set _defense += _unit.defense>>
+	<<set _hp += _unit.hp>>
 <</if>>
 
 <<set _attack *= _engageMod>>
diff --git a/src/Mods/SecExp/unitsRebellionReport.tw b/src/Mods/SecExp/unitsRebellionReport.tw
index 4611aabeacb..0b897d8b341 100644
--- a/src/Mods/SecExp/unitsRebellionReport.tw
+++ b/src/Mods/SecExp/unitsRebellionReport.tw
@@ -46,200 +46,6 @@
 		<</for>>
 		participated in the battle without taking any casualties. They remained loyal until the end.
 	<</if>>
-	<<if $rebellingID.length > 0 && $battleResult != -1>>
-		<br>
-		<br>
-		/* militia */
-		<<set _militiaRebelledID = []>>
-		<<set _militiaManpower = 0>>
-		<<for _j = 0; _j < $militiaUnits.length; _j++>>
-			<<if $militiaUnits[_j].active == 1 && $rebellingID.includes($militiaUnits[_j].ID)>>
-				$militiaUnits[_j].platoonName,
-				<<set _militiaRebelledID.push($militiaUnits[_j].ID)>>
-				<<set _militiaManpower += Math.clamp($militiaUnits[_j].troops - random(_averageLosses),0,$militiaUnits[_j].troops)>>
-			<</if>>
-		<</for>>
-		<<if _militiaRebelledID.length > 0>>
-			had the gall to betray you and join your enemies.
-			<span id="militiaResult">
-				<br><<link "Dissolve the units">>
-					<<run removeUnits(_militiaRebelledID)>>
-					<<set $militiaFreeManpower += _militiaManpower>>
-					<<for _i = 0; _i < $militiaUnits.length; _i++>>
-						<<if $militiaUnits[_i].active == 1>>
-							<<set $militiaUnits[_i].loyalty = Math.clamp($militiaUnits[_i].loyalty - random(10,40),0,100)>>
-						<</if>>
-					<</for>>
-					<<replace "#militiaResult">>
-						<br>Units dissolved.
-					<</replace>>
-				<</link>>
-				<br>//Manpower will be refunded, but will negatively influence the loyalty of the other units//
-				<br><<link "Purge the dissidents and dissolve the units">>
-					<<run removeUnits(_militiaRebelledID)>>
-					<<set $militiaFreeManpower += _militiaManpower * 0.5>>
-					<<replace "#militiaResult">>
-						<br>Dissidents purged and units dissolved.
-					<</replace>>
-				<</link>>
-				<br>//Will not influence the loyalty of the other units, but only half the manpower will be refunded.//
-				<br><<link "Execute them all">>
-					<<run removeUnits(_militiaRebelledID)>>
-					<<for _i = 0; _i < $militiaUnits.length; _i++>>
-						<<if $militiaUnits[_i].active == 1>>
-							<<set $militiaUnits[_i].loyalty = Math.clamp($militiaUnits[_i].loyalty + random(10,40),0,100)>>
-						<</if>>
-					<</for>>
-					<<replace "#militiaResult">>
-						<br>Units executed. Dissent will not be tolerated.
-					<</replace>>
-				<</link>>
-				<br>//Will positively influence the loyalty of the other units, but no manpower will be refunded.//
-			</span>
-		<</if>>
-
-		/* slaves */
-		<<set _slaveRebelledID = []>>
-		<<set _slaveManpower = 0>>
-		<br><br>
-		<<for _j = 0; _j < $slaveUnits.length; _j++>>
-			<<if $slaveUnits[_j].active == 1 && $rebellingID.includes($slaveUnits[_j].ID)>>
-				$slaveUnits[_j].platoonName,
-				<<set _slaveRebelledID.push($slaveUnits[_j].ID)>>
-				<<set _slaveManpower += Math.clamp($slaveUnits[_j].troops - random(_averageLosses),0,$slaveUnits[_j].troops)>>
-			<</if>>
-		<</for>>
-		<<if _slaveRebelledID.length > 0>>
-			decided in their blind arrogance to betray you.
-			<span id="slaveResult">
-				<br><<link "Dissolve the units">>
-					<<run removeUnits(_slaveRebelledID)>>
-					<<set $menials += _slaveManpower>>
-					<<for _i = 0; _i < $slaveUnits.length; _i++>>
-						<<if $slaveUnits[_i].active == 1>>
-							<<set $slaveUnits[_i].loyalty = Math.clamp($slaveUnits[_i].loyalty - random(10,40),0,100)>>
-						<</if>>
-					<</for>>
-					<<replace "#slaveResult">>
-						<br>Units dissolved.
-					<</replace>>
-				<</link>>
-				<br>//Manpower will be refunded, but will negatively influence the loyalty of the other units//
-				<br><<link "Purge the dissidents and dissolve the units">>
-					<<run removeUnits(_slaveRebelledID)>>
-					<<set $menials += _slaveManpower * 0.5>>
-					<<replace "#slaveResult">>
-						<br>Dissidents purged and units dissolved.
-					<</replace>>
-				<</link>>
-				<br>//Will not influence the loyalty of the other units, but only half the manpower will be refunded.//
-				<br><<link "Execute them all">>
-					<<run removeUnits(_slaveRebelledID)>>
-					<<for _i = 0; _i < $slaveUnits.length; _i++>>
-						<<if $slaveUnits[_i].active == 1>>
-							<<set $slaveUnits[_i].loyalty = Math.clamp($slaveUnits[_i].loyalty + random(10,40),0,100)>>
-						<</if>>
-					<</for>>
-					<<replace "#slaveResult">>
-						<br>Units executed. Dissent will not be tolerated.
-					<</replace>>
-				<</link>>
-				<br>//Will positively influence the loyalty of the other units, but no manpower will be refunded.//
-			</span>
-		<</if>>
-
-		/* mercs */
-		<<set _mercRebelledID = []>>
-		<<set _mercManpower = 0>>
-		<br><br>
-		<<for _j = 0; _j < $mercUnits.length; _j++>>
-			<<if $mercUnits[_j].active == 1 && $rebellingID.includes($mercUnits[_j].ID)>>
-				$mercUnits[_j].platoonName,
-				<<set _mercRebelledID.push($mercUnits[_j].ID)>>
-				<<set _mercManpower += Math.clamp($mercUnits[_j].troops - random(_averageLosses),0,$mercUnits[_j].troops)>>
-			<</if>>
-		<</for>>
-		<<if _mercRebelledID.length > 0>>
-			made the grave mistake of betraying you.
-			<span id="mercResult">
-				<br><<link "Dissolve the units">>
-					<<run removeUnits(_mercRebelledID)>>
-					<<set $mercFreeManpower += _mercManpower>>
-					<<for _i = 0; _i < $mercUnits.length; _i++>>
-						<<if $mercUnits[_i].active == 1>>
-							<<set $mercUnits[_i].loyalty = Math.clamp($mercUnits[_i].loyalty - random(10,40),0,100)>>
-						<</if>>
-					<</for>>
-					<<replace "#mercResult">>
-						<br>Units dissolved.
-					<</replace>>
-				<</link>>
-				<br>//Manpower will be refunded, but will negatively influence the loyalty of the other units//
-				<br><<link "Purge the dissidents and dissolve the units">>
-					<<run removeUnits(_mercRebelledID)>>
-					<<set $mercFreeManpower += _mercManpower * 0.5>>
-					<<replace "#mercResult">>
-						<br>Dissidents purged and units dissolved.
-					<</replace>>
-				<</link>>
-				<br>//Will not influence the loyalty of the other units, but only half the manpower will be refunded.//
-				<br><<link "Execute them all">>
-					<<run removeUnits(_mercRebelledID)>>
-					<<for _i = 0; _i < $mercUnits.length; _i++>>
-						<<if $mercUnits[_i].active == 1>>
-							<<set $mercUnits[_i].loyalty = Math.clamp($mercUnits[_i].loyalty + random(10,40),0,100)>>
-						<</if>>
-					<</for>>
-					<<replace "#mercResult">>
-						<br>Units executed. Dissent will not be tolerated.
-					<</replace>>
-				<</link>>
-				<br>//Will positively influence the loyalty of the other units, but no manpower will be refunded.//
-			</span>
-		<</if>>
-	<<elseif $rebellingID.length > 0>>
-		<<set _militiaRebelledID = []>>
-		<<for _j = 0; _j < $militiaUnits.length; _j++>>
-			<<if $militiaUnits[_j].active == 1 && $rebellingID.includes($militiaUnits[_j].ID)>>
-				<<set _militiaRebelledID.push($militiaUnits[_j].ID)>>
-				$militiaUnits[_j].platoonName,
-			<</if>>
-		<</for>>
-		<<if _militiaRebelledID.length > 0>>
-			had the gall to betray you and join your enemies. They participated in the looting following the rebellion, then vanished in the wastes.
-		<</if>>
-		<<run cashX(forceNeg(1000 * _militiaRebelledID.length), "war")>>
-		<br>
-		<<run removeUnits(_militiaRebelledID)>>
-		<<set _slaveRebelledID = []>>
-		<<for _j = 0; _j < $slaveUnits.length; _j++>>
-			<<if $slaveUnits[_j].active == 1 && $rebellingID.includes($slaveUnits[_j].ID)>>
-				<<set _slaveRebelledID.push($slaveUnits[_j].ID)>>
-				$slaveUnits[_j].platoonName,
-			<</if>>
-		<</for>>
-		<<if _slaveRebelledID.length > 0>>
-			decided in their blind arrogance to betray you. They participated in the looting following the rebellion, then vanished in the wastes.
-		<</if>>
-		<<run cashX(forceNeg(1000 * _slaveRebelledID.length), "war")>>
-		<<run removeUnits(_slaveRebelledID)>>
-		<br>
-		<<set _mercRebelledID = []>>
-		<<set _count = 0>>
-		<<for _j = 0; _j < $mercUnits.length; _j++>>
-			<<if $mercUnits[_j].active == 1 && $rebellingID.includes($mercUnits[_j].ID)>>
-				<<set _mercRebelledID.push($mercUnits[_j].ID)>>
-				<<set _count++>>
-				$mercUnits[_j].platoonName,
-			<</if>>
-		<</for>>
-		<<if _mercRebelledID.length > 0>>
-			made the grave mistake of betraying you. They participated in the looting following the rebellion, then vanished in the wastes.
-		<</if>>
-		<<run cashX(forceNeg(1000 * _mercRebelledID.length), "war")>>
-		<<run removeUnits(_mercRebelledID)>>
-	<</if>>
-
 <<elseif $losses > 0>>
 	/* if the losses are more than zero */
 	/* generates a list of randomized losses, from which each unit picks one at random */
@@ -493,201 +299,202 @@
 			<</if>>
 		<</for>>
 	<</if>>
+<<else>>
+	<br>@@.red;Error: losses are a negative number or NaN@@
+<</if>>
+
+<<if $rebellingID.length > 0 && $battleResult >= 2>>
+	/* rebellion win */
 	<br><br>
-	<<if $rebellingID.length > 0 && $battleResult >= 2>>
-		/* win */
-		/* militia */
-		<<set _militiaRebelledID = []>>
-		<<set _militiaManpower = 0>>
-		<<for _j = 0; _j < $militiaUnits.length; _j++>>
-			<<if $militiaUnits[_j].active == 1 && $rebellingID.includes($militiaUnits[_j].ID)>>
-				$militiaUnits[_j].platoonName,
-				<<set _militiaRebelledID.push($militiaUnits[_j].ID)>>
-				<<set _militiaManpower += Math.clamp($militiaUnits[_j].troops - random(_averageLosses),0,$militiaUnits[_j].troops)>>
-			<</if>>
-		<</for>>
-		<<if _militiaRebelledID.length > 0>>
-			had the gall to betray you and join your enemies.
-			<span id="militiaResult">
-				<br><<link "Dissolve the units">>
-					<<run removeUnits(_militiaRebelledID)>>
-					<<set $militiaFreeManpower += _militiaManpower>>
-					<<for _i = 0; _i < $militiaUnits.length; _i++>>
-						<<if $militiaUnits[_i].active == 1>>
-							<<set $militiaUnits[_i].loyalty = Math.clamp($militiaUnits[_i].loyalty - random(10,40),0,100)>>
-						<</if>>
-					<</for>>
-					<<replace "#militiaResult">>
-						<br>Units dissolved.
-					<</replace>>
-				<</link>>
-				<br>//Manpower will be refunded, but will negatively influence the loyalty of the other units//
-				<br><<link "Purge the dissidents and dissolve the units">>
-					<<run removeUnits(_militiaRebelledID)>>
-					<<set $militiaFreeManpower += _militiaManpower * 0.5>>
-					<<replace "#militiaResult">>
-						<br>Dissidents purged and units dissolved.
-					<</replace>>
-				<</link>>
-				<br>//Will not influence the loyalty of the other units, but only half the manpower will be refunded.//
-				<br><<link "Execute them all">>
-					<<run removeUnits(_militiaRebelledID)>>
-					<<for _i = 0; _i < $militiaUnits.length; _i++>>
-						<<if $militiaUnits[_i].active == 1>>
-							<<set $militiaUnits[_i].loyalty = Math.clamp($militiaUnits[_i].loyalty + random(10,40),0,100)>>
-						<</if>>
-					<</for>>
-					<<replace "#militiaResult">>
-						<br>Units executed. Dissent will not be tolerated.
-					<</replace>>
-				<</link>>
-				<br>//Will positively influence the loyalty of the other units, but no manpower will be refunded.//
-			</span>
+	/* militia */
+	<<set _militiaRebelledID = []>>
+	<<set _militiaManpower = 0>>
+	<<for _j = 0; _j < $militiaUnits.length; _j++>>
+		<<if $militiaUnits[_j].active == 1 && $rebellingID.includes($militiaUnits[_j].ID)>>
+			$militiaUnits[_j].platoonName,
+			<<set _militiaRebelledID.push($militiaUnits[_j].ID)>>
+			<<set _militiaManpower += Math.clamp($militiaUnits[_j].troops - random(_averageLosses),0,$militiaUnits[_j].troops)>>
 		<</if>>
+	<</for>>
+	<<if _militiaRebelledID.length > 0>>
+		had the gall to betray you and join your enemies.
+		<span id="militiaResult">
+			<br><<link "Dissolve the units">>
+				<<run $militiaUnits.deleteWith((u) => _militiaRebelledID.includes(u.ID))>>
+				<<set $militiaFreeManpower += _militiaManpower>>
+				<<for _i = 0; _i < $militiaUnits.length; _i++>>
+					<<if $militiaUnits[_i].active == 1>>
+						<<set $militiaUnits[_i].loyalty = Math.clamp($militiaUnits[_i].loyalty - random(10,40),0,100)>>
+					<</if>>
+				<</for>>
+				<<replace "#militiaResult">>
+					<br>Units dissolved.
+				<</replace>>
+			<</link>>
+			<br>//Manpower will be refunded, but will negatively influence the loyalty of the other units//
+			<br><<link "Purge the dissidents and dissolve the units">>
+				<<run $militiaUnits.deleteWith((u) => _militiaRebelledID.includes(u.ID))>>
+				<<set $militiaFreeManpower += _militiaManpower * 0.5>>
+				<<replace "#militiaResult">>
+					<br>Dissidents purged and units dissolved.
+				<</replace>>
+			<</link>>
+			<br>//Will not influence the loyalty of the other units, but only half the manpower will be refunded.//
+			<br><<link "Execute them all">>
+				<<run $militiaUnits.deleteWith((u) => _militiaRebelledID.includes(u.ID))>>
+				<<for _i = 0; _i < $militiaUnits.length; _i++>>
+					<<if $militiaUnits[_i].active == 1>>
+						<<set $militiaUnits[_i].loyalty = Math.clamp($militiaUnits[_i].loyalty + random(10,40),0,100)>>
+					<</if>>
+				<</for>>
+				<<replace "#militiaResult">>
+					<br>Units executed. Dissent will not be tolerated.
+				<</replace>>
+			<</link>>
+			<br>//Will positively influence the loyalty of the other units, but no manpower will be refunded.//
+		</span>
+	<</if>>
 
-		/* slaves */
-		<<set _slaveRebelledID = []>>
-		<<set _slaveManpower = 0>>
-		<br>
-		<<for _j = 0; _j < $slaveUnits.length; _j++>>
-			<<if $slaveUnits[_j].active == 1 && $rebellingID.includes($slaveUnits[_j].ID)>>
-				$slaveUnits[_j].platoonName,
-				<<set _slaveRebelledID.push($slaveUnits[_j].ID)>>
-				<<set _slaveManpower += Math.clamp($slaveUnits[_j].troops - random(_averageLosses),0,$slaveUnits[_j].troops)>>
-			<</if>>
-		<</for>>
-		<<if _slaveRebelledID.length > 0>>
-			decided in their blind arrogance to betray you.
-			<span id="slaveResult">
-				<br><<link "Dissolve the units">>
-					<<run removeUnits(_slaveRebelledID)>>
-					<<set $menials += _slaveManpower>>
-					<<for _i = 0; _i < $militiaUnits.length; _i++>>
-						<<if $slaveUnits[_i].active == 1>>
-							<<set $slaveUnits[_i].loyalty = Math.clamp($slaveUnits[_i].loyalty - random(10,40),0,100)>>
-						<</if>>
-					<</for>>
-					<<replace "#slaveResult">>
-						<br>Units dissolved.
-					<</replace>>
-				<</link>>
-				<br>//Manpower will be refunded, but will negatively influence the loyalty of the other units//
-				<br><<link "Purge the dissidents and dissolve the units">>
-					<<run removeUnits(_slaveRebelledID)>>
-					<<set $menials += _slaveManpower * 0.5>>
-					<<replace "#slaveResult">>
-						<br>Dissidents purged and units dissolved.
-					<</replace>>
-				<</link>>
-				<br>//Will not influence the loyalty of the other units, but only half the manpower will be refunded.//
-				<br><<link "Execute them all">>
-					<<run removeUnits(_slaveRebelledID)>>
-					<<for _i = 0; _i < $slaveUnits.length; _i++>>
-						<<if $slaveUnits[_i].active == 1>>
-							<<set $slaveUnits[_i].loyalty = Math.clamp($slaveUnits[_i].loyalty + random(10,40),0,100)>>
-						<</if>>
-					<</for>>
-					<<replace "#slaveResult">>
-						<br>Units executed. Dissent will not be tolerated.
-					<</replace>>
-				<</link>>
-				<br>//Will positively influence the loyalty of the other units, but no manpower will be refunded.//
-			</span>
+	/* slaves */
+	<<set _slaveRebelledID = []>>
+	<<set _slaveManpower = 0>>
+	<br>
+	<<for _j = 0; _j < $slaveUnits.length; _j++>>
+		<<if $slaveUnits[_j].active == 1 && $rebellingID.includes($slaveUnits[_j].ID)>>
+			$slaveUnits[_j].platoonName,
+			<<set _slaveRebelledID.push($slaveUnits[_j].ID)>>
+			<<set _slaveManpower += Math.clamp($slaveUnits[_j].troops - random(_averageLosses),0,$slaveUnits[_j].troops)>>
 		<</if>>
+	<</for>>
+	<<if _slaveRebelledID.length > 0>>
+		decided in their blind arrogance to betray you.
+		<span id="slaveResult">
+			<br><<link "Dissolve the units">>
+				<<run $slaveUnits.deleteWith((u) => _slaveRebelledID.includes(u.ID))>>
+				<<set $menials += _slaveManpower>>
+				<<for _i = 0; _i < $militiaUnits.length; _i++>>
+					<<if $slaveUnits[_i].active == 1>>
+						<<set $slaveUnits[_i].loyalty = Math.clamp($slaveUnits[_i].loyalty - random(10,40),0,100)>>
+					<</if>>
+				<</for>>
+				<<replace "#slaveResult">>
+					<br>Units dissolved.
+				<</replace>>
+			<</link>>
+			<br>//Manpower will be refunded, but will negatively influence the loyalty of the other units//
+			<br><<link "Purge the dissidents and dissolve the units">>
+				<<run $slaveUnits.deleteWith((u) => _slaveRebelledID.includes(u.ID))>>
+				<<set $menials += _slaveManpower * 0.5>>
+				<<replace "#slaveResult">>
+					<br>Dissidents purged and units dissolved.
+				<</replace>>
+			<</link>>
+			<br>//Will not influence the loyalty of the other units, but only half the manpower will be refunded.//
+			<br><<link "Execute them all">>
+				<<run $slaveUnits.deleteWith((u) => _slaveRebelledID.includes(u.ID))>>
+				<<for _i = 0; _i < $slaveUnits.length; _i++>>
+					<<if $slaveUnits[_i].active == 1>>
+						<<set $slaveUnits[_i].loyalty = Math.clamp($slaveUnits[_i].loyalty + random(10,40),0,100)>>
+					<</if>>
+				<</for>>
+				<<replace "#slaveResult">>
+					<br>Units executed. Dissent will not be tolerated.
+				<</replace>>
+			<</link>>
+			<br>//Will positively influence the loyalty of the other units, but no manpower will be refunded.//
+		</span>
+	<</if>>
 
-		/* mercs */
-		<<set _mercRebelledID = []>>
-		<<set _mercManpower = 0>>
-		<br>
-		<<for _j = 0; _j < $mercUnits.length; _j++>>
-			<<if $mercUnits[_j].active == 1 && $rebellingID.includes($mercUnits[_j].ID)>>
-				$mercUnits[_j].platoonName,
-				<<set _mercRebelledID.push($mercUnits[_j].ID)>>
-				<<set _mercManpower += Math.clamp($mercUnits[_j].troops - random(_averageLosses),0,$mercUnits[_j].troops)>>
-			<</if>>
-		<</for>>
-		<<if _mercRebelledID.length > 0>>
-			made the grave mistake of betraying you.
-			<span id="mercResult">
-				<br><<link "Dissolve the units">>
-					<<run removeUnits(_mercRebelledID)>>
-					<<set $mercFreeManpower += _mercManpower>>
-					<<for _i = 0; _i < $militiaUnits.length; _i++>>
-						<<if $mercUnits[_i].active == 1>>
-							<<set $mercUnits[_i].loyalty = Math.clamp($mercUnits[_i].loyalty - random(10,40),0,100)>>
-						<</if>>
-					<</for>>
-					<<replace "#mercResult">>
-						<br>Units dissolved.
-					<</replace>>
-				<</link>>
-				<br>//Manpower will be refunded, but will negatively influence the loyalty of the other units//
-				<br><<link "Purge the dissidents and dissolve the units">>
-					<<run removeUnits(_mercRebelledID)>>
-					<<set $mercFreeManpower += _mercManpower * 0.5>>
-					<<replace "#mercResult">>
-						<br>Dissidents purged and units dissolved.
-					<</replace>>
-				<</link>>
-				<br>//Will not influence the loyalty of the other units, but only half the manpower will be refunded.//
-				<br><<link "Execute them all">>
-					<<run removeUnits(_mercRebelledID)>>
-					<<for _i = 0; _i < $mercUnits.length; _i++>>
-						<<if $mercUnits[_i].active == 1>>
-							<<set $mercUnits[_i].loyalty = Math.clamp($mercUnits[_i].loyalty + random(10,40),0,100)>>
-						<</if>>
-					<</for>>
-					<<replace "#mercResult">>
-						<br>Units executed. Dissent will not be tolerated.
-					<</replace>>
-				<</link>>
-				<br>//Will positively influence the loyalty of the other units, but no manpower will be refunded.//
-			</span>
+	/* mercs */
+	<<set _mercRebelledID = []>>
+	<<set _mercManpower = 0>>
+	<br>
+	<<for _j = 0; _j < $mercUnits.length; _j++>>
+		<<if $mercUnits[_j].active == 1 && $rebellingID.includes($mercUnits[_j].ID)>>
+			$mercUnits[_j].platoonName,
+			<<set _mercRebelledID.push($mercUnits[_j].ID)>>
+			<<set _mercManpower += Math.clamp($mercUnits[_j].troops - random(_averageLosses),0,$mercUnits[_j].troops)>>
 		<</if>>
-
-	/* loss */
-	<<elseif $rebellingID.length > 0>>
-		<<set _militiaRebelledID = []>>
-		<<for _j = 0; _j < $militiaUnits.length; _j++>>
-			<<if $militiaUnits[_j].active == 1 && $rebellingID.includes($militiaUnits[_j].ID)>>
-				<<set _militiaRebelledID.push($militiaUnits[_j].ID)>>
-				$militiaUnits[_j].platoonName,
-			<</if>>
-		<</for>>
-		<<if _militiaRebelledID.length > 0>>
-			had the gall to betray you and join your enemies. They participated in the looting following the battle, then vanished in the wastes.
+	<</for>>
+	<<if _mercRebelledID.length > 0>>
+		made the grave mistake of betraying you.
+		<span id="mercResult">
+			<br><<link "Dissolve the units">>
+				<<run $mercUnits.deleteWith((u) => _mercRebelledID.includes(u.ID))>>
+				<<set $mercFreeManpower += _mercManpower>>
+				<<for _i = 0; _i < $militiaUnits.length; _i++>>
+					<<if $mercUnits[_i].active == 1>>
+						<<set $mercUnits[_i].loyalty = Math.clamp($mercUnits[_i].loyalty - random(10,40),0,100)>>
+					<</if>>
+				<</for>>
+				<<replace "#mercResult">>
+					<br>Units dissolved.
+				<</replace>>
+			<</link>>
+			<br>//Manpower will be refunded, but will negatively influence the loyalty of the other units//
+			<br><<link "Purge the dissidents and dissolve the units">>
+				<<run $mercUnits.deleteWith((u) => _mercRebelledID.includes(u.ID))>>
+				<<set $mercFreeManpower += _mercManpower * 0.5>>
+				<<replace "#mercResult">>
+					<br>Dissidents purged and units dissolved.
+				<</replace>>
+			<</link>>
+			<br>//Will not influence the loyalty of the other units, but only half the manpower will be refunded.//
+			<br><<link "Execute them all">>
+				<<run $mercUnits.deleteWith((u) => _mercRebelledID.includes(u.ID))>>
+				<<for _i = 0; _i < $mercUnits.length; _i++>>
+					<<if $mercUnits[_i].active == 1>>
+						<<set $mercUnits[_i].loyalty = Math.clamp($mercUnits[_i].loyalty + random(10,40),0,100)>>
+					<</if>>
+				<</for>>
+				<<replace "#mercResult">>
+					<br>Units executed. Dissent will not be tolerated.
+				<</replace>>
+			<</link>>
+			<br>//Will positively influence the loyalty of the other units, but no manpower will be refunded.//
+		</span>
+	<</if>>
+<<elseif $rebellingID.length > 0>>
+	/* rebellion loss */
+	<br><br>
+	<<set _militiaRebelledID = []>>
+	<<for _j = 0; _j < $militiaUnits.length; _j++>>
+		<<if $militiaUnits[_j].active == 1 && $rebellingID.includes($militiaUnits[_j].ID)>>
+			<<set _militiaRebelledID.push($militiaUnits[_j].ID)>>
+			$militiaUnits[_j].platoonName,
 		<</if>>
-		<<run cashX(forceNeg(1000 * _militiaRebelledID.length), "war")>>
-		<br>
-		<<run removeUnits(_militiaRebelledID)>>
-		<<set _slaveRebelledID = []>>
-		<<for _j = 0; _j < $slaveUnits.length; _j++>>
-			<<if $slaveUnits[_j].active == 1 && $rebellingID.includes($slaveUnits[_j].ID)>>
-				<<set _slaveRebelledID.push($slaveUnits[_j].ID)>>
-				$slaveUnits[_j].platoonName,
-			<</if>>
-		<</for>>
-		<<if _slaveRebelledID.length > 0>>
-			decided in their blind arrogance to betray you. They participated in the looting following the battle, then vanished in the wastes.
+	<</for>>
+	<<if _militiaRebelledID.length > 0>>
+		had the gall to betray you and join your enemies. They participated in the looting following the battle, then vanished in the wastes.
+	<</if>>
+	<<run cashX(forceNeg(1000 * _militiaRebelledID.length), "war")>>
+	<<run $militiaUnits.deleteWith((u) => _militiaRebelledID.includes(u.ID))>>
+	<br>
+	<<set _slaveRebelledID = []>>
+	<<for _j = 0; _j < $slaveUnits.length; _j++>>
+		<<if $slaveUnits[_j].active == 1 && $rebellingID.includes($slaveUnits[_j].ID)>>
+			<<set _slaveRebelledID.push($slaveUnits[_j].ID)>>
+			$slaveUnits[_j].platoonName,
 		<</if>>
-		<<run cashX(forceNeg(1000 * _slaveRebelledID.length), "war")>>
-		<<run removeUnits(_slaveRebelledID)>>
-		<br>
-		<<set _mercRebelledID = []>>
-		<<set _count = 0>>
-		<<for _j = 0; _j < $mercUnits.length; _j++>>
-			<<if $mercUnits[_j].active == 1 && $rebellingID.includes($mercUnits[_j].ID)>>
-				<<set _mercRebelledID.push($mercUnits[_j].ID)>>
-				<<set _count++>>
-				$mercUnits[_j].platoonName,
-			<</if>>
-		<</for>>
-		<<if _mercRebelledID.length > 0>>
-			made the grave mistake of betraying you. They participated in the looting following the battle, then vanished in the wastes.
+	<</for>>
+	<<if _slaveRebelledID.length > 0>>
+		decided in their blind arrogance to betray you. They participated in the looting following the battle, then vanished in the wastes.
+	<</if>>
+	<<run cashX(forceNeg(1000 * _slaveRebelledID.length), "war")>>
+	<<run $slaveUnits.deleteWith((u) => _slaveRebelledID.includes(u.ID))>>
+	<br>
+	<<set _mercRebelledID = []>>
+	<<set _count = 0>>
+	<<for _j = 0; _j < $mercUnits.length; _j++>>
+		<<if $mercUnits[_j].active == 1 && $rebellingID.includes($mercUnits[_j].ID)>>
+			<<set _mercRebelledID.push($mercUnits[_j].ID)>>
+			<<set _count++>>
+			$mercUnits[_j].platoonName,
 		<</if>>
-		<<run cashX(forceNeg(1000 * _mercRebelledID.length), "war")>>
-		<<run removeUnits(_mercRebelledID)>>
+	<</for>>
+	<<if _mercRebelledID.length > 0>>
+		made the grave mistake of betraying you. They participated in the looting following the battle, then vanished in the wastes.
 	<</if>>
-<<else>>
-	<br>@@.red;Error: losses are a negative number or NaN@@
+	<<run cashX(forceNeg(1000 * _mercRebelledID.length), "war")>>
+	<<run $mercUnits.deleteWith((u) => _mercRebelledID.includes(u.ID))>>
 <</if>>
diff --git a/src/Mods/SecExp/widgets/battleWidgets.js b/src/Mods/SecExp/widgets/battleWidgets.js
deleted file mode 100644
index 22789549aab..00000000000
--- a/src/Mods/SecExp/widgets/battleWidgets.js
+++ /dev/null
@@ -1,38 +0,0 @@
-globalThis.calcSFStatistics = function() {
-	let upgradesSum = V.SF.Squad.Armoury + V.SF.Squad.Drugs + (V.SF.Squad.AA+V.SF.Squad.TA < 1) + (V.SF.Squad.AV+V.SF.Squad.TV);
-	if (!Number.isInteger(upgradesSum)) {
-		upgradesSum = jsRandom(10, 15);
-	}
-
-	if (V.slaveRebellion !== 1 || V.citizenRebellion !== 1) {
-		/* atk, def */
-		V.SFatk = Math.trunc(0.65 * upgradesSum);
-		V.SFdef = Math.trunc(0.40 * upgradesSum);
-		/* hp */
-		V.carriableSoldiers = 125 * (V.SF.Squad.GunS + ((V.SF.Squad.AV + V.SF.Squad.TV)/2));
-		if (!Number.isInteger(V.carriableSoldiers)) {
-			V.carriableSoldiers = V.SF.ArmySize / 10;
-		}
-		if (V.SF.ArmySize > V.carriableSoldiers) {
-			V.SFhp = V.carriableSoldiers * App.SecExp.BaseSpecialForcesUnit.hp;
-		} else {
-			V.carriableSoldiers = V.SF.ArmySize;
-			V.SFhp = V.carriableSoldiers * App.SecExp.BaseSpecialForcesUnit.hp;
-		}
-	} else {
-		/* atk, def */
-		V.SFatk = Math.trunc(0.75 * upgradesSum);
-		V.SFdef = Math.trunc(0.50 * upgradesSum);
-		/* hp */
-		V.SFhp = V.SF.ArmySize * App.SecExp.BaseSpecialForcesUnit.hp;
-	}
-};
-
-/**
- * @param {Array<number>} rebellionIDs Array of unit IDs to be removed.
- */
-globalThis.removeUnits = function(rebellionIDs) {
-	V.militiaUnits = V.militiaUnits.filter(unit => !rebellionIDs.includes(unit.ID));
-	V.slaveUnits = V.slaveUnits.filter(unit => !rebellionIDs.includes(unit.ID));
-	V.mercUnits = V.mercUnits.filter(unit => !rebellionIDs.includes(unit.ID));
-};
-- 
GitLab