INTRODUCTION

Skyrim Unbound Reborn includes an extension framework that allows users to extend the available options by creating addons. In most cases creating an addon requires using xEdit or Creation Kit (xEdit is enough for most things) and some basic mod editing/creation skills.

PapyrusUtil is required for addons support.

Addons are only read once, when you start a new game from the main menu. Therefore, if you added, removed, updated or edited an addon, you need to start a new game to see the changes.

What options can be added by addons:

  • Armors
  • Weapons - to existing or new weapon types and weapon sets
  • Staffs (in SUR they work different from other weapons)
  • Spells
  • Items - as new options
  • Locations - to existing or new location types and holds. Optionally, you can run custom scripts, which allows you to do anything you want in addition to teleporting the player to the starting location: add items, enable/disable objects, complete/advance/start quests, add the player to a faction, etc.

What addons consist of:
  • The main part of a SUR addon is a .json file (or multiple files) located in Data\SKSE\Plugins\SkyrimUnbound\Addons folder. It defines all the new options that your addon adds.
  • Depending on what your addon does, existing records may be enough for you and then your addon will only consist of a .json file. But often you will need to create new records, then your addon will also contain a .esp plugin with these records.
  • Location addons with scripts also require you to write a papyrus script (simple location addons don't require any scripts).

Feel free to ask me for help or suggest new features (i don't promise to add everything people ask, though)


GENERAL INFO ABOUT JSONS

  • The addon JSONs are in Data\SKSE\Plugins\SkyrimUnbound\Addons folder.
  • JSON names don't matter and only affect the order in which they are loaded.
  • The JSONs are only read once, when the player starts a new game from the main menu, so you need to start a new game if you added, removed, updated or edited an addon to see any changes.
  • Option names and descriptions allow both normal strings ("Musical Instrument") and SkyUI translation strings ("$SU_MusicalInstrument", strings that get replaced with normal strings specified in files Data\interface\translations\<plugin name>_<language>.txt that you can create for your plugin). The latter are of course better since it simplifies translations of your addon.
  • Due to Papyrus and PapyrusUtil limitations, all names of json properties are case-sensitive, but at the same time SUR can't automatically find incorrect case errors for values that are considered optional, so be careful.
  • Property name "__comment" is reserved for comments in json.


If you don't know what JSON is

.json is a file format widely used for transmitting data. It can be edited using any text editor, even the Windows Notepad. It's designed to be human-readable and the syntax is very simple. You can google it but it may be enough to just look at some addon examples.


Referencing forms (records) in JSON

Game forms (records) are referenced using a string in a special format. For example:
"fWeapon" : "0x801|Dawnguard.esm"
  • Dawnguard.esm - the name (with the extension) of the plugin that the record is defined in.
  • 801 - the FormID of the record without the plugin index (the plugin index is the first 2 digits of a FormID, or the first 5 digits (FExxx) if the plugin is .esl or ESL-flagged). The leading zeros can be removed like in this example. I.e. in this example the full FormID was 04000801 but we only need 000801 = 801.
  • 0x - a mandatory prefix (technically it just says that the number 801 is hexadecimal and not decimal).

Alternatively you can specify FormID as a decimal number without the "0x" prefix. To convert FormIDs to this format you can use an online converter, which there are plenty of. For example the example above in the decimal format will look like:
"fWeapon" : "2049|Dawnguard.esm"
This is the only format supported on LE (since the LE version of PapyrusUtil is outdated).


Structure

Addon JSONs can contain the following blocks (objects and arrays of objects):
  • armors
  • weaponTypes
  • weaponSets
  • weapons
  • staffs
  • spells
  • items
  • locationTypes
  • holds
  • locations
  • requirements
All of them are optional, most likely your addon will only have one or a few. Below they are described in groups. The order of these blocks and elements inside them doesn't matter.


SECTIONS

Addon Requirements
Spoiler:  
Show
The "requirements" block allows you to add requirements to your JSON: plugins required for the JSON to be loaded, as well as incompatible plugins that will prevent the JSON from loading.
Even though it's not required, it's recommended to specify requirements of your JSON, since they prevent a bug that old versions of PapyrusUtil (for LE or SE v1.5.97 i.e. pre-AE) have - if the plugin that a record (form) is from is missing or disabled, it will try to pick the record with the same FormID from Skyrim.esm. So specifying requirements prevents this bug if the user disabled your addon .esp or the .esp of the mod that the addon is for, or doesn't even have that mod installed.
It's also useful if you want to distribute multiple addons that don't have their own plugins in a single archive, so that the user installs one package and only the addons for the mods user has installed will be loaded.

You don't need to specify vanilla plugins or Skyrim Unbound.esp there. You don't need to specify all of the required plugins, just the ones that are enough to check if they're loaded in the game or not. For example if your addon comes with a .esp Skyrim Unbound Addon - Cool Sexy Armor.esp that has Cool Sexy Armor.esp and Skyrim.esm as masters, it's enough to only specify Skyrim Unbound Addon - Cool Sexy Armor.esp in requirements, since if any of its masters aren't loaded it won't be loaded too.

Examples

Spoiler:  
Show
1:
{
    "requirements" :
    {
        "required" : [ "MysticismMagic.esp" ]
    },
    ...
}

2:
{
    "requirements" :
    {
        "required" : [ "Apocalypse - Magic of Skyrim.esp" ],
        "requiredAny" : [ "Apocalypse Adjustments - Vanilla.esp", "Apocalypse Adjustments - Ordinator.esp", "Apocalypse Adjustments - Adamant.esp" ]
    },
    ...
}
("required" is actually redundant here, since the plugins in "requiredAny" have it as a master and won't be loaded if it's disabled or missing)


Structure

"requirements" : object (optional)
Contains arrays:

"required" : string array (optional)
An array of plugin names (with extensions). If any of the listed plugins is not found, the JSON won't be loaded.

"requiredAny" : string array (optional)
An array of plugin names (with extensions). If none of the listed plugins is found, the JSON won't be loaded.

"incompatible" : string array (optional)
An array of plugin names (with extensions). If any of the listed plugins is found, the JSON won't be loaded.

Armors
Spoiler:  
Show
Addons can add new armor sets as separate MCM options. If you want to add variants to the default options or change them in any other way, you need not to create an addon but to edit SUR leveled lists. New armors are not included in the randomization, unless you set AddToRandom flag.

Examples

Spoiler:  
Show
A complete JSON that should work:
1. Whiterun Guard Armor. Not added to randomization, doesn't have any flags.
2. Forsworn Armor. Not added to randomization. Has flags to disable hoods and shields because forsworn don't use them.
3. Chef Clothes. Added to randomization.

{
    "armors" :
    [
        {
            "fArmorSet" : "0xD33C6|Skyrim.esm",
            "sName" : "Whiterun Guard Armor",
            "sType" : "Light"
        },
        {
            "fArmorSet" : "0x43BD9|Skyrim.esm",
            "sName" : "Forsworn Armor",
            "sType" : "Light",
            "sFlags" : [ "NoRandomHood", "NoRandomShield" ]
        },
        {
            "fArmorSet" : "0x1BC5E|Skyrim.esm",
            "sName" : "Chef Clothes",
            "sType" : "Clothes",
            "sFlags" : [ "AddToRandom" ]
        }
    ]
}


Structure

"armors" : array of objects (optional)
New armor set options. One element of this array is one armor option. Properties:

"fArmorSet" : form (required)
LeveledItem (leveled list) or Outfit record that contains the armor set. If a leveled list or outfit containing all the armor parts you need already exists, you can just use it. If it doesn't, you can create a new one in xEdit or CK. Yes, you can't just list armor parts in JSON.
There are no limitations on the structure and contents of the armor set list. The only rules:
  • If you want to define a default headgear, include it in the armor set. Depending on the Headgear selected by the player, it will be untouched, automatically removed or replaced with a hood.
  • If you want to specify a shield that will be given with your armor set, include it in the armor set. If the shield is not included, the defaullt one will be given (hide shield for clothes and light armor, iron shield for heavy armor). The shield will be automatically removed when it's not needed.
In fact you can add any additional items that need to be given with this armor set into it.

"sName" : string (required)
The name of your armor set that will be displayed in the MCM.

"sType" : string (required)
The armor type of this armor. Valid values: "Clothes", "Light", "Heavy".

"fAutoWeaponSet" : form (optional)
Using this property you can specify a weapon set that will be always given with your armor set if Weapon Set is Random in the MCM. Usually this is a weapon set from your addon. The form is the same FormList that you specify in fWeaponSet when adding new weapon sets.

"sFlags" : string array (optional)
Flags here are strings in the array. To add a flag, add it to the array (at any position). Flags:
  • "AddToRandom" - Includes your armor set in the randomization (new armors are not included in the randomization without this flag).
  • "EntryLevelArmor" - Includes your armor set in the randomization if Only Entry-Level Armors is enabled in the MCM. Does nothing for clothes, since SUR considers any clothes entry-level armor. Requires AddToRandom flag.
  • "NoRandomHood" - Prevents getting a college hood with your armor set when Headgear is set to Random.
  • "NoRandomShield" - Prevents getting a shield with your armor set when all weapons or One-Handed Combination are random. This is not needed for clothes, since shields are not randomly given with clothes.
  • "MageOutfit" - Tells SUR that your armor set is mage robes or mage armor. It affects randomization.
  • "VampireOutfit" - Only useful with AddToRandom. This flag excludes this armor from the randomization if the player is not a vampire.

Weapons
Spoiler:  
Show
Addons can add new weapons (including arrows and bolts) to existing or new types and weapon sets. New weapon types can be added to randomization. New weapon sets currently can't be, only by using "fAutoWeaponSet" on new armors that are added to randomization. Adding staffs is described in a different section "Staffs and Spells".

Examples

Spoiler:  
Show
1. The Vanilla Crossbow addon ("Crossbow" and "Bolts" are default weapon types):

{
    "weapons" :
    [
        {
            "fWeapon" : "0x801|Dawnguard.esm",
            "sType" : "Crossbow",
            "sWeaponSet" : "Steel"
        },
        {
            "fWeapon" : "0xBB3|Dawnguard.esm",
            "sType" : "Bolt",
            "sWeaponSet" : "Steel"
        }
    ]
}

2. The CC Fishing addon:

{
    "weapons" :
    [
        {
            "fWeapon" : "0xE44|ccBGSSSE001-Fish.esm",
            "sType" : "Dagger",
            "sWeaponSet" : "Ancient Nord"
        },
        {
            "fWeapon" : "0xE46|ccBGSSSE001-Fish.esm",
            "sType" : "Mace",
            "sWeaponSet" : "Ancient Nord"
        },
        {
            "fWeapon" : "0xE47|ccBGSSSE001-Fish.esm",
            "sType" : "Warhammer",
            "sWeaponSet" : "Ancient Nord"
        }
    ]
}

3. See the Beyond Skyrim Morrowind - Bonemold Weapon Pack addon

4. Weapon type flags look like this (see Armors examples if you want to see how it looks in a JSON):

"sFlags" : [ "AddToRandom" ]


Structure

"weaponTypes" : array of objects (optional)
New weapon type options. One element of this array is one weapon type option. Properties:

"fWeaponTypeList" : form (required)
The FormList record of your weapon type. You can use an empty formlist and make SUR fill it by setting this formlist in the "fWeaponTypeList" property on each of the new weapons that belong to this type, or just pre-fill the formlist, i.e. add all the weapons into it yourself.

"sName" : string (required)
The name of your weapon type option.

"sCategory" : string (required)
The category of your weapon type. Valid values: "1H", "2H", "Ranged".

"sFlags" : string array (optional)
Flags here are strings in the array. To add a flag, add it to the array (at any position). Flags:
  • "AddToRandom" - Includes your weapon type in the randomization (new weapon types are not included in the randomization without this flag). Not supported for ranged weapons.


"weaponSets" : array of objects (optional)
New weapon set options. One element of this array is one weapon set option. Paramaters:

"fWeaponSet" : form (required)
The FormList record of your weapon set. You can use an empty formlist and make SUR fill it by setting this formlist in the "fWeaponSet" property on each of the new weapons that belong to this set, or just pre-fill the formlist, i.e. add all the weapons into it yourself.

"sName" : string (required)
The name of your weapon set option.

"fAutoWeaponSetOn" : array of forms (optional)
Array of armor sets (fArmorSet) that this weapon set will be set as auto weapon set on. Has the same effect as fAutoWeaponSet property on armors, no need to use both.


"weapons" : array of objects (optional)
New weapons (arrows and bolts are considered weapons here). Specific weapons can't be selected from the MCM directly: the player or the randomization selects a weapon type and a weapon set, and SUR chooses a random weapon that belongs to both the type and the set. By default there is only one weapon for each weapon type+set combination, however more weapons can be added by addons. Properties:

"fWeapon" : form (required)
The weapon, usually Weapon, Ammo or LeveledItem (leveled list) record.

"sType" : string (optional)
If your weapon belongs to one of the default weapon types, this must be the name of that type. Crossbows and Bolts are default types too, they're just hidden if no addons extend them. Valid values: "Dagger", "Sword", "Axe", "Mace", "Scimitar", "Greatsword", "Battleaxe", "Warhammer", "Bow", "Crossbow", "Arrow", "Bolt".

"fWeaponTypeList" : form (optional)
If your weapon belongs to an addon-added weapon type, this is its formlist record (the one specified in the "fWeaponTypeList" property of that weapon type). This can be used to add weapons to weapon types added by other addons, as well as to the ones from your addon if you don't want to pre-fill the formlist (if you pre-fill it, you don't need to use this property).

"sWeaponSet" : string (optional)
If your weapon belongs to one of the default weapon sets, this must be the name of that weapon set. Valid values: "Iron", "Steel", "Imperial", "Orcish", "Elven", "Ancient Nord".

"fWeaponSet" : form (optional)
If your weapon belongs to an addon-added weapon set, this is its the formlist record (the one specified in the "fWeaponSet" property of that weapon set). This can be used to add weapons to weapon sets added by other addons, as well as to the ones from your addon if you don't want to pre-fill the formlist (if you pre-fill it, you don't need to use this property).

In fact a weapon doesn't have to belong to a weapon set: if there are no weapons of the selected type that belong to the selected weapon set, the iron and steel sets, a random weapon belonging to this weapon type will be picked.

Spells and staffs
Spoiler:  
Show
Addons can add new spells and staffs (it works the same for spells and staffs so i groupped them here), as well as remove them. All of the new spells and staffs are included in the randomization.

Examples

Spoiler:  
Show
See the default spell/staff addons packed with SUR. I copied the _Mysticism.json without the "requirements" block here:

{
    
    "spells" :
    {
        "remove" :
        [
            "0x211EB|Skyrim.esm"
        ],
        "alteration" :
        [
            "0x3C2436|MysticismMagic.esp",
            "0x5D175|Skyrim.esm"
        ],
        "conjuration" :
        [
            "0x1CE06|Dragonborn.esm",
            "0x413541|MysticismMagic.esp",
            "0x4DBA4|Skyrim.esm"
        ],
        "illusion" :
        [
            "0x4DEE9|Skyrim.esm"
        ],
        "restoration" :
        [
            "0x8C886D|MysticismMagic.esp",
            "0x7388D3|MysticismMagic.esp",
            "0x4D3F8|Skyrim.esm"
        ]
    },
    "staffs" :
    {
        "conjuration" :
        [
            "0xE1FB5B|MysticismMagic.esp",
            "0xBE122|Skyrim.esm"
        ],
        "illusion" :
        [
            "0xC62046|MysticismMagic.esp"
        ],
        "restoration" :
        [
            "0xD2C9FB|MysticismMagic.esp",
            "0xD4B03C|MysticismMagic.esp",
            "0x29B94|Skyrim.esm"
        ]
    }
}


Structure

"spells" : object (optional)
"staffs" : object (optional)

Both objects have the same arrays inside. In "spells" they contains spells, in "staffs" they contain staffs:

"remove" : form array (optional)
Array of spells/staffs to remove from SUR. This can only remove the default ones and the ones added by JSONs loaded before yours (they're loaded in the alphabetical order). This can be used for magic overhauls that change the level of spells that are novice in vanilla, as well as for "patch addons" such as the default ApocalypseBalanceAdjustments.json that applies Balance Adjustments for Apocalypse changes to the default Apocalypse addon.

"alteration" : form array (optional)
"conjuration" : form array (optional)
"destruction" : form array (optional)
"illusion" : form array (optional)
"restoration" : form array (optional)

Arrays of spells/staffs of the corresponding schools to add to SUR (the names are extracted automatically).

Items
Spoiler:  
Show
Addons can add new separate item options. If you want to change what the default item options give, you need not to create an addon but to edit SUR leveled lists.

Examples

Spoiler:  
Show
A complete JSON that should work:
1. Broom. Count = 1, named automatically.
2. Flute. Count = 1, named automatically.
3. Three blood potions. A name is assigned.
All options are of Disabled-Enabled-Random type.

{
    "items" :
    [
        {
            "fItem" : "0x6717F|Skyrim.esm"
        },
        {
            "fItem" : "0xDABA7|Skyrim.esm"
        },
        {
            "fItem" : "0x18EF3|Dawnguard.esm",
            "iCount" : 3,
            "sName" : "Blood Potions"
        }
    ]
}

Musical Instruments - for Skyrim's Got Talent. Both options are of menu type.
{
    "items" :
    [
        {
            "fItem" : "0xBC1567|Skyrim Unbound.esp",
            "sName" : "$SU_MusicalInstrument"
        },
        {
            "fItem" : "0x80A|Skyrim Unbound Addon - Skyrim's Got Talent.esp",
            "sName" : "$SU_MusicalInstrumentPlayingLevel",
            "options" : [
                {
                    "fItem" : "0x805|Skyrim Unbound Addon - Skyrim's Got Talent.esp",
                    "sName" : "$SU_SkyrimsGotTalentRank1"
                },
                {
                    "fItem" : "0x806|Skyrim Unbound Addon - Skyrim's Got Talent.esp",
                    "sName" : "$SU_SkyrimsGotTalentRank2"
                },
                {
                    "fItem" : "0x807|Skyrim Unbound Addon - Skyrim's Got Talent.esp",
                    "sName" : "$SU_SkyrimsGotTalentRank3"
                },
                {
                    "fItem" : "0x808|Skyrim Unbound Addon - Skyrim's Got Talent.esp",
                    "sName" : "$SU_SkyrimsGotTalentRank4"
                },
                {
                    "fItem" : "0x809|Skyrim Unbound Addon - Skyrim's Got Talent.esp",
                    "sName" : "$SU_SkyrimsGotTalentRank5"
                }
            ]
        }
    ]
}


Structure

"items" : array of objects (optional)
New item options. One element of this array is one MCM option. There are two types of options that you can create:
1. Disabled-Enabled-Random-type options. These options represent one item (or set of items given at once) that the player can set to Disabled, Enabled or Random (50% chance).
2. Menu-type options: Disabled + Random + any number of menu options. These options represent a type of items (or sets of items given at once) from which the player can select a certain one, or set it to Disabled or Random (a random option from the available ones with 100% chance).
Both types of options are set to Disabled by default.

Properties:

"fItem" : form (required)
For Disabled-Enabled-Random-type options - the item that this option gives to the player. It can be any type of item, including armor and weapons, as well as a LeveledItem (leveled list). If you want to add some randomization (i don't mean a "Random" option in the MCM, it's always there) or give multiple different items from one MCM option, you need to use a leveled list (create a new one or use an existing one).
For menu-type options - a FormList. You can either leave it empty and specify all items in the options property, pre-fill this formlist with items, or mix these two ways. Specifying an item in options allows you to set name and count for this item. For items not specified in options but present in the formlist the name will be automatically extracted (but that doesn't work for leveled lists, since they don't have names).

"iCount" : integer (optional)
The count of items to add to the player. If this property is not set, the count is 1.

"sName" : string (optional)
The name of your MCM option. If this property is not set, the name will be extracted from the item set in "fItem". Leveled lists don't have names, so if you use one then you have to specify a name. For menu-type option this property must always be set.

"fItemByWealth" : array of forms (optional)
For Disabled-Enabled-Random-type options. Allows to give different items depending on the player's Wealth. This is an array of 4 forms (same types as in "fItem") matching the 4 Wealth sets: Beggar, Poor, Medium and Wealthy. The one matching the player's Wealth is given to the player instead the one specified in "fItem" (you can use that one in this array for one or more Wealth sets too, and i recommend doing so). One form can be in the list multiple times. Syntax: "fItemByWealth" : [ beggarForm, poorForm, mediumForm, wealthyForm ]

"options" : array of objects (optional)
For menu-type options. One element of this array is one option inside the menu. Properties:
Spoiler:  
Show
"fItem" : form (required)
The item that this menu option gives to the player. It can be any type of item, including armor and weapons, as well as a LeveledItem (leveled list). If you want to add some randomization (i don't mean a "Random" option in the MCM, it's always there) or give multiple different items from one MCM option, you need to use a leveled list (create a new one or use an existing one).

"iCount" : integer (optional)
The count of items to add to the player. If this property is not set, the count is 1. If iCount is set both on the item level and the option level, the resulting count will be the multiplication of the two counts.

"sName" : string (optional)
The name of this menu option. If this property is not set, the name will be extracted from the item set in "fItem". Leveled lists don't have names, so if you use one then you have to specify a name.

"fItemByWealth" : array of forms (optional)
Allows to give different items depending on the player's Wealth. This is an array of 4 forms (same types as in "fItem") matching the 4 Wealth sets: Beggar, Poor, Medium and Wealthy. The one matching the player's Wealth is given to the player instead the one specified in "fItem" (you can use that one in this array for one or more Wealth sets too, and i recommend you do so). One form can be in the list multiple times. Syntax: "fItemByWealth" : [ beggarForm, poorForm, mediumForm, wealthyForm ]

Locations
Spoiler:  
Show
Addons can add new locations to existing or new location types and holds (mod-added worldspaces can be added as holds). New location types are not included in the randomization (this feature may be added later). New locations added to default location types included in the randomization are included in the randomization. It's also possible (while being completely optional) to attach scripts to locations, which allows you perform any actions in addition to teleporting the player to the starting location, such as: adding items, enabling/disabling objects, completing/advancing/starting quests, adding the player to a faction, etc (see Running Scripts section below).

Examples

See the Orc Strongholds and Bruma addons from the installer. The Orc Strongholds addon adds a new location type and uses a script. The Bruma addon adds a new hold and simple locations (without scripts) that belong to this hold and default location types.


Structure

"locationTypes" : array of objects (optional)
New location types. One element of this array is one location type (such as City or Inn). Properties:

"fLocationTypeList" : form (required)
The FormList record of your location type. You can use an empty formlist and make SUR fill it by setting this formlist in the "fLocationTypeList" property on each of the new locations that belong to this type, or just pre-fill the formlist, i.e. add all the locations into it yourself.

"sName" : string (required)
The name of your location type.

"sDescription" : string (optional)
The description of your location type that is displayed at the bottom of the MCM.

"fQuest" : form (optional)
(see Running Scripts section) The quest hosting the custom script.

"sParam" : string (optional)
(see Running Scripts section) A custom string parameter passed as locationTypeParam to custom scripts.

"sAutoLocalInhabitant" : string (optional)
Sets how Local Inhabitant = Auto works for locations of this type. This property can also be set for a specific location, the location value overrides the location type one. Valid values: "Region", "Settlement". If not set, Local Inhabitant will be set to Disabled.

"iAutoBounty" : array of integers (optional)
Enables the Auto Bounty feature for this location type. Contains 3 elements: the minimum bounty, the maximum bounty and the chance of bounty in percents. You can set -1 as the minimum and/or maximum bounty to use default bounty (the one used for bandit camps). Example: 60% chance of bounty between 250 and 2500 septims - "iAutoBounty" : [ 250, 2500, 60 ]

"conditions" : object (optional)
Adds conditions that restrict when this location type is included into the randomization. All conditions have logical AND between them. All conditions are optional. If there are no locations of the location types selected by the player with matching conditions in the holds selected by the player, it ignores all conditions. Conditions currently supported:
Spoiler:  
Show
"never" : 1. Never true.
"raceIs" : array of strings. Valid values: "Argonian", "Breton", "Dark Elf", "Imperial", "High Elf", "Khajiit", "Nord", "Orc", "Redguard", "Wood Elf".
"raceIsNot" : array of strings.
"hasSpells" : 0/1
"hasWeapon" : 0/1. Doesn't count items from the Inventory MCM tab as weapons.
"hasWeaponOrSpells" : 0/1. Doesn't count items from the Inventory MCM tab as weapons.
"isInArmor" : 0/1. 0 = True if the player is in clothes. 1 = True if the player is in light or heavy armor.
"isInRags" : 0/1. Check is the player is in the "Rags" armor set.
"isVampire" : 0/1.
"isWerewolf" : 0/1.
"wealthIsOrLowerThan" : 1-4. 1 = Beggar, 2 = Poor, 3 = Medium, 4 = Wealthy.
"wealthIsOrHigherThan" : 1-4.

an example for a location that is by some reason only fits noble breton or high elf spell warriors:
conditions: {
    "raceIs" : [ "Breton", "High Elf" ],
    "hasSpells": 1,
    "hasWeapon" : 1,
    "isInArmor" : 1,
    "wealthIsOrHigherThan" : 3
}

but actually only use conditions when they are really needed.


"holds" : array of objects (optional)
New holds. One element of this array is one hold. Property:

"fHoldList" : form (required)
The FormList record of your hold. You can use an empty formlist and make SUR fill it by setting this formlist in the "fHoldList" property on each of the new locations that belong to this hold, or just pre-fill the formlist, i.e. add all the locations into it yourself.

"sName" : string (required)
The name of your hold.

"fCrimeFaction" : form (optional)
A Faction record, the so called "crime faction" of your hold (the faction that is reponsible for tracking crimes in this hold). Specifying this property enables the Bounty option for your hold and automatically generates a Jail location for it.


"locations" : array of objects (optional)
New locations. One element of this array is one location option (such as City > Solitude or Inn > The Winking Skeever). Properties:

"fLocation" : form (required)
This can be either an object reference that the player will be teleported to (a world object, usually an invisible marker like XMarkerHeading) or a FormList with such references, one of which will be picked randomly (it allows you to have multiple positions for one location, like in the default City and Town locations). You can use an existing marker (which of course you can only see in Creation Kit), but usually you want to place a new marker, this can be done using Creation Kit or Marker Placer. All references (markers) must be persistent (have Persistent flag in their record header or/and be located in a plugin that is not .esm/.esm-flagged. i recommend to add Persistent flag anyway), whether it's one specified in fLocation, or ones included in a formlist specified in fLocation.

"sName" : string (optional)
The name of your starting location. If the name is not set, SUR will try to automatically retrieve it in the following priority: parent cell > parent location > parent worldspace.

"sType" : string (optional)
If your location belongs to one of the default types, this must be the name of that type. Jail locations can't be added this way, they get created automatically for addon-added holds. There are also default types Player Home and Other that are hidden if no addons extend them. "Other" can be used if your location doesn't belong to any of the default types and you don't want to create a new one. Valid values: "City", "Town", "Inn", "Wilderness", "Border", "Docks", "Temple", "Player Home", "Homeless Shelter", "Faction Headquarters", "Bandit Camp", "Warlock Lair", "Vampire Lair", "Other". See notes about default location types below.

"fLocationTypeList" : form (optional)
If your location belongs to an addon-added location type, this is its formlist record (the one specified in the "fLocationTypeList" property of that location type). This can be used to add locations to location types added by other addons, as well as to the ones from your addon if you don't want to pre-fill the formlist (if you pre-fill it, you don't need to use this property).

"sHold" : string (optional)
If your location belongs to one of the default holds, this must be the name of that hold. Valid values: "Eastmarch", "Falkreath", "Haafingar", "Hjaalmarch", "The Pale", "The Reach", "The Rift", "Whiterun", "Winterhold", "Solstheim", "Other". "Other" can be used if your location doesn't belong to any of the default holds and you don't want to create a new one. Each location must have a hold assigned, whether it's through "sHold", "fHoldList" or by prefilling your hold formlist.

"fHoldList" : form (optional)
If your location belong to an addon-added hold, this is its formlist record (the one specified in the "fHoldList" property of that hold). This can be used to add locations to holds added by other addons, as well as to the ones from your addon if you don't want to pre-fill the formlist (if you pre-fill it, you don't need to use this property). Each location must have a hold assigned, whether it's through "sHold", "fHoldList" or by prefilling your hold formlist.

"fQuest" : form (optional)
(see Running Scripts section) The quest hosting the custom script.

"sParam" : string (optional)
(see Running Scripts section) A custom string parameter passed as locationParam to custom scripts.

"sAutoLocalInhabitant" : string (optional)
Sets how Local Inhabitant = Auto works for this location. This property can also be set for a location type, the location value overrides the location type one. Valid values: "Disabled", "Region", "Settlement". If not set, the location type value will be used (for vanilla location types: Player Home, Homeless Shelter, Faction Headquarters = "Settlement"; Bandit, Warlock, Vampire Lair = "Region").

