docs(runicnpc): stage 7 built and walked; API 6 and the dropLoot switch

- runicnpc/API.md: version 6, the profile's loot block and a spawn's
  loot.dropTable override.
- rust-link/PROTOCOL.md: §19.15, world.place's dropLoot.
- runicnpc/PLAN.md: stage 7's "Built" section. It covers the builds it
  was tested on (Rust 25681086, Oxide 2.0.7801, Carbon 2.0.262), the
  harness (s7 25/25, all 270/270, both rigs) and the walk on both rigs
  through the site. It also lists what building it found: Rust's
  ApplyLoot after the hook, and the inventory already emptied when the
  hook runs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
2026-10-06 01:43:15 -05:00
parent 192eddb4b1
commit 846119220e
3 changed files with 90 additions and 5 deletions

View File

@@ -1,7 +1,8 @@
# RunicNPC — the API
**API version 5** (RunicNPC stage 6, 2026-10-05). This is the reference for other plugins. Why it has this
shape is in [PLAN.md](PLAN.md) §4 and the decisions D221–D238, D249, D253–D272 and D273–D289. **API 5 added**
**API version 6** (RunicNPC stage 7, 2026-10-06). This is the reference for other plugins. Why it has this
shape is in [PLAN.md](PLAN.md) §4 and the decisions D221–D238, D249, D253–D272, D273–D289 and D290–D301. **API 6
added** the profile's `loot` block (PLAN.md stage 7); nothing of API 5 changed shape. **API 5 added**
the profile's `boss` and `use` blocks and the `passive` role with its `stand` movement, the hooks
`OnRunicNpcBossSpawned`, `OnRunicNpcBossPhase`, `OnRunicNpcBossDied` and `OnRunicNpcUsed`, and `role` and `boss`
in `RunicNpc_List` (PLAN.md stage 6). Nothing of API 4 changed shape. **API 4 added** the faction
@@ -19,7 +20,7 @@ what Oxide's `Call` reaches by name (PLAN.md §1.2).
[PluginReference] private Plugin RunicNPC;
int api = RunicNPC?.Call<int>("RunicNpc_ApiVersion") ?? 0;
if (api < 5) { /* too old for this caller: say so */ }
if (api < 6) { /* too old for this caller: say so */ }
BasePlayer npc = RunicNPC.Call("RunicNpc_Spawn", position, "warden", "plugin:MyPlugin", null) as BasePlayer;
```
@@ -49,7 +50,7 @@ Every NPC has an owner, and the owner decides its lifetime (PLAN.md §2).
### `RunicNpc_ApiVersion()` → `int`
The API version: `5`.
The API version: `6`.
### `RunicNpc_Spawn(Vector3 at, string profile, string owner, JObject overrides)` → `BasePlayer`
@@ -281,7 +282,16 @@ All are called on every plugin, and **none of them answers**: a return value is
{ "at": 0.25, "damageDealt": 1.5, "speed": 1.3, "kit": "warden_heavy", "line": "{name} is enraged!" }
]
},
"use": null
"use": null,
"loot": {
"start": "rust",
"always": [ { "item": "scrap", "min": 100, "max": 300, "chance": 1 }, { "item": "rope", "min": 1, "max": 1, "chance": 0.5 } ],
"pool": { "pick": 1, "nothing": 85, "rows": [ { "item": "lmg.m249", "min": 1, "max": 1, "weight": 10 }, { "item": "explosive.timed", "min": 1, "max": 1, "weight": 5 } ] },
"crate": "locked",
"hackSeconds": 600,
"corpseSeconds": 0,
"dropTable": true
}
}
```
@@ -335,6 +345,15 @@ A passive profile has no `boss` and a `use` block:
| `phases[].line` | at most 256 characters | D286. Said only to the players near it (within `barDistance`) and to those who have hurt it. |
| `use` | API 5. required on a passive profile, refused on any other | D278. What pressing E on it does, from up to 3 m: `mode` `chat` says one of `lines` (picked at random, each at most 256 characters) in that player's chat; `mode` `window` opens a window with `title` (blank: its name), `text` (at most 2,000 characters) and a Close button. `{player}` in either is the player's name. |
| `loot` | API 6. null, or the block below | D290–D301. null keeps Rust's scientist loot, as before stage 7. |
| `loot.start` | `rust` (default), `kit` or `none` | D290, D300. What the corpse holds before the table: Rust's scientist loot and the clothes it wore; what it carried (its kit) and the clothes; or nothing at all, clothes included. |
| `loot.always` | up to 30 rows: `item` (a short name this Rust has), `min` and `max` (1 or more), `chance` (above 0, at most 1), optional `skin` | D291. Each row rolls on its own. |
| `loot.pool` | `pick` (0 to its rows), `nothing` (a weight, 0 or more), up to 30 `rows` with `weight` (above 0) instead of `chance` | D291. At most `pick` draws by weight, never the same row twice; a draw of "nothing" uses one up. A kill never pays out more than `pick` pool rows. |
| `loot.crate` | null (the corpse), `wooden`, `military`, `elite` or `locked` | D292, D301. The table goes into one of Rust's crates beside the corpse, with Rust's own loot off, and it stays until emptied (a locked crate keeps Rust's own 2-hour removal unhacked). The always-rows plus `pick` must fit its slots: 6, 6, 12 or 24 (the corpse has 24). |
| `loot.hackSeconds` | 1 to 86,400; default 900 | D297. A locked crate's hack time, counted once a player starts the hack. |
| `loot.corpseSeconds` | null (Rust's own 600 s), 0 (no corpse) or more | D294. |
| `loot.dropTable` | true (default) or false | D296. False rolls no table; the start and the corpse's time still apply. An event's Place NPCs step sends it false through a spawn's overrides. |
A phase changes **that boss's own copy** of its profile; another NPC of the same profile is not touched. A boss gives
no reward itself (D276, D285).