Implement inspect mode for shape metadata and enhance overlay interactions

This commit is contained in:
Marco 2026-03-27 13:30:49 +01:00
commit ab5e514e61
6 changed files with 325 additions and 38 deletions

View file

@ -180,6 +180,98 @@ function overlayNotes(item, info) {
return notes;
}
function classifyBaseKind(info) {
if (info.isRoof) {
return "roof";
}
if (info.isOccl || info.isInvitem) {
return "helper";
}
if (info.isLand) {
return "terrain";
}
return "base";
}
function inspectLabel(layer, kind) {
if (layer === "overlay") {
return overlayLabel(kind);
}
if (kind === "roof") {
return "Roof Shape";
}
if (kind === "helper") {
return "Occluding Helper";
}
if (kind === "terrain") {
return "Terrain Shape";
}
return "Map Shape";
}
function serializeInspectableItem(node, minLeft, minTop, id, layer, stackOrder) {
const { item, info, frame } = node;
const kind = layer === "overlay" ? classifyOverlayKind(item, info) : classifyBaseKind(info);
const sceneLeft = node.left - minLeft;
const sceneTop = node.top - minTop;
return {
id,
layer,
stackOrder,
kind,
label: inspectLabel(layer, kind),
family: info.family,
source: item.source,
world: {
x: item.x,
y: item.y,
z: item.z
},
mapNum: item.mapNum,
npcNum: item.npcNum,
nextItem: item.nextItem,
quality: item.quality,
shape: item.shape,
frame: item.frame,
frameSize: {
width: frame.width,
height: frame.height,
xoff: frame.xoff,
yoff: frame.yoff
},
screen: {
left: sceneLeft,
top: sceneTop,
right: node.right - minLeft,
bottom: node.bottom - minTop,
width: node.right - node.left,
height: node.bottom - node.top,
anchorX: Math.trunc(sceneLeft + (node.right - node.left) / 2),
anchorY: node.bottom - minTop
},
flags: {
raw: item.flags,
hex: toHex(item.flags),
invisible: Boolean(item.flags & FLAG_INVISIBLE),
flipped: Boolean(item.flags & FLAG_FLIPPED)
},
traits: {
editor: info.isEditor,
roof: info.isRoof,
occluding: info.isOccl,
translucent: info.isTranslucent,
solid: info.isSolid,
fixed: info.isFixed,
land: info.isLand,
draw: info.isDraw,
invitem: info.isInvitem,
animType: info.animType
},
notes: overlayNotes(item, info)
};
}
function serializeOverlayItem(node, minLeft, minTop, index) {
const { item, info, frame } = node;
const kind = classifyOverlayKind(item, info);
@ -417,6 +509,7 @@ export class BuildManager {
overlays: [],
overlayNodes: [],
overlayNodeById: new Map(),
inspectables: [],
minLeft: 0,
minTop: 0,
width: TILE_SIZE,
@ -471,6 +564,7 @@ export class BuildManager {
overlays: [],
overlayNodes: [],
overlayNodeById: new Map(),
inspectables: [],
minLeft: 0,
minTop: 0,
width: TILE_SIZE,
@ -516,6 +610,34 @@ export class BuildManager {
for (let index = 0; index < overlays.length; index += 1) {
overlayNodeById.set(overlays[index].id, overlayProjection.projected[index]);
}
const inspectables = [];
for (let index = 0; index < sorted.prepared.length; index += 1) {
const node = sorted.prepared[index];
const stackOrder = node.order >= 0 ? node.order : index;
inspectables.push(
serializeInspectableItem(
node,
bounds.minLeft,
bounds.minTop,
`base:${stackOrder}:${node.item.source}:${node.item.shape}:${node.item.frame}:${node.item.x}:${node.item.y}:${node.item.z}`,
"base",
stackOrder
)
);
}
for (let index = 0; index < overlays.length; index += 1) {
inspectables.push(
serializeInspectableItem(
overlayProjection.projected[index],
bounds.minLeft,
bounds.minTop,
`overlay:${overlays[index].id}`,
"overlay",
sorted.prepared.length + index
)
);
}
inspectables.sort((left, right) => left.stackOrder - right.stackOrder);
const invalidItemCount = sorted.invalidItemCount + overlayProjection.invalidItemCount;
const invalidItems = [...sorted.invalidItems, ...overlayProjection.invalidItems].slice(0, 20);
@ -566,6 +688,7 @@ export class BuildManager {
overlays,
overlayNodes: overlayProjection.projected,
overlayNodeById,
inspectables,
minLeft: bounds.minLeft,
minTop: bounds.minTop,
width: bounds.width,
@ -638,6 +761,13 @@ export class BuildManager {
};
}
getInspectData(jobId, gameId, mapId) {
const job = this.requireReadyJob(jobId, gameId, mapId);
return {
items: job.build.inspectables
};
}
async renderOverlaySprite(jobId, gameId, mapId, overlayId, format = "webp") {
const job = this.requireReadyJob(jobId, gameId, mapId);
const overlay = job.build.overlays.find((item) => item.id === overlayId);