Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -727,8 +727,8 @@ <h1>BACKPACK</h1>
</div>

<div class="footer">
<button id="item-use-btn">Use (enter)</button>
<button id="item-unequip-btn">Unequip (delete)</button>
<button id="item-use-btn">Use (R)</button>
<button id="item-drop-btn">Drop (X)</button>
<button id="inventory-close-btn">Close (I)</button>
</div>
</div>
Expand Down
140 changes: 110 additions & 30 deletions src/backpack.js
Original file line number Diff line number Diff line change
@@ -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 `<div class="inventory-slot">
return `<div class="inventory-slot ${selectedItem === item.itemName ? 'selected' : ''}" data-id="${item.itemName}">
<img src="${item.assetUrl}" with="100%" alt="" />
<span class="quantity-badge">${item.qty || 1}</span>
<div class="tooltip">${item.itemName} ×${item.qty || 1}</div>
Expand Down Expand Up @@ -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';
Expand All @@ -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;
}
Expand All @@ -99,46 +158,72 @@ 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;
}

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);
});
}
}

Expand All @@ -157,11 +242,6 @@ export class Backpack {

static showButton() {
backpackBtn.style.display = 'block';

const instance = Backpack.getInstance();
backpackBtn.addEventListener('click', () => {
instance.openBackpack();
});
}

static removeButton() {
Expand Down
4 changes: 2 additions & 2 deletions src/interactions/map_campus_house_1/backpack.interaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
66 changes: 53 additions & 13 deletions src/styles/backpack.css
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,6 @@

.backpack-frame .backpack-container {
padding: 0 16px 16px 16px;
max-height: 300px;
overflow: auto;
}

.backpack-frame .backpack-graphic {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}
}