mirror of
https://github.com/zkldi/Tachi.git
synced 2026-10-01 19:38:13 +03:00
style: migrate to cadence
This commit is contained in:
@@ -1,3 +1,2 @@
|
||||
node_modules
|
||||
js
|
||||
scripts
|
||||
js
|
||||
+26
-51
@@ -1,59 +1,34 @@
|
||||
{
|
||||
"root": true,
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"plugins": [
|
||||
"@typescript-eslint"
|
||||
"@typescript-eslint",
|
||||
"import",
|
||||
"cadence"
|
||||
],
|
||||
"env": {
|
||||
"node": true
|
||||
},
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:prettier/recommended"
|
||||
"plugin:cadence/recommended"
|
||||
],
|
||||
"rules": {
|
||||
"prettier/prettier": [
|
||||
"error",
|
||||
{
|
||||
"printWidth": 100,
|
||||
"tabWidth": 4,
|
||||
"semi": true,
|
||||
"singleQuote": false,
|
||||
"trailingComma": "es5",
|
||||
"bracketSpacing": true,
|
||||
"endOfLine": "auto",
|
||||
"useTabs": true
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/no-unused-vars": "error",
|
||||
"@typescript-eslint/no-unused-expressions": "error",
|
||||
"@typescript-eslint/no-extra-semi": "off",
|
||||
"@typescript-eslint/no-non-null-assertion": "off",
|
||||
"no-unused-labels": "error",
|
||||
"prefer-const": "error",
|
||||
"eqeqeq": "error",
|
||||
"no-eval": "error",
|
||||
"no-var": "error",
|
||||
"no-new-object": "error",
|
||||
"quote-props": [
|
||||
"error",
|
||||
"as-needed"
|
||||
],
|
||||
"no-array-constructor": "error",
|
||||
"prefer-template": "error",
|
||||
"prefer-promise-reject-errors": "error",
|
||||
"no-useless-escape": "error",
|
||||
"no-loop-func": "error",
|
||||
"prefer-rest-params": "error",
|
||||
"default-param-last": "error",
|
||||
"no-new-func": "error",
|
||||
"arrow-body-style": "error",
|
||||
"no-useless-constructor": "error",
|
||||
"no-duplicate-imports": "error",
|
||||
"dot-notation": "error",
|
||||
"space-before-blocks": "error",
|
||||
"no-prototype-builtins": "off",
|
||||
"prefer-arrow-callback": "error"
|
||||
}
|
||||
"no-inline-comments": "off"
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"files": [
|
||||
"**/*.test.ts"
|
||||
],
|
||||
"rules": {
|
||||
// Our test files break these rules *all* the time, and there's
|
||||
// no point refactoring. Our tests deliberately play with the
|
||||
// dynamic nature of TS to more accurately test arbitrary input.
|
||||
"@typescript-eslint/no-floating-promises": "off",
|
||||
"@typescript-eslint/no-unsafe-member-access": "off",
|
||||
"@typescript-eslint/no-unsafe-assignment": "off",
|
||||
"@typescript-eslint/no-unsafe-return": "off",
|
||||
"@typescript-eslint/no-unsafe-argument": "off",
|
||||
"@typescript-eslint/no-unsafe-call": "off",
|
||||
// we abuse this one for faking data, but we really shouldn't.
|
||||
"@typescript-eslint/consistent-type-assertions": "warn"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
+4
-7
@@ -9,19 +9,16 @@
|
||||
"scripts": {
|
||||
"test": "tap",
|
||||
"build": "tsc",
|
||||
"prepublish": "tsc"
|
||||
"prepublish": "tsc",
|
||||
"lint": "eslint . --ext .ts --fix"
|
||||
},
|
||||
"author": "zkldi",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@types/node": "17.0.8",
|
||||
"@types/tap": "15.0.3",
|
||||
"@typescript-eslint/eslint-plugin": "4.19.0",
|
||||
"@typescript-eslint/parser": "4.19.0",
|
||||
"eslint": "7.22.0",
|
||||
"eslint-config-prettier": "8.1.0",
|
||||
"eslint-plugin-prettier": "3.3.1",
|
||||
"prettier": "2.2.1",
|
||||
"eslint": "8",
|
||||
"eslint-plugin-cadence": "0.0.3",
|
||||
"tap": "15.0.9",
|
||||
"ts-node": "10.0.0",
|
||||
"typescript": "4.6.3"
|
||||
|
||||
Generated
+811
-278
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
import t from "tap";
|
||||
import { GetGameConfig, GetGamePTConfig } from "./config";
|
||||
import { allSupportedGames } from "./static-config";
|
||||
import t from "tap";
|
||||
|
||||
t.test("#GetGameConfig", (t) => {
|
||||
t.plan(allSupportedGames.length);
|
||||
@@ -18,6 +18,7 @@ t.test("#GetGamePTConfig", (t) => {
|
||||
|
||||
for (const playtype of gameConfig.validPlaytypes) {
|
||||
const conf = GetGamePTConfig(game, playtype);
|
||||
|
||||
t.equal(
|
||||
conf.idString,
|
||||
`${game}:${playtype}`,
|
||||
|
||||
+34
-26
@@ -1,17 +1,16 @@
|
||||
import { FormatInt, GPTTierlists } from "..";
|
||||
import { FormatInt } from "..";
|
||||
import {
|
||||
BMSGenocideDans,
|
||||
BMSLNDans,
|
||||
BMSScratchDans,
|
||||
BMSStSlDans,
|
||||
CHUNITHMColours,
|
||||
ClassInfo,
|
||||
DDRDans,
|
||||
GameClassSets,
|
||||
GitadoraColours,
|
||||
IIDXDans,
|
||||
JubeatColours,
|
||||
PMSDans,
|
||||
|
||||
// PMSDans,
|
||||
PopnClasses,
|
||||
SDVXDans,
|
||||
@@ -19,8 +18,11 @@ import {
|
||||
WaccaColours,
|
||||
WaccaStageUps,
|
||||
} from "../game-classes";
|
||||
import { ESDJudgementFormat } from "../lib/esd";
|
||||
import {
|
||||
import { FormatSieglindeBMS, FormatSieglindePMS } from "../utils/util";
|
||||
import type { GPTTierlists } from "..";
|
||||
import type { ClassInfo, GameClassSets } from "../game-classes";
|
||||
import type { ESDJudgementFormat } from "../lib/esd";
|
||||
import type {
|
||||
Playtypes,
|
||||
Game,
|
||||
Difficulties,
|
||||
@@ -33,13 +35,12 @@ import {
|
||||
JudgementLookup,
|
||||
GPTSupportedVersions,
|
||||
} from "../types";
|
||||
import { FormatSieglindeBMS, FormatSieglindePMS } from "../utils/util";
|
||||
|
||||
export interface GameConfig<G extends Game = Game> {
|
||||
internalName: string;
|
||||
name: string;
|
||||
defaultPlaytype: Playtypes[G];
|
||||
validPlaytypes: Playtypes[G][];
|
||||
validPlaytypes: Array<Playtypes[G]>;
|
||||
}
|
||||
|
||||
interface BaseGamePTConfig<I extends IDStrings> {
|
||||
@@ -51,9 +52,9 @@ interface BaseGamePTConfig<I extends IDStrings> {
|
||||
defaultSessionRatingAlg: SessionCalculatedDataLookup[I];
|
||||
defaultProfileRatingAlg: UGSRatingsLookup[I];
|
||||
|
||||
scoreRatingAlgs: ScoreCalculatedDataLookup[I][];
|
||||
sessionRatingAlgs: SessionCalculatedDataLookup[I][];
|
||||
profileRatingAlgs: UGSRatingsLookup[I][];
|
||||
scoreRatingAlgs: Array<ScoreCalculatedDataLookup[I]>;
|
||||
sessionRatingAlgs: Array<SessionCalculatedDataLookup[I]>;
|
||||
profileRatingAlgs: Array<UGSRatingsLookup[I]>;
|
||||
|
||||
scoreRatingAlgDescriptions: Record<ScoreCalculatedDataLookup[I], string>;
|
||||
sessionRatingAlgDescriptions: Record<SessionCalculatedDataLookup[I], string>;
|
||||
@@ -65,35 +66,35 @@ interface BaseGamePTConfig<I extends IDStrings> {
|
||||
>;
|
||||
profileRatingAlgFormatters: Partial<Record<UGSRatingsLookup[I], (v: number) => string>>;
|
||||
|
||||
difficulties: Difficulties[I][];
|
||||
difficulties: Array<Difficulties[I]>;
|
||||
shortDifficulties: Partial<Record<Difficulties[I], string>>;
|
||||
defaultDifficulty: Difficulties[I];
|
||||
difficultyColours: Record<Difficulties[I], string | null>;
|
||||
|
||||
grades: Grades[I][];
|
||||
grades: Array<Grades[I]>;
|
||||
gradeColours: Record<Grades[I], string>;
|
||||
clearGrade: Grades[I];
|
||||
gradeBoundaries: number[] | null;
|
||||
gradeBoundaries: Array<number> | null;
|
||||
|
||||
lamps: Lamps[I][];
|
||||
lamps: Array<Lamps[I]>;
|
||||
lampColours: Record<Lamps[I], string>;
|
||||
clearLamp: Lamps[I];
|
||||
|
||||
classHumanisedFormat: Record<GameClassSets[I], ClassInfo[]>;
|
||||
classHumanisedFormat: Record<GameClassSets[I], Array<ClassInfo>>;
|
||||
|
||||
judgements: JudgementLookup[I][];
|
||||
judgements: Array<JudgementLookup[I]>;
|
||||
|
||||
scoreBucket: "grade" | "lamp";
|
||||
|
||||
supportedVersions: GPTSupportedVersions[I][];
|
||||
supportedVersions: Array<GPTSupportedVersions[I]>;
|
||||
|
||||
tierlists: GPTTierlists[I][];
|
||||
tierlists: Array<GPTTierlists[I]>;
|
||||
tierlistDescriptions: Record<GPTTierlists[I], string>;
|
||||
}
|
||||
|
||||
interface GamePTConfigWithESD<I extends IDStrings> extends BaseGamePTConfig<I> {
|
||||
supportsESD: true;
|
||||
judgementWindows: ESDJudgementFormat[];
|
||||
judgementWindows: Array<ESDJudgementFormat>;
|
||||
}
|
||||
|
||||
interface GamePTConfigWithoutESD<I extends IDStrings> extends BaseGamePTConfig<I> {
|
||||
@@ -340,6 +341,7 @@ const GAME_PT_CONFIGS: GamePTConfigs = {
|
||||
MAX: COLOUR_SET.white,
|
||||
},
|
||||
clearGrade: "A",
|
||||
|
||||
// IIDX has some unhinged grade boundaries...
|
||||
// You might think these two lines are identical, but they are not. The order of operations affects
|
||||
// floating point math at this point.
|
||||
@@ -537,6 +539,7 @@ const GAME_PT_CONFIGS: GamePTConfigs = {
|
||||
MAX: COLOUR_SET.white,
|
||||
},
|
||||
clearGrade: "A",
|
||||
|
||||
// see iidx:SP for an explaination of why this is wrote like this.
|
||||
gradeBoundaries: [
|
||||
0,
|
||||
@@ -1099,6 +1102,7 @@ const GAME_PT_CONFIGS: GamePTConfigs = {
|
||||
MAX: COLOUR_SET.white,
|
||||
},
|
||||
clearGrade: "A",
|
||||
|
||||
// see iidx:SP for an explaination of why this is wrote like this.
|
||||
gradeBoundaries: [
|
||||
0,
|
||||
@@ -1210,6 +1214,7 @@ const GAME_PT_CONFIGS: GamePTConfigs = {
|
||||
MAX: COLOUR_SET.white,
|
||||
},
|
||||
clearGrade: "A",
|
||||
|
||||
// see iidx:SP for an explaination of why this is wrote like this.
|
||||
gradeBoundaries: [
|
||||
0,
|
||||
@@ -1277,8 +1282,7 @@ const GAME_PT_CONFIGS: GamePTConfigs = {
|
||||
profileRatingAlgs: ["MFCP", "ktRating"],
|
||||
|
||||
scoreRatingAlgDescriptions: {
|
||||
MFCP:
|
||||
"Marvelous Full Combo Points. An algorithm used in LIFE4 that rewards players for getting MFCs.",
|
||||
MFCP: "Marvelous Full Combo Points. An algorithm used in LIFE4 that rewards players for getting MFCs.",
|
||||
ktRating: "TODO REMOVE ME",
|
||||
},
|
||||
profileRatingAlgDescriptions: {
|
||||
@@ -1409,8 +1413,7 @@ const GAME_PT_CONFIGS: GamePTConfigs = {
|
||||
profileRatingAlgs: ["MFCP", "ktRating"],
|
||||
|
||||
scoreRatingAlgDescriptions: {
|
||||
MFCP:
|
||||
"Marvelous Full Combo Points. An algorithm used in LIFE4 that rewards players for getting MFCs.",
|
||||
MFCP: "Marvelous Full Combo Points. An algorithm used in LIFE4 that rewards players for getting MFCs.",
|
||||
ktRating: "TODO REMOVE ME",
|
||||
},
|
||||
profileRatingAlgDescriptions: {
|
||||
@@ -1589,6 +1592,7 @@ const GAME_PT_CONFIGS: GamePTConfigs = {
|
||||
"SSS+": COLOUR_SET.white,
|
||||
},
|
||||
clearGrade: "A",
|
||||
|
||||
// @hack Maimai's top grade depends on the chart's maximum percent
|
||||
// we just set it at percentMax, but it's not technically correct
|
||||
gradeBoundaries: [0, 10, 20, 40, 60, 80, 90, 94, 97, 98, 99, 99.5, 100, Infinity],
|
||||
@@ -1822,8 +1826,7 @@ const GAME_PT_CONFIGS: GamePTConfigs = {
|
||||
},
|
||||
profileRatingAlgDescriptions: {
|
||||
naiveRate: "A naive rating algorithm that just averages your 50 best scores.",
|
||||
rate:
|
||||
"Rating as it's implemented in game, taking 15 scores from the latest version and 35 from all old versions.",
|
||||
rate: "Rating as it's implemented in game, taking 15 scores from the latest version and 35 from all old versions.",
|
||||
},
|
||||
sessionRatingAlgDescriptions: {
|
||||
rate: "The average of your best 10 ratings this session.",
|
||||
@@ -1865,6 +1868,7 @@ const GAME_PT_CONFIGS: GamePTConfigs = {
|
||||
MASTER: COLOUR_SET.white,
|
||||
},
|
||||
clearGrade: "S",
|
||||
|
||||
// i'm pretty sure the first grade is below the limit of reliable FPA accuracy
|
||||
gradeBoundaries: [0, 0.0001, 3.0001, 70, 80, 85, 90, 93, 95, 97, 98, 99, 100],
|
||||
|
||||
@@ -1948,6 +1952,7 @@ const GAME_PT_CONFIGS: GamePTConfigs = {
|
||||
S: COLOUR_SET.teal,
|
||||
},
|
||||
clearGrade: "A",
|
||||
|
||||
// TECHNICALLY THIS IS NOT CORRECT
|
||||
// A Fail is capped at A rank (82%), no matter what!
|
||||
gradeBoundaries: [0, 50, 62, 72, 82, 90, 95, 98],
|
||||
@@ -2117,6 +2122,7 @@ const GAME_PT_CONFIGS: GamePTConfigs = {
|
||||
MAX: COLOUR_SET.white,
|
||||
},
|
||||
clearGrade: "A",
|
||||
|
||||
// see iidx:SP for an explaination of why this is wrote like this.
|
||||
gradeBoundaries: [
|
||||
0,
|
||||
@@ -2224,6 +2230,7 @@ const GAME_PT_CONFIGS: GamePTConfigs = {
|
||||
MAX: COLOUR_SET.white,
|
||||
},
|
||||
clearGrade: "A",
|
||||
|
||||
// see iidx:SP for an explaination of why this is wrote like this.
|
||||
gradeBoundaries: [
|
||||
0,
|
||||
@@ -2396,5 +2403,6 @@ export function GetGamePTConfig<I extends IDStrings = IDStrings>(
|
||||
playtype: Playtypes[Game]
|
||||
): GamePTConfig<I> {
|
||||
const idString = `${game}:${playtype}` as IDStrings;
|
||||
return (GAME_PT_CONFIGS[idString] as unknown) as GamePTConfig<I>;
|
||||
|
||||
return GAME_PT_CONFIGS[idString] as unknown as GamePTConfig<I>;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {
|
||||
import type {
|
||||
FileUploadImportTypes,
|
||||
IRImportTypes,
|
||||
APIImportTypes,
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
GPTSupportedVersions,
|
||||
} from "..";
|
||||
|
||||
export const fileImportTypes: FileUploadImportTypes[] = [
|
||||
export const fileImportTypes: Array<FileUploadImportTypes> = [
|
||||
"file/eamusement-iidx-csv",
|
||||
"file/eamusement-sdvx-csv",
|
||||
"file/batch-manual",
|
||||
@@ -17,7 +17,7 @@ export const fileImportTypes: FileUploadImportTypes[] = [
|
||||
"file/pli-iidx-csv",
|
||||
];
|
||||
|
||||
export const irImportTypes: IRImportTypes[] = [
|
||||
export const irImportTypes: Array<IRImportTypes> = [
|
||||
"ir/direct-manual",
|
||||
"ir/barbatos",
|
||||
"ir/fervidex",
|
||||
@@ -27,7 +27,7 @@ export const irImportTypes: IRImportTypes[] = [
|
||||
"ir/kshook-sv6c",
|
||||
];
|
||||
|
||||
export const apiImportTypes: APIImportTypes[] = [
|
||||
export const apiImportTypes: Array<APIImportTypes> = [
|
||||
"api/arc-iidx",
|
||||
"api/arc-sdvx",
|
||||
"api/eag-iidx",
|
||||
@@ -37,13 +37,13 @@ export const apiImportTypes: APIImportTypes[] = [
|
||||
"api/min-sdvx",
|
||||
];
|
||||
|
||||
export const allImportTypes: ImportTypes[] = [
|
||||
export const allImportTypes: Array<ImportTypes> = [
|
||||
...fileImportTypes,
|
||||
...irImportTypes,
|
||||
...apiImportTypes,
|
||||
];
|
||||
|
||||
export const allIDStrings: IDStrings[] = [
|
||||
export const allIDStrings: Array<IDStrings> = [
|
||||
"iidx:SP",
|
||||
"bms:14K",
|
||||
"bms:7K",
|
||||
@@ -65,7 +65,7 @@ export const allIDStrings: IDStrings[] = [
|
||||
"itg:Stamina",
|
||||
];
|
||||
|
||||
export const allSupportedGames: Game[] = [
|
||||
export const allSupportedGames: Array<Game> = [
|
||||
"iidx",
|
||||
"museca",
|
||||
"maimai",
|
||||
|
||||
+122
-122
@@ -1,112 +1,112 @@
|
||||
export enum IIDX_LAMPS {
|
||||
NO_PLAY,
|
||||
FAILED,
|
||||
ASSIST_CLEAR,
|
||||
EASY_CLEAR,
|
||||
CLEAR,
|
||||
HARD_CLEAR,
|
||||
EX_HARD_CLEAR,
|
||||
FULL_COMBO,
|
||||
NO_PLAY = 0,
|
||||
FAILED = 1,
|
||||
ASSIST_CLEAR = 2,
|
||||
EASY_CLEAR = 3,
|
||||
CLEAR = 4,
|
||||
HARD_CLEAR = 5,
|
||||
EX_HARD_CLEAR = 6,
|
||||
FULL_COMBO = 7,
|
||||
}
|
||||
|
||||
export enum CHUNITHM_LAMPS {
|
||||
FAILED,
|
||||
CLEAR,
|
||||
FULL_COMBO,
|
||||
ALL_JUSTICE,
|
||||
ALL_JUSTICE_CRITICAL,
|
||||
FAILED = 0,
|
||||
CLEAR = 1,
|
||||
FULL_COMBO = 2,
|
||||
ALL_JUSTICE = 3,
|
||||
ALL_JUSTICE_CRITICAL = 4,
|
||||
}
|
||||
|
||||
export enum SDVX_LAMPS {
|
||||
FAILED,
|
||||
CLEAR,
|
||||
EXCESSIVE_CLEAR,
|
||||
ULTIMATE_CHAIN,
|
||||
PERFECT_ULTIMATE_CHAIN,
|
||||
FAILED = 0,
|
||||
CLEAR = 1,
|
||||
EXCESSIVE_CLEAR = 2,
|
||||
ULTIMATE_CHAIN = 3,
|
||||
PERFECT_ULTIMATE_CHAIN = 4,
|
||||
}
|
||||
|
||||
export enum MUSECA_LAMPS {
|
||||
FAILED,
|
||||
CLEAR,
|
||||
CONNECT_ALL,
|
||||
PERFECT_CONNECT_ALL,
|
||||
FAILED = 0,
|
||||
CLEAR = 1,
|
||||
CONNECT_ALL = 2,
|
||||
PERFECT_CONNECT_ALL = 3,
|
||||
}
|
||||
|
||||
export enum DDR_LAMPS {
|
||||
FAILED,
|
||||
CLEAR,
|
||||
LIFE4,
|
||||
FULL_COMBO,
|
||||
GREAT_FULL_COMBO,
|
||||
PERFECT_FULL_COMBO,
|
||||
MARVELOUS_FULL_COMBO,
|
||||
FAILED = 0,
|
||||
CLEAR = 1,
|
||||
LIFE4 = 2,
|
||||
FULL_COMBO = 3,
|
||||
GREAT_FULL_COMBO = 4,
|
||||
PERFECT_FULL_COMBO = 5,
|
||||
MARVELOUS_FULL_COMBO = 6,
|
||||
}
|
||||
|
||||
export enum MAIMAI_LAMPS {
|
||||
FAILED,
|
||||
CLEAR,
|
||||
FULL_COMBO,
|
||||
ALL_PERFECT,
|
||||
ALL_PERFECT_PLUS,
|
||||
FAILED = 0,
|
||||
CLEAR = 1,
|
||||
FULL_COMBO = 2,
|
||||
ALL_PERFECT = 3,
|
||||
ALL_PERFECT_PLUS = 4,
|
||||
}
|
||||
|
||||
export enum GITADORA_LAMPS {
|
||||
FAILED,
|
||||
CLEAR,
|
||||
FULL_COMBO,
|
||||
EXCELLENT,
|
||||
FAILED = 0,
|
||||
CLEAR = 1,
|
||||
FULL_COMBO = 2,
|
||||
EXCELLENT = 3,
|
||||
}
|
||||
|
||||
export enum IIDX_GRADES {
|
||||
F,
|
||||
E,
|
||||
D,
|
||||
C,
|
||||
B,
|
||||
A,
|
||||
AA,
|
||||
AAA,
|
||||
MAX_MINUS,
|
||||
MAX,
|
||||
F = 0,
|
||||
E = 1,
|
||||
D = 2,
|
||||
C = 3,
|
||||
B = 4,
|
||||
A = 5,
|
||||
AA = 6,
|
||||
AAA = 7,
|
||||
MAX_MINUS = 8,
|
||||
MAX = 9,
|
||||
}
|
||||
|
||||
export enum CHUNITHM_GRADES {
|
||||
D,
|
||||
C,
|
||||
B,
|
||||
BB,
|
||||
BBB,
|
||||
A,
|
||||
AA,
|
||||
AAA,
|
||||
S,
|
||||
SS,
|
||||
SSS,
|
||||
D = 0,
|
||||
C = 1,
|
||||
B = 2,
|
||||
BB = 3,
|
||||
BBB = 4,
|
||||
A = 5,
|
||||
AA = 6,
|
||||
AAA = 7,
|
||||
S = 8,
|
||||
SS = 9,
|
||||
SSS = 10,
|
||||
}
|
||||
|
||||
export enum SDVX_GRADES {
|
||||
D,
|
||||
C,
|
||||
B,
|
||||
A,
|
||||
A_PLUS,
|
||||
AA,
|
||||
AA_PLUS,
|
||||
AAA,
|
||||
AAA_PLUS,
|
||||
S,
|
||||
D = 0,
|
||||
C = 1,
|
||||
B = 2,
|
||||
A = 3,
|
||||
A_PLUS = 4,
|
||||
AA = 5,
|
||||
AA_PLUS = 6,
|
||||
AAA = 7,
|
||||
AAA_PLUS = 8,
|
||||
S = 9,
|
||||
}
|
||||
|
||||
export enum MUSECA_GRADES {
|
||||
没,
|
||||
拙,
|
||||
凡,
|
||||
佳,
|
||||
良,
|
||||
優,
|
||||
秀,
|
||||
傑,
|
||||
傑G,
|
||||
没 = 0,
|
||||
拙 = 1,
|
||||
凡 = 2,
|
||||
佳 = 3,
|
||||
良 = 4,
|
||||
優 = 5,
|
||||
秀 = 6,
|
||||
傑 = 7,
|
||||
傑G = 8,
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -116,57 +116,57 @@ export enum MUSECA_GRADES {
|
||||
* to 975K.
|
||||
*/
|
||||
export enum MUSECA_GRADES_ASCII {
|
||||
ZERO,
|
||||
K600,
|
||||
K700,
|
||||
K800,
|
||||
K850,
|
||||
K900,
|
||||
K950,
|
||||
K975,
|
||||
K1000,
|
||||
ZERO = 0,
|
||||
K600 = 1,
|
||||
K700 = 2,
|
||||
K800 = 3,
|
||||
K850 = 4,
|
||||
K900 = 5,
|
||||
K950 = 6,
|
||||
K975 = 7,
|
||||
K1000 = 8,
|
||||
}
|
||||
|
||||
export enum DDR_GRADES {
|
||||
D,
|
||||
D_PLUS,
|
||||
C_MINUS,
|
||||
C,
|
||||
C_PLUS,
|
||||
B_MINUS,
|
||||
B,
|
||||
B_PLUS,
|
||||
A_MINUS,
|
||||
A,
|
||||
A_PLUS,
|
||||
AA_MINUS,
|
||||
AA,
|
||||
AA_PLUS,
|
||||
AAA,
|
||||
D = 0,
|
||||
D_PLUS = 1,
|
||||
C_MINUS = 2,
|
||||
C = 3,
|
||||
C_PLUS = 4,
|
||||
B_MINUS = 5,
|
||||
B = 6,
|
||||
B_PLUS = 7,
|
||||
A_MINUS = 8,
|
||||
A = 9,
|
||||
A_PLUS = 10,
|
||||
AA_MINUS = 11,
|
||||
AA = 12,
|
||||
AA_PLUS = 13,
|
||||
AAA = 14,
|
||||
}
|
||||
|
||||
export enum MAIMAI_GRADES {
|
||||
F,
|
||||
E,
|
||||
D,
|
||||
C,
|
||||
B,
|
||||
A,
|
||||
AA,
|
||||
AAA,
|
||||
S,
|
||||
S_PLUS,
|
||||
SS,
|
||||
SS_PLUS,
|
||||
SSS,
|
||||
SSS_PLUS,
|
||||
F = 0,
|
||||
E = 1,
|
||||
D = 2,
|
||||
C = 3,
|
||||
B = 4,
|
||||
A = 5,
|
||||
AA = 6,
|
||||
AAA = 7,
|
||||
S = 8,
|
||||
S_PLUS = 9,
|
||||
SS = 10,
|
||||
SS_PLUS = 11,
|
||||
SSS = 12,
|
||||
SSS_PLUS = 13,
|
||||
}
|
||||
|
||||
export enum GITADORA_GRADES {
|
||||
C,
|
||||
B,
|
||||
A,
|
||||
S,
|
||||
SS,
|
||||
MAX,
|
||||
C = 0,
|
||||
B = 1,
|
||||
A = 2,
|
||||
S = 3,
|
||||
SS = 4,
|
||||
MAX = 5,
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { APIPermissions } from "../types";
|
||||
import type { APIPermissions } from "../types";
|
||||
|
||||
// Some parts of tachi-server and some parts of the schemas refer to this
|
||||
// in order to assert that permissions are correctly assigned.
|
||||
|
||||
+15
-14
@@ -1,7 +1,7 @@
|
||||
// Classes refer to things like dans.
|
||||
// Not the JS construct.
|
||||
|
||||
import { IDStrings, integer } from "./types";
|
||||
import type { IDStrings, integer } from "./types";
|
||||
|
||||
export interface ClassInfo {
|
||||
display: string;
|
||||
@@ -11,7 +11,7 @@ export interface ClassInfo {
|
||||
backgroundColor: string;
|
||||
color: string;
|
||||
};
|
||||
variant?: "primary" | "secondary" | "success" | "warning" | "danger" | "info";
|
||||
variant?: "danger" | "info" | "primary" | "secondary" | "success" | "warning";
|
||||
}
|
||||
|
||||
function mouseoverCSS(id: string, d: string, m: string, bg = "black", color = "white") {
|
||||
@@ -22,7 +22,7 @@ function mouseoverVariant(
|
||||
id: string,
|
||||
d: string,
|
||||
m: string,
|
||||
variant: "primary" | "secondary" | "success" | "warning" | "danger" | "info"
|
||||
variant: "danger" | "info" | "primary" | "secondary" | "success" | "warning"
|
||||
) {
|
||||
return { id, display: d, mouseover: m, variant };
|
||||
}
|
||||
@@ -30,7 +30,7 @@ function mouseoverVariant(
|
||||
function noMouseoverVariant(
|
||||
id: string,
|
||||
d: string,
|
||||
variant: "primary" | "secondary" | "success" | "warning" | "danger" | "info"
|
||||
variant: "danger" | "info" | "primary" | "secondary" | "success" | "warning"
|
||||
) {
|
||||
return { id, display: d, variant };
|
||||
}
|
||||
@@ -39,7 +39,7 @@ function noMouseoverCSS(id: string, d: string, bg = "#131313", color = "white")
|
||||
return { id, display: d, css: { backgroundColor: bg, color } };
|
||||
}
|
||||
|
||||
export const IIDXDans: ClassInfo[] = [
|
||||
export const IIDXDans: Array<ClassInfo> = [
|
||||
mouseoverCSS("KYU_7", "七級", "7th Kyu", "green"),
|
||||
mouseoverCSS("KYU_6", "六級", "6th Kyu", "green"),
|
||||
mouseoverCSS("KYU_5", "五級", "5th Kyu", "green"),
|
||||
@@ -61,7 +61,7 @@ export const IIDXDans: ClassInfo[] = [
|
||||
mouseoverVariant("KAIDEN", "皆伝", "Kaiden", "warning"),
|
||||
];
|
||||
|
||||
export const GitadoraColours: ClassInfo[] = [
|
||||
export const GitadoraColours: Array<ClassInfo> = [
|
||||
mouseoverCSS("WHITE", "白", "White", "white", "black"),
|
||||
mouseoverCSS("ORANGE", "橙", "Orange", "orange"),
|
||||
mouseoverCSS("ORANGE_GRD", "橙グラ", "Orange Gradient", "orange"),
|
||||
@@ -78,11 +78,12 @@ export const GitadoraColours: ClassInfo[] = [
|
||||
mouseoverCSS("BRONZE", "銅", "Bronze", "bronze"),
|
||||
mouseoverCSS("SILVER", "銀", "Silver", "silver", "black"),
|
||||
mouseoverCSS("GOLD", "金", "Gold", "gold"),
|
||||
|
||||
// @todo #11 Come up with CSS for Gitadora's rainbow icon.
|
||||
mouseoverCSS("RAINBOW", "虹", "Rainbow", "todo"),
|
||||
];
|
||||
|
||||
export const BMSGenocideDans: ClassInfo[] = [
|
||||
export const BMSGenocideDans: Array<ClassInfo> = [
|
||||
mouseoverCSS("NORMAL_1", "☆1", "Normal 1st Dan", "lightblue"),
|
||||
mouseoverCSS("NORMAL_2", "☆2", "Normal 2nd Dan", "lightblue"),
|
||||
mouseoverCSS("NORMAL_3", "☆3", "Normal 3rd Dan", "lightblue"),
|
||||
@@ -107,7 +108,7 @@ export const BMSGenocideDans: ClassInfo[] = [
|
||||
mouseoverCSS("OVERJOY", "(^^)", "Overjoy", "purple"),
|
||||
];
|
||||
|
||||
export const BMSStSlDans: ClassInfo[] = [
|
||||
export const BMSStSlDans: Array<ClassInfo> = [
|
||||
mouseoverCSS("SL0", "sl0", "Satellite 0"),
|
||||
mouseoverCSS("SL1", "sl1", "Satellite 1"),
|
||||
mouseoverCSS("SL2", "sl2", "Satellite 2"),
|
||||
@@ -135,7 +136,7 @@ export const BMSStSlDans: ClassInfo[] = [
|
||||
mouseoverCSS("ST11", "st11", "Stella 11"),
|
||||
];
|
||||
|
||||
export const BMSLNDans: ClassInfo[] = [
|
||||
export const BMSLNDans: Array<ClassInfo> = [
|
||||
mouseoverCSS("DAN_1", "◆1", "LN 1st Dan", "lightblue"),
|
||||
mouseoverCSS("DAN_2", "◆2", "LN 2nd Dan", "lightblue"),
|
||||
mouseoverCSS("DAN_3", "◆3", "LN 3rd Dan", "lightblue"),
|
||||
@@ -151,7 +152,7 @@ export const BMSLNDans: ClassInfo[] = [
|
||||
mouseoverCSS("UDON", "◆うどん", "LN Udon", "gold"),
|
||||
];
|
||||
|
||||
export const BMSScratchDans: ClassInfo[] = [
|
||||
export const BMSScratchDans: Array<ClassInfo> = [
|
||||
mouseoverCSS("KYU_7", "七級", "Scratch 7th Kyu", "green"),
|
||||
mouseoverCSS("KYU_6", "六級", "Scratch 6th Kyu", "green"),
|
||||
mouseoverCSS("KYU_5", "五級", "Scratch 5th Kyu", "green"),
|
||||
@@ -172,7 +173,7 @@ export const BMSScratchDans: ClassInfo[] = [
|
||||
mouseoverVariant("KAIDEN", "皆伝", "Scratch Kaiden", "warning"),
|
||||
];
|
||||
|
||||
export const SDVXDans: ClassInfo[] = [
|
||||
export const SDVXDans: Array<ClassInfo> = [
|
||||
mouseoverCSS("DAN_1", "LV.01", "1st Dan"),
|
||||
mouseoverCSS("DAN_2", "LV.02", "2nd Dan"),
|
||||
mouseoverCSS("DAN_3", "LV.03", "3rd Dan"),
|
||||
@@ -187,7 +188,7 @@ export const SDVXDans: ClassInfo[] = [
|
||||
mouseoverCSS("INF", "LV.INF", "Inf. Dan", "purple", "gold"),
|
||||
];
|
||||
|
||||
export const SDVXVFClasses: ClassInfo[] = [
|
||||
export const SDVXVFClasses: Array<ClassInfo> = [
|
||||
noMouseoverCSS("SIENNA_I", "Sienna I"),
|
||||
noMouseoverCSS("SIENNA_II", "Sienna II"),
|
||||
noMouseoverCSS("SIENNA_III", "Sienna III"),
|
||||
@@ -340,12 +341,12 @@ export interface GameClassSets {
|
||||
"maimai:Single": never;
|
||||
"jubeat:Single": "colour";
|
||||
"museca:Single": never;
|
||||
"bms:7K": "genocideDan" | "stslDan" | "lnDan" | "scratchDan";
|
||||
"bms:7K": "genocideDan" | "lnDan" | "scratchDan" | "stslDan";
|
||||
"bms:14K": "genocideDan";
|
||||
"chunithm:Single": "colour";
|
||||
"gitadora:Gita": "colour";
|
||||
"gitadora:Dora": "colour";
|
||||
"wacca:Single": "stageUp" | "colour";
|
||||
"wacca:Single": "colour" | "stageUp";
|
||||
"pms:Controller": "dan";
|
||||
"pms:Keyboard": "dan";
|
||||
"itg:Stamina": never;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import t from "tap";
|
||||
|
||||
import * as tachiCommon from "./index";
|
||||
import t from "tap";
|
||||
|
||||
// we just check that we're exporting stuff properly
|
||||
t.test("Property Checks", (t) => {
|
||||
|
||||
@@ -1,539 +0,0 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-nocheck
|
||||
|
||||
// disable all type checking, this is a legacy file and is only kept around
|
||||
// as a reference
|
||||
|
||||
import { Game, Playtypes, ChartDocument, ChartDocument } from "../types";
|
||||
|
||||
// human readable stuff for versions
|
||||
const versionHuman: Record<Game, Record<string, string>> = {
|
||||
iidx: {
|
||||
0: "1st Style",
|
||||
1: "substream",
|
||||
2: "2nd Style",
|
||||
3: "3rd Style",
|
||||
4: "4th Style",
|
||||
5: "5th Style",
|
||||
6: "6th Style",
|
||||
7: "7th Style",
|
||||
8: "8th Style",
|
||||
9: "9th Style",
|
||||
10: "10th Style",
|
||||
11: "IIDX RED",
|
||||
12: "HAPPY SKY",
|
||||
13: "DISTORTED",
|
||||
14: "GOLD",
|
||||
15: "DJ TROOPERS",
|
||||
16: "EMPRESS",
|
||||
17: "SIRIUS",
|
||||
18: "Resort Anthem",
|
||||
19: "Lincle",
|
||||
20: "tricoro",
|
||||
21: "SPADA",
|
||||
22: "PENDUAL",
|
||||
23: "copula",
|
||||
24: "SINOBUZ",
|
||||
25: "CANNON BALLERS",
|
||||
26: "ROOTAGE",
|
||||
27: "HEROIC VERSE",
|
||||
28: "BISTROVER",
|
||||
},
|
||||
museca: {
|
||||
1: "",
|
||||
1.5: "1+1/2",
|
||||
},
|
||||
maimai: {
|
||||
maimai: "",
|
||||
maimaiplus: "PLUS",
|
||||
green: "GReeN",
|
||||
greenplus: "GReeN PLUS",
|
||||
orange: "ORANGE",
|
||||
orangeplus: "ORANGE PLUS",
|
||||
pink: "PiNK",
|
||||
pinkplus: "PiNK PLUS",
|
||||
murasaki: "MURASAKi",
|
||||
murasakiplus: "MURASAKi PLUS",
|
||||
milk: "MiLK",
|
||||
milkplus: "MiLK PLUS",
|
||||
finale: "FiNALE",
|
||||
},
|
||||
// jubeat: {
|
||||
// jubeat: "",
|
||||
// ripples: "ripples",
|
||||
// knit: "knit",
|
||||
// copious: "copious",
|
||||
// saucer: "saucer",
|
||||
// saucerfulfill: "saucer fulfill",
|
||||
// prop: "prop",
|
||||
// qubell: "Qubell",
|
||||
// clan: "clan",
|
||||
// festo: "festo",
|
||||
// },
|
||||
// popn: {
|
||||
// 1: "1",
|
||||
// 2: "2",
|
||||
// 3: "3",
|
||||
// 4: "4",
|
||||
// 5: "5",
|
||||
// 6: "6",
|
||||
// 7: "7",
|
||||
// 8: "8",
|
||||
// 9: "9",
|
||||
// 10: "10",
|
||||
// 11: "11",
|
||||
// 12: "12 Iroha",
|
||||
// 13: "13 CARNIVAL",
|
||||
// 14: "14 FEVER!",
|
||||
// 15: "15 ADVENTURE",
|
||||
// 16: "16 PARTY♪",
|
||||
// 17: "17 THE MOVIE",
|
||||
// 18: "18 Sengoku",
|
||||
// 19: "19 TUNE STREET",
|
||||
// 20: "20 fantasia",
|
||||
// park: "Sunny Park",
|
||||
// lapis: "Lapistoria",
|
||||
// eclale: "éclale",
|
||||
// usaneko: "Usaneko",
|
||||
// peace: "peace",
|
||||
// },
|
||||
sdvx: {
|
||||
booth: "BOOTH",
|
||||
inf: "II -infinite infection-",
|
||||
gw: "III GRAVITY WARS",
|
||||
heaven: "IV HEAVENLY HAVEN",
|
||||
vivid: "V VIVID WAVE",
|
||||
},
|
||||
ddr: {
|
||||
1: "1st Mix",
|
||||
2: "2nd Mix",
|
||||
3: "3rd Mix",
|
||||
4: "4th Mix",
|
||||
5: "5th Mix",
|
||||
max: "MAX",
|
||||
max2: "MAX2",
|
||||
extreme: "EXTREME",
|
||||
snova: "SuperNOVA",
|
||||
snova2: "SuperNOVA 2",
|
||||
x: "X",
|
||||
x2: "X2",
|
||||
x3: "X3 vs. 2nd Mix",
|
||||
2013: "(2013)",
|
||||
2014: "(2014)",
|
||||
a: "Ace",
|
||||
a20: "A20",
|
||||
},
|
||||
chunithm: {
|
||||
chuni: "",
|
||||
chuniplus: "PLUS",
|
||||
air: "AIR",
|
||||
airplus: "AIR PLUS",
|
||||
star: "STAR",
|
||||
starplus: "STAR PLUS",
|
||||
amazon: "AMAZON",
|
||||
amazonplus: "AMAZON PLUS",
|
||||
crystal: "CRYSTAL",
|
||||
crystalplus: "CRYSTAL PLUS",
|
||||
},
|
||||
gitadora: {
|
||||
xg: "XG",
|
||||
xg2: "XG2",
|
||||
xg3: "XG3",
|
||||
gitadora: "",
|
||||
overdrive: "OverDrive",
|
||||
triboost: "Tri-Boost",
|
||||
triboostplus: "Tri-Boost (Re:EVOLVE)",
|
||||
matixx: "Matixx",
|
||||
exchain: "EXCHAIN",
|
||||
nextage: "NEX+AGE",
|
||||
},
|
||||
bms: {
|
||||
0: "BMS",
|
||||
},
|
||||
usc: {
|
||||
0: "USC",
|
||||
},
|
||||
};
|
||||
|
||||
// release orders of the games.
|
||||
const gameOrders = {
|
||||
iidx: [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"7",
|
||||
"8",
|
||||
"9",
|
||||
"10",
|
||||
"11",
|
||||
"12",
|
||||
"13",
|
||||
"14",
|
||||
"15",
|
||||
"16",
|
||||
"17",
|
||||
"18",
|
||||
"19",
|
||||
"20",
|
||||
"21",
|
||||
"22",
|
||||
"23",
|
||||
"24",
|
||||
"25",
|
||||
"26",
|
||||
"27",
|
||||
"28",
|
||||
],
|
||||
museca: ["1", "1.5"],
|
||||
maimai: [
|
||||
"maimai",
|
||||
"maimaiplus",
|
||||
"green",
|
||||
"greenplus",
|
||||
"orange",
|
||||
"orangeplus",
|
||||
"pink",
|
||||
"pinkplus",
|
||||
"murasaki",
|
||||
"murasakiplus",
|
||||
"milk",
|
||||
"milkplus",
|
||||
"finale",
|
||||
],
|
||||
jubeat: [
|
||||
"jubeat",
|
||||
"ripples",
|
||||
"knit",
|
||||
"copious",
|
||||
"saucer",
|
||||
"saucerfulfill",
|
||||
"prop",
|
||||
"qubell",
|
||||
"clan",
|
||||
"festo",
|
||||
],
|
||||
popn: [
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"7",
|
||||
"8",
|
||||
"9",
|
||||
"10",
|
||||
"11",
|
||||
"12",
|
||||
"13",
|
||||
"14",
|
||||
"15",
|
||||
"16",
|
||||
"17",
|
||||
"18",
|
||||
"19",
|
||||
"20",
|
||||
"park",
|
||||
"lapis",
|
||||
"eclale",
|
||||
"usaneko",
|
||||
"peace",
|
||||
],
|
||||
sdvx: ["booth", "inf", "gw", "heaven", "vivid"],
|
||||
ddr: [
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"max",
|
||||
"max2",
|
||||
"extreme",
|
||||
"snova",
|
||||
"snova2",
|
||||
"x",
|
||||
"x2",
|
||||
"x3",
|
||||
"2013",
|
||||
"2014",
|
||||
"a",
|
||||
"a20",
|
||||
],
|
||||
bms: ["0"],
|
||||
chunithm: [
|
||||
"chuni",
|
||||
"chuniplus",
|
||||
"air",
|
||||
"airplus",
|
||||
"star",
|
||||
"starplus",
|
||||
"amazon",
|
||||
"amazonplus",
|
||||
"crystal",
|
||||
"crystalplus",
|
||||
],
|
||||
gitadora: [
|
||||
"gf1",
|
||||
"gf2dm1",
|
||||
"gf3dm2",
|
||||
"gf4dm3",
|
||||
"gf5dm4",
|
||||
"gf6dm5",
|
||||
"gf7dm6",
|
||||
"gf8dm7",
|
||||
"gf8dm7plus",
|
||||
"gf9dm8",
|
||||
"gf10dm9",
|
||||
"gf11dm10",
|
||||
"v",
|
||||
"v2",
|
||||
"v3",
|
||||
"v4",
|
||||
"v5",
|
||||
"v6",
|
||||
"v7",
|
||||
"v8",
|
||||
"xg",
|
||||
"xg2",
|
||||
"xg3",
|
||||
"gitadora",
|
||||
"overdrive",
|
||||
"triboost",
|
||||
"triboostplus",
|
||||
"matixx",
|
||||
"exchain",
|
||||
"nextage",
|
||||
],
|
||||
usc: ["0"],
|
||||
};
|
||||
|
||||
function humaniseGame<T extends Game>(game: T, pt?: Playtypes[T]): string {
|
||||
if (!pt) {
|
||||
return gameHuman[game];
|
||||
}
|
||||
|
||||
if (validPlaytypes[game].length === 1) {
|
||||
return gameHuman[game];
|
||||
}
|
||||
|
||||
return `${gameHuman[game]} (${pt})`;
|
||||
}
|
||||
|
||||
const ratingParameters = {
|
||||
iidx: {
|
||||
failHarshnessMultiplier: 0.3,
|
||||
pivotPercent: 0.7777, // Grade: AA
|
||||
clearExpMultiplier: 1,
|
||||
},
|
||||
bms: {
|
||||
failHarshnessMultiplier: 0.5,
|
||||
pivotPercent: 0.7777, // Grade: AA
|
||||
clearExpMultiplier: 0.75,
|
||||
},
|
||||
museca: {
|
||||
failHarshnessMultiplier: 1,
|
||||
pivotPercent: 0.8, // grade: not fail
|
||||
clearExpMultiplier: 1, // no real reason
|
||||
},
|
||||
popn: {
|
||||
failHarshnessMultiplier: 1,
|
||||
pivotPercent: 0.8, // grade: A
|
||||
clearExpMultiplier: 0.4, // no real reason
|
||||
},
|
||||
maimai: {
|
||||
failHarshnessMultiplier: 1,
|
||||
pivotPercent: 0.8,
|
||||
clearExpMultiplier: 1,
|
||||
},
|
||||
jubeat: {
|
||||
failHarshnessMultiplier: 0.9,
|
||||
pivotPercent: 0.7, // grade: A (clear)
|
||||
clearExpMultiplier: 1,
|
||||
},
|
||||
sdvx: {
|
||||
failHarshnessMultiplier: 1,
|
||||
pivotPercent: 0.92,
|
||||
clearExpMultiplier: 1.45, // testing
|
||||
},
|
||||
usc: {
|
||||
failHarshnessMultiplier: 1,
|
||||
pivotPercent: 0.92,
|
||||
clearExpMultiplier: 1.45, // testing
|
||||
},
|
||||
ddr: {
|
||||
failHarshnessMultiplier: 0.9,
|
||||
pivotPercent: 0.9,
|
||||
clearExpMultiplier: 1,
|
||||
},
|
||||
chunithm: {
|
||||
failHarshnessMultiplier: 1,
|
||||
pivotPercent: 0.975,
|
||||
clearExpMultiplier: 1.1,
|
||||
},
|
||||
gitadora: {
|
||||
// GENERIC PARAMS. THIS IS **NOT** THE USED CALC.
|
||||
failHarshnessMultiplier: 1,
|
||||
pivotPercent: 0.8,
|
||||
clearExpMultiplier: 1.1,
|
||||
},
|
||||
};
|
||||
|
||||
function ChangeAlpha(string: string, alpha: string): string {
|
||||
const spl = string.split(",");
|
||||
spl[spl.length - 1] = `${alpha})`;
|
||||
return spl.join(",");
|
||||
}
|
||||
|
||||
function DirectScoreGradeDelta(
|
||||
game: Game,
|
||||
score: number,
|
||||
percent: number,
|
||||
chart: ChartDocument,
|
||||
delta: number
|
||||
): SGDReturn | null {
|
||||
const grade = GetGrade(game, percent);
|
||||
|
||||
if (!grade) {
|
||||
throw new Error(`Invalid grade created from ${game}, ${percent}`);
|
||||
}
|
||||
|
||||
const scoreObj: PartialScore = {
|
||||
scoreData: {
|
||||
score,
|
||||
grade,
|
||||
},
|
||||
};
|
||||
|
||||
return ScoreGradeDelta(game, scoreObj, chart, delta);
|
||||
}
|
||||
|
||||
interface SGDReturn {
|
||||
grade: string;
|
||||
delta: number;
|
||||
formattedString: string;
|
||||
}
|
||||
|
||||
interface PartialScore {
|
||||
scoreData: {
|
||||
score: number;
|
||||
grade: string;
|
||||
};
|
||||
}
|
||||
|
||||
function ScoreGradeDelta(
|
||||
game: Game,
|
||||
score: PartialScore,
|
||||
chart: ChartDocument,
|
||||
delta: number
|
||||
): SGDReturn | null {
|
||||
const nextGrade = grades[game][grades[game].indexOf(score.scoreData.grade) + delta];
|
||||
|
||||
if (nextGrade) {
|
||||
const nextGradePercent = gradeBoundaries[game][grades[game].indexOf(nextGrade)];
|
||||
|
||||
const nGScore = CalculateScore(game, nextGradePercent, chart);
|
||||
|
||||
if (nGScore) {
|
||||
const delta = score.scoreData.score - nGScore;
|
||||
let formattedString = `(${nextGrade})`;
|
||||
|
||||
if (Number.isInteger(delta)) {
|
||||
formattedString += delta >= 0 ? `+${delta}` : `${delta}`;
|
||||
} else {
|
||||
formattedString += delta >= 0 ? `+${delta.toFixed(2)}` : `${delta.toFixed(2)}`;
|
||||
}
|
||||
|
||||
return {
|
||||
grade: nextGrade,
|
||||
delta: delta,
|
||||
formattedString: formattedString,
|
||||
};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function AbsoluteScoreGradeDelta(
|
||||
game: Game,
|
||||
score: number,
|
||||
percent: number,
|
||||
absDelta: number
|
||||
): SGDReturn | null {
|
||||
const grade = grades[game][absDelta];
|
||||
if (grade) {
|
||||
let chart = null;
|
||||
if (game === "iidx" || game === "bms") {
|
||||
const reversedNC = Math.floor((score / percent) * 100) / 2;
|
||||
chart = {
|
||||
data: {
|
||||
notecount: reversedNC,
|
||||
},
|
||||
} as unknown; // heheh
|
||||
}
|
||||
|
||||
const sc = CalculateScore(game, gradeBoundaries[game][absDelta], chart as ChartDocument);
|
||||
if (sc) {
|
||||
const delta = score - sc;
|
||||
let formattedString = `(${grade})`;
|
||||
formattedString += delta >= 0 ? `+${delta}` : `${delta}`;
|
||||
return {
|
||||
grade: grade,
|
||||
delta: delta,
|
||||
formattedString: formattedString,
|
||||
};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function CalculateScore(game: Game, percent: number, chart: ChartDocument): number | null {
|
||||
let score = percent;
|
||||
|
||||
if (game === "iidx" || game === "bms") {
|
||||
score = (chart as ChartDocument<"iidx:SP">).data.notecount * 2 * (percent / 100);
|
||||
} else if (game === "ddr" || game === "museca" || game === "jubeat" || game === "chunithm") {
|
||||
score = 1_000_000 * (percent / 100);
|
||||
} else if (game === "popn") {
|
||||
score = 100_000 * (percent / 100);
|
||||
} else if (game === "sdvx" || game === "usc") {
|
||||
score = 10_000_000 * (percent / 100);
|
||||
}
|
||||
|
||||
if (score) {
|
||||
return score;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function PercentToScore(percent: number, game: Game, chartData: ChartDocument): number {
|
||||
let eScore = 0;
|
||||
|
||||
if (game === "iidx" || game === "bms") {
|
||||
eScore = percent * (chartData as ChartDocument<"iidx:SP">).data.notecount * 2;
|
||||
} else if (game === "museca" || game === "jubeat") {
|
||||
eScore = percent * 1000000;
|
||||
} else if (game === "popn") {
|
||||
eScore = percent * 100000;
|
||||
} else if (game === "sdvx" || game === "usc") {
|
||||
eScore = percent * 10000000;
|
||||
} else if (game === "ddr") {
|
||||
// todo
|
||||
} else if (game === "chunithm") {
|
||||
eScore = percent * 1000000;
|
||||
} else if (game === "gitadora") {
|
||||
eScore = percent;
|
||||
}
|
||||
|
||||
return eScore;
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
import t from "tap";
|
||||
import { GetGamePTConfig } from "../config/config";
|
||||
import { CalculateESD, ESDCompare, PercentCompare } from "./esd";
|
||||
import { GetGamePTConfig } from "../config/config";
|
||||
import t from "tap";
|
||||
|
||||
function approx(t: Tap.Test, given: number, recieved: number, acc = 0.01) {
|
||||
if (Math.abs(given - recieved) < acc) {
|
||||
return t.pass();
|
||||
} else {
|
||||
return t.fail(`Expected ${given} to be within ${acc} of ${recieved}.`);
|
||||
}
|
||||
|
||||
return t.fail(`Expected ${given} to be within ${acc} of ${recieved}.`);
|
||||
}
|
||||
|
||||
const gptConfig = GetGamePTConfig("iidx", "SP");
|
||||
@@ -20,6 +20,7 @@ const judgementWindows = gptConfig.judgementWindows;
|
||||
|
||||
t.test("#CalculateESD", (t) => {
|
||||
const jdg = judgementWindows;
|
||||
|
||||
approx(t, CalculateESD(jdg, 0.8), 17.58);
|
||||
approx(t, CalculateESD(jdg, 0), 200);
|
||||
t.throws(() => CalculateESD(jdg, 0, true));
|
||||
@@ -37,6 +38,7 @@ t.test("#ESDCompare", (t) => {
|
||||
|
||||
t.test("#PercentCompare", (t) => {
|
||||
const jdg = judgementWindows;
|
||||
|
||||
approx(t, PercentCompare(jdg, 0.99, 0.98), -5.53);
|
||||
approx(t, PercentCompare(jdg, 0.99, 0.98), PercentCompare(jdg, 0.99, 0.98, 1));
|
||||
|
||||
|
||||
+17
-12
@@ -13,7 +13,8 @@ function cdf(x: number, mean: number, variance: number) {
|
||||
function erf(x: number) {
|
||||
// save the sign of x
|
||||
const sign = x >= 0 ? 1 : -1;
|
||||
x = Math.abs(x);
|
||||
|
||||
const absX = Math.abs(x);
|
||||
|
||||
// constants
|
||||
const a1 = 0.254829592;
|
||||
@@ -24,8 +25,9 @@ function erf(x: number) {
|
||||
const p = 0.3275911;
|
||||
|
||||
// A&S formula 7.1.26
|
||||
const t = 1.0 / (1.0 + p * x);
|
||||
const y = 1.0 - ((((a5 * t + a4) * t + a3) * t + a2) * t + a1) * t * Math.exp(-x * x);
|
||||
const t = 1.0 / (1.0 + p * absX);
|
||||
const y = 1.0 - ((((a5 * t + a4) * t + a3) * t + a2) * t + a1) * t * Math.exp(-absX * absX);
|
||||
|
||||
return sign * y; // erf(-x) = -erf(x);
|
||||
}
|
||||
|
||||
@@ -46,21 +48,21 @@ const MEAN = 0;
|
||||
* @returns The percent this std. deviation would produce.
|
||||
*/
|
||||
function StdDeviationToPercent(
|
||||
judgements: ESDJudgementFormat[],
|
||||
judgements: Array<ESDJudgementFormat>,
|
||||
stddev: number,
|
||||
largestValue: number
|
||||
) {
|
||||
let lastJudgeMSBorder = 0;
|
||||
let prbSum = 0;
|
||||
|
||||
for (let i = 0; i < judgements.length; i++) {
|
||||
const judge = judgements[i];
|
||||
for (const judge of judgements) {
|
||||
const nVal = CDFBetween(lastJudgeMSBorder, judge.msBorder, MEAN, stddev ** 2) * judge.value;
|
||||
|
||||
lastJudgeMSBorder = judge.msBorder;
|
||||
prbSum += nVal;
|
||||
prbSum = prbSum + nVal;
|
||||
}
|
||||
|
||||
prbSum /= largestValue;
|
||||
prbSum = prbSum / largestValue;
|
||||
|
||||
return prbSum;
|
||||
}
|
||||
@@ -83,7 +85,7 @@ export interface ESDJudgementFormat {
|
||||
* @returns
|
||||
*/
|
||||
export function CalculateESD(
|
||||
judgements: ESDJudgementFormat[],
|
||||
judgements: Array<ESDJudgementFormat>,
|
||||
percent: number,
|
||||
errOnInaccuracy = false
|
||||
): number {
|
||||
@@ -92,6 +94,7 @@ export function CalculateESD(
|
||||
"(ESD) Invalid percent. Percent must be between 0 and 1, and also a number."
|
||||
);
|
||||
}
|
||||
|
||||
const largestValue = judgements.slice(0).sort((a, b) => b.value - a.value)[0].value;
|
||||
|
||||
// massive optimisation possible here by using better initial estimates with precalc'd table of values.
|
||||
@@ -128,7 +131,7 @@ export function CalculateESD(
|
||||
}
|
||||
|
||||
if (errOnInaccuracy) {
|
||||
throw `(ESD-JS) Did not reach value within MAX_ITERATIONS (${MAX_ITERATIONS})`;
|
||||
throw new Error(`(ESD-JS) Did not reach value within MAX_ITERATIONS (${MAX_ITERATIONS})`);
|
||||
}
|
||||
|
||||
return estSD;
|
||||
@@ -148,6 +151,7 @@ export function ESDCompare(baseESD: number, compareESD: number, cdeg = 1): numbe
|
||||
let inv = false;
|
||||
let variance;
|
||||
let bound;
|
||||
|
||||
if (compareESD > baseESD) {
|
||||
inv = true;
|
||||
variance = compareESD ** 2;
|
||||
@@ -162,7 +166,7 @@ export function ESDCompare(baseESD: number, compareESD: number, cdeg = 1): numbe
|
||||
let besdc = BASE_CASE - esdc;
|
||||
|
||||
if (inv) {
|
||||
besdc *= -1;
|
||||
besdc = besdc * -1;
|
||||
}
|
||||
|
||||
return besdc * 100;
|
||||
@@ -172,12 +176,13 @@ export function ESDCompare(baseESD: number, compareESD: number, cdeg = 1): numbe
|
||||
* Converts two percents to ESD, then runs ESDCompare.
|
||||
*/
|
||||
export function PercentCompare(
|
||||
judgements: ESDJudgementFormat[],
|
||||
judgements: Array<ESDJudgementFormat>,
|
||||
baseP: number,
|
||||
compareP: number,
|
||||
cdeg = 1
|
||||
): number {
|
||||
const e1 = CalculateESD(judgements, baseP);
|
||||
const e2 = CalculateESD(judgements, compareP);
|
||||
|
||||
return ESDCompare(e1, e2, cdeg);
|
||||
}
|
||||
|
||||
+71
-70
@@ -2,15 +2,18 @@
|
||||
// their name in the database to the object they schemaify.
|
||||
// The schemas themselves are wrapped in functions that throw on error.
|
||||
|
||||
import p, {
|
||||
import { allIDStrings, allImportTypes, allSupportedGames } from "../config/static-config";
|
||||
import { ALL_PERMISSIONS } from "../constants/permissions";
|
||||
import { GetGameConfig, GetGamePTConfig } from "../index";
|
||||
import { UserAuthLevels } from "../types";
|
||||
import p from "prudence";
|
||||
import type { GamePTConfig } from "../index";
|
||||
import type { Game, IDStrings, NotificationBody, Playtypes } from "../types";
|
||||
import type {
|
||||
PrudenceSchema,
|
||||
ValidationFunctionParentOptionsKeychain,
|
||||
ValidSchemaValue,
|
||||
} from "prudence";
|
||||
import { allIDStrings, allImportTypes, allSupportedGames } from "../config/static-config";
|
||||
import { ALL_PERMISSIONS } from "../constants/permissions";
|
||||
import { GamePTConfig, GetGameConfig, GetGamePTConfig } from "../index";
|
||||
import { Game, IDStrings, NotificationBody, Playtypes, UserAuthLevels } from "../types";
|
||||
|
||||
export const optNull = (v: ValidSchemaValue): ValidationFunctionParentOptionsKeychain =>
|
||||
p.optional(p.nullable(v));
|
||||
@@ -33,7 +36,7 @@ function prSchemaFnWrap(schema: PrudenceSchema) {
|
||||
};
|
||||
}
|
||||
|
||||
const PR_GameStats = (game: Game, playtype: Playtypes[Game], gptConfig: GamePTConfig) => ({
|
||||
const PR_GAME_STATS = (game: Game, playtype: Playtypes[Game], gptConfig: GamePTConfig) => ({
|
||||
userID: p.isPositiveNonZeroInteger,
|
||||
game: p.is(game),
|
||||
playtype: p.is(playtype),
|
||||
@@ -43,7 +46,7 @@ const PR_GameStats = (game: Game, playtype: Playtypes[Game], gptConfig: GamePTCo
|
||||
),
|
||||
});
|
||||
|
||||
const PR_GoalInfo = {
|
||||
const PR_GOAL_INFO = {
|
||||
progress: "?number",
|
||||
progressHuman: "string",
|
||||
outOf: "number",
|
||||
@@ -98,7 +101,8 @@ const extractGPTIDString = (self: unknown) => {
|
||||
throw new Error(`Expected a string where self.idString is. Got ${s.idString}`);
|
||||
}
|
||||
|
||||
const [game, playtype] = s.idString.split(":");
|
||||
// if there's no ":" in the string, this returns only one element.
|
||||
const [game, playtype] = s.idString.split(":") as [string, string | undefined];
|
||||
|
||||
if (!IsValidGame(game)) {
|
||||
throw new Error(`Expected valid game -- got ${game} from idString ${s.idString}.`);
|
||||
@@ -138,12 +142,12 @@ const extractGPT = (self: unknown) => {
|
||||
return { game: s.game, playtype: s.playtype };
|
||||
};
|
||||
|
||||
const PR_Random = p.isIn("NONRAN", "RANDOM", "R-RANDOM", "S-RANDOM", "MIRROR");
|
||||
const PR_RANDOM = p.isIn("NONRAN", "RANDOM", "R-RANDOM", "S-RANDOM", "MIRROR");
|
||||
|
||||
const GetScoreMeta = (game: Game, playtype: Playtypes[Game]): PrudenceSchema => {
|
||||
if (game === "iidx" && playtype === "SP") {
|
||||
return {
|
||||
random: optNull(PR_Random),
|
||||
random: optNull(PR_RANDOM),
|
||||
assist: optNull(p.isIn("NO ASSIST", "AUTO SCRATCH", "LEGACY NOTE", "FULL ASSIST")),
|
||||
range: optNull(p.isIn("NONE", "SUDDEN+", "HIDDEN+", "SUD+ HID+", "LIFT", "LIFT SUD+")),
|
||||
gauge: optNull(p.isIn("ASSISTED EASY", "EASY", "NORMAL", "HARD", "EX-HARD")),
|
||||
@@ -151,8 +155,8 @@ const GetScoreMeta = (game: Game, playtype: Playtypes[Game]): PrudenceSchema =>
|
||||
} else if (game === "iidx" && playtype === "DP") {
|
||||
return {
|
||||
random: optNull({
|
||||
left: PR_Random,
|
||||
right: PR_Random,
|
||||
left: PR_RANDOM,
|
||||
right: PR_RANDOM,
|
||||
}),
|
||||
assist: optNull(p.isIn("NO ASSIST", "AUTO SCRATCH", "LEGACY NOTE", "FULL ASSIST")),
|
||||
range: optNull(p.isIn("NONE", "SUDDEN+", "HIDDEN+", "SUD+ HID+", "LIFT", "LIFT SUD+")),
|
||||
@@ -167,15 +171,15 @@ const GetScoreMeta = (game: Game, playtype: Playtypes[Game]): PrudenceSchema =>
|
||||
};
|
||||
} else if (game === "bms" && playtype === "7K") {
|
||||
return {
|
||||
random: optNull(PR_Random),
|
||||
random: optNull(PR_RANDOM),
|
||||
inputDevice: optNull(p.isIn("KEYBOARD", "BM_CONTROLLER")),
|
||||
client: optNull(p.isIn("LR2", "lr2oraja")),
|
||||
};
|
||||
} else if (game === "bms" && playtype === "14K") {
|
||||
return {
|
||||
random: optNull({
|
||||
left: PR_Random,
|
||||
right: PR_Random,
|
||||
left: PR_RANDOM,
|
||||
right: PR_RANDOM,
|
||||
}),
|
||||
inputDevice: optNull(p.isIn("KEYBOARD", "BM_CONTROLLER")),
|
||||
client: optNull(p.isIn("LR2", "lr2oraja")),
|
||||
@@ -194,7 +198,7 @@ function IsValidGame(str: string): str is Game {
|
||||
}
|
||||
|
||||
const getPlaytype = (game: Game, self: unknown): Playtypes[Game] => {
|
||||
if (!self || typeof self !== "object") {
|
||||
if (self === null || typeof self !== "object") {
|
||||
throw new Error("Expected an object.");
|
||||
}
|
||||
|
||||
@@ -203,6 +207,7 @@ const getPlaytype = (game: Game, self: unknown): Playtypes[Game] => {
|
||||
const s = self as Record<string, unknown>;
|
||||
|
||||
const playtype = s.playtype as string;
|
||||
|
||||
if (!IsValidPlaytype(game, playtype)) {
|
||||
throw new Error(`Expected any of ${gameConfig.validPlaytypes.join(", ")}`);
|
||||
}
|
||||
@@ -213,7 +218,7 @@ const getPlaytype = (game: Game, self: unknown): Playtypes[Game] => {
|
||||
const games = allSupportedGames;
|
||||
|
||||
const isValidPlaytype = (self: unknown, parent: Record<string, unknown>) => {
|
||||
if (!parent.game || typeof parent.game !== "string" || !IsValidGame(parent.game)) {
|
||||
if (typeof parent.game !== "string" || !IsValidGame(parent.game)) {
|
||||
throw new Error(`Invalid Schema, need game to base IsValidPlaytype off of.`);
|
||||
}
|
||||
|
||||
@@ -416,7 +421,7 @@ function GetChartDataForGPT(idString: IDStrings): PrudenceSchema {
|
||||
}
|
||||
}
|
||||
|
||||
const PR_SongDocument = (game: Game): PrudenceSchema => {
|
||||
const PR_SONG_DOCUMENT = (game: Game): PrudenceSchema => {
|
||||
const data = GetSongDataForGame(game);
|
||||
|
||||
return {
|
||||
@@ -429,7 +434,7 @@ const PR_SongDocument = (game: Game): PrudenceSchema => {
|
||||
};
|
||||
};
|
||||
|
||||
const PR_ChartDocument = (game: Game) => (self: unknown) => {
|
||||
const PR_CHART_DOCUMENT = (game: Game) => (self: unknown) => {
|
||||
const playtype = getPlaytype(game, self);
|
||||
|
||||
const gptConfig = GetGamePTConfig(game, playtype);
|
||||
@@ -459,14 +464,14 @@ const PR_ChartDocument = (game: Game) => (self: unknown) => {
|
||||
// Returns true on success, throws on failure.
|
||||
export type SchemaValidatorFunction = (self: unknown) => true;
|
||||
|
||||
type GameSchemas = Record<`${"songs" | "charts"}-${Game}`, SchemaValidatorFunction>;
|
||||
type GameSchemas = Record<`${"charts" | "songs"}-${Game}`, SchemaValidatorFunction>;
|
||||
|
||||
function CreateGameSchemas() {
|
||||
const obj: Partial<GameSchemas> = {};
|
||||
|
||||
for (const game of games) {
|
||||
obj[`songs-${game}`] = prSchemaFnWrap(PR_SongDocument(game));
|
||||
obj[`charts-${game}`] = PR_ChartDocument(game);
|
||||
obj[`songs-${game}`] = prSchemaFnWrap(PR_SONG_DOCUMENT(game));
|
||||
obj[`charts-${game}`] = PR_CHART_DOCUMENT(game);
|
||||
}
|
||||
|
||||
// guaranteed to no longer be partial
|
||||
@@ -486,6 +491,7 @@ const GAME_SCHEMAS = CreateGameSchemas();
|
||||
const PRE_SCHEMAS = {
|
||||
"bms-course-lookup": prSchemaFnWrap({
|
||||
title: "string",
|
||||
|
||||
// Must be comprised of 4 md5 hashes, which are 32 chars long.
|
||||
md5sums: (self) => {
|
||||
if (typeof self !== "string") {
|
||||
@@ -496,7 +502,7 @@ const PRE_SCHEMAS = {
|
||||
return "Expected 32 * 4 characters (4 md5 hashes long).";
|
||||
}
|
||||
|
||||
if (!self.match(/^[a-z0-9]*$/u)) {
|
||||
if (!/^[a-z0-9]*$/u.exec(self)) {
|
||||
return "Expected all chars to be in the range of a-z0-9.";
|
||||
}
|
||||
|
||||
@@ -516,9 +522,9 @@ const PRE_SCHEMAS = {
|
||||
type: p.isIn("songs", "charts", "static"),
|
||||
data: (self, parent) => {
|
||||
if (parent.type === "songs") {
|
||||
return true; //temp. should be a song.
|
||||
return true; // temp. should be a song.
|
||||
} else if (parent.type === "charts") {
|
||||
return true; //temp. should be a chart.
|
||||
return true; // temp. should be a chart.
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -652,14 +658,12 @@ const PRE_SCHEMAS = {
|
||||
|
||||
const gptConfig = GetGamePTConfig(game, playtype);
|
||||
|
||||
const hitMeta = Object.assign(
|
||||
{
|
||||
fast: optNull(p.isPositiveInteger),
|
||||
slow: optNull(p.isPositiveInteger),
|
||||
maxCombo: optNull(p.isPositiveInteger),
|
||||
},
|
||||
GetHitMeta(game)
|
||||
);
|
||||
const hitMeta = {
|
||||
fast: optNull(p.isPositiveInteger),
|
||||
slow: optNull(p.isPositiveInteger),
|
||||
maxCombo: optNull(p.isPositiveInteger),
|
||||
...GetHitMeta(game),
|
||||
};
|
||||
|
||||
const scoreMeta = GetScoreMeta(game, playtype);
|
||||
|
||||
@@ -701,14 +705,12 @@ const PRE_SCHEMAS = {
|
||||
|
||||
const gptConfig = GetGamePTConfig(game, playtype);
|
||||
|
||||
const hitMeta = Object.assign(
|
||||
{
|
||||
fast: optNull(p.isPositiveInteger),
|
||||
slow: optNull(p.isPositiveInteger),
|
||||
maxCombo: optNull(p.isPositiveInteger),
|
||||
},
|
||||
GetHitMeta(game)
|
||||
);
|
||||
const hitMeta = {
|
||||
fast: optNull(p.isPositiveInteger),
|
||||
slow: optNull(p.isPositiveInteger),
|
||||
maxCombo: optNull(p.isPositiveInteger),
|
||||
...GetHitMeta(game),
|
||||
};
|
||||
|
||||
return prSchemaFnWrap({
|
||||
composedFrom: {
|
||||
@@ -803,6 +805,7 @@ const PRE_SCHEMAS = {
|
||||
importID: "string",
|
||||
scoreIDs: ["string"],
|
||||
game: p.isIn(games),
|
||||
|
||||
// @ts-expect-error We've asserted this is definitely a game.
|
||||
playtypes: [(self) => p.isIn(GetGameConfig(self.game).validPlaytypes)(self)],
|
||||
errors: [
|
||||
@@ -830,8 +833,8 @@ const PRE_SCHEMAS = {
|
||||
goalInfo: [
|
||||
{
|
||||
goalID: "string",
|
||||
old: PR_GoalInfo,
|
||||
new: PR_GoalInfo,
|
||||
old: PR_GOAL_INFO,
|
||||
new: PR_GOAL_INFO,
|
||||
},
|
||||
],
|
||||
milestoneInfo: [
|
||||
@@ -857,7 +860,7 @@ const PRE_SCHEMAS = {
|
||||
|
||||
const gptConfig = GetGamePTConfig(game, playtype);
|
||||
|
||||
return prSchemaFnWrap(PR_GameStats(game, playtype, gptConfig))(self);
|
||||
return prSchemaFnWrap(PR_GAME_STATS(game, playtype, gptConfig))(self);
|
||||
},
|
||||
"game-stats-snapshots": (self: unknown) => {
|
||||
const { game, playtype } = extractGPT(self);
|
||||
@@ -865,7 +868,7 @@ const PRE_SCHEMAS = {
|
||||
const gptConfig = GetGamePTConfig(game, playtype);
|
||||
|
||||
return prSchemaFnWrap(
|
||||
Object.assign(PR_GameStats(game, playtype, gptConfig), {
|
||||
Object.assign(PR_GAME_STATS(game, playtype, gptConfig), {
|
||||
rankings: Object.fromEntries(
|
||||
gptConfig.profileRatingAlgs.map((e) => [
|
||||
e,
|
||||
@@ -890,6 +893,7 @@ const PRE_SCHEMAS = {
|
||||
preferredScoreAlg: p.nullable(p.isIn(gptConfig.scoreRatingAlgs)),
|
||||
preferredSessionAlg: p.nullable(p.isIn(gptConfig.sessionRatingAlgs)),
|
||||
preferredProfileAlg: p.nullable(p.isIn(gptConfig.profileRatingAlgs)),
|
||||
|
||||
// ouch
|
||||
stats: p.and(
|
||||
[
|
||||
@@ -991,7 +995,7 @@ const PRE_SCHEMAS = {
|
||||
// Typescript moment...
|
||||
const s = self as Record<string, unknown>;
|
||||
|
||||
for (const key in ALL_PERMISSIONS) {
|
||||
for (const key of Object.keys(ALL_PERMISSIONS)) {
|
||||
if (s[key] !== undefined && typeof s[key] !== "boolean") {
|
||||
return `Invalid permission value of ${s[key]} at ${key}.`;
|
||||
}
|
||||
@@ -1016,8 +1020,8 @@ const PRE_SCHEMAS = {
|
||||
|
||||
return prSchemaFnWrap({
|
||||
idString: p.isIn(allIDStrings),
|
||||
chartDoc: PR_ChartDocument(game),
|
||||
songDoc: PR_SongDocument(game),
|
||||
chartDoc: PR_CHART_DOCUMENT(game),
|
||||
songDoc: PR_SONG_DOCUMENT(game),
|
||||
userIDs: [p.isPositiveNonZeroInteger],
|
||||
})(self);
|
||||
},
|
||||
@@ -1050,16 +1054,9 @@ const PRE_SCHEMAS = {
|
||||
["import", "importParse", "session", "pb"].map((k) => [k, p.isPositive])
|
||||
),
|
||||
abs: Object.fromEntries(
|
||||
[
|
||||
"parse",
|
||||
"import",
|
||||
"importParse",
|
||||
"session",
|
||||
"pb",
|
||||
"ugs",
|
||||
"goal",
|
||||
"milestone",
|
||||
].map((k) => [k, p.isPositive])
|
||||
["parse", "import", "importParse", "session", "pb", "ugs", "goal", "milestone"].map(
|
||||
(k) => [k, p.isPositive]
|
||||
)
|
||||
),
|
||||
}),
|
||||
"arc-saved-profiles": prSchemaFnWrap({
|
||||
@@ -1073,14 +1070,14 @@ const PRE_SCHEMAS = {
|
||||
createdAt: p.isPositive,
|
||||
consumed: "boolean",
|
||||
consumedBy: (self, parent) => {
|
||||
if (parent.consumed) {
|
||||
if (parent.consumed === true) {
|
||||
return self === null;
|
||||
}
|
||||
|
||||
return p.isPositiveInteger(self);
|
||||
},
|
||||
consumedAt: (self, parent) => {
|
||||
if (parent.consumed) {
|
||||
if (parent.consumed === true) {
|
||||
return self === null;
|
||||
}
|
||||
|
||||
@@ -1100,16 +1097,20 @@ const PRE_SCHEMAS = {
|
||||
|
||||
let subSchema = {};
|
||||
|
||||
if (type === "RIVALED_BY") {
|
||||
subSchema = {
|
||||
userID: p.isPositiveNonZeroInteger,
|
||||
game: p.isIn(games),
|
||||
playtype: isValidPlaytype,
|
||||
};
|
||||
} else if (type === "MILESTONE_CHANGED") {
|
||||
subSchema = {
|
||||
milestoneID: "string",
|
||||
};
|
||||
switch (type) {
|
||||
case "RIVALED_BY": {
|
||||
subSchema = {
|
||||
userID: p.isPositiveNonZeroInteger,
|
||||
game: p.isIn(games),
|
||||
playtype: isValidPlaytype,
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
case "MILESTONE_CHANGED":
|
||||
subSchema = {
|
||||
milestoneID: "string",
|
||||
};
|
||||
}
|
||||
|
||||
const err = p(self, subSchema);
|
||||
|
||||
+303
-295
File diff suppressed because it is too large
Load Diff
+14
-17
@@ -1,6 +1,6 @@
|
||||
import { ChartDocument, SongDocument, Game, IDStrings, Playtypes } from "..";
|
||||
import { GetGameConfig, GetGamePTConfig } from "../config/config";
|
||||
import { Grades, integer } from "../types";
|
||||
import type { ChartDocument, SongDocument, Game, IDStrings, Playtypes } from "..";
|
||||
import type { Grades, integer } from "../types";
|
||||
|
||||
export function FormatInt(v: number): string {
|
||||
return v.toString();
|
||||
@@ -9,6 +9,7 @@ export function FormatInt(v: number): string {
|
||||
export function FormatDifficulty(chart: ChartDocument, game: Game): string {
|
||||
if (game === "bms" || game === "pms") {
|
||||
const bmsChart = chart as ChartDocument<"bms:7K" | "bms:14K">;
|
||||
|
||||
return (
|
||||
bmsChart.data.tableFolders.map((e) => `${e.table}${e.level}`).join(", ") || "Unrated"
|
||||
);
|
||||
@@ -70,11 +71,7 @@ export function FormatGame(game: Game, playtype: Playtypes[Game]): string {
|
||||
return `${gameConfig.name} (${playtype})`;
|
||||
}
|
||||
|
||||
export function FormatChart(
|
||||
game: Game,
|
||||
song: SongDocument,
|
||||
chart: ChartDocument<IDStrings>
|
||||
): string {
|
||||
export function FormatChart(game: Game, song: SongDocument, chart: ChartDocument): string {
|
||||
if (game === "bms") {
|
||||
const tables = (chart as ChartDocument<"bms:7K" | "bms:14K">).data.tableFolders;
|
||||
|
||||
@@ -83,11 +80,11 @@ export function FormatChart(
|
||||
let realTitle = bmsSong.title;
|
||||
|
||||
if (bmsSong.data.subtitle) {
|
||||
realTitle += ` - ${bmsSong.data.subtitle}`;
|
||||
realTitle = `${realTitle} - ${bmsSong.data.subtitle}`;
|
||||
}
|
||||
|
||||
if (bmsSong.data.genre) {
|
||||
realTitle += ` [${bmsSong.data.genre}]`;
|
||||
realTitle = `${realTitle} [${bmsSong.data.genre}]`;
|
||||
}
|
||||
|
||||
if (tables.length === 0) {
|
||||
@@ -96,24 +93,22 @@ export function FormatChart(
|
||||
|
||||
return `${realTitle} (${tables.map((e) => `${e.table}${e.level}`).join(", ")})`;
|
||||
} else if (game === "usc") {
|
||||
const uscChart = chart as ChartDocument<"usc:Keyboard" | "usc:Controller">;
|
||||
|
||||
const tables = Object.entries(uscChart.data.tableFolders);
|
||||
const uscChart = chart as ChartDocument<"usc:Controller" | "usc:Keyboard">;
|
||||
|
||||
// If this chart isn't an official, render it differently
|
||||
if (!uscChart.data.isOfficial) {
|
||||
// Same as BMS. turn this into SongTitle (Keyboard MXM normal1, insane2)
|
||||
return `${song.title} (${chart.playtype} ${chart.difficulty} ${tables
|
||||
.map((e) => `${e[0]}${e[1]}`)
|
||||
.join(", ")})`;
|
||||
} else if (uscChart.data.isOfficial && tables.length !== 0) {
|
||||
return `${song.title} (${chart.playtype} ${
|
||||
chart.difficulty
|
||||
} ${uscChart.data.tableFolders.map((e) => `${e.table}${e.level}`).join(", ")})`;
|
||||
} else if (uscChart.data.tableFolders.length !== 0) {
|
||||
// if this chart is an official **AND** is on tables (unlikely), render
|
||||
// it as so:
|
||||
|
||||
// SongTitle (Keyboard MXM 17, normal1, insane2)
|
||||
return `${song.title} (${chart.playtype} ${chart.difficulty} ${
|
||||
chart.level
|
||||
}, ${tables.map((e) => `${e[0]}${e[1]}`).join(", ")})`;
|
||||
}, ${uscChart.data.tableFolders.map((e) => `${e.table}${e.level}`).join(", ")})`;
|
||||
}
|
||||
|
||||
// otherwise, it's just an official and should be rendered like any other game.
|
||||
@@ -231,6 +226,7 @@ export function GenericFormatGradeDelta<I extends IDStrings = IDStrings>(
|
||||
|
||||
export function FormatSieglindeBMS(sgl: number): string {
|
||||
const fixedSgl = sgl.toFixed(2);
|
||||
|
||||
if (sgl < 13) {
|
||||
return `${fixedSgl} (☆${fixedSgl})`;
|
||||
}
|
||||
@@ -240,6 +236,7 @@ export function FormatSieglindeBMS(sgl: number): string {
|
||||
|
||||
export function FormatSieglindePMS(sgl: number): string {
|
||||
const fixedSgl = sgl.toFixed(2);
|
||||
|
||||
if (sgl < 13) {
|
||||
return `${fixedSgl} (○${fixedSgl})`;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Game, Playtypes } from ".";
|
||||
import { GameClassSets } from "./game-classes";
|
||||
import { GoalImportStat, IDStrings, integer, MilestoneImportStat } from "./types";
|
||||
import type { Game, Playtypes } from ".";
|
||||
import type { GameClassSets } from "./game-classes";
|
||||
import type { GoalImportStat, IDStrings, integer, MilestoneImportStat } from "./types";
|
||||
|
||||
/**
|
||||
* An event fired when a users class improves.
|
||||
@@ -25,12 +25,12 @@ export interface WebhookEventGoalAchievedV1 {
|
||||
content: {
|
||||
userID: integer;
|
||||
game: Game;
|
||||
goals: {
|
||||
goals: Array<{
|
||||
goalID: string;
|
||||
old: GoalImportStat;
|
||||
new: GoalImportStat;
|
||||
playtype: Playtypes[Game];
|
||||
}[];
|
||||
}>;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -16,10 +16,10 @@
|
||||
"sourceMap": true
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts"
|
||||
"src/**/*.ts",
|
||||
"src/**/*.test.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"src/**/*.test.ts",
|
||||
"src/legacy/**/*.ts"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user