feat(rust): Rust's own world events — feed, credit rule, triggers, Call a world event (PLAN_REDESIGNS §14.3.1)
The site half of stage 1, on protocol 14: - server/worldEvents.js: the 24 types, D372's four credit rules, the grid from the map's geometry (none off the map), and the per-viewer reading. - The feed serves `world.event` (a public kind): an unknown type is staff's; the event and its grid are public; names only for the presence audience (D376); Steam ids and positions only for staff (D353). A downed one carries `credited`/`creditedCount` by the server's rule. Client lines for all 24 types and a World events filter. - rust_servers.credit_rule (blow, last, most, all; default blow), set on the server's settings. Ingest counts heli_kills (patrol helicopters and Chinooks, D379) and apc_kills from `world.event` by that rule, so the line and Rotorbane / Tankbreaker agree. A protocol-13 tally's heliKills/apcKills still add as before. - The tally's cratesHacked / dropsLooted → crates_hacked / drops_looted; title categories Safecracker and Scavenger (D356); leaderboard columns Crates hacked and Drops looted, sortable (D377). - 24 engagement triggers rust.world.<event> (D355): ceiling everyone, cooldown subject the server and event (crates add where), names only. - server/eventWorldCall.js: rust.world.call, "Call a world event" (risk change, reversible none, budget rust.world.calls). A position for an airdrop or a patrol helicopter only; the Chinook takes none (D378). Server 585/585 (22 new in test/worldEvents.test.js), client 84/84, every check; booted against the pinned core (43 triggers, 12 actions, 10 budgets, 20 option sources), routes.manifest unchanged at 89. Engagement manifest and swagger fragment regenerated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
@@ -548,7 +548,81 @@ const BOSSES = [
|
||||
},
|
||||
]
|
||||
|
||||
const TRIGGERS = Object.freeze([RAID, ...BROADCASTS, ACCOUNT, REWARD, ...CLANS, ...MODERATION, ...NPCS, ...BOSSES])
|
||||
// ── Rust's own world events (protocol 14, PLAN_REDESIGNS §14.3.1, D355) ─────
|
||||
//
|
||||
// One trigger per `world.event` type, `rust.world.<type>`. Public news, as a
|
||||
// boss is: `everyone` is the ceiling and `subscribers` the default. A phase
|
||||
// gate reads the same catalogue, so "advance when Bradley is destroyed" is
|
||||
// `{ on: 'rust.world.bradley.destroyed' }` with no further code.
|
||||
//
|
||||
// The cooldown subject is the server and the type, and the crate types add
|
||||
// where the crate is — one line per oil rig per cooldown, not one per hack on
|
||||
// the map. `player` is the in-game name only, as a boss's killer is; no Steam
|
||||
// id is declared.
|
||||
|
||||
const WORLD_SUBJECT = { name: 'subject', type: 'string', required: true, example: 'main:heli.downed',
|
||||
description: 'The server and the event (and, for a crate, where it is). The cooldown subject; not meant for display.' }
|
||||
const WORLD_GRID = { name: 'grid', type: 'string', required: false, example: 'G7',
|
||||
description: 'The map grid square. Absent when the server has no map geometry yet, or the event is off the map.' }
|
||||
const WORLD_PLAYER = { name: 'player', type: 'string', required: false, example: 'Marisol',
|
||||
description: 'The player it names, by in-game name. Absent when nobody is named.' }
|
||||
const WORLD_CREDITED = [
|
||||
{ name: 'player', type: 'string', required: false, example: 'Marisol and Darrow',
|
||||
description: 'Who is credited, by in-game name and by the server\'s credit rule: "Alice", "Alice and Bob", "Alice, Bob and 2 others". Absent when nobody is.' },
|
||||
{ name: 'credited', type: 'int', required: true, example: 2,
|
||||
description: 'How many players the credit rule names.' },
|
||||
]
|
||||
const WORLD_WHERE = (example, list) => ({ name: 'where', type: 'string', required: true, example,
|
||||
description: `Where it is: ${list}.` })
|
||||
const CRATE_WHERE = WORLD_WHERE('oilrig_large', 'oilrig_large, oilrig_small, cargo, ghostship or chinook (a crate a Chinook dropped)')
|
||||
const WORLD_HARBOUR = { name: 'harbour', type: 'string', required: false, example: 'Harbor',
|
||||
description: 'The harbour, by the game\'s own name for it.' }
|
||||
const WORLD_MONUMENT = { name: 'monument', type: 'string', required: false, example: 'Water Treatment Plant',
|
||||
description: 'The monument it dropped at, by the game\'s own name for it.' }
|
||||
const WORLD_COUNT = { name: 'count', type: 'int', required: true, example: 12,
|
||||
description: 'How many: road Bradleys come several at once.' }
|
||||
|
||||
/** Each world event's label, description and the variables it carries beyond the server's. */
|
||||
const WORLD_EVENTS = [
|
||||
['heli.arrived', 'A patrol helicopter arrived', 'A patrol helicopter flew onto the map.', [WORLD_GRID]],
|
||||
['heli.left', 'The patrol helicopter left', 'A patrol helicopter left the map without being downed.', [WORLD_GRID]],
|
||||
['heli.downed', 'A patrol helicopter was downed', 'Players brought down a patrol helicopter.', [WORLD_GRID, ...WORLD_CREDITED]],
|
||||
['bradley.spawned', 'Bradley is out', 'Launch Site\'s Bradley spawned, or road Bradleys took to the roads.', [WORLD_WHERE('road', 'launch_site or road'), WORLD_COUNT]],
|
||||
['bradley.destroyed', 'A Bradley was destroyed', 'Players destroyed a Bradley APC: Launch Site\'s, a road one, or another.', [WORLD_WHERE('launch_site', 'launch_site, road or other'), WORLD_GRID, ...WORLD_CREDITED]],
|
||||
['cargo.arrived', 'The cargo ship arrived', 'The cargo ship sailed onto the map.', [WORLD_GRID]],
|
||||
['cargo.docking', 'The cargo ship is docking', 'The cargo ship turned in to dock at a harbour.', [WORLD_GRID, WORLD_HARBOUR]],
|
||||
['cargo.docked', 'The cargo ship docked', 'The cargo ship docked at a harbour, with fresh crates.', [WORLD_GRID, WORLD_HARBOUR]],
|
||||
['cargo.undocked', 'The cargo ship cast off', 'The cargo ship left the harbour it was docked at.', [WORLD_GRID, WORLD_HARBOUR]],
|
||||
['cargo.left', 'The cargo ship is leaving', 'The cargo ship turned for the edge of the map.', [WORLD_GRID]],
|
||||
['chinook.arrived', 'A Chinook arrived', 'A Chinook flew onto the map with a locked crate.', [WORLD_GRID]],
|
||||
['chinook.dropped', 'A Chinook dropped its crate', 'A Chinook dropped a locked crate at a monument.', [WORLD_GRID, WORLD_MONUMENT]],
|
||||
['chinook.downed', 'A Chinook was shot down', 'Players shot down a Chinook.', [WORLD_GRID, ...WORLD_CREDITED]],
|
||||
['airdrop.called', 'An airdrop is on its way', 'A cargo plane is bringing an airdrop: called by a supply signal, or by the schedule.', [WORLD_GRID, WORLD_PLAYER]],
|
||||
['airdrop.landed', 'An airdrop landed', 'An airdrop reached the ground.', [WORLD_GRID, WORLD_PLAYER]],
|
||||
['airdrop.looted', 'An airdrop was looted', 'Somebody opened an airdrop first.', [WORLD_GRID, WORLD_PLAYER]],
|
||||
['crate.hack.started', 'A locked crate is being hacked', 'Somebody started hacking a locked crate. On an oil rig this also calls its scientists.', [CRATE_WHERE, WORLD_GRID, WORLD_PLAYER]],
|
||||
['crate.unlocked', 'A locked crate unlocked', 'A hacked locked crate finished unlocking.', [CRATE_WHERE, WORLD_GRID, WORLD_PLAYER]],
|
||||
['crate.looted', 'A locked crate was looted', 'Somebody opened an unlocked locked crate first.', [CRATE_WHERE, WORLD_GRID, WORLD_PLAYER]],
|
||||
['vendor.arrived', 'The Travelling Vendor arrived', 'The Travelling Vendor drove onto the map.', [WORLD_GRID]],
|
||||
['vendor.left', 'The Travelling Vendor left', 'The Travelling Vendor left the map.', [WORLD_GRID]],
|
||||
['satellite.crashed', 'A satellite crashed', 'A satellite came down, and its crates are burning.', [WORLD_GRID]],
|
||||
['deepsea.opened', 'The Deep Sea opened', 'The Deep Sea is open: its ghost ships and islands can be reached.', []],
|
||||
['deepsea.closed', 'The Deep Sea closed', 'The Deep Sea closed.', []],
|
||||
]
|
||||
|
||||
const WORLD = WORLD_EVENTS.map(([event, label, description, vars]) => ({
|
||||
id: `rust.world.${event}`,
|
||||
label,
|
||||
description,
|
||||
kind: 'event',
|
||||
subjectKey: 'subject',
|
||||
audience: 'subscribers',
|
||||
ceiling: 'everyone',
|
||||
version: V1,
|
||||
variables: [...SERVER, ...HEADLINE, WORLD_SUBJECT, ...vars],
|
||||
}))
|
||||
|
||||
const TRIGGERS = Object.freeze([RAID, ...BROADCASTS, ACCOUNT, REWARD, ...CLANS, ...MODERATION, ...NPCS, ...BOSSES, ...WORLD])
|
||||
|
||||
const TRIGGER_IDS = Object.freeze(Object.fromEntries(TRIGGERS.map((t) => [t.id, t.id])))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user