3.2 ModMenu by Zat - LionShield/Kingdoms-and-Castles-Toolkit GitHub Wiki
This API provides functionality to interact with the mod menu.
In order to use the API in your project, copy all of the files from Zat's Mod repository) into your project. To test them, make sure you subscribed to the ModMenu mod on Steam Workshop or else the menu will not show up (press O to toggle the menu on and off). That's all there is to it!
The mod menu is a loose collection of InteractiveSettings. These are the individual settings you can see in the menu, containing
- a path (
<category 1>.<category n>.<settingName>), - a description (an arbitrary string, can be updated at runtime)
- and a specific type (e.g.
Button,Slider, etc.)
The mod menu automatically generates all UI elements and puts them into categories defined by their path. For example, a setting with a path SettingsClass.General.Gameplay.Difficulty would result in generating the top-level category General (That is defined in the class SettingsClass having the category Gameplay inside that which in turn holds the UI element Difficuly.
Note: If a setting does not provide a category in its path, it is placed in the "Unspecified" default category.
Mods may share settings, however if you wish to update a shared setting's UI, make sure only one mod changes it upon receiving UI updates. If multiple mods change a shared setting's UI upon receiving UI updates, the menu may indefinitely trigger UI updates due to receiving varying data about a setting from various mods.
As of now, the mod menu implements the following types of settings:
-
Button (
InteractiveButtonSettings): A simple button with a configurable label. It will trigger an update whenever it is pressed. Use it to allow users to execute functions of your mod, e.g. resetting or cleaning up stuff. Usage:
[Setting("Name in menu", "Description in settings menu")]
[Button("Label")]
public InteractiveButtonSetting SettingNameInCode { get; private set; }-
Slider (
InteractiveSliderSettings): A numeric slider with configurable minimum, maximum, value and label. You can also set it to only allow whole numer selections. It will trigger an update when its value changes. Use it to let users configure any numeric values.
Usage:
Wheremin,maxandvalueare floats.wholeNumberis a bool
[Setting("Name in menu", "Description in settings menu")]
[Slider(min, max, value, "label", wholeNumber)]
public InteractiveSliderSetting SettingNameInCode { get; private set; }-
Select (
InteractiveSelectSettings): A dropdown menu with configurable options to choose from and selected index. It will trigger an update when users select an option. You can use it to allow users to choose from various options (e.g. difficulty "Very Easy", "Easy", "Normal", "Hard", "Very Hard").
Usage:
Wherevalueis the index of the value in the array and the array are the array of possible options
NoteSettingNameInCode.valuewill return the index of the option not the option. UseSettingNameInCode.options[SettingNameInCode.value]in order to get the option that is selected
[Setting("Name in menu", "Description in settings menu")]
[Select(value, new string[]{"option1", "option2"})]
public InteractiveSelectSetting SettingNameInCode { get; private set; }-
Toggle (
InteractiveToggleSettings): A checkbox with configurable label and value. It will trigger an update it is being clicked. Use it to allow users to toggle options on and off (e.g. enable/disable your mod).
Usage:
Wherevalueis a boolean wheretrueis the box is ticked
[Setting("Name in menu", "Description in settings menu")]
[Toggle(value, "label")]
public InteractiveToggleSetting SettingNameInCode { get; private set; }-
Color (
InteractiveColorSettings): A color-picker featuring sliders for RGBA channels with configurable color value. It will trigger an update whenever a slider is being modified by users. Use it to allow users to pick a color for something (e.g. the color of visual indicators).
Usage:
Wherered,green,blueandalphaare floats. Alpha is optional
[Setting("Name in menu", "Description in settings menu")]
[Color(red, green, blue, alpha)]
public InteractiveColorSetting SettingNameInCode { get; private set; }-
Hotkey (
InteractiveHotKeySettings): A button that listens for keypresses when being clicked. It will trigger an update whenever a new key was set by users. Use it to allow users to rebind mod-functionality (e.g. bind a toggle-key to toggle your mod on/off).
Usage:
Wherekeyis aUnityEngine.KeyCode,ctrl,altandshiftare booleans.
[Setting("Name in menu", "Description in settings menu")]
[Hotkey(key, ctrl, alt, shift)]
public InteractiveHotkeySetting SettingNameInCode { get; private set; }The access the values of your settings is a very simple process.
InteractiveSetting interactiveSetting = ..<category_n>.
Button
Does not store settings. Use it by inside of your private void OnModRegistered(ModSettingsProxy proxy, SettingsEntry[] saved){} adding:
SettingsClass.ButtonCategory.Button1.OnUpdate.AddListener(
(SettingsClass) =>
{
// Code to execute whenever the button is pressed
// Eg a settings reset button
});Slider
float setting = ModSettingsClass.SliderCategory.Slider1.value;
Select
int settingIndex = ModSettingsClass.SliderCategory.Slider1.value;
string setting = ModSettingsClass.SliderCategory.Slider1.options[ModSettingsClass.SliderCategory.Slider1.value];
In your start section you may want to set a shorter variable to equal the setting by reference. Eg
class MyMod{
private string _setting = "";
public void Start(){
_setting = ModSettingsClass.SliderCategory.Slider1.options[ModSettingsClass.SliderCategory.Slider1.value];
}Toggle
boolean setting = ModSettingsClass.ToggleCategory.Toggle1.value
Color
float red = ModSettingsClass.ColorCategory.Color1.Color.r
float green = ModSettingsClass.ColorCategory.Color1.Color.g
float blue = ModSettingsClass.ColorCategory.Color1.Color.b
HotKey
UnityEngine.HotKey hotkey = ModSettingsClass.HotKeyCategory.HotKey1.value
Using the mod menu in your own mod is rather simple. Here's all you need to do:
- Create a Settings class using the
Modannotation in the same namespace as the rest of your mod. In the annotation you specify information about your mod that will appear in the settings menu (ModName, Version, AuthorName).
This class contains all of your settings and settings categories for your mod that will appear in the mod menu. (Settings categories are optional though do make organising large amounts of settings easier).
To create a category just create a class full of the methods seen above and then in the class that has the Mod annotation assign them as seen below using the Category annotation. You can even have straight settings in the Mod class along with the categories!
They will appear in the same order in the mod menu as they do now in the Mod class.
[Mod("KingdomRenderer", "v1.1", "ArchieV / greenking2000")]
public class KingdomRendererSettings
{
[Category("Automatic Rendering")]
public AutomaticRendering AutomaticRendering { get; private set; }
[Category("Manual Rendering")]
public ManualRendering ManualRendering { get; private set; }
}
public class ManualRendering
{
[Setting("Manual Rending Enabled", "Whether or not to enable manual rendering (Requires rendering to be enabled)")]
[Toggle(true, "Enabled")]
public InteractiveToggleSetting ManualEnabled { get; private set; }
[Setting("Manual Render Hotkey", "Which button to press to manually render the map")]
[Hotkey(KeyCode.N)]
public InteractiveHotkeySetting RenderKey { get; private set; }
}
public class AutomaticRendering
{
[Setting("Automatic Rending Enabled", "Whether or not to enable automatic rendering (Requires rendering to be enabled)")]
[Toggle(true, "Enabled")]
public InteractiveToggleSetting AutomaticEnabled { get; private set; }
[Setting("Number of renders per 10 years", "Number of renderings per 10 in-game year")]
[Slider(1, 150, 30, "30", true)]
public InteractiveSliderSetting RendersPer10Year { get; private set; }
}- Register your
ModConfigin the mod menu by using theModSettingsBootstrapper, specifying your config, a callback for successful registration and a callback for failed registration:
public void Start(){
// This method is built into KaC and will run when the game starts
ModSettingsBootstrapper.Register(config.ModConfig, (proxy, saved) =>
{
config.Install(proxy, saved);
OnModRegistered(proxy, saved);
}, (ex) =>
{
Debugging.Log("KingdomRenderer", $"Failed to register mod: {ex.Message}");
Debugging.Log("KingdomRenderer", ex.StackTrace);
});The first callback will receive a reference to a ModSettingsProxy that allows for direct interaction with the mod menu and an array of SettingEntrys that were loaded (in case any settings matching your config were saved earlier). In this callback you can register event listeners that are fired when certain settings are updated or when a reset was issued via UI ("Reset" button clicked):
private void OnModRegistered(ModSettingsProxy proxy, SettingsEntry[] saved)
{
try
{
this.proxy = proxy;
if (!proxy)
{
Debugging.Log("KingdomRenderer", "Failed to register proxy");
return;
}
// Change the text in the settings menu when the options are changed
// Change the update interval label to be the new value
// This needs to be done for sliders if you want the label to be the same as the value it holds
kingdomRendererSettings.AutomaticRendering.RendersPer10Year.OnUpdate.AddListener(
(kingdomRendererSettings) =>
{
this.kingdomRendererSettings.AutomaticRendering.RendersPer10Year.Label =
this.kingdomRendererSettings.AutomaticRendering.RendersPer10Year.Value.ToString();
});
}
catch (Exception ex)
{
Debugging.Log("KingdomRenderer", $"OnRegisterMod failed: {ex.Message}");
Debugging.Log("KingdomRenderer", ex.StackTrace);
}
helper.Log("Mod registered with ModMenu");
}
}!To see the full code used here see here
Note: Mod loading happens synchroniously, however you won't have much control over the order the mods are loaded in or the time it takes to load them. ModSettingsBootstrapper will retry to register your config and even wait between retries. This process can take a few seconds so you may want to decouple your mod's logic from this.
So you encountered a problem using the menu. Please make sure you're using the latest files!
The mod menu outputs errors to output.txt in its mod folder and this is a good place to start investigating errors. The API itself, used in your own mod, provides quite some debugging prints, too. To enable them, set the static fields ModSettingsProxy.Helper and IMCPort.helper to an instance of KCModHelper (obtained in OnScriptLoad and PreScriptLoad). They will then print out any errors they encounter into your local output.txt file in your mod's folder.
These are some problems that developers encountered when working with the menu:
-
Registration failed in mod, but mod menu registered mod: The API may respond with e.g.
The RPC timed out and was not answered in-timeor other error messages when youOnModRegisteredcallback throws an exception. Please make sure that you handle exceptions in your callbacks! - No settings displayed in mod menu: Please make sure to actually define any settings in your ModConfig before registering it.
-
The mod menu doesn't show: Make sure that you actually subscribed to the mod menu in Steam Workshop and that is is enabled and running (check its
output.txt, it will issue a line saying that it started execution). PressOto toggle the menu on and off.
Mod menu by Zat AKA BigMo
Original guide by Zat
Updated 2020-12-04 to InteractiveModMenu by ArchieV AKA greenking2000
Example settings in KingdomRenderer by ArchieV
Example settings in MiniMap by Zat
