feat: API 5, bosses, passive NPCs and the example profiles (stage 6) #10

Merged
whitlocktech merged 4 commits from feat/stage-6 into edge 2026-10-06 00:45:58 +00:00
Member

What

RunicNPC stage 6 (docs/runicnpc/PLAN.md stage 6; decisions D273–D289; API.md version 5).

  • Bosses (D273–D277, D286, D287):
    • a boss block on any role but passive, with a health bar for players within its distance and anyone who hurt it, redrawn in place at most 4×/s;
    • phases at health fractions that change the boss's own copy of its profile (damage, aim, speed, ranges), swap its kit, summon adds one per frame, and say a line to players near it;
    • spawn and death lines to everyone;
    • hooks OnRunicNpcBossSpawned, OnRunicNpcBossPhase and OnRunicNpcBossDied.
  • Passive NPCs (D278, D279, D288):
    • never fights: Rust's GetBestTarget and AttackTick are refused through IAIAttack, since Rust's design picks targets IsTarget never sees;
    • unhurtable unless its profile says so;
    • moves as its profile says, including the new stand;
    • answers E through OnPlayerInput with a chat line or a window, at most once a second; hook OnRunicNpcUsed.
  • Examples (D280, D284, D289): four profiles and their kits, written into Kits' data file on the first load only (data/RunicNPC/state.json). Each kit needs kits.runicnpc, which nobody holds.
  • The boss's adds and teardown (D287): RunicNpc_Despawn(<boss net id>) also removes the adds that boss summoned, even after it died, so an event's teardown removes them.
  • The swap's field list regenerated for Rust 2634.289.1: two new fields, and eleven Rust now marks [NonSerialized].
  • The harness:
    • the s6 group (42 checks);
    • two checks fixed after the Rust update's navmesh rebuild;
    • the probes rnt.stockprobe and rnt.monprobe.

Tested

Tested on Rust buildid 25681086 (2634.289.1), Oxide 2.0.7801 and Carbon 2.0.262. Each was checked against the latest release before the runs.

  • s6: 42 of 42 on both rigs, including s6.boss.despawnTakesAdds, which came with the teardown fix.
  • rnt.run all: Oxide 244/246 and Carbon 243/246 before the two fixed checks. Each fixed check, and Carbon's escort check, then passed on both rigs.
  • Site walk on both rigs: a boss and a passive NPC made on the form, pushed and placed from the map; phases, adds, a kit swap, the three boss triggers; an event's gate advancing on the kill, and its teardown removing the adds.

Results and findings: docs#317 (runicnpc/PLAN.md stage 6).

Checklist

  • I have read CONTRIBUTING.md.
  • node scripts/checkPlugin.js passes (API 5, 24 calls, 11 hooks, requires Kits).
  • AI tools were used. Tool(s): Claude Code (Claude Opus 5.5). The commits carry the Co-Authored-By trailer.
  • I agree that my contribution is licensed under GPL-3.0-or-later.

🤖 Generated with Claude Code

https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY

