Menus in The Witcher 3

Menus in The Witcher 3: Wild Hunt are contained in the  folder as XML files  and you can access the values in menus using the CInGameConfigWrapper class. Each file in this directory is loaded into the game at load. Any duplicate group id's will overwrite any previously loaded group with the same id, and the files are loaded alphabetically.

file
The  file, found in the   folder, stores all user selected values for variables set in menus. The standard  format is in use here and the basic structure looks something like:

[GROUPID] VAR1=true VAR2=2.1

Basic XML Structure
The basic structure of a menu XML file is as follows:

        

Group
Each group represents a menu page and menu hierarchy is set by the dot/period character (".") in the displayName of that Group.



This will result in an entry of " " in the main menu, with a submenu " " and that submenu having a nested submenu " ".

VisibleVars
The heart and soul of the menu, the VisibleVars section contains entries for each variable that the menu configures. Each variable has an id (string based) and a displayType, which is the type of widget that is used to configure that variable.

   

TOGGLE
This widget has two values,, displayed as "Enabled", and  , displayed as "Disabled", and clicking the widget toggles between the values. This obviously translates well to the bool type. When the menu option isn't set, the value defaults to. This displayType has no options.

SLIDER
This widget is a numerical slider from a starting value to an ending value, with a certain amount of steps in between. The syntax for specifying is below:



If the value isn't set, the value defaults to 0 (even if the starting value is higher than this, causing an odd display!). The number of steps is the number of selectable values from the starting value to the ending value, including both ends, which can cause some odd rounding: make sure to think about the number of steps carefully. If you'd like to select a whole number (integer) from 0 to 10, there should be 11 steps.

OPTIONS
This widget is different from the others because it contains an entries array much like a preset array, which can change many different variables. It is displayed much like a slider, except the first and last entries are displayed on the ends of the slider, and the selected option is displayed as the value.

   <Entry varId="INTERNALVAR1" value="0"/> <Entry varId="INTERNALVAR2" value="0"/> </Option> <Option id="1" displayName="SECOND_OPTION_NAME"> <Entry varId="INTERNALVAR1" value="1"/> <Entry varId="INTERNALVAR2" value="3"/> </Option> </OptionsArray> </Var>

PresetsArray
The PresetArray section of a menu contains preset values for variables defined in the VisibleVars section. They do not need to contain an entry for each variable and activating each preset will set the values of each variable with an entry in the Preset. Presets have a id number that is assigned, normally starting from 0 (and going to 1, 2, 3... and so on) and each Entry in the Preset contains a variable key-value pair.

<Preset id="ID_NUM" displayName="PRESET_NAME"> <Entry varId="VAR1" value="true"/> <Entry varId="VAR2" value="1.0"/> </Preset>

tags
Tags can be accessed from scripts and read by them using CInGameConfigWrapper methods: import final function DoVarHasTag( groupName : name, varName : name, tag : name ) : bool; Returns true if var(option) has tag passed to it as third argument, false if it doesn't. import final function DoGroupHasTag( groupName : name, tag : name ) : bool; Similar, but with whole options group instead of option.

To add custom tag, simply add it in xml (separate tags with ; ) : tags="myTag;myTag1"> Then use mentioned methods to read them in scripts them.

Tags used in vanilla game:
 * customNames
 * customDisplayName
 * nonLocalized
 * refreshEngine
 * graphics
 * buffered

visibilityCondition
With visibilityCondition, option will be displayed only if script tag pointed there is activated.

To add custom visibilityCondition, you need to first add it in xml: visibilityCondition="myCondition"> Then you have to activate tag "myCondition" in scripts by using CInGameConfigWrapper methods: import final function ActivateScriptTag( tag : name ); Activates tag (condition). import final function DeactivateScriptTag( tag : name ) Deactivates tag (condition). import final function IsTagActive( tag : name ) : bool; Returns true if tag passed as argument is active, false if it's not.

When you're using visibilityCondition linked to another option that will activate it when user is in menu, you have to reopen menu when it's activated by using CR4IngameMenu method: public function ReopenMenu

Conditions used in vanilla game:
 * mainMenu - option is displayed only in main game menu (where you can start new game or load a save)
 * hideAlways - options is always hidden (actually it could be anything that is never activated, "blablabla", "asdsdwds", "e=mc^2". but for clarity it's best to use hideAlways like Reds)

overrideGroup
Normally, variables are stored in the  file under their group, you can override this and store the variable under another group instead. Especially useful for submenus in a mod, since you normally want all of a mods settings to be together, despite being in different groups.

Properly Localizing
For a menu to show up with proper looking text (in Witcher 1.12) without the [http://www.nexusmods.com/witcher3/mods/897/? Custom Localization Fix] mod installed, it must be localized using w3strings files.

Prefixes
The w3strings file must use keys with the proper prefixes in order to show up in menus. It is also recommended to use a prefix for your mod as well, to avoid key collision.


 * Group names, including empty parent menus, must be prefixed with "panel_"
 * The Mods submenu has the localization key "panel_Mods"
 * Example: panel_et_name


 * If a menu has presets, it must have the localization entry "preset_PARENT_GROUPNAME"
 * Example: preset_Mods_et_name


 * Each Preset's name must be prefixed with "preset_value_"
 * Example: preset_value_et_default_kb


 * Variable names must be prefixed with "option_"
 * Example: option_et_camera_weight