Jump to content



Recommended Posts

As announced last week, the Panacea update is now in development. Today, we’re going to tell you more about the camera Lua API that’s among the many new features and improvements.


This addition has been a longtime request from the community. The time for it seemed ripe since the removal of quaternions and the standardization of orientation methods to have a coherent and consistent whole.




This API will allow you to get much more information about the game's camera and the status of the player linked to it. You will be able to get the position and orientation of the camera in the world coordinate system, but it'll also be expressed in the construct local coordinate system. 


You will find at the end of the devblog the example corresponding to the image.


For these changes, we have added under unit:

  • [int] unit.isMasterPlayerSeated(): Checks if the player currently running the control unit is seated.
  • [int] unit.getMasterPlayerSeatId(): Returns the UID of the seat on which the player currently running the control unit is sitting.


And so under system:

  • [event] system.cameraChanged([int] mode): Emitted when the player changes the camera mode.
  • [number] system.getCameraHorizontalFov(): Return the current value of the player's horizontal field of view
  • [number] system.getCameraVerticalFov(): Return the current value of the player's vertical field of view
  • [int] system.getCameraMode(): Returns the active camera mode.
  • [int] system.isFirstPerson(): Checks if the active camera is in first person view.
  • [vec3] system.getCameraPos(): Returns the position of the camera in the construct’s local coordinates.
  • [vec3] system.getCameraWorldPos(): Returns the position of the camera in world coordinates.
  • [vec3] system.getCameraWorldForward(): Returns the forward direction vector of the active camera in world coordinates.
  • [vec3] system.getCameraWorldRight(): Returns the right direction vector of the active camera in world coordinates.
  • [vec3] system.getCameraWorldUp(): Returns the up direction vector of the active camera in world coordinates.
  • [vec3] system.getCameraForward(): Returns the forward direction vector of the active camera in the construct’s local coordinates.
  • [vec3] system.getCameraRight(): Returns the right direction vector of the active camera in the construct’s local coordinates.
  • [vec3] system.getCameraUp(): Returns the up direction vector of the active camera in the construct’s local coordinates.


We took advantage of these changes to fix an inconsistency between the positions returned by unit.getMasterPlayerWorldPosition and system.getPlayerWorldPosition. These two functions were not returning the same position; they will now return the position of the player's feet.

Note: As you may have guessed, the system.getFov function will be deprecated in favor of the consistency of getCameraHorizontalFov.




We have decided to remove the ability to write lines directly to the clients log files using Lua. Following a long review, the decision has been made with security concerns in mind as it allowed some nefarious players, for example, to automate mechanics that are not intended to be automated, allowed the creation of bots, and left a door open for players to maliciously inflate the players log files. 


The following functions are now completely removed:

  • system.logInfo()
  • system.logWarning()
  • system.logError()


This feature was never really supported (and notably absent in the codex); however, it is unfortunate that it also impacts less malicious creations. I'm thinking in particular of the audio framework created by the player ZarTaen, used in the popular Arch Orbital HUD flight system, created by the players Dimencia and Archaegeo.

For that reason and as a first iteration, we are integrating an API that will allow you to play sounds from Lua. It’s got a very basic functionality now that allows for growth in the future.

  • system.playSound([string] filePath) : Play a sound file from inside the audio folder in your user folder. Only one sound at the same time.
  • system.stopSound(): Stop the playing sound.


For obvious reasons, the playing of sounds is restricted to control units that are run explicitly by pressing F on them. You will find the user folder in the same folder where you will find the holograms or screenshots folder.

Said folder can be found by navigating to “My Documents/NQ/DualUniverse/audio”, and sub folders can be used to organize and bundle audio files.




As always, we hope you like these new features. Please feel free to come and share your thoughts and ideas, and let us know what Lua-related improvements you would like to see in the future. NQ-Ligo looks forward to chatting with you in this forum thread!



Below is an example of Lua code using the new API for the camera. This example allows you to parse the element you are looking at and get Lua information such as the element name, class, methods and fields.

To use this example, you will need to link to a controller (e.g. a programming board) and name two slots ‘core’ (for the core unit) and ‘slotIndustry’ (for an industrial unit in this example).




Under unit.start

--Declaration of globals
tooltip = {name="",class="", dist=0, methods={}, fields={}}
target = nil
slotIndustry.size = 1.25 --Setup the size of the linked industrie element, with the slot named slotIndustry

