diff --git a/src/editor/engine/lua/RobloxShim.js b/src/editor/engine/lua/RobloxShim.js index ae21a12..9b50bdb 100644 --- a/src/editor/engine/lua/RobloxShim.js +++ b/src/editor/engine/lua/RobloxShim.js @@ -274,23 +274,48 @@ function isProbablySignalName(prop) { || /(Changed|Added|Removed|Began|Ended|Clicked|Activated|Touched|Died|Loaded|Hover|Connect|Event|Signal|Reached)$/.test(prop); } -// Callable stub: функция которая ничего не делает + поля Connect/Wait/Fire, -// чтобы выглядеть и как метод (`obj:Method()`), и как сигнал (`sig:Connect()`), -// и как объект (`obj.Property`). -function makeCallableStub(name) { - // Используем function чтобы Proxy мог иметь apply trap - const fnTarget = function () { return fnTarget; }; - fnTarget.__stubName = name || 'stub'; - fnTarget.Connect = function () { return { Disconnect: () => {}, disconnect: () => {}, Connected: false }; }; - fnTarget.connect = fnTarget.Connect; - fnTarget.Wait = function () { return undefined; }; - fnTarget.wait = fnTarget.Wait; - fnTarget.Fire = function () { return undefined; }; - fnTarget.fire = fnTarget.Fire; - fnTarget.Disconnect = function () {}; - fnTarget.disconnect = fnTarget.Disconnect; - // Proxy чтобы любое доступ к unknown полю/индексу возвращал тот же stub - return new Proxy(fnTarget, { +// Универсальный object-stub: ведёт себя как сигнал, как Instance, как Tool/Folder. +// НЕ function — иначе wasmoon мапит в Lua-function и Lua-индексация `.field` +// падает с "attempt to index a function value". +function makeObjectStub(name) { + const target = { + __stubName: name || 'stub', + // Signal API + Connect() { return { Disconnect() {}, disconnect() {}, Connected: false }; }, + connect() { return this.Connect(); }, + Wait() { return undefined; }, + wait() { return undefined; }, + Fire() {}, + fire() {}, + Disconnect() {}, + disconnect() {}, + // Instance read-API + FindFirstChild() { return undefined; }, + FindFirstChildOfClass() { return undefined; }, + FindFirstAncestor() { return undefined; }, + FindFirstAncestorOfClass() { return undefined; }, + GetChildren() { return []; }, + GetDescendants() { return []; }, + IsA() { return false; }, + GetFullName() { return name || 'stub'; }, + Destroy() {}, + Clone() { return makeObjectStub(name); }, + GetAttribute() { return undefined; }, + SetAttribute() {}, + GetPropertyChangedSignal() { return makeObjectStub('Changed'); }, + // Tool/Animation/Sound — частые no-op методы + Activate() {}, Deactivate() {}, Equip() {}, Unequip() {}, + Play() {}, Stop() {}, Pause() {}, Resume() {}, + AdjustSpeed() {}, LoadAnimation() { return makeObjectStub('Animation'); }, + TakeDamage() {}, MoveTo() {}, + // Базовые поля + Parent: undefined, + Name: name || 'stub', + ClassName: 'Folder', + Children: [], + }; + target.WaitForChild = function (childName) { return makeObjectStub(childName); }; + return new Proxy(target, { get(t, prop) { if (Object.prototype.hasOwnProperty.call(t, prop) || prop in t) { return t[prop]; @@ -301,12 +326,11 @@ function makeCallableStub(name) { prop.startsWith('__') || prop.startsWith('Symbol')) { return undefined; } - const child = makeCallableStub(prop); + const child = makeObjectStub(prop); t[prop] = child; return child; }, set(t, prop, value) { t[prop] = value; return true; }, - apply() { return fnTarget; }, // obj() возвращает себя }); } @@ -354,10 +378,10 @@ function newInstance(className, name) { prop.startsWith('__') || prop.startsWith('Symbol')) { return undefined; } - // Callable-stub: можно вызвать как функцию, как сигнал (:Connect), - // как объект (.Property), как child Instance (.WaitForChild). Не падает - // на любом обращении со стороны импортированных скриптов. - const stub = makeCallableStub(prop); + // Object-stub: ведёт себя как сигнал (Connect), как Instance + // (WaitForChild, GetChildren), как Tool (Activate). НЕ function — + // иначе Lua упадёт с "attempt to index a function value". + const stub = makeObjectStub(prop); t[prop] = stub; return stub; },