feat(games): track Reverse: 1999, which resets at 05:00
Adds the r1999 GameId and its metadata, plus GameMeta.resetHourLocal for the fact that makes it unlike every other game here: it rolls its day at 05:00 server time rather than 04:00, on a single global UTC-5 server, so its day-key boundary is 10:00 UTC. resetOffsets could not carry that. Landing 10:00 UTC through the offset would mean claiming a UTC-6 server, and serverOffsetUtc answers more questions than this one. The new field overrides RESET_HOUR_LOCAL per game and is absent everywhere else, so no existing reader's logged day moved — a test pins that for the games that already had readers. Both facts are read off the source, not assumed: all 154 rows of the wiki's event list state (UTC-5) and run 05:00 to 04:59, an event ending one minute before the reset the next one begins on. No adapter yet, so the game has no events until one lands. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 5
parent
7926a70850
commit
1a65a1f06c
+19
-1
@@ -137,6 +137,24 @@ export function serverOffsetUtc(region: Region, game?: LaneId): number {
|
||||
return override ?? REGION_RESET_UTC_OFFSET[region];
|
||||
}
|
||||
|
||||
/**
|
||||
* The hour of its own server day a game rolls over on.
|
||||
*
|
||||
* Almost always `RESET_HOUR_LOCAL`. A game that resets on a different hour says
|
||||
* so in `resetHourLocal` (`games.ts`) — Reverse: 1999 rolls at 05:00 — and that
|
||||
* cannot be folded into `serverOffsetUtc`: shifting a game's stated offset to
|
||||
* land the right reset instant would misreport the server clock to everything
|
||||
* else that asks for it.
|
||||
*
|
||||
* A lane the reader invented has no server to know about and takes the default,
|
||||
* for the same reason `serverOffsetUtc` does.
|
||||
*/
|
||||
export function resetHourFor(game?: LaneId): number {
|
||||
const override =
|
||||
game === undefined ? undefined : GAMES[game as GameId]?.resetHourLocal;
|
||||
return override ?? RESET_HOUR_LOCAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset from UTC midnight to this game's reset instant.
|
||||
*
|
||||
@@ -147,7 +165,7 @@ export function serverOffsetUtc(region: Region, game?: LaneId): number {
|
||||
* a data change, not a constant.
|
||||
*/
|
||||
function shift(region: Region, game?: LaneId): number {
|
||||
return serverOffsetUtc(region, game) * HOUR - RESET_HOUR_LOCAL * HOUR;
|
||||
return serverOffsetUtc(region, game) * HOUR - resetHourFor(game) * HOUR;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -39,6 +39,21 @@ export interface GameMeta {
|
||||
* docs/DATA-MODEL.md.
|
||||
*/
|
||||
resetOffsets?: Partial<Record<Region, number>> | undefined;
|
||||
/**
|
||||
* The hour of the server's own day this game rolls over on, when it is not
|
||||
* `RESET_HOUR_LOCAL` (04:00).
|
||||
*
|
||||
* Most gacha servers reset at 04:00 local. Reverse: 1999 resets at 05:00, and
|
||||
* the difference is not cosmetic: `resetOffsets` alone cannot express it,
|
||||
* because bending a game's stated server offset to land the right instant
|
||||
* would put every other reader of that offset an hour out.
|
||||
*
|
||||
* Like `resetOffsets` this feeds `dayKey`, which is a **localStorage key**, so
|
||||
* the same warning applies — changing it for a game that already has readers
|
||||
* re-labels the game-day their logged ticks fall in. Absent means 04:00, which
|
||||
* is why adding this field moved no existing game.
|
||||
*/
|
||||
resetHourLocal?: number | undefined;
|
||||
}
|
||||
|
||||
export const GAMES: Record<GameId, GameMeta> = {
|
||||
@@ -66,6 +81,12 @@ export const GAMES: Record<GameId, GameMeta> = {
|
||||
// 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" },
|
||||
// Reverse: 1999 runs one global server on a fixed UTC-5 and rolls its day at
|
||||
// **05:00**, not the 04:00 every other game here uses. Both facts come from
|
||||
// the source rather than from habit: all 154 rows on the wiki's event list
|
||||
// state `(UTC-5)`, and every one of them starts at 05:00 and ends at 04:59 —
|
||||
// an event ending one minute before the reset that the next one begins on.
|
||||
r1999: { id: "r1999", name: "Reverse: 1999", short: "R1999", hue: "#C9A227" , studio: "Bluepoch", dailyTasks: "Daily missions", resetOffsets: { asia: -5, america: -5, europe: -5 }, resetHourLocal: 5 },
|
||||
};
|
||||
|
||||
export const GAME_LIST: GameMeta[] = Object.values(GAMES);
|
||||
|
||||
@@ -10,6 +10,7 @@ export const GameId = z.enum([
|
||||
"nte", // Neverness to Everness
|
||||
"nikki", // Infinity Nikki
|
||||
"p5x", // Persona 5: The Phantom X
|
||||
"r1999", // Reverse: 1999
|
||||
]);
|
||||
export type GameId = z.infer<typeof GameId>;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user