SkillKit
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
模組介紹 作者原文
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
安裝與使用 安裝:BepInEx 外掛
- 本站下載的壓縮包就是模組本體,通常是 BepInEx 外掛(解壓後得到 .dll 或外掛資料夾)。
- 建議流程:用社群模組管理器(如 r2modman)選中本遊戲後匯入該模組,管理器會自動裝好 BepInEx 前置並處理依賴。
- 手動流程:先在遊戲目錄裝好 BepInEx 載入器,再把解壓出的 .dll 或外掛資料夾放進 BepInEx/plugins/。
- 啟動遊戲,BepInEx 控制台會列印載入日誌;確認這個模組出現在日誌裡即為安裝成功。
帶「前置依賴」的模組需要一併安裝,否則不會載入。功能開關通常在遊戲內或外掛自帶的設定檔裡調整。
下面這些內容需要先安裝,否則該模組不會生效: