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
.
ModSettings options are automatically generated based on the type of each property in your EverestModuleSettings class.
bool => TextMenu.OnOff
Using a boolean
property will generate a TextMenu.OnOff item, which can be toggled using left/right or confirm.
[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 => 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 => 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).
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
}
[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(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(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]
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]
Adding the SettingNeedsRelaunch
attribute to a property will show a warning that a relaunch is required for changes to take effect.
[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(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(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(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(bool inGame)]
Adding the SettingInGame
attribute to a property will set whether the option is displayed in the in-game pause menu.
[SettingIgnore]
Adding the SettingIgnore
attribute to a property will prevent the option from being displayed in the ModOptions menu.
[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.
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.
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.
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.
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:
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.
!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.
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
}
Setting*
attributes will stop working.