import { describe, expect, test } from 'vitest' import { generateWilderness, getPerimeterOpenings, UNIMPLEMENTED_PASSES, } from '../src/game/wilderness.ts' import type { WildernessPiece } from '../src/game/wilderness.ts' import type { Act2TopographyStats, Act3TopographyStats, Act4TopographyStats, Act5TopographyStats, } from '../src/game/wilderness-acts.ts' import type { Ds1, Ds1Cell } from '../src/formats/ds1.ts' function makeMockDs1( width: number, height: number, hasGapInWalls: boolean = false, floorStyle: number = 1, wallStyle: number = 1, ): Ds1 { const cells: Ds1Cell[][] = [] for (let y = 0; y < height; y += 1) { const row: Ds1Cell[] = [] for (let x = 0; x < width; x += 1) { const isGap = hasGapInWalls && x === Math.floor(width / 2) row.push({ walls: isGap ? [] : [{ prop1: 2, sequence: 0, style: wallStyle, type: 0, unknown1: 0, unknown2: 0, hidden: false }], floors: [{ prop1: 2, sequence: 0, style: floorStyle, unknown1: 0, unknown2: 0, hidden: false }], shadows: [], substitutions: [], }) } cells.push(row) } return { version: 18, width, height, act: 1, substitutionType: 0, wallLayers: 1, floorLayers: 1, cells, objects: [], npcPathOffset: null, } } function makeMockBorderPieces(actPrefix: string): WildernessPiece[] { const borderDs1 = makeMockDs1(8, 8, false) const openBorderDs1 = makeMockDs1(8, 8, true) return [ { name: `${actPrefix} - Border 1`, border: true, levels: [borderDs1, borderDs1, borderDs1, openBorderDs1] }, { name: `${actPrefix} - Border 2`, border: true, levels: [borderDs1] }, { name: `${actPrefix} - Border 3`, border: true, levels: [borderDs1] }, { name: `${actPrefix} - Border 4`, border: true, levels: [borderDs1, borderDs1, borderDs1, openBorderDs1] }, { name: `${actPrefix} - Border 5`, border: true, levels: [borderDs1] }, { name: `${actPrefix} - Border 6`, border: true, levels: [borderDs1] }, { name: `${actPrefix} - Border 7`, border: true, levels: [borderDs1] }, { name: `${actPrefix} - Border 8`, border: true, levels: [borderDs1] }, ] } describe('Wilderness Outdoor Initializers for Acts 2-5 (Issue #51)', () => { test('UNIMPLEMENTED_PASSES has 5 outdoor passes removed', () => { expect(UNIMPLEMENTED_PASSES).not.toContain('DRLGOUTDESR_InitAct2OutdoorLevel') expect(UNIMPLEMENTED_PASSES).not.toContain('DRLGOUTPLACE_InitAct3OutdoorLevel') expect(UNIMPLEMENTED_PASSES).not.toContain('DRLGOUTDOORS_InitAct4OutdoorLevel') expect(UNIMPLEMENTED_PASSES).not.toContain('DRLGOUTSIEGE_InitAct5OutdoorLevel') expect(UNIMPLEMENTED_PASSES).not.toContain('DRLG_GenerateJungles') expect(UNIMPLEMENTED_PASSES).toEqual([ 'DRLGVER_CreateVertices', 'DRLGOUTPLACE_CreateLevelConnections', 'DRLGOUTDOORS_SpawnAct3Mephisto', ]) }) describe('Act 2: DRLGOUTDESR_InitAct2OutdoorLevel', () => { test('Rocky Waste (41) generates sand caravan trails and canyon ridge walls', () => { const pieces = makeMockBorderPieces('Act 2') const result = generateWilderness({ levelId: 41, levelName: 'Rocky Waste', levelTypeName: 'Act 2 - Desert', sizeX: 80, sizeY: 80, subType: 0, subTheme: 0, seed: 41001, pieces, substitutions: [], }) expect(result.stats.act2Topography).toBeDefined() const topo = result.stats.act2Topography as Act2TopographyStats expect(topo.sandTrailCells).toBeGreaterThan(50) expect(topo.canyonWalls).toBeGreaterThan(20) let foundSandTrail = false let foundCanyonWall = false for (let y = 0; y < result.level.height; y += 1) { for (let x = 0; x < result.level.width; x += 1) { const cell = result.level.cells[y]![x]! if (cell.floors.some(f => f.style === 5 && f.prop1 === 194)) foundSandTrail = true if (cell.walls.some(w => w.style === 1 && w.prop1 === 129)) foundCanyonWall = true } } expect(foundSandTrail).toBe(true) expect(foundCanyonWall).toBe(true) }) test('Far Oasis (43) generates lush water oasis pools', () => { const pieces = makeMockBorderPieces('Act 2') const result = generateWilderness({ levelId: 43, levelName: 'Far Oasis', levelTypeName: 'Act 2 - Desert', sizeX: 80, sizeY: 80, subType: 0, subTheme: 0, seed: 43001, pieces, substitutions: [], }) expect(result.stats.act2Topography).toBeDefined() const topo = result.stats.act2Topography as Act2TopographyStats expect(topo.oasisPoolCells).toBeGreaterThan(15) let foundOasisWater = false for (let y = 0; y < result.level.height; y += 1) { for (let x = 0; x < result.level.width; x += 1) { const cell = result.level.cells[y]![x]! if (cell.floors.some(f => f.style === 2 && f.prop1 === 2)) foundOasisWater = true } } expect(foundOasisWater).toBe(true) }) test('Valley of Snakes (45) creates narrow canyon gorge', () => { const pieces = makeMockBorderPieces('Act 2') const result = generateWilderness({ levelId: 45, levelName: 'Valley of Snakes', levelTypeName: 'Act 2 - Desert', sizeX: 80, sizeY: 80, subType: 0, subTheme: 0, seed: 45001, pieces, substitutions: [], }) expect(result.stats.act2Topography).toBeDefined() const topo = result.stats.act2Topography as Act2TopographyStats expect(topo.canyonWalls).toBeGreaterThan(30) }) test('Canyon of the Magi (46) places 7 Tal Rasha tombs, central dais, and has 0 perimeter openings', () => { const pieces = makeMockBorderPieces('Act 2') expect(getPerimeterOpenings(46, 10, 10).size).toBe(0) const result = generateWilderness({ levelId: 46, levelName: 'Canyon of the Magi', levelTypeName: 'Act 2 - Desert', sizeX: 80, sizeY: 80, subType: 0, subTheme: 0, seed: 46001, pieces, substitutions: [], }) expect(result.stats.act2Topography).toBeDefined() const topo = result.stats.act2Topography as Act2TopographyStats expect(topo.tombEntrances).toBe(7) // Verify central Arcane Sanctuary warp platform (portal/dais) let foundCentralDais = false for (let y = 35; y <= 45; y += 1) { for (let x = 35; x <= 45; x += 1) { const cell = result.level.cells[y]![x]! if (cell.floors.some(f => f.style === 3)) foundCentralDais = true } } expect(foundCentralDais).toBe(true) }) }) describe('Act 3: DRLGOUTPLACE_InitAct3OutdoorLevel & DRLG_GenerateJungles', () => { test('Jungles (76 Spider Forest) generate river channel, wooden river bridges, and jungle paths', () => { const tailDs1 = makeMockDs1(64, 32) const headDs1 = makeMockDs1(64, 32) const clearingDs1 = makeMockDs1(32, 32) const riverDs1 = makeMockDs1(32, 32) const pieces: WildernessPiece[] = [ { name: 'Act 3 - Jungle Tail', border: false, levels: [tailDs1] }, { name: 'Act 3 - Jungle Head', border: false, levels: [headDs1] }, { name: 'Act 3 - Clearing Webby E', border: false, levels: [clearingDs1] }, { name: 'Act 3 - Jungle NS W', border: false, levels: [riverDs1] }, ] const result = generateWilderness({ levelId: 76, levelName: 'Spider Forest', levelTypeName: 'Act 3 - Jungle', sizeX: 64, sizeY: 192, subType: 0, subTheme: 0, seed: 76001, pieces, substitutions: [], }) expect(result.stats.act3Topography).toBeDefined() const topo = result.stats.act3Topography as Act3TopographyStats expect(topo.riverBridges).toBeGreaterThanOrEqual(2) expect(topo.junglePathCells).toBeGreaterThan(20) // Verify wooden river bridge planks (style 4, prop1 2) spanning across East channel let foundBridgePlank = false for (let y = 70; y <= 90; y += 1) { for (let x = 32; x < 60; x += 1) { const cell = result.level.cells[y]![x]! if (cell.floors.some(f => f.style === 4 && f.prop1 === 2)) foundBridgePlank = true } } expect(foundBridgePlank).toBe(true) }) test('Kurast Causeway (82) stamps causeway with central walkway, balustrades, and canal water', () => { const groundDs1 = makeMockDs1(48, 16) const pieces: WildernessPiece[] = [ { name: 'Act 3 - Ground', border: false, levels: [groundDs1] }, ] const result = generateWilderness({ levelId: 82, levelName: 'Kurast Causeway', levelTypeName: 'Act 3 - Kurast', sizeX: 48, sizeY: 16, subType: 0, subTheme: 0, seed: 82001, pieces, substitutions: [], }) expect(result.stats.act3Topography).toBeDefined() const topo = result.stats.act3Topography as Act3TopographyStats expect(topo.causewayCells).toBeGreaterThan(50) expect(topo.canalCells).toBeGreaterThan(100) // Verify central walkway (style 5), stone balustrade walls (style 1), and canal water (style 2) let foundWalkway = false let foundBalustrade = false let foundCanalWater = false for (let y = 0; y < result.level.height; y += 1) { for (let x = 0; x < result.level.width; x += 1) { const cell = result.level.cells[y]![x]! if (cell.floors.some(f => f.style === 5 && f.prop1 === 194)) foundWalkway = true if (cell.walls.some(w => w.style === 1 && w.prop1 === 129)) foundBalustrade = true if (cell.floors.some(f => f.style === 2 && f.prop1 === 2)) foundCanalWater = true } } expect(foundWalkway).toBe(true) expect(foundBalustrade).toBe(true) expect(foundCanalWater).toBe(true) }) test('Kurast Causeway (82) preserves preset when Act 3 - Bridge is provided', () => { const bridgeDs1 = makeMockDs1(48, 16, false, 29, 31) const pieces: WildernessPiece[] = [ { name: 'Act 3 - Bridge', border: false, levels: [bridgeDs1] }, ] const result = generateWilderness({ levelId: 82, levelName: 'Kurast Causeway', levelTypeName: 'Act 3 - Kurast', sizeX: 48, sizeY: 16, subType: 0, subTheme: 0, seed: 82001, pieces, substitutions: [], }) let foundPresetFloor = false let foundPresetWall = false for (let y = 0; y < result.level.height; y += 1) { for (let x = 0; x < result.level.width; x += 1) { const cell = result.level.cells[y]![x]! if (cell.floors.some(f => f.style === 29)) foundPresetFloor = true if (cell.walls.some(w => w.style === 31)) foundPresetWall = true } } expect(foundPresetFloor).toBe(true) expect(foundPresetWall).toBe(true) }) test('Kurast Bazaar (79) generates paved stone avenues, canals, and stone bridges', () => { const pieces = makeMockBorderPieces('Act 3') const result = generateWilderness({ levelId: 79, levelName: 'Kurast Bazaar', levelTypeName: 'Act 3 - Kurast', sizeX: 80, sizeY: 80, subType: 0, subTheme: 0, seed: 79001, pieces, substitutions: [], }) expect(result.stats.act3Topography).toBeDefined() const topo = result.stats.act3Topography as Act3TopographyStats expect(topo.causewayCells).toBeGreaterThan(50) expect(topo.canalCells).toBeGreaterThan(30) expect(topo.riverBridges).toBeGreaterThanOrEqual(1) }) }) describe('Act 4: DRLGOUTDOORS_InitAct4OutdoorLevel', () => { test('Outer Steppes (104) generates lava chasms, basalt lips, and rocky bridge causeways', () => { const pieces = makeMockBorderPieces('Act 4') const result = generateWilderness({ levelId: 104, levelName: 'Outer Steppes', levelTypeName: 'Act 4 - Mesa', sizeX: 80, sizeY: 80, subType: 0, subTheme: 0, seed: 104001, pieces, substitutions: [], }) expect(result.stats.act4Topography).toBeDefined() const topo = result.stats.act4Topography as Act4TopographyStats expect(topo.lavaChasmCells).toBeGreaterThan(100) expect(topo.rockyBridges).toBeGreaterThan(5) // Verify boiling molten lava (style 20, sequence 3, prop1 2) and rocky bridges (style 10, sequence 0, prop1 2) let foundLava = false let foundRockyBridge = false for (let y = 0; y < result.level.height; y += 1) { for (let x = 0; x < result.level.width; x += 1) { const cell = result.level.cells[y]![x]! if (cell.floors.some(f => f.style === 20 && f.sequence === 3 && f.prop1 === 2)) foundLava = true if (cell.floors.some(f => f.style === 10 && f.sequence === 0 && f.prop1 === 2)) foundRockyBridge = true } } expect(foundLava).toBe(true) expect(foundRockyBridge).toBe(true) }) test('Plains of Despair (105) creates Izual prison plateau', () => { const pieces = makeMockBorderPieces('Act 4') const result = generateWilderness({ levelId: 105, levelName: 'Plains of Despair', levelTypeName: 'Act 4 - Mesa', sizeX: 80, sizeY: 80, subType: 0, subTheme: 0, seed: 105001, pieces, substitutions: [], }) expect(result.stats.act4Topography).toBeDefined() const topo = result.stats.act4Topography as Act4TopographyStats expect(topo.plateauCells).toBeGreaterThan(20) }) }) describe('Act 5: DRLGOUTSIEGE_InitAct5OutdoorLevel', () => { test('Frigid Highlands (111) generates transverse siege barricades, choke-point gates, and trenches', () => { const pieces = makeMockBorderPieces('Act 5') const result = generateWilderness({ levelId: 111, levelName: 'Frigid Highlands', levelTypeName: 'Act 5 - Barricade', sizeX: 80, sizeY: 80, subType: 0, subTheme: 0, seed: 111001, pieces, substitutions: [], }) expect(result.stats.act5Topography).toBeDefined() const topo = result.stats.act5Topography as Act5TopographyStats expect(topo.barricadeWalls).toBeGreaterThan(40) expect(topo.trenchCells).toBeGreaterThan(30) // Verify spiked palisade walls (style 2, prop1 129) and trench floors (style 5, prop1 194) let foundBarricadeWall = false let foundTrench = false for (let y = 0; y < result.level.height; y += 1) { for (let x = 0; x < result.level.width; x += 1) { const cell = result.level.cells[y]![x]! if (cell.walls.some(w => w.style === 2 && w.prop1 === 129)) foundBarricadeWall = true if (cell.floors.some(f => f.style === 5 && f.prop1 === 194)) foundTrench = true } } expect(foundBarricadeWall).toBe(true) expect(foundTrench).toBe(true) }) test('Arreat Plateau (112) generates snow crater frozen lakes with ice floors', () => { const pieces = makeMockBorderPieces('Act 5') const result = generateWilderness({ levelId: 112, levelName: 'Arreat Plateau', levelTypeName: 'Act 5 - Barricade', sizeX: 80, sizeY: 80, subType: 0, subTheme: 0, seed: 112001, pieces, substitutions: [], }) expect(result.stats.act5Topography).toBeDefined() const topo = result.stats.act5Topography as Act5TopographyStats expect(topo.frozenLakeCells).toBeGreaterThan(20) let foundIceFloor = false for (let y = 0; y < result.level.height; y += 1) { for (let x = 0; x < result.level.width; x += 1) { const cell = result.level.cells[y]![x]! if (cell.floors.some(f => f.style === 2 && f.sequence === 0 && f.prop1 === 2)) foundIceFloor = true } } expect(foundIceFloor).toBe(true) }) }) })