feat: TruePVE in the zone step (PLAN_REDESIGNS §14.3.2, WIP)
Stage 3 of the §14.3 integration plan (D362, D368): world.zone's optional
truePve ({exclude: true} or {ruleset}), all or nothing, unmapped on every
path that erases one of our zones, with truepve.json for erases while
TruePVE is away. Docs and PRs still to come.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
@@ -62,6 +62,7 @@ const ZONE_MESSAGE_MAX = 256
|
||||
const DOME_STACK_MAX = 10
|
||||
/** A blank stack: 3 spheres, chosen by eye at noon in RunicNPC's 9d player session (D326). */
|
||||
const DOME_STACK_DEFAULT = 3
|
||||
const TRUEPVE_RULESET_MAX = 64
|
||||
|
||||
/**
|
||||
* ZoneDomes' own sphere types, by the number its API takes (D194). Only Standard
|
||||
@@ -127,6 +128,10 @@ const PERMANENT = new Set([
|
||||
// the server (`escort-offline`) is left to retry: they may join.
|
||||
'no-zone',
|
||||
'runicnpc-old',
|
||||
// PLAN_REDESIGNS §14.3.2 (D368): no TruePVE on the server, or no ruleset of
|
||||
// the name the step typed. Only the call can tell the second.
|
||||
'truepve-missing',
|
||||
'truepve-ruleset',
|
||||
])
|
||||
|
||||
const BUDGETS = [
|
||||
@@ -165,6 +170,28 @@ function domeMissing(mods) {
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* The zone's TruePVE mapping (§14.3.2, D368): `{ exclude: true }` for "no PvP
|
||||
* protection", `{ ruleset }` for a ruleset staff typed, or null for neither.
|
||||
* Both is refused, and either is refused where the server said it has no
|
||||
* TruePVE. A server that has not said is left to the plugin (`truepve-missing`),
|
||||
* as a dome is. Whether the ruleset exists is the plugin's to answer.
|
||||
*/
|
||||
function truePveMapping(params, mods) {
|
||||
const raw = params.noPvpProtection
|
||||
const exclude = raw === true || raw === 'true'
|
||||
const ruleset = params.pvpRuleset === undefined || params.pvpRuleset === null ? '' : String(params.pvpRuleset).trim()
|
||||
if (exclude && ruleset) {
|
||||
return { ok: false, error: 'a zone has no PvP protection or a TruePVE ruleset, not both' }
|
||||
}
|
||||
if (ruleset.length > TRUEPVE_RULESET_MAX) {
|
||||
return { ok: false, error: `a TruePVE ruleset's name is at most ${TRUEPVE_RULESET_MAX} characters` }
|
||||
}
|
||||
if (!exclude && !ruleset) return { ok: true, wire: null }
|
||||
if (mods && mods.truePve && !mods.truePve.loaded) return { ok: false, error: 'TruePVE is not installed on this server' }
|
||||
return { ok: true, wire: exclude ? { exclude: true } : { ruleset } }
|
||||
}
|
||||
|
||||
/** A number param, or undefined when left blank. */
|
||||
function num(raw) {
|
||||
if (raw === undefined || raw === null || raw === '') return undefined
|
||||
@@ -731,6 +758,20 @@ const ACTIONS = [
|
||||
'A ZoneDomes dome over the zone. "standard" is a full shaded dome; the colours show only where the sphere meets the ground or a building. Needs ZoneDomes and RunicGatewayDomes.cs on the server. Blank is no dome.',
|
||||
},
|
||||
{ name: 'domeStack', type: 'int', required: false, example: DOME_STACK_DEFAULT, description: `How many spheres the dome stacks, 1 to ${DOME_STACK_MAX}; more is darker. Left blank, ${DOME_STACK_DEFAULT}.` },
|
||||
{
|
||||
name: 'noPvpProtection',
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
example: true,
|
||||
description: 'No PvP protection in this zone: TruePVE leaves it alone, so players can hurt each other inside. Needs TruePVE on the server. Not with a ruleset.',
|
||||
},
|
||||
{
|
||||
name: 'pvpRuleset',
|
||||
type: 'string',
|
||||
required: false,
|
||||
example: 'pvp-arena',
|
||||
description: `Or this TruePVE ruleset in the zone, by its name in TruePVE's config (names are case-sensitive, up to ${TRUEPVE_RULESET_MAX} characters). A name TruePVE does not have fails the step when it runs. Needs TruePVE on the server.`,
|
||||
},
|
||||
],
|
||||
|
||||
async perform({ runId, idempotencyKey, params, verify }) {
|
||||
@@ -785,6 +826,10 @@ const ACTIONS = [
|
||||
dome = { type: kind.type, stack: stack === undefined ? DOME_STACK_DEFAULT : stack }
|
||||
}
|
||||
|
||||
// §14.3.2: TruePVE's rules in the zone, where the server said it has TruePVE.
|
||||
const truePve = truePveMapping(params, mods)
|
||||
if (!truePve.ok) return { ok: false, retry: false, error: truePve.error }
|
||||
|
||||
// The dry run stops here, and has checked everything it can without the
|
||||
// game. It does not ask whether the monument exists: a step authored for
|
||||
// next wipe's map would fail every dry run until the wipe.
|
||||
@@ -810,6 +855,7 @@ const ACTIONS = [
|
||||
...(hasMessage ? { delivery } : {}),
|
||||
...(format ? { format } : {}),
|
||||
...(dome ? { dome } : {}),
|
||||
...(truePve.wire ? { truePve: truePve.wire } : {}),
|
||||
},
|
||||
'zone',
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user