Diary #54 – Workers and Resources: Iron Village

The theme of the 1.2 update. Original logo ©3Division. I hope you’ll forgive my very dumb visual gag 😅. I had an idea and it stuck in my head.

I haven’t put one of these dev diaries together in a bit, life has been a bit hectic, but I wanted to talk about the infrastructure behind the new UI windows that let you get a closer look at your… workers and resources.

I showed the maximum production rates in the last diary, but compared to implementing the current production rate charts, that was pretty easy: check the building counts (how many of each building we have), look up the inputs, outputs, and production times, and calculate the maximum output. All of that information was already there, I just chose to refactor things to make that data more easily and efficiently accessible.

(Discussion of coding practices follows, feel free to skip down to the pretty pictures later on.)

Meanwhile for current production rates, I needed data on what resources were produced, and when. That was information that just wasn’t being saved. There was the recent approval events feature in the Railway Status window, which records changes in approval rating and their times, but that was specifically designed just for approval events. So, using knowledge of Godot best practices that I acquired after starting development of Iron Village, I started putting together an EventLogManager.

A lot of the code in Iron Village uses singletons: basically, an object that’s designed to only have one instance at a time. Some of these are “autoloads”, and are always-on global instances. This isn’t necessarily the best practice in normal coding, but in the scope of a single player game there’s a few areas that this is perfect for, including the GlobalSettings object that manages client settings, and the SteamworksManager which handles connecting to and communicating with Steam. Some of the singletons are specific to the root scene, where the actual gameplay takes place (as opposed to the main menu scene) – the best example for this dev diary is the ApprovalManager, which handles tracking the railway’s approval of your work. It doesn’t make sense to have multiple managers for this, so it’s a singleton.

Singletons by themselves aren’t bad, and are actually really useful in the context of single player games. What wasn’t so great was that the ResourceVehicle class (a train car carrying cargo) was directly calling this singleton to report approval changes. It’s not the end of the world, but making a bunch of these calls from various different nodes can turn things into a bit of a confusing web. Godot is designed to work as a “tree”: basically, there’s a root “node” (an object, e.g. an image, a sound player, a collision shape, or just an abstract invisible thing), and that node can have a bunch of children, and each child node can have its own children, etc. In this case, a great grandchild node of the root (the ResourceVehicle) was calling a separate child node (ApprovalManager).

The saying goes (yes, Godot is big and old enough that it has its own sayings), “call down, signal up”. Basically, a parent node will tell its children what to do (this is a fantasy of course, because the children actually listen), but the child should emit a signal: it yells into the void, and the parent may or may not react to that signal (that part is super realistic). This makes the flow of the program much more predictable, and hopefully avoids confusing bugs where the game isn’t doing what you think it should have done.

To start the EventLogManager, I rewrote the approval event code to use the upward signaling. This did involve a decent amount of boilerplate code unfortunately, since the call would have to go ResourceVehicle -> Train -> TrainManager -> GameManager -> ApprovalManager, instead of ResourceVehicle -> ApprovalManager. (In most cases, it is actually even worse, the water for a locomotive is represented by a ResourceVehicle child of a Locomotive.) However, the code flow is at least quite a bit easier to track.

I also did the same thing for buildings producing and consuming resources (ResourceProducer -> Building -> BuildingsManager -> GameManager -> EventLogManager), and so now there’s a system for signaling and recording resource production, resource consumption, and approval changes. The current production rates and railway status windows can just refer to the EventLogManager for these events, and make any calculations from the logs.

Since you made it this far (or just skipped ahead), here’s a side-by-side comparison of the level 2, 3, and 4 railway HQs (not including additional props rendered in game):

Iron Village 1.2 is definitely coming together now, there was just a lot of underlying infrastructure to put together to support it. Hope that was at least somewhat interesting. 😊


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *