diff --git a/src/__tests__/materials.test.ts b/src/__tests__/materials.test.ts index 383b04e..503ed36 100644 --- a/src/__tests__/materials.test.ts +++ b/src/__tests__/materials.test.ts @@ -1,4 +1,5 @@ import { describe, expect, test } from "bun:test"; +import { MaterialRegistry } from "../materials/registry"; import { BEHAVIOR_GAS, BEHAVIOR_LIQUID, @@ -36,3 +37,38 @@ describe("material types", () => { expect(sand.density).toBe(1.5); }); }); + +describe("MaterialRegistry", () => { + test("has air at id 0", () => { + const reg = new MaterialRegistry(); + const air = reg.get(0); + expect(air.name).toBe("air"); + expect(air.density).toBe(0); + }); + + test("has sand registered", () => { + const reg = new MaterialRegistry(); + const sand = reg.getByName("sand"); + expect(sand).toBeDefined(); + expect(sand!.behavior).toBe(BEHAVIOR_POWDER); + }); + + test("all material ids are unique", () => { + const reg = new MaterialRegistry(); + const ids = reg.all().map((m) => m.id); + expect(new Set(ids).size).toBe(ids.length); + }); + + test("all material ids are in range 0-255", () => { + const reg = new MaterialRegistry(); + for (const m of reg.all()) { + expect(m.id).toBeGreaterThanOrEqual(0); + expect(m.id).toBeLessThanOrEqual(255); + } + }); + + test("get throws for unknown id", () => { + const reg = new MaterialRegistry(); + expect(() => reg.get(254)).toThrow(); + }); +}); diff --git a/src/materials/registry.ts b/src/materials/registry.ts new file mode 100644 index 0000000..4398cd2 --- /dev/null +++ b/src/materials/registry.ts @@ -0,0 +1,89 @@ +import { + BEHAVIOR_GAS, + BEHAVIOR_POWDER, + BEHAVIOR_SOLID, + type Material, +} from "./types"; + +const BUILTIN_MATERIALS: Material[] = [ + { + id: 0, + name: "air", + behavior: BEHAVIOR_GAS, + density: 0, + color: [0, 0, 0, 0], + hardness: 0, + angleOfRepose: 0, + }, + { + id: 1, + name: "sand", + behavior: BEHAVIOR_POWDER, + density: 1.5, + color: [0.76, 0.7, 0.5, 1.0], + hardness: 0.1, + angleOfRepose: 34, + }, + { + id: 2, + name: "dirt", + behavior: BEHAVIOR_POWDER, + density: 1.3, + color: [0.45, 0.32, 0.18, 1.0], + hardness: 0.2, + angleOfRepose: 40, + }, + { + id: 3, + name: "rock", + behavior: BEHAVIOR_SOLID, + density: 2.5, + color: [0.5, 0.5, 0.5, 1.0], + hardness: 1.0, + angleOfRepose: 90, + }, + { + id: 4, + name: "food", + behavior: BEHAVIOR_SOLID, + density: 1.0, + color: [0.2, 0.8, 0.2, 1.0], + hardness: 0.0, + angleOfRepose: 0, + }, + { + id: 5, + name: "home", + behavior: BEHAVIOR_SOLID, + density: 0, + color: [0.3, 0.3, 1.0, 1.0], + hardness: 0.0, + angleOfRepose: 0, + }, +]; + +export class MaterialRegistry { + private byId: Map = new Map(); + private byName: Map = new Map(); + + constructor() { + for (const m of BUILTIN_MATERIALS) { + this.byId.set(m.id, m); + this.byName.set(m.name, m); + } + } + + get(id: number): Material { + const m = this.byId.get(id); + if (!m) throw new Error(`Unknown material id: ${id}`); + return m; + } + + getByName(name: string): Material | undefined { + return this.byName.get(name); + } + + all(): Material[] { + return [...this.byId.values()]; + } +}