mirror of
https://github.com/zkldi/Tachi.git
synced 2026-09-22 23:18:05 +03:00
feat: maimai support (#925)
Co-authored-by: Olivia Simmons <38598881+justOkapi@users.noreply.github.com> Co-authored-by: tachi-seeds <107944937+tachi-seeds@users.noreply.github.com> Co-authored-by: beerpiss <lacvtg.a1.2023@gmail.com>
This commit is contained in:
co-authored by
Olivia Simmons
tachi-seeds
beerpiss
parent
2a9fa8e313
commit
14f8f79a15
Binary file not shown.
|
After Width: | Height: | Size: 118 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 269 KiB |
@@ -2,6 +2,9 @@
|
||||
|
||||
This is the main monorepo for Tachi.
|
||||
|
||||

|
||||

|
||||
|
||||
## What is Tachi?
|
||||
|
||||
Tachi is a modern, powerful, modular Rhythm Game Score Tracker.
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { ToAPIURL } from "util/api";
|
||||
import { RFA } from "util/misc";
|
||||
import { heySplashes } from "util/splashes";
|
||||
import Divider from "components/util/Divider";
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import { IsNullish } from "util/misc";
|
||||
import React from "react";
|
||||
import { COLOUR_SET, PBScoreDocument, ScoreDocument } from "tachi-common";
|
||||
|
||||
export default function MaimaiJudgementCell({
|
||||
score,
|
||||
}: {
|
||||
score: ScoreDocument<"maimai:Single"> | PBScoreDocument<"maimai:Single">;
|
||||
}) {
|
||||
const judgements = score.scoreData.judgements;
|
||||
|
||||
if (
|
||||
IsNullish(judgements.miss) ||
|
||||
IsNullish(judgements.great) ||
|
||||
IsNullish(judgements.good) ||
|
||||
IsNullish(judgements.perfect)
|
||||
) {
|
||||
return <td>No Data.</td>;
|
||||
}
|
||||
|
||||
return (
|
||||
<td>
|
||||
<strong>
|
||||
<span style={{ color: COLOUR_SET.vibrantYellow }}>{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>
|
||||
);
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import JubeatJudgementCell from "components/tables/cells/JubeatJudgementCell";
|
||||
import JubeatScoreCell from "components/tables/cells/JubeatScoreCell";
|
||||
import JubilityCell from "components/tables/cells/JubilityCell";
|
||||
import LampCell from "components/tables/cells/LampCell";
|
||||
import MaimaiJudgementCell from "components/tables/cells/MaimaiJudgementCell";
|
||||
import MaimaiDXJudgementCell from "components/tables/cells/MaimaiDXJudgementCell";
|
||||
import MillionsScoreCell from "components/tables/cells/MillionsScoreCell";
|
||||
import MusecaJudgementCell from "components/tables/cells/MusecaJudgementCell";
|
||||
@@ -160,6 +161,106 @@ export const GPT_CLIENT_IMPLEMENTATIONS: GPTClientImplementations = {
|
||||
),
|
||||
ratingCell: ({ sc }) => <JubilityCell score={sc} />,
|
||||
},
|
||||
"maimai:Single": {
|
||||
enumIcons: defaultEnumIcons,
|
||||
enumColours: {
|
||||
lamp: {
|
||||
FAILED: COLOUR_SET.red,
|
||||
CLEAR: COLOUR_SET.green,
|
||||
"FULL COMBO": COLOUR_SET.blue,
|
||||
"ALL PERFECT": COLOUR_SET.gold,
|
||||
"ALL PERFECT+": COLOUR_SET.teal,
|
||||
},
|
||||
grade: {
|
||||
F: COLOUR_SET.gray,
|
||||
E: COLOUR_SET.gray,
|
||||
D: COLOUR_SET.gray,
|
||||
C: COLOUR_SET.red,
|
||||
B: COLOUR_SET.maroon,
|
||||
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,
|
||||
},
|
||||
},
|
||||
classColours: {
|
||||
dan: {
|
||||
DAN_1: "warning",
|
||||
DAN_2: "warning",
|
||||
DAN_3: "warning",
|
||||
DAN_4: "warning",
|
||||
DAN_5: "warning",
|
||||
DAN_6: "warning",
|
||||
DAN_7: "warning",
|
||||
DAN_8: "warning",
|
||||
DAN_9: "warning",
|
||||
DAN_10: "warning",
|
||||
|
||||
KAIDEN: "warning",
|
||||
|
||||
SHINDAN_1: bg("purple"),
|
||||
SHINDAN_2: bg("purple"),
|
||||
SHINDAN_3: bg("purple"),
|
||||
SHINDAN_4: bg("purple"),
|
||||
SHINDAN_5: bg("purple"),
|
||||
SHINDAN_6: bg("purple"),
|
||||
SHINDAN_7: bg("purple"),
|
||||
SHINDAN_8: bg("purple"),
|
||||
SHINDAN_9: bg("purple"),
|
||||
SHINDAN_10: bg("purple"),
|
||||
|
||||
SHINKAIDEN: bg("purple"),
|
||||
},
|
||||
colour: {
|
||||
WHITE: bgc("white", "black"),
|
||||
BLUE: bgc("cyan", "black"),
|
||||
GREEN: bg("green"),
|
||||
YELLOW: bgc("yellow", "black"),
|
||||
RED: bg("red"),
|
||||
PURPLE: bg("purple"),
|
||||
BRONZE: bg("brown"),
|
||||
SILVER: bg("gray"),
|
||||
GOLD: "warning",
|
||||
|
||||
RAINBOW: {
|
||||
background:
|
||||
"linear-gradient(-45deg, #f0788a, #f48fb1, #9174c2, #79bcf2, #70a173, #f7ff99, #faca7d, #ff9d80, #f0788a)",
|
||||
color: "black",
|
||||
},
|
||||
},
|
||||
},
|
||||
difficultyColours: {
|
||||
Easy: COLOUR_SET.blue,
|
||||
Basic: COLOUR_SET.green,
|
||||
Advanced: COLOUR_SET.orange,
|
||||
Expert: COLOUR_SET.red,
|
||||
Master: COLOUR_SET.purple,
|
||||
"Re:Master": COLOUR_SET.white,
|
||||
},
|
||||
ratingSystems: [],
|
||||
scoreHeaders: [
|
||||
["Percent", "%", NumericSOV((x) => x?.scoreData.percent)],
|
||||
["Judgements", "Hits", NumericSOV((x) => x?.scoreData.percent)],
|
||||
["Lamp", "Lamp", NumericSOV((x) => x?.scoreData.enumIndexes.lamp)],
|
||||
],
|
||||
scoreCoreCells: ({ sc }) => (
|
||||
<>
|
||||
<ScoreCell
|
||||
colour={GetEnumColour(sc, "grade")}
|
||||
grade={sc.scoreData.grade}
|
||||
percent={sc.scoreData.percent}
|
||||
/>
|
||||
<MaimaiJudgementCell score={sc} />
|
||||
<LampCell lamp={sc.scoreData.lamp} colour={GetEnumColour(sc, "lamp")} />
|
||||
</>
|
||||
),
|
||||
ratingCell: ({ sc, rating }) => <RatingCell score={sc} rating={rating} />,
|
||||
},
|
||||
"maimaidx:Single": {
|
||||
enumIcons: defaultEnumIcons,
|
||||
enumColours: {
|
||||
|
||||
@@ -6,6 +6,7 @@ import { GITADORA_CONF, GITADORA_DORA_CONF, GITADORA_GITA_CONF } from "./game-su
|
||||
import { IIDX_CONF, IIDX_DP_CONF, IIDX_SP_CONF } from "./game-support/iidx";
|
||||
import { ITG_CONF, ITG_STAMINA_CONF } from "./game-support/itg";
|
||||
import { JUBEAT_CONF, JUBEAT_SINGLE_CONF } from "./game-support/jubeat";
|
||||
import { MAIMAI_CONF, MAIMAI_SINGLE_CONF } from "./game-support/maimai";
|
||||
import { MAIMAI_DX_CONF, MAIMAI_DX_SINGLE_CONF } from "./game-support/maimai-dx";
|
||||
import { MUSECA_CONF, MUSECA_SINGLE_CONF } from "./game-support/museca";
|
||||
import { PMS_CONF, PMS_CONTROLLER_CONF, PMS_KEYBOARD_CONF } from "./game-support/pms";
|
||||
@@ -37,6 +38,7 @@ export const GAME_CONFIGS = {
|
||||
bms: BMS_CONF,
|
||||
gitadora: GITADORA_CONF,
|
||||
jubeat: JUBEAT_CONF,
|
||||
maimai: MAIMAI_CONF,
|
||||
maimaidx: MAIMAI_DX_CONF,
|
||||
popn: POPN_CONF,
|
||||
sdvx: SDVX_CONF,
|
||||
@@ -83,6 +85,7 @@ export const GAME_PT_CONFIGS = {
|
||||
"wacca:Single": WACCA_SINGLE_CONF,
|
||||
"jubeat:Single": JUBEAT_SINGLE_CONF,
|
||||
"popn:9B": POPN_9B_CONF,
|
||||
"maimai:Single": MAIMAI_SINGLE_CONF,
|
||||
"maimaidx:Single": MAIMAI_DX_SINGLE_CONF,
|
||||
"pms:Controller": PMS_CONTROLLER_CONF,
|
||||
"pms:Keyboard": PMS_KEYBOARD_CONF,
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
import { FAST_SLOW_MAXCOMBO } from "./_common";
|
||||
import { FmtPercent } from "../../utils/util";
|
||||
import { ClassValue, ToDecimalPlaces } from "../config-utils";
|
||||
import { z } from "zod";
|
||||
import type { INTERNAL_GAME_CONFIG, INTERNAL_GAME_PT_CONFIG } from "../../types/internals";
|
||||
|
||||
export const MAIMAI_CONF = {
|
||||
name: "maimai",
|
||||
playtypes: ["Single"],
|
||||
songData: z.strictObject({
|
||||
titleJP: z.string(),
|
||||
artistJP: z.string(),
|
||||
displayVersion: z.string(),
|
||||
}),
|
||||
} as const satisfies INTERNAL_GAME_CONFIG;
|
||||
|
||||
const MaimaiDans = [
|
||||
ClassValue("DAN_1", "初段", "1st Dan"),
|
||||
ClassValue("DAN_2", "二段", "2nd Dan"),
|
||||
ClassValue("DAN_3", "三段", "3rd Dan"),
|
||||
ClassValue("DAN_4", "四段", "4th Dan"),
|
||||
ClassValue("DAN_5", "五段", "5th Dan"),
|
||||
ClassValue("DAN_6", "六段", "6th Dan"),
|
||||
ClassValue("DAN_7", "七段", "7th Dan"),
|
||||
ClassValue("DAN_8", "八段", "8th Dan"),
|
||||
ClassValue("DAN_9", "九段", "9th Dan"),
|
||||
ClassValue("DAN_10", "十段", "10th Dan"),
|
||||
ClassValue("KAIDEN", "皆伝", "Kaiden"),
|
||||
|
||||
ClassValue("SHINDAN_1", "真初段", "Shinshodan"),
|
||||
ClassValue("SHINDAN_2", "真二段", "2nd Shindan"),
|
||||
ClassValue("SHINDAN_3", "真三段", "3rd Shindan"),
|
||||
ClassValue("SHINDAN_4", "真四段", "4th Shindan"),
|
||||
ClassValue("SHINDAN_5", "真五段", "5th Shindan"),
|
||||
ClassValue("SHINDAN_6", "真六段", "6th Shindan"),
|
||||
ClassValue("SHINDAN_7", "真七段", "7th Shindan"),
|
||||
ClassValue("SHINDAN_8", "真八段", "8th Shindan"),
|
||||
ClassValue("SHINDAN_9", "真九段", "9th Shindan"),
|
||||
ClassValue("SHINDAN_10", "真十段", "10th Shindan"),
|
||||
ClassValue("SHINKAIDEN", "真皆伝", "Shinkaiden"),
|
||||
];
|
||||
|
||||
const MaimaiColours = [
|
||||
ClassValue("WHITE", "White"),
|
||||
ClassValue("BLUE", "Blue"),
|
||||
ClassValue("GREEN", "Green"),
|
||||
ClassValue("YELLOW", "Yellow"),
|
||||
ClassValue("RED", "Red"),
|
||||
ClassValue("PURPLE", "Purple"),
|
||||
ClassValue("BRONZE", "Bronze"),
|
||||
ClassValue("SILVER", "Silver"),
|
||||
ClassValue("GOLD", "Gold"),
|
||||
ClassValue("RAINBOW", "Rainbow"),
|
||||
];
|
||||
|
||||
export const MAIMAI_SINGLE_CONF = {
|
||||
providedMetrics: {
|
||||
percent: {
|
||||
type: "DECIMAL",
|
||||
chartDependentMax: true,
|
||||
formatter: FmtPercent,
|
||||
description:
|
||||
"The percent this score was worth. Sometimes called 'rate' in game. This is upper-bounded by how many BREAK notes the chart has.",
|
||||
},
|
||||
lamp: {
|
||||
type: "ENUM",
|
||||
values: ["FAILED", "CLEAR", "FULL COMBO", "ALL PERFECT", "ALL PERFECT+"],
|
||||
minimumRelevantValue: "CLEAR",
|
||||
description: "The type of clear this score was.",
|
||||
},
|
||||
},
|
||||
|
||||
derivedMetrics: {
|
||||
grade: {
|
||||
type: "ENUM",
|
||||
values: [
|
||||
"F",
|
||||
"E",
|
||||
"D",
|
||||
"C",
|
||||
"B",
|
||||
"A",
|
||||
"AA",
|
||||
"AAA",
|
||||
"S",
|
||||
"S+",
|
||||
"SS",
|
||||
"SS+",
|
||||
"SSS",
|
||||
"SSS+",
|
||||
],
|
||||
minimumRelevantValue: "A",
|
||||
description: "The grade this score was.",
|
||||
},
|
||||
},
|
||||
|
||||
defaultMetric: "percent",
|
||||
preferredDefaultEnum: "grade",
|
||||
|
||||
optionalMetrics: FAST_SLOW_MAXCOMBO,
|
||||
|
||||
scoreRatingAlgs: {
|
||||
rate: { description: "Rating as it's implemented in game.", formatter: ToDecimalPlaces(2) },
|
||||
},
|
||||
sessionRatingAlgs: {
|
||||
rate: {
|
||||
description: "The average of your best 10 ratings this session.",
|
||||
formatter: ToDecimalPlaces(2),
|
||||
},
|
||||
},
|
||||
profileRatingAlgs: {
|
||||
naiveRate: {
|
||||
description:
|
||||
"An average of your 30 best ratings. This is different from the rating in-game, as that is song-based and takes into account recent scores.",
|
||||
formatter: ToDecimalPlaces(2),
|
||||
},
|
||||
},
|
||||
|
||||
defaultScoreRatingAlg: "rate",
|
||||
defaultSessionRatingAlg: "rate",
|
||||
defaultProfileRatingAlg: "naiveRate",
|
||||
|
||||
// Similar to CHUNITHM, this game has a dynamic set of difficulties,
|
||||
// with a song able to have as many UTAGE difficulties as it likes.
|
||||
// This will also not be implemented for the same reasons as CHUNITHM.
|
||||
// Sorry!
|
||||
difficulties: {
|
||||
type: "FIXED",
|
||||
order: ["Easy", "Basic", "Advanced", "Expert", "Master", "Re:Master"],
|
||||
shorthand: {
|
||||
Easy: "ESY",
|
||||
Basic: "BAS",
|
||||
Advanced: "ADV",
|
||||
Expert: "EXP",
|
||||
Master: "MAS",
|
||||
"Re:Master": "Re:MAS",
|
||||
},
|
||||
default: "Master",
|
||||
},
|
||||
|
||||
classes: {
|
||||
colour: {
|
||||
type: "DERIVED",
|
||||
values: MaimaiColours,
|
||||
},
|
||||
dan: {
|
||||
type: "PROVIDED",
|
||||
values: MaimaiDans,
|
||||
},
|
||||
},
|
||||
|
||||
orderedJudgements: ["perfect", "great", "good", "miss"],
|
||||
|
||||
versions: {
|
||||
finale: "FiNALE",
|
||||
},
|
||||
|
||||
chartData: z.strictObject({
|
||||
inGameID: z.number(),
|
||||
inGameStrID: z.string(),
|
||||
maxPercent: z.number(),
|
||||
}),
|
||||
|
||||
preferences: z.strictObject({}),
|
||||
scoreMeta: z.strictObject({}),
|
||||
|
||||
supportedMatchTypes: ["songTitle", "tachiSongID", "inGameID"],
|
||||
} as const satisfies INTERNAL_GAME_PT_CONFIG;
|
||||
@@ -42,6 +42,14 @@ export enum DDR_LAMPS {
|
||||
MARVELOUS_FULL_COMBO = 6,
|
||||
}
|
||||
|
||||
export enum MAIMAI_LAMPS {
|
||||
FAILED = 0,
|
||||
CLEAR = 1,
|
||||
FULL_COMBO = 2,
|
||||
ALL_PERFECT = 3,
|
||||
ALL_PERFECT_PLUS = 4,
|
||||
}
|
||||
|
||||
export enum MAIMAIDX_LAMPS {
|
||||
FAILED = 0,
|
||||
CLEAR = 1,
|
||||
|
||||
@@ -132,12 +132,34 @@ export const MUSECA_GBOUNDARIES = MakeGradeBoundaries<GetEnumValue<"museca:Singl
|
||||
傑G: 1_000_000,
|
||||
});
|
||||
|
||||
/**
|
||||
* @note - The SSS+ rank boundary is chart-dependent - it is the maximum percent of a chart.
|
||||
* 104 is just the upper limit of the maximum percent.
|
||||
*/
|
||||
export const MAIMAI_GBOUNDARIES = MakeGradeBoundaries<GetEnumValue<"maimai:Single", "grade">>({
|
||||
F: 0,
|
||||
E: 10,
|
||||
D: 20,
|
||||
C: 40,
|
||||
B: 60,
|
||||
A: 80,
|
||||
AA: 90,
|
||||
AAA: 94,
|
||||
S: 97,
|
||||
"S+": 98,
|
||||
SS: 99,
|
||||
"SS+": 99.5,
|
||||
SSS: 100,
|
||||
// The actual SSS+ boundary is chart-dependent!
|
||||
"SSS+": 104,
|
||||
});
|
||||
|
||||
export const MAIMAIDX_GBOUNDARIES = MakeGradeBoundaries<GetEnumValue<"maimaidx:Single", "grade">>({
|
||||
D: 0,
|
||||
C: 10,
|
||||
B: 20,
|
||||
BB: 40,
|
||||
BBB: 60,
|
||||
C: 50,
|
||||
B: 60,
|
||||
BB: 70,
|
||||
BBB: 75,
|
||||
A: 80,
|
||||
AA: 90,
|
||||
AAA: 94,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -28110,6 +28110,305 @@
|
||||
"title": "Level 1 (FESTiVAL PLUS)",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"level": "1",
|
||||
"versions": "finale"
|
||||
},
|
||||
"folderID": "F1a0c0e32b477738519b1a69e167b4c6f87fa5ffd704b2a47759b414a7da4b8c4",
|
||||
"game": "maimai",
|
||||
"inactive": false,
|
||||
"playtype": "Single",
|
||||
"searchTerms": [],
|
||||
"title": "Level 1 (FiNALE)",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"level": "10",
|
||||
"versions": "finale"
|
||||
},
|
||||
"folderID": "F15a62cbb7b4af62198f1048d315d4f784d0f9943c967ebee2e8207b376513668",
|
||||
"game": "maimai",
|
||||
"inactive": false,
|
||||
"playtype": "Single",
|
||||
"searchTerms": [],
|
||||
"title": "Level 10 (FiNALE)",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"level": "10+",
|
||||
"versions": "finale"
|
||||
},
|
||||
"folderID": "Fb26affb9911425f64b06ee78ae717f52ca2b19d06ffd26a7c1cbe90de8cea7e0",
|
||||
"game": "maimai",
|
||||
"inactive": false,
|
||||
"playtype": "Single",
|
||||
"searchTerms": [],
|
||||
"title": "Level 10+ (FiNALE)",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"level": "11",
|
||||
"versions": "finale"
|
||||
},
|
||||
"folderID": "F5b7589434609a8a5117dbc71c421e9c6b8588926cf0ca01eec1cbb8f2792aa49",
|
||||
"game": "maimai",
|
||||
"inactive": false,
|
||||
"playtype": "Single",
|
||||
"searchTerms": [],
|
||||
"title": "Level 11 (FiNALE)",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"level": "11+",
|
||||
"versions": "finale"
|
||||
},
|
||||
"folderID": "F77b0b508a335d4ad32c351690364f6ec75615499660c033ce33103b489802cf8",
|
||||
"game": "maimai",
|
||||
"inactive": false,
|
||||
"playtype": "Single",
|
||||
"searchTerms": [],
|
||||
"title": "Level 11+ (FiNALE)",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"level": "12",
|
||||
"versions": "finale"
|
||||
},
|
||||
"folderID": "Fae9a5d9ce589b0286a41cebf212aafba4d209e22ff8d6874717c10b743f45a36",
|
||||
"game": "maimai",
|
||||
"inactive": false,
|
||||
"playtype": "Single",
|
||||
"searchTerms": [],
|
||||
"title": "Level 12 (FiNALE)",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"level": "12+",
|
||||
"versions": "finale"
|
||||
},
|
||||
"folderID": "F6467544964f67cadfbb688470493baece6a97cdda1d0108949a807436ffe9f72",
|
||||
"game": "maimai",
|
||||
"inactive": false,
|
||||
"playtype": "Single",
|
||||
"searchTerms": [],
|
||||
"title": "Level 12+ (FiNALE)",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"level": "13",
|
||||
"versions": "finale"
|
||||
},
|
||||
"folderID": "F5fd3fe0b33f6d9473c16f970a4a4a7b775c1ef833ad346d230cc76ec105cb6bd",
|
||||
"game": "maimai",
|
||||
"inactive": false,
|
||||
"playtype": "Single",
|
||||
"searchTerms": [],
|
||||
"title": "Level 13 (FiNALE)",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"level": "13+",
|
||||
"versions": "finale"
|
||||
},
|
||||
"folderID": "F6b5f9be654a31e6eb13f4f5d4fbb252392d02b0dbdd163e736f37dcd3a37ba77",
|
||||
"game": "maimai",
|
||||
"inactive": false,
|
||||
"playtype": "Single",
|
||||
"searchTerms": [],
|
||||
"title": "Level 13+ (FiNALE)",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"level": "14",
|
||||
"versions": "finale"
|
||||
},
|
||||
"folderID": "F506c71630d7208d67522261012fdc3ab253ddaa3b5c9ba4e31a5c6fe88a7ffb9",
|
||||
"game": "maimai",
|
||||
"inactive": false,
|
||||
"playtype": "Single",
|
||||
"searchTerms": [],
|
||||
"title": "Level 14 (FiNALE)",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"level": "2",
|
||||
"versions": "finale"
|
||||
},
|
||||
"folderID": "Fae54b2e40ea8e8a3cab7f007a516f6c4dbbb0eda8a6d211d02e1b8cce5cf1ff9",
|
||||
"game": "maimai",
|
||||
"inactive": false,
|
||||
"playtype": "Single",
|
||||
"searchTerms": [],
|
||||
"title": "Level 2 (FiNALE)",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"level": "3",
|
||||
"versions": "finale"
|
||||
},
|
||||
"folderID": "Fc0ffd85c2959bd4f39703ca5df640bc55ef375de5c5633c2724ee9b3b22cd86d",
|
||||
"game": "maimai",
|
||||
"inactive": false,
|
||||
"playtype": "Single",
|
||||
"searchTerms": [],
|
||||
"title": "Level 3 (FiNALE)",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"level": "4",
|
||||
"versions": "finale"
|
||||
},
|
||||
"folderID": "F4e7ac5d666f05d39cbb4d1a6ea8061241a904685f2e888b3e97388de6bc70e9a",
|
||||
"game": "maimai",
|
||||
"inactive": false,
|
||||
"playtype": "Single",
|
||||
"searchTerms": [],
|
||||
"title": "Level 4 (FiNALE)",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"level": "5",
|
||||
"versions": "finale"
|
||||
},
|
||||
"folderID": "F6f79ae4e23953dbff6bfd3709360805b2652b863dedf29c7a7880157e2bc36d1",
|
||||
"game": "maimai",
|
||||
"inactive": false,
|
||||
"playtype": "Single",
|
||||
"searchTerms": [],
|
||||
"title": "Level 5 (FiNALE)",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"level": "5+",
|
||||
"versions": "finale"
|
||||
},
|
||||
"folderID": "Fe789cf5db5b4153582c557a99c1b3af74dbf8aaa90afc5cb2d9faa05ec60cf03",
|
||||
"game": "maimai",
|
||||
"inactive": false,
|
||||
"playtype": "Single",
|
||||
"searchTerms": [],
|
||||
"title": "Level 5+ (FiNALE)",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"level": "6",
|
||||
"versions": "finale"
|
||||
},
|
||||
"folderID": "F1c076b827e34f5f3c03c8ee2626773c1bed614687e605acf866d3f2acd2a8bb2",
|
||||
"game": "maimai",
|
||||
"inactive": false,
|
||||
"playtype": "Single",
|
||||
"searchTerms": [],
|
||||
"title": "Level 6 (FiNALE)",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"level": "6+",
|
||||
"versions": "finale"
|
||||
},
|
||||
"folderID": "F017c78011efb1784e53b2c659fcaf089daede5ec2e39a26d55a3ddc53c4c5b58",
|
||||
"game": "maimai",
|
||||
"inactive": false,
|
||||
"playtype": "Single",
|
||||
"searchTerms": [],
|
||||
"title": "Level 6+ (FiNALE)",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"level": "7",
|
||||
"versions": "finale"
|
||||
},
|
||||
"folderID": "Fee6ff8ec22ed89de6c619a5e0584a19f2e80eefc8ceddb15ab99a99ed6b17dab",
|
||||
"game": "maimai",
|
||||
"inactive": false,
|
||||
"playtype": "Single",
|
||||
"searchTerms": [],
|
||||
"title": "Level 7 (FiNALE)",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"level": "7+",
|
||||
"versions": "finale"
|
||||
},
|
||||
"folderID": "Fbec2ce2959e8a23ae39cbba3396136617c289836e50b9a9f635ded49fe546736",
|
||||
"game": "maimai",
|
||||
"inactive": false,
|
||||
"playtype": "Single",
|
||||
"searchTerms": [],
|
||||
"title": "Level 7+ (FiNALE)",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"level": "8",
|
||||
"versions": "finale"
|
||||
},
|
||||
"folderID": "Fb2baf0a296fac30b8b2270f5ca3e7c759c403e2d8c110ec424e05727c6c33d18",
|
||||
"game": "maimai",
|
||||
"inactive": false,
|
||||
"playtype": "Single",
|
||||
"searchTerms": [],
|
||||
"title": "Level 8 (FiNALE)",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"level": "8+",
|
||||
"versions": "finale"
|
||||
},
|
||||
"folderID": "F85df71c7b200899ec6759b2dfee5446c7da0a0af49524b3b261c4aac34f18e09",
|
||||
"game": "maimai",
|
||||
"inactive": false,
|
||||
"playtype": "Single",
|
||||
"searchTerms": [],
|
||||
"title": "Level 8+ (FiNALE)",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"level": "9",
|
||||
"versions": "finale"
|
||||
},
|
||||
"folderID": "F5534b37f2e158f7ad946f7d38b61c085b3fc6b2ddaf2cc57ae0a2693e12b803e",
|
||||
"game": "maimai",
|
||||
"inactive": false,
|
||||
"playtype": "Single",
|
||||
"searchTerms": [],
|
||||
"title": "Level 9 (FiNALE)",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"level": "9+",
|
||||
"versions": "finale"
|
||||
},
|
||||
"folderID": "F8fd9946d8a24ba19f98736bded20ad6c173063a5b4a1322c004a8775851cd5cb",
|
||||
"game": "maimai",
|
||||
"inactive": false,
|
||||
"playtype": "Single",
|
||||
"searchTerms": [],
|
||||
"title": "Level 9+ (FiNALE)",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"level": "1",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3155,6 +3155,40 @@
|
||||
"tableID": "maimaidx-Single-festivalplus-levels",
|
||||
"title": "maimai DX (FESTiVAL PLUS)"
|
||||
},
|
||||
{
|
||||
"default": true,
|
||||
"description": "Levels for maimai FiNALE.",
|
||||
"folders": [
|
||||
"F1a0c0e32b477738519b1a69e167b4c6f87fa5ffd704b2a47759b414a7da4b8c4",
|
||||
"Fae54b2e40ea8e8a3cab7f007a516f6c4dbbb0eda8a6d211d02e1b8cce5cf1ff9",
|
||||
"Fc0ffd85c2959bd4f39703ca5df640bc55ef375de5c5633c2724ee9b3b22cd86d",
|
||||
"F4e7ac5d666f05d39cbb4d1a6ea8061241a904685f2e888b3e97388de6bc70e9a",
|
||||
"F6f79ae4e23953dbff6bfd3709360805b2652b863dedf29c7a7880157e2bc36d1",
|
||||
"Fe789cf5db5b4153582c557a99c1b3af74dbf8aaa90afc5cb2d9faa05ec60cf03",
|
||||
"F1c076b827e34f5f3c03c8ee2626773c1bed614687e605acf866d3f2acd2a8bb2",
|
||||
"F017c78011efb1784e53b2c659fcaf089daede5ec2e39a26d55a3ddc53c4c5b58",
|
||||
"Fee6ff8ec22ed89de6c619a5e0584a19f2e80eefc8ceddb15ab99a99ed6b17dab",
|
||||
"Fbec2ce2959e8a23ae39cbba3396136617c289836e50b9a9f635ded49fe546736",
|
||||
"Fb2baf0a296fac30b8b2270f5ca3e7c759c403e2d8c110ec424e05727c6c33d18",
|
||||
"F85df71c7b200899ec6759b2dfee5446c7da0a0af49524b3b261c4aac34f18e09",
|
||||
"F5534b37f2e158f7ad946f7d38b61c085b3fc6b2ddaf2cc57ae0a2693e12b803e",
|
||||
"F8fd9946d8a24ba19f98736bded20ad6c173063a5b4a1322c004a8775851cd5cb",
|
||||
"F15a62cbb7b4af62198f1048d315d4f784d0f9943c967ebee2e8207b376513668",
|
||||
"Fb26affb9911425f64b06ee78ae717f52ca2b19d06ffd26a7c1cbe90de8cea7e0",
|
||||
"F5b7589434609a8a5117dbc71c421e9c6b8588926cf0ca01eec1cbb8f2792aa49",
|
||||
"F77b0b508a335d4ad32c351690364f6ec75615499660c033ce33103b489802cf8",
|
||||
"Fae9a5d9ce589b0286a41cebf212aafba4d209e22ff8d6874717c10b743f45a36",
|
||||
"F6467544964f67cadfbb688470493baece6a97cdda1d0108949a807436ffe9f72",
|
||||
"F5fd3fe0b33f6d9473c16f970a4a4a7b775c1ef833ad346d230cc76ec105cb6bd",
|
||||
"F6b5f9be654a31e6eb13f4f5d4fbb252392d02b0dbdd163e736f37dcd3a37ba77",
|
||||
"F506c71630d7208d67522261012fdc3ab253ddaa3b5c9ba4e31a5c6fe88a7ffb9"
|
||||
],
|
||||
"game": "maimai",
|
||||
"inactive": false,
|
||||
"playtype": "Single",
|
||||
"tableID": "maimai-Single-finale-levels",
|
||||
"title": "maimai (FiNALE)"
|
||||
},
|
||||
{
|
||||
"default": false,
|
||||
"description": "Levels for maimai DX in FESTiVAL.",
|
||||
@@ -4053,4 +4087,4 @@
|
||||
"tableID": "wacca-Single-reverse-levels",
|
||||
"title": "WACCA Reverse Levels"
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
const logger = require("../logger");
|
||||
const {
|
||||
MutateCollection,
|
||||
CreateFolderID,
|
||||
ReadCollection,
|
||||
WriteCollection,
|
||||
} = require("../util");
|
||||
const { MutateCollection, CreateFolderID, ReadCollection, WriteCollection } = require("../util");
|
||||
|
||||
const translateMap = new Map();
|
||||
|
||||
@@ -14,11 +9,7 @@ MutateCollection("folders.json", (folders) => {
|
||||
logger.info("Updating Folders.");
|
||||
|
||||
for (const folder of folders) {
|
||||
const newFolderID = CreateFolderID(
|
||||
folder.data,
|
||||
folder.game,
|
||||
folder.playtype
|
||||
);
|
||||
const newFolderID = CreateFolderID(folder.data, folder.game, folder.playtype);
|
||||
|
||||
translateMap.set(folder.folderID, newFolderID);
|
||||
|
||||
@@ -37,9 +28,7 @@ try {
|
||||
const newFolderID = translateMap.get(e);
|
||||
|
||||
if (!newFolderID) {
|
||||
throw new Error(
|
||||
`${e} doesn't exist as a folderID? Can't autofix.`
|
||||
);
|
||||
throw new Error(`${e} doesn't exist as a folderID? Can't autofix.`);
|
||||
}
|
||||
|
||||
return newFolderID;
|
||||
@@ -51,9 +40,6 @@ try {
|
||||
return tables;
|
||||
});
|
||||
} catch (err) {
|
||||
logger.error(
|
||||
"Failed to update tables.json, reverting all auto-folder fixes.",
|
||||
{ err }
|
||||
);
|
||||
logger.error("Failed to update tables.json, reverting all auto-folder fixes.", { err });
|
||||
WriteCollection("folders.json", origFolders);
|
||||
}
|
||||
|
||||
@@ -16,10 +16,10 @@ MutateCollection("charts-iidx.json", (charts) => {
|
||||
chart.data["2dxtraSet"] === null &&
|
||||
chart.difficulty !== "LEGGENDARIA" &&
|
||||
chart.difficulty !== "BEGINNER"
|
||||
) {
|
||||
chart.versions.push("inf");
|
||||
}
|
||||
) {
|
||||
chart.versions.push("inf");
|
||||
}
|
||||
}
|
||||
|
||||
return charts;
|
||||
});
|
||||
return charts;
|
||||
});
|
||||
|
||||
@@ -17,38 +17,41 @@ const TIERS = {
|
||||
text: "16F",
|
||||
value: 16.0,
|
||||
},
|
||||
...(Object.fromEntries(
|
||||
...Object.fromEntries(
|
||||
[
|
||||
"16弱(16EとF) 地力 記号・A〜Z",
|
||||
"16弱(16EとF) 地力 あ〜わ",
|
||||
"16弱(16EとF)鍵盤・つまみ",
|
||||
].map(key => [
|
||||
key, {
|
||||
].map((key) => [
|
||||
key,
|
||||
{
|
||||
text: "16E",
|
||||
value: 16.1,
|
||||
},
|
||||
])
|
||||
)),
|
||||
...(Object.fromEntries(
|
||||
),
|
||||
...Object.fromEntries(
|
||||
[
|
||||
"16中(16CとD)地力 記号・A〜Z",
|
||||
"16中(16CとD)地力 あ〜わ",
|
||||
"16中(16CとD)鍵盤・つまみ",
|
||||
].map(key => [
|
||||
key, {
|
||||
].map((key) => [
|
||||
key,
|
||||
{
|
||||
text: "16C/D",
|
||||
value: 16.3,
|
||||
},
|
||||
])
|
||||
)),
|
||||
...(Object.fromEntries(
|
||||
["16強(16AとB) 地力", "16強(16AとB) 鍵盤・つまみ"].map(key => [
|
||||
key, {
|
||||
),
|
||||
...Object.fromEntries(
|
||||
["16強(16AとB) 地力", "16強(16AとB) 鍵盤・つまみ"].map((key) => [
|
||||
key,
|
||||
{
|
||||
text: "16A/B",
|
||||
value: 16.5,
|
||||
},
|
||||
])
|
||||
)),
|
||||
),
|
||||
"16強+(16A以上S未満)": {
|
||||
text: "16A+",
|
||||
value: 16.7,
|
||||
@@ -238,7 +241,7 @@ const MANUAL_TITLE_MAP = {
|
||||
"VALKYRIE ASAULT": "VALKYRIE ASSAULT",
|
||||
SuperMiracleEmsemble: "SuperMiracleEnsemble",
|
||||
"仔羊のナヴァラン・クリシェを添えて": "~仔羊のナヴァラン・クリシェを添えて~",
|
||||
"あいあむなんばーわんぱとらちゃん様": "あいあむなんばーわんパトラちゃん様", // hirigana/katakana
|
||||
あいあむなんばーわんぱとらちゃん様: "あいあむなんばーわんパトラちゃん様", // hirigana/katakana
|
||||
|
||||
// 17s
|
||||
"Emperors divide": "Emperor's Divide",
|
||||
@@ -441,7 +444,9 @@ function addTiers(levelNum, csvData, headerRow, leftOffset, simple) {
|
||||
const [_, title, difficulty] = chartString.match(/^(.*?)(?:\[([A-Z]{3})\])?$/);
|
||||
if (
|
||||
difficulty &&
|
||||
!["NOV", "ADV", "EXH", "MXM", "INF", "GRV", "HVN", "VVD", "XCD"].includes(difficulty)
|
||||
!["NOV", "ADV", "EXH", "MXM", "INF", "GRV", "HVN", "VVD", "XCD"].includes(
|
||||
difficulty
|
||||
)
|
||||
) {
|
||||
console.log(`Unknown difficulty ${difficulty} for ${title}.`);
|
||||
}
|
||||
|
||||
@@ -201,7 +201,7 @@ for (const tableFile of options.charts) {
|
||||
// So it doesn't give VF. (Consider changing this.)
|
||||
levelNum: 0,
|
||||
playtype,
|
||||
|
||||
|
||||
songID,
|
||||
tierlistInfo: {},
|
||||
versions: {},
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
# maimai DX Support
|
||||
|
||||
This game has the internal GPTString of `maimai:Single`.
|
||||
|
||||
!!! note
|
||||
For information on what each section means, please see [Common Config](../common-config/index.md).
|
||||
|
||||
## Metrics
|
||||
|
||||
For more information on what metrics are and how they work, see [TODO]!
|
||||
|
||||
### Provided Metrics
|
||||
|
||||
| Metric Name | Type | Description |
|
||||
| :: | :: | :: |
|
||||
| `percent` | Decimal | The percent this score was worth. Sometimes called 'rate' in game. This is between 0 and 104 (upper bound, each chart has its own maximum percent) |
|
||||
| `lamp` | "FAILED", "CLEAR", "FULL COMBO", "ALL PERFECT", "ALL PERFECT+" | The type of clear this score was. |
|
||||
|
||||
### Derived Metrics
|
||||
|
||||
| Metric Name | Type | Description |
|
||||
| :: | :: | :: |
|
||||
| `grade` | "D", "C", "B", "A", "AA", "AAA", "S", "S+", "SS", "SS+", "SSS", "SSS+" | The grade this score was. |
|
||||
|
||||
### Optional Metrics
|
||||
|
||||
| Metric Name | Type | Description |
|
||||
| :: | :: | :: |
|
||||
| `fast` | Integer | The amount of mistakes in this score that were a result of hitting early. |
|
||||
| `slow` | Integer | The amount of mistakes in this score that were a result of hitting late. |
|
||||
| `maxCombo` | Integer | The largest combo in this score. |
|
||||
|
||||
## Judgements
|
||||
|
||||
The folowing judgements are defined:
|
||||
|
||||
- `perfect`
|
||||
- `great`
|
||||
- `good`
|
||||
- `miss`
|
||||
|
||||
## Rating Algorithms
|
||||
|
||||
### Score Rating Algorithms
|
||||
|
||||
| Name | Description |
|
||||
| :: | :: |
|
||||
| `rate` | Rating as it's implemented in game. |
|
||||
|
||||
### Session Rating Algorithms
|
||||
|
||||
| Name | Description |
|
||||
| :: | :: |
|
||||
| `rate` | The average of your best 10 ratings this session. |
|
||||
|
||||
### Profile Rating Algorithms
|
||||
|
||||
The default rating algorithm is `naiveRate`.
|
||||
|
||||
| Name | Description |
|
||||
| :: | :: |
|
||||
| `naiveRate` | An average of your 30 best ratings. This is different from the rating in-game, as that is song-based and takes into account recent scores. |
|
||||
|
||||
## Difficulties
|
||||
|
||||
- `Basic`
|
||||
- `Advanced`
|
||||
- `Expert`
|
||||
- `Master`
|
||||
- `Re:Master`
|
||||
|
||||
## Classes
|
||||
|
||||
| Name | Type | Values |
|
||||
| :: | :: | :: |
|
||||
| `colour` | DERIVED | WHITE, BLUE, GREEN, YELLOW, RED, PURPLE, BRONZE, SILVER, GOLD, RAINBOW
|
||||
| `dan` | PROVIDED | DAN_1, DAN_2, DAN_3, DAN_4, DAN_5, DAN_6, DAN_7, DAN_8, DAN_9, DAN_10, KAIDEN, SHINDAN_1, SHINDAN_2, SHINDAN_3, SHINDAN_4, SHINDAN_5, SHINDAN_6, SHINDAN_7, SHINDAN_8, SHINDAN_9, SHINDAN_10, SHINKAIDEN
|
||||
|
||||
## Versions
|
||||
|
||||
| ID | Pretty Name |
|
||||
| :: | :: |
|
||||
| `finale` | FiNALE |
|
||||
|
||||
## Supported Match Types
|
||||
|
||||
- `songTitle`
|
||||
- `tachiSongID`
|
||||
Generated
+25
-24
@@ -2353,48 +2353,48 @@ packages:
|
||||
'@jridgewell/resolve-uri': 3.1.0
|
||||
'@jridgewell/sourcemap-codec': 1.4.14
|
||||
|
||||
/@msgpackr-extract/msgpackr-extract-darwin-arm64/2.0.2:
|
||||
resolution: {integrity: sha512-FMX5i7a+ojIguHpWbzh5MCsCouJkwf4z4ejdUY/fsgB9Vkdak4ZnoIEskOyOUMMB4lctiZFGszFQJXUeFL8tRg==}
|
||||
/@msgpackr-extract/msgpackr-extract-darwin-arm64/2.2.0:
|
||||
resolution: {integrity: sha512-Z9LFPzfoJi4mflGWV+rv7o7ZbMU5oAU9VmzCgL240KnqDW65Y2HFCT3MW06/ITJSnbVLacmcEJA8phywK7JinQ==}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@msgpackr-extract/msgpackr-extract-darwin-x64/2.0.2:
|
||||
resolution: {integrity: sha512-DznYtF3lHuZDSRaIOYeif4JgO0NtO2Xf8DsngAugMx/bUdTFbg86jDTmkVJBNmV+cxszz6OjGvinnS8AbJ342g==}
|
||||
/@msgpackr-extract/msgpackr-extract-darwin-x64/2.2.0:
|
||||
resolution: {integrity: sha512-vq0tT8sjZsy4JdSqmadWVw6f66UXqUCabLmUVHZwUFzMgtgoIIQjT4VVRHKvlof3P/dMCkbMJ5hB1oJ9OWHaaw==}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@msgpackr-extract/msgpackr-extract-linux-arm/2.0.2:
|
||||
resolution: {integrity: sha512-Gy9+c3Wj+rUlD3YvCZTi92gs+cRX7ZQogtwq0IhRenloTTlsbpezNgk6OCkt59V4ATEWSic9rbU92H/l7XsRvA==}
|
||||
/@msgpackr-extract/msgpackr-extract-linux-arm/2.2.0:
|
||||
resolution: {integrity: sha512-SaJ3Qq4lX9Syd2xEo9u3qPxi/OB+5JO/ngJKK97XDpa1C587H9EWYO6KD8995DAjSinWvdHKRrCOXVUC5fvGOg==}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@msgpackr-extract/msgpackr-extract-linux-arm64/2.0.2:
|
||||
resolution: {integrity: sha512-b0jMEo566YdM2K+BurSed7bswjo3a6bcdw5ETqoIfSuxKuRLPfAiOjVbZyZBgx3J/TAM/QrvEQ/VN89A0ZAxSg==}
|
||||
/@msgpackr-extract/msgpackr-extract-linux-arm64/2.2.0:
|
||||
resolution: {integrity: sha512-hlxxLdRmPyq16QCutUtP8Tm6RDWcyaLsRssaHROatgnkOxdleMTgetf9JsdncL8vLh7FVy/RN9i3XR5dnb9cRA==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@msgpackr-extract/msgpackr-extract-linux-x64/2.0.2:
|
||||
resolution: {integrity: sha512-zrBHaePwcv4cQXxzYgNj0+A8I1uVN97E7/3LmkRocYZ+rMwUsnPpp4RuTAHSRoKlTQV3nSdCQW4Qdt4MXw/iHw==}
|
||||
/@msgpackr-extract/msgpackr-extract-linux-x64/2.2.0:
|
||||
resolution: {integrity: sha512-94y5PJrSOqUNcFKmOl7z319FelCLAE0rz/jPCWS+UtdMZvpa4jrQd+cJPQCLp2Fes1yAW/YUQj/Di6YVT3c3Iw==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@msgpackr-extract/msgpackr-extract-win32-x64/2.0.2:
|
||||
resolution: {integrity: sha512-fpnI00dt+yO1cKx9qBXelKhPBdEgvc8ZPav1+0r09j0woYQU2N79w/jcGawSY5UGlgQ3vjaJsFHnGbGvvqdLzg==}
|
||||
/@msgpackr-extract/msgpackr-extract-win32-x64/2.2.0:
|
||||
resolution: {integrity: sha512-XrC0JzsqQSvOyM3t04FMLO6z5gCuhPE6k4FXuLK5xf52ZbdvcFe1yBmo7meCew9B8G2f0T9iu9t3kfTYRYROgA==}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
requiresBuild: true
|
||||
@@ -8019,25 +8019,26 @@ packages:
|
||||
/ms/2.1.3:
|
||||
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
||||
|
||||
/msgpackr-extract/2.0.2:
|
||||
resolution: {integrity: sha512-coskCeJG2KDny23zWeu+6tNy7BLnAiOGgiwzlgdm4oeSsTpqEJJPguHIuKZcCdB7tzhZbXNYSg6jZAXkZErkJA==}
|
||||
/msgpackr-extract/2.2.0:
|
||||
resolution: {integrity: sha512-0YcvWSv7ZOGl9Od6Y5iJ3XnPww8O7WLcpYMDwX+PAA/uXLDtyw94PJv9GLQV/nnp3cWlDhMoyKZIQLrx33sWog==}
|
||||
hasBin: true
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
node-gyp-build-optional-packages: 5.0.2
|
||||
node-gyp-build-optional-packages: 5.0.3
|
||||
optionalDependencies:
|
||||
'@msgpackr-extract/msgpackr-extract-darwin-arm64': 2.0.2
|
||||
'@msgpackr-extract/msgpackr-extract-darwin-x64': 2.0.2
|
||||
'@msgpackr-extract/msgpackr-extract-linux-arm': 2.0.2
|
||||
'@msgpackr-extract/msgpackr-extract-linux-arm64': 2.0.2
|
||||
'@msgpackr-extract/msgpackr-extract-linux-x64': 2.0.2
|
||||
'@msgpackr-extract/msgpackr-extract-win32-x64': 2.0.2
|
||||
'@msgpackr-extract/msgpackr-extract-darwin-arm64': 2.2.0
|
||||
'@msgpackr-extract/msgpackr-extract-darwin-x64': 2.2.0
|
||||
'@msgpackr-extract/msgpackr-extract-linux-arm': 2.2.0
|
||||
'@msgpackr-extract/msgpackr-extract-linux-arm64': 2.2.0
|
||||
'@msgpackr-extract/msgpackr-extract-linux-x64': 2.2.0
|
||||
'@msgpackr-extract/msgpackr-extract-win32-x64': 2.2.0
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/msgpackr/1.6.1:
|
||||
resolution: {integrity: sha512-Je+xBEfdjtvA4bKaOv8iRhjC8qX2oJwpYH4f7JrG4uMVJVmnmkAT4pjKdbztKprGj3iwjcxPzb5umVZ02Qq3tA==}
|
||||
optionalDependencies:
|
||||
msgpackr-extract: 2.0.2
|
||||
msgpackr-extract: 2.2.0
|
||||
dev: false
|
||||
|
||||
/multer/1.4.4:
|
||||
@@ -8159,8 +8160,8 @@ packages:
|
||||
whatwg-url: 5.0.0
|
||||
dev: false
|
||||
|
||||
/node-gyp-build-optional-packages/5.0.2:
|
||||
resolution: {integrity: sha512-PiN4NWmlQPqvbEFcH/omQsswWQbe5Z9YK/zdB23irp5j2XibaA2IrGvpSWmVVG4qMZdmPdwPctSy4a86rOMn6g==}
|
||||
/node-gyp-build-optional-packages/5.0.3:
|
||||
resolution: {integrity: sha512-k75jcVzk5wnnc/FMxsf4udAoTEUv2jY3ycfdSd3yWu6Cnd1oee6/CfZJApyscA4FJOmdoixWwiwOyf16RzD5JA==}
|
||||
hasBin: true
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ test-env:
|
||||
- TCHIS_CONF_LOCATION=test.conf.json5
|
||||
# On a local machine, these tests run in under 2-3 minutes.
|
||||
# On github actions, these tests can take an inordinate amount of time.
|
||||
# The timeout is set to 16 minutes, which is ridiculous.
|
||||
# The timeout is set to 10 minutes, which is ridiculous.
|
||||
# Doesn't matter for real machines.
|
||||
timeout: 600
|
||||
ts: true
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
"jubeat",
|
||||
"pms",
|
||||
"gitadora",
|
||||
"maimai",
|
||||
"maimaidx",
|
||||
"itg",
|
||||
],
|
||||
|
||||
@@ -4,6 +4,7 @@ import { GITADORA_DORA_IMPL, GITADORA_GITA_IMPL } from "./games/gitadora";
|
||||
import { IIDX_DP_IMPL, IIDX_SP_IMPL } from "./games/iidx";
|
||||
import { ITG_STAMINA_IMPL } from "./games/itg";
|
||||
import { JUBEAT_IMPL } from "./games/jubeat";
|
||||
import { MAIMAI_IMPL } from "./games/maimai";
|
||||
import { MAIMAIDX_IMPL } from "./games/maimaidx";
|
||||
import { MUSECA_IMPL } from "./games/museca";
|
||||
import { POPN_9B_IMPL } from "./games/popn";
|
||||
@@ -33,6 +34,7 @@ export const GPT_SERVER_IMPLEMENTATIONS: GPTImplementations = {
|
||||
"gitadora:Gita": GITADORA_GITA_IMPL,
|
||||
"itg:Stamina": ITG_STAMINA_IMPL,
|
||||
"jubeat:Single": JUBEAT_IMPL,
|
||||
"maimai:Single": MAIMAI_IMPL,
|
||||
"maimaidx:Single": MAIMAIDX_IMPL,
|
||||
"museca:Single": MUSECA_IMPL,
|
||||
"popn:9B": POPN_9B_IMPL,
|
||||
|
||||
@@ -0,0 +1,201 @@
|
||||
import { MAIMAI_IMPL } from "./maimai";
|
||||
import db from "external/mongo/db";
|
||||
import CreateLogCtx from "lib/logger/logger";
|
||||
import { CreatePBDoc } from "lib/score-import/framework/pb/create-pb-doc";
|
||||
import { MAIMAI_SINGLE_CONF } from "tachi-common/config/game-support/maimai";
|
||||
import t from "tap";
|
||||
import { mkMockScore, mkMockPB, dmf } from "test-utils/misc";
|
||||
import ResetDBState from "test-utils/resets";
|
||||
import { TestSnapshot } from "test-utils/single-process-snapshot";
|
||||
import { TestingMaimaiChart } from "test-utils/test-data";
|
||||
import type { ProvidedMetrics, ScoreData, integer } from "tachi-common";
|
||||
|
||||
const logger = CreateLogCtx(__filename);
|
||||
|
||||
const baseMetrics: ProvidedMetrics["maimai:Single"] = {
|
||||
lamp: "CLEAR",
|
||||
percent: 97.01,
|
||||
};
|
||||
|
||||
const GRADES = MAIMAI_SINGLE_CONF.derivedMetrics.grade.values;
|
||||
const LAMPS = MAIMAI_SINGLE_CONF.providedMetrics.lamp.values;
|
||||
|
||||
const scoreData: ScoreData<"maimai:Single"> = {
|
||||
lamp: "CLEAR",
|
||||
percent: 97.01,
|
||||
grade: "S",
|
||||
judgements: {},
|
||||
optional: { enumIndexes: {} },
|
||||
enumIndexes: {
|
||||
grade: GRADES.indexOf("S"),
|
||||
lamp: LAMPS.indexOf("CLEAR"),
|
||||
},
|
||||
};
|
||||
|
||||
const mockScore = mkMockScore("maimai", "Single", TestingMaimaiChart, scoreData);
|
||||
const mockPB = mkMockPB("maimai", "Single", TestingMaimaiChart, scoreData);
|
||||
|
||||
t.test("maimai Implementation", (t) => {
|
||||
t.test("Percent Validator", (t) => {
|
||||
t.equal(MAIMAI_IMPL.chartSpecificValidators.percent(100.78, TestingMaimaiChart), true);
|
||||
t.equal(MAIMAI_IMPL.chartSpecificValidators.percent(0, TestingMaimaiChart), true);
|
||||
|
||||
TestSnapshot(
|
||||
t,
|
||||
MAIMAI_IMPL.chartSpecificValidators.percent(-1, TestingMaimaiChart),
|
||||
`Maimai Percent Validator: negative`
|
||||
);
|
||||
|
||||
TestSnapshot(
|
||||
t,
|
||||
MAIMAI_IMPL.chartSpecificValidators.percent(101, TestingMaimaiChart),
|
||||
`Maimai Percent Validator: excessive`
|
||||
);
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test("Grade Deriver", (t) => {
|
||||
const f = (percent: number, expected: string) =>
|
||||
t.equal(
|
||||
MAIMAI_IMPL.derivers.grade(dmf(baseMetrics, { percent }), TestingMaimaiChart),
|
||||
expected,
|
||||
`A percent of ${percent} should result in grade=${expected}.`
|
||||
);
|
||||
|
||||
f(0, "F");
|
||||
f(10, "E");
|
||||
f(20, "D");
|
||||
f(40, "C");
|
||||
f(60, "B");
|
||||
f(80, "A");
|
||||
f(90, "AA");
|
||||
f(94, "AAA");
|
||||
f(97, "S");
|
||||
f(98, "S+");
|
||||
f(99, "SS");
|
||||
f(99.5, "SS+");
|
||||
f(100, "SSS");
|
||||
f(100.78, "SSS+");
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test("Score Calcs", (t) => {
|
||||
t.equal(
|
||||
MAIMAI_IMPL.scoreCalcs.rate(scoreData, TestingMaimaiChart),
|
||||
14.85,
|
||||
"Basic rate check"
|
||||
);
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.todo("Session Calcs");
|
||||
t.todo("Profile Calcs");
|
||||
|
||||
t.test("Colour Deriver", (t) => {
|
||||
const f = (v: number | null, expected: string | null) =>
|
||||
t.equal(
|
||||
MAIMAI_IMPL.classDerivers.colour({ naiveRate: v }),
|
||||
expected,
|
||||
`A rate of ${v} should result in ${expected}.`
|
||||
);
|
||||
|
||||
f(null, null);
|
||||
f(0, "WHITE");
|
||||
|
||||
f(15, "RAINBOW");
|
||||
f(14.5, "GOLD");
|
||||
f(14, "SILVER");
|
||||
f(13, "BRONZE");
|
||||
f(12, "PURPLE");
|
||||
f(10, "RED");
|
||||
f(7, "YELLOW");
|
||||
f(4, "GREEN");
|
||||
f(2, "BLUE");
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test("Goal Formatters", (t) => {
|
||||
t.test("Criteria", (t) => {
|
||||
t.equal(MAIMAI_IMPL.goalCriteriaFormatters.percent(93.14), "Get 93.14% on");
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test("Progress", (t) => {
|
||||
const f = (
|
||||
k: keyof typeof MAIMAI_IMPL.goalProgressFormatters,
|
||||
modifant: Partial<ScoreData<"maimai:Single">>,
|
||||
goalValue: integer,
|
||||
expected: string
|
||||
) =>
|
||||
t.equal(
|
||||
MAIMAI_IMPL.goalProgressFormatters[k](
|
||||
dmf(mockPB, {
|
||||
scoreData: modifant,
|
||||
}),
|
||||
goalValue
|
||||
),
|
||||
expected
|
||||
);
|
||||
|
||||
f("grade", { grade: "S", percent: 97.5 }, GRADES.indexOf("SS"), "(S+)-0.50%");
|
||||
|
||||
f("grade", { grade: "S", percent: 97.5 }, GRADES.indexOf("SSS+"), "(S+)-0.50%");
|
||||
f("grade", { grade: "SSS", percent: 100.2 }, GRADES.indexOf("SSS+"), "SSS+0.20%");
|
||||
f("grade", { grade: "SSS+", percent: 100.78 }, GRADES.indexOf("SSS+"), "SSS+");
|
||||
|
||||
f("percent", { percent: 98.23 }, 1_000_000, "98.23%");
|
||||
f("lamp", { lamp: "CLEAR" }, LAMPS.indexOf("CLEAR"), "CLEAR");
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test("Out Of", (t) => {
|
||||
t.equal(MAIMAI_IMPL.goalOutOfFormatters.percent(99.11), "99.11%");
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test("PB Merging", (t) => {
|
||||
t.beforeEach(ResetDBState);
|
||||
|
||||
t.test("Should join best lamp", async (t) => {
|
||||
await db.scores.insert(mockScore);
|
||||
await db.scores.insert(
|
||||
dmf(mockScore, {
|
||||
scoreID: "bestLamp",
|
||||
scoreData: {
|
||||
percent: 0,
|
||||
lamp: "FULL COMBO",
|
||||
enumIndexes: { lamp: LAMPS.indexOf("FULL COMBO") },
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
t.hasStrict(await CreatePBDoc("maimai:Single", 1, TestingMaimaiChart, logger), {
|
||||
composedFrom: [
|
||||
{ name: "Best Percent" },
|
||||
{ name: "Best Lamp", scoreID: "bestLamp" },
|
||||
],
|
||||
scoreData: {
|
||||
percent: mockScore.scoreData.percent,
|
||||
lamp: "FULL COMBO",
|
||||
enumIndexes: { lamp: LAMPS.indexOf("FULL COMBO") },
|
||||
},
|
||||
});
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.end();
|
||||
});
|
||||
@@ -0,0 +1,155 @@
|
||||
import { GoalFmtPercent, GoalOutOfFmtPercent, GradeGoalFormatter } from "./_common";
|
||||
import { CreatePBMergeFor } from "game-implementations/utils/pb-merge";
|
||||
import { ProfileAvgBestN } from "game-implementations/utils/profile-calc";
|
||||
import { SessionAvgBest10For } from "game-implementations/utils/session-calc";
|
||||
import { MaimaiRate } from "rg-stats";
|
||||
import { GetGrade, MAIMAI_GBOUNDARIES } from "tachi-common";
|
||||
import { IsNullish } from "utils/misc";
|
||||
import type { GPTServerImplementation } from "game-implementations/types";
|
||||
|
||||
export const MAIMAI_IMPL: GPTServerImplementation<"maimai:Single"> = {
|
||||
chartSpecificValidators: {
|
||||
percent: (percent, chart) => {
|
||||
if (percent < 0) {
|
||||
return "Percent cannot be negative.";
|
||||
}
|
||||
|
||||
if (percent > chart.data.maxPercent) {
|
||||
return `Percent cannot be greater than ${chart.data.maxPercent} for this chart.`;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
},
|
||||
derivers: {
|
||||
grade: ({ percent }, chart) => {
|
||||
if (percent === chart.data.maxPercent) {
|
||||
return "SSS+";
|
||||
}
|
||||
|
||||
return GetGrade(MAIMAI_GBOUNDARIES, percent);
|
||||
},
|
||||
},
|
||||
scoreCalcs: {
|
||||
rate: (scoreData, chart) =>
|
||||
MaimaiRate.calculate(scoreData.percent, chart.data.maxPercent, chart.levelNum),
|
||||
},
|
||||
sessionCalcs: { rate: SessionAvgBest10For("rate") },
|
||||
profileCalcs: {
|
||||
naiveRate: ProfileAvgBestN("rate", 30),
|
||||
},
|
||||
classDerivers: {
|
||||
colour: (ratings) => {
|
||||
const rate = ratings.naiveRate;
|
||||
|
||||
if (IsNullish(rate)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (rate >= 15) {
|
||||
return "RAINBOW";
|
||||
} else if (rate >= 14.5) {
|
||||
return "GOLD";
|
||||
} else if (rate >= 14) {
|
||||
return "SILVER";
|
||||
} else if (rate >= 13) {
|
||||
return "BRONZE";
|
||||
} else if (rate >= 12) {
|
||||
return "PURPLE";
|
||||
} else if (rate >= 10) {
|
||||
return "RED";
|
||||
} else if (rate >= 7) {
|
||||
return "YELLOW";
|
||||
} else if (rate >= 4) {
|
||||
return "GREEN";
|
||||
} else if (rate >= 2) {
|
||||
return "BLUE";
|
||||
}
|
||||
|
||||
return "WHITE";
|
||||
},
|
||||
},
|
||||
goalCriteriaFormatters: {
|
||||
percent: GoalFmtPercent,
|
||||
},
|
||||
goalProgressFormatters: {
|
||||
percent: (pb) => `${pb.scoreData.percent.toFixed(2)}%`,
|
||||
lamp: (pb) => pb.scoreData.lamp,
|
||||
grade: (pb, gradeIndex) => {
|
||||
if (pb.scoreData.grade === "SSS+") {
|
||||
return "SSS+";
|
||||
}
|
||||
|
||||
// gradeIndex is guaranteed to be a valid rank
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const goalGrade = MAIMAI_GBOUNDARIES[gradeIndex]!.name;
|
||||
|
||||
// Grade SSS+ is chart-dependent, and it isn't possible to get the
|
||||
// max score/max percent from only the percent.
|
||||
//
|
||||
// As such, if the goal is to SSS+, we have to return the current rank+delta.
|
||||
if (goalGrade === "SSS+" && pb.scoreData.grade === "SSS") {
|
||||
const boundary =
|
||||
MAIMAI_GBOUNDARIES.find((c) => c.name === "SSS")?.lowerBound ?? 100;
|
||||
const delta = pb.scoreData.percent - boundary;
|
||||
|
||||
return `SSS+${delta.toFixed(2)}%`;
|
||||
}
|
||||
|
||||
return GradeGoalFormatter(
|
||||
MAIMAI_GBOUNDARIES,
|
||||
pb.scoreData.grade,
|
||||
pb.scoreData.percent,
|
||||
goalGrade,
|
||||
(v) => `${v.toFixed(2)}%`
|
||||
);
|
||||
},
|
||||
},
|
||||
goalOutOfFormatters: {
|
||||
percent: GoalOutOfFmtPercent,
|
||||
},
|
||||
pbMergeFunctions: [
|
||||
CreatePBMergeFor("largest", "enumIndexes.lamp", "Best Lamp", (base, score) => {
|
||||
base.scoreData.lamp = score.scoreData.lamp;
|
||||
}),
|
||||
],
|
||||
defaultMergeRefName: "Best Percent",
|
||||
scoreValidators: [
|
||||
(s) => {
|
||||
if (s.scoreData.percent > 104) {
|
||||
return "Score cannot be greater than 104%.";
|
||||
}
|
||||
},
|
||||
(s) => {
|
||||
if (s.scoreData.lamp === "ALL PERFECT+" && !(s.scoreData.grade === "SSS+")) {
|
||||
return "Cannot have an ALL PERFECT+ without grade SSS+.";
|
||||
}
|
||||
|
||||
if (s.scoreData.grade === "SSS+" && !(s.scoreData.lamp === "ALL PERFECT+")) {
|
||||
return "Cannot have grade SSS+ without an ALL PERFECT+";
|
||||
}
|
||||
},
|
||||
(s) => {
|
||||
let { great, good, miss } = s.scoreData.judgements;
|
||||
|
||||
great ??= 0;
|
||||
good ??= 0;
|
||||
miss ??= 0;
|
||||
|
||||
if (s.scoreData.lamp === "ALL PERFECT") {
|
||||
// `great`, `good` and `miss` are all coalesced to 0, so they're all
|
||||
// numbers, even if eslint doesn't think so.
|
||||
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands
|
||||
if (great + good + miss > 0) {
|
||||
return "Cannot have an ALL PERFECT with any non-perfect judgements.";
|
||||
}
|
||||
}
|
||||
|
||||
if (s.scoreData.lamp === "FULL COMBO") {
|
||||
if (miss > 0) {
|
||||
return "Cannot have a FULL COMBO if the score has misses.";
|
||||
}
|
||||
}
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -44,10 +44,10 @@ t.test("Maimai DX Implementation", (t) => {
|
||||
);
|
||||
|
||||
f(0, "D");
|
||||
f(10, "C");
|
||||
f(20, "B");
|
||||
f(40, "BB");
|
||||
f(60, "BBB");
|
||||
f(50, "C");
|
||||
f(60, "B");
|
||||
f(70, "BB");
|
||||
f(75, "BBB");
|
||||
f(80, "A");
|
||||
f(90, "AA");
|
||||
f(94, "AAA");
|
||||
|
||||
@@ -43,6 +43,7 @@ function perToScore(goal: GoalDocument) {
|
||||
case "bms":
|
||||
case "gitadora":
|
||||
case "jubeat":
|
||||
case "maimai":
|
||||
case "maimaidx":
|
||||
case "pms":
|
||||
case "itg":
|
||||
|
||||
@@ -87,6 +87,12 @@ const scoreMovers: ScoreMovers = {
|
||||
"sdvx:Single": NEUTRAL_MV,
|
||||
"wacca:Single": NEUTRAL_MV,
|
||||
"museca:Single": NEUTRAL_MV,
|
||||
"maimai:Single": (old) => ({
|
||||
percent: old.percent,
|
||||
judgements: old.judgements,
|
||||
lamp: old.lamp as any,
|
||||
optional: old.hitMeta,
|
||||
}),
|
||||
"maimaidx:Single": (old) => ({
|
||||
percent: old.percent,
|
||||
judgements: old.judgements,
|
||||
|
||||
@@ -48,6 +48,8 @@
|
||||
"EX Score Validator Controller: excessive": "EX Score cannot be greater than 4512 for this chart.",
|
||||
"EX Score Validator Keyboard: negative": "EX Score cannot be negative.",
|
||||
"EX Score Validator Keyboard: excessive": "EX Score cannot be greater than 4512 for this chart.",
|
||||
"Maimai Percent Validator: negative": "Percent cannot be negative.",
|
||||
"Maimai Percent Validator: excessive": "Percent cannot be greater than 100.78 for this chart.",
|
||||
"WACCA IMPL Validators: Should disallow ALL MARVELOUS with non-perfect score.": [
|
||||
"ALL MARVELOUS scores must have a perfect score. Got 999999 instead."
|
||||
],
|
||||
|
||||
@@ -369,6 +369,22 @@ export const TestingGitadoraChart: ChartDocument<"gitadora:Dora" | "gitadora:Git
|
||||
versions: ["konaste"],
|
||||
};
|
||||
|
||||
export const TestingMaimaiChart: ChartDocument<"maimai:Single"> = {
|
||||
chartID: "6e087e5f58519a2697374b9bff776a004046e952",
|
||||
data: {
|
||||
inGameID: 834,
|
||||
inGameStrID: "pandora_paradoxxx",
|
||||
maxPercent: 100.78,
|
||||
},
|
||||
difficulty: "Master",
|
||||
isPrimary: true,
|
||||
level: "13+",
|
||||
levelNum: 13.9,
|
||||
playtype: "Single",
|
||||
songID: 834,
|
||||
versions: ["finale"],
|
||||
};
|
||||
|
||||
export const TestingMaimaiDXChart: ChartDocument<"maimaidx:Single"> = {
|
||||
chartID: "fbde73d543f93cbad9ec67052a7165700c5c8df6",
|
||||
data: {
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
"popn",
|
||||
"jubeat",
|
||||
"pms",
|
||||
"maimai",
|
||||
"maimaidx",
|
||||
],
|
||||
IMPORT_TYPES: [
|
||||
|
||||
Reference in New Issue
Block a user