OpenLoco v26.03 is out! This month’s release focuses on moving to our (graphics) backend to SDL3, as well as a rework of the audio engine. Some more cool new features have been in the works since last month, but they haven’t landed yet this month. Please be patient for a while longer :-)
What follows is a quick summary of the new features and bug fixes for this month.
Migrate codebase to SDL3
When implementing a game, programmers can choose to either target a particular operating system directly, or use an intermediary layer that makes it possible to target multiple platforms in one go. To this end, OpenLoco has made use of SDL since the beginning. Recently, a major new version of the SDL API was released, meaning old APIs were replaced and new ones added. To make it possible to leverage the new functionality, we had to rewrite part of our codebase.
The majority set of rewrites was merged early in the month (#2882), followed by a series of improvements on display handling by new contributor @kevinz26 (#3671). Nice!
Audio volume can now be set separately by channel
There are many different kinds of audio in Locomotion. It even includes a jukebox! Like Locomotion, OpenLoco only had a single volume slider, grouped with the music options. Until this month, that is, as now you can set a specific volume for each specific kind of audio (#3678)!

Vehicle sounds now use reverb when entering or leaving a tunnel
Armed with a newly-refactored audio subsystem (#3678), @ZehMatt set out to add a new effect to the game: reverb! This is now used for vehicle running sounds when they are in a tunnel #3678. We think it’s a nice touch!
Here’s what it sounded like in v26.02:
And here’s what it sounds like in v26.03:
Improved entity invalidation
Normally, tiles are ‘invalidated’ whenever elements or entities on them have changed, requiring them to be redrawn. However, under some circumstances, e.g. when dragging a vehicle ‘ghost’ around, it was possible for a (ghostly) remnant of the vehicle to stay behind. It seems we missed a spot, somewhere!
This was reworked by making the invalidation implicit in the entity’s moveTo call, rather than explicit.
@ZehMatt realised there was an optimisation here: sometimes, a tile was invalidated even if the vehicle hadn’t
actually moved (#3312)!
This probably won’t lead to much of a rendering speedup, but it’s a nice bonus!
News settings default to newspaper style again
Last November, we reworked the remaining Locomotion config settings, folding them into our own, modern OpenLoco config file. We missed an edge case, however. For new config files, news settings were not initialised correctly. Indeed, all news settings defaulted to off, rather than the newspaper style players have come to expect. Oops.
New config files will now get the right ‘newspaper’ defaults (#3657). Unfortunately, we can’t reset the settings without affecting players with custom news settings, however. So if you’re not seeing news unexpectedly, please check the news options in the messages window.
New widget identification
When a player interacts with an element in an in-game window, a so-called widget, you often want something to happen. To do this, the input subsystem relays to the relevant window that a particular widget has been interacted with in a certain way, so that the window can do what is expected.
Like Locomotion before it, OpenLoco handles this by using a numeric index. Using enums,
the first widget in a window is assigned index 0, the next index 1, etc.
This is great if your widgets are sequential, but we would like to move to a
more declarative way of denoting the window layout. Suddenly, widgets are not so sequential any more,
meaning we need a new way to identify them. Enter WidgetId
#3560.
Instead of simple enum sequences, WidgetIds are instantiated using a string, which is hashed to a number at compile time.
This means we still get something that’s easy for machines to compare with, while also making it possible
for interface design to be moved externally to the C++ internals. Something nice to look forward to.
Improved assertions
Traditionally, in C and C++, programmers can use assert to denote what would otherwise be implicit
expectations in source code. For example, you might want to make sure that a particular thing never happens,
e.g. the sky shouldn’t be painted in a green colour, somehow. Such extra checks take time, though, so typically
asserts are skipped entirely in non-debug builds.
There are times when we do want things to fail, though, and when they do, preferably with a bit of extra information. Enter our own assertion diagnostics #3656, courtesy of @ZehMatt. These have the added advantage of saving the source location, so error traces don’t show the error handler as the source of the error, but the actual location where the assertion was triggered. Hopefully, these will help narrow down any pesky mistakes in the future!
Drawing circles
Locomotion sported a necessary feature for new players: in-game tutorials. While not really going into the nitty-gritty of the game, they helpfully covered the basics. Unfortunately, though, they are simply a sequence of mouse pointer locations, recorded separately for common screen resolutions at the time the game was released: 640×480, 800×600, and 1024×768. If these resolutions seem alien to you, well, this is what most of us had to contend with back in the day!
In recent discussion, we talked about how best to replace the old ‘mouse’ tutorials. One idea is to use the new widget identifiers to mark the elements that instead should be clicked by the player to advance the tutorial.
One way to mark them would be to use circles, someone suggested. A problem presented itself: oddly enough, we had no primitive yet to draw circles! @ZehMatt stepped up and implemented one in #3559.
Tutorials haven’t changed yet, and probably won’t for a while. But at least we can now draw circles!

OpenGraphics
This month saw 3 contributions from @shussaura85:
An updated dry grass object:

Two new roads:

