// ── What an event marker says (runicnpc stage 8, D302, D303) ────────────── // // An NPC's marker says its name and its event; a boss is drawn bigger, and no // health is shown (D303 — the server does not even send it). A boss placed // outside any event is in the world layer as `kind: 'boss'` (D302). // // The event's title comes from core's public calendar, which publishes each // run's id for this: a map row holds a run id and nothing else. The calendar // lists announced events only, so an unannounced event is "an event" here and // nothing more. // // Pure, and free of React, so it is tested in Node. /** `{ [runId]: title }` from core's public calendar answer. */ export function titlesFrom(calendar) { const out = {} for (const e of (calendar && calendar.entries) || []) { if (e && e.kind === 'run' && e.runId != null && e.title) out[String(e.runId)] = e.title } return out } /** The run ids an answer's events layer names that `titles` does not know yet. */ export function unknownRuns(events, titles) { const out = new Set() for (const e of events || []) { if (e && e.runId != null && !Object.prototype.hasOwnProperty.call(titles || {}, String(e.runId))) out.add(String(e.runId)) } return [...out] } /** The tooltip for one events-layer row that is not a zone. */ export function eventTip(row, titles) { const event = (titles && row && row.runId != null && titles[String(row.runId)]) || 'an event' if (!row || row.kind !== 'npc') return `Event crate · ${event}` const who = row.name || 'Event NPC' const role = row.boss ? ' (boss)' : row.add ? " (a boss's add)" : '' return `${who}${role} · ${event}` } /** Whether a row is drawn as a boss: bigger (D303). */ export function isBoss(row) { return Boolean(row && (row.boss || row.kind === 'boss')) } // ── Dynamic PVP's zones (protocol 14, PLAN_REDESIGNS §14.3.3) ────────────── /** Dynamic PVP's own events as the map names them; a monument's or an operator's is shown as written. */ export const PVP_NAMES = Object.freeze({ Helicopter: 'Patrol helicopter', Bradley: 'Bradley APC', Satellite: 'Satellite crash', TimedSupply: 'Airdrop', SupplySignal: 'Signalled airdrop', CargoShip: 'Cargo ship', HackableCrate: 'Locked crate', ExcavatorIgnition: 'Giant Excavator', GhostShip: 'Ghost ship', DeepSeaIsland: 'Deep Sea island', IslandCannon: 'Island cannon', }) /** A Dynamic PVP ring's tooltip: "PvP zone · Patrol helicopter". */ export function pvpTip(z) { const name = PVP_NAMES[z.event] || z.event return name ? `PvP zone · ${name}` : 'PvP zone' }