SkillKit

v0.1.6·Updated 2026-09-06

Ship a learnable custom skill for Outward without rediscovering the traps: SideLoader pack-load wiring, effect-bucket routing, the stuck-cast guard, dynamic quickslot icons, native cast sync. Requires

About this mod Author’s original text

SkillKit — ship a learnable custom skill without rediscovering the traps

📖 Full documentation: SkillKit wiki page

A BepInEx library plugin for Outward (Definitive Edition, Mono branch) that owns the mechanism

of shipping a custom skill: SideLoader pack-load wiring, effect-bucket routing, the stuck-cast

guard, dynamic multi-state quickslot icons, weapon-dependent native cast animation, learn helper,

and convergent re-stamping. Consumers own the content: the SL_Skill XML, the ItemID block, the

icon PNGs, and what the skill actually does.

Extracted from Beastwhispering 2026-07-06 (plan + evidence trail: docs/skilltoolkit-plan.md).

Requires: BepInEx 5 (BepInExPack_Outward) and ForgeKit — the only declared

[BepInDependency], and a hard one. SideLoader must be present at runtime (SkillKit does

nothing without it) but is deliberately not a [BepInDependency]: it's a compile reference only,

resolved locally from the gitignored reference/gamelibs/SideLoader.dll, with load ordering handled

through SL.OnPacksLoaded. StoryKit follows the same precedent. The distinction matters — BepInEx

will happily load SkillKit with SideLoader absent, and the failure surfaces later and less clearly

than a dependency error would.

Consuming the kit

  • Reference the project (Private=false — the DLL ships once, from dist/SkillKit, never

inside your mod's folder: two copies in plugins/ = duplicate-GUID load + split types):

  • Declare the load-order dependency on your plugin class:

[BepInPlugin(GUID, NAME, VERSION)]

[BepInDependency(SkillKit.Plugin.GUID, SkillKit.Plugin.VERSION)] // VERSION = the min-version floor, see kits/versioning.md

  • Ship an SL_Skill XML in your own SideLoader pack (/SideLoader/Items... next to your

DLL — see the template below), then register a spec from your Awake:

SkillRegistry.Register(new SkillSpec

{

ItemId = 12345, // an id from YOUR mod's community-allocated range

Label = "PetCommand", // log tag

OnCast = player => TogglePetCommand(), // what the skill DOES (runs from the native pipeline)

// OPTIONAL: multi-state quickslot icon (vanilla PistolSkill Fire/Reload mechanism).

// For a plain STATIC icon use Icon = DynamicIcon.Fixed("YourSkill.png") — same

// convergent restamping, one state.

Icon = new DynamicIcon

{

Select = () => passive? "engage": "disengage", // state key, polled on every sync

Sprites = // embedded PNGs in YOUR assembly

{

["engage"] = "PetCommandEngage.png",

["disengage"] = "PetCommandDisengage.png",

},

},

// OPTIONAL: native cast animation, re-resolved per equipped weapon every frame

CastAnim = player => IsBow(player)? new CastPick("PowerShot", Character.SpellCastModifier.Immobilized): new CastPick("Probe", Character.SpellCastModifier.Attack),

});

  • SkillRegistry.Learn(player, itemId) grants it (TryUnlockSkill + immediate re-stamp).

SkillRegistry.Verify(player) asserts every spec's wired state — run it from a dev verb after

pack load; bug-16/19 regressions are silent by nature, so assert, don't infer.

The kit's own Plugin drives the convergence loop (per-frame cast-anim sync, ~2s icon tick, a

re-stamp at player-ready on every scene load). Call SkillRegistry.SyncIcons(player) yourself

whenever an icon's state input flips, for same-frame feedback.

Diagnostics to bind into your command channel: DumpLearned(player, filter),

DumpPrefabs(filter), CastDump(player), CastClear(player).

The trap table (why each piece exists)

| Piece | The trap it encodes |

|---|---|

| Effect host named "ActivationEffects" (SetupOne) | Bug 19: EffectSynchronizer.RegisterEffect buckets an Effect by its host GameObject's NAME — only an Activation name lands where Skill.SkillStarted() reads. Wrong name = silent no-op, zero errors. Never wire an effect GameObject by hand. |

| CastGuard (spec default ClearCastAfter = true) | Bug 16: donor 8200040's Spark cast NEVER fires its CastDone animation event for a cloned skill — IsCasting sticks true forever and every later CastSpell (any mod's!) silently n

Install & use Install: BepInEx plugin

  1. The archive from playbuff is the mod itself — usually a BepInEx plugin (a .dll or a plugin folder once unzipped).
  2. Recommended: use a community mod manager (such as r2modman) — pick this game, import the mod, and the manager sets up BepInEx and its dependencies for you.
  3. Manual route: install the BepInEx loader in the game folder, then drop the unzipped .dll or plugin folder into BepInEx/plugins/.
  4. Launch the game — the BepInEx console logs every plugin it loads; seeing this mod in the log means it is installed.

Mods with listed dependencies need those installed too, or they will not load. Options are usually changed in-game or in the plugin's config file.

Dependencies

Install these first, or the mod will not work:

BepInEx-BepInExPack_Outwardsinai-dev-SideLoaderCeruleanCutlass-ForgeKit

File & checksum

Filename
SkillKit-0.1.6.zip
File size
145.9 KiB