From 19cd9eeecebf886b05db27ec8bc76a86eb57f02a Mon Sep 17 00:00:00 2001 From: threememories Date: Mon, 13 Apr 2026 22:53:20 +0000 Subject: [PATCH] Upload files to "/" --- index.html | 164 ++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 117 insertions(+), 47 deletions(-) diff --git a/index.html b/index.html index 21ede5d..de4def4 100644 --- a/index.html +++ b/index.html @@ -1910,9 +1910,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"; @@ -1920,17 +1921,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}`; + } } }); } @@ -2799,13 +2811,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) { @@ -2815,13 +2831,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); @@ -2838,7 +2862,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 @@ -3346,10 +3371,15 @@ 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) { - // ---> NEW: FOOLPROOF RESET: Ensure the Commander wakes up ready for action! <--- + // FOOLPROOF RESET: Wake up! player.commander.ap = 2; player.commander.hasFought = false; player.commander.hasBeenAmbushed = false; @@ -3364,20 +3394,28 @@ 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); + // Collect enemy commanders in adjacent territories OR on the SAME territory! 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); + 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); - // PRIORITY 4: RETREAT (HP < 35%) - if (player.commander.hp < 35) { + // PRIORITY 4: RETREAT (HP < 50% - Retreating sooner!) + if (player.commander.hp < 50) { if (friendlyNeighbors.length > 0) { - let safeSpots = friendlyNeighbors.filter(c => !c.neighbours.some(n => { - let nc = this.countries.find(x=>x.name===n); return nc && nc.owner !== player.name; - })); - if (safeSpots.length === 0) safeSpots = friendlyNeighbors; + // 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]; @@ -3390,8 +3428,10 @@ Gamestate.aiManeuver = function(i){ continue; } } else if (neighbors.length > 0) { - // DESPERATE ESCAPE: Completely surrounded! Run anywhere! - let escapeRoute = neighbors[Math.floor(Math.random() * neighbors.length)]; + // 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; @@ -3400,34 +3440,46 @@ Gamestate.aiManeuver = function(i){ } } - // PRIORITIES 1 & 3: ATTACK ENEMY COMMANDERS + // 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 isStranded = (cmdrLoc.owner !== player.name); // Be aggressive if deep in enemy lines! - let isHealthy = (player.commander.hp >= 50); // Lowered threshold for more action - let isTargetWeak = (player.commander.hp > target.commander.hp + 10); + let isHealthy = (player.commander.hp >= 65); // Need solid HP to hunt + let isTargetWeak = (player.commander.hp > target.commander.hp + 20); - if (isHomeDefense || isStranded || isHealthy || isTargetWeak) { + if (isHomeDefense || isHealthy || isTargetWeak) { targetToDuel = target; break; } } if (targetToDuel) { - player.commander.ap -= 1; movedOrAction = true; + // 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; - if (cmdrLoc.owner === player.name) { + // 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); } - - let targetLoc = this.countries.find(c => c.name === targetToDuel.commander.loc); - if (targetLoc && targetLoc.owner === targetToDuel.name) { + if (currentLocAfterMove && currentLocAfterMove.owner === targetToDuel.name) { rawDmgToTarget = Math.floor(Math.random() * 11) + 5; } @@ -3440,19 +3492,21 @@ Gamestate.aiManeuver = function(i){ targetToDuel.commander.wasAttacked = true; player.commander.wasAttacked = true; - if (Gamestate.logAction) Gamestate.logAction(`[ REGICIDE DUEL ] ${player.name} attacked ${targetToDuel.name}'s Commander! (Dealt ${cappedDmgToTarget} DMG, Took ${cappedDmgToSelf} DMG)`, 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: FRONTLINE DEFENSE BUFF OR STRANDED WANDERING + // PRIORITY 2: TACTICAL POSITIONING (Don't stray too far) if (!movedOrAction) { if (friendlyNeighbors.length > 0) { - 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; - })); + // 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) { @@ -3462,21 +3516,37 @@ Gamestate.aiManeuver = function(i){ continue; } } else if (cmdrLoc.owner !== player.name) { - friendlyNeighbors.sort((a,b) => b.army - a.army); - player.commander.loc = friendlyNeighbors[0].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 { - // ---> NEW: COMMANDER IS STRANDED AND NO ONE IS AROUND TO FIGHT! <--- - // 30% chance to roam randomly (so they don't look frozen), 70% chance to sit still and incite a rebellion! - if (neighbors.length > 0 && Math.random() < 0.30) { - let wanderTarget = neighbors[Math.floor(Math.random() * neighbors.length)]; - player.commander.loc = wanderTarget.name; + // 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 roaming through ${formatTerritoryName(wanderTarget.name)}.`); + 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; + } } } }