From 65e9e098da4a95aed04a0fc12ced446abfc5959b Mon Sep 17 00:00:00 2001 From: threememories Date: Mon, 18 May 2026 10:58:04 -0500 Subject: [PATCH] Add files via upload --- index.html | 211 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 179 insertions(+), 32 deletions(-) diff --git a/index.html b/index.html index bab97a6..7c1c036 100644 --- a/index.html +++ b/index.html @@ -3342,7 +3342,7 @@ - + @@ -4957,16 +4957,28 @@ if (totalPlayable > 0 && (playerOwnedCount / totalPlayable) > 0.35) { }); Gamestate.debugLoggingEnabled = false; // The global flag for our new mode + Gamestate.debugLog = []; // NEW: Array to hold the last 50 technical logs // New helper function for conditional logging Gamestate.logDebug = function(message) { + // 1. Always push to the hidden rolling array + let now = new Date(); + let timeString = now.toLocaleTimeString([], { hour12: false }); + this.debugLog.push(`[${timeString}] ${message}`); + if (this.debugLog.length > 500) { + this.debugLog.shift(); // Remove the oldest entry if we exceed 500 + } + + // 2. Keep the old behavior of printing to UI if toggle is checked + + if (this.debugLoggingEnabled) { - // Prepend [DEBUG] to make these messages stand out this.logAction(`[DEBUG] ${message}`); } }; Gamestate.logQueue = []; + Gamestate.isLogging = false; Gamestate.logAction = function (message, isImportant = false, isNuke = false) { @@ -5654,11 +5666,17 @@ Gamestate.renderInventory = function () { // 2. Identify Valid Territories Based on Phase if (this.stage === "Battle") { - valid = owned.filter(t => t.army > 1 && !t.isLockedDown && !t.isExploring && t.neighbours.some(n => { - let nc = this.countries.find(x => x.name === n); - return nc && nc.owner !== this.player.name && !nc.isCrater && !(this.isAllianceMode && this.areAllies(this.player.name, nc.owner)); - })); + valid = owned.filter(t => { + if (t.army <= 1 || t.isLockedDown || t.isExploring) return false; + let hasEnemyNeighbor = t.neighbours.some(n => { + let nc = this.countries.find(x => x.name === n); + return nc && nc.owner !== this.player.name && !nc.isCrater && !(this.isAllianceMode && this.areAllies(this.player.name, nc.owner)); + }); + let hasTrespasser = this.commandersEnabled && this.players.some(p => p !== this.player && p.alive && !p.isNeutral && p.commander && p.commander.hp > 0 && p.commander.loc === t.name); + return hasEnemyNeighbor || hasTrespasser; + }); } else if (this.stage === "Commander Phase" && this.commandersEnabled && this.player.commander && this.player.commander.ap > 0) { + let cmdrCountry = this.countries.find(c => c.name === this.player.commander.loc); if (cmdrCountry && !this.player.commander.isConverting) valid.push(cmdrCountry); } else if (this.stage === "Maneuver") { @@ -8421,20 +8439,11 @@ Gamestate.openDiplomacy = function (targetName) { } -Gamestate.handleClick = function (e) { + Gamestate.handleClick = function (e) { if (this.aiTurn || this.modalIsOpen) return; - // --- NEW: Spontaneous Map Encounter Trigger (3% chance per click) --- - // Only allowed if encounters are enabled, we are NOT mid-attack, we aren't targeting a nuke, AND we are past Turn 3 - if (this.encountersEnabled && this.turn > 3 && Math.random() < 0.03) { - if (this.stage !== "Battle" || !this.prevCountry) { - if (this.stage !== "Nuke Targeting" && this.stage !== "Frenzy Targeting") { - this.resolveCreatureEncounter(); - return; // Stop the click action so the modal takes priority - } - } - } // --- RELIC TARGETING HANDLER --- + if (this.targetingMode === 'relic' && this.pendingRelic) { const countryId = e.target.id; const country = this.countries.find(c => c.name === countryId); @@ -9036,10 +9045,12 @@ Gamestate.handleClick = function (e) { ownedTerritories.forEach(t => { if (t.army > 1 && !t.isExploring) { 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++; } + let hasTrespasser = this.commandersEnabled && this.players.some(p => p !== this.player && p.alive && !p.isNeutral && p.commander && p.commander.hp > 0 && p.commander.loc === t.name); + if (hasEnemyNeighbor || hasTrespasser) { currentStrikeForce += (t.army - 1); validAttacks++; } } }); if (validAttacks === 0) apPercentage = 0; + else { if (this.lastStage !== "Battle") this.initialStrikeForce = currentStrikeForce; if (currentStrikeForce > (this.initialStrikeForce || 1)) this.initialStrikeForce = currentStrikeForce; @@ -10062,6 +10073,12 @@ if (sourceMapEl && sourceMapEl.nextElementSibling) sourceMapEl.nextElementSiblin return; } + // --- RANDOM WASTELAND ENCOUNTERS (8% Chance per phase transition) --- + if (this.encountersEnabled && this.turn > 3 && Math.random() < 0.08) { + this.resolveCreatureEncounter(); + return; // Pause phase transition so the modal takes priority. Player will click End Phase again after. + } + // --- FORTIFY / RECRUITMENT -> BATTLE BRIDGE --- if (this.stage === "Fortify" || this.stage === "Recruitment") { // Modal confirmation for unspent Caps @@ -11657,9 +11674,13 @@ this.checkAutoPhaseAdvance(); } if (winModal) winModal.style.display = "block"; } + + // --- NEW: UNIVERSAL VICTORY CHECK --- + this.checkWinCondition(); } Gamestate.processRadDecay = async function () { + // --- NEW: DECREMENT RADAWAY IMMUNITY --- this.players.forEach(p => { if (p.radImmunity && p.radImmunity > 0) p.radImmunity--; @@ -11946,9 +11967,10 @@ this.checkAutoPhaseAdvance(); } if (this.perksEnabled) { for (let p of this.players) { - if (p && p.perk) { + if (p && p.perk && p.alive) { // --- Chem Frenzy Cooldown --- if (p.perk.id === 'chem_frenzy' && p.chemFrenzyCooldown > 0) { + p.chemFrenzyCooldown--; } // --- Technology Overdrive Cooldown --- @@ -14570,7 +14592,30 @@ this.addXP(player, xpReward); let winMessage = document.querySelector('.win-message'); if (!winModal || !winMessage) return; + // --- NEW: DEATH SWEEP FOR GHOST FACTIONS --- + this.players.forEach(p => { + if (p.alive && !p.isNeutral) { + let isDead = false; + if (this.commandersEnabled) { + if (!p.commander || p.commander.hp <= 0) isDead = true; + } else { + if (p.areas.length === 0) isDead = true; + } + + if (isDead) { + p.alive = false; + p.areas = []; + p.army = 0; + let index = this.players.indexOf(p); + if (typeof infoName !== 'undefined' && infoName[index]) { + infoName[index].parentElement.classList.add('defeated'); + } + } + } + }); + // --- CHECK 1: PLAYER DEFEAT --- + let playerDead = false; let isAllyDeath = false; @@ -15460,7 +15505,8 @@ Gamestate.triggerEncounterCheck = async function (triggerType, territoryName = n { file: "TheWanderer.mp3", title: "Dion DiMucci - The Wanderer" }, { file: "TakeMeHomeCountryRoads.mp3", title: "John Denver - Take Me Home Country Roads" } ]; - let currentTrackIndex = 0; let isRadioActive = false; let pipboyAudio = new Audio(); let broadcastDelay; + let currentTrackIndex = 0; let isRadioActive = false; let pipboyAudio = new Audio(); let broadcastDelay; let consecutiveRadioFailures = 0; + const radioBtn = document.getElementById('radio-toggle'); radioBtn.addEventListener('click', () => { if (isRadioActive) { @@ -15479,21 +15525,52 @@ Gamestate.triggerEncounterCheck = async function (triggerType, territoryName = n if (!isRadioActive) return; let currentTrack = wastelandRadio[currentTrackIndex]; pipboyAudio.src = currentTrack.file; - pipboyAudio.play(); + + let playPromise = pipboyAudio.play(); + + if (playPromise !== undefined) { + playPromise.then(() => { + // Playback successful! Reset the failure counter. + consecutiveRadioFailures = 0; + if (Gamestate && Gamestate.logAction) { + Gamestate.logAction(`RADIO: Picked up a broadcast... Now playing "${currentTrack.title}".`); + // --- SINGING LOG EVENT --- + if (currentTrack.file.includes("TakeMeHomeCountryRoads")) { + setTimeout(() => Gamestate.logAction("'Wait, I know this one...'"), 4000); + setTimeout(() => Gamestate.logAction("You start singing: '♫ Almost heaven, West Virginia.'"), 7000); + setTimeout(() => Gamestate.logAction("'♫ Blue Ridge Mountains, Shenandoah River.'"), 12700); + setTimeout(() => Gamestate.logAction("'♫ Life is old there, older than the trees...Great song.'"), 18800); + } + } + }).catch(error => { + // File not found or playback failed + if (!isRadioActive) return; + + consecutiveRadioFailures++; + + // Check if we've cycled through the whole list with no success + if (consecutiveRadioFailures >= wastelandRadio.length) { + isRadioActive = false; + radioBtn.classList.remove('radio-on'); + if (Gamestate && Gamestate.logAction) { + Gamestate.logAction("RADIO: Hardware failure. No audio files detected on any frequency. Powering down..."); + } + consecutiveRadioFailures = 0; // Reset for next time they try to turn it on + return; + } - if (Gamestate && Gamestate.logAction) { - Gamestate.logAction(`RADIO: Picked up a broadcast... Now playing "${currentTrack.title}".`); - - // --- SINGING LOG EVENT --- - if (currentTrack.file.includes("TakeMeHomeCountryRoads")) { - setTimeout(() => Gamestate.logAction("'Wait, I know this one...'"), 4000); - setTimeout(() => Gamestate.logAction("You start singing: '♫ Almost heaven, West Virginia.'"), 7000); - setTimeout(() => Gamestate.logAction("'♫ Blue Ridge Mountains, Shenandoah River.'"), 12700); - setTimeout(() => Gamestate.logAction("'♫ Life is old there, older than the trees...Great song.'"), 18800); - } + if (Gamestate && Gamestate.logAction) { + Gamestate.logAction("RADIO: *BZZZT* ...Heavy radiation interference. Skipping dead frequency."); + } + // Skip to next track after a brief delay of static + currentTrackIndex = (currentTrackIndex + 1) % wastelandRadio.length; + broadcastDelay = setTimeout(() => { playWastelandTrack(); }, 3000); + }); } } + + pipboyAudio.addEventListener('ended', () => { if (!isRadioActive) return; currentTrackIndex = (currentTrackIndex + 1) % wastelandRadio.length; if (Gamestate && Gamestate.logAction) Gamestate.logAction("RADIO: Broadcast ended. Scanning frequencies for the next signal..."); @@ -15539,9 +15616,11 @@ Gamestate.triggerEncounterCheck = async function (triggerType, territoryName = n diplomacy: this.diplomacy, dogmeatQuest: this.dogmeatQuest, activeRelicPool: this.activeRelicPool, + debugLog: this.debugLog, // NEW: Save the 50-line technical history // The Main Entities players: this.players, + countries: this.countries, bobbleheads: this.bobbleheads }; @@ -15621,6 +15700,7 @@ Gamestate.triggerEncounterCheck = async function (triggerType, territoryName = n this.diplomacy = loadedData.diplomacy; this.dogmeatQuest = loadedData.dogmeatQuest; this.activeRelicPool = loadedData.activeRelicPool; + this.debugLog = loadedData.debugLog || []; // NEW: Restore the technical history this.players = loadedData.players; this.countries = loadedData.countries; this.bobbleheads = loadedData.bobbleheads; @@ -15663,7 +15743,62 @@ Gamestate.triggerEncounterCheck = async function (triggerType, territoryName = n } }; + // --- NEW: DIAGNOSTIC EXPORT & HIDDEN TRIGGER --- + Gamestate.exportDiagnostics = function() { + if (!this.player) return; // Game hasn't started yet + + let p = this.player; + let preset = document.getElementById('game-mode-preset') ? document.getElementById('game-mode-preset').value : "UNKNOWN"; + + let report = "========================================\n"; + report += " WASTELAND CONQUEST - DIAGNOSTIC REPORT \n"; + report += "========================================\n\n"; + + report += "--- OPERATOR PROFILE ---\n"; + report += `Name: ${p.name}\nFaction: ${p.country}\nLevel: ${p.level || 1} (${p.xp || 0} XP)\n`; + report += `Commander Health: ${this.commandersEnabled && p.commander ? p.commander.hp : 'N/A'}\n`; + report += `Territories Owned: ${p.areas.length}\n\n`; + + report += "--- SIMULATION PARAMETERS ---\n"; + report += `Turn: ${this.turn} | Stage: ${this.stage} | Preset: ${preset.toUpperCase()} | Difficulty: ${this.difficulty || "NORMAL"}\n`; + report += `Wasteland Economy: ${!!this.wastelandEconomyActive} | Faction Perks: ${!!this.perksEnabled} | Commanders: ${!!this.commandersEnabled}\n`; + report += `Fog of War: ${!!(document.getElementById('opt-fog-of-war') && document.getElementById('opt-fog-of-war').checked)} | Radstorms: ${!!this.hazardsEnabled} | Encounters: ${!!this.encountersEnabled} | Scorched Earth: ${!!this.nukesEnabled}\n\n`; + + report += "--- TECHNICAL LOG (LAST 500 EVENTS) ---\n"; + if (this.debugLog && this.debugLog.length > 0) { + + this.debugLog.forEach(line => { report += line + "\n"; }); + } else { + report += "No debug logs recorded yet.\n"; + } + + report += "\n========================================\nEND OF REPORT\n========================================\n"; + + const blob = new Blob([report], { type: "text/plain" }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = `Wasteland_Diagnostic_Day${this.turn}.txt`; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + URL.revokeObjectURL(url); + + if (this.showToast) this.showToast("DIAGNOSTIC REPORT EXPORTED", "var(--pip-color)"); + }; + + // The hidden keyboard shortcut (Ctrl + Shift + D) + window.addEventListener('keydown', function(e) { + if (e.ctrlKey && e.shiftKey && (e.key === 'd' || e.key === 'D')) { + e.preventDefault(); // Prevent default browser behavior like bookmarking + if (Gamestate && Gamestate.exportDiagnostics) { + Gamestate.exportDiagnostics(); + } + } + }); + Gamestate.updateInfo = function () { + Gamestate.originalUpdateInfo.call(this); // Run the normal game logic first @@ -15794,6 +15929,7 @@ Gamestate.updateInfo = function () { `> MAP LEGEND (A visual guide explaining map colors, borders, and icons)
` + `> HOLOTAPE ARCHIVE (A look at the hit Fallout games that inspired this sim)
` + `> SAVE & LOAD (Protect progress by learning to backup and restore data)
` + + `> BUG REPORTING (How to export diagnostic logs)
` + `> SYSTEM CREDITS (See the latest system updates and developer information)

