(function () { "use strict"; var TAB_IDS = ["game", "multiplayer"]; function deepCloneSettingsData(source) { return JSON.parse(JSON.stringify(source || [])); } function flattenSettings(sections) { var flat = []; sections.forEach(function (section) { section.items.forEach(function (item) { flat.push(item); }); }); return flat; } function buildMarkup(options) { var showHeading = options.showHeading !== false; var gameTabLabel = options.gameTabLabel || "FALLOUT 4"; var multiplayerTabLabel = options.multiplayerTabLabel || "MULTIPLAYER"; return ( '
' + '
' + (showHeading ? '

SETTINGS

' : "") + '
' + '" + '" + "
" + '
' + '" + '
' + '
' + '

' + "
" + "
" + "
" + "
" ); } /** * Reusable Commonwealth Online settings UI. * * Usage (pause menu, main menu, etc.): * * var settings = CO_Settings.create({ * mount: document.getElementById("settingsMount"), * promptBar: document.getElementById("settingsPromptBar"), // optional * bridge: window.CommonwealthOnlineUI, * onBack: function () { ... }, * layout: "embedded", // or "standalone" * }); * settings.show(); */ function create(options) { if (!options || !options.mount) { throw new Error("CO_Settings.create requires a mount element"); } var mountEl = options.mount; var promptBarEl = options.promptBar || null; var bridge = options.bridge || {}; var onBack = typeof options.onBack === "function" ? options.onBack : function () {}; var layout = options.layout || "embedded"; var showHeading = options.showHeading !== false; var multiplayerSettings = deepCloneSettingsData( options.settingsData || window.CO_SettingsData ); var flatSettings = flattenSettings(multiplayerSettings); var visible = false; var settingsFocus = "tabs"; var activeTab = options.defaultTab || "multiplayer"; var settingIndex = 0; mountEl.innerHTML = buildMarkup({ layout: layout, showHeading: showHeading, gameTabLabel: options.gameTabLabel, multiplayerTabLabel: options.multiplayerTabLabel }); var rootEl = mountEl.querySelector(".co-settings"); var settingsTabsEl = mountEl.querySelector("[data-co-settings-tabs]"); var settingsRowsEl = mountEl.querySelector("[data-co-settings-rows]"); var settingsDescriptionEl = mountEl.querySelector("[data-co-settings-description]"); var gameSettingsPane = mountEl.querySelector('[data-co-settings-pane="game"]'); var multiplayerSettingsPane = mountEl.querySelector('[data-co-settings-pane="multiplayer"]'); var openGameSettingsBtn = mountEl.querySelector("[data-co-settings-open-game]"); var tabButtons = settingsTabsEl ? Array.prototype.slice.call(settingsTabsEl.querySelectorAll(".settings-tab")) : []; function getCurrentSetting() { return flatSettings[settingIndex] || null; } function formatSettingValue(setting) { if (setting.type === "toggle") { return setting.value ? "ON" : "OFF"; } if (setting.type === "slider") { return setting.value + "%"; } return String(setting.value); } function notifySettingChanged(setting) { if (typeof bridge.setMultiplayerSetting === "function") { bridge.setMultiplayerSetting(setting.key, setting.value); } } function renderTabs() { if (!settingsTabsEl) { return; } tabButtons.forEach(function (tab) { var tabId = tab.getAttribute("data-tab"); var isSelected = tabId === activeTab; tab.classList.toggle("is-selected", isSelected); tab.setAttribute("aria-selected", isSelected ? "true" : "false"); tab.tabIndex = isSelected ? 0 : -1; }); settingsTabsEl.classList.toggle("is-focus-region", settingsFocus === "tabs"); } function buildValueMarkup(setting) { if (setting.type === "slider") { var min = setting.min != null ? setting.min : 0; var max = setting.max != null ? setting.max : 100; var fill = ((setting.value - min) / (max - min)) * 100; return ( '" ); } var usesCycler = setting.type === "toggle" || setting.type === "select"; if (usesCycler) { return ( '' + '' + '' + '' + "" ); } return ''; } function renderRows() { if (!settingsRowsEl) { return; } settingsRowsEl.innerHTML = ""; var flatIndex = 0; multiplayerSettings.forEach(function (section) { var sectionEl = document.createElement("div"); sectionEl.className = "settings-section"; var heading = document.createElement("h3"); heading.className = "settings-section__heading"; heading.textContent = section.section; sectionEl.appendChild(heading); section.items.forEach(function (setting) { var index = flatIndex; flatIndex += 1; var button = document.createElement("button"); button.type = "button"; button.className = "settings-row"; button.setAttribute("role", "option"); button.setAttribute("data-setting-index", String(index)); button.setAttribute("data-setting-key", setting.key); if (index === settingIndex) { button.classList.add("is-selected"); } if (setting.type === "text" || setting.type === "keybind") { button.classList.add("is-readonly"); } button.innerHTML = '' + '' + buildValueMarkup(setting) + "" + ''; button.querySelector(".settings-row__label").textContent = setting.label; var valueText = button.querySelector(".settings-row__value-text"); if (valueText) { valueText.textContent = formatSettingValue(setting); } button.addEventListener("click", function () { settingIndex = index; settingsFocus = "content"; activeTab = "multiplayer"; render(); }); sectionEl.appendChild(button); }); settingsRowsEl.appendChild(sectionEl); }); settingsRowsEl.classList.toggle( "is-focus-region", settingsFocus === "content" && activeTab === "multiplayer" ); } function renderDescription() { if (!settingsDescriptionEl) { return; } var setting = getCurrentSetting(); settingsDescriptionEl.textContent = setting && setting.description ? setting.description : ""; } function renderPanes() { if (gameSettingsPane) { gameSettingsPane.hidden = activeTab !== "game"; gameSettingsPane.classList.toggle( "is-focus-region", settingsFocus === "content" && activeTab === "game" ); } if (multiplayerSettingsPane) { multiplayerSettingsPane.hidden = activeTab !== "multiplayer"; } if (openGameSettingsBtn) { openGameSettingsBtn.classList.toggle( "is-focused", settingsFocus === "content" && activeTab === "game" ); } } function render() { renderTabs(); renderPanes(); renderRows(); renderDescription(); } function setActiveTab(tabId) { if (TAB_IDS.indexOf(tabId) === -1) { return; } activeTab = tabId; render(); } function moveTab(delta) { var current = TAB_IDS.indexOf(activeTab); if (current < 0) { current = 0; } var next = current + delta; if (next < 0) { next = TAB_IDS.length - 1; } else if (next >= TAB_IDS.length) { next = 0; } setActiveTab(TAB_IDS[next]); } function setSettingsFocus(region) { settingsFocus = region; render(); } function adjustSetting(delta) { var setting = getCurrentSetting(); if (!setting || activeTab !== "multiplayer") { return; } if (setting.type === "text") { console.log("[CO Settings] Display name editing not implemented:", setting.key); return; } if (setting.type === "keybind") { console.log( "[CO Settings] Keybind selected (rebinding not implemented):", setting.key, setting.value ); return; } if (setting.type === "toggle") { setting.value = !setting.value; notifySettingChanged(setting); render(); return; } if (setting.type === "select") { var optionsList = setting.options || []; if (!optionsList.length) { return; } var current = optionsList.indexOf(setting.value); if (current < 0) { current = 0; } var next = current + (delta === 0 ? 1 : delta); if (next < 0) { next = optionsList.length - 1; } else if (next >= optionsList.length) { next = 0; } setting.value = optionsList[next]; notifySettingChanged(setting); render(); return; } if (setting.type === "slider") { var step = setting.step != null ? setting.step : 5; var min = setting.min != null ? setting.min : 0; var max = setting.max != null ? setting.max : 100; var amount = delta === 0 ? step : delta * step; var nextValue = Math.max(min, Math.min(max, setting.value + amount)); if (nextValue === setting.value) { return; } setting.value = nextValue; notifySettingChanged(setting); render(); } } function activateSetting() { var setting = getCurrentSetting(); if (!setting || activeTab !== "multiplayer") { return; } if (setting.type === "toggle" || setting.type === "select" || setting.type === "slider") { adjustSetting(setting.type === "toggle" ? 0 : 1); return; } if (setting.type === "keybind") { console.log( "[CO Settings] Keybind selected (rebinding not implemented):", setting.key, setting.value ); return; } if (setting.type === "text") { console.log("[CO Settings] Display name editing not implemented:", setting.key); } } function moveSettingRow(delta) { if (!flatSettings.length) { return; } var next = settingIndex + delta; if (next < 0) { next = flatSettings.length - 1; } else if (next >= flatSettings.length) { next = 0; } settingIndex = next; render(); if (settingsRowsEl) { var selectedRow = settingsRowsEl.querySelector( '[data-setting-index="' + settingIndex + '"]' ); if (selectedRow && typeof selectedRow.scrollIntoView === "function") { selectedRow.scrollIntoView({ block: "nearest" }); } } } function openVanillaGameSettings() { if (typeof bridge.openGameSettings === "function") { bridge.openGameSettings(); } } tabButtons.forEach(function (tab) { tab.addEventListener("click", function () { settingsFocus = "tabs"; setActiveTab(tab.getAttribute("data-tab")); }); }); if (openGameSettingsBtn) { openGameSettingsBtn.addEventListener("click", function () { settingsFocus = "content"; activeTab = "game"; render(); openVanillaGameSettings(); }); } function show() { visible = true; settingsFocus = "tabs"; activeTab = options.defaultTab || "multiplayer"; settingIndex = 0; if (promptBarEl) { promptBarEl.hidden = false; } render(); } function hide() { visible = false; if (promptBarEl) { promptBarEl.hidden = true; } } function handleKeydown(event) { if (!visible) { return false; } switch (event.key) { case "ArrowUp": event.preventDefault(); if (settingsFocus === "content" && activeTab === "multiplayer") { moveSettingRow(-1); } return true; case "ArrowDown": event.preventDefault(); if (settingsFocus === "content" && activeTab === "multiplayer") { moveSettingRow(1); } return true; case "ArrowLeft": event.preventDefault(); if (settingsFocus === "tabs") { moveTab(-1); } else if (activeTab === "multiplayer") { adjustSetting(-1); } else { moveTab(-1); } return true; case "ArrowRight": event.preventDefault(); if (settingsFocus === "tabs") { moveTab(1); } else if (activeTab === "multiplayer") { adjustSetting(1); } else { moveTab(1); } return true; case "Tab": event.preventDefault(); setSettingsFocus(settingsFocus === "tabs" ? "content" : "tabs"); return true; case "Enter": case " ": event.preventDefault(); if (settingsFocus === "tabs") { setSettingsFocus("content"); } else if (activeTab === "game") { openVanillaGameSettings(); } else { activateSetting(); } return true; case "Escape": event.preventDefault(); hide(); onBack(); return true; default: return false; } } function destroy() { hide(); mountEl.innerHTML = ""; } return { show: show, hide: hide, isVisible: function () { return visible; }, handleKeydown: handleKeydown, render: render, destroy: destroy, getSettingsData: function () { return multiplayerSettings; } }; } window.CO_Settings = { create: create }; })();