Outward
Install with App

Details

Latest version
0.1.12
Last Updated
First Uploaded
Downloads
598
Likes
0
Size
241KB
Dependants

StoryKit changelog

0.1.12 — 2026-09-05

  • StoryKit 0.1.12: complete the partial bump (manifest/thunderstore/csproj + consumer pins)
  • Merge branch 'worktree-agent-ace7c8e6b563bfb04'
  • ForgeKit 0.4.13 / DonorKit 0.1.9 / SpawnKit 0.6.1: session-3 fixes
  • Fix release-blocking version skew: NetKit 0.2.10, Beastwhispering 0.2.17
  • StoryKit 0.1.12: NpcDialogueHold — stop-and-face during dialogue for every spawned NPC
  • Echoes/StoryKit/GhostPeer: talk-to-wraith set-free SPIKE (Lane 6/7) — additive, OFF by default
  • GhostPeer: import saved enchantments — a hex-blade stays a hex-blade
  • GhostPeer VP7: full-character import (stats/skills/quickslot policy from the save) + post-rest weapon re-stamp
  • DangerousRoads 0.1.11 / StoryKit 0.1.11: bug-swarm close-out (BUG-TROGVSTROG, FC13, SK-D1-NRE, STALEBUSY)
  • StoryKit: STALEBUSY follow-up — clear a proven-stale LOCAL Buyer latch (never a remote one)
  • StoryKit: STALEBUSY review fixes — a checkout in flight is hard-busy; correct the host-latch claim
  • Merge branch 'main' into worktree-agent-ae7f08be6931734b3
  • StoryKit/DangerousRoads: STALEBUSY Merchant.Buyer is a latch — corroborate it, and name the busy leg
  • StoryKit: SK-D1-NRE heal CharacterAI's null quest-event ref before disabling the AI
  • StoryKit 0.1.10: the StoryKit→NetKit edge is ratified and landed (BepInDependency + KitContract.Declare + DependencyDirectionTests; Echoes gains the direct NetKit refs the R2 dist-dup rule demands)
  • StoryKit 0.1.9: MP-hardening sweep close-out (SK-D1/D2/D3/D4/D5/D6/D12/D13, SK-D14 v2)
  • StoryKit: SK-D13 review follow-up — forcing Unregister overload; wire SK-D8's Overdue ceiling and the DR teardowns through it
  • Merge branch 'main' into worktree-agent-a91f6de81384aa9e3
  • StoryKit: SK-D6 master migration detected by role, not room name — reset, abandon in-flight, sweep the departed host's minted orphans
  • StoryKit: SK-D13 despawn guard moves inside the registry — refuse (never defer) while the NPC is in dialogue
  • … and 23 more (see git history)

0.1.12 — 2026-09-04 (built, not live-verified)

  • Every StoryKit dialogue NPC now stops moving and faces the player for the duration of a conversation (NpcDialogueHold). Live bug (Cobalt 2026-09-04): a wandering Echoes echo ("LaughingJim") kept walking away while being talked to. Baked in at the rig level — a Harmony guard on AISWander.Update (the same source-suppression pattern as TorsoLookGuard) that, while the body's dialogue/shop is running (NpcRegistry.IsBodyInDialogue, the single busy-read source of truth), halts the wander at its source (CharacterAI.StopMovement + SpeedModif=0, so no per-frame re-write or re-path drift) and turns the whole body (not just the head) to face the nearest local player. The freeze is a per-frame suppression: nothing on the AISWander route is mutated, so the exact borrowed patrol resumes on its own the tick the conversation ends — no stored state, no coroutine. Opt-out via the new additive NpcSpec.HoldStillInDialogue (default true); echoes/merchants/trainers/mobile NPCs inherit it with zero consumer code. Decidable bits (ShouldHold, TryFaceYaw, ShortestTurn) are pure StoryKit.Core.DialogueHold, unit-tested. Live retest: echoes-testplan.md EC-HOLD1.

0.1.11 — 2026-08-30 (built, not live-verified)

The bug swarm off the 2026-08-30 live session.

  • Despawns no longer leave a swallowed CharacterAI.OnDisable NRE (SK-D1-NRE). Root cause found in the decompile: m_aiActiveOnQuestEvent is a serializable class Unity only allocates during scene deserialization, so every SideLoader-built body carries null and vanilla derefs it unguarded on BOTH enable and disable. NpcRegistry now heals a null field with an empty reference at rig time (and before pre-disables for unrigged orphans) — never overwriting a real quest gate. Side effect: vanilla's own distance-culling NREs on our bodies stop too. Retest: storykit-mp-testplan.md SK-D1-NRE.
  • The shop-busy read is self-limiting and transaction-aware (STALEBUSY — the live session saw despawn retries read HELD after the shop closed). Vanilla's Merchant.Buyer is a durable latch with exactly one fragile clearing path (the shopper's own ShopMenu.OnHide → QuitShop → exit RPC — which silently no-ops on a missing pouch; disconnect/death/scene-change/save-restore all leave it set). The merchant busy leg now corroborates a local shopper with the actual Shop panel (3s open grace), trusts a remote shopper for 300s then expires, and treats an in-flight checkout (IsTransactioPending) as HARD busy ahead of everything — money mid-move can never lose its merchant. A PROVEN-stale local latch is cleared (fixing vanilla's merchant-unshoppable-for-the- session wedge and unfreezing the greet-pinned walk); the remote leg is never cleared (clearing under a genuinely-shopping guest trades one latch for a worse one). Every refusal/HELD line now names WHICH leg read busy. Guest caveat: a guest browsing continuously past 300s may have the merchant walk off (T3-mp). Retests: road-merchant-testplan.md SK-D8b/c/d.

0.1.10 — 2026-08-30 (built, not live-verified)

  • The StoryKit → NetKit dependency edge is RATIFIED and landed (Cobalt, 2026-08-30; docs/storykit-dialogue-wire-plan.md open question 1). [BepInDependency(NetKit)] + KitContract.Declare + the DependencyDirectionTests allowance, in one change. This release carries the EDGE only — no wire code yet: the story channel, the dlg revision store and the story.act request/ack are a later sweep's work, per the plan doc. No shipped profile gains a DLL (every profile already carries NetKit via CompanionKit/SpawnKit); the bundle-closure computation just starts saying so honestly.

The multiplayer half of the MP-hardening sweep. Every finding below is BUILT and unit-green; none of it has been in front of a live two-box session. Rows: docs/storykit-mp-testplan.md SKMP1SKMP6 and SK-D12aSK-D12d/SK-D5b, docs/road-merchant-testplan.md RM26.

  • NpcRegistry.Despawn no longer reports a success it did not achieve (SK-D1). SideLoader's destroy always failed — a SetActive-first NRE in CharacterAI.OnDisable, swallowed by PUN — and the UID leaked forever behind a normal-looking log line. The AI is now disabled before the destroy, the destroy is wrapped, and a ~0.3 s verification pass logs despawned only once CharacterManager has actually forgotten the UID; when SideLoader's coroutine died it force-cleans (manager removal plus a direct destroy, mirroring DelayedCharacterDestroy). The bool keeps its signature, and its XML doc now says what it has always meant: "destroy issued", never "the world changed". Rules in StoryKit.Core.DespawnRules.
  • Every mutating registry op is master-gated (SK-D3). Despawn, Unregister, Respawn, Rebuild and ReplaceGreetings refuse with a named line — [STORYKIT] <Op>('<id>') REFUSED — non-master client …. One guest call used to delete an NPC permanently on every peer (SideLoader's destroy RPCs to all) and log as if it had worked. Rebuild/ReplaceGreetings are gated rather than documented as local-cosmetic, because a guest-local rebuild forks guest spec state; Rebuild also no longer mutates e.Spec before its liveness read. The SpawnAtAndGet guest gate is hoisted to the top, so a guest SpawnAt refuses synchronously instead of returning true and deferring. Authority rules in StoryKit.Core.RegistryAuthority.
  • The despawn-vs-dialogue guard moved INSIDE the registry (SK-D13). Despawn / Unregister / Respawn refuse with [STORYKIT] <Op>('<id>') REFUSED — in dialogue/trading … while IsInDialogue(id) reads busy, instead of destroying a Character under a running DialogueTree (the merchant-greet bug, previously guarded caller-side only and blind to guests). The semantics are REFUSE, not defer: the bool stays honest after SK-D1, and it composes with caller-side deferrals like DangerousRoads' ShopDefer because every retry takes a fresh read, so a double-defer cannot wedge. Unregister refuses the whole op while busy rather than dropping the entry around a live body. Verdicts in StoryKit.Core.DespawnBusyRules. Known limitation, recorded on the row: a guest's conversation with a DangerousRoads-minted (key-less) spec still reads not-busy (AO4-1).
  • ADDITIVE: forcing overloads Despawn(id, evenIfBusy) and Unregister(id, evenIfBusy) for teardown that must win. They exist because SK-D13's refusal broke SK-D8's composition: RoadWalker's Overdue ceiling fired End() while busy and MerchantCard.ClearLive tore its handles down around a REFUSED Unregister, leaving a live merchant, a live entry and a live template with nobody left to retry. The docs are explicit that a non-forcing caller must KEEP its handle on a false. COMPAT_SINCE unchanged at 0.1.5.
  • A Photon master migration is no longer a no-op (SK-D6). NpcDirector watches isMasterClient alongside the room name (StoryKit.Core.RoomRoleWatch; a simultaneous room change subsumes the flip). On a flip: a named log line, ForgetLive(), _specFailures.Reset() (SK-D4's hand-off), an immediate converge, and NpcRegistry.AbandonInFlight() — an epoch counter that makes in-flight SpawnWhenLive / RespawnWhenClear coroutines abandon themselves by name, also applied on a room change. A PROMOTED master sweeps the departed host's minted orphans (SL-named, no local template, no local spec, plus OrphanSweepRules.LooksMintedSpecId — deliberately conservative against SK-D11's ~95% false-positive recognizer) through SK-D1's verified destroy; a mid-dialogue orphan is left standing with its own line. The inverse guard: a peer demoted inside the SK-D1 verify window logs NOT force-cleaning rather than force-cleaning locally. Static specs still self-heal via FirstMissingIn after ForgetLive. Core: MigrationRules.cs.
  • One throwing spec no longer takes every later spec down with it (SK-D4). Both director walks — master placement and guest rig — isolate each spec in its own try/catch, so a throw no longer skips the rest, no longer silences the AO4-1 unmatched-template sweep, and no longer retries forever. Failures are counted per id and the director gives up on a spec BY NAME after 5 (new pure StoryKit.Core.RigFailurePolicy). A hard cap rather than a timed backoff, deliberately: the 3 s convergence tick is already the clock, and a second clock is a second thing to reason about in a log. The ladder is failure 1 = warning with the full exception, 2–4 = one line with the count, 5 = an error naming the id and saying attempts stop. Three re-arms: a success clears the id, a room change resets, and a SCENE change resets — the last is what makes the guarantee true in single-player, where PhotonNetwork.room is null all session and the room watch never fires again.
  • A guest no longer rigs replicated NPCs inside the loader's paused window (SK-D5). The IsGameplayResumed() gate sits above the master/guest branch in Converge, so guests stop producing naked / T-posed bodies with an inactive AIStatesRoot. And the "already rigged" marker is now honest: DialogueBuilder writes NpcLookFollow at the END of a successful rig rather than at the top, so a throw further down no longer leaves a body marked-but-unrigged and never revisited. DialogueBuilder.Build RETHROWS after installing its fallback graph, which makes a failed dialogue build a counted terminal state that escalates to SK-D4's give-up line instead of a silent re-rig every 3 s. The director also re-attempts a spec whose rig has thrown despite the marker, logging (retry #N after a failed rig) — harmless once the marker move is in, and the belt to its braces.
  • StoryKit NPCs enter vanilla's multiplayer dialogue lock (SK-D12). Every NPC now gets a DialogueActorLocalize carrying its spec id as ActorLocKey, which is what SceneInteractionManager requires before it will arbitrate a conversation through the master client. The key was always empty, so dialogue started local-only on every peer: two players could open the same NPC at once and the host could not see that a guest was talking — NpcRegistry.IsInDialogue's MP branch was dead code, which is also what made SK-D13's guard toothless for guests. A localization row is registered under the key first, so the NPC is still shown by name rather than by its id, and the rig-census line now carries actorKey=. Rules in StoryKit.Core.DialogueLockRules. Two known limits, documented as rows and neither fixed: one benign Unity-swallowed DialogueActorLocalize.Awake NRE per rig (Awake runs inside AddComponent, before LocKey can be assigned), and an in-session language change renaming every StoryKit NPC to its raw spec id until the next rig (the loc table is cleared on reload and there is no re-registration hook).
  • The merchant's fallback stock stops refilling on demand (SK-D2). The fallback item list now rides the same 72-game-hour refresh clock as a real stock roll (MerchantPouch.m_nextRefreshTime), so buying a shop out and reopening it mints nothing until the clock comes round. Every reopen of an emptied shop used to regenerate the whole fallback list through GenerateItemNetwork — an unbounded item fountain, in solo as much as in co-op. New pure rule StoryKit.Core.MerchantRefreshRules; one entry point MerchantWiring.RefreshAndMaybeFillFallback replaces the two ad-hoc refresh-then-fill call sites. Row RM26.
  • ADDITIVE, Core only, nothing wired: StoryKit.Core.DialogueWire — the pure codecs and state for the planned guest-dialogue wire (channel/verb constants, epoch+revision stamps with a migration-safe compare ledger, the action-invoke request body, and an ok/refused ack grammar with named refusal reasons), unit-tested. No channel is registered and no dependency edge landed: the StoryKit→NetKit edge is an architectural decision for Cobalt, and the case is written up in docs/storykit-dialogue-wire-plan.md (SK-D14 v2, with DW1DW10 draft verify rows).
  • Comment fix (SK-D12): the Awake-NRE rationale in the file was wrong; corrected in place, with the two known limits recorded beside it.
  • Dependency pins raised to the sweep floors.

0.1.8 — 2026-08-30 (built, not live-verified)

  • RingSpawner.SpawnAround — collision-safe ring spawn (additive; COMPAT_SINCE unchanged). Puts registered spec ids on a ring around a centre, slot 0 at centreAngleDeg clockwise from a world heading (180 + the player's forward = behind the player), fanned across spreadDeg. Every slot is navmesh-snapped (Walkable area, 2.5 m), rejected on a non-trigger collider at body height (Physics.CheckCapsule r=0.4, 0.3–1.8 m) or when within minSpacing of a placed body, then RingPlan.Fallbacks (±15°, ±30°, then radius ×0.75 / ×1.25) are tried in order; a spec with no surviving candidate is Refused with the reason. Spawns via SpawnAtAndGet(…, SpawnPolicy.Refuse) so the gameplay-live gate applies. Pure planner StoryKit.Core.RingPlan (RingPlanTests). Log tag [RING].

0.1.7 — 2026-08-29 (built, not live-verified)

  • The spawn gate, in the registry (additive; COMPAT_SINCE unchanged at 0.1.5). SpawnAt / SpawnAtAndGet / TrySpawn never spawn while ForgeKit.Lifecycle.IsGameplayLive() is false — a body born in the loader's paused window comes up T-posed / naked / with an inactive AIStatesRoot (education/who-poses-the-body.md #8). New NpcRegistry.SpawnPolicy { Refuse, Defer } + overloads: SpawnAt/TrySpawn default to Defer (spawns when the gate opens, ≤30 s then anyway, abandoned on a scene change; returns true = live now or accepted); SpawnAtAndGet defaults to Refuse (null + one [STORYKIT] '<id>': spawn REFUSED — gameplay is not live (<which half>) line) because its callers (the DangerousRoads cards, Echoes) need a synchronous body. NpcDirector.IsGameplayResumed now IS the kit predicate.
  • Posture census replaces the Bug-46 RepairVisualsWatch: one [STORYKIT] posture '<id>' @3s: renderers= visualsInit= animator= init= ctrl= aiRoot= closeToPlayer= spawnAnimDone= live= line per spawned body. Repairs in place: bare → InitDefaultVisuals+Rebind (as before); animator unbound / disabled / no controller → enable+Rebind; AIStatesRoot inactive → named, and re-activated only when vanilla's own rule (CharacterAI.cs:523-531: live, spawn anim done, alive, not quest-gated) would have. Echoes' private copy of both the gate and the census is gone.
  • AiSpec.WanderSpeed default 1.1 → NpcSpec.WalkSpeed (0.3), with NpcSpec.RunSpeed (1.1) as the other word. SideLoader's 1.1 is a full run; every "sprinting NPC" sighting was this default. The test pedlar says RunSpeed explicitly; the stranger card says WalkSpeed. Consumers that set a config value (DR merchant/patrol) are unchanged.

0.1.6 — 2026-08-29

  • Saved characters (0.1.50.1.6, additive; COMPAT_SINCE unchanged at 0.1.5). Lifted from Echoes for its second consumer (DangerousRoads' familiar-stranger card):
    • StoryKit.Saves.SaveScan / SavedCharacterRecord — read every other saved character off disk (newest readable snapshot, vanilla CharacterSave.LoadFromFile; null-vs-empty contract kept).
    • SavedCharacterSpec.FromRecord(rec, id, opts) — a look-alike NpcSpec: visual indices verbatim, worn gear resolved to SL slots via the prefab's EquipSlot.
    • SaveHandoff.Mint / OfferPick / RemoveFromSave — saved entry → live Item; the one-take container-panel pick (Harmony postfix on ItemContainer.RemoveItem, filtered to the offer container); permanent write-back as a NEW snapshot folder. Log tag [SAVEHANDOFF].
    • NpcSpec.Visuals (VisualIndices, wins over RandomVisuals), NpcSpec.BackpackId, NpcSpec.ShieldId.
    • Core: SavedItemParser (parser + hierarchy classifiers + EnchantmentIds), SaveSnapshotRules.
  • Echoes 0.1.1 now depends on StoryKit and registers its echoes through NpcRegistry (Echoes.Core.EchoRules keeps its surface as forwarders over StoryKit.Core.SavedItemParser).

0.1.5 — 2026-08-28

  • Fix stale dependency pins across the fleet; DonorKit 0.1.7
  • StoryKit: Action-kind dialogue choices + weapon outfit slot
  • ForgeKit/Hireling: mark script-owned NPCs so they can't be clone-adopted
  • SplitScene: refuse a solo unflipped-guest crossing instead of wedging
  • RM18b: root cause CONFIRMED at the clamp rail — restore the guard, add a pose sampler
  • RM18b: root-cause the merchant's backward spine arch + guard behind a live toggle
  • StoryKit: keep dialogue-menu highlight in place across leaf replies
  • AO4-2/AO4-7+8: document the Mobile=true merchant rule; explain the stash.Graph write
  • AO4-13: state the look-range change as tidy-up, not as two bugs (review CHANGES)
  • AO4-13: one look range in NpcLookFollow, and no first-frame turn to world origin
  • AO4-12: escape control characters in the qeventdb JSON dump
  • AO4-10: qeventadd's auto-registered signatures are not savable
  • AO4-7+8: cut orphaned dialogue nodes loose on rebuild, deferred under a live talk
  • AO4-6: keep the stock-table sweep off the merchant's spawn frame
  • AO4-5: drop a Train row that has no train node instead of showing a dead one
  • AO4-4: quest-event presence is stack > 0, and stack 0 prints as DORMANT
  • AO4-3: verify SideLoader actually took our template after ApplyTemplate
  • AO4-2: a static merchant is refused, not silently shopless
  • AO4-1: warn on a replicated SideLoader body with no local template (interim)
  • Callback dialogue leaves (0.1.30.1.4, additive; COMPAT_SINCE unchanged at 0.1.3). ChoiceKind.Action + Choice.Action(id, text, replyText, actionId): a Reply leaf that first runs a host-registered callback, and whose reply text is REPLACED at execution time when that callback returns a non-empty string — so a row can answer with live state the spec never knew. Legal at any menu depth (unlike Train/Shop there is no singleton node to point at).
    • StoryKit.ChoiceActions.Register(specId, actionId, Func<Character,string>) / Unregister(specId) / TryGet / Has. Keyed by SPEC id so a consumer minting per-spawn-unique specs (DangerousRoads' dr_merchant_<n>) sheds the whole table in one call — NpcRegistry.Unregister now does exactly that.
    • MenuPlanner.Plan(…, Func<string,bool> handlerAvailable): an Action row with no registered handler is dropped at plan time (MenuDropReason.ActionNoHandler), the Shop row's ShopNoMerchant treatment. An Action row emits an ActionNode + its Reply statement (menu[i] → action → reply → greeting/parent menu) while still consuming exactly ONE out-connection of the menu, so the availableChoices index-match holds.
    • SpecValidation refuses Kind=Action with an empty ActionId.
    • RunActionTask resolves the talking player at EXECUTION time (the OpenTrainerTask precedent, never a serialized binding) and calls the handler inside a try/catch — a consumer's throw logs and the conversation still reaches the statement.
  • OutfitSpec.WeaponName: the rolled outfit can hold a weapon, resolved by display name through the same loose item-name ladder as chest/helmet/boots (overrides NpcSpec.WeaponId; an unresolvable name warns and is skipped, never the spawn).
  • Every body NpcRegistry.RigCharacter touches is now stamped ForgeKit.ScriptedBody (owner=StoryKit, reason=story NPC '<id>') — the "do not adopt this body" marker — plus the SpawnAtAndGet path that deliberately skips the OnSpawn callback, which stamps it itself. That covers every master-side spawn. On a guest, NpcDirector.EnsureGuestRigs walks NpcRegistry.Specs(), so STATIC specs (Maren, the trainers) are marked there too, but a per-spawn spec minted at card time (dr_merchant_<n>) is registered master-side only and its guest replica is not marked — harmless today, since the only consumer refuses on a non-master. Rationale and the live incident it closes: src/ForgeKit/ScriptedBody.cs and the ForgeKit 0.4.10 changelog entry.
  • TorsoLookGuard (+ pure StoryKit.Core.TorsoLook.ShouldMute, unit-tested): mutes vanilla's torso look-at pitch on StoryKit bodies in the ONE case vanilla left undamped. Character.UpdateLateAnim2 rotates two spine bones from a pitch clamped to +50° (up to ~60° of backward arch, legs untouched) and only damps it if (m_currentWeapon && !flag) — our NPCs are unarmed, so a locked target pegs the clamp and it unwinds at 2°/s. An armed, aiming, sprinting or unlocked body is left entirely to vanilla. Membership is an instance-id set maintained at spawn/despawn, not a GetComponent in a LateUpdate that runs for every Character in the scene. Ships behind TorsoLookGuard.Enabled (default off) so roadsmerchant torsofix on|off can A/B it live — road-merchant-testplan RM18b, not yet graded in game.
  • DialogueSelectionMemory: DialoguePanel no longer snaps the menu highlight back to the top row when a leaf Choice.Reply loops back to the same parent menu — it stays on the row you picked. Navigating to a genuinely different menu (Back, a submenu, a new NPC) still resets to the top.
  • Random looks: NpcSpec.RandomVisuals (gender/skin/head/hair/hair colour rolled per spec id + session salt, bounds from the game's CharacterVisualsPresets) and NpcSpec.OutfitPool / OutfitSpec { ChestName, HelmetName, BootsName } (one outfit rolled per spawn, names resolved through the apostrophe-tolerant item-name ladder; an unresolvable piece warns and is skipped). Pure OutfitRoll.Pick / VisualRoll.Seed|Roll|Roll01 in Core, unit-tested.
  • NpcRegistry.IsInDialogue(id): true while the NPC's dialogue tree is running, the game lists it as in conversation, or its shop has a buyer — for consumers that walk the NPC.
  • Mobile / combat / merchant NPCs: NpcSpec.Mobile (SideLoader melee AI, unpinned body), AiSpec, CombatSpec (health/protection/resists/damage multiplier/targetable factions by NAME), Faction by name, BackpackName by display name, MerchantSpec (vanilla Merchant graft, caravan stock table lookup, fallback items, refresh rate), Choice.Shop → a real ShopDialogueAction node.
  • NpcRegistry.SpawnAtAndGet / TryGetLive / TryGetMerchant / Rebuild / ReplaceGreetings.
  • NpcRegistry.Unregister(id): despawn if live, SL_Character.Unregister() the template, remove the entry — for consumers minting per-spawn spec ids (the road merchant).
  • Stats re-enable: StoryNpcTemplate (an SL_CharacterTrainer subclass) keeps CharacterStats enabled for Mobile/Combat specs instead of SideLoader's unconditional disable.
  • Dev verb npcmerchanttest [despawn] — the built-in proving merchant (live-verify owed).
  • Existing static NPCs are unchanged (no Mobile → same pin/NoFall/snap/stats-strip path).

0.1.3 — 2026-08-19

  • StoryKit: quest-event catalog tools — enumerate, dump and toggle by name
  • Review follow-ups on the F3c leaf-kit wave (M1, M2, L3, L5, L6)
  • Static-analysis wave F3c: leaf-kit fixes (A7-1/2/4/8/9/13/14/15/16/20/23/24/25/27)
  • 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

  • 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 E: close the test-debt list (§4)
  • Maren teaches: nested tutorial dialogue + StoryKit submenus
  • W4: fixes for everything D1 found, plus the join-race P1
  • StoryKit: fake-null cleanup across the NPC rig and the recon taps

0.1.1 — 2026-08-02

  • Cleanup: collapse three repeated verb preambles into helpers that already had a home
  • ForgeKit/SkillKit/StoryKit: fix five guards that could never fire, and stop the recovery verb lying
  • Docs sweep: archive, condense, and validate the whole documentation tree

0.1.0 — 2026-07-30

  • Thunderstore release prep: changelogs + real category tags for BW
  • Thunderstore release prep: BW/SkillKit/StoryKit manifests + descriptions
  • Icon pipeline: condition generation on real Outward art, restyle every icon
  • Review fixes for b5118e3 MP wave: real corpse-view mute (group block), QESYNC poison purge, CHAR-GUARD player exemption + ghost identity check
  • 2026-07-27 MP session closeout: NK-F3 guest-join fix (QuestEventSyncGuard + unconditional srecon signatures + save purge), V-PARKLEAK corpse-view mute, AddCharacter dup-UID guard, [DIAG] loading line; session handoff + testplan/STATUS updates (428->352 queue burn)
  • Review must-fixes: plain-NPC fallback safety, empty choice menu, Dialogue guard, owner side-table leak
  • StoryKit T2/T4: kill-switch is recoverable; recon verbs route through VerbHost
  • StoryKit T1/T3/T5: plain-dialogue NPCs, on-demand late registration, internal OpenTrainerTask
  • Maren MP review fixes: scene-scope the guest warning, adopt the replica
  • Maren MP: converge placements against CharacterManager + watch the room
  • StoryKit: per-NPC toggle for NpcLookFollow; Maren stands static
  • Evening fix batch: Bugs 44-47 + 39 + unequip-hint nit (built, NOT live-verified)
  • Integrate verb re-homing wave + 2026-07-20 SP session results + fix batch
  • fix: high-severity wave from the 2026-07-19 static-analysis review
  • Maren: relocate default spawn to Cobalt's spot + StoryKit ground-snap
  • Guest pets Phase A (M0-M5): guests can tame, fight, and buy the skill tree
  • Small-mods bundle (review 2026-07-14): TreeLayout breakthrough violations now hard-refuse (Cobalt's ruling), Hireling recruit/dismiss master-gated, AggroKit cause-stack maintenance skipped when observation is off, StoryKit verbs through VerbHost
  • StoryKit: NPCs/trainers/dialogue as a kit — Maren goes live in Cierzo

0.1.0 — initial release

  • Add an NPC to Outward as plain data: a consumer describes placement, appearance, dialogue, and (if the NPC is a trainer) the skills it sells, and StoryKit builds the character, spawns it at a fixed spot, and wires the conversation — no custom UI, no NodeCanvas hand-editing.
  • NpcDirector spawns the registered NPC at player-ready on every scene load — master-only in co-op, duplicate-safe.
  • DialogueBuilder compiles a described conversation into a real NodeCanvas graph, reusing SideLoader's own trainer dialogue nodes.
  • A described skill tree compiles and sells from the vanilla Trainer window — no custom shop UI.
  • NPCs, trainers, and dialogue only. No quest/story-event authoring API in this release.
Thunderstore development is made possible with ads. Please consider making an exception to your adblock.