feat(packer): 提取可交互 NPC 的地图定位并独立存储
将场景数据生成时对 objectType 的判定粒度细化。保留对普通怪物的忽略过滤,对于包含 baseIsMonsters 标识的命名 NPC 记录,将类型标识拆分为 'npc' 并单独放入 scene.json 的 npcs 数组中打包。
This commit is contained in:
parent
e3ca14c357
commit
11e0688930
|
|
@ -344,14 +344,19 @@ for (const row of objectsTable.rows) {
|
||||||
|
|
||||||
/** Object members grouped by token and mode, built once. */
|
/** Object members grouped by token and mode, built once. */
|
||||||
const objectMembers = new Map<string, Map<string, string[]>>()
|
const objectMembers = new Map<string, Map<string, string[]>>()
|
||||||
|
const monsterMembers = new Map<string, Map<string, string[]>>()
|
||||||
for (const name of allNames) {
|
for (const name of allNames) {
|
||||||
if (!name.toLowerCase().startsWith(OBJECT_PREFIX.toLowerCase())) continue
|
const isObj = name.toLowerCase().startsWith(OBJECT_PREFIX.toLowerCase())
|
||||||
const rest = name.slice(OBJECT_PREFIX.length).split('\\')
|
const isMon = name.toLowerCase().startsWith('data\\global\\monsters\\')
|
||||||
|
if (!isObj && !isMon) continue
|
||||||
|
const prefixLen = isObj ? OBJECT_PREFIX.length : 22 // 'data\\global\\monsters\\'.length
|
||||||
|
const rest = name.slice(prefixLen).split('\\')
|
||||||
if (rest.length < 3) continue
|
if (rest.length < 3) continue
|
||||||
const token = (rest[0] ?? '').toUpperCase()
|
const token = (rest[0] ?? '').toUpperCase()
|
||||||
const mode = (rest[1] ?? '').toLowerCase()
|
const mode = (rest[1] ?? '').toLowerCase()
|
||||||
if (!objectMembers.has(token)) objectMembers.set(token, new Map())
|
const map = isObj ? objectMembers : monsterMembers
|
||||||
const modes = objectMembers.get(token)!
|
if (!map.has(token)) map.set(token, new Map())
|
||||||
|
const modes = map.get(token)!
|
||||||
if (!modes.has(mode)) modes.set(mode, [])
|
if (!modes.has(mode)) modes.set(mode, [])
|
||||||
modes.get(mode)!.push(name)
|
modes.get(mode)!.push(name)
|
||||||
}
|
}
|
||||||
|
|
@ -378,8 +383,9 @@ function compRank(path: string): number {
|
||||||
* @param modeToken - animation mode token the engine places the object in (`NU`/`OP`/…).
|
* @param modeToken - animation mode token the engine places the object in (`NU`/`OP`/…).
|
||||||
* @returns the member name and how many candidates there were.
|
* @returns the member name and how many candidates there were.
|
||||||
*/
|
*/
|
||||||
function pickObjectMember(token: string, modeToken: string): { member: string; candidates: number } | null {
|
function pickObjectMember(token: string, modeToken: string, baseIsMonsters = false): { member: string; candidates: number } | null {
|
||||||
const dirs = objectMembers.get(token)
|
const map = baseIsMonsters ? monsterMembers : objectMembers
|
||||||
|
const dirs = map.get(token)
|
||||||
if (dirs === undefined) return null
|
if (dirs === undefined) return null
|
||||||
const all = [...dirs.values()].flat().filter(name => {
|
const all = [...dirs.values()].flat().filter(name => {
|
||||||
const lower = name.toLowerCase()
|
const lower = name.toLowerCase()
|
||||||
|
|
@ -641,6 +647,7 @@ async function bakeDs1Variant(
|
||||||
|
|
||||||
const objectPages = new PageBuilder(palette)
|
const objectPages = new PageBuilder(palette)
|
||||||
const objects: unknown[] = []
|
const objects: unknown[] = []
|
||||||
|
const npcs: unknown[] = []
|
||||||
const missingObjects: string[] = []
|
const missingObjects: string[] = []
|
||||||
let objectsWithArt = 0
|
let objectsWithArt = 0
|
||||||
const placementByMember = new Map<string, { placement: Placement; offsetX: number; offsetY: number }>()
|
const placementByMember = new Map<string, { placement: Placement; offsetX: number; offsetY: number }>()
|
||||||
|
|
@ -655,7 +662,7 @@ async function bakeDs1Variant(
|
||||||
}
|
}
|
||||||
if (resolved.kind === 'monster') continue
|
if (resolved.kind === 'monster') continue
|
||||||
const row = resolved.row
|
const row = resolved.row
|
||||||
const pick = resolved.token === '' ? null : pickObjectMember(resolved.token, resolved.mode)
|
const pick = resolved.token === '' ? null : pickObjectMember(resolved.token, resolved.mode, resolved.kind === 'npc')
|
||||||
const orthoX = (object.x - object.y) * ORTHO_SUB_TILE_WIDTH + scene.originX
|
const orthoX = (object.x - object.y) * ORTHO_SUB_TILE_WIDTH + scene.originX
|
||||||
const orthoY = (object.x + object.y) * ORTHO_SUB_TILE_HEIGHT + scene.originY
|
const orthoY = (object.x + object.y) * ORTHO_SUB_TILE_HEIGHT + scene.originY
|
||||||
|
|
||||||
|
|
@ -723,7 +730,8 @@ async function bakeDs1Variant(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
objects.push({
|
const list = resolved.kind === 'npc' ? npcs : objects
|
||||||
|
list.push({
|
||||||
id: object.id,
|
id: object.id,
|
||||||
type: object.type,
|
type: object.type,
|
||||||
name: row?.name ?? resolved.token,
|
name: row?.name ?? resolved.token,
|
||||||
|
|
@ -793,6 +801,7 @@ async function bakeDs1Variant(
|
||||||
walls: scene.walls.map(draw => [draw.frameIndex, draw.x, draw.y, draw.cellX, draw.cellY]),
|
walls: scene.walls.map(draw => [draw.frameIndex, draw.x, draw.y, draw.cellX, draw.cellY]),
|
||||||
roofs: scene.roofs.map(draw => [draw.frameIndex, draw.x, draw.y, draw.cellX, draw.cellY]),
|
roofs: scene.roofs.map(draw => [draw.frameIndex, draw.x, draw.y, draw.cellX, draw.cellY]),
|
||||||
objects,
|
objects,
|
||||||
|
npcs,
|
||||||
collision: { width: scene.gridWidth, height: scene.gridHeight, runs },
|
collision: { width: scene.gridWidth, height: scene.gridHeight, runs },
|
||||||
spawn: spawn === null ? null : [Math.round(spawn.x), Math.round(spawn.y)],
|
spawn: spawn === null ? null : [Math.round(spawn.x), Math.round(spawn.y)],
|
||||||
stats: {
|
stats: {
|
||||||
|
|
@ -804,6 +813,7 @@ async function bakeDs1Variant(
|
||||||
missingRefs: scene.missingRefs,
|
missingRefs: scene.missingRefs,
|
||||||
walkable: 1 - [...scene.blocked].reduce((sum, value) => sum + value, 0) / scene.blocked.length,
|
walkable: 1 - [...scene.blocked].reduce((sum, value) => sum + value, 0) / scene.blocked.length,
|
||||||
objects: objects.length,
|
objects: objects.length,
|
||||||
|
npcs: npcs.length,
|
||||||
objectsWithArt,
|
objectsWithArt,
|
||||||
objectsUnresolved: missingObjects,
|
objectsUnresolved: missingObjects,
|
||||||
objectsArtPending: (objects as any[]).filter(o => o.member !== null && o.frame === null).length,
|
objectsArtPending: (objects as any[]).filter(o => o.member !== null && o.frame === null).length,
|
||||||
|
|
@ -840,6 +850,7 @@ async function bakeDs1Variant(
|
||||||
cells: `${String(scene.cellsX)}x${String(scene.cellsY)}`,
|
cells: `${String(scene.cellsX)}x${String(scene.cellsY)}`,
|
||||||
frames: scene.frames.length,
|
frames: scene.frames.length,
|
||||||
objects: objects.length,
|
objects: objects.length,
|
||||||
|
npcs: npcs.length,
|
||||||
pages: pageFiles.length,
|
pages: pageFiles.length,
|
||||||
objectPages: objectFiles.length,
|
objectPages: objectFiles.length,
|
||||||
missingTiles: scene.missingTiles,
|
missingTiles: scene.missingTiles,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue