refactor(map-gen/maze): align special room placement with D2 scanning rules (fixes #46)
This commit is contained in:
parent
1fa3f8b7a2
commit
8bb99ded7e
|
|
@ -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
|
||||
* (the room holding a 'prev' or 'entrance' piece, or `level.first`).
|
||||
*/
|
||||
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.
|
||||
* Place a special room group following standard D2 scanning rules:
|
||||
* scans room candidates against valid wall connections, LvlPrest file definitions,
|
||||
* and room flags, rather than imposing an artificial global maximum BFS distance.
|
||||
*
|
||||
* @param level - the level.
|
||||
* @param pieces - the level's pieces.
|
||||
|
|
@ -1630,11 +1610,9 @@ function placeSpecialGroup(
|
|||
index: PieceIndex,
|
||||
stats: MazeStats,
|
||||
): void {
|
||||
const isExit = group.kind === 'next' || group.kind === 'down' || group.kind === 'quest'
|
||||
const distMap = bfsDistances(level)
|
||||
|
||||
// Phase 1: Try to replace an existing unfinished room in place, preferring
|
||||
// maximum BFS distance from entrance for exits/objectives and minimum for entrances.
|
||||
// Phase 1: Try to replace an existing unfinished room in place, scanning
|
||||
// room candidates against valid wall connections, LvlPrest definitions,
|
||||
// and room flags in standard D2 roster order.
|
||||
for (let offset = 0; offset < group.entries.length; offset += 1) {
|
||||
const entry = group.entries[(startCursor + offset) % group.entries.length]!
|
||||
const wanted = sideMaskFromToken(entry.plainSides)
|
||||
|
|
@ -1642,20 +1620,14 @@ function placeSpecialGroup(
|
|||
const special = findSpecial(pieces, group.kind, entry.sides)
|
||||
if (special === undefined) continue
|
||||
|
||||
const candidates = level.rooms.filter(r => {
|
||||
const chosen = level.rooms.find(r => {
|
||||
if (roomSideMask(r) !== wanted) return false
|
||||
if (!r.hasDs1) return true
|
||||
if (group.inPlaceOnly === true && (r.piece === special || r.piece?.kind === 'theme')) return true
|
||||
return false
|
||||
})
|
||||
|
||||
if (candidates.length > 0) {
|
||||
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]!
|
||||
if (chosen !== undefined) {
|
||||
chosen.piece = special
|
||||
chosen.variant = 0
|
||||
chosen.hasDs1 = true
|
||||
|
|
@ -1670,16 +1642,12 @@ function placeSpecialGroup(
|
|||
return
|
||||
}
|
||||
|
||||
// Phase 2: Grow a new dead-end room off the best available parent room.
|
||||
const parentCandidates = level.rooms.filter(
|
||||
r => !r.hasDs1 || r.piece?.kind === 'theme',
|
||||
)
|
||||
parentCandidates.sort((a, b) => {
|
||||
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
|
||||
})
|
||||
// Phase 2: Grow a new dead-end room off an available parent room in D2
|
||||
// roster order (plain rooms without DS1 scanned first, followed by theme rooms).
|
||||
const parentCandidates = [
|
||||
...level.rooms.filter(r => !r.hasDs1),
|
||||
...level.rooms.filter(r => r.hasDs1 && r.piece?.kind === 'theme'),
|
||||
]
|
||||
|
||||
for (const parent of parentCandidates) {
|
||||
for (let offset = 0; offset < group.entries.length; offset += 1) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue