OpenLoco v24.10 is out! In this release we’ve added support for subfolders in the object index, reimplemented chart drawing, started work on a new widget system, and many other things!

For a complete summary of changes, please find the OpenLoco changelog on GitHub.

First off: assorted bug fixes (#2623, #2625, #2653, #2658)

As usual, we start off with some bug fixes. While we’re taking great care not to break the game as we’re reimplementing bits of it, we’re only human, and do make mistakes. Often, it’s small assumptions that end up not holding.

First on the list is a crash that could occur when attempting to place a track underground. Under some circumstances, this could lead to a negative Z coordinate, which is invalid in the game’s coordinate system. Oops!

Second is a crash that could occur when recalculating a vehicle’s reliability. As it turns out, this is hard to do if a vehicle doesn’t have any cars or parts left, such as when you’re selling it gradually. It’s a small code change, but selling no longer causes a crash now.

We continue with a more obscure crash: resizing the game window while a loading bar is open could cause the game to crash! This needed a bit more shuffling of code, but in the end, the game no longer crashes! As an added bonus, the loading bar is now repositioned when the game window is resized. Nice!

Finally, and perhaps most impressively, some track rendering bugs were fixed. In previous reports, we’ve detailed how the track ‘paint’ functions have been reimplemented. It turns out there were a few unintuitive mistakes in bounding box calculations where track and track additions were concerned. In some edge cases, entire vehicles could temporarily clip out of existence! We include an extreme example below, and what it’s supposed to look like.

Track clipping (before) Track clipping (after)

Updates to object index and selection (#2629, #2650)

A frequent request we’ve gotten over the years, is to allow the game object data to be organised in a more flexible way. In order for this to be possible, the full object manager and index code needed to be reimplemented. Good news, we’re at that point! This month, @duncanspumpkin reworked the object index code, such that the index process now recurses into subfolders. Moreover, you can now store custom object data files in a new objects folder in your OpenLoco user folder. This way, you can keep your custom objects separate from the vanilla object files, making it easier to revert in case of a buggy one.

This brings us to the object selection window. There are many object types in the game, which can make the advanced and expert modes more than a little puzzling. This month, we’ve changed things such that related object types now appear together. This is done in a way analogous to the vehicles tab, which was already using sub-tabs for each of the vehicle types.

Grouping these items together brings the number of main object tabs down from 20 (advanced) or 34 (expert) to just 12. This makes the window less cluttered, and thereby hopefully makes it easier to find the right tab.

Before / after:

Object selection (before) Object selection (after)

Replacing loco_globals with state structs (#2607, #2639, #2640, #2641)

In the process of reimplementing all Locomotion functions in C++, we have to interface with the original game’s memory space. We achieve this by means of a structure we call loco_globals. As time progresses, we rely less and less on ‘vanilla’ memory space, and as such we can replace these globals with our own structs. This month, we’ve continued work on doing just that.

The construction window now uses a ConstructionState struct, replacing 70 loco_globals. This will hopefully make it easier to extend the construction window in the future.

On a smaller scale, the windows for the various news styles (newspaper, plain window, ticker) now use a new NewsState struct, replacing 10 loco_globals. This has also allowed us to massively simplify the way the news windows’ interactive “photo” viewports get initialised, making the code safer, more robust, and more readable.

Finally, the graphs in company list window now use a GraphSettings struct, replacing 25 loco_globals. This also sets the stage for the next item on our list…

Implement chart/graph drawing and allow resizing charts (#2646, #2666)

While the company list window had been reimplemented in C++ for a few years, we still relied on the vanilla routines to actually draw the various charts available. This month, this was finally tackled.

Not all of the graph settings were understood yet, but reimplementing the code gradually revealed their meanings. Our C++ implementation quickly opened up the possibility of resizing the chart windows, examples of which can be seen below. In the future, we could even add a few more charts!

Before / after:

In-game charts (before) In-game charts (after)

Implement various town and scenario functions (#2632, #2633, #2636, #2637)

While our reimplementation efforts continue, we’re sometimes surprised to find smaller bits and bobs that haven’t yet been reimplemented. Such is the case with various town functions. For example, the town initialisation function hadn’t yet been covered. It wasn’t difficult, but tackling this is essential for future expansion of the town struct.

More interesting to cover is the town name generation. While we had a rough idea of how this worked, now that the name generation code is covered, we know exactly how the town name objects work. Expect an update for the Object Editor to cover this in the future.

Widget system refactor work (#2635, #2642, #2659, #2669)

As won’t be surprising to players familiar with the game, Locomotion uses its own window system to display various bits of information. These windows are all made up of several widgets. It’s a versatile system, but it’s not very flexible: all widgets are positioned at precise pixel coordinates, and span a particular width and height. This makes it cumbersome to resize a window, for example: all widgets have to be manually repositioned and resized as needed. Surely we can do better?

Leaving the rigid struct design behind, @ZehMatt set out to refactor the widget system further, moving each of the individual widget types into their own headers and compilation units. In the near future, this will let each of the widget types have their own event functions, and let widgets be containers for other widgets. As such, the widget hierarchy will allow us to infer positions based on parent and anchors instead. This will require a fair bit more work, but it will be worth it!

Updated: