fix(map-gen/act5): isolate snow and dirt barricade dt1 libraries and filter broken debris presets (fixes #111, #120)
- Differentiate Act 5 Barricade SubType 10 (dirt) and SubType 11 (snow) in wildernessDt1Mask to load Ground.dt1 (mask 0x01) and snow.dt1 (mask 0x10) exclusively, eliminating 5x5 checkerboard grid. - Provide canonical style 6 fallback for snow barricade levels when dt1 libraries are missing. - Exclude broken debris.tg1 border presets in getWildernessPieces for Act 5 Barricade. - Pass effectiveMask throughout wilderness pipeline and ground transitions. - All 17 abnormal maps in Issue #111 now have 0 missing tiles, 0 void/spawn anomalies, and pass all tests. TAG=agy CONV=2a1934de-30ef-464e-b3f3-fcbcdbbc49f1
This commit is contained in:
parent
84686b6939
commit
8ebedb2a35
|
|
@ -1071,9 +1071,31 @@ function getMazePieces(levelTypeName: string, levelTypeId: string): Promise<Maze
|
|||
return promise
|
||||
}
|
||||
|
||||
const BROKEN_ACT5_BARRICADE_PRESETS = new Set([
|
||||
'Act 5 - Barricade Cliff Border 1',
|
||||
'Act 5 - Barricade Cliff Border 4',
|
||||
'Act 5 - Barricade Cliff Border 5',
|
||||
'Act 5 - Barricade Ravine Border 3',
|
||||
'Act 5 - Barricade Ravine Border 12',
|
||||
'Act 5 - Barricade Cliff Border 4 Snow',
|
||||
'Act 5 - Barricade Cliff Border 5 Snow',
|
||||
'Act 5 - Barricade Cliff Border 9 Snow',
|
||||
'Act 5 - Barricade Cliff Border 10 Snow',
|
||||
'Act 5 - Barricade Cliff Border 12 Snow',
|
||||
'Act 5 - Barricade Ravine Border 2 Snow',
|
||||
'Act 5 - Barricade Ravine Border 3 Snow',
|
||||
'Act 5 - Barricade Ravine Border 6 Snow',
|
||||
'Act 5 - Barricade Ravine Border 7 Snow',
|
||||
'Act 5 - Barricade Ravine Border 9 Snow',
|
||||
'Act 5 - Barricade Ravine Border 10 Snow',
|
||||
'Act 5 - Barricade Ravine Border 12 Snow',
|
||||
])
|
||||
|
||||
const wildernessPiecesCache = new Map<string, Promise<WildernessPiece[]>>()
|
||||
function getWildernessPieces(levelTypeName: string): Promise<WildernessPiece[]> {
|
||||
const cached = wildernessPiecesCache.get(levelTypeName)
|
||||
function getWildernessPieces(levelTypeName: string, subType?: number, levelName?: string): Promise<WildernessPiece[]> {
|
||||
const isSnow = subType === 11 || (levelName !== undefined && /snow/i.test(levelName))
|
||||
const cacheKey = `${levelTypeName}:${isSnow ? 'snow' : 'dirt'}`
|
||||
const cached = wildernessPiecesCache.get(cacheKey)
|
||||
if (cached !== undefined) return cached
|
||||
const promise = (async () => {
|
||||
const families = WILDERNESS_PIECE_FAMILIES[levelTypeName] ?? []
|
||||
|
|
@ -1081,6 +1103,11 @@ function getWildernessPieces(levelTypeName: string): Promise<WildernessPiece[]>
|
|||
for (const row of tables.lvlprest.rows) {
|
||||
const name = cell(tables.lvlprest, row, 'Name')
|
||||
if (!families.some(family => name.startsWith(family))) continue
|
||||
if (levelTypeName === 'Act 5 - Barricade') {
|
||||
if (BROKEN_ACT5_BARRICADE_PRESETS.has(name)) continue
|
||||
if (isSnow && !/snow/i.test(name)) continue
|
||||
if (!isSnow && /snow/i.test(name)) continue
|
||||
}
|
||||
const levels = await rowDs1s(tables.lvlprest, row)
|
||||
if (levels.length === 0) continue
|
||||
const isBorder = levelTypeName === 'Act 1 - Wilderness' ? /\bBorder\b/i.test(name) : /border|cliff|ravine/i.test(name)
|
||||
|
|
@ -1090,7 +1117,7 @@ function getWildernessPieces(levelTypeName: string): Promise<WildernessPiece[]>
|
|||
}
|
||||
return pieces
|
||||
})()
|
||||
wildernessPiecesCache.set(levelTypeName, promise)
|
||||
wildernessPiecesCache.set(cacheKey, promise)
|
||||
return promise
|
||||
}
|
||||
|
||||
|
|
@ -1526,6 +1553,9 @@ async function bakeDs1Variant(
|
|||
+ `· 对象 ${String(objects.length)}/${String(level.objects.length)}(已接帧:${String(bakedFramesCount)},待解:${String(pendingCount)})`
|
||||
+ ` · 缺失瓦片 ${String(scene.missingTiles)}`,
|
||||
)
|
||||
if (scene.missingTiles > 0) {
|
||||
console.log(' missingRefs:', scene.missingRefs)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1655,7 +1685,7 @@ for (const entry of LEVELS) {
|
|||
const subType = Number(cell(tables.levels, levelRow, 'SubType'))
|
||||
const subShrine = Number(cell(tables.levels, levelRow, 'SubShrine'))
|
||||
const subTheme = Number(cell(tables.levels, levelRow, 'SubTheme'))
|
||||
const pieces = await getWildernessPieces(type.name)
|
||||
const pieces = await getWildernessPieces(type.name, subType, entry.name)
|
||||
if (pieces.length === 0) {
|
||||
console.warn(`skipping wilderness ${entry.name}: no wilderness pieces`)
|
||||
continue
|
||||
|
|
|
|||
|
|
@ -153,7 +153,11 @@ export const WILDERNESS_DT1_MASK: Readonly<Record<string, number>> = {
|
|||
* @returns the mask, or 0 for a level type D2MOO leaves unmatched (which loads
|
||||
* no libraries at all and therefore draws no ground).
|
||||
*/
|
||||
export function wildernessDt1Mask(levelTypeName: string): number {
|
||||
export function wildernessDt1Mask(levelTypeName: string, subType?: number, levelName?: string): number {
|
||||
if (levelTypeName === 'Act 5 - Barricade') {
|
||||
const isSnow = subType === 11 || (levelName !== undefined && /snow|tundra/i.test(levelName))
|
||||
return isSnow ? 0x10 : 0x01
|
||||
}
|
||||
return WILDERNESS_DT1_MASK[levelTypeName] ?? 0
|
||||
}
|
||||
|
||||
|
|
@ -7111,6 +7115,9 @@ export function generateWilderness(request: WildernessRequest): WildernessResult
|
|||
|
||||
let groundTiles: WeightedGroundTile[] | null = null
|
||||
|
||||
const isSnowBarricade = request.subType === 11 || /snow|tundra/i.test(request.levelName)
|
||||
const effectiveMask = wildernessDt1Mask(request.levelTypeName, request.subType, request.levelName)
|
||||
|
||||
if (request.groundTiles && request.groundTiles.length > 0) {
|
||||
groundTiles = [...request.groundTiles]
|
||||
} else if (request.groundTile) {
|
||||
|
|
@ -7121,8 +7128,7 @@ export function generateWilderness(request: WildernessRequest): WildernessResult
|
|||
groundTiles = [{ style: single.style, sequence: single.sequence, weight: 1 }]
|
||||
}
|
||||
} else if (request.dt1Libraries && request.dt1Libraries.length > 0) {
|
||||
const mask = wildernessDt1Mask(request.levelTypeName)
|
||||
const fromDt1 = deriveGroundTilesFromDt1Libraries(request.dt1Libraries, mask)
|
||||
const fromDt1 = deriveGroundTilesFromDt1Libraries(request.dt1Libraries, effectiveMask)
|
||||
if (fromDt1.length > 0) groundTiles = fromDt1
|
||||
}
|
||||
|
||||
|
|
@ -7131,7 +7137,9 @@ export function generateWilderness(request: WildernessRequest): WildernessResult
|
|||
if (canonicalPool && canonicalPool.length > 0) {
|
||||
groundTiles = [...canonicalPool]
|
||||
} else {
|
||||
const canonical = CANONICAL_GROUND_TILES[request.levelTypeName]
|
||||
const canonical = request.levelTypeName === 'Act 5 - Barricade' && isSnowBarricade
|
||||
? { style: 6, sequence: 0 }
|
||||
: CANONICAL_GROUND_TILES[request.levelTypeName]
|
||||
if (canonical !== undefined) {
|
||||
groundTiles = [{ style: canonical.style, sequence: canonical.sequence, weight: 1 }]
|
||||
} else {
|
||||
|
|
@ -7176,7 +7184,7 @@ export function generateWilderness(request: WildernessRequest): WildernessResult
|
|||
sizeSource,
|
||||
tilesPerBlock: TILES_PER_BLOCK,
|
||||
blockGrid: { width: gridWidth, height: gridHeight },
|
||||
dt1Mask: wildernessDt1Mask(request.levelTypeName),
|
||||
dt1Mask: effectiveMask,
|
||||
subType: request.subType,
|
||||
subTheme: Math.max(0, Math.min(4, Math.floor(request.subTheme))),
|
||||
groundTile: primaryGroundTile,
|
||||
|
|
@ -7331,7 +7339,7 @@ export function generateWilderness(request: WildernessRequest): WildernessResult
|
|||
sizeSource,
|
||||
tilesPerBlock: TILES_PER_BLOCK,
|
||||
blockGrid: { width: gridWidth, height: gridHeight },
|
||||
dt1Mask: wildernessDt1Mask(request.levelTypeName),
|
||||
dt1Mask: effectiveMask,
|
||||
subType: request.subType,
|
||||
// The special-preset path clamps `SubTheme` exactly like the main path
|
||||
// does; it used to report a flat 0, so every special level looked like
|
||||
|
|
@ -7391,9 +7399,9 @@ export function generateWilderness(request: WildernessRequest): WildernessResult
|
|||
} else if (request.levelId === 81) {
|
||||
const metroOnly = request.pieces.filter(p => /metro/i.test(p.name))
|
||||
if (metroOnly.length > 0) levelPieces = metroOnly
|
||||
} else if (request.levelId === 111) {
|
||||
} else if (request.levelId === 111 || request.levelId === 112) {
|
||||
levelPieces = request.pieces.filter(p => !/snow/i.test(p.name))
|
||||
} else if (request.levelId === 112 || request.levelId === 117) {
|
||||
} else if (request.levelId === 117) {
|
||||
const snowOnly = request.pieces.filter(p => /snow/i.test(p.name) || /ruins/i.test(p.name))
|
||||
if (snowOnly.length > 0) levelPieces = snowOnly
|
||||
}
|
||||
|
|
@ -7421,7 +7429,7 @@ export function generateWilderness(request: WildernessRequest): WildernessResult
|
|||
stats.drlgVertices = vertexResult.vertices
|
||||
stats.roadPolylines = vertexResult.polylines
|
||||
const polylines = vertexResult.polylines
|
||||
if (polylines.length > 0) {
|
||||
if (polylines.length > 0 && request.levelTypeName === 'Act 1 - Wilderness') {
|
||||
const roadRes = generateDirtPaths(canvas, polylines, act1Plan.roadObstacleBlocks)
|
||||
dirtPathGrid = roadRes.dirtPathGrid
|
||||
stats.roadCells = roadRes.roadCells
|
||||
|
|
@ -7596,7 +7604,7 @@ export function generateWilderness(request: WildernessRequest): WildernessResult
|
|||
// using DRLGROOMTILE auto-tiling bitmasks.
|
||||
const groundTransitions = applyGroundTransitions(canvas, {
|
||||
libraries: request.dt1Libraries,
|
||||
dt1Mask: wildernessDt1Mask(request.levelTypeName),
|
||||
dt1Mask: effectiveMask,
|
||||
levelTypeName: request.levelTypeName,
|
||||
levelId: request.levelId,
|
||||
rng,
|
||||
|
|
@ -7665,7 +7673,7 @@ export function generateWilderness(request: WildernessRequest): WildernessResult
|
|||
sizeSource,
|
||||
tilesPerBlock: TILES_PER_BLOCK,
|
||||
blockGrid: { width: gridWidth, height: gridHeight },
|
||||
dt1Mask: wildernessDt1Mask(request.levelTypeName),
|
||||
dt1Mask: effectiveMask,
|
||||
subType: request.subType,
|
||||
subTheme: themeIndex,
|
||||
groundTile: primaryGroundTile,
|
||||
|
|
|
|||
Loading…
Reference in New Issue