fix(wilderness): prevent sealed borders on outdoor levels and track preset failures
TAG=agy CONV=2a1934de-30ef-464e-b3f3-fcbcdbbc49f1
This commit is contained in:
parent
b8810072ce
commit
fea677b0b4
|
|
@ -1405,6 +1405,7 @@ export function getPerimeterOpenings(
|
||||||
levelId: number,
|
levelId: number,
|
||||||
gridWidth: number,
|
gridWidth: number,
|
||||||
gridHeight: number,
|
gridHeight: number,
|
||||||
|
gates?: readonly PlannedGate[],
|
||||||
): Set<string> {
|
): Set<string> {
|
||||||
const openings = new Set<string>()
|
const openings = new Set<string>()
|
||||||
const key = (x: number, y: number) => `${String(x)},${String(y)}`
|
const key = (x: number, y: number) => `${String(x)},${String(y)}`
|
||||||
|
|
@ -1414,25 +1415,32 @@ export function getPerimeterOpenings(
|
||||||
return openings
|
return openings
|
||||||
}
|
}
|
||||||
|
|
||||||
// Outdoor wilderness levels across Acts 1, 2, 3, 4, 5:
|
if (gates !== undefined && gates.length > 0) {
|
||||||
// Act 1 (2..7), Act 2 desert (41..46), Act 3 Kurast (79..82),
|
for (const gate of gates) {
|
||||||
// Act 4 Mesa (104..106), Act 5 outdoor (111..112, 117):
|
switch (gate.side) {
|
||||||
// Default entrance on South edge, exit on East edge
|
case 'north':
|
||||||
if (
|
openings.add(key(Math.floor(gridWidth / 2), 0))
|
||||||
(levelId >= 2 && levelId <= 7) ||
|
break
|
||||||
(levelId >= 41 && levelId <= 46) ||
|
case 'south':
|
||||||
(levelId >= 79 && levelId <= 82) ||
|
openings.add(key(Math.floor(gridWidth / 2), gridHeight - 1))
|
||||||
(levelId >= 104 && levelId <= 106) ||
|
break
|
||||||
(levelId >= 111 && levelId <= 112) ||
|
case 'west':
|
||||||
levelId === 117
|
openings.add(key(0, Math.floor(gridHeight / 2)))
|
||||||
) {
|
break
|
||||||
const inX = Math.floor(gridWidth / 2)
|
case 'east':
|
||||||
openings.add(key(inX, gridHeight - 1))
|
openings.add(key(gridWidth - 1, Math.floor(gridHeight / 2)))
|
||||||
const outY = Math.floor(gridHeight / 2)
|
break
|
||||||
openings.add(key(gridWidth - 1, outY))
|
}
|
||||||
|
}
|
||||||
return openings
|
return openings
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Outdoor wilderness levels across Acts 1..5:
|
||||||
|
// Default entrance on South edge, exit on East edge so borders are never sealed shut
|
||||||
|
const inX = Math.floor(gridWidth / 2)
|
||||||
|
openings.add(key(inX, gridHeight - 1))
|
||||||
|
const outY = Math.floor(gridHeight / 2)
|
||||||
|
openings.add(key(gridWidth - 1, outY))
|
||||||
return openings
|
return openings
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1458,6 +1466,7 @@ export function getPerimeterOpenings(
|
||||||
* @param levelId - optional level id for perimeter opening rules.
|
* @param levelId - optional level id for perimeter opening rules.
|
||||||
* @param openGates - optional map from `${x},${y}` block key to forced variant index.
|
* @param openGates - optional map from `${x},${y}` block key to forced variant index.
|
||||||
* @param claimedBorderBlocks - optional set of `${x},${y}` keys to skip (e.g. Town Transitions).
|
* @param claimedBorderBlocks - optional set of `${x},${y}` keys to skip (e.g. Town Transitions).
|
||||||
|
* @param gates - optional planned gates from the world graph.
|
||||||
* @returns the number of pieces stamped.
|
* @returns the number of pieces stamped.
|
||||||
*/
|
*/
|
||||||
function layBorder(
|
function layBorder(
|
||||||
|
|
@ -1470,6 +1479,7 @@ function layBorder(
|
||||||
levelId?: number,
|
levelId?: number,
|
||||||
openGates?: ReadonlyMap<string, number>,
|
openGates?: ReadonlyMap<string, number>,
|
||||||
claimedBorderBlocks?: ReadonlySet<string>,
|
claimedBorderBlocks?: ReadonlySet<string>,
|
||||||
|
gates?: readonly PlannedGate[],
|
||||||
): number {
|
): number {
|
||||||
if (pieces.length === 0) {
|
if (pieces.length === 0) {
|
||||||
stats.unresolved.push('no border piece for this level type')
|
stats.unresolved.push('no border piece for this level type')
|
||||||
|
|
@ -1512,7 +1522,12 @@ function layBorder(
|
||||||
{ x: 0, y: gridHeight - 1 }, // SW
|
{ x: 0, y: gridHeight - 1 }, // SW
|
||||||
]
|
]
|
||||||
|
|
||||||
const openings = levelId !== undefined ? getPerimeterOpenings(levelId, gridWidth, gridHeight) : new Set<string>()
|
const openings = levelId !== undefined ? getPerimeterOpenings(levelId, gridWidth, gridHeight, gates) : new Set<string>()
|
||||||
|
if (openGates !== undefined) {
|
||||||
|
for (const gateKey of openGates.keys()) {
|
||||||
|
openings.add(gateKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let cursor = rng.int(0, ordered.length - 1)
|
let cursor = rng.int(0, ordered.length - 1)
|
||||||
let stamped = 0
|
let stamped = 0
|
||||||
|
|
@ -1673,6 +1688,7 @@ function planAct1OutdoorLevel(
|
||||||
gridWidth: number,
|
gridWidth: number,
|
||||||
gridHeight: number,
|
gridHeight: number,
|
||||||
rng: Rng,
|
rng: Rng,
|
||||||
|
stats?: WildernessStats,
|
||||||
): Act1OutdoorPlan {
|
): Act1OutdoorPlan {
|
||||||
const openGates = new Map<string, number>()
|
const openGates = new Map<string, number>()
|
||||||
const claimedBorderBlocks = new Set<string>()
|
const claimedBorderBlocks = new Set<string>()
|
||||||
|
|
@ -1802,13 +1818,21 @@ function planAct1OutdoorLevel(
|
||||||
toLevelId?: number,
|
toLevelId?: number,
|
||||||
): boolean => {
|
): boolean => {
|
||||||
const piece = findPiece(prefix)
|
const piece = findPiece(prefix)
|
||||||
if (piece === undefined || piece.levels.length === 0) return false
|
if (piece === undefined || piece.levels.length === 0) {
|
||||||
|
stats?.unresolved.push(`preset piece not found: ${prefix}`)
|
||||||
|
stats?.notes.push(`failed to place preset: ${label} (missing piece ${prefix})`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
const variant = rng.int(0, piece.levels.length - 1)
|
const variant = rng.int(0, piece.levels.length - 1)
|
||||||
const ds1 = piece.levels[variant]!
|
const ds1 = piece.levels[variant]!
|
||||||
const blocksX = Math.max(1, Math.floor(ds1.width / TILES_PER_BLOCK))
|
const blocksX = Math.max(1, Math.floor(ds1.width / TILES_PER_BLOCK))
|
||||||
const blocksY = Math.max(1, Math.floor(ds1.height / TILES_PER_BLOCK))
|
const blocksY = Math.max(1, Math.floor(ds1.height / TILES_PER_BLOCK))
|
||||||
const pos = pickFreeInteriorBox(blocksX, blocksY, 2)
|
const pos = pickFreeInteriorBox(blocksX, blocksY, 2)
|
||||||
if (pos === null) return false
|
if (pos === null) {
|
||||||
|
stats?.unresolved.push(`no free interior box for preset: ${label} (${prefix})`)
|
||||||
|
stats?.notes.push(`failed to place preset: ${label} (no free interior box ${String(blocksX)}x${String(blocksY)})`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
const { bx, by } = pos
|
const { bx, by } = pos
|
||||||
claimBox(bx, by, blocksX, blocksY, true)
|
claimBox(bx, by, blocksX, blocksY, true)
|
||||||
specialPresets.push({ name: piece.name, ds1, bx, by, blocksX, blocksY })
|
specialPresets.push({ name: piece.name, ds1, bx, by, blocksX, blocksY })
|
||||||
|
|
@ -1900,7 +1924,11 @@ function planAct1OutdoorLevel(
|
||||||
*/
|
*/
|
||||||
const placeGraveyardPreset = (withGate: boolean): boolean => {
|
const placeGraveyardPreset = (withGate: boolean): boolean => {
|
||||||
const gyPiece = findPiece('Act 1 - Graveyard')
|
const gyPiece = findPiece('Act 1 - Graveyard')
|
||||||
if (gyPiece?.levels[0] === undefined || gridWidth < 5 || gridHeight < 6) return false
|
if (gyPiece?.levels[0] === undefined || gridWidth < 5 || gridHeight < 6) {
|
||||||
|
stats?.unresolved.push('failed to place Graveyard preset')
|
||||||
|
stats?.notes.push('failed to place Graveyard preset (missing piece or grid too small)')
|
||||||
|
return false
|
||||||
|
}
|
||||||
const ds1 = gyPiece.levels[0]
|
const ds1 = gyPiece.levels[0]
|
||||||
claimBox(1, 1, 3, 4, true)
|
claimBox(1, 1, 3, 4, true)
|
||||||
specialPresets.push({ name: gyPiece.name, ds1, bx: 1, by: 1, blocksX: 3, blocksY: 4 })
|
specialPresets.push({ name: gyPiece.name, ds1, bx: 1, by: 1, blocksX: 3, blocksY: 4 })
|
||||||
|
|
@ -1941,7 +1969,9 @@ function planAct1OutdoorLevel(
|
||||||
*/
|
*/
|
||||||
const placeInteriorPresets = (levelId: number): void => {
|
const placeInteriorPresets = (levelId: number): void => {
|
||||||
if (levelId === 2) {
|
if (levelId === 2) {
|
||||||
addInteriorPresetWithRoad('Act 1 - DOE Entrance', 'Den of Evil Entrance', undefined, 8)
|
if (!addInteriorPresetWithRoad('Act 1 - DOE Entrance', 'Den of Evil Entrance', undefined, 8)) {
|
||||||
|
stats?.notes.push('Den of Evil Entrance preset failed to place')
|
||||||
|
}
|
||||||
const bivouac = findPiece('Act 1 - Bivouac') ?? findPiece('Act 1 - Cottages')
|
const bivouac = findPiece('Act 1 - Bivouac') ?? findPiece('Act 1 - Cottages')
|
||||||
if (bivouac?.levels[0] !== undefined) {
|
if (bivouac?.levels[0] !== undefined) {
|
||||||
const ds1 = bivouac.levels[0]
|
const ds1 = bivouac.levels[0]
|
||||||
|
|
@ -1951,28 +1981,52 @@ function planAct1OutdoorLevel(
|
||||||
if (pos !== null) {
|
if (pos !== null) {
|
||||||
claimBox(pos.bx, pos.by, w, h, true)
|
claimBox(pos.bx, pos.by, w, h, true)
|
||||||
specialPresets.push({ name: bivouac.name, ds1, bx: pos.bx, by: pos.by, blocksX: w, blocksY: h })
|
specialPresets.push({ name: bivouac.name, ds1, bx: pos.bx, by: pos.by, blocksX: w, blocksY: h })
|
||||||
|
} else {
|
||||||
|
stats?.notes.push('Bivouac preset failed to place (no free box)')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (levelId === 3) {
|
} else if (levelId === 3) {
|
||||||
addInteriorPresetWithRoad('Act 1 - Cave Entrance', 'Cave Level 1 Entrance', undefined, 9)
|
if (!addInteriorPresetWithRoad('Act 1 - Cave Entrance', 'Cave Level 1 Entrance', undefined, 9)) {
|
||||||
addInteriorPresetWithRoad('Act 1 - Bivouac', 'Bivouac Camp', 'south')
|
stats?.notes.push('Cave Level 1 Entrance preset failed to place')
|
||||||
|
}
|
||||||
|
if (!addInteriorPresetWithRoad('Act 1 - Bivouac', 'Bivouac Camp', 'south')) {
|
||||||
|
stats?.notes.push('Bivouac Camp preset failed to place')
|
||||||
|
}
|
||||||
} else if (levelId === 4) {
|
} else if (levelId === 4) {
|
||||||
addInteriorPresetWithRoad('Act 1 - Cairn Stones', 'Cairn Stones', 'south', 38)
|
if (!addInteriorPresetWithRoad('Act 1 - Cairn Stones', 'Cairn Stones', 'south', 38)) {
|
||||||
addInteriorPresetWithRoad('Act 1 - Cave Entrance', 'Underground Passage Entrance', undefined, 10)
|
stats?.notes.push('Cairn Stones preset failed to place')
|
||||||
|
}
|
||||||
|
if (!addInteriorPresetWithRoad('Act 1 - Cave Entrance', 'Underground Passage Entrance', undefined, 10)) {
|
||||||
|
stats?.notes.push('Underground Passage Entrance preset failed to place')
|
||||||
|
}
|
||||||
} else if (levelId === 5) {
|
} else if (levelId === 5) {
|
||||||
addInteriorPresetWithRoad('Act 1 - Inifus', 'Tree of Inifuss', 'south')
|
if (!addInteriorPresetWithRoad('Act 1 - Inifus', 'Tree of Inifuss', 'south')) {
|
||||||
addInteriorPresetWithRoad('Act 1 - Cave Entrance', 'Underground Passage Exit', undefined, 10)
|
stats?.notes.push('Tree of Inifuss preset failed to place')
|
||||||
|
}
|
||||||
|
if (!addInteriorPresetWithRoad('Act 1 - Cave Entrance', 'Underground Passage Exit', undefined, 10)) {
|
||||||
|
stats?.notes.push('Underground Passage Exit preset failed to place')
|
||||||
|
}
|
||||||
} else if (levelId === 6) {
|
} else if (levelId === 6) {
|
||||||
// The Forgotten Tower is always beside one of the Black Marsh's roads,
|
// The Forgotten Tower is always beside one of the Black Marsh's roads,
|
||||||
// never dropped on open ground, which is why it goes through the
|
// never dropped on open ground, which is why it goes through the
|
||||||
// road-joining path rather than the plain scatter.
|
// road-joining path rather than the plain scatter.
|
||||||
addInteriorPresetWithRoad('Act 1 - Tower 1', 'Forgotten Tower', 'south', 20)
|
if (!addInteriorPresetWithRoad('Act 1 - Tower 1', 'Forgotten Tower', 'south', 20)) {
|
||||||
addInteriorPresetWithRoad('Act 1 - Cave Entrance', 'Hole Level 1 Entrance', undefined, 11)
|
stats?.notes.push('Forgotten Tower preset failed to place')
|
||||||
|
}
|
||||||
|
if (!addInteriorPresetWithRoad('Act 1 - Cave Entrance', 'Hole Level 1 Entrance', undefined, 11)) {
|
||||||
|
stats?.notes.push('Hole Level 1 Entrance preset failed to place')
|
||||||
|
}
|
||||||
} else if (levelId === 7) {
|
} else if (levelId === 7) {
|
||||||
addInteriorPresetWithRoad('Act 1 - Cave Entrance', 'Pit Level 1 Entrance', undefined, 12)
|
if (!addInteriorPresetWithRoad('Act 1 - Cave Entrance', 'Pit Level 1 Entrance', undefined, 12)) {
|
||||||
|
stats?.notes.push('Pit Level 1 Entrance preset failed to place')
|
||||||
|
}
|
||||||
} else if (levelId === 39) {
|
} else if (levelId === 39) {
|
||||||
addInteriorPresetWithRoad('Act 1 - Corral Fill', 'Cow Corral', 'south')
|
if (!addInteriorPresetWithRoad('Act 1 - Corral Fill', 'Cow Corral', 'south')) {
|
||||||
addInteriorPresetWithRoad('Act 1 - Bivouac', 'Cow Bivouac', 'east')
|
stats?.notes.push('Cow Corral preset failed to place')
|
||||||
|
}
|
||||||
|
if (!addInteriorPresetWithRoad('Act 1 - Bivouac', 'Cow Bivouac', 'east')) {
|
||||||
|
stats?.notes.push('Cow Bivouac preset failed to place')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1989,7 +2043,12 @@ function planAct1OutdoorLevel(
|
||||||
if (placedTown && gate === townSeam) continue
|
if (placedTown && gate === townSeam) continue
|
||||||
addBorderGate(SIDE_TO_EDGE[gate.side], gate.variant ?? 3, gate.label, gate.toLevelId)
|
addBorderGate(SIDE_TO_EDGE[gate.side], gate.variant ?? 3, gate.label, gate.toLevelId)
|
||||||
}
|
}
|
||||||
if (id === 17) placeGraveyardPreset(false)
|
if (id === 17) {
|
||||||
|
if (!placeGraveyardPreset(false)) {
|
||||||
|
stats?.unresolved.push('Burial Grounds Graveyard preset failed to place')
|
||||||
|
stats?.notes.push('Burial Grounds Graveyard preset failed to place')
|
||||||
|
}
|
||||||
|
}
|
||||||
placeInteriorPresets(id)
|
placeInteriorPresets(id)
|
||||||
} else if (id === 2) {
|
} else if (id === 2) {
|
||||||
// Blood Moor: Town Transition + Exit Gate to Cold Plains + Den of Evil Entrance
|
// Blood Moor: Town Transition + Exit Gate to Cold Plains + Den of Evil Entrance
|
||||||
|
|
@ -2032,7 +2091,10 @@ function planAct1OutdoorLevel(
|
||||||
placeInteriorPresets(7)
|
placeInteriorPresets(7)
|
||||||
} else if (id === 17) {
|
} else if (id === 17) {
|
||||||
// Burial Grounds (5x6 blocks): Graveyard preset (3x4 blocks at bx=1, by=1) + South Entry Gate
|
// Burial Grounds (5x6 blocks): Graveyard preset (3x4 blocks at bx=1, by=1) + South Entry Gate
|
||||||
if (!placeGraveyardPreset(true)) addBorderGate('bottom', 4, 'Burial Grounds Gate', 3)
|
if (!placeGraveyardPreset(true)) {
|
||||||
|
stats?.notes.push('Burial Grounds Graveyard preset failed, falling back to border gate')
|
||||||
|
addBorderGate('bottom', 4, 'Burial Grounds Gate', 3)
|
||||||
|
}
|
||||||
} else if (id === 39 || id === 46) {
|
} else if (id === 39 || id === 46) {
|
||||||
// Moo Moo Farm (39) & Canyon of the Magi (46): Closed border, no border gates
|
// Moo Moo Farm (39) & Canyon of the Magi (46): Closed border, no border gates
|
||||||
if (id === 39) placeInteriorPresets(39)
|
if (id === 39) placeInteriorPresets(39)
|
||||||
|
|
@ -5117,7 +5179,7 @@ export function generateWilderness(request: WildernessRequest): WildernessResult
|
||||||
let act1Plan: Act1OutdoorPlan | undefined
|
let act1Plan: Act1OutdoorPlan | undefined
|
||||||
let dirtPathGrid: Uint8Array | undefined
|
let dirtPathGrid: Uint8Array | undefined
|
||||||
if (request.levelTypeName === 'Act 1 - Wilderness' || request.gates !== undefined) {
|
if (request.levelTypeName === 'Act 1 - Wilderness' || request.gates !== undefined) {
|
||||||
act1Plan = planAct1OutdoorLevel(request, gridWidth, gridHeight, rng)
|
act1Plan = planAct1OutdoorLevel(request, gridWidth, gridHeight, rng, stats)
|
||||||
stats.anchors = act1Plan.anchors.length
|
stats.anchors = act1Plan.anchors.length
|
||||||
stats.entrances = toEntrances(act1Plan.anchors)
|
stats.entrances = toEntrances(act1Plan.anchors)
|
||||||
const polylines = buildRoadNetworkPolylines(act1Plan, gridWidth, gridHeight, rng)
|
const polylines = buildRoadNetworkPolylines(act1Plan, gridWidth, gridHeight, rng)
|
||||||
|
|
@ -5140,6 +5202,7 @@ export function generateWilderness(request: WildernessRequest): WildernessResult
|
||||||
request.levelId,
|
request.levelId,
|
||||||
act1Plan?.openGates,
|
act1Plan?.openGates,
|
||||||
act1Plan?.claimedBorderBlocks,
|
act1Plan?.claimedBorderBlocks,
|
||||||
|
request.gates,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Step 4B: Stamp anchored special presets (Town Transition, Den of Evil, Cave Entrance, Cairn Stones, etc.)
|
// Step 4B: Stamp anchored special presets (Town Transition, Den of Evil, Cave Entrance, Cairn Stones, etc.)
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,29 @@ describe("Wilderness Features & Border Rules", () => {
|
||||||
expect(bloodMoorOpenings.has("9,5")).toBe(true)
|
expect(bloodMoorOpenings.has("9,5")).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("getPerimeterOpenings defaults to open borders for Act 3 Jungles and outdoor levels", () => {
|
||||||
|
// Act 3: Spider Forest (76), Great Marsh (77), Flayer Jungle (78)
|
||||||
|
const spiderForestOpenings = getPerimeterOpenings(76, 10, 10)
|
||||||
|
expect(spiderForestOpenings.size).toBe(2)
|
||||||
|
expect(spiderForestOpenings.has("5,9")).toBe(true)
|
||||||
|
expect(spiderForestOpenings.has("9,5")).toBe(true)
|
||||||
|
|
||||||
|
// Act 5: Arreat Plateau (113)
|
||||||
|
const arreatOpenings = getPerimeterOpenings(113, 10, 10)
|
||||||
|
expect(arreatOpenings.size).toBe(2)
|
||||||
|
})
|
||||||
|
|
||||||
|
test("getPerimeterOpenings maps planned gates to perimeter block coordinates", () => {
|
||||||
|
const plannedGates = [
|
||||||
|
{ side: 'north' as const, toLevelId: 75, label: 'To Town' },
|
||||||
|
{ side: 'east' as const, toLevelId: 77, label: 'To Marsh' },
|
||||||
|
]
|
||||||
|
const openings = getPerimeterOpenings(76, 12, 14, plannedGates)
|
||||||
|
expect(openings.size).toBe(2)
|
||||||
|
expect(openings.has("6,0")).toBe(true) // north: (12/2, 0)
|
||||||
|
expect(openings.has("11,7")).toBe(true) // east: (12-1, 14/2)
|
||||||
|
})
|
||||||
|
|
||||||
test("selectBorderVariant picks open variant for openings and closed variant for closed walls", () => {
|
test("selectBorderVariant picks open variant for openings and closed variant for closed walls", () => {
|
||||||
const closedDs1 = makeMockDs1(8, 8, false)
|
const closedDs1 = makeMockDs1(8, 8, false)
|
||||||
const openDs1 = makeMockDs1(8, 8, true)
|
const openDs1 = makeMockDs1(8, 8, true)
|
||||||
|
|
@ -356,4 +379,32 @@ describe("Wilderness Features & Border Rules", () => {
|
||||||
expect(foundContinuousEW).toBe(true)
|
expect(foundContinuousEW).toBe(true)
|
||||||
expect(foundEndCapEW).toBe(true)
|
expect(foundEndCapEW).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("records unresolved presets and notes when requested preset piece is missing", () => {
|
||||||
|
const borderDs1 = makeMockDs1(8, 8, false)
|
||||||
|
const piece: WildernessPiece = {
|
||||||
|
name: "Act 1 - Wild Border 1",
|
||||||
|
border: true,
|
||||||
|
levels: [borderDs1],
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = generateWilderness({
|
||||||
|
levelId: 2,
|
||||||
|
levelName: "Blood Moor",
|
||||||
|
levelTypeName: "Act 1 - Wilderness",
|
||||||
|
sizeX: 80,
|
||||||
|
sizeY: 80,
|
||||||
|
subType: 0,
|
||||||
|
subTheme: 0,
|
||||||
|
seed: 9999,
|
||||||
|
pieces: [piece], // Missing Den of Evil Entrance preset!
|
||||||
|
substitutions: [],
|
||||||
|
})
|
||||||
|
|
||||||
|
const unresolved = result.stats.unresolved as string[]
|
||||||
|
const notes = result.stats.notes as string[]
|
||||||
|
expect(unresolved.length).toBeGreaterThan(0)
|
||||||
|
expect(unresolved.some((u: string) => u.includes('Act 1 - DOE Entrance'))).toBe(true)
|
||||||
|
expect(notes.some((n: string) => n.includes('Den of Evil Entrance'))).toBe(true)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue