OpenLoco v24.05 is out! We’ve made so much progress this month, so we’re very excited to share this new release with you all. Let’s dig into the details in this dev log. For a summary of changes, please find the changelog on GitHub.
Progress bars are back, and better than ever! (#2411, #2440)
One of the charming features of the original Locomotion game was its use of a train for a progress bar. This has been missing in OpenLoco for a while, but we’ve brought these progress bars back in this release! But why were they removed in the first place?
When we reimplemented loading and saving games in C++, we were surprised by how much faster our implementation was, such that we elected progress bars were unnecessary. However, loading objects can still take a couple of seconds, especially on saves that pack a bunch.
In addition to progress bars for loading and saving games, the game would use ‘native’ Windows progress bars before the game is launched, e.g. to indicate it was checking and indexing its object files. We never got around to reimplementing these in C++, so these were left alone, and therefore were working like vanilla Locomotion.
We’ve now reworked the OpenLoco startup procedure, such that the game palette is loaded before objects are checked. This means we can now enjoy progress bars on the game canvas from very early on! They have also been re-added for save game loading and saving, as well as landscape generation.
You might think rendering progress bars might slow down the game a little, but the opposite is true. In fact, in adding progress bars, we actually discovered the title scene was being loaded twice! We were able to narrow down why this is happening, and so sped things up quite considerably!
Make focus optional for text input widgets (#2416)
Over the past few releases, we’ve added text input fields to filter items in various lists. For example, you can now filter objects in the object selection window, and components the vehicle build window. The only problem was that these fields would always be focused, and therefore broke shortcuts.
Text fields can now be focused by clicking them, and focus can be removed (blurred) by clicking somewhere else. We expect this will make things more intuitive again.
Game command reimplementation efforts continue (#2402, #2410, #2439, #2456)
Our long-standing project to reimplement the game commands continues! Basically covering anything a player can do on a map, a full reimplementation is a requirement for us to add a stable multiplayer mode.
This month, we reimplemented the airplane vehicle placement command, and the commands for removing train stations and ship ports (docks). This brings the number of implemented game commands to 63 out of 84, leaving just 21 remaining! Unfortunately, some of the remaining ones are quite long and tricky, but we’re getting there!
Add last month production column to the industries list (#2426)
New contributor @Sebazzz added a new column to the industries list that displays last month’s production. Below is a screenshot to show it in action. Nice work!
Rework window storage and increase the window limit (#2419, #2436, #2437, #2438)
Locomotion only reserved space for 12 windows, including toolbars and panels. While this has served us well, this could sometimes be a bit limiting – especially on larger displays. This month, we’ve refactored the ways windows are kept in memory internally, and increased the storage limit to 64 windows.
Side note: a basic window takes up about 2kB of RAM, not including widgets. This means vanilla was using 24kB of memory for windows. Thankfully, memory isn’t that scarce any more, so we’re affording a whopping 128kB now.
Correct naming and use of ViewportVisibility::heightMarksOnTrack (#2468)
Remember how we changed the granualarity of the see-through viewport flags earlier this year? Well, it seems we missed a bit, which meant the surface height markers were appearing during construction, when track height markers were meant to be shown!
Interestingly, this is down to a much older mistake in our reimplementation, where we had swapped the two flags internally. Only now that our implementation is furthering do these mistaken labels start having an effect!
Implement tile element insertion (#2415, #2421)
In grid-based games like Locomotion, tile elements comprise everything that is or can be placed on the map. Oddly enough, though, we hadn’t gotten around to reimplementing the function that actually allocates these elements. Instead, we just continued to use the vanilla implementation. That is no longer needed now.
Last time, we mentioned we were working on No Locomotion versions of the game. These versions do not call into vanilla Locomotion functions at all, and highlight areas that our code doesn’t cover yet. Tile element insertion was one of these areas. This means we can now actually get in-game!
Implement number formatting (#2118, #2453)
Whenever strings are displayed in-game, they go through a formatting process. This process can change text colours or fonts, but also fill in placeholders with actual data. This can be the name of an industry, but also a numerical value, including currency. Turns out we hadn’t actually reimplemented number formatting in C++!
Formatting is a surprisingly controversial topic, what with different cultures using different ways of separating decimals, or indeed grouping thousands – or indeed not grouping by thousands, but ten-thousands (Chinese, Japanese, Korean).
We’ve elected to defer number formatting to a library we had previously incorporated, libfmt. While we currently only support grouping numbers by thousands, we aim to support alternatives in the future, which should be made easier through this library.
Rework string formatting (#2461, #2464, #2469)
With the last formatting codes reimplemented, we could finally refactor the way strings are reformatted. The previous approach was very similar to vanilla, which wasn’t actually that memory safe. This mostly showed in the buffer length not being taken into account at all times. This could become problematic in the future, particularly when networked games come in.
This month, string formatting has been completely reworked. It is now memory safe, and prepared for a move to unicode strings in a future release.
Rework widget allocation (#2418, #2420, #2452)
On the topic of reworking code that has been with us since the project was started… This month, we also reworked how widgets are allocated. Previously, widgets were shared across all windows of the same type. Our new approach allocates widgets for the window, adding storage to allow for format arguments to be stored per widget as well.
In practice, this does not mean much for the game yet. However, behind the scenes, this means a lot of rearranging of widgets only needs to happen once, instead of every time a window is rendered. This will require some more refactoring, but we think it will be worth it.
Implement surface update function (#2414)
We also implemented the surface update function this month. This function takes care of surface variation, such as grass growing back after terraforming. In addition, it updates snow coverage on mountains. Interestingly, it also takes care of fences around industrial areas. Nice to have this in as well!
Remove a bunch of old and dead functions (#2430, #2432, #2433, #2435)
Our code base has grown quite organically over time. Some parts of the code had not been touched much since they were first implemented, though. It turns out some of the code in question was not actually used at all anymore! Such ‘dead code’ distracts from the other code present in the code base, so removing it was a no-brainer.
Unload scenarioText objects when entering custom title (#2466)
Normally, scenarios only allow one set of title and description to be present. User @Sebazzz noticed that it wasn’t possible to change the scenario titles at all for existing saves loaded in the scenario editor. This was due to a particular kind of game object called ‘scenario text objects’. These allow for a different title and description to be used depending on the language used in the game. However, they’re also read-only, so in this case, they’re not particularly helpful. We’ve changed it such that these objects are automatically deselected and unloaded in such cases.