diff --git a/README.md b/README.md index 80f29cf..a2841e0 100644 --- a/README.md +++ b/README.md @@ -144,6 +144,11 @@ one now, kept when the run ends: an airdrop or a patrol helicopter can be sent t alone — and the run log says so. Refused where Guarded Crate or NpcSpawn is absent, where the event is not one the server has, or where the framework cannot run its console command (`docs/modules/rust/PLAN_REDESIGNS.md` §14.3.5). +- **ZLevels Remastered** (nivex, 3.3.1). The servers page says whether it is loaded, and that is all: + its levels are never read, and the leaderboard has no column for them. Its XP boosts are its own + permissions (`zlevelsremastered.vip1`–`vip3`), so a *Double XP weekend* is a **Permissions until the event ends** + step, which takes effect at once and is taken back when the run ends + (`docs/modules/rust/PLAN_REDESIGNS.md` §14.3.6). News and event lines said in chat can wear one styled group's title and colours — the **announcement voice**, chosen in Admin → Rust servers. The line is said by the bridge plugin with no diff --git a/client/src/routes/admin/ChatTitles.jsx b/client/src/routes/admin/ChatTitles.jsx index e40622e..8ca53f3 100644 --- a/client/src/routes/admin/ChatTitles.jsx +++ b/client/src/routes/admin/ChatTitles.jsx @@ -265,7 +265,18 @@ export function integrationsLine(r) { const withTruePve = r.integrations.truePve ? `${withBackpacks} ${truePveLine(r.integrations.truePve)}` : withBackpacks const withDynamicPvp = r.integrations.dynamicPvp ? `${withTruePve} ${dynamicPvpLine(r.integrations.dynamicPvp)}` : withTruePve const withDt = r.integrations.dangerousTreasures ? `${withDynamicPvp} ${dangerousTreasuresLine(r.integrations.dangerousTreasures, r.console)}` : withDynamicPvp - return r.integrations.guardedCrate ? `${withDt} ${guardedCrateLine(r.integrations.guardedCrate, r.console)}` : withDt + const withGc = r.integrations.guardedCrate ? `${withDt} ${guardedCrateLine(r.integrations.guardedCrate, r.console)}` : withDt + return r.integrations.zLevels ? `${withGc} ${zLevelsLine(r.integrations.zLevels)}` : withGc +} + +/** + * ZLevels Remastered (PLAN_REDESIGNS §14.3.6), from protocol 14. Only whether it is + * loaded: its levels never reach the site (D374), and its XP boosts are permissions + * an event lends like any other. + */ +export function zLevelsLine(z) { + if (!z.loaded) return 'ZLevels is not loaded — there are no XP boosts for an event to lend.' + return `${`ZLevels ${z.version || ''}`.trim()} is loaded — an event may lend its XP boosts (zlevelsremastered.vip1 to vip3) as permissions.` } /** diff --git a/server/swagger/doc.js b/server/swagger/doc.js index 4f10d45..9af6a60 100644 --- a/server/swagger/doc.js +++ b/server/swagger/doc.js @@ -289,6 +289,11 @@ module.exports = { events: { type: 'array', items: { type: 'string' }, example: ['Easy', 'Medium', 'Hard', 'Elite'] }, }, }, + zLevels: { + type: 'object', + description: 'Protocol 14 (PLAN_REDESIGNS §14.3.6, D374): whether ZLevels Remastered is loaded, and nothing more. Its levels are never read; its XP boosts are permissions an event lends through a Permissions until the event ends step.', + properties: { loaded: { type: 'boolean', example: true }, version: { type: 'string', example: '3.3.1' } }, + }, }, }, console: { diff --git a/swagger-fragment.json b/swagger-fragment.json index 13c922a..a202017 100644 --- a/swagger-fragment.json +++ b/swagger-fragment.json @@ -5579,6 +5579,50 @@ } } } + }, + "zLevels": { + "type": "object", + "properties": { + "type": { + "type": "string", + "example": "object" + }, + "description": { + "type": "string", + "example": "Protocol 14 (PLAN_REDESIGNS §14.3.6, D374): whether ZLevels Remastered is loaded, and nothing more. Its levels are never read; its XP boosts are permissions an event lends through a Permissions until the event ends step." + }, + "properties": { + "type": "object", + "properties": { + "loaded": { + "type": "object", + "properties": { + "type": { + "type": "string", + "example": "boolean" + }, + "example": { + "type": "boolean", + "example": true + } + } + }, + "version": { + "type": "object", + "properties": { + "type": { + "type": "string", + "example": "string" + }, + "example": { + "type": "string", + "example": "3.3.1" + } + } + } + } + } + } } } }