From 71209a962a00c3bf2e30b5ba4f3080c5fbe11a50 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Fri, 4 Dec 2020 00:40:57 -0500 Subject: [PATCH] Add simpler Player Settings page --- WebHostLib/static/assets/playerSettings.js | 62 +++- WebHostLib/static/static/playerSettings.json | 350 +++++++++---------- WebHostLib/static/styles/playerSettings.css | 12 +- WebHostLib/templates/playerSettings.html | 10 +- 4 files changed, 245 insertions(+), 189 deletions(-) diff --git a/WebHostLib/static/assets/playerSettings.js b/WebHostLib/static/assets/playerSettings.js index 1ede58ae..c5ec3a3d 100644 --- a/WebHostLib/static/assets/playerSettings.js +++ b/WebHostLib/static/assets/playerSettings.js @@ -2,7 +2,14 @@ window.addEventListener('load', () => { fetchSettingData().then((settingData) => { createDefaultSettings(settingData); buildUI(settingData); - populateSettings(); + document.getElementById('export-settings').addEventListener('click', () => exportSettings()); + document.getElementById('generate-race').addEventListener('click', () => generateGame(true)) + document.getElementById('generate-game').addEventListener('click', () => generateGame()); + + const playerSettings = JSON.parse(localStorage.getItem('playerSettings')); + const nameInput = document.getElementById('player-name'); + nameInput.addEventListener('keyup', (event) => updateSetting(event)); + nameInput.value = playerSettings.name; }); }); @@ -17,7 +24,7 @@ const fetchSettingData = () => new Promise((resolve, reject) => { try{ resolve(JSON.parse(ajax.responseText)); } catch(error){ reject(error); } }; - ajax.open('GET', `${window.location.origin}/static/static/playerSettings.json` ,true); + ajax.open('GET', `${window.location.origin}/static/static/playerSettings.json`, true); ajax.send(); }); @@ -27,6 +34,9 @@ const createDefaultSettings = (settingData) => { for (let roSetting of Object.keys(settingData.readOnly)){ newSettings[roSetting] = settingData.readOnly[roSetting]; } + for (let generalOption of Object.keys(settingData.generalOptions)){ + newSettings[generalOption] = settingData.generalOptions[generalOption]; + } for (let gameOption of Object.keys(settingData.gameOptions)){ newSettings[gameOption] = settingData.gameOptions[gameOption].defaultValue; } @@ -42,7 +52,7 @@ const buildUI = (settingData) => { const leftGameOpts = {}; const rightGameOpts = {}; Object.keys(settingData.gameOptions).forEach((key, index) => { - if (index % 2 === 0) { leftGameOpts[key] = settingData.gameOptions[key]; } + if (index < Object.keys(settingData.gameOptions).length / 2) { leftGameOpts[key] = settingData.gameOptions[key]; } else { rightGameOpts[key] = settingData.gameOptions[key]; } }); document.getElementById('game-options-left').appendChild(buildOptionsTable(leftGameOpts)); @@ -52,7 +62,7 @@ const buildUI = (settingData) => { const leftRomOpts = {}; const rightRomOpts = {}; Object.keys(settingData.romOptions).forEach((key, index) => { - if (index % 2 === 0) { leftRomOpts[key] = settingData.romOptions[key]; } + if (index < Object.keys(settingData.romOptions).length / 2) { leftRomOpts[key] = settingData.romOptions[key]; } else { rightRomOpts[key] = settingData.romOptions[key]; } }); document.getElementById('rom-options-left').appendChild(buildOptionsTable(leftRomOpts)); @@ -60,6 +70,7 @@ const buildUI = (settingData) => { }; const buildOptionsTable = (settings) => { + const currentSettings = JSON.parse(localStorage.getItem('playerSettings')); const table = document.createElement('table'); const tbody = document.createElement('tbody'); @@ -78,12 +89,18 @@ const buildOptionsTable = (settings) => { // td Right const tdr = document.createElement('td'); const select = document.createElement('select'); + select.setAttribute('data-key', setting); settings[setting].options.forEach((opt) => { const option = document.createElement('option'); option.setAttribute('value', opt.value); option.innerText = opt.name; + if ((isNaN(currentSettings[setting]) && (parseInt(opt.value, 10) === parseInt(currentSettings[setting]))) || + (opt.value === currentSettings[setting])) { + option.selected = true; + } select.appendChild(option); }); + select.addEventListener('change', (event) => updateSetting(event)); tdr.appendChild(select); tr.appendChild(tdr); tbody.appendChild(tr); @@ -93,12 +110,37 @@ const buildOptionsTable = (settings) => { return table; }; -const populateSettings = () => { - // TODO -}; - -const updateSetting = (key, setting) => { +const updateSetting = (event) => { + console.log(event.target.value); const options = JSON.parse(localStorage.getItem('playerSettings')); - options[key] = setting; + options[event.target.getAttribute('data-key')] = isNaN(event.target.value) ? + event.target.value : parseInt(event.target.value, 10); localStorage.setItem('playerSettings', JSON.stringify(options)); }; + +const exportSettings = () => { + const settings = JSON.parse(localStorage.getItem('playerSettings')); + if (!settings.name || settings.name.trim().length === 0) { settings.name = "noname"; } + const yamlText = jsyaml.safeDump(settings, { noCompatMode: true }).replaceAll(/'(\d+)':/g, (x, y) => `${y}:`); + download(`${document.getElementById('player-name').value}.yaml`, yamlText); +}; + +/** Create an anchor and trigger a download of a text file. */ +const download = (filename, text) => { + const downloadLink = document.createElement('a'); + downloadLink.setAttribute('href','data:text/yaml;charset=utf-8,'+ encodeURIComponent(text)) + downloadLink.setAttribute('download', filename); + downloadLink.style.display = 'none'; + document.body.appendChild(downloadLink); + downloadLink.click(); + document.body.removeChild(downloadLink); +}; + +const generateGame = (raceMode = false) => { + axios.post('/api/generate', { + weights: { player: localStorage.getItem('playerSettings') }, + presetData: { player: localStorage.getItem('playerSettings') }, + playerCount: 1, + race: raceMode ? '1' : '0', + }); +}; diff --git a/WebHostLib/static/static/playerSettings.json b/WebHostLib/static/static/playerSettings.json index 9c031b49..597689c0 100644 --- a/WebHostLib/static/static/playerSettings.json +++ b/WebHostLib/static/static/playerSettings.json @@ -12,78 +12,54 @@ "debug": "off" }, "generalOptions": { - "name": "Player" + "name": "PlayerName" }, "gameOptions": { - "glitches_required":{ - "type": "select", - "friendlyName": "Glitches Required", - "description": "Choose which glitches will be considered in-logic", - "defaultValue": "none", - "options": [ - { - "name": "None", - "value": "none" - }, - { - "name": "Minor Glitches", - "value": "minor_glitches" - }, - { - "name": "Overworld Glitches", - "value": "overworld_glitches" - }, - { - "name": "No Logic", - "value": "no_logic" - } - ] - }, - "dark_room_logic": { + "goals": { "type": "select", - "friendlyName": "Dark Room Logic", - "description": "Choose your logical access to dark rooms", - "defaultValue": "lamp", + "friendlyName": "Goal", + "description": "Choose the condition for winning the game", + "defaultValue": "ganon", "options": [ { - "name": "Lamp Required", - "value": "lamp" + "name": "Kill Ganon", + "value": "ganon" }, { - "name": "Torches Lightable", - "value": "torches" + "name": "Fast Ganon (Pyramid Always Open)", + "value": "fast_ganon" }, { - "name": "Always In-Logic", - "value": "none" + "name": "All Dungeons", + "value": "dungeons" + }, + { + "name": "Master Sword Pedestal", + "value": "pedestal" + }, + { + "name": "Triforce Hunt", + "value": "triforce_hunt" } ] }, - "dungeon_items": { + "mode": { "type": "select", - "friendlyName": "Dungeon Item Shuffle", - "description": "Choose which dungeon items you want shuffled", - "defaultValue": "none", + "friendlyName": "World State", + "description": "Choose the state of the game world", + "defaultValue": "standard", "options": [ { - "name": "None", - "value": "none" + "name": "Standard", + "value": "standard" }, { - "name": "Map & Compass", - "value": "mc" + "name": "Open", + "value": "open" }, { - "name": "Small Keys Only", - "value": "s" - }, - { - "name": "Big Keys Only", - "value": "b" - }, - { - "name": "Full Keysanity", - "value": "mscb" + "name": "Inverted", + "value": "inverted" } ] }, @@ -127,74 +103,6 @@ } ] }, - "entrance_shuffle": { - "type": "select", - "friendlyName": "Entrance Shuffle", - "description": "Shuffles the game map. Not recommended for beginners", - "defaultValue": "none", - "options": [ - { - "name": "None", - "value": "none" - }, - { - "name": "Dungeon Entrances", - "value": "dungeonssimple" - }, - { - "name": "Dungeon Interiors", - "value": "dungeonsfull" - }, - { - "name": "Simple Entrances", - "value": "simple" - }, - { - "name": "Restricted", - "value": "restricted" - }, - { - "name": "Full Shuffle", - "value": "full" - }, - { - "name": "Crossed Shuffle", - "value": "crossed" - }, - { - "name": "Insanity Shuffle", - "value": "insanity" - } - ] - }, - "goals": { - "type": "select", - "friendlyName": "Goal", - "description": "Choose the condition for winning the game", - "defaultValue": "ganon", - "options": [ - { - "name": "Kill Ganon", - "value": "ganon" - }, - { - "name": "Fast Ganon (Pyramid Always Open)", - "value": "fast_ganon" - }, - { - "name": "All Dungeons", - "value": "dungeons" - }, - { - "name": "Master Sword Pedestal", - "value": "pedestal" - }, - { - "name": "Triforce Hunt", - "value": "triforce_hunt" - } - ] - }, "tower_open": { "type": "select", "friendlyName": "Ganon's Tower Access", @@ -283,26 +191,6 @@ } ] }, - "mode": { - "type": "select", - "friendlyName": "World State", - "description": "Choose the state of the game world", - "defaultValue": "standard", - "options": [ - { - "name": "Standard", - "value": "standard" - }, - { - "name": "Open", - "value": "open" - }, - { - "name": "Inverted", - "value": "inverted" - } - ] - }, "retro": { "type": "select", "friendlyName": "Retro Mode", @@ -316,14 +204,6 @@ { "name": "Enabled", "value": "on" - }, - { - "name": "Classic (Universal Small Keys)", - "value": null, - "overrides": { - "retro": "on", - "dungeon_items": "+u" - } } ] }, @@ -367,6 +247,122 @@ } ] }, + "glitches_required":{ + "type": "select", + "friendlyName": "Glitches Required", + "description": "Choose which glitches will be considered in-logic", + "defaultValue": "none", + "options": [ + { + "name": "None", + "value": "none" + }, + { + "name": "Minor Glitches", + "value": "minor_glitches" + }, + { + "name": "Overworld Glitches", + "value": "overworld_glitches" + }, + { + "name": "No Logic", + "value": "no_logic" + } + ] + }, + "dark_room_logic": { + "type": "select", + "friendlyName": "Dark Room Logic", + "description": "Choose your logical access to dark rooms", + "defaultValue": "lamp", + "options": [ + { + "name": "Lamp Required", + "value": "lamp" + }, + { + "name": "Torches Lightable", + "value": "torches" + }, + { + "name": "Always In-Logic", + "value": "none" + } + ] + }, + "dungeon_items": { + "type": "select", + "friendlyName": "Dungeon Item Shuffle", + "description": "Choose which dungeon items you want shuffled", + "defaultValue": "none", + "options": [ + { + "name": "None", + "value": "none" + }, + { + "name": "Map & Compass", + "value": "mc" + }, + { + "name": "Small Keys Only", + "value": "s" + }, + { + "name": "Big Keys Only", + "value": "b" + }, + { + "name": "Full Keysanity", + "value": "mscb" + }, + { + "name": "Universal Small Keys", + "value": "u" + } + ] + }, + "entrance_shuffle": { + "type": "select", + "friendlyName": "Entrance Shuffle", + "description": "Shuffles the game map. Not recommended for beginners", + "defaultValue": "none", + "options": [ + { + "name": "None", + "value": "none" + }, + { + "name": "Dungeon Entrances", + "value": "dungeonssimple" + }, + { + "name": "Dungeon Interiors", + "value": "dungeonsfull" + }, + { + "name": "Simple Entrances", + "value": "simple" + }, + { + "name": "Restricted", + "value": "restricted" + }, + { + "name": "Full Shuffle", + "value": "full" + }, + { + "name": "Crossed Shuffle", + "value": "crossed" + }, + { + "name": "Insanity Shuffle", + "value": "insanity" + } + ] + }, "item_pool": { "type": "select", "friendlyName": "Item Pool", @@ -549,34 +545,6 @@ } ] }, - "heartcolor": { - "type": "select", - "friendlyName": "Heart Color", - "description": "Change the color of your hearts in-game", - "defaultValue": "red", - "options": [ - { - "name": "Red", - "value": "red" - }, - { - "name": "Blue", - "value": "blue" - }, - { - "name": "Green", - "value": "green" - }, - { - "name": "Yellow", - "value": "yellow" - }, - { - "name": "Random", - "value": "random" - } - ] - }, "heartbeep": { "type": "select", "friendlyName": "Heart-Beep Speed", @@ -605,6 +573,34 @@ } ] }, + "heartcolor": { + "type": "select", + "friendlyName": "Heart Color", + "description": "Change the color of your hearts in-game", + "defaultValue": "red", + "options": [ + { + "name": "Red", + "value": "red" + }, + { + "name": "Blue", + "value": "blue" + }, + { + "name": "Green", + "value": "green" + }, + { + "name": "Yellow", + "value": "yellow" + }, + { + "name": "Random", + "value": "random" + } + ] + }, "ow_palettes": { "type": "select", "friendlyName": "Overworld Palette", diff --git a/WebHostLib/static/styles/playerSettings.css b/WebHostLib/static/styles/playerSettings.css index c08f6070..7924d88d 100644 --- a/WebHostLib/static/styles/playerSettings.css +++ b/WebHostLib/static/styles/playerSettings.css @@ -14,6 +14,13 @@ html{ color: #eeffeb; } +#player-settings #player-settings-button-row{ + display: flex; + flex-direction: row; + justify-content: space-between; + margin-top: 15px; +} + #player-settings code{ background-color: #d9cd8e; border-radius: 4px; @@ -71,7 +78,7 @@ html{ flex-direction: row; } -#player-settings .left, #game-options .right{ +#player-settings .left, #player-settings .right{ flex-grow: 1; } @@ -80,7 +87,10 @@ html{ } #player-settings table label{ + display: block; + min-width: 200px; margin-right: 4px; + cursor: default; } @media all and (max-width: 1000px){ diff --git a/WebHostLib/templates/playerSettings.html b/WebHostLib/templates/playerSettings.html index 48a94a43..4f2f6307 100644 --- a/WebHostLib/templates/playerSettings.html +++ b/WebHostLib/templates/playerSettings.html @@ -3,6 +3,8 @@ {% block head %} Player Settings + + {% endblock %} @@ -17,7 +19,7 @@


- +

Game Options

@@ -31,5 +33,11 @@
+ +
+ + + +
{% endblock %}