# Getting Started with Mod Config Menu If you want to add config options to your Binding of Isaac mod, then instead of programming your own menu system from scratch, you can register your mod's configuration options with [Mod Config Menu](https://github.com/Zamiell/isaac-mod-config-menu), or MCM for short. MCM is a mod that provides an extendable in-game menu.
## The `ModConfigMenu` Global Variable The first thing you want to do is check for the existence of the `ModConfigMenu` global variable. If it exists then you can proceed. ```lua local function modConfigMenuInit() if ModConfigMenu == nil then return end -- Insert code here to add the menu options for your mod end ```
## Common Scenarios In most cases, you will be providing the player with multiple choice options. This can take the form of "on/off" or "A/B/C". ### On/Off On/off looks like this: ```lua -- Specify a table of the MCM settings for this mod, along with their default values local settings = { myBoolean = false, } ModConfigMenu.AddSetting( "My Settings Page", -- This should be unique for your mod "Tab 1", -- If you don't want multiple tabs, then set this to nil { Type = ModConfigMenu.OptionType.BOOLEAN, CurrentSetting = function() return settings.myBoolean end, Display = function() return "My Boolean: " .. (settings.myBoolean and "on" or "off") end, OnChange = function(b) settings.myBoolean = b end, Info = { -- This can also be a function instead of a table "Info on 1st line", "More info on 2nd line", } } ) ``` ### A/B/C (multiple choice) If you need 3 or more options then that looks like this: ```lua local choices = { "A", "B", "C", } -- Specify a table of the MCM settings for this mod, along with their default values local settings = { myMultipleChoice = choices[1] -- The first choice by default } local function getTableIndex(tbl, val) for i, v in ipairs(tbl) do if v == val then return i end end return 0 end ModConfigMenu.AddSetting( "My Settings Page", "Tab 1", { Type = ModConfigMenu.OptionType.NUMBER, CurrentSetting = function() return getTableIndex(choices, settings.myMultipleChoice) end, Minimum = 1, Maximum = #choices, Display = function() return "My Multiple Choice: " .. settings.myMultipleChoice end, OnChange = function(n) settings.myMultipleChoice = choices[n] end, -- Text in the "Info" section will automatically word-wrap, unlike in the main section above Info = { "Info on 1st line" } } ) ``` ### Scroll Bar A scroll bar is a multiple choice option that renders as a scroll bar with 10 bars. The player can choose between 0 and 10 out of 10 for a total of 11 options. This is commonly used to get a decimal number between 0 and 1 (by i.e. dividing by 10). ```lua local settings = {} settings.myScrollBar = 0 ModConfigMenu.AddSetting( "My Settings Page", "Tab 1", { Type = ModConfigMenu.OptionType.SCROLL, CurrentSetting = function() return settings.myScrollBar end, Display = function() return "My Scroll Bar: $scroll" .. settings.myScrollBar end, OnChange = function(n) settings.myScrollBar = n end, Info = { "Info on 1st line" } } ) ```
## Layout You can add a title, text, or vertical spacer like this: ```lua ModConfigMenu.AddTitle("My Settings Page", "Tab 1", "My Title") ModConfigMenu.AddText("My Settings Page", "Tab 1", "My Text") ModConfigMenu.AddSpace("My Settings Page", "Tab 1") ```
## Saving and Loading ### Saving You are responsible for saving your settings, which can be as simple as: ```lua local json = require("json") local mod = RegisterMod("MyMod", 1) -- If your mod does not store any other save data, then you can simply run the "save" function in -- every MCM "OnChange" function. Otherwise, you need to run it on both the "OnChange" functions and -- MC_PRE_GAME_EXIT. local function save() -- TODO: use pcall to gracefully handle failure local jsonString = json.encode(settings) mod:SaveData(jsonString) end ``` ### Loading You are also responsible for loading your saved settings, which can be as simple as: ```lua -- If your mod does not store any other save data, then you can simply run the "load" function -- during mod initialization. Otherwise, you need to run it on both initialization and at the -- beginning of a run. local function load() if not mod:HasData() then return end -- TODO: use pcall to gracefully handle failure -- TODO: add checks for validate the data local jsonString = mod:LoadData() settings = json.decode(jsonString) end ``` ### IsaacScript If you are coding your mod using TypeScript, then the [IsaacScript](https://isaacscript.github.io/) standard library has a save data manager that you should use instead of handling saving and loading yourself manually. ```ts // Config.ts export class Config { feature1 = true; feature2 = 1; // and so on } // modConfigMenu.ts const v = { persistent: { config: new Config(), } } function modConfigMenuInit() { saveDataManager("modConfigMenu", v); } ```
## Further Reading - See the [Mod Reference](https://wofsauge.github.io/IsaacDocs/rep/ModReference.html) documentation for related saving and loading information. - See the [API documentation](api.md).