diff --git a/apps/manavoxel/apps/web/src/lib/components/Inventory.svelte b/apps/manavoxel/apps/web/src/lib/components/Inventory.svelte new file mode 100644 index 000000000..1af5ea77c --- /dev/null +++ b/apps/manavoxel/apps/web/src/lib/components/Inventory.svelte @@ -0,0 +1,74 @@ + + +
+ {#each { length: MAX_INVENTORY_SLOTS } as _, i} + {@const item = inventory.slots[i]} + {@const isHeld = inventory.heldSlot === i} + + {/each} +
diff --git a/apps/manavoxel/apps/web/src/lib/engine/inventory.ts b/apps/manavoxel/apps/web/src/lib/engine/inventory.ts new file mode 100644 index 000000000..f428763c8 --- /dev/null +++ b/apps/manavoxel/apps/web/src/lib/engine/inventory.ts @@ -0,0 +1,85 @@ +import type { SpriteData } from '$lib/editor/sprite-editor.svelte'; +import type { ItemProperties, Rarity, ElementType } from '@manavoxel/shared'; + +export interface GameItem { + id: string; + name: string; + sprite: SpriteData; + properties: ItemProperties; + rarity: Rarity; +} + +const defaultProperties: ItemProperties = { + damage: 0, + range: 1, + speed: 1, + durabilityMax: 100, + durabilityCurrent: 100, + element: 'neutral', + rarity: 'common', + sound: 'hit_default', + particle: 'none', +}; + +let nextItemId = 1; + +/** Create a new item from sprite data */ +export function createItem( + name: string, + sprite: SpriteData, + partialProps?: Partial +): GameItem { + return { + id: `item_${nextItemId++}`, + name, + sprite, + properties: { ...defaultProperties, ...partialProps }, + rarity: partialProps?.rarity ?? 'common', + }; +} + +export const MAX_INVENTORY_SLOTS = 8; +export const MAX_EQUIPMENT_SLOTS = 1; // Held item + +export class Inventory { + slots: (GameItem | null)[] = $state(Array(MAX_INVENTORY_SLOTS).fill(null)); + heldSlot: number = $state(-1); // -1 = nothing held + + get heldItem(): GameItem | null { + if (this.heldSlot < 0 || this.heldSlot >= this.slots.length) return null; + return this.slots[this.heldSlot]; + } + + /** Add item to first empty slot. Returns slot index or -1 if full. */ + addItem(item: GameItem): number { + const emptySlot = this.slots.findIndex((s) => s === null); + if (emptySlot === -1) return -1; + this.slots[emptySlot] = item; + return emptySlot; + } + + /** Remove item from a slot */ + removeItem(slot: number): GameItem | null { + if (slot < 0 || slot >= this.slots.length) return null; + const item = this.slots[slot]; + this.slots[slot] = null; + if (this.heldSlot === slot) this.heldSlot = -1; + return item; + } + + /** Select a slot to hold */ + selectSlot(slot: number) { + if (slot < 0 || slot >= this.slots.length) return; + this.heldSlot = this.heldSlot === slot ? -1 : slot; + } + + /** Check if inventory is full */ + get isFull(): boolean { + return this.slots.every((s) => s !== null); + } + + /** Count of items */ + get count(): number { + return this.slots.filter((s) => s !== null).length; + } +} diff --git a/apps/manavoxel/apps/web/src/routes/+page.svelte b/apps/manavoxel/apps/web/src/routes/+page.svelte index 94eef7ea4..f64a5d53f 100644 --- a/apps/manavoxel/apps/web/src/routes/+page.svelte +++ b/apps/manavoxel/apps/web/src/routes/+page.svelte @@ -5,6 +5,8 @@ import type { ToolType } from '$lib/editor/tools'; import SpriteEditor from '$lib/editor/sprite-editor.svelte'; import type { SpriteData } from '$lib/editor/sprite-editor.svelte'; + import InventoryUI from '$lib/components/Inventory.svelte'; + import { Inventory, createItem } from '$lib/engine/inventory'; let canvasContainer: HTMLDivElement; let engine: GameEngine | null = $state(null); @@ -16,7 +18,8 @@ let currentFloor = $state(0); let totalFloors = $state(1); let showSpriteEditor = $state(false); - let createdItems: SpriteData[] = $state([]); + let inventory = $state(new Inventory()); + let itemCounter = $state(0); const tools: { id: ToolType; label: string; key: string }[] = [ { id: 'brush', label: 'Brush', key: 'B' }, @@ -217,6 +220,18 @@ {/if} + + {#if !showSpriteEditor} +
+ { + inventory.removeItem(slot); + }} + /> +
+ {/if} +
@@ -236,7 +251,12 @@ width={16} height={32} onSave={(data) => { - createdItems = [...createdItems, data]; + itemCounter++; + const item = createItem(`Item ${itemCounter}`, data); + const slot = inventory.addItem(item); + if (slot >= 0) { + inventory.selectSlot(slot); + } showSpriteEditor = false; }} onClose={() => (showSpriteEditor = false)}