fix(studio): оружие попадает по NPC (pickable+npcId) → авто-floater урона работает

Меши NPC ставились isPickable=false → raycast бластера/меча проходил сквозь
них, урон и авто-floater не срабатывали. Теперь меши NPC pickable + npcId в
metadata; damageByMesh находит NPC по metadata (быстро) или по rootMesh.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
min 2026-06-07 13:49:06 +03:00
parent 854074bfa2
commit e4fdd91b12

View File

@ -161,6 +161,20 @@ export class NpcManager {
r15Animator, r15Animator,
}; };
this.npcs.set(id, npc); this.npcs.set(id, npc);
// Пометить меши NPC для попаданий оружия (бластер/меч): pickable + npcId
// в metadata. Без pickable raycast оружия проходит сквозь NPC и урон/
// floater'ы не срабатывают (задача 40).
try {
const root = npc.data && npc.data.rootMesh;
if (root) {
root.isPickable = true;
root.metadata = Object.assign({}, root.metadata, { npcId: id });
for (const m of root.getChildMeshes(false)) {
m.isPickable = true;
m.metadata = Object.assign({}, m.metadata, { npcId: id });
}
}
} catch (e) { /* ignore */ }
return id; return id;
} }
@ -308,16 +322,23 @@ export class NpcManager {
* содержат hit-меш (или предка). Вызывает damage() авто-floater. */ * содержат hit-меш (или предка). Вызывает damage() авто-floater. */
damageByMesh(mesh, amount) { damageByMesh(mesh, amount) {
if (!mesh) return false; if (!mesh) return false;
// 1) Быстрый путь: npcId в metadata меша (или предка).
let m = mesh;
for (let i = 0; i < 8 && m; i++) {
const nid = m.metadata && m.metadata.npcId;
if (nid != null && this.npcs.has(nid)) { this.damage(nid, amount); return true; }
m = m.parent;
}
// 2) Fallback: сравнение с rootMesh по иерархии.
for (const npc of this.npcs.values()) { for (const npc of this.npcs.values()) {
if (npc.dead) continue; if (npc.dead) continue;
const root = npc.data && npc.data.rootMesh; const root = npc.data && npc.data.rootMesh;
if (!root) continue; if (!root) continue;
let m = mesh, hit = false; let mm = mesh;
for (let i = 0; i < 8 && m; i++) { for (let i = 0; i < 8 && mm; i++) {
if (m === root) { hit = true; break; } if (mm === root) { this.damage(npc.id, amount); return true; }
m = m.parent; mm = mm.parent;
} }
if (hit) { this.damage(npc.id, amount); return true; }
} }
return false; return false;
} }