diff --git a/src/community/docsGamesBuildersLua.js b/src/community/docsGamesBuildersLua.js index a42d6f6..6461412 100644 --- a/src/community/docsGamesBuildersLua.js +++ b/src/community/docsGamesBuildersLua.js @@ -1763,30 +1763,21 @@ for i, e in ipairs(enemies) do end) end --- Клик по сцене — бьём ближайшего врага в радиусе 4 -UserInputService.InputBegan:Connect(function(input, gp) - if gp then return end - if input.UserInputType ~= Enum.UserInputType.MouseButton1 then return end - if won then return end - local px = __rbxl_player_x() - local pz = __rbxl_player_z() - for _, e in ipairs(enemies) do - if not e._dead and e.hp > 0 then - local dx = e.x - px - local dz = e.z - pz - local dist = math.sqrt(dx*dx + dz*dz) - if dist < 4 then - e.hp = e.hp - 30 - if e.hp < 0 then e.hp = 0 end - __rbxl_npc_damage(e.ref, 30) - __rbxl_set_label(e.ref, e.name .. " HP: " .. e.hp, "#ff5555", 3) - __rbxl_spawn_particles("sparks", e.x, 2, e.z, 0.4, 1) - hitSound:Play() - break -- бьём только одного за клик - end - end - end -end)`, +-- Клик по конкретному NPC (как в Тире — pick по 3D-объекту). +-- BabylonScene выполняет raycast при ЛКМ и шлёт click с target=NPC. +-- Регистрируем callback для каждого врага по его локальному ref. +for _, e in ipairs(enemies) do + local enemy = e -- замыкание + __rbxl_npc_on_click(enemy.ref, function() + if enemy._dead or won then return end + enemy.hp = enemy.hp - 30 + if enemy.hp < 0 then enemy.hp = 0 end + __rbxl_npc_damage(enemy.ref, 30) + __rbxl_set_label(enemy.ref, enemy.name .. " HP: " .. enemy.hp, "#ff5555", 3) + __rbxl_spawn_particles("sparks", enemy.x, 2, enemy.z, 0.4, 1) + hitSound:Play() + end) +end`, }, // ═══════════════════════════════════════════════════════════════ diff --git a/src/editor/engine/BabylonScene.js b/src/editor/engine/BabylonScene.js index f16d81f..ec00bd8 100644 --- a/src/editor/engine/BabylonScene.js +++ b/src/editor/engine/BabylonScene.js @@ -3035,6 +3035,7 @@ export class BabylonScene { if (md.isBlock) { return { kind: 'block', ref: { x: md.gridX, y: md.gridY, z: md.gridZ } }; } + if (md.npcId != null) return { kind: 'npc', id: md.npcId }; if (md.isModel) return { kind: 'model', id: md.instanceId }; if (md.isPrimitive) return { kind: 'primitive', id: md.primitiveId }; return null; diff --git a/src/editor/engine/lua/RobloxShim.js b/src/editor/engine/lua/RobloxShim.js index 600b0ed..b10a99f 100644 --- a/src/editor/engine/lua/RobloxShim.js +++ b/src/editor/engine/lua/RobloxShim.js @@ -1895,6 +1895,10 @@ export function registerRobloxShim(lua, opts) { global.set('__rbxl_npc_on_death', (ref, fn) => { if (typeof fn === 'function') _npcDeathCbs.set(String(ref || ''), fn); }); + const _npcClickCbs = new Map(); // localRef → fn + global.set('__rbxl_npc_on_click', (ref, fn) => { + if (typeof fn === 'function') _npcClickCbs.set(String(ref || ''), fn); + }); // Инвентарь invUI — паритет с JS game.inventory.add(itemId, count). // Сначала определяем итем (один раз), потом добавляем. const _localInventory = new Map(); @@ -2302,6 +2306,20 @@ export function registerRobloxShim(lua, opts) { try { equippedTool.Deactivated.Fire(); } catch (_) {} } // Mouse-события из плеера: клики, движение, клавиши при equipped Tool + // BabylonScene шлёт глобальный 'click' при ЛКМ. Если в payload + // target — это попадание по 3D-объекту. Для NPC фейерим cb. + if (p.type === 'click' && p.target && p.target.kind === 'npc' && p.target.id != null) { + const realRef = 'npc:' + p.target.id; + let localRef = null; + for (const [k, v] of _localToRealNpc.entries()) { + if (v === realRef) { localRef = k; break; } + } + for (const [ref, fn] of _npcClickCbs.entries()) { + if (ref === realRef || ref === localRef) { + _pendingHandlerQueue.push({ fn, args: [] }); + } + } + } // BabylonScene шлёт глобальный 'click' при ЛКМ — это эквивалент // mouseButton1Down. Мапим в наши handler-ы. if (p.type === 'click' || p.type === 'mouseButton1Down') {