File information

Last updated

Original upload

Created by


Uploaded by


Virus scan

Safe to use

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.

Permissions and credits
This was originally intended as a tutorial on how to add LINK++ support for your mod, but ended up being something bigger. I believe that a small piece of code is worth a thousand words, so most of the tutorial is implemented as a mod. This page only touches the surface. This mod, in addition to being an example of how to add LINK++ support, is also a template that can be used as a starter for new mods (and which I plan to use as the basis for my future mods). This template provides a solid architectural basis for easily adding new features and modifying existing ones. The code is very carefully commented. I also do not consider myself an absolute pro in Oblivion scripting and do not claim that all the solutions presented in the code are the most optimal and the only correct. If you find a bug, or think that some part is implemented incorrectly/not optimally, feel free to share your thoughts. 

BAIN archive contains 2 modules: 
  •  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.

General description
As stated in LINK++ mod page, it's fully compatible with mods with original LINK support. Thus, if you don't want to use any LINK++ specific features, you can refer to LINK's documentation and add support for LINK++ in your mod the same way as you would do it for regular LINK. If you want to use LINK++ features however, you'll need to understand some basics of how it works first. Before that, I'll shortly explain how original LINK works for those who don't know:

  1. It creates a list of all loaded mods.
  2. 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".
  3. If .cfg file is present in any of those places, the mod is considered configurable, otherwise not.
  4. 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.
  5. 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.
LINK++ does all the same steps, but with some additions:

  1. 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++. 
  2. 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.
  3. 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.
  4. 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.
  5. 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:

  1. Define .cfg file and optionally any amount of .ini files for each of your modules.
  2. 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++.
  3. 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.

CFG files
As mentioned above, both LINK and LINK++ use .cfg files to describe configurations. Each entry in .cfg file defines a specific module's menu setting. In addition to LINK's 5 setting types (toggles, sliders, keys, categories and undocumented empty lines) LINK++ adds a new one: action button.

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:

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:

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:

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.

Handling configurations request
As it was mentioned above, your mod must set handler that will receive LINK++ modules configuration request and respond to it. Here is an example of the most basic handler implementation:

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

A more advanced example with full list of fields can be found in ModTemplate's code.

LINK++ expression language
LINK++ expression language is a small scripting language extension that is used for specifying settings conditions and action buttons handler arguments. Before explaining it I'll explain the problem it solves first. The thing is, LINK++ only applies configurations after pressing "Return" button in module's configuration menu. Until that, all setting's underlying variable values stays intact. But when you are specifying conditions for settings or arguments for action button's handler, you often want to use values that user configured in menu. For that purpose, LINK++ expression language is used. It's a bit different for conditions and arguments, but the idea is that if you want to use some variable "MyQuest.MyVar" that is configurable in current menu, instead of, e.g. writing

"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.

Integrating LINK++ menus into your mod
LINK++ provides simple API for showing its menus programmatically. This can be useful, for example, for integrating your mod's options button directly into your mod's UI. There is an example of how you can do this in archive (LppMenuIntegrationDemo.esp). It demonstrates this feature with simple message box menus, but you can of course use it with generic menus as well.

Module's list menu integration (filtered):

Module's config menu integration:

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

LINK++ adds many validations on all kinds of input data, so if you'll misuse something, you'll likely receive a notification about that. In order to get more info on what went wrong, you can also set LINK++ log level to debug or trace and enable batch scripts output either in LINK++ configuration menu, or in .ini file:

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.