| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113 |
- import { resolve } from "node:path";
- import { describe, expect, it } from "vitest";
- import { createEngine, ContentRegistry, InventorySystem, World, WorldEventBus } from "../src/index.js";
- const repoRoot = resolve(process.cwd(), "../..");
- const contentDir = resolve(repoRoot, "content");
- const modsDir = resolve(repoRoot, "mods");
- function setup() {
- const world = new World();
- const registry = new ContentRegistry();
- const events = new WorldEventBus();
- world.create({ id: "player", type: "player", x: 0, y: 0 });
- const inv = new InventorySystem();
- inv.init({ world, registry, events });
- return { inv, world, events };
- }
- describe("InventorySystem", () => {
- it("init creates an inventory field on the player entity", () => {
- const { world } = setup();
- expect(world.get("player")?.inventory).toEqual({});
- });
- it("give/count/items roundtrip", () => {
- const { inv } = setup();
- expect(inv.give("hoe", 2)).toBe(2);
- expect(inv.give("hoe", 1)).toBe(3);
- expect(inv.count("hoe")).toBe(3);
- expect(inv.count("parsnip")).toBe(0);
- expect(inv.items()).toEqual({ hoe: 3 });
- });
- it("items() returns a snapshot copy, not a live reference", () => {
- const { inv } = setup();
- inv.give("hoe", 1);
- const snapshot = inv.items();
- inv.give("hoe", 1);
- expect(snapshot).toEqual({ hoe: 1 });
- expect(inv.items()).toEqual({ hoe: 2 });
- });
- it("give emits item-acquire with itemId, qty, and total", () => {
- const { inv, events } = setup();
- const seen: unknown[] = [];
- events.on("item-acquire", (p) => seen.push(p));
- inv.give("hoe", 2);
- expect(seen).toEqual([{ itemId: "hoe", qty: 2, total: 2 }]);
- });
- it("take decrements and emits item-use", () => {
- const { inv, events } = setup();
- const seen: unknown[] = [];
- events.on("item-use", (p) => seen.push(p));
- inv.give("hoe", 3);
- expect(inv.take("hoe", 1)).toBe(1);
- expect(seen).toEqual([{ itemId: "hoe", qty: 1, total: 2 }]);
- expect(inv.count("hoe")).toBe(2);
- });
- it("take clamps to the available quantity when insufficient", () => {
- const { inv, events } = setup();
- const seen: unknown[] = [];
- events.on("item-use", (p) => seen.push(p));
- inv.give("hoe", 3);
- expect(inv.take("hoe", 10)).toBe(3);
- expect(inv.count("hoe")).toBe(0);
- expect(inv.items()).toEqual({});
- expect(seen).toEqual([{ itemId: "hoe", qty: 3, total: 0 }]);
- });
- it("take of an absent item returns 0 and emits nothing", () => {
- const { inv, events } = setup();
- const seen: unknown[] = [];
- events.on("item-use", (p) => seen.push(p));
- expect(inv.take("hoe", 5)).toBe(0);
- expect(seen).toHaveLength(0);
- });
- it("give/take reject non-positive quantities", () => {
- const { inv } = setup();
- expect(() => inv.give("hoe", 0)).toThrow(/positive integer/);
- expect(() => inv.give("hoe", -1)).toThrow(/positive integer/);
- expect(() => inv.give("hoe", 1.5)).toThrow(/positive integer/);
- expect(() => inv.take("hoe", 0)).toThrow(/positive integer/);
- });
- it("wireEvents: inventory events fire onItemAcquire/onItemUse hooks with a HookContext", async () => {
- const engine = createEngine({ contentDir, modsDir });
- await engine.start();
- try {
- const acquired: unknown[] = [];
- const used: unknown[] = [];
- engine.hooks.on("onItemAcquire", (ctx) => {
- acquired.push({ world: ctx.world.get("player")?.id, time: ctx.time.get().day });
- });
- engine.hooks.on("onItemUse", (ctx) => {
- used.push({ world: ctx.world.get("player")?.id, time: ctx.time.get().day });
- });
- const inv = engine.systems.get<InventorySystem>("inventory");
- expect(inv).toBeDefined();
- inv!.give("hoe", 2);
- inv!.take("hoe", 1);
- expect(acquired).toEqual([{ world: "player", time: 1 }]);
- expect(used).toEqual([{ world: "player", time: 1 }]);
- expect(inv!.items()).toEqual({ hoe: 1 });
- } finally {
- await engine.stop();
- }
- });
- });
|