diff --git a/src/community/docsGamesBuildersLua.js b/src/community/docsGamesBuildersLua.js index ce3cff1..789cb96 100644 --- a/src/community/docsGamesBuildersLua.js +++ b/src/community/docsGamesBuildersLua.js @@ -170,7 +170,7 @@ finishEvent.Event:Connect(function() won = true winSound:Play() __rbxl_show_text("Победа! Ты дошёл до финиша!", 5) - -- Конфетти над игроком (как JS game.scene.spawnParticles) + -- Конфетти над игроком (паритет с JS game.scene.spawnParticles) local pos = __rbxl_player_pos() __rbxl_spawn_particles("confetti", pos.x, pos.y + 3, pos.z, 3, 3) end)`, diff --git a/src/editor/engine/GameRuntime.js b/src/editor/engine/GameRuntime.js index 07a2904..3fd2fe2 100644 --- a/src/editor/engine/GameRuntime.js +++ b/src/editor/engine/GameRuntime.js @@ -326,10 +326,6 @@ export class GameRuntime { } } catch (_) {} } else { - if (cmd === 'scene.particles' || cmd === 'ui.showText') { - // eslint-disable-next-line no-console - console.warn('[Lua onCommand]', cmd, JSON.stringify(payload)); - } this._handleCommand(null, cmd, payload); } }); @@ -960,7 +956,20 @@ export class GameRuntime { this._syncPhysicsToScene(); } const state = this._collectState(); + // Реальная позиция игрока для Lua __rbxl_player_pos() + const player = this.scene3d?.player; + let realPos = null; + if (player?.body?.position) { + const p = player.body.position; + realPos = { x: p.x, y: p.y, z: p.z }; + } else if (state?.player) { + realPos = { x: state.player.x, y: state.player.y, z: state.player.z }; + } for (const sb of this.sandboxes) { + // Обновляем реальную позицию игрока для Lua-shim + if (realPos && sb.api?.updatePlayerPos) { + try { sb.api.updatePlayerPos(realPos.x, realPos.y, realPos.z); } catch (_) {} + } // Для скриптов с target — добавляем актуальную позицию self const stateForSb = sb.target ? { ...state, selfPosition: this._collectSelfPosition(sb.target) } @@ -4872,8 +4881,6 @@ export class GameRuntime { * Делегирует в BabylonScene._spawnParticleEffect, который знает о Babylon. */ _spawnParticles(payload) { - // eslint-disable-next-line no-console - console.warn('[_spawnParticles] called with:', JSON.stringify(payload), 'scene3d=', !!this.scene3d, 'fn=', !!this.scene3d?._spawnParticleEffect); if (!payload || !this.scene3d?._spawnParticleEffect) return; try { this.scene3d._spawnParticleEffect(payload); diff --git a/src/editor/engine/lua/RobloxShim.js b/src/editor/engine/lua/RobloxShim.js index 1e511d6..8dcd536 100644 --- a/src/editor/engine/lua/RobloxShim.js +++ b/src/editor/engine/lua/RobloxShim.js @@ -1827,10 +1827,11 @@ export function registerRobloxShim(lua, opts) { }); }); // Позиция игрока для удобства (для confetti над головой и т.п.) + // Берётся из api.updatePlayerPos который GameRuntime обновляет каждый кадр. global.set('__rbxl_player_pos', () => { try { - const p = hrp._position || { X: 0, Y: 0, Z: 0 }; - return { x: p.X, y: p.Y, z: p.Z }; + const p = api._realPlayerPos || hrp._position || { X: 0, Y: 0, Z: 0 }; + return { x: p.x ?? p.X, y: p.y ?? p.Y, z: p.z ?? p.Z }; } catch (_) { return { x: 0, y: 0, z: 0 }; } }); // Достаём ссылку на Lua-функцию один раз; вызовы безопасны (не doStringSync) @@ -1845,8 +1846,10 @@ export function registerRobloxShim(lua, opts) { // Для Этапа 3 сделаем proxy через явный метод. В Этапе 4 — введём // metatable на Lua-стороне (более чистый путь). - // Возвращаем api для main-loop - return { + // Возвращаем api для main-loop. api объявляется заранее, чтобы closures + // вроде __rbxl_player_pos и updatePlayerPos могли его видеть. + const api = { + _realPlayerPos: null, onSceneSnapshot(snap) { try { const prims = snap?.primitives || []; @@ -2076,7 +2079,13 @@ export function registerRobloxShim(lua, opts) { return allTools.find(t => t.Name === name); }, getAllTools() { return allTools.slice(); }, + // GameRuntime каждый кадр шлёт реальную позицию игрока сюда. + // __rbxl_player_pos() её возвращает Lua-скриптам. + updatePlayerPos(x, y, z) { + api._realPlayerPos = { x: +x, y: +y, z: +z }; + }, // Доступ к ключевым объектам (для тестов и отладки) partById, localPlayer, humanoid, character, workspace, players, game, }; + return api; }