"sForceLocation" : string (optional)
When Local Inhabitant is enabled, to identify to which settlement your location belongs SUR checks in which location your marker is. By location i mean the technical location, the Location record set in the properties of the cell (or the worldspace if the cell doesn't have any location set). However if you place a marker in a mod-added interior that is not properly set or on the edge of a settlement, it's possible that your marker won't technically be in this settlement location. In such cases if you want the Local Inhabitant option to work with your location, you need to set sForceLocation. Valid values: "Dawnstar", "Falkreath", "Markarth", "Morthal", "Riften", "Solitude", "Whiterun", "Windhelm", "Winterhold", "Raven Rock", "Dragon Bridge", "Ivarstead", "Karthwasten", "Riverwood", "Rorikstead", "Shor's Stone".

"iAutoBounty" : array of integers (optional)
Enables the Auto Bounty feature for this location type. Contains 3 elements: the minimum bounty, the maximum bounty and the chance of bounty in percents. You can set -1 as the minimum and/or maximum bounty to use default bounty (the one used for bandit camps). Overrides the same setting set on the location type. Example: 60% chance of bounty between 250 and 2500 septims - "iAutoBounty" : [ 250, 2500, 60 ]

"conditions" : object (optional)
Adds conditions that restrict when this location is included into the randomization. See the same property in "locationTypes" for detals.


Notes about default location types

Wilderness
Spoiler:  
Show
Nearby predators are automatically disabled for wilderness locations.

Bandit Camp, Warlock Lair, Vampire Lair
Spoiler:  
Show
Managing all the factions is handled automatically for all locations of these types, including addon-added ones, however the method used has limitations on structure of locations that can be used.
To better understand these limitations, you need to know how it works. The corresponding NPCs in the starting location need to be detected. If the player starts in an interior, it follows the player through any number of interior cells detecting NPCs in these cells. Once the player reaches an exterior or if the player started in an exterior, it detects NPCs around and stops. Therefore, there are the following limitations:
1) If the location has a single interior cell, the player must start in that interior.
2) If the location has multiple interior cells, all the interior cells must be connected in chain that looks like Exterior > Interior > Interior, where you can get from one end to the other one by only one way, and the player must start in the final/farthest interior cell, so that the player always goes from that cell through all the other interior cells and all of them are loaded before exiting to the exterior.
3) If the location has interior cell(s) and multiple entrances to the interior, these entrances must not be too far from each other in the exterior.
(Locations that only have an exterior are fine)
This limits where the starting marker can be placed and doesn't allow to use complex cell structures such as most forts.


