AggroKit
Reusable aggro/threat control tooling for Outward BepInEx mods: per-pair targetability gates, detection veto/redirect, taunt and force-target primitives. Inert until a mod uses it — a library dependency, installed automatically.
| Date uploaded | 3 days ago |
| Version | 0.1.3 |
| Download link | CeruleanCutlass-AggroKit-0.1.3.zip |
| Downloads | 48 |
| Dependency string | CeruleanCutlass-AggroKit-0.1.3 |
This mod requires the following mods to function
CeruleanCutlass-ForgeKit
Dependency-free dev-tooling for Outward BepInEx mods: file-driven dev command loop, self-test harness, on-screen toasts, player-ready lifecycle wait, embedded/override table loaders, and a shared dev-verb pack (movement/combat/skill/status probes).
Preferred version: 0.4.3README
AggroKit
📖 Full documentation: AggroKit wiki page
Reusable aggro/threat-control tooling for people writing BepInEx 5 mods for Outward: Definitive Edition. It doesn't add anything a player sees on its own — it's a library dependency other mods (CompanionKit, and anything built on top of it) pull in, and other modders are welcome to build on it too.
Requires: BepInEx 5 (Outward's Mono branch — see Compatibility below) + ForgeKit (hard dependency, installed automatically).
What's in it
| Piece | What it gives you |
|---|---|
AggroTools |
Patch-free primitives for reading/adjusting engine aggro state without a Harmony patch of your own. |
TargetableOverrides |
A per-pair targetability gate — make one specific character untargetable to one specific other character, without touching the game's global targeting rules. |
AggroEvents |
Observability hooks (opt-in, off by default) plus an opt-in squad-self-target guard (also off by default — see BlockSquadSelfTarget). |
Installing (for players)
Drop the AggroKit folder into BepInEx/plugins/. It has no effect by itself — install it
because another mod you're using declares it as a dependency.
Configuration
BepInEx/config/cobalt.aggrokit.cfg (generated on first launch):
[Dev]
## Poll BepInEx/config/ak_cmd.txt for dev commands (aggrodump/forcetarget/taunt/calm/...).
EnableCommandFile = true
## Whether DumpKey logs an aggro dump of all AI characters near the player.
EnableDumpKey = true
## Key that logs an aggro dump of all AI characters near the player (needs EnableDumpKey=true).
# Default value: F3
DumpKey = F3
[Research]
## Re-arm AggroEvents' purely-observational Harmony taps that record the event buffer. OFF by
## default so these hot AI-tick paths stay un-patched. With EnablePatches on, the detection-side
## control patches and the targetability override gate are installed regardless of this key — they're
## inert until a verb activates them.
EnableObservation = false
[Fixes]
## Master switch for EVERY Harmony patch AggroKit installs. Off = it patches nothing at all, while
## the library calls and the ak_cmd.txt verbs keep working — so AggroKit can be ruled in or out of a
## mod-conflict bisection without uninstalling it. Applied at load; changing it needs a relaunch.
EnablePatches = true
## Opt-in fix for a squad-contagion self-target assignment in AISCombat.SetPreferredTarget.
## DEFAULT OFF: the case is unreachable in vanilla, and the block is a skipping Harmony prefix, which
## also suppresses any other mod's prefix on the same method. Read live — no relaunch needed.
BlockSquadSelfTarget = false
Dev verbs
AggroKit polls BepInEx/config/ak_cmd.txt (write a line, it runs on the next poll — works while
the game is paused). [Dev] EnableCommandFile = false turns the channel off.
| Verb | What it does |
|---|---|
aggrodump [radius] |
Aggro dump of every AI near the player, plus the player's targetability state |
aistates [name|nearest] |
Dump one AI's state machine |
aggrolog [n|on|off|clear] |
The [AGGRO] event buffer |
watch <name|nearest|off> |
Watch an AI's (state, target) changes |
forcetarget [name|nearest] |
Force an AI onto the player, and auto-watch it |
feud <A> <B> [nooverride|both] |
Make any two AIs fight each other |
taunt [radius] / calm [radius] |
Pull every AI in radius onto the player / calm them |
untargetable / targetable |
Toggle the player's QuestNonTargetable |
undetectable / detectable |
Toggle the player's DetectabilityMult |
shieldme / unshieldme |
Toggle incoming-aggro shielding on the player |
stealthme [off] |
Veto AI detection of the player |
decoy <name>|off |
Redirect detections of the player onto another AI |
noaggro [off] |
The player's hits generate no aggro |
clearoverrides |
Clear all targetability overrides |
status |
One glance: player flags, overrides, controls, watches, aggrolog |
restore |
Undo everything — targetable + detectable, all overrides/controls/watches cleared |
selftest |
Zero-interaction environment checks ([SELFTEST] PASS/FAIL … DONE) |
The [Dev] DumpKey bind (default F3) runs aggrodump without the channel. Keys are a
cross-mod resource in this workspace — AggroKit claims its bind through ForgeKit.Keybinds so a
collision with another mod is reported rather than silently firing both.
Using it (for modders)
<!-- your .csproj -->
<ProjectReference Include="path\to\AggroKit\AggroKit.csproj" Private="false" />
[BepInPlugin(GUID, NAME, VERSION)]
[BepInDependency(AggroKit.Plugin.GUID)]
public class Plugin : BaseUnityPlugin
{
// AggroTools / TargetableOverrides / AggroEvents are static entry points —
// call them directly once AggroKit has loaded (declare it as a dependency above).
}
Private="false" matters — it stops MSBuild from copying a second AggroKit.dll into your
mod's own output folder. The kit ships from its own BepInEx/plugins/AggroKit/ folder; your mod
just references it and declares the dependency so BepInEx loads it first.
Compatibility
Outward must be on its Mono Steam beta branch, not the default IL2CPP build — like every
BepInEx 5 mod for this game. If your game runs but no BepInEx mods load and there's no crash log,
this is almost always why (Properties → Betas → select mono in Steam).
License
Apache License 2.0 — see LICENSE in the repository root. You may use, modify, and redistribute
this kit (including in commercial mods) provided you keep the copyright/license notice; see the
license text for the full terms.
CHANGELOG
AggroKit changelog
0.1.3 — 2026-08-19
- Merge branch 'fix/sa-0815-leafkits' into feature/pet-self-feed
- Static-analysis wave F3c: leaf-kit fixes (A7-1/2/4/8/9/13/14/15/16/20/23/24/25/27)
- Test debt (F4): model every project in the dependency gate, AggroKit's first tests, UsageSpec corpus coverage
- Coercion wave: every ForgeKit integration completes cleanly through the TUI seam
- Forge shell fixes: SSH commands via bash -c (fish login shell); set/cfgdump on every channel mod
- Forge shell: catalog dump + response protocol + set/cfgdump in ForgeKit; forge CLI/REPL + completion packs; wiki-enriched name db
0.1.2 — 2026-08-11
Prompted by a player report that bisected a vanilla-content bug down to AggroKit. The report did not hold up — AggroKit patches nothing on the path it was blamed for — but answering it took five passes over the decompiled game, because AggroKit was effectively unfalsifiable in the field. This release is about that, plus the one real defect the investigation found.
[Fixes] BlockSquadSelfTargetnow defaults OFF. It was the mod's only unconditional change to vanilla AI, and it is implemented as a skipping Harmony prefix — which under HarmonyX suppresses every other mod's prefix onAISCombat.SetPreferredTargetas well as the original. The case it guards is unreachable in vanilla (both callers filter to allies, and an ally is never a valid target), so it was paying a cross-mod cost for a benefit that cannot be demonstrated. Still available for anyone who actually hits the fake-combat stall.- New
[Fixes] EnablePatchesmaster switch (default on). Disables every Harmony patch AggroKit installs while keeping the library primitives and theak_cmd.txtdev verbs working — so AggroKit can be ruled in or out of a mod-conflict bisection without uninstalling it. Five patch classes previously had no gate at all. - AggroKit now says what it patched. A boot-time census of the vanilla methods actually patched
(read back from the Harmony instance, not a hand-maintained list) plus the resolved gate values,
both also surfaced by the
statusverb. - The self-target block reports when it fires, bounded per session — previously it was silent unless the dev-only ring buffer was enabled, which it never is in a shipped install.
HarmonyPriority(Priority.Last)on the three prefixes that can skip the original, so when AggroKit suppresses a method, other mods' patches have already had their say.- The
IsTargetablepostfix — the one patch body doing real work with notry/catch— now routes throws to the same boundedSwallowedreporter as every other patch body. - The self-test is now gate-aware: it asserts each patch's installed state matches its config
rather than asserting it is installed, so
EnablePatches = falseno longer reads as a failure. It also cross-checks the census against the gates. - Docs: the README, wiki page and Thunderstore description no longer describe an "always-on" fix,
and both now document
EnablePatches.
Also in this release:
- AggroKit: trim manifest description to Thunderstore's 250-char limit
- Merge branch 'main' into fix/sa-0808-kits
- Static-analysis 2026-08-08 wave D-kits: P2-13/P2-15 + the kit P3 group
- Static-analysis 2026-08-08 wave 0: nine P1/P2 fixes
- W4: fixes for everything D1 found, plus the join-race P1
- AggroKit: fake-null cleanup for the aggro taps and taunt forensics
- A1: the fake-null accessors, and every Plugin.cs cleared
- A0 pilot: the fake-null rewrite recipe, proven on one file
0.1.1 — 2026-08-02
- Logging: answer "why did nothing happen?" without needing LogVerbose on in advance
- Cleanup: one anchor-collect loop, one ownership side-table
- Docs sweep: archive, condense, and validate the whole documentation tree