+
v1.9 [MOBILE PORT & HOTFIXES]
+
Commander Combat Adjustments: Assassinations are much harder. Troop damage to Commanders is reduced, ambushes are limited to once per turn, and entrenched Commanders require taking the territory first.
+
Advanced AI Tactics: AI commanders now retreat at 50% HP, avoid crowded territories, hold defensive chokepoints, and navigate home intelligently if stranded.
+
Turbo Mode Auto-Skip: The game now automatically advances the phase when your AP depletes.
+
Smart UI & Map Upgrades: The map perfectly renders multiple VIP stars and adds V.A.T.S. hover warnings for entrenched targets.
+
New Holotape Loaded: Added Fallout 4 (The Commonwealth) to the theme selector.
+
v1.8 [MOBILE PORT & HOTFIXES]
Hardware Port: RobCo OS now natively supports handheld (mobile) terminals. Device must be rotated to Landscape mode to initialize.
Threat Patch: Resolved a logic error preventing the Wild Ghouls subsystem from properly infesting unclaimed sectors at boot.
@@ -1293,6 +1345,11 @@ const themeFactions = {
{ name: "General Oliver", country: "New California Republic" }, { name: "Caesar", country: "Caesar's Legion" },
{ name: "Mr. House", country: "New Vegas Securitrons" }, { name: "Elder McNamara", country: "Mojave Brotherhood" },
{ name: "Papa Khan", country: "Great Khans" }, { name: "Motor-Runner", country: "The Fiends" }
+ ],
+ "fo4": [
+ { name: "Preston Garvey", country: "The Minutemen" }, { name: "Father", country: "The Institute" },
+ { name: "Elder Maxson", country: "Brotherhood of Steel" }, { name: "Desdemona", country: "The Railroad" },
+ { name: "Captain Wes", country: "The Gunners" }, { name: "Colter", country: "Nuka-World Raiders" }
]
};
@@ -1443,11 +1500,17 @@ if (Gamestate.nukesEnabled && targetCountry.isSilo) {
infoLines.push(`
COMMAND SILO (+${defBuff}% DEFENSE)`);
}
- // Loop through all players to see if ANY commander is standing here
+// Loop through all players to see if ANY commander is standing here
if (Gamestate.commandersEnabled) {
Gamestate.players.forEach(p => {
- if (p.alive && !p.isNeutral && p.commander && p.commander.loc === targetCountry.name) {
- let isStranded = p.name !== targetCountry.owner ? " (STRANDED)" : "";
+ if (p.alive && !p.isNeutral && p.commander && p.commander.hp > 0 && p.commander.loc === targetCountry.name) {
+ // NEW: Dynamically flag them as Stranded or Entrenched!
+ let isStranded = "";
+ if (p.name !== targetCountry.owner) {
+ isStranded = "
(STRANDED)";
+ } else {
+ isStranded = "
(ENTRENCHED: VIP DMG RESIST)";
+ }
infoLines.push(`
★ ${p.name} COMMANDER${isStranded} (${p.commander.hp} HP)`);
}
});
@@ -1489,7 +1552,43 @@ const helpBtn = document.querySelector('#help-btn');
const helpModal = document.querySelector('#help-modal');
const closeHelpBtn = document.querySelector('#close-help-btn');
+
let Gamestate = {};
+// --- ERA-SPECIFIC DYNAMIC THEME LOADER ---
+const themeSelector = document.getElementById('chosen-theme');
+const leaderInput = document.getElementById('chosen-leader');
+const factionInput = document.getElementById('chosen-country');
+
+Gamestate.eraFactions = {
+ fo3: ["The Enclave", "Super Mutants", "Talon Company", "Reilly's Rangers", "Outcasts"],
+ fnv: ["Caesar's Legion", "Mr. House", "Great Khans", "Boomers", "Fiends"],
+ fo4: ["The Institute", "Brotherhood of Steel", "The Railroad", "The Gunners", "Raider Gangs"]
+};
+
+if (themeSelector) {
+ themeSelector.addEventListener('change', function(e) {
+ let theme = e.target.value;
+
+ // 1. SAFELY Swap CSS (Only removes theme classes, leaves game logic alone!)
+ document.body.classList.remove('theme-fo3', 'theme-fnv', 'theme-fo4');
+ if (theme !== 'fo3') document.body.classList.add('theme-' + theme);
+
+// 2. Auto-fill names and perfectly adjust the cursor
+ if (theme === 'fo3') {
+ if(leaderInput) { leaderInput.value = "Lone Wanderer"; leaderInput.style.width = "13ch"; }
+ if(factionInput) { factionInput.value = "Brotherhood of Steel"; factionInput.style.width = "20ch"; }
+ } else if (theme === 'fnv') {
+ if(leaderInput) { leaderInput.value = "Courier Six"; leaderInput.style.width = "11ch"; }
+ if(factionInput) { factionInput.value = "New California Republic"; factionInput.style.width = "23ch"; }
+ } else if (theme === 'fo4') {
+ if(leaderInput) { leaderInput.value = "Sole Survivor"; leaderInput.style.width = "13ch"; }
+ if(factionInput) { factionInput.value = "The Minutemen"; factionInput.style.width = "13ch"; }
+ }
+ });
+
+ // Trigger on boot to lock in the colors safely
+ themeSelector.dispatchEvent(new Event('change'));
+}
Gamestate.logQueue = [];
Gamestate.isLogging = false;
@@ -1718,7 +1817,8 @@ document.getElementById('secret-dev-key')?.addEventListener('click', (e) => {
});
document.getElementById('dev-heal')?.addEventListener('click', () => { if(this.player.commander) this.player.commander.hp = 100; this.updateInfo(); this.showToast("Dev: Commander Healed"); });
document.getElementById('dev-storm')?.addEventListener('click', () => { this.radstorm.cooldown = 1; this.processRadstorm(); document.getElementById('dev-modal').style.display = 'none'; });
-document.getElementById('dev-win-slider')?.addEventListener('input', function() {
+
+ document.getElementById('dev-win-slider')?.addEventListener('input', function() {
let val = parseInt(this.value);
if (val === -1) {
document.getElementById('dev-win-val').textContent = "NORMAL";
@@ -1728,9 +1828,26 @@ document.getElementById('dev-win-slider')?.addEventListener('input', function()
Gamestate.devWinOverride = val / 100;
}
});
+
+ // --- DEV TOOL: FORCE UI THEME SWAP ---
+ document.getElementById('dev-theme-override')?.addEventListener('change', function(e) {
+ let theme = e.target.value;
+
+ // Safely wipe the current era's CSS tags
+ document.body.classList.remove('theme-fnv', 'theme-fo4');
+
+ // Apply the new theme (FO3 is default, so it doesn't need an extra class)
+ if (theme !== 'fo3') {
+ document.body.classList.add('theme-' + theme);
+ }
+
+ // Show a dev toast confirming the swap
+ if (Gamestate.showToast) Gamestate.showToast("Dev Override: UI forced to " + theme.toUpperCase());
+ });
}
Gamestate.updateButtonText = function() {
+ let end = document.getElementById('end');
if (!end) return;
if (this.stage === "Fortify") {
end.textContent = "Deploy Troops"; end.style.opacity = "0.5"; end.style.pointerEvents = "none";
@@ -1739,11 +1856,9 @@ Gamestate.updateButtonText = function() {
end.textContent = "End Attack Phase"; end.style.opacity = "1"; end.style.pointerEvents = "auto";
}
else if (this.stage === "Maneuver") {
- if(this.commandersEnabled && this.player.alive && this.player.commander) {
- end.textContent = this.maneuverSource ? "Next Phase" : "Skip Move";
- } else {
- end.textContent = "End Turn";
- }
+ // If Commanders are on, it says Next Phase. Otherwise, End Turn.
+ let nextStr = (this.commandersEnabled && this.player.alive && this.player.commander) ? "Next Phase" : "End Turn";
+ end.textContent = this.hasManeuvered ? nextStr : "Skip Move";
end.style.opacity = "1"; end.style.pointerEvents = "auto";
}
else if (this.stage === "Commander Phase") {
@@ -1785,14 +1900,17 @@ this.devWinOverride = -1;
if (el) { el.classList.remove('radstorm-warning', 'radstorm-active', 'allied-territory', 'crater', 'glowing-sea'); c.isCrater = false; c.radDecay = 0; c.isSilo = false; c.siloTurns = 0; }
});
- let themeDropdown = document.getElementById('chosen-theme');
+let themeDropdown = document.getElementById('chosen-theme');
let selectedTheme = themeDropdown ? themeDropdown.value : "fo3";
- document.body.classList.remove('theme-fnv');
- if (selectedTheme === "fnv") document.body.classList.add('theme-fnv');
-
+
+ // Safely remove old themes and apply the new one
+ document.body.classList.remove('theme-fnv', 'theme-fo4');
+ if (selectedTheme !== "fo3") document.body.classList.add('theme-' + selectedTheme);
+
this.players = JSON.parse(JSON.stringify(basePlayers));
- let factionList = themeFactions[selectedTheme];
+ // Fetch the correct faction list, with a failsafe fallback so the game never crashes again
+ let factionList = themeFactions[selectedTheme] || themeFactions["fo3"];
for(let i = 0; i < 6; i++) { this.players[i].name = factionList[i].name; this.players[i].country = factionList[i].country; this.players[i].codes = 0; }
let horrors = this.players.find(p => p.isNeutral && p.name === "Wasteland Horrors");
@@ -1911,9 +2029,10 @@ Gamestate.drawMapText = function() {
if (areaOnMap && textNode) {
if(country.isCrater) { textNode.innerHTML = ""; return; }
- let text = `${country.army}`;
let iconHtml = "";
+
+ // 1. Check for Command Silo
if(this.nukesEnabled && country.isSilo) {
let isLaunchSite = (this.activeNuke && this.activeNuke.launchSilo === country.name);
let siloColor = isLaunchSite ? "#ff3333" : "#ffcc00";
@@ -1921,17 +2040,28 @@ Gamestate.drawMapText = function() {
iconHtml += `
☢ `;
}
+ // 2. Check for Multiple Commanders
if(this.commandersEnabled) {
this.players.forEach(p => {
- if (p.alive && !p.isNeutral && p.commander && p.commander.loc === country.name) {
- iconHtml += `
★ `;
+ // FIX: Added "hp > 0" so dead commanders don't leave a ghost star!
+ if (p.alive && !p.isNeutral && p.commander && p.commander.hp > 0 && p.commander.loc === country.name) {
+ // Added dx="2" to give multiple stars a tiny bit of breathing room
+ iconHtml += `
★`;
}
});
}
- if (iconHtml !== "") { text += `
${iconHtml}`; }
+ // 3. Perfect Stacking Output
+ // We grab the exact X coordinate of the text to force the stars to center directly over the number
+ let xCoord = textNode.getAttribute("x");
- textNode.innerHTML = text;
+ if (iconHtml !== "") {
+ // Stack the icons up (-12), then drop the army number back down (16)
+ textNode.innerHTML = `
${iconHtml}${country.army}`;
+ } else {
+ // If no icons, just reset the army number to normal
+ textNode.innerHTML = `
${country.army}`;
+ }
}
});
}
@@ -2269,32 +2399,57 @@ Gamestate.updateInfo = function(){
if (this.player.areas.length <= 5) { hpFill.style.background = "#ff0000"; hpFill.style.boxShadow = "0 0 10px rgba(255, 0, 0, 0.8)"; }
else { hpFill.style.background = "var(--pip-color)"; hpFill.style.boxShadow = "var(--pip-glow)"; }
- let apPercentage = 0;
+let apPercentage = 0;
+ let shouldAutoSkip = false; // The Turbo Sensor
+
if (this.stage === "Fortify") {
- let maxReserve = Math.max(this.player.reserve + this.playerTroopsPlaced, 1); apPercentage = (this.player.reserve / maxReserve) * 100;
+ let maxReserve = Math.max(this.player.reserve + this.playerTroopsPlaced, 1);
+ apPercentage = (this.player.reserve / maxReserve) * 100;
} else if (this.stage === "Battle") {
- let currentStrikeForce = 0; let validAttacks = 0; let ownedTerritories = this.countries.filter(c => c.owner === this.player.name);
+ let currentStrikeForce = 0;
+ let validAttacks = 0; let ownedTerritories = this.countries.filter(c => c.owner === this.player.name);
ownedTerritories.forEach(t => {
if (t.army > 1) {
let hasEnemyNeighbor = t.neighbours.some(n => { let nc = this.countries.find(x => x.name === n); return nc && nc.owner !== this.player.name && !nc.isCrater; });
if (hasEnemyNeighbor) { currentStrikeForce += (t.army - 1); validAttacks++; }
}
});
- if (validAttacks === 0) apPercentage = 0;
+ if (validAttacks === 0) {
+ apPercentage = 0;
+ shouldAutoSkip = true; // No valid attacks left!
+ }
else {
if (this.lastStage !== "Battle") this.initialStrikeForce = currentStrikeForce;
if (currentStrikeForce > (this.initialStrikeForce || 1)) this.initialStrikeForce = currentStrikeForce;
apPercentage = Math.min(100, (currentStrikeForce / Math.max(this.initialStrikeForce, 1)) * 100);
}
} else if (this.stage === "Maneuver") {
- let canManeuver = false; let ownedTerritories = this.countries.filter(c => c.owner === this.player.name);
+ let canManeuver = false;
+ let ownedTerritories = this.countries.filter(c => c.owner === this.player.name);
for (let t of ownedTerritories) { if (t.army > 1 && t.neighbours.some(n => { let nc = this.countries.find(x => x.name === n); return nc && nc.owner === this.player.name; })) { canManeuver = true; break; } }
apPercentage = canManeuver ? (this.maneuverSource ? 0 : 100) : 0;
} else if (this.stage === "Commander Phase" && this.commandersEnabled && this.player.commander) {
apPercentage = (this.player.commander.ap / 2) * 100;
+ if (this.player.commander.ap <= 0) shouldAutoSkip = true; // Out of AP!
} else apPercentage = 0;
apFill.style.width = apPercentage + "%";
+
+ // --- TURBO MODE AUTO-SKIP ENGINE ---
+ let turbo = document.getElementById('turbo-toggle') && document.getElementById('turbo-toggle').checked;
+ if (turbo && shouldAutoSkip && !this.aiTurn) {
+ if (!this.autoSkipTimer) {
+ this.autoSkipTimer = setTimeout(() => {
+ this.autoSkipTimer = null;
+ if (!this.aiTurn && (this.stage === "Battle" || this.stage === "Commander Phase")) {
+ if (Gamestate.logAction) Gamestate.logAction("[ TURBO ] AP Depleted. Auto-advancing phase...", true);
+ this.handleEndTurn();
+ }
+ }, 1200); // 1.2 second delay so you can read the final combat logs
+ }
+ } else {
+ if (this.autoSkipTimer) { clearTimeout(this.autoSkipTimer); this.autoSkipTimer = null; }
+ }
if (apPercentage <= 0) { apFill.style.opacity = "0"; apFill.style.visibility = "hidden"; } else { apFill.style.opacity = "1"; apFill.style.visibility = "visible"; }
if (hpPercentage <= 0) { hpFill.style.opacity = "0"; hpFill.style.visibility = "hidden"; } else { hpFill.style.opacity = "1"; hpFill.style.visibility = "visible"; }
} else if (!this.player.alive && hpFill && apFill) {
@@ -2503,8 +2658,9 @@ Gamestate.handleEndTurn = async function(){
if(this.prevTarget) this.prevTarget.classList.remove('flash');
this.prevCountry = null; this.prevTarget = null;
- if (canManeuver) {
- this.stage = "Maneuver";
+if (canManeuver) {
+ this.stage = "Maneuver";
+ this.hasManeuvered = false; // Tracks if they moved a unit
this.maneuverSource = null; this.maneuverDest = null;
this.updateButtonText();
if (turnInfo) turnInfo.textContent = "Maneuver Phase";
@@ -2525,11 +2681,20 @@ Gamestate.handleEndTurn = async function(){
this.stage = "Commander Phase";
this.player.commander.ap = 2;
this.player.commander.hasFought = false; // Reset attack limit
+this.player.commander.hasBeenAmbushed = false; // Reset attack limit
this.aiTurn = false; // Guard against AI hijacking
this.updateButtonText();
if(turnInfo) turnInfo.textContent = "Commander Phase";
- if(turnInfoMessage) turnInfoMessage.textContent = "Move your Commander or use a Stimpak. (Costs 1 AP)";
+// --- DYNAMIC COMMANDER DIRECTIVE ---
+ if (this.player.commander) {
+ let canHeal = (this.player.commander.stimpaks > 0 && this.player.commander.hp < 100);
+ if (canHeal) {
+ turnInfoMessage.textContent = "Move your Commander or use a Stimpak. (Costs 1 AP per move)";
+ } else {
+ turnInfoMessage.textContent = "Move your Commander. (Costs 1 AP per move)";
+ }
+ }
this.updateInfo();
return;
}
@@ -2619,20 +2784,25 @@ Gamestate.attack = async function(e){
let trespasser = this.players.find(p => p !== this.player && p.alive && !p.isNeutral && p.commander && p.commander.hp > 0 && p.commander.loc === country.name);
if (this.commandersEnabled && trespasser) {
- // --- 1-TROOP EXPLOIT PATCH (PURGE) ---
if (country.army <= 1) {
this.showToast("Garrison too weak! Need at least 2 troops to execute a Purge.", "red");
e.target.classList.add('flash'); this.prevTarget = e.target; this.prevCountry = country; return;
}
+ if (trespasser.commander.hasBeenAmbushed) {
+ this.showToast("Target has already evaded a tactical strike this turn!", "red");
+ e.target.classList.add('flash'); this.prevTarget = e.target; this.prevCountry = country; return;
+ }
// Summon the UI in "Purge" mode (passing 'true' at the end)
let choice = await this.showTacticalModal(trespasser.name, formatTerritoryName(country.name), true);
if (choice === "ambush") {
- trespasser.commander.wasAttacked = true; // Pauses their subversion timer!
- // Your stationed troops immediately open fire on the trespasser!
+ trespasser.commander.wasAttacked = true;
+ trespasser.commander.hasBeenAmbushed = true; // LIMIT TO 1 PER ROUND
if (map) map.style.pointerEvents = "none";
+
let atkForce = country.army;
- let dmgToVip = atkForce * Math.floor(Math.random() * 4 + 2); // Deadly home-turf advantage!
+ // NERFED DAMAGE: 1 to 2 DMG per troop
+ let dmgToVip = atkForce * (Math.floor(Math.random() * 2) + 1);
let retaliation = Math.floor(Math.random() * 2);
trespasser.commander.hp -= dmgToVip;
@@ -2670,33 +2840,42 @@ if (this.commandersEnabled && trespasser) {
let attackChoice = "assault"; // Defaults to normal combat if no VIP is present
- // If an enemy Commander is hiding here, summon the UI!
+// If an enemy Commander is hiding here, enforce the new territory rules!
if (this.commandersEnabled && strandedCmdr) {
- attackChoice = await this.showTacticalModal(strandedCmdr.name, formatTerritoryName(country.name));
-
- // If the player clicked "ABORT ORDER"
- if (attackChoice === "cancel") {
- this.prevCountry = null; this.prevTarget = null; return;
+ let isCmdrOnOwnLand = (country.owner === strandedCmdr.name);
+ let isCmdrOnAllyLand = this.areAllies(strandedCmdr.name, country.owner);
+
+ if (isCmdrOnOwnLand || isCmdrOnAllyLand) {
+ // Protected by territory or allies! Must assault territory first.
+ attackChoice = "assault";
+ } else if (strandedCmdr.commander.hasBeenAmbushed) {
+ // Protected by the once-per-round limit!
+ attackChoice = "assault";
+ } else {
+ attackChoice = await this.showTacticalModal(strandedCmdr.name, formatTerritoryName(country.name));
+ if (attackChoice === "cancel") {
+ this.prevCountry = null; this.prevTarget = null; return;
+ }
}
}
-// BRANCH 1: THE ASSASSINATION (Suppressive Fire)
+ // BRANCH 1: THE ASSASSINATION (Suppressive Fire)
if (attackChoice === "ambush") {
- // --- 1-TROOP EXPLOIT PATCH ---
if (this.prevCountry.army <= 1) {
this.showToast("Cannot Ambush: Minimum garrison of 1 troop must remain.", "red");
this.prevCountry = null; this.prevTarget = null; return;
}
- strandedCmdr.commander.wasAttacked = true; // Pauses subversion timer
+ strandedCmdr.commander.wasAttacked = true;
+ strandedCmdr.commander.hasBeenAmbushed = true; // LIMIT TO 1 PER ROUND
let attackerMap = document.getElementById(this.prevCountry.name);
- let defenderMap = document.getElementById(country.name); // <--- This was the missing line!
-
+ let defenderMap = document.getElementById(country.name);
if (map) map.style.pointerEvents = "none";
await this.vatsTargeting(attackerMap, defenderMap);
let atkForce = this.prevCountry.army - 1;
- let dmgToVip = atkForce * Math.floor(Math.random() * 3 + 2); // 2 to 4 dmg per attacking troop
+ // NERFED DAMAGE: 1 to 2 DMG per troop
+ let dmgToVip = atkForce * (Math.floor(Math.random() * 2) + 1);
let retaliation = Math.floor(Math.random() * 3); // VIP fires back, kills 0 to 2 troops
strandedCmdr.commander.hp -= dmgToVip;
@@ -2759,13 +2938,17 @@ Gamestate.maneuver = async function(e){
let country = this.countries.find(c => c.name === e.target.id);
if(!country || country.isCrater) return;
- if(this.stage === "Commander Phase") {
+if(this.stage === "Commander Phase") {
if(!this.player.commander || this.player.commander.ap <= 0) return;
let cmdrLoc = this.countries.find(c => c.name === this.player.commander.loc);
if(!cmdrLoc) return;
- if(cmdrLoc.neighbours.includes(country.name)) {
- let enemyCmdr = this.players.find(p => p !== this.player && p.alive && !p.isNeutral && p.commander && p.commander.loc === country.name);
+ let isNeighbor = cmdrLoc.neighbours.includes(country.name);
+ let isCurrentLoc = (cmdrLoc.name === country.name);
+
+ if(isNeighbor || isCurrentLoc) {
+ // Look for an enemy commander on the clicked territory
+ let enemyCmdr = this.players.find(p => p !== this.player && p.alive && !p.isNeutral && p.commander && p.commander.hp > 0 && p.commander.loc === country.name);
if(enemyCmdr) {
if (this.player.commander.hasFought) {
@@ -2775,13 +2958,21 @@ Gamestate.maneuver = async function(e){
}
this.player.commander.ap -= 1;
- await this.logAction(`[ REGICIDE DUEL ] Commander initiated direct combat with ${enemyCmdr.name}'s Commander!`, true);
- // 3. Combat Math (Safe Home Advantage: 5-15 DMG Max)
+ // If we attacked from a neighbor, step into the territory to face them!
+ if (isNeighbor) {
+ this.player.commander.loc = country.name;
+ this.player.commander.siegeTurns = 0;
+ }
+
+ await this.logAction(`[ REGICIDE DUEL ] Commander initiated direct combat with ${enemyCmdr.name}'s Commander at ${formatTerritoryName(country.name)}!`, true);
+
+ // Combat Math (Safe Home Advantage: 5-15 DMG Max)
let rawDmgToEnemy = Math.floor(Math.random() * 16) + 10;
let rawDmgToSelf = Math.floor(Math.random() * 11) + 10;
- if (cmdrLoc.owner === this.player.name) { rawDmgToSelf = Math.floor(Math.random() * 11) + 5; }
+ // Check if the territory the duel is happening on belongs to you
+ if (country.owner === this.player.name) { rawDmgToSelf = Math.floor(Math.random() * 11) + 5; }
let cappedDmgToEnemy = Math.min(25, rawDmgToEnemy);
let cappedDmgToSelf = Math.min(25, rawDmgToSelf);
@@ -2798,7 +2989,8 @@ Gamestate.maneuver = async function(e){
if(this.player.commander.hp <= 0) await this.killCommander(this.player);
this.updateInfo();
- } else {
+ } else if (isNeighbor) {
+ // NO ENEMY COMMANDER - JUST MOVE
this.player.commander.loc = country.name; this.player.commander.ap -= 1;
this.player.commander.siegeTurns = 0; // Reset subversion timer on move
@@ -2841,18 +3033,21 @@ Gamestate.maneuver = async function(e){
}
moveAmount = input;
}
- country.army += moveAmount;
+country.army += moveAmount;
this.prevCountry.army -= moveAmount;
+ this.hasManeuvered = true; // Flips the button text
let sourceMap = document.getElementById(`${this.prevCountry.name}`); let destMap = document.getElementById(`${country.name}`);
if (sourceMap && sourceMap.nextElementSibling) sourceMap.nextElementSibling.textContent = this.prevCountry.army;
- if (destMap && destMap.nextElementSibling) destMap.nextElementSibling.textContent = country.army;
+if (destMap && destMap.nextElementSibling) destMap.nextElementSibling.textContent = country.army;
if(this.commandersEnabled && this.player.commander && this.player.commander.loc === this.prevCountry.name && e.shiftKey) {
this.player.commander.loc = country.name;
- this.player.commander.siegeTurns = 0; // Reset subversion timer
- this.logAction(`Commander escorted to ${formatTerritoryName(country.name)}.`);
+ if (Gamestate.logAction) this.logAction(`Commander escorted to ${formatTerritoryName(country.name)}.`);
}
+
+ this.hasManeuvered = true;
+ this.updateButtonText(); // <--- This forces the button to change!
this.updateInfo();
}
}
@@ -3015,6 +3210,8 @@ player.areas.forEach(areaName => {
}
if(winModal) winModal.style.display = "block";
}
+// NEW: Run the master win check to see if that was the final enemy!
+ this.checkWinCondition();
}
Gamestate.processRadDecay = async function() {
@@ -3162,6 +3359,49 @@ if (infoName[i-1]) infoName[i-1].parentElement.classList.remove('highlight');
}
}
+Gamestate.checkWinCondition = function() {
+ if (this.gameOver) return; // Stop if the game is already over!
+
+ // Rule 1: Do you own every single piece of playable land?
+ let totalPlayableLand = this.countries.filter(c => !c.isCrater).length;
+ let playerLand = this.countries.filter(c => c.owner === this.player.name).length;
+ let ownsAllLand = (playerLand >= totalPlayableLand);
+
+ // Rule 2: Are all rival factions/commanders wiped out?
+ let allRivalsDead = true;
+ if (this.commandersEnabled) {
+ // Scans the globe for any rival commander with a heartbeat
+ let livingRivalCmdrs = this.players.filter(p => p !== this.player && !p.isNeutral && p.alive && p.commander && p.commander.hp > 0);
+ if (livingRivalCmdrs.length > 0) allRivalsDead = false;
+ } else {
+ // Standard Risk rules if commanders are disabled
+ let livingRivals = this.players.filter(p => p !== this.player && !p.isNeutral && p.alive);
+ if (livingRivals.length > 0) allRivalsDead = false;
+ }
+
+ // WIN TRIGGER: Must meet BOTH conditions!
+ if (ownsAllLand && allRivalsDead) {
+ this.gameOver = true;
+ let winModal = document.querySelector('#win-modal');
+ let winMessage = document.querySelector('.win-message');
+
+ if(winMessage) {
+ winMessage.textContent = "TOTAL DOMINATION!";
+ winMessage.style.color = "var(--pip-color)";
+ let subMsg = winMessage.nextElementSibling;
+ if(subMsg && subMsg.tagName === 'P') {
+ if (this.commandersEnabled) {
+ subMsg.textContent = "All rival commanders have been executed and the wasteland is entirely under your control.";
+ } else {
+ subMsg.textContent = "You have conquered all territories in the wasteland.";
+ }
+ }
+ }
+ if(winModal) winModal.style.display = "block";
+ }
+};
+
+
// AI DIPLOMACY LOGIC
if(Math.random() < 0.15 && this.players[i].cards.length > 0) {
let potentialAllies = this.players.filter(p => p !== this.players[i] && p.alive && !p.isNeutral && !this.areAllies(this.players[i].name, p.name) && (!this.diplomacy.grudges[p.name] || !this.diplomacy.grudges[p.name].includes(this.players[i].name)));
@@ -3238,10 +3478,13 @@ if (infoName[i-1]) infoName[i-1].parentElement.classList.remove('highlight');
if(this.commandersEnabled && this.players[i].commander) {
let loc = this.countries.find(c => c.name === this.players[i].commander.loc);
- if(loc && loc.owner === this.players[i].name) { let regen = loc.isSilo ? 4 : 2; this.players[i].commander.hp = Math.min(100, this.players[i].commander.hp + regen); }
+ if(loc && loc.owner === this.players[i].name) { let regen = loc.isSilo ? 4 : 2; this.players[i].commander.hp = Math.min(100, this.players[i].commander.hp + regen);
+ }
- // FIX: Refill the AI Commander's Action Points every turn!
+ // FIX: Refill the AI Commander's Action Points & Combat Fatigue every turn!
this.players[i].commander.ap = 2;
+ this.players[i].commander.hasBeenAmbushed = false;
+ this.players[i].commander.hasFought = false; // Wakes the AI up!
}
this.updateInfo();
@@ -3300,7 +3543,19 @@ Gamestate.aiManeuver = function(i){
let player = this.players[i];
let owned = this.countries.filter(c => c.owner === player.name);
+ // Helper to check how many commanders are on a territory
+ let getCmdrCount = (locName) => {
+ return this.players.filter(p => p.alive && p.commander && p.commander.hp > 0 && p.commander.loc === locName).length;
+ };
+
+ // --- COMMANDER AI LOGIC ---
if(this.commandersEnabled && player.commander && player.commander.hp > 0) {
+
+ // FOOLPROOF RESET: Wake up!
+ player.commander.ap = 2;
+ player.commander.hasFought = false;
+ player.commander.hasBeenAmbushed = false;
+
let maxLoops = 5;
while(player.commander.ap > 0 && maxLoops > 0) {
maxLoops--;
@@ -3311,137 +3566,166 @@ Gamestate.aiManeuver = function(i){
let neighbors = cmdrLoc.neighbours.map(n => this.countries.find(x=>x.name===n)).filter(c => c !== undefined);
let friendlyNeighbors = neighbors.filter(c => c.owner === player.name);
- // 1. RETREAT
- if (player.commander.hp < 40 && friendlyNeighbors.length > 0) {
- friendlyNeighbors.sort((a,b) => b.army - a.army);
- if (friendlyNeighbors[0].army > cmdrLoc.army || (this.nukesEnabled && friendlyNeighbors[0].isSilo)) {
- player.commander.loc = friendlyNeighbors[0].name;
- player.commander.ap -= 1; movedOrAction = true;
- player.commander.siegeTurns = 0; // Reset subversion timer
- if (Gamestate.logAction) Gamestate.logAction(`VIP MOVEMENT: ${player.name}'s Commander retreats to ${formatTerritoryName(friendlyNeighbors[0].name)}.`);
- }
- }
-
- // 2. ATTACK
- if (!movedOrAction && !player.commander.hasFought && player.commander.hp >= 40) {
- let enemyCmdrs = [];
- neighbors.forEach(n => {
- let eCmdr = this.players.find(p => p !== player && p.alive && !p.isNeutral && p.commander && p.commander.hp > 0 && p.commander.loc === n.name);
- if (eCmdr) enemyCmdrs.push(eCmdr);
- });
-
- if (enemyCmdrs.length > 0) {
- enemyCmdrs.sort((a,b) => a.commander.hp - b.commander.hp);
- let target = enemyCmdrs[0];
-
- if (player.commander.hp >= target.commander.hp || player.commander.hp > 70) {
- player.commander.ap -= 1; movedOrAction = true;
-
- let rawDmgToTarget = Math.floor(Math.random() * 16) + 10;
- let rawDmgToSelf = Math.floor(Math.random() * 11) + 10;
-
- if (cmdrLoc.owner === player.name) { rawDmgToSelf = Math.floor(Math.random() * 11) + 5; }
- let targetLoc = this.countries.find(c => c.name === target.commander.loc);
- if (targetLoc && targetLoc.owner === target.name) { rawDmgToTarget = Math.floor(Math.random() * 11) + 5; }
-
- let cappedDmgToTarget = Math.min(25, rawDmgToTarget);
- let cappedDmgToSelf = Math.min(25, rawDmgToSelf);
-
- target.commander.hp -= cappedDmgToTarget;
- player.commander.hp -= cappedDmgToSelf;
- player.commander.hasFought = true;
- target.commander.wasAttacked = true;
- player.commander.wasAttacked = true;
-
- if (Gamestate.logAction) Gamestate.logAction(`[ REGICIDE DUEL ] ${player.name} attacked ${target.name}'s Commander! (Dealt ${cappedDmgToTarget} DMG, Took ${cappedDmgToSelf} DMG)`, true);
- if(target.commander.hp <= 0) this.killCommander(target);
- if(player.commander.hp <= 0) this.killCommander(player);
+ // Collect enemy commanders in adjacent territories OR on the SAME territory!
+ let enemyCmdrs = [];
+ this.players.forEach(p => {
+ if (p !== player && p.alive && !p.isNeutral && p.commander && p.commander.hp > 0) {
+ if (p.commander.loc === cmdrLoc.name || cmdrLoc.neighbours.includes(p.commander.loc)) {
+ enemyCmdrs.push(p);
}
}
- }
+ });
+ enemyCmdrs.sort((a,b) => a.commander.hp - b.commander.hp);
- // 3. P.A.C.E.
- if (!movedOrAction) {
+ // PRIORITY 4: RETREAT (HP < 50% - Retreating sooner!)
+ if (player.commander.hp < 50) {
if (friendlyNeighbors.length > 0) {
- let silo = friendlyNeighbors.find(c => this.nukesEnabled && c.isSilo);
- if (silo && cmdrLoc.name !== silo.name) {
- player.commander.loc = silo.name;
+ // Find a safe spot that isn't already crowded
+ let safeSpots = friendlyNeighbors.filter(c =>
+ !c.neighbours.some(n => { let nc = this.countries.find(x=>x.name===n); return nc && nc.owner !== player.name; }) &&
+ getCmdrCount(c.name) < 2
+ );
+ if (safeSpots.length === 0) safeSpots = friendlyNeighbors.filter(c => getCmdrCount(c.name) < 2);
+ if (safeSpots.length === 0) safeSpots = friendlyNeighbors; // Desperate fallback
+
+ let silo = safeSpots.find(c => this.nukesEnabled && c.isSilo);
+ safeSpots.sort((a,b) => b.army - a.army);
+ let retreatTarget = silo ? silo : safeSpots[0];
+
+ if (retreatTarget.name !== cmdrLoc.name && (retreatTarget.army > cmdrLoc.army || silo)) {
+ player.commander.loc = retreatTarget.name;
player.commander.ap -= 1; movedOrAction = true;
player.commander.siegeTurns = 0;
- } else if (!silo) {
- let chokePoints = friendlyNeighbors.filter(c => c.neighbours.some(n => {
- let nc = this.countries.find(x=>x.name===n); return nc && nc.owner !== player.name && !nc.isCrater;
- }));
- if (chokePoints.length > 0) {
- chokePoints.sort((a,b) => b.army - a.army);
- player.commander.loc = chokePoints[0].name; player.commander.ap -= 1; movedOrAction = true;
+ if (Gamestate.logAction) Gamestate.logAction(`VIP MOVEMENT: ${player.name}'s Commander retreats to ${formatTerritoryName(retreatTarget.name)}.`);
+ continue;
+ }
+ } else if (neighbors.length > 0) {
+ // DESPERATE ESCAPE: Run anywhere to survive, but avoid crowding!
+ let escapeOptions = neighbors.filter(c => getCmdrCount(c.name) < 2);
+ if (escapeOptions.length === 0) escapeOptions = neighbors;
+ let escapeRoute = escapeOptions[Math.floor(Math.random() * escapeOptions.length)];
+ player.commander.loc = escapeRoute.name;
+ player.commander.ap -= 1; movedOrAction = true;
+ player.commander.siegeTurns = 0;
+ if (Gamestate.logAction) Gamestate.logAction(`VIP MOVEMENT: ${player.name}'s stranded Commander desperately flees to ${formatTerritoryName(escapeRoute.name)}!`);
+ continue;
+ }
+ }
+
+ // PRIORITIES 1 & 3: DUEL ENEMY COMMANDERS
+ if (!movedOrAction && !player.commander.hasFought) {
+ let targetToDuel = null;
+ for (let target of enemyCmdrs) {
+ let targetLoc = this.countries.find(c => c.name === target.commander.loc);
+
+ // Don't dive into heavily crowded territories
+ if (target.commander.loc !== cmdrLoc.name && getCmdrCount(target.commander.loc) >= 2) continue;
+
+ let isHomeDefense = (cmdrLoc.owner === player.name || (targetLoc && targetLoc.owner === player.name));
+ let isHealthy = (player.commander.hp >= 65); // Need solid HP to hunt
+ let isTargetWeak = (player.commander.hp > target.commander.hp + 20);
+
+ if (isHomeDefense || isHealthy || isTargetWeak) {
+ targetToDuel = target; break;
+ }
+ }
+
+ if (targetToDuel) {
+ // Move to their territory if not already there
+ if (player.commander.loc !== targetToDuel.commander.loc) {
+ player.commander.loc = targetToDuel.commander.loc;
+ player.commander.ap -= 1;
+ player.commander.siegeTurns = 0;
+ } else {
+ player.commander.ap -= 1; // Uses 1 AP to fight on the same tile
+ }
+ movedOrAction = true;
+
+ let rawDmgToTarget = Math.floor(Math.random() * 16) + 10;
+ let rawDmgToSelf = Math.floor(Math.random() * 11) + 10;
+
+ // BUG FIX: Strictly check the territory they are standing on AFTER they moved to attack
+ let currentLocAfterMove = this.countries.find(c => c.name === player.commander.loc);
+
+ if (currentLocAfterMove && currentLocAfterMove.owner === player.name) {
+ rawDmgToSelf = Math.floor(Math.random() * 11) + 5;
+ player.commander.hp = Math.min(100, player.commander.hp + 10);
+ }
+ if (currentLocAfterMove && currentLocAfterMove.owner === targetToDuel.name) {
+ rawDmgToTarget = Math.floor(Math.random() * 11) + 5;
+ }
+
+ let cappedDmgToTarget = Math.min(25, rawDmgToTarget);
+ let cappedDmgToSelf = Math.min(25, rawDmgToSelf);
+
+ targetToDuel.commander.hp -= cappedDmgToTarget;
+ player.commander.hp -= cappedDmgToSelf;
+ player.commander.hasFought = true;
+ targetToDuel.commander.wasAttacked = true;
+ player.commander.wasAttacked = true;
+
+ if (Gamestate.logAction) Gamestate.logAction(`[ REGICIDE DUEL ] ${player.name} engaged ${targetToDuel.name}'s Commander at ${formatTerritoryName(currentLocAfterMove.name)}! (Dealt ${cappedDmgToTarget} DMG, Took ${cappedDmgToSelf} DMG)`, true);
+ if(targetToDuel.commander.hp <= 0) this.killCommander(targetToDuel);
+ if(player.commander.hp <= 0) this.killCommander(player);
+ continue;
+ }
+ }
+
+ // PRIORITY 2: TACTICAL POSITIONING (Don't stray too far)
+ if (!movedOrAction) {
+ if (friendlyNeighbors.length > 0) {
+ // Find a border territory (chokepoint) that isn't crowded
+ let chokePoints = friendlyNeighbors.filter(c =>
+ getCmdrCount(c.name) < 2 &&
+ c.neighbours.some(n => { let nc = this.countries.find(x=>x.name===n); return nc && nc.owner !== player.name && !nc.isCrater; })
+ );
+ if (chokePoints.length > 0) {
+ chokePoints.sort((a,b) => b.army - a.army);
+ if (chokePoints[0].name !== cmdrLoc.name) {
+ player.commander.loc = chokePoints[0].name;
+ player.commander.ap -= 1; movedOrAction = true;
player.commander.siegeTurns = 0;
- } else {
- friendlyNeighbors.sort((a,b) => b.army - a.army);
- if (friendlyNeighbors[0].army > cmdrLoc.army + 3) {
- player.commander.loc = friendlyNeighbors[0].name;
- player.commander.ap -= 1; movedOrAction = true;
- player.commander.siegeTurns = 0;
- }
+ continue;
+ }
+ } else if (cmdrLoc.owner !== player.name) {
+ // Stranded? Go back to friendly land!
+ let uncrowdedFriendly = friendlyNeighbors.filter(c => getCmdrCount(c.name) < 2);
+ if(uncrowdedFriendly.length === 0) uncrowdedFriendly = friendlyNeighbors;
+ uncrowdedFriendly.sort((a,b) => b.army - a.army);
+ player.commander.loc = uncrowdedFriendly[0].name;
+ player.commander.ap -= 1; movedOrAction = true;
+ player.commander.siegeTurns = 0;
+ }
+ } else {
+ // STRANDED TACTICS: Try to path TOWARDS home instead of randomly wandering.
+ let pathHome = neighbors.find(n => {
+ let nc = this.countries.find(x=>x.name===n);
+ return nc && nc.neighbours.some(nn => { let nnc = this.countries.find(x=>x.name===nn); return nnc && nnc.owner === player.name; });
+ });
+
+ if (pathHome && getCmdrCount(pathHome) < 2) {
+ player.commander.loc = pathHome;
+ player.commander.ap -= 1; movedOrAction = true;
+ player.commander.siegeTurns = 0;
+ if (Gamestate.logAction) Gamestate.logAction(`VIP MOVEMENT: ${player.name}'s stranded Commander is falling back towards friendly lines!`);
+ continue;
+ } else if (neighbors.length > 0 && Math.random() < 0.20) {
+ // Only a 20% chance to roam randomly if totally lost
+ let uncrowded = neighbors.filter(c => getCmdrCount(c.name) < 2);
+ if(uncrowded.length > 0) {
+ let wanderTarget = uncrowded[Math.floor(Math.random() * uncrowded.length)];
+ player.commander.loc = wanderTarget.name;
+ player.commander.ap -= 1; movedOrAction = true;
+ player.commander.siegeTurns = 0;
+ continue;
}
}
- } else if (neighbors.length > 0 && Math.random() < 0.80) {
- let wanderTarget = neighbors[Math.floor(Math.random() * neighbors.length)];
- player.commander.loc = wanderTarget.name; player.commander.ap -= 1; movedOrAction = true;
- player.commander.siegeTurns = 0;
- if (Gamestate.logAction) Gamestate.logAction(`VIP MOVEMENT: ${player.name}'s stranded Commander is wandering through ${formatTerritoryName(wanderTarget.name)}.`);
}
}
if (!movedOrAction || player.commander.hp <= 0) break;
}
}
-// --- 1. ACTIVATE THE STIMPAK BUTTON ---
-document.getElementById('dev-stimpak').onclick = () => {
- if (Gamestate.player && Gamestate.player.commander) {
- Gamestate.player.commander.stimpaks++;
- Gamestate.updateInfo();
- if (Gamestate.logAction) Gamestate.logAction("OVERSEER OVERRIDE: +1 Stimpak added to inventory.");
- }
-};
-
-// --- 2. DYNAMIC DEV MENU LOCKOUTS ---
-// Call this function right before you open your Dev Modal!
-Gamestate.refreshDevMenuStatus = function() {
- let btnHeal = document.getElementById('dev-heal');
- let btnStimpak = document.getElementById('dev-stimpak');
- let btnCode = document.getElementById('dev-code');
- let btnStorm = document.getElementById('dev-storm');
-
- // 1. DIRECTLY READ THE START MENU CHECKBOXES
- let cmdrOn = document.getElementById('opt-commander') ? document.getElementById('opt-commander').checked : false;
- let nukesOn = document.getElementById('opt-nukes') ? document.getElementById('opt-nukes').checked : false;
- let stormsOn = document.getElementById('opt-radstorms') ? document.getElementById('opt-radstorms').checked : false;
-
- // 2. HANDLE COMMANDER BUTTONS
- if (cmdrOn) {
- if(btnHeal) { btnHeal.disabled = false; btnHeal.style.opacity = "1"; btnHeal.style.cursor = "pointer"; btnHeal.style.pointerEvents = "auto"; }
- if(btnStimpak) { btnStimpak.disabled = false; btnStimpak.style.opacity = "1"; btnStimpak.style.cursor = "pointer"; btnStimpak.style.pointerEvents = "auto"; }
- } else {
- if(btnHeal) { btnHeal.disabled = true; btnHeal.style.opacity = "0.2"; btnHeal.style.cursor = "not-allowed"; btnHeal.style.pointerEvents = "none"; }
- if(btnStimpak) { btnStimpak.disabled = true; btnStimpak.style.opacity = "0.2"; btnStimpak.style.cursor = "not-allowed"; btnStimpak.style.pointerEvents = "none"; }
- }
-
- // 3. HANDLE NUKE BUTTON
- if (nukesOn) {
- if(btnCode) { btnCode.disabled = false; btnCode.style.opacity = "1"; btnCode.style.cursor = "pointer"; btnCode.style.pointerEvents = "auto"; }
- } else {
- if(btnCode) { btnCode.disabled = true; btnCode.style.opacity = "0.2"; btnCode.style.cursor = "not-allowed"; btnCode.style.pointerEvents = "none"; }
- }
-
- // 4. HANDLE RADSTORM BUTTON
- if (stormsOn) {
- if(btnStorm) { btnStorm.disabled = false; btnStorm.style.opacity = "1"; btnStorm.style.cursor = "pointer"; btnStorm.style.pointerEvents = "auto"; }
- } else {
- if(btnStorm) { btnStorm.disabled = true; btnStorm.style.opacity = "0.2"; btnStorm.style.cursor = "not-allowed"; btnStorm.style.pointerEvents = "none"; }
- }
-};
-
// --- REGULAR TROOP MANEUVER LOGIC ---
let internal = owned.filter(c => c.army > 1 && c.neighbours.every(n => { let nc = this.countries.find(x => x.name === n); return nc && nc.owner === player.name && !nc.isCrater; }));
if(internal.length > 0) {
@@ -3453,10 +3737,52 @@ Gamestate.refreshDevMenuStatus = function() {
let siloNeighbor = source.neighbours.find(n => { let nc = this.countries.find(x => x.name === n); return nc && nc.owner === player.name && nc.isSilo; });
if(siloNeighbor) dest = this.countries.find(x => x.name === siloNeighbor);
}
- let moveAmount = source.army - 1; dest.army += moveAmount; source.army -= moveAmount;
+ let moveAmount = source.army - 1;
+ dest.army += moveAmount; source.army -= moveAmount;
}
}
}
+
+// --- 1. ACTIVATE THE STIMPAK BUTTON ---
+document.getElementById('dev-stimpak').onclick = () => {
+ if (Gamestate.player && Gamestate.player.commander) {
+ Gamestate.player.commander.stimpaks++;
+ Gamestate.updateInfo();
+ if (Gamestate.logAction) Gamestate.logAction("OVERSEER OVERRIDE: +1 Stimpak added to inventory.");
+ }
+};
+
+// --- 2. DYNAMIC DEV MENU LOCKOUTS ---
+Gamestate.refreshDevMenuStatus = function() {
+ let btnHeal = document.getElementById('dev-heal');
+ let btnStimpak = document.getElementById('dev-stimpak');
+ let btnCode = document.getElementById('dev-code');
+ let btnStorm = document.getElementById('dev-storm');
+
+ let cmdrOn = document.getElementById('opt-commander') ? document.getElementById('opt-commander').checked : false;
+ let nukesOn = document.getElementById('opt-nukes') ? document.getElementById('opt-nukes').checked : false;
+ let stormsOn = document.getElementById('opt-radstorms') ? document.getElementById('opt-radstorms').checked : false;
+
+ if (cmdrOn) {
+ if(btnHeal) { btnHeal.disabled = false; btnHeal.style.opacity = "1"; btnHeal.style.cursor = "pointer"; btnHeal.style.pointerEvents = "auto"; }
+ if(btnStimpak) { btnStimpak.disabled = false; btnStimpak.style.opacity = "1"; btnStimpak.style.cursor = "pointer"; btnStimpak.style.pointerEvents = "auto"; }
+ } else {
+ if(btnHeal) { btnHeal.disabled = true; btnHeal.style.opacity = "0.2"; btnHeal.style.cursor = "not-allowed"; btnHeal.style.pointerEvents = "none"; }
+ if(btnStimpak) { btnStimpak.disabled = true; btnStimpak.style.opacity = "0.2"; btnStimpak.style.cursor = "not-allowed"; btnStimpak.style.pointerEvents = "none"; }
+ }
+
+ if (nukesOn) {
+ if(btnCode) { btnCode.disabled = false; btnCode.style.opacity = "1"; btnCode.style.cursor = "pointer"; btnCode.style.pointerEvents = "auto"; }
+ } else {
+ if(btnCode) { btnCode.disabled = true; btnCode.style.opacity = "0.2"; btnCode.style.cursor = "not-allowed"; btnCode.style.pointerEvents = "none"; }
+ }
+
+ if (stormsOn) {
+ if(btnStorm) { btnStorm.disabled = false; btnStorm.style.opacity = "1"; btnStorm.style.cursor = "pointer"; btnStorm.style.pointerEvents = "auto"; }
+ } else {
+ if(btnStorm) { btnStorm.disabled = true; btnStorm.style.opacity = "0.2"; btnStorm.style.cursor = "not-allowed"; btnStorm.style.pointerEvents = "none"; }
+ }
+};
Gamestate.battle = async function(country, opponent, player, i){
let defender = document.getElementById(`${opponent.name}`); let attacker = document.getElementById(`${country.name}`);
@@ -3686,6 +4012,22 @@ Gamestate.fixMapTextOrder = function() {
// Then boot the game!
+// Dynamic Player Name Auto-fill for Start Menu
+document.getElementById('chosen-theme')?.addEventListener('change', function(e) {
+ let leader = document.getElementById('chosen-leader');
+ let faction = document.getElementById('chosen-country');
+ if (e.target.value === 'fo3') {
+ if(leader) leader.value = "Lone Wanderer";
+ if(faction) faction.value = "Brotherhood of Steel";
+ } else if (e.target.value === 'fnv') {
+ if(leader) leader.value = "Courier Six";
+ if(faction) faction.value = "New California Republic";
+ } else if (e.target.value === 'fo4') {
+ if(leader) leader.value = "Sole Survivor";
+ if(faction) faction.value = "The Minutemen";
+ }
+});
+
Gamestate.init();