Upload files to "/"

This commit is contained in:
threememories 2026-04-30 20:34:57 +00:00
parent e30b33a688
commit 2c6cfccc40
1 changed files with 331 additions and 50 deletions

View File

@ -7914,8 +7914,16 @@ Gamestate.openDiplomacy = function (targetName) {
// STAGE 1: SELECTING THE ATTACKING TERRITORY // STAGE 1: SELECTING THE ATTACKING TERRITORY
if (!this.prevCountry) { if (!this.prevCountry) {
if (country.owner === this.player.name && country.army > 1) { if (country.owner === this.player.name && country.army > 1) {
// NEW: Block attacking if exploring
if (country.isExploring) {
if (Gamestate.showToast) Gamestate.showToast("Cannot attack: Troops are currently exploring.", "red");
return;
}
this.prevCountry = country; // Source selected this.prevCountry = country; // Source selected
e.target.classList.add('flash'); e.target.classList.add('flash');
this.prevTarget = e.target; this.prevTarget = e.target;
if (turnInfoMessage) turnInfoMessage.textContent = "Now, select an adjacent enemy territory to ATTACK."; if (turnInfoMessage) turnInfoMessage.textContent = "Now, select an adjacent enemy territory to ATTACK.";
} else { } else {
@ -7994,7 +8002,15 @@ Gamestate.openDiplomacy = function (targetName) {
// WHEN YOU CLICK YOUR FIRST TERRITORY (Selecting the attacker) // WHEN YOU CLICK YOUR FIRST TERRITORY (Selecting the attacker)
if (!this.prevCountry) { if (!this.prevCountry) {
if (country.owner === this.player.name) { if (country.owner === this.player.name) {
// NEW: Block attacking if exploring
if (country.isExploring) {
if (Gamestate.showToast) Gamestate.showToast("Cannot attack: Troops are currently exploring.", "red");
return;
}
// --- INTERNAL PURGE LOGIC --- // --- INTERNAL PURGE LOGIC ---
let trespasser = this.players.find(p => p !== this.player && p.alive && !p.isNeutral && p.commander && p.commander.hp > 0 && p.commander.loc === country.name); 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) { if (this.commandersEnabled && trespasser) {
if (country.army <= 1) { if (country.army <= 1) {
@ -8173,7 +8189,14 @@ Gamestate.openDiplomacy = function (targetName) {
if (this.showToast) this.showToast("Maneuver blocked: Territory is under Elder's Edict lockdown.", "red"); if (this.showToast) this.showToast("Maneuver blocked: Territory is under Elder's Edict lockdown.", "red");
return; return;
} }
// NEW: Block maneuvering if exploring
if (country.isExploring) {
if (Gamestate.showToast) Gamestate.showToast("Maneuver blocked: Troops are currently exploring.", "red");
return;
}
if (country.army <= 1) { if (country.army <= 1) {
if (this.showToast) this.showToast("Maneuver blocked: Must have more than 1 troop to move.", "red"); if (this.showToast) this.showToast("Maneuver blocked: Must have more than 1 troop to move.", "red");
return; return;
} }
@ -8593,10 +8616,23 @@ Gamestate.openDiplomacy = function (targetName) {
if (i === this.players.length) { if (i === this.players.length) {
if (this.player.areas.length === 0 || (this.commandersEnabled && this.player.commander && this.player.commander.hp <= 0)) { if (this.player.areas.length === 0 || (this.commandersEnabled && this.player.commander && this.player.commander.hp <= 0)) {
this.player.alive = false; this.player.alive = false;
setTimeout(() => this.aiMove(), 100); this.checkWinCondition(); // This triggers the defeat modal instantly
return; return;
} }
// --- NEW: Process Exploration Countdowns FIRST ---
for (let c of this.countries) {
if (c.owner === this.player.name && c.isExploring) {
c.exploreTurnsLeft--;
if (c.exploreTurnsLeft <= 0) {
await this.resolveExplorationOutcome(c);
}
}
}
if (this.bobbleheads) { if (this.bobbleheads) {
this.bobbleheads.forEach(b => { this.bobbleheads.forEach(b => {
if (b.active) { if (b.active) {
b.active = false; b.active = false;
@ -9000,7 +9036,11 @@ Gamestate.openDiplomacy = function (targetName) {
let country = this.countries.find(c => c.name === area); let country = this.countries.find(c => c.name === area);
if (country && country.army > 1 && country.owner === this.players[i].name && (!this.nukesEnabled || !country.isSilo || Math.random() < 0.2)) { if (country && country.army > 1 && country.owner === this.players[i].name && (!this.nukesEnabled || !country.isSilo || Math.random() < 0.2)) {
await this.aiAttack(country, i, turbo); await this.aiAttack(country, i, turbo);
// NEW: If this attack ended the game, freeze the AI instantly!
if (this.gameOver) return;
} }
} }
this.aiManeuver(i); this.aiManeuver(i);
if (this.players[i].conqueredThisTurn) { if (this.players[i].conqueredThisTurn) {
@ -9641,11 +9681,31 @@ Gamestate.openDiplomacy = function (targetName) {
isVictory = true; isVictory = true;
player.conqueredThisTurn = true; player.conqueredThisTurn = true;
// --- THIS IS THE NEW ENCOUNTER TRIGGER --- // --- NEW: EXPEDITION WIPED OUT CHECK ---
await this.triggerEncounterCheck('post_conquest'); if (opponent.isExploring) {
// Only show the modal if the HUMAN player's expedition was destroyed
if (originalOwner === this.player.name) {
const title = "Signal Lost";
const msg = `>>> CONNECTION SEVERED <<<<br><br>The expedition force searching the ${opponent.explorePOI || 'area'} at ${formatTerritoryName(opponent.name)} has been completely wiped out by an attack from ${player.name}.<br><br>All hands and recovered assets are lost.`;
const choices = [{ id: "acknowledge", text: "[Acknowledge] A tragic loss." }];
this.modalIsOpen = true;
await this.showEncounterModal(title, msg, choices, () => {
this.logAction(`[ TRAGEDY ] The expedition at ${formatTerritoryName(opponent.name)} was slaughtered by ${player.name}.`, true);
return null; // Skip outcome delay screen
});
this.modalIsOpen = false;
}
// Clear the exploring flags so the new owner doesn't inherit them
opponent.isExploring = false;
opponent.exploreTurnsLeft = 0;
opponent.exploreType = null;
opponent.explorePOI = null;
}
flavor = (Math.random() < 0.10) ? (" " + combatFlavors[Math.floor(Math.random() * combatFlavors.length)]) : "!"; flavor = (Math.random() < 0.10) ? (" " + combatFlavors[Math.floor(Math.random() * combatFlavors.length)]) : "!";
this.players.forEach(p => { this.players.forEach(p => {
if (p.name === opponent.owner) { if (p.name === opponent.owner) {
let index = p.areas.indexOf(opponent.name); let index = p.areas.indexOf(opponent.name);
if (index > -1) { if (index > -1) {
@ -9677,7 +9737,13 @@ Gamestate.openDiplomacy = function (targetName) {
country.army -= movedTroops; country.army -= movedTroops;
if (defender) defender.style.fill = opponent.color; if (defender) defender.style.fill = opponent.color;
// --- NEW ENCOUNTER TRIGGER IS NOW HERE (After troops have arrived!) ---
if (player.isPlayer) {
await this.triggerEncounterCheck('post_conquest', opponent.name);
}
if (this.perksEnabled && player.perk) { if (this.perksEnabled && player.perk) {
if (player.perk.id === 'fev_infection') { if (player.perk.id === 'fev_infection') {
// (Bonus Fix: Made the victory FEV math match the repulse FEV math!) // (Bonus Fix: Made the victory FEV math match the repulse FEV math!)
const converted = Math.max(1, Math.floor(originalDefenderArmy * 0.25)); const converted = Math.max(1, Math.floor(originalDefenderArmy * 0.25));
@ -9866,6 +9932,39 @@ Gamestate.openDiplomacy = function (targetName) {
Gamestate.checkWinCondition = function () { Gamestate.checkWinCondition = function () {
if (this.gameOver) return; if (this.gameOver) return;
let winModal = document.querySelector('#win-modal');
let winMessage = document.querySelector('.win-message');
if (!winModal || !winMessage) return;
// --- CHECK 1: PLAYER DEFEAT ---
let playerDead = false;
if (this.commandersEnabled && (!this.player.commander || this.player.commander.hp <= 0)) {
playerDead = true;
} else if (this.player.areas.length === 0) {
playerDead = true;
}
if (playerDead) {
this.gameOver = true;
this.player.alive = false;
let defeatFlavors = [
"Your faction is nothing but a footnote in the wasteland's bloody history.",
"Your forces have been scattered, your settlements burned. The wastes claim another victim.",
"War never changes. And this time, you were on the losing side."
];
let cmdrFlavor = "Your Commander has fallen. Without leadership, your forces scattered into the irradiated winds.";
winMessage.textContent = "YOU DIED.";
winMessage.style.color = "#ff3333";
let subMsg = winMessage.nextElementSibling;
if (subMsg && subMsg.tagName === 'P') {
subMsg.textContent = this.commandersEnabled ? cmdrFlavor : defeatFlavors[Math.floor(Math.random() * defeatFlavors.length)];
}
winModal.style.display = "block";
return;
}
let totalPlayableLand = this.countries.filter(c => !c.isCrater).length; let totalPlayableLand = this.countries.filter(c => !c.isCrater).length;
let playerLand = this.countries.filter(c => c.owner === this.player.name).length; let playerLand = this.countries.filter(c => c.owner === this.player.name).length;
let ownsAllLand = (playerLand >= totalPlayableLand); let ownsAllLand = (playerLand >= totalPlayableLand);
@ -9879,48 +9978,52 @@ Gamestate.openDiplomacy = function (targetName) {
if (livingRivals.length > 0) allRivalsDead = false; if (livingRivals.length > 0) allRivalsDead = false;
} }
// --- CHECK 1: TOTAL DOMINATION --- // --- CHECK 2: TOTAL DOMINATION ---
if (ownsAllLand && allRivalsDead) { if (ownsAllLand && allRivalsDead) {
this.gameOver = true; this.gameOver = true;
let winModal = document.querySelector('#win-modal'); let domFlavors = [
let winMessage = document.querySelector('.win-message'); "You have conquered the wastes. All who opposed you are dust in the wind.",
"From the ashes of the old world, your empire rises absolute.",
if (winMessage) { "No rivals remain. The wasteland belongs to you alone."
winMessage.textContent = "TOTAL DOMINATION!"; ];
winMessage.style.color = "var(--pip-color)"; winMessage.textContent = "TOTAL DOMINATION!";
let subMsg = winMessage.nextElementSibling; winMessage.style.color = "var(--pip-color)";
if (subMsg && subMsg.tagName === 'P') { let subMsg = winMessage.nextElementSibling;
if (this.commandersEnabled) { if (subMsg && subMsg.tagName === 'P') {
subMsg.textContent = "All rival commanders have been executed and the wasteland is entirely under your control."; subMsg.textContent = domFlavors[Math.floor(Math.random() * domFlavors.length)];
} else {
subMsg.textContent = "You have conquered all territories in the wasteland.";
}
}
} }
if (winModal) winModal.style.display = "block"; winModal.style.display = "block";
return; return;
} }
// --- CHECK 2: SHARED ALLIED VICTORY --- // --- CHECK 3: SHARED ALLIED VICTORY ---
let livingFactions = this.players.filter(p => p.alive && !p.isNeutral && p.name !== this.player.name); let livingFactions = this.players.filter(p => p.alive && !p.isNeutral && p.name !== this.player.name);
if (livingFactions.length > 0) { if (livingFactions.length > 0) {
let allLivingAreIdolized = livingFactions.every(p => this.diplomacy.reputation[p.name] && this.diplomacy.reputation[p.name][this.player.name] >= 35); let allLivingAreAllies = false;
if (this.isAllianceMode) {
// NEW: In Alliance mode, check if everyone alive shares your Team ID
allLivingAreAllies = livingFactions.every(p => p.team === this.player.team);
} else {
// Classic Mode: Require Idolized diplomacy reputation
allLivingAreAllies = livingFactions.every(p => this.diplomacy.reputation[p.name] && this.diplomacy.reputation[p.name][this.player.name] >= 35);
}
if (allLivingAreIdolized) { if (allLivingAreAllies) {
this.gameOver = true; this.gameOver = true;
let winModal = document.querySelector('#win-modal'); let allyFlavors = [
let winMessage = document.querySelector('.win-message'); "Through diplomacy and shared blood, you and your allies have united the wastes.",
"A coalition of power now rules the wasteland. Together, you brought order from chaos.",
if (winMessage) { "The wars are over. Your alliance stands victorious over the irradiated ashes."
winMessage.textContent = "ALLIED VICTORY!"; ];
winMessage.style.color = "#0088ff"; // A distinct "peace" color winMessage.textContent = "ALLIED VICTORY!";
let subMsg = winMessage.nextElementSibling; winMessage.style.color = "#39ff14"; // Distinct green/alliance color
if (subMsg && subMsg.tagName === 'P') { let subMsg = winMessage.nextElementSibling;
subMsg.textContent = "Through masterful diplomacy and shared trust, the remaining factions have united under your leadership."; if (subMsg && subMsg.tagName === 'P') {
} subMsg.textContent = allyFlavors[Math.floor(Math.random() * allyFlavors.length)];
} }
if (winModal) winModal.style.display = "block"; winModal.style.display = "block";
} }
} }
}; };
@ -9945,21 +10048,30 @@ Gamestate.openDiplomacy = function (targetName) {
// If an onChoice function is provided, execute it and get the result message // If an onChoice function is provided, execute it and get the result message
if (onChoice) { if (onChoice) {
const resultMessage = await onChoice(choice.id); const resultMessage = await onChoice(choice.id);
titleEl.textContent = "Outcome";
messageEl.innerHTML = resultMessage; // Display the result // NEW: If the event returned text, show the 2.5s delay screen.
choicesContainer.innerHTML = ''; // Clear the buttons // If it returned null, close the modal instantly!
if (resultMessage) {
titleEl.textContent = "Outcome";
messageEl.innerHTML = resultMessage; // Display the result
choicesContainer.innerHTML = ''; // Clear the buttons
// Wait 2.5 seconds before closing the modal // Wait 2.5 seconds before closing the modal
setTimeout(() => { setTimeout(() => {
modal.style.display = 'none';
resolve(choice.id);
}, 2500);
} else {
modal.style.display = 'none'; modal.style.display = 'none';
resolve(choice.id); resolve(choice.id);
}, 2500); }
} else { } else {
// Original behavior if no onChoice is provided // Original behavior if no onChoice is provided
modal.style.display = 'none'; modal.style.display = 'none';
resolve(choice.id); resolve(choice.id);
} }
}; };
choicesContainer.appendChild(button); choicesContainer.appendChild(button);
}); });
@ -10050,7 +10162,7 @@ Gamestate.openDiplomacy = function (targetName) {
const army = territory.army; const army = territory.army;
const threat = creature.threat; const threat = creature.threat;
const tName = formatTerritoryName(territory.name); const tName = formatTerritoryName(territory.name);
const cName = `<strong>${creature.name}</strong>`; const cName = `${creature.name}`;
// Calculate Risk Assessment // Calculate Risk Assessment
let riskText, riskColor; let riskText, riskColor;
@ -10163,15 +10275,20 @@ Gamestate.openDiplomacy = function (targetName) {
} }
} }
} }
}; }; // <-- This closes the callback properly!
// These lines properly close the Creature Encounter function!
await this.showEncounterModal(title, message, choices, onChoiceCallback);
this.modalIsOpen = false;
this.updateInfo();
this.drawMapText();
};
Gamestate.resolveRadioEncounter = async function () { Gamestate.resolveRadioEncounter = async function () {
if (this.player.areas.length === 0 || this.modalIsOpen) return; if (this.player.areas.length === 0 || this.modalIsOpen) return;
// Find a valid territory: not already exploring, army > 1 // Find a valid territory: not already exploring, army > 1
const validTerritories = this.player.areas.map(a => this.countries.find(c => c.name === a)).filter(c => c && c.army > 1 && !c.isExploring); const validTerritories = this.player.areas.map(a => this.countries.find(c => c.name === a)).filter(c => c && c.army > 1 && !c.isExploring);
if (validTerritories.length === 0) return; if (validTerritories.length === 0) return;
const territory = validTerritories[Math.floor(Math.random() * validTerritories.length)]; const territory = validTerritories[Math.floor(Math.random() * validTerritories.length)];
// Pick an encounter type // Pick an encounter type
@ -10193,7 +10310,7 @@ Gamestate.openDiplomacy = function (targetName) {
} }
const title = "Radio Transmission"; const title = "Radio Transmission";
const message = `>>> INCOMING TRANSMISSION <<<<br><br>Garrison forces at <strong>${formatTerritoryName(territory.name)}</strong> report they have discovered <strong>${poiName}</strong>.<br><br>Do you want to send a detachment to ${actionVerb}? This will lock the territory down for 2 turns and reduce their defenses by 15%.`; const message = `>>> INCOMING TRANSMISSION <<<<br><br>Garrison forces at ${formatTerritoryName(territory.name)} report they have discovered ${poiName}.<br><br>Do you want to send a detachment to ${actionVerb}? This will lock the territory down for 2 turns and reduce their defenses by 15%.`;
const choices = [ const choices = [
{ id: "explore", text: "[Investigate] Send the detachment." }, { id: "explore", text: "[Investigate] Send the detachment." },
@ -10211,10 +10328,10 @@ Gamestate.openDiplomacy = function (targetName) {
territory.explorePOI = poiName; territory.explorePOI = poiName;
this.logAction(`[ EXPEDITION ] Troops at ${formatTerritoryName(territory.name)} have begun investigating ${poiName}. They will report back in 2 turns.`, true); this.logAction(`[ EXPEDITION ] Troops at ${formatTerritoryName(territory.name)} have begun investigating ${poiName}. They will report back in 2 turns.`, true);
return `Expedition launched. The garrison is now vulnerable.`; return null; // Skip the outcome screen
} else { } else {
this.logAction(`[ EXPEDITION ] You ordered the garrison at ${formatTerritoryName(territory.name)} to hold their position and ignore the ${poiName}.`); this.logAction(`[ EXPEDITION ] You ordered the garrison at ${formatTerritoryName(territory.name)} to hold their position and ignore the ${poiName}.`);
return `Transmission ended. The garrison will maintain their post.`; return null; // Skip the outcome screen
} }
}; };
@ -10224,6 +10341,128 @@ Gamestate.openDiplomacy = function (targetName) {
this.drawMapText(); this.drawMapText();
}; };
Gamestate.resolveExplorationOutcome = async function (territory) {
if (this.modalIsOpen) return;
this.modalIsOpen = true;
territory.isExploring = false; // Remove the searching status
const title = "Expedition Returned";
let message = `Your detachment has returned from exploring the ${territory.explorePOI} at ${formatTerritoryName(territory.name)}.<br><br>`;
let logMsg = "";
const roll = Math.random();
// 30% Chance for High Value Loot (Caps or Units)
if (roll < 0.30) {
let capsFound = Math.floor(Math.random() * 15) + 10;
if (this.wastelandEconomyActive) this.player.caps += capsFound;
message += `They hit the jackpot! Your troops recovered <strong style="color:#ffcc00">${capsFound} Caps</strong> from a pre-war stash.`;
logMsg = `[ EXPEDITION ] Success at ${formatTerritoryName(territory.name)}. Found ${capsFound} Caps!`;
}
// 20% Chance for Stimpak
else if (roll < 0.50 && this.commandersEnabled && this.player.commander && this.player.commander.stimpaks < 3) {
this.player.commander.stimpaks++;
let navInv = document.getElementById('nav-inv');
if (navInv) navInv.classList.add('inv-pulse');
message += `They discovered a sealed medical kit containing a rare <strong style="color:var(--pip-color)">Stimpak</strong>!`;
logMsg = `[ EXPEDITION ] Success at ${formatTerritoryName(territory.name)}. Recovered a Stimpak!`;
}
// 25% Chance for an Ambush / Trap (Loss of units)
else if (roll < 0.75) {
let casualties = Math.floor(territory.army * 0.20) + 1; // 20% casualties
if (territory.army - casualties < 1) casualties = territory.army - 1; // Never wipe out
if (casualties > 0) {
territory.army -= casualties;
this.player.army -= casualties;
message += `It was a trap! The detachment was ambushed, suffering <strong style="color:#ff3333">${casualties} casualties</strong> before retreating.`;
logMsg = `[ EXPEDITION ] Disaster at ${formatTerritoryName(territory.name)}. Lost ${casualties} troops to a trap.`;
} else {
message += `They triggered an ancient security system but narrowly escaped without casualties.`;
logMsg = `[ EXPEDITION ] Troops at ${formatTerritoryName(territory.name)} triggered a trap but escaped unharmed.`;
}
}
// 25% Chance for Nothing
else {
message += `The area had already been stripped clean by scavengers. They returned empty-handed.`;
logMsg = `[ EXPEDITION ] The search at ${formatTerritoryName(territory.name)} turned up nothing.`;
}
const choices = [{ id: "acknowledge", text: "[Acknowledge] Resume normal patrols." }];
const onChoiceCallback = () => {
this.logAction(logMsg, true);
return null; // Skip the outcome screen
};
await this.showEncounterModal(title, message, choices, onChoiceCallback);
this.modalIsOpen = false;
this.updateInfo();
this.drawMapText();
};
Gamestate.resolveBattleEncounter = async function (territoryName) {
// Force unlock the modal state (in case the Move Troops slider confused it)
this.modalIsOpen = false;
const territory = this.countries.find(c => c.name === territoryName);
if (!territory || territory.army < 1) return; // FIX: Even 1 troop can discover a vault after a battle
// First, check if this territory has a designated Vault
let poiName = "";
let type = "";
let actionVerb = "investigate the ruins";
let isVault = false;
const possibleVaults = encounterData.vaults.filter(v => v.territory === territoryName);
if (possibleVaults.length > 0) {
// Pick a vault if there are multiple in this territory
poiName = possibleVaults[Math.floor(Math.random() * possibleVaults.length)].name;
type = "vault";
actionVerb = "breach the vault door";
isVault = true;
} else {
// If no vault, pick a generic location or container
const types = ["location", "container"];
type = types[Math.floor(Math.random() * types.length)];
if (type === "location") {
poiName = encounterData.genericLocations[Math.floor(Math.random() * encounterData.genericLocations.length)];
actionVerb = "secure the area";
} else {
poiName = encounterData.containers[Math.floor(Math.random() * encounterData.containers.length)];
actionVerb = "attempt to crack it open";
}
}
const title = isVault ? "Vault Discovered" : "Post-Battle Discovery";
const message = `>>> SECTOR SECURED <<<<br><br>While clearing out the last of the enemy resistance at ${formatTerritoryName(territory.name)}, your troops uncovered ${poiName}.<br><br>Do you want to order the garrison to ${actionVerb}? This will lock the territory down for 2 turns and reduce their defenses by 15%.`;
const choices = [
{ id: "explore", text: "[Investigate] Send the detachment." },
{ id: "ignore", text: "[Ignore] Focus on fortifications." }
];
this.modalIsOpen = true;
const onChoiceCallback = (decision) => {
if (decision === 'explore') {
territory.isExploring = true;
territory.exploreTurnsLeft = 2;
territory.exploreType = type;
territory.explorePOI = poiName;
this.logAction(`[ EXPEDITION ] Conquerors at ${formatTerritoryName(territory.name)} are breaching ${poiName}. Reports expected in 2 turns.`, true);
return null; // Skip the outcome screen
} else {
this.logAction(`[ TACTICAL ] The garrison at ${formatTerritoryName(territory.name)} ignored ${poiName} to focus on defense.`);
return null; // Skip the outcome screen
}
};
await this.showEncounterModal(title, message, choices, onChoiceCallback); await this.showEncounterModal(title, message, choices, onChoiceCallback);
this.modalIsOpen = false; this.modalIsOpen = false;
@ -10232,27 +10471,69 @@ Gamestate.openDiplomacy = function (targetName) {
}; };
Gamestate.triggerEncounterCheck = async function (triggerType) { Gamestate.triggerEncounterCheck = async function (triggerType, territoryName = null) {
// If encounters are disabled in the game settings, do nothing. // If encounters are disabled in the game settings, do nothing.
if (!this.encountersEnabled) return; if (!this.encountersEnabled) return;
// 1. EXPLORATION LOCKOUT: Cannot trigger new events if already exploring
let isCurrentlyExploring = this.countries.some(c => c.owner === this.player.name && c.isExploring);
if (isCurrentlyExploring) return;
// Initialize trackers if they don't exist
if (this.turnsSinceLastEncounter === undefined) this.turnsSinceLastEncounter = 0;
if (this.encounterCooldown === undefined) this.encounterCooldown = 0;
let chance = 0; let chance = 0;
if (triggerType === 'start_of_turn') { if (triggerType === 'start_of_turn') {
chance = 1.0; // TEST MODE: 100% chance to trigger! // Update timers at the start of the turn
if (this.encounterCooldown > 0) this.encounterCooldown--;
this.turnsSinceLastEncounter++;
// 2. THE 3-TURN COOLDOWN: 0% chance while resting
if (this.encounterCooldown > 0) {
chance = 0;
}
// 3. THE 9-TURN PITY TIMER: 100% chance if starved
else if (this.turnsSinceLastEncounter >= 9) {
chance = 1.0;
}
// STANDARD RATE
else {
chance = 0.08; // 8% chance per turn
}
} else if (triggerType === 'post_conquest') { } else if (triggerType === 'post_conquest') {
chance = 1.0; // TEST MODE: 100% chance to trigger! // If on cooldown, no post-battle discoveries allowed
if (this.encounterCooldown > 0) {
chance = 0;
} else {
chance = 0.15; // 15% chance per conquest
}
} }
// Roll the dice
if (Math.random() < chance) { if (Math.random() < chance) {
if (triggerType === 'start_of_turn') { if (triggerType === 'start_of_turn') {
// Reset timers!
this.turnsSinceLastEncounter = 0;
this.encounterCooldown = 3; // Enforce 3-turn break
await this.resolveRadioEncounter(); await this.resolveRadioEncounter();
} else if (triggerType === 'post_conquest' && territoryName) {
// Reset timers!
this.turnsSinceLastEncounter = 0;
this.encounterCooldown = 3; // Enforce 3-turn break
await this.resolveBattleEncounter(territoryName);
} }
// Post-conquest will go here shortly!
} }
}; };
/** /**
* Sets the initial reputation values between all players based on their faction affinities. * Sets the initial reputation values between all players based on their faction affinities.
* This function should be called once at the start of a new game. * This function should be called once at the start of a new game.