import translate from "./translate-hints.js"; import update from "./fetch-updates.js"; // Global state for overview var current_scene = ""; var current_seed = Number.MAX_VALUE; var current_checks = 0; var current_entrances = 0; var current_codes = Number.MAX_VALUE; var current_hints = 0; var total_checks = 0; var total_entrances = 0; // Global state internal var server_address = "localhost:51111/"; var cross_codes = {}; var can_access_api_server = true; var hide_completed_areas = false; window.onload = async () => { await get_updated_server_address(); fetch(`${document.URL}static/tracker/data/holy_cross_codes.json`) .then((response) => response.json()) .then( (data) => { cross_codes = JSON.parse(JSON.stringify(data)); initialize_elements(); refresh_elements(); }, (error) => { console.log(error); } ); }; async function get_updated_server_address() { fetch(`${document.URL}get/address`) .then( (response) => response.json(), (error) => { console.log("Are you sure the front end is up?"); } ) .then( (data) => { const parsed_data = JSON.parse(JSON.stringify(data)); server_address = parsed_data["listen_address"]; }, (error) => { console.log(error); } ); } // A function to call when the page loads to ensure that we have the correct data when we connect to the server. async function initialize_elements() { // Grab all updates from the backend. let overview = await update.refresh_overview(server_address); if (!overview.overview) { return; } const checks = await update.refresh_checks(server_address); const entrances = await update.refresh_entrances(server_address); const hints = await update.refresh_hints(server_address); overview = overview.overview; // Initialize global state current_checks = checks.collected; current_entrances = entrances.found; total_checks = checks.total; total_entrances = entrances.total; await update_overview(overview, false); // Initialize breakdown list let breakdown_list = document .getElementById("breakdown-list") .cloneNode(true); let summary_list = document.getElementById("summary-list").cloneNode(true); // Initialize the breakdown and summary lists breakdown_list = await initialize_breakdown_list( breakdown_list, checks, entrances ); summary_list = await initialize_summary_list(summary_list, checks, entrances); // Replace breakdown list document .getElementById("breakdown-list") .replaceWith(breakdown_list.cloneNode(true)); // Replace summary list document .getElementById("summary-list") .replaceWith(summary_list.cloneNode(true)); // Update codes list update_codes(overview.codes); // Update hints list update_hints(hints); return; } async function refresh_elements() { const response = await update.refresh_overview(server_address); // Check if the "Hide completed areas" option is checked hide_completed_areas = document.getElementById("hideDone").checked; if (response.overview) { if (!can_access_api_server) { console.log("I found the server!"); can_access_api_server = true; await initialize_elements(); } await update_if_changes(response.overview); } else { if (can_access_api_server) { console.log("Could not access the API server."); } can_access_api_server = false; } setTimeout(refresh_elements, 500); } async function update_if_changes(overview) { document.getElementById("status-block").classList.add("hidden"); const changed_seed = overview.seed != current_seed; const changed_scene = overview.scene != current_scene; const changed_checks = overview.items != current_checks; const changed_entrances = overview.entrances != current_entrances; const changed_hints = overview.hints != current_hints; const any_change = changed_seed || changed_scene || changed_checks || changed_entrances || changed_hints; if (any_change) { await perform_updates( changed_seed, changed_checks, changed_entrances, changed_hints ); await update_overview(overview, changed_scene); } await update_codes(overview.codes); } async function perform_updates( changed_seed, changed_checks, changed_entrances, changed_hints ) { if (changed_seed) { initialize_elements(); console.log(`Seed changed to: ${current_seed}`); } else { if (changed_checks) { update.refresh_checks(server_address).then((data) => { update_breakdown_list(data, { entrances: false, checks: true }); update_summary_list(data, { entrances: false, checks: true }); }); } if (changed_entrances) { update.refresh_entrances(server_address).then((data) => { update_breakdown_list(data, { entrances: true, checks: false }); update_summary_list(data, { entrances: true, checks: false }); }); } if (changed_hints) { update.refresh_hints(server_address).then((data) => update_hints(data)); } } } async function update_overview(overview, changed_scene) { let overview_checks_title = document .getElementById("overview-totals") .querySelector(".overview-checks"); let overview_entrances_title = document .getElementById("overview-totals") .querySelector(".overview-entrances"); // Set content to updated data. overview_checks_title.textContent = `${overview.items}/${total_checks} (${ total_checks - overview.items } left)`; overview_entrances_title.textContent = `${ overview.entrances * 2 }/${total_entrances} (${total_entrances - overview.entrances * 2} left)`; // Set global state current_seed = overview.seed; current_scene = overview.scene; current_checks = overview.items; current_entrances = overview.entrances; current_hints = overview.hints; if (changed_scene) { await update_scene(overview.scene); } } async function update_codes(codes) { // Codes that are always active const default_cross_codes = cross_codes.Default; let new_cross_codes_block_list = document .getElementById("codes-list") .cloneNode(true); let cross_codes_block_list_item = document .getElementById("codes-list") .firstElementChild.cloneNode(true); new_cross_codes_block_list.innerHTML = ""; new_cross_codes_block_list.appendChild( cross_codes_block_list_item.cloneNode(true) ); cross_codes_block_list_item.classList.remove("hidden"); const codes_length = Array.from(Object.keys(codes)).length; if (current_codes != Array.from(Object.keys(codes)).length) { Object.keys(codes) .sort((i, j) => { return codes[i].Distance - codes[j].Distance; }) .forEach((codename, index) => { cross_codes_block_list_item.querySelector( ".codes-list-item-title" ).textContent = codename; if (codes[codename].Global) { cross_codes_block_list_item.querySelector( ".codes-list-item-code" ).textContent = cross_codes["Global"][codename] .replace(/U/g, "⬆️") .replace(/R/g, "➡️") .replace(/D/g, "⬇️") .replace(/L/g, "⬅️"); } else { cross_codes_block_list_item.querySelector( ".codes-list-item-code" ).textContent = cross_codes[current_scene][codename] .replace(/U/g, "⬆️") .replace(/R/g, "➡️") .replace(/D/g, "⬇️") .replace(/L/g, "⬅️"); } cross_codes_block_list_item.dataset.codename = codename; cross_codes_block_list_item.dataset.order = index; new_cross_codes_block_list.appendChild( cross_codes_block_list_item.cloneNode(true) ); }); Object.keys(default_cross_codes).forEach((code) => { cross_codes_block_list_item.querySelector( ".codes-list-item-title" ).textContent = code; cross_codes_block_list_item.querySelector( ".codes-list-item-code" ).textContent = default_cross_codes[code] .replace(/U/g, "⬆️") .replace(/R/g, "➡️") .replace(/D/g, "⬇️") .replace(/L/g, "⬅️"); new_cross_codes_block_list.appendChild( cross_codes_block_list_item.cloneNode(true) ); }); document .getElementById("codes-list") .replaceWith(new_cross_codes_block_list); // Change the number of current codes if it's different current_codes = codes_length; } else { Object.keys(codes) .sort((i, j) => { return codes[i].Distance - codes[j].Distance; }) .forEach((code, index) => { const classes = Array.from( document .getElementById("codes-list") .querySelector(`[data-codename="${code}"]`).classList ).filter((classname) => classname.startsWith("order-")); if (classes.length > 0) { document .getElementById("codes-list") .querySelector(`[data-codename="${code}"]`) .classList.remove(classes); } if (codes[code].Global) { document .getElementById("codes-list") .querySelector(`[data-codename="${code}"]`) .classList.add(`order-last`); } else { document .getElementById("codes-list") .querySelector(`[data-codename="${code}"]`) .classList.add(`order-${index + 1}`); } if (codes[code].InRange) { document .getElementById("codes-list") .querySelector(`[data-codename="${code}"]`) .classList.add(`bg-[#ffe28558]`); } else { document .getElementById("codes-list") .querySelector(`[data-codename="${code}"]`) .classList.remove(`bg-[#ffe28558]`); } document .getElementById("codes-list") .querySelector(`[data-codename="${code}"]`).dataset.order = index + 1; }); } } async function apply_summary_colors(summary) { // Apply color coding to summary block summary.element.classList.remove( "from-highlight-both-light", "from-highlight-checks-light", "from-highlight-entrances-light", "from-highlight-empty-light", "from-highlight-undiscovered-light", "to-highlight-both-dark", "to-highlight-checks-dark", "to-highlight-entrances-dark", "to-highlight-empty-dark", "to-highlight-undiscovered-dark", "text-highlight-both-text", "text-highlight-checks-text", "text-highlight-entrances-text", "text-highlight-empty-text", "text-highlight-undiscovered-text" ); if (summary.checks_total > 0 && summary.entrances_total > 0) { if ( summary.checks_collected == summary.checks_total && summary.entrances_found == summary.entrances_total ) { summary.element.classList.add( "from-highlight-empty-light", "to-highlight-empty-dark", "text-highlight-empty-text" ); } else if ( summary.checks_collected <= 0 && summary.entrances_found <= 0 && summary.entrances_total > 0 ) { summary.element.classList.add( "from-highlight-undiscovered-light", "to-highlight-undiscovered-dark", "text-highlight-undiscovered-text" ); } else if (summary.checks_collected >= summary.checks_total) { summary.element.classList.add( "from-highlight-checks-light", "to-highlight-checks-dark", "text-highlight-checks-text" ); } else if (summary.entrances_found >= summary.entrances_total) { summary.element.classList.add( "from-highlight-entrances-light", "to-highlight-entrances-dark", "text-highlight-entrances-text" ); } else { summary.element.classList.add( "from-highlight-both-light", "to-highlight-both-dark", "text-highlight-both-text" ); } } else { summary.element.classList.add( "from-highlight-undiscovered-light", "to-highlight-undiscovered-dark", "text-highlight-undiscovered-text" ); } return summary.element; } async function initialize_breakdown_list(breakdown_list, checks, entrances) { // Initialize breakdown list let breakdown_list_item = breakdown_list.firstElementChild.cloneNode(true); breakdown_list.innerHTML = ""; breakdown_list.appendChild(breakdown_list_item.cloneNode(true)); breakdown_list_item.classList.remove("hidden"); for (const scene of Object.keys(checks.scenes)) { // Initialize breakdown element breakdown_list_item = await initialize_breakdown( breakdown_list_item, scene, checks.scenes[scene], entrances.scenes[scene] ); // Add breakdown element to breakdown list breakdown_list.appendChild(breakdown_list_item.cloneNode(true)); } // Return initialized breakdown list return breakdown_list; } async function initialize_breakdown( breakdown_element, scene, checks, entrances ) { // Initialize breakdown data breakdown_element.dataset.scene = scene; breakdown_element.querySelector(".breakdown-block-title").textContent = scene; // Populate breakdown lists const breakdown_checks_list = breakdown_element.querySelector(".breakdown-checks"); const breakdown_entrances_list = breakdown_element.querySelector( ".breakdown-entrances" ); const breakdown_mapped_list = breakdown_element.querySelector( ".breakdown-block-mapped-list" ); const new_breakdown_checks_list = await initialize_checks_list( breakdown_checks_list, checks ); const new_entrances_lists = await initialize_entrances_list( breakdown_entrances_list, breakdown_mapped_list, entrances ); // Replace checks, entrances, and mapped lists breakdown_element .querySelector(".breakdown-checks") .replaceWith(new_breakdown_checks_list); breakdown_element .querySelector(".breakdown-entrances") .replaceWith(new_entrances_lists.entrances_list); breakdown_element .querySelector(".breakdown-block-mapped-list") .replaceWith(new_entrances_lists.mapped_list); // Hide breakdowns that aren't for the current scene if (current_scene == scene) { breakdown_element.classList.add("order-first"); breakdown_element.classList.remove("hidden", "order-last"); breakdown_element.dataset.current = true; } else { breakdown_element.classList.add("hidden", "order-last"); breakdown_element.classList.remove("order-first"); breakdown_element.dataset.current = false; } // Return initialized breakdown return breakdown_element; } async function initialize_checks_list(checks_list, checks) { // Initialize new checks list for the breakdown let new_breakdown_block_checks_list = checks_list .querySelector(".breakdown-block-checks-list") .cloneNode(true); let breakdown_block_checks_list_item = new_breakdown_block_checks_list.firstElementChild.cloneNode(true); // Clear new checks list and append blank entry new_breakdown_block_checks_list.innerHTML = ""; new_breakdown_block_checks_list.appendChild( breakdown_block_checks_list_item.cloneNode(true) ); breakdown_block_checks_list_item.classList.remove("hidden"); // Update the check totals checks_list.querySelector( ".breakdown-block-checks-title" ).textContent = `Checks: ${checks.collected}/${checks.total} (${checks.remaining} left)`; checks_list.querySelector( ".breakdown-block-checks-title" ).dataset.checksCollected = checks.collected; checks_list.querySelector( ".breakdown-block-checks-title" ).dataset.checksRemaining = checks.remaining; checks_list.querySelector( ".breakdown-block-checks-title" ).dataset.checksTotal = checks.total; // Populate new checks list Object.keys(checks.checks).forEach((check) => { if (checks.checks[check].name == "") { breakdown_block_checks_list_item.classList.remove("hidden"); } else { breakdown_block_checks_list_item.classList.add("hidden"); } breakdown_block_checks_list_item.textContent = `❌ ${check}`; breakdown_block_checks_list_item.dataset.check = check; new_breakdown_block_checks_list.appendChild( breakdown_block_checks_list_item.cloneNode(true) ); }); // Replace checks list checks_list .querySelector(".breakdown-block-checks-list") .replaceWith(new_breakdown_block_checks_list); return checks_list; } async function initialize_entrances_list( entrances_list, mapped_list, entrances ) { // Create new entrances and mapped lists and append empty entrance and mapped element let new_breakdown_block_entrances_list = entrances_list .querySelector(".breakdown-block-entrances-list") .cloneNode(true); let breakdown_block_entrances_list_item = new_breakdown_block_entrances_list.firstElementChild.cloneNode(true); new_breakdown_block_entrances_list.innerHTML = ""; new_breakdown_block_entrances_list.appendChild( breakdown_block_entrances_list_item.cloneNode(true) ); breakdown_block_entrances_list_item.classList.remove("hidden"); let new_breakdown_block_mapped_list = mapped_list.cloneNode(true); let breakdown_block_mapped_list_item = new_breakdown_block_mapped_list.firstElementChild.cloneNode(true); new_breakdown_block_mapped_list.innerHTML = ""; new_breakdown_block_mapped_list.appendChild( breakdown_block_mapped_list_item.cloneNode(true) ); breakdown_block_mapped_list_item.classList.remove("hidden"); // Update the entrance totals entrances_list.querySelector( ".breakdown-block-entrances-title" ).textContent = `Entrances: ${entrances.found}/${entrances.total} (${entrances.remaining} left)`; entrances_list.querySelector( ".breakdown-block-entrances-title" ).dataset.entrancesFound = entrances.found; entrances_list.querySelector( ".breakdown-block-entrances-title" ).dataset.entrancesRemaining = entrances.remaining; entrances_list.querySelector( ".breakdown-block-entrances-title" ).dataset.entrancesTotal = entrances.total; // Populate new entrances and mapped lists Object.keys(entrances.doors).forEach((entrance) => { breakdown_block_entrances_list_item.dataset.entrance = entrance; breakdown_block_entrances_list_item.textContent = `❌ ${entrance}`; breakdown_block_mapped_list_item.dataset.mapped = entrance; if (entrances.doors[entrance].door == "") { breakdown_block_entrances_list_item.classList.remove("hidden"); new_breakdown_block_entrances_list.appendChild( breakdown_block_entrances_list_item.cloneNode(true) ); breakdown_block_mapped_list_item.textContent = `✔️ ${entrance} -> `; breakdown_block_mapped_list_item.dataset.scene = ""; breakdown_block_mapped_list_item.classList.add("hidden"); new_breakdown_block_mapped_list.appendChild( breakdown_block_mapped_list_item.cloneNode(true) ); } else { breakdown_block_entrances_list_item.classList.add("hidden"); new_breakdown_block_entrances_list.appendChild( breakdown_block_entrances_list_item.cloneNode(true) ); breakdown_block_mapped_list_item.textContent = `✔️ ${entrance} -> ${entrances.doors[entrance].door}`; breakdown_block_mapped_list_item.dataset.scene = entrances.doors[entrance].scene; breakdown_block_mapped_list_item.classList.remove("hidden"); new_breakdown_block_mapped_list.appendChild( breakdown_block_mapped_list_item.cloneNode(true) ); } }); entrances_list .querySelector(".breakdown-block-entrances-list") .replaceWith(new_breakdown_block_entrances_list); // Return entrance and mapped list elements return { entrances_list: entrances_list, mapped_list: new_breakdown_block_mapped_list, }; } async function initialize_summary_list(summary_list, checks, entrances) { // Initialize summary list let summary_list_item = summary_list.firstElementChild.firstElementChild.cloneNode(true); summary_list.firstElementChild.innerHTML = ""; summary_list.firstElementChild.appendChild(summary_list_item.cloneNode(true)); summary_list_item.classList.remove("hidden"); for (const scene of Object.keys(checks.scenes)) { // Initialize summary element summary_list_item = await initialize_summary( summary_list_item, scene, checks.scenes[scene], entrances.scenes[scene] ); // Add summary element to summary list summary_list.firstElementChild.appendChild( summary_list_item.cloneNode(true) ); } // Return initialized summary list return summary_list; } async function initialize_summary(summary_element, scene, checks, entrances) { // Initialize summary data summary_element.dataset.scene = scene; summary_element.querySelector(".summary-title").textContent = scene; const summary_checks = summary_element.querySelector(".summary-checks"); summary_checks.dataset.checksCollected = checks.collected; summary_checks.dataset.checksRemaining = checks.remaining; summary_checks.dataset.checksTotal = checks.total; summary_checks.textContent = `Checks: ${checks.collected}/${checks.total} (${checks.remaining})`; const summary_entrances = summary_element.querySelector(".summary-entrances"); summary_entrances.dataset.entrancesFound = entrances.found; summary_entrances.dataset.entrancesRemaining = entrances.remaining; summary_entrances.dataset.entrancesTotal = entrances.total; summary_entrances.textContent = `Entrances: ${entrances.found}/${entrances.total} (${entrances.remaining})`; // Apply summary color coding summary_element.firstElementChild.replaceWith( await apply_summary_colors({ element: summary_element.firstElementChild, checks_collected: checks.collected, checks_total: checks.total, entrances_found: entrances.found, entrances_total: entrances.total, }) ); // Hide summaries for scenes with no entrances and the current scene and completed areas if "Hide completed areas" is checked if (entrances.total <= 0 || current_scene == scene) { summary_element.dataset.current = current_scene == scene; summary_element.classList.add("hidden"); } else { if (hide_completed_areas) { if (checks.remaining > 0 && entrances.remaining > 0) { summary_element.classList.remove("hidden"); } else { summary_element.classList.add("hidden"); } } else { summary_element.dataset.current = false; summary_element.classList.remove("hidden"); } } // Return initialized summary return summary_element; } async function update_summary(scene, checks, entrances) { // Grab summary checks and entrances const summary_checks = document.querySelector( `.summary[data-scene="${scene}"] .summary-checks` ); const summary_entrances = document.querySelector( `.summary[data-scene="${scene}"] .summary-entrances` ); // Make variables for whether checks or entrances updated const checks_changed = summary_checks.dataset.checksCollected != checks.collected; const entrances_changed = summary_entrances.dataset.entrancesFound != entrances.found; // Check for changes, and if so, update if (checks_changed) { summary_checks.dataset.checksCollected = checks.collected; summary_checks.dataset.checksRemaining = checks.remaining; summary_checks.dataset.checksTotal = checks.total; summary_checks.textContent = `Checks: ${checks.collected}/${checks.total} (${checks.remaining})`; } if (entrances_changed) { summary_entrances.dataset.entrancesFound = entrances.found; summary_entrances.dataset.entrancesRemaining = entrances.remaining; summary_entrances.dataset.entrancesTotal = entrances.total; summary_entrances.textContent = `Entrances: ${entrances.found}/${entrances.total} (${entrances.remaining})`; } // Apply color coding if (checks_changed || entrances_changed) { document .querySelector(`.summary[data-scene="${scene}"]`) .firstElementChild.replaceWith( await apply_summary_colors({ element: document.querySelector(`.summary[data-scene="${scene}"]`) .firstElementChild, checks_collected: checks.collected, checks_total: checks.total, entrances_found: entrances.found, entrances_total: entrances.total, }) ); } } async function update_summary_list(data, changed) { // Check whether entrances or checks changed if (changed.checks) { Object.keys(data.scenes).forEach((scene) => update_summary(scene, data.scenes[scene], { found: document.querySelector( `.summary[data-scene="${scene}"] .summary-entrances` ).dataset.entrancesFound, remaining: document.querySelector( `.summary[data-scene="${scene}"] .summary-entrances` ).dataset.entrancesRemaining, total: document.querySelector( `.summary[data-scene="${scene}"] .summary-entrances` ).dataset.entrancesTotal, }) ); } else if (changed.entrances) { Object.keys(data.scenes).forEach((scene) => update_summary( scene, { collected: document.querySelector( `.summary[data-scene="${scene}"] .summary-checks` ).dataset.checksCollected, remaining: document.querySelector( `.summary[data-scene="${scene}"] .summary-checks` ).dataset.checksRemaining, total: document.querySelector( `.summary[data-scene="${scene}"] .summary-checks` ).dataset.checksTotal, }, data.scenes[scene] ) ); } } async function update_breakdown_checks(scene, checks) { // Update the check totals document.querySelector( `.breakdown[data-scene="${scene}"] .breakdown-block-checks-title` ).textContent = `Checks: ${checks.collected}/${checks.total} (${checks.remaining} left)`; document.querySelector( `.breakdown[data-scene="${scene}"] .breakdown-block-checks-title` ).dataset.checksCollected = checks.collected; document.querySelector( `.breakdown[data-scene="${scene}"] .breakdown-block-checks-title` ).dataset.checksRemaining = checks.remaining; document.querySelector( `.breakdown[data-scene="${scene}"] .breakdown-block-checks-title` ).dataset.checksTotal = checks.total; // Hide collected checks for (const check of Object.keys(checks.checks)) { const check_item = document.querySelector( `.breakdown[data-scene="${scene}"] [data-check="${check}"]` ); if (checks.checks[check].name == "") { check_item.classList.remove("hidden"); } else { check_item.classList.add("hidden"); } } } async function update_breakdown_entrances(scene, entrances) { // Update the entrance totals document.querySelector( `.breakdown[data-scene="${scene}"] .breakdown-block-entrances-title` ).textContent = `Entrances: ${entrances.found}/${entrances.total} (${entrances.remaining} left)`; document.querySelector( `.breakdown[data-scene="${scene}"] .breakdown-block-entrances-title` ).dataset.entrancesFound = entrances.found; document.querySelector( `.breakdown[data-scene="${scene}"] .breakdown-block-entrances-title` ).dataset.entrancesRemaining = entrances.remaining; document.querySelector( `.breakdown[data-scene="${scene}"] .breakdown-block-entrances-title` ).dataset.entrancesTotal = entrances.total; // Map found entrances for (const entrance of Object.keys(entrances.doors)) { const entrance_item = document.querySelector( `.breakdown[data-scene="${scene}"] [data-entrance="${entrance}"]` ); const mapped_item = document.querySelector( `.breakdown[data-scene="${scene}"] [data-mapped="${entrance}"]` ); if (entrances.doors[entrance].door == "") { entrance_item.classList.remove("hidden"); mapped_item.classList.add("hidden"); } else { entrance_item.classList.add("hidden"); mapped_item.classList.remove("hidden"); mapped_item.dataset.scene = entrances.doors[entrance].scene; mapped_item.textContent = `✔️ ${entrance} -> ${entrances.doors[entrance].scene}`; } } } async function update_breakdown(scene, checks, entrances) { // Grab breakdown checks and entrances const breakdown_checks = document.querySelector( `.breakdown[data-scene="${scene}"] .breakdown-block-checks-title` ); const breakdown_entrances = document.querySelector( `.breakdown[data-scene="${scene}"] .breakdown-block-entrances-title` ); // Check for changes, and if so, update if (breakdown_checks.dataset.checksCollected != checks.collected) { update_breakdown_checks(scene, checks); } if (breakdown_entrances.dataset.entrancesFound != entrances.found) { update_breakdown_entrances(scene, entrances); } } async function update_breakdown_list(data, changed) { // Check whether entrances or checks changed if (changed.checks) { Object.keys(data.scenes).forEach((scene) => update_breakdown(scene, data.scenes[scene], { found: document.querySelector( `.breakdown[data-scene="${scene}"] .breakdown-block-entrances-title` ).dataset.entrancesFound, }) ); } else if (changed.entrances) { Object.keys(data.scenes).forEach((scene) => update_breakdown( scene, { collected: document.querySelector( `.breakdown[data-scene="${scene}"] .breakdown-block-checks-title` ).dataset.checksCollected, }, data.scenes[scene] ) ); } } async function update_hints(hints) { let hints_list = document.getElementById("hints-list").cloneNode(true); let hints_list_item = hints_list.firstElementChild.cloneNode(true); hints_list.innerHTML = ""; hints_list.appendChild(hints_list_item.cloneNode(true)); hints_list_item.classList.remove("hidden"); Object.keys(hints).forEach((hint_index) => { let hint = hints[hint_index].split( /(\[[\w\s]+?\]|\"[\w \d\>\<#.\-\']+\"|\<[\w\d#]+\>)/gm ); hint = hint.map((segment) => { segment = segment.trim(); if (segment) { if ( !( segment.startsWith("[") || segment.startsWith("<") || segment.startsWith('"') ) ) { segment = translate(segment.trim()); } else if (segment.startsWith("[")) { segment = segment.trim(); } else { segment = ` ${segment.trim()} `; } return segment; } }); hints[hint_index] = hint.join(""); }); Object.keys(hints).forEach((hint_index) => { let hint = hints[hint_index]; let matches = Array.from( hint.matchAll(/\<([\w\d#]+)\>(.*)(\<[\w\d#]+\>)/gm) ); if (matches.length >= 1) { hint = hint.replace( /(\<[\w\d#]+\>.*\<[\w\d#]+\>)/gm, `${matches[0][2]}` ); } hint = hint.replace(/ "|" /gm, " "); hints_list_item.firstElementChild.innerHTML = hint; hints_list.appendChild(hints_list_item.cloneNode(true)); }); document.getElementById("hints-list").innerHTML = ""; document.getElementById("hints-list").replaceWith(hints_list); } async function update_scene(scene) { // Grab current scene and updated scene breakdown/summary elements const current_breakdown_old = document.querySelector( `.breakdown[data-current="true"]` ); const current_breakdown = document.querySelector( `.breakdown[data-scene="${scene}"]` ); const current_summary_old = document.querySelector( `.summary[data-current="true"]` ); const current_summary = document.querySelector( `.summary[data-scene="${scene}"]` ); // Update the current breakdown current_breakdown_old.dataset.current = false; current_breakdown_old.classList.add("hidden", "order-last"); current_breakdown_old.classList.remove("order-first"); current_breakdown.dataset.current = true; current_breakdown.classList.add("order-first"); current_breakdown.classList.remove("hidden", "order-last"); // Update the current summary current_summary_old.dataset.current = false; if (hide_completed_areas) { const checks_remaining = parseInt( current_summary_old.dataset.checksRemaining ); const entrances_remaining = parseInt( current_summary_old.dataset.entrancesRemaining ); if (checks_remaining > 0 && entrances_remaining > 0) { current_summary_old.classList.remove("hidden"); } else { current_summary_old.classList.add("hidden"); } } else { current_summary_old.classList.remove("hidden"); } current_summary.dataset.current = true; current_summary.classList.add("hidden"); }