The Lua API was rewritten, and most of what it breaks is a rename. Run your
scripts with trx.api.strict() turned on and fix what it reports, then read
"Changed behavior" below - those are the changes that leave a script running
and doing something else.
Name Lua scripts after what they belong to
A game flow no longer declares its scripts; both the global main_script
key and each level's script key were removed. A level loading wall.tr2
runs scripts/wall.lua in its own game's directory, so rename any script
whose name does not already match its level. What main_script pointed at
goes in scripts/_game.lua, which the game runs as it starts.
A game that extends another looks in its own directory alone, and brings
its own copy of any level script it wants. One that ships no _game.lua
runs its base game's.
Update Lara's outfit definitions
The braid entries became arrays, to support up to two, and a
joints_object can be given for TR4/5 outfits. Update outfits.json5
against the shipped file and the outfits documentation.
Update weapon definitions
The ammunition keys in weapons.json5 were renamed to say what they count.
A shot is one pull of the trigger, which for the shotgun spends six rounds;
the flare counts a flare where a weapon counts a shot. The old names are
still read, so a file that keeps them goes on working.
initial_qty is now initial_shotspickup_qty is now box_shotsinventory_qty is now box_label_qtyammo.pickup_qty_alt is ignored. It only applied to flares in Japanese NG,
which is no longer a game mode, and a flare box now always gives
ammo.pickup_qty flares.
Update game flows that anchor Bacon Lara
The setup_bacon_lara sequence event was removed. The anchor room is an
anchor_room object property now, which a level editor can set on the
object or on a single item, and a script can set in on_game_start:
trx.objects.bacon_lara.properties.anchor_room = 10
At its default of -1, the room Bacon Lara is placed in is the anchor. Refer to the Atlantis level in the default game flow.
Update TR3 artefact pickups and plinth scions
The glow color and rotation speed of TR3 artefacts are Lua properties now
rather than hardcoded, and an O_SCION_ITEM_1 pickup needs a pickup mode
of PLINTH_SCION to invoke Lara's extra animation. Refer to the OG Lua
scripts.
Mechanical, one for one:
| 1.9 | 1.10 |
|---|---|
trx.items.fn.get() |
trx.items.get() |
trx.rooms.fn.get() |
trx.rooms.get() |
trx.rooms.fn.Room |
trx.rooms.Room |
trx.rooms.fn.FlipStatus |
trx.rooms.FlipStatus |
trx.rooms.fn.flip() |
trx.rooms.flip() |
trx.rooms.fn.flip_effect() |
trx.rooms.flip_effect() |
room.idx |
room.num |
item.index |
item.num |
item.anim |
item.anim_num |
item.frame |
item.frame_num |
level.name |
level.title |
trx.lara.mesh.hand_r |
trx.lara.Mesh.HAND_R |
trx.lara.extra_mesh.oar |
trx.lara.ExtraMesh.OAR |
trx.pickup.Mode.X |
trx.items.PickupMode.X |
trx.console.log.LogLevel |
trx.log.LogLevel |
trx.music.get_track() |
trx.music.current_track |
trx.music.get_looped_track() |
trx.music.looped_track |
trx.music.available_tracks() |
trx.music.tracks |
The fn namespaces are gone: index the module directly, trx.items[16],
trx.items["lara"], trx.rooms[14]. The mesh tables became declared enums,
so every name in them is upper case, not the two shown above alone.
Handler arguments now say what kind of number they carry: on_room_change
takes old_room_num and new_room_num, on_flyby_end takes sequence_num,
the on_cutscene_* handlers take cutscene_num, trx.savegame's slot
argument is slot_num and trx.inventory's object argument is object_id.
They are positional, so this only matters to a script's own documentation.
| 1.9 | Use instead |
|---|---|
item.idx |
the handle itself |
item.flags |
trigger_mask, is_reversed, is_triggered, is_killed, is_one_shot |
item.status, items.Status |
the boolean fields - see below |
trx.items.find(), trx.items.first() |
trx.items.query, :of_object() and :in_room(), then :matches() or :first() |
trx.game.settings.play_any_level = true |
trx.config.override("flow.play_any_level", true) |
trx.pickup |
trx.items.PickupMode |
trx.events.EventType, hook ._type |
nothing; the nine hooks are the whole API |
trx.music.play_track() |
trx.music.play() |
trx.music.is_available(id) |
trx.music.tracks[id] ~= nil |
trx.sound.is_available(id) |
trx.sound.samples[id] ~= nil |
before_level_file, after_level_file, before_item_setup, after_item_setup, after_level_state |
on_game_start - see below |
The first four change what a script does without raising. The rest report themselves.
Items and rooms count from 0
The numbering matches what level editors show: trx.items[13] is
trx.items[12] now, and trx.rooms[15] is trx.rooms[14].
item.room_num, camera.room_num, camera.target_room_num and
find_valid_pos's room argument follow, and
for i = 1, #trx.items do local item = trx.items[i] becomes
for num, item in pairs(trx.items) do. on_pickup always counted from 0,
so drop the item_num + 1 that bridged the gap.
trx.config.get() returns the option's own type
trx.config.get("flow.cheat_keys") == "true" is false whatever the setting
holds; test the value itself. Colors and enums are still strings. set()
still writes to the player's settings and keeps the change - use the new
override() and restore() for what a level wants only while it runs.
trx.lara.extra_anim is a boolean
It says whether a scripted animation is driving Lara, where it used to be
the relative animation number of O_LARA_EXTRA, or -1. ~= -1 is now
always true. The number itself is trx.lara.item.anim_num.
max_hit_points carries hit_points with it
Writing it moves the item's current hit points by the same difference, so
the companion write is no longer needed.
Handles are opaque, compare by identity, and go stale
An item or room handle is no longer a { idx = ... } table and cannot
carry keys of your own; pass the handle where you passed the index.
trx.items[0] == trx.items[0] is true now, where every lookup used to hand
back a fresh table. A handle to a killed item, or any room handle after a
level change, raises stale ITEM handle rather than addressing whatever
took the slot - guard one held across time with :is_valid().
item.status became separate boolean fields
status in 1.9 |
1.10 |
|---|---|
ACTIVE, running |
is_simulated, started by activate() |
ACTIVE, targetable enemy |
is_in_play; is_targetable for auto-aim |
INVISIBLE |
not is_visible |
DEACTIVATED |
is_finished |
is_present is new: in the world at all, linked in its room. The item
query narrows on each - simulated, present, visible, finished,
in_play, alive, targetable.
on_game_start replaces the level lifecycle events
It is the one moment a level script gets before play: the level file is
loaded, its items are set up, savegame state has been applied, and nothing
has been drawn. A handler moves across as it stands, and an object property
no longer has to be written before its item is initialised, which is what
the earlier moments were for.
trx.events.on_game_start(function(is_save)
trx.items[65].properties.range = { x = 14, y = 6, z = 14 }
end)
It fires for cutscene and demo levels too, and the title screen has
on_title_start. The level is trx.game.current_level rather than a
number handed to the handler.
trx.game.levels leaves out the gym
Where a game flow has one, every entry has shifted down by one and the last
level - previously unreachable - is in the list. Drop any offset that
stepped over the gym; it is trx.game.gym and trx.game.play_gym(). The
same holds for trx.game.cutscenes, trx.game.demos and their play_
functions. Every field on a level is read-only.
Music and sound take a catalog id
trx.music.play, trx.sound.play and trx.sound.stop take a
trx.catalog.music or trx.catalog.samples value, which maps to the right
track or sample per game, rather than the level's own slot. Reach a slot
through its handle - trx.music.tracks[slot]:play(),
trx.sound.samples[slot]:play() - and both play functions hand back the
stream they started. trx.sound.stop_all is unchanged.
These raise where they used to pass
| Call | Why |
|---|---|
item.hit_points = 99999 |
truncated to the field's width |
room.wind = 1, room.cold = nil |
room flags take booleans only |
| writing an out-of-range room | did nothing |
trx.rooms["5"] |
index with a number |
trx.console.log("a", "b") |
format the message yourself |
item.object_id = ... |
spawn the type you want instead |
| writing to an enum or catalog | broke every later lookup |
{ x = , y = } |
a position needs all three |
These read differently
false, not nil, so
if room.underwater == nil no longer detects a dry roomtrx.objects[id] is nil for an id the game does not have, where it
used to hand back an object that answered to nothingpairs() over one
yields the constants alonetrx.events.detach takes the Listener an attach handed back, not a
number; listener.id is the numberUpdate Lara pushblock animations
Lara's pushblock animations (non-continuous) are now split to line up with
the length of the animations of the blocks themselves. Ensure to update
catalog_lara_anims.csv and either lara_animations.bin or the Lara object
in your level WADs.
Update Lara's outfit definitions and samples
The footstep_sample_id SFX reference was removed from Lara's outfit
definitions and replaced with an is_barefoot flag. Update outfits.json5
and catalog_samples.csv accordingly - refer to OG shipped assets.
Update game mode selection config option
enable_game_modes (boolean) was changed to game_modes_policy, with the
options being never, always and on-completion. Update the gameflow if
this setting is enforced.
Update O_SPARKS_GFX sprites
The O_SPARKS_GFX sprites from TR3 were combined with TR4. Download the TR3X
assets file from
https://lostartefacts.dev/pub/tr3-assets.zip
, or use the
shipped sparks_gfx.bin injection.
Update Assault Course Lua stats access:
The separate trx.assault_stats module has been merged into
trx.assault.stats.
trx.assault_stats.add_record(30.0)trx.assault.stats.add_record(30.0)TR3 sparks object was renamed:
In cfg/catalog_objects.csv, update the following object name:
O_EXPLOSION_1 → O_SPARKS_GFXUpdate fish/piranha setup:
Fish and piranha objects no longer require a timer field to be set in
triggers, and instead their swim range needs to be defined in Lua. Refer to
the OG TR3 level scripts for reference.
The O_EXPLOSION_1 sprite sequence is no longer used for these objects. Use
fish_sprites.bin for TR3 levels, or define O_PIRAHNA_GFX and
O_TROPICAL_FISH_GFX in your level WAD. The TRX assets WAD for TR3 contains
the default setup.
Update bat emitter sprites:
The O_EXPLOSION_1 sprite sequence is no longer used for bat emitters. Use
bat_sprites.bin for TR3 levels, or define O_BAT_GFX in your level WAD.
The TRX assets WAD for TR3 contains the default setup.
Update Cobra setup: Cobras in level sequence 9 and above are no longer hard-coded to have a small attack, forget and alert radius. Use Lua to specify this setup if required.
Update quest item end-level handling: TR3's quest items will no longer end the level by default when picked up. Use Lua or regular pickup triggers instead; refer to the OG TR3 level scripts for reference.
Update side flame emitters:
O_FLAME_EMITTER_SIDE instances will no longer have a hard-coded 4 second
interval in level sequence 7; all instances will default to 2 seconds. Refer
to the Madubu Gorge Lua script to alter the interval.
Update spikes sound effects: Animated spikes in TR3 are no longer hard-coded to play specific sound effects in levels 5 and 7 only. Regular animation commands can be used instead to play in any level.
Update AI Patrol 1:
Levels with sequence 14 and 15 are no longer hard-coded to retain
O_AI_PATROL_1 items where an enemy should have the AI bits set but also use
the item as a pathing target. Instead, place two O_AI_PATROL_1 items in the
same position to retain behaviour.
Vehicles and heavy triggers: All vehicle types except for the mounted gun can now activate heavy triggers. This is configurable per object and item in Lua, and the setting is enabled by default. Disable the option in cases where this may interfere with triggers intended for other heavy activators e.g. pushblocks. This is not configurable for the mine cart, which still relies on Lara striking switches.
Replace cold_water with room flags:
The cold_water game-flow property has been removed. Use room flags instead:
damaging controls Lara's exposure meter.cold controls Lara's visible breath.You can set these flags from Lua:
trx.rooms[room_num].damaging = truetrx.rooms[room_num].cold = trueO_DISPOSABLE_ANIMATING_1…10 have been removed. Use
O_ANIMATING_EXT_1…10 instead, and set the kill_on_trigger item property
to true when you want the old disposable behavior.item.max_hit_points Lua field has been removed. Use item
properties instead:item.max_hit_points = 20item.properties.max_hit_points = 20TR1 and TR2 blood catalog names were renamed:
In cfg/catalog_objects.csv, update old symbols to the new names:
O_BLOOD_1 → O_BLOODThis also affects catalog-derived Lua names (trx.catalog.objects):
blood_1 → bloodUpdate weapon ammo quantities:
In weapons.json5, the old pickup_qty and pickup_qty_alt fields have
been reorganized under a new nested ammo object. This lets weapon pickups
grant a different amount of ammo than their matching ammo pickups.
To match the previous setup:
weapons.json5.ammo object if it doesn't already exist.pickup_qty into both ammo.initial_qty and
ammo.pickup_qty fields.ammo.inventory_qty field.pickup_qty_alt field (e.g. flares):ammo.pickup_qty_alt.pickup_qty and pickup_qty_alt fields.TR1 Atlantean catalog names were changed:
In cfg/catalog_objects.csv, update old symbols to the new names:
O_WARRIOR_1 → O_ATLANTEAN_WINGEDO_WARRIOR_2 → O_ATLANTEAN_SHOOTERO_WARRIOR_3 → O_ATLANTEAN_GROUNDThis also affects catalog-derived Lua names (trx.catalog.objects):
warrior_1 → atlantean_wingedwarrior_2 → atlantean_shooterwarrior_3 → atlantean_groundTR1 missile catalog names were renamed:
In cfg/catalog_objects.csv, update old missile symbols to the new names:
O_MISSILE_1 → O_NATLA_GUNO_MISSILE_2 → O_MISSILE_ATLANTEAN_SHARDO_MISSILE_3 → O_MISSILE_ATLANTEAN_BOMBO_MISSILE_4 and O_MISSILE_5 are no longer used and should be removed.This also affects catalog-derived Lua names (trx.catalog.objects):
missile_1 → natla_gunmissile_2 → missile_atlantean_shardmissile_3 → missile_atlantean_bombTR2 breakable window catalog names were renamed:
In cfg/catalog_objects.csv, update old breakable windows to the new names:
O_WINDOW_1 → O_SMASH_OBJECT_1O_WINDOW_2 → O_SMASH_OBJECT_2This also affects catalog-derived Lua names (trx.catalog.objects):
window_1 → smash_object_1window_2 → smash_object_2Flooding flip effect sound ID was changed:
In cfg/catalog_samples.csv, add an alias for SFX_FLOOD:
81, SFX_FLOOD79, SFX_FLOODLara skin system:
Lara's outfit must now be defined using additional skin objects, along with
game-flow and JSON setup. Refer to outfits documentation.
Lua event name cleanup:
The following events got new names:
on_level_init → before_level_fileon_level_start → after_level_fileon_level_load → after_level_stateon_control → before_controlon_control_post → after_controlLua objects catalog name cleanup:
All keys in trx.catalog.objects had their O_ prefix removed and were
converted to lowercase.
Before: trx.catalog.objects.O_BANDIT_1
After: trx.catalog.objects.bandit_1
Savegame file pattern rename:
Replace savegame_fmt_bson with savegame_file_fmt in game flow files.
The old savegame_fmt_bson key is still accepted but logs a warning and is
scheduled for removal in TRX 1.5.
Legacy savegame pattern removed:
Remove the savegame_fmt_legacy key from game flow files.
Game flow options moved to the config module:
Certain settings are no longer part of the game flow spec and instead
became hidden player settings. To change them, put them in the
enforced_config section. List of the affected settings:
demo_delayenable_killer_pushblocksLara shotgun animation:
Lara now uses the TR2+ approach of a separate shotgun mesh on her back. You
must use the lara_guns.bin injection or otherwise refer to
https://github.com/LostArtefacts/TRXInjectionTool/blob/main/docs/ASSETS.md
Lara extra animations:
Lara now uses the TR2+ approach of having defined state changes for extra
animations (scion pickups, Midas touch etc). You must use the lara_extra.bin
injection or otherwise refer to
https://github.com/LostArtefacts/TRXInjectionTool/blob/main/docs/ASSETS.md
Update file paths
cfg/TR1X_gameflow.json5 file to cfg/tr1/gameflow.json5.cfg/TR1X_strings*.json5 files to cfg/tr1/strings*.json5.cfg/TRX_common_strings*.json5 files to cfg/base_strings*.json5.TR1X_strings_ub.json5.This is how the directory should look:
.
└── cfg
├── base_strings.json5
├── base_strings-pl.json5 (in case you want to provide translation files)
├── base_strings-….json5 (in case you want to provide translation files)
├── tr1
│ ├── gameflow.json5
│ ├── strings.json5
│ ├── strings-pl.json5 (in case you want to provide translation files)
│ └── strings-….json5 (in case you want to provide translation files)
└── poses.json5
Update fog configuration
If you wish to force your fog settings on player:
draw_distance_fade to fog_startdraw_distance_max to fog_endIf you wish to give the player agency to change the fog:
draw_distance_fade and draw_distance_maxRename basic keys
file key with path for every level.music key with music_track for every level.Update level enumeration structure:
"type": "title" property is no longer supported. Instead, the title
level needs to be placed in the top-level "title" key."type": "cutscene" property is no longer supported. Instead, the
cutscenes need to be placed in the top-level "cutscenes" array."fmvs" array.Update individual level sequences
start_game should be removed.exit_to_cine should be removed.exit_to_level should be replaced with level_complete. No parameter needed.display_picture no longer takes a picture_path argument and instead just takes a path.loading_screen no longer takes a picture_path argument and instead just takes a path.level_stats no longer takes a level_id argument.total_stats no longer takes a picture_path argument and instead takes a background_path.play_fmv no longer takes a fmv_path argument and instead takes a fmv_id.play_synced_audio is renamed to play_music and takes a music_track argument rather than audio_id.Update strings
The game strings are now placed in a separate file, TR1X_strings.json5 in
preparation to eventually support internationalization. Elements such as
item titles or item names need to be configured entirely in the new file, so
all "strings" keys can be safely removed from the game flow. Refer to
game strings documentation for more details.
Game flow options moved to the config module:
Certain settings are no longer part of the game flow spec and instead
became hidden player settings. To change them, put them in the
enforced_config section. List of the affected settings:
lockout_option_ringload_save_disabledplay_any_leveldemo_delaycheat_keysenable_killer_pushblocksRemoved game flow settings
The following game flow features were removed and are no longer available:
cmd_initcmd_titlecmd_death_in_democmd_death_in_gamecmd_demo_endcmd_demo_interruptsingle_levelis_demo_versionLara extra animations:
Lara's extra animations have been combined with TR1. You must use the
lara_extra.bin injection or otherwise refer to
https://github.com/LostArtefacts/TRXInjectionTool/blob/main/docs/ASSETS.md
Secret track:
The setting secret_track is no longer present – the engine will always
play MX_SECRET track. To change its slot, please refer to the
catalog_music.csv file.
Update file paths
cfg/TR2X_gameflow.json5 file to cfg/tr2/gameflow.json5.cfg/TR2X_strings*.json5 files to cfg/tr2/strings*.json5.cfg/TRX_common_strings*.json5 files to cfg/base_strings*.json5.TR2X_strings_ub.json5.This is how the directory should look:
.
└── cfg
├── base_strings.json5
├── base_strings-pl.json5 (in case you want to provide translation files)
├── base_strings-….json5 (in case you want to provide translation files)
├── tr2
│ ├── gameflow.json5
│ ├── strings.json5
│ ├── strings-pl.json5 (in case you want to provide translation files)
│ └── strings-….json5 (in case you want to provide translation files)
└── poses.json5
Rename objects
"detonator_1" with "gong"."detonator_2" with "detonator_box".Re-add pistols
Pistols are no longer added automatically to a level that follows one in
which Lara previously lost her weapons. A game flow entry to re-add pistols
will be required - refer to the Diving Area level in the default game flow.
Bears, wolves and ice warriors
If you wish to use the bear, wolf or ice warrior (monk with no shadow) from
The Golden Mask while still being able to use big spiders, small spiders and
other monks, use the following object slots.
Disabling gym
The option gym_enabled is no longer available. If you need to remove the
access to Lara's Home, please either remove the relevant level from the
game flow (this may break existing saves), or change its type to "dummy"
to get it ignored (this will work with existing saves).
Great Wall sequences, specifically the
give_item entries.