Mod Settings - EverestAPI/Resources GitHub Wiki

To save settings associated with your mod, you can make use of the EverestModuleSettings class, which will automatically generate options that can be found in the ModOptions menu.

First create a class that extends EverestModuleSettings and add the following to your EverestModule class.

public override Type SettingsType => typeof(ExampleModuleSettings);
public static ExampleModuleSettings Settings => (ExampleModuleSettings) Instance._Settings;

Now add any number of Properties 🔗 to your Settings class. Options will be generated based on the property type and attributes.

Settings will be saved to and loaded from Saves/modsettings-[modname].celeste.

Property Reference

Property Types

ModSettings options are automatically generated based on the type of each property in your EverestModuleSettings class.

Boolean

bool => TextMenu.OnOff 

Using a boolean property will generate a TextMenu.OnOff item, which can be toggled using left/right or confirm.

Integer

[SettingRange(int min, int max)]  
int => TextMenu.Slider  
[SettingRange(int min, int max, bool largeRange)]  
int => TextMenuExt.IntSlider 

Using an int property with the SettingRange attribute will generate a TextMenu.Slider item, which can go from min to max using left/right.
Setting largeRange to true will generate a TextMenuExt.IntSlider, which is optimized for larger ranges (upwards of 50 values).

Enum

Enum => TextMenu.Slider  

Using an Enum type property will generate a TextMenu.Slider item using the values of the enum type, ordered by an integer cast.

The displayed name of each value is the name of the appropriate enum constant by default. To specify a different name, use the dialog key modoptions_yourmod_settingname_EnumValue, where yourmod is created from the name of your EverestModuleSettings type by stripping Settings off the end and making the rest lowercase, settingname is the lowercased name of the property the value belongs to, and EnumValue is the name of the enum constant, preserving case.

String

string => TextMenu.Button => OuiTextEntry

Using a string property will generate a TextMenu.Button which, when pressed, will open up a text entry screen similar to File Naming.
The SettingMaxLength attribute can be used to set the maximum possible length of the string (defaults to 12 characters).

⚠️ This property will not display on the in-game pause menu.

ButtonBinding

ButtonBinding => TextMenu.Button => ModuleSettingsButtonConfigUI/ModuleSettingsKeyboardConfigUI

Using a ButtonBinding property will generate two TextMenu.Buttons which will open up a ButtonConfigUI and KeyboardConfigUI, respectively. The DefaultButtonBinding attribute can be used to set the default bindings.

Once added, the ButtonBinding property can then be used similarly to any Input.
For example:

public override void Update(){
   if (Settings.CustomButtonBinding.Pressed)
      // Do something
}

Attributes

SettingName

[SettingName(string name)]  

Adding the SettingName attribute to a property will set the name of the option to the dialog id assiociated with name.
If SettingName is not added, or the dialog id does not exist, the name of the option will be the property name with spaces before capital letters.

SettingSubText

[SettingSubText(string description)]  

Adding the SettingSubText attribute to a property will add a description that will show when the option is selected.
If description is a dialog id, the associated dialog will be displayed.

SettingSubHeader

[SettingSubHeader(string subheader)]

Adding the SettingSubHeader attribute to a property will add a subheader before that setting. The text of the subheader will be determined by using the subheader argument as a dialog ID, or the string itself if the dialog ID cannot be found.

ℹ️ Requires Everest version 3505 or later.

SettingSubMenu

[SettingSubMenu]

Adding the SettingSubMenu attribute to a nested class (not a property) will show properties inside that class as a submenu, if there is a property that is an instance of that class.
For more information, see Creating submenus below.

SettingNeedsRelaunch

[SettingNeedsRelaunch]

Adding the SettingNeedsRelaunch attribute to a property will show a warning that a relaunch is required for changes to take effect.

SettingRange

[SettingRange(int min, int max)]  
[SettingRange(int min, int max, bool largeRange)]  

Adding the SettingRange attribute to an Integer property will allow that property to be displayed as a slider.
The range of the slider is set using min and max (inclusive).
If largeRange is true, the option will be a TextMenuExt.IntSlider, which is functionally the same, but better suited for larger ranges (upwards of 50 values).

SettingNumberInput

[SettingNumberInput(bool allowNegatives, int maxLength)]

Adding the SettingNumberInput attribute to an Integer or Float property will create a button to open a number entry screen.
maxLength (default 12) sets the number of digits allowed.

SettingMaxLength

[SettingMaxLength(int max)]

Adding the SettingMaxLength attribute to a String property will set the maximum possible length of the string to max (defaults to 12 characters).

DefaultButtonBinding

[DefaultButtonBinding(Buttons button, Keys key)]

Adding the DefaultButtonBinding attribute to a ButtonBinding property will set the default button and key associated with the setting to the supplied values. The ForceDefaultButton and ForceDefaultKey properties can be set in order to ensure that the specified input is always bound. If there is no default binding set the button or key to 0.

ℹ️ The Buttons and Keys Enumeration types come from the Microsoft.Xna.Framework.Input namespace.

SettingInGame

[SettingInGame(bool inGame)]

Adding the SettingInGame attribute to a property will set whether the option is displayed in the in-game pause menu.

SettingIgnore

[SettingIgnore]

Adding the SettingIgnore attribute to a property will prevent the option from being displayed in the ModOptions menu.

YamlIgnore

[YamlIgnore]

Adding the YamlIgnore attribute to a property will prevent the setting from being saved into the modsettings file.

