feat: maimaidx support

This PR was squashed into one commit for easier merging.
This commit is contained in:
j1nxie
2022-12-26 16:07:01 +00:00
committed by zkldi
parent eeea973f91
commit 8d8d511ec2
30 changed files with 84656 additions and 15 deletions
@@ -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,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";
+1 -1
View File
@@ -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>(
+1
View File
@@ -6,6 +6,7 @@ const scales: Record<Game, number> = {
pms: 2,
chunithm: 5,
gitadora: 1,
maimaidx: 1,
museca: 5,
sdvx: 5,
usc: 5,
+1
View File
@@ -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,
},
+140
View File
@@ -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",
+2
View File
@@ -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",
+66
View File
@@ -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,
}
+43
View File
@@ -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";
+8
View File
@@ -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,
+2
View File
@@ -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,
+50
View File
@@ -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);
})();
+1
View File
@@ -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,
+1
View File
@@ -47,6 +47,7 @@
"popn",
"jubeat",
"pms",
"maimaidx",
],
IMPORT_TYPES: [
"file/eamusement-iidx-csv",