Framework for mods to define armor variants by swapping armor addons dynamically at runtime.
Dynamic Armor Variants Extended is a fork of the original Dynamic Armor Variants by Parapets, extended with new features/native API.
Runtime compatibility intentionally stays close to the original project:
- plugin name remains
DynamicArmorVariants.dll - save data code remains compatible with the original DAV plugin
- config loading still uses
Data/SKSE/Plugins/DynamicArmorVariants
Changes 100% vibecoded with Codex
- Added a public SKSE messaging API for native integrations.
- Added support for registration/deletion of new variants at runtime.
- Added support for replacement identifiers that specify both owning armor and armor addon.
- This lets variants render in completely disjoint slots from the original armor, so long as the actor doesn't have a conflict
- Added caching around variant resolution for minor performance improvements.
Variant replacement maps support two replacement identifier forms:
Plugin.esp|AddonLocalID- Replaces with that
ARMAand keeps the source armor as the default owner.
- Replaces with that
Plugin.esp|ArmorLocalID|Plugin.esp|AddonLocalID- Replaces with that
ARMAand explicitly supplies the owningARMOto use while visiting the addon.
- Replaces with that
This applies to values in both replaceByForm and replaceBySlot, and to arrays of those values.
Variants may also define an optional integer priority in the range
-1000000 to 1000000.
- Higher
prioritywins when multiple condition-active variants compete. - Manually applied variant overrides still always take precedence over condition-based resolution.
Example:
{
"priority": 10,
"replaceByForm": {
"Skyrim.esm|0010D6A1": [
"Dragonborn.esm|0001C655|Skyrim.esm|00027DE4",
"Skyrim.esm|00027DE5"
]
}
}- xmake
- Visual Studio 2022 with Desktop development with C++
git submodule update --init --recursiveFrom Windows:
xmake f -y -m release
xmake build -yFrom WSL:
./scripts/build.shThe plugin DLL is produced under build/windows/x64/<mode>/DynamicArmorVariants.dll.
To build from WSL and deploy into the pt_test MO2 modlist:
./scripts/build-deploy.sh
./scripts/build-deploy.sh --clean
./scripts/build-deploy.sh debugBy default this deploys to /mnt/f/games/skyrim/modlists/pt_test/mods/Dynamic Armor Variants Extended. Set MOD_DIR to override the destination.
The default deploy mode is releasedbg, so the script also deploys DynamicArmorVariants.pdb when available. Use ./scripts/build-deploy.sh release for a stripped release build.
The deploy step copies the built DLL, the repository data/ tree, and compiled Papyrus output.
To build a release archive:
./scripts/build-package.shFor the full WSL build, deploy, packaging, tag, and GitHub release workflow, see
docs/Build-Deploy-Release.md.
This writes a zip under dist/ named like:
Dynamic Armor Variants Extended v1.1.0.zip
For a real release package, keep the worktree clean and put a semver tag like
v1.7.1 on HEAD. Otherwise scripts/version.sh will emit a dev version
string and the archive name will include the -dev+<sha> suffix.
The release package includes:
- a FOMOD installer layout compatible with the original DAV package structure
DynamicArmorVariants.dllDynamicArmorVariants.pdb- compiled Papyrus
.pex - Papyrus source
.psc
./scripts/format.sh
./scripts/format.sh --check
./scripts/lint.sh
./scripts/lint.sh src/main/DynamicArmorManager.cppFrom WSL, format.sh prefers Linux clang-format when available. lint.sh prefers the
Visual Studio LLVM x64 clang-tidy.exe because the generic Visual Studio Llvm/bin
binary is a 32-bit build that crashes in this environment.
lint.sh also passes /Y- to disable MSVC PCH use during linting. xmake's generated
compile_commands.json points clang-tidy at a PCH path that does not exist, so disabling
PCH is the reliable way to lint these translation units.
Dynamic Armor Variants Extended exposes a versioned SKSE messaging API for native integrations.
Public consumer files:
include/DynamicArmorVariantsExtendedAPI.hinclude/DynamicArmorVariantsExtendedAPI.cpp
Fetch the interface after SKSE::MessagingInterface::kPostLoad:
auto* dav = DynamicArmorVariantsExtendedAPI::GetDynamicArmorVariantsExtendedInterface001();Interface 001 exposes:
RegisterVariantJson(name, variantJson)DeleteVariant(name)SetVariantConditionsJson(name, conditionsJson)RefreshActor(actor)ApplyVariantOverride(actor, variantName, keepExistingOverrides = false)RemoveVariantOverride(actor, variantName)
RegisterVariantJson expects the same JSON object shape as a single entry in the variants array from the config files. If the variant already exists, it is fully overwritten.
SetVariantConditionsJson accepts either:
- a raw JSON array of condition strings
- a JSON object with
conditionsand optionalrefs
Example conditions payload:
{
"refs": {
"target": "Skyrim.esm|00000007"
},
"conditions": [
"GetDistance TARGET <= 256",
"IsSneaking == 1"
]
}