@luminocity/lemonate-engine 26.3.16 → 26.3.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -41290,7 +41290,7 @@ class Item extends EventEmitter {
41290
41290
  }
41291
41291
  }
41292
41292
 
41293
- var engine$1 = "26.3.16";
41293
+ var engine$1 = "26.3.17";
41294
41294
  var bullet = "3.26";
41295
41295
  var lua = "5.4.3";
41296
41296
  var packageVersionInfo = {
@@ -131483,9 +131483,9 @@ var lua_api_internal = "\nlocal msgpack = require('engine/msgpack');\nlocal pack
131483
131483
 
131484
131484
  var lua_api_audio = "\nlocal _internal = require('engine/_internal');\n\n--- @module audio\nlocal audio = {}\n\n---Get an FFT analysis from the audio engine. It returns an array of 256 samples\n---@function getAnalysis\nfunction audio.getAnalysis()\n return _internal.sendMessage('audio.getAnalysis')\nend\n\nreturn audio\n";
131485
131485
 
131486
- var lua_api_behaviour = "local SceneObject = require('engine/sceneobject')\nlocal JE = require('engine/json/jsonencoder')()\n\nlocal _behavioursMapByNodeId = {}\nlocal _behavioursMapByScriptId = {}\n\nlocal Behaviour = Class.new()\n\n-- Put the class in the global scope (Behaviour is the old deprecated name)\n_G.Behaviour = Behaviour\n_G.Entity = Behaviour\n\n--- Constructor hook called when a new Behaviour is created.\n---@private\nfunction Behaviour:__new()\n self._isBehaviour = true\n self._type = \"Behaviour\"\n self._nodeId = nil\n self._scriptName = \"\"\n self._isActive = true\n self._isInitialized = false\n\n -- Adds a default node property\n self:addProperty(\"node\", Property.Node)\nend\n\n--- Destructor hook called before the Behaviour is destroyed.\n---@private\nfunction Behaviour:__destroy()\n print(\"__destroy called for: \", self._nodeId)\nend\n\n--- Get all behaviours attached to a specific node ID.\n---@param nodeId string The ID of the scene graph node\n---@return table A table containing all behaviours attached to the node\nfunction Behaviour.getByNodeId(nodeId)\n local result = {}\n local nodeBehaviours = _behavioursMapByNodeId[nodeId]\n if nodeBehaviours then\n for k,v in pairs(nodeBehaviours) do\n table.insert(result, v.behaviour)\n end\n end\n return result\nend\n\n--- Get a single behaviour attached to a node ID with a specific script name.\n---@param nodeId string The ID of the scene graph node\n---@param scriptName string The script name of the behaviour\n---@return Behaviour|nil The behaviour if found, or nil\nfunction Behaviour.getByNodeIdAndScriptName(nodeId, scriptName)\n local nodeEntities = _behavioursMapByNodeId[nodeId]\n if nodeEntities then\n for k,v in pairs(nodeEntities) do\n if v.scriptName == scriptName then\n return v.behaviour\n end\n end\n end\n return nil\nend\n\n--- Returns the node ID this Behaviour is attached to.\n---@return string|nil Node ID of the attached scene object\nfunction Behaviour:getNodeId()\n return self._nodeId\nend\n\n--- Returns the script name of this Behaviour.\n---@return string Script name\nfunction Behaviour:getScriptName()\n return self._scriptName\nend\n\n--- Returns a table of all properties defined on the behaviour class.\n---@return table A table with fields: name, defaultValue, type, order\nfunction Behaviour:getProperties()\n local props = {}\n local class = self.getClass()\n\n for k,v in pairs(class) do\n if type(v) == 'table' and v._isProperty then\n table.insert(props, { name=k, defaultValue=v.value, type=v.datatype, order=v.order, info=v.info })\n end\n end\n\n for k,v in pairs(class.properties) do\n if type(v) == 'table' and v._isProperty then\n table.insert(props, { name=k, defaultValue=v.value, type=v.datatype, order=v.order, info=v.info })\n end\n end\n\n return props\nend\n\n--- When a script is destroyed, we need to call this to clear the behaviour out of the list\n---@private\nfunction Behaviour:_disposeBehaviour()\n local scripts = _behavioursMapByNodeId[self._nodeId]\n if scripts then\n scripts[self._scriptId] = nil\n end\n\n _behavioursMapByScriptId[self._scriptId] = nil\nend\n\n--- Internal setter for a property\n---@private\n---@param name string Property name\n---@param value any Value to set\nfunction Behaviour:_setProperty(name, value)\n self[name] = value\nend\n\n--- Internal method to attach a scene node to this Behaviour\n---@private\n---@param msg table Contains fields: value=nodeId, scriptName, scriptId\nfunction Behaviour:_setNode(msg)\n __assertTrace(type(msg) == 'table', \"_setNode() called with nil or a non-table.\")\n\n if self.node then\n error(\"Node is already set on behaviour!\")\n end\n\n local nodeId = msg.value\n __assertTrace(type(nodeId) == 'string', \"_setNode() needs a nodeId of type string.\")\n\n local node = SceneObject.findById(nodeId)\n __assertTrace(node, \"_setNode() node not found\")\n\n local scriptName = msg.scriptName\n __assertTrace(scriptName, \"_setNode() needs a scriptName\")\n\n local scriptId = msg.scriptId\n __assertTrace(scriptId, \"_setNode() needs a scriptId\")\n\n self.node = node\n self._nodeId = nodeId\n self._scriptName = msg.scriptName\n self._scriptId = scriptId\n\n _behavioursMapByScriptId[scriptId] = self\n\n if not _behavioursMapByNodeId[nodeId] then\n _behavioursMapByNodeId[nodeId] = {}\n end\n _behavioursMapByNodeId[nodeId][scriptId] = {\n node = node,\n scriptName = scriptName,\n behaviour = self\n }\nend\n\n--- Return the properties of this Behaviour as JSON\n---@private\n---@return string JSON string of properties\nfunction Behaviour:_getPropsJson()\n return JE(self:getProperties())\nend\n\n--- Returns the active state of the behaviour. If the owner item is inactive, so are all attached behaviours\n---@return boolean true, if active, othersise false\nfunction Behaviour:isActive()\n return self._isActive\nend\n\n--- Returns the initialized state of the behaviour. If true, init ran through or there is no init\n---@return boolean true, if initialized, othersise false\nfunction Behaviour:isInitialized()\n return self._isInitialized\nend\n\n--- Clears all registered behaviours (used internally). They are expected to be empty anyway due to scripts\n--- cleaning up behind themselves. This function will assert this\n---@private\nfunction Behaviour._clearBehaviours()\n -- Clear in-place to avoid any stale references to the _behaviours table object\n -- preventing Lua GC from reclaiming behaviour instances.\n for nodeId, nodeBehaviours in pairs(_behavioursMapByNodeId) do\n -- nodeBehaviours is a keyed table (scriptId -> { ... })\n if nodeBehaviours and next(nodeBehaviours) ~= nil then\n -- This warning is useful when scripts forget to dispose their behaviours,\n -- but the check previously used `#nodeBehaviours` which is incorrect for keyed tables.\n print(\"WARNING: Some behaviours did not get cleaned properly. This is an engine bug!\")\n -- Keep clearing anyway to enable GC.\n end\n\n if nodeBehaviours then\n for scriptId in pairs(nodeBehaviours) do\n nodeBehaviours[scriptId] = nil\n end\n end\n\n _behavioursMapByNodeId[nodeId] = nil\n end\n\n -- Also clear the second map that has the behaviours by their script ID directly\n _behavioursMapByScriptId = {}\nend\n\nfunction Behaviour:_init()\n if type(self.init) == 'function' then\n local ok, err = pcall(self.init, self)\n if not ok then\n print(self._scriptName .. \":init() failed:\", err)\n end\n end\n self._isInitialized = true\n self._isActive = true\nend\n\nfunction Behaviour:_ondisable()\n self._isActive = false\n if type(self.ondisable) == 'function' then\n local ok, err = pcall(self.ondisable, self)\n if not ok then\n print(self._scriptName .. \":ondisable() failed:\", err)\n end\n end\nend\n\nfunction Behaviour:_onenable()\n self._isActive = true\n if type(self.onenable) == 'function' then\n local ok, err = pcall(self.onenable, self)\n if not ok then\n print(self._scriptName .. \":onenable() failed:\", err)\n end\n end\nend\n\nfunction Behaviour:_ondestroy()\n if type(self.ondestroy) == 'function' then\n local ok, err = pcall(self.ondestroy, self)\n if not ok then\n print(self._scriptName .. \":ondestroy() failed:\", err)\n end\n end\n self._isActive = false\n self._isInitialized = false\nend\n\nfunction Behaviour._updateAll(deltaTime, deltaTimeSmooth)\n local callcount = 0\n\n for _, behaviour in pairs(_behavioursMapByScriptId) do\n if behaviour._isActive and behaviour._isInitialized and type(behaviour.update) == 'function' then\n callcount = callcount + 1\n local ok, err = pcall(behaviour.update, behaviour, deltaTime, deltaTimeSmooth)\n if not ok then\n print(behaviour._scriptName .. \":update() failed:\", err)\n end\n end\n\n -- call the deprecated prerender hook if it exists\n if behaviour._isActive and behaviour._isInitialized and type(behaviour.prerender) == 'function' then\n callcount = callcount + 1\n local ok, err = pcall(behaviour.prerender, behaviour)\n if not ok then\n print(behaviour._scriptName .. \":prerender() failed:\", err)\n end\n end\n end\n\n return callcount\nend\n\nfunction Behaviour._renderAll()\n local callcount = 0\n\n for _, behaviour in pairs(_behavioursMapByScriptId) do\n if behaviour._isActive and behaviour._isInitialized and type(behaviour.render) == 'function' then\n callcount = callcount + 1\n local ok, err = pcall(behaviour.render, behaviour, deltaTime, deltaTimeSmooth)\n if not ok then\n print(behaviour._scriptName .. \":render() failed:\", err)\n end\n end\n end\n\n return callcount\nend\n\nfunction Behaviour._renderguiAll()\n local callcount = 0\n\n for _, behaviour in pairs(_behavioursMapByScriptId) do\n if behaviour._isActive and behaviour._isInitialized and type(behaviour.rendergui) == 'function' then\n callcount = callcount + 1\n local ok, err = pcall(behaviour.rendergui, behaviour, deltaTime, deltaTimeSmooth)\n if not ok then\n print(behaviour._scriptName .. \":rendergui() failed:\", err)\n end\n end\n end\n\n return callcount\nend\n\nfunction Behaviour._setActive(nodes)\n for scriptId, active in pairs(nodes) do\n local node = _behavioursMapByScriptId[scriptId]\n if node then\n node._isActive = active\n else\n print(\"ERROR: Behaviour._setActive() cannot find script: \" .. scriptId)\n end\n end\nend\n\nfunction Behaviour._debugStats()\n local nodeCount = 0\n local behaviourCount = 0\n for _, nodeBehaviours in pairs(_behavioursMapByNodeId) do\n nodeCount = nodeCount + 1\n if nodeBehaviours then\n for _ in pairs(nodeBehaviours) do\n behaviourCount = behaviourCount + 1\n end\n end\n end\n\n return {\n nodeCount = nodeCount,\n behaviourCount = behaviourCount\n }\nend\n\nreturn Behaviour\n";
131486
+ var lua_api_behaviour = "local SceneObject = require('engine/sceneobject')\nlocal Canvas = require('engine/canvas')\nlocal JE = require('engine/json/jsonencoder')()\n\nlocal _behavioursMapByNodeId = {}\nlocal _behavioursMapByScriptId = {}\n\nlocal Behaviour = Class.new()\n\n-- Put the class in the global scope (Behaviour is the old deprecated name)\n_G.Behaviour = Behaviour\n_G.Entity = Behaviour\n\n--- Constructor hook called when a new Behaviour is created.\n---@private\nfunction Behaviour:__new()\n self._isBehaviour = true\n self._type = \"Behaviour\"\n self._nodeId = nil\n self._scriptName = \"\"\n self._isActive = true\n self._isInitialized = false\n\n -- Adds a default node property\n self:addProperty(\"node\", Property.Node)\nend\n\n--- Destructor hook called before the Behaviour is destroyed.\n---@private\nfunction Behaviour:__destroy()\n print(\"__destroy called for: \", self._nodeId)\nend\n\n--- Get all behaviours attached to a specific node ID.\n---@param nodeId string The ID of the scene graph node\n---@return table A table containing all behaviours attached to the node\nfunction Behaviour.getByNodeId(nodeId)\n local result = {}\n local nodeBehaviours = _behavioursMapByNodeId[nodeId]\n if nodeBehaviours then\n for k,v in pairs(nodeBehaviours) do\n table.insert(result, v.behaviour)\n end\n end\n return result\nend\n\n--- Get a single behaviour attached to a node ID with a specific script name.\n---@param nodeId string The ID of the scene graph node\n---@param scriptName string The script name of the behaviour\n---@return Behaviour|nil The behaviour if found, or nil\nfunction Behaviour.getByNodeIdAndScriptName(nodeId, scriptName)\n local nodeEntities = _behavioursMapByNodeId[nodeId]\n if nodeEntities then\n for k,v in pairs(nodeEntities) do\n if v.scriptName == scriptName then\n return v.behaviour\n end\n end\n end\n return nil\nend\n\n--- Returns the node ID this Behaviour is attached to.\n---@return string|nil Node ID of the attached scene object\nfunction Behaviour:getNodeId()\n return self._nodeId\nend\n\n--- Returns the script name of this Behaviour.\n---@return string Script name\nfunction Behaviour:getScriptName()\n return self._scriptName\nend\n\n--- Returns a table of all properties defined on the behaviour class.\n---@return table A table with fields: name, defaultValue, type, order\nfunction Behaviour:getProperties()\n local props = {}\n local class = self.getClass()\n\n for k,v in pairs(class) do\n if type(v) == 'table' and v._isProperty then\n table.insert(props, { name=k, defaultValue=v.value, type=v.datatype, order=v.order, info=v.info })\n end\n end\n\n for k,v in pairs(class.properties) do\n if type(v) == 'table' and v._isProperty then\n table.insert(props, { name=k, defaultValue=v.value, type=v.datatype, order=v.order, info=v.info })\n end\n end\n\n return props\nend\n\n--- When a script is destroyed, we need to call this to clear the behaviour out of the list\n---@private\nfunction Behaviour:_disposeBehaviour()\n local scripts = _behavioursMapByNodeId[self._nodeId]\n if scripts then\n scripts[self._scriptId] = nil\n end\n\n _behavioursMapByScriptId[self._scriptId] = nil\nend\n\n--- Internal setter for a property\n---@private\n---@param name string Property name\n---@param value any Value to set\nfunction Behaviour:_setProperty(name, value)\n self[name] = value\nend\n\n--- Internal method to attach a scene node to this Behaviour\n---@private\n---@param msg table Contains fields: value=nodeId, scriptName, scriptId\nfunction Behaviour:_setNode(msg)\n __assertTrace(type(msg) == 'table', \"_setNode() called with nil or a non-table.\")\n\n if self.node then\n error(\"Node is already set on behaviour!\")\n end\n\n local nodeId = msg.value\n __assertTrace(type(nodeId) == 'string', \"_setNode() needs a nodeId of type string.\")\n\n local node = SceneObject.findById(nodeId)\n __assertTrace(node, \"_setNode() node not found\")\n\n local scriptName = msg.scriptName\n __assertTrace(scriptName, \"_setNode() needs a scriptName\")\n\n local scriptId = msg.scriptId\n __assertTrace(scriptId, \"_setNode() needs a scriptId\")\n\n self.node = node\n self._nodeId = nodeId\n self._scriptName = msg.scriptName\n self._scriptId = scriptId\n\n _behavioursMapByScriptId[scriptId] = self\n\n if not _behavioursMapByNodeId[nodeId] then\n _behavioursMapByNodeId[nodeId] = {}\n end\n _behavioursMapByNodeId[nodeId][scriptId] = {\n node = node,\n scriptName = scriptName,\n behaviour = self\n }\nend\n\n--- Return the properties of this Behaviour as JSON\n---@private\n---@return string JSON string of properties\nfunction Behaviour:_getPropsJson()\n return JE(self:getProperties())\nend\n\n--- Returns the active state of the behaviour. If the owner item is inactive, so are all attached behaviours\n---@return boolean true, if active, othersise false\nfunction Behaviour:isActive()\n return self._isActive\nend\n\n--- Returns the initialized state of the behaviour. If true, init ran through or there is no init\n---@return boolean true, if initialized, othersise false\nfunction Behaviour:isInitialized()\n return self._isInitialized\nend\n\n--- Clears all registered behaviours (used internally). They are expected to be empty anyway due to scripts\n--- cleaning up behind themselves. This function will assert this\n---@private\nfunction Behaviour._clearBehaviours()\n -- Clear in-place to avoid any stale references to the _behaviours table object\n -- preventing Lua GC from reclaiming behaviour instances.\n for nodeId, nodeBehaviours in pairs(_behavioursMapByNodeId) do\n -- nodeBehaviours is a keyed table (scriptId -> { ... })\n if nodeBehaviours and next(nodeBehaviours) ~= nil then\n -- This warning is useful when scripts forget to dispose their behaviours,\n -- but the check previously used `#nodeBehaviours` which is incorrect for keyed tables.\n print(\"WARNING: Some behaviours did not get cleaned properly. This is an engine bug!\")\n -- Keep clearing anyway to enable GC.\n end\n\n if nodeBehaviours then\n for scriptId in pairs(nodeBehaviours) do\n nodeBehaviours[scriptId] = nil\n end\n end\n\n _behavioursMapByNodeId[nodeId] = nil\n end\n\n -- Also clear the second map that has the behaviours by their script ID directly\n _behavioursMapByScriptId = {}\nend\n\nfunction Behaviour:_init()\n if type(self.init) == 'function' then\n local ok, err = pcall(self.init, self)\n if not ok then\n print(self._scriptName .. \":init() failed:\", err)\n end\n end\n self._isInitialized = true\n self._isActive = true\nend\n\nfunction Behaviour:_ondisable()\n self._isActive = false\n if type(self.ondisable) == 'function' then\n local ok, err = pcall(self.ondisable, self)\n if not ok then\n print(self._scriptName .. \":ondisable() failed:\", err)\n end\n end\nend\n\nfunction Behaviour:_onenable()\n self._isActive = true\n if type(self.onenable) == 'function' then\n local ok, err = pcall(self.onenable, self)\n if not ok then\n print(self._scriptName .. \":onenable() failed:\", err)\n end\n end\nend\n\nfunction Behaviour:_ondestroy()\n if type(self.ondestroy) == 'function' then\n local ok, err = pcall(self.ondestroy, self)\n if not ok then\n print(self._scriptName .. \":ondestroy() failed:\", err)\n end\n end\n self._isActive = false\n self._isInitialized = false\nend\n\nfunction Behaviour._updateAll(deltaTime, deltaTimeSmooth)\n local callcount = 0\n\n for _, behaviour in pairs(_behavioursMapByScriptId) do\n if behaviour._isActive and behaviour._isInitialized and type(behaviour.update) == 'function' then\n callcount = callcount + 1\n local ok, err = pcall(behaviour.update, behaviour, deltaTime, deltaTimeSmooth)\n if not ok then\n print(behaviour._scriptName .. \":update() failed:\", err)\n end\n end\n\n -- call the deprecated prerender hook if it exists\n if behaviour._isActive and behaviour._isInitialized and type(behaviour.prerender) == 'function' then\n callcount = callcount + 1\n local ok, err = pcall(behaviour.prerender, behaviour)\n if not ok then\n print(behaviour._scriptName .. \":prerender() failed:\", err)\n end\n end\n end\n\n return callcount\nend\n\nfunction Behaviour._renderAll()\n local callcount = 0\n\n for _, behaviour in pairs(_behavioursMapByScriptId) do\n if behaviour._isActive and behaviour._isInitialized and type(behaviour.render) == 'function' then\n callcount = callcount + 1\n local ok, err = pcall(behaviour.render, behaviour, deltaTime, deltaTimeSmooth)\n if not ok then\n print(behaviour._scriptName .. \":render() failed:\", err)\n end\n end\n end\n\n Canvas.commit()\n\n return callcount\nend\n\nfunction Behaviour._renderguiAll()\n local callcount = 0\n\n for _, behaviour in pairs(_behavioursMapByScriptId) do\n if behaviour._isActive and behaviour._isInitialized and type(behaviour.rendergui) == 'function' then\n callcount = callcount + 1\n local ok, err = pcall(behaviour.rendergui, behaviour, deltaTime, deltaTimeSmooth)\n if not ok then\n print(behaviour._scriptName .. \":rendergui() failed:\", err)\n end\n end\n end\n\n return callcount\nend\n\nfunction Behaviour._setActive(nodes)\n for scriptId, active in pairs(nodes) do\n local node = _behavioursMapByScriptId[scriptId]\n if node then\n node._isActive = active\n else\n print(\"ERROR: Behaviour._setActive() cannot find script: \" .. scriptId)\n end\n end\nend\n\nfunction Behaviour._debugStats()\n local nodeCount = 0\n local behaviourCount = 0\n for _, nodeBehaviours in pairs(_behavioursMapByNodeId) do\n nodeCount = nodeCount + 1\n if nodeBehaviours then\n for _ in pairs(nodeBehaviours) do\n behaviourCount = behaviourCount + 1\n end\n end\n end\n\n return {\n nodeCount = nodeCount,\n behaviourCount = behaviourCount\n }\nend\n\nreturn Behaviour\n";
131487
131487
 
131488
- var lua_api_canvas = "\nlocal _internal = require('engine/_internal');\n\n---@module canvas\nlocal canvas = {}\n\n---Clear the canvas.\n---@function\nfunction canvas.clear()\n _internal.sendMessage('canvas.clear')\nend\n\n---Returns the width of the canvas in pixels.\n---@function\nfunction canvas.getWidth()\n return _internal.sendMessage('canvas.getWidth')\nend\n\n---Returns the height of the canvas in pixels.\n---@function\nfunction canvas.getHeight()\n return _internal.sendMessage('canvas.getHeight')\nend\n\n---Sets the width of lines in the canvas.\n---@param w number the width to set the lines to.\nfunction canvas.setLineWidth(w)\n _internal.sendMessage('canvas.setLineWidth', { w=w })\nend\n\n---Sets the type of endings applied to the ends of a line.\n---@param cap string the type of corner to create on a line. One of \"butt\", \"round\", \"square\".\nfunction canvas.setLineCap(cap)\n _internal.sendMessage('canvas.setLineCap', { cap=cap })\nend\n\n---Sets the type of corner created when two lines meet.\n---@param join string the type of connection to create between two lines. One of \"bevel\", \"round\", or \"miter\".\nfunction canvas.setLineJoin(join)\n _internal.sendMessage('canvas.setLineJoin', { join=join })\nend\n\n---Sets the maximum miter length. Miter length is the distance between the inner corner and the outer corner of the join.\n---@param limit number the maximum miter length. When the miter length exceeds this value, the corner is trimmed.\nfunction canvas.setMiterLimit(limit)\n _internal.sendMessage('canvas.setMiterLimit', { limit=limit })\nend\n\n---Sets the color of the stroke (outline) for shapes drawn on the canvas.\n---@param r number Red channel value (0–255)\n---@param g number Green channel value (0–255)\n---@param b number Blue channel value (0–255)\n---@param a number Alpha channel value (0–1)\nfunction canvas.setStrokeColor(r, g, b, a)\n _internal.sendMessage('canvas.setStrokeColor', { r=r, g=g, b=b, a=a })\nend\n\n---Sets the color that is used to fill in shapes and text when they are drawn on the canvas.\n---@param r number Red channel value (0–255)\n---@param g number Green channel value (0–255)\n---@param b number Blue channel value (0–255)\n---@param a number Alpha channel value (0–1)\nfunction canvas.setFillColor(r, g, b, a)\n _internal.sendMessage('canvas.setFillColor', { r=r, g=g, b=b, a=a })\nend\n\n---Sets the color to use for shadows.\n---@param r number Red channel value (0–255)\n---@param g number Green channel value (0–255)\n---@param b number Blue channel value (0–255)\n---@param a number Alpha channel value (0–1)\nfunction canvas.setShadowColor(r, g, b, a)\n _internal.sendMessage('canvas.setShadowColor', { r=r, g=g, b=b, a=a })\nend\n\n---Sets the blur level for shadows.\n---@param level number the level of blur to be applied to shadows (in pixels).\nfunction canvas.setShadowBlurLevel(level)\n _internal.sendMessage('canvas.setShadowBlurLevel', { level=level })\nend\n\n---Sets the blur level for shadows.\n---@param x number the vertical offset of the shadow in pixels.\n---@param y number the horizontal offset of the shadow in pixels.\nfunction canvas.setShadowOffset(x, y)\n _internal.sendMessage('canvas.setShadowOffset', { x=x, y=y })\nend\n\n---Begins a new path.\n---@function\nfunction canvas.beginPath()\n _internal.sendMessage('canvas.beginPath')\nend\n\n---Close the current path by connecting the last and first point in the path, creating a loop.\n---@function\nfunction canvas.closePath()\n _internal.sendMessage('canvas.closePath')\nend\n\n---Determines if the specified point is in the current path.\n---@param x number x-coordinate of the point to check.\n---@param y number y-coordinate of the point to check.\n---@return boolean indicating if the point is in the path or not.\nfunction canvas.isPointInPath(x, y)\n return _internal.sendMessage('canvas.isPointInPath', { x=x, y=y })\nend\n\n---Sets the clipping path to the current drawing path.\n---@function\nfunction canvas.clip()\n _internal.sendMessage('canvas.clip')\nend\n\n---Draws the outline of a shape or path using the current stroke style, line width and styles.\n---@function\nfunction canvas.stroke()\n _internal.sendMessage('canvas.stroke')\nend\n\n---Fills a shape or path with the current fill style.\n---@function\nfunction canvas.fill()\n _internal.sendMessage('canvas.fill')\nend\n\n---Moves the current drawing position to the specified coordinates.\n---@param x number x-coordinate of the new current drawing position.\n---@param y number y-coordinate of the new current drawing position.\nfunction canvas.moveTo(x, y)\n _internal.sendMessage('canvas.moveTo', { x=x, y=y })\nend\n\n---Draws a straight line from the current position to the specified coordinates.\n---@param x number the x-coordinate to draw the line to.\n---@param y number the y-coordinate to draw the line to.\nfunction canvas.lineTo(x, y)\n _internal.sendMessage('canvas.lineTo', { x=x, y=y })\nend\n\n---Sets the type of compositing operation to apply when drawing new shapes.\n---@param operation string the compositing operation to use, possible values are:\n---\"source-over\", \"source-in\", \"source-out\", \"source-atop\", \"destination-over\",\n---\"destination-in\", \"destination-out\", \"destination-atop\", \"lighter\", \"copy\", \"xor\".\nfunction canvas.setCompositeOperation(operation)\n _internal.sendMessage('canvas.setCompositeOperation', { operation=operation })\nend\n\n---Sets the transparency value that is applied to all rendering operations.\n---@param value number a value between 0.0 (fully transparent) and 1.0 (fully opaque).\nfunction canvas.setGlobalAlpha(value)\n _internal.sendMessage('canvas.setGlobalAlpha', { value=value })\nend\n\n---Creates a rectangle path which is only drawn after calling stroke() or fill().\n---@param x number x-coordinate of the rectangle upper-left corner.\n---@param y number y-coordinate of the rectangle upper-left corner.\n---@param w number width of the rectangle.\n---@param h number height of the rectangle.\nfunction canvas.pathRect(x, y, w, h)\n _internal.sendMessage('canvas.pathRect', { x=x, y=y, w=w, h=h })\nend\n\n---Adds a rounded rectangle to the current path. Must be followed by stroke() or fill().\n---@param x number x-coordinate of the rectangle upper-left corner.\n---@param y number y-coordinate of the rectangle upper-left corner.\n---@param w number width of the rectangle.\n---@param h number height of the rectangle.\n---@param radius number|table corner radius applied to all four corners (uniform), or a table\n---of 1–4 values to set individual corner radius in the order [top-left, top-right,\n---bottom-right, bottom-left], following the HTML Canvas API specification.\nfunction canvas.roundRect(x, y, w, h, radius)\n _internal.sendMessage('canvas.roundRect', { x=x, y=y, w=w, h=h, radius=radius })\nend\n\n---Draw a rectangle outline using the current stroke style.\n---@param x number x-coordinate of the rectangle upper-left corner.\n---@param y number y-coordinate of the rectangle upper-left corner.\n---@param w number width of the rectangle.\n---@param h number height of the rectangle.\nfunction canvas.strokeRect(x, y, w, h)\n _internal.sendMessage('canvas.strokeRect', { x=x, y=y, w=w, h=h })\nend\n\n---Draw a rectangle with a filled interior using the current fill style,\n---@param x number x-coordinate of the rectangle upper-left corner.\n---@param y number y-coordinate of the rectangle upper-left corner.\n---@param w number width of the rectangle.\n---@param h number height of the rectangle.\nfunction canvas.fillRect(x, y, w, h)\n _internal.sendMessage('canvas.fillRect', { x=x, y=y, w=w, h=h })\nend\n\n---Clears the specified rectangle by setting its pixels to transparent.\n---@param x number x-coordinate of the rectangle upper-left corner.\n---@param y number y-coordinate of the rectangle upper-left corner.\n---@param w number width of the rectangle.\n---@param h number height of the rectangle.\nfunction canvas.clearRect(x, y, w, h)\n _internal.sendMessage('canvas.clearRect', { x=x, y=y, w=w, h=h })\nend\n\n---Draws a circle with given parameters. Must be followed by stroke() or fill().\n---@param x number x-coordinates of the center of the circle.\n---@param y number y-coordinates of the center of the circle.\n---@param r number the radius of the circle\nfunction canvas.circle(x, y, r)\n _internal.sendMessage('canvas.circle', { x=x, y=y, r=r })\nend\n\n---Draws an arc with given parameters. Must be followed by stroke() or fill().\n---@param x number x-coordinates of the center of the circle.\n---@param y number y-coordinates of the center of the circle.\n---@param r number the radius of the circle.\n---@param startAngle number the starting angle in radians.\n---@param endAngle number the ending angle, in radians.\n---@param anticlockwise boolean a flag which specifies whether the drawing should be counterclockwise or clockwise.\nfunction canvas.arc(x, y, r, startAngle, endAngle, anticlockwise)\n _internal.sendMessage('canvas.arc', { x=x, y=y, r=r, startAngle=startAngle,\n endAngle=endAngle, anticlockwise=anticlockwise })\nend\n\n---Adds a quadratic Bézier curve to the path with given control point and end point.\n---@param cpx number x-coordinate of the control point.\n---@param cpy number y-coordinate of the control point.\n---@param x number x-coordinate of the end point.\n---@param y number y-coordinate of the end point.\nfunction canvas.quadraticCurve(cpx, cpy, x, y)\n _internal.sendMessage('canvas.quadraticCurve', { cpx=cpx, cpy=cpy, x=x, y=y })\nend\n\n---Draws a bezier curve from the current position to the specified point, using the specified control points.\n---@param cp1x number x-coordinate of the first control point.\n---@param cp1y number y-coordinate of the first control point.\n---@param cp2x number x-coordinate of the second control point.\n---@param cp2y number y-coordinate of the second control point.\n---@param x number x-coordinate of the end point.\n---@param y number y-coordinate of the end point.\nfunction canvas.bezierCurve(cp1x, cp1y, cp2x, cp2y, x, y)\n _internal.sendMessage('canvas.bezierCurve', { cp1x=cp1x, cp1y=cp1y,\n cp2x=cp2x, cp2y=cp2y,\n x=x, y=y })\nend\n\n---@class Gradient\n---A class to internally link gradient objects from the Lua environment to Lumino codebase.\n---A Gradient object can be create using @{createLinearGradient} or @{createRadialGradient}\n---@private\nlocal Gradient = {}\n\n---Creates a new gradient object with the given index\n---@param idx number index of the gradient object\n---@private\nfunction Gradient:new(idx)\n -- Store the gradient object in a field of the class instance metatable\n local obj = { idx = idx}\n Class.__setmetatable(obj, self)\n self.__index = self\n -- Return the instance\n return obj\nend\n\n---Hook that runs before an object is deleted by the garbage collector.\n---It calls a function to delete the gradient object in JS\n---@private\nfunction Gradient:__gc()\n _internal.sendMessage('canvas.deleteGradient', { gradientIdx = self.idx })\nend\n-- Add the Gradient class to the Canvas namespace\ncanvas.Gradient = Gradient\n\n---Sets a gradient object to active.\n---@param gradient Gradient a gradient object\nfunction canvas.setGradientActive(gradient)\n if gradient.idx >= 0 then\n _internal.sendMessage('canvas.setGradientActive', { gradientIdx = gradient.idx })\n end\nend\n\n---Adds a new stop, defined by an offset and a color, to a gradient object.\n---@param gradient Gradient a gradient object\n---@param offset number a value between 0.0 and 1.0 where the new color stop is positioned.\n---@param r number Red channel value (0–255)\n---@param g number Green channel value (0–255)\n---@param b number Blue channel value (0–255)\n---@param a number Alpha channel value (0–1)\nfunction canvas.colorStop(gradient, offset, r, g, b, a)\n _internal.sendMessage('canvas.colorStop', { gradientIdx=gradient.idx, offset=offset, r=r, g=g, b=b, a=a })\nend\n\n---Creates a linear gradient object.\n---@param x1 number the x-coordinate of the start point of the gradient\n---@param y1 number the y-coordinate of the start point of the gradient\n---@param x2 number the x-coordinate of the end point of the gradient\n---@param y2 number the y-coordinate of the end point of the gradient\n---@return Gradient a new gradient object\n---@usage local gradient = Canvas.createLinearGradient(0, 0, 50, 0)\nfunction canvas.createLinearGradient(x1, y1, x2, y2)\n local idx = _internal.sendMessage('canvas.createLinearGradient', { x1=x1, y1=y1,\n x2=x2, y2=y2 })\n local gradient = Gradient:new(idx)\n return gradient\nend\n\n---Creates a radial gradient object.\n---@param x1 number x-coordinate of the start circle\n---@param y1 number y-coordinate of the start circle\n---@param r1 number radius of the start circle\n---@param x2 number x-coordinate of the end circle\n---@param y2 number y-coordinate of the end circle\n---@param r2 number radius of the end circle\n---@return Gradient a new gradient object\nfunction canvas.createRadialGradient(x1, y1, r1, x2, y2, r2)\n local idx = _internal.sendMessage('canvas.createRadialGradient', { x1=x1, y1=y1, r1=r1,\n x2=x2, y2=y2, r2=r2})\n local gradient = Gradient:new(idx)\n return gradient\nend\n\n---Returns an array with strings of all available fonts. This will list all custom loaded fonts through Loader.loadFont and\n---fonts registered in Canvas Fonts in your project. It will also probe for common system fonts although the list might not be complete.\n---It is strongly recommended to use only custom uploaded fonts to have reliable results on all platforms.\n---@return table an array with all the available fonts as strings\nfunction canvas.getAvailableFonts()\n return _internal.sendMessage('canvas.getAvailableFonts')\nend\n\n---Sets a font style.\n---@param font string The font style to be set, in the format of 'font-style font-size font-family'. Example: 'italic 20px Arial'.\nfunction canvas.setFont(font)\n _internal.sendMessage('canvas.setFont', { font=font })\nend\n\n---Sets the current text alignment\n---@param align string The new text alignment to be set. One of 'start', 'end', 'left', 'right', or 'center'.\nfunction canvas.setTextAlign(align)\n _internal.sendMessage('canvas.setTextAlign', { align=align })\nend\n\n---Sets the text baseline used when drawing text.\n---@param baseline string The text baseline to set. One of 'alphabetic', 'top', 'hanging', 'middle', 'ideographic', or 'bottom'.\nfunction canvas.setTextBaseline(baseline)\n _internal.sendMessage('canvas.setTextBaseline', { baseline=baseline })\nend\n\n---Measures the width of the specified text in pixels.\n---@param text string The text to measure.\nfunction canvas.measureTextWidth(text)\n return _internal.sendMessage('canvas.measureTextWidth', { text=text })\nend\n\n---Draws and fills a given text string at the specified coordinates.\n---@param text string The text string to be drawn and filled.\n---@param x number The x-coordinate where the text will be drawn.\n---@param y number The y-coordinate where the text will be drawn.\nfunction canvas.fillText(text, x, y)\n _internal.sendMessage('canvas.fillText', { text=text, x=x, y=y })\nend\n\n---Draws the outline of the given text at the specified coordinates.\n---@param text string The text string to be drawn.\n---@param x number The x-coordinate where the text will be drawn.\n---@param y number The y-coordinate where the text will be drawn.\nfunction canvas.strokeText(text, x, y)\n _internal.sendMessage('canvas.strokeText', { text=text, x=x, y=y })\nend\n\n-- -TODO: ImageData is not yet ready for deployment because using it for pixel manipulation would be\n-- -inefficient and slow due to the need for JSON communication with JavaScript. While JSON\n-- -communication is necessary for the security of the application, it is not well-suited for tasks\n-- -that require frequent or high-volume data transfer. It is better to use ImageData for low\n-- -frequency tasks such as updating UI and handling events, rather than trying to use it for more\n-- -demanding tasks that may be slowed down by the use of JSON objects.\n\n-- --- Image class to link imageData objects in the canvas to Lua\n-- ---@class imageData\n-- local Image = {}\n\n-- --- Creates a new Image object with the given index\n-- ---@param idx number index of the imageData object in the canvas\n-- function Image:new(idx)\n-- local obj = {}\n-- Class.__setmetatable(obj, self)\n-- self.__index = self\n-- self.idx = idx\n-- return obj\n-- end\n-- canvas.Image = Image\n\n-- ---Creates a new, blank Image object with the specified dimensions.\n-- ---All of the pixels in the new object are transparent black.\n-- ---@param width number the width of the Image object\n-- ---@param height number the height of the Image object\n-- ---@return Image a new Image object\n-- function canvas.createImage(width, height)\n-- local idx = _internal.sendMessage('canvas.createImage', { width=width, height=height })\n-- local image = Image:new(idx)\n-- return image\n-- end\n\n-- ---Write the RGBA channels of an Image object\n-- ---@param r number the r channel of the Image object\n-- ---@param g number the g channel of the Image object\n-- ---@param b number the b channel of the Image object\n-- ---@param a number the a channel of the Image object\n-- function canvas.writeImageChannels(image, r, g, b, a)\n-- _internal.sendMessage('canvas.writeImageChannels', { imageIdx=image.idx, r=r, g=g, b=b, a=a })\n-- end\n\n-- ---Draws an image given by an url at the specified position and dimensions.\n-- ---@param url string\n-- ---@param x number\n-- ---@param y number\n-- ---@param width number\n-- ---@param height number\n-- function canvas.drawImageFromUrl(url, x, y, width, height)\n-- _internal.sendMessage('canvas.drawImageFromUrl', { url=url, x=x, y=y, width=width, height=height })\n-- end\n\n-- ---Repeats a given image on the canvas with a given pattern.\n-- ---@param image imageSource an image object\n-- ---@param repeat string one of repeat, repeat-x, repeat-y, no-repeat\n-- function canvas.imagePattern(image, repeatPattern)\n-- _internal.sendMessage('canvas.pattern', { image=image, repeatPattern=repeatPattern })\n-- end\n\n---Draws the image data at the given coordinates.\n---@param image Texture the image data to paint\n---@param x number x-coordinate of the top left corner where the image data will be painted\n---@param y number y-coordinate of the top left corner where the image data will be painted\n---@param width number width of the image on screen (optional)\n---@param height number height of the image on screen (optional)\n---@param mode number mode to use for stretching. possible values are 0 = stretch, 1 = fit, 2 = cover\nfunction canvas.drawImage(image, x, y, width, height, mode)\n __assertTrace(image and image._isTexture, \"image is not a Texture object\")\n _internal.sendMessage('canvas.drawImage', { image= image._handle, x=x, y=y, width=width, height=height, mode=mode })\nend\n\n---Draws the image data at the given coordinates.\n---@param image Texture the image data to paint\n---@param x number x-coordinate of the top left corner where the image data will be painted\n---@param y number y-coordinate of the top left corner where the image data will be painted\n---@param width number width of the image on screen (optional)\n---@param height number height of the image on screen (optional)\nfunction canvas.drawSubImage(image, x, y, width, height, sourceX, sourceY, sourceWidth, sourceHeight)\n __assertTrace(image and image._isTexture, \"image is not a Texture object\")\n _internal.sendMessage('canvas.drawSubImage', { image= image._handle,\n x=x, y=y, width=width, height=height,\n sourceX=sourceX, sourceY=sourceY, sourceWidth=sourceWidth, sourceHeight=sourceHeight })\nend\n\n--- Scales the canvas by the specified factors along the x and y axes.\n--- Transformations are applied relative to the canvas origin (0,0).\n--- To scale around a different point, use `translate()` to move the origin before scaling.\n---@param x number Scale factor along the x-axis.\n---@param y number Scale factor along the y-axis.\nfunction canvas.scale(x, y)\n _internal.sendMessage('canvas.scale', { x=x, y=y })\nend\n\n--- Rotates the canvas by the specified angle in radians.\n--- Rotation is performed around the canvas origin (0,0).\n--- To rotate around a different point, translate the canvas to that point before calling rotate().\n---@param a number Angle in radians.\nfunction canvas.rotate(a)\n _internal.sendMessage('canvas.rotate', { a=a })\nend\n\n\n--- Moves (translates) the canvas origin by the specified amounts.\n--- All subsequent drawing and transformations (scale, rotate, etc.) are relative to the new origin.\n---@param x number Amount to translate along the x-axis.\n---@param y number Amount to translate along the y-axis.\nfunction canvas.translate(x, y)\n _internal.sendMessage('canvas.translate', { x=x, y=y })\nend\n\n---Applies a 2D transformation matrix to the canvas.\n---@param horizontal_scale number The amount to scale horizontally.\n---@param horizontal_skew number The amount of horizontal skew.\n---@param vertical_skew number The amount of vertical skew.\n---@param vertical_scale number The amount to scale vertically.\n---@param horizontal_translation number The amount of horizontal translation.\n---@param vertical_translation number The amount of vertical translation.\nfunction canvas.transform(horizontal_scale, horizontal_skew, vertical_skew,\n vertical_scale, horizontal_translation, vertical_translation)\n _internal.sendMessage('canvas.transform', { a=horizontal_scale, b=horizontal_skew,\n c=vertical_skew, d=vertical_scale,\n e=horizontal_translation, f=vertical_translation })\nend\n\n---Resets the current canvas transform to the identity matrix and then invokes the transform() function with the given parameters.\n---@param horizontal_scale number The amount to scale horizontally.\n---@param horizontal_skew number The amount of horizontal skew.\n---@param vertical_skew number The amount of vertical skew.\n---@param vertical_scale number The amount to scale vertically.\n---@param horizontal_translation number The amount of horizontal translation.\n---@param vertical_translation number The amount of vertical translation.\nfunction canvas.resetAndSetTransform(horizontal_scale, horizontal_skew, vertical_skew,\n vertical_scale, horizontal_translation, vertical_translation)\n _internal.sendMessage('canvas.resetAndSetTransform', { a=horizontal_scale, b=horizontal_skew,\n c=vertical_skew, d=vertical_scale,\n e=horizontal_translation, f=vertical_translation })\nend\n\n---Reset the transformation matrix to the identity matrix.\n---@function\nfunction canvas.resetTransform()\n _internal.sendMessage('canvas.resetTransform')\nend\n\n---Pushes the current canvas state to a stack so that you can restore it.\n---This can be useful for performing multiple transformations or styles on the canvas, and\n---then restoring it to its original state. The canvas state in fact includes current transformation matrix\n---stroke and fill styles, shadow styles, and other attributes. To restore the canvas state you call\n---the restoreState() function.\n---@function\nfunction canvas.saveState()\n _internal.sendMessage('canvas.saveState')\nend\n\n---Pops the the most recently saved canvas from the saved state stack and restores it.\n---@function\nfunction canvas.restoreState()\n _internal.sendMessage('canvas.restoreState')\nend\n\n--- Resets the canvas to its default state, clearing the canvas and restoring all\n--- context -properties (transforms, styles, clipping region, etc.) to their\n--- defaults. Reset() is more thorough than -calling clear() alone, which only\n--- erases pixels but leaves context state intact.\n---@function\nfunction canvas.reset()\n _internal.sendMessage('canvas.reset')\nend\n\nreturn canvas\n";
131488
+ var lua_api_canvas = "\nlocal _internal = require('engine/_internal');\n\n---@module canvas\nlocal canvas = {}\n\n-- Lua-side command buffer for canvas calls.\nlocal _cmds = {}\n\nlocal function _captureSendMessage(target, payload, buffers)\n table.insert(_cmds, {\n target = target,\n payload = payload,\n buffers = buffers\n })\n return nil\nend\n\nlocal function _runWithCapturedSend(fn, ...)\n local oldSendMessage = _internal.sendMessage\n _internal.sendMessage = _captureSendMessage\n\n local result = { pcall(fn, ...) }\n\n _internal.sendMessage = oldSendMessage\n\n if not result[1] then\n error(result[2])\n end\n\n return table.unpack(result, 2)\nend\n\nlocal function _commitIfNeeded()\n if #_cmds == 0 then\n return { committed = 0 }\n end\n\n local result = _internal.sendMessage('canvas.commit', { cmds = _cmds })\n _cmds = {}\n return result\nend\n\n---Commit all pending buffered canvas commands.\n---@return table|nil result Optional engine-side debug info.\nfunction canvas.commit()\n return _commitIfNeeded()\nend\n\n---Debug helper: returns count of pending buffered commands in Lua.\n---@return number\nfunction canvas.getPendingCommandCount()\n return #_cmds\nend\n\n---Debug helper: returns current pending buffered commands in Lua.\n---@return table\nfunction canvas.getPendingCommands()\n return _cmds\nend\n\n---Debug helper: returns most recently committed command list on engine side.\n---@return table\nfunction canvas.getLastCommittedCommands()\n _commitIfNeeded()\n return _internal.sendMessage('canvas.getLastCommittedCommands')\nend\n\n---Clear the canvas.\n---@function\nfunction canvas.clear()\n _internal.sendMessage('canvas.clear')\nend\n\n---Returns the width of the canvas in pixels.\n---@function\nfunction canvas.getWidth()\n return _internal.sendMessage('canvas.getWidth')\nend\n\n---Returns the height of the canvas in pixels.\n---@function\nfunction canvas.getHeight()\n return _internal.sendMessage('canvas.getHeight')\nend\n\n---Sets the width of lines in the canvas.\n---@param w number the width to set the lines to.\nfunction canvas.setLineWidth(w)\n _internal.sendMessage('canvas.setLineWidth', { w=w })\nend\n\n---Sets the type of endings applied to the ends of a line.\n---@param cap string the type of corner to create on a line. One of \"butt\", \"round\", \"square\".\nfunction canvas.setLineCap(cap)\n _internal.sendMessage('canvas.setLineCap', { cap=cap })\nend\n\n---Sets the type of corner created when two lines meet.\n---@param join string the type of connection to create between two lines. One of \"bevel\", \"round\", or \"miter\".\nfunction canvas.setLineJoin(join)\n _internal.sendMessage('canvas.setLineJoin', { join=join })\nend\n\n---Sets the maximum miter length. Miter length is the distance between the inner corner and the outer corner of the join.\n---@param limit number the maximum miter length. When the miter length exceeds this value, the corner is trimmed.\nfunction canvas.setMiterLimit(limit)\n _internal.sendMessage('canvas.setMiterLimit', { limit=limit })\nend\n\n---Sets the color of the stroke (outline) for shapes drawn on the canvas.\n---@param r number Red channel value (0–255)\n---@param g number Green channel value (0–255)\n---@param b number Blue channel value (0–255)\n---@param a number Alpha channel value (0–1)\nfunction canvas.setStrokeColor(r, g, b, a)\n _internal.sendMessage('canvas.setStrokeColor', { r=r, g=g, b=b, a=a })\nend\n\n---Sets the color that is used to fill in shapes and text when they are drawn on the canvas.\n---@param r number Red channel value (0–255)\n---@param g number Green channel value (0–255)\n---@param b number Blue channel value (0–255)\n---@param a number Alpha channel value (0–1)\nfunction canvas.setFillColor(r, g, b, a)\n _internal.sendMessage('canvas.setFillColor', { r=r, g=g, b=b, a=a })\nend\n\n---Sets the color to use for shadows.\n---@param r number Red channel value (0–255)\n---@param g number Green channel value (0–255)\n---@param b number Blue channel value (0–255)\n---@param a number Alpha channel value (0–1)\nfunction canvas.setShadowColor(r, g, b, a)\n _internal.sendMessage('canvas.setShadowColor', { r=r, g=g, b=b, a=a })\nend\n\n---Sets the blur level for shadows.\n---@param level number the level of blur to be applied to shadows (in pixels).\nfunction canvas.setShadowBlurLevel(level)\n _internal.sendMessage('canvas.setShadowBlurLevel', { level=level })\nend\n\n---Sets the blur level for shadows.\n---@param x number the vertical offset of the shadow in pixels.\n---@param y number the horizontal offset of the shadow in pixels.\nfunction canvas.setShadowOffset(x, y)\n _internal.sendMessage('canvas.setShadowOffset', { x=x, y=y })\nend\n\n---Begins a new path.\n---@function\nfunction canvas.beginPath()\n _internal.sendMessage('canvas.beginPath')\nend\n\n---Close the current path by connecting the last and first point in the path, creating a loop.\n---@function\nfunction canvas.closePath()\n _internal.sendMessage('canvas.closePath')\nend\n\n---Determines if the specified point is in the current path.\n---@param x number x-coordinate of the point to check.\n---@param y number y-coordinate of the point to check.\n---@return boolean indicating if the point is in the path or not.\nfunction canvas.isPointInPath(x, y)\n return _internal.sendMessage('canvas.isPointInPath', { x=x, y=y })\nend\n\n---Sets the clipping path to the current drawing path.\n---@function\nfunction canvas.clip()\n _internal.sendMessage('canvas.clip')\nend\n\n---Draws the outline of a shape or path using the current stroke style, line width and styles.\n---@function\nfunction canvas.stroke()\n _internal.sendMessage('canvas.stroke')\nend\n\n---Fills a shape or path with the current fill style.\n---@function\nfunction canvas.fill()\n _internal.sendMessage('canvas.fill')\nend\n\n---Moves the current drawing position to the specified coordinates.\n---@param x number x-coordinate of the new current drawing position.\n---@param y number y-coordinate of the new current drawing position.\nfunction canvas.moveTo(x, y)\n _internal.sendMessage('canvas.moveTo', { x=x, y=y })\nend\n\n---Draws a straight line from the current position to the specified coordinates.\n---@param x number the x-coordinate to draw the line to.\n---@param y number the y-coordinate to draw the line to.\nfunction canvas.lineTo(x, y)\n _internal.sendMessage('canvas.lineTo', { x=x, y=y })\nend\n\n---Sets the type of compositing operation to apply when drawing new shapes.\n---@param operation string the compositing operation to use, possible values are:\n---\"source-over\", \"source-in\", \"source-out\", \"source-atop\", \"destination-over\",\n---\"destination-in\", \"destination-out\", \"destination-atop\", \"lighter\", \"copy\", \"xor\".\nfunction canvas.setCompositeOperation(operation)\n _internal.sendMessage('canvas.setCompositeOperation', { operation=operation })\nend\n\n---Sets the transparency value that is applied to all rendering operations.\n---@param value number a value between 0.0 (fully transparent) and 1.0 (fully opaque).\nfunction canvas.setGlobalAlpha(value)\n _internal.sendMessage('canvas.setGlobalAlpha', { value=value })\nend\n\n---Creates a rectangle path which is only drawn after calling stroke() or fill().\n---@param x number x-coordinate of the rectangle upper-left corner.\n---@param y number y-coordinate of the rectangle upper-left corner.\n---@param w number width of the rectangle.\n---@param h number height of the rectangle.\nfunction canvas.pathRect(x, y, w, h)\n _internal.sendMessage('canvas.pathRect', { x=x, y=y, w=w, h=h })\nend\n\n---Adds a rounded rectangle to the current path. Must be followed by stroke() or fill().\n---@param x number x-coordinate of the rectangle upper-left corner.\n---@param y number y-coordinate of the rectangle upper-left corner.\n---@param w number width of the rectangle.\n---@param h number height of the rectangle.\n---@param radius number|table corner radius applied to all four corners (uniform), or a table\n---of 1–4 values to set individual corner radius in the order [top-left, top-right,\n---bottom-right, bottom-left], following the HTML Canvas API specification.\nfunction canvas.roundRect(x, y, w, h, radius)\n _internal.sendMessage('canvas.roundRect', { x=x, y=y, w=w, h=h, radius=radius })\nend\n\n---Draw a rectangle outline using the current stroke style.\n---@param x number x-coordinate of the rectangle upper-left corner.\n---@param y number y-coordinate of the rectangle upper-left corner.\n---@param w number width of the rectangle.\n---@param h number height of the rectangle.\nfunction canvas.strokeRect(x, y, w, h)\n _internal.sendMessage('canvas.strokeRect', { x=x, y=y, w=w, h=h })\nend\n\n---Draw a rectangle with a filled interior using the current fill style,\n---@param x number x-coordinate of the rectangle upper-left corner.\n---@param y number y-coordinate of the rectangle upper-left corner.\n---@param w number width of the rectangle.\n---@param h number height of the rectangle.\nfunction canvas.fillRect(x, y, w, h)\n _internal.sendMessage('canvas.fillRect', { x=x, y=y, w=w, h=h })\nend\n\n---Clears the specified rectangle by setting its pixels to transparent.\n---@param x number x-coordinate of the rectangle upper-left corner.\n---@param y number y-coordinate of the rectangle upper-left corner.\n---@param w number width of the rectangle.\n---@param h number height of the rectangle.\nfunction canvas.clearRect(x, y, w, h)\n _internal.sendMessage('canvas.clearRect', { x=x, y=y, w=w, h=h })\nend\n\n---Draws a circle with given parameters. Must be followed by stroke() or fill().\n---@param x number x-coordinates of the center of the circle.\n---@param y number y-coordinates of the center of the circle.\n---@param r number the radius of the circle\nfunction canvas.circle(x, y, r)\n _internal.sendMessage('canvas.circle', { x=x, y=y, r=r })\nend\n\n---Draws an arc with given parameters. Must be followed by stroke() or fill().\n---@param x number x-coordinates of the center of the circle.\n---@param y number y-coordinates of the center of the circle.\n---@param r number the radius of the circle.\n---@param startAngle number the starting angle in radians.\n---@param endAngle number the ending angle, in radians.\n---@param anticlockwise boolean a flag which specifies whether the drawing should be counterclockwise or clockwise.\nfunction canvas.arc(x, y, r, startAngle, endAngle, anticlockwise)\n _internal.sendMessage('canvas.arc', { x=x, y=y, r=r, startAngle=startAngle,\n endAngle=endAngle, anticlockwise=anticlockwise })\nend\n\n---Adds a quadratic Bézier curve to the path with given control point and end point.\n---@param cpx number x-coordinate of the control point.\n---@param cpy number y-coordinate of the control point.\n---@param x number x-coordinate of the end point.\n---@param y number y-coordinate of the end point.\nfunction canvas.quadraticCurve(cpx, cpy, x, y)\n _internal.sendMessage('canvas.quadraticCurve', { cpx=cpx, cpy=cpy, x=x, y=y })\nend\n\n---Draws a bezier curve from the current position to the specified point, using the specified control points.\n---@param cp1x number x-coordinate of the first control point.\n---@param cp1y number y-coordinate of the first control point.\n---@param cp2x number x-coordinate of the second control point.\n---@param cp2y number y-coordinate of the second control point.\n---@param x number x-coordinate of the end point.\n---@param y number y-coordinate of the end point.\nfunction canvas.bezierCurve(cp1x, cp1y, cp2x, cp2y, x, y)\n _internal.sendMessage('canvas.bezierCurve', { cp1x=cp1x, cp1y=cp1y,\n cp2x=cp2x, cp2y=cp2y,\n x=x, y=y })\nend\n\n---@class Gradient\n---A class to internally link gradient objects from the Lua environment to Lumino codebase.\n---A Gradient object can be create using @{createLinearGradient} or @{createRadialGradient}\n---@private\nlocal Gradient = {}\n\n---Creates a new gradient object with the given index\n---@param idx number index of the gradient object\n---@private\nfunction Gradient:new(idx)\n -- Store the gradient object in a field of the class instance metatable\n local obj = { idx = idx}\n Class.__setmetatable(obj, self)\n self.__index = self\n -- Return the instance\n return obj\nend\n\n---Hook that runs before an object is deleted by the garbage collector.\n---It calls a function to delete the gradient object in JS\n---@private\nfunction Gradient:__gc()\n -- Keep deletion ordering deterministic when gradient operations are buffered.\n _commitIfNeeded()\n _internal.sendMessage('canvas.deleteGradient', { gradientIdx = self.idx })\nend\n-- Add the Gradient class to the Canvas namespace\ncanvas.Gradient = Gradient\n\n---Sets a gradient object to active.\n---@param gradient Gradient a gradient object\nfunction canvas.setGradientActive(gradient)\n if gradient.idx >= 0 then\n _internal.sendMessage('canvas.setGradientActive', { gradientIdx = gradient.idx })\n end\nend\n\n---Adds a new stop, defined by an offset and a color, to a gradient object.\n---@param gradient Gradient a gradient object\n---@param offset number a value between 0.0 and 1.0 where the new color stop is positioned.\n---@param r number Red channel value (0–255)\n---@param g number Green channel value (0–255)\n---@param b number Blue channel value (0–255)\n---@param a number Alpha channel value (0–1)\nfunction canvas.colorStop(gradient, offset, r, g, b, a)\n _internal.sendMessage('canvas.colorStop', { gradientIdx=gradient.idx, offset=offset, r=r, g=g, b=b, a=a })\nend\n\n---Creates a linear gradient object.\n---@param x1 number the x-coordinate of the start point of the gradient\n---@param y1 number the y-coordinate of the start point of the gradient\n---@param x2 number the x-coordinate of the end point of the gradient\n---@param y2 number the y-coordinate of the end point of the gradient\n---@return Gradient a new gradient object\n---@usage local gradient = Canvas.createLinearGradient(0, 0, 50, 0)\nfunction canvas.createLinearGradient(x1, y1, x2, y2)\n local idx = _internal.sendMessage('canvas.createLinearGradient', { x1=x1, y1=y1,\n x2=x2, y2=y2 })\n local gradient = Gradient:new(idx)\n return gradient\nend\n\n---Creates a radial gradient object.\n---@param x1 number x-coordinate of the start circle\n---@param y1 number y-coordinate of the start circle\n---@param r1 number radius of the start circle\n---@param x2 number x-coordinate of the end circle\n---@param y2 number y-coordinate of the end circle\n---@param r2 number radius of the end circle\n---@return Gradient a new gradient object\nfunction canvas.createRadialGradient(x1, y1, r1, x2, y2, r2)\n local idx = _internal.sendMessage('canvas.createRadialGradient', { x1=x1, y1=y1, r1=r1,\n x2=x2, y2=y2, r2=r2})\n local gradient = Gradient:new(idx)\n return gradient\nend\n\n---Returns an array with strings of all available fonts. This will list all custom loaded fonts through Loader.loadFont and\n---fonts registered in Canvas Fonts in your project. It will also probe for common system fonts although the list might not be complete.\n---It is strongly recommended to use only custom uploaded fonts to have reliable results on all platforms.\n---@return table an array with all the available fonts as strings\nfunction canvas.getAvailableFonts()\n return _internal.sendMessage('canvas.getAvailableFonts')\nend\n\n---Sets a font style.\n---@param font string The font style to be set, in the format of 'font-style font-size font-family'. Example: 'italic 20px Arial'.\nfunction canvas.setFont(font)\n _internal.sendMessage('canvas.setFont', { font=font })\nend\n\n---Sets the current text alignment\n---@param align string The new text alignment to be set. One of 'start', 'end', 'left', 'right', or 'center'.\nfunction canvas.setTextAlign(align)\n _internal.sendMessage('canvas.setTextAlign', { align=align })\nend\n\n---Sets the text baseline used when drawing text.\n---@param baseline string The text baseline to set. One of 'alphabetic', 'top', 'hanging', 'middle', 'ideographic', or 'bottom'.\nfunction canvas.setTextBaseline(baseline)\n _internal.sendMessage('canvas.setTextBaseline', { baseline=baseline })\nend\n\n---Measures the width of the specified text in pixels.\n---@param text string The text to measure.\nfunction canvas.measureTextWidth(text)\n return _internal.sendMessage('canvas.measureTextWidth', { text=text })\nend\n\n---Draws and fills a given text string at the specified coordinates.\n---@param text string The text string to be drawn and filled.\n---@param x number The x-coordinate where the text will be drawn.\n---@param y number The y-coordinate where the text will be drawn.\nfunction canvas.fillText(text, x, y)\n _internal.sendMessage('canvas.fillText', { text=text, x=x, y=y })\nend\n\n---Draws the outline of the given text at the specified coordinates.\n---@param text string The text string to be drawn.\n---@param x number The x-coordinate where the text will be drawn.\n---@param y number The y-coordinate where the text will be drawn.\nfunction canvas.strokeText(text, x, y)\n _internal.sendMessage('canvas.strokeText', { text=text, x=x, y=y })\nend\n\n-- -TODO: ImageData is not yet ready for deployment because using it for pixel manipulation would be\n-- -inefficient and slow due to the need for JSON communication with JavaScript. While JSON\n-- -communication is necessary for the security of the application, it is not well-suited for tasks\n-- -that require frequent or high-volume data transfer. It is better to use ImageData for low\n-- -frequency tasks such as updating UI and handling events, rather than trying to use it for more\n-- -demanding tasks that may be slowed down by the use of JSON objects.\n\n-- --- Image class to link imageData objects in the canvas to Lua\n-- ---@class imageData\n-- local Image = {}\n\n-- --- Creates a new Image object with the given index\n-- ---@param idx number index of the imageData object in the canvas\n-- function Image:new(idx)\n-- local obj = {}\n-- Class.__setmetatable(obj, self)\n-- self.__index = self\n-- self.idx = idx\n-- return obj\n-- end\n-- canvas.Image = Image\n\n-- ---Creates a new, blank Image object with the specified dimensions.\n-- ---All of the pixels in the new object are transparent black.\n-- ---@param width number the width of the Image object\n-- ---@param height number the height of the Image object\n-- ---@return Image a new Image object\n-- function canvas.createImage(width, height)\n-- local idx = _internal.sendMessage('canvas.createImage', { width=width, height=height })\n-- local image = Image:new(idx)\n-- return image\n-- end\n\n-- ---Write the RGBA channels of an Image object\n-- ---@param r number the r channel of the Image object\n-- ---@param g number the g channel of the Image object\n-- ---@param b number the b channel of the Image object\n-- ---@param a number the a channel of the Image object\n-- function canvas.writeImageChannels(image, r, g, b, a)\n-- _internal.sendMessage('canvas.writeImageChannels', { imageIdx=image.idx, r=r, g=g, b=b, a=a })\n-- end\n\n-- ---Draws an image given by an url at the specified position and dimensions.\n-- ---@param url string\n-- ---@param x number\n-- ---@param y number\n-- ---@param width number\n-- ---@param height number\n-- function canvas.drawImageFromUrl(url, x, y, width, height)\n-- _internal.sendMessage('canvas.drawImageFromUrl', { url=url, x=x, y=y, width=width, height=height })\n-- end\n\n-- ---Repeats a given image on the canvas with a given pattern.\n-- ---@param image imageSource an image object\n-- ---@param repeat string one of repeat, repeat-x, repeat-y, no-repeat\n-- function canvas.imagePattern(image, repeatPattern)\n-- _internal.sendMessage('canvas.pattern', { image=image, repeatPattern=repeatPattern })\n-- end\n\n---Draws the image data at the given coordinates.\n---@param image Texture the image data to paint\n---@param x number x-coordinate of the top left corner where the image data will be painted\n---@param y number y-coordinate of the top left corner where the image data will be painted\n---@param width number width of the image on screen (optional)\n---@param height number height of the image on screen (optional)\n---@param mode number mode to use for stretching. possible values are 0 = stretch, 1 = fit, 2 = cover\nfunction canvas.drawImage(image, x, y, width, height, mode)\n __assertTrace(image and image._isTexture, \"image is not a Texture object\")\n _internal.sendMessage('canvas.drawImage', { image= image._handle, x=x, y=y, width=width, height=height, mode=mode })\nend\n\n---Draws the image data at the given coordinates.\n---@param image Texture the image data to paint\n---@param x number x-coordinate of the top left corner where the image data will be painted\n---@param y number y-coordinate of the top left corner where the image data will be painted\n---@param width number width of the image on screen (optional)\n---@param height number height of the image on screen (optional)\nfunction canvas.drawSubImage(image, x, y, width, height, sourceX, sourceY, sourceWidth, sourceHeight)\n __assertTrace(image and image._isTexture, \"image is not a Texture object\")\n _internal.sendMessage('canvas.drawSubImage', { image= image._handle,\n x=x, y=y, width=width, height=height,\n sourceX=sourceX, sourceY=sourceY, sourceWidth=sourceWidth, sourceHeight=sourceHeight })\nend\n\n--- Scales the canvas by the specified factors along the x and y axes.\n--- Transformations are applied relative to the canvas origin (0,0).\n--- To scale around a different point, use `translate()` to move the origin before scaling.\n---@param x number Scale factor along the x-axis.\n---@param y number Scale factor along the y-axis.\nfunction canvas.scale(x, y)\n _internal.sendMessage('canvas.scale', { x=x, y=y })\nend\n\n--- Rotates the canvas by the specified angle in radians.\n--- Rotation is performed around the canvas origin (0,0).\n--- To rotate around a different point, translate the canvas to that point before calling rotate().\n---@param a number Angle in radians.\nfunction canvas.rotate(a)\n _internal.sendMessage('canvas.rotate', { a=a })\nend\n\n\n--- Moves (translates) the canvas origin by the specified amounts.\n--- All subsequent drawing and transformations (scale, rotate, etc.) are relative to the new origin.\n---@param x number Amount to translate along the x-axis.\n---@param y number Amount to translate along the y-axis.\nfunction canvas.translate(x, y)\n _internal.sendMessage('canvas.translate', { x=x, y=y })\nend\n\n---Applies a 2D transformation matrix to the canvas.\n---@param horizontal_scale number The amount to scale horizontally.\n---@param horizontal_skew number The amount of horizontal skew.\n---@param vertical_skew number The amount of vertical skew.\n---@param vertical_scale number The amount to scale vertically.\n---@param horizontal_translation number The amount of horizontal translation.\n---@param vertical_translation number The amount of vertical translation.\nfunction canvas.transform(horizontal_scale, horizontal_skew, vertical_skew,\n vertical_scale, horizontal_translation, vertical_translation)\n _internal.sendMessage('canvas.transform', { a=horizontal_scale, b=horizontal_skew,\n c=vertical_skew, d=vertical_scale,\n e=horizontal_translation, f=vertical_translation })\nend\n\n---Resets the current canvas transform to the identity matrix and then invokes the transform() function with the given parameters.\n---@param horizontal_scale number The amount to scale horizontally.\n---@param horizontal_skew number The amount of horizontal skew.\n---@param vertical_skew number The amount of vertical skew.\n---@param vertical_scale number The amount to scale vertically.\n---@param horizontal_translation number The amount of horizontal translation.\n---@param vertical_translation number The amount of vertical translation.\nfunction canvas.resetAndSetTransform(horizontal_scale, horizontal_skew, vertical_skew,\n vertical_scale, horizontal_translation, vertical_translation)\n _internal.sendMessage('canvas.resetAndSetTransform', { a=horizontal_scale, b=horizontal_skew,\n c=vertical_skew, d=vertical_scale,\n e=horizontal_translation, f=vertical_translation })\nend\n\n---Reset the transformation matrix to the identity matrix.\n---@function\nfunction canvas.resetTransform()\n _internal.sendMessage('canvas.resetTransform')\nend\n\n---Pushes the current canvas state to a stack so that you can restore it.\n---This can be useful for performing multiple transformations or styles on the canvas, and\n---then restoring it to its original state. The canvas state in fact includes current transformation matrix\n---stroke and fill styles, shadow styles, and other attributes. To restore the canvas state you call\n---the restoreState() function.\n---@function\nfunction canvas.saveState()\n _internal.sendMessage('canvas.saveState')\nend\n\n---Pops the the most recently saved canvas from the saved state stack and restores it.\n---@function\nfunction canvas.restoreState()\n _internal.sendMessage('canvas.restoreState')\nend\n\n--- Resets the canvas to its default state, clearing the canvas and restoring all\n--- context -properties (transforms, styles, clipping region, etc.) to their\n--- defaults. Reset() is more thorough than -calling clear() alone, which only\n--- erases pixels but leaves context state intact.\n---@function\nfunction canvas.reset()\n _internal.sendMessage('canvas.reset')\nend\n\n-- Automatically wrap canvas methods for buffering, so non-query calls are queued\n-- and query/result-producing calls force an immediate commit first.\nlocal _passthroughMethods = {\n commit = true,\n getPendingCommandCount = true,\n getPendingCommands = true,\n}\n\nlocal _immediateMethods = {\n getLastCommittedCommands = true,\n getWidth = true,\n getHeight = true,\n isPointInPath = true,\n createLinearGradient = true,\n createRadialGradient = true,\n getAvailableFonts = true,\n measureTextWidth = true,\n}\n\nfor name, fn in pairs(canvas) do\n if type(fn) == \"function\" and name ~= \"Gradient\" then\n if _passthroughMethods[name] then\n -- keep as-is\n elseif _immediateMethods[name] then\n canvas[name] = function(...)\n _commitIfNeeded()\n return fn(...)\n end\n else\n canvas[name] = function(...)\n return _runWithCapturedSend(fn, ...)\n end\n end\n end\nend\n\nreturn canvas\n";
131489
131489
 
131490
131490
  var lua_api_class = "\nlocal Class = {}\n\nClass.__setmetatable = setmetatable\n_G.setmetatable = function()\n error(\"Do not use setmetatable. Use Class.new() instead!\")\nend\n\n-- Put the class in the global scope\n_G.Class = Class\n\nfunction Class.new(self, super)\n if self ~= Class then\n -- new was called with dot syntax, so we need to move self to super.\n super = self\n end\n\n local class, metatable, properties = {}, {}, {}\n class.metatable = metatable\n class.properties = properties\n\n function metatable:__index(key)\n local prop = properties[key]\n if prop then\n return prop:get()\n elseif class[key] ~= nil then\n return class[key]\n elseif class.__customIndex then\n return class.__customIndex(self, key)\n elseif super then\n return super.metatable.__index(self, key)\n else\n return nil\n end\n end\n\n function metatable:__newindex(key, value)\n local prop = properties[key]\n if prop then\n prop:set(value)\n elseif class.__customNewIndex then\n class.__customNewIndex(self, key, value)\n else\n if (super and super.metatable.__customnewindex(self, key, value)) then\n return\n end\n rawset(self, key, value)\n end\n end\n\n function metatable:__customnewindex(key, value)\n if class.__customNewIndex then\n class.__customNewIndex(self, key, value)\n return true\n elseif super then\n return super.metatable.__customnewindex(self, key, value)\n end\n return false\n end\n\n function class:getClass()\n return class\n end\n\n function class.new(self, ...)\n local obj = Class.__setmetatable({}, class.metatable)\n\n if obj.__new then\n if self ~= class then\n -- new was called with dot syntax\n obj:__new(self, ...)\n else\n obj:__new(...)\n end\n end\n\n return obj\n end\n\n function tableLength(T)\n local count = 0\n for _ in pairs(T) do\n count = count + 1\n end\n return count\n end\n\n function class:addProperty(key, propType, defaultValue, options)\n options = options or {}\n if type(options) == 'string' then\n options = { info=options }\n end\n\n if class.properties[key] then\n error(\"A property with the name '\" .. key .. \"' already exists!\");\n return;\n end\n class.properties[key] = Property.new(propType, defaultValue, tableLength(class.properties), options);\n end\n\n return class\nend\n\nreturn Class\n";
131491
131491
 
@@ -131577,7 +131577,7 @@ var lua_api_renderer = "\nlocal _internal = require('engine/_internal');\nlocal
131577
131577
 
131578
131578
  var lua_api_sceneentry = "\nlocal Class = require('engine/class')\nlocal _internal = require('engine/_internal');\n\n--- @class SceneEntry\nlocal SceneEntry = Class.new()\n\nSceneEntry._id = nil\n\n--- Hook for the Class type when a new object is created\n---@private\nfunction SceneEntry:__new(id)\n self._id = id\nend\n\n--- Load the scene\nfunction SceneEntry:load()\n self:setLoaded(true)\nend\n\n--- Unload the scene\nfunction SceneEntry:unload()\n self:setLoaded(false)\nend\n\n--- Activate the scene, meaning it will be rendered\nfunction SceneEntry:activate()\n self:setActivated(true)\nend\n\n--- Deactivate the scene, meaning it will not be rendered anymore\nfunction SceneEntry:deactivate()\n self:setActivated(false)\nend\n\n--- Set the activation state of the scene. An active scene will be rendered, an inactive one won't\n---@param value boolean true or false\nfunction SceneEntry:setActivated(value)\n _internal.sendMessage('sceneentry.setActivated', { id = self._id, value = value })\nend\n\n--- Set the loading state of the scene. An unloaded scene will not be rendered and freed from memory\n---@param value boolean true or false\nfunction SceneEntry:setLoaded(value)\n _internal.sendMessage('sceneentry.setLoaded', { id = self._id, value = value })\nend\n\n--- Returns the activation state of the scene\n---@return boolean Activation state\nfunction SceneEntry:isActive()\n return _internal.sendMessage('sceneentry.isActive', { id = self._id })\nend\n\n--- Returns the loading state of the scene\n---@return boolean Loading state\nfunction SceneEntry:isLoaded()\n return _internal.sendMessage('sceneentry.isLoaded', { id = self._id })\nend\n\nreturn SceneEntry\n";
131579
131579
 
131580
- var lua_api_sceneobject = "\nlocal _internal = require('engine/_internal');\nlocal Object = require 'engine/object'\nlocal handle = require 'engine/handle'\nlocal Transform = require 'engine/math/transform'\nlocal Vector3 = require 'engine/math/vector3'\nlocal Bounds = require 'engine/math/bounds'\nlocal Tools = require 'engine/tools'\nlocal Promise = require 'engine/promise'\n\n------------------------------------------------------------------\n-- SceneObject class\n------------------------------------------------------------------\n\n--- @class SceneObject\nlocal SceneObject = Class.new(Object)\nSceneObject._isSceneObject = true\nSceneObject._type = \"SceneObject\"\n\n--- Enumeration of the available types to use with SceneObject.create()\n--- @enum Type\nSceneObject.Type = {\n Audio = \"SgAudio\",\n AudioListener = \"AudioListener\",\n Billboard = \"SgBillboard\",\n Box = \"SgBox\",\n Camera = \"SgCamera\",\n CircleShadow = \"SgCircleShadow\",\n Cone = \"SgCone\",\n Cylinder = \"SgCylinder\",\n GaussianSplats = \"SgGaussianSplats\",\n Group = \"SgGroup\",\n Lightsource = \"SgLightsource\",\n Mesh = \"SgMesh\",\n Particles = \"SgParticles\",\n Plane = \"SgPlane\",\n Points = \"SgPoints\",\n PositionalAudio = \"SgPositionalAudio\",\n Prefab = \"SgPrefab\",\n Ring = \"SgRing\",\n Scene = \"Scene\",\n Sky = \"SgSky\",\n Sphere = \"SgSphere\",\n Text = \"SgText\",\n Torus = \"SgTorus\",\n Vehicle = \"SgVehicle\"\n}\n\n---@private\nfunction SceneObject:__new(handle, nodeId)\n Object.__new(self, handle)\n self._type = \"SceneObject\"\n self._isSceneObject = true\n self._nodeId = nodeId\nend\n\nfunction capitalizeFirstLetter(str)\n local firstChar = str:sub(1, 1)\n if firstChar:lower() == firstChar then\n firstChar = firstChar:upper()\n return firstChar .. str:sub(2)\n end\n return str\nend\n\n---@private\nfunction SceneObject:__customIndex(key)\n -- any private properties of scene objects start with an underscore. if so, just use rawget, otherwise\n -- get the value of the field from the engine and return it\n local value = rawget(self, key)\n if value or type(key) ~= 'string' or string.sub(key, 1, 1) == '_' then\n return value\n else\n return self:get(key)\n end\nend\n\n---@private\nfunction SceneObject:__customNewIndex(key, value)\n -- any private properties of scene objects start with an underscore. if so, just use rawset, otherwise\n -- write the value to the field in the engine\n if type(key) ~= 'string' or string.sub(key, 1, 1) == '_' then\n rawset(self, key, value)\n else\n self:set(key, value)\n end\nend\n\n--- Finds all objects with the given name.\n---\n--- The search can be restricted to a specific hierarchy by passing a `root` node.\n--- If no `root` is provided, the search covers the entire scene.\n---\n--- This function supports both static and instance usage:\n---\n--- **Static call**\n--- SceneObject.findAllByName(name root?)\n--- - Searches the entire scene unless a `root` node is provided.\n---\n--- **Instance call**\n--- obj:findAllByName(name, root?)\n--- - Uses `obj` as the root of the search unless an explicit `root`\n--- argument is passed.\n--- - Ideal when searching only within a specific hierarchy.\n---\n--- @param name string Name of the objects in the scenegraph.\n--- @param root (optional) SceneObject The root node to restrict the search to.\n--- @return table A table containing all found objects, or an empty table if none are found.\n--- @usage\n--- -- Global search\n--- local objects = SceneObject.findAllByName(\"Enemy\")\n--- for _, obj in ipairs(objects) do\n--- print(\"Found object with type:\", obj:getType())\n--- end\n---\n--- -- Scoped search under a specific parent node\n--- local level = SceneObject.findByName(\"Level1\")\n--- local objects = SceneObject.findAllByName(\"Enemy\", level)\n--- for _, obj in ipairs(objects) do\n--- print(\"Found object under parent with type:\", obj:getType())\n--- end\n---\n--- -- Instance call: search within a specific object's hierarchy\n--- local level = SceneObject.findByName(\"Level1\")\n--- local enemies = level:findAllByName(\"Enemy\")\n--- for _, obj in ipairs(enemies) do\n--- print(\"Found enemy under level:\", obj:getName())\n--- end\nfunction SceneObject.findAllByName(self, name, root)\n -- Detect if called as instance method\n if type(self) == \"table\" and self._isSceneObject then\n -- Instance call: default root to self if not provided (and self is not the class)\n if root == nil and self ~= SceneObject then\n root = self\n end\n else\n -- Static call: shift parameters\n name, root = self, name\n end\n \n __assertTrace(name, \"name is empty\")\n \n local objects = _internal.sendMessage('sceneobject.findAllByName', { \n name = name, \n root = root and root._handle or nil \n })\n \n for key, value in pairs(objects) do\n objects[key] = handle.createObject(value)\n end\n \n return objects\nend\n\n--- Finds the first occurrence of an object with the given name.\n---\n--- The search can be restricted to a specific hierarchy by passing a `root` node.\n--- If no `root` is provided, the search covers the entire scene.\n---\n--- This function supports both static and instance usage:\n---\n--- **Static call**\n--- SceneObject.findByName(name root)\n--- - Searches the entire scene unless a `root` node is provided.\n---\n--- **Instance call**\n--- obj:findByName(name root)\n--- - Uses `obj` as the root of the search unless an explicit `root`\n--- argument is passed.\n--- - Ideal when searching only within a specific hierarchy.\n---\n--- @param name string Name of the object in the scenegraph.\n--- @param root (optional) SceneObject The root node to restrict the search to.\n--- @return Object|nil The first object found with the given name, or `nil` if not found.\n--- @usage\n--- -- Global search\n--- local obj = SceneObject.findByName(\"Player\")\n--- if obj then\n--- print(\"Found object:\", obj:getName())\n--- end\n---\n--- -- Scoped search under a specific parent node\n--- local obj = SceneObject.findByName(\"Player\", parentNode)\n--- if obj then\n--- print(\"Found object under parent:\", obj:getName())\n--- end\n---\n--- -- Instance call: search within a specific object's hierarchy\n--- local player = SceneObject.findByName(\"Player\")\n--- local weapon = player:findByName(\"Weapon\")\n--- if weapon then\n--- print(\"Found weapon under player:\", weapon:getName())\n--- end\nfunction SceneObject.findByName(self, name, root)\n -- Detect if called as instance method\n if type(self) == \"table\" and self._isSceneObject then\n -- Instance call: default root to self if not provided\n if root == nil and self ~= SceneObject then\n root = self\n end\n else\n -- Static call: shift parameters\n name, root = self, name\n end\n \n __assertTrace(name, \"name is empty\")\n \n return handle.createObject(_internal.sendMessage('sceneobject.findByName', { \n name = name, \n root = root and root._handle or nil \n }))\nend\n\n---Find an object by its unique ID and return it.\n---\n--- @param id string The unique ID of the object.\n--- @return Object The found object or nil if not found.\n--- @usage\n--- local obj = SceneObject.findById(\"2135a78324fe\")\n--- if obj then\n--- print(\"Found object: \", obj:getName())\n--- end\nfunction SceneObject.findById(self, id)\n if type(self) == 'string' then\n id = self\n end\n if not id then\n return nil\n end\n return handle.createObject(_internal.sendMessage('sceneobject.findById', { id=id }))\nend\n\n--- Finds all objects that have the specified tag.\n---\n--- The search can be limited to a specific hierarchy by passing a `root` node.\n--- If no `root` is provided, the search scans the entire scene.\n---\n--- This function supports both static and instance usage:\n---\n--- **Static call**\n--- SceneObject.findByTag(tag, root)\n--- - Searches the entire scene unless a `root` node is provided.\n---\n--- **Instance call**\n--- obj:findByTag(tag, root)\n--- - Uses `obj` as the root of the search unless an explicit `root`\n--- argument is passed.\n--- - Ideal when searching only within a specific hierarchy.\n---\n--- @param tag string The tag to look for.\n--- @param root (optional) SceneObject The root node to restrict the search to.\n--- @return table A table of objects that have the given tag. May be empty if no matches are found.\n--- @usage\n--- -- Global search\n--- local enemies = SceneObject.findByTag(\"Enemy\")\n--- for _, enemy in ipairs(enemies) do\n--- print(\"Found enemy:\", enemy:getName())\n--- end\n---\n--- -- Scoped search under a specific parent node\n--- local tagged = SceneObject.findByTag(\"Interactable\", parentNode)\n--- for _, obj in ipairs(tagged) do\n--- print(\"Found interactable under parent:\", obj:getName())\n--- end\n---\n--- -- Instance call: search within a specific object's hierarchy\n--- local level = SceneObject.findByName(\"Level1\")\n--- local enemies = level:findByTag(\"Enemy\")\n--- for _, enemy in ipairs(enemies) do\n--- print(\"Found enemy under level:\", enemy:getName())\n--- end\nfunction SceneObject.findByTag(self, tag, root)\n -- Detect if called as instance method\n if type(self) == \"table\" and self._isSceneObject then\n -- Instance call: default root to self if not provided (and self is not the class)\n if root == nil and self ~= SceneObject then\n root = self\n end\n else\n -- Static call: shift parameters\n tag, root = self, tag\n end\n \n __assertTrace(tag, \"tag is empty\")\n \n local objects = _internal.sendMessage('sceneobject.findByTag', { \n tag = tag, \n root = root and root._handle or nil \n })\n \n for key, value in pairs(objects) do\n objects[key] = handle.createObject(value)\n end\n \n return objects\nend\n\n---Create a new object in the scenegraph. This is a generic function, for specialized versions, see the respective Sg<name> classes like SgBox.\n--- @param objectType string Type of object (e.g., SceneObject.Type.Sphere).\n--- @param options table The options to use. Common options are listed below. Additional type-specific options are documented in the specialized functions (e.g., SgBox.create, SgSphere.create).\n--- @param parent Object the parent object to add the new one to or nil to put it at root level\n--- @return Promise a promise resolving to a SceneObject\n--- @usage\n--- -- Options can be omitted. This example shows the defaults,\n--- -- only specify the ones you want different.\n--- SceneObject.create(SceneObject.Type.Sphere, {\n--- active = true,\n--- static = false,\n--- name = \"\",\n--- transform = Transform.new(),\n--- layers = {0},\n--- tags = {},\n--- receiveShadow = false,\n--- castShadow = false,\n--- -- Type-specific options (e.g., for Sphere):\n--- radius = 1,\n--- widthSegments = 8,\n--- heightSegments = 6\n--- }, nil):next(function(obj)\n--- obj:setActive(true)\n--- end)\nfunction SceneObject.create(self, objectType, options, parent)\n if (type(self) == \"string\") then\n objectType, options, parent = self, objectType, options\n end\n\n __assertTrace(type(objectType) == 'string', 'objectType must be a string')\n __assertTrace(parent == nil or (type(parent) == 'table' and parent._isSceneObject), 'parent must be a SceneObject')\n\n local p = _internal.sendMessage('sceneobject.create', {\n type = objectType,\n options = options,\n parent = parent and parent._handle or nil\n })\n return Promise:new(p)\nend\n\n--- Get the ID of the scene object.\n--- @return string The unique ID of the object.\n--- @usage\n--- local obj = SceneObject.findByName(\"Cube\")\n--- print(\"Object ID:\", obj:getId())\nfunction SceneObject:getId()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return self._nodeId\nend\n\n--- Get the name of the scene object.\n--- @return string The user-defined name of the object.\n--- @usage\n--- local obj = SceneObject.findByName(\"Cube\")\n--- print(\"Object name:\", obj:getName())\nfunction SceneObject:getName()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return _internal.sendMessage('sceneobject.getName', { obj=self._handle })\nend\n\n--- Return the display name of the scene object.\n--- In case the object has no user-given name, a name will be generated\n--- in the format \"type <id>\"\n--- @return string Displayname\n--- @usage\n--- local obj = SceneObject.findByName(\"Enemy\")\n--- print(\"Display name:\", obj:getDisplayName())\nfunction SceneObject:getDisplayName()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return _internal.sendMessage('sceneobject.getDisplayName', { obj=self._handle })\nend\n\n--- Return the type of the object\n--- @return string Type\n--- @usage\n--- local obj = SceneObject.findByName(\"Wall\")\n--- print(\"Object type:\", obj:getType())\nfunction SceneObject:getType()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return _internal.sendMessage('sceneobject.getType', { obj=self._handle })\nend\n\n--- Set a field of the scene object to a new value.\n---\n--- @param name string The name of the field.\n--- @param value any The new value to set.\n--- @usage\n--- local obj = SceneObject.findByName(\"Light\")\n--- obj:set(\"Brightness\", \"High\")\nfunction SceneObject:set(name, value)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n name = capitalizeFirstLetter(name)\n _internal.sendMessage('sceneobject.set', { obj=self._handle, name=name, value=value })\nend\n\n--- Get a field value from the scene object.\n---\n--- @param name string The name of the field.\n--- @return string The value of the field.\n--- @usage\n--- local obj = SceneObject.findByName(\"Lamp\")\n--- local intensity = obj:get(\"Intensity\")\n--- print(\"Intensity: \" .. intensity)\nfunction SceneObject:get(name)\n\n -- This is a replacement for the old get() function without parameters that would just return a node reference\n -- It needs to stay in place as long as there are still scripts that depend on it.\n if name == nil then\n return self\n end\n\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n name = capitalizeFirstLetter(name)\n local field = _internal.sendMessage('sceneobject.get', { obj=self._handle, name=name })\n if field == nil then\n return nil\n end\n return toType(field.value, field.type)\nend\n\n--- Set a field of the scene object to a new value.\n---@deprecated 25.11 Use ``set`` which is the new shorter version\n--- @param name string The name of the field.\n--- @param value any The new value to set.\n--- @usage\n--- local obj = SceneObject.findByName(\"Light\")\n--- obj:setFieldValue(\"Brightness\", \"High\")\nfunction SceneObject:setFieldValue(name, value)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n name = capitalizeFirstLetter(name)\n _internal.sendMessage('sceneobject.set', { obj=self._handle, name=name, value=value })\nend\n\n--- Get a field value from the scene object.\n---@deprecated 25.11 Use ``get`` which is the new shorter version\n--- @param name string The name of the field.\n--- @return string The value of the field.\n--- @usage\n--- local obj = SceneObject.findByName(\"Lamp\")\n--- local intensity = obj:getFieldValue(\"Intensity\")\n--- print(\"Intensity: \" .. intensity)\nfunction SceneObject:getFieldValue(name)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n name = capitalizeFirstLetter(name)\n local field = _internal.sendMessage('sceneobject.get', { obj=self._handle, name=name })\n if field == nil then\n return nil\n end\n return toType(field.value, field.type)\nend\n\n--- Animate a field from its current value to a new value.\n---\n--- @param name string The name of the field\n--- @param value any The new value to animate to.\n--- @param duration number The duration in milliseconds for the animation to take\n--- @param ease string The type of easing to use. Possible values: 'linear', 'easeInOutQuad'\n--- @return number animation id. Can be used to cancel animation using cancelAnimation(id)\nfunction SceneObject:animateFieldValue(name, value, duration, ease)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(not (type(value) == 'table'), \"Cannot animate objects!\");\n name = capitalizeFirstLetter(name)\n return _internal.sendMessage('sceneobject.animateFieldValue', { obj=self._handle, name=name, value=value, duration=duration, ease=ease })\nend\n\n--- Cancel a running animation\n---\n--- @param name string The name of the field\nfunction SceneObject:cancelAnimation(id)\n _internal.sendMessage('sceneobject.cancelAnimation', { obj=self._handle, id=id })\nend\n\n--- Set a field of the scene object to a new color value.\n---@deprecated 25.09 Use ``set`` which handle the correct type automatically.\n--- @param name string The name of the field.\n--- @param value Color The new color value.\n--- @usage\n--- local obj = SceneObject.findByName(\"Cube\")\n--- obj:setFieldColorValue(\"Color\", Color.new(1, 0, 0, 1)) -- Sets the object to red.\nfunction SceneObject:setFieldColorValue(name, value)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(value._isColor, \"value is not a Color\")\n name = capitalizeFirstLetter(name)\n _internal.sendMessage('sceneobject.set', { obj=self._handle, name=name, value=value:toData() })\nend\n\n--- Set a field of the scene object to a new number value.\n---@deprecated 25.09 Use ``set`` which handle the correct type automatically.\n--- @param name string The name of the field.\n--- @param value number The new number value.\nfunction SceneObject:setFieldNumberValue(name, value)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(type(value) == \"number\", \"value must be a number\")\n name = capitalizeFirstLetter(name)\n _internal.sendMessage('sceneobject.set', { obj=self._handle, name=name, value=value })\nend\n\n---Set a field of the scene object to a new enum value\n--- @param name string\n--- @param value number\nfunction SceneObject:setFieldEnumValue(name, value)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n name = capitalizeFirstLetter(name)\n _internal.sendMessage('sceneobject.set', { obj=self._handle, name=name, value=value })\nend\n\n---Set a field of the scene object to a new boolean value\n---@deprecated 25.09 Use ``set`` which handle the correct type automatically.\n--- @param name string\n--- @param value boolean\nfunction SceneObject:setFieldBooleanValue(name, value)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n if value then value = true else value = false end\n name = capitalizeFirstLetter(name)\n _internal.sendMessage('sceneobject.set', { obj=self._handle, name=name, value=value })\nend\n\n--- Get the layers of the scene object\n--- @return table A table containing layer indices\nfunction SceneObject:getLayers()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n local internal_layers = self:getFieldValue(\"Layers\")\n __assertTrace(type(internal_layers) == \"table\", \"Expected 'Layers' to be a table\")\n\n -- convert internal 0-based indices to frontend 1-based indices\n local layers = {}\n for _, layer_idx in ipairs(internal_layers) do\n table.insert(layers, layer_idx + 1)\n end\n\n return layers\nend\n\n--- Set the layers of the scene object.\n--- Layers control rendering visibility and interaction with other objects.\n---\n--- @param layers table A table containing layer indices.\n--- @usage\n--- local obj = SceneObject.findByName(\"Character\")\n--- obj:setLayers({1, 3, 5}) -- Assigns the object to layers 1, 3, and 5.\nfunction SceneObject:setLayers(layers)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(type(layers) == \"table\", \"Layers must be provided as a table\")\n self:setFieldValue(\"Layers\", layers)\nend\n\n--- Get the value of a scene object's field as color value.\n---@deprecated 25.09 Use ``get`` which always returns the correct type.\n--- @return Color value of the field\n--- @usage\n--- local obj = SceneObject.findByName(\"Lamp\")\n--- local color = obj:getFieldColorValue(\"Color\")\n--- print(\"Color: \" .. color)\nfunction SceneObject:getFieldColorValue(name)\n return self:getFieldValue(name)\nend\n\n--- Get the value of a scene object's field as number value.\n---@deprecated 25.09 Use ``get`` which always returns the correct type.\n--- @return number value of the field\nfunction SceneObject:getFieldNumberValue(name)\n return self:getFieldValue(name)\nend\n\n--- Get the value of a scene object's field as boolean value.\n---@deprecated 25.09 Use ``get`` which always returns the correct type.\n--- @return boolean value of the field\nfunction SceneObject:getFieldBooleanValue(name)\n return self:getFieldValue(name)\nend\n\n--- Get the value of a scene object's field as string value.\n---@deprecated 25.09 Use ``get`` which always returns the correct type.\n--- @return string value of the field\nfunction SceneObject:getFieldEnumValue(name)\n return self:getFieldValue(name)\nend\n\n--- Get the value of a scene object's field as string value\n--- @return string value of the field\nfunction SceneObject:getFieldEnumOptions(name)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return _internal.sendMessage('sceneobject.getFieldOptions', { obj=self._handle, name=name })\nend\n\n--- Set a field of the scene object to a new boolean value.\n---@deprecated 25.09 Use ``set`` which handle the correct type automatically.\n--- @param name string The name of the field.\n--- @param value boolean The new boolean value.\n--- @usage\n--- local obj = SceneObject.findByName(\"Door\")\n--- obj:setFieldBooleanValue(\"PhysicsEnabled\", true)\nfunction SceneObject:setFieldBooleanValue(name, value)\n self:setFieldValue(name, not not value)\nend\n\n---Get a link of the scene object to a new value\n---@deprecated 25.11 Use ``get`` which handles items now too\n--- @param name string name of the link field\n--- @return number handle of the linked item or nil if not set\nfunction SceneObject:getLinkItem(name)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n name = capitalizeFirstLetter(name)\n return handle.createObject(_internal.sendMessage('sceneobject.getLinkItem', { obj=self._handle, name=name }))\nend\n\n---Set a link from the scene object\n---@deprecated 25.11 Use ``set`` which handles items now too\n--- @param name string name of the link field\n--- @param item string value can be either an item ID or an Item object\nfunction SceneObject:setLinkItem(name, item)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(not item or (item and item._handle and item._isItem), \"item has to be an object of Item class or nil!\");\n name = capitalizeFirstLetter(name)\n _internal.sendMessage('sceneobject.setLinkItem', { obj=self._handle, name=name, item=item })\nend\n\n--- Set the position of the scene object.\n--- Sets the object's local position to the given coordinates.\n---\n--- @param x number Position along the X-axis.\n--- @param y number Position along the Y-axis.\n--- @param z number Position along the Z-axis.\n--- @usage\n--- local obj = SceneObject.findByName(\"Player\")\n--- if obj then\n--- obj:setPosition(0, 5, 0) -- Places the object at (0, 5, 0)\n--- end\nfunction SceneObject:setPosition(x, y, z)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n\n _internal.sendMessage('sceneobject.setPosition', {\n obj = self._handle,\n x = x,\n y = y,\n z = z\n })\nend\n\n--- Translate (move) the scene object by an offset.\n--- Adds the given offset to the object's current local position.\n---\n--- @param x number Offset along the X-axis.\n--- @param y number Offset along the Y-axis.\n--- @param z number Offset along the Z-axis.\n--- @usage\n--- local obj = SceneObject.findByName(\"Player\")\n--- if obj then\n--- obj:translate(0, 1, 0) -- Moves the object up by 1 unit from its current position\n--- end\nfunction SceneObject:translate(x, y, z)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.translate', {\n obj = self._handle,\n x = x,\n y = y,\n z = z\n })\nend\n\n--- Rotate the scene object.\n--- Rotation is applied in degrees and follows Euler angles (XYZ order).\n---\n--- @param x number Rotation around the X-axis in degrees.\n--- @param y number Rotation around the Y-axis in degrees.\n--- @param z number Rotation around the Z-axis in degrees.\n--- @usage\n--- local obj = SceneObject.findByName(\"Cube\")\n--- if obj then\n--- obj:rotate(45, 0, 0) -- Rotates 45 degrees around the X-axis\n--- end\nfunction SceneObject:rotate(x, y, z)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.rotate', { obj=self._handle, x=x, y=y, z=z })\nend\n\n--- Scale the scene object.\n---\n--- @param x number Scale factor along the X-axis.\n--- @param y number Scale factor along the Y-axis.\n--- @param z number Scale factor along the Z-axis.\n--- @usage\n--- local obj = SceneObject.findByName(\"Tree\")\n--- if obj then\n--- obj:scale(2, 2, 2) -- Doubles the size of the object\n--- end\nfunction SceneObject:scale(x, y, z)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.scale', { obj=self._handle, x=x, y=y, z=z })\nend\n\n--- Get the local transform of a scene object.\n--- The transform contains position, rotation, and scale relative to the object's parent.\n---\n--- @param cacheLocal boolean if set to true, the transform will be cached locally for the future for performance reasons. This should only be used if no other script changes the transform\n--- @return Transform The object's local transform.\n--- @usage\n--- local obj = SceneObject.findByName(\"Character\")\n--- local transform = obj:getTransform()\n--- print(\"Local position:\", transform.position)\nfunction SceneObject:getTransform(cacheLocal)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n if self._transform == nil or not cacheLocal then\n self._transform = Transform.fromData(_internal.sendMessage('sceneobject.getTransform', { obj=self._handle }))\n end\n return self._transform:clone()\nend\n\n--- Apply a function to modify the object's transform, then update it.\n--- This allows making changes to the transform while ensuring they are properly applied back.\n---\n--- @param func function A function that takes a Transform object as its only argument.\n--- @param cacheLocal boolean if set to true, the transform will be cached locally for the future for performance reasons. This should only be used if no other script changes the transform\n--- @usage\n--- local obj = SceneObject.findByName(\"Cube\")\n--- obj:withTransform(function(transform)\n--- transform.position.x = transform.position.x + 1 -- Move object 1 unit to the right\n--- end)\nfunction SceneObject:withTransform(func, cacheLocal)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(type(func) == 'function', \"argument func is not a function\")\n local transform = self:getTransform(cacheLocal)\n func(transform)\n self:setTransform(transform)\nend\n\n--- Get the world transform of a scene object.\n--- The world transform includes absolute position, rotation, and scale in the scene.\n---\n--- @return Transform The object's world transform.\n--- @usage\n--- local obj = SceneObject.findByName(\"Light\")\n--- local worldTransform = obj:getWorldTransform()\n--- print(\"World position:\", worldTransform.position)\nfunction SceneObject:getWorldTransform()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return Transform.fromData(_internal.sendMessage('sceneobject.getWorldTransform', { obj=self._handle }))\nend\n\n--- Get the forward direction vector in world space.\n--- Returns the local -Z axis transformed by the object's rotation.\n---\n--- @return Vector3 The forward direction in world space.\nfunction SceneObject:getForward()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return Vector3.fromData(_internal.sendMessage('sceneobject.getForward', { obj=self._handle }))\nend\n\n--- Get the right direction vector in world space.\n--- Returns the local +X axis transformed by the object's rotation.\n---\n--- @return Vector3 The right direction in world space.\nfunction SceneObject:getRight()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return Vector3.fromData(_internal.sendMessage('sceneobject.getRight', { obj=self._handle }))\nend\n\n--- Get the up direction vector in world space.\n--- Returns the local +Y axis transformed by the object's rotation.\n---\n--- @return Vector3 The up direction in world space.\nfunction SceneObject:getUp()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return Vector3.fromData(_internal.sendMessage('sceneobject.getUp', { obj=self._handle }))\nend\n\n--- Set the local transform of a scene object.\n--- Updates the object's position, rotation, and scale relative to its parent.\n---\n--- @param transform Transform The new local transform to apply.\n--- @usage\n--- local obj = SceneObject.findByName(\"Door\")\n--- local newTransform = Transform.new()\n--- newTransform.position = Vector3.new(0, 2, 0) -- Move the object up 2 units\n--- obj:setTransform(newTransform)\nfunction SceneObject:setTransform(transform)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(transform and transform._isTransform, \"transform has to be an object of type Transform\")\n self._transform = transform\n _internal.sendMessage('sceneobject.setTransform', { obj=self._handle, transform=self._transform:toData() })\nend\n\n--- Set the physics position of the scene object.\n---\n--- This directly updates the position of the object inside the physics engine.\n--- The position is expressed in world coordinates.\n--- Only works for objects added to the physics engine\n--- @param position Vector3 New world position of the object.\n--- @usage\n--- local obj = SceneObject.findByName(\"Box\")\n--- obj:setPhysicsPosition(Vector3.new(0, 5, 0))\nfunction SceneObject:setPhysicsPosition(position)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(position and position._isVector3, \"position has to be an object of type Vector3\")\n _internal.sendMessage('sceneobject.setPhysicsPosition', { obj=self._handle, position=position:toData() })\nend\n\n--- Set the physics rotation of the scene object.\n---\n--- This directly updates the rotation of the object inside the physics engine.\n--- The rotation is expressed in world space.\n--- Only works for objects added to the physics engine\n--- @param rotation Quaternion New world rotation of the object.\n--- @usage\n--- local obj = SceneObject.findByName(\"Box\")\n--- obj:setPhysicsRotation(Quaternion.fromEuler(0, math.pi, 0))\nfunction SceneObject:setPhysicsRotation(rotation)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(rotation and rotation._isQuaternion, \"rotation has to be an object of type Quaternion\")\n _internal.sendMessage('sceneobject.setPhysicsRotation', { obj=self._handle, rotation=rotation:toData() })\nend\n\nfunction SceneObject:setPhysicsRotation(rotation)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(rotation and rotation._isQuaternion, \"rotation has to be an object of type Quaternion\")\n _internal.sendMessage('sceneobject.setPhysicsRotation', { obj=self._handle, rotation=rotation:toData() })\nend\n\n--- Get the linear velocity of the scene object.\n--- This function returns the object's velocity in world coordinates.\n--- Only works for objects added to the physics engine with `watchLinearVelocity` enabled.\n---\n--- @return Vector3 The linear velocity of the object.\n--- @usage\n--- local obj = SceneObject.findByName(\"Ball\")\n--- local velocity = obj:getLinearVelocity()\n--- print(\"Velocity:\", velocity)\nfunction SceneObject:getLinearVelocity()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return Vector3.fromData(_internal.sendMessage('sceneobject.getLinearVelocity', { obj=self._handle }))\nend\n\n--- Set the linear velocity of the scene object.\n--- This function assigns a new velocity to the object, but only works if the object is in the physics engine.\n---\n--- @param velocity Vector3 The new linear velocity to apply.\n--- @usage\n--- local obj = SceneObject.findByName(\"Car\")\n--- obj:setLinearVelocity(Vector3.new(10, 0, 0)) -- Move object at speed 10 in the X direction.\nfunction SceneObject:setLinearVelocity(velocity)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(velocity and velocity._isVector3, \"velocity must be an object of type Vector3\")\n local data = velocity:toData()\n _internal.sendMessage('sceneobject.setLinearVelocity', { obj=self._handle, velocity=data })\nend\n\n--- Get the angular velocity of the scene object.\n--- Only works for objects added to the physics engine with `watchAngularVelocity` enabled.\n---\n--- @return Vector3 The angular velocity of the object.\n--- @usage\n--- local obj = SceneObject.findByName(\"Wheel\")\n--- local angVel = obj:getAngularVelocity()\n--- print(\"Angular velocity:\", angVel)\nfunction SceneObject:getAngularVelocity()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return Vector3.fromData(_internal.sendMessage('sceneobject.getAngularVelocity', { obj=self._handle }))\nend\n\n--- Set the angular velocity of the scene object.\n---\n--- @param velocity Vector3 The new angular velocity to apply.\n--- @usage\n--- local obj = SceneObject.findByName(\"Propeller\")\n--- obj:setAngularVelocity(Vector3.new(0, 10, 0)) -- Rotate around Y-axis.\nfunction SceneObject:setAngularVelocity(velocity)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(velocity and velocity._isVector3, \"velocity must be an object of type Vector3\")\n local data = velocity:toData()\n _internal.sendMessage('sceneobject.setAngularVelocity', { obj=self._handle, velocity=data })\nend\n\n--- Apply an impulse to the scene object.\n---\n--- An **impulse** is a sudden force applied to an object that immediately\n--- changes its velocity. This is different from a continuous force, which\n--- is applied over time.\n---\n--- This function only works on objects that are physics-enabled.\n---\n--- @param impulse Vector3 The force impulse to apply.\n--- @param relPos Vector3 The relative position where the impulse is applied. Default is (0, 0, 0)\nfunction SceneObject:applyImpulse(impulse, relPos)\n relPos = relPos or Vector3.new(0, 0, 0)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(impulse and impulse._isVector3, \"impulse must be an object of type Vector3\")\n __assertTrace(relPos and relPos._isVector3, \"relPos must be an object of type Vector3\")\n local impulseData = impulse:toData()\n local relPosData = relPos:toData()\n _internal.sendMessage('sceneobject.applyImpulse', { obj=self._handle, impulse=impulseData, relPos=relPosData })\nend\n\n--- Apply an impulse to the scene object that respects constraints.\n---\n--- Only works on physics-enabled objects.\n---\n--- @param impulse Vector3 The force impulse to apply.\n--- @param relPos Vector3 The relative position where the impulse is applied. Default is (0, 0, 0)\nfunction SceneObject:applyPushImpulse(impulse, relPos)\n relPos = relPos or Vector3.new(0, 0, 0)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(impulse and impulse._isVector3, \"impulse must be an object of type Vector3\")\n __assertTrace(relPos and relPos._isVector3, \"relPos must be an object of type Vector3\")\n _internal.sendMessage('sceneobject.applyPushImpulse', { obj=self._handle, impulse=impulse:toData(), relPos=relPos:toData() })\nend\n\n--- Apply an impulse to the center of the object.\n---\n--- Applies a sudden velocity change at the object's center. Only works on physics-enabled objects.\n---\n--- @param impulse Vector3 The force impulse to apply.\n--- @usage\n--- local obj = SceneObject.findByName(\"Box\")\n--- obj:applyCentralImpulse(Vector3.new(0, 5, 0)) -- Apply upward impulse.\nfunction SceneObject:applyCentralImpulse(impulse)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(impulse and impulse._isVector3, \"impulse must be an object of type Vector3\")\n _internal.sendMessage('sceneobject.applyCentralImpulse', { obj=self._handle, impulse=impulse:toData() })\nend\n\n--- Apply a central impulse that respects constraints.\n---\n--- Only works on physics-enabled objects and respects any joint or constraint applied.\n---\n--- @param impulse Vector3 The force impulse to apply.\n--- @usage\n--- local obj = SceneObject.findByName(\"Box\")\n--- obj:applyCentralPushImpulse(Vector3.new(0, 5, 0)) -- Apply upward impulse.\nfunction SceneObject:applyCentralPushImpulse(impulse)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(impulse and impulse._isVector3, \"impulse must be an object of type Vector3\")\n _internal.sendMessage('sceneobject.applyCentralPushImpulse', { obj=self._handle, impulse=impulse:toData() })\nend\n\n--- Apply a force to the scene object.\n---\n--- Unlike impulses, **forces** are applied over time, influencing the object's acceleration.\n--- Only works on physics-enabled objects.\n---\n--- @param force Vector3 The force vector to apply.\n--- @param relPos Vector3 The relative position where the force is applied. Default is (0, 0, 0)\nfunction SceneObject:applyForce(force, relPos)\n relPos = relPos or Vector3.new(0, 0, 0)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(force and force._isVector3, \"force must be an object of type Vector3\")\n __assertTrace(relPos and relPos._isVector3, \"relPos must be an object of type Vector3\")\n _internal.sendMessage('sceneobject.applyForce', { obj=self._handle, force=force:toData(), relPos=relPos:toData() })\nend\n\n--- Clear any forces on a scene object.\n--- Only works on physics-enabled objects.\n--- @usage\n--- local obj = SceneObject.findByName(\"Ball\")\n--- obj:clearForces()\nfunction SceneObject:clearForces()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.clearForces', { obj=self._handle })\nend\n\n--- Delete the scene object from the scene.\n---\n--- @usage\n--- local obj = scene:findByName(\"TemporaryObject\")\n--- if obj then\n--- obj:delete()\n--- end\nfunction SceneObject:delete()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.delete', { obj=self._handle })\n if self._handle then\n handle.invalidate(self._handle)\n end\n self._handle = nil\nend\n\n--- Clone a scene object to a new position and return it.\n---\n--- The position can be provided as:\n--- 1. Separate `x, y, z` coordinates.\n--- 2. A `Vector3` containing the position.\n---\n--- If no position is provided, the clone will appear in the same location as the original.\n---\n--- @param x number | Vector3 The X-coordinate of the new position, or a `Vector3` containing `x, y, z`. If a `Vector3` is passed, `y` and `z` should be omitted.\n--- @param y number (optional) The Y-coordinate of the new position. Omit this if passing a `Vector3`.\n--- @param z number (optional) The Z-coordinate of the new position. Omit this if passing a `Vector3`.\n--- @param parent SceneObject (optional) The parent object to attach the new object to.\n--- @return Promise A promise resolving to a SceneObject.\n---\n--- @usage\n--- local obj = scene:findByName(\"Tree\")\n--- obj:clone(5, 0, 2):next(function(clone)\n--- clone:setActive(true) -- Activate the cloned object\n--- end)\n---\n--- local pos = Vector3.new(3, 1, 0)\n--- obj:clone(pos):next(function(clone)\n--- clone:setFieldValue(\"Name\", \"ClonedTree\") -- Rename the cloned object\n--- end)\n---\n--- local parentObj = scene:findByName(\"Group\")\n--- obj:clone(pos, parentObj):next(function(clone)\n--- clone:setFieldValue(\"Name\", \"AnotherClonedTree\") -- Rename the cloned object\n--- end)\nfunction SceneObject:clone(x, y, z, parent)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n\n -- Handle case where first argument is a Vector3 and second argument is the parent\n if type(x) == \"table\" and x._isVector3 then\n x, y, z, parent = x.x, x.y, x.z, y -- Shift arguments\n end\n\n local p = _internal.sendMessage('sceneobject.clone', {\n obj = self._handle,\n x = x,\n y = y,\n z = z,\n parent = parent and parent._handle or nil\n })\n\n return Promise:new(p)\nend\n\n--- Create a new object of same type from this one. If no options are specified this will be basically a clone.\n--- If an options table is passed, this will specify all fields with their values that should be changed.\n--- At the very least, this should contain a new Transform, otherwise the new object will be at the exact location\n--- of the old one.\n--- @param options table the table of fields to set on the new object\n--- @param parent SceneObject (optional) The parent object to attach the new object to.\n--- @return Promise A promise resolving to a SceneObject.\n---\n--- @usage\n--- local obj = SceneObject.findByName(\"Tree\")\n--- local parentObj = SceneObject.findByName(\"Group\")\n--- local clone = await(obj:createFromThis({\n--- transform = Transform.new(Vector3.new(4, 6, 8)),\n--- active = true,\n--- name = \"Cloned tree\"\n--- }, parentObj)\nfunction SceneObject:createFromThis(options, parent)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(parent == nil or parent._isSceneObject, \"parent is not nil or a SceneObject\")\n\n local p = _internal.sendMessage('sceneobject.createFromThis', {\n obj = self._handle,\n options = options,\n parent = parent and parent._handle or nil\n })\n\n return Promise:new(p)\nend\n\n--- Move the scene object to a different parent. This parent can not be a child of the scene object\n--- @param parent SceneObject (optional) The parent object to attach the new object to. if nil, the object will be moved to the scenegraph root\nfunction SceneObject:moveToParent(parent)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(not parent or parent._isSceneObject, \"parent is not a SceneObject\")\n _internal.sendMessage('sceneobject.moveToParent', { obj = self._handle, parent = parent and parent._handle or nil })\nend\n\n--- Set the active state of the scene object.\n--- @param value boolean True to activate, false to deactivate.\nfunction SceneObject:setActive(value)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n self:setFieldValue(\"Active\", value)\nend\n\n--- Get the active state of the scene object.\n--- @return boolean True if the object is active, false otherwise.\nfunction SceneObject:getActive()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return self:getFieldBooleanValue(\"Active\")\nend\n\n--- Check if the object has a specific tag.\n--- @param tagName string The tag name to check.\n--- @return boolean True if the object has the tag, false otherwise.\nfunction SceneObject:hasTag(tagName)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n local tags = self:getFieldValue(\"Tags\")\n for _, value in pairs(tags) do\n if value == tagName then\n return true\n end\n end\n return false\nend\n\n--- Get a list of all tags assigned to the object.\n--- @return table An array of tag names.\nfunction SceneObject:getTags()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return self:getFieldValue(\"Tags\")\nend\n\n--- Assign a set of tags to the object.\n--- @param tags table An array of tag names.\nfunction SceneObject:setTags(tags)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n Tools.validateArrayOfStrings(tags)\n self:setFieldValue(\"Tags\", tags)\nend\n\n--- Add a tag to the object.\n--- @param tag string The tag to add.\nfunction SceneObject:addTag(tag)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(type(tag) == 'string', \"tag needs to be a string\")\n local tags = self:getTags()\n table.insert(tags, tag)\n self:setTags(tags)\nend\n\n--- Remove all tags from the object.\nfunction SceneObject:clearTags()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n self:setTags({})\nend\n\n--- Take a snapshot of the object and its children, if specified.\n--- This allows restoring the object to a previous state.\n--- @param recursive boolean Whether to include child objects in the snapshot.\n--- @return number The snapshot handle.\nfunction SceneObject:takeSnapshot(recursive)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return _internal.sendMessage('sceneobject.takeSnapshot', { obj=self._handle, recursive=recursive })\nend\n\n--- Restore a snapshot, reverting any changes made after the snapshot was taken.\n--- @param snapshot number The snapshot handle to restore.\nfunction SceneObject:rollbackSnapshot(snapshot)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.rollbackSnapshot', { obj=self._handle, snapshot=snapshot })\nend\n\n--- Check if the object is enabled.\n--- @return boolean True if the object is enabled, false otherwise.\nfunction SceneObject:isEnabled()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return _internal.sendMessage('sceneobject.isEnabled', { obj=self._handle })\nend\n\n--- Enable the object.\nfunction SceneObject:enable()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.enable', { obj=self._handle })\nend\n\n--- Disable the object.\nfunction SceneObject:disable()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.disable', { obj=self._handle })\nend\n\n--- Get the material assigned to the object.\n--- @return Material The material, or nil if none is assigned.\nfunction SceneObject:getMaterial()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return self:getLinkItem(\"Material\")\nend\n\n--- Set the object's material.\n--- @param mat Material The material to set or nil to clear the material\nfunction SceneObject:setMaterial(mat)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(mat == nil or (type(mat) == 'table' and mat._isMaterial), \"self is not a SceneObject\")\n self:setLinkItem(\"Material\", mat)\nend\n\n--- Get all behaviours attached to this object.\n---@deprecated 25.12 use getBehaviours.\n--- @return table A list of Behaviour objects.\nfunction SceneObject:getEntities()\n return self:getBehaviours()\nend\n\n--- Get all behaviours attached to this object.\n--- @return table A list of Behaviour objects.\nfunction SceneObject:getBehaviours()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n if not self._nodeId then\n error(\"This scene object does not have a node Id. Cannot get its behaviours.\")\n end\n return Behaviour.getByNodeId(self._nodeId)\nend\n\n--- Get a behaviour attached to this object by its script name.\n--- @param name string The script name.\n--- @return Behaviour The corresponding behaviour, or nil if not found.\nfunction SceneObject:getBehaviourByScriptName(name)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n if not self._nodeId then\n error(\"This scene object does not have a node Id. Cannot get its entities.\")\n end\n return Behaviour.getByNodeIdAndScriptName(self._nodeId, name)\nend\n\n--- Get a behaviour attached to this object by its script name.\n---@deprecated\n--- @param name string The script name.\n--- @return Behaviour The corresponding behaviour, or nil if not found.\nfunction SceneObject:getEntityByScriptName(name)\n return self:getBehaviourByScriptName(name)\nend\n\n--- Get a behaviour attached to this object by its script name.\n--- @param name string The script name.\n--- @return Behaviour The corresponding behaviour, or nil if not found.\nfunction SceneObject:behaviour(name)\n return self:getBehaviourByScriptName(name)\nend\n\n--- Get a behaviour attached to this object by its script name.\n--- @param name string The script name.\n--- @return Behaviour The corresponding behaviour, or nil if not found.\nfunction SceneObject:entity(name)\n return self:getBehaviourByScriptName(name)\nend\n\n--- Get the parent of the object.\n--- @return SceneObject The parent object, or nil if none exists.\nfunction SceneObject:getParent()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return handle.createObject(_internal.sendMessage('sceneobject.getParent', { obj=self._handle }))\nend\n\n--- Get the children of the object.\n--- @return table A table of child objects.\nfunction SceneObject:getChildren()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n local objects = _internal.sendMessage('sceneobject.getChildren', { obj=self._handle })\n for key, value in pairs(objects) do\n objects[key] = handle.createObject(value)\n end\n return objects\nend\n\n--- Enable or disable automatic recreation of the scene object between play and stop.\n---\n--- When `value` is true, the object will be automatically recreated every time\n--- the scene starts or stops. This is useful for objects that need to reset\n--- physics, state, or procedural components at each run.\n---\n--- @param value boolean Enable (`true`) or disable (`false`) automatic recreation.\nfunction SceneObject:setAutoRecreate(value)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.setAutoRecreate', { obj=self._handle, value=value })\nend\n\n--- Immediately recreate the scene object.\n---\n--- This forces the object to reset its state, physics, and components as if\n--- it was newly instantiated in the scene.\nfunction SceneObject:recreate()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.recreate', { obj=self._handle })\nend\n\n--- Return the bounds of the scene object\n---@return Bounds the bounds of the object\nfunction SceneObject:getBounds()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n local bounds = _internal.sendMessage('sceneobject.getBounds', { obj=self._handle })\n local center = Vector3.new(bounds.center.x, bounds.center.y, bounds.center.z)\n local extents = Vector3.new(bounds.extents.x, bounds.extents.y, bounds.extents.z)\n return Bounds.new(center, extents)\nend\n\nreturn SceneObject\n";
131580
+ var lua_api_sceneobject = "\nlocal _internal = require('engine/_internal');\nlocal Object = require 'engine/object'\nlocal handle = require 'engine/handle'\nlocal Transform = require 'engine/math/transform'\nlocal Vector3 = require 'engine/math/vector3'\nlocal Bounds = require 'engine/math/bounds'\nlocal Tools = require 'engine/tools'\nlocal Promise = require 'engine/promise'\n\n------------------------------------------------------------------\n-- SceneObject class\n------------------------------------------------------------------\n\n--- @class SceneObject\nlocal SceneObject = Class.new(Object)\nSceneObject._isSceneObject = true\nSceneObject._type = \"SceneObject\"\n\n--- Enumeration of the available types to use with SceneObject.create()\n--- @enum Type\nSceneObject.Type = {\n Audio = \"SgAudio\",\n AudioListener = \"AudioListener\",\n Billboard = \"SgBillboard\",\n Box = \"SgBox\",\n Camera = \"SgCamera\",\n CircleShadow = \"SgCircleShadow\",\n Cone = \"SgCone\",\n Cylinder = \"SgCylinder\",\n GaussianSplats = \"SgGaussianSplats\",\n Group = \"SgGroup\",\n Lightsource = \"SgLightsource\",\n Mesh = \"SgMesh\",\n Particles = \"SgParticles\",\n Plane = \"SgPlane\",\n Points = \"SgPoints\",\n PositionalAudio = \"SgPositionalAudio\",\n Prefab = \"SgPrefab\",\n Ring = \"SgRing\",\n Scene = \"Scene\",\n Sky = \"SgSky\",\n Sphere = \"SgSphere\",\n Text = \"SgText\",\n Torus = \"SgTorus\",\n Vehicle = \"SgVehicle\"\n}\n\n---@private\nfunction SceneObject:__new(handle, nodeId)\n Object.__new(self, handle)\n self._type = \"SceneObject\"\n self._isSceneObject = true\n self._nodeId = nodeId\nend\n\nfunction capitalizeFirstLetter(str)\n local firstChar = str:sub(1, 1)\n if firstChar:lower() == firstChar then\n firstChar = firstChar:upper()\n return firstChar .. str:sub(2)\n end\n return str\nend\n\n---@private\nfunction SceneObject:__customIndex(key)\n -- any private properties of scene objects start with an underscore. if so, just use rawget, otherwise\n -- get the value of the field from the engine and return it\n local value = rawget(self, key)\n if value or type(key) ~= 'string' or string.sub(key, 1, 1) == '_' then\n return value\n else\n return self:get(key)\n end\nend\n\n---@private\nfunction SceneObject:__customNewIndex(key, value)\n -- any private properties of scene objects start with an underscore. if so, just use rawset, otherwise\n -- write the value to the field in the engine\n if type(key) ~= 'string' or string.sub(key, 1, 1) == '_' then\n rawset(self, key, value)\n else\n self:set(key, value)\n end\nend\n\n--- Finds all objects with the given name.\n---\n--- The search can be restricted to a specific hierarchy by passing a `root` node.\n--- If no `root` is provided, the search covers the entire scene.\n---\n--- This function supports both static and instance usage:\n---\n--- **Static call**\n--- SceneObject.findAllByName(name root?)\n--- - Searches the entire scene unless a `root` node is provided.\n---\n--- **Instance call**\n--- obj:findAllByName(name, root?)\n--- - Uses `obj` as the root of the search unless an explicit `root`\n--- argument is passed.\n--- - Ideal when searching only within a specific hierarchy.\n---\n--- @param name string Name of the objects in the scenegraph.\n--- @param root (optional) SceneObject The root node to restrict the search to.\n--- @return table A table containing all found objects, or an empty table if none are found.\n--- @usage\n--- -- Global search\n--- local objects = SceneObject.findAllByName(\"Enemy\")\n--- for _, obj in ipairs(objects) do\n--- print(\"Found object with type:\", obj:getType())\n--- end\n---\n--- -- Scoped search under a specific parent node\n--- local level = SceneObject.findByName(\"Level1\")\n--- local objects = SceneObject.findAllByName(\"Enemy\", level)\n--- for _, obj in ipairs(objects) do\n--- print(\"Found object under parent with type:\", obj:getType())\n--- end\n---\n--- -- Instance call: search within a specific object's hierarchy\n--- local level = SceneObject.findByName(\"Level1\")\n--- local enemies = level:findAllByName(\"Enemy\")\n--- for _, obj in ipairs(enemies) do\n--- print(\"Found enemy under level:\", obj:getName())\n--- end\nfunction SceneObject.findAllByName(self, name, root)\n -- Detect if called as instance method\n if type(self) == \"table\" and self._isSceneObject then\n -- Instance call: default root to self if not provided (and self is not the class)\n if root == nil and self ~= SceneObject then\n root = self\n end\n else\n -- Static call: shift parameters\n name, root = self, name\n end\n \n __assertTrace(name, \"name is empty\")\n \n local objects = _internal.sendMessage('sceneobject.findAllByName', { \n name = name, \n root = root and root._handle or nil \n })\n \n for key, value in pairs(objects) do\n objects[key] = handle.createObject(value)\n end\n \n return objects\nend\n\n--- Finds the first occurrence of an object with the given name.\n---\n--- The search can be restricted to a specific hierarchy by passing a `root` node.\n--- If no `root` is provided, the search covers the entire scene.\n---\n--- This function supports both static and instance usage:\n---\n--- **Static call**\n--- SceneObject.findByName(name root)\n--- - Searches the entire scene unless a `root` node is provided.\n---\n--- **Instance call**\n--- obj:findByName(name root)\n--- - Uses `obj` as the root of the search unless an explicit `root`\n--- argument is passed.\n--- - Ideal when searching only within a specific hierarchy.\n---\n--- @param name string Name of the object in the scenegraph.\n--- @param root (optional) SceneObject The root node to restrict the search to.\n--- @return Object|nil The first object found with the given name, or `nil` if not found.\n--- @usage\n--- -- Global search\n--- local obj = SceneObject.findByName(\"Player\")\n--- if obj then\n--- print(\"Found object:\", obj:getName())\n--- end\n---\n--- -- Scoped search under a specific parent node\n--- local obj = SceneObject.findByName(\"Player\", parentNode)\n--- if obj then\n--- print(\"Found object under parent:\", obj:getName())\n--- end\n---\n--- -- Instance call: search within a specific object's hierarchy\n--- local player = SceneObject.findByName(\"Player\")\n--- local weapon = player:findByName(\"Weapon\")\n--- if weapon then\n--- print(\"Found weapon under player:\", weapon:getName())\n--- end\nfunction SceneObject.findByName(self, name, root)\n -- Detect if called as instance method\n if type(self) == \"table\" and self._isSceneObject then\n -- Instance call: default root to self if not provided\n if root == nil and self ~= SceneObject then\n root = self\n end\n else\n -- Static call: shift parameters\n name, root = self, name\n end\n \n __assertTrace(name, \"name is empty\")\n \n return handle.createObject(_internal.sendMessage('sceneobject.findByName', { \n name = name, \n root = root and root._handle or nil \n }))\nend\n\n---Find an object by its unique ID and return it.\n---\n--- @param id string The unique ID of the object.\n--- @return Object The found object or nil if not found.\n--- @usage\n--- local obj = SceneObject.findById(\"2135a78324fe\")\n--- if obj then\n--- print(\"Found object: \", obj:getName())\n--- end\nfunction SceneObject.findById(self, id)\n if type(self) == 'string' then\n id = self\n end\n if not id then\n return nil\n end\n return handle.createObject(_internal.sendMessage('sceneobject.findById', { id=id }))\nend\n\n--- Finds all objects that have the specified tag.\n---\n--- The search can be limited to a specific hierarchy by passing a `root` node.\n--- If no `root` is provided, the search scans the entire scene.\n---\n--- This function supports both static and instance usage:\n---\n--- **Static call**\n--- SceneObject.findByTag(tag, root)\n--- - Searches the entire scene unless a `root` node is provided.\n---\n--- **Instance call**\n--- obj:findByTag(tag, root)\n--- - Uses `obj` as the root of the search unless an explicit `root`\n--- argument is passed.\n--- - Ideal when searching only within a specific hierarchy.\n---\n--- @param tag string The tag to look for.\n--- @param root (optional) SceneObject The root node to restrict the search to.\n--- @return table A table of objects that have the given tag. May be empty if no matches are found.\n--- @usage\n--- -- Global search\n--- local enemies = SceneObject.findByTag(\"Enemy\")\n--- for _, enemy in ipairs(enemies) do\n--- print(\"Found enemy:\", enemy:getName())\n--- end\n---\n--- -- Scoped search under a specific parent node\n--- local tagged = SceneObject.findByTag(\"Interactable\", parentNode)\n--- for _, obj in ipairs(tagged) do\n--- print(\"Found interactable under parent:\", obj:getName())\n--- end\n---\n--- -- Instance call: search within a specific object's hierarchy\n--- local level = SceneObject.findByName(\"Level1\")\n--- local enemies = level:findByTag(\"Enemy\")\n--- for _, enemy in ipairs(enemies) do\n--- print(\"Found enemy under level:\", enemy:getName())\n--- end\nfunction SceneObject.findByTag(self, tag, root)\n -- Detect if called as instance method\n if type(self) == \"table\" and self._isSceneObject then\n -- Instance call: default root to self if not provided (and self is not the class)\n if root == nil and self ~= SceneObject then\n root = self\n end\n else\n -- Static call: shift parameters\n tag, root = self, tag\n end\n \n __assertTrace(tag, \"tag is empty\")\n \n local objects = _internal.sendMessage('sceneobject.findByTag', { \n tag = tag, \n root = root and root._handle or nil \n })\n \n for key, value in pairs(objects) do\n objects[key] = handle.createObject(value)\n end\n \n return objects\nend\n\n---Create a new object in the scenegraph. This is a generic function, for specialized versions, see the respective Sg<name> classes like SgBox.\n--- @param objectType string Type of object (e.g., SceneObject.Type.Sphere).\n--- @param options table The options to use. Common options are listed below. Additional type-specific options are documented in the specialized functions (e.g., SgBox.create, SgSphere.create).\n--- @param parent Object the parent object to add the new one to or nil to put it at root level\n--- @return Promise a promise resolving to a SceneObject\n--- @usage\n--- -- Options can be omitted. This example shows the defaults,\n--- -- only specify the ones you want different.\n--- SceneObject.create(SceneObject.Type.Sphere, {\n--- active = true,\n--- static = false,\n--- name = \"\",\n--- transform = Transform.new(),\n--- layers = {0},\n--- tags = {},\n--- receiveShadow = false,\n--- castShadow = false,\n--- -- Type-specific options (e.g., for Sphere):\n--- radius = 1,\n--- widthSegments = 8,\n--- heightSegments = 6\n--- }, nil):next(function(obj)\n--- obj:setActive(true)\n--- end)\nfunction SceneObject.create(self, objectType, options, parent)\n if (type(self) == \"string\") then\n objectType, options, parent = self, objectType, options\n end\n\n __assertTrace(type(objectType) == 'string', 'objectType must be a string')\n __assertTrace(parent == nil or (type(parent) == 'table' and parent._isSceneObject), 'parent must be a SceneObject')\n\n local p = _internal.sendMessage('sceneobject.create', {\n type = objectType,\n options = options,\n parent = parent and parent._handle or nil\n })\n return Promise:new(p)\nend\n\n--- Get the ID of the scene object.\n--- @return string The unique ID of the object.\n--- @usage\n--- local obj = SceneObject.findByName(\"Cube\")\n--- print(\"Object ID:\", obj:getId())\nfunction SceneObject:getId()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return self._nodeId\nend\n\n--- Get the name of the scene object.\n--- @return string The user-defined name of the object.\n--- @usage\n--- local obj = SceneObject.findByName(\"Cube\")\n--- print(\"Object name:\", obj:getName())\nfunction SceneObject:getName()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return _internal.sendMessage('sceneobject.getName', { obj=self._handle })\nend\n\n--- Return the display name of the scene object.\n--- In case the object has no user-given name, a name will be generated\n--- in the format \"type <id>\"\n--- @return string Displayname\n--- @usage\n--- local obj = SceneObject.findByName(\"Enemy\")\n--- print(\"Display name:\", obj:getDisplayName())\nfunction SceneObject:getDisplayName()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return _internal.sendMessage('sceneobject.getDisplayName', { obj=self._handle })\nend\n\n--- Return the type of the object\n--- @return string Type\n--- @usage\n--- local obj = SceneObject.findByName(\"Wall\")\n--- print(\"Object type:\", obj:getType())\nfunction SceneObject:getType()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return _internal.sendMessage('sceneobject.getType', { obj=self._handle })\nend\n\n--- Set a field of the scene object to a new value.\n---\n--- @param name string The name of the field.\n--- @param value any The new value to set.\n--- @usage\n--- local obj = SceneObject.findByName(\"Light\")\n--- obj:set(\"Brightness\", \"High\")\nfunction SceneObject:set(name, value)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n name = capitalizeFirstLetter(name)\n _internal.sendMessage('sceneobject.set', { obj=self._handle, name=name, value=value })\nend\n\n--- Get a field value from the scene object.\n---\n--- @param name string The name of the field.\n--- @return string The value of the field.\n--- @usage\n--- local obj = SceneObject.findByName(\"Lamp\")\n--- local intensity = obj:get(\"Intensity\")\n--- print(\"Intensity: \" .. intensity)\nfunction SceneObject:get(name)\n\n -- This is a replacement for the old get() function without parameters that would just return a node reference\n -- It needs to stay in place as long as there are still scripts that depend on it.\n if name == nil then\n return self\n end\n\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n name = capitalizeFirstLetter(name)\n local field = _internal.sendMessage('sceneobject.get', { obj=self._handle, name=name })\n if field == nil then\n return nil\n end\n return toType(field.value, field.type)\nend\n\n--- Set a field of the scene object to a new value.\n---@deprecated 25.11 Use ``set`` which is the new shorter version\n--- @param name string The name of the field.\n--- @param value any The new value to set.\n--- @usage\n--- local obj = SceneObject.findByName(\"Light\")\n--- obj:setFieldValue(\"Brightness\", \"High\")\nfunction SceneObject:setFieldValue(name, value)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n name = capitalizeFirstLetter(name)\n _internal.sendMessage('sceneobject.set', { obj=self._handle, name=name, value=value })\nend\n\n--- Get a field value from the scene object.\n---@deprecated 25.11 Use ``get`` which is the new shorter version\n--- @param name string The name of the field.\n--- @return string The value of the field.\n--- @usage\n--- local obj = SceneObject.findByName(\"Lamp\")\n--- local intensity = obj:getFieldValue(\"Intensity\")\n--- print(\"Intensity: \" .. intensity)\nfunction SceneObject:getFieldValue(name)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n name = capitalizeFirstLetter(name)\n local field = _internal.sendMessage('sceneobject.get', { obj=self._handle, name=name })\n if field == nil then\n return nil\n end\n return toType(field.value, field.type)\nend\n\n--- Animate a field from its current value to a new value.\n---\n--- @param name string The name of the field\n--- @param value any The new value to animate to.\n--- @param duration number The duration in milliseconds for the animation to take\n--- @param ease string The type of easing to use. Possible values: 'linear', 'easeInOutQuad'\n--- @return number animation id. Can be used to cancel animation using cancelAnimation(id)\nfunction SceneObject:animateFieldValue(name, value, duration, ease)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(not (type(value) == 'table'), \"Cannot animate objects!\");\n name = capitalizeFirstLetter(name)\n return _internal.sendMessage('sceneobject.animateFieldValue', { obj=self._handle, name=name, value=value, duration=duration, ease=ease })\nend\n\n--- Cancel a running animation\n---\n--- @param name string The name of the field\nfunction SceneObject:cancelAnimation(id)\n _internal.sendMessage('sceneobject.cancelAnimation', { obj=self._handle, id=id })\nend\n\n--- Set a field of the scene object to a new color value.\n---@deprecated 25.09 Use ``set`` which handle the correct type automatically.\n--- @param name string The name of the field.\n--- @param value Color The new color value.\n--- @usage\n--- local obj = SceneObject.findByName(\"Cube\")\n--- obj:setFieldColorValue(\"Color\", Color.new(1, 0, 0, 1)) -- Sets the object to red.\nfunction SceneObject:setFieldColorValue(name, value)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(value._isColor, \"value is not a Color\")\n name = capitalizeFirstLetter(name)\n _internal.sendMessage('sceneobject.set', { obj=self._handle, name=name, value=value:toData() })\nend\n\n--- Set a field of the scene object to a new number value.\n---@deprecated 25.09 Use ``set`` which handle the correct type automatically.\n--- @param name string The name of the field.\n--- @param value number The new number value.\nfunction SceneObject:setFieldNumberValue(name, value)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(type(value) == \"number\", \"value must be a number\")\n name = capitalizeFirstLetter(name)\n _internal.sendMessage('sceneobject.set', { obj=self._handle, name=name, value=value })\nend\n\n---Set a field of the scene object to a new enum value\n--- @param name string\n--- @param value number\nfunction SceneObject:setFieldEnumValue(name, value)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n name = capitalizeFirstLetter(name)\n _internal.sendMessage('sceneobject.set', { obj=self._handle, name=name, value=value })\nend\n\n---Set a field of the scene object to a new boolean value\n---@deprecated 25.09 Use ``set`` which handle the correct type automatically.\n--- @param name string\n--- @param value boolean\nfunction SceneObject:setFieldBooleanValue(name, value)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n if value then value = true else value = false end\n name = capitalizeFirstLetter(name)\n _internal.sendMessage('sceneobject.set', { obj=self._handle, name=name, value=value })\nend\n\n--- Get the layers of the scene object\n--- @return table A table containing layer indices\nfunction SceneObject:getLayers()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n local internal_layers = self:getFieldValue(\"Layers\")\n __assertTrace(type(internal_layers) == \"table\", \"Expected 'Layers' to be a table\")\n\n -- convert internal 0-based indices to frontend 1-based indices\n local layers = {}\n for _, layer_idx in ipairs(internal_layers) do\n table.insert(layers, layer_idx + 1)\n end\n\n return layers\nend\n\n--- Set the layers of the scene object.\n--- Layers control rendering visibility and interaction with other objects.\n---\n--- @param layers table A table containing layer indices.\n--- @usage\n--- local obj = SceneObject.findByName(\"Character\")\n--- obj:setLayers({1, 3, 5}) -- Assigns the object to layers 1, 3, and 5.\nfunction SceneObject:setLayers(layers)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(type(layers) == \"table\", \"Layers must be provided as a table\")\n self:setFieldValue(\"Layers\", layers)\nend\n\n--- Get the value of a scene object's field as color value.\n---@deprecated 25.09 Use ``get`` which always returns the correct type.\n--- @return Color value of the field\n--- @usage\n--- local obj = SceneObject.findByName(\"Lamp\")\n--- local color = obj:getFieldColorValue(\"Color\")\n--- print(\"Color: \" .. color)\nfunction SceneObject:getFieldColorValue(name)\n return self:getFieldValue(name)\nend\n\n--- Get the value of a scene object's field as number value.\n---@deprecated 25.09 Use ``get`` which always returns the correct type.\n--- @return number value of the field\nfunction SceneObject:getFieldNumberValue(name)\n return self:getFieldValue(name)\nend\n\n--- Get the value of a scene object's field as boolean value.\n---@deprecated 25.09 Use ``get`` which always returns the correct type.\n--- @return boolean value of the field\nfunction SceneObject:getFieldBooleanValue(name)\n return self:getFieldValue(name)\nend\n\n--- Get the value of a scene object's field as string value.\n---@deprecated 25.09 Use ``get`` which always returns the correct type.\n--- @return string value of the field\nfunction SceneObject:getFieldEnumValue(name)\n return self:getFieldValue(name)\nend\n\n--- Get the value of a scene object's field as string value\n--- @return string value of the field\nfunction SceneObject:getFieldEnumOptions(name)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return _internal.sendMessage('sceneobject.getFieldOptions', { obj=self._handle, name=name })\nend\n\n--- Set a field of the scene object to a new boolean value.\n---@deprecated 25.09 Use ``set`` which handle the correct type automatically.\n--- @param name string The name of the field.\n--- @param value boolean The new boolean value.\n--- @usage\n--- local obj = SceneObject.findByName(\"Door\")\n--- obj:setFieldBooleanValue(\"PhysicsEnabled\", true)\nfunction SceneObject:setFieldBooleanValue(name, value)\n self:setFieldValue(name, not not value)\nend\n\n---Get a link of the scene object to a new value\n---@deprecated 25.11 Use ``get`` which handles items now too\n--- @param name string name of the link field\n--- @return number handle of the linked item or nil if not set\nfunction SceneObject:getLinkItem(name)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n name = capitalizeFirstLetter(name)\n return handle.createObject(_internal.sendMessage('sceneobject.getLinkItem', { obj=self._handle, name=name }))\nend\n\n---Set a link from the scene object\n---@deprecated 25.11 Use ``set`` which handles items now too\n--- @param name string name of the link field\n--- @param item string value can be either an item ID or an Item object\nfunction SceneObject:setLinkItem(name, item)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(not item or (item and item._handle and item._isItem), \"item has to be an object of Item class or nil!\");\n name = capitalizeFirstLetter(name)\n _internal.sendMessage('sceneobject.setLinkItem', { obj=self._handle, name=name, item=item })\nend\n\n--- Set the position of the scene object.\n--- Sets the object's local position to the given coordinates.\n---\n--- @param x number Position along the X-axis.\n--- @param y number Position along the Y-axis.\n--- @param z number Position along the Z-axis.\n--- @usage\n--- local obj = SceneObject.findByName(\"Player\")\n--- if obj then\n--- obj:setPosition(0, 5, 0) -- Places the object at (0, 5, 0)\n--- end\nfunction SceneObject:setPosition(x, y, z)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n\n _internal.sendMessage('sceneobject.setPosition', {\n obj = self._handle,\n x = x,\n y = y,\n z = z\n })\nend\n\n--- Translate (move) the scene object by an offset.\n--- Adds the given offset to the object's current local position.\n---\n--- @param x number Offset along the X-axis.\n--- @param y number Offset along the Y-axis.\n--- @param z number Offset along the Z-axis.\n--- @usage\n--- local obj = SceneObject.findByName(\"Player\")\n--- if obj then\n--- obj:translate(0, 1, 0) -- Moves the object up by 1 unit from its current position\n--- end\nfunction SceneObject:translate(x, y, z)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.translate', {\n obj = self._handle,\n x = x,\n y = y,\n z = z\n })\nend\n\n--- Rotate the scene object.\n--- Rotation is applied in degrees and follows Euler angles (XYZ order).\n---\n--- @param x number Rotation around the X-axis in degrees.\n--- @param y number Rotation around the Y-axis in degrees.\n--- @param z number Rotation around the Z-axis in degrees.\n--- @usage\n--- local obj = SceneObject.findByName(\"Cube\")\n--- if obj then\n--- obj:rotate(45, 0, 0) -- Rotates 45 degrees around the X-axis\n--- end\nfunction SceneObject:rotate(x, y, z)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.rotate', { obj=self._handle, x=x, y=y, z=z })\nend\n\n--- Scale the scene object.\n---\n--- @param x number Scale factor along the X-axis.\n--- @param y number Scale factor along the Y-axis.\n--- @param z number Scale factor along the Z-axis.\n--- @usage\n--- local obj = SceneObject.findByName(\"Tree\")\n--- if obj then\n--- obj:scale(2, 2, 2) -- Doubles the size of the object\n--- end\nfunction SceneObject:scale(x, y, z)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.scale', { obj=self._handle, x=x, y=y, z=z })\nend\n\n--- Get the local transform of a scene object.\n--- The transform contains position, rotation, and scale relative to the object's parent.\n---\n--- @param cacheLocal boolean if set to true, the transform will be cached locally for the future for performance reasons. This should only be used if no other script changes the transform\n--- @return Transform The object's local transform.\n--- @usage\n--- local obj = SceneObject.findByName(\"Character\")\n--- local transform = obj:getTransform()\n--- print(\"Local position:\", transform.position)\nfunction SceneObject:getTransform(cacheLocal)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n if self._transform == nil or not cacheLocal then\n self._transform = Transform.fromData(_internal.sendMessage('sceneobject.getTransform', { obj=self._handle }))\n end\n return self._transform:clone()\nend\n\n--- Apply a function to modify the object's transform, then update it.\n--- This allows making changes to the transform while ensuring they are properly applied back.\n---\n--- @param func function A function that takes a Transform object as its only argument.\n--- @param cacheLocal boolean if set to true, the transform will be cached locally for the future for performance reasons. This should only be used if no other script changes the transform\n--- @usage\n--- local obj = SceneObject.findByName(\"Cube\")\n--- obj:withTransform(function(transform)\n--- transform.position.x = transform.position.x + 1 -- Move object 1 unit to the right\n--- end)\nfunction SceneObject:withTransform(func, cacheLocal)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(type(func) == 'function', \"argument func is not a function\")\n local transform = self:getTransform(cacheLocal)\n func(transform)\n self:setTransform(transform)\nend\n\n--- Get the world transform of a scene object.\n--- The world transform includes absolute position, rotation, and scale in the scene.\n---\n--- @return Transform The object's world transform.\n--- @usage\n--- local obj = SceneObject.findByName(\"Light\")\n--- local worldTransform = obj:getWorldTransform()\n--- print(\"World position:\", worldTransform.position)\nfunction SceneObject:getWorldTransform()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return Transform.fromData(_internal.sendMessage('sceneobject.getWorldTransform', { obj=self._handle }))\nend\n\n--- Get the forward direction vector in world space.\n--- Returns the local -Z axis transformed by the object's rotation.\n---\n--- @return Vector3 The forward direction in world space.\nfunction SceneObject:getForward()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return Vector3.fromData(_internal.sendMessage('sceneobject.getForward', { obj=self._handle }))\nend\n\n--- Get the right direction vector in world space.\n--- Returns the local +X axis transformed by the object's rotation.\n---\n--- @return Vector3 The right direction in world space.\nfunction SceneObject:getRight()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return Vector3.fromData(_internal.sendMessage('sceneobject.getRight', { obj=self._handle }))\nend\n\n--- Get the up direction vector in world space.\n--- Returns the local +Y axis transformed by the object's rotation.\n---\n--- @return Vector3 The up direction in world space.\nfunction SceneObject:getUp()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return Vector3.fromData(_internal.sendMessage('sceneobject.getUp', { obj=self._handle }))\nend\n\n--- Set the local transform of a scene object.\n--- Updates the object's position, rotation, and scale relative to its parent.\n---\n--- @param transform Transform The new local transform to apply.\n--- @usage\n--- local obj = SceneObject.findByName(\"Door\")\n--- local newTransform = Transform.new()\n--- newTransform.position = Vector3.new(0, 2, 0) -- Move the object up 2 units\n--- obj:setTransform(newTransform)\nfunction SceneObject:setTransform(transform)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(transform and transform._isTransform, \"transform has to be an object of type Transform\")\n self._transform = transform\n _internal.sendMessage('sceneobject.setTransform', { obj=self._handle, transform=self._transform:toData() })\nend\n\n--- Set the physics position of the scene object.\n---\n--- This directly updates the position of the object inside the physics engine.\n--- The position is expressed in world coordinates.\n--- Only works for objects added to the physics engine\n--- @param position Vector3 New world position of the object.\n--- @usage\n--- local obj = SceneObject.findByName(\"Box\")\n--- obj:setPhysicsPosition(Vector3.new(0, 5, 0))\nfunction SceneObject:setPhysicsPosition(position)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(position and position._isVector3, \"position has to be an object of type Vector3\")\n _internal.sendMessage('sceneobject.setPhysicsPosition', { obj=self._handle, position=position:toData() })\nend\n\n--- Set the physics rotation of the scene object.\n---\n--- This directly updates the rotation of the object inside the physics engine.\n--- The rotation is expressed in world space.\n--- Only works for objects added to the physics engine\n--- @param rotation Quaternion New world rotation of the object.\n--- @usage\n--- local obj = SceneObject.findByName(\"Box\")\n--- obj:setPhysicsRotation(Quaternion.fromEuler(0, math.pi, 0))\nfunction SceneObject:setPhysicsRotation(rotation)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(rotation and rotation._isQuaternion, \"rotation has to be an object of type Quaternion\")\n _internal.sendMessage('sceneobject.setPhysicsRotation', { obj=self._handle, rotation=rotation:toData() })\nend\n\nfunction SceneObject:setPhysicsRotation(rotation)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(rotation and rotation._isQuaternion, \"rotation has to be an object of type Quaternion\")\n _internal.sendMessage('sceneobject.setPhysicsRotation', { obj=self._handle, rotation=rotation:toData() })\nend\n\n--- Get the linear velocity of the scene object.\n--- This function returns the object's velocity in world coordinates.\n--- Only works for objects added to the physics engine with `watchLinearVelocity` enabled.\n---\n--- @return Vector3 The linear velocity of the object.\n--- @usage\n--- local obj = SceneObject.findByName(\"Ball\")\n--- local velocity = obj:getLinearVelocity()\n--- print(\"Velocity:\", velocity)\nfunction SceneObject:getLinearVelocity()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return Vector3.fromData(_internal.sendMessage('sceneobject.getLinearVelocity', { obj=self._handle }))\nend\n\n--- Set the linear velocity of the scene object.\n--- This function assigns a new velocity to the object, but only works if the object is in the physics engine.\n---\n--- @param velocity Vector3 The new linear velocity to apply.\n--- @usage\n--- local obj = SceneObject.findByName(\"Car\")\n--- obj:setLinearVelocity(Vector3.new(10, 0, 0)) -- Move object at speed 10 in the X direction.\nfunction SceneObject:setLinearVelocity(velocity)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(velocity and velocity._isVector3, \"velocity must be an object of type Vector3\")\n local data = velocity:toData()\n _internal.sendMessage('sceneobject.setLinearVelocity', { obj=self._handle, velocity=data })\nend\n\n--- Get the angular velocity of the scene object.\n--- Only works for objects added to the physics engine with `watchAngularVelocity` enabled.\n---\n--- @return Vector3 The angular velocity of the object.\n--- @usage\n--- local obj = SceneObject.findByName(\"Wheel\")\n--- local angVel = obj:getAngularVelocity()\n--- print(\"Angular velocity:\", angVel)\nfunction SceneObject:getAngularVelocity()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return Vector3.fromData(_internal.sendMessage('sceneobject.getAngularVelocity', { obj=self._handle }))\nend\n\n--- Set the angular velocity of the scene object.\n---\n--- @param velocity Vector3 The new angular velocity to apply.\n--- @usage\n--- local obj = SceneObject.findByName(\"Propeller\")\n--- obj:setAngularVelocity(Vector3.new(0, 10, 0)) -- Rotate around Y-axis.\nfunction SceneObject:setAngularVelocity(velocity)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(velocity and velocity._isVector3, \"velocity must be an object of type Vector3\")\n local data = velocity:toData()\n _internal.sendMessage('sceneobject.setAngularVelocity', { obj=self._handle, velocity=data })\nend\n\n--- Apply an impulse to the scene object.\n---\n--- An **impulse** is a sudden force applied to an object that immediately\n--- changes its velocity. This is different from a continuous force, which\n--- is applied over time.\n---\n--- This function only works on objects that are physics-enabled.\n---\n--- @param impulse Vector3 The force impulse to apply.\n--- @param relPos Vector3 The relative position where the impulse is applied. Default is (0, 0, 0)\nfunction SceneObject:applyImpulse(impulse, relPos)\n relPos = relPos or Vector3.new(0, 0, 0)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(impulse and impulse._isVector3, \"impulse must be an object of type Vector3\")\n __assertTrace(relPos and relPos._isVector3, \"relPos must be an object of type Vector3\")\n local impulseData = impulse:toData()\n local relPosData = relPos:toData()\n _internal.sendMessage('sceneobject.applyImpulse', { obj=self._handle, impulse=impulseData, relPos=relPosData })\nend\n\n--- Apply an impulse to the scene object that respects constraints.\n---\n--- Only works on physics-enabled objects.\n---\n--- @param impulse Vector3 The force impulse to apply.\n--- @param relPos Vector3 The relative position where the impulse is applied. Default is (0, 0, 0)\nfunction SceneObject:applyPushImpulse(impulse, relPos)\n relPos = relPos or Vector3.new(0, 0, 0)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(impulse and impulse._isVector3, \"impulse must be an object of type Vector3\")\n __assertTrace(relPos and relPos._isVector3, \"relPos must be an object of type Vector3\")\n _internal.sendMessage('sceneobject.applyPushImpulse', { obj=self._handle, impulse=impulse:toData(), relPos=relPos:toData() })\nend\n\n--- Apply an impulse to the center of the object.\n---\n--- Applies a sudden velocity change at the object's center. Only works on physics-enabled objects.\n---\n--- @param impulse Vector3 The force impulse to apply.\n--- @usage\n--- local obj = SceneObject.findByName(\"Box\")\n--- obj:applyCentralImpulse(Vector3.new(0, 5, 0)) -- Apply upward impulse.\nfunction SceneObject:applyCentralImpulse(impulse)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(impulse and impulse._isVector3, \"impulse must be an object of type Vector3\")\n _internal.sendMessage('sceneobject.applyCentralImpulse', { obj=self._handle, impulse=impulse:toData() })\nend\n\n--- Apply a central impulse that respects constraints.\n---\n--- Only works on physics-enabled objects and respects any joint or constraint applied.\n---\n--- @param impulse Vector3 The force impulse to apply.\n--- @usage\n--- local obj = SceneObject.findByName(\"Box\")\n--- obj:applyCentralPushImpulse(Vector3.new(0, 5, 0)) -- Apply upward impulse.\nfunction SceneObject:applyCentralPushImpulse(impulse)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(impulse and impulse._isVector3, \"impulse must be an object of type Vector3\")\n _internal.sendMessage('sceneobject.applyCentralPushImpulse', { obj=self._handle, impulse=impulse:toData() })\nend\n\n--- Apply a force to the scene object.\n---\n--- Unlike impulses, **forces** are applied over time, influencing the object's acceleration.\n--- Only works on physics-enabled objects.\n---\n--- @param force Vector3 The force vector to apply.\n--- @param relPos Vector3 The relative position where the force is applied. Default is (0, 0, 0)\nfunction SceneObject:applyForce(force, relPos)\n relPos = relPos or Vector3.new(0, 0, 0)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(force and force._isVector3, \"force must be an object of type Vector3\")\n __assertTrace(relPos and relPos._isVector3, \"relPos must be an object of type Vector3\")\n _internal.sendMessage('sceneobject.applyForce', { obj=self._handle, force=force:toData(), relPos=relPos:toData() })\nend\n\n--- Clear any forces on a scene object.\n--- Only works on physics-enabled objects.\n--- @usage\n--- local obj = SceneObject.findByName(\"Ball\")\n--- obj:clearForces()\nfunction SceneObject:clearForces()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.clearForces', { obj=self._handle })\nend\n\n--- Delete the scene object from the scene.\n---\n--- @usage\n--- local obj = scene:findByName(\"TemporaryObject\")\n--- if obj then\n--- obj:delete()\n--- end\nfunction SceneObject:delete()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.delete', { obj=self._handle })\n if self._handle then\n handle.invalidate(self._handle)\n end\n self._handle = nil\nend\n\n--- Clone a scene object to a new position and return it.\n---\n--- The position can be provided as:\n--- 1. Separate `x, y, z` coordinates.\n--- 2. A `Vector3` containing the position.\n---\n--- If no position is provided, the clone will appear in the same location as the original.\n---\n--- @param x number | Vector3 The X-coordinate of the new position, or a `Vector3` containing `x, y, z`. If a `Vector3` is passed, `y` and `z` should be omitted.\n--- @param y number (optional) The Y-coordinate of the new position. Omit this if passing a `Vector3`.\n--- @param z number (optional) The Z-coordinate of the new position. Omit this if passing a `Vector3`.\n--- @param parent SceneObject (optional) The parent object to attach the new object to.\n--- @return Promise A promise resolving to a SceneObject.\n---\n--- @usage\n--- local obj = scene:findByName(\"Tree\")\n--- obj:clone(5, 0, 2):next(function(clone)\n--- clone:setActive(true) -- Activate the cloned object\n--- end)\n---\n--- local pos = Vector3.new(3, 1, 0)\n--- obj:clone(pos):next(function(clone)\n--- clone:setFieldValue(\"Name\", \"ClonedTree\") -- Rename the cloned object\n--- end)\n---\n--- local parentObj = scene:findByName(\"Group\")\n--- obj:clone(pos, parentObj):next(function(clone)\n--- clone:setFieldValue(\"Name\", \"AnotherClonedTree\") -- Rename the cloned object\n--- end)\nfunction SceneObject:clone(x, y, z, parent)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n\n -- Handle case where first argument is a Vector3 and second argument is the parent\n if type(x) == \"table\" and x._isVector3 then\n x, y, z, parent = x.x, x.y, x.z, y -- Shift arguments\n end\n\n local p = _internal.sendMessage('sceneobject.clone', {\n obj = self._handle,\n x = x,\n y = y,\n z = z,\n parent = parent and parent._handle or nil\n })\n\n return Promise:new(p)\nend\n\n--- Create a new object of same type from this one. If no options are specified this will be basically a clone.\n--- If an options table is passed, this will specify all fields with their values that should be changed.\n--- At the very least, this should contain a new Transform, otherwise the new object will be at the exact location\n--- of the old one.\n--- @param options table the table of fields to set on the new object\n--- @param parent SceneObject (optional) The parent object to attach the new object to.\n--- @return Promise A promise resolving to a SceneObject.\n---\n--- @usage\n--- local obj = SceneObject.findByName(\"Tree\")\n--- local parentObj = SceneObject.findByName(\"Group\")\n--- local clone = await(obj:createFromThis({\n--- transform = Transform.new(Vector3.new(4, 6, 8)),\n--- active = true,\n--- name = \"Cloned tree\"\n--- }, parentObj)\nfunction SceneObject:createFromThis(options, parent)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(parent == nil or parent._isSceneObject, \"parent is not nil or a SceneObject\")\n\n local p = _internal.sendMessage('sceneobject.createFromThis', {\n obj = self._handle,\n options = options,\n parent = parent and parent._handle or nil\n })\n\n return Promise:new(p)\nend\n\n--- Move the scene object to a different parent. This parent can not be a child of the scene object\n--- @param parent SceneObject (optional) The parent object to attach the new object to. if nil, the object will be moved to the scenegraph root\nfunction SceneObject:moveToParent(parent)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(not parent or parent._isSceneObject, \"parent is not a SceneObject\")\n _internal.sendMessage('sceneobject.moveToParent', { obj = self._handle, parent = parent and parent._handle or nil })\nend\n\n--- Set the active state of the scene object.\n--- @param value boolean True to activate, false to deactivate.\nfunction SceneObject:setActive(value)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n self:setFieldValue(\"Active\", value)\nend\n\n--- Get the active state of the scene object.\n--- @return boolean True if the object is active, false otherwise.\nfunction SceneObject:getActive()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return self:getFieldBooleanValue(\"Active\")\nend\n\n--- Check if the object has a specific tag.\n--- @param tagName string The tag name to check.\n--- @return boolean True if the object has the tag, false otherwise.\nfunction SceneObject:hasTag(tagName)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n local tags = self:getFieldValue(\"Tags\")\n for _, value in pairs(tags) do\n if value == tagName then\n return true\n end\n end\n return false\nend\n\n--- Get a list of all tags assigned to the object.\n--- @return table An array of tag names.\nfunction SceneObject:getTags()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return self:getFieldValue(\"Tags\")\nend\n\n--- Assign a set of tags to the object.\n--- @param tags table An array of tag names.\nfunction SceneObject:setTags(tags)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n Tools.validateArrayOfStrings(tags)\n self:setFieldValue(\"Tags\", tags)\nend\n\n--- Add a tag to the object.\n--- @param tag string The tag to add.\nfunction SceneObject:addTag(tag)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(type(tag) == 'string', \"tag needs to be a string\")\n local tags = self:getTags()\n table.insert(tags, tag)\n self:setTags(tags)\nend\n\n--- Remove all tags from the object.\nfunction SceneObject:clearTags()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n self:setTags({})\nend\n\n--- Take a snapshot of the object and its children, if specified.\n--- This allows restoring the object to a previous state.\n--- @param recursive boolean Whether to include child objects in the snapshot.\n--- @return number The snapshot handle.\nfunction SceneObject:takeSnapshot(recursive)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return _internal.sendMessage('sceneobject.takeSnapshot', { obj=self._handle, recursive=recursive })\nend\n\n--- Restore a snapshot, reverting any changes made after the snapshot was taken.\n--- @param snapshot number The snapshot handle to restore.\nfunction SceneObject:rollbackSnapshot(snapshot)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.rollbackSnapshot', { obj=self._handle, snapshot=snapshot })\nend\n\n--- Check if the object is enabled.\n--- @return boolean True if the object is enabled, false otherwise.\nfunction SceneObject:isEnabled()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return _internal.sendMessage('sceneobject.isEnabled', { obj=self._handle })\nend\n\n--- Enable the object.\nfunction SceneObject:enable()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.enable', { obj=self._handle })\nend\n\n--- Disable the object.\nfunction SceneObject:disable()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.disable', { obj=self._handle })\nend\n\n--- Get the material assigned to the object.\n--- @return Material The material, or nil if none is assigned.\nfunction SceneObject:getMaterial()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return self:getLinkItem(\"Material\")\nend\n\n--- Set the object's material.\n--- @param mat Material The material to set or nil to clear the material\nfunction SceneObject:setMaterial(mat)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n __assertTrace(mat == nil or (type(mat) == 'table' and mat._isMaterial), \"self is not a SceneObject\")\n self:setLinkItem(\"Material\", mat)\nend\n\n--- Get all behaviours attached to this object.\n---@deprecated 25.12 use getBehaviours.\n--- @return table A list of Behaviour objects.\nfunction SceneObject:getEntities()\n return self:getBehaviours()\nend\n\n--- Get all behaviours attached to this object.\n--- @return table A list of Behaviour objects.\nfunction SceneObject:getBehaviours()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n if not self._nodeId then\n error(\"This scene object does not have a node Id. Cannot get its behaviours.\")\n end\n return Behaviour.getByNodeId(self._nodeId)\nend\n\n--- Get a behaviour attached to this object by its script name.\n--- @param name string The script name.\n--- @return Behaviour The corresponding behaviour, or nil if not found.\nfunction SceneObject:getBehaviourByScriptName(name)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n if not self._nodeId then\n error(\"This scene object does not have a node Id. Cannot get its entities.\")\n end\n return Behaviour.getByNodeIdAndScriptName(self._nodeId, name)\nend\n\n--- Get a behaviour attached to this object by its script name.\n---@deprecated\n--- @param name string The script name.\n--- @return Behaviour The corresponding behaviour, or nil if not found.\nfunction SceneObject:getEntityByScriptName(name)\n return self:getBehaviourByScriptName(name)\nend\n\n--- Get a behaviour attached to this object by its script name.\n--- @param name string The script name.\n--- @return Behaviour The corresponding behaviour, or nil if not found.\nfunction SceneObject:behaviour(name)\n return self:getBehaviourByScriptName(name)\nend\n\n--- Get a behaviour attached to this object by its script name.\n--- @param name string The script name.\n--- @return Behaviour The corresponding behaviour, or nil if not found.\nfunction SceneObject:entity(name)\n return self:getBehaviourByScriptName(name)\nend\n\n--- Get the parent of the object.\n--- @return SceneObject The parent object, or nil if none exists.\nfunction SceneObject:getParent()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n return handle.createObject(_internal.sendMessage('sceneobject.getParent', { obj=self._handle }))\nend\n\n--- Get the children of the object.\n--- @return table A table of child objects.\nfunction SceneObject:getChildren()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n local objects = _internal.sendMessage('sceneobject.getChildren', { obj=self._handle })\n for key, value in pairs(objects) do\n objects[key] = handle.createObject(value)\n end\n return objects\nend\n\n--- Enable or disable automatic recreation of the scene object between play and stop.\n---\n--- When `value` is true, the object will be automatically recreated every time\n--- the scene starts or stops. This is useful for objects that need to reset\n--- physics, state, or procedural components at each run.\n---\n--- @param value boolean Enable (`true`) or disable (`false`) automatic recreation.\nfunction SceneObject:setAutoRecreate(value)\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.setAutoRecreate', { obj=self._handle, value=value })\nend\n\n--- Immediately recreate the scene object.\n---\n--- This forces the object to reset its state, physics, and components as if\n--- it was newly instantiated in the scene.\nfunction SceneObject:recreate()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n _internal.sendMessage('sceneobject.recreate', { obj=self._handle })\nend\n\n--- Return the bounds of the scene object\n---@return Bounds the bounds of the object\nfunction SceneObject:getBounds()\n __assertTrace(self._isSceneObject, \"self is not a SceneObject\")\n local bounds = _internal.sendMessage('sceneobject.getBounds', { obj=self._handle })\n local center = Vector3.new(bounds.center.x, bounds.center.y, bounds.center.z)\n local size = Vector3.new(bounds.size.x, bounds.size.y, bounds.size.z)\n return Bounds.new(center, size)\nend\n\nreturn SceneObject\n";
131581
131581
 
131582
131582
  var lua_api_sceneobjects_audio = "\nlocal _internal = require('engine/_internal')\nlocal SceneObject = require('engine/sceneobject')\n\n------------------------------------------------------------------\n-- SgAudio class\n------------------------------------------------------------------\n\n--- @class SgAudio\nlocal SgAudio = Class.new(SceneObject)\n\n--- @private\nfunction SgAudio:__new(handle, nodeId)\n SceneObject.__new(self, handle, nodeId)\n self._type = \"SgAudio\"\n self._isSgAudio = true\nend\n\n--- Create a new audio object in the scenegraph.\n---@param options table A table of options to fill the parameters\n---@param parent SceneObject The parent scene object to add this to\n---@return Promise a promise which will resolve to the created object\n---@usage\n--- -- Options can be omitted. This example shows the defaults,\n--- -- only specify the ones you want different.\n--- SgAudio.create({\n--- active = true,\n--- name = \"\",\n--- transform = Transform.new(),\n--- layers = {0},\n--- tags = {},\n--- autoplay = false,\n--- loop = false,\n--- volume = 1,\n--- panning = 0,\n--- playbackRate = 1,\n--- offset = 0\n--- })\nfunction SgAudio.create(self, options, parent)\n -- check if static function was called as method. if so, shift the parameters\n if not (type(self) == 'table' and type(self.new) == 'function') then\n parent, options = options, self\n end\n return SceneObject.create(\"SgAudio\", options, parent)\nend\n\n--- playback the audio\n---@param force boolean Force the sound to replay even if it is already playing\nfunction SgAudio:play(force)\n assert(self._isSgAudio, \"self is not an SgAudio object\")\n _internal.sendMessage('sgaudio.play', { obj=self._handle, force=force })\nend\n\n--- stop the audio\nfunction SgAudio:stop()\n assert(self._isSgAudio, \"self is not an SgAudio object\")\n _internal.sendMessage('sgaudio.stop', { obj=self._handle })\nend\n\n--- Checks if the audio is playing\n---@return boolean true, if sound is playing, false otherwise\nfunction SgAudio:isPlaying()\n assert(self._isSgAudio, \"self is not an SgAudio object\")\n return _internal.sendMessage('sgaudio.isPlaying', { obj=self._handle })\nend\n\nfunction SgAudio:setPlaybackRate(value)\n assert(self._isSgAudio, \"self is not an SgAudio object\")\n self:setFieldNumberValue(\"PlaybackRate\", value)\nend\n\nfunction SgAudio:getPlaybackRate()\n assert(self._isSgAudio, \"self is not an SgAudio object\")\n return self:getFieldNumberValue(\"PlaybackRate\");\nend\n\nfunction SgAudio:getAudioCollectionEntry()\n assert(self._isSgAudio, \"self is not an SgAudio object\")\n return self:getFieldValue(\"AudioCollectionEntry\")\nend\n\nfunction SgAudio:setAudioCollectionEntry(name)\n assert(self._isSgAudio, \"self is not an SgAudio object\")\n self:setFieldValue(\"AudioCollectionEntry\", name)\nend\n\nreturn SgAudio\n";
131583
131583
 
@@ -132126,6 +132126,28 @@ class RtCanvas extends RtBase {
132126
132126
  reset() {
132127
132127
  this._ctx().reset();
132128
132128
  }
132129
+ commit(params) {
132130
+ const cmds = Array.isArray(params?.cmds) ? params.cmds : [];
132131
+ for (const cmd of cmds) {
132132
+ const target = cmd?.target;
132133
+ if (typeof target !== 'string')
132134
+ continue;
132135
+ if (!target.startsWith('canvas.'))
132136
+ continue;
132137
+ const funcName = target.substring('canvas.'.length);
132138
+ if (!funcName || funcName === 'commit' || funcName === 'getLastCommittedCommands')
132139
+ continue;
132140
+ const fn = this[funcName];
132141
+ if (typeof fn !== 'function') {
132142
+ console.warn('Unknown canvas command in commit:', target, cmd);
132143
+ continue;
132144
+ }
132145
+ fn.call(this, cmd.payload || {});
132146
+ }
132147
+ return {
132148
+ committed: cmds.length,
132149
+ };
132150
+ }
132129
132151
  }
132130
132152
 
132131
132153
  // -----------------------------------------------------------------------------------------------------------
@@ -134288,7 +134310,7 @@ class RtSceneObject extends RtBase {
134288
134310
  bbox.getSize(size);
134289
134311
  return {
134290
134312
  center: center,
134291
- extents: size.multiplyScalar(0.5)
134313
+ size: size
134292
134314
  };
134293
134315
  }
134294
134316
  }