feat: API 4, factions, escort, allies, tether, turrets, kit extras and PVE (stage 5) #8

Merged
whitlocktech merged 3 commits from feat/stage-5 into edge 2026-10-05 08:38:34 +00:00
Member

What

Stage 5's behaviour (docs runicnpc/PLAN.md stage 5, D253–D272), API 4. One of the stage's PRs, walked once end to end (D253): this one, Rust-Plugins, Module-Rust, and docs.

Profiles guard role; faction, relations (its own exceptions), alertRadius (40), turrets, hurtByPlayers, hurtsPlayers, kitUse. With none set it fights players only (D255)
Faction table In profiles.json, one row per pair, both ways (D268). RunicNpc_Factions / RunicNpc_SetFactions; rnpc.faction on a standalone server
Fighting NPCs Only the factions it hunts, within its sense range and its chase range of its spot (D263); its target is Horror for the length of its own shot only. Shoots back at whoever shot it (D268); allies within the alert radius join in (D256)
Allies RunicNpc_Ally: clan, team, or a player and their team. Never targeted, and defended with what they own (D257)
Escort RunicNpc_Escort, /rnpc follow. An order; walks home when the charge is gone, raising OnRunicNpcEscortEnded (D269)
Tether RunicNpc_Tether, a placement's tether, tether= (D272)
Turrets ignore via CanBeTargeted; always via a Harmony patch on NPCAutoTurret.Ignore/IsEntityHostile, for our always NPCs only (D267)
Kit extras Heal, melee, flamethrower, grenades, rockets, each opted in and used up (D260, D265)
PVE CanEntityTakeDamage answered both ways; the two checkboxes enforced by RunicNPC itself (D261, D271)

Your condition on D267 held in every run, on both frameworks: the map's own sentries (85–94, sampled four times a second) never targeted a stock scientist or bandit guard; the always NPC was targeted and killed, and a default one beside it was left alone.

Three things the rigs found and this fixes: Rust's CanSeeTarget is no line of sight for an NPC target (unbounded rays; our fight uses a bounded linecast); an escort never let go of a destroyed charge (Unity's ==); rockets could not fire at NPCs.

Tested

tools/RunicNpcTest.cs 0.5.0, rnt.run s5 (52 checks): 52/52 on Oxide and on Carbon. rnt.run all (stages 2–5): 206/207 on each, the one failure the escort test's layout, since fixed. Stages 2–4 unchanged. node scripts/checkPlugin.js and its tests pass. Walked on the site with the bridge and Module-Rust (docs PR). The harness is removed from both rigs.

Checklist

  • I have read CONTRIBUTING.md.
  • Tested on both rigs (Oxide and Carbon).
  • 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 Stage 5's behaviour (docs `runicnpc/PLAN.md` stage 5, D253–D272), API 4. One of the stage's PRs, walked once end to end (D253): this one, Rust-Plugins, Module-Rust, and docs. | | | |---|---| | Profiles | `guard` role; `faction`, `relations` (its own exceptions), `alertRadius` (40), `turrets`, `hurtByPlayers`, `hurtsPlayers`, `kitUse`. With none set it fights players only (D255) | | Faction table | In `profiles.json`, one row per pair, both ways (D268). `RunicNpc_Factions` / `RunicNpc_SetFactions`; `rnpc.faction` on a standalone server | | Fighting NPCs | Only the factions it hunts, within its sense range and **its chase range of its spot** (D263); its target is `Horror` for the length of its own shot only. Shoots back at whoever shot it (D268); allies within the alert radius join in (D256) | | Allies | `RunicNpc_Ally`: clan, team, or a player and their team. Never targeted, and defended with what they own (D257) | | Escort | `RunicNpc_Escort`, `/rnpc follow`. An order; walks home when the charge is gone, raising `OnRunicNpcEscortEnded` (D269) | | Tether | `RunicNpc_Tether`, a placement's `tether`, `tether=` (D272) | | Turrets | `ignore` via `CanBeTargeted`; `always` via a Harmony patch on `NPCAutoTurret.Ignore`/`IsEntityHostile`, for our `always` NPCs only (D267) | | Kit extras | Heal, melee, flamethrower, grenades, rockets, each opted in and used up (D260, D265) | | PVE | `CanEntityTakeDamage` answered both ways; the two checkboxes enforced by RunicNPC itself (D261, D271) | **Your condition on D267 held in every run, on both frameworks:** the map's own sentries (85–94, sampled four times a second) never targeted a stock scientist or bandit guard; the `always` NPC was targeted and killed, and a `default` one beside it was left alone. Three things the rigs found and this fixes: Rust's `CanSeeTarget` is no line of sight for an NPC target (unbounded rays; our fight uses a bounded linecast); an escort never let go of a destroyed charge (Unity's `==`); rockets could not fire at NPCs. ## Tested `tools/RunicNpcTest.cs` 0.5.0, `rnt.run s5` (52 checks): **52/52 on Oxide and on Carbon**. `rnt.run all` (stages 2–5): 206/207 on each, the one failure the escort test's layout, since fixed. Stages 2–4 unchanged. `node scripts/checkPlugin.js` and its tests pass. Walked on the site with the bridge and Module-Rust (docs PR). The harness is removed from both rigs. ## Checklist - [x] I have read CONTRIBUTING.md. - [x] Tested on both rigs (Oxide and Carbon). - [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 3 commits 2026-10-05 06:17:23 +00:00
