feat(rust): Guarded Crate — feed, map, Warden, Place a guarded crate (PLAN_REDESIGNS §14.3.5)
Stage 6 of the §14.3 integration plan, on protocol 14. Spec: docs rust-link/PROTOCOL.md §20.9. - gc.started and gc.ended public, gc.failed staff's; guardedCrate.js reads each row per viewer (the clearer's name for the presence audience, Steam id, position and key for staff, `won` for everyone). - Triggers rust.gc.started and rust.gc.ended (a crate a player cleared only). - The step rust.gc.place: an event from rust.options.gc_events and a world call's location; refused at verify for Guarded Crate absent, NpcSpawn missing (D364), console unavailable, or an unknown event. Kept (D373); the run log's success line says so (D380). Budget rust.gc.crates. - Warden (D375): guarded_won, title category guardedwon, Crates cleared column. - Map marker, server settings line, swagger, engagement manifest, bundle, README. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
@@ -55,6 +55,10 @@ export const FEED_KINDS = Object.freeze([
|
||||
// ending (`dt.ended`) is staff's, and the map shows the chest go.
|
||||
'dt.started',
|
||||
'dt.won',
|
||||
// Guarded Crate (§14.3.5): a crate landing, and one cleared, read per viewer
|
||||
// like a treasure. A timeout (`gc.failed`) is staff's; the map shows it go.
|
||||
'gc.started',
|
||||
'gc.ended',
|
||||
])
|
||||
|
||||
/** The filters the feed offers, and the kinds each one asks the API for. */
|
||||
@@ -69,7 +73,7 @@ export const FILTERS = Object.freeze([
|
||||
},
|
||||
{ id: 'server', label: 'Server', kinds: ['server.wipe', 'server.initialized', 'server.shutdown'] },
|
||||
{ id: 'bosses', label: 'Bosses', kinds: ['npc.boss.spawned', 'npc.boss.died'] },
|
||||
{ id: 'world', label: 'World events', kinds: ['world.event', 'pvp.zone.created', 'dt.started', 'dt.won'] },
|
||||
{ id: 'world', label: 'World events', kinds: ['world.event', 'pvp.zone.created', 'dt.started', 'dt.won', 'gc.started', 'gc.ended'] },
|
||||
])
|
||||
|
||||
export function kindsFor(filterId) {
|
||||
@@ -168,6 +172,11 @@ export function describe(row) {
|
||||
case 'dt.ended':
|
||||
return treasureLine(row.kind, frame)
|
||||
|
||||
case 'gc.started':
|
||||
case 'gc.ended':
|
||||
case 'gc.failed':
|
||||
return guardedLine(row.kind, frame)
|
||||
|
||||
default:
|
||||
return { tone: 'other', actor: name, verb: String((row && row.kind) || 'unknown'), subject: null, detail: '' }
|
||||
}
|
||||
@@ -377,4 +386,25 @@ function treasureLine(kind, frame) {
|
||||
: { tone: 'kill', actor: null, verb: 'The treasure was won', subject: null, detail }
|
||||
}
|
||||
|
||||
/**
|
||||
* Guarded Crate (protocol 14, PLAN_REDESIGNS §14.3.5). The server has read the
|
||||
* frame for this viewer: `grid`, `won` (whether a player killed the last
|
||||
* guard), and the clearer's `name` only for the presence audience (D376).
|
||||
*/
|
||||
function guardedLine(kind, frame) {
|
||||
const detail = frame.grid || ''
|
||||
if (kind === 'gc.started') {
|
||||
return { tone: 'server', actor: null, verb: 'A guarded crate landed', subject: null, detail }
|
||||
}
|
||||
if (kind === 'gc.failed') {
|
||||
return { tone: 'server', actor: null, verb: 'A guarded crate timed out with its guards standing', subject: null, detail }
|
||||
}
|
||||
if (frame.won === false) {
|
||||
return { tone: 'server', actor: null, verb: 'A guarded crate ended with nobody clearing it', subject: null, detail }
|
||||
}
|
||||
return frame.name
|
||||
? { tone: 'kill', actor: frame.name, verb: 'cleared the guarded crate', subject: null, detail }
|
||||
: { tone: 'kill', actor: null, verb: 'The guarded crate was cleared', subject: null, detail }
|
||||
}
|
||||
|
||||
export default { describe, FEED_KINDS, FILTERS, kindsFor }
|
||||
|
||||
Reference in New Issue
Block a user