Files
gacha-event-tracker/src/shared/games.ts
T
Lucas WintherandClaude Opus 5 1dd58d3f52 feat(arknights): track Arknights from its own wiki
The most-named game in the release thread, and the cheapest one to add: the
GameId, hue and dailyTasks have been sitting in games.ts since launch with no
source behind them.

arknights.wiki.gg is a better source than Game8 for this game, but it is not the
mp-event shape wikigg.ts reads, so it gets its own parser. Two properties of the
page drive the code:

- Every row lists CN and Global, about five months apart. Only Global is
  published; a row without one yields no event rather than borrowing the CN
  date. A CN date on a Global calendar is a confidently wrong date.
- Only the next boundary carries a machine-readable timer — the end while an
  event runs, the start while it is upcoming — so precision differs per side and
  flips when the event goes live. An exact instant is accepted only when it
  falls on the same UTC day as the date beside it, because startsAt.slice(0, 10)
  is part of the event ID: a start that moved a day would orphan every
  completion mark on the morning the event began.

Also sets resetOffsets for all three regions to UTC-7. Not a blanket per-game
offset — Arknights genuinely runs one Global server for every region we model,
and the page evidences the clock: every ending event ends at 10:59:59Z, which is
03:59:59 at UTC-7, one second before a 04:00 reset.

Six events, counted independently against the page before and after parsing.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-08-17 18:05:01 +02:00

75 lines
4.4 KiB
TypeScript

import type { GameId, Region } from "./schema.ts";
export interface GameMeta {
id: GameId;
name: string;
/** Short label for narrow lanes and chips. */
short: string;
/**
* Hue identity. This axis encodes *which game* only — urgency is a separate
* axis (see time.ts). Keeping them orthogonal is what lets a glance answer
* "whose event is this?" and "how long have I got?" at the same time.
*/
hue: string;
/** Who makes it. Credited in the colophon, derived rather than hardcoded. */
studio: string;
/**
* What the game's standing daily chore actually consists of, in the terms
* the game itself uses. Deliberately the routine every player recognises —
* this is a reminder, not a guide, and a wrong specific would be worse than
* no hint at all.
*/
dailyTasks: string;
/**
* Server clock offsets that differ from the regional default, per region.
*
* Not every game runs one server per region. Where a game serves two of our
* regions off a single machine, the reader's region is still the right
* question — it just gets a different answer for that game than
* `REGION_RESET_UTC_OFFSET` gives.
*
* Deliberately a sparse override rather than a full table: listing only the
* regions that actually differ keeps the diff to the fact that changed, and
* a region absent here keeps the default answer it has always had.
*
* This feeds `dayKey`, which is a **localStorage key**. Adding or changing an
* entry re-labels the game-day some already-logged ticks fall in, for readers
* in that region only — see `src/shared/daily.ts` § shift and
* docs/DATA-MODEL.md.
*/
resetOffsets?: Partial<Record<Region, number>> | undefined;
}
export const GAMES: Record<GameId, GameMeta> = {
genshin: { id: "genshin", name: "Genshin Impact", short: "Genshin", hue: "#4EA8DE" , studio: "HoYoverse", dailyTasks: "Commissions, resin" },
hsr: { id: "hsr", name: "Honkai: Star Rail", short: "Star Rail", hue: "#7B8CFF" , studio: "HoYoverse", dailyTasks: "Daily training, Trailblaze Power" },
zzz: { id: "zzz", name: "Zenless Zone Zero", short: "ZZZ", hue: "#F2A03D" , studio: "HoYoverse", dailyTasks: "Daily missions, battery" },
wuwa: { id: "wuwa", name: "Wuthering Waves", short: "Wuwa", hue: "#3DD6A0" , studio: "Kuro Games", dailyTasks: "Daily activity, waveplate" },
// Arknights runs a single Global (EN) server for all three of our regions on
// a fixed UTC-7, so every region gets the same override rather than the
// regional default. Evidenced by the source rather than assumed: every ending
// event on arknights.wiki.gg carries an exact end of 10:59:59Z, which is
// 03:59:59 at UTC-7 — one second before a 04:00 reset. Without this a
// European reader's Arknights day would roll at 03:00 UTC while the game
// rolls at 11:00, ticking the wrong box for eight hours.
arknights: { id: "arknights", name: "Arknights", short: "Arknights", hue: "#9AA3B8" , studio: "Hypergryph", dailyTasks: "Daily missions, sanity", resetOffsets: { asia: -7, america: -7, europe: -7 } },
// Endfield has two server groups, not three: Europe is served off the same
// machine as the Americas, on a fixed UTC-5. So a European player's day rolls
// at 09:00 UTC — 11:00 in Copenhagen in summer, 10:00 in winter — six hours
// after the HoYo/Kuro pattern above. Asia has its own server and is unchanged,
// and `america` already resolves to -5, so Europe is the only real override.
endfield: { id: "endfield", name: "Arknights: Endfield", short: "Endfield", hue: "#E8635A" , studio: "Hypergryph", dailyTasks: "Daily missions", resetOffsets: { europe: -5 } },
nte: { id: "nte", name: "Neverness to Everness", short: "NTE", hue: "#C77DFF" , studio: "Hotta Studio", dailyTasks: "Daily tasks" },
nikki: { id: "nikki", name: "Infinity Nikki", short: "Nikki", hue: "#F27BB0" , studio: "Infold Games", dailyTasks: "Daily tasks, Vital Energy" },
// No `resetOffsets`: nothing in the source states a server map that differs
// from the regional default, and an offset invented here would move real
// readers' day keys. Add one only against evidence — see games.ts § resetOffsets.
p5x: { id: "p5x", name: "Persona 5: The Phantom X", short: "P5X", hue: "#D62246" , studio: "Perfect World", dailyTasks: "Daily missions, stamina" },
};
export const GAME_LIST: GameMeta[] = Object.values(GAMES);
export function gameMeta(id: GameId): GameMeta {
return GAMES[id];
}