Writing a mod for Cities Skylines (1)

Writing a mod for Cities Skylines (1)

Hi!

Last week I mentioned that I was in the progress of writing my first, simple mod for Cities Skylines. Now that it’s finished, I’m going to describe how I did it and take a look into the internals of Cities Skylines.

The “Natural Disasters” DLC for Cities Skylines adds the ability for disasters to randomly happen in your city and potentially destroying part of it. To protect your citizens, a collection of emergency shelters has been added to the game. A medium city with 150 000 inhabitants needs quite a lot of shelters to be completely protected, which in turn cost a lot of money to maintain. Disasters happen only very seldom, and thus this cost is a big waste for the city’s already rather small weekly budget. It should be possible to enable all of the shelters when a disaster is on it’s way, and that is where my mod comes in. It adds a new “batch enable/disable” button to a shelter’s information panel, allowing you to enable or disable all shelters with a single click.

For this to be possible, I first had to carefully go through these excellent tutorials. It turns out that are not a lot of useful resources on how to develop simple mods for CS, and the documentation provided by Colossal Order isn’t a lot better either. The full source code for my mod is publicly available on GitHub.

The implementation

The project only consists of 3 simple C# classes. A class implementing the IUserMod interface should be present in all cases. It contains the mod’s name and a short description that will be shown in CS’s content manager.

using ICities;
using UnityEngine;

namespace CSBatchBuildingEnabler {
    public class CSBatchBuildingEnabler: IUserMod {
        public string Name => "Batch Building Enabler";
        public string Description => "Batch enable/disable all buildings of a specific type.";
    }
}

We want to eventually add a button to the information panel of the emergency shelter. For this to work, we first have to find out how a button can be created, and then how we can append it to the respective UI element of the panel. Modifying the UI is not something that is directly supported by Colossal Order and information about how to do this, can only be found on 3rd party websites or by manually digging through a decompiled version of the game.

Decompiling the game

Cities Skylines is built using the Unity engine, and is thus largely written in C#. An important advantage of C# is that it can easily be decompiled using a program such as ILSpy. By browsing through all provided classes in CS’s main DLL’s (Assembly-CSharp.dll and ColossalManaged.dll), I found a lot of different UI-elements that are apparently being used in the game:

List of some UI-elements found in a decompiled ColossalManaged.dll

The UIButton seems to be perfect for what we want to do. Before we continue, we should find the information panel to which we append a new button. As mentioned in this tutorial such a panel is found in one of the WorldInfoPanel classes. By searching for “shelter” in the decompiled Assembly-CSharp.dll, I found the ShelterWorldInfoPanel. We can check that this indeed is the panel we are looking for by installing the “ModTools” mod. This mod allows us to execute some C# code while running the game, and directly see the result. We try to append a button to the aforementioned class with following code:

ShelterWorldInfoPanel shelterInfoPanel = UIView.library.Get<ShelterWorldInfoPanel>(typeof(ShelterWorldInfoPanel).Name);
UIButton button = shelterInfoPanel.component.AddUIComponent<UIButton>();

button.width = 200f;
button.height = 40f;
button.text = "Batch enable/disable";

button.relativePosition = new Vector3(230f, 260f);

Our new button immediately pops up:

A first try in adding the desired button

One can immediately spot that the button has no styling whatsoever. It looks like a plain label that is clickable. This page helps us to fix this issue and demonstrates which styles should be applied to the button in order for it to look the same as all other buttons in the game. The code that controls the button’s placement now looks like this:

ShelterWorldInfoPanel shelterInfoPanel = UIView.library.Get<ShelterWorldInfoPanel>(typeof(ShelterWorldInfoPanel).Name);
UIButton button = shelterInfoPanel.component.AddUIComponent<UIButton>();

button.width = 200f;
button.height = 40f;
button.text = "Batch enable/disable";

// Style the button to look like a menu button.
button.normalBgSprite = "ButtonMenu";
button.disabledBgSprite = "ButtonMenuDisabled";
button.hoveredBgSprite = "ButtonMenuHovered";
button.focusedBgSprite = "ButtonMenuFocused";
button.pressedBgSprite = "ButtonMenuPressed";
button.textColor = new Color32(255, 255, 255, 255);
button.disabledTextColor = new Color32(7, 7, 7, 255);
button.hoveredTextColor = new Color32(7, 132, 255, 255);
button.focusedTextColor = new Color32(255, 255, 255, 255);
button.pressedTextColor = new Color32(30, 30, 44, 255);

// Enable button sounds.
button.playAudioEvents = true;

// Place the button.
button.relativePosition = new Vector3(230f, 260f);

Finding the best position for a button is a matter of trial-and-error using ModTools. I’ve tried more than 10 different coordinates, until I found the perfect spot. Our button looks a lot better at this moment:

Styling our custom button to make it look better

Conclusion

At this moment, we’ve already implemented a large part of our mod. It’s appearance is more or less done, and we only need to add most of the logic that controls the status of all shelters. This will be covered in the following and last part of how I developed my first mod for Cities Skylines.

Leave a Reply

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