mirror of
https://github.com/zkldi/Tachi.git
synced 2026-10-07 06:18:03 +03:00
checkpoint: 6
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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> | 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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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> | 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> | 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> | string | null; inGameID: integer };
|
||||
"sdvx:Single": { inGameID: integer };
|
||||
"usc:Keyboard": ChartDataUSC;
|
||||
"usc:Controller": ChartDataUSC;
|
||||
"jubeat:Single": { inGameID: Array<integer> | 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<number>;
|
||||
notesPerMeasure: Array<number>;
|
||||
};
|
||||
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<string, never>;
|
||||
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<string, never>;
|
||||
"jubeat:Single": Record<string, never>;
|
||||
"museca:Single": Record<string, never>;
|
||||
"bms:7K": BMS7KScoreMeta;
|
||||
"bms:14K": BMS7KScoreMeta & {
|
||||
random: { left: RanOptions; right: RanOptions } | null;
|
||||
};
|
||||
"chunithm:Single": Record<string, never>;
|
||||
"gitadora:Gita": Record<string, never>;
|
||||
"gitadora:Dora": Record<string, never>;
|
||||
"wacca:Single": { mirror: boolean | null };
|
||||
"pms:Controller": PMSScoreMeta;
|
||||
"pms:Keyboard": PMSScoreMeta;
|
||||
"itg:Stamina": Record<string, never>;
|
||||
}
|
||||
|
||||
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<number | null> | null;
|
||||
comboBreak: integer | null;
|
||||
|
||||
gsmEASY: Array<number | null> | null;
|
||||
gsmNORMAL: Array<number | null> | null;
|
||||
gsmHARD: Array<number | null> | null;
|
||||
gsmEX_HARD: Array<number | null> | 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<number> | 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<integer> | 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<string, never>;
|
||||
"sdvx:Single": { vf6Target: number };
|
||||
"usc:Controller": { vf6Target: number };
|
||||
"usc:Keyboard": { vf6Target: number };
|
||||
"maimaidx:Single": Record<string, never>;
|
||||
"jubeat:Single": { jubilityTarget: number };
|
||||
"museca:Single": Record<string, never>;
|
||||
"bms:7K": { displayTables: Array<string> };
|
||||
"bms:14K": { displayTables: Array<string> };
|
||||
"chunithm:Single": Record<string, never>;
|
||||
"gitadora:Gita": Record<string, never>;
|
||||
"gitadora:Dora": Record<string, never>;
|
||||
"wacca:Single": Record<string, never>;
|
||||
"pms:Controller": Record<string, never>;
|
||||
"pms:Keyboard": Record<string, never>;
|
||||
"itg:Stamina": Record<string, never>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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<ClassInfo>;
|
||||
}
|
||||
@@ -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<Difficulty extends string> {
|
||||
/**
|
||||
* 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<Difficulty>;
|
||||
|
||||
/**
|
||||
@@ -95,15 +93,6 @@ export interface FixedDifficulties<Difficulty extends string> {
|
||||
*/
|
||||
difficultyShorthand: Partial<Record<Difficulty, string>>;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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<string, RatingAlgorithmConfig>;
|
||||
profileRatingAlgs: Record<string, RatingAlgorithmConfig>;
|
||||
|
||||
supportedClasses: Record<string, ClassConfig>;
|
||||
|
||||
defaultScoreRatingAlg: string;
|
||||
defaultSessionRatingAlg: string;
|
||||
defaultProfileRatingAlg: string;
|
||||
|
||||
supportedClasses: Record<string, ClassConfig>;
|
||||
|
||||
difficultyConfig: DifficultyConfig;
|
||||
|
||||
orderedJudgements: ReadonlyArray<string>;
|
||||
@@ -44,7 +44,9 @@ export type INTERNAL_GPT_CONFIG = Readonly<{
|
||||
|
||||
supportedMatchTypes: ReadonlyArray<MatchTypes>;
|
||||
|
||||
chartData: ZodRawShape;
|
||||
preferences: AnyZodObject;
|
||||
chartData: AnyZodObject;
|
||||
scoreMeta: AnyZodObject;
|
||||
}>;
|
||||
|
||||
/**
|
||||
@@ -58,5 +60,5 @@ export type INTERNAL_GAME_CONFIG<PT extends string = string> = Readonly<{
|
||||
name: string;
|
||||
validPlaytypes: ReadonlyArray<PT>;
|
||||
defaultPlaytype: PT;
|
||||
songData: ZodRawShape;
|
||||
songData: AnyZodObject;
|
||||
}>;
|
||||
|
||||
@@ -67,9 +67,19 @@ export type ExtractMetricType<M extends ScoreMetric> = 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<R extends Record<string, ScoreMetric>> = {
|
||||
[K in keyof R]: R[K] extends EnumScoreMetric ? K : never;
|
||||
@@ -78,7 +88,7 @@ export type ExtractEnumMetricNames<R extends Record<string, ScoreMetric>> = {
|
||||
type PossibleMetrics = ExtractMetricType<ScoreMetric>;
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
||||
Reference in New Issue
Block a user