--Custom function declaration
function isLinked(id)
    for k,s in pairs(unit) do
        if type(s) == 'table' and s.getId and s.getId() == id then
            return true, s
    return false

--Initial setup



Under unit.tick(‘compute’)

--Declaration of local math functions
local acos, tan, sqrt, deg = math.acos, math.tan, math.sqrt, math.deg

--Get elements on the construct and useful positions
local ids = core.getElementIdList()
local ppos = vec3(unit.getMasterPlayerPosition())
local cpos = vec3(system.getCameraPos())
local cFwd = vec3(system.getCameraForward())

--Set tooltip info by defaults
tooltip = {name="",class="", dist=0}
target = nil

--Iterate on all elements ids
for k,id in pairs(ids) do
    local pos = vec3(core.getElementPositionById(id))

    local dir = pos-cpos
    local dist = dir:len()
    local angle = dir:angle_between(cFwd)

    local r, el = isLinked(id)
    --Check if the player is looking at the element
    if el and angle < tan((el.size or 0)/dist) or angle < tan(1/dist) then
        --Register default data info
        tooltip.name = core.getElementNameById(id)
        tooltip.class = core.getElementTypeById(id)
        tooltip.dist = dist
        tooltip.methods = {}
        tooltip.fields = {}

        --If the element is linked scan the methods and fields of the element
        if r then
            local km, kf = 1,1
            for key,p in pairs(el) do
                if type(p) == 'function' then
                    tooltip.methods[km] = {name = key, value = ''}
                    km = km+1
                    tooltip.fields[kf] = {name = key, value = p}
                    kf = kf+1

            --Sort the methods and field by alphabetically
            table.sort(tooltip.methods, function(a,b) return a.name < b.name end)
            table.sort(tooltip.fields, function(a,b) return a.name < b.name end)


Under update:

 --If not looking at any element, do not draw on update
if tooltip.name=="" then return end

 --Compute the tooltip frame height
local height = 90+ (#tooltip.methods + #tooltip.fields)*14

--Build the interface in SVG
local svg = {
    [[<style>* {border:1px solid red} #node{ transform: translate(50%, 50%);} svg {position: absolute; left:0px; top:0px;}]],
    [[text {font-size:1vh; text-anchor:start; fill:#ccc}]],
    [[text#eName {font-size:1.25vh; fill:#fff}]],
    [[text#eClass {fill:#ccc; }]],
    [[text.pName {fill:#fff; }]],
    [[text.pValue {text-anchor:end; fill:#ccc; }]],
    [[<svg width=100% height=100% viewBox="0 0 1920 1080">
    <g id="node">]],
        string.format([[<rect x=150 y=%.2f fill=rgba(0,0,0,0.5) width=300 height=%.2f />]], -height/2, height),
        string.format([[<text id="eName" x=160 y=%.2f >%s</text>]], -height/2 +20, tooltip.name),
        string.format([[<text id="eClass" font-style="italic" x=170 y=%.2f >%s</text>]], -height/2 + 40, tooltip.class),
        string.format([[<text id="eName" x=160 y=%.2f >Methods</text>]], -height/2 + 60)

--Compose the list of methods of the element
local cSVG, yMethods = #svg, 0
if tooltip.methods then
    for k,m in pairs(tooltip.methods) do
        yMethods = -height/2 + 60 + k*14
        svg[cSVG+k] = string.format('<text class="pName" x=170 y=%.2f >%s</text>',

cSVG = #svg+1
yMethods = yMethods +20
svg[cSVG] = string.format([[<text id="eName" x=160 y=%.2f >Fields</text>]], yMethods)

--Compose the list of fields of the element
if tooltip.fields then
    for k,f in pairs(tooltip.fields) do
        local sValue = ""
        local yFields = yMethods + k*14

        if type(f.value) == 'number' then
            sValue = string.format('%.4f', f.value)
        elseif type(f.value) == 'boolean' then
           sValue = string.format('%s', f.value and 'true' or 'false')
        elseif type(f.value) == 'string' then
            sValue = f.value
            sValue = tostring(f.value)

        svg[cSVG+k] = string.format('<text class="pName" x=170 y=%.2f >%s</text><text class="pValue" x=440 y=%.2f >%s</text>',

svg[#svg+1] = [[</g></svg>]]

--Concat and display the SVG



Link to comment
Share on other sites

This topic is now closed to further replies.

  • Create New...