import { describe, it, expect } from 'vitest' import { ADDITIVE_OBJECT_TOKENS, type ObjectDrawable, } from '../src/scene/act-scene.ts' import { buildObjectDrawables, executeDepthDrawDispatch, MockRenderer, type PackObjectInput, type SimulatedWall, } from '../scripts/verify-scene-object-dispatch.ts' describe('Scene Object Rendering Dispatch (Milestone 1 / R1)', () => { it('correctly maps all 1.13c additive tokens to blendMode: "additive"', () => { const tokens = ['11', '12', 'R1', 'R2', 'R3', 'R4', 'R5', '1R', '2R', '3R', '4R', 'HA'] for (const token of tokens) { expect(ADDITIVE_OBJECT_TOKENS.has(token)).toBe(true) const drawables = buildObjectDrawables([ { x: 100, y: 200, token, frame: { x: 0, y: 0, width: 32, height: 32, offsetX: 0, offsetY: 0, page: 0 }, }, ]) expect(drawables).toHaveLength(1) expect(drawables[0]!.blendMode).toBe('additive') } }) it('correctly identifies trans=7 and trans=1 objects as additive', () => { const drawables = buildObjectDrawables([ { x: 10, y: 20, token: 'UNKNOWN', trans: 7, frame: { x: 0, y: 0, width: 32, height: 32, offsetX: 0, offsetY: 0, page: 0 }, }, { x: 30, y: 40, token: 'OTHER', trans: 1, frame: { x: 0, y: 0, width: 32, height: 32, offsetX: 0, offsetY: 0, page: 0 }, }, ]) expect(drawables).toHaveLength(2) expect(drawables[0]!.blendMode).toBe('additive') expect(drawables[1]!.blendMode).toBe('additive') }) it('preserves blendMode: "normal" for regular objects and torches', () => { const drawables = buildObjectDrawables([ { x: 10, y: 20, token: 'CHEST', trans: 0, frame: { x: 0, y: 0, width: 32, height: 32, offsetX: 0, offsetY: 0, page: 0 }, }, { x: 30, y: 40, token: 'TO', trans: 0, frame: { x: 0, y: 0, width: 32, height: 32, offsetX: 0, offsetY: 0, page: 0 }, }, ]) expect(drawables).toHaveLength(2) expect(drawables[0]!.blendMode).toBe('normal') expect(drawables[1]!.blendMode).toBe('normal') }) it('dispatches additive objects with blendMode: "additive" and regular objects with "normal"', () => { const rawObjects: PackObjectInput[] = [ { id: 1, token: '11', depth: 2, frame: { x: 0, y: 0, width: 10, height: 10, offsetX: 0, offsetY: 0, page: 0 }, x: 10, y: 0 }, { id: 2, token: 'BOX', depth: 6, frame: { x: 0, y: 0, width: 10, height: 10, offsetX: 0, offsetY: 0, page: 0 }, x: 30, y: 0 }, ] const objs = buildObjectDrawables(rawObjects) const walls: SimulatedWall[] = [ { frame: { x: 0, y: 0, width: 32, height: 32, _depth: 4 } as any, x: 0, y: 0, cellX: 2, cellY: 2, page: 0 }, ] const renderer = new MockRenderer() executeDepthDrawDispatch(renderer, { walls, objs, maxDepthSteps: [Infinity], }) expect(renderer.draws).toHaveLength(3) // Draw 0: Object 1 (Token 11, depth 2) -> additive expect(renderer.draws[0]!.target).toBe('object') expect(renderer.draws[0]!.depth).toBe(2) expect(renderer.draws[0]!.blendMode).toBe('additive') // Draw 1: Wall (depth 4) -> blendMode undefined (options has { atlas }) expect(renderer.draws[1]!.target).toBe('wall') expect(renderer.draws[1]!.depth).toBe(4) expect(renderer.draws[1]!.blendMode).toBeUndefined() // Draw 2: Object 2 (BOX, depth 6) -> normal expect(renderer.draws[2]!.target).toBe('object') expect(renderer.draws[2]!.depth).toBe(6) expect(renderer.draws[2]!.blendMode).toBe('normal') }) it('strictly preserves depth ordering across mixed walls, objects, and incremental depth steps', () => { const rawObjects: PackObjectInput[] = [ { id: 1, depth: 3, token: '11', frame: { x: 0, y: 0, width: 10, height: 10, offsetX: 0, offsetY: 0, page: 0 }, x: 15, y: 0 }, { id: 2, depth: 8, token: '12', frame: { x: 0, y: 0, width: 10, height: 10, offsetX: 0, offsetY: 0, page: 0 }, x: 40, y: 0 }, { id: 3, depth: 15, token: 'CHEST', frame: { x: 0, y: 0, width: 10, height: 10, offsetX: 0, offsetY: 0, page: 0 }, x: 75, y: 0 }, ] const objs = buildObjectDrawables(rawObjects) const walls: SimulatedWall[] = [ { frame: { x: 0, y: 0, width: 32, height: 32, _depth: 1 } as any, x: 0, y: 0, cellX: 0, cellY: 1, page: 0 }, { frame: { x: 0, y: 0, width: 32, height: 32, _depth: 5 } as any, x: 0, y: 0, cellX: 2, cellY: 3, page: 0 }, { frame: { x: 0, y: 0, width: 32, height: 32, _depth: 10 } as any, x: 0, y: 0, cellX: 5, cellY: 5, page: 0 }, ] const renderer = new MockRenderer() executeDepthDrawDispatch(renderer, { walls, objs, maxDepthSteps: [4, 9, Infinity], }) expect(renderer.draws).toHaveLength(6) // Verify non-decreasing order for (let i = 1; i < renderer.draws.length; i++) { expect(renderer.draws[i]!.depth).toBeGreaterThanOrEqual(renderer.draws[i - 1]!.depth) } const depths = renderer.draws.map(d => d.depth) expect(depths).toEqual([1, 3, 5, 8, 10, 15]) }) })