` + adminLink + `> LOG OFF THE GUIDE`; @@ -16182,6 +16318,8 @@ html += ` "page-data": "> MISSION DATA: SAVE & LOAD PROTOCOLS

The simulation allows the Overseer to suspend current progress and resume operations at a later date by exporting data as a JSON file.

SAVING A SIMULATION: During your active turn, locate the Save button (marked with the icon) positioned next to the REBOOT GAME button. Clicking this will compile your complete session state—including map control, troop deployments, Bottle Cap reserves, Commander health levels, and active rulesets—and prompt you to download it as a JSON file to your local hardware.

LOADING A SIMULATION: You can only resume a suspended simulation during the initial RobCo boot sequence before a match begins. Select the LOAD GAME option from the boot menu and upload your previously saved JSON file to fully restore your session parameters.

DATA RETENTION WARNING: The terminal exports save data strictly as localized JSON files. There is no external cloud backup or browser auto-save. You are responsible for securing these files on your local operating system. If you delete or misplace your JSON file, your archived simulation cannot be recovered.", +"page-diagnostic": "> ROBCO OS DIAGNOSTIC TOOL

If you encounter a simulation error, anomaly, or game-breaking bug, you can generate a technical diagnostic report to assist the developer.

HOW TO GENERATE A REPORT:
1. Press Ctrl + Shift + D on your keyboard at any time during gameplay.
2. A file named Wasteland_Diagnostic_Day[X].txt will automatically download to your device.
3. This file contains your current simulation parameters, operator profile, and a 500-line technical log of the AI's recent under-the-hood logic.

