The goal of implementing the script for FNIS Alternate Animation (hereinafter referred to as FNIS AA) in C++ is to achieve the following:
- Enable FNIS and Nemesis-compatible patching tools, such as d_merge and Pandora, to easily support FNIS AA simply by outputting the specified JSON format.
- Improve performance (by constructing the data required by FNIS scripts when reading JSON and caching it in memory).
This will be useful for users who are using the Patching Tool and FNIS AA mods such as XPMSE or FNIS Sexy Move SE.
-
Skyrim Special Edition / VR
-
A patching tool capable of converting FNIS AA to OAR and generating
SKSE/plugins/fnis_aa/config.json
-
Enable the FNIS AA mod and this mod in MO2 or similar software
-
Apply the patch as usual using a patching tool compatible with this plugin
-
Launch the game. No further setup needed.
FNIS generates the following script each time it runs to define the valid ranges for the variables used by all FNIS AA mods that you have enabled.
-
FNIS_aa2.pex -
FNISVersionGenerated.pex
Instead of compiling the script every time, this plugin defines functions based on JSON using SKSE Plugin.
Also, overwrite the following script to speed up execution
FNIS_aa.pex
[Patching Tool(e.g., d_merge, Pandora)]
├──> FNIS AA to OAR
`SKSE/Plugins/
├──> Injects FNISaa_* variables. Using BehaviorDataInjector is another option.
└──> Generated [fnis_aa/config.json] --------> [C++ Plugin] (Native memory allocation)
|
[Existing Mod Scripts] --------------|
| |
| (Call SetAnimGroupEX) |
v v
[Native Interceptor] ---------> [Dynamic Vector] (mod_id 0 ~ Unlimited)
| |
| (Calculate: Base + Index) |
v v
[Actor Variable] -------------> Sets "FNISaa_<group>" to behavior graphGenerated by patching tool. Located at SKSE/Plugins/fnis_aa/config.json.
{
"crc": 1520082533,
"fnis_version": "V07.06.00.0",
"fnis_creature_version": "V07.06.00.0",
"mods": [
{
"prefix": "fsm",
"name": "fsm",
"mod_id": 0,
"groups": [
{
"name": "_mt",
"base": 1
}
]
},
{
"prefix": "fs3",
"name": "fs3",
"mod_id": 1,
"groups": [
{
"name": "_mt",
"base": 10
},
{
"name": "_mtx",
"base": 1
}
]
},
{
"prefix": "xpe",
"name": "xpe",
"mod_id": 2,
"groups": [
{
"name": "_1hmeqp",
"base": 1
},
{
"name": "_2hmeqp",
"base": 1
}
]
}
]
}| Field | Description |
|---|---|
crc |
Fingerprint of the slot layout. Invalidates saved values on load order change. |
fnis_version |
FNIS version string returned by FNISVersionGenerated.get(). |
prefix |
3-character mod prefix used by FNIS (e.g. xpe for XPMSE). |
mod_id |
FNIS AA id. (start from 0) |
base |
First slot index occupied by this mod for this group. (start from 1. 0 is default) |
-
FNIS version string format:
format: "V<major:2>.{minor:2}.{patch:2}{flags(Option):1}" [1..2] = Major [4..5] = Minor1 [7..8] = Minor2 [10] = Flags (0=Release, 1=Alpha, 2=Beta): <- Option Example: "V07.06.00", "V07.06.002"
slot 0..N mod A (base=1, slot_count=N)
slot N..M mod B (base=N+1, slot_count=M-N)GetAAprefixList returns a string encoded as {mod_id:2}{group_id:2}{base:2}.
Since each digit is two characters long, the total cannot exceed 99.
These were primarily used in the original FNIS_aa.pex, which serves as a wrapper, but since heap allocation is wasteful, this plugin does not use them.
In other words, if you are only using mods that do not depend on the behavior of GetAAprefixList, I believe it should be fine to register more than 99 entries.
The crc field is a fingerprint of the current slot layout. On each game
load, the plugin writes the current CRC to FNISaa_crc on the actor.
If the value differs from what was saved, the calling Papyrus script should
re-apply all animation variables to reflect the updated layout.
This SKSE plugin simply registers a function and does nothing else.
Therefore, you can uninstall it at any time.
See developer.md