diff --git a/scripts/pack-act-assets.ts b/scripts/pack-act-assets.ts index 4cb36fa..3e3d8a2 100644 --- a/scripts/pack-act-assets.ts +++ b/scripts/pack-act-assets.ts @@ -1071,9 +1071,31 @@ function getMazePieces(levelTypeName: string, levelTypeId: string): Promise>() -function getWildernessPieces(levelTypeName: string): Promise { - const cached = wildernessPiecesCache.get(levelTypeName) +function getWildernessPieces(levelTypeName: string, subType?: number, levelName?: string): Promise { + 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 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 } 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 diff --git a/src/game/wilderness.ts b/src/game/wilderness.ts index 54f18e5..b51fcca 100644 --- a/src/game/wilderness.ts +++ b/src/game/wilderness.ts @@ -153,7 +153,11 @@ export const WILDERNESS_DT1_MASK: Readonly> = { * @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,