fix(wilderness): prevent destructive wiping of walls and presets in roads and bridges
1. Protect placed presets and preset walls from being wiped in stampThemedRoads: check placedRects in paintRoadTile, ensuring preset walls are never cleared. 2. Protect preset floors and walls in stampAct3KurastCauseway so artist DS1 tiles are not overwritten by procedural causeway passes; wire specialPlacedRects. 3. Protect placed preset walls and floors in synthesizeBridgePlanks in Act 3 jungles, recording placedRects for all jungle pieces. 4. Add unit tests for Causeway preset preservation and road preset wall preservation. TAG=agy CONV=2a1934de-30ef-464e-b3f3-fcbcdbbc49f1
This commit is contained in:
parent
37dc936169
commit
fa307bff46
|
|
@ -418,6 +418,7 @@ export function stampAct3KurastCauseway(
|
||||||
canvas: Canvas,
|
canvas: Canvas,
|
||||||
pieces: readonly WildernessPiece[],
|
pieces: readonly WildernessPiece[],
|
||||||
stats: WildernessStats,
|
stats: WildernessStats,
|
||||||
|
specialPlacedRects?: PlacedPresetRect[],
|
||||||
): Set<string> {
|
): Set<string> {
|
||||||
const placed = new Set<string>()
|
const placed = new Set<string>()
|
||||||
const piece = pieces.find(p => p.name === 'Act 3 - Bridge')
|
const piece = pieces.find(p => p.name === 'Act 3 - Bridge')
|
||||||
|
|
@ -427,6 +428,15 @@ export function stampAct3KurastCauseway(
|
||||||
stampDs1(canvas, ds1, 0, 0)
|
stampDs1(canvas, ds1, 0, 0)
|
||||||
placed.add('Act 3 - Bridge')
|
placed.add('Act 3 - Bridge')
|
||||||
stats.substitutions.push({ name: 'Act 3 - Bridge', role: 'object', enabled: true, clusters: 1 })
|
stats.substitutions.push({ name: 'Act 3 - Bridge', role: 'object', enabled: true, clusters: 1 })
|
||||||
|
if (specialPlacedRects) {
|
||||||
|
specialPlacedRects.push({
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
w: ds1.width,
|
||||||
|
h: ds1.height,
|
||||||
|
name: 'Act 3 - Bridge',
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Construct / ensure canonical causeway layout:
|
// Construct / ensure canonical causeway layout:
|
||||||
|
|
@ -438,6 +448,15 @@ export function stampAct3KurastCauseway(
|
||||||
|
|
||||||
for (let y = 0; y < canvas.height; y += 1) {
|
for (let y = 0; y < canvas.height; y += 1) {
|
||||||
for (let x = 0; x < canvas.width; x += 1) {
|
for (let x = 0; x < canvas.width; x += 1) {
|
||||||
|
if (ds1 && x >= 0 && x < ds1.width && y >= 0 && y < ds1.height) {
|
||||||
|
const ds1Cell = ds1.cells[y]?.[x]
|
||||||
|
const hasFloor = ds1Cell?.floors && ds1Cell.floors.length > 0 && !ds1Cell.floors[0]?.hidden
|
||||||
|
const hasWall = ds1Cell?.walls && ds1Cell.walls.length > 0 && !ds1Cell.walls[0]?.hidden
|
||||||
|
if (hasFloor || hasWall) {
|
||||||
|
// Keep artist-placed preset tiles from ds1; do not destructively wipe
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
if (x >= walkwayLeft && x <= walkwayRight) {
|
if (x >= walkwayLeft && x <= walkwayRight) {
|
||||||
// Paved stone causeway: style 5, prop1 194
|
// Paved stone causeway: style 5, prop1 194
|
||||||
paintFloor(canvas, x, y, makeFloor(5, 0, 194), true)
|
paintFloor(canvas, x, y, makeFloor(5, 0, 194), true)
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@
|
||||||
|
|
||||||
import type { Ds1, Ds1Cell, Ds1Floor, Ds1Wall } from '../formats/ds1.ts'
|
import type { Ds1, Ds1Cell, Ds1Floor, Ds1Wall } from '../formats/ds1.ts'
|
||||||
import { Rng } from './rng.ts'
|
import { Rng } from './rng.ts'
|
||||||
import type { Canvas, SubstitutionReport, WildernessPiece, WildernessStats } from './wilderness.ts'
|
import type { Canvas, PlacedPresetRect, SubstitutionReport, WildernessPiece, WildernessStats } from './wilderness.ts'
|
||||||
|
|
||||||
/** Connection between two Act 3 zones. */
|
/** Connection between two Act 3 zones. */
|
||||||
export interface JungleZoneConnection {
|
export interface JungleZoneConnection {
|
||||||
|
|
@ -242,6 +242,7 @@ function synthesizeBridgePlanks(
|
||||||
bridgeX: number,
|
bridgeX: number,
|
||||||
bridgeY: number,
|
bridgeY: number,
|
||||||
orientation: 'horizontal' | 'vertical',
|
orientation: 'horizontal' | 'vertical',
|
||||||
|
bridgeDs1?: Ds1,
|
||||||
): void {
|
): void {
|
||||||
const makeBridgeFloor = (): Ds1Floor => ({
|
const makeBridgeFloor = (): Ds1Floor => ({
|
||||||
prop1: 2,
|
prop1: 2,
|
||||||
|
|
@ -261,6 +262,14 @@ function synthesizeBridgePlanks(
|
||||||
if (cy >= 0 && cy < canvas.height && cx >= 0 && cx < canvas.width) {
|
if (cy >= 0 && cy < canvas.height && cx >= 0 && cx < canvas.width) {
|
||||||
const row = canvas.cells[cy]
|
const row = canvas.cells[cy]
|
||||||
if (row?.[cx]) {
|
if (row?.[cx]) {
|
||||||
|
if (bridgeDs1 && dx < bridgeDs1.width && dy < bridgeDs1.height) {
|
||||||
|
const ds1Cell = bridgeDs1.cells[dy]?.[dx]
|
||||||
|
const hasPresetWall = ds1Cell?.walls && ds1Cell.walls.length > 0 && !ds1Cell.walls[0]?.hidden
|
||||||
|
const hasPresetFloor = ds1Cell?.floors && ds1Cell.floors.length > 0 && !ds1Cell.floors[0]?.hidden && ds1Cell.floors[0]?.style !== 1
|
||||||
|
if (hasPresetWall || hasPresetFloor) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
;(row[cx]!.floors as Ds1Floor[])[0] = makeBridgeFloor()
|
;(row[cx]!.floors as Ds1Floor[])[0] = makeBridgeFloor()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -274,6 +283,14 @@ function synthesizeBridgePlanks(
|
||||||
if (cy >= 0 && cy < canvas.height && cx >= 0 && cx < canvas.width) {
|
if (cy >= 0 && cy < canvas.height && cx >= 0 && cx < canvas.width) {
|
||||||
const row = canvas.cells[cy]
|
const row = canvas.cells[cy]
|
||||||
if (row?.[cx]) {
|
if (row?.[cx]) {
|
||||||
|
if (bridgeDs1 && dx < bridgeDs1.width && dy < bridgeDs1.height) {
|
||||||
|
const ds1Cell = bridgeDs1.cells[dy]?.[dx]
|
||||||
|
const hasPresetWall = ds1Cell?.walls && ds1Cell.walls.length > 0 && !ds1Cell.walls[0]?.hidden
|
||||||
|
const hasPresetFloor = ds1Cell?.floors && ds1Cell.floors.length > 0 && !ds1Cell.floors[0]?.hidden && ds1Cell.floors[0]?.style !== 1
|
||||||
|
if (hasPresetWall || hasPresetFloor) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
;(row[cx]!.floors as Ds1Floor[])[0] = makeBridgeFloor()
|
;(row[cx]!.floors as Ds1Floor[])[0] = makeBridgeFloor()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -343,6 +360,7 @@ export function DRLG_GenerateJungles(
|
||||||
rng: Rng,
|
rng: Rng,
|
||||||
stats: WildernessStats,
|
stats: WildernessStats,
|
||||||
stampDs1Fn: (canvas: Canvas, source: Ds1, originX: number, originY: number) => number,
|
stampDs1Fn: (canvas: Canvas, source: Ds1, originX: number, originY: number) => number,
|
||||||
|
placedRects?: PlacedPresetRect[],
|
||||||
): Set<string> {
|
): Set<string> {
|
||||||
const placed = new Set<string>()
|
const placed = new Set<string>()
|
||||||
|
|
||||||
|
|
@ -523,8 +541,9 @@ export function DRLG_GenerateJungles(
|
||||||
const offsetX = Math.max(0, Math.floor((32 - bDs1.width) / 2))
|
const offsetX = Math.max(0, Math.floor((32 - bDs1.width) / 2))
|
||||||
const offsetY = Math.max(0, Math.floor((32 - bDs1.height) / 2))
|
const offsetY = Math.max(0, Math.floor((32 - bDs1.height) / 2))
|
||||||
stampDs1Fn(canvas, bDs1, bx + offsetX, by + offsetY)
|
stampDs1Fn(canvas, bDs1, bx + offsetX, by + offsetY)
|
||||||
|
placedRects?.push({ x: bx + offsetX, y: by + offsetY, w: bDs1.width, h: bDs1.height, name: bridgePiece.name })
|
||||||
}
|
}
|
||||||
synthesizeBridgePlanks(canvas, bx, by, orientation)
|
synthesizeBridgePlanks(canvas, bx, by, orientation, bridgePiece?.levels[0])
|
||||||
placed.add('Act 3 - Bridge')
|
placed.add('Act 3 - Bridge')
|
||||||
stats.substitutions.push({ name: 'Act 3 - Bridge', role: 'object', enabled: true, clusters: 1 })
|
stats.substitutions.push({ name: 'Act 3 - Bridge', role: 'object', enabled: true, clusters: 1 })
|
||||||
}
|
}
|
||||||
|
|
@ -538,9 +557,11 @@ export function DRLG_GenerateJungles(
|
||||||
bridges.push({ col: bridgeCol, row: bridgeRow, x: bx, y: by, pieceName: 'Act 3 - Bridge' })
|
bridges.push({ col: bridgeCol, row: bridgeRow, x: bx, y: by, pieceName: 'Act 3 - Bridge' })
|
||||||
|
|
||||||
if (bridgePiece?.levels[0]) {
|
if (bridgePiece?.levels[0]) {
|
||||||
stampDs1Fn(canvas, bridgePiece.levels[0], bx, by)
|
const bDs1 = bridgePiece.levels[0]
|
||||||
|
stampDs1Fn(canvas, bDs1, bx, by)
|
||||||
|
placedRects?.push({ x: bx, y: by, w: bDs1.width, h: bDs1.height, name: bridgePiece.name })
|
||||||
}
|
}
|
||||||
synthesizeBridgePlanks(canvas, bx, by, 'horizontal')
|
synthesizeBridgePlanks(canvas, bx, by, 'horizontal', bridgePiece?.levels[0])
|
||||||
placed.add('Act 3 - Bridge')
|
placed.add('Act 3 - Bridge')
|
||||||
stats.substitutions.push({ name: 'Act 3 - Bridge', role: 'object', enabled: true, clusters: 1 })
|
stats.substitutions.push({ name: 'Act 3 - Bridge', role: 'object', enabled: true, clusters: 1 })
|
||||||
}
|
}
|
||||||
|
|
@ -553,11 +574,13 @@ export function DRLG_GenerateJungles(
|
||||||
stampDs1Fn(canvas, tailPiece.levels[0], 0, 0)
|
stampDs1Fn(canvas, tailPiece.levels[0], 0, 0)
|
||||||
placed.add(tailPiece.name)
|
placed.add(tailPiece.name)
|
||||||
stats.substitutions.push({ name: tailPiece.name, role: 'object', enabled: true, clusters: 1 })
|
stats.substitutions.push({ name: tailPiece.name, role: 'object', enabled: true, clusters: 1 })
|
||||||
|
placedRects?.push({ x: 0, y: 0, w: tailPiece.levels[0].width, h: tailPiece.levels[0].height, name: tailPiece.name })
|
||||||
}
|
}
|
||||||
if (headPiece?.levels[0]) {
|
if (headPiece?.levels[0]) {
|
||||||
stampDs1Fn(canvas, headPiece.levels[0], 0, (numRows - 1) * 32)
|
stampDs1Fn(canvas, headPiece.levels[0], 0, (numRows - 1) * 32)
|
||||||
placed.add(headPiece.name)
|
placed.add(headPiece.name)
|
||||||
stats.substitutions.push({ name: headPiece.name, role: 'object', enabled: true, clusters: 1 })
|
stats.substitutions.push({ name: headPiece.name, role: 'object', enabled: true, clusters: 1 })
|
||||||
|
placedRects?.push({ x: 0, y: (numRows - 1) * 32, w: headPiece.levels[0].width, h: headPiece.levels[0].height, name: headPiece.name })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 8. Stamp Interior River and Clearing Presets
|
// 8. Stamp Interior River and Clearing Presets
|
||||||
|
|
@ -575,6 +598,7 @@ export function DRLG_GenerateJungles(
|
||||||
cell.pieceName = riverPiece.name
|
cell.pieceName = riverPiece.name
|
||||||
placed.add(riverPiece.name)
|
placed.add(riverPiece.name)
|
||||||
stats.substitutions.push({ name: riverPiece.name, role: 'object', enabled: true, clusters: 1 })
|
stats.substitutions.push({ name: riverPiece.name, role: 'object', enabled: true, clusters: 1 })
|
||||||
|
placedRects?.push({ x, y, w: ds1.width, h: ds1.height, name: riverPiece.name })
|
||||||
}
|
}
|
||||||
} else if (cell.type === 'clearing') {
|
} else if (cell.type === 'clearing') {
|
||||||
const preferredOpening =
|
const preferredOpening =
|
||||||
|
|
@ -591,6 +615,7 @@ export function DRLG_GenerateJungles(
|
||||||
cell.pieceName = clearingPiece.name
|
cell.pieceName = clearingPiece.name
|
||||||
placed.add(clearingPiece.name)
|
placed.add(clearingPiece.name)
|
||||||
stats.substitutions.push({ name: clearingPiece.name, role: 'object', enabled: true, clusters: 1 })
|
stats.substitutions.push({ name: clearingPiece.name, role: 'object', enabled: true, clusters: 1 })
|
||||||
|
placedRects?.push({ x, y, w: ds1.width, h: ds1.height, name: clearingPiece.name })
|
||||||
clearings.push({
|
clearings.push({
|
||||||
col: c,
|
col: c,
|
||||||
row: r,
|
row: r,
|
||||||
|
|
@ -605,9 +630,11 @@ export function DRLG_GenerateJungles(
|
||||||
// 9. Re-stamp Bridges on top of river water tiles to ensure bridge plank floor integrity
|
// 9. Re-stamp Bridges on top of river water tiles to ensure bridge plank floor integrity
|
||||||
for (const b of bridges) {
|
for (const b of bridges) {
|
||||||
if (bridgePiece?.levels[0]) {
|
if (bridgePiece?.levels[0]) {
|
||||||
stampDs1Fn(canvas, bridgePiece.levels[0], b.x, b.y)
|
const ds1 = bridgePiece.levels[0]
|
||||||
|
stampDs1Fn(canvas, ds1, b.x, b.y)
|
||||||
|
placedRects?.push({ x: b.x, y: b.y, w: ds1.width, h: ds1.height, name: bridgePiece.name })
|
||||||
}
|
}
|
||||||
synthesizeBridgePlanks(canvas, b.x, b.y, 'horizontal')
|
synthesizeBridgePlanks(canvas, b.x, b.y, 'horizontal', bridgePiece?.levels[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
// 10. Record Layout Statistics
|
// 10. Record Layout Statistics
|
||||||
|
|
|
||||||
|
|
@ -3122,7 +3122,7 @@ function stampAct3Jungle(
|
||||||
stats: WildernessStats,
|
stats: WildernessStats,
|
||||||
placedRects?: PlacedPresetRect[],
|
placedRects?: PlacedPresetRect[],
|
||||||
): Set<string> {
|
): Set<string> {
|
||||||
return DRLG_GenerateJungles(canvas, levelId, pieces, rng, stats, stampDs1)
|
return DRLG_GenerateJungles(canvas, levelId, pieces, rng, stats, stampDs1, placedRects)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Theme configuration for outdoor road rasterization. */
|
/** Theme configuration for outdoor road rasterization. */
|
||||||
|
|
@ -3156,7 +3156,7 @@ export function stampThemedRoads(
|
||||||
): void {
|
): void {
|
||||||
const isInsideProtectedPreset = (x: number, y: number): boolean => {
|
const isInsideProtectedPreset = (x: number, y: number): boolean => {
|
||||||
for (const r of placedRects) {
|
for (const r of placedRects) {
|
||||||
if (theme.isProtectedPreset ? !theme.isProtectedPreset(r.name) : /River|Bridge/i.test(r.name)) {
|
if (theme.isProtectedPreset ? !theme.isProtectedPreset(r.name) : false) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (x >= r.x && x < r.x + r.w && y >= r.y && y < r.y + r.h) {
|
if (x >= r.x && x < r.x + r.w && y >= r.y && y < r.y + r.h) {
|
||||||
|
|
@ -3176,7 +3176,11 @@ export function stampThemedRoads(
|
||||||
if (existingFloor && (existingFloor.style === 2 || existingFloor.style === 3 || existingFloor.style === 4)) {
|
if (existingFloor && (existingFloor.style === 2 || existingFloor.style === 3 || existingFloor.style === 4)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
const belongsToPreset = placedRects.some(r => x >= r.x && x < r.x + r.w && y >= r.y && y < r.y + r.h)
|
||||||
if (cell.walls.some(w => !w.hidden && w.prop1 !== 0)) {
|
if (cell.walls.some(w => !w.hidden && w.prop1 !== 0)) {
|
||||||
|
if (belongsToPreset) {
|
||||||
|
return
|
||||||
|
}
|
||||||
if (clearStrayClutter) {
|
if (clearStrayClutter) {
|
||||||
// Clear random LvlSub bush/tree clutter on the main highway spine
|
// Clear random LvlSub bush/tree clutter on the main highway spine
|
||||||
;(cell.walls as Ds1Wall[]).length = 0
|
;(cell.walls as Ds1Wall[]).length = 0
|
||||||
|
|
@ -3508,7 +3512,7 @@ export function stampAct2DesertRoads(
|
||||||
unknown2: 0,
|
unknown2: 0,
|
||||||
hidden: false,
|
hidden: false,
|
||||||
}),
|
}),
|
||||||
isProtectedPreset: (name) => !/River|Bridge/i.test(name),
|
isProtectedPreset: (name) => name !== 'Act 1 - River',
|
||||||
isIgnoredSpurPreset: (name) => /Fill|Berms/i.test(name),
|
isIgnoredSpurPreset: (name) => /Fill|Berms/i.test(name),
|
||||||
},
|
},
|
||||||
openGates,
|
openGates,
|
||||||
|
|
@ -3564,7 +3568,7 @@ export function stampAct3KurastAvenues(
|
||||||
unknown2: 0,
|
unknown2: 0,
|
||||||
hidden: false,
|
hidden: false,
|
||||||
}),
|
}),
|
||||||
isProtectedPreset: (name) => !/River|Bridge/i.test(name),
|
isProtectedPreset: (name) => name !== 'Act 1 - River',
|
||||||
isIgnoredSpurPreset: (name) => /Fill/i.test(name),
|
isIgnoredSpurPreset: (name) => /Fill/i.test(name),
|
||||||
},
|
},
|
||||||
openGates,
|
openGates,
|
||||||
|
|
@ -3620,7 +3624,7 @@ export function stampAct5SnowRoads(
|
||||||
unknown2: 0,
|
unknown2: 0,
|
||||||
hidden: false,
|
hidden: false,
|
||||||
}),
|
}),
|
||||||
isProtectedPreset: (name) => !/River|Bridge/i.test(name),
|
isProtectedPreset: (name) => name !== 'Act 1 - River',
|
||||||
isIgnoredSpurPreset: (name) => /Fill|Filler/i.test(name),
|
isIgnoredSpurPreset: (name) => /Fill|Filler/i.test(name),
|
||||||
},
|
},
|
||||||
openGates,
|
openGates,
|
||||||
|
|
@ -5141,7 +5145,7 @@ export function generateWilderness(request: WildernessRequest): WildernessResult
|
||||||
if (request.levelId === 83) {
|
if (request.levelId === 83) {
|
||||||
placedSpecial = stampAct3Travincal(canvas, request.pieces, stats, specialPlacedRects)
|
placedSpecial = stampAct3Travincal(canvas, request.pieces, stats, specialPlacedRects)
|
||||||
} else if (request.levelId === 82) {
|
} else if (request.levelId === 82) {
|
||||||
placedSpecial = stampAct3KurastCauseway(canvas, request.pieces, stats)
|
placedSpecial = stampAct3KurastCauseway(canvas, request.pieces, stats, specialPlacedRects)
|
||||||
} else if (request.levelId >= 76 && request.levelId <= 78) {
|
} else if (request.levelId >= 76 && request.levelId <= 78) {
|
||||||
placedSpecial = stampAct3Jungle(canvas, request.levelId, request.pieces, rng, stats, specialPlacedRects)
|
placedSpecial = stampAct3Jungle(canvas, request.levelId, request.pieces, rng, stats, specialPlacedRects)
|
||||||
const populationRng = new Rng(request.seed ^ 0x6c07)
|
const populationRng = new Rng(request.seed ^ 0x6c07)
|
||||||
|
|
|
||||||
|
|
@ -13,15 +13,21 @@ import type {
|
||||||
} from '../src/game/wilderness-acts.ts'
|
} from '../src/game/wilderness-acts.ts'
|
||||||
import type { Ds1, Ds1Cell } from '../src/formats/ds1.ts'
|
import type { Ds1, Ds1Cell } from '../src/formats/ds1.ts'
|
||||||
|
|
||||||
function makeMockDs1(width: number, height: number, hasGapInWalls: boolean = false): Ds1 {
|
function makeMockDs1(
|
||||||
|
width: number,
|
||||||
|
height: number,
|
||||||
|
hasGapInWalls: boolean = false,
|
||||||
|
floorStyle: number = 1,
|
||||||
|
wallStyle: number = 1,
|
||||||
|
): Ds1 {
|
||||||
const cells: Ds1Cell[][] = []
|
const cells: Ds1Cell[][] = []
|
||||||
for (let y = 0; y < height; y += 1) {
|
for (let y = 0; y < height; y += 1) {
|
||||||
const row: Ds1Cell[] = []
|
const row: Ds1Cell[] = []
|
||||||
for (let x = 0; x < width; x += 1) {
|
for (let x = 0; x < width; x += 1) {
|
||||||
const isGap = hasGapInWalls && x === Math.floor(width / 2)
|
const isGap = hasGapInWalls && x === Math.floor(width / 2)
|
||||||
row.push({
|
row.push({
|
||||||
walls: isGap ? [] : [{ prop1: 2, sequence: 0, style: 1, type: 0, unknown1: 0, unknown2: 0, hidden: false }],
|
walls: isGap ? [] : [{ prop1: 2, sequence: 0, style: wallStyle, type: 0, unknown1: 0, unknown2: 0, hidden: false }],
|
||||||
floors: [{ prop1: 2, sequence: 0, style: 1, unknown1: 0, unknown2: 0, hidden: false }],
|
floors: [{ prop1: 2, sequence: 0, style: floorStyle, unknown1: 0, unknown2: 0, hidden: false }],
|
||||||
shadows: [],
|
shadows: [],
|
||||||
substitutions: [],
|
substitutions: [],
|
||||||
})
|
})
|
||||||
|
|
@ -231,9 +237,9 @@ describe('Wilderness Outdoor Initializers for Acts 2-5 (Issue #51)', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
test('Kurast Causeway (82) stamps causeway with central walkway, balustrades, and canal water', () => {
|
test('Kurast Causeway (82) stamps causeway with central walkway, balustrades, and canal water', () => {
|
||||||
const bridgeDs1 = makeMockDs1(48, 16)
|
const groundDs1 = makeMockDs1(48, 16)
|
||||||
const pieces: WildernessPiece[] = [
|
const pieces: WildernessPiece[] = [
|
||||||
{ name: 'Act 3 - Bridge', border: false, levels: [bridgeDs1] },
|
{ name: 'Act 3 - Ground', border: false, levels: [groundDs1] },
|
||||||
]
|
]
|
||||||
|
|
||||||
const result = generateWilderness({
|
const result = generateWilderness({
|
||||||
|
|
@ -271,6 +277,38 @@ describe('Wilderness Outdoor Initializers for Acts 2-5 (Issue #51)', () => {
|
||||||
expect(foundCanalWater).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', () => {
|
test('Kurast Bazaar (79) generates paved stone avenues, canals, and stone bridges', () => {
|
||||||
const pieces = makeMockBorderPieces('Act 3')
|
const pieces = makeMockBorderPieces('Act 3')
|
||||||
const result = generateWilderness({
|
const result = generateWilderness({
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ import {
|
||||||
} from '../src/game/wilderness.ts'
|
} from '../src/game/wilderness.ts'
|
||||||
import type { WildernessPiece, PlacedPresetRect, Canvas } from '../src/game/wilderness.ts'
|
import type { WildernessPiece, PlacedPresetRect, Canvas } from '../src/game/wilderness.ts'
|
||||||
import { Rng } from '../src/game/rng.ts'
|
import { Rng } from '../src/game/rng.ts'
|
||||||
import type { Ds1, Ds1Cell } from '../src/formats/ds1.ts'
|
import type { Ds1, Ds1Cell, Ds1Wall } from '../src/formats/ds1.ts'
|
||||||
|
|
||||||
function makeMockDs1(width: number, height: number): Ds1 {
|
function makeMockDs1(width: number, height: number): Ds1 {
|
||||||
const cells: Ds1Cell[][] = []
|
const cells: Ds1Cell[][] = []
|
||||||
|
|
@ -412,5 +412,33 @@ describe('Multi-Act Outdoor Roads & Boundary Divider Networks', () => {
|
||||||
expect(roadFloors).toBe(0)
|
expect(roadFloors).toBe(0)
|
||||||
expect(dividerWalls).toBe(0)
|
expect(dividerWalls).toBe(0)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('stampThemedRoads preserves preset walls on the highway spine', () => {
|
||||||
|
const canvas = createCanvas(width, height, 1, 1, 0)
|
||||||
|
const rng = new Rng(42)
|
||||||
|
const hubX = inX * TILES_PER_BLOCK + 4
|
||||||
|
const hubY = outY * TILES_PER_BLOCK + 4
|
||||||
|
// Preset directly intersects the road spine
|
||||||
|
const placedRects: PlacedPresetRect[] = [
|
||||||
|
{ name: 'Act 1 - Ruin', x: hubX - 2, y: hubY - 2, w: 5, h: 5 },
|
||||||
|
]
|
||||||
|
// Paint preset wall inside the preset on the highway spine
|
||||||
|
const targetCell = canvas.cells[hubY]?.[hubX]
|
||||||
|
expect(targetCell).toBeDefined()
|
||||||
|
;(targetCell!.walls as Ds1Wall[]).push({
|
||||||
|
prop1: 100,
|
||||||
|
sequence: 0,
|
||||||
|
style: 99,
|
||||||
|
type: 1,
|
||||||
|
unknown1: 0,
|
||||||
|
unknown2: 0,
|
||||||
|
hidden: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
stampAct1DirtRoads(canvas, gridW, gridH, inX, outY, placedRects, rng)
|
||||||
|
|
||||||
|
// The wall inside the preset must NOT have been wiped
|
||||||
|
expect(targetCell!.walls.some(w => w.style === 99 && w.prop1 === 100)).toBe(true)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue