Merge branch 'fix/issue-46'
This commit is contained in:
commit
c310119ed2
|
|
@ -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) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue