From 7329d1a1ccfc51e75e48cc1ae01a3ec8ef406a28 Mon Sep 17 00:00:00 2001 From: threememories Date: Fri, 24 Apr 2026 19:07:56 +0000 Subject: [PATCH] Upload files to "/" --- index.html | 557 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 354 insertions(+), 203 deletions(-) diff --git a/index.html b/index.html index 4682946..6ad1ddb 100644 --- a/index.html +++ b/index.html @@ -80,12 +80,13 @@ body { /* --- Modals --- */ .overlay { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background: rgba(0, 0, 0, 0.85); z-index: 9000; } - .start-modal, .cards-modal, .win-content, #confirm-restart-modal .content { - position: absolute !important; top: 50% !important; left: 50% !important; transform: translate(-50%, -50%) !important; - background: var(--pip-panel-solid) !important; color: var(--pip-color) !important; - padding: 25px 35px !important; border-radius: 4px !important; box-shadow: var(--pip-glow) !important; - text-align: center; border: 2px solid var(--pip-color) !important; width: 320px; max-width: 90vw; - } +.start-modal, .cards-modal, .win-content, #confirm-restart-modal .content { + position: absolute !important; top: 50% !important; left: 50% !important; transform: translate(-50%, -50%) !important; + background: var(--pip-panel-solid) !important; color: var(--pip-color) !important; + padding: 25px 35px !important; border-radius: 4px !important; box-shadow: var(--pip-glow) !important; + text-align: center; border: 2px solid var(--pip-color) !important; width: 650px; max-width: 90vw; +} + .help-modal-content { width: 850px !important; max-width: 95vw !important; text-align: left !important; } .manual-footer { font-size: 14px; margin-top: 30px; opacity: 0.7; font-family: 'VT323', monospace; text-transform: uppercase; text-align: center; line-height: 1.2; } .form-group { margin-bottom: 15px; text-align: left; } @@ -830,117 +831,111 @@ button:active {
-
-
-

ROBCO OS v2.0

- -
- - -
+
+
-
- - -
- -
-
- - - - - - -
+ +

Wasteland Conquest

+

A RobCo Industries Strategic Simulation

- -
- - - -
-
- -
- - -
- -
- - - - - - - - - - - - - - - - - - - - - - - -
- - - +
+ +
+ +
+ + +
+ +
+
+ + + +
+
+ + +
+
+ +
+ + +
+ + +
+ + +
+ + +
+ +
+ + + + + +
+
@@ -1048,7 +1043,11 @@ button:active {

Total Cost: 0 Caps

- +
+ + +
+
@@ -2096,9 +2095,10 @@ function updateDynamicCursor(hexColor) { document.addEventListener('DOMContentLoaded', () => { - const factionInput = document.getElementById('chosen-country'); + const factionInput = document.getElementById('chosen-country-input'); const tooltip = document.getElementById('vats-tooltip'); // WE ARE USING YOUR EXISTING TOOLTIP ELEMENT + if (!factionInput || !tooltip) { console.error("Tooltip or Faction Input not found!"); return; @@ -2538,11 +2538,148 @@ const helpBtn = document.querySelector('#help-btn'); const helpModal = document.querySelector('#help-modal'); const closeHelpBtn = document.querySelector('#close-help-btn'); +// --- NEW: GAME MODE PRESET LOGIC (FINAL FORMATTING) --- +const presetSelector = document.getElementById('game-mode-preset'); +const descriptionBox = document.getElementById('preset-description'); +const customRulesBox = document.getElementById('custom-rules-container'); +const checkboxes = { + perks: document.getElementById('opt-perks'), + commander: document.getElementById('opt-commander'), + fog: document.getElementById('opt-fog-of-war'), + radstorms: document.getElementById('opt-radstorms'), + horrors: document.getElementById('opt-horrors'), + encounters: document.getElementById('opt-encounters'), + nukes: document.getElementById('opt-nukes'), + flatTrade: document.getElementById('opt-flat-trade') +}; + +const presetData = { + 'classic': { + narrative: `"The old world is dead. The new world is a battleground. This is a pure contest of strategy and will. Amass your forces, conquer the continents, and eliminate all rivals. In this simulation, only tactical superiority matters. No heroes, no cataclysms—just war. War never changes."`, + mechanics: { economy: false, perks: false, commander: false, fog: false, radstorms: false, horrors: false, encounters: false, nukes: false } + }, + 'survival': { + narrative: `"Forget conquering the world; your only goal is to survive it. The air itself is poison, radioactive storms blot out the sun, and packs of ghouls infest the ruins. The map is a dark, unknown space. Every cap counts. Every step could be your last. Welcome to the real wasteland."`, + mechanics: { economy: true, perks: false, commander: false, fog: true, radstorms: true, horrors: true, encounters: true, nukes: false } + }, + 'heroes': { + narrative: `"History is not made by armies, but by individuals. In this simulation, the fate of the wasteland rests on the shoulders of its Commanders. Leverage your faction's unique doctrines and your leader's abilities to outmaneuver your foes. This is a war of personalities, where a single heroic action can turn the tide."`, + mechanics: { economy: true, perks: true, commander: true, fog: false, radstorms: false, horrors: false, encounters: true, nukes: false } + }, + 'apocalypse': { + narrative: `"The end of the world wasn't the end. The real apocalypse is now. The sky rains radiation, the dead walk the earth, and the forgotten keys to nuclear fire are back in play. There are no safe havens. There is no hope. There is only the roar of the storm, the shriek of the ghouls, and the terrifying whistle of the falling bomb."`, + mechanics: { economy: true, perks: false, commander: false, fog: false, radstorms: true, horrors: true, encounters: true, nukes: true } + }, + 'alliance': { + narrative: `"No faction can stand alone against the darkness. In this scenario, you are bound by oath to a brother-in-arms. You will be automatically allied with a lore-friendly faction, and you will share their fate. If they fall, you fall. Coordinate your attacks, defend your ally's borders, and crush the other coalitions to achieve a shared victory."`, + mechanics: { economy: true, perks: true, commander: true, fog: true, radstorms: false, horrors: false, encounters: false, nukes: false } + }, + 'covert': { + narrative: `"The deadliest weapon is the one your enemy never sees coming. This is a war fought in the shadows. Use your Commander to infiltrate enemy lines, leverage your faction's perks for an asymmetric advantage, and navigate the fog of war to set the perfect trap. Information is your ammunition. Secrecy is your armor."`, + mechanics: { economy: true, perks: true, commander: true, fog: true, radstorms: false, horrors: false, encounters: true, nukes: false } + }, + 'nuclear': { + narrative: `"The ghosts of the Great War have returned. This simulation is a tense cold war, a frantic arms race to secure the world's remaining nuclear arsenal. Protect your Commander, hunt for launch codes, and make the impossible choice. Will you be the savior of the wasteland, or will you become its destroyer?"`, + mechanics: { economy: true, perks: false, commander: true, fog: true, radstorms: false, horrors: false, encounters: false, nukes: true } + } +}; + +function updatePresetView() { + const selectedMode = presetSelector.value; + if (themeSelector) { themeSelector.dispatchEvent(new Event('change')); } + + if (selectedMode === 'custom') { + descriptionBox.style.display = 'none'; + customRulesBox.style.display = 'block'; + } else { + descriptionBox.style.display = 'block'; + customRulesBox.style.display = 'none'; + + const data = presetData[selectedMode]; + if (!data) return; + + // Update checkboxes behind the scenes + checkboxes.perks.checked = data.mechanics.perks; + checkboxes.commander.checked = data.mechanics.commander; + checkboxes.fog.checked = data.mechanics.fog; + checkboxes.radstorms.checked = data.mechanics.radstorms; + checkboxes.horrors.checked = data.mechanics.horrors; + checkboxes.encounters.checked = data.mechanics.encounters; + checkboxes.nukes.checked = data.mechanics.nukes; + + const economyEnabled = data.mechanics.economy; + + // Build the comma-separated mechanics string + let enabledMechanics = []; + if (economyEnabled) enabledMechanics.push("Wasteland Economy"); + if (data.mechanics.perks) enabledMechanics.push("Faction Perks"); + if (data.mechanics.commander) enabledMechanics.push("Commanders"); + if (data.mechanics.fog) enabledMechanics.push("Fog of War"); + if (data.mechanics.radstorms) enabledMechanics.push("Radstorms"); + if (data.mechanics.horrors) enabledMechanics.push("Wild Ghouls"); + if (data.mechanics.encounters) enabledMechanics.push("Dynamic Encounters"); + if (data.mechanics.nukes) enabledMechanics.push("Scorched Earth"); + + let mechanicsString; + if (enabledMechanics.length > 0) { + mechanicsString = enabledMechanics.join(', '); + } else { + mechanicsString = "None (Card trade-in reinforcements only)"; + } + + // Build the final description HTML + descriptionBox.innerHTML = `

${data.narrative}

MECHANICS ENABLED:
${mechanicsString}`; + } + +} + +// Add event listener and run once on load +presetSelector.addEventListener('change', updatePresetView); +document.addEventListener('DOMContentLoaded', updatePresetView); + + let Gamestate = {}; + +// --- STANDALONE VISUAL THEME CONTROLLER --- +function applyTheme() { + const themeDropdown = document.getElementById('chosen-theme'); + if (!themeDropdown) return; + + const theme = themeDropdown.value; + const leaderInput = document.getElementById('chosen-leader'); + const factionInput = document.getElementById('chosen-country-input'); + + // 1. Apply Theme: Update cursor color + if (typeof updateDynamicCursor === 'function') { + if (theme === 'fnv') { updateDynamicCursor('#ffb642'); } + else if (theme === 'fo4') { updateDynamicCursor('#22ccff'); } + else { updateDynamicCursor('#18ff62'); } + } + // 2. Apply Theme: Swap CSS body class + document.body.classList.remove('theme-fo3', 'theme-fnv', 'theme-fo4'); + if (theme !== 'fo3') { document.body.classList.add('theme-' + theme); } + + // 3. Apply Theme: Auto-fill default names + if (theme === 'fo3') { + if (leaderInput) { leaderInput.value = "Lone Wanderer"; } + if (factionInput) { factionInput.value = "Brotherhood of Steel"; } + } else if (theme === 'fnv') { + if (leaderInput) { leaderInput.value = "Courier Six"; } + if (factionInput) { factionInput.value = "New California Republic"; } + } else if (theme === 'fo4') { + if (leaderInput) { leaderInput.value = "Sole Survivor"; } + if (factionInput) { factionInput.value = "The Minutemen"; } + } +} + +// Ensure the theme is applied on page load and when the theme dropdown is changed. +document.addEventListener('DOMContentLoaded', applyTheme); +document.getElementById('chosen-theme')?.addEventListener('change', applyTheme); + + // --- ERA-SPECIFIC DYNAMIC THEME LOADER --- const themeSelector = document.getElementById('chosen-theme'); -const leaderInput = document.getElementById('chosen-leader'); -const factionInput = document.getElementById('chosen-country'); + // ================================================================= // NEW CUSTOM DROPDOWN & TOOLTIP LOGIC @@ -2554,27 +2691,6 @@ document.addEventListener('DOMContentLoaded', () => { const perksEnabledCheckbox = document.getElementById('opt-perks'); - // Function to enable or disable the custom dropdown based on the checkbox - const toggleFactionInput = () => { - const perksAreOn = perksEnabledCheckbox.checked; - if (perksAreOn) { - factionInput.disabled = false; - factionInput.style.pointerEvents = 'auto'; - factionInput.style.opacity = '1'; - } else { - factionInput.disabled = true; - factionInput.style.pointerEvents = 'none'; - factionInput.style.opacity = '0.5'; - tooltip.style.display = 'none'; // Also hide the tooltip - } - }; - - // Add an event listener to the checkbox itself - perksEnabledCheckbox.addEventListener('change', toggleFactionInput); - - // Call it once on load to set the initial state - toggleFactionInput(); - if (!factionInput || !optionsContainer || !tooltip) return; // --- Function to populate the options --- @@ -2626,10 +2742,35 @@ document.addEventListener('DOMContentLoaded', () => { // --- Event Listeners to control the dropdown --- factionInput.addEventListener('focus', () => { - populateCustomDropdown(); - optionsContainer.style.display = 'block'; + const selectedMode = presetSelector.value; + + // For Alliance Warfare, force the dropdown open and make the input read-only. + if (selectedMode === 'alliance') { + factionInput.readOnly = true; + populateCustomDropdown(); + optionsContainer.style.display = 'block'; + } + // For other modes with perks, allow the dropdown but also allow typing. + else if (checkboxes.perks.checked) { + factionInput.readOnly = false; + populateCustomDropdown(); + optionsContainer.style.display = 'block'; + } + // For modes without perks, do nothing, leaving it as a simple textbox. + else { + factionInput.readOnly = false; + } }); + // Add a listener to remove the read-only state if the user switches away from Alliance mode. + presetSelector.addEventListener('change', () => { + if (presetSelector.value !== 'alliance') { + factionInput.readOnly = false; + } + }); + + + // Hide dropdown if you click anywhere else document.addEventListener('click', (e) => { if (!factionInput.contains(e.target) && !optionsContainer.contains(e.target)) { @@ -2641,46 +2782,11 @@ document.addEventListener('DOMContentLoaded', () => { const themeSelector = document.getElementById('chosen-theme'); if (themeSelector) { themeSelector.addEventListener('change', (e) => { - // First, populate the faction list for the new theme - populateCustomDropdown(); - - // Now, perform all the theme-switching actions - const theme = e.target.value; - const leaderInput = document.getElementById('chosen-leader'); - const factionInput = document.getElementById('chosen-country-input'); - - // 1. Update cursor color - if (typeof updateDynamicCursor === 'function') { - if (theme === 'fnv') { - updateDynamicCursor('#ffb642'); - } else if (theme === 'fo4') { - updateDynamicCursor('#22ccff'); - } else { - updateDynamicCursor('#18ff62'); - } - } - - // 2. Swap CSS body class - document.body.classList.remove('theme-fo3', 'theme-fnv', 'theme-fo4'); - if (theme !== 'fo3') { - document.body.classList.add('theme-' + theme); - } - - // 3. Auto-fill default names - if (theme === 'fo3') { - if (leaderInput) { leaderInput.value = "Lone Wanderer"; } - if (factionInput) { factionInput.value = "Brotherhood of Steel"; } - } else if (theme === 'fnv') { - if (leaderInput) { leaderInput.value = "Courier Six"; } - if (factionInput) { factionInput.value = "New California Republic"; } - } else if (theme === 'fo4') { - if (leaderInput) { leaderInput.value = "Sole Survivor"; } - if (factionInput) { factionInput.value = "The Minutemen"; } - } + // The populateCustomDropdown function is now called by the faction input's 'focus' event. }); // Trigger on boot to apply the default theme immediately - themeSelector.dispatchEvent(new Event('change')); + } @@ -3188,6 +3294,20 @@ Gamestate.updateButtonText = function() { Gamestate.start = async function() { + + // --- APPLY VISUAL THEME AT GAME START --- + const themeDropdown = document.getElementById('chosen-theme'); + if (themeDropdown) { + const theme = themeDropdown.value; + document.body.classList.remove('theme-fo3', 'theme-fnv', 'theme-fo4'); + if (theme !== 'fo3') { + document.body.classList.add('theme-' + theme); + } + } + + + + // --- Read Game Options from UI First --- this.perksEnabled = document.getElementById('opt-perks')?.checked; this.nukesEnabled = document.getElementById('opt-nukes')?.checked; @@ -3312,6 +3432,8 @@ this.wastelandEconomyActive = this.perksEnabled || this.nukesEnabled || this.com }); } else { // --- CLASSIC SETUP: Faction Perks Disabled (Corrected) --- + const themeDropdown = document.getElementById('chosen-theme'); + const selectedTheme = themeDropdown ? themeDropdown.value : "fo3"; const playerLeaderInput = document.getElementById('chosen-leader'); const playerCountryInput = document.getElementById('chosen-country-input'); const playerColorInput = document.getElementById('chosen-color'); @@ -4375,7 +4497,8 @@ if (cardCount) { let viewCardsBtn = document.getElementById('view-cards-btn'); if (viewCardsBtn) { if (this.wastelandEconomyActive) { - viewCardsBtn.textContent = "VIEW STASH"; +viewCardsBtn.textContent = "RECRUITMENT"; +viewCardsBtn.onclick = () => this.showRecruitmentModal(); viewCardsBtn.style.opacity = "1"; viewCardsBtn.style.pointerEvents = "auto"; viewCardsBtn.classList.remove('ready-to-trade'); @@ -5744,7 +5867,7 @@ Gamestate.aiMove = async function() { if (this.wastelandEconomyActive) { this.stage = "Income"; this.updateButtonText(); - if (turnInfoMessage) turnInfoMessage.textContent = "Collect your taxes from the wasteland."; +if (turnInfoMessage) turnInfoMessage.textContent = "Collect taxes and recruit new troops."; let income = this.player.areas.length * 2; let continentIncome = 0; @@ -5762,7 +5885,6 @@ if (this.wastelandEconomyActive) { this.stage = "Fortify"; let bonus = this.unitBonus(this.player, 0); this.player.reserve += bonus; - this.player.army += bonus; } this.updateButtonText(); @@ -5892,20 +6014,38 @@ if (this.wastelandEconomyActive) { if (!turbo) await this.logAction(`${this.players[i].name} recruited ${troopsToBuy} new troops.`); } +} else { + // Original AI card trade-in and reinforcement logic +if (this.wastelandEconomyActive) { + // New AI Economy Logic + let income = this.players[i].areas.length * 2; + let continentIncome = 0; + continents.forEach(continent => { + let ownsContinent = continent.areas.every(area => this.players[i].areas.includes(area) && !this.countries.find(c => c.name === area).isCrater); + if (ownsContinent) { continentIncome += 5; } + }); + income += continentIncome; + this.players[i].caps += income; + if (!turbo) await this.logAction(`${this.players[i].name} collected ${income} Caps in taxes.`); + + const troopCost = 5; + const spendingPercentage = (Math.random() * 0.5) + 0.5; // AI will spend 50-100% of their caps + const affordableTroops = Math.floor(this.players[i].caps / troopCost); + const troopsToBuy = Math.floor(affordableTroops * spendingPercentage); + const totalCost = troopsToBuy * troopCost; + + if (troopsToBuy > 0) { + this.players[i].caps -= totalCost; + this.players[i].reserve += troopsToBuy; + this.players[i].army += troopsToBuy; + if (!turbo) await this.logAction(`${this.players[i].name} recruited ${troopsToBuy} new troops.`); + } + } else { // Original AI card trade-in and reinforcement logic this.players[i].reserve = this.unitBonus(this.players[i], i); let troopsToPlace = this.players[i].reserve; let tradeIndices = this.getBestTrade(this.players[i].cards); - const mrHousePlayer = this.players.find(p => this.perksEnabled && p.perk && p.perk.id === 'the_house_always_wins'); - if (mrHousePlayer && tradeIndices) { - const cardsTraded = 3; - const houseBonus = Math.floor(cardsTraded / 3); - if (houseBonus > 0) { - mrHousePlayer.cards.push({ country: "House Always Wins", type: "Wild" }); - if (!turbo) await this.logAction(`"The House Always Wins..." Mr. House takes a cut of the trade.`); - } - } if (tradeIndices) { let bonus = getTradeBonus(); tradeIndices.sort((a, b) => b - a).forEach(index => { @@ -5917,6 +6057,8 @@ if (this.wastelandEconomyActive) { } this.players[i].army += this.players[i].reserve; if (troopsToPlace > 0 && !turbo) await this.logAction(`${this.players[i].name} deployed ${troopsToPlace} troops to their sectors.`); +} + } let areaToFortify = ["", 0]; @@ -6712,22 +6854,31 @@ Gamestate.showRecruitmentModal = function() { costDisplay.textContent = this.value * troopCost; }; - const newConfirmBtn = confirmBtn.cloneNode(true); - confirmBtn.parentNode.replaceChild(newConfirmBtn, confirmBtn); +const newConfirmBtn = confirmBtn.cloneNode(true); +const cancelBtn = document.getElementById('recruitment-skip'); // Or 'recruitment-cancel' if you renamed it +const newCancelBtn = cancelBtn.cloneNode(true); - newConfirmBtn.onclick = () => { - const troopsToBuy = parseInt(slider.value); - const totalCost = troopsToBuy * troopCost; +confirmBtn.parentNode.replaceChild(newConfirmBtn, confirmBtn); +cancelBtn.parentNode.replaceChild(newCancelBtn, cancelBtn); - if (this.player.caps >= totalCost) { - this.player.caps -= totalCost; - this.player.reserve += troopsToBuy; - this.player.army += troopsToBuy; - this.logAction(`RECRUITMENT: Purchased +${troopsToBuy} troops for ${totalCost} Caps.`, true); - } - modal.style.display = 'none'; - resolve(); - }; +newConfirmBtn.onclick = () => { + const troopsToBuy = parseInt(slider.value); + const totalCost = troopsToBuy * troopCost; + + if (this.player.caps >= totalCost) { + this.player.caps -= totalCost; + this.player.reserve += troopsToBuy; + this.player.army += troopsToBuy; + this.logAction(`RECRUITMENT: Purchased +${troopsToBuy} troops for ${totalCost} Caps.`, true); + } + modal.style.display = 'none'; + resolve(); +}; + +newCancelBtn.onclick = () => { + modal.style.display = 'none'; + resolve(); +}; modal.style.display = 'flex'; });