You are on page 1of 7

ATTENTION MODDERS - ATTENTION MODDERS - ATTENTION MODDERS - ATTENTION MODDERS -

ATTENTION MODDERS - ATTENTION MODDERS


===================================================================================
==================================

PLEASE READ EVERYTHING CAREFULLY before making a mod using FNIS AA (alternate
animations) functionality.

USING FNIS AA INAPPROPRIATELY can result in considerable, unnecessary CPU load,


and/or total failure !!!!!

Overview
========

With FNIS AA it is possible to dynamically (i.e. during runtime) replace 537 (3rd
person) character standard animations with an "arbitrary" number of custom
replacements, called alternates. This feature allows a tremendous variety of
animation solutions. However, this is severely hampered by restrictions of Papyrus
and the general game architecture.

The selection of alternate animations is done using Havok animation variables


(animvars). In order to reduce the amout of these AnimVars, and the number of
function calls in your mod, the animations are subdivided into 54 Animation Groups,
where all animations of one group are controlled by one AnimVar. You will always
change all animations of one group at the same time.

The size of the groups range from one (like for the mt_idle.hkx) up to 41 for the
1hm_attack animations except for the power attacks. For a complete list of all
groups and their animation files please to the file

>>> Alternate_AnimationGroups.txt <<<

in tools/GenerateFNIS_for_Users. But please: DO NOT CHANGE


Alternate_AnimationGroups.txt. It is used by the Generator itself.

The Problems
============

The problems FNIS AA has to solve are not so much stemming from the behavior
changes themselves. They are relatively easy and straight-forward so it was
possible to have a tool which automatically inserted almost all the necessary
changes to the behaviors. It's because FNIS supports an arbitrary number of mods,
and exactly these mods can be added, modified and removed between save and load.
And because of some System and Papyrus short-comings.

1) Limited NPC functionality


----------------------------

AnimVars work well for the player. As soon as the player is in 3rd person, her/his
3rd person AnimVars are available. Not so for the NPCs. Whenever an NPC "looses
3D", s/he looses all AnimVar values as well. As soon as 3D is regained, all values
are set to 0. "Loosing 3D" is probably a matter of the game cache, not loosing
sight or being in a different interior. If you go for example from the Whiterun
market into one shop and come bach, AnimVars are still unchanged. If you fast
travel to another place and immediately come back, AnimVars are reset.
To solve this problem, Skyrim doesn't provide much help. Most notably, there are no
custom actor values you could use. FNIS Sexy MOve, for example, uses MiscItems
("Sexy Coins") in the NPC's inventory to determine the AnimVar values once the NPC
is close to the player. Maybe you can find another solution, suited to your
situation.

2) Changing Installation Situations


-----------------------------------

All mods share the same AnimVar for the same AnimGroup. Each mod gets a range
assigned, and therefore a mod and group specific base value has to be added every
time an AnimVar assignment is made. But whenever a game is loaded, it is possible
that your mod's base values have changed whenever the user has added or updated or
deleted a mod that uses AA functionality.

FNIS supports this situation in 2 ways:

- On game load FNIS will check all AnimVars for the player and all NPCs in
question, and adjust them appropriately

- You have to make sure that you always use the updated base value(s). But DO NOT
RUN the designated FNIS functions EVERY TIME you have to change the AnimVar. They
can can become very time consuming. You should do this in a PlayerAlias script.
There you can compare the crc value of the current installation with the previous
one, and update if that value has changed. If it is not possible to to make a
player alias script, then you should at least check for different crc before
loading the base value(s) anew.

3) Data/performance Limitations
--------------------------

Due to game architecture and Papyrus limitations it was not possibleto implement
the AA functionality without considerable restrictions. BE RESPONSIBLE TOWARDS THE
COMMUNITY, AND DON'T DO THINGS UNNECESSARILY!

