Upload files to "/"

This commit is contained in:
threememories 2026-04-13 22:53:20 +00:00
parent 1680f12981
commit 19cd9eeece
1 changed files with 117 additions and 47 deletions

View File

@ -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 += `<tspan fill="${siloColor}" font-size="22px" ${pulse}></tspan> `;
}
// 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 += `<tspan fill="${p.color}" font-size="20px" filter="drop-shadow(0 0 3px #000)"></tspan> `;
// 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 += `<tspan fill="${p.color}" font-size="20px" filter="drop-shadow(0 0 3px #000)" dx="2"></tspan>`;
}
});
}
if (iconHtml !== "") { text += `<tspan dy="-15">${iconHtml}</tspan>`; }
// 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 = `<tspan x="${xCoord}" dy="-12">${iconHtml}</tspan><tspan x="${xCoord}" dy="16">${country.army}</tspan>`;
} else {
// If no icons, just reset the army number to normal
textNode.innerHTML = `<tspan x="${xCoord}" dy="3">${country.army}</tspan>`;
}
}
});
}
@ -2804,8 +2816,12 @@ Gamestate.maneuver = async function(e){
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);
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);
// Don't dive into heavily crowded territories
if (target.commander.loc !== cmdrLoc.name && getCmdrCount(target.commander.loc) >= 2) continue;
if (isHomeDefense || isStranded || isHealthy || isTargetWeak) {
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) {
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,24 +3516,40 @@ 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)];
// 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;
if (Gamestate.logAction) Gamestate.logAction(`VIP MOVEMENT: ${player.name}'s stranded Commander is roaming through ${formatTerritoryName(wanderTarget.name)}.`);
continue;
}
}
}
}
if (!movedOrAction || player.commander.hp <= 0) break;
}
}