From 8add907aae72aaa6e76c31166a6fc9dbd70af299 Mon Sep 17 00:00:00 2001 From: Karine91 <26932594+Karine91@users.noreply.github.com> Date: Mon, 27 Oct 2025 01:55:40 +0100 Subject: [PATCH] add functionality for using item of a backpack --- index.html | 4 +- src/backpack.js | 140 ++++++++++++++---- .../backpack.interaction.js | 4 +- src/styles/backpack.css | 66 +++++++-- 4 files changed, 167 insertions(+), 47 deletions(-) diff --git a/index.html b/index.html index 7faf820..6033095 100644 --- a/index.html +++ b/index.html @@ -727,8 +727,8 @@

BACKPACK

diff --git a/src/backpack.js b/src/backpack.js index a40b132..05704b6 100644 --- a/src/backpack.js +++ b/src/backpack.js @@ -1,18 +1,34 @@ -import { getGameState, setGameState } from './utils/gameState'; +import { getGameState, saveGameState, setGameState } from './utils/gameState'; import { k } from './kplayCtx'; - +import { updateEnergyUI } from './utils/energyUpdate'; const backpackBtn = document.getElementById('backpack-btn'); let isOpened = false; +let selectedItem = null; + +function consumeInteraction(energyQty) { + const gameState = getGameState(); + gameState.player.energy += energyQty; + setGameState(gameState); + updateEnergyUI(gameState.player.energy); +} + +const itemsMap = { + banana: () => consumeInteraction(10), +}; export class Backpack { - constructor(state, maxSlots = 20) { + constructor(maxSlots = 20) { this.maxSlots = maxSlots; - this.inventory = state; + } + + get inventory() { + const gameState = getGameState(); + return gameState.player.backpack.items; } getInventorySlotHtml(item) { - return `
+ return `
${item.qty || 1}
${item.itemName} ×${item.qty || 1}
@@ -54,11 +70,57 @@ export class Backpack { ); } } + this.saveInventoryState(this.inventory); + } + + saveInventoryState() { const newState = getGameState(); newState.player.backpack.items = this.inventory; setGameState(newState); } + selectItem(slotEl) { + const itemId = slotEl.getAttribute('data-id'); + selectedItem = itemId; + // deselect others + const slots = document.querySelectorAll( + '#backpack-content .inventory-slot:not(.empty)' + ); + slots.forEach((item) => { + item.classList.remove('selected'); + }); + //higlight selected item + slotEl.classList.add('selected'); + } + + decreaseItemQty(itemId) { + const ind = this.inventory.findIndex( + (item) => item.itemName === itemId + ); + + if (ind != -1) { + if (this.inventory[ind].qty >= 2) { + --this.inventory[ind].qty; + } else { + //remove item + this.inventory.splice(ind, 1); + selectedItem = null; + } + this.saveInventoryState(); + this.render(); + } + } + + useItem() { + if (!selectedItem) return; + const action = itemsMap[selectedItem]; + action?.(); + // Decrease the qty + this.decreaseItemQty(selectedItem); + } + + dropItem() {} + render() { const backpack = document.getElementById('backpack-content'); backpack.style.display = 'flex'; @@ -79,10 +141,7 @@ export class Backpack { static getInstance() { const gameState = getGameState(); if (gameState.player.backpack) { - return new Backpack( - gameState.player.backpack.items, - gameState.player.backpack.maxSlots - ); + return new Backpack(gameState.player.backpack.maxSlots); } return false; } @@ -99,34 +158,35 @@ export class Backpack { items: [], maxSlots: 20, }; + saveGameState(gameState); } - handleCloseBackpack() { - if (!k.isFocused()) { - k.pressButton('backpack'); - k.canvas.focus(); + handleDocumentKeypress(e) { + if (e.key == 'i') { + //workaround for press backpack key if canvas lose the focus + if (!k.isFocused()) { + k.canvas.focus(); + k.pressButton('backpack'); + } + } else if (e.key == 'r') { + this.useItem(); } } openBackpack() { this.render(); - document.addEventListener('keypress', this.handleCloseBackpack); - const inventoryCloseBtn = document.getElementById( - 'inventory-close-btn' + document.addEventListener( + 'keypress', + this.handleDocumentKeypress.bind(this) ); - inventoryCloseBtn.addEventListener('click', this.handleCloseBackpack); isOpened = true; } hideBackpack() { this.hide(); - document.removeEventListener('keypress', this.handleCloseBackpack); - const inventoryCloseBtn = document.getElementById( - 'inventory-close-btn' - ); - inventoryCloseBtn.removeEventListener( - 'click', - this.handleCloseBackpack + document.removeEventListener( + 'keypress', + this.handleDocumentKeypress.bind(this) ); isOpened = false; } @@ -134,11 +194,36 @@ export class Backpack { static init() { const instance = Backpack.getInstance(); if (instance) { - backpackBtn.style.display = 'block'; + Backpack.showButton(); backpackBtn.addEventListener('click', () => { instance.openBackpack(); }); + + const inventoryCloseBtn = document.getElementById( + 'inventory-close-btn' + ); + const useItem = document.getElementById('item-use-btn'); + const dropItem = document.getElementById('item-drop-btn'); + inventoryCloseBtn.addEventListener( + 'click', + instance.hideBackpack.bind(instance) + ); + useItem.addEventListener('click', instance.useItem.bind(instance)); + dropItem.addEventListener( + 'click', + instance.dropItem.bind(instance) + ); + + document + .getElementById('backpack-content') + .addEventListener('click', (e) => { + const slot = e.target.closest( + '.inventory-slot:not(.empty)' + ); + if (!slot) return; + instance.selectItem(slot); + }); } } @@ -157,11 +242,6 @@ export class Backpack { static showButton() { backpackBtn.style.display = 'block'; - - const instance = Backpack.getInstance(); - backpackBtn.addEventListener('click', () => { - instance.openBackpack(); - }); } static removeButton() { diff --git a/src/interactions/map_campus_house_1/backpack.interaction.js b/src/interactions/map_campus_house_1/backpack.interaction.js index d068fb9..1906964 100644 --- a/src/interactions/map_campus_house_1/backpack.interaction.js +++ b/src/interactions/map_campus_house_1/backpack.interaction.js @@ -14,10 +14,10 @@ export const backpackInteractions = (player, k, map) => { const dialog = []; if (wantBackpack) { - Backpack.initState(); k.destroy(backpack); // show backpack on side right panel - Backpack.showButton(); + Backpack.initState(); + Backpack.init(); dialog.push('Aye, a trusty pack for a worthy traveler.'); } else { diff --git a/src/styles/backpack.css b/src/styles/backpack.css index ff4d3fd..9ba96b4 100644 --- a/src/styles/backpack.css +++ b/src/styles/backpack.css @@ -47,8 +47,6 @@ .backpack-frame .backpack-container { padding: 0 16px 16px 16px; - max-height: 300px; - overflow: auto; } .backpack-frame .backpack-graphic { @@ -92,6 +90,11 @@ position: relative; } +.backpack-frame .inventory-slot.selected, +.backpack-frame .inventory-slot.selected:hover { + background: #48d387; +} + .backpack-frame .inventory-slot:hover { background: #2d7a4f; transform: translateY(-2px); @@ -202,26 +205,63 @@ cursor: pointer; } -@media (max-width: 600px) { +/* Adding responsive CSS media queries for mobile devices */ +@media (max-width: 768px) { + #backpack-content .backpack-frame { + width: 90vw; + max-width: 400px; + } + .backpack-frame .inventory-grid { - grid-template-columns: repeat(4, 1fr); - gap: 8px; + grid-template-columns: repeat(5, 1fr); + gap: 6px; + } +} + +@media (max-width: 480px) { + #backpack-content .backpack-frame { + width: 70vw; + border: 6px solid #2a1a42; } - .backpack-frame .title-bar h1 { - font-size: 16px; - letter-spacing: 2px; + .backpack-frame .title-bar { + font-size: 14px; + padding: 8px 10px; + letter-spacing: 0.5px; } .backpack-frame .backpack-container { - max-height: 180px; + padding: 12px; + } + + .backpack-frame .inventory-grid { + grid-template-columns: repeat(4, 1fr); + gap: 5px; + margin-bottom: 12px; } - .backpack-frame .inventory-slot { - font-size: 24px; + .backpack-frame .backpack-flap { + height: 10px; + margin-bottom: 8px; } +} - .backpack-frame .footer button { - font-size: 10px; +/* Adding landscape mobile optimization */ +@media (max-height: 800px) { + #backpack-content .backpack-frame { + width: 85vw; + max-width: 600px; + margin: 10px auto; + } + + .backpack-frame .backpack-flap { + height: 12px; + margin-bottom: 6px; + } + + .backpack-frame .inventory-grid { + grid-template-columns: repeat(10, 1fr); + gap: 6px; + margin-bottom: 8px; } }