How to check where the player started in conditions? How to implement conditions when implementing Local Inhabitant in non-vanilla settlements?
Spoiler:  
Show
There is a reference StartingLocationMarker placed by SUR at the position where the player starts. You can then check its location using one of the following functions:
GetInCurrentLoc()
GetInCurrentLocFormList()
GetInCell()
GetInSameCell()
IsInSameCurrentLocAsRef()
GetDistance()
Alternatively you can create a custom quest for your location and set a custom global variable, which you can then check via GetGlobalValue().

To check if Local Inhabitant is enabled at all you can check global variable PlayerIsLocalInhabitantOfSettlement/PlayerIsLocalInhabitantOfRegion ("Disabled" = 0/0, "Region" = 0/1, "Settlement" = 1/1).

If you need to check if the player started as a Local Inhabitant in a specific settlement, you need to combine checks for StartingLocationMarker and PlayerIsLocalInhabitantOfSettlement/PlayerIsLocalInhabitantOfRegion.

Example for dialogues that shouldn't play if the player is a local inhabitant in a specific settlement:
Any.GetGlobalValue(PlayerIsLocalInhabitantOfSettlement) = 0 OR
StartingLocationMarker.GetInCurrentLoc(the settlement location) = 0

Example for the ones that should play only if the player is a local inhabitant in a specific settlement:
Any.GetGlobalValue(PlayerIsLocalInhabitantOfSettlement) = 1 AND
StartingLocationMarker.GetInCurrentLoc(the settlement location) = 1


