Skip to content

Releases: LostArtefacts/TR-Rando

V1.3.0

29 May 16:26

Choose a tag to compare

Main issues covered: #43, #58, #97, #124, #125

The changes are separated into the following sections.

  1. Dependency Changes
  2. TRGE Updates
  3. Cross-level Enemies
  4. Texture Framework
  5. Multiple Unarmed Locations
  6. Item Randomizer
  7. Refactoring
  8. UI Updates
  9. Misc
  10. Key Items

Dependency Changes

TRGE Updates

  • The "Modified by…" string that appears in the title screen/inventory can now be specified in German and French as well as in English. The language is detected in the script file.
  • Fixes a problem where disabling title screen demos was ineffective.
  • Fixes a problem with Floating Islands if the version swapper tool had been used after TRGE had performed a backup of the original files. The swapper tool replaces FLOATING.TR2, but TRGE will always use the file that it originally backed up. Checks will now detect when the swapper has been used, and the backed-up file will be replaced. For the Randomizer, this meant that despite switching to UK Box, it was possible to still see texture corruption in this level.
  • Fixes sprite texture bounds of imported weapons (Floating Islands and Dragon's Lair).
  • Fixes air bubbles showing as blood splats when swimming in HSH.
  • Fixes upside-down paintings in HSH piano room (original bug in the game).

#97 Cross-level Enemies

Excluding a few restrictions, any enemy type can now appear in any level. TRModelTransporter is responsible for exporting enemy definitions (or more loosely, model definitions) to JSON format, and these definitions are made up of animations, animation frames, cinematic frames, colours, sound, meshes, meshtrees, object textures, sprite textures and sprite sequences. Beside each JSON file is a PNG of the textures used by the model. The definition holds a map of how to relate the PNG to the object or sprite textures used by the model.

Some models have several dependencies on others. A good example is MarcoBartoli – this definition holds a reference to the dragon explosion (4 separate models), the dragon itself (another 4 separate models) and the model that defines Lara's animation of inspecting the dagger. The Venice goons and the Tibet mercenaries are others like this, all sharing various behaviour. This complexity is all handled within TRModelImporter, so given an entity to import, it will resolve all dependencies.

The models are exported once and stored in Resources\Models, rather than exporting each time they are needed.

Aliases

Some enemies have aliases, so these are models with the same ID, animations, sounds etc, but different textures. These have been defined as follows to allow specific entity types to be imported (the original levels they appear in are shown in brackets).

  • Barracuda
    • BarracudaIce (CoT, Ice Palace)
    • BarracudaUnwater (40F, Doria, LQ, Deck)
    • BarracudaXian (Xian)
  • StickWieldingGoon1
    • StickWieldingGoon1Bandana (Rig, Diving Area)
    • StickWieldingGoon1BlackJacket (HSH)
    • StickWieldingGoon1BodyWarmer (Venice)
    • StickWieldingGoon1GreenVest (40F, Doria, LQ, Deck)
    • StickWieldingGoon1WhiteVest (Bartoli, Opera)
  • TigerOrSnowLeopard
    • BengalTiger (Great Wall, Xian)
    • SnowLeopard (Tibet, CoT)
    • WhiteTiger (Ice Palace)

Only one entity per family can exist in a level as the model can only point to one set of textures.

Limits

The biggest limit is that only 16 texture tiles are supported per level. When transporting models, the textures of those no longer in use are removed from the tiles, so freeing up some space. Fortunately, Core duplicated a lot of textures in the original levels so there has been room for manipulation here to make even more space available. This was done by going through every texture and comparing it pixel-by-pixel with every other texture. On an exact match, the first texture is removed from the tile, and the texture object updated to point to the second tile location instead. For example, in tile index 3 in Great Wall, the 64x64 square at position [64, 152] is already contained within the 64x80 rectangle at position [72, 0], so the square is deleted and the texture object is repointed to [72, 0] - so gaining 64x64 pixels of space. JSON files are stored in Resources\Textures\Deduplication for each level, and these are used to process the remapping during randomization (this is much more efficient than running pixel-by-pixel checks each time). The mapping files also detail any dependencies, so for example if two enemies share the same texture, the area in the tile will only be removed if both enemies are also being removed.

Another limit is that the game only supports 2048 texture objects (TRLevelReader.Model.TR2Level.NumObjectTextures). When importing enemies, we re-use all of the texture objects of the removed enemies, but in some cases this is still not enough. An example is in Diving Area, which only has 26 free slots, so if the enemies that are being imported are more texture-heavy than the ones removed, we hit a problem. A workaround is described below in the randomization process.

Packing

After the tiles have been stripped of duplicates and the old enemies have been removed, the new enemy textures can be imported into the tiles. RectanglePacker deals with this bin-packing problem – you can see a demo of this in action at https://github.com/lahm86/RectanglePacker/raw/main/Resources/PackingDemo.gif. It would be ideal to start completely afresh and repack every texture, similar to the demo, but this takes between 10-20 seconds per level. For efficiency, we begin with the pre-populated level tiles and start packing on the last occupied tile, then fill them up until we reach the end of tile 16. If there are still more textures to add, we move to the first tile and continue from there. RectanglePacker is only one solution to bin-packing, so there is definitely room for improvement with it.

Randomization Process

Selecting Enemies

EnemyRandomizer has been updated with the option to include cross-level enemies. If this is not used, the standard native enemy randomization will take place. The number of enemy types per level remains the same for the bulk of levels, but for some there will be more and others less. The likes of Great Wall has plenty of tile space and free object textures, so we add an extra 2 enemy types here to make 6. Opera House and Diving Area are tight on space, so we drop 1 each here to 6 and 5.

TR2RandomizerCore.Utilities.EnemyUtilities contains most of the rules that control which enemies can be selected. Other than the special cases below, enemies are simply chosen at random, but we still honour the previous requirements for water and droppable enemies as well as ensuring that the chicken, dragon, and HSH shotgun goon remain at the end of their default levels. A check is also done at the end of Diving Area to ensure that both enemies here can be killed (so we specifically avoid the eels).

HSH

The kill count seems to break in HSH when you change the enemy types – in some cases you can finish the level after one kill, in others, the ShotgunGoon never spawns. So for now, we only randomize StickWieldingGoon1 into a different alias for this level.

Dragon

The dragon can only appear in the following levels outside of Lair.

  • Great Wall
  • Opera House
  • Doria
  • The Deck
  • Tibet
  • CoT

The main reason is that the explosion has a 128x128 texture, so levels tight on space won't support this without performing full texture repacking. In addition, if the dragon spawns in a compact room, it can be impossible to kill. It tends to land in the void when it falls so the dagger can't be reached. Lara can also void during the explosion. So, those levels that can support the textures have rooms defined for supporting the dragon. These are held in Resources\enemy_restrictions.json, which is a map of level name -> entity ID -> room number list.

In addition to room restrictions, the dragon will only appear a maximum of once per level. If multiple dragons spawn close together, the game tends to crash.

MercSnowmobDriver

The file described above also lists room restrictions for MercSnowmobDriver. This is restricted to showing a maximum of twice per level (outside of Tibet). As the black skidoo depends on the red skidoo, this will be added as an entity to a level when MercSnowmobDriver is present. One red skidoo is added to the same spot as the first MercSnowmobDriver. This should ideally be zoned along with the room restrictions for this enemy and the dragon.

Chicken

The chicken is restricted to appear in at most 3 different levels (there are currently no restrictions on location or count within each level). There is also scope in EnemyUtilities to limit other enemies across the game in a similar way if needed.

Performance

Texture packing is quite time consuming – on average it takes around 5 seconds per level. As a result, EnemyRandomizer uses threading to run the model import work in parallel.
...

Read more

V1.2.2

28 Feb 13:57

Choose a tag to compare

Incorporates: https://github.com/lahm86/TRGameflowEditor/releases/tag/v0.9.3-beta

  • Flares have been removed from the randomization pool (for external editors) until the sprite issue is resolved.
  • Fairer secret reward allocation in randomization (for external editors).
  • Minor optimisation in the data folder restore window.

V1.2.1

27 Feb 12:37
a92b577

Choose a tag to compare

Includes new Textures.

Provided by Lahm's PR as it summarises the changes well:

There are quite a few changes but I’ll describe them all below. The solution (TR2RandomizerCore only) now needs a reference to TRGE.Core.dll and TRGE.Coord.dll, which you can get from TRGE. Feel free to clone and build it to get the debug versions or just use the current beta release.

Below is a list of each of the issues referenced in this PR.

#29, #31, #39, #47, #48, #76, #77, #80, #93, #98, #99, #100, #101, #102, #103, #104

Script Editing

The main change is introducing the ability to edit TOMBPC.dat via TRGE, which allows modification of the following.

  • Rewards for collecting all secrets (#48).
  • The sequence that levels are played in (#39).
  • The levels where Lara loses her weapons (#98).
  • The levels where Lara loses her ammo/health/flares (#99).
  • The ambient soundtrack in each level (including the title screen) (#77 – partially, does not change in-level sounds, like trigger music, enemies etc).
  • The track to play when a secret is found. This is a global setting in the script file, and cannot be modified per level (#77, again partially).
  • The levels that have sunsets e.g., Bartoli’s Hideout by default (#100).

The script can be edited to modify several other bits and pieces, but the above covers the main features relevant to TR2-Rando.
So now when opening a data folder to edit, that folder must contain the TOMBPC.dat file and each of the level files described in the dat file. The easiest thing to do is just point it directly to the main data folder e.g., C:\Program Files (x86)\Steam\steamapps\common\Tomb Raider (II)\data. The UI now tracks folder history (#103), so you can re-select the same folder easily when re-opening the application.

Backup and Restore #102

All level files and the script file are backed up automatically when a data folder is opened. TRGE needs a backed-up script file so it knows how to restore information (e.g., if we randomize level sequencing, but then in the next session choose not to, it needs to know the default order) so it was straight-forward to extend this to include the level files too. There is an option in the UI to restore a folder to default, which will copy the entire backup to the original directory. If you empty the recent history items, it will delete all backups.

UI & Coord Updates #104

I’ve separated the logic from the UI, so TR2RandomizerCore now holds all the logic and TR2RandomizerView is the new UI. I left the current TR2Randomizer project in case there is anything in there you want to keep.

The main updates to the UI were to include the new script editing options, but I’ve also added in new seed controls with the ability to randomize each individually, all at once (as you had in place already) and also an option to apply the same seed to all elements. This is linked to the mockup in #29 mainly. There are three container controls as follows.

  • TR2RandomizerView.Controls.ManagedSeedControl - contains a checkbox, title, descriptive text and a seed control.
  • TR2RandomizerView.Controls.ManagedSeedBoolControl - contains a checkbox, title, descriptive text, a seed control plus an additional checkbox (used for 'hard secrets', for example).
  • TR2RandomizerView.Controls.ManagedIntControl - contains a checkbox, title, descriptive text, a seed control, plus an additional NumericUpDown control (used for example when choosing the number of unarmed levels).

TR2RandomizerView.Model.ControllerOptions contains the settings for the UI, and is the main interop between the UI and the core.

Hitting Randomize will bring up a progress dialog, and the user can now cancel this. All writes are done to a temporary directory first and only at the very end of the process is everything copied to the target directory (so if anything fails, the target directory’s integrity isn’t affected). The user can’t cancel the operation once it starts the commit.

The UI theme colours are in App.xaml so feel free to change. I got the app icon from flaticon.com, but again feel free to adjust! I’ve tested the UI in Win7, Win8.1 and Win10.

The UI only speaks to TR2RandomizerCore. Core is linked to TRGE and it registers TR2RandomizerCore.Randomizers.TR2LevelRandomizer as the level editor to use. TRGE works by first saving the script data, applying any initial changes to level files (such as adding pistols to default locations for unarmed levels – more info on this below) and then passes control to TR2LevelRandomizer.SaveImpl to begin the main randomization. I’ve only had to alter the randomizers themselves slightly, described further below.

Update Checker #76

I was doing this for TRGE anyway, so have just copied the same across to the new UI. It works by looking at the Product value in the Assembly information for the main exe and compares this with the latest tag_name from GitHub. So the Product field in VS just needs to end with the current release tag in GitHub and if it finds that a different release is available, it will show an “Update Available” menu option. At the moment, it is set to TR2Rando V1.1b2, so it pulls out V1.1b2 from this string. See TR2RandomizerView.Updates.UpdateChecker.

Import/Export #93

You can now export settings to a compressed json file, and import again via the Tools menu in the UI. You can also import by dropping a valid file onto the main window. I had a look at making a profile string as mentioned in the issue, but haven’t incorporated this yet (TR2RandomizerView.Model.ProfileString – it’s a messy class just now and needs more work to make it robust). I think the file import/export is sufficient for now anyway.

In TR2LevelRandomizer, there are methods for handling importing and exporting config (it just needs to set the data that is supplied to it, or provide the data that needs to be exported – the rest is handled by TRGE). So ApplyConfig is called either when importing a new config file or simply when a data folder is reopened normally from a previous session. StoreConfig is used to save the current config after a rando session, as well as during export.

Unarmed Levels #98

Any level can now start unarmed, not just Offshore Rig/HSH. When a level’s state changes to being unarmed, TRGE will inject the pistols into predefined locations in the relevant level file before any randomisation takes place. These locations are in TR2RandomizerCore\Resources\unarmed_locations.json. The logic for looking in the plane cargo for Rig has been extended to cover all levels. It might be good to check if we need to be a bit more generous if replacing the pistols with something different in the more difficult levels (e.g., tackling Dragon’s Lair with the Harpoon might not be popular).

Main Randomizers

TR2RandomizerCore.Randomizers.RandomizerBase now stores a List of TRGE.Core.TR23ScriptedLevel objects instead of level name strings. These represent each level in the script file and contain all relevant information, such as the name, file name, whether or not the level starts unarmed etc. So the randomizers iterate over this list now instead of the strings. I’ve added a LoadLevelInstance method to set the instance variable for the TR2Level, plus an extra instance variable of type TR23ScriptedLevel as this is useful in ItemRandomizer for example, when checking for unarmed levels outwith the main looping method.

Each randomizer has a reference to a TRGE.Core.SaveMonitor, which is used to track progress of the randomization. The main looping methods check the SaveMonitor on each iteration to see if the operation has been cancelled by the user. You can also fire custom progress messages here if necessary (I've left them commented out for now). TR2LevelRandomizer sets the overall text before starting each randomizer anyway.

At the moment, TR2LevelRandomizer passes the TR23ScriptedLevel list to the individual randomizers in the original game sequence for these levels. If you want to instead pass the list in whichever sequence the levels have been randomized to, you can uncomment the sorting code in TR2LevelRandomizer.SaveImpl.

Successive Rando

A bit of a side effect with import/export and the auto backups is that randomization is now always done based on the original level files. So, if you randomize twice or more successively with the same seed/settings you will always get the same results. If this is a big drawback, I can have a look at trying to allow successive again, but I think for import/export it would be difficult – I think the idea with that is people could share their settings, but if someone had randomized successively, exported, then the other person imported and randomized once, it wouldn’t match.

Home Sweet Home and Textures

For HSH, I had to get all textures, animations, moveables etc. into the level because it could potentially no longer start with Lara losing her weapons/ammo. I used TRViewer to build what was needed, and TRGE handles getting this in place before any randomization. You can see when it copies the level files to the backup folder, it creates a new house.tr2 file with these extra items included and renames the original to .bak. If restoring, the .bak file is used to put things back to normal. So in relation to #47, any weapon can now be added to this level. ItemRandomizer.PopulateHSHCloset is still empty for the time being.

Unfortunately, adjusting HSH had the side effect of changing the textiles, as TRViewer optimises the layout going from largest to smallest. So effectively I broke the texture randomization for this level. Regardless, I worked through the images and got them all updated, so it’s working properly again for the dressing gown and secrets. I also added another couple of dr...

Read more

V1.1b2

01 Aug 12:13

Choose a tag to compare

Test version covering the following issues:

#61 QoL improvements
#68 Extra ammo provided for OR
#67 Floater and Lair secrets should be corrected
#65 #75 Fixed Dragons Lair softlock with Jade soldier being allocated a cracker
#62 Enemy randomization logic has changed to avoid a softlock in HSH
#60 Ice Palace will now always spawn a guardian at EOL rooms so you can end the level
#74 Removed original textures from Randomization pool
#64 Diving suit textures improved
#66 #71 Fixed bugged secret locations

TR2 Rando V1.0

29 Jul 08:09
11cff10

Choose a tag to compare

Initial release of basic TR2 Randomizer

TR2Rando Release Candidate 5

12 Jun 17:04

Choose a tag to compare

Pre-release

Full, basic, randomization.

TR2Rando Release Candidate 4

11 Jun 17:43

Choose a tag to compare

Pre-release

Randomizes ammo items, fixed a bug where items would be placed far OOB

TR2Rando Release Candidate 3

10 Jun 20:33

Choose a tag to compare

Pre-release

Basic item randomization, but every level will probably soft lock 😆

TR2Rando Release Candidate 2

07 Jun 18:20

Choose a tag to compare

Pre-release

Includes debug feature, leave seed blank to place all secrets.

TR2Rando Release Candidate 1

07 Jun 17:29

Choose a tag to compare

Pre-release

Release Candidate for the initial version of the TR2 Randomizer.