ℹ️ This attribute comes from the YamlDotNet.Serialization namespace.

Custom Entries

Making a custom entry for a property

If your setting does not fit any case above and you need to write your own code to make it appear in Mod Options, you can do so by implementing a method called Create{PropertyName}Entry:

public void CreateSomethingEntry(TextMenu menu, bool inGame)

This method will be called by Everest when the option should be added to the menu. It should add an option to the given menu. inGame will be true if the player accessed mod options from the pause menu, or false if they accessed it from the main menu.

For example:

public int ToggleBetween40And50 { get; set; } = 40;

public void CreateToggleBetween40And50Entry(TextMenu menu, bool inGame) {
    menu.Add(new TextMenu.OnOff("Should Be 50", ToggleBetween40And50 == 50)
        .Change(enabled => ToggleBetween40And50 = (enabled ? 50 : 40)));
}

This code creates an on/off switch for an integer option. If the switch is off, ToggleBetween40And50 is set to 40, and if the switch is on, ToggleBetween40And50 is set to 50.

Creating submenus

You can easily group related settings into submenus, for a cleaner look.
Just create a nested class storing those settings, and then create a named instance of it. For example:

public SubMenuOne SubmenuName { get; set; } = new SubMenuOne();

[SettingSubMenu]
public class SubMenuOne
{
    // All settings inside this class will show up inside the submenu
    public bool SettingOne { get; set; } = true;

    [SettingRange(0, 100, LargeRange = true)]
    public int SettingTwo { get; set; } = 10;
}

This will create a submenu with an arrow next to it.
Once pressed, the options "pop out", and moving up/down now moves inside the submenu, looping back up once you reach the last submenu item.
Pressing back moves you back to the original menu.

Dynamic settings (settings created at runtime)

You can use a custom creator method to create dynamic settings.
These can appear inside a submenu (example below), but they don't have to; you can also place the dummy entry and its Create...Entry() method outside of a nested submenu class.

// Used to actually store the dynamic settings
// SettingIgnore makes Everest hide it, but it will still load/save the settings automatically
[SettingIgnore]
public SortedDictionary<string, bool> DynamicSettings { get; set; } = [];

[YamlIgnore]
public DynamicSettingsMenu SubmenuName { get; set; }

[SettingSubMenu]
public class DynamicSettingsMenu
{
    // The Create...Entry method just below replaces this one item with whatever you add in the method
    [YamlIgnore]
    public bool DynamicSettingsDummy { get; set; } = true;

    // Don't make this static no matter what your IDE says; Everest only looks for instance methods
    public void CreateDynamicSettingsDummyEntry(TextMenuExt.SubMenu subMenu, bool inGame)
    {
        // Add a bunch of boolean settings
        foreach (var (setting, value) in YourModule.Settings.DynamicSettings)
        {
            subMenu.Add(new TextMenu.OnOff(setting, value).Change(newValue =>
                YourModule.Settings.DynamicSettings[setting] = newValue));
        }
    }
}

Elsewhere in the code, add the settings you want to the DynamicSettings dictionary with their default value.
After that, they will show up as soon as the user opens the settings menu.

If you have settings of various types, just change the type of the dictionary to e.g. <string, object>, and perform type checks in the foreach loop: create TextMenu.OnOff settings if value is bool, TextMenu.Slider if value is int and so on.

Creating a full-screen submenu

You can create a submenu that is displayed full-screen by creating a class like this:

using Celeste.Mod.UI;

namespace Celeste.Mod.Example {
    class OuiExampleSubmenu : OuiGenericMenu, OuiModOptions.ISubmenu {
        public override string MenuName => "SUBMENU EXAMPLE";

        protected override void addOptionsToMenu(TextMenu menu) {
            menu.Add(new TextMenu.OnOff("Some toggle", false)
                .Change(newValue => Logger.Log("OuiExampleSubmenu", $"The value changed to {newValue}")));
        }
    }
}

This will create a menu looking like this:

Submenu screenshot

After that, you have to create a button in Mod Options to access this screen. You can do so by adding this to your settings class:

[YamlIgnore]
public int SubmenuExample { get; set; } = 0;

public void CreateSubmenuExampleEntry(TextMenu menu, bool inGame) {
    if (!inGame) {
        menu.Add(new TextMenu.Button("Submenu Example")
            .Pressed(() => OuiGenericMenu.Goto<OuiExampleSubmenu>(overworld => overworld.Goto<OuiModOptions>(), new object[0])));
    }
}

The first parameter of OuiGenericMenu.Goto is a delegate called to go back to the parent menu (in this case, Mod Options). The second parameter is an arbitrary parameter array you'll be able to access from your submenu with the parameters local variable.

⚠️ This doesn't work for in-game submenus (hence the !inGame check in the example code).
If you need an in-game submenu, look at the examples in the sections above. If you absolutely need a full-screen in-game submenu, ExtendedVariantMode 🔗 has those, but it takes a fair bit of code (hundreds of lines).
Ask maddie480 on Discord.

Making a custom Mod Options section

If the above is not enough for your needs, you can decide to build the whole Mod Options section for your mod by yourself. To do that, override the CreateModMenuSection method in your EverestModule class (not your settings class):

public override void CreateModMenuSection(TextMenu menu, bool inGame, EventInstance snapshot) {
    // your own section creation logic
}

⚠️ Since, by doing that, you are handling menu creation by yourself, all Setting* attributes will stop working.

⚠️ **GitHub.com Fallback** ⚠️