feat: 50 игр на Lua + импорт Roblox для всех + поддержка Lua в плеере #39

Merged
min merged 215 commits from feat/lua-50-games-bundle into main 2026-06-09 21:59:25 +00:00
3 changed files with 34 additions and 24 deletions
Showing only changes of commit 9eebbd302e - Show all commits

View File

@ -1763,30 +1763,21 @@ for i, e in ipairs(enemies) do
end) end)
end end
-- Клик по сцене бьём ближайшего врага в радиусе 4 -- Клик по конкретному NPC (как в Тире pick по 3D-объекту).
UserInputService.InputBegan:Connect(function(input, gp) -- BabylonScene выполняет raycast при ЛКМ и шлёт click с target=NPC.
if gp then return end -- Регистрируем callback для каждого врага по его локальному ref.
if input.UserInputType ~= Enum.UserInputType.MouseButton1 then return end for _, e in ipairs(enemies) do
if won then return end local enemy = e -- замыкание
local px = __rbxl_player_x() __rbxl_npc_on_click(enemy.ref, function()
local pz = __rbxl_player_z() if enemy._dead or won then return end
for _, e in ipairs(enemies) do enemy.hp = enemy.hp - 30
if not e._dead and e.hp > 0 then if enemy.hp < 0 then enemy.hp = 0 end
local dx = e.x - px __rbxl_npc_damage(enemy.ref, 30)
local dz = e.z - pz __rbxl_set_label(enemy.ref, enemy.name .. " HP: " .. enemy.hp, "#ff5555", 3)
local dist = math.sqrt(dx*dx + dz*dz) __rbxl_spawn_particles("sparks", enemy.x, 2, enemy.z, 0.4, 1)
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() hitSound:Play()
break -- бьём только одного за клик end)
end end`,
end
end
end)`,
}, },
// ═══════════════════════════════════════════════════════════════ // ═══════════════════════════════════════════════════════════════

View File

@ -3035,6 +3035,7 @@ export class BabylonScene {
if (md.isBlock) { if (md.isBlock) {
return { kind: 'block', ref: { x: md.gridX, y: md.gridY, z: md.gridZ } }; 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.isModel) return { kind: 'model', id: md.instanceId };
if (md.isPrimitive) return { kind: 'primitive', id: md.primitiveId }; if (md.isPrimitive) return { kind: 'primitive', id: md.primitiveId };
return null; return null;

View File

@ -1895,6 +1895,10 @@ export function registerRobloxShim(lua, opts) {
global.set('__rbxl_npc_on_death', (ref, fn) => { global.set('__rbxl_npc_on_death', (ref, fn) => {
if (typeof fn === 'function') _npcDeathCbs.set(String(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). // Инвентарь invUI — паритет с JS game.inventory.add(itemId, count).
// Сначала определяем итем (один раз), потом добавляем. // Сначала определяем итем (один раз), потом добавляем.
const _localInventory = new Map(); const _localInventory = new Map();
@ -2302,6 +2306,20 @@ export function registerRobloxShim(lua, opts) {
try { equippedTool.Deactivated.Fire(); } catch (_) {} try { equippedTool.Deactivated.Fire(); } catch (_) {}
} }
// Mouse-события из плеера: клики, движение, клавиши при equipped Tool // 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' при ЛКМ — это эквивалент // BabylonScene шлёт глобальный 'click' при ЛКМ — это эквивалент
// mouseButton1Down. Мапим в наши handler-ы. // mouseButton1Down. Мапим в наши handler-ы.
if (p.type === 'click' || p.type === 'mouseButton1Down') { if (p.type === 'click' || p.type === 'mouseButton1Down') {