import { resolve } from "node:path"; import { describe, expect, it } from "vitest"; import { createEngine, type AnimalEntity, type BuildingPlacement, type GrowthSystem, type InventorySystem, type PlacementSystem, type PlotEntity, type PlantingSystem, } from "../src/index.js"; const repoRoot = resolve(process.cwd(), "../.."); const contentDir = resolve(repoRoot, "examples", "sample-game", "content"); function engineSetup() { return createEngine({ contentDir }); } function plantingOf(engine: ReturnType): PlantingSystem { const planting = engine.systems.get("planting"); if (!planting) throw new Error("planting system not booted"); return planting; } function growthOf(engine: ReturnType): GrowthSystem { const growth = engine.systems.get("growth"); if (!growth) throw new Error("growth system not booted"); return growth; } function placementOf(engine: ReturnType): PlacementSystem { const placement = engine.systems.get("placement"); if (!placement) throw new Error("placement system not booted"); return placement; } function inventoryOf(engine: ReturnType): InventorySystem { const inv = engine.systems.get("inventory"); if (!inv) throw new Error("inventory system not booted"); return inv; } function sowParsnip(engine: ReturnType): PlotEntity { inventoryOf(engine).give("parsnip-seed", 3); const planting = plantingOf(engine); planting.till(3, 3); return planting.sow(3, 3, "parsnip-seed"); } describe("entity appearance (spriteRef) maintenance", () => { it("engine seeds the player entity with spriteRef 'player'", async () => { const engine = engineSetup(); await engine.start(); try { const player = engine.world.get("player") as { spriteRef?: string } | undefined; expect(player?.spriteRef).toBe("player"); } finally { await engine.stop(); } }); it("PlantingSystem.sow sets plot.spriteRef to the crop's stage-0 spriteRef", async () => { const engine = engineSetup(); await engine.start(); try { const plot = sowParsnip(engine); expect(plot.spriteRef).toBe("parsnip-sprout"); } finally { await engine.stop(); } }); it("GrowthSystem.forceStage updates plot.spriteRef to the forced stage's spriteRef", async () => { const engine = engineSetup(); await engine.start(); try { const plot = sowParsnip(engine); const grown = growthOf(engine).forceStage("plot:3:3", 2); expect(grown.spriteRef).toBe("parsnip-mature"); } finally { await engine.stop(); } }); it("GrowthSystem day-boundary stage advance updates plot.spriteRef", async () => { const engine = engineSetup(); await engine.start(); try { const plot = sowParsnip(engine); expect(plot.spriteRef).toBe("parsnip-sprout"); const growth = growthOf(engine); const planting = plantingOf(engine); planting.water(3, 3); growth.step(480); planting.water(3, 3); growth.step(480); const advanced = engine.world.get("plot:3:3") as PlotEntity; expect(advanced.stageIndex).toBe(1); expect(advanced.spriteRef).toBe("parsnip-grown"); } finally { await engine.stop(); } }); it("PlantingSystem.harvest clears plot.spriteRef with the crop fields", async () => { const engine = engineSetup(); await engine.start(); try { const plot = sowParsnip(engine); growthOf(engine).forceStage("plot:3:3", 2); plantingOf(engine).harvest(3, 3); const cleared = engine.world.get("plot:3:3") as PlotEntity; expect(cleared.cropItemId).toBeUndefined(); expect(cleared.spriteRef).toBeUndefined(); } finally { await engine.stop(); } }); it("GrowthSystem.spawnAnimal sets animal.spriteRef to the animal's stage-0 spriteRef", async () => { const engine = engineSetup(); await engine.start(); try { const animal = growthOf(engine).spawnAnimal("chicken", 9, 3); expect(animal.spriteRef).toBe("chicken-chick"); } finally { await engine.stop(); } }); it("GrowthSystem.forceAnimalStage updates animal.spriteRef", async () => { const engine = engineSetup(); await engine.start(); try { const animal = growthOf(engine).spawnAnimal("chicken", 9, 3); const laying = growthOf(engine).forceAnimalStage(animal.id, 2); expect(laying.spriteRef).toBe("chicken-laying"); expect((engine.world.get(animal.id) as AnimalEntity).spriteRef).toBe("chicken-laying"); } finally { await engine.stop(); } }); it("PlacementSystem.place copies the building def's spriteRef onto the entity", async () => { const engine = engineSetup(); await engine.start(); try { const coop = placementOf(engine).place("coop", 10, 4) as BuildingPlacement; expect(coop.spriteRef).toBe("coop"); const fence = placementOf(engine).place("fence", 8, 8) as BuildingPlacement; expect(fence.spriteRef).toBe("fence"); } finally { await engine.stop(); } }); it("spriteRef stays absent when the def has no spriteRef (fallback shape drawing)", async () => { const engine = engineSetup(); await engine.start(); try { engine.registry.register("item", { id: "plain-seed", name: "Plain Seed", kind: "seed", stackable: true }); engine.registry.register("crop", { id: "plain", seedItem: "plain-seed", stages: [ { name: "sprout", days: 1, waterRequired: false }, { name: "grown", days: 1, waterRequired: false }, ], seasons: ["spring"], drops: [{ itemId: "parsnip", qty: 1 }], }); inventoryOf(engine).give("plain-seed", 1); const planting = plantingOf(engine); planting.till(6, 6); const plot = planting.sow(6, 6, "plain-seed"); expect(plot.cropItemId).toBe("plain"); expect(plot.spriteRef).toBeUndefined(); } finally { await engine.stop(); } }); });