A value that can notify listeners when it changes.
A signal lets a script react to changes without polling every frame. You can
read the current value, listen for changes, and combine signals with &,
| and ~.
Setting a signal to its current value does nothing: listeners do not run, and signals derived from it do not update. This keeps combined signals cheap to listen to. A combined signal fires only when its own result changes, not every time one of its inputs changes.
A derived signal is read like any other signal, so one expression can provide both the current result and change notifications.
Signals should carry numbers, strings or booleans, not handles. Handles are created fresh on each read, so two reads of the same handle do not compare equal and would make the signal report a change every frame. When a signal represents an engine-owned object, it carries the object's numeric id; listeners can then read the handle when they need it.
Signals stay idle until something uses them: a polled signal starts reading only when it is created.
What a script makes lasts as follows:
trx.signal.Signal:map,
trx.signal.combine, an operator or trx.signal.polled, stops when the
level ends. One that a global script derives follows its sources while
something holds it or listens to it, or until trx.signal.Signal:stop.trx.events
says.trx.signal.Signal:on stays attached until
trx.signal.Listener:detach, whichever script attached it.trx.game.signals, last the session.trx.signal.tick (trx.signal.Signal). A signal that increments on every engine tick, regardless of what is on screen. Anything listening to it runs every tick.
Use this when a script needs to poll state that has no dedicated signal,
such as Lara's current position. A dedicated signal is cheaper when one
exists, because this one wakes listeners even when the state they care
about has not changed. (read-only)A value that notifies listeners when it changes.
Operators:
signal & signal. Both signals are true. Fires when the result changes.~signal. The signal is not true: ~trx.cutscenes.signals.is_playing.signal | signal. Either signal is true. Fires when the result changes.Methods:
signal:eq(value)
Whether the signal holds this value.
Parameters:
Returns: trx.signal.Signal. The derived signal.
signal:get()
The value the signal holds now.
Returns: any. What it holds.
signal:map(fn)
Creates a signal by applying a function to this signal's value.
Use this for derived values that are not simple boolean combinations, such as a bar fill amount or resolved key text.
Parameters:
Returns: trx.signal.Signal. The derived signal.
signal:on(fn)
Calls the handler with the new value whenever the signal changes. Attaching
a listener does not call it immediately; read the signal directly when you
need its current value.
The listener stays attached until trx.signal.Listener:detach, even when a
level script attaches it, so that it can follow an engine signal past the
level. A level script that needs the value for its own level only can
derive a signal instead, which stops with the level, or detach the listener
in trx.events.on_level_unload.
Parameters:
Returns: trx.signal.Listener. The listener handle used to detach later.
signal:set(value)
Sets the signal's value. Setting the current value again does nothing, so
repeated writes are cheap.
Parameters:
Returns: boolean. Whether the value changed and listeners ran.
signal:stop()
Stops a derived signal from following its sources. It keeps its last value
and will not update again. Signals made by level scripts stop when the level
ends; global scripts can call this to stop one earlier.
Returns: boolean. Whether the signal was still following any sources.
A signal listener that can be detached later.
Methods:
trx.signal.polled(read)
Creates a signal by reading a value once per tick and notifying listeners
only when that value changes.
Use this for state that has no dedicated engine signal. The read function runs every tick, but listeners run only on changes, so several listeners on one polled signal share one read.
Parameters:
read (function). The function to read each tick. Tables compare by identity, so returning a fresh table every tick reports a change every tick.Returns: trx.signal.Signal. The polled signal.
trx.signal.polled_while(shown, read, idle)
Creates a signal that reads a value once per tick while another signal is
true, and holds a fixed value while it is not.
Use this for state that has no dedicated engine signal and matters only while something shows it, such as a readout that the player can turn off. A reader that is off costs nothing per tick.
Parameters:
shown (trx.signal.Signal). Whether to read.read (function). The function to read each tick while the first signal is true.idle (any). The value to hold while the first signal is false.Returns: trx.signal.Signal. The signal.
Example:
local shown = trx.signal.config("ui.enable_fps_counter")
local fps = trx.signal.polled_while(shown, trx.game.get_fps, 0)
trx.signal.config(key)
Returns a signal for a config setting.
The signal holds the setting's current value and updates whenever the player or a script changes it. Asking for the same setting twice returns the same signal.
Parameters:
key (string). Dotted setting path, as accepted by trx.config.get.Returns: trx.signal.Signal. The setting's signal.
trx.signal.combine(...)
Creates a signal by applying a function to several source signals.
Pass the signals first and the function last. trx.signal.Signal:map is the
one-signal version. For boolean combinations, &, | and ~ are shorter.
Parameters:
... (trx.signal.Signal or function). The signals to read, in the order the function takes them, then the function that computes the derived value.Returns: trx.signal.Signal. The derived signal.
Example:
local fill = trx.signal.combine(
trx.lara.signals.hp,
trx.lara.signals.max_hp,
function(hp, max_hp)
return hp / max_hp
end
)
trx.signal.new([value])
Creates a script-owned signal with an initial value.
Parameters:
Returns: trx.signal.Signal. The new signal.