Core Impact Framework 2.0 - API Usage

⚠️ Retro-compatibility: Every JSON file written for CIF V1 will continue to work as-is with CIF 2.0. On load, the framework automatically detects and converts V1 BipedMapping and V1-style flat Filters to the new V2 format in memory. Existing mods such as Sanguine Symphony do not require any update.

The reverse is not true: JSON files written for V2 (new filter axes, new modifiers, new Biped Mapping groups, logical operators) are not understood by CIF V1. If your mod targets V2-only features, it will require CIF 2.0 or later as a hard dependency.

Table of Contents

πŸ“Œ Introduction

The Core Impact Framework (CIF) is an advanced and modular overhaul of Skyrim's impact system, designed to offer fine-grained customization of interactions between weapons, projectiles, and actors in the game. This framework allows for the adjustment, modification, and enrichment of impact effects in a wide variety of situations, making combat more immersive and dynamic. With a flexible system of filters and modifiers, it gives mod creators precise control over how impacts are managed based on context.

Version 2.0 is a complete overhaul of the filter and mapping-selection systems. It splits filtering into three clear axes (Victim, Attacker, HitContext), introduces logical operators (OR / AND / NOR) inside every filter, adds a Trie-based selection engine for much better performance at scale, and exposes a C++ hook system (pre-hit / post-hit / post-deferred-hit) so multiple mods can cooperate cleanly on the same impact event.

πŸ“Œ What's New in 2.0

πŸ”Ή Available Filters

Filters allow for further refinement of impact management based on several criteria:

Thanks to this approach, CIF allows for deep customization, providing a more realistic, varied, and impactful combat experience.

πŸ”Ή Advanced Modifiers

The CIF modifiers offer in-depth customization of impacts with a multitude of options. You can not only adjust visual, sound, and dynamic effects but also influence elements such as blood splatter, objects appearing after a hit, dismemberment, stagger, disarm, or damage multipliers.

Here are some examples of possible modifiers:

These modifiers offer maximum flexibility to make impacts even more immersive and varied.

πŸ“Œ Prerequisites

Before starting this tutorial, and to save time, make sure you have the following skills:

If you're not comfortable with any of these skills or tools, it is recommended to acquire them before following this tutorial.

Great, now that we're all set up, let's dive into using the Core Impact Framework API.

πŸ“Œ Location of JSON Files

The JSON files should be placed in Data/SKSE/CoreImpactFramework. The exact organization of these files doesn't matter as long as they remain in this directory. You are free to organize them in subfolders as you see fit.

Similarly, there are no specific rules for naming the files: you are completely free to name them however you like.

In summary: as long as they are in this folder, everything is fine. The rest is up to you! βœ…

πŸ“Œ Impact Mapping - Structure and Functioning

{
  "ImpactMapping": [
    {
      "Comment": "Replaces the head impact with sparks for heavy armor when struck by ranged weapons, but never against Draugr.",
      "Priority": 10,
      "Filters": {
        "Victim": {
          "Materials": ["Skyrim.esm:0x12F3F"],
          "!Races": ["DraugrRace"]
        },
        "HitContext": {
          "BipedSlots": [31],
          "ArmorClasses": ["Heavy"],
          "WeaponTypes": ["Ranged"],
          "Blocked": ["No"]
        }
      },
      "Modifiers": {
        "ImpactData": "Skyrim.esm:0xF69C4"
      }
    }
  ]
}

ImpactMapping is an array in your JSON file that allows you to define rules for customizing impacts. Each object in this array contains:

This system allows you to adapt impacts based on a multitude of in-game situations.

⚠️ Important: ImpactMapping is strictly intended to replace visual and sound elements related to impacts. It is crucial to adhere to this rule, as once overrides are processed, only a single compiled ImpactMapping will be retained. Misusing this system to apply third-party, non-visual effects may interfere with other mods attempting to display impact visuals, leading to missing or incorrect effects.

For any functionality you wish to implement that does not directly affect visuals or sounds (such as using a spell to launch a script or apply damage, for example), please use Hit Mapping instead. HitMapping is more permissive and supports stacking, allowing multiple entries to be executed together without overwriting each other.

πŸ”Ή Priority - Rule Priority

The Priority field defines the order in which rules in the ImpactMapping are processed. A rule with a higher priority will be applied first and can override those with lower priority. Negative values are allowed.

This system allows for the addition of exceptions and specific behaviors based on the importance of the rules.

πŸ”Ή Filters - Conditions to Validate

When an impact occurs, the Filters are evaluated. All three axes (Victim, Attacker, HitContext) that are present must validate for the Modifiers to be applied.

πŸ“ Important Notes:

πŸ”Ή Modifiers - Actions to Perform

The Modifiers are the actions that will be executed once the Filters have been validated. These actions allow you to replace or enhance the impact effects, adding sound effects, visual effects, or modifying the behavior of the impacts in the game.

The specific details of each Modifier element are covered in the Modifiers section below.

πŸ”Ή Other parameters

Override : When this parameter is enabled (true), the framework continues to process objects even if the filters are valid. The Modifiers of the highest-priority objects will be kept and will override those of subsequent objects. This allows for the merging of multiple rule sets for the same impact event, where the highest-priority Modifiers take precedence.

For example, one set of rules might replace the sound effect, while another sets a new decal to be displayed, all within a single impact event. You can use this parameter as many times as necessary to create layered and varied effects.

OverrideMerge : This parameter, used in conjunction with Override, is less commonly used but allows merging array-type elements (such as ExtraImpactData) with those of lower-level objects. When OverrideMerge is enabled, the array elements will be added to those already present in the subsequent objects, rather than completely replacing them. This allows you to enrich effects without removing existing data, offering more flexibility in customizing impacts.

πŸ“Œ Hit Mapping - Structure and Functioning

{
  "HitMapping": [
    {
      "Comment": "Plays a sound and a slight magical effect upon impact, and staggers the victim.",
      "Priority": 10,
      "Class": "MyCustomEffects",
      "Deferred": true,
      "Filters": {
        "Victim": {
          "Materials": ["Skyrim.esm:0x12F3F"]
        },
        "HitContext": {
          "Blocked": ["No"]
        }
      },
      "Modifiers": {
        "ExtraImpactData": ["Skyrim.esm:0x7331F"],
        "ExtraSound": ["Skyrim.esm:0x51939"],
        "Stagger": 0.5
      }
    },
    {
      "Comment": "When a hit is made to the head, in addition to the previous sound effect, the magical effect is replaced by a new one, resolved once all mods have reacted to the hit.",
      "Priority": 20,
      "Class": "MyCustomEffects",
      "Override": true,
      "Deferred": true,
      "Filters": {
        "Victim": {
          "Materials": ["Skyrim.esm:0x12F3F"]
        },
        "HitContext": {
          "BipedLimbs": ["Head"],
          "Blocked": ["No"]
        }
      },
      "Modifiers": {
        "ExtraImpactData": ["Skyrim.esm:0x105F36"]
      }
    }
  ]
}

HitMapping is an array in your JSON file that defines additional rules for handling impacts, similar in structure to ImpactMapping but intended for different use cases.

While ImpactMapping is strictly reserved for replacing visual and sound effects related to impacts, HitMapping is specifically designed for gameplay-related modifications that do not directly affect visuals or sounds. It supports stacking, allowing multiple entries to coexist and be executed together without overwriting each other.

The elements of HitMapping are:

πŸ”Ή Class - Grouping and Overriding Objects

The Class parameter enables the use of the override system across multiple objects sharing the same class name. When you define a Class for an entry, it allows all objects with the same class name to either override or merge their modifiers depending on the Override setting. This provides greater control and flexibility when you want to apply specific behaviors or effects to a group of similar objects without needing to manually manage each individual entry.

πŸ”Ή Deferred - Delayed Resolution

Setting "Deferred": true on a HitMapping entry tells the framework to resolve and apply that entry's modifiers only after all registered mods have had the opportunity to react to the hit through the Pre-Hit and Post-Hit hooks, instead of immediately.

This is useful when your rule needs to take into account data that other plugins might still be adjusting (for example, a damage value modified by another mod's Post-Hit callback). Deferred entries are resolved via the dedicated RegisterPostDeferredHitCallback hook on the C++ side.

Possible Values: Boolean (default: false)

πŸ”Ή Filters - Conditions to Validate

Filters in HitMapping are identical to those used in ImpactMapping.

Please refer to that section for a detailed explanation of how filters work.

πŸ”Ή Modifiers - Actions to Perform

⚠️ Only specific modifiers are available for HitMapping. Modifiers that directly alter the primary visual or audio effects of an impact are reserved for ImpactMapping and cannot be used globally through HitMapping.

The following modifiers are available for use in HitMapping to customize the impact effects:

πŸ“Œ Filters

Each filter is a condition that must be met for the modifiers to be applied. It's not necessary to declare all filters, only the ones you wish to use should be defined.

πŸ“ Before moving on to the filter list, here are a few clarifications on the use of FormID and EditorID in the JSON:

πŸ”Ή Logical Operators (New in 2.0)

In V1, list-based filters always worked as an OR, with the sole exception of Globales and Conditions, which were always evaluated as an AND. In V2, every filter key can now use one of three prefixes to control this behavior explicitly:

Prefix Logic Meaning
[no prefix] OR At least one element in the list must be valid.
& AND Every element in the list must be valid.
! NOR No element in the list may be valid (exclusion).

For example, "Keywords", "&Keywords", and "!Keywords" can be combined in the same filter block: "must have at least one of these general keywords, must have all of these specific keywords, and must have none of these excluded keywords."

Not every key supports all three prefixes, for example, thresholds like Percentage or MaxHealth are single values and have no OR/AND/NOR variant. The tables below indicate, for each key, which prefixes are supported.

Victim / Attacker Filters

These filters are placed under "Filters": { "Victim": { ... } } and/or "Filters": { "Attacker": { ... } }. Both share the exact same set of keys, Victim evaluates the actor being hit, Attacker evaluates the actor delivering the hit (when known).

Parameter Prefixes Description and Possible Values
Races none, !

Description: Restricts to specific actor races.

Possible Values: EditorID (Race), FormID (Race)

Example:

"Races": ["RaceNord", "Plugin.esp:0x800"]

Keywords none, !, &

Description: Matches keywords on the actor.

Possible Values: EditorID (Keyword), FormID (Keyword)

Example (all humanoid races except the bestial ones):

"Keywords": ["ActorTypeNPC"],
"!Keywords": ["IsBeastRace"]

Skins none, !

Description: Checks if the actor has one of the listed skin armors equipped.

Possible Values: FormID (Armor)

Example:

"Skins": ["Plugin.esp:0x800"]

Materials none, !

Description: Checks the actor's race material type. In most cases, this is the MaterialSkin form (0x12F3F), used for humanoid skin.

Possible Values: FormID (MaterialType)

Example:

"Materials": ["Skyrim.esm:0x12F3F"]

FormID none, !

Description: Filters based on the FormID of the actor, either the placed reference or its base object.

Possible Values: FormID (Actor or ActorBase)

Example:

"FormID": ["Plugin.esp:0x800"]

Sex none, !

Description: Filters based on the actor's biological sex.

Possible Values: Male, Female

Example:

"Sex": ["Female"]

Skeletons none, !

Description: Filters based on the path of the actor's skeleton (case-insensitive), useful for custom races/creature mods that share a common skeleton file without sharing a race.

Possible Values: String (skeleton path)

Example:

"Skeletons": ["Actors\\Canine\\Character Assets Dog\\skeleton.nif"]

⚠️ JSON requires backslashes to be escaped, so write \\ instead of just \.

Worn none, !, &

Description: Checks whether the actor currently has one (or all, with &) of the listed objects equipped.

Possible Values: FormID (any equippable object)

Example:

"Worn": ["Plugin.esp:0x900"]

WornKeywords none, !, &

Description: Matches keywords carried by the actor's currently worn items, rather than the actor itself.

Possible Values: EditorID (Keyword), FormID (Keyword)

Example:

"WornKeywords": ["ArmorHeavy"]

Perks none, !, &

Description: Checks whether the actor has one (or all) of the listed perks, independently of the perk's own conditions.

Possible Values: FormID (Perk)

Example:

"Perks": ["Plugin.esp:0x910"]

Spells none, !, &

Description: Checks whether the actor knows/has one (or all) of the listed spells.

Possible Values: FormID (Spell)

Example:

"Spells": ["Plugin.esp:0x920"]

MagicEffects none, !, &

Description: Checks whether the actor is currently affected by one (or all) of the listed active magic effects.

Possible Values: FormID (Magic Effect)

Example:

"MagicEffects": ["Plugin.esp:0x930"]

πŸ“ Note: Globales and Conditions (perk-based scripted conditions) are only available under HitContext, since they usually need access to the full hit context (Target/Subject) rather than a single actor. See below.

HitContext Filters

These filters are placed under "Filters": { "HitContext": { ... } }. They evaluate everything about the hit itself: weapon, projectile, armor struck, biped location, attack type, and thresholds.

Parameter Prefixes Description and Possible Values
Globales none, &, !

Description: Conditions based on global game variables. Each entry has three elements: the global variable, a comparison operator, and a value.

Possible Values: Array of 3 elements:

  • Global Variable: EditorID, FormID
  • Comparison: ==, != (or <>), <, >, <=, >=
  • Value: Int, Float

Example:

"&Globales": [
  ["My_Custom_GV", "==", 1],
  ["Plugin.esp:0x800", ">=", 100.0]
]

⚠️ In V1, Globales always worked as an AND. In V2, you must explicitly use &Globales to reproduce that behavior; plain Globales is now an OR (Any).

Conditions none, &, !=None

Description: Array of perk FormIDs used purely for their scripted Conditions (entry points are ignored). Target refers to the victim, Subject refers to the attacker.

Possible Values: FormID (Perk)

Example:

"&Conditions": ["Plugin.esp:0x800"]

⚠️ In V1, Conditions always worked as an AND. In V2, use &Conditions to reproduce that behavior; plain Conditions is now an OR (Any).

BipedSlots none, !, &

Description: Filters based on the affected body slot(s). Renamed from BipedSlot in V1 (singular).

⚠️ Requires a proper Biped Mapping for the targeted actor. Vanilla creatures are covered by the mod already.

Possible Values: Int, EditorID (Global Variable), FormID (Global Variable)

Example:

"BipedSlots": [30, 32]

BipedLimbs none, !

Description: Filters based on the biped bone group's limb identifier, as declared with IsLimbEntry in the Biped Mapping.

Possible Values: String

Example:

"BipedLimbs": ["RightArm"]

BipedNodes none, !

Description: Filters based on the exact bone node name that was hit.

Possible Values: String

Example:

"BipedNodes": ["NPC Head [Head]"]

BipedKeys none, !, &

Description: Filters based on the custom bone-group name (key) declared in the Biped Mapping's BipedBones object.

Possible Values: String

Example:

"BipedKeys": ["LegRightLower"]

ArmorClasses none, !

Description: Restricts conditions to certain armor types.

Possible Values: Default, Cloth, Light, Heavy

Example:

"ArmorClasses": ["Light", "Heavy"]

ArmorKeywords none, !, &

Description: Matches keywords against the relevant armor piece.

Possible Values: EditorID (Keyword), FormID (Keyword)

Example:

"ArmorKeywords": ["ArmorHeavy"]

Armors none, !

Description: Checks if the armor piece struck matches any in the provided list.

Possible Values: FormID (Armor)

Example:

"Armors": ["Plugin.esp:0x800"]

WeaponTypes none, !

Description: Filters by weapon type. Renamed from WeaponsType in V1.

Possible Values: OneHandSword, TwoHandSword, OneHandAxe, TwoHandAxe, OneHandMace, TwoHandMace, Dagger, Ranged, Magic, HandToHand, Beast, Other

Example:

"WeaponTypes": ["OneHandSword", "TwoHandSword"]

WeaponKeywords none, !, &

Description: Matches keywords against the weapon involved in the hit.

Possible Values: EditorID (Keyword), FormID (Keyword)

Example:

"WeaponKeywords": ["WeapTypeSword"]

Weapons none, !

Description: Checks if the weapon used matches any in the provided list.

Possible Values: FormID (Weapon)

Example:

"Weapons": ["Plugin.esp:0x800"]

MagicItems none, !

Description: Checks if the spell/scroll/enchantment responsible for the hit matches any in the list. Separated from MagicEffects so you can filter by the whole magic item rather than by its individual effects.

Possible Values: FormID (Spell, Scroll, Enchantment)

Example:

"MagicItems": ["Plugin.esp:0x850"]

Projectiles none, !

Description: Applies conditions based on the projectile used (arrows, bolts, magical missiles, etc.).

Possible Values: FormID (Projectile)

Example:

"Projectiles": ["Plugin.esp:0x800"]

MagicEffects none, !

Description: Checks the magic effect directly responsible for the hit (e.g. the specific effect inside a spell that dealt damage).

Possible Values: FormID (Magic Effect)

Example:

"MagicEffects": ["Plugin.esp:0x860"]

Attacks none, !

Description: Restricts to specific attack types.

Possible Values: Regular, Power, Bash

Example:

"Attacks": ["Regular", "Power"]

Sources none, !

Description: Restricts to the origin of the hit (which "hand" or channel produced it).

Possible Values: RightHand, LeftHand, DualHand, Shout, Other

Example:

"Sources": ["RightHand", "DualHand"]

Blocked none, !

Description: Determines whether the attack was blocked, and how.

Possible Values: No, ShieldLight, ShieldHeavy, Weapon

Example:

"Blocked": ["ShieldLight", "ShieldHeavy"]

States none, !

Description: Filters based on the victim's state.

Possible Values: Alive, Dying, Killmove, Dead

Example:

"States": ["Dying", "Killmove"]

Critical none, !

Description: Filters based on whether the hit was a critical attack. Renamed from CriticalAttack in V1.

Possible Values: Yes, No

Example:

"Critical": ["Yes"]

Sneak none, !

Description: Filters based on whether the hit was delivered while sneaking. Renamed from SneakAttack in V1.

Possible Values: Yes, No

Example:

"Sneak": ["No"]

Percentage single value

Description: Chance for the condition to be valid. A random number between 0 and 100 is rolled; if lower than this value, the filter validates.

Possible Values: Float, EditorID/FormID (Global Variable)

Example:

"Percentage": 25.0

PercentageMult single value

Description: A multiplier applied on top of Percentage before the roll, letting you scale the chance dynamically (e.g. from a perk rank or difficulty global) without rewriting Percentage itself.

Possible Values: Float, EditorID/FormID (Global Variable) (default: 1.0)

Example:

"PercentageMult": "Plugin.esp:0x870"

MaxHealth single value

Description: Percentage of the victim's maximum health. Valid as long as current health is below this value.

Possible Values: Float, EditorID/FormID (Global Variable)

Example:

"MaxHealth": 80.0

MinDamage single value

Description: Minimum percentage of health that must be removed by the attack for the filter to validate.

Possible Values: Float, EditorID/FormID (Global Variable)

Example:

"MinDamage": 20.0

MaxLimbHealth single value

Description: Same idea as MaxHealth, but evaluated against the remaining health of the specific limb that was hit rather than the actor's overall health. Useful for progressive limb damage/dismemberment effects.

Possible Values: Float, EditorID/FormID (Global Variable)

Example:

"MaxLimbHealth": 30.0

πŸ“Œ Modifiers

Once the filters are validated, the following modifiers may be applied.

Parameter Description and Possible Values
ImpactData

Description: The primary impact effect applied upon hit, replacing the game's original effect.

Possible Values: FormID (Impact Data)

Example:

"ImpactData": "Plugin.esp:0x800"

⚠️ This modifier is only available for ImpactMapping.

BloodSpray

Description: This spell is cast toward the direction of the blood spray and is used to generate visual gore effects upon impact. Each Magic Effect in the spell must have a projectile assigned, as each one triggers an individual blood spray.

The first effect is launched at full velocity, while subsequent ones use reduced speed to simulate realistic blood physics. The framework automatically adjusts the velocity, so there's no need to modify the projectiles properties yourself.

Possible Values: FormID (Spell)

Example:

"BloodSpray": "Plugin.esp:0x800"

Bloodpool

Description: Array of blood pool / ground decal effects spawned at the exact node corresponding to the impacted limb.

Possible Values: Array of Bloodpool ProfileID

Example:

"Bloodpool": ["MyBloodpoolProfile"]

⚠️ The Dynamic Bloodpool Framework is required.
See also, DBF API Documentation

ExtraImpactData

Description: An array of additional impact effects applied upon hit. Each effect in the array will be played individually, but no sound or decal will be applied. Accepts either a FormID (Impact Data) or a direct .nif path.

Possible Values: Array of FormID (Impact Data) or String (.nif path)

Example:

"ExtraImpactData": ["Plugin.esp:0x801", "Meshes\\myeffect.nif"]

ExtraSound

Description: An array of additional sounds played upon hit.

Possible Values: Array of FormID (Sound Descriptor)

Example:

"ExtraSound": ["Plugin.esp:0x900", "Plugin.esp:0x901"]

PreserveOriginalSound

Description: Whether to keep the original impact sound in addition to the overridden one. This is only relevant if ImpactData is specified.

Possible Values: Boolean

Example:

"PreserveOriginalSound": true

⚠️ This modifier is only available for ImpactMapping.

PreserveOriginalDecal

Description: Whether to keep the original impact decal in addition to the overridden one. This is only relevant if ImpactData is specified.

Possible Values: Boolean

Example:

"PreserveOriginalDecal": true

⚠️ This modifier is only available for ImpactMapping.

SoundOverride

Description: Sound effect that overrides the default impact sound, taking precedence over PreserveOriginalSound.

Possible Values: FormID (Descriptor Form)

Example:

"SoundOverride": "Plugin.esp:0x800"

⚠️ This modifier is only available for ImpactMapping.

DecalOverride

Description: Decal effect that overrides the default impact decal.

Possible Values: FormID (Impact Data)

Example:

"DecalOverride": "Plugin.esp:0x800"

⚠️ This modifier is only available for ImpactMapping.

RemoveDecal

Description: Whether to remove the impact decal entirely. This takes precedence over all other settings related to decals.

Possible Values: Boolean

Example:

"RemoveDecal": false

⚠️ This modifier is only available for ImpactMapping.

RemoveSound

Description: Whether to remove the impact sound entirely. This takes precedence over all other settings related to sound.

Possible Values: Boolean

Example:

"RemoveSound": true

⚠️ This modifier is only available for ImpactMapping.

RemoveBloodSplatter

Description: Determines whether to force the game to disable default blood splatter generation on impact.

πŸ“ Note: This does not affect dynamic blood sprays.

Possible Values: Boolean

Example:

"RemoveBloodSplatter": true

⚠️ This modifier is only available for ImpactMapping.

ImpactBounce

Description: Whether to allow physical projectiles, such as arrows, to bounce upon impact.

Possible Values: Boolean

Example:

"ImpactBounce": true

⚠️ This modifier is only available for ImpactMapping.

Stagger

Description: Applies an additional stagger effect to the victim on hit.

Possible Values: Float, EditorID/FormID (Global Variable)

Notes:

  • Values between 0.0 and 1.0 apply a standard stagger, where 1.0 is the strongest possible stagger.
  • Values greater than 1.0 apply a Havok impulse instead, with 1.0 being the minimum impulse strength.

Example:

"Stagger": 0.75
Disarm

Description: Whether the hit should disarm the victim.

Possible Values: Boolean

Note: If the victim is blocking, their equipped weapon or shield will be disarmed with priority.

Example:

"Disarm": true
Eject

Description: Whether the victim's equipped item associated with the impacted body part should be ejected.

Possible Values: Boolean

Notes:

  • The equipped item is determined using the BipedMappings configuration.
  • For example, a hit to the head can eject the victim's helmet.

Example:

"Eject": true
DamageMult

Description: Multiplier applied to the overall damage of the hit.

Possible Values: Float, EditorID/FormID (Global Variable) (default: 1.0)

Example:

"DamageMult": 1.5

⚠️ This modifier does not work when the Deferred parameter is set to true.

Spells

Description: Array of spells cast when the modifier is triggered. The spells will be applied immediately, with the target being the actor hit and the subject being the one responsible for the hit.

Possible Values: Array of FormID (Spell)

Example:

"Spells": ["Plugin.esp:0x800", "Plugin.esp:0x801"]

PlacedObjects

Description: Array of objects placed in the world upon hit. These can be any visible object in the world. Most often, you will want to use explosions. The object will be placed precisely at the impact point.

Possible Values: Array of FormID (Any Form)

Example:

"PlacedObjects": ["Plugin.esp:0x800", "Plugin.esp:0x801"]

πŸ“Œ Biped Mapping (For new races/creature only)

This section explains how to define the nodes associated with biped slots. These definitions enable the use of certain filters and features (BipedSlots, BipedNodes, BipedLimbs, BipedKeys). In most cases, only the biped slots of NPCs capable of wearing armor will be used, but nothing prevents you from creating your own mapping for specific custom creatures.

In V2, each bone group is declared under its own name (the "key"), and carries its own Priority, list of BipedSlots, and an IsLimbEntry flag, instead of a single node array per raw slot number as in V1.

Example: The following configuration shows how to define the mapping for Draugr.

{
  "Filters": {
    "Skeletons": ["Actors\\Draugr\\Character Assets\\Skeleton.nif", "Actors\\Draugr\\Character Assets\\SkeletonS.nif", "Actors\\Draugr\\Character Assets\\SkeletonF.nif"]
  },
  "BipedBones": {
    "Head": {
      "BipedSlots": [30, 31, 41, 42, 43],
      "Nodes": ["NPC Head [Head]", "NPC Neck [Neck]"],
      "IsLimbEntry": true,
      "Priority": 10
    },
    "Torso": {
      "BipedSlots": [32],
      "Nodes": ["NPC Spine [Spn0]", "NPC Spine1 [Spn1]", "NPC Spine2 [Spn2]", "NPC COM [COM ]"],
      "IsLimbEntry": true,
      "Priority": 10
    },
    "ArmLeftUpper": {
      "BipedSlots": [33],
      "Nodes": ["NPC L UpperArm [LUar]"],
      "Priority": 20
    },
    "ArmLeftLower": {
      "BipedSlots": [33],
      "Nodes": ["NPC L Forearm [LLar]", "NPC L Hand [LHnd]"],
      "Priority": 30
    },
    "ArmLeft": {
      "BipedSlots": [33],
      "Nodes": ["NPC L UpperArm [LUar]", "NPC L Forearm [LLar]", "NPC L Hand [LHnd]"],
      "IsLimbEntry": true,
      "Priority": 10
    },
    "ArmRightUpper": {
      "BipedSlots": [33],
      "Nodes": ["NPC R UpperArm [RUar]"],
      "Priority": 20
    },
    "ArmRightLower": {
      "BipedSlots": [33],
      "Nodes": ["NPC R Forearm [RLar]", "NPC R Hand [RHnd]"],
      "Priority": 30
    },
    "ArmRight": {
      "BipedSlots": [33],
      "Nodes": ["NPC R UpperArm [RUar]", "NPC R Forearm [RLar]", "NPC R Hand [RHnd]"],
      "IsLimbEntry": true,
      "Priority": 10
    },
    "LegLeftUpper": {
      "BipedSlots": [37],
      "Nodes": ["NPC L Thigh [LThg]"],
      "Priority": 20
    },
    "LegLeftLower": {
      "BipedSlots": [37],
      "Nodes": ["NPC L Calf [LClf]", "NPC L Foot [LLft ]"],
      "Priority": 30
    },
    "LegLeft": {
      "BipedSlots": [37],
      "Nodes": ["NPC L Thigh [LThg]", "NPC L Calf [LClf]", "NPC L Foot [LLft ]"],
      "IsLimbEntry": true,
      "Priority": 10
    },
    "LegRightUpper": {
      "BipedSlots": [37],
      "Nodes": ["NPC R Thigh [RThg]"],
      "Priority": 20
    },
    "LegRightLower": {
      "BipedSlots": [37],
      "Nodes": ["NPC R Calf [RClf]", "NPC R Foot [Rft ]"],
      "Priority": 30
    },
    "LegRight": {
      "BipedSlots": [37],
      "Nodes": ["NPC R Thigh [RThg]", "NPC R Calf [RClf]", "NPC R Foot [Rft ]"],
      "IsLimbEntry": true,
      "Priority": 10
    }
  }
}

The table below shows the properties related to Biped Mapping. The priority system works the same way as for Impact Mapping, and the filters work in the same way as Victim filters above.

Priority

Description: The priority of the biped mapping entry itself. Higher values will be applied first. Negative values can also be used.

Possible Values: Integer

Example:

"Priority": 10

Filters

Description: Filters that define the characters this biped mapping will apply to. Uses the same keys and logical-operator prefixes (Races, Keywords, Skins, Materials, FormID, Conditions, Sex, Skeletons...) as the Victim filters described above.

Example:

"Filters": {
  "Races": ["RaceDarkElf", "RaceWoodElf"],
  "&Keywords": ["ActorTypeNPC"]
}

BipedBones

Description: An object whose keys are free-form group names (the "biped key", usable with the BipedKeys filter) and whose values describe one bone group each:

  • Priority: order of this group relative to other overlapping groups on the same actor.
  • BipedSlots: array of biped slot integers this group represents. Biped slots are listed in the Creation Kit when creating a piece of armor.
  • Nodes: array of node names for this group. These are short character strings found in the "skeleton.nif" file of the targeted race(s). You can open and analyze these files using NifSkope.
  • IsLimbEntry: marks whether this group represents a unique "limb", exposing it to the BipedLimbs filter and to the limbEntriesOnly parameter of the C++ API's GetBipedBonesMap.

Example:

"BipedBones": {
  "Head": {
    "Priority": 0,
    "IsLimbEntry": true,
    "BipedSlots": [30, 31, 41, 42, 43],
    "Nodes": ["NPC Head [Head]", "NPC Neck [Neck]"]
  }
}

πŸ“Œ Legacy Conversion (V1 β†’ V2)

At startup, CIF 2.0 automatically converts legacy V1 filters into the new V2 format in memory. Nothing is written back to disk, ensuring full compatibility with existing V1 JSON files.

Your existing V1 filters will be transparently converted to their equivalent V2 structure and used normally by the framework.

πŸ“Œ C++ API (ModAPI.h)

πŸ“„ Download the header file: API.h

CIF 2.0 exposes a native C++ API for other SKSE plugins, distributed as a single header (ModAPI.h) to include in your project. It lets your plugin: read/write CIF's own INI settings, resolve an actor's Biped Mapping, build a RuntimeHitContext on demand, trigger a CIF-compatible blood spray, and, most importantly, hook into the hit pipeline itself.

πŸ”Ή Retrieving the API

Include ModAPI.h in your project and request the interface once CIF has finished initializing (typically on kPostLoadGame / kNewGame):

SKSE::GetMessagingInterface()->RegisterListener([](MessagingInterface::Message* message) 
{
    switch (message->type) 
    {
        case MessagingInterface::kPostLoadGame:
        case MessagingInterface::kNewGame:
        {
            if (auto* api = static_cast<CIF_API::Interface*>(CIF_API::GetAPI())) {
                logger::info("Core Impact Framework API v{} registered successfully.", api->GetVersion().string("."));
            } else {
                logger::warn("Core Impact Framework API not found.");
            }
        }
        break;
    }
});

CIF_API::GetAPI() locates CoreImpactFramework.dll, requests the interface through its exported RequestPluginAPI function, and caches the pointer for subsequent calls. It returns nullptr if CIF isn't installed or hasn't loaded yet.

πŸ”Ή Interface Methods

Method Description
GetVersion() Returns the running CIF version as a REL::Version.
GetIniValue(key_section, defaultValue) Reads a value from CIF's own INI settings. Returns an IniValue (std::variant<bool, int, float, std::string>), or defaultValue if not found.
SetIniValue(key_section, value) Overrides a CIF INI setting at runtime. Returns true on success.
GetBipedBonesMap(actor, limbEntriesOnly) Resolves and returns the BipedBonesMap (unordered_map<string, BipedBonesEntry>) applicable to the given actor, i.e. the compiled result of the JSON Biped Mapping. Set limbEntriesOnly to true to only get groups declared with IsLimbEntry: true.
GenerateContext(victim, attacker = nullptr) Builds and returns a full RuntimeHitContext for the given pair of actors right now, without requiring an actual hit event. Useful to preview what CIF would resolve for a given situation.
GetBloodCollisionLayer() Returns the RE::BGSCollisionLayer* used by CIF's own dynamic blood spray projectiles, so your own projectiles can share it.
CastBloodSpray(caster, spell, position, direction, power, spread) Manually triggers a CIF-compatible blood spray spell at a given world position/direction, with optional power and spread overrides.
RegisterPreHitCallback(eventName, priority, callback) Registers a callback invoked before CIF evaluates its filters, with a mutable reference to the vanilla RE::HitData. See Hooks below.
RegisterPostHitCallback(eventName, priority, callback) Registers a callback invoked right after CIF has resolved a hit (immediate HitMapping/ImpactMapping entries), with the resulting RuntimeHitContext.
RegisterPostDeferredHitCallback(eventName, priority, callback) Same as above, but fired after all Deferred HitMapping entries have also been resolved.

πŸ“ Registering a callback with an eventName that is already in use replaces the previous registration under that name for that hook. Within a hook, callbacks run in descending priority order (higher first), the exact same convention used by the Priority field in the JSON mappings.

πŸ”Ή Hooks - Pre-Hit, Post-Hit, Post-Deferred-Hit

These three hooks are the backbone of cross-mod cooperation in CIF 2.0. Instead of every mod hooking the game's hit pipeline independently and risking conflicts, they register with CIF and get called in a single, priority-ordered sequence:

  1. Pre-Hit: fired first, with a mutable RE::HitData&, before CIF's own filters run. This is the place to alter the raw hit before anything is evaluated.
  2. CIF evaluates its ImpactMapping/HitMapping (non-deferred entries) and applies the resulting modifiers.
  3. Post-Hit: fired with the resulting RuntimeHitContext (read-only, passed by value), reflecting the immediate resolution.
  4. Any Deferred HitMapping entries are resolved.
  5. Post-Deferred-Hit: fired last, with the final RuntimeHitContext once every mod has had a chance to react.
using namespace CIF_API;

if (auto* api = static_cast<Interface*>(GetAPI())) {

    api->RegisterPreHitCallback("MyMod_PreHit", 0, [](const std::string eventName, RE::HitData& hitData) {
        // Inspect or tweak the raw hit before CIF's filters run.
    });

    api->RegisterPostHitCallback("MyMod_PostHit", 0, [](const std::string eventName, const Interface::RuntimeHitContext context) {
        if (context.state == Interface::Filter::StateFilter::kDead) {
            // React to a killing blow, read context.bipedEntry, context.damageCalc, etc.
        }
    });

    api->RegisterPostDeferredHitCallback("MyMod_PostDeferredHit", 0, [](const std::string eventName, const Interface::RuntimeHitContext context) {
        // Final, fully-resolved context - safe place for dismemberment/localized-damage mods to act.
    });
}

πŸ”Ή RuntimeHitContext

This structure summarizes everything CIF knows about a hit at the point the hook fires. It mirrors the JSON filter axes: victim-side fields, attacker-side fields, hit-context fields, and the resulting modifiers.

Group Fields
Victim victimFormID, victimBaseFormID, victimRaceFormID, victimMaterialFormID, victimSkinFormID, victimWornFormIDs, victimWornKeywordsFormIDs, victimKeywordsFormIDs, victimPerksFormIDs, victimSpellsFormIDs, victimMagicEffectsFormIDs, victimSex
Attacker attackerFormID, attackerBaseFormID, attackerRaceFormID, attackerMaterialFormID, attackerSkinFormID, attackerWornFormIDs, attackerWornKeywordsFormIDs, attackerKeywordsFormIDs, attackerPerksFormIDs, attackerSpellsFormIDs, attackerMagicEffectsFormIDs, attackerSex
HitContext weaponFormID, weaponKeywordsFormIDs, magicItemFormID, projectileFormID, magicEffectFormID, armorFormID, armorKeywordsFormIDs, weaponType, armorClass, blocked, state, attack, source, criticalAttack, sneakAttack, percentHealth, percentDamage, limbHealth, bipedEntry, hitPosition, hitDirection
Modifiers damageMult, damageCalc, damageLimbMult

bipedEntry is a BipedEntry structure (bipedNode, bipedLimb, bipedKeys, bipedSlots) describing exactly which node/limb/group was struck, the runtime equivalent of the BipedNodes / BipedLimbs / BipedKeys / BipedSlots filters.

All the enums used by these fields (Filter::WeaponType, Filter::ArmorClassType, Filter::BlockedFilter, Filter::StateFilter, Filter::AttackFilter, Filter::SourceFilter, Filter::CriticalAttackFilter, Filter::SneakAttackFilter, Filter::ActorSex) live under CIF_API::Interface_V1::Filter and map 1:1 to the possible values documented in the Filters section above.

πŸ“Œ Testing in the Game

After configuring your JSON files, it is crucial to test your changes in the game to ensure they work as intended. If you encounter a crash when launching the game, it likely means that the structure of one of your JSON files is malformed (possibly an extra comma). Feel free to use tools like JSON Formatter to validate your JSON files or find potential errors.

Once the game is running, be sure to check the CoreImpactFramework.log file (located in Documents\My Games\Skyrim Special Edition\SKSE) for any errors or issues with your JSON files, including the legacy-conversion log lines mentioned in the Legacy Conversion section. Regularly check this file during your testing to identify and quickly correct errors. This will help you refine your configuration and ensure smooth integration into the game.