fix(maze-objects): 删掉 isOpenFloor 里恒真的 `|| true`

`hasFloor` 的内容检测写成 `(f.prop1 !== 0 || f.style !== 0 || f.sequence !== 0
|| true)`,末尾的 `|| true` 让整个条件恒为真:DS1 每个格子的每一层地板都有一条
记录,未绘制处是全零槽位,于是「没有地板美术的空格」也被判定为可放物件的地面。

删除 `|| true` 后,全零地板槽位不再算作地面;新增 6 个回归用例覆盖
painted / 全零 / 仅 style / 仅 sequence / hidden / 多层,以及「整关地板全为空槽
时不生成任何动态物件」。

TAG=agy
CONV=2a1934de-30ef-464e-b3f3-fcbcdbbc49f1
This commit is contained in:
troytt 2026-09-17 11:30:47 +00:00
parent 5b3a9db986
commit e817ace5ef
2 changed files with 60 additions and 2 deletions

View File

@ -234,8 +234,12 @@ export function isOpenFloor(cells: readonly (readonly Ds1Cell[])[], cx: number,
const cell = row[cx]
if (!cell) return false
// Must contain floor
const hasFloor = cell.floors.length > 0 && cell.floors.some(f => !f.hidden && (f.prop1 !== 0 || f.style !== 0 || f.sequence !== 0 || true))
// Must contain floor *art*. A `Ds1Floor` whose `prop1`, `style` and `sequence`
// are all zero is an empty slot, not a tile: DS1 stores one entry per floor
// layer for every cell, so a one-layer map has a zero-filled entry everywhere
// the level does not paint. The `|| true` that used to close this condition
// made the whole content test vacuous, so objects were placed on void cells.
const hasFloor = cell.floors.length > 0 && cell.floors.some(f => !f.hidden && (f.prop1 !== 0 || f.style !== 0 || f.sequence !== 0))
if (!hasFloor) return false
// Must not have a solid, visible wall

View File

@ -2,6 +2,7 @@ import { describe, expect, test } from 'vitest'
import type { Ds1, Ds1Cell, Ds1Object } from '../src/formats/ds1.ts'
import {
populateDungeonObjects,
isOpenFloor,
CANONICAL_SHRINES,
CHEST_IDS_BY_ACT,
RACK_IDS_BY_ACT,
@ -482,3 +483,56 @@ describe('generateMaze end-to-end dynamic object integration', () => {
expect(foundStatic).toBe(true)
})
})
/**
* Regression cover for the `|| true` that used to close `isOpenFloor`'s content
* test (issue #58 item 2). With it, every `Ds1Floor` entry counted as floor art,
* so objects were seeded on cells the level never paints.
*/
describe('isOpenFloor content check', () => {
/** One cell grid with the given floor entries and no walls. */
function grid(floors: Ds1Cell['floors']): Ds1Cell[][] {
return [[{ walls: [], floors, shadows: [], substitutions: [] }]]
}
const painted = { prop1: 1, sequence: 0, style: 0, unknown1: 0, unknown2: 0, hidden: false }
const empty = { prop1: 0, sequence: 0, style: 0, unknown1: 0, unknown2: 0, hidden: false }
test('a painted floor cell is open floor', () => {
expect(isOpenFloor(grid([painted]), 0, 0)).toBe(true)
})
test('a cell whose only floor entry is all-zero is not open floor', () => {
expect(isOpenFloor(grid([empty]), 0, 0)).toBe(false)
})
test('style or sequence alone still counts as art', () => {
expect(isOpenFloor(grid([{ ...empty, style: 3 }]), 0, 0)).toBe(true)
expect(isOpenFloor(grid([{ ...empty, sequence: 7 }]), 0, 0)).toBe(true)
})
test('a hidden painted floor does not rescue an otherwise empty cell', () => {
expect(isOpenFloor(grid([{ ...painted, hidden: true }]), 0, 0)).toBe(false)
})
test('a multi-layer cell is open floor when any layer carries art', () => {
expect(isOpenFloor(grid([empty, painted]), 0, 0)).toBe(true)
expect(isOpenFloor(grid([empty, empty]), 0, 0)).toBe(false)
})
test('objects are not seeded into a level whose floors are all empty slots', () => {
const { level, rooms } = createSyntheticDungeon()
for (const row of level.cells) {
for (const cell of row) {
;(cell.floors as Ds1Cell['floors'][number][]).splice(
0,
cell.floors.length,
{ prop1: 0, sequence: 0, style: 0, unknown1: 0, unknown2: 0, hidden: false },
)
}
}
const result = populateDungeonObjects(rooms, 0, 0, level, 'Act 1 - Cave', 2, { density: 1.0 }, new Rng(7))
expect(result.placedObjects.length).toBe(0)
})
})