Locations - Running Scripts
Spoiler:  
Show
It's possible to attach scripts to locations, which allows you to perform any actions in addition to teleporting the player to the starting locations: adding items, enabling/disabling objects, completing/advancing/starting quests, adding the player to a faction, etc.

Examples

See the Orc Strongholds addon.


Steps

1. Create a new quest that will host your script
Create a new quest (in CK or xEdit). You don't need to create a real quest with objectives and stuff, just an empty invisible quest to attach your script to it.

2. Create a new script that extends SkyrimUnboundLocationAddonScript on the created quest
Attach a new script to the created quest. The script must extend SkyrimUnboundLocationAddonScript: you can replace "Quest" to "SkyrimUnboundLocationAddonScript" when adding a new script in CK or in the first line of an already created script so the first line looks like this:
Scriptname YourCustomScriptName extends SkyrimUnboundLocationAddonScript

3. Add PrepareStart function to your script
Add the following function to your script. This is the main function of your script. It will be called by SUR before teleporting the player to the starting location.

function PrepareStart(ObjectReference teleportMarker, string locationTypeParam, string locationParam)
endFunction

4. Add the created quest to your JSON
To make SUR know that it should run your quest and for which locations it should do that, you need to specify your quest in the fQuest property. You can set it either on a specific location or a location type. A single quest can be used for multiple locations or/and location types (but you can use as many quests (with their own scripts) as you want). If for some location fQuest is set both on this location and its location type, only the quest on the location will be used.

