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