ALTERNATIVE METHOD (SAVE FILES):
If the game freezes and you cannot press the shortcut, please use the Save Game button if the UI is still responsive. Save files now automatically embed these technical logs as well!

Please include this text file or your save file when submitting bug reports to ensure rapid resolution.", + "page-items": `> ITEM DATABASE

The wasteland is full of valuable pre-war technology and chems. This section details the items you can find and use to gain a tactical advantage.

> STIMPAKS
> BOBBLEHEADS
> WASTELAND RELICS

< BACK TO MAIN DIRECTORY`, "page-stimpaks": `> ITEMS: STIMPAKS

A miraculous pre-war healing agent. Stimpaks are essential for keeping your Commander alive in the field.

FUNCTION:
Instantly restores 20 HP to your Commander. If you have the 'Medic' perk, this is increased to 40 HP.

USAGE:
Stimpaks can only be used during the Commander Phase. Activating one costs 1 Action Point (AP). You can use a Stimpak by clicking the button in the Commander UI or from the main Inventory screen.

ACQUISITION:
Stimpaks are found randomly by completing Wasteland Encounters or by looting the supplies of a defeated rival Commander.

< BACK TO ITEM DATABASE`, @@ -16191,9 +16329,18 @@ html += ` "page-relics": `> ITEMS: WASTELAND RELICS

Extremely rare and powerful single-use artifacts. Six are randomly seeded into the loot pool each match.

* G.E.C.K.: Restores a Crater or Radstorm tile to lush land and spawns troops.
* Fat Man: Devastating mini-nuke strike. Range is limited to 3 territories from your border.
* Stealth Boy: Hide your territories from enemy intel for 2 turns.
* Bottlecap Mine: Trap a friendly territory. Detonates on an incoming enemy.
* Cryolator: Freeze an enemy territory, preventing all actions for 1 turn. Range is limited to 3 territories from your border.
* Vault-Tec Lunchbox: A random assortment of Caps and Troops.
* Super Stimpak: Auto-revives your Commander upon taking fatal damage.
* Jet: Instantly take a second, consecutive turn.
* RadAway: Grants total immunity to Radstorms and nuke fallout for 3 turns.
* Silver Shroud Card: Blockade an enemy land, preventing reinforcements for 3 turns.
* Wasteland Survival Guide: Instantly and successfully complete all active map expeditions.

< BACK TO ITEM DATABASE`, -"page-about": "> SYSTEM CREDITS & LEGAL

