mirror of
https://github.com/zkldi/Tachi.git
synced 2026-10-10 00:08:22 +03:00
feat: maimaidx support
This PR was squashed into one commit for easier merging.
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
import useSetSubheader from "components/layout/header/useSetSubheader";
|
||||
import Divider from "components/util/Divider";
|
||||
import ExternalLink from "components/util/ExternalLink";
|
||||
import { TachiConfig } from "lib/config";
|
||||
import React from "react";
|
||||
|
||||
export default function MaimaiDXSiteImportPage() {
|
||||
useSetSubheader(["Import Scores", "maimai DX Site Importer"]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1 className="text-center mb-4">What is the maimai DX site importer?</h1>
|
||||
<div>
|
||||
The maimai DX Site Importer is a script that will scrape your profile on the maimai
|
||||
DX website and import it to {TachiConfig.name}.
|
||||
</div>
|
||||
<Divider />
|
||||
<h1 className="text-center my-4">Setup Instructions</h1>
|
||||
Instructions are available on{" "}
|
||||
<ExternalLink href="https://github.com/j1nxie/kt-maimaidx-site-importer">
|
||||
the GitHub repository
|
||||
</ExternalLink>
|
||||
.
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -9,7 +9,7 @@ export default function WACCASiteImportPage() {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1 className="text-center mb-4">What Is The WACCA Site Importer?</h1>
|
||||
<h1 className="text-center mb-4">What is the WACCA site importer?</h1>
|
||||
<div>
|
||||
The WACCA Site Importer is a script that will scrape your profile on the WACCA
|
||||
website and import it to {TachiConfig.name}.
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import { ONE_DAY } from "util/constants/time";
|
||||
import { CreateChartIDMap, CreateChartLink, CreateSongMap } from "util/data";
|
||||
import { ChangeAtPosition, DistinctArr, NO_OP } from "util/misc";
|
||||
import { DistinctArr, NO_OP } from "util/misc";
|
||||
import { NumericSOV, StrSOV } from "util/sorts";
|
||||
import { GetScaleAchievedFn } from "util/tierlist";
|
||||
import { FormatDate, FormatTime } from "util/time";
|
||||
|
||||
@@ -22,9 +22,9 @@ import {
|
||||
SessionDocument,
|
||||
SongDocument,
|
||||
UserGameStats,
|
||||
Playtype,
|
||||
} from "tachi-common";
|
||||
import { ScoreDataset } from "types/tables";
|
||||
import { Playtype } from "tachi-common";
|
||||
|
||||
interface Data {
|
||||
import: ImportDocument;
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import { IsNullish } from "util/misc";
|
||||
import React from "react";
|
||||
import { COLOUR_SET, PBScoreDocument, ScoreDocument } from "tachi-common";
|
||||
|
||||
export default function MaimaiDXJudgementCell({
|
||||
score,
|
||||
}: {
|
||||
score: ScoreDocument<"maimaidx:Single"> | PBScoreDocument<"maimaidx:Single">;
|
||||
}) {
|
||||
const judgements = score.scoreData.judgements;
|
||||
|
||||
if (
|
||||
IsNullish(judgements.miss) ||
|
||||
IsNullish(judgements.great) ||
|
||||
IsNullish(judgements.good) ||
|
||||
IsNullish(judgements.pcrit) ||
|
||||
IsNullish(judgements.perfect)
|
||||
) {
|
||||
return <td>No Data.</td>;
|
||||
}
|
||||
|
||||
return (
|
||||
<td>
|
||||
<strong>
|
||||
<span style={{ color: COLOUR_SET.vibrantYellow }}>{judgements.pcrit}</span>-
|
||||
<span style={{ color: COLOUR_SET.orange }}>{judgements.perfect}</span>-
|
||||
<span style={{ color: COLOUR_SET.pink }}>{judgements.great}</span>-
|
||||
<span style={{ color: COLOUR_SET.green }}>{judgements.good}</span>-
|
||||
<span style={{ color: COLOUR_SET.gray }}>{judgements.miss}</span>
|
||||
</strong>
|
||||
</td>
|
||||
);
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
import { FormatMillions } from "util/misc";
|
||||
import React from "react";
|
||||
import { Game, PBScoreDocument, ScoreDocument } from "tachi-common";
|
||||
import { Playtype } from "tachi-common";
|
||||
import { Game, PBScoreDocument, ScoreDocument, Playtype } from "tachi-common";
|
||||
import ScoreCell from "./ScoreCell";
|
||||
|
||||
export default function ScoreCellLarge(props: {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Game } from "tachi-common";
|
||||
import { Playtype } from "tachi-common";
|
||||
import { Game, Playtype } from "tachi-common";
|
||||
import { BMSGraphsComponent } from "./components/BMSScoreDropdownParts";
|
||||
import { IIDXGraphsComponent } from "./components/IIDXScoreDropdownParts";
|
||||
import { ITGGraphsComponent } from "./components/ITGScoreDropdownParts";
|
||||
|
||||
@@ -103,6 +103,7 @@ export function GetGPTCoreHeaders<
|
||||
case "jubeat":
|
||||
case "wacca":
|
||||
case "chunithm":
|
||||
case "maimaidx":
|
||||
case "itg":
|
||||
return [
|
||||
ScoreHeader,
|
||||
|
||||
@@ -10,9 +10,9 @@ import {
|
||||
GetGamePTConfig,
|
||||
UserDocument,
|
||||
ShowcaseStatDetails,
|
||||
Playtype,
|
||||
} from "tachi-common";
|
||||
import { GamePT, SetState } from "types/react";
|
||||
import { Playtype } from "tachi-common";
|
||||
import { SongChartsSearch } from "types/api-returns";
|
||||
import DebounceSearch from "components/util/DebounceSearch";
|
||||
import Muted from "components/util/Muted";
|
||||
|
||||
@@ -5,8 +5,8 @@ import {
|
||||
ScoreCalculatedDataLookup,
|
||||
SessionCalculatedDataLookup,
|
||||
ProfileRatingLookup,
|
||||
Playtype,
|
||||
} from "tachi-common";
|
||||
import { Playtype } from "tachi-common";
|
||||
import useLUGPTSettings from "./useLUGPTSettings";
|
||||
|
||||
export default function useScoreRatingAlg<I extends IDStrings = IDStrings>(
|
||||
|
||||
@@ -6,6 +6,7 @@ const scales: Record<Game, number> = {
|
||||
pms: 2,
|
||||
chunithm: 5,
|
||||
gitadora: 1,
|
||||
maimaidx: 1,
|
||||
museca: 5,
|
||||
sdvx: 5,
|
||||
usc: 5,
|
||||
|
||||
@@ -42,6 +42,7 @@ const ScaleNameAchievedFns: ScaleAchievedFns = {
|
||||
"kt-HC": (k) => k.scoreData.lampIndex >= IIDX_LAMPS.HARD_CLEAR,
|
||||
"kt-EXHC": (k) => k.scoreData.lampIndex >= IIDX_LAMPS.EX_HARD_CLEAR,
|
||||
},
|
||||
"maimaidx:Single": {},
|
||||
"museca:Single": {
|
||||
"tachi-score": null,
|
||||
},
|
||||
|
||||
@@ -16,6 +16,8 @@ import {
|
||||
SDVXVFClasses,
|
||||
WaccaColours,
|
||||
WaccaStageUps,
|
||||
MaimaiDXColours,
|
||||
MaimaiDXDans,
|
||||
} from "../game-classes";
|
||||
import type { GPTTierlists } from "..";
|
||||
import type { ClassInfo, GameClassSets } from "../game-classes";
|
||||
@@ -146,6 +148,12 @@ const GAME_CONFIGS: GameConfigs = {
|
||||
internalName: "jubeat",
|
||||
validPlaytypes: ["Single"],
|
||||
},
|
||||
maimaidx: {
|
||||
defaultPlaytype: "Single",
|
||||
name: "maimai DX",
|
||||
internalName: "maimaidx",
|
||||
validPlaytypes: ["Single"],
|
||||
},
|
||||
popn: {
|
||||
defaultPlaytype: "9B",
|
||||
name: "pop'n music",
|
||||
@@ -1266,6 +1274,138 @@ const GAME_PT_CONFIGS: GamePTConfigs = {
|
||||
"sgl-HC": "Sieglinde Hard Clear ratings.",
|
||||
},
|
||||
},
|
||||
"maimaidx:Single": {
|
||||
idString: "maimaidx:Single",
|
||||
|
||||
percentMax: 101,
|
||||
|
||||
defaultScoreRatingAlg: "rate",
|
||||
defaultSessionRatingAlg: "rate",
|
||||
defaultProfileRatingAlg: "naiveRate",
|
||||
|
||||
scoreRatingAlgs: ["rate"],
|
||||
sessionRatingAlgs: ["rate"],
|
||||
profileRatingAlgs: ["naiveRate", "rate"],
|
||||
|
||||
scoreRatingAlgDescriptions: {
|
||||
rate: "Rating as it's implemented in game.",
|
||||
},
|
||||
profileRatingAlgDescriptions: {
|
||||
naiveRate: "A naive rating algorithm that just sums 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.",
|
||||
},
|
||||
sessionRatingAlgDescriptions: {
|
||||
rate: "The average of your best 10 ratings this session.",
|
||||
},
|
||||
|
||||
scoreRatingAlgFormatters: {},
|
||||
profileRatingAlgFormatters: {},
|
||||
sessionRatingAlgFormatters: {},
|
||||
|
||||
difficulties: [
|
||||
"Basic",
|
||||
"Advanced",
|
||||
"Expert",
|
||||
"Master",
|
||||
"Re:Master",
|
||||
"DX Basic",
|
||||
"DX Advanced",
|
||||
"DX Expert",
|
||||
"DX Master",
|
||||
"DX Re:Master",
|
||||
],
|
||||
shortDifficulties: {
|
||||
Basic: "BAS",
|
||||
Advanced: "ADV",
|
||||
Expert: "EXP",
|
||||
Master: "MAS",
|
||||
"Re:Master": "Re:MAS",
|
||||
"DX Basic": "DX BAS",
|
||||
"DX Advanced": "DX ADV",
|
||||
"DX Expert": "DX EXP",
|
||||
"DX Master": "DX MAS",
|
||||
"DX Re:Master": "DX Re:MAS",
|
||||
},
|
||||
defaultDifficulty: "Master",
|
||||
difficultyColours: {
|
||||
Basic: COLOUR_SET.green,
|
||||
Advanced: COLOUR_SET.orange,
|
||||
Expert: COLOUR_SET.red,
|
||||
Master: COLOUR_SET.purple,
|
||||
"Re:Master": COLOUR_SET.white,
|
||||
"DX Basic": COLOUR_SET.green,
|
||||
"DX Advanced": COLOUR_SET.orange,
|
||||
"DX Expert": COLOUR_SET.red,
|
||||
"DX Master": COLOUR_SET.purple,
|
||||
"DX Re:Master": COLOUR_SET.white,
|
||||
},
|
||||
|
||||
grades: [
|
||||
"D",
|
||||
"C",
|
||||
"B",
|
||||
"BB",
|
||||
"BBB",
|
||||
"A",
|
||||
"AA",
|
||||
"AAA",
|
||||
"S",
|
||||
"S+",
|
||||
"SS",
|
||||
"SS+",
|
||||
"SSS",
|
||||
"SSS+",
|
||||
],
|
||||
gradeColours: {
|
||||
D: COLOUR_SET.gray,
|
||||
C: COLOUR_SET.red,
|
||||
B: COLOUR_SET.maroon,
|
||||
BB: COLOUR_SET.purple,
|
||||
BBB: COLOUR_SET.paleGreen,
|
||||
A: COLOUR_SET.green,
|
||||
AA: COLOUR_SET.paleBlue,
|
||||
AAA: COLOUR_SET.blue,
|
||||
S: COLOUR_SET.gold,
|
||||
"S+": COLOUR_SET.vibrantYellow,
|
||||
SS: COLOUR_SET.paleOrange,
|
||||
"SS+": COLOUR_SET.orange,
|
||||
SSS: COLOUR_SET.teal,
|
||||
"SSS+": COLOUR_SET.white,
|
||||
},
|
||||
clearGrade: "A",
|
||||
|
||||
gradeBoundaries: [0, 10, 20, 40, 60, 80, 90, 94, 97, 98, 99, 99.5, 100, 100.5],
|
||||
|
||||
lamps: ["FAILED", "CLEAR", "FULL COMBO", "FULL COMBO+", "ALL PERFECT", "ALL PERFECT+"],
|
||||
lampColours: {
|
||||
FAILED: COLOUR_SET.red,
|
||||
CLEAR: COLOUR_SET.green,
|
||||
"FULL COMBO": COLOUR_SET.blue,
|
||||
"FULL COMBO+": COLOUR_SET.paleBlue,
|
||||
"ALL PERFECT": COLOUR_SET.gold,
|
||||
"ALL PERFECT+": COLOUR_SET.teal,
|
||||
},
|
||||
clearLamp: "CLEAR",
|
||||
|
||||
classHumanisedFormat: {
|
||||
colour: MaimaiDXColours,
|
||||
dan: MaimaiDXDans,
|
||||
},
|
||||
classProperties: {
|
||||
colour: { downgradable: true },
|
||||
dan: { downgradable: true },
|
||||
},
|
||||
|
||||
supportsESD: false,
|
||||
judgements: ["pcrit", "perfect", "great", "good", "miss"],
|
||||
|
||||
scoreBucket: "grade",
|
||||
|
||||
supportedVersions: ["universeplus"],
|
||||
|
||||
tierlists: [],
|
||||
tierlistDescriptions: {},
|
||||
},
|
||||
"gitadora:Gita": {
|
||||
idString: "gitadora:Gita",
|
||||
|
||||
|
||||
@@ -54,6 +54,7 @@ export const allIDStrings: Array<IDStrings> = [
|
||||
"gitadora:Dora",
|
||||
"gitadora:Gita",
|
||||
"iidx:DP",
|
||||
"maimaidx:Single",
|
||||
"museca:Single",
|
||||
"sdvx:Single",
|
||||
"usc:Controller",
|
||||
@@ -68,6 +69,7 @@ export const allIDStrings: Array<IDStrings> = [
|
||||
export const allSupportedGames: Array<Game> = [
|
||||
"iidx",
|
||||
"museca",
|
||||
"maimaidx",
|
||||
"jubeat",
|
||||
"popn",
|
||||
"sdvx",
|
||||
|
||||
@@ -42,6 +42,15 @@ export enum DDR_LAMPS {
|
||||
MARVELOUS_FULL_COMBO = 6,
|
||||
}
|
||||
|
||||
export enum MAIMAIDX_LAMPS {
|
||||
FAILED = 0,
|
||||
CLEAR = 1,
|
||||
FULL_COMBO = 2,
|
||||
FULL_COMBO_PLUS = 3,
|
||||
ALL_PERFECT = 4,
|
||||
ALL_PERFECT_PLUS = 5,
|
||||
}
|
||||
|
||||
export enum GITADORA_LAMPS {
|
||||
FAILED = 0,
|
||||
CLEAR = 1,
|
||||
@@ -389,3 +398,60 @@ export enum JUBEAT_COLOURS {
|
||||
ORANGE = 8,
|
||||
GOLD = 9,
|
||||
}
|
||||
|
||||
export enum MAIMAIDX_COLOURS {
|
||||
WHITE = 0,
|
||||
BLUE = 1,
|
||||
GREEN = 2,
|
||||
YELLOW = 3,
|
||||
RED = 4,
|
||||
PURPLE = 5,
|
||||
BRONZE = 6,
|
||||
SILVER = 7,
|
||||
GOLD = 8,
|
||||
PLATINUM = 9,
|
||||
RAINBOW = 10,
|
||||
}
|
||||
|
||||
export enum MAIMAIDX_DANS {
|
||||
DAN_1 = 0,
|
||||
DAN_2 = 1,
|
||||
DAN_3 = 2,
|
||||
DAN_4 = 3,
|
||||
DAN_5 = 4,
|
||||
DAN_6 = 5,
|
||||
DAN_7 = 6,
|
||||
DAN_8 = 7,
|
||||
DAN_9 = 8,
|
||||
DAN_10 = 9,
|
||||
|
||||
SHINSHODAN = 10,
|
||||
SHINDAN_2 = 11,
|
||||
SHINDAN_3 = 12,
|
||||
SHINDAN_4 = 13,
|
||||
SHINDAN_5 = 14,
|
||||
SHINDAN_6 = 15,
|
||||
SHINDAN_7 = 16,
|
||||
SHINDAN_8 = 17,
|
||||
SHINDAN_9 = 18,
|
||||
SHINDAN_10 = 19,
|
||||
|
||||
SHINKAIDEN = 20,
|
||||
}
|
||||
|
||||
export enum MAIMAIDX_GRADES {
|
||||
D = 0,
|
||||
C = 1,
|
||||
B = 2,
|
||||
BB = 3,
|
||||
BBB = 4,
|
||||
A = 5,
|
||||
AA = 6,
|
||||
AAA = 7,
|
||||
S = 8,
|
||||
S_PLUS = 9,
|
||||
SS = 10,
|
||||
SS_PLUS = 11,
|
||||
SSS = 12,
|
||||
SSS_PLUS = 13,
|
||||
}
|
||||
|
||||
@@ -334,6 +334,48 @@ export const JubeatColours = [
|
||||
noMouseoverVariant("GOLD", "Gold", "warning"),
|
||||
];
|
||||
|
||||
export const MaimaiDXDans = [
|
||||
mouseoverVariant("DAN_1", "初段", "1st Dan", "warning"),
|
||||
mouseoverVariant("DAN_2", "二段", "2nd Dan", "warning"),
|
||||
mouseoverVariant("DAN_3", "三段", "3rd Dan", "warning"),
|
||||
mouseoverVariant("DAN_4", "四段", "4th Dan", "warning"),
|
||||
mouseoverVariant("DAN_5", "五段", "5th Dan", "warning"),
|
||||
mouseoverVariant("DAN_6", "六段", "6th Dan", "warning"),
|
||||
mouseoverVariant("DAN_7", "七段", "7th Dan", "warning"),
|
||||
mouseoverVariant("DAN_8", "八段", "8th Dan", "warning"),
|
||||
mouseoverVariant("DAN_9", "九段", "9th Dan", "warning"),
|
||||
mouseoverVariant("DAN_10", "十段", "10th Dan", "warning"),
|
||||
|
||||
mouseoverCSS("SHINDAN_1", "真初段", "Shinshodan", "purple"),
|
||||
mouseoverCSS("SHINDAN_2", "真二段", "2nd Shindan", "purple"),
|
||||
mouseoverCSS("SHINDAN_3", "真三段", "3rd Shindan", "purple"),
|
||||
mouseoverCSS("SHINDAN_4", "真四段", "4th Shindan", "purple"),
|
||||
mouseoverCSS("SHINDAN_5", "真五段", "5th Shindan", "purple"),
|
||||
mouseoverCSS("SHINDAN_6", "真六段", "6th Shindan", "purple"),
|
||||
mouseoverCSS("SHINDAN_7", "真七段", "7th Shindan", "purple"),
|
||||
mouseoverCSS("SHINDAN_8", "真八段", "8th Shindan", "purple"),
|
||||
mouseoverCSS("SHINDAN_9", "真九段", "9th Shindan", "purple"),
|
||||
mouseoverCSS("SHINDAN_10", "真十段", "10th Shindan", "purple"),
|
||||
|
||||
mouseoverCSS("SHINKAIDEN", "真皆伝", "Shinkaiden", "purple"),
|
||||
];
|
||||
|
||||
export const MaimaiDXColours = [
|
||||
noMouseoverCSS("WHITE", "White", "white", "black"),
|
||||
noMouseoverCSS("BLUE", "Blue", "cyan", "black"),
|
||||
noMouseoverCSS("GREEN", "Green", "green"),
|
||||
noMouseoverCSS("YELLOW", "Yellow", "yellow", "black"),
|
||||
noMouseoverCSS("RED", "Red", "red"),
|
||||
noMouseoverCSS("PURPLE", "Purple", "purple"),
|
||||
noMouseoverCSS("BRONZE", "Bronze", "brown"),
|
||||
noMouseoverCSS("SILVER", "Silver", "gray"),
|
||||
noMouseoverVariant("GOLD", "Gold", "warning"),
|
||||
noMouseoverCSS("PLATINUM", "Platinum", "lightgoldenrodyellow", "black"),
|
||||
|
||||
// come up with color for dx's rainbow
|
||||
noMouseoverCSS("RAINBOW", "Rainbow", "todo"),
|
||||
];
|
||||
|
||||
export interface GameClassSets {
|
||||
"iidx:SP": "dan";
|
||||
"iidx:DP": "dan";
|
||||
@@ -341,6 +383,7 @@ export interface GameClassSets {
|
||||
"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";
|
||||
|
||||
@@ -279,6 +279,10 @@ function GetSongDataForGame(game: Game): PrudenceSchema {
|
||||
return {
|
||||
displayVersion: "string",
|
||||
};
|
||||
case "maimaidx":
|
||||
return {
|
||||
displayVersion: "string",
|
||||
};
|
||||
case "museca":
|
||||
return {
|
||||
titleJP: "string",
|
||||
@@ -364,6 +368,10 @@ function GetChartDataForGPT(idString: IDStrings): PrudenceSchema {
|
||||
worldRecord: "?number",
|
||||
bpiCoefficient: "?number",
|
||||
};
|
||||
case "maimaidx:Single":
|
||||
return {
|
||||
isLatest: "boolean",
|
||||
};
|
||||
case "museca:Single":
|
||||
return {
|
||||
inGameID: p.isPositiveInteger,
|
||||
|
||||
@@ -42,6 +42,7 @@ const CURRENT_DATABASE_SEEDS: Record<keyof AllDatabaseSeeds, true> = {
|
||||
"charts-iidx.json": true,
|
||||
"charts-itg.json": true,
|
||||
"charts-jubeat.json": true,
|
||||
"charts-maimaidx.json": true,
|
||||
"charts-museca.json": true,
|
||||
"charts-pms.json": true,
|
||||
"charts-popn.json": true,
|
||||
@@ -58,6 +59,7 @@ const CURRENT_DATABASE_SEEDS: Record<keyof AllDatabaseSeeds, true> = {
|
||||
"songs-iidx.json": true,
|
||||
"songs-itg.json": true,
|
||||
"songs-jubeat.json": true,
|
||||
"songs-maimaidx.json": true,
|
||||
"songs-museca.json": true,
|
||||
"songs-pms.json": true,
|
||||
"songs-popn.json": true,
|
||||
|
||||
@@ -30,6 +30,7 @@ export type IDStrings =
|
||||
| "iidx:SP"
|
||||
| "itg:Stamina"
|
||||
| "jubeat:Single"
|
||||
| "maimaidx:Single"
|
||||
| "museca:Single"
|
||||
| "pms:Controller"
|
||||
| "pms:Keyboard"
|
||||
@@ -46,6 +47,7 @@ export interface IDStringToPlaytype {
|
||||
"sdvx:Single": "Single";
|
||||
"usc:Keyboard": "Keyboard";
|
||||
"usc:Controller": "Controller";
|
||||
"maimaidx:Single": "Single";
|
||||
"jubeat:Single": "Single";
|
||||
"museca:Single": "Single";
|
||||
"bms:7K": "7K";
|
||||
@@ -66,6 +68,7 @@ export interface IDStringToGame {
|
||||
"sdvx:Single": "sdvx";
|
||||
"usc:Keyboard": "usc";
|
||||
"usc:Controller": "usc";
|
||||
"maimaidx:Single": "maimaidx";
|
||||
"jubeat:Single": "jubeat";
|
||||
"museca:Single": "museca";
|
||||
"bms:7K": "bms";
|
||||
@@ -83,6 +86,7 @@ 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";
|
||||
@@ -119,6 +123,7 @@ export type Game =
|
||||
| "iidx"
|
||||
| "itg"
|
||||
| "jubeat"
|
||||
| "maimaidx"
|
||||
| "museca"
|
||||
| "pms"
|
||||
| "popn"
|
||||
@@ -159,6 +164,7 @@ export interface Playtypes {
|
||||
popn: "9B";
|
||||
sdvx: "Single";
|
||||
usc: "Controller" | "Keyboard";
|
||||
maimaidx: "Single";
|
||||
jubeat: "Single";
|
||||
museca: "Single";
|
||||
chunithm: "Single";
|
||||
@@ -181,6 +187,21 @@ export interface Grades {
|
||||
"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;
|
||||
@@ -233,6 +254,13 @@ export interface Lamps {
|
||||
"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;
|
||||
@@ -257,6 +285,17 @@ export interface Difficulties {
|
||||
"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";
|
||||
@@ -387,6 +426,7 @@ export interface SessionCalculatedDataLookup {
|
||||
"sdvx:Single": "ProfileVF6" | "VF6";
|
||||
"usc:Keyboard": "ProfileVF6" | "VF6";
|
||||
"usc:Controller": "ProfileVF6" | "VF6";
|
||||
"maimaidx:Single": "rate";
|
||||
"jubeat:Single": "jubility";
|
||||
"museca:Single": "curatorSkill";
|
||||
"bms:7K": "sieglinde";
|
||||
@@ -607,6 +647,7 @@ export interface ProfileRatingLookup {
|
||||
"sdvx:Single": "VF6";
|
||||
"usc:Keyboard": "VF6";
|
||||
"usc:Controller": "VF6";
|
||||
"maimaidx:Single": "naiveRate" | "rate";
|
||||
"jubeat:Single": "jubility" | "naiveJubility";
|
||||
"museca:Single": "curatorSkill";
|
||||
"bms:7K": "sieglinde";
|
||||
@@ -680,6 +721,7 @@ export interface GPTSupportedVersions {
|
||||
| "qubell"
|
||||
| "ripples"
|
||||
| "saucer";
|
||||
"maimaidx:Single": "universeplus";
|
||||
"museca:Single": "1.5-b" | "1.5";
|
||||
"bms:7K": never;
|
||||
"bms:14K": never;
|
||||
@@ -728,6 +770,7 @@ interface ChartDocumentData {
|
||||
"usc:Keyboard": CDDataUSC;
|
||||
"usc:Controller": CDDataUSC;
|
||||
"jubeat:Single": { inGameID: Array<integer> | integer; isHardMode: boolean };
|
||||
"maimaidx:Single": { isLatest: boolean };
|
||||
"museca:Single": { inGameID: integer };
|
||||
"bms:7K": CDDataBMS;
|
||||
"bms:14K": CDDataBMS;
|
||||
@@ -770,6 +813,7 @@ export interface GPTTierlists {
|
||||
"sdvx:Single": "clear";
|
||||
"usc:Keyboard": never;
|
||||
"usc:Controller": never;
|
||||
"maimaidx:Single": never;
|
||||
"jubeat:Single": never;
|
||||
"museca:Single": never;
|
||||
"chunithm:Single": never;
|
||||
@@ -804,6 +848,7 @@ export interface ChartDocument<I extends IDStrings = IDStrings> extends MongoDBD
|
||||
interface SongDocumentData {
|
||||
iidx: { genre: string; displayVersion: string | null };
|
||||
museca: { titleJP: string; artistJP: string; displayVersion: string };
|
||||
maimaidx: { displayVersion: string };
|
||||
jubeat: { displayVersion: string };
|
||||
popn: {
|
||||
displayVersion: string | null;
|
||||
@@ -955,6 +1000,7 @@ interface ScoreMetaLookup {
|
||||
"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;
|
||||
@@ -1029,6 +1075,7 @@ export interface HitMetaLookup {
|
||||
};
|
||||
"usc:Controller": USCHitMeta;
|
||||
"usc:Keyboard": USCHitMeta;
|
||||
"maimaidx:Single": BASE_VALID_HIT_META;
|
||||
"jubeat:Single": BASE_VALID_HIT_META;
|
||||
"museca:Single": BASE_VALID_HIT_META;
|
||||
"bms:7K": BMSHitMeta;
|
||||
@@ -1059,6 +1106,7 @@ export interface JudgementLookup {
|
||||
"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;
|
||||
@@ -1079,6 +1127,7 @@ export interface ScoreCalculatedDataLookup {
|
||||
"sdvx:Single": "VF6";
|
||||
"usc:Keyboard": "VF6";
|
||||
"usc:Controller": "VF6";
|
||||
"maimaidx:Single": "rate";
|
||||
"jubeat:Single": "jubility";
|
||||
"museca:Single": "curatorSkill";
|
||||
"bms:7K": "sieglinde";
|
||||
@@ -1336,6 +1385,7 @@ export interface UGPTSpecificPreferences {
|
||||
"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> };
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,151 @@
|
||||
const { parse } = require("csv-parse/sync");
|
||||
const fs = require("fs");
|
||||
const fetch = require("node-fetch");
|
||||
const { Command } = require("commander");
|
||||
const { CreateChartID, ReadCollection, WriteCollection } = require("../../util");
|
||||
const { decode } = require("html-entities");
|
||||
const logger = require("../../logger");
|
||||
|
||||
const program = new Command();
|
||||
|
||||
const DATA_URL = "https://maimai.sega.jp/data/maimai_songs.json";
|
||||
|
||||
const versionMap = new Map([
|
||||
[0, null],
|
||||
[100, "maimai"],
|
||||
[110, "maimai PLUS"],
|
||||
[120, "GreeN"],
|
||||
[130, "GreeN PLUS"],
|
||||
[140, "ORANGE"],
|
||||
[150, "ORANGE PLUS"],
|
||||
[160, "PiNK"],
|
||||
[170, "PiNK PLUS"],
|
||||
[180, "MURASAKi"],
|
||||
[185, "MURASAKi PLUS"],
|
||||
[190, "MiLK"],
|
||||
[195, "MiLK PLUS"],
|
||||
[199, "FiNALE"],
|
||||
[200, "maimaiでらっくす"],
|
||||
[205, "maimaiでらっくす PLUS"],
|
||||
[210, "Splash"],
|
||||
[215, "Splash PLUS"],
|
||||
[220, "UNiVERSE"],
|
||||
[225, "UNiVERSE PLUS"],
|
||||
[230, "FESTiVAL"],
|
||||
]);
|
||||
|
||||
const diffNames = ["bas", "adv", "exp", "mas", "remas"];
|
||||
|
||||
const diffMap = new Map([
|
||||
["bas", "Basic"],
|
||||
["adv", "Advanced"],
|
||||
["exp", "Expert"],
|
||||
["mas", "Master"],
|
||||
["remas", "Re:Master"],
|
||||
]);
|
||||
|
||||
(async () => {
|
||||
const songs = ReadCollection("songs-maimaidx.json");
|
||||
const charts = ReadCollection("charts-maimaidx.json");
|
||||
|
||||
const existingSongs = new Map(songs.map((e) => [e.title, e.id]));
|
||||
const existingCharts = new Map(charts.map((e) => [`${e.songID} ${e.difficulty}`, e.chartID]));
|
||||
|
||||
const datum = await fetch(DATA_URL).then((r) => r.json());
|
||||
|
||||
// not properly implemented yet as i can't find romaji titles for
|
||||
// japanese titled songs
|
||||
const searchTerms = [];
|
||||
const altTitles = [];
|
||||
|
||||
let songID = Math.max(Math.max(...existingSongs.values()), 0) + 1;
|
||||
|
||||
for (const data of datum) {
|
||||
let thisSongID = songID;
|
||||
|
||||
const version = Number(data.version.substring(0, 3));
|
||||
|
||||
if (existingSongs.has(data.title)) {
|
||||
thisSongID = existingSongs.get(data.title);
|
||||
} else {
|
||||
songID++;
|
||||
songs.push({
|
||||
id: thisSongID,
|
||||
title: data.title.trim(),
|
||||
artist: data.artist.trim(),
|
||||
searchTerms,
|
||||
altTitles,
|
||||
data: {
|
||||
displayVersion: versionMap.get(version),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
diffNames.forEach((diff) => {
|
||||
// check for existence of difficulty
|
||||
if (!data[`dx_lev_${diff}`]) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (existingCharts.get(`${thisSongID} ${diffMap.get(diff)}`)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const chartID = CreateChartID();
|
||||
const isLatest = Number(version) === 225;
|
||||
|
||||
const lvNum = Number(data[`dx_lev_${diff}`].replace(/\+/u, ".7"));
|
||||
|
||||
charts.push({
|
||||
songID: thisSongID,
|
||||
chartID: chartID,
|
||||
rgcID: null,
|
||||
level: data[`dx_lev_${diff}`],
|
||||
levelNum: lvNum,
|
||||
isPrimary: true,
|
||||
difficulty: `DX ${diffMap.get(diff)}`,
|
||||
playtype: "Single",
|
||||
data: {
|
||||
isLatest,
|
||||
},
|
||||
tierlistInfo: {},
|
||||
versions: ["universeplus"],
|
||||
});
|
||||
});
|
||||
|
||||
diffNames.forEach((diff) => {
|
||||
// check for existence of difficulty
|
||||
if (!data[`lev_${diff}`]) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (existingCharts.get(`${thisSongID} ${diffMap.get(diff)}`)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const chartID = CreateChartID();
|
||||
const isLatest = Number(version) === 225;
|
||||
|
||||
const lvNum = Number(data[`lev_${diff}`].replace(/\+/u, ".7"));
|
||||
|
||||
charts.push({
|
||||
songID: thisSongID,
|
||||
chartID: chartID,
|
||||
rgcID: null,
|
||||
level: data[`lev_${diff}`],
|
||||
levelNum: lvNum,
|
||||
isPrimary: true,
|
||||
difficulty: diffMap.get(diff),
|
||||
playtype: "Single",
|
||||
data: {
|
||||
isLatest,
|
||||
},
|
||||
tierlistInfo: {},
|
||||
versions: ["universeplus"],
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
WriteCollection("charts-maimaidx.json", charts);
|
||||
WriteCollection("songs-maimaidx.json", songs);
|
||||
})();
|
||||
@@ -45,6 +45,7 @@
|
||||
"jubeat",
|
||||
"pms",
|
||||
"gitadora",
|
||||
"maimaidx",
|
||||
],
|
||||
IMPORT_TYPES: [
|
||||
"file/eamusement-iidx-csv",
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
WACCARate,
|
||||
ITGHighestUnbroken,
|
||||
CuratorSkill,
|
||||
MaimaiDXRate,
|
||||
} from "rg-stats";
|
||||
import { GetGamePTConfig } from "tachi-common";
|
||||
import { IsNullish } from "utils/misc";
|
||||
@@ -67,6 +68,7 @@ const CalculatedDataFunctions: CalculatedDataFunctionsType = {
|
||||
"pms:Keyboard": CalculateDataPMSorBMS,
|
||||
"pms:Controller": CalculateDataPMSorBMS,
|
||||
"itg:Stamina": CalculateDataITGStamina,
|
||||
"maimaidx:Single": CalculateDataMaimaiDX,
|
||||
};
|
||||
|
||||
// Creates Game-Specific calculatedData for the provided game & playtype.
|
||||
@@ -274,3 +276,12 @@ function CalculateDataITGStamina(
|
||||
highest256: h256,
|
||||
};
|
||||
}
|
||||
|
||||
function CalculateDataMaimaiDX(
|
||||
dryScore: DryScore<"maimaidx:Single">,
|
||||
chart: ChartDocument<"maimaidx:Single">
|
||||
) {
|
||||
const rate = MaimaiDXRate.calculate(dryScore.scoreData.percent, chart.levelNum);
|
||||
|
||||
return { rate };
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { InternalFailure, InvalidScoreFailure } from "./converter-failures";
|
||||
import CreateLogCtx from "lib/logger/logger";
|
||||
import { ESDCore, GetGamePTConfig } from "tachi-common";
|
||||
import { FloorToNDP, IsNullish, NotNullish } from "utils/misc";
|
||||
import { IsNullish, NotNullish } from "utils/misc";
|
||||
import type {
|
||||
ChartDocument,
|
||||
Game,
|
||||
@@ -62,6 +62,7 @@ export function GenericCalculatePercent(game: Game, score: number, chart?: Chart
|
||||
return (score / 10_000_000) * 100;
|
||||
case "popn":
|
||||
return (score / 100_000) * 100;
|
||||
|
||||
case "bms":
|
||||
case "pms":
|
||||
case "iidx": {
|
||||
@@ -96,12 +97,8 @@ export function GenericCalculatePercent(game: Game, score: number, chart?: Chart
|
||||
// this is a hack: we need a refactor for this.
|
||||
case "gitadora":
|
||||
case "itg":
|
||||
case "maimaidx":
|
||||
return score;
|
||||
|
||||
default: {
|
||||
logger.severe(`Invalid game passed of ${game} to GenericCalcPercent.`);
|
||||
throw new InternalFailure(`Invalid game passed of ${game} to GenericCalcPercent.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -112,6 +112,10 @@ function GetGPTSessionCalcDataFn(game: Game, playtype: Playtype): SessionCalcDat
|
||||
return (scd) => ({
|
||||
naiveRating: AvgBest10Map(scd, "rating"),
|
||||
});
|
||||
case "maimaidx:Single":
|
||||
return (scd) => ({
|
||||
rate: AvgBest10Map(scd, "rate"),
|
||||
});
|
||||
case "itg:Stamina":
|
||||
return (scd) => ({
|
||||
blockRating: AvgBestNMap(scd, "blockRating", 5),
|
||||
|
||||
@@ -2,6 +2,7 @@ import {
|
||||
CHUNITHM_COLOURS,
|
||||
GITADORA_COLOURS,
|
||||
JUBEAT_COLOURS,
|
||||
MAIMAIDX_COLOURS,
|
||||
POPN_CLASSES,
|
||||
SDVX_VF_CLASSES,
|
||||
WACCA_COLOURS,
|
||||
@@ -254,3 +255,44 @@ function JubilityToColour(jubility: number) {
|
||||
|
||||
return JUBEAT_COLOURS.BLACK;
|
||||
}
|
||||
|
||||
export function CalculateMaimaiDXColour(
|
||||
game: Game,
|
||||
playtype: Playtype,
|
||||
userID: integer,
|
||||
ratings: Record<string, number | null>
|
||||
) {
|
||||
if (IsNullish(ratings.rate)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const colour = MaimaiDXRateToColour(ratings.rate);
|
||||
|
||||
return { colour };
|
||||
}
|
||||
|
||||
function MaimaiDXRateToColour(rate: number) {
|
||||
if (rate >= 15000) {
|
||||
return MAIMAIDX_COLOURS.RAINBOW;
|
||||
} else if (rate >= 14500) {
|
||||
return MAIMAIDX_COLOURS.PLATINUM;
|
||||
} else if (rate >= 14000) {
|
||||
return MAIMAIDX_COLOURS.GOLD;
|
||||
} else if (rate >= 13000) {
|
||||
return MAIMAIDX_COLOURS.SILVER;
|
||||
} else if (rate >= 12000) {
|
||||
return MAIMAIDX_COLOURS.BRONZE;
|
||||
} else if (rate >= 10000) {
|
||||
return MAIMAIDX_COLOURS.PURPLE;
|
||||
} else if (rate >= 7000) {
|
||||
return MAIMAIDX_COLOURS.RED;
|
||||
} else if (rate >= 4000) {
|
||||
return MAIMAIDX_COLOURS.YELLOW;
|
||||
} else if (rate >= 2000) {
|
||||
return MAIMAIDX_COLOURS.GREEN;
|
||||
} else if (rate >= 1000) {
|
||||
return MAIMAIDX_COLOURS.BLUE;
|
||||
}
|
||||
|
||||
return MAIMAIDX_COLOURS.WHITE;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
CalculatePopnClass,
|
||||
CalculateSDVXClass,
|
||||
CalculateWACCAColour,
|
||||
CalculateMaimaiDXColour,
|
||||
} from "./builtin-class-handlers";
|
||||
import deepmerge from "deepmerge";
|
||||
import db from "external/mongo/db";
|
||||
@@ -51,6 +52,9 @@ const STATIC_CLASS_HANDLERS: ClassHandlerMap = {
|
||||
Gita: CalculateGitadoraColour,
|
||||
Dora: CalculateGitadoraColour,
|
||||
},
|
||||
maimaidx: {
|
||||
Single: CalculateMaimaiDXColour,
|
||||
},
|
||||
museca: null,
|
||||
popn: {
|
||||
"9B": CalculatePopnClass,
|
||||
|
||||
@@ -134,6 +134,11 @@ function GetGPTRatingFunction(game: Game, playtype: Playtype): RatingFunction {
|
||||
return async (g, p, u) => ({
|
||||
curatorSkill: await LazySumN("curatorSkill", 20)(g, p, u),
|
||||
});
|
||||
case "maimaidx:Single":
|
||||
return async (g, p, u, l) => ({
|
||||
naiveRate: await LazySumN("rate", 50)(g, p, u),
|
||||
rate: await CalculateMaimaiDXRate(g, p, u, l),
|
||||
});
|
||||
case "wacca:Single":
|
||||
return async (g, p, u, l) => ({
|
||||
naiveRate: await LazySumN("rate", 50)(g, p, u),
|
||||
@@ -228,6 +233,69 @@ async function CalculateWACCARate(
|
||||
);
|
||||
}
|
||||
|
||||
// basically the same as WACCA's.
|
||||
async function CalculateMaimaiDXRate(
|
||||
game: Game,
|
||||
playtype: Playtype,
|
||||
userID: integer,
|
||||
_logger: KtLogger
|
||||
) {
|
||||
const newChartIDs = (
|
||||
await db.charts.maimaidx.find({ "data.isLatest": true }, { projection: { chartID: 1 } })
|
||||
).map((e) => e.chartID);
|
||||
|
||||
const oldChartIDs = (
|
||||
await db.charts.maimaidx.find({ "data.isLatest": false }, { projection: { chartID: 1 } })
|
||||
).map((e) => e.chartID);
|
||||
|
||||
const best15New = await db["personal-bests"].find(
|
||||
{
|
||||
game,
|
||||
playtype,
|
||||
userID,
|
||||
chartID: { $in: newChartIDs },
|
||||
"calculatedData.rate": { $type: "number" },
|
||||
},
|
||||
{
|
||||
sort: {
|
||||
"calculatedData.rate": -1,
|
||||
},
|
||||
limit: 15,
|
||||
projection: {
|
||||
"calculatedData.rate": 1,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const best35Old = await db["personal-bests"].find(
|
||||
{
|
||||
game,
|
||||
playtype,
|
||||
userID,
|
||||
chartID: { $in: oldChartIDs },
|
||||
"calculatedData.rate": { $type: "number" },
|
||||
},
|
||||
{
|
||||
sort: {
|
||||
"calculatedData.rate": -1,
|
||||
},
|
||||
limit: 35,
|
||||
projection: {
|
||||
"calculatedData.rate": 1,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (best15New.length + best35Old.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
best15New.reduce((a, e) => a + e.calculatedData.rate!, 0) +
|
||||
best35Old.reduce((a, e) => a + e.calculatedData.rate!, 0)
|
||||
);
|
||||
}
|
||||
|
||||
async function CalculateGitadoraSkill(
|
||||
game: Game,
|
||||
playtype: Playtype,
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
"popn",
|
||||
"jubeat",
|
||||
"pms",
|
||||
"maimaidx",
|
||||
],
|
||||
IMPORT_TYPES: [
|
||||
"file/eamusement-iidx-csv",
|
||||
|
||||
Reference in New Issue
Block a user