5. Write your script
Now you can write any code you want but first you need to know two things.

5.1. How to identify the current location and location type if you used your quest for multiple locations or/and location types AND what are the paramaters of the PrepareStart function
To identify the current location (the one selected by the player or randomly this time), you can use the parameters of the PrepareStart() function:

ObjectReference teleportMarker

Spoiler:  
Show
This is the reference (the marker) that the player is being teleported to.

One of the ways to identify current location is to add all your markers as ObjectReference properties and compare teleportMarker to them to indentify which one it is:

ObjectReference Property MyMarker1 Auto
ObjectReference Property MyMarker2 Auto

function PrepareStart(ObjectReference teleportMarker, string locationTypeParam, string locationParam)
if teleportMarker == MyMarker1
;do stuff needed for location 1
elseif teleportMarker == MyMarker2
;do stuff needed for location 2
endif
;do stuff needed for both locations
endFunction

string locationTypeParam, string locationParam

Spoiler:  
Show
These strings are custom parameters. They're empty strings by default but you can specify them in your JSON for any of your locations and location types. locationTypeParam is the sParam property set on the location type, locationParam is the sParam property set on the specific location. If you use a location type from another addon, its location type param will be passed to your function too.

One of possible uses of these custom parameters is to identify the current location (as an alternative to teleportMarker):

function PrepareStart(ObjectReference teleportMarker, string locationTypeParam, string locationParam)
    if location TypeParam == "MyType1" ;as set on the location type in the .json
        if location Param == "1" ;as set on the location in the .json
            ;do stuff for location 1 of type 1
        elseif location Param == "2"
            ;do stuff for location 2 of type 1
        endif
    elseif location TypeParam == "MyType2"
        ;do stuff needed for all locations of type 2
    endif
endFunction

You may find other uses of these parameters.

5.2. When your code runs and how to control that
The PrepareStart function is run before teleporting the player to the strarting location. The player doesn't get teleported unless one of two things happen:
1) Your PrepareStart function returned (ended)
2) Your script called TeleportPlayer() function
TeleportPlayer() allows teleportation and waits until the player is teleported, and can be used if you need to run some code after teleportation. If you don't need that, you don't need to use this function.
I.e. there are two options:

function PrepareStart(ObjectReference teleportMarker, string locationTypeParam, string locationParam)
    ; code that runs before teleportation
endFunction

function PrepareStart(ObjectReference teleportMarker, string locationTypeParam, string locationParam)
    ; code that runs before teleportation
    TeleportPlayer()
    ; code that runs after teleportation
endFunction

If you have some code that runs after teleportation, SUR doesn't wait for the PrepareStart() function to return - both scripts run in in parallel and SUR enables controls and advances the SkryimUnbound quest stage regardless of whether your function already returned or not. This is the desired behavior in most cases, but not if you want to show some messagebox or other menu to the player in your location addon script, because then if the player has other mods that add menus that pop up after the game start (like class selection), these menus will interfere with yours. For such cases there's AfterTeleportationSync() function.
AfterTeleportationSync() is another function that you can use in addition to PrepareStart(). It's called after the player is teleported to the starting location and (which is the difference from code after you call TeleportPlayer() in PrepareStart()) SUR waits for this function to return before enabling player controls and advancing the SkyrimUnbound quest stage. It means that mod-added menus that pop-up after the game start and check these things to determine when to pop up will be delayed until this function returns.

function AfterTeleportationSync(ObjectReference teleportMarker, string locationTypeParam, string locationParam)
endFunction


Notes

Starting and stopping your quest
When creating a new quest in CK the "Start Game Enabled" flag is set. It's recommended to uncheck it, SUR will start your quest if it isn't started yet. It's also recommended to stop your quest once everything is done, unless you need it to be running. To do this, call Stop() in the last line of the PrepareStart function. This way your quest and script will only work when they're needed.

Article information

Added on

Edited on

Written by

lilebonymace
  翻译: