Fixed: renaming a reaction didn't update its trigger action. The trigger picker kept showing the old name until you reloaded the scene, and after a reload, any trigger wired to the old name broke.
Renamed reactions now show up in the trigger picker right away under the new name, and the old name is removed cleanly.
No more duplicate reaction names
Fixed: two reactions could end up with the same name (for example, adding a new one after deleting one from the middle of the list). When that happened, one of them silently disappeared from the trigger picker.
Every reaction name is now kept unique, whether it's added, pasted, renamed or loaded, and a number is added automatically if needed ("Reaction 3 2", "Hit Copy 2").
Switched from PC EA (early access ending Sep 16, 2026, with CC BY-ND as secondary) to plain CC BY-ND.
New features
Hold phase — new per-reaction Hold Duration (s) slider (0–5). The force now stays at peak for that time between jerk and return, so the body parks in a displaced pose instead of immediately recoiling. 0 (the default) skips the hold entirely, so old presets behave exactly as before.
Active toggle — master on/off bool on the left panel. When off, all incoming fire calls are ignored (reactions already in flight finish normally). It's a registered storable, so scene triggers can arm/disarm the whole plugin. Defaults ON for backward compatibility.
Global Cooldown (s) slider (0–30) — minimum time between any two fires across all reactions, separate from the per-reaction cooldown. Only a fire that actually starts stamps the timer; a call rejected for another reason (disabled reaction, per-reaction cooldown, no valid nodes) doesn't begin a cooldown.
Random groups (Timeline convention) — name reactions like flinch/left, flinch/right, flinch/duck and a single extra trigger action flinch/* appears that fires one of them at random. Only groups with 2+ members get the action; disabled members are skipped; it never repeats the last-fired reaction back-to-back. Group actions are rebuilt whenever reaction names change.
Transition Actions are now driven — the trigger's Transition tab (previously unused) receives the reaction's 0→1→0 envelope every physics tick: 0 at rest, 1 at peak jerk, pinned at 1 during hold, back to 0 on return. Any float target (gaze offsets, morphs, other plugins' params) rides the same easing curve as the body. This came out of the AutoThrusterPlus session where a gaze plugin owning the head was fighting SuddenMovements — now you drive the gaze plugin's own parameters instead of the controller.
Fixes / improvements
Test Trigger button now bypasses the Active gate and Global Cooldown, so you can preview while disarmed without consuming the scene's cooldown.
Reaction.Fire() returns a bool (whether it actually started) so the caller can distinguish a real fire from a rejected one.
Transition targets are settled to 0 before End Actions fire and on StopImmediate(), so driven values land back at their start.
Copy/Paste reaction now preserves the trigger (Start/End/Transition actions) instead of stripping it; restoration is deferred a frame via the same path scene loads use.
Copies are named "<name> Copy", "<name> Copy 2", … (appended) instead of "Copy of <name>" (prepended), so a copy of flinch/left stays in the flinch random group.
Save Preset now uses GetJSON(true, true, true) (forceStore) so storables sitting at their default value are still written — presets are self-contained rather than dependent on current defaults.
Null-atom path in Node.Resolve() now also clears ctrl, not just rb.
holdDuration is serialized to/from JSON and defaults to 0 when loading older reactions.
Help text expanded (height 1000 → 1400) with new sections: Master Controls, Random Groups, Triggers, and "Using Transitions With Other Plugins".
Switched from PC EA (early access ending Sep 16, 2026, with CC BY-ND as secondary) to plain CC BY-ND.
New features
Hold phase — new per-reaction Hold Duration (s) slider (0–5). The force now stays at peak for that time between jerk and return, so the body parks in a displaced pose instead of immediately recoiling. 0 (the default) skips the hold entirely, so old presets behave exactly as before.
Active toggle — master on/off bool on the left panel. When off, all incoming fire calls are ignored (reactions already in flight finish normally). It's a registered storable, so scene triggers can arm/disarm the whole plugin. Defaults ON for backward compatibility.
Global Cooldown (s) slider (0–30) — minimum time between any two fires across all reactions, separate from the per-reaction cooldown. Only a fire that actually starts stamps the timer; a call rejected for another reason (disabled reaction, per-reaction cooldown, no valid nodes) doesn't begin a cooldown.
Random groups (Timeline convention) — name reactions like flinch/left, flinch/right, flinch/duck and a single extra trigger action flinch/* appears that fires one of them at random. Only groups with 2+ members get the action; disabled members are skipped; it never repeats the last-fired reaction back-to-back. Group actions are rebuilt whenever reaction names change.
Transition Actions are now driven — the trigger's Transition tab (previously unused) receives the reaction's 0→1→0 envelope every physics tick: 0 at rest, 1 at peak jerk, pinned at 1 during hold, back to 0 on return. Any float target (gaze offsets, morphs, other plugins' params) rides the same easing curve as the body. This came out of the AutoThrusterPlus session where a gaze plugin owning the head was fighting SuddenMovements — now you drive the gaze plugin's own parameters instead of the controller.
Fixes / improvements
Test Trigger button now bypasses the Active gate and Global Cooldown, so you can preview while disarmed without consuming the scene's cooldown.
Reaction.Fire() returns a bool (whether it actually started) so the caller can distinguish a real fire from a rejected one.
Transition targets are settled to 0 before End Actions fire and on StopImmediate(), so driven values land back at their start.
Copy/Paste reaction now preserves the trigger (Start/End/Transition actions) instead of stripping it; restoration is deferred a frame via the same path scene loads use.
Copies are named "<name> Copy", "<name> Copy 2", … (appended) instead of "Copy of <name>" (prepended), so a copy of flinch/left stays in the flinch random group.
Save Preset now uses GetJSON(true, true, true) (forceStore) so storables sitting at their default value are still written — presets are self-contained rather than dependent on current defaults.
Null-atom path in Node.Resolve() now also clears ctrl, not just rb.
holdDuration is serialized to/from JSON and defaults to 0 when loading older reactions.
Help text expanded (height 1000 → 1400) with new sections: Master Controls, Random Groups, Triggers, and "Using Transitions With Other Plugins".