diff --git a/runicnpc/PLAN.md b/runicnpc/PLAN.md index c01da08..4efaf6f 100644 --- a/runicnpc/PLAN.md +++ b/runicnpc/PLAN.md @@ -120,6 +120,8 @@ architectural or design decision is implemented. | **D297** | **The locked crate's hack timer is the profile's**, Rust's own 15 minutes by default (stage 7). | Always Rust's 15 minutes. | | **D298** | **On a server with no website, a loot table is edited in the data file** (`data/RunicNPC/profiles.json`) and read again with `rnpc.reload`. No new in-game commands (stage 7). | `rnpc.loot` verbs; JSON through `rnpc.profile set`. | | **D299** | **Stage 7 is one stage, opening with a spike on both rigs, walked once end to end**, as stages 5 and 6 were (D253, D282) (stage 7). | 7a (the table and corpse) and 7b (the crates), each walked and merged before the next. | +| **D300** | **A corpse that starts with "nothing" (D290) is truly empty:** the clothes the NPC wore come off too, so it lies unclothed and holds nothing until its table is added (stage 7 build). | Keep the worn clothes, as on any Rust corpse. | +| **D301** | **A dropped crate stays until it is emptied**, as Rust's own crates do. The locked crate still removes itself after Rust's own time unhacked (7,200 s) (stage 7 build). | The profile sets how long an unopened crate stays, 30 minutes by default. | **Borrowing, not copying.** NpcSpawn states no licence at all, so its source grants us nothing and is read only as a description of *what* can be done in Rust. HumanNPC is MIT on uMod, which is GPL-compatible, but §1.2 rules out its @@ -1356,6 +1358,62 @@ The org lead's answers on 2026-10-05 are D290–D299. **It is one stage, opening **Tested by** harness kills whose corpses and crates are read back, 100 times each, against the table's chances and the pool's cap; and a walk on both rigs. +**Measured (2026-10-05, `tools/RunicNpcTest.cs`: `rnt.spike7`).** Both rigs, on Rust 2634.289.1 (buildid +25681086), Oxide 2.0.7801 and Carbon 2.0.262, each checked against the latest first. 13 of 14 checks passed on each +rig; the fourteenth could not be run from outside our NPC (below). + +**1. The corpse (D290).** + +- **Today's corpse holds Rust's scientist loot, and the kit's clothing.** The main container gets the prefab's loot + slots (for example a salvaged hammer, tech parts, pistol and rifle ammunition and a bandage; on another kill only 12 + rifle ammunition). The clothing container holds what the NPC wore (the kit's burlap and boonie). Its belt and main + items (the revolver and its ammunition) are not in the corpse. Rust removes the corpse after **600 s**. +- **Both frameworks raise `OnCorpsePopulate(npc, corpse)` for our NPC**, once, after Rust filled the corpse. +- **A subclass cannot fill its own corpse on both frameworks.** Rust's `HumanNPC.ApplyLoot` is `protected` in + Oxide's assembly and `public` in Carbon's (Carbon publicises Rust's assembly), and an override cannot change a + member's access, so one source file cannot override it for both. `CopyInventoryToCorpse` is a read-only virtual + property, with the same risk. **So the build fills the corpse in `OnCorpsePopulate`:** it keeps or empties Rust's + loot, adds the kit's belt and main items for a kit start, then rolls the table. Emptying a corpse and filling it + there worked on both rigs (`scrap×150` alone). +- The kit start could not be measured from the harness, which cannot switch the property from outside. It is built + and tested in the build. + +**2. The crates (D292).** Each of Rust's four crates was spawned at a death spot with Rust's own loot switched off +before it spawned (`initialLootSpawn` false, no refresh), then filled: + +| Crate | Prefab | Slots | Rust's loot at spawn | Ours after | Refills | Gone when emptied | +|---|---|---|---|---|---|---| +| Wooden box | `crate_normal_2` | 6 | none | scrap×200 | no | yes | +| Military crate | `crate_normal` | 6 | none | scrap×200 | no | yes | +| Elite crate | `crate_elite` | 12 | none | scrap×200 | no | yes | +| Locked crate | `codelockedhackablecrate` | 24 | none | scrap×200 | no | yes | + +A table larger than the crate's slots will not fit; the build says so on the profile. + +**3. The locked crate's timer (D297).** Rust's is one server-wide setting, `hackablelockedcrate.requiredhackseconds` +(900 s), and each crate counts its own seconds up to it. **Starting a crate's count where the profile's time +remains gives each crate its own timer:** asked 10 s, it unlocked after 11.5–12.1 s; asked 20 s, after +21.1–22.4 s (Rust ticks once a second). Asked longer than Rust's 900 s (1,800), the count starts below zero +(−898 after 3 s) and still unlocks on time; **what a player's screen shows for a count below zero is not known** +without a client, so the walk checks it. An unhacked locked crate is removed after Rust's own `decaySeconds` +(7,200 s). + +**4. Removing the corpse (D294).** `ResetRemovalTime(5)` removed it after 5.1–5.3 s; a corpse killed at once is gone. + +**What this sets for the build, with nothing to decide:** + +- The corpse is filled in `OnCorpsePopulate`, not by overriding Rust's members. +- A crate spawns with Rust's loot off and no refresh, and is filled with the table. +- The locked crate's timer is its starting count. +- A corpse's time is `ResetRemovalTime`, or it is killed at once. + +**Open for the walk:** a locked crate set longer than 15 minutes, as a player's screen shows it. + +**The build's own questions, answered by the org lead on 2026-10-05 (D300, D301):** + +1. **A "nothing" corpse and the clothes it wore. → D300:** truly empty, clothes too. +2. **How long an unopened crate lasts. → D301:** until it is emptied, as Rust's crates do. + ### Stage 8 — On the map and in the app Event NPCs and bosses on the live map (protocol 11's `map.live`), a live "left: 3/8" on the event page, and the same