Skip to content

MCM Advanced Features

schlangster edited this page Dec 27, 2012 · 62 revisions

People interested in advanced features shouldn't need guides. Just look at the API Reference and put 2+2 together!

(Translation: This guide will be finished later.)

Topics to cover:

  • Pages
  • Option flags
  • Grouped updating (may not be necessary anymore after next skse update because UI functions should execute instantly)
  • Keymap conflict management
  • Script versioning
  • Loading/unloading custom content
  • Localization

Script Initialization

OnConfigInit

Normally, when working with arrays or other variables that cannot be initialized immediately at the point of declaration, you have to put initialization code in OnInit.

For your config menu, however, you should rather use the OnConfigInit event we provide and avoid OnInit. If that's not possible and you have to use OnInit for some reason, make sure to call parent.OnInit() the original event handler of SKI_ConfigBase is not skipped.

string[] myArray

; WRONG
event OnInit()
	myArray= new myArray[5]
	; ...
endEvent

; Meh... avoid using OnInit()
event OnInit()
	parent.OnInit()
	myArray= new myArray[5]
	; ...
endEvent

; OK
event OnConfigInit()
	myArray= new myArray[5]
	; ...
endEvent

The reason you should avoid OnInit() is that config menus should be designed so that MCM (which is part of SkyUI) can be removed at any time and added again later. If that happens, the variables of your script will be reset, BUT OnInit() won't be called again. This may leave some of variables uninitialized. OnConfigInit() doesn't have this problem.

OnGameReload

There are certain settings you might want to apply after each game reload, for example scripted INI changes or other modifications that persist in memory.

To do this, extend OnGameReload of SKI_ConfigBase. Be aware that this is not part of the base API, so you'll be responsible for calling parent.OnGameReload(). Not doing that will break the menu.

event OnGameReload()
	parent.OnGameReload() ; Don't forget to call the parent!
	
	Utility.SetINIFloat("someSetting", someValue)
endEvent

Pages

If your config menu contains a lot of options, you might want to consider diving them into several categories. Or, even if all options would fit into a single page, it might still be a good idea to separate general from advanced settings.

For this purpose config menus support multiple pages. Setting them up is straightforward.

Page names

Define your page names by setting the Pages property of your config menu. You can either do that in the Creation Kit property editor, just like you set ModName, or you can do it in the OnConfigInit event of your script. An example for the scripted variant:

event OnConfigInit()
	Pages = new string[2]
	Pages[0] = "Page 1"
	Pages[1] = "Page 2"
endEvent

Now, whenever your config menu is active, the list of pages will be shown below the mod name.

Page content

In general, setting up options for multiple pages works exactly the same as using a single page; you add them in OnPageReset. The only difference is that you check the page parameter when deciding which options to add:

event OnPageReset(string page)
	if (page == "Page 1")
		; Add page 1 options
	elseIf (page == "Page 2")
		; Add page 2 options
	endIf
endEvent

By default, after selecting your mod from the main list, no page is active and the page parameter is is "". Since we didn't handle this case in our example, the option list will be blank until the user has chosen a page. On way to fill that void is adding a custom logo of your mod . For further details on this topic, see Loading custom content.

Pages and other events

Since each option ID is unique, for all the other events it won't matter which option a page is on. However, if you have a lot of options, you may want to spread your code over several functions.

In this scenario, it's helpful being able to check the current page even outside of OnPageReset; you do that by accessing the CurrentPage property:

event OnOptionSelect(int option)
	if (CurrentPage == "Gameplay")
		HandleGameplayOptionSelect(option)
	elseIf (CurrentPage == "Immersion")
		HandleImmersionOptionSelect(option)
	endIf
endIf

Be aware that this makes moving around options between several pages, or renaming pages, slightly more tedious, since you have to change more than just OnPageReset when doing so.

Options

While the basics about options have already been covered, there are two more topics that have been left out so far: Option flags and grouped updating.

Option flags

All Add*Option functions have an optional flags parameter. It can be used to enable certain behavior for the option. Accepted flags are

  • OPTION_FLAG_NONE, to clear the flags;
  • OPTION_FLAG_DISABLED, to grey out and disable the option.
int flags
if (isMyOptionDisabled)
	flags = OPTION_FLAG_DISABLED
else
	flags = OPTION_FLAG_NONE
endIf
AddToggleOption("Toggle this", myToggleValue, flags)

To change the flags later in combination with Set*OptionValue functions, use SetOptionFlags(optionID, flags).

Grouped updating

If you use the Set*OptionValue functions to change several options at once, Papyrus may slightly delay function execution at any point in the script. This results in asynchronous updating of the option display.

To prevent this, Set*OptionValue and SetOptionFlags support an optional noUpdate parameter. Example:

SetTextOptionValue(oid1, "Value1", true) ; Don't redraw the list yet
SetTextOptionValue(oid2, "Value2", true) ; ...
SetTextOptionValue(oid3, "Value3", true) ; ...
SetTextOptionValue(oid4, "Value4")       ; Refresh now

Custom Content

It's possible to load content from an external file into the option list area. The original option list will be hidden when that happens.

Supported source file formats are SWF for Flash movies and DDS for images. PNG and some other image formats may work as well, but they will most likely be imported in bad quality.

event OnPageReset(string a_page)

	; Load custom .swf for animated logo that's displayed when no page is selected yet.
	if (a_page == "")
		LoadCustomContent("skyui/skyui_splash.swf") ; Path relative to Data/Interface
		return
	else
		UnloadCustomContent()
	endIf

	; ... rest of the OnPageReset

Localization

To support for multiple languages in your config menu, you can utilize the UI localization capabilities provided by SKSE. This section will explain how this works exactly.

The game itself stores the translated strings in Data/Interface/Translate_LANGUAGE.txt, where LANGUAGE is replaced by the current game language, i.e. Translate_ENGLISH.txt. This file contains lines of key/value pairs for the translated strings, separated by a tab stop. Each key has to start with the $ sign. Example:

...
$Back	Back
$Backstabs	Backstabs
$Barters	Barters
...

Whenever the contents of a textfield in UI match a key in this file, it's replaced with the translated value. Even without SKSE, you could add your own translations by modifying the original translates file. The problem is that this would lead to conflicts if multiple mods want to extend this file.

That's why SKSE adds support to load translations from additional files. For each active mod in the load order, Data/Interface/Translations/modname_LANGUAGE.txt is checked for translations, where modname is replaced by the name of the mod data file (i.e. SkyUI_ENGLISH.txt, if the data file is SkyUI.esp). The translation format is the same as described above.

Be aware, however, that these translations only work, if the textfield contents match the translation key exactly. Given

$Hello	Hello

"$Hello" results in "Hello", but "$Hello World" stays "$Hello World".

Loading translation files from inside BSA file is possible. Languages the game supports are CZECH, ENGLISH, FRENCH, GERMAN, ITALIAN, POLISH, RUSSIAN, SPANISH and JAPANESE. Only the file that matches your current language is loaded; there is no fallback to ENGLISH as default.

Example

If your mod is named MyMod.esp, localize your page names by adding

$General	General
$Advanced	Advanced
$Help	Help

to Data/Interface/Translations/MyMod_ENGLISH.txt,

$General	Allgemein
$Advanced	Fortgeschritten
$Help	Hilfe

to Data/Interface/Translations/MyMod_GERMAN.txt etc.

Then, in the config menu, name your pages accordingly:

Pages[0] = "$General"
Pages[1] = "$Advanced"
Pages[2] = "$Help"
Clone this wiki locally