Merge branch 'fix/issue-46'

This commit is contained in:
troytt 2026-09-17 08:40:20 +00:00
commit c310119ed2
1 changed files with 14 additions and 46 deletions

View File

@ -1591,29 +1591,9 @@ function themeRoll(level: MazeLevel, pieces: readonly MazePiece[], stats: MazeSt
} }
/** /**
* Compute BFS topological distance of every room from the level's entrance room * Place a special room group following standard D2 scanning rules:
* (the room holding a 'prev' or 'entrance' piece, or `level.first`). * scans room candidates against valid wall connections, LvlPrest file definitions,
*/ * and room flags, rather than imposing an artificial global maximum BFS distance.
function bfsDistances(level: MazeLevel): Map<Room, number> {
const start = level.rooms.find(r => r.piece?.kind === 'prev' || r.piece?.kind === 'entrance') ?? level.first
const dist = new Map<Room, number>([[start, 0]])
const queue: Room[] = [start]
while (queue.length > 0) {
const current = queue.shift()!
const d = dist.get(current)!
for (const orth of current.orths) {
if (!dist.has(orth.room)) {
dist.set(orth.room, d + 1)
queue.push(orth.room)
}
}
}
return dist
}
/**
* Place a special room group using topological dead-end / BFS distance selection
* and `LvlPrest.txt` full-room preset substitution.
* *
* @param level - the level. * @param level - the level.
* @param pieces - the level's pieces. * @param pieces - the level's pieces.
@ -1630,11 +1610,9 @@ function placeSpecialGroup(
index: PieceIndex, index: PieceIndex,
stats: MazeStats, stats: MazeStats,
): void { ): void {
const isExit = group.kind === 'next' || group.kind === 'down' || group.kind === 'quest' // Phase 1: Try to replace an existing unfinished room in place, scanning
const distMap = bfsDistances(level) // room candidates against valid wall connections, LvlPrest definitions,
// and room flags in standard D2 roster order.
// Phase 1: Try to replace an existing unfinished room in place, preferring
// maximum BFS distance from entrance for exits/objectives and minimum for entrances.
for (let offset = 0; offset < group.entries.length; offset += 1) { for (let offset = 0; offset < group.entries.length; offset += 1) {
const entry = group.entries[(startCursor + offset) % group.entries.length]! const entry = group.entries[(startCursor + offset) % group.entries.length]!
const wanted = sideMaskFromToken(entry.plainSides) const wanted = sideMaskFromToken(entry.plainSides)
@ -1642,20 +1620,14 @@ function placeSpecialGroup(
const special = findSpecial(pieces, group.kind, entry.sides) const special = findSpecial(pieces, group.kind, entry.sides)
if (special === undefined) continue if (special === undefined) continue
const candidates = level.rooms.filter(r => { const chosen = level.rooms.find(r => {
if (roomSideMask(r) !== wanted) return false if (roomSideMask(r) !== wanted) return false
if (!r.hasDs1) return true if (!r.hasDs1) return true
if (group.inPlaceOnly === true && (r.piece === special || r.piece?.kind === 'theme')) return true if (group.inPlaceOnly === true && (r.piece === special || r.piece?.kind === 'theme')) return true
return false return false
}) })
if (candidates.length > 0) { if (chosen !== undefined) {
candidates.sort((a, b) => {
const da = distMap.get(a) ?? 0
const db = distMap.get(b) ?? 0
return isExit ? db - da : da - db
})
const chosen = candidates[0]!
chosen.piece = special chosen.piece = special
chosen.variant = 0 chosen.variant = 0
chosen.hasDs1 = true chosen.hasDs1 = true
@ -1670,16 +1642,12 @@ function placeSpecialGroup(
return return
} }
// Phase 2: Grow a new dead-end room off the best available parent room. // Phase 2: Grow a new dead-end room off an available parent room in D2
const parentCandidates = level.rooms.filter( // roster order (plain rooms without DS1 scanned first, followed by theme rooms).
r => !r.hasDs1 || r.piece?.kind === 'theme', const parentCandidates = [
) ...level.rooms.filter(r => !r.hasDs1),
parentCandidates.sort((a, b) => { ...level.rooms.filter(r => r.hasDs1 && r.piece?.kind === 'theme'),
if (a.hasDs1 !== b.hasDs1) return a.hasDs1 ? 1 : -1 ]
const da = distMap.get(a) ?? 0
const db = distMap.get(b) ?? 0
return isExit ? db - da : da - db
})
for (const parent of parentCandidates) { for (const parent of parentCandidates) {
for (let offset = 0; offset < group.entries.length; offset += 1) { for (let offset = 0; offset < group.entries.length; offset += 1) {