summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Weipert <code@drogueronin.de>2023-08-18 16:07:39 +0200
committerDaniel Weipert <code@drogueronin.de>2023-08-18 16:07:39 +0200
commitd3e65b98ca932aef1e05e33d74eaf62be520cdd4 (patch)
tree3645410b3c4c857d1a602f6b6abda71b106aae64
parentaa44f67ab57673528e96a4a075fbd8cd0354bd68 (diff)
inventory and items
-rw-r--r--db/all-items.json1
-rw-r--r--db/all-items.php10
-rw-r--r--index.html32
-rw-r--r--resources/css/menu.css53
-rw-r--r--resources/js/classes/InventoryItem.js40
-rw-r--r--resources/js/classes/Item.js22
-rw-r--r--resources/js/classes/State.js2
-rw-r--r--resources/js/db.js16
-rw-r--r--resources/js/game.js17
-rw-r--r--resources/js/helpers.js7
-rw-r--r--resources/js/ui.js148
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));
diff --git a/index.html b/index.html
index b4d1eb5..39dc63e 100644
--- a/index.html
+++ b/index.html
@@ -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);
},