docs(runicnpc): stage 6 built and walked; API 5; protocol §19.14
API.md version 5: the profile's boss and use blocks, the passive role and stand, the boss hooks and OnRunicNpcUsed, role and boss in RunicNpc_List, RunicNpc_Despawn taking a boss's adds, the example kits written once, and a Kits reload taking RunicNPC with it (D283). PROTOCOL.md §19.14: npc.boss.spawned, npc.boss.phase and npc.boss.died, their classes, and runicnpc_api 5. PLAN.md stage 6: what was built, the builds it was tested on, the harness and the site walk on both rigs, and what building it found. The example kits' permission is kits.runicnpc. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
@@ -11,7 +11,7 @@ design answered 2026-09-30 and 2026-10-01** (D253–D266, §0), **its spike meas
|
||||
and **its build's own questions answered 2026-10-01** (D267–D272). **Stage 5 built and tested 2026-10-01, walked on the
|
||||
site 2026-10-05** on both rigs (§9); its API is [API.md](API.md) version 4. **Stage 6's design answered
|
||||
2026-10-05** (D273–D282, §0), **its spike measured 2026-10-05** on both rigs, and **its two questions answered the
|
||||
same day** (D283, D284, §9). **Its build's own questions were answered the same day too** (D285–D289).
|
||||
same day** (D283, D284, §9). **Its build's own questions were answered the same day too** (D285–D289), and **stage 6 was built and walked on the site 2026-10-05** on both rigs (§9); its API is [API.md](API.md) version 5.
|
||||
|
||||
RunicNPC is Runic Gateway's own NPC plugin for Rust servers, in its own repository,
|
||||
[`RunicGateway/runicnpc-rust`](https://gitea.whitlocktech.com/RunicGateway/runicnpc-rust). It runs on Oxide and
|
||||
@@ -1236,8 +1236,10 @@ around it, each point snapped to the navmesh.
|
||||
What this adds to the build (D283, D284):
|
||||
|
||||
- **The kits are named `rnpc_raider`, `rnpc_campguard`, `rnpc_sniper` and `rnpc_juggernaut`.**
|
||||
- **Each carries `RequiredPermission: runicnpc.examplekits`**, which nobody is granted, so no player can claim one
|
||||
by typing its name (`IsHidden` alone does not stop that). RunicNPC's `GiveKit` call ignores the permission.
|
||||
- **Each carries `RequiredPermission: kits.runicnpc`**, which nobody is granted, so no player can claim one
|
||||
by typing its name (`IsHidden` alone does not stop that). RunicNPC's `GiveKit` call ignores the permission. It
|
||||
was `runicnpc.examplekits` until the build found Oxide warning on every Kits load that a permission Kits
|
||||
registers must start with `kits.`; the org lead chose Kits' prefix (2026-10-05).
|
||||
- **The first install runs in this order:** write the kits, set the flag, reload Kits.
|
||||
- Because of D283, the reload takes RunicNPC down and back once.
|
||||
- On that second load the flag is already set, so nothing is written twice and nothing loops.
|
||||
@@ -1259,6 +1261,64 @@ What this adds to the build (D283, D284):
|
||||
4. **How a passive NPC moves. → D288:** as its profile says.
|
||||
5. **The example profiles. → D289:** the set in §0.
|
||||
|
||||
**Built (2026-10-05), three branches and this one.** RunicNPC API 5 (`runicnpc-rust` `feat/stage-6`), the bridge
|
||||
(`Rust-Plugins` `feat/runicnpc-stage6`) and the site (`Module-Rust` `feat/runicnpc-stage6`). The API is
|
||||
[API.md](API.md) version 5; the wire is [`PROTOCOL.md`](../rust-link/PROTOCOL.md) §19.14.
|
||||
|
||||
| Piece | What it does |
|
||||
|---|---|
|
||||
| RunicNPC API 5 | The boss box (bar, phases, lines and adds; the hooks `OnRunicNpcBossSpawned`, `OnRunicNpcBossPhase`, `OnRunicNpcBossDied` and `OnRunicNpcUsed`); the passive role with `stand` and press E through `OnPlayerInput`; the four example profiles and kits, written once (`data/RunicNPC/state.json`); `role` and `boss` in `RunicNpc_List`. The swap's field list regenerated for Rust 2634.289.1 |
|
||||
| The bridge | `npc.boss.spawned`, `npc.boss.phase`, `npc.boss.died`; `runicnpc_api = 5`. A profile's `boss` and `use` travel in the push unchanged |
|
||||
| The site | The profile form's boss box (phases with damage, aim, speed, ranges, a kit swap, adds and a line) and the passive role with its press-E answer; the checks in RunicNPC's words, a phase's kit checked on each server, its adds a site profile that is not a boss; the public triggers `rust.boss.spawned`, `rust.boss.phase`, `rust.boss.killed`; the feed's boss rows (a spawn public, a death behind the presence setting, a phase staff's) |
|
||||
|
||||
**The builds it was tested on (checked against the latest, 2026-10-05):** Rust 2634.289.1 (buildid 25681086,
|
||||
the latest public build), Oxide 2.0.7801 and Carbon 2.0.262 (both the latest releases). This morning's spike ran on
|
||||
the previous Rust build, before either rig restarted.
|
||||
|
||||
**Tested.** The harness's `s6` group (42 checks) passes on both rigs: refusals, passive (never fights, unhurtable
|
||||
by default, press E once a second, a window, wandering), the boss (three phases, its own copy of the profile, the
|
||||
death hook's contributors, adds that fight on, `RunicNpc_Despawn` of the dead boss taking its adds) and the
|
||||
examples. `rnt.run all`: 244/246 on Oxide and 243/246 on Carbon before two checks were fixed (below); each fixed
|
||||
check and the escort check then passed on both rigs. Module-Rust: 491 server and 67 client tests, and every
|
||||
`check:*` script.
|
||||
|
||||
**Walked, 2026-10-05, through a walk site, signed in to the admin panel in the browser.** No player was on the rigs,
|
||||
so a probe plugin hurt bosses as a stand-in player.
|
||||
|
||||
| Row | Oxide | Carbon |
|
||||
|---|---|---|
|
||||
| A boss profile made on the form (two phases: 50% two adds and a line; 25% damage ×1.5, speed ×1.3 and a line), saved and pushed; the rig holds the block as typed | pass | pass (made through the same API) |
|
||||
| Placed by clicking the live map; `npc.boss.spawned` stored, `rust.boss.spawned` fired | pass | — |
|
||||
| Phase 1 summoned two adds, owned as the boss is; `rust.boss.phase` fired for each phase | pass | pass (and the 25% kit swap: it then held `rnpc_raider`) |
|
||||
| The kill: `rust.boss.killed` fired; the adds fought on (D287) | pass | pass |
|
||||
| The public events route: signed out it shows the spawn only; signed in as an admin, the death too (its killer, 1 damager); never a phase | pass | — |
|
||||
| An event placing the boss: its phase gate advanced on `rust.boss.killed` where `byEvent`, and the run's teardown removed the boss's adds | pass after a fix (below) | pass |
|
||||
| A passive profile made on the form: choosing passive showed Press E, hid Boss, unticked "Players can hurt it" and set `stand`; pushed as typed | pass | — |
|
||||
| Placed from the map; 500 hits from a player left it at 150/150; it stood and took no target | pass | — |
|
||||
|
||||
**Not walked:** everything a real client shows, waiting for the in-game walk with a player: the bar on screen and
|
||||
its redraws, the lines in chat or a popup, a real E press and the window, and the example profiles on a fresh
|
||||
server's own boot.
|
||||
|
||||
**What building it found:**
|
||||
|
||||
- **Rust's design picks targets that `IsTarget` refuses.** `HumanNPC.GetBestTarget` ranks every player in
|
||||
`Senses.Players`, which `IsTarget` does not filter, and fires through `AttackTick`. A passive NPC shot at players
|
||||
until RunicNPC answered both (IAIAttack, which it already re-implements) with nothing for a passive one. **Stage 5's
|
||||
ally sparing may have the same hole**: it was not part of this stage and is not changed here.
|
||||
- **A boss's adds outlived the event that placed it.** Teardown removes what a step placed by net id; the adds
|
||||
RunicNPC spawned are in no ledger. RunicNPC now keeps each boss's adds by the boss's net id, after it dies too,
|
||||
and `RunicNpc_Despawn` of that id removes them. Walked again: run 65's cleanup removed both.
|
||||
- **Oxide warns on a permission Kits registers without its prefix.** The example kits' permission is `kits.runicnpc`
|
||||
(the org lead's choice), not `runicnpc.examplekits`.
|
||||
- **The Rust update's navmesh rebuild moved two checks' ground.** `sentry.shoots` now picks a spot with a clear line
|
||||
(a rock was between the sentry and its stand-in; with a clear line it hit 28 times). `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 now says both.
|
||||
- **The bridge's `npc.health` frame carries no owner** (stage 4's `OnRunicNpcHealth` passes null), so
|
||||
`rust.npc.health` never says `byEvent`, and a gate on "the event's boss below 50%" cannot be written. Not changed
|
||||
here; the boss phase trigger does carry it.
|
||||
|
||||
### Stage 7 — Loot
|
||||
|
||||
Loot tables authored on the site, the corpse's default loot cleared or kept, a crate on death, corpse removal.
|
||||
|
||||
Reference in New Issue
Block a user