diff --git a/common/src/config/config-utils.ts b/common/src/config/config-utils.ts index e62cfb469..8c80c74b1 100644 --- a/common/src/config/config-utils.ts +++ b/common/src/config/config-utils.ts @@ -29,15 +29,13 @@ export function ClassValue(id: string, display: string, hoverText?: string): Cla return { id, display, hoverText }; } -export function zodTierlistData() { - return z.nullable( - z.object({ - value: z.number(), - text: z.string(), - individualDifference: z.boolean(), - }) - ); -} +export const zodTierlistData = z.nullable( + z.strictObject({ + value: z.number(), + text: z.string(), + individualDifference: z.boolean(), + }) +); export const zodInt = z.number().int(); export const zodNonNegativeInt = z.number().int().nonnegative(); diff --git a/common/src/config/game-support/bms.ts b/common/src/config/game-support/bms.ts index 3709377fa..a6468b5ba 100644 --- a/common/src/config/game-support/bms.ts +++ b/common/src/config/game-support/bms.ts @@ -7,12 +7,12 @@ export const BMS_CONF = { defaultPlaytype: "7K", name: "BMS", validPlaytypes: ["7K", "14K"], - songData: { + songData: z.strictObject({ genre: z.nullable(z.string()), subtitle: z.nullable(z.string()), subartist: z.nullable(z.string()), tableString: z.nullable(z.string()), - }, + }), } as const satisfies INTERNAL_GAME_CONFIG; function FormatSieglindeBMS(sgl: number): string { @@ -130,6 +130,8 @@ export const BMSScratchDans = [ ClassValue("KAIDEN", "皆伝", "Scratch Kaiden"), ]; +const RANDOM_SCHEMA = z.enum(["MIRROR", "NONRAN", "R-RANDOM", "RANDOM", "S-RANDOM"]); + export const BMS_7K_CONF = { providedMetrics: { score: { type: "INTEGER" }, @@ -239,14 +241,25 @@ export const BMS_7K_CONF = { chartSets: [], - chartData: { + chartData: z.strictObject({ notecount: zodNonNegativeInt, hashMD5: z.string(), hashSHA256: z.string(), - tableFolders: z.array(z.object({ table: z.string(), level: z.string() })), + tableFolders: z.array(z.strictObject({ table: z.string(), level: z.string() })), sglEC: z.number().nullable(), sglHC: z.number().nullable(), - }, + }), + + preferences: z.strictObject({ + displayTables: z.array(z.string()), + }), + + scoreMeta: z.strictObject({ + random: RANDOM_SCHEMA.optional(), + inputDevice: z.enum(["BM_CONTROLLER", "KEYBOARD"]).optional(), + client: z.enum(["lr2oraja", "LR2"]).optional(), + gauge: z.enum(["EASY", "NORMAL", "HARD", "EX-HARD"]).optional(), + }), supportedMatchTypes: ["bmsChartHash", "tachiSongID"], } as const satisfies INTERNAL_GPT_CONFIG; @@ -257,4 +270,11 @@ export const BMS_14K_CONF = { genocideDan: { type: "PROVIDED", values: BMSGenocideDans }, stslDan: { type: "PROVIDED", values: BMSDPSlDans }, }, + + scoreMeta: z.strictObject({ + random: z.tuple([RANDOM_SCHEMA, RANDOM_SCHEMA]).optional(), + inputDevice: z.enum(["BM_CONTROLLER", "KEYBOARD"]).optional(), + client: z.enum(["lr2oraja", "LR2"]).optional(), + gauge: z.enum(["EASY", "NORMAL", "HARD", "EX-HARD"]).optional(), + }), } as const satisfies INTERNAL_GPT_CONFIG; diff --git a/common/src/config/game-support/chunithm.ts b/common/src/config/game-support/chunithm.ts index 8642d26b0..e9cd39ef2 100644 --- a/common/src/config/game-support/chunithm.ts +++ b/common/src/config/game-support/chunithm.ts @@ -7,10 +7,10 @@ export const CHUNITHM_CONF = { defaultPlaytype: "Single", name: "CHUNITHM", validPlaytypes: ["Single"], - songData: { + songData: z.strictObject({ genre: z.string(), displayVersion: z.string(), - }, + }), } as const satisfies INTERNAL_GAME_CONFIG; export const CHUNITHMColours = [ @@ -97,9 +97,13 @@ export const CHUNITHM_SINGLE_CONF = { chartSets: ["Paradise Lost"], - chartData: { + chartData: z.strictObject({ inGameID: zodNonNegativeInt, - }, + }), + + preferences: z.strictObject({}), + + scoreMeta: z.strictObject({}), supportedMatchTypes: ["inGameID", "songTitle", "tachiSongID"], } as const satisfies INTERNAL_GPT_CONFIG; diff --git a/common/src/config/game-support/gitadora.ts b/common/src/config/game-support/gitadora.ts index ee8cb658b..05a5ee9b9 100644 --- a/common/src/config/game-support/gitadora.ts +++ b/common/src/config/game-support/gitadora.ts @@ -1,5 +1,5 @@ import { FAST_SLOW_MAXCOMBO } from "./_common"; -import { ClassValue } from "../config-utils"; +import { ClassValue, zodNonNegativeInt } from "../config-utils"; import { z } from "zod"; import type { INTERNAL_GAME_CONFIG, INTERNAL_GPT_CONFIG } from "../../types/internals"; @@ -7,10 +7,10 @@ export const GITADORA_CONF = { defaultPlaytype: "Dora", name: "GITADORA", validPlaytypes: ["Gita", "Dora"], - songData: { + songData: z.strictObject({ isHot: z.boolean(), displayVersion: z.string(), - }, + }), } as const satisfies INTERNAL_GAME_CONFIG; const GitadoraColours = [ @@ -109,9 +109,13 @@ export const GITADORA_GITA_CONF = { chartSets: ["Konaste"], - chartData: { - inGameID: z.number().int(), - }, + chartData: z.strictObject({ + inGameID: zodNonNegativeInt, + }), + + preferences: z.strictObject({}), + + scoreMeta: z.strictObject({}), supportedMatchTypes: ["inGameID", "songTitle", "tachiSongID"], } as const satisfies INTERNAL_GPT_CONFIG; diff --git a/common/src/config/game-support/iidx.ts b/common/src/config/game-support/iidx.ts index b0296df7d..0a26e4005 100644 --- a/common/src/config/game-support/iidx.ts +++ b/common/src/config/game-support/iidx.ts @@ -7,10 +7,10 @@ export const IIDX_CONF = { defaultPlaytype: "SP", name: "beatmania IIDX", validPlaytypes: ["SP", "DP"], - songData: { + songData: z.strictObject({ genre: z.string(), displayVersion: z.nullable(z.string()), - }, + }), } as const satisfies INTERNAL_GAME_CONFIG; export const IIDXDans = [ @@ -45,6 +45,8 @@ const BASE_IIDX_CHART_DATA = { bpiCoefficient: z.number().positive().nullable(), }; +const RANDOM_SCHEMA = z.enum(["NONRAN", "MIRROR", "R-RANDOM", "RANDOM", "S-RANDOM"]); + export const IIDX_SP_CONF = { providedMetrics: { score: { type: "INTEGER" }, @@ -206,12 +208,24 @@ export const IIDX_SP_CONF = { "INFINITAS", ], - chartData: { + chartData: z.strictObject({ ...BASE_IIDX_CHART_DATA, - ncTier: zodTierlistData(), - hcTier: zodTierlistData(), - exhcTier: zodTierlistData(), - }, + ncTier: zodTierlistData, + hcTier: zodTierlistData, + exhcTier: zodTierlistData, + }), + + preferences: z.strictObject({ + display2DXTra: z.boolean(), + bpiTarget: z.number(), + }), + + scoreMeta: z.strictObject({ + random: RANDOM_SCHEMA.optional(), + assist: z.enum(["AUTO SCRATCH", "LEGACY NOTE", "NO ASSIST", "FULL ASSIST"]).optional(), + range: z.enum(["NONE", "HIDDEN+", "SUDDEN+", "LIFT", "LIFT SUD+", "SUD+ HID+"]).optional(), + gauge: z.enum(["ASSISTED EASY", "EASY", "NORMAL", "HARD", "EX-HARD"]).optional(), + }), supportedMatchTypes: ["inGameID", "tachiSongID", "songTitle"], } as const satisfies INTERNAL_GPT_CONFIG; @@ -219,9 +233,16 @@ export const IIDX_SP_CONF = { export const IIDX_DP_CONF = { ...IIDX_SP_CONF, - chartData: { + chartData: z.strictObject({ ...BASE_IIDX_CHART_DATA, - dpTier: zodTierlistData(), - }, + dpTier: zodTierlistData, + }), + + scoreMeta: z.strictObject({ + random: z.tuple([RANDOM_SCHEMA, RANDOM_SCHEMA]).optional(), + assist: z.enum(["AUTO SCRATCH", "LEGACY NOTE", "NO ASSIST", "FULL ASSIST"]).optional(), + range: z.enum(["NONE", "HIDDEN+", "SUDDEN+", "LIFT", "LIFT SUD+", "SUD+ HID+"]).optional(), + gauge: z.enum(["ASSISTED EASY", "EASY", "NORMAL", "HARD", "EX-HARD"]).optional(), + }), } as const satisfies INTERNAL_GPT_CONFIG; diff --git a/common/src/config/game-support/itg.ts b/common/src/config/game-support/itg.ts index 426746304..0d1e597dd 100644 --- a/common/src/config/game-support/itg.ts +++ b/common/src/config/game-support/itg.ts @@ -6,7 +6,10 @@ export const ITG_CONF = { defaultPlaytype: "Stamina", name: "ITG", validPlaytypes: ["Stamina"], - songData: { subtitle: z.string(), originalPack: z.string() }, + songData: z.strictObject({ + subtitle: z.string(), + originalPack: z.string(), + }), } as const satisfies INTERNAL_GAME_CONFIG; export const ITG_STAMINA_CONF = { @@ -96,16 +99,20 @@ export const ITG_STAMINA_CONF = { chartSets: [], - chartData: { + chartData: z.strictObject({ hashGSv3: z.string(), difficultyTag: z.enum(["Beginner", "Easy", "Medium", "Hard", "Expert", "Edit"] as const), length: z.number().positive(), charter: z.string(), - // What BPM tier should this chart appear in? - primaryBPM: z.number(), + // Pick one BPM to represent this chart. If that doesn't make any sense + // (i.e. chart has significant bpm changes) + // this should be null. + // Note that for some charts with BPM changes, Archi typically picks a common + // BPM to place it at. Not sure if that's automatable. + primaryBPM: z.number().nullable(), - breakdown: z.object({ + breakdown: z.strictObject({ detailed: z.string(), partiallySimplified: z.string(), simplified: z.string(), @@ -114,7 +121,11 @@ export const ITG_STAMINA_CONF = { npsPerMeasure: z.array(zodNonNegativeInt), notesPerMeasure: z.array(zodNonNegativeInt), - }, + }), + + preferences: z.strictObject({}), + + scoreMeta: z.strictObject({}), supportedMatchTypes: ["itgChartHash", "tachiSongID"], } as const satisfies INTERNAL_GPT_CONFIG; diff --git a/common/src/config/game-support/jubeat.ts b/common/src/config/game-support/jubeat.ts index 09b210fa7..26a550d90 100644 --- a/common/src/config/game-support/jubeat.ts +++ b/common/src/config/game-support/jubeat.ts @@ -7,9 +7,9 @@ export const JUBEAT_CONF = { defaultPlaytype: "Single", name: "jubeat", validPlaytypes: ["Single"], - songData: { + songData: z.strictObject({ displayVersion: z.string(), - }, + }), } as const satisfies INTERNAL_GAME_CONFIG; const JubeatColours = [ @@ -102,9 +102,13 @@ export const JUBEAT_SINGLE_CONF = { "festo", ], - chartData: { + chartData: z.strictObject({ inGameID: z.union([z.array(zodNonNegativeInt), zodNonNegativeInt]), - }, + }), + + preferences: z.strictObject({ jubilityTarget: z.number() }), + + scoreMeta: z.strictObject({}), supportedMatchTypes: ["inGameID", "tachiSongID"], } as const satisfies INTERNAL_GPT_CONFIG; diff --git a/common/src/config/game-support/maimai-dx.ts b/common/src/config/game-support/maimai-dx.ts index 87e074706..226b54efe 100644 --- a/common/src/config/game-support/maimai-dx.ts +++ b/common/src/config/game-support/maimai-dx.ts @@ -7,9 +7,9 @@ export const MAIMAI_DX_CONF = { defaultPlaytype: "Single", name: "maimai DX", validPlaytypes: ["Single"], - songData: { + songData: z.strictObject({ displayVersion: z.string(), - }, + }), } as const satisfies INTERNAL_GAME_CONFIG; const MaimaiDXDans = [ @@ -161,9 +161,12 @@ export const MAIMAI_DX_SINGLE_CONF = { chartSets: ["UNiVERSE PLUS"], - chartData: { + chartData: z.strictObject({ isLatest: z.boolean(), - }, + }), + + preferences: z.strictObject({}), + scoreMeta: z.strictObject({}), supportedMatchTypes: ["songTitle", "tachiSongID"], } as const satisfies INTERNAL_GPT_CONFIG; diff --git a/common/src/config/game-support/museca.ts b/common/src/config/game-support/museca.ts index dec43ea90..b557fe4a6 100644 --- a/common/src/config/game-support/museca.ts +++ b/common/src/config/game-support/museca.ts @@ -7,11 +7,11 @@ export const MUSECA_CONF = { defaultPlaytype: "Single", name: "MÚSECA", validPlaytypes: ["Single"], - songData: { + songData: z.strictObject({ titleJP: z.string(), artistJP: z.string(), displayVersion: z.string(), - }, + }), } as const satisfies INTERNAL_GAME_CONFIG; export const MUSECA_SINGLE_CONF = { @@ -80,7 +80,11 @@ export const MUSECA_SINGLE_CONF = { chartSets: ["1 + 1/2", "1 + 1/2 Rev. B"], - chartData: { inGameID: zodNonNegativeInt }, + chartData: z.strictObject({ inGameID: zodNonNegativeInt }), + + preferences: z.strictObject({}), + + scoreMeta: z.strictObject({}), supportedMatchTypes: ["songTitle", "tachiSongID", "inGameID"], } as const satisfies INTERNAL_GPT_CONFIG; diff --git a/common/src/config/game-support/pms.ts b/common/src/config/game-support/pms.ts index 2add5a25b..9ce5de6de 100644 --- a/common/src/config/game-support/pms.ts +++ b/common/src/config/game-support/pms.ts @@ -7,12 +7,12 @@ export const PMS_CONF = { defaultPlaytype: "Controller", name: "PMS", validPlaytypes: ["Controller", "Keyboard"], - songData: { + songData: z.strictObject({ genre: z.nullable(z.string()), subtitle: z.nullable(z.string()), subartist: z.nullable(z.string()), tableString: z.nullable(z.string()), - }, + }), } as const satisfies INTERNAL_GAME_CONFIG; const PMSDans = [ @@ -121,14 +121,21 @@ export const PMS_CONTROLLER_CONF = { chartSets: [], - chartData: { + chartData: z.strictObject({ notecount: zodNonNegativeInt, hashMD5: z.string(), hashSHA256: z.string(), - tableFolders: z.array(z.object({ table: z.string(), level: z.string() })), + tableFolders: z.array(z.strictObject({ table: z.string(), level: z.string() })), sglEC: z.number().nullable(), sglHC: z.number().nullable(), - }, + }), + + preferences: z.strictObject({}), + scoreMeta: z.strictObject({ + random: z.enum(["MIRROR", "NONRAN", "R-RANDOM", "RANDOM", "S-RANDOM"]).optional(), + client: z.enum(["beatoraja"]).optional(), + gauge: z.enum(["EASY", "NORMAL", "HARD", "EX-HARD"]).optional(), + }), supportedMatchTypes: ["bmsChartHash", "tachiSongID"], } as const satisfies INTERNAL_GPT_CONFIG; diff --git a/common/src/config/game-support/popn.ts b/common/src/config/game-support/popn.ts index 2abdb8ae9..954857d52 100644 --- a/common/src/config/game-support/popn.ts +++ b/common/src/config/game-support/popn.ts @@ -7,11 +7,11 @@ export const POPN_CONF = { defaultPlaytype: "9B", name: "pop'n music", validPlaytypes: ["9B"], - songData: { + songData: z.strictObject({ displayVersion: z.nullable(z.string()), genre: z.string(), genreEN: z.nullable(z.string()), - }, + }), } as const satisfies INTERNAL_GAME_CONFIG; const PopnClasses = [ @@ -118,11 +118,21 @@ export const POPN_9B_CONF = { chartSets: ["peace", "Kaimei Riddles"], - chartData: { + chartData: z.strictObject({ // Array | string | null hashSHA256: z.union([z.array(z.string()), z.string(), z.null()]), inGameID: zodNonNegativeInt, - }, + }), + + scoreMeta: z.strictObject({ + hiSpeed: z.number().optional(), + hidden: z.number().optional(), + sudden: z.number().optional(), + random: z.enum(["MIRROR", "NONRAN", "RANDOM", "S-RANDOM"]).optional(), + gauge: z.enum(["EASY", "NORMAL", "HARD", "DANGER"]).optional(), + }), + + preferences: z.strictObject({}), supportedMatchTypes: ["inGameID", "tachiSongID", "popnChartHash"], } as const satisfies INTERNAL_GPT_CONFIG; diff --git a/common/src/config/game-support/sdvx.ts b/common/src/config/game-support/sdvx.ts index f532b60ca..1b8a6f14a 100644 --- a/common/src/config/game-support/sdvx.ts +++ b/common/src/config/game-support/sdvx.ts @@ -6,9 +6,9 @@ export const SDVX_CONF = { defaultPlaytype: "Single", name: "SOUND VOLTEX", validPlaytypes: ["Single"], - songData: { + songData: z.strictObject({ displayVersion: z.string(), - }, + }), } as const satisfies INTERNAL_GAME_CONFIG; const SDVXDans = [ @@ -159,10 +159,13 @@ export const SDVX_SINGLE_CONF = { "Konaste", ], - chartData: { + chartData: z.strictObject({ inGameID: zodNonNegativeInt, - clearTier: zodTierlistData(), - }, + clearTier: zodTierlistData, + }), + + preferences: z.strictObject({ vf6Target: z.number() }), + scoreMeta: z.strictObject({}), supportedMatchTypes: ["sdvxInGameID", "songTitle", "tachiSongID"], } as const satisfies INTERNAL_GPT_CONFIG; diff --git a/common/src/config/game-support/usc.ts b/common/src/config/game-support/usc.ts index 6e86394c0..5f661a06b 100644 --- a/common/src/config/game-support/usc.ts +++ b/common/src/config/game-support/usc.ts @@ -7,7 +7,7 @@ export const USC_CONF = { defaultPlaytype: "Controller", name: "USC", validPlaytypes: ["Controller", "Keyboard"], - songData: {}, + songData: z.strictObject({}), } as const satisfies INTERNAL_GAME_CONFIG; export const USC_CONTROLLER_CONF = { @@ -82,17 +82,23 @@ export const USC_CONTROLLER_CONF = { chartSets: [], - chartData: { + chartData: z.strictObject({ hashSHA1: z.union([z.array(z.string()), z.string()]), isOfficial: z.boolean(), effector: z.string(), tableFolders: z.array( - z.object({ + z.strictObject({ table: z.string(), level: z.string(), }) ), - }, + }), + + preferences: z.strictObject({ vf6Target: z.number() }), + scoreMeta: z.strictObject({ + noteMod: z.enum(["MIR-RAN", "MIRROR", "NORMAL", "RANDOM"]).optional(), + gaugeMod: z.enum(["NORMAL", "HARD", "PERMISSIVE"]).optional(), + }), supportedMatchTypes: ["uscChartHash", "tachiSongID"], } as const satisfies INTERNAL_GPT_CONFIG; diff --git a/common/src/config/game-support/wacca.ts b/common/src/config/game-support/wacca.ts index bd8de0897..424905783 100644 --- a/common/src/config/game-support/wacca.ts +++ b/common/src/config/game-support/wacca.ts @@ -7,12 +7,12 @@ export const WACCA_CONF = { defaultPlaytype: "Single", name: "WACCA", validPlaytypes: ["Single"], - songData: { + songData: z.strictObject({ titleJP: z.string(), artistJP: z.string(), genre: z.string(), displayVersion: z.nullable(z.string()), - }, + }), } as const satisfies INTERNAL_GAME_CONFIG; export const WaccaStageUps = [ @@ -132,9 +132,12 @@ export const WACCA_SINGLE_CONF = { chartSets: ["REVERSE"], - chartData: { + chartData: z.strictObject({ isHot: z.boolean(), - }, + }), + + preferences: z.strictObject({}), + scoreMeta: z.strictObject({ mirror: z.boolean().optional() }), supportedMatchTypes: ["songTitle", "tachiSongID"], } as const satisfies INTERNAL_GPT_CONFIG; diff --git a/common/src/types/_TOREMOVE-game-implementations.ts b/common/src/types/_TOREMOVE-game-implementations.ts deleted file mode 100644 index 715930b1d..000000000 --- a/common/src/types/_TOREMOVE-game-implementations.ts +++ /dev/null @@ -1,755 +0,0 @@ -import type { integer } from "../types"; - -/** - * Utility index type. Given an GPTString [I], return its playtype. - */ -export interface GPTStringToPlaytype { - "iidx:SP": "SP"; - "iidx:DP": "DP"; - "popn:9B": "9B"; - "sdvx:Single": "Single"; - "usc:Keyboard": "Keyboard"; - "usc:Controller": "Controller"; - "maimaidx:Single": "Single"; - "jubeat:Single": "Single"; - "museca:Single": "Single"; - "bms:7K": "7K"; - "bms:14K": "14K"; - "chunithm:Single": "Single"; - "gitadora:Gita": "Gita"; - "gitadora:Dora": "Dora"; - "wacca:Single": "Single"; - "pms:Keyboard": "Keyboard"; - "pms:Controller": "Controller"; - "itg:Stamina": "Stamina"; -} - -/** - * Utility index type. Given a GPTString [I], return its game. - * - * @example GPTStringToGame["iidx:SP"] -> "iidx" - */ -export interface GPTStringToGame { - "iidx:SP": "iidx"; - "iidx:DP": "iidx"; - "popn:9B": "popn"; - "sdvx:Single": "sdvx"; - "usc:Keyboard": "usc"; - "usc:Controller": "usc"; - "maimaidx:Single": "maimaidx"; - "jubeat:Single": "jubeat"; - "museca:Single": "museca"; - "bms:7K": "bms"; - "bms:14K": "bms"; - "chunithm:Single": "chunithm"; - "gitadora:Gita": "gitadora"; - "gitadora:Dora": "gitadora"; - "wacca:Single": "wacca"; - "pms:Keyboard": "pms"; - "pms:Controller": "pms"; - "itg:Stamina": "itg"; -} - -/** - * Given a game, return all IDStrings this game has. - */ -export interface GameToIDStrings { - iidx: "iidx:DP" | "iidx:SP"; - sdvx: "sdvx:Single"; - usc: "usc:Controller" | "usc:Keyboard"; - maimaidx: "maimaidx:Single"; - jubeat: "jubeat:Single"; - museca: "museca:Single"; - bms: "bms:7K" | "bms:14K"; - chunithm: "chunithm:Single"; - gitadora: "gitadora:Dora" | "gitadora:Gita"; - popn: "popn:9B"; - wacca: "wacca:Single"; - pms: "pms:Controller" | "pms:Keyboard"; - itg: "itg:Stamina"; -} - -type IIDXGrades = "A" | "AA" | "AAA" | "B" | "C" | "D" | "E" | "F" | "MAX-" | "MAX"; -type SDVXGrades = "A" | "A+" | "AA" | "AA+" | "AAA" | "AAA+" | "B" | "C" | "D" | "PUC" | "S"; - -type GitadoraGrades = "A" | "B" | "C" | "MAX" | "S" | "SS"; - -/** - * What grades do Tachi's supported GPTs have? - * Grades are an ordered list of discrete values for a given game, - * typically derived from score values. - * - * All GPTs **must** define a series of grades, as they are used frequently in the UI - * for things like folder graphs and raise breakdowns. - */ -export interface Grades { - "iidx:SP": IIDXGrades; - "iidx:DP": IIDXGrades; - "popn:9B": "A" | "AA" | "AAA" | "B" | "C" | "D" | "E" | "S"; - "sdvx:Single": SDVXGrades; - "usc:Keyboard": SDVXGrades; - "usc:Controller": SDVXGrades; - "maimaidx:Single": - | "A" - | "AA" - | "AAA" - | "B" - | "BB" - | "BBB" - | "C" - | "D" - | "S" - | "S+" - | "SS" - | "SS+" - | "SSS" - | "SSS+"; - "jubeat:Single": "A" | "B" | "C" | "D" | "E" | "EXC" | "S" | "SS" | "SSS"; - "museca:Single": "佳" | "傑" | "傑G" | "優" | "凡" | "拙" | "没" | "秀" | "良"; - "bms:7K": IIDXGrades; - "bms:14K": IIDXGrades; - "chunithm:Single": "A" | "AA" | "AAA" | "B" | "BB" | "BBB" | "C" | "D" | "S" | "SS" | "SSS"; - "gitadora:Gita": GitadoraGrades; - "gitadora:Dora": GitadoraGrades; - "wacca:Single": - | "A" - | "AA" - | "AAA" - | "B" - | "C" - | "D" - | "MASTER" - | "S" - | "S+" - | "SS" - | "SS+" - | "SSS" - | "SSS+"; - "pms:Controller": IIDXGrades; - "pms:Keyboard": IIDXGrades; - "itg:Stamina": "★" | "★★" | "★★★" | "★★★★" | "A" | "B" | "C" | "D" | "S-" | "S" | "S+"; -} - -type IIDXLamps = - | "ASSIST CLEAR" - | "CLEAR" - | "EASY CLEAR" - | "EX HARD CLEAR" - | "FAILED" - | "FULL COMBO" - | "HARD CLEAR" - | "NO PLAY"; - -type GitadoraLamps = "CLEAR" | "EXCELLENT" | "FAILED" | "FULL COMBO"; - -type SDVXLamps = - | "CLEAR" - | "EXCESSIVE CLEAR" - | "FAILED" - | "PERFECT ULTIMATE CHAIN" - | "ULTIMATE CHAIN"; - -/** - * What lamps do Tachi's supported GPTs have? Lamps are an ordered list of discrete - * values, typically representing a "clear type" or something similar. - * - * Common examples of lamps include "FAILED", "CLEAR" and "FULL COMBO". - * - * All GPTs **must** define a series of lamps, as they're used frequently in the UI - * for things like folder graphs and raise breakdowns. - */ -export interface Lamps { - "iidx:SP": IIDXLamps; - "iidx:DP": IIDXLamps; - "popn:9B": "CLEAR" | "EASY CLEAR" | "FAILED" | "FULL COMBO" | "PERFECT"; - "sdvx:Single": SDVXLamps; - "usc:Keyboard": SDVXLamps; - "usc:Controller": SDVXLamps; - "maimaidx:Single": - | "ALL PERFECT" - | "ALL PERFECT+" - | "CLEAR" - | "FAILED" - | "FULL COMBO" - | "FULL COMBO+"; - "jubeat:Single": "CLEAR" | "EXCELLENT" | "FAILED" | "FULL COMBO"; - "museca:Single": "CLEAR" | "CONNECT ALL" | "FAILED" | "PERFECT CONNECT ALL"; - "bms:7K": IIDXLamps; - "bms:14K": IIDXLamps; - "chunithm:Single": "ALL JUSTICE CRITICAL" | "ALL JUSTICE" | "CLEAR" | "FAILED" | "FULL COMBO"; - "gitadora:Gita": GitadoraLamps; - "gitadora:Dora": GitadoraLamps; - "wacca:Single": "ALL MARVELOUS" | "CLEAR" | "FAILED" | "FULL COMBO" | "MISSLESS"; - "pms:Controller": IIDXLamps; - "pms:Keyboard": IIDXLamps; - "itg:Stamina": "CLEAR" | "FAILED" | "FULL COMBO" | "FULL EXCELLENT COMBO" | "QUAD"; -} - -type IIDX2DXTraSets = "All Scratch" | "Kichiku" | "Kiraku"; -type IIDXSPDifficulties = "ANOTHER" | "BEGINNER" | "HYPER" | "LEGGENDARIA" | "NORMAL"; -type IIDXDPDifficulties = "ANOTHER" | "HYPER" | "LEGGENDARIA" | "NORMAL"; - -/** - * What difficulties do Tachi's supported GPTs have? - * - * A "difficulty" is a string attached onto a chart document. In the simplest terms, - * this determines how many charts a given song can have, and how to distinguish - * them. - * A chart **must** be unique on songID + playtype + difficulty. - * An example would be something like: - * FREEDOM DiVE SP ANOTHER - * FREEDOM DiVE DP ANOTHER - * FREEDOM DiVE DP HYPER - * - * For most arcade games this is a fixed set of values, like "Easy", "Normal" - * or "Hard". However, Tachi isn't limited to a fixed set of strings. - * Some games - especially home games - tend to support any amount of difficulties - * per song. For this case, you can set the supported difficulties to just `string`. - * This will allow *any* string in that field, and therefore a song can have any - * amount of charts. - */ -export interface Difficulties { - "iidx:SP": `${"" | `${IIDX2DXTraSets} `}${IIDXSPDifficulties}`; - "iidx:DP": `${"" | `${IIDX2DXTraSets} `}${IIDXDPDifficulties}`; - "popn:9B": "Easy" | "EX" | "Hyper" | "Normal"; - "sdvx:Single": "ADV" | "EXH" | "GRV" | "HVN" | "INF" | "MXM" | "NOV" | "VVD" | "XCD"; - "usc:Controller": "ADV" | "EXH" | "INF" | "NOV"; - "usc:Keyboard": "ADV" | "EXH" | "INF" | "NOV"; - "maimaidx:Single": - | "Advanced" - | "Basic" - | "DX Advanced" - | "DX Basic" - | "DX Expert" - | "DX Master" - | "DX Re:Master" - | "Expert" - | "Master" - | "Re:Master"; - "jubeat:Single": "ADV" | "BSC" | "EXT" | "HARD ADV" | "HARD BSC" | "HARD EXT"; - "museca:Single": "Green" | "Red" | "Yellow"; - "bms:7K": "CHART"; - "bms:14K": "CHART"; - "chunithm:Single": "ADVANCED" | "BASIC" | "EXPERT" | "MASTER"; - "gitadora:Gita": - | "ADVANCED" - | "BASIC" - | "BASS ADVANCED" - | "BASS BASIC" - | "BASS EXTREME" - | "BASS MASTER" - | "EXTREME" - | "MASTER"; - "gitadora:Dora": "ADVANCED" | "BASIC" | "EXTREME" | "MASTER"; - "wacca:Single": "EXPERT" | "HARD" | "INFERNO" | "NORMAL"; - "pms:Controller": "CHART"; - "pms:Keyboard": "CHART"; - "itg:Stamina": string; -} - -/** - * What rating algorithms are ran on sessions for this GPT? - * - * Sessions may have data calculated about them and attached on. - * For example, we may want to average the best 10 skill values this user got in - * a session. Things like this allow us to sort a users sessions and display their - * best. - * - * This type declares the names of those algorithms, and typesafety enforces that - * all of them get an implementation. - */ -export interface SessionRatingAlgorithms { - "iidx:SP": "BPI" | "ktLampRating"; - "iidx:DP": "BPI" | "ktLampRating"; - "popn:9B": "classPoints"; - "sdvx:Single": "ProfileVF6" | "VF6"; - "usc:Keyboard": "ProfileVF6" | "VF6"; - "usc:Controller": "ProfileVF6" | "VF6"; - "maimaidx:Single": "rate"; - "jubeat:Single": "jubility"; - "museca:Single": "curatorSkill"; - "bms:7K": "sieglinde"; - "bms:14K": "sieglinde"; - "chunithm:Single": "naiveRating"; - "gitadora:Gita": "skill"; - "gitadora:Dora": "skill"; - "wacca:Single": "rate"; - "pms:Controller": "sieglinde"; - "pms:Keyboard": "sieglinde"; - "itg:Stamina": "average32Speed" | "blockRating"; -} - -/** - * What rating algorithms apply to a users profile for this GPT? - * - * This should be self explanatory. - */ -export interface ProfileRatingAlgorithms { - "iidx:SP": "BPI" | "ktLampRating"; - "iidx:DP": "BPI" | "ktLampRating"; - "popn:9B": "naiveClassPoints"; - "sdvx:Single": "VF6"; - "usc:Keyboard": "VF6"; - "usc:Controller": "VF6"; - "maimaidx:Single": "naiveRate" | "rate"; - "jubeat:Single": "jubility" | "naiveJubility"; - "museca:Single": "curatorSkill"; - "bms:7K": "sieglinde"; - "bms:14K": "sieglinde"; - "chunithm:Single": "naiveRating"; - "gitadora:Gita": "naiveSkill" | "skill"; - "gitadora:Dora": "naiveSkill" | "skill"; - "wacca:Single": "naiveRate" | "rate"; - "pms:Controller": "sieglinde"; - "pms:Keyboard": "sieglinde"; - "itg:Stamina": "highest32" | "highest256" | "highestBlock"; -} - -/** - * What rating algorithms exist for scores in this GPT? - * - * Scores themselves may have calculated data attached onto them. - * This type declares the names of those algorithms, and their implementation is - * enforced by typesafety. - */ -export interface ScoreRatingAlgorithms { - "iidx:SP": "BPI" | "ktLampRating"; - "iidx:DP": "BPI" | "ktLampRating"; - "popn:9B": "classPoints"; - "sdvx:Single": "VF6"; - "usc:Keyboard": "VF6"; - "usc:Controller": "VF6"; - "maimaidx:Single": "rate"; - "jubeat:Single": "jubility"; - "museca:Single": "curatorSkill"; - "bms:7K": "sieglinde"; - "bms:14K": "sieglinde"; - "chunithm:Single": "rating"; - "gitadora:Gita": "skill"; - "gitadora:Dora": "skill"; - "wacca:Single": "rate"; - "pms:Controller": "sieglinde"; - "pms:Keyboard": "sieglinde"; - "itg:Stamina": "blockRating" | "highest32" | "highest256"; -} - -type SupportedIIDXVersions = - | "3-cs" - | "4-cs" - | "5-cs" - | "6-cs" - | "7-cs" - | "8-cs" - | "9-cs" - | "10-cs" - | "11-cs" - | "12-cs" - | "13-cs" - | "14-cs" - | "15-cs" - | "16-cs" - | "20" - | "21" - | "22" - | "23" - | "24" - | "25" - | "26-omni" - | "26" - | "27-2dxtra" - | "27-omni" - | "27" - | "28-2dxtra" - | "28-omni" - | "28" - | "29-omni" - | "29" - | "30" - | "bmus" - | "inf"; - -interface ChartDataIIDX { - notecount: integer; - inGameID: Array | integer; - hashSHA256: string | null; - "2dxtraSet": string | null; - kaidenAverage: integer | null; - worldRecord: integer | null; - bpiCoefficient: number | null; -} - -interface ChartDataPMS { - notecount: integer; - hashMD5: string; - hashSHA256: string; - tableFolders: Array<{ table: string; level: string }>; -} - -type ChartDataBMS = ChartDataPMS & { - aiLevel?: string | null; -}; - -interface ChartDataUSC { - hashSHA1: Array | string; - isOfficial: boolean; - effector: string; - tableFolders: Array<{ table: string; level: string }>; -} - -/** - * What GPT-Specific content is stored on each chart? - * - * All charts in Tachi get a `data` property where they can store GPT-specific content. - * This allows us to store things like `kaidenAverage` for IIDX, but not make that - * property exist on - say - pop'n, where it wouldn't make any sense. - */ -export interface ChartData { - "iidx:SP": ChartDataIIDX; - "iidx:DP": ChartDataIIDX; - "popn:9B": { hashSHA256: Array | string | null; inGameID: integer }; - "sdvx:Single": { inGameID: integer }; - "usc:Keyboard": ChartDataUSC; - "usc:Controller": ChartDataUSC; - "jubeat:Single": { inGameID: Array | integer; isHardMode: boolean }; - "maimaidx:Single": { isLatest: boolean }; - "museca:Single": { inGameID: integer }; - "bms:7K": ChartDataBMS; - "bms:14K": ChartDataBMS; - "chunithm:Single": { inGameID: integer }; - "gitadora:Gita": { inGameID: integer }; - "gitadora:Dora": { inGameID: integer }; - "wacca:Single": { isHot: boolean }; - "pms:Controller": ChartDataPMS; - "pms:Keyboard": ChartDataPMS; - "itg:Stamina": { - hashGSv3: string; - difficultyTag: "Beginner" | "Challenge" | "Easy" | "Edit" | "Hard" | "Medium"; - breakdown: { - detailed: string; - partiallySimplified: string; - simplified: string; - total: string; - npsPerMeasure: Array; - notesPerMeasure: Array; - }; - length: number; - charter: string; - displayBPM: number; - }; -} - -/** - * What Game-Specific content is stored on each song? - * - * All songs in Tachi get a `data` field which allows them to store game-specific - * fields. This allows us to have things like "genre" for some games, but not all. - */ -export interface SongData { - iidx: { genre: string; displayVersion: string | null }; - museca: { titleJP: string; artistJP: string; displayVersion: string }; - maimaidx: { displayVersion: string }; - jubeat: { displayVersion: string }; - popn: { - displayVersion: string | null; - genre: string; - genreEN: string | null; - }; - sdvx: { displayVersion: string }; - usc: Record; - bms: { - genre: string | null; - subtitle: string | null; - subartist: string | null; - tableString: string | null; - }; - chunithm: { genre: string; displayVersion: string }; - gitadora: { isHot: boolean; displayVersion: string }; - wacca: { - titleJP: string; - artistJP: string; - genre: string; - displayVersion: string | null; - }; - pms: { - genre: string | null; - subtitle: string | null; - subartist: string | null; - tableString: string | null; - }; - itg: { - subtitle: string; - banner: string | null; - }; -} - -/** - * What tierlists does each GPT support? - * - * If the GPT has no tierlists, set this to `never`. - */ -export interface SupportedTierlists { - "iidx:SP": "kt-EXHC" | "kt-HC" | "kt-NC"; - "iidx:DP": "dp-tier"; - "bms:7K": "sgl-EC" | "sgl-HC"; - "bms:14K": "sgl-EC" | "sgl-HC"; - "popn:9B": never; - "sdvx:Single": "clear"; - "usc:Keyboard": never; - "usc:Controller": never; - "maimaidx:Single": never; - "jubeat:Single": never; - "museca:Single": never; - "chunithm:Single": never; - "gitadora:Gita": never; - "gitadora:Dora": never; - "wacca:Single": never; - "pms:Controller": "sgl-EC" | "sgl-HC"; - "pms:Keyboard": "sgl-EC" | "sgl-HC"; - "itg:Stamina": never; -} - -type RanOptions = "MIRROR" | "NONRAN" | "R-RANDOM" | "RANDOM" | "S-RANDOM"; - -interface IIDXSPScoreMeta { - random: RanOptions | null; - assist: "AUTO SCRATCH" | "FULL ASSIST" | "LEGACY NOTE" | "NO ASSIST" | null; - range: "HIDDEN+" | "LIFT SUD+" | "LIFT" | "NONE" | "SUD+ HID+" | "SUDDEN+" | null; - gauge: "ASSISTED EASY" | "EASY" | "EX-HARD" | "HARD" | "NORMAL" | null; -} - -interface BMS7KScoreMeta { - random: RanOptions | null; - inputDevice: "BM_CONTROLLER" | "KEYBOARD" | null; - client: "LR2" | "lr2oraja" | null; - gauge: "EASY" | "EX-HARD" | "HARD" | "NORMAL" | null; -} - -interface PMSScoreMeta { - random: RanOptions | null; - client: "beatoraja" | null; - gauge: "EASY" | "EX-HARD" | "HARD" | "NORMAL" | null; -} - -interface USCScoreMeta { - noteMod: "MIR-RAN" | "MIRROR" | "NORMAL" | "RANDOM" | null; - gaugeMod: "HARD" | "NORMAL" | "PERMISSIVE" | null; -} - -/** - * What metadata should exist for scores on this GPT? - * - * This is *score* specific metadata and cannot **ever** exist on a PB. - * That is to say, the datapoints here don't make sense to ever be merged. - * You can't merge a NONRAN option with MIRROR! - * - * This is intended for things like modifiers or options. - * (What RANDOM was the user using? etc.) - */ -export interface ScoreMeta { - "iidx:SP": IIDXSPScoreMeta; - "iidx:DP": IIDXSPScoreMeta & { - random: { left: RanOptions; right: RanOptions } | null; - }; - "popn:9B": { - hiSpeed: number | null; - hidden: integer | null; - sudden: integer | null; - random: "MIRROR" | "NONRAN" | "RANDOM" | "S-RANDOM" | null; - gauge: "DANGER" | "EASY" | "HARD" | "NORMAL" | null; - }; - "sdvx:Single": { inSkillAnalyser: boolean | null }; - "usc:Controller": USCScoreMeta; - "usc:Keyboard": USCScoreMeta; - "maimaidx:Single": Record; - "jubeat:Single": Record; - "museca:Single": Record; - "bms:7K": BMS7KScoreMeta; - "bms:14K": BMS7KScoreMeta & { - random: { left: RanOptions; right: RanOptions } | null; - }; - "chunithm:Single": Record; - "gitadora:Gita": Record; - "gitadora:Dora": Record; - "wacca:Single": { mirror: boolean | null }; - "pms:Controller": PMSScoreMeta; - "pms:Keyboard": PMSScoreMeta; - "itg:Stamina": Record; -} - -interface BASE_ADDITIONAL_METRICS { - fast: integer | null; - slow: integer | null; - maxCombo: integer | null; -} - -type IIDXAdditionalMetrics = BASE_ADDITIONAL_METRICS & { - bp: integer | null; - gauge: number | null; - gaugeHistory: Array | null; - comboBreak: integer | null; - - gsmEASY: Array | null; - gsmNORMAL: Array | null; - gsmHARD: Array | null; - gsmEX_HARD: Array | null; -}; - -type BMSJudgePermutations = `${"e" | "l"}${"bd" | "gd" | "gr" | "pg" | "pr"}`; - -type BMSAdditionalMetrics = BASE_ADDITIONAL_METRICS & { - [K in BMSJudgePermutations]: integer; -} & { - bp: integer | null; - gauge: number | null; - gaugeHistory: Array | null; -}; - -type USCAdditionalMetrics = BASE_ADDITIONAL_METRICS & { - gauge: number | null; -}; - -/** - * What GPT-Specific content exists for this score that isn't the actual score metrics? - * - * These are metrics like gauge values at the end of the score, etc. Things like - * fast/slow and maxCombo can also be here. - * - * Everything here is generally optional or nullable, and does not *have* to be provided - * by a score import. - */ -export interface AdditionalMetrics { - "iidx:SP": IIDXAdditionalMetrics; - "iidx:DP": IIDXAdditionalMetrics; - "popn:9B": BASE_ADDITIONAL_METRICS & { - gauge: number | null; - specificClearType: - | "clearCircle" - | "clearDiamond" - | "clearStar" - | "easyClear" - | "failedCircle" - | "failedDiamond" - | "failedStar" - | "fullComboCircle" - | "fullComboDiamond" - | "fullComboStar" - | "perfect" - | null; - }; - "sdvx:Single": BASE_ADDITIONAL_METRICS & { - gauge: number | null; - exScore: number | null; - }; - "usc:Controller": USCAdditionalMetrics; - "usc:Keyboard": USCAdditionalMetrics; - "maimaidx:Single": BASE_ADDITIONAL_METRICS; - "jubeat:Single": BASE_ADDITIONAL_METRICS; - "museca:Single": BASE_ADDITIONAL_METRICS; - "bms:7K": BMSAdditionalMetrics; - "bms:14K": BMSAdditionalMetrics; - "chunithm:Single": BASE_ADDITIONAL_METRICS; - "gitadora:Gita": BASE_ADDITIONAL_METRICS; - "gitadora:Dora": BASE_ADDITIONAL_METRICS; - "wacca:Single": BASE_ADDITIONAL_METRICS; - "pms:Controller": BMSAdditionalMetrics; - "pms:Keyboard": BMSAdditionalMetrics; - "itg:Stamina": BASE_ADDITIONAL_METRICS & { - gaugeHistory: Array | null; - diedAt: integer | null; - }; -} - -type IIDXJudges = "bad" | "good" | "great" | "pgreat" | "poor"; -type GitadoraJudges = "good" | "great" | "miss" | "ok" | "perfect"; - -// judges might need to be changed here... -// @bug Known that sdvx calls misses errors. I, however, don't care. -type SDVXJudges = "critical" | "miss" | "near"; - -/** - * What judgements exist for this GPT? - * - * Tachi mandates that a game has a concept of judgements (i.e. timing windows). - * This type declares the names of those timing windows. - */ -export interface Judgements { - "iidx:SP": IIDXJudges; - "iidx:DP": IIDXJudges; - "popn:9B": "bad" | "cool" | "good" | "great"; - "sdvx:Single": SDVXJudges; - "usc:Controller": SDVXJudges; - "usc:Keyboard": SDVXJudges; - "maimaidx:Single": "good" | "great" | "miss" | "pcrit" | "perfect"; - "jubeat:Single": "good" | "great" | "miss" | "perfect" | "poor"; - "museca:Single": "critical" | "miss" | "near"; - "bms:7K": IIDXJudges; - "bms:14K": IIDXJudges; - "chunithm:Single": "attack" | "jcrit" | "justice" | "miss"; - "gitadora:Gita": GitadoraJudges; - "gitadora:Dora": GitadoraJudges; - "wacca:Single": "good" | "great" | "marvelous" | "miss"; - "pms:Controller": "bad" | "cool" | "good" | "great" | "poor"; - "pms:Keyboard": "bad" | "cool" | "good" | "great" | "poor"; - "itg:Stamina": "decent" | "excellent" | "fantastic" | "great" | "miss" | "wayoff"; -} - -/** - * If any, what preferences (settings) can users set for this GPT? - * - * This allows us to support GPT-Specific preferences, like a "BPI Target", which only - * makes sense for IIDX. - */ -export interface UGPTSpecificPreferences { - "iidx:SP": { display2DXTra: boolean; bpiTarget: number }; - "iidx:DP": { display2DXTra: boolean; bpiTarget: number }; - "popn:9B": Record; - "sdvx:Single": { vf6Target: number }; - "usc:Controller": { vf6Target: number }; - "usc:Keyboard": { vf6Target: number }; - "maimaidx:Single": Record; - "jubeat:Single": { jubilityTarget: number }; - "museca:Single": Record; - "bms:7K": { displayTables: Array }; - "bms:14K": { displayTables: Array }; - "chunithm:Single": Record; - "gitadora:Gita": Record; - "gitadora:Dora": Record; - "wacca:Single": Record; - "pms:Controller": Record; - "pms:Keyboard": Record; - "itg:Stamina": Record; -} - -/** - * What classes does this GPT support? - * - * This type declares the names of class sets for a given GPT. - * - * A class is a *discrete* value from an ordered list of values. Common examples - * would be dans, jubility colours, etc. - * - * You can think of these as discrete profile metrics. - * - * Note that the actual values for these classes are declared in each game's - * configuration. - */ -export interface GameClassSets { - "iidx:SP": "dan"; - "iidx:DP": "dan"; - "popn:9B": "class"; - "sdvx:Single": "dan" | "vfClass"; - "usc:Keyboard": never; - "usc:Controller": never; - "maimaidx:Single": "colour" | "dan"; - "jubeat:Single": "colour"; - "museca:Single": never; - "bms:7K": "genocideDan" | "lnDan" | "scratchDan" | "stslDan"; - "bms:14K": "genocideDan" | "stslDan"; - "chunithm:Single": "colour"; - "gitadora:Gita": "colour"; - "gitadora:Dora": "colour"; - "wacca:Single": "colour" | "stageUp"; - "pms:Controller": "dan"; - "pms:Keyboard": "dan"; - "itg:Stamina": never; -} diff --git a/common/src/types/batch-manual.ts b/common/src/types/batch-manual.ts index 7c9b2d346..85137c81d 100644 --- a/common/src/types/batch-manual.ts +++ b/common/src/types/batch-manual.ts @@ -1,16 +1,3 @@ -import type { GameClassSets } from "../config/game-classes"; -import type { - IDStrings, - Lamps, - JudgementLookup, - integer, - HitMetaLookup, - ScoreMetaLookup, - Difficulties, - IDStringToGame, - IDStringToPlaytype, - GPTSupportedVersions, -} from "../types"; // These MatchTypes don't need `difficulty` set in the batch manual. type MatchTypesNoDifficulty = "bmsChartHash" | "itgChartHash" | "popnChartHash" | "uscChartHash"; diff --git a/common/src/types/documents.ts b/common/src/types/documents.ts index 8cdd33c06..7d252764f 100644 --- a/common/src/types/documents.ts +++ b/common/src/types/documents.ts @@ -1,27 +1,4 @@ -import type { AllClassSets, GameClasses } from "../config/game-classes"; import type { UserAuthLevels, integer } from "../types"; -import type { - Game, - Playtype, - Playtypes, - Difficulties, - Lamps, - Grades, - UGPTSpecificPreferences, - SessionRatingAlgorithms, - GPTStrings, - ProfileRatingAlgorithms, - GPTStringToPlaytype, - GPTStringToGame, - ChartData, - SupportedTierlists, - SupportedVersions, - SongData, - Judgements, - AdditionalMetrics, - ScoreMeta, - ScoreRatingAlgorithms, -} from "./_TOREMOVE-game-implementations"; import type { APIPermissions } from "./api"; import type { ImportTypes } from "./import-types"; import type { BaseNotification, NotificationBody } from "./notifications"; diff --git a/common/src/types/game-config.ts b/common/src/types/game-config.ts index 53a8757ad..d26dd3762 100644 --- a/common/src/types/game-config.ts +++ b/common/src/types/game-config.ts @@ -23,8 +23,9 @@ export interface ClassInfo { interface BaseClassConfig { /** - * What are the possible values for this class field? This should be in - * ascending order. + * What are the possible values for this class field? + * + * @note This should be in ascending order. */ values: Array; } @@ -61,30 +62,27 @@ export interface DerivedClassConfig extends BaseClassConfig { export type ClassConfig = DerivedClassConfig | ProvidedClassConfig; +/** + * This game's difficulty names are arbitrary (unique) strings. This makes sense + * for a lot of home games, where a song may have any number of difficulties + * attached onto it that we want to care for (think osu!). + */ export interface DynamicDifficulties { - /** - * This game's difficulty names are arbitrary (unique) strings. This makes sense - * for a lot of home games, where a song may have any number of difficulties - * attached onto it that we want to care for (think osu!). - */ type: "DYNAMIC"; } +/** + * The amount of difficulties that may belong to a song is a fixed possible set. + * + * For example, if the game only ever supports Easy, Normal and Hard difficulties + * this would be static. + * + * If the game was more like osu!, where a song can have arbitrary unique strings + * as difficulty names, you want DynamicDifficulties instead. + */ export interface FixedDifficulties { - /** - * The amount of difficulties that may belong to a song is a fixed possible set. - * - * For example, if the game only ever supports Easy, Normal and Hard difficulties - * this would be static. - * - * If the game was more like osu!, where a song can have arbitrary unique strings - * as difficulty names, you want "DYNAMIC" instead. - */ type: "FIXED"; - /** - * What order should these difficulties appear in? - */ difficultyOrder: ReadonlyArray; /** @@ -95,15 +93,6 @@ export interface FixedDifficulties { */ difficultyShorthand: Partial>; - /** - * When you click on a song with no difficulty, what should it default to? - * - * @note If the song doesn't have this difficulty, it will pick in an - * undefined order. - * - * i.e. the default difficulty is "ANOTHER" and the song has no "ANOTHER" available, - * then it will just pick something at random that still belongs to that chart. - */ defaultDifficulty: Difficulty; } diff --git a/common/src/types/internals.ts b/common/src/types/internals.ts index 3df9af61f..6ecc5e60f 100644 --- a/common/src/types/internals.ts +++ b/common/src/types/internals.ts @@ -1,7 +1,7 @@ import type { MatchTypes } from "./batch-manual"; import type { ClassConfig, DifficultyConfig, RatingAlgorithmConfig } from "./game-config"; import type { ScoreMetric } from "./metrics"; -import type { ZodRawShape } from "zod"; +import type { AnyZodObject, ZodRawShape } from "zod"; /** * This is **NOT** intended for external use. @@ -30,12 +30,12 @@ export type INTERNAL_GPT_CONFIG = Readonly<{ sessionRatingAlgs: Record; profileRatingAlgs: Record; - supportedClasses: Record; - defaultScoreRatingAlg: string; defaultSessionRatingAlg: string; defaultProfileRatingAlg: string; + supportedClasses: Record; + difficultyConfig: DifficultyConfig; orderedJudgements: ReadonlyArray; @@ -44,7 +44,9 @@ export type INTERNAL_GPT_CONFIG = Readonly<{ supportedMatchTypes: ReadonlyArray; - chartData: ZodRawShape; + preferences: AnyZodObject; + chartData: AnyZodObject; + scoreMeta: AnyZodObject; }>; /** @@ -58,5 +60,5 @@ export type INTERNAL_GAME_CONFIG = Readonly<{ name: string; validPlaytypes: ReadonlyArray; defaultPlaytype: PT; - songData: ZodRawShape; + songData: AnyZodObject; }>; diff --git a/common/src/types/metrics.ts b/common/src/types/metrics.ts index 0e29f3f5c..39f8c08a2 100644 --- a/common/src/types/metrics.ts +++ b/common/src/types/metrics.ts @@ -67,9 +67,19 @@ export type ExtractMetricType = M extends DecimalScoreMet /** * Extract all the names of enum types from this record of score metrics. * + * This is used for enforcing the "default enum" for a GPT. All games have to have + * a default, preferred enum, for things like folder raises and graphs. + * + * For most games, this will be "grade" or "lamp". However, we need a typesafe way + * of checking that this metric is an enum. + * + * Please ignore how magical this type is. I'm sorry. You aren't expected to understand + * this. + * * @example - * ExtractEnumMetricNames<{ score: { type: "INTEGER" } }, grade: { type: "ENUM" ... }> - * would return a type of "grade" + * ExtractEnumMetricNames<{ score: { type: "INTEGER" } }, grade: { type: "ENUM" ... }, + * lamp: { type: "ENUM", ... }> + * would return a type of "grade" | "lamp" */ export type ExtractEnumMetricNames> = { [K in keyof R]: R[K] extends EnumScoreMetric ? K : never; @@ -78,7 +88,7 @@ export type ExtractEnumMetricNames> = { type PossibleMetrics = ExtractMetricType; /** - * Turn a record of ScoreMetrics into their actual representative values. + * Turn a record of ScoreMetrics into their actual literal values. * * @example ExtractMetrics<{ score: IntegerScoreMetric; lamp: EnumScoreMetric<"FAILED"|"CLEAR"> }> * will equal