fix(admin): no decision numbers in admin copy, and "1 player holds one"
Twenty-four strings an administrator reads carried an internal decision number - "(D189)", "(D198)", "(D257, D270)" and the like: the permission screen's group Servers tab and a chip tooltip, the event editor's step field and budget descriptions, two option sources, a command reward's skipped note, and five NPC profile validation errors. A D-number means something to the plan, never to an operator. Each is removed, and the sentence around it left as it was. The developer-facing swagger descriptions and code comments keep theirs. The servers page's chat-title line read "Last pushed: 1 player hold one." It now says "holds" for one. Two tests pinned the old wording and now assert the text instead. Found while writing the runicgateway.com Rust docs (its PLAN.md §16, phase 15). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
@@ -49,7 +49,7 @@ export function titlesSummary(titles, push, categories) {
|
|||||||
const rules = (titles && titles.rules) || []
|
const rules = (titles && titles.rules) || []
|
||||||
if (!rules.length) return 'No chat titles.'
|
if (!rules.length) return 'No chat titles.'
|
||||||
const list = rules.map((r) => `${ruleTitle(r, categories)} (top ${r.topN} ${statOf(categories, r.stat).label.toLowerCase()})`).join(', ')
|
const list = rules.map((r) => `${ruleTitle(r, categories)} (top ${r.topN} ${statOf(categories, r.stat).label.toLowerCase()})`).join(', ')
|
||||||
const shown = push ? ` Last pushed: ${push.count} player${push.count === 1 ? '' : 's'} hold one${push.betterChat ? '' : ' — BetterChat is not loaded, so they are not showing in game yet'}.` : ''
|
const shown = push ? ` Last pushed: ${push.count} player${push.count === 1 ? ' holds' : 's hold'} one${push.betterChat ? '' : ' — BetterChat is not loaded, so they are not showing in game yet'}.` : ''
|
||||||
return `Chat titles: ${list}.${shown}`
|
return `Chat titles: ${list}.${shown}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -542,7 +542,7 @@ function GroupPanel({ view, group, act, busy, chatFields }) {
|
|||||||
{tab === 'servers' && (
|
{tab === 'servers' && (
|
||||||
<div className="sans" style={{ fontSize: '0.82rem' }}>
|
<div className="sans" style={{ fontSize: '0.82rem' }}>
|
||||||
<p className="dim" style={{ margin: '0 0 8px' }}>
|
<p className="dim" style={{ margin: '0 0 8px' }}>
|
||||||
A group belongs to one server unless it is shared (D189). A shared group carries the same permissions and players on every server
|
A group belongs to one server unless it is shared. A shared group carries the same permissions and players on every server
|
||||||
it is on; a change made in one game gives that server its own copy.
|
it is on; a change made in one game gives that server its own copy.
|
||||||
</p>
|
</p>
|
||||||
<label style={{ display: 'block', marginBottom: 6 }}>
|
<label style={{ display: 'block', marginBottom: 6 }}>
|
||||||
@@ -791,7 +791,7 @@ export default function Permissions() {
|
|||||||
: sync.state === 'failed' ? <Chip tone="bad" title={sync.error || ''}>last sync failed</Chip>
|
: sync.state === 'failed' ? <Chip tone="bad" title={sync.error || ''}>last sync failed</Chip>
|
||||||
: <Chip tone="warn">waiting for a sync</Chip>
|
: <Chip tone="warn">waiting for a sync</Chip>
|
||||||
)}
|
)}
|
||||||
{sync && !sync.importedAt && <Chip tone="warn" title="The first sync imports everything on the server (D198)">not imported yet</Chip>}
|
{sync && !sync.importedAt && <Chip tone="warn" title="The first sync imports everything on the server">not imported yet</Chip>}
|
||||||
<button type="button" className="btn btn-ghost" disabled={busy} onClick={() => act(() => api.adminPermissions.sync(server.id))}>Sync now</button>
|
<button type="button" className="btn btn-ghost" disabled={busy} onClick={() => act(() => api.adminPermissions.sync(server.id))}>Sync now</button>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -169,7 +169,7 @@ function replyDetail(data) {
|
|||||||
...(replies.length
|
...(replies.length
|
||||||
? { replies: replies.map((r) => ({ command: String(r.command || ''), reply: String(r.reply || ''), ...(r.steamId ? { steamId: String(r.steamId) } : {}) })) }
|
? { replies: replies.map((r) => ({ command: String(r.command || ''), reply: String(r.reply || ''), ...(r.steamId ? { steamId: String(r.steamId) } : {}) })) }
|
||||||
: {}),
|
: {}),
|
||||||
...(skippedCount ? { skipped, skippedCount, skippedNote: 'skipped: not on the server when the reward ran (D383)' } : {}),
|
...(skippedCount ? { skipped, skippedCount, skippedNote: 'skipped: not on the server when the reward ran' } : {}),
|
||||||
...(data.repeat ? { repeat: true, note: 'answered from the first attempt; nothing was run twice' } : {}),
|
...(data.repeat ? { repeat: true, note: 'answered from the first attempt; nothing was run twice' } : {}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -452,7 +452,7 @@ const OPTION_SOURCES = [
|
|||||||
{
|
{
|
||||||
id: 'rust.options.servers',
|
id: 'rust.options.servers',
|
||||||
label: 'Rust servers',
|
label: 'Rust servers',
|
||||||
description: 'Every enabled server on this site. A lease holds a value on one of them (D73).',
|
description: 'Every enabled server on this site. A lease holds a value on one of them.',
|
||||||
async resolve() {
|
async resolve() {
|
||||||
const list = await servers.listForPolling()
|
const list = await servers.listForPolling()
|
||||||
return list.map((s) => ({ value: s.id, label: s.name || s.id }))
|
return list.map((s) => ({ value: s.id, label: s.name || s.id }))
|
||||||
|
|||||||
@@ -356,7 +356,7 @@ const participationOpen = {
|
|||||||
{ name: 'server', type: 'string', required: true, example: 'main', source: 'rust.options.servers',
|
{ name: 'server', type: 'string', required: true, example: 'main', source: 'rust.options.servers',
|
||||||
description: 'Which server counts.' },
|
description: 'Which server counts.' },
|
||||||
{ name: 'zone', type: 'string', required: false, example: 'Airfield brawl', source: 'rust.options.runzones',
|
{ name: 'zone', type: 'string', required: false, example: 'Airfield brawl', source: 'rust.options.runzones',
|
||||||
description: 'The name an earlier "Open a zone" step of this run gave its zone. Left blank, the whole server counts (D100).' },
|
description: 'The name an earlier "Open a zone" step of this run gave its zone. Left blank, the whole server counts.' },
|
||||||
{ name: 'score', type: 'string', required: true, example: 'both', source: 'rust.options.scoremodes',
|
{ name: 'score', type: 'string', required: true, example: 'both', source: 'rust.options.scoremodes',
|
||||||
description: 'What earns a place: seconds present, kills, or both.' },
|
description: 'What earns a place: seconds present, kills, or both.' },
|
||||||
{ name: 'killsOf', type: 'string', required: false, example: 'npcs', source: 'rust.options.killsof',
|
{ name: 'killsOf', type: 'string', required: false, example: 'npcs', source: 'rust.options.killsof',
|
||||||
@@ -538,7 +538,7 @@ const kitEntitle = {
|
|||||||
budgetMs: BUDGET_MS,
|
budgetMs: BUDGET_MS,
|
||||||
params: [
|
params: [
|
||||||
{ name: 'kit', type: 'string', required: true, example: 'main/vip-starter', source: 'rust.options.kits',
|
{ name: 'kit', type: 'string', required: true, example: 'main/vip-starter', source: 'rust.options.kits',
|
||||||
description: 'The kit, as server/kit. The reward reaches only that server (D102).' },
|
description: 'The kit, as server/kit. The reward reaches only that server.' },
|
||||||
{ name: 'recipients', type: 'string', required: true, example: 'top', source: 'rust.options.recipientmodes',
|
{ name: 'recipients', type: 'string', required: true, example: 'top', source: 'rust.options.recipientmodes',
|
||||||
description: 'Who gets it: everyone who scored, the top N, a score of at least X, N drawn at random, the top X per cent — or every linked player of the server, with no tally at all (a kit weekend).' },
|
description: 'Who gets it: everyone who scored, the top N, a score of at least X, N drawn at random, the top X per cent — or every linked player of the server, with no tally at all (a kit weekend).' },
|
||||||
{ name: 'count', type: 'float', required: false, example: 3,
|
{ name: 'count', type: 'float', required: false, example: 3,
|
||||||
@@ -1172,13 +1172,13 @@ const announce = {
|
|||||||
budgetMs: BUDGET_MS,
|
budgetMs: BUDGET_MS,
|
||||||
params: [
|
params: [
|
||||||
{ name: 'server', type: 'string', required: true, example: 'main', source: 'rust.options.chatservers',
|
{ name: 'server', type: 'string', required: true, example: 'main', source: 'rust.options.chatservers',
|
||||||
description: 'Which server, or * for every server (D105).' },
|
description: 'Which server, or * for every server.' },
|
||||||
{ name: 'message', type: 'string', required: true, example: 'The airfield brawl starts in five minutes!',
|
{ name: 'message', type: 'string', required: true, example: 'The airfield brawl starts in five minutes!',
|
||||||
description: `The line, up to ${MAX_CHAT} characters.` },
|
description: `The line, up to ${MAX_CHAT} characters.` },
|
||||||
// Optional, and the action stays version 1: a bump would stop every step
|
// Optional, and the action stays version 1: a bump would stop every step
|
||||||
// already written from dispatching until somebody re-saved it (§33.2).
|
// already written from dispatching until somebody re-saved it (§33.2).
|
||||||
{ name: 'delivery', type: 'string', required: false, example: 'chat', source: 'rust.options.delivery',
|
{ name: 'delivery', type: 'string', required: false, example: 'chat', source: 'rust.options.delivery',
|
||||||
description: 'chat (the default), or popup — which needs PopupNotifications on the server, and is refused with a reason where it is missing (D141).' },
|
description: 'chat (the default), or popup — which needs PopupNotifications on the server, and is refused with a reason where it is missing.' },
|
||||||
],
|
],
|
||||||
|
|
||||||
// One per server reached. `*` is priced at the enabled servers when core asks,
|
// One per server reached. `*` is priced at the enabled servers when core asks,
|
||||||
@@ -1361,7 +1361,7 @@ const OPTION_SOURCES = [
|
|||||||
{ value: 'npcs', label: 'NPCs, animals included' },
|
{ value: 'npcs', label: 'NPCs, animals included' },
|
||||||
{ value: 'both', label: 'Players and NPCs' },
|
{ value: 'both', label: 'Players and NPCs' },
|
||||||
]),
|
]),
|
||||||
fixed('rust.options.recipientmodes', 'Recipients', 'Who a reward goes to (D101).', [
|
fixed('rust.options.recipientmodes', 'Recipients', 'Who a reward goes to.', [
|
||||||
{ value: 'everyone', label: 'Everyone who scored' },
|
{ value: 'everyone', label: 'Everyone who scored' },
|
||||||
{ value: 'top', label: 'The top N (ties in)' },
|
{ value: 'top', label: 'The top N (ties in)' },
|
||||||
{ value: 'minScore', label: 'A score of at least X' },
|
{ value: 'minScore', label: 'A score of at least X' },
|
||||||
@@ -1369,7 +1369,7 @@ const OPTION_SOURCES = [
|
|||||||
{ value: 'topPercent', label: 'The top X per cent (ties in)' },
|
{ value: 'topPercent', label: 'The top X per cent (ties in)' },
|
||||||
{ value: 'linked', label: 'Every linked player of the server (no tally)' },
|
{ value: 'linked', label: 'Every linked player of the server (no tally)' },
|
||||||
]),
|
]),
|
||||||
fixed('rust.options.delivery', 'Delivery', 'Where a line goes (D141).', [
|
fixed('rust.options.delivery', 'Delivery', 'Where a line goes.', [
|
||||||
{ value: 'chat', label: 'Chat' },
|
{ value: 'chat', label: 'Chat' },
|
||||||
{ value: 'popup', label: 'A popup — needs PopupNotifications on the server' },
|
{ value: 'popup', label: 'A popup — needs PopupNotifications on the server' },
|
||||||
]),
|
]),
|
||||||
|
|||||||
@@ -146,13 +146,13 @@ const BUDGETS = [
|
|||||||
id: 'rust.npcs',
|
id: 'rust.npcs',
|
||||||
label: 'NPCs placed',
|
label: 'NPCs placed',
|
||||||
unit: 'NPCs',
|
unit: 'NPCs',
|
||||||
description: 'Scientists and guards an event puts in the world — its own dial, so fights can be capped apart from loot (D89).',
|
description: 'Scientists and guards an event puts in the world — its own dial, so fights can be capped apart from loot.',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'rust.zone.minutes',
|
id: 'rust.zone.minutes',
|
||||||
label: 'Zone time',
|
label: 'Zone time',
|
||||||
unit: 'minutes',
|
unit: 'minutes',
|
||||||
description: 'How long the zones an event opens stand, added up. Every zone declares its minutes, and the game erases it when they run out (D96).',
|
description: 'How long the zones an event opens stand, added up. Every zone declares its minutes, and the game erases it when they run out.',
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -626,7 +626,7 @@ const NPC_ORDER_PARAMS = [
|
|||||||
required: false,
|
required: false,
|
||||||
example: '76561198000000001',
|
example: '76561198000000001',
|
||||||
description:
|
description:
|
||||||
"A player's Steam id, or a {placeholder} from the run's start params: the NPCs keep close to that player and fight whoever attacks them, and walk back to their spot if the player dies or leaves (D269). The player must be on the server. RunicNPC profiles only.",
|
"A player's Steam id, or a {placeholder} from the run's start params: the NPCs keep close to that player and fight whoever attacks them, and walk back to their spot if the player dies or leaves. The player must be on the server. RunicNPC profiles only.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'allyClan',
|
name: 'allyClan',
|
||||||
@@ -635,7 +635,7 @@ const NPC_ORDER_PARAMS = [
|
|||||||
source: 'rust.options.clans',
|
source: 'rust.options.clans',
|
||||||
example: 'main/1234567',
|
example: 'main/1234567',
|
||||||
description:
|
description:
|
||||||
"A clan on the same server: the NPCs never target its members and defend them and what they own (D257). RunicNPC profiles only; give this or allyTeamOf, not both.",
|
"A clan on the same server: the NPCs never target its members and defend them and what they own. RunicNPC profiles only; give this or allyTeamOf, not both.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'allyTeamOf',
|
name: 'allyTeamOf',
|
||||||
@@ -643,7 +643,7 @@ const NPC_ORDER_PARAMS = [
|
|||||||
required: false,
|
required: false,
|
||||||
example: '76561198000000001',
|
example: '76561198000000001',
|
||||||
description:
|
description:
|
||||||
"A player's Steam id, or a {placeholder}: the NPCs are allied to that player and their team (D257, D270). RunicNPC profiles only.",
|
"A player's Steam id, or a {placeholder}: the NPCs are allied to that player and their team. RunicNPC profiles only.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'tether',
|
name: 'tether',
|
||||||
@@ -651,7 +651,7 @@ const NPC_ORDER_PARAMS = [
|
|||||||
required: false,
|
required: false,
|
||||||
example: true,
|
example: true,
|
||||||
description:
|
description:
|
||||||
'Keep the NPCs inside the zone this event made around the point (D272): a Make a zone step must come first. RunicNPC profiles only.',
|
'Keep the NPCs inside the zone this event made around the point: a Make a zone step must come first. RunicNPC profiles only.',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Runicnpc stage 7 (D296): on by default; a step older than the switch drops loot as before.
|
// Runicnpc stage 7 (D296): on by default; a step older than the switch drops loot as before.
|
||||||
@@ -660,7 +660,7 @@ const NPC_ORDER_PARAMS = [
|
|||||||
required: false,
|
required: false,
|
||||||
example: true,
|
example: true,
|
||||||
description:
|
description:
|
||||||
"Whether the NPCs drop their profile's loot table when killed (D296). On unless switched off; off, their corpse still holds what the profile starts it with. An event's own rewards are separate. RunicNPC profiles only.",
|
"Whether the NPCs drop their profile's loot table when killed. On unless switched off; off, their corpse still holds what the profile starts it with. An event's own rewards are separate. RunicNPC profiles only.",
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -908,7 +908,7 @@ const OPTION_SOURCES = [
|
|||||||
// cached list would offer monuments that are not there any more.
|
// cached list would offer monuments that are not there any more.
|
||||||
id: 'rust.options.monuments',
|
id: 'rust.options.monuments',
|
||||||
label: 'Monuments',
|
label: 'Monuments',
|
||||||
description: "Each server's monuments on its current map. A kind that repeats is numbered, #1 first (D93).",
|
description: "Each server's monuments on its current map. A kind that repeats is numbered, #1 first.",
|
||||||
searchable: true,
|
searchable: true,
|
||||||
async resolve({ q } = {}) {
|
async resolve({ q } = {}) {
|
||||||
const term = String(q || '').trim().toLowerCase()
|
const term = String(q || '').trim().toLowerCase()
|
||||||
@@ -983,7 +983,7 @@ const OPTION_SOURCES = [
|
|||||||
{
|
{
|
||||||
id: 'rust.options.domes',
|
id: 'rust.options.domes',
|
||||||
label: 'Domes',
|
label: 'Domes',
|
||||||
description: "ZoneDomes' dome colours (D194). A coloured dome shows only where it meets terrain or a structure.",
|
description: "ZoneDomes' dome colours. A coloured dome shows only where it meets terrain or a structure.",
|
||||||
async resolve() {
|
async resolve() {
|
||||||
return DOMES.map((d) => ({ value: d.value, label: d.label }))
|
return DOMES.map((d) => ({ value: d.value, label: d.label }))
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ function checkFactions(input) {
|
|||||||
}
|
}
|
||||||
if (!RELATIONS.includes(relation)) return { ok: false, error: `factions[${i}]: '${relation}' is not hostile, neutral or allied` }
|
if (!RELATIONS.includes(relation)) return { ok: false, error: `factions[${i}]: '${relation}' is not hostile, neutral or allied` }
|
||||||
const key = pairKey(a, b)
|
const key = pairKey(a, b)
|
||||||
if (seen.has(key)) return { ok: false, error: `factions[${i}]: ${a} and ${b} are given twice (one row is both ways, D268)` }
|
if (seen.has(key)) return { ok: false, error: `factions[${i}]: ${a} and ${b} are given twice (one row is both ways)` }
|
||||||
seen.add(key)
|
seen.add(key)
|
||||||
rows.push(a < b ? { a, b, relation } : { a: b, b: a, relation })
|
rows.push(a < b ? { a, b, relation } : { a: b, b: a, relation })
|
||||||
}
|
}
|
||||||
@@ -173,7 +173,7 @@ function checkBody(input) {
|
|||||||
if (names.some((n) => n.length > DISPLAY_NAME_MAX)) return { ok: false, error: `names: each at most ${DISPLAY_NAME_MAX} characters` }
|
if (names.some((n) => n.length > DISPLAY_NAME_MAX)) return { ok: false, error: `names: each at most ${DISPLAY_NAME_MAX} characters` }
|
||||||
|
|
||||||
const kits = Array.isArray(b.kits) ? [...new Set(b.kits.map((k) => String(k === null || k === undefined ? '' : k).trim()).filter(Boolean))] : null
|
const kits = Array.isArray(b.kits) ? [...new Set(b.kits.map((k) => String(k === null || k === undefined ? '' : k).trim()).filter(Boolean))] : null
|
||||||
if (!kits || kits.length === 0) return { ok: false, error: 'kits: give at least one; Kits is how an NPC is equipped (D217)' }
|
if (!kits || kits.length === 0) return { ok: false, error: 'kits: give at least one; Kits is how an NPC is equipped' }
|
||||||
if (kits.length > KITS_MAX) return { ok: false, error: `kits: at most ${KITS_MAX}` }
|
if (kits.length > KITS_MAX) return { ok: false, error: `kits: at most ${KITS_MAX}` }
|
||||||
|
|
||||||
const prefab = String(b.prefab || '').trim()
|
const prefab = String(b.prefab || '').trim()
|
||||||
@@ -242,8 +242,8 @@ function checkBody(input) {
|
|||||||
const hasBoss = b.boss !== undefined && b.boss !== null
|
const hasBoss = b.boss !== undefined && b.boss !== null
|
||||||
const hasUse = b.use !== undefined && b.use !== null
|
const hasUse = b.use !== undefined && b.use !== null
|
||||||
if (passive && hasBoss) return { ok: false, error: 'boss: a passive NPC never fights, so it cannot be a boss' }
|
if (passive && hasBoss) return { ok: false, error: 'boss: a passive NPC never fights, so it cannot be a boss' }
|
||||||
if (passive && !hasUse) return { ok: false, error: 'use: a passive NPC says what pressing E on it does (D278)' }
|
if (passive && !hasUse) return { ok: false, error: 'use: a passive NPC says what pressing E on it does' }
|
||||||
if (hasUse && !passive) return { ok: false, error: 'use: only a passive NPC answers E (D278)' }
|
if (hasUse && !passive) return { ok: false, error: 'use: only a passive NPC answers E' }
|
||||||
let use = null
|
let use = null
|
||||||
if (hasUse) {
|
if (hasUse) {
|
||||||
const checked = checkUse(b.use)
|
const checked = checkUse(b.use)
|
||||||
|
|||||||
@@ -272,7 +272,7 @@ async function create(input, userId = null) {
|
|||||||
async function update(id, input) {
|
async function update(id, input) {
|
||||||
const existing = await db.getProfile(id)
|
const existing = await db.getProfile(id)
|
||||||
if (!existing) throw new NpcError('no such profile', 404)
|
if (!existing) throw new NpcError('no such profile', 404)
|
||||||
if (existing.replaced) throw new NpcError('this profile is kept aside as replaced (D251): restore it before editing it', 409)
|
if (existing.replaced) throw new NpcError('this profile is kept aside as replaced: restore it before editing it', 409)
|
||||||
const clean = await validate(input, existing.id)
|
const clean = await validate(input, existing.id)
|
||||||
await db.saveProfile({ ...clean, id: existing.id })
|
await db.saveProfile({ ...clean, id: existing.id })
|
||||||
const before = reach(existing)
|
const before = reach(existing)
|
||||||
|
|||||||
@@ -262,7 +262,7 @@ test('a reward sends Steam ids and names to the bridge, which fills the placehol
|
|||||||
assert.strictEqual(result.ok, true)
|
assert.strictEqual(result.ok, true)
|
||||||
assert.deepStrictEqual([result.detail.people, result.detail.rewarded], [2, 1])
|
assert.deepStrictEqual([result.detail.people, result.detail.rewarded], [2, 1])
|
||||||
assert.deepStrictEqual([result.detail.skipped, result.detail.skippedCount], [['p3'], 1])
|
assert.deepStrictEqual([result.detail.skipped, result.detail.skippedCount], [['p3'], 1])
|
||||||
assert.match(result.detail.skippedNote, /D383/)
|
assert.match(result.detail.skippedNote, /not on the server when the reward ran/)
|
||||||
assert.strictEqual(result.resources, undefined)
|
assert.strictEqual(result.resources, undefined)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -169,8 +169,8 @@ const BOSS = {
|
|||||||
test('stage 6: a boss or passive profile is refused for what RunicNPC would refuse, in its own words', () => {
|
test('stage 6: a boss or passive profile is refused for what RunicNPC would refuse, in its own words', () => {
|
||||||
const cases = [
|
const cases = [
|
||||||
[{ ...PASSIVE, ...BOSS }, 'boss: a passive NPC never fights, so it cannot be a boss'],
|
[{ ...PASSIVE, ...BOSS }, 'boss: a passive NPC never fights, so it cannot be a boss'],
|
||||||
[{ role: 'passive' }, 'use: a passive NPC says what pressing E on it does (D278)'],
|
[{ role: 'passive' }, 'use: a passive NPC says what pressing E on it does'],
|
||||||
[{ use: { mode: 'chat', lines: ['hi'] } }, 'use: only a passive NPC answers E (D278)'],
|
[{ use: { mode: 'chat', lines: ['hi'] } }, 'use: only a passive NPC answers E'],
|
||||||
[{ movement: { mode: 'stand' } }, "movement.mode: stand is a passive NPC's"],
|
[{ movement: { mode: 'stand' } }, "movement.mode: stand is a passive NPC's"],
|
||||||
[{ ...PASSIVE, use: { mode: 'shout' } }, "use.mode: 'shout' is not chat or window"],
|
[{ ...PASSIVE, use: { mode: 'shout' } }, "use.mode: 'shout' is not chat or window"],
|
||||||
[{ ...PASSIVE, use: { mode: 'chat', lines: [] } }, 'use.lines: a chat answer gives at least one line'],
|
[{ ...PASSIVE, use: { mode: 'chat', lines: [] } }, 'use.lines: a chat answer gives at least one line'],
|
||||||
@@ -542,7 +542,7 @@ test('the faction table is one row per pair, both ways, refused in RunicNPC\'s w
|
|||||||
assert.match(shape.checkFactions([{ a: 'red', b: 'red', relation: 'hostile' }]).error, /always allied to itself/)
|
assert.match(shape.checkFactions([{ a: 'red', b: 'red', relation: 'hostile' }]).error, /always allied to itself/)
|
||||||
assert.match(shape.checkFactions([{ a: 'scientists', b: 'animals', relation: 'hostile' }]).error, /both are Rust's own/)
|
assert.match(shape.checkFactions([{ a: 'scientists', b: 'animals', relation: 'hostile' }]).error, /both are Rust's own/)
|
||||||
assert.match(shape.checkFactions([{ a: 'red', b: 'blue', relation: 'angry' }]).error, /'angry' is not hostile, neutral or allied/)
|
assert.match(shape.checkFactions([{ a: 'red', b: 'blue', relation: 'angry' }]).error, /'angry' is not hostile, neutral or allied/)
|
||||||
assert.match(shape.checkFactions([{ a: 'red', b: 'blue', relation: 'hostile' }, { a: 'blue', b: 'red', relation: 'allied' }]).error, /given twice \(one row is both ways, D268\)/)
|
assert.match(shape.checkFactions([{ a: 'red', b: 'blue', relation: 'hostile' }, { a: 'blue', b: 'red', relation: 'allied' }]).error, /given twice \(one row is both ways\)/)
|
||||||
assert.match(shape.checkFactions('red').error, /a list of/)
|
assert.match(shape.checkFactions('red').error, /a list of/)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user