feat(rust): NPC profile and placement routes, the event picker, triggers and titles (runicnpc stage 4)

Admin: /admin/rust/npcs for profiles (create, change, delete, restore a
replaced one, push now) and each server's placements (list, add from a map
point, change, remove, rename, respawn). Public: the profiles a leaderboard
ranks by, one profile's ranking counted as the profile says (D247, D250), and
one player's kills by profile (D252). Player: your own kills by profile.

The Place NPCs step offers the site's profiles first, then Rust's own
(D243). rust.npc.died and rust.npc.health are triggers a phase can wait on.
A title rule can rank a profile's kills. Swagger fragment, engagement and
route manifests regenerated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
2026-09-30 04:34:43 -05:00
parent 564a234890
commit fa16f0ad2e
20 changed files with 3224 additions and 24 deletions

View File

@@ -92,6 +92,45 @@ rustRouter.get(
servers.listLeaderboard,
)
rustRouter.get(
'/servers/:id/npc-profiles',
// #swagger.tags = ['Public · Rust']
// #swagger.summary = 'The NPC profiles a server’s leaderboard can rank by'
// #swagger.description = 'The site’s RunicNPC profiles pushed to this server (docs/runicnpc/PLAN.md stage 4, D250): each one’s id, its name, a label (the first of its NPC names), and how its kills are counted (D247) — `server` (this server’s kills of that name), `name` (every server’s kills of that name) or `profile` (this site profile’s, wherever it was pushed). Empty on a site without RunicNPC.'
// #swagger.parameters['id'] = { in: 'path', required: true, description: 'The server’s slug', schema: { type: 'string' } }
/* #swagger.responses[200] = { description: 'The profiles', content: { "application/json": { schema: { $ref: "#/components/schemas/RustNpcBoardProfiles" } } } } */
siteMode,
servers.listNpcProfiles,
)
rustRouter.get(
'/servers/:id/npc-leaderboard',
// #swagger.tags = ['Public · Rust']
// #swagger.summary = 'Who has killed the most NPCs of one profile'
// #swagger.description = 'One RunicNPC profile’s kills, most first, counted as the profile says (D247, D250). Per wipe when `wipe` is given, all time otherwise. A profile counted across servers reaches every server’s current wipe when `wipe` is this server’s current one, and only this server’s rows for an older wipe. Each row carries the chat `titles` the player holds now, as the main leaderboard’s do.'
// #swagger.parameters['id'] = { in: 'path', required: true, description: 'The server’s slug', schema: { type: 'string' } }
// #swagger.parameters['profile'] = { in: 'query', required: true, description: 'The profile’s id, from npc-profiles', schema: { type: 'integer' } }
// #swagger.parameters['wipe'] = { in: 'query', required: false, description: 'Restrict to one wipe id', schema: { type: 'string' } }
// #swagger.parameters['limit'] = { in: 'query', required: false, description: 'Rows to return, capped at 200', schema: { type: 'integer' } }
/* #swagger.responses[200] = { description: 'The ranking', content: { "application/json": { schema: { $ref: "#/components/schemas/RustNpcLeaderboard" } } } } */
/* #swagger.responses[404] = { description: 'No such profile on this server' } */
siteMode,
servers.npcLeaderboard,
)
rustRouter.get(
'/servers/:id/players/:steamId/npc-kills',
// #swagger.tags = ['Public · Rust']
// #swagger.summary = 'One player’s kills of each NPC profile on a server'
// #swagger.description = 'What opening a leaderboard row shows (D252): the player’s kills of RunicNPC’s NPCs by profile on this server, for the wipe the page shows (`wipe`), or all time without it. Counted by profile name on this server, whatever a profile’s leaderboard counts; labelled with the site’s name for the profile where it has one. The same numbers the leaderboard already makes public, split by profile.'
// #swagger.parameters['id'] = { in: 'path', required: true, description: 'The server’s slug', schema: { type: 'string' } }
// #swagger.parameters['steamId'] = { in: 'path', required: true, description: 'The player’s Steam id', schema: { type: 'string' } }
// #swagger.parameters['wipe'] = { in: 'query', required: false, description: 'Restrict to one wipe id', schema: { type: 'string' } }
/* #swagger.responses[200] = { description: 'The kills', content: { "application/json": { schema: { $ref: "#/components/schemas/RustNpcPlayerKills" } } } } */
siteMode,
servers.playerNpcKills,
)
rustRouter.get(
'/servers/:id/wipes',
// #swagger.tags = ['Public · Rust']