About this mod
This tutorial will show you how to add LINK++ support to your mod as well as provide ready mod template that can be used as a starter for your new mods.
- Requirements
- Permissions and credits
- Changelogs
- 00_CORE - contains a mod template that implements LINK++ support, as well as a dummy legacy mod for which it is shown how to add LINK++ support externally.
- 01_MENU_INTEGRATION_DEMO - contains an example of using LINK++ API to integrate LINK++ menus into other mods.
- It creates a list of all loaded mods.
- Takes each mod's filename without extension and looks up a .cfg for that mod by following paths: "Data\<modName>.cfg", "Data\ini\<modName>.cfg" and "Data\cfg\<modName>.cfg".
- If .cfg file is present in any of those places, the mod is considered configurable, otherwise not.
- When you click on mod's entry in mods list menu, if it's configurable, LINK will read its .cfg file and build config menu from it.
- When you press "Defaults" button in mod's config menu, it'll search for mod's ini file by following paths: "Data\<modName>.ini" and "Data\ini\<modName>.ini". If it finds one, it runs it to set default values. Otherwise, it silently ignores it.
- Instead of just mods, LINK++ operates with an abstraction that I called just "module". A module is a logically isolated part of mod. Most mods will contain just one module, but some can contain multiple. This allows to define multiple configuration menus for single mod. Also, mods with LINK++ support can be merged together and the resulted mod will contain all of their modules, and thus each of merged mods would still be configurable with LINK++.
- Before scanning all loaded mods, it dispatches "LppModInfoRequest" event. All mods that are supposed to add LINK++ configuration menus must handle this event and dispatch an "LppModInfoResponse" event in response. Data send in this event must contain a list of modules configurations. Those configurations includes such data as: module's display name, path to .cfg file, paths to .ini files, path to custom icon, etc. You can find a full list in Mod Template's code. Some of them are optional.
- When it processes a list of loaded mods, it checks if anyone send a LINK++ configurations for each mod. If that's the case, it'll use that configurations and create entries for each defined module, otherwise it'll convert this mod to a single module and process it the same way as original LINK.
- When clicking on module's entry in modules list menu, if it's a LINK++ module, the .cfg defined in "LppModInfoResponse" will be used, otherwise, will be searched in the same locations where LINK search for them.
- When pressing "Defaults" button, it will run all .ini files specified for module in "LppModInfoResponse" (or .ini file from the paths where original LINK searches for them, if it's original LINK's module). If none was specified, this button will not even appear.
So in short, in order to add LINK++ support for you mod, you must:
- Define .cfg file and optionally any amount of .ini files for each of your modules.
- In your mod's initialization code check if LINK++ installed, if that's the case, read your ini files only once on first initialization and set up "LppModInfoResponse" event handler. Otherwise, handle configurations manually as you would do it without LINK/LINK++.
- In "LppModInfoResponse" handler respond with all necessary info about your mod.
All of this already implemented in ModTemplate.esp, you can just use it as a starter. More on that below.
Toggles.
Toggle defines a setting that can take a fixed set of numeric values (basically a enum). This is how it looks in menu:

When pressing on option's value, it'll cycle to next available value. Here is a code example of how to define the above toggle in .cfg file:
Sliders.
Slider defines a setting that can take a specified range of numeric values. This is how it looks in menu:

You can move slider to change that setting. Here is a code example of how to define the above slider in .cfg file:
Key binding.
Key binding defines a setting that can take any DirectX key's code value. This is how it looks in menu:

The mapped name of currently selected key is displayed on button. After pressing that button you can press any key to select a new value for that setting. Here is a code example of how to define the above key setting in .cfg file:
Action button.
Unlike many other settings, action buttons doesn't have any underlying variables. Instead, they can invoke any specified script upon pressing. You can look into ModTemplate's code to understand how you can define handlers for action buttons. This is how it looks in menu:

Keep in mind that action button settings are currently not exportable, so it's better not to use them for making changes that will be expected by user to be exportable. I may change that in later updates. Here is a code example of how to define the above action button in .cfg file:
Category.
Unlike many other settings, categories doesn't have any underlying variables. In fact they don't configure anything at all, they are just used to group multiple settings together. This is how it looks in menu:

Upon clicking on category, all settings below it until the end or next category will be hidden/revealed. If category is hidden by condition, all settings below it until the end or next category will be hidden as well. Here is a code example of how to define the above category in .cfg file:
Empty line.
This is actually an original LINK's undocumented setting type. It just adds empty line, can be useful to visually separate parts of configurations within category. This is how it looks in menu:

Here is a code example of how to define the empty line in .cfg file:
Conditions.
For any setting type it's possible to specify condition. If that condition returns 0, this setting will not be displayed in menu. Here is an example how to do it:
set LINK.cfg_setting_condition to sv_Construct "${ModTemplate.demo_slider} >= 50"
The visibility of settings will change in real time:
IMPORTANT NOTE: It's highly recommended NOT to place your .cfg files under locations where original LINK searches for them. The recommended location for .cfg files is "Data\ini\LPP\*.cfg". If you want your mod to use some of LINK++ features and yet work with both LINK and LINK++, you can do it, but it's recommended that .cfg file will be installed in different places depending on whether LINK or LINK++ is used. You can make a BAIN wizard that will check, for example, if "Data\LINK.cfg" is present (a file that is specific for original LINK), and if so, install module with LINK's .cfg file, otherwise with LINK++'s .cfg file.
scn OnLppModInfoRequest
Array_var args
Array_var response ; StringMap used as data structure that contains response to LINK++ request.
Array_var config ; Temporary StringMap that is used for building response. Response can contain any non-zero amount of configs.
begin function { args }
Let response := ar_Construct StringMap
Let response->configs := ar_Construct Array
Let config := ar_Construct StringMap
Let config->cfgPath := "Data\ini\LPP\<cfg_file_name>.cfg"
ar_Append response->configs config
; Send response to LINK++.
DispatchEvent "LppModInfoResponse" response
end
A more advanced example with full list of fields can be found in ModTemplate's code.
"MyQuest.MyVar == 42"
in your condition, you'll write
"${MyQuest.MyVar} == 42"
This will replace ${MyQuest.MyVar} with currently configured value in this menu. For action button arguments you'll also need to explicitely specify expression type, e.g.:
"${MyQuest.MyFloatVar}:Num,Sample text:Str"
You can find more info on that in ModTemplate's code.
Module's list menu integration (filtered):

Module's config menu integration:

You can also see how Slowpard used it in his "Progress tracker" mod:

Set LINK.logLevel To 0
Set LINK.suppressBatchScriptsOutput To 0
If batch scripts output is suppressed, all logs on all levels in scripts triggered by batch scripts (like reading .cfg files for example) will not appear.