- Maximum 128 sets in one user installation (one set defines all custom animations
in one mod for one particular group. For example, XPMSE/Racemenu requires 6 sets
alone for the euquip animations for 6 different weapon types).

- Considerable CPU load when calculating the base values. Especially when the user
comes close to the 128 set limit.

These limitations can possibly be reduced by the use of an SKSE plugin, which I
will attempt in a future release. But I don't want to make FNIS generally dependent
from SKSE to begin with.

How to set up Alternate Animation for your mod (example)


========================================================

1) Define an animation folder (animations\<mymod>) and an FNIS animation list


(animations\FNIS_<mymod>_List.txt), as usual

2) Got to the file GenerateFNIS_for_Users\Alternate_AnimationGroups.txt to find out


which animations you want to replace, and which group they belong to

3) In the FNIS list use the following definitions:


'3 character abbreviation for your mod
AAprefix mym
'all animation sets you need in your mod
AAset _mtidle 3 '3 alternate animations for all files of group
_mtidle (mt_idle.hkx)
AAset _1hmidle 2 '2 alternate animations for all files of group
_1hmidle (1hm_idle.hkx, sneak1hm_idle.hkx)

4) In the animations\<mymod> place all your alternate animations. Each one has the
name <mod_prefix><number_0_to_9>_<original_filename>:
mym0_mt_idle.hkx
mym1_mt_idle.hkx
mym2_mt_idle.hkx
mym0_1hm_idle.hkx
mym0_sneak1hm_idle.hkx
mym1_1hm_idle.hkx
mym1_sneak1hm_idle.hkx
If you don't want to define alternates for certain files of a group then DO NOT USE
COPIES of the standard files. Simply omit the alternates you don't need. FNIS will
automatically use standard animations instead!

5)Scripting
The following is the RECOMMENDED structure of a mod using FNIS AA functionality. If
you don't use such structure, pleas keep in mind that all functions (except
SetAnimGroup() itself) can be very time consuming.

Quest script
------------
Scriptname myModQuest extends Quest Conditional
int Property myModID Auto
int Property my_mtidle_base Auto
int Property my_1hmidle_base Auto
int Property myModCRC Auto

Event OnInit()
myModCRC = FNIS_aa.GetInstallationCRC()
if ( myModCRC == 0 )
; Installation Error: no AA generated by FNIS
else
myModID = FNIS_aa.GetAAModID("mym", "<myMod>", true) ; true
during test phase only
my_mtidle_base = FNIS_aa.GetGroupBaseValue(myModID, FNIS_aa._mtidle(),
"<myMod>", true)
my_1hmidle_base = FNIS_aa.GetGroupBaseValue(myModID,
FNIS_aa._1hmidle(), "<myMod>", true)
endif
EndEvent

PlayerAlias script
------------------
Scriptname myMod_PlayerAlias extends ReferenceAlias
myModQuest Property myMod Auto

event OnPlayerLoadGame()
int current_crc = FNIS_aa.GetInstallationCRC()
if ( current_crc == 0 )
; Installation Error: no AA generated by FNIS
elseif ( current_crc != myMod.myModCRC )
myMod.myModID = FNIS_aa.GetAAModID("mym", "<myMod>", true)
; true during test phase only
myMod.my_mtidle_base = FNIS_aa.GetGroupBaseValue(myModID,
FNIS_aa._mtidle(), "<myMod>", true)
myMod.my_1hmidle_base = FNIS_aa.GetGroupBaseValue(myModID,
FNIS_aa._1hmidle(), "<myMod>", true)
myMod.myModCRC = current_crc
endif
endEvent

Script changing the groups' AnimVar


----------------------------------
..................
if <set_my_animation>
bOk = FNIS_aa.SetAnimGroup(Game.GetPlayer(), "_mtidle",
myMod.my_mtidle_base, <value_0_to_9>, "myMod", true)
else
bOk = FNIS_aa.SetAnimGroup(Game.GetPlayer(), "_mtidle", 0, 0, "myMod",
true) ' this sets to vanilla
endif
if !bOk
; there was a problem
endif
..................

Alternate Trigger Events


========================

Skyrim behaviors use numerous animation events to control the use of animations,
and interact with the engine. For example to indicate the hit moment during combat,
or the moment a weapon is sheathed or unsheathed. Or when the foot sound is played.
Or, or, or... It is apparent, that custom animation replacers might require
different timing to execute those things. Or add or remove some events, for example
when a hovering movement animation doesn't need the foot sound.

Keeep in mind, that it it is possible to send the animevents either from the
behavior or from the animation. In order to undersand the used anim event, convert
the animation files into xml (using hkxcmd), and use CondenseBehavior
http://www.nexusmods.com/skyrim/mods/32113 to read the behavior files (although
behavior files are rather complicated and large, it is relatively easy with
CondenseBehavior to find the animation files, and understand understand which
animevents they send).

Now you can modify the anim events for Alternate Animations in the behavior files.
For each Alternate Animation you want to change anim events for, you have to add
one command to the mod's animation list. Note that you always REPLACE all anim
events for an animation. If you just want to change the time of one anim event, you
still have to list ALL anim events.

Format:
T <alternate_animation_name> <anim_event_1> <time_1> <anim_event_2>
<time_2> .......

<alternate_animation_name> has the format


<mod_prefix><number_0_to_9>_<original_filename>
If time_<i> is negative, it indicates a send time relative to the end of the
animation.

Example:
The following command is part of the XPMSE animation list. They are necessary due
to a "bug" in the unequip animations from the "dagger on the ankles" animations.
When moving, the dagger could not be sheathed any more. In this file the animator
has moved the anim event weaponSheathe from 0.900 to 0.966667 (in the animation
file). However this caused a syc problem with the anim event Unequip_OutMoving,
defined at 0.9333 sec in behaviors (-0.4 relative to a 1.33 sec animation). The
following fix simply moves Unequip_OutMoving to 1.0 sec (-0.33 relative to 1.33).

T xpe3_Dag_Unequip.hkx Unequip_OutMoving -0.333 Unequip_Out -0.00


BeginWeaponSheathe 0.00

Alternate Trigger Events under PCEA2


====================================

If you want to change a trigger under PCEA2 you can do this with the following
steps:
- in the subfolder that includes the concerned animation file, create a file
FNIS_PCEA2_<any_name_you_like>_List.txt.
- in FNIS_PCEA2_<any_name_you_like>_List.txt add trigger events ("T" commands)
similar the above, with the exception that the file names are the vanilla ones
(without "<mod_prefix><number_0_to_9>_")

T <original_filename> <anim_event_1> <time_1> <anim_event_2> <time_2> .......

FNIS AA Functions
=================

; =============================================================================
; bool FNIS_aa.SetAnimGroup(actor ac, string animGroup, int base, int number,
string mod, bool debugOutput = false)
; =============================================================================
; This function activates new custom versions for a group of alternate animations
; by defining the appropriate animation variables, e.g "FNISaa_mtidle"
; It also sets an animgroup specific (e.g. "FNISaa_mtidle_crc") and an overall
; variables ("FNISaa_crc") with crc values needed for base adjustment on start.
;
; "number" is between 0 and 9, which determines the number of the animations
; selected for this specific mod. It will always change the number for all anims
; of a group "animGroup" according to Alternate_Animations.txt. E.g. changing the
; number for the group "_1hmidle", will select 1hm_idle.hkx and sneak1hm_idle.hkx
;
; "base" is goup, mod, and installation specific value which will be added to
; "number" and reflect animations of another mod which come first in load order
;
; NOTE: the function GetGroupBaseValue to determine the base value can be VERY
; time consuming. If you intend to frequently set AA animation variables, it is
; recommended to calculate the base values only once OnInit and OnPlayerLoadGame
;
; This example will activate the animation fsm3_mt_idle.hkx for the player
; int myModID = FNIS_aa.GetAAmodID("fsm", "FNIS Sexy Move"); "fsm" for FNIS Sexy
Move
; int myBase = FNIS_aa.GetGroupBaseValue(myModID, FNIS_aa._mtidle(), "FNIS Sexy
Move")
; bool result = FNIS_aa.SetAnimGroup(Game.GetPlayer(), "_mtidle", myBase, 3, "FNIS
Sexy Move")
;
; @param actor - actor
; animGroup - name of the animgroup to be modified (e.g.
"_mtidle")
; base - load, mod and animgroup specific base value
; (see GetGroupBaseValue()
; number - mod specific animation number (0 to 9)
; mod - (full)full mod name (for error message and debug)
; debugOutput - true to add debug output to logfile
; @return bool - false, if AnimVar could not be set

; =============================================================================
; int FNIS_aa.GetAAmodID(string myAAprefix, string mod, bool debugOutput = false)
; =============================================================================
; Function returns the AAmodID. myAAprefix is a 3 character abbreviation of
; the mod using the FNIS Alternate Animation functionality.
; This function can be timeconsuming. If you intend to frequently set the
; AA animation variables, it is recommended to calculate the AA prefix of your
; mod only once OnInit and OnPlayerLoadGame
; Because this function can be lengthy, and the ID can change after each load,
; it is recommended to read the ID only once OnInit and OnPlayerLoadGame
; Note: the prefix strings are patched by the FNIS generator (instead of mm0 ..)
; @param myAAprefix - 3 character AA prefix
; mod - (full)full mod name (for error message and debug)
; debugOutput - true to add debug output to logfile
; @return int - >=0 ID (for this load run only)
; -1 myAAprefix not defined (not included by
generator)

; =============================================================================
; int FNIS_aa.GetGroupBaseValue(int AAmodID, int AAgroupID, string mod, bool
debugOutput = false)
; =============================================================================
; Function returns the GroupBaseValue for the mod identified by AAmodID and
; and anim group identified by AAgroupID.
; The resulting base value has to be added to the mod's animation index when
; setting the group specific animation variable
;
; This function can be timeconsuming. If you intend to frequently set the
; AA animation variables, it is recommended to calculate the base values
; only once OnInit and OnPlayerLoadGame
; @param AAmodID - mod ID for the mod calculated by GetAAmodID()
; AAgroupID - group ID for the animation group
; mod - (full)full mod name (for error message and debug)
; debugOutput - true to add debug output to logfile
; @return int - >=1: base value for the mod and anim group
; - 0: not found / parameters wrong

; =============================================================================
; int[] FNIS_aa.GetAllGroupBaseValues(int AAmodID, string mod, bool debugOutput =
false)
; =============================================================================
; Function returns an array with the GroupBaseValues for ALL groups defined by
; the mod AAmodID. The resulting base values have to be added to all of the
; mod's animation indeces when setting the group specific animation variable
;
; This function can be timeconsuming, however less timeconsuming than calling
; GetGroupBaseValue() for 3 or more animation groups. It's recommended to call
; this function always OnInit and OnPlayerLoadGame.
; @param AAmodID - AA prefix ID for the mod calculated by GetAAmodID()
; mod - (full)full mod name (for error message and debug)
; debugOutput - true to add debug output to logfile
; @return int[54] - >=1: base value for the mod and anim group
; - 0: not found / parameters wrong

; =============================================================================
; int FNIS_aa.GetInstallationCRC()
; =============================================================================
; Function returns a crc value for the current user installation.
; Whenever this value changes after game load, the mod's base values might have
; changed and need to be re-loaded
; @return int - crc value for thecurrent user installation

; =============================================================================
; int FNIS_aa.<AA_groupname>()
; =============================================================================
; These functions return the int values of the 54 Alternate Animation Groups
; e.g FNIS_aa._2hmidle() returns 2
; @return int - 0 <= int value <= 53