## What RunicNPC stage 6 (docs/runicnpc/PLAN.md stage 6; decisions D273–D289; API.md version 5). - **Bosses (D273–D277, D286, D287):** - a `boss` block on any role but passive, with a health bar for players within its distance and anyone who hurt it, redrawn in place at most 4×/s; - phases at health fractions that change the boss's own copy of its profile (damage, aim, speed, ranges), swap its kit, summon adds one per frame, and say a line to players near it; - spawn and death lines to everyone; - hooks `OnRunicNpcBossSpawned`, `OnRunicNpcBossPhase` and `OnRunicNpcBossDied`. - **Passive NPCs (D278, D279, D288):** - never fights: Rust's `GetBestTarget` and `AttackTick` are refused through IAIAttack, since Rust's design picks targets `IsTarget` never sees; - unhurtable unless its profile says so; - moves as its profile says, including the new `stand`; - answers E through `OnPlayerInput` with a chat line or a window, at most once a second; hook `OnRunicNpcUsed`. - **Examples (D280, D284, D289):** four profiles and their kits, written into Kits' data file on the first load only (`data/RunicNPC/state.json`). Each kit needs `kits.runicnpc`, which nobody holds. - **The boss's adds and teardown (D287):** `RunicNpc_Despawn(<boss net id>)` also removes the adds that boss summoned, even after it died, so an event's teardown removes them. - **The swap's field list** regenerated for Rust 2634.289.1: two new fields, and eleven Rust now marks `[NonSerialized]`. - **The harness:** - the `s6` group (42 checks); - two checks fixed after the Rust update's navmesh rebuild; - the probes `rnt.stockprobe` and `rnt.monprobe`. ## Tested Tested on Rust buildid 25681086 (2634.289.1), Oxide 2.0.7801 and Carbon 2.0.262. Each was checked against the latest release before the runs. - **`s6`:** 42 of 42 on both rigs, including `s6.boss.despawnTakesAdds`, which came with the teardown fix. - **`rnt.run all`:** Oxide 244/246 and Carbon 243/246 before the two fixed checks. Each fixed check, and Carbon's escort check, then passed on both rigs. - **Site walk on both rigs:** a boss and a passive NPC made on the form, pushed and placed from the map; phases, adds, a kit swap, the three boss triggers; an event's gate advancing on the kill, and its teardown removing the adds. Results and findings: docs#317 (`runicnpc/PLAN.md` stage 6). ## Checklist - [x] I have read CONTRIBUTING.md. - [x] `node scripts/checkPlugin.js` passes (API 5, 24 calls, 11 hooks, requires Kits). - [x] AI tools were used. Tool(s): `Claude Code (Claude Opus 5.5)`. The commits carry the `Co-Authored-By` trailer. - [x] I agree that my contribution is licensed under GPL-3.0-or-later. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
wtclaude added 4 commits 2026-10-05 18:12:51 +00:00
The boss box on any role but passive (D273): a health bar for players
within its distance and everyone who hurt it, redrawn in place at most
four times a second (D274); phases at health fractions that change its
own copy of the profile (damage, aim, speed, ranges), swap its kit,
summon adds one per frame and say a line (D275); spawn and death lines
to everyone, phase lines to players near it (D277, D286); adds fight on
after it dies (D287). Hooks OnRunicNpcBossSpawned, _BossPhase and
_BossDied.

The passive role (D278, D279, D288): never fights (GetBestTarget and
AttackTick refuse, since Rust's design picks targets from every sensed
player), cannot be hurt unless its profile says so, moves as its profile
says including the new `stand`, and answers E through OnPlayerInput
with a chat line or a window. Hook OnRunicNpcUsed.

The four example profiles and their kits, written into Kits' data file
on the first load only (D280, D284, D289), each kit needing a
permission nobody holds. Kits' return re-checks the profiles.

The swap's field list regenerated for Rust 2634.289.1: two new fields,
eleven now [NonSerialized].

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
Kits registers a kit's RequiredPermission, and Oxide warns on every Kits
load that a permission a plugin registers must start with that plugin's
name: "Missing plugin name prefix 'kits' for permission
'runicnpc.examplekits'". The org lead chose Kits' prefix. Nobody is
granted it, so no player can claim an example kit by name; RunicNPC's
GiveKit call skips it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
The s6 group (41 checks): profile refusals, the passive role (never
fights, unhurtable by default, press E through OnPlayerInput once a
second, a window, wandering), the boss (three phases: damage, adds,
kit swap, speed, aim and ranges; its own copy of the profile; the death
hook's contributors; adds that fight on) and the example kits.

After Rust 2634.289.1 (buildid 25681086) rebuilt the navmesh,
sentry.shoots's first spot had a rock between the sentry and its
stand-in: it now uses a clear line, as the stage 5 tests do (28 hits).
move.monument.roams: the military tunnel's own 30 scientists no longer
move with no real player near, so ours standing too is the same
behaviour, and the check says both.

Two probes for that: rnt.stockprobe and rnt.monprobe.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
fix: an event's teardown removes a boss's adds (D287)
All checks were successful
PR Checks / plugin-checks (pull_request) Successful in -1m33s
3be2c54260
The walk found an event run's boss adds outliving the run: teardown
removes what a step placed by net id, and the adds RunicNPC spawned for
a boss's phase are in no step's ledger. RunicNPC now keeps each boss's
live adds by the boss's net id, after the boss dies too, and
RunicNpc_Despawn(<boss net id>) removes them with it, which is the call
the bridge's teardown makes. An add leaves the set when it dies.

Walked on the Oxide rig: event 36, run 65 placed the boss, its phase
summoned two adds owned by run:65, the boss was killed, the event
advanced on rust.boss.killed, and the run's cleanup removed both adds.
New harness check s6.boss.despawnTakesAdds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
whitlocktech merged commit d068635b45 into edge 2026-10-06 00:45:58 +00:00
whitlocktech deleted branch feat/stage-6 2026-10-06 00:45:59 +00:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: RunicGateway/runicnpc-rust#10
No description provided.