SYSTEM VERSION: 2.4
> View Update History (Patch Notes)
Check for Latest Updates

ORIGINAL ENGINE ARCHITECTURE:
This simulation was heavily modified from the original Risk framework created by Vinayak Vedantam (https://github.com/vvedanta).

PORTABLE DEPLOYMENT:
Players can download the self-contained game as a .html file. To download: Save this webpage (Ctrl+S) as a single HTML file. (The playable music is not included).

SUPPORT THE DEVELOPER:
Support my work by buying my book, SurvivalSOS: Fundamentals of Survival Available on Amazon

FEEDBACK:
Submit Bug Report / Suggestion

DISCLAIMER:
This is an independent, fan-made project and is not affiliated with or endorsed by Bethesda Softworks, ZeniMax Media, or Microsoft. All Fallout-related intellectual property belongs to its respective owners. No copyright or trademark infringement is intended.", +"page-about": "> SYSTEM CREDITS & LEGAL

SYSTEM VERSION: 2.4.1
> View Update History (Patch Notes)
Check for Latest Updates

ORIGINAL ENGINE ARCHITECTURE:
This simulation was heavily modified from the original Risk framework created by Vinayak Vedantam (https://github.com/vvedanta).

PORTABLE DEPLOYMENT:
Players can download the self-contained game as a .html file. To download: Save this webpage (Ctrl+S) as a single HTML file. (The playable music is not included).

SUPPORT THE DEVELOPER:
Support my work by buying my book, SurvivalSOS: Fundamentals of Survival Available on Amazon

FEEDBACK:
Submit Bug Report / Suggestion

DISCLAIMER:
This is an independent, fan-made project and is not affiliated with or endorsed by Bethesda Softworks, ZeniMax Media, or Microsoft. All Fallout-related intellectual property belongs to its respective owners. No copyright or trademark infringement is intended.", "page-patch-notes": "> UPDATE HISTORY (PATCH NOTES)

" + +"v2.4.1 [HOTFIX & STABILITY UPDATE]
" + +"- Ghost Factions Fixed: Fixed critical bugs where factions eliminated by environmental damage or Commander duels remained active as 'ghosts', preventing the game from ending.
" + +"- Phantom Perks: Eliminated factions can no longer trigger passive abilities (like Prydwen drops) from the grave.
" + +"- Trespassing AP: Fixed an issue where players received 0 Action Points when a lone enemy Commander trespassed on a fully conquered map.
" + +"- Encounter Exploit: Random encounters are no longer tied to map clicks to prevent spamming; they now trigger naturally during phase transitions.
" + +"- Developer Tools: Added a comprehensive 'Bug Reporting' guide to the RobCo Terminal to assist players in exporting and submitting technical diagnostics.
" + +"- Diagnostics: Embedded 500-line technical logs into save files and added a hidden diagnostic export tool for easier bug tracking.
" + +"- Wasteland Radio: Added fail-safes for missing MP3 files. The Pip-Boy will now skip dead frequencies with thematic static and automatically power down if no audio files are detected.

" + + "v2.4 [THE COMPANION & COGNITION UPDATE]
" + "- New Companion System (Dogmeat): Added a rare, multi-stage quest to find and rescue Dogmeat. Players can choose to take him in an 'Injured' state (which incurs debuffs) or heal him with resources to unlock powerful combat, loot-finding, and mine-defusing buffs.
" + "- Scorched Earth Overhaul: Ground Zero is no longer permanently destroyed. Nuked territories now suffer severe, 10-turn radiation attrition (80% initial losses) that slowly cools off. The launch engine now supports simultaneous nuclear strikes from multiple factions.
" +