Stage 5's behaviour (docs runicnpc/PLAN.md, D253-D272):

- profiles gain faction, relations (exceptions), alertRadius, turrets,
  hurtByPlayers, hurtsPlayers and kitUse, and the guard role; the faction
  table rides in profiles.json, one row per pair, both ways (D254, D268);
- our own sensing and fight beside Rust's design: only the factions a
  profile hunts, within its leash of its anchor (D263), the target made
  Horror for the length of our own shot, and Rust's attack routine;
- shooting back at whoever hurt it (D268), the alert radius (D256), clan,
  team or player allies that are never targeted and are defended with
  what they own (D257), escort as an order that walks home when its
  charge is gone (D269), and a placement's tether through ZoneManager
  (D272);
- turrets: ignore through CanBeTargeted, and always through a Harmony
  patch on the safe-zone sentries for our always NPCs only (D267);
- the kit's extras, each opted into and used up: heal, melee,
  flamethrower, grenades and rockets (D260, D265);
- TruePVE/NextGenPVE answered both ways unless a profile turns a direction
  off, and the opt-out enforced by RunicNPC itself (D261, D271);
- API 4: RunicNpc_Factions, _SetFactions, _Escort, _Ally, _Tether and the
  OnRunicNpcEscortEnded hook; rnpc follow, rnpc.faction and tether=.

Compiled and loaded on both rigs (Oxide and Carbon); the sentry patch
applies on both.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
What the rigs' stage 5 runs found in the plugin:

- an escort whose charge was destroyed was never released: Unity's ==
  says a destroyed entity is null, so CheckEscort returned before it
  looked. It now tests the reference itself (D269);
- our own fight's line of sight no longer uses Rust's CanSeeTarget for an
  NPC target: its rays are unbounded, so on open ground they run past the
  target into the terrain behind it. A bounded linecast to the target's
  centre, which counts the target's own collider as seen;
- a rocket's line of sight: Rust's sensed one for a real player, ours for
  an NPC or an animal, which are never in Rust's player memory.

tools/RunicNpcTest.cs 0.5.0 adds `rnt.run s5`: 52 checks of the faction
table and its refusals, faction fights, the leash, shooting back, the alarm,
allies spared and defended (players and buildings), escort, the guard,
the tether, PVE, turrets (`ignore` through the hook, `always` on the map's
own sentries with the org lead's condition: no stock scientist or bandit
guard ever targeted), and the kit's extras used up. Also `rnt.duel`,
`rnt.kitprobe`, `rnt.rocketprobe` and `rnt.list`, for looking.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
test(harness): the escort test walks its charge away from the keeper; rnt.escortprobe (stage 5)
All checks were successful
PR Checks / plugin-checks (pull_request) Successful in -1m37s
900ac4e992
Run alone on each rig, s5 is 52/52 on Oxide and on Carbon, and rnt.run all
was otherwise green on both (stages 2-4 unchanged).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
whitlocktech merged commit cf8c661087 into edge 2026-10-05 08:38:34 +00:00
whitlocktech deleted branch feat/stage-5 2026-10-05 08:38:35 +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#8
No description provided.