Scripting with Lua needs:
A level's script is named after the level it belongs to. For a level that loads
level1.phd, create scripts/level1.lua in the game's directory and put the
following content in it:
trx.events.on_game_start(function()
trx.log.info("hello from level 1!")
end)
Nothing declares the file: the game flow names the level, and the script beside it under that name runs as the level loads.
Start the game. The logs should show the following:
INF | 2025-10-04 12:12:23.155 [scripts/level1.lua:2:?] hello from level 1!
A game can also ship one script that belongs to the game rather than to any of
its levels. Create scripts/_game.lua in the game's directory and it runs once
as the game starts, with the game flow, the strings and the settings all in
place. Nothing declares it: the file being there is what runs it. It is where a
game declares settings of its own, with trx.config.declare.
An expansion that has nothing of its own to set up needs no file: the script of the game it extends runs instead. Shipping one replaces that script rather than adding to it, so anything worth keeping goes in a module both require.
A script can put what it has in common with another in a file of its own and require it. A name carries the directory it lives in, so a call site says which file it means:
local mine = require("tr1.my_module") -- games/tr1/modules/my_module.lua
local nested = require("tr1.my_group.my_module") -- games/tr1/modules/my_group/my_module.lua
local other = require("tr1-ub.my_module") -- any installed game, by its directory name
local pooled = require("common.my_module") -- modules/my_module.lua, beside the engine
A module lives in modules/, alongside the scripts/ the engine runs, and a
name reaches modules/ alone: there is no name for a level script or for
_game.lua. common is the pool every game can reach, in modules/ next to
the executable; create the directory if it is not there. A game's own directory
is named the way it appears in games/, so a script says which game it is
reaching into and gets the same file whichever game is running. trx is
reserved for the engine's own modules, which are the global trx table rather
than something to require.
A required script runs once, and every later call is handed what the first one returned:
-- modules/my_module.lua
local M = {}
function M.greet() trx.log.info("hello from my_module") end
return M
Because each form names one directory, the same name in two of them is two
separate modules, and a game's own script is never mistaken for one it extends.
A script required by a level script belongs to that level: it runs again for
the next one, so anything it attaches is there for each level in turn. One
required by _game.lua runs once for the game. The first require of a name
decides which of the two it is, and every require after it is handed that
module, so the game and a level never hold two copies of one name.
Names hold letters, digits, _, - and the . that separates directories.
Case does not count, so a name spelled two ways is one module. There is no way
to name a file outside the directory the form points at.
The /lua console command also runs short Lua commands in-game:
/lua