Upload files to "/"
This commit is contained in:
parent
1680f12981
commit
19cd9eeece
162
index.html
162
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 += `<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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue