-
Notifications
You must be signed in to change notification settings - Fork 169
MCM Quickstart
The Mod Configuration Menu (MCM) originally is a general-purpose control panel for Fallout: New Vegas created by Pelinor. We took up the task to port the concept to Skyrim and implemented it as the SkyUI control panel.
This guide will give you a quick overview of all necessary steps to add your own config menu. Once you're familiar with the basic concepts, you should have no trouble picking up the rest from the examples we provide, or just by taking a look at the API reference.
For the rest of this guide, we assume that you're familiar with the Creation Kit and Papyrus. If that's not the case, you should probably head over to the Creation Kit wiki and do some reading there first.
In case you were linked to this guide directly from somewhere else, you should have a look at the overview first, especially to get the SkyUI SDK files: https://github.com/schlangster/skyui/wiki/
A config menu is controlled by a Papyrus script. If you want a global script that's not attached to any particular actor or object in the game world, the common method is to use a quest script. Such a quest script has to be attached to an actual quest that has the sole purpose of binding the script. It doesn't do anything quests would normally do.
All config menus have to extend SKI_ConfigBase, which is a script provided by us.
Consequently, to create a new config menu for your mod, you have to
-
create a new script that will control your config menu. For now, it doesn't have to be more than this:
scriptname MyConfigMenu extends SKI_ConfigBase
-
create a new quest and attach your script to it.
The first thing you'll want to customize for you new config menu is the name that will be displayed in the control panel. You do this by setting the ModName
property of your attached script in the Creation Kit property editor.
You might also notice the Pages
property while doing that - it can be ignored for now.
There is still one, very important, thing left to do. Each config menu has to run some maintenance code when the game is reloaded. This has to be set up manually:
- In the quest you created, select the Quest Aliases tab and add a new reference alias. Name it
PlayerAlias
. - For Fill Type, select the player reference (Specific Reference, Cell
any
, RefPlayerRef
). - In the Scripts list, add
SKI_PlayerLoadGameAlias
.
This screenshot shows what the reload alias setup it looks like in the end.
That's all it takes to make your menu show up in the control panel. It doesn't have any content and just shows an empty panel, but we are going to change that in the following sections.
As you may recall, the config menu script you created extends SKI_ConfigBase
. SKI_ConfigBase
provides several events that are executed when the user interacts with the config menu.
You customize your config menu by implementing these events, pretty much like you would implement OnUpdate
or OnInit
for other scripts.
For each of those events, you're expected to do certain things. An event that's generated when the user clicked a check box option, for example, expects you to react to that input.
The first event you should implement is
event OnPageReset(string page)
{Called when a new page is selected, including the initial empty page}
endEvent
The config panel keeps very little internal state. It doesn't remember all the options for all the menus it manages. Instead, whenever the active config menu changes, or even when the page changes, the current content is completely cleared and forgotten. The config panel then calls OnPageReset
on the active config menu, and in this event you are supposed to add content to the option list - or in other words: to fill the current page.
The event is called OnPageReset
because each config menu supports up to 128 pages you can use to structure your options. You don't have to use multiple pages though - in this first example, we won't do it either. The initial page when selecting a mod in the config panel is the default page "".
You may add up to 128 option entries per page. Each entry is indexed by its position from 0 to 127. The list that holds these options shows two entries per row. That makes a grid with 2 columns and 64 rows. First row holds options 0 and 1, second row 2 and 3, and so on. Last row holds options 126 and 127.
To insert an option at a certain position, you can move the option cursor with SetCursorPosition
, then call the function that adds the option.
For example
SetCursorPosition(3)
AddToggleOption("Hello world?", true)
adds a check box on the right half of the second row.
Adding an option automatically forwards the cursor to the next entry. Which entry that is depends on the fill mode. There are two supported fill modes: LEFT_TO_RIGHT
and TOP_TO_BOTTOM
. You set them with SetCursorFillMode
.
``` SetCursorFillMode(LEFT_TO_RIGHT) AddToggleOption("A", true) AddToggleOption("B", true) AddToggleOption("C", true) AddToggleOption("D", true) ``` Result: ``` A | B C | D ``` |
```
SetCursorFillMode(TOP_TO_BOTTOM) AddToggleOption("A", true) AddToggleOption("B", true) AddToggleOption("C", true) AddToggleOption("D", true)
A | B | C | D |
; Toggle states bool aVal = false; bool bVal = false; bool cVal = false; bool dVal = false event OnPageReset(string page) SetCursorFillMode(TOP_TO_BOTTOM) SetCursorPosition(0) ; Can be removed because it starts at 0 anyway
endEvent
event OnOptionSelect(int option) {Called when a non-interactive option has been selected} endEvent
; OIDs int aOID int bOID ; Toggle states bool aVal = false; bool bVal = true; event OnPageReset(string page) AddHeaderOption("Group 1") aOID = AddToggleOption("A", aVal) bOID = AddToggleOption("B", bVal) endEvent event OnOptionSelect(int option) if (option == aOID) ; ... handle A select elseIf (option == bOID) ; ... handle B select endIf endEvent
event OnOptionSelect(int option) if (option == aOID) aVal = !aVal SetToggleOptionValue(aOID, aVal) elseIf (option == bOID) bVal = !bVal SetToggleOptionValue(bOID, bVal) endIf endEvent
event OnOptionDefault(int option) if (option == aOID) aVal = false ; default value SetToggleOptionValue(aOID, aVal) elseIf (option == bOID) bVal = true ; default value SetToggleOptionValue(bOID, bVal) endIf endEvent
event OnOptionHighlight(int option) if (option == aOID) SetInfoText("This is option A. It serves absolutely no purpose.\nDefault: false") elseIf (option == bOID) SetInfoText("This is option B.\nOption B this is.\nDefault: true") endIf endEvent
|