// Elevate is the venue that reads SMALL and OUTDOORS. Both of those are carried // entirely by tile kinds — void for sky, yard for the open deck — so they are // exactly the properties a well-meaning tidy-up would flatten back into // Voltage without noticing. These pin them. import { describe, expect, it } from 'vitest'; import { ELEVATE } from '../../src/scenes/floor/layouts/elevate'; import { assertLayout } from './layoutInvariants'; import { MAP_H, MAP_W, isWalkable, tileAt, worldToTile, } from '../../src/scenes/floor/venueMap'; import type { TileKind } from '../../src/scenes/floor/venueMap'; const { map, props, lights } = ELEVATE; const countTiles = (kind: TileKind): number => map.tiles.reduce((n, t) => (t === kind ? n + 1 : n), 0); const NEIGHBOURS = [[1, 0], [-1, 0], [0, 1], [0, -1]] as const; describe('ELEVATE layout', () => { it('satisfies the shared venue invariants', () => { assertLayout(ELEVATE, { bar: 3, dance: 4, booth: 4, toilet: 2, smoke: 2, entry: 1, exit: 1, }); }); it('is a small room in a lot of sky', () => { // VENUES.md §1: venues differ by arrangement, never by grid extent, so // "small" is spelled with void. More than a third of the grid is sky. expect(countTiles('void')).toBeGreaterThan((MAP_W * MAP_H) / 3); }); it('is outdoors — the deck is yard, not floor', () => { // FloorView hangs its rain emitter off 'yard'. Repainting the terrace as // 'floor' would switch the weather off and cost the venue its character. expect(countTiles('yard')).toBeGreaterThan(3 * countTiles('floor')); // The only roofed pockets are the lift lobby and the dunnies. for (let ty = 0; ty < MAP_H; ty++) { for (let tx = 0; tx < MAP_W; tx++) { if (tileAt(map, tx, ty) !== 'floor') continue; const indoors = tx <= 14 || (tx >= 46 && tx <= 56); expect(indoors, `floor tile ${tx},${ty} is out in the rain`).toBe(true); } } }); it('has no smoking room — the whole roof is open air', () => { expect(countTiles('smokeDoor')).toBe(0); for (const a of map.anchors.smoke) { const t = worldToTile(a.x, a.y); expect(tileAt(map, t.tx, t.ty)).toBe('yard'); } // The designated corner is dressed with an urn and heat lamps, not a fence. expect(props.filter((p) => p.kind === 'ashUrn').length).toBeGreaterThanOrEqual(1); expect(props.filter((p) => p.kind === 'patioHeater').length).toBeGreaterThanOrEqual(2); }); it('puts the DJ on display: a plinth, not a sealed booth', () => { expect(countTiles('djbooth')).toBe(0); expect(countTiles('djfloor')).toBeGreaterThan(0); // Every plinth tile touches somewhere the crowd can stand — nothing is // walled off around the DJ, which is the difference from Voltage. for (let ty = 0; ty < MAP_H; ty++) { for (let tx = 0; tx < MAP_W; tx++) { if (tileAt(map, tx, ty) !== 'djfloor') continue; const open = NEIGHBOURS.some(([dx, dy]) => isWalkable(map, tx + dx, ty + dy)); expect(open, `plinth tile ${tx},${ty} is enclosed`).toBe(true); } } expect(props.some((p) => p.kind === 'djPlinth')).toBe(true); }); it('has two unisex cubicles and no urinals', () => { expect(map.stalls).toHaveLength(2); expect(props.some((p) => p.kind === 'urinal')).toBe(false); expect(props.some((p) => p.kind === 'marbleSink')).toBe(true); }); it('serves cocktails, not beer', () => { expect(props.some((p) => p.kind === 'taps')).toBe(false); expect(props.some((p) => p.kind === 'cocktailBar')).toBe(true); expect(props.filter((p) => p.kind === 'champBucket').length).toBeGreaterThanOrEqual(2); // The 'taps' post still has to exist — it is the bartender role's station, // here the serving side of the cocktail bar. expect(isWalkable(map, ELEVATE.posts.taps!.tx, ELEVATE.posts.taps!.ty)).toBe(true); }); it('you arrive by lift, not off the street', () => { for (const a of map.anchors.exit) { const t = worldToTile(a.x, a.y); expect(t.tx).toBe(0); expect(tileAt(map, t.tx, t.ty)).toBe('exit'); } expect(props.some((p) => p.kind === 'liftDoor')).toBe(true); // The lobby is the one place you are dry and being looked at. expect(props.some((p) => p.kind === 'stampPodium')).toBe(true); expect(props.filter((p) => p.kind === 'ropePost').length).toBeGreaterThanOrEqual(4); }); it('edges the terrace in glass with the city beyond it', () => { // South and east boundaries are balustrade, not parapet. for (let tx = 14; tx <= 58; tx++) expect(tileAt(map, tx, 37)).toBe('fence'); for (let ty = 7; ty <= 37; ty++) expect(tileAt(map, 58, ty)).toBe('fence'); expect(props.filter((p) => p.kind === 'balustrade').length).toBeGreaterThanOrEqual(4); // Skyline cards are scenery: they hang out in the void past the glass. const sky = props.filter((p) => p.kind === 'skylineCard'); expect(sky.length).toBeGreaterThanOrEqual(4); for (const card of sky) { expect(tileAt(map, card.tx, card.ty), `${card.id} is on the deck`).toBe('void'); } }); it('adds exactly one signature colour to the house set', () => { // SPACES.md §1: you navigate by glow. Elevate's own colour is skyline gold; // a second invention breaks the rule for every venue at once. const colours = new Set(lights.map((l) => l.colour)); expect(colours.has(0xd8a848)).toBe(true); expect(colours.size).toBe(6); expect(lights.some((l) => l.pulse === 'beat')).toBe(true); expect(lights.some((l) => l.pulse === 'flicker')).toBe(true); }); });