diff options
author | Daniel Weipert <code@drogueronin.de> | 2023-08-18 16:07:39 +0200 |
---|---|---|
committer | Daniel Weipert <code@drogueronin.de> | 2023-08-18 16:07:39 +0200 |
commit | d3e65b98ca932aef1e05e33d74eaf62be520cdd4 (patch) | |
tree | 3645410b3c4c857d1a602f6b6abda71b106aae64 | |
parent | aa44f67ab57673528e96a4a075fbd8cd0354bd68 (diff) |
inventory and items
-rw-r--r-- | db/all-items.json | 1 | ||||
-rw-r--r-- | db/all-items.php | 10 | ||||
-rw-r--r-- | index.html | 32 | ||||
-rw-r--r-- | resources/css/menu.css | 53 | ||||
-rw-r--r-- | resources/js/classes/InventoryItem.js | 40 | ||||
-rw-r--r-- | resources/js/classes/Item.js | 22 | ||||
-rw-r--r-- | resources/js/classes/State.js | 2 | ||||
-rw-r--r-- | resources/js/db.js | 16 | ||||
-rw-r--r-- | resources/js/game.js | 17 | ||||
-rw-r--r-- | resources/js/helpers.js | 7 | ||||
-rw-r--r-- | resources/js/ui.js | 148 |
11 files changed, 342 insertions, 6 deletions
diff --git a/db/all-items.json b/db/all-items.json new file mode 100644 index 0000000..c996fc1 --- /dev/null +++ b/db/all-items.json @@ -0,0 +1 @@ +["aardant","allies_address","ancient_egg","ancient_tea","app_banking","app_contacts","app_map","app_tuxepedia","boost_armour","boost_dodge","boost_melee","boost_ranged","boost_speed","booster_tech","cureall","dojo_pass","earth_booster","earthmover_key","fire_berry","fire_booster","fishing_rod","flintstone","gold_pass","greenwash_badge","hatchet","imperial_potion","imperial_tea","lucky_bamboo","mega_potion","metal_booster","metal_cherry","miaow_milk","mm_earth","mm_fire","mm_metal","mm_water","mm_wood","mystery_tea","neptune","nimrod_badge","nu_phone","omnichannel_badge","ox_stick","p_monsters_eyes_meet","p_starry_night","p_trepidation","peace_lily","petrified_dung","poseidon","potion","pyramidion","raise_armour","raise_dodge","raise_hp","raise_melee","raise_ranged","raise_speed","reg_papers","restoration","revive","rhincus_fossil","scoop_badge","sea_girdle","shaft_badge","shammer_fossil","sledgehammer","spyder_pass","stovepipe","super_potion","surfboard","sweet_sand","tea","tectonic_drill","thunderstone","tm_avalanche","tm_blossom","tm_earth","tm_fire","tm_metal","tm_surf","tm_water","tm_wood","tuxeball","tuxeball_ancient","tuxeball_candy","tuxeball_crusher","tuxeball_earth","tuxeball_female","tuxeball_fire","tuxeball_hardened","tuxeball_hearty","tuxeball_lavish","tuxeball_male","tuxeball_metal","tuxeball_neuter","tuxeball_omni","tuxeball_peppy","tuxeball_refined","tuxeball_salty","tuxeball_water","tuxeball_wood","tuxeball_xero","tuxeball_zesty","water_booster","wood_booster"]
\ No newline at end of file diff --git a/db/all-items.php b/db/all-items.php new file mode 100644 index 0000000..b6d9a49 --- /dev/null +++ b/db/all-items.php @@ -0,0 +1,10 @@ +<?php + +$allItems = []; +foreach (scandir(dirname(__DIR__) . '/modules/tuxemon/mods/tuxemon/db/item') as $file) { + if (in_array($file, ['.', '..'])) continue; + + $allItems[] = pathinfo($file, PATHINFO_FILENAME); +} + +file_put_contents(__DIR__ . '/all-items.json', json_encode($allItems)); @@ -54,6 +54,22 @@ </div> </template> + <template id="tpl___tabs"> + <div class="tabs"></div> + </template> + <template id="tpl___tabs__tab-heading"> + <div data-template-type="multiple"> + <input data-template-slot="input" type="radio" name="{TABS}" id="{TAB_BUTTON}" aria-controls="{TAB_PANEL}"> + <label data-template-slot="label" class="tabs__tab-heading menu-button" for="{TAB_BUTTON}"></label> + </div> + </template> + <template id="tpl___tabs__panels"> + <div class="tabs__panels"></div> + </template> + <template id="tpl___tabs__tab-panel"> + <section data-template-slot="panel" id="{TAB_PANEL}" class="tabs__tab-panel"></section> + </template> + <template id="tpl___battle__monster"> <div class="battle__monster"> <div class="battle__monster-info"> @@ -189,6 +205,7 @@ <template id="tpl___moveset__list"> <div class="moveset__list"></div> </template> + <template id="tpl___moveset__item"> <div class="moveset__item"> <span data-template-slot="name" class="moveset__item__name"></span> @@ -198,6 +215,20 @@ </div> </template> + <template id="tpl___inventory"> + <div class="inventory"></div> + </template> + + <template id="tpl___inventory__item"> + <div class="inventory__item"> + <img data-template-slot="sprite" /> + <div> + <span data-template-slot="name"></span> + x<span data-template-slot="quantity"></span> + </div> + </div> + </template> + <template id="tpl___menu__journal"> <div class="menu__journal"> <button data-template-slot="save" class="menu-button">Save</button> @@ -226,6 +257,7 @@ <script type="text/javascript" src="/resources/js/classes/Technique.js"></script> <script type="text/javascript" src="/resources/js/classes/StatusEffect.js"></script> <script type="text/javascript" src="/resources/js/classes/Item.js"></script> + <script type="text/javascript" src="/resources/js/classes/InventoryItem.js"></script> <script type="text/javascript" src="/resources/js/classes/State.js"></script> <script type="text/javascript" src="/resources/js/db.js"></script> <script type="text/javascript" src="/resources/js/formula.js"></script> diff --git a/resources/css/menu.css b/resources/css/menu.css index 72c225a..de8d8ce 100644 --- a/resources/css/menu.css +++ b/resources/css/menu.css @@ -42,6 +42,43 @@ } +.tabs { + display: grid; +} + +.tabs > input { + display: none; +} + +.tabs > input:checked + .tabs__tab-heading { + background-color: rgba(255, 255, 255, 0.75); +} +.tabs__tab-heading { + cursor: pointer; + background-color: rgba(0, 0, 0, 0.1); + text-align: center; +} + +.tabs__panels { + grid-column: 1 / -1; +} + +.tabs__tab-panel { + display: none; +} + +.tabs > input:nth-child(1):checked ~ .tabs__panels > .tabs__tab-panel:nth-child(1), +.tabs > input:nth-child(3):checked ~ .tabs__panels > .tabs__tab-panel:nth-child(2), +.tabs > input:nth-child(5):checked ~ .tabs__panels > .tabs__tab-panel:nth-child(3), +.tabs > input:nth-child(7):checked ~ .tabs__panels > .tabs__tab-panel:nth-child(4), +.tabs > input:nth-child(9):checked ~ .tabs__panels > .tabs__tab-panel:nth-child(5), +.tabs > input:nth-child(11):checked ~ .tabs__panels > .tabs__tab-panel:nth-child(6) { + display: block; +} + + + + .gender-icon { line-height: 1em; } @@ -72,6 +109,22 @@ } + +.inventory {} + +.inventory__popup { + align-items: start; +} +.inventory__popup .popup { + margin-top: 10vh; +} + +.inventory .tabs__tab-heading { + font-size: 1.25rem; +} + + + .menu__journal { display: flex; flex-direction: column; diff --git a/resources/js/classes/InventoryItem.js b/resources/js/classes/InventoryItem.js new file mode 100644 index 0000000..9dfdbcc --- /dev/null +++ b/resources/js/classes/InventoryItem.js @@ -0,0 +1,40 @@ +class InventoryItem { + /** + * @type {Item} + */ + item = null; + + /** + * @type {number} + */ + quantity = 1; + + /** + * @param {Item} item + */ + constructor (item) { + this.item = item; + } + + /* Item */ + + get slug () { + return this.item.slug; + } + + get name () { + return this.item.name; + } + + get category () { + return this.item.category; + } + + get type () { + return this.item.type; + } + + get sprite () { + return this.item.sprite; + } +} diff --git a/resources/js/classes/Item.js b/resources/js/classes/Item.js index e274a20..6207cc2 100644 --- a/resources/js/classes/Item.js +++ b/resources/js/classes/Item.js @@ -1 +1,21 @@ -class Item {} +class Item { + constructor (slug) { + this.slug = slug; + } + + get name () { + return slugToName(this.slug); + } + + get category () { + return DB.items[this.slug].category; + } + + get type () { + return DB.items[this.slug].type; + } + + get sprite () { + return DB.items[this.slug].sprite; + } +} diff --git a/resources/js/classes/State.js b/resources/js/classes/State.js index 2384a85..c0065d1 100644 --- a/resources/js/classes/State.js +++ b/resources/js/classes/State.js @@ -10,7 +10,7 @@ class State { monsters = []; /** - * @type {Item[]} + * @type {InventoryItem[]} */ inventory = []; diff --git a/resources/js/db.js b/resources/js/db.js index 96b971a..64b2e9b 100644 --- a/resources/js/db.js +++ b/resources/js/db.js @@ -1,16 +1,19 @@ const DB = { allMonsters: [], allAnimations: {}, + allItems: [], monsters: {}, shapes: {}, elements: {}, techniques: {}, statusEffects: {}, + items: {}, }; async function initializeDB () { DB.allMonsters = await fetch('/db/all-monsters.json').then((response) => response.json()); DB.allAnimations = await fetch('/db/animations.json').then((response) => response.json()); + DB.allItems = await fetch('/db/all-items.json').then((response) => response.json()); DB.shapes = await fetch('/modules/tuxemon/mods/tuxemon/db/shape/shapes.json').then((response) => response.json()); @@ -60,3 +63,16 @@ async function fetchStatusEffect (slug) { return new StatusEffect(slug); } + +/** + * @param {string} slug + * + * @returns {Promise<StatusEffect>} + */ +async function fetchItem (slug) { + if (! DB.items[slug]) { + DB.items[slug] = await fetch(`/modules/tuxemon/mods/tuxemon/db/item/${slug}.json`).then((response) => response.json()); + } + + return new Item(slug); +} diff --git a/resources/js/game.js b/resources/js/game.js index 140f9d5..e383677 100644 --- a/resources/js/game.js +++ b/resources/js/game.js @@ -462,6 +462,7 @@ UI.elements.menuCatch.addEventListener('click', Game.catchMonster); state.enemy.monster = await fetchMonster(possibleStarterMonsters[Math.round(Math.random() * (possibleStarterMonsters.length - 1))]); state.partyMonsters = [ + await fetchMonster('dollfin'), await fetchMonster(possibleStarterMonsters[Math.round(Math.random() * (possibleStarterMonsters.length - 1))]), await fetchMonster('corvix'), await fetchMonster('lunight'), @@ -477,6 +478,22 @@ UI.elements.menuCatch.addEventListener('click', Game.catchMonster); state.activeMonster = state.partyMonsters[0]; state.activeTechnique = state.activeMonster.activeTechniques[0]; + state.inventory = [ + new InventoryItem(await fetchItem('tuxeball')), + new InventoryItem(await fetchItem('ancient_egg')), + new InventoryItem(await fetchItem('sweet_sand')), + new InventoryItem(await fetchItem('tectonic_drill')), + new InventoryItem(await fetchItem('surfboard')), + new InventoryItem(await fetchItem('sledgehammer')), + new InventoryItem(await fetchItem('raise_melee')), + new InventoryItem(await fetchItem('raise_speed')), + new InventoryItem(await fetchItem('mm_fire')), + new InventoryItem(await fetchItem('mm_water')), + new InventoryItem(await fetchItem('cureall')), + new InventoryItem(await fetchItem('potion')), + new InventoryItem(await fetchItem('super_potion')), + ]; + UI.drawEnemyMonster(); UI.drawActiveMonster(); UI.drawActiveTechniques(); diff --git a/resources/js/helpers.js b/resources/js/helpers.js index 019f822..1622eb2 100644 --- a/resources/js/helpers.js +++ b/resources/js/helpers.js @@ -43,3 +43,10 @@ function mixColors(...colors) { return `rgb(${r}, ${g}, ${b})`; } + +/** + * @returns {string} + */ +function randomString () { + return (Math.random() + 1).toString(36).substring(2); +} diff --git a/resources/js/ui.js b/resources/js/ui.js index 3739ce6..455e785 100644 --- a/resources/js/ui.js +++ b/resources/js/ui.js @@ -1,6 +1,11 @@ const Template = { popup: document.querySelector('#tpl___popup'), + tabs: document.querySelector('#tpl___tabs'), + tabHeading: document.querySelector('#tpl___tabs__tab-heading'), + tabPanels: document.querySelector('#tpl___tabs__panels'), + tabPanel: document.querySelector('#tpl___tabs__tab-panel'), + battleMonster: document.querySelector('#tpl___battle__monster'), battleHpBar: document.querySelector('#tpl___battle__hp-bar'), battleExpBar: document.querySelector('#tpl___battle__exp-bar'), @@ -17,6 +22,9 @@ const Template = { movesetList: document.querySelector('#tpl___moveset__list'), movesetItem: document.querySelector('#tpl___moveset__item'), + inventory: document.querySelector('#tpl___inventory'), + inventoryItem: document.querySelector('#tpl___inventory__item'), + menuJournal: document.querySelector('#tpl___menu__journal'), dialogSave: document.querySelector('#tpl___dialog__save'), dialogLoad: document.querySelector('#tpl___dialog__load'), @@ -49,7 +57,22 @@ const UI = { const templateBase = document.createElement('div'); templateBase.innerHTML = template.innerHTML.trim(); - return templateBase.firstChild; + const templateNode = templateBase.firstChild; + + /** + * @param {HTMLElement} targetElement + */ + templateNode.appendTo = function (targetElement) { + if (templateNode.dataset.templateType && templateNode.dataset.templateType === 'multiple') { + for (const child of [...this.children]) { + targetElement.appendChild(child); + } + } else { + targetElement.appendChild(this); + } + }; + + return templateNode; }, /** @@ -69,6 +92,56 @@ const UI = { }, /** + * @typedef {Object} Tab + * @property {HTMLElement} heading + * @property {HTMLElement} content + * @inner + * + * @param {Tab[]} tabs + * + * @returns {HTMLElement} + */ + createTabs (tabs) { + const wrap = UI.createTemplate(Template.tabs); + const panelsNode = UI.createTemplate(Template.tabPanels); + + wrap.style.gridTemplateColumns = '1fr '.repeat(tabs.length); + + const name = randomString(); + + for (const idx in tabs) { + const tab = tabs[idx]; + const tabHeading = UI.createTemplate(Template.tabHeading); + const tabPanel = UI.createTemplate(Template.tabPanel); + + const inputId = `${name}_${idx}`; + const panelId = randomString(); + + const tabHeadingInput = tabHeading.querySelector('[data-template-slot="input"]'); + tabHeadingInput.name = name; + tabHeadingInput.id = inputId; + tabHeadingInput.setAttribute('aria-controls', panelId); + if (idx == 0) { + tabHeadingInput.checked = true; + } + + const tabHeadingLabel = tabHeading.querySelector('[data-template-slot="label"]'); + tabHeadingLabel.setAttribute('for', inputId); + tabHeadingLabel.appendChild(tab.heading); + + tabPanel.id = panelId; + tabPanel.appendChild(tab.content); + + tabHeading.appendTo(wrap); + panelsNode.appendChild(tabPanel); + } + + wrap.appendChild(panelsNode); + + return wrap; + }, + + /** * @param {HTMLElement} slotNode * @param {HTMLElement} replacingNode * @@ -527,15 +600,82 @@ const UI = { UI.drawPopup(popup); }, - openInventoryMenu () { // TODO + openInventoryMenu () { const popup = UI.createPopup(); + const inventory = UI.createTemplate(Template.inventory); + + const tabs = { + heal: { + heading: 'Heal', + items: [], + }, + stats: { + heading: 'Stats', + items: [], + }, + balls: { + heading: 'Balls', + items: [], + }, + techniques: { + heading: 'Techniques', + items: [], + }, + other: { + heading: 'Other', + items: [], + }, + keyItems: { + heading: 'Key Items', + items: [], + }, + }; - const inventory = document.createElement('div'); - inventory.id = 'inventory'; for (const item of state.inventory) { + const inventoryItemNode = UI.createTemplate(Template.inventoryItem); + + inventoryItemNode.querySelector('[data-template-slot="sprite"]').src = `/modules/tuxemon/mods/tuxemon/${item.sprite}`; + inventoryItemNode.querySelector('[data-template-slot="name"]').textContent = item.name; + inventoryItemNode.querySelector('[data-template-slot="quantity"]').textContent = item.quantity; + + if (['potion', 'revive'].includes(item.category)) { + tabs['heal'].items.push(inventoryItemNode); + } + else if (['stats'].includes(item.category)) { + tabs['stats'].items.push(inventoryItemNode); + } + else if (['capture'].includes(item.category)) { + tabs['balls'].items.push(inventoryItemNode); + } + else if (['technique'].includes(item.category)) { + tabs['techniques'].items.push(inventoryItemNode); + } + else if (['KeyItem'].includes(item.type)) { + tabs['keyItems'].items.push(inventoryItemNode); + } + else { + tabs['other'].items.push(inventoryItemNode); + } } + const tabsNode = UI.createTabs(Object.values(tabs).map((tab) => { + const content = document.createElement('div'); + for (const item of tab.items) { + content.appendChild(item); + } + + return { + heading: document.createTextNode(tab.heading), + content: content, + }; + })); + + tabsNode.style.gridTemplateColumns = '1fr 1fr 1fr'; + inventory.appendChild(tabsNode); + popup.querySelector('.popup').appendChild(inventory); + popup.classList.add('inventory__popup'); + UI.drawPopup(popup); }, |