mirror of
https://github.com/zkldi/Tachi.git
synced 2026-10-03 12:28:13 +03:00
DDR Implementation (#1132)
* feat: DDR Implementation * style: lint fixes * chore: update rg-stats * feat: ddr konaste songs and charts update
This commit is contained in:
+1
-1
@@ -12,4 +12,4 @@ TACHI_NAME="Tachi Local Dev"
|
||||
|
||||
# turn this off if you're on linux for actually good performance
|
||||
# this needs to be on for windows because lol lmao
|
||||
FORCE_FS_POLLING="true"
|
||||
FORCE_FS_POLLING="true"
|
||||
|
||||
+2
-2
@@ -68,7 +68,7 @@
|
||||
"react-router-bootstrap": "^0.25.0",
|
||||
"react-router-dom": "5.1.2",
|
||||
"react-select": "^5.6.1",
|
||||
"rg-stats": "0.5.6",
|
||||
"rg-stats": "0.5.8",
|
||||
"sync-fetch": "^0.3.1",
|
||||
"tachi-common": "workspace:../common",
|
||||
"vite-plugin-html": "^3.2.0"
|
||||
@@ -91,4 +91,4 @@
|
||||
"node": "20",
|
||||
"pnpm": "8"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -397,6 +397,7 @@ function InputAlert({ game }: { game: Game }) {
|
||||
case "popn":
|
||||
case "wacca":
|
||||
case "ongeki":
|
||||
case "ddr":
|
||||
return (
|
||||
<>
|
||||
<strong>Scores must be achieved on an arcade-size controller!</strong>
|
||||
|
||||
@@ -29,6 +29,8 @@ const GPT_UTILS: Record<GPTString, Array<GPTUtility>> = {
|
||||
"maimaidx:Single": [],
|
||||
"maimai:Single": [],
|
||||
"ongeki:Single": [],
|
||||
"ddr:SP": [],
|
||||
"ddr:DP": [],
|
||||
};
|
||||
|
||||
export function GetGPTUtils(game: Game, playtype: Playtype) {
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import { ChangeOpacity } from "util/color-opacity";
|
||||
import React from "react";
|
||||
import { integer } from "tachi-common";
|
||||
|
||||
export default function DDRScoreCell({
|
||||
score,
|
||||
colour,
|
||||
grade,
|
||||
scoreRenderFn,
|
||||
}: {
|
||||
score?: integer;
|
||||
grade: string;
|
||||
colour: string;
|
||||
showScore?: boolean;
|
||||
scoreRenderFn?: (s: number) => string;
|
||||
}) {
|
||||
return (
|
||||
<td
|
||||
style={{
|
||||
backgroundColor: ChangeOpacity(colour, 0.2),
|
||||
}}
|
||||
>
|
||||
<strong>{grade}</strong>
|
||||
<br />
|
||||
{score !== undefined && <>{scoreRenderFn ? scoreRenderFn(score) : score}</>}
|
||||
</td>
|
||||
);
|
||||
}
|
||||
@@ -27,6 +27,7 @@ import { GPTClientImplementation } from "./types";
|
||||
import { SDVX_IMPL, USC_IMPL } from "./games/sdvx-usc";
|
||||
import { GITADORA_DORA_IMPL, GITADORA_GITA_IMPL } from "./games/gitadora";
|
||||
import { ARCAEA_TOUCH_IMPL } from "./games/arcaea";
|
||||
import { DDR_DP_IMPL, DDR_SP_IMPL } from "./games/ddr";
|
||||
|
||||
type GPTClientImplementations = {
|
||||
[GPT in GPTString]: GPTClientImplementation<GPT>;
|
||||
@@ -40,6 +41,8 @@ const defaultEnumIcons = {
|
||||
export const GPT_CLIENT_IMPLEMENTATIONS: GPTClientImplementations = {
|
||||
"iidx:SP": IIDX_SP_IMPL,
|
||||
"iidx:DP": IIDX_DP_IMPL,
|
||||
"ddr:SP": DDR_SP_IMPL,
|
||||
"ddr:DP": DDR_DP_IMPL,
|
||||
"chunithm:Single": {
|
||||
sessionImportantScoreCount: 30,
|
||||
enumIcons: defaultEnumIcons,
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
import { NumericSOV } from "util/sorts";
|
||||
import { GPTClientImplementation } from "lib/types";
|
||||
import { COLOUR_SET, GPTStrings } from "tachi-common";
|
||||
import ScoreCell from "components/tables/cells/ScoreCell";
|
||||
import { GetEnumColour } from "lib/game-implementations";
|
||||
import React from "react";
|
||||
import RatingCell from "components/tables/cells/RatingCell";
|
||||
import LampCell from "../../components/tables/cells/LampCell";
|
||||
import DDRScoreCell from "../../components/tables/cells/DDRScoreCell";
|
||||
import { bg, bgc } from "./_util";
|
||||
|
||||
const DDR_ENUM_COLOURS: GPTClientImplementation<GPTStrings["ddr"]>["enumColours"] = {
|
||||
grade: {
|
||||
E: COLOUR_SET.gray,
|
||||
D: COLOUR_SET.paleBlue,
|
||||
"D+": COLOUR_SET.paleBlue,
|
||||
"C-": COLOUR_SET.purple,
|
||||
C: COLOUR_SET.purple,
|
||||
"C+": COLOUR_SET.purple,
|
||||
"B-": COLOUR_SET.blue,
|
||||
B: COLOUR_SET.blue,
|
||||
"B+": COLOUR_SET.blue,
|
||||
"A-": COLOUR_SET.gold,
|
||||
A: COLOUR_SET.gold,
|
||||
"A+": COLOUR_SET.gold,
|
||||
"AA-": COLOUR_SET.gold,
|
||||
AA: COLOUR_SET.gold,
|
||||
"AA+": COLOUR_SET.gold,
|
||||
AAA: COLOUR_SET.teal,
|
||||
},
|
||||
lamp: {
|
||||
FAILED: COLOUR_SET.maroon,
|
||||
ASSIST: COLOUR_SET.purple,
|
||||
CLEAR: COLOUR_SET.blue,
|
||||
"FULL COMBO": COLOUR_SET.vibrantBlue,
|
||||
"GREAT FULL COMBO": COLOUR_SET.vibrantGreen,
|
||||
"PERFECT FULL COMBO": COLOUR_SET.gold,
|
||||
"MARVELOUS FULL COMBO": COLOUR_SET.pink,
|
||||
LIFE4: COLOUR_SET.vibrantRed,
|
||||
},
|
||||
};
|
||||
|
||||
const DDR_DIFF_COLOURS: GPTClientImplementation<GPTStrings["ddr"]>["difficultyColours"] = {
|
||||
BEGINNER: COLOUR_SET.blue,
|
||||
BASIC: COLOUR_SET.paleGreen,
|
||||
DIFFICULT: COLOUR_SET.red,
|
||||
EXPERT: COLOUR_SET.vibrantYellow,
|
||||
CHALLENGE: COLOUR_SET.purple,
|
||||
};
|
||||
|
||||
const DDR_HEADERS: GPTClientImplementation<"ddr:SP" | "ddr:DP">["scoreHeaders"] = [
|
||||
["Score", "Score", NumericSOV((x) => x.scoreData.score)],
|
||||
["Lamp", "Lamp", NumericSOV((x) => x.scoreData.enumIndexes.lamp)],
|
||||
];
|
||||
|
||||
const DDR_COLOURS: GPTClientImplementation<"ddr:SP" | "ddr:DP">["classColours"] = {
|
||||
flare: {
|
||||
NONE: bg("gray"),
|
||||
"NONE+": bg("gray"),
|
||||
"NONE++": bg("gray"),
|
||||
"NONE+++": bg("gray"),
|
||||
MERCURY: bg("blue"),
|
||||
"MERCURY+": bg("blue"),
|
||||
"MERCURY++": bg("blue"),
|
||||
"MERCURY+++": bg("blue"),
|
||||
VENUS: bgc("yellow", "black"),
|
||||
"VENUS+": bgc("yellow", "black"),
|
||||
"VENUS++": bgc("yellow", "black"),
|
||||
"VENUS+++": bgc("yellow", "black"),
|
||||
EARTH: bg("forestgreen"),
|
||||
"EARTH+": bg("forestgreen"),
|
||||
"EARTH++": bg("forestgreen"),
|
||||
"EARTH+++": bg("forestgreen"),
|
||||
MARS: bg("red"),
|
||||
"MARS+": bg("red"),
|
||||
"MARS++": bg("red"),
|
||||
"MARS+++": bg("red"),
|
||||
JUPITER: bg("darkgreen"),
|
||||
"JUPITER+": bg("darkgreen"),
|
||||
"JUPITER++": bg("darkgreen"),
|
||||
"JUPITER+++": bg("darkgreen"),
|
||||
SATURN: bg("purple"),
|
||||
"SATURN+": bg("purple"),
|
||||
"SATURN++": bg("purple"),
|
||||
"SATURN+++": bg("purple"),
|
||||
URANUS: bgc("powderblue", "black"),
|
||||
"URANUS+": bgc("powderblue", "black"),
|
||||
"URANUS++": bgc("powderblue", "black"),
|
||||
"URANUS+++": bgc("powderblue", "black"),
|
||||
NEPTUNE: bg("darkslateblue"),
|
||||
"NEPTUNE+": bg("darkslateblue"),
|
||||
"NEPTUNE++": bg("darkslateblue"),
|
||||
"NEPTUNE+++": bg("darkslateblue"),
|
||||
SUN: bg("orange"),
|
||||
"SUN+": bg("orange"),
|
||||
"SUN++": bg("orange"),
|
||||
"SUN+++": bg("orange"),
|
||||
WORLD: bg("black"),
|
||||
},
|
||||
};
|
||||
|
||||
const DDRCoreCells: GPTClientImplementation<GPTStrings["ddr"]>["scoreCoreCells"] = ({
|
||||
sc,
|
||||
chart,
|
||||
}) => (
|
||||
<>
|
||||
<DDRScoreCell
|
||||
colour={GetEnumColour(sc, "grade")}
|
||||
grade={sc.scoreData.grade}
|
||||
score={sc.scoreData.score}
|
||||
/>
|
||||
<LampCell lamp={sc.scoreData.lamp} colour={GetEnumColour(sc, "lamp")} />
|
||||
</>
|
||||
);
|
||||
|
||||
const DDRRatingCell: GPTClientImplementation<GPTStrings["ddr"]>["ratingCell"] = ({
|
||||
sc,
|
||||
chart,
|
||||
rating,
|
||||
}) => (
|
||||
<>
|
||||
<RatingCell rating={rating} score={sc} />
|
||||
</>
|
||||
);
|
||||
|
||||
export const DDR_SP_IMPL: GPTClientImplementation<"ddr:SP"> = {
|
||||
sessionImportantScoreCount: 20,
|
||||
difficultyColours: DDR_DIFF_COLOURS,
|
||||
enumColours: DDR_ENUM_COLOURS,
|
||||
enumIcons: {
|
||||
grade: "sort-alpha-up",
|
||||
lamp: "lightbulb",
|
||||
},
|
||||
ratingSystems: [],
|
||||
scoreHeaders: DDR_HEADERS,
|
||||
classColours: DDR_COLOURS,
|
||||
scoreCoreCells: DDRCoreCells,
|
||||
ratingCell: DDRRatingCell,
|
||||
};
|
||||
|
||||
export const DDR_DP_IMPL: GPTClientImplementation<"ddr:DP"> = {
|
||||
sessionImportantScoreCount: 20,
|
||||
difficultyColours: DDR_DIFF_COLOURS,
|
||||
enumColours: DDR_ENUM_COLOURS,
|
||||
enumIcons: {
|
||||
grade: "sort-alpha-up",
|
||||
lamp: "lightbulb",
|
||||
},
|
||||
ratingSystems: [],
|
||||
scoreHeaders: DDR_HEADERS,
|
||||
classColours: DDR_COLOURS,
|
||||
scoreCoreCells: DDRCoreCells,
|
||||
ratingCell: DDRRatingCell,
|
||||
};
|
||||
@@ -3,6 +3,7 @@
|
||||
import { ARCAEA_CONF, ARCAEA_TOUCH_CONF } from "./game-support/arcaea";
|
||||
import { BMS_14K_CONF, BMS_7K_CONF, BMS_CONF } from "./game-support/bms";
|
||||
import { CHUNITHM_CONF, CHUNITHM_SINGLE_CONF } from "./game-support/chunithm";
|
||||
import { DDR_CONF, DDR_DP_CONF, DDR_SP_CONF } from "./game-support/ddr";
|
||||
import { GITADORA_CONF, GITADORA_DORA_CONF, GITADORA_GITA_CONF } from "./game-support/gitadora";
|
||||
import { IIDX_CONF, IIDX_DP_CONF, IIDX_SP_CONF } from "./game-support/iidx";
|
||||
import { ITG_CONF, ITG_STAMINA_CONF } from "./game-support/itg";
|
||||
@@ -50,6 +51,7 @@ export const GAME_CONFIGS = {
|
||||
itg: ITG_CONF,
|
||||
arcaea: ARCAEA_CONF,
|
||||
ongeki: ONGEKI_CONF,
|
||||
ddr: DDR_CONF,
|
||||
} as const satisfies Record<string, INTERNAL_GAME_CONFIG>;
|
||||
|
||||
/**
|
||||
@@ -98,6 +100,8 @@ export const GAME_PT_CONFIGS = {
|
||||
"itg:Stamina": ITG_STAMINA_CONF,
|
||||
"arcaea:Touch": ARCAEA_TOUCH_CONF,
|
||||
"ongeki:Single": ONGEKI_SINGLE_CONF,
|
||||
"ddr:SP": DDR_SP_CONF,
|
||||
"ddr:DP": DDR_DP_CONF,
|
||||
} as const satisfies Record<GPTString, INTERNAL_GAME_PT_CONFIG>;
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,195 @@
|
||||
import { IIDXDans } from "./iidx";
|
||||
import { FmtNum, FmtPercent, FmtScoreNoCommas } from "../../utils/util";
|
||||
import { ClassValue, zodNonNegativeInt } from "../config-utils";
|
||||
import { p } from "prudence";
|
||||
import { z } from "zod";
|
||||
import type { INTERNAL_GAME_CONFIG, INTERNAL_GAME_PT_CONFIG } from "../../types/internals";
|
||||
|
||||
export const DDR_FLARE_CATEGORIES = z.enum(["CLASSIC", "WHITE", "GOLD", "NONE"]);
|
||||
|
||||
export const DDR_DIFFICULTIES = ["BEGINNER", "BASIC", "DIFFICULT", "EXPERT", "CHALLENGE"];
|
||||
|
||||
export const DDRFlare = [
|
||||
ClassValue("NONE", "NONE"),
|
||||
ClassValue("NONE+", "NONE+"),
|
||||
ClassValue("NONE++", "NONE++"),
|
||||
ClassValue("NONE+++", "NONE+++"),
|
||||
ClassValue("MERCURY", "水星 / MERCURY"),
|
||||
ClassValue("MERCURY+", "水星 / MERCURY+"),
|
||||
ClassValue("MERCURY++", "水星 / MERCURY++"),
|
||||
ClassValue("MERCURY+++", "水星 / MERCURY+++"),
|
||||
ClassValue("VENUS", "金星 / VENUS"),
|
||||
ClassValue("VENUS+", "金星 / VENUS+"),
|
||||
ClassValue("VENUS++", "金星 / VENUS++"),
|
||||
ClassValue("VENUS+++", "金星 / VENUS+++"),
|
||||
ClassValue("EARTH", "地球 / EARTH"),
|
||||
ClassValue("EARTH+", "地球 / EARTH+"),
|
||||
ClassValue("EARTH++", "地球 / EARTH++"),
|
||||
ClassValue("EARTH+++", "地球 / EARTH+++"),
|
||||
ClassValue("MARS", "火星 / MARS"),
|
||||
ClassValue("MARS+", "火星 / MARS+"),
|
||||
ClassValue("MARS++", "火星 / MARS++"),
|
||||
ClassValue("MARS+++", "火星 / MARS+++"),
|
||||
ClassValue("JUPITER", "木星 / JUPITER"),
|
||||
ClassValue("JUPITER+", "木星 / JUPITER+"),
|
||||
ClassValue("JUPITER++", "木星 / JUPITER++"),
|
||||
ClassValue("JUPITER+++", "木星 / JUPITER+++"),
|
||||
ClassValue("SATURN", "土星 / SATURN"),
|
||||
ClassValue("SATURN+", "土星 / SATURN+"),
|
||||
ClassValue("SATURN++", "土星 / SATURN++"),
|
||||
ClassValue("SATURN+++", "土星 / SATURN+++"),
|
||||
ClassValue("URANUS", "天王星 / URANUS"),
|
||||
ClassValue("URANUS+", "天王星 / URANUS+"),
|
||||
ClassValue("URANUS++", "天王星 / URANUS++"),
|
||||
ClassValue("URANUS+++", "天王星 / URANUS+++"),
|
||||
ClassValue("NEPTUNE", "海王星 / NEPTUNE"),
|
||||
ClassValue("NEPTUNE+", "海王星 / NEPTUNE+"),
|
||||
ClassValue("NEPTUNE++", "海王星 / NEPTUNE++"),
|
||||
ClassValue("NEPTUNE+++", "海王星 / NEPTUNE+++"),
|
||||
ClassValue("SUN", "太陽 / SUN"),
|
||||
ClassValue("SUN+", "太陽 / SUN+"),
|
||||
ClassValue("SUN++", "太陽 / SUN++"),
|
||||
ClassValue("SUN+++", "太陽 / SUN+++"),
|
||||
ClassValue("WORLD", "世界 / WORLD"),
|
||||
];
|
||||
|
||||
export const DDR_CONF = {
|
||||
name: "DDR",
|
||||
playtypes: ["SP", "DP"],
|
||||
songData: z.strictObject({
|
||||
inGameID: zodNonNegativeInt,
|
||||
flareCategory: DDR_FLARE_CATEGORIES,
|
||||
}),
|
||||
} as const satisfies INTERNAL_GAME_CONFIG;
|
||||
|
||||
export const DDR_SP_CONF = {
|
||||
providedMetrics: {
|
||||
score: {
|
||||
type: "INTEGER",
|
||||
validate: p.isBetween(0, 1_000_000),
|
||||
formatter: FmtNum,
|
||||
description: "The score value. This is between 0 and 1 million.",
|
||||
},
|
||||
|
||||
lamp: {
|
||||
type: "ENUM",
|
||||
values: [
|
||||
"FAILED",
|
||||
"ASSIST",
|
||||
"CLEAR",
|
||||
"FULL COMBO",
|
||||
"GREAT FULL COMBO",
|
||||
"PERFECT FULL COMBO",
|
||||
"MARVELOUS FULL COMBO",
|
||||
"LIFE4",
|
||||
],
|
||||
minimumRelevantValue: "CLEAR",
|
||||
description: "The type of clear this user got.",
|
||||
},
|
||||
},
|
||||
|
||||
derivedMetrics: {
|
||||
grade: {
|
||||
type: "ENUM",
|
||||
values: [
|
||||
"E",
|
||||
"D",
|
||||
"D+",
|
||||
"C-",
|
||||
"C",
|
||||
"C+",
|
||||
"B-",
|
||||
"B",
|
||||
"B+",
|
||||
"A-",
|
||||
"A",
|
||||
"A+",
|
||||
"AA-",
|
||||
"AA",
|
||||
"AA+",
|
||||
"AAA",
|
||||
],
|
||||
minimumRelevantValue: "D",
|
||||
description:
|
||||
"The grade this score was. Note that grades are capped at F if this was a fail.",
|
||||
},
|
||||
},
|
||||
|
||||
optionalMetrics: {
|
||||
flare: {
|
||||
type: "ENUM",
|
||||
values: ["0", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "EX"],
|
||||
minimumRelevantValue: "0",
|
||||
description: "The Flare rank. If no Flare is provided, Flare 0 is chosen by default.",
|
||||
},
|
||||
},
|
||||
|
||||
defaultMetric: "score",
|
||||
preferredDefaultEnum: "lamp",
|
||||
|
||||
scoreRatingAlgs: {
|
||||
flareSkill: {
|
||||
description: "Flare Skill as it's implemented in DDR World.",
|
||||
formatter: FmtScoreNoCommas,
|
||||
},
|
||||
},
|
||||
|
||||
sessionRatingAlgs: {
|
||||
flareSkill: {
|
||||
description: "Average of your 10 best Flare Points this session",
|
||||
formatter: FmtScoreNoCommas,
|
||||
},
|
||||
},
|
||||
|
||||
profileRatingAlgs: {
|
||||
flareSkill: {
|
||||
description:
|
||||
"Flare Skill as it's implemented in DDR World, taking 30 best flare points from 3 different categories: CLASSIC (DDR 1st~X3 vs 2ndMIX), WHITE (DDR(2013)~DDR A), GOLD (DDR A20~WORLD).",
|
||||
formatter: FmtScoreNoCommas,
|
||||
},
|
||||
},
|
||||
|
||||
defaultScoreRatingAlg: "flareSkill",
|
||||
defaultProfileRatingAlg: "flareSkill",
|
||||
defaultSessionRatingAlg: "flareSkill",
|
||||
|
||||
difficulties: {
|
||||
type: "FIXED",
|
||||
order: DDR_DIFFICULTIES,
|
||||
shorthand: {
|
||||
BEGINNER: "BEG",
|
||||
BASIC: "BAS",
|
||||
DIFFICULT: "DIF",
|
||||
EXPERT: "EXP",
|
||||
CHALLENGE: "CHA",
|
||||
},
|
||||
default: "EXPERT",
|
||||
},
|
||||
|
||||
classes: {
|
||||
flare: {
|
||||
type: "DERIVED",
|
||||
values: DDRFlare,
|
||||
},
|
||||
},
|
||||
|
||||
orderedJudgements: ["MARVELOUS", "PERFECT", "GREAT", "GOOD", "MISS", "OK"],
|
||||
|
||||
versions: {
|
||||
a3: "A3",
|
||||
konaste: "Konaste",
|
||||
},
|
||||
|
||||
chartData: z.strictObject({
|
||||
inGameID: zodNonNegativeInt,
|
||||
}),
|
||||
|
||||
preferences: z.strictObject({}),
|
||||
scoreMeta: z.strictObject({}),
|
||||
|
||||
supportedMatchTypes: ["inGameID", "songTitle", "tachiSongID"],
|
||||
} as const satisfies INTERNAL_GAME_PT_CONFIG;
|
||||
|
||||
export const DDR_DP_CONF = {
|
||||
...DDR_SP_CONF,
|
||||
} as const satisfies INTERNAL_GAME_PT_CONFIG;
|
||||
@@ -221,3 +221,23 @@ export const ONGEKI_GBOUNDARIES = MakeGradeBoundaries<GetEnumValue<"ongeki:Singl
|
||||
SSS: 1_000_000,
|
||||
"SSS+": 1_007_500,
|
||||
});
|
||||
|
||||
export const DDR_GBOUNDARIES = MakeGradeBoundaries<GetEnumValue<GPTStrings["ddr"], "grade">>({
|
||||
AAA: 990000,
|
||||
"AA+": 950000,
|
||||
AA: 900000,
|
||||
"AA-": 890000,
|
||||
"A+": 850000,
|
||||
A: 800000,
|
||||
"A-": 790000,
|
||||
"B+": 750000,
|
||||
B: 700000,
|
||||
"B-": 690000,
|
||||
"C+": 650000,
|
||||
C: 600000,
|
||||
"C-": 590000,
|
||||
"D+": 550000,
|
||||
D: 0,
|
||||
// cannot be achieved. This can only be attained if the player failed the chart.
|
||||
E: -Infinity,
|
||||
});
|
||||
|
||||
@@ -131,6 +131,7 @@ export function FormatDifficultySearch(chart: ChartDocument, game: Game): string
|
||||
case "arcaea":
|
||||
case "ongeki":
|
||||
case "chunithm":
|
||||
case "ddr":
|
||||
case "usc":
|
||||
return chart.difficulty;
|
||||
case "iidx":
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -12337,6 +12337,652 @@
|
||||
"title": "ULTIMA (SUN)",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"data¬tableFolders": {
|
||||
"~elemMatch": {
|
||||
"level": "1",
|
||||
"table": "Level "
|
||||
}
|
||||
}
|
||||
},
|
||||
"folderID": "F0c09b0447497e6ce7016e593f26fb860526c47705e4823fe8e31079b8dc20814",
|
||||
"game": "ddr",
|
||||
"inactive": false,
|
||||
"playtype": "DP",
|
||||
"searchTerms": [],
|
||||
"title": "Level 1",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"data¬tableFolders": {
|
||||
"~elemMatch": {
|
||||
"level": "10",
|
||||
"table": "Level "
|
||||
}
|
||||
}
|
||||
},
|
||||
"folderID": "F0c9a877c6bedb79a8a49edfc353b1051f85752b57c91a2ae6d12bed0a3196417",
|
||||
"game": "ddr",
|
||||
"inactive": false,
|
||||
"playtype": "DP",
|
||||
"searchTerms": [],
|
||||
"title": "Level 10",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"data¬tableFolders": {
|
||||
"~elemMatch": {
|
||||
"level": "11",
|
||||
"table": "Level "
|
||||
}
|
||||
}
|
||||
},
|
||||
"folderID": "F520973c7d85f0d197862a897b1a81fc931080e185f6b098be2028ba22c492fc7",
|
||||
"game": "ddr",
|
||||
"inactive": false,
|
||||
"playtype": "DP",
|
||||
"searchTerms": [],
|
||||
"title": "Level 11",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"data¬tableFolders": {
|
||||
"~elemMatch": {
|
||||
"level": "12",
|
||||
"table": "Level "
|
||||
}
|
||||
}
|
||||
},
|
||||
"folderID": "Fc5acf2723ed0ae888f129be0b77d5414a6fab3420eb5e02d5eb3c08a3fa49741",
|
||||
"game": "ddr",
|
||||
"inactive": false,
|
||||
"playtype": "DP",
|
||||
"searchTerms": [],
|
||||
"title": "Level 12",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"data¬tableFolders": {
|
||||
"~elemMatch": {
|
||||
"level": "13",
|
||||
"table": "Level "
|
||||
}
|
||||
}
|
||||
},
|
||||
"folderID": "F80d3a7b581735e1b0c8f1669937fad455ec66b34f3c55457278f4cfd36b1c115",
|
||||
"game": "ddr",
|
||||
"inactive": false,
|
||||
"playtype": "DP",
|
||||
"searchTerms": [],
|
||||
"title": "Level 13",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"data¬tableFolders": {
|
||||
"~elemMatch": {
|
||||
"level": "14",
|
||||
"table": "Level "
|
||||
}
|
||||
}
|
||||
},
|
||||
"folderID": "Ff975e532c7589db66cc619384cf45ee1d9a14d775be4efd2c9253ba8630b13ed",
|
||||
"game": "ddr",
|
||||
"inactive": false,
|
||||
"playtype": "DP",
|
||||
"searchTerms": [],
|
||||
"title": "Level 14",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"data¬tableFolders": {
|
||||
"~elemMatch": {
|
||||
"level": "15",
|
||||
"table": "Level "
|
||||
}
|
||||
}
|
||||
},
|
||||
"folderID": "Fcdd639a6c30acc4703b98ba6ee87613229bfe726c0f5a3438ce913b52c68e537",
|
||||
"game": "ddr",
|
||||
"inactive": false,
|
||||
"playtype": "DP",
|
||||
"searchTerms": [],
|
||||
"title": "Level 15",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"data¬tableFolders": {
|
||||
"~elemMatch": {
|
||||
"level": "16",
|
||||
"table": "Level "
|
||||
}
|
||||
}
|
||||
},
|
||||
"folderID": "F408f16670afeff015eb0d8f1c149013a3cb2b29bd474bc676e5993ca25b312cd",
|
||||
"game": "ddr",
|
||||
"inactive": false,
|
||||
"playtype": "DP",
|
||||
"searchTerms": [],
|
||||
"title": "Level 16",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"data¬tableFolders": {
|
||||
"~elemMatch": {
|
||||
"level": "17",
|
||||
"table": "Level "
|
||||
}
|
||||
}
|
||||
},
|
||||
"folderID": "Fdde615d765569b32535711ef67fd85dde59c31999dda1f48176529f03b6e5de3",
|
||||
"game": "ddr",
|
||||
"inactive": false,
|
||||
"playtype": "DP",
|
||||
"searchTerms": [],
|
||||
"title": "Level 17",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"data¬tableFolders": {
|
||||
"~elemMatch": {
|
||||
"level": "18",
|
||||
"table": "Level "
|
||||
}
|
||||
}
|
||||
},
|
||||
"folderID": "F9934345ea33451bd21be4151a5ddc130aa735960e8bf9ff66a060e42819f3888",
|
||||
"game": "ddr",
|
||||
"inactive": false,
|
||||
"playtype": "DP",
|
||||
"searchTerms": [],
|
||||
"title": "Level 18",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"data¬tableFolders": {
|
||||
"~elemMatch": {
|
||||
"level": "19",
|
||||
"table": "Level "
|
||||
}
|
||||
}
|
||||
},
|
||||
"folderID": "Fc6d420869984a20cc8954cff43df46c49b0e2c06998b94e3226655bf13c75d3c",
|
||||
"game": "ddr",
|
||||
"inactive": false,
|
||||
"playtype": "DP",
|
||||
"searchTerms": [],
|
||||
"title": "Level 19",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"data¬tableFolders": {
|
||||
"~elemMatch": {
|
||||
"level": "2",
|
||||
"table": "Level "
|
||||
}
|
||||
}
|
||||
},
|
||||
"folderID": "F1e62ed269483936c348678b307c649bf674e0d3a3735d9635ed43dae79caa7c0",
|
||||
"game": "ddr",
|
||||
"inactive": false,
|
||||
"playtype": "DP",
|
||||
"searchTerms": [],
|
||||
"title": "Level 2",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"data¬tableFolders": {
|
||||
"~elemMatch": {
|
||||
"level": "3",
|
||||
"table": "Level "
|
||||
}
|
||||
}
|
||||
},
|
||||
"folderID": "F848646e7a6689964ef7ca99672e32d41560dd4e50b49a2247e800fbff1958323",
|
||||
"game": "ddr",
|
||||
"inactive": false,
|
||||
"playtype": "DP",
|
||||
"searchTerms": [],
|
||||
"title": "Level 3",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"data¬tableFolders": {
|
||||
"~elemMatch": {
|
||||
"level": "4",
|
||||
"table": "Level "
|
||||
}
|
||||
}
|
||||
},
|
||||
"folderID": "F83239573a4f29a9ac847425afba58cf41fc43bc74d415daf5826611936b59da2",
|
||||
"game": "ddr",
|
||||
"inactive": false,
|
||||
"playtype": "DP",
|
||||
"searchTerms": [],
|
||||
"title": "Level 4",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"data¬tableFolders": {
|
||||
"~elemMatch": {
|
||||
"level": "5",
|
||||
"table": "Level "
|
||||
}
|
||||
}
|
||||
},
|
||||
"folderID": "F37df248ab2f58f732f24940293c5e91c4aaf5e64344d5fb1cd192a9e4b0d8d45",
|
||||
"game": "ddr",
|
||||
"inactive": false,
|
||||
"playtype": "DP",
|
||||
"searchTerms": [],
|
||||
"title": "Level 5",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"data¬tableFolders": {
|
||||
"~elemMatch": {
|
||||
"level": "6",
|
||||
"table": "Level "
|
||||
}
|
||||
}
|
||||
},
|
||||
"folderID": "F1e794bc73e08cd2843130b7a9df76877941b3a4d2789ec76c2c3bab878c9f43d",
|
||||
"game": "ddr",
|
||||
"inactive": false,
|
||||
"playtype": "DP",
|
||||
"searchTerms": [],
|
||||
"title": "Level 6",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"data¬tableFolders": {
|
||||
"~elemMatch": {
|
||||
"level": "7",
|
||||
"table": "Level "
|
||||
}
|
||||
}
|
||||
},
|
||||
"folderID": "Fb59f8945c3d7b0eaa113c9899e9ae55d5a7ad7e157d374c0d9a74235287d4794",
|
||||
"game": "ddr",
|
||||
"inactive": false,
|
||||
"playtype": "DP",
|
||||
"searchTerms": [],
|
||||
"title": "Level 7",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"data¬tableFolders": {
|
||||
"~elemMatch": {
|
||||
"level": "8",
|
||||
"table": "Level "
|
||||
}
|
||||
}
|
||||
},
|
||||
"folderID": "F2463f6bc988f960f2b6e822187805a35b7c66412e9cfecf2ba0f81c2a9aacebc",
|
||||
"game": "ddr",
|
||||
"inactive": false,
|
||||
"playtype": "DP",
|
||||
"searchTerms": [],
|
||||
"title": "Level 8",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"data¬tableFolders": {
|
||||
"~elemMatch": {
|
||||
"level": "9",
|
||||
"table": "Level "
|
||||
}
|
||||
}
|
||||
},
|
||||
"folderID": "F5efd26d77d73dc52c934ff7e97b554de63e20ead805525069bc4993cf4d50ffb",
|
||||
"game": "ddr",
|
||||
"inactive": false,
|
||||
"playtype": "DP",
|
||||
"searchTerms": [],
|
||||
"title": "Level 9",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"data¬tableFolders": {
|
||||
"~elemMatch": {
|
||||
"level": "1",
|
||||
"table": "Level "
|
||||
}
|
||||
}
|
||||
},
|
||||
"folderID": "F298d0411534d7b38b7cbca14a43a1e4de71f30d73a5bdbb7f677b30fcc6e6cc2",
|
||||
"game": "ddr",
|
||||
"inactive": false,
|
||||
"playtype": "SP",
|
||||
"searchTerms": [],
|
||||
"title": "Level 1",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"data¬tableFolders": {
|
||||
"~elemMatch": {
|
||||
"level": "10",
|
||||
"table": "Level "
|
||||
}
|
||||
}
|
||||
},
|
||||
"folderID": "Ff99e411eb0e3dbaa2dab8e52dda4ef35ccc327c788140c10c2d2a286c55cdd46",
|
||||
"game": "ddr",
|
||||
"inactive": false,
|
||||
"playtype": "SP",
|
||||
"searchTerms": [],
|
||||
"title": "Level 10",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"data¬tableFolders": {
|
||||
"~elemMatch": {
|
||||
"level": "11",
|
||||
"table": "Level "
|
||||
}
|
||||
}
|
||||
},
|
||||
"folderID": "F5437f25765ead737606c7fd9042163a0063532c125fe17d9e77d113b7d1ac7ef",
|
||||
"game": "ddr",
|
||||
"inactive": false,
|
||||
"playtype": "SP",
|
||||
"searchTerms": [],
|
||||
"title": "Level 11",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"data¬tableFolders": {
|
||||
"~elemMatch": {
|
||||
"level": "12",
|
||||
"table": "Level "
|
||||
}
|
||||
}
|
||||
},
|
||||
"folderID": "F9ad83b2f07753c7fbebee715cbaddd0e444bf6c808e4b26fdb8fcd42a46bab92",
|
||||
"game": "ddr",
|
||||
"inactive": false,
|
||||
"playtype": "SP",
|
||||
"searchTerms": [],
|
||||
"title": "Level 12",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"data¬tableFolders": {
|
||||
"~elemMatch": {
|
||||
"level": "13",
|
||||
"table": "Level "
|
||||
}
|
||||
}
|
||||
},
|
||||
"folderID": "Fe80638cfba7c94e1dbf7c6a8e094ee1b771f886f9b083b5d9ed6d89fbe24290d",
|
||||
"game": "ddr",
|
||||
"inactive": false,
|
||||
"playtype": "SP",
|
||||
"searchTerms": [],
|
||||
"title": "Level 13",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"data¬tableFolders": {
|
||||
"~elemMatch": {
|
||||
"level": "14",
|
||||
"table": "Level "
|
||||
}
|
||||
}
|
||||
},
|
||||
"folderID": "F5a72b2268e14987eec04b9bd471740109354a683151443469ad44cada067fb12",
|
||||
"game": "ddr",
|
||||
"inactive": false,
|
||||
"playtype": "SP",
|
||||
"searchTerms": [],
|
||||
"title": "Level 14",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"data¬tableFolders": {
|
||||
"~elemMatch": {
|
||||
"level": "15",
|
||||
"table": "Level "
|
||||
}
|
||||
}
|
||||
},
|
||||
"folderID": "F8a04baa09fb2466d8f5c45d3223a4fd75dd2cff80b09dd3db87d54bee98ee49c",
|
||||
"game": "ddr",
|
||||
"inactive": false,
|
||||
"playtype": "SP",
|
||||
"searchTerms": [],
|
||||
"title": "Level 15",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"data¬tableFolders": {
|
||||
"~elemMatch": {
|
||||
"level": "16",
|
||||
"table": "Level "
|
||||
}
|
||||
}
|
||||
},
|
||||
"folderID": "F50bad5092469b33ef153acb28aa545a7f5cd0b6ff699ef5e6145667f4377508d",
|
||||
"game": "ddr",
|
||||
"inactive": false,
|
||||
"playtype": "SP",
|
||||
"searchTerms": [],
|
||||
"title": "Level 16",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"data¬tableFolders": {
|
||||
"~elemMatch": {
|
||||
"level": "17",
|
||||
"table": "Level "
|
||||
}
|
||||
}
|
||||
},
|
||||
"folderID": "F0d6274231e5060bb8f1b4f72f3e0b132825629bbc9a193b3fe8781aa10c628e3",
|
||||
"game": "ddr",
|
||||
"inactive": false,
|
||||
"playtype": "SP",
|
||||
"searchTerms": [],
|
||||
"title": "Level 17",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"data¬tableFolders": {
|
||||
"~elemMatch": {
|
||||
"level": "18",
|
||||
"table": "Level "
|
||||
}
|
||||
}
|
||||
},
|
||||
"folderID": "Ffb7d33d91fc0ab5c613dd200a51d595fca03d9252f5c10d0a55952c769aeae66",
|
||||
"game": "ddr",
|
||||
"inactive": false,
|
||||
"playtype": "SP",
|
||||
"searchTerms": [],
|
||||
"title": "Level 18",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"data¬tableFolders": {
|
||||
"~elemMatch": {
|
||||
"level": "19",
|
||||
"table": "Level "
|
||||
}
|
||||
}
|
||||
},
|
||||
"folderID": "F210ff4a4b62852c199717f07532d74371db48ac8922fec0496b8f97813d8c1c7",
|
||||
"game": "ddr",
|
||||
"inactive": false,
|
||||
"playtype": "SP",
|
||||
"searchTerms": [],
|
||||
"title": "Level 19",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"data¬tableFolders": {
|
||||
"~elemMatch": {
|
||||
"level": "2",
|
||||
"table": "Level "
|
||||
}
|
||||
}
|
||||
},
|
||||
"folderID": "F2f3c320dcb69a9810f88f1bef0ec357b6f25d1200dab5daea6c7d88b4021f0ea",
|
||||
"game": "ddr",
|
||||
"inactive": false,
|
||||
"playtype": "SP",
|
||||
"searchTerms": [],
|
||||
"title": "Level 2",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"data¬tableFolders": {
|
||||
"~elemMatch": {
|
||||
"level": "3",
|
||||
"table": "Level "
|
||||
}
|
||||
}
|
||||
},
|
||||
"folderID": "F0f6e04147a238bb365fa78d8b45d53f8fb7af3b2b0084c0e7630512c6fb28f94",
|
||||
"game": "ddr",
|
||||
"inactive": false,
|
||||
"playtype": "SP",
|
||||
"searchTerms": [],
|
||||
"title": "Level 3",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"data¬tableFolders": {
|
||||
"~elemMatch": {
|
||||
"level": "4",
|
||||
"table": "Level "
|
||||
}
|
||||
}
|
||||
},
|
||||
"folderID": "F374c59150d51b54d8096c9873095e4e425d615f35ed88bb03ff1f8c939dcf345",
|
||||
"game": "ddr",
|
||||
"inactive": false,
|
||||
"playtype": "SP",
|
||||
"searchTerms": [],
|
||||
"title": "Level 4",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"data¬tableFolders": {
|
||||
"~elemMatch": {
|
||||
"level": "5",
|
||||
"table": "Level "
|
||||
}
|
||||
}
|
||||
},
|
||||
"folderID": "F319b480a6607d528b8a630d5a08904c01b5d1357e5c80a23bab5e03cb2d8d6fa",
|
||||
"game": "ddr",
|
||||
"inactive": false,
|
||||
"playtype": "SP",
|
||||
"searchTerms": [],
|
||||
"title": "Level 5",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"data¬tableFolders": {
|
||||
"~elemMatch": {
|
||||
"level": "6",
|
||||
"table": "Level "
|
||||
}
|
||||
}
|
||||
},
|
||||
"folderID": "F8f4111b72204384257bfd6acd615dd50e28414348efc773a28073fed4165c71f",
|
||||
"game": "ddr",
|
||||
"inactive": false,
|
||||
"playtype": "SP",
|
||||
"searchTerms": [],
|
||||
"title": "Level 6",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"data¬tableFolders": {
|
||||
"~elemMatch": {
|
||||
"level": "7",
|
||||
"table": "Level "
|
||||
}
|
||||
}
|
||||
},
|
||||
"folderID": "F20ee236c6cb25263eac9af9c9d873b741499778995e3f6ca97a688410ee948b2",
|
||||
"game": "ddr",
|
||||
"inactive": false,
|
||||
"playtype": "SP",
|
||||
"searchTerms": [],
|
||||
"title": "Level 7",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"data¬tableFolders": {
|
||||
"~elemMatch": {
|
||||
"level": "8",
|
||||
"table": "Level "
|
||||
}
|
||||
}
|
||||
},
|
||||
"folderID": "F35e045b4f2b46bf783ff17ab67e2e834f36561cc7eb7ffeeba2a69e5538bcb01",
|
||||
"game": "ddr",
|
||||
"inactive": false,
|
||||
"playtype": "SP",
|
||||
"searchTerms": [],
|
||||
"title": "Level 8",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"data¬tableFolders": {
|
||||
"~elemMatch": {
|
||||
"level": "9",
|
||||
"table": "Level "
|
||||
}
|
||||
}
|
||||
},
|
||||
"folderID": "F5379a63559ac7925560c02e98fc8a32c4a8210c989203cb8deaac01120c017b0",
|
||||
"game": "ddr",
|
||||
"inactive": false,
|
||||
"playtype": "SP",
|
||||
"searchTerms": [],
|
||||
"title": "Level 9",
|
||||
"type": "charts"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"levelNum": {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1313,6 +1313,66 @@
|
||||
"tableID": "chunithm-Single-sun-difficulties",
|
||||
"title": "CHUNITHM (SUN) (Difficulties)"
|
||||
},
|
||||
{
|
||||
"default": true,
|
||||
"description": "All songs in DDR A3",
|
||||
"folders": [
|
||||
"F0c09b0447497e6ce7016e593f26fb860526c47705e4823fe8e31079b8dc20814",
|
||||
"F1e62ed269483936c348678b307c649bf674e0d3a3735d9635ed43dae79caa7c0",
|
||||
"F848646e7a6689964ef7ca99672e32d41560dd4e50b49a2247e800fbff1958323",
|
||||
"F83239573a4f29a9ac847425afba58cf41fc43bc74d415daf5826611936b59da2",
|
||||
"F37df248ab2f58f732f24940293c5e91c4aaf5e64344d5fb1cd192a9e4b0d8d45",
|
||||
"F1e794bc73e08cd2843130b7a9df76877941b3a4d2789ec76c2c3bab878c9f43d",
|
||||
"Fb59f8945c3d7b0eaa113c9899e9ae55d5a7ad7e157d374c0d9a74235287d4794",
|
||||
"F2463f6bc988f960f2b6e822187805a35b7c66412e9cfecf2ba0f81c2a9aacebc",
|
||||
"F5efd26d77d73dc52c934ff7e97b554de63e20ead805525069bc4993cf4d50ffb",
|
||||
"F0c9a877c6bedb79a8a49edfc353b1051f85752b57c91a2ae6d12bed0a3196417",
|
||||
"F520973c7d85f0d197862a897b1a81fc931080e185f6b098be2028ba22c492fc7",
|
||||
"Fc5acf2723ed0ae888f129be0b77d5414a6fab3420eb5e02d5eb3c08a3fa49741",
|
||||
"F80d3a7b581735e1b0c8f1669937fad455ec66b34f3c55457278f4cfd36b1c115",
|
||||
"Ff975e532c7589db66cc619384cf45ee1d9a14d775be4efd2c9253ba8630b13ed",
|
||||
"Fcdd639a6c30acc4703b98ba6ee87613229bfe726c0f5a3438ce913b52c68e537",
|
||||
"F408f16670afeff015eb0d8f1c149013a3cb2b29bd474bc676e5993ca25b312cd",
|
||||
"Fdde615d765569b32535711ef67fd85dde59c31999dda1f48176529f03b6e5de3",
|
||||
"F9934345ea33451bd21be4151a5ddc130aa735960e8bf9ff66a060e42819f3888",
|
||||
"Fc6d420869984a20cc8954cff43df46c49b0e2c06998b94e3226655bf13c75d3c"
|
||||
],
|
||||
"game": "ddr",
|
||||
"inactive": false,
|
||||
"playtype": "DP",
|
||||
"tableID": "ddr-DP-a3",
|
||||
"title": "DDR A3"
|
||||
},
|
||||
{
|
||||
"default": true,
|
||||
"description": "All songs in DDR A3",
|
||||
"folders": [
|
||||
"F298d0411534d7b38b7cbca14a43a1e4de71f30d73a5bdbb7f677b30fcc6e6cc2",
|
||||
"F2f3c320dcb69a9810f88f1bef0ec357b6f25d1200dab5daea6c7d88b4021f0ea",
|
||||
"F0f6e04147a238bb365fa78d8b45d53f8fb7af3b2b0084c0e7630512c6fb28f94",
|
||||
"F374c59150d51b54d8096c9873095e4e425d615f35ed88bb03ff1f8c939dcf345",
|
||||
"F319b480a6607d528b8a630d5a08904c01b5d1357e5c80a23bab5e03cb2d8d6fa",
|
||||
"F8f4111b72204384257bfd6acd615dd50e28414348efc773a28073fed4165c71f",
|
||||
"F20ee236c6cb25263eac9af9c9d873b741499778995e3f6ca97a688410ee948b2",
|
||||
"F35e045b4f2b46bf783ff17ab67e2e834f36561cc7eb7ffeeba2a69e5538bcb01",
|
||||
"F5379a63559ac7925560c02e98fc8a32c4a8210c989203cb8deaac01120c017b0",
|
||||
"Ff99e411eb0e3dbaa2dab8e52dda4ef35ccc327c788140c10c2d2a286c55cdd46",
|
||||
"F5437f25765ead737606c7fd9042163a0063532c125fe17d9e77d113b7d1ac7ef",
|
||||
"F9ad83b2f07753c7fbebee715cbaddd0e444bf6c808e4b26fdb8fcd42a46bab92",
|
||||
"Fe80638cfba7c94e1dbf7c6a8e094ee1b771f886f9b083b5d9ed6d89fbe24290d",
|
||||
"F5a72b2268e14987eec04b9bd471740109354a683151443469ad44cada067fb12",
|
||||
"F8a04baa09fb2466d8f5c45d3223a4fd75dd2cff80b09dd3db87d54bee98ee49c",
|
||||
"F50bad5092469b33ef153acb28aa545a7f5cd0b6ff699ef5e6145667f4377508d",
|
||||
"F0d6274231e5060bb8f1b4f72f3e0b132825629bbc9a193b3fe8781aa10c628e3",
|
||||
"Ffb7d33d91fc0ab5c613dd200a51d595fca03d9252f5c10d0a55952c769aeae66",
|
||||
"F210ff4a4b62852c199717f07532d74371db48ac8922fec0496b8f97813d8c1c7"
|
||||
],
|
||||
"game": "ddr",
|
||||
"inactive": false,
|
||||
"playtype": "SP",
|
||||
"tableID": "ddr-SP-a3",
|
||||
"title": "DDR A3"
|
||||
},
|
||||
{
|
||||
"default": true,
|
||||
"description": "Level Folders for GITADORA Konaste.",
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
const { MutateCollection, CreateFolderID } = require("../util");
|
||||
|
||||
// Change these for whatever table you are adding.
|
||||
const GAME = "pms";
|
||||
const PLAYTYPES = ["Controller", "Keyboard"];
|
||||
const PREFIX = "PLv";
|
||||
const TITLE = "Normal PMS Database";
|
||||
const SHORTTITLE = "normalpmsdb"; // this is used in the tableID
|
||||
const DESCRIPTION =
|
||||
"The Normal PMS Database. This contains almost every PMS file ever created, lv1-45.";
|
||||
const GAME = "ddr";
|
||||
const PLAYTYPES = ["SP", "DP"];
|
||||
const PREFIX = "Level ";
|
||||
const TITLE = "DDR A3";
|
||||
const SHORTTITLE = "a3"; // this is used in the tableID
|
||||
const DESCRIPTION = "All songs in DDR A3";
|
||||
const LEVELS = [
|
||||
"1",
|
||||
"2",
|
||||
@@ -28,34 +27,6 @@ const LEVELS = [
|
||||
"17",
|
||||
"18",
|
||||
"19",
|
||||
"20",
|
||||
"21",
|
||||
"22",
|
||||
"23",
|
||||
"24",
|
||||
"25",
|
||||
"26",
|
||||
"27",
|
||||
"28",
|
||||
"29",
|
||||
"30",
|
||||
"31",
|
||||
"32",
|
||||
"33",
|
||||
"34",
|
||||
"35",
|
||||
"36",
|
||||
"37",
|
||||
"38",
|
||||
"39",
|
||||
"40",
|
||||
"41",
|
||||
"42",
|
||||
"43",
|
||||
"44",
|
||||
"45",
|
||||
"45+",
|
||||
"?",
|
||||
];
|
||||
|
||||
const ptFolders = {};
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
# Import DDR Gamedata
|
||||
|
||||
1) Generate gamedata under the following XML format :
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<mdb>
|
||||
<music>
|
||||
<mcode __type="u32">999</mcode>
|
||||
<basename>randomsong</basename>
|
||||
<title>This song doesn't exist</title>
|
||||
<title_yomi>thissongdoesntexist</title_yomi>
|
||||
<artist>Some Artist</artist>
|
||||
<bpmmax __type="u16">120</bpmmax>
|
||||
<series __type="u8">2</series>
|
||||
<bemaniflag __type="u32">1</bemaniflag>
|
||||
<limited_cha __type="u8">1</limited_cha>
|
||||
<diffLv __type="u8" __count="10">3 5 6 9 0 0 6 8 8 0</diffLv>
|
||||
</music>
|
||||
<music>
|
||||
...
|
||||
</music>
|
||||
...
|
||||
</mdb>
|
||||
```
|
||||
|
||||
File must be stored in the same folder as the script with the name `musicdb.xml`
|
||||
|
||||
2) At the last line of the script, eventually change the version if necessary. Current supported versions are `a3` and `konaste` (which are very similar since konaste is based on a3)
|
||||
3) Run `ts-node parse-gameData-xml.ts`
|
||||
@@ -0,0 +1,37 @@
|
||||
import fs from "fs";
|
||||
import {Music, parseGameData} from "./parse-gameData";
|
||||
|
||||
interface JsonMusic {
|
||||
mcode: number,
|
||||
basename: string,
|
||||
title: string,
|
||||
title_sort: string,
|
||||
artist: string,
|
||||
bpmmax: number,
|
||||
bpmmin: number,
|
||||
level_str: string,
|
||||
editable: boolean,
|
||||
seriesid: number,
|
||||
ac_seriesid: number,
|
||||
limited_cha: number,
|
||||
bemaniflag: number,
|
||||
}
|
||||
|
||||
function jsonMusicToMusic(jsonMusic: JsonMusic): Music {
|
||||
return {
|
||||
mcode: jsonMusic.mcode,
|
||||
basename: jsonMusic.basename,
|
||||
title: jsonMusic.title,
|
||||
title_yomi: jsonMusic.title_sort,
|
||||
artist: jsonMusic.artist,
|
||||
bpmmax: jsonMusic.bpmmax,
|
||||
series: jsonMusic.ac_seriesid,
|
||||
bemaniflag: jsonMusic.bemaniflag,
|
||||
limited_cha: jsonMusic.limited_cha,
|
||||
diffLv: jsonMusic.level_str,
|
||||
}
|
||||
}
|
||||
|
||||
const gameData = JSON.parse(fs.readFileSync("music.json", "utf-8")).map(jsonMusicToMusic);
|
||||
|
||||
parseGameData("konaste", gameData);
|
||||
@@ -0,0 +1,9 @@
|
||||
import {XMLParser} from "fast-xml-parser";
|
||||
import fs from "fs";
|
||||
import {Music, parseGameData} from "./parse-gameData";
|
||||
|
||||
const parser = new XMLParser();
|
||||
|
||||
const gameData: Music[] = parser.parse(fs.readFileSync("musicdb.xml")).mdb.music;
|
||||
|
||||
parseGameData("a3", gameData);
|
||||
@@ -0,0 +1,147 @@
|
||||
import { CreateChartID, MutateCollection, ReadCollection, WriteCollection } from "../../util";
|
||||
import {
|
||||
ChartDocument,
|
||||
Difficulties,
|
||||
type GameConfig,
|
||||
GetGameConfig,
|
||||
Playtypes,
|
||||
SongDocument,
|
||||
Versions,
|
||||
} from "tachi-common";
|
||||
import { DDR_FLARE_CATEGORIES } from "tachi-common/config/game-support/ddr";
|
||||
|
||||
const gameConfig = GetGameConfig("ddr") as GameConfig<"ddr">;
|
||||
|
||||
export interface Music {
|
||||
mcode: number;
|
||||
basename: string;
|
||||
title: string;
|
||||
title_yomi: string;
|
||||
artist: string;
|
||||
bpmmax: number;
|
||||
series: number;
|
||||
bemaniflag: number;
|
||||
limited_cha: number;
|
||||
diffLv: string;
|
||||
}
|
||||
|
||||
const DIFFICULTIES: Array<Difficulties["ddr:SP" | "ddr:DP"]> = [
|
||||
"BEGINNER",
|
||||
"BASIC",
|
||||
"DIFFICULT",
|
||||
"EXPERT",
|
||||
"CHALLENGE",
|
||||
];
|
||||
|
||||
function seriesToFlareCategory(series: number) {
|
||||
if (!series) {
|
||||
return DDR_FLARE_CATEGORIES.enum.NONE;
|
||||
}
|
||||
// Before X3 vs 2nd MIX
|
||||
if (series <= 13) {
|
||||
return DDR_FLARE_CATEGORIES.enum.CLASSIC;
|
||||
}
|
||||
// Between 2013 and A
|
||||
if (series > 13 && series <= 17) {
|
||||
return DDR_FLARE_CATEGORIES.enum.WHITE;
|
||||
}
|
||||
// Between A20 and WORLD
|
||||
if (series > 17 && series <= 20) {
|
||||
return DDR_FLARE_CATEGORIES.enum.GOLD;
|
||||
}
|
||||
return DDR_FLARE_CATEGORIES.enum.NONE;
|
||||
}
|
||||
|
||||
function buildSong(music: Music): SongDocument<"ddr"> {
|
||||
return {
|
||||
artist: `${music.artist}`,
|
||||
title: `${music.title}`,
|
||||
id: music.mcode,
|
||||
data: {
|
||||
inGameID: music.mcode,
|
||||
flareCategory: seriesToFlareCategory(music.series),
|
||||
},
|
||||
altTitles: [],
|
||||
searchTerms: [],
|
||||
};
|
||||
}
|
||||
|
||||
function buildChart(
|
||||
music: Music,
|
||||
playtype: Playtypes["ddr"],
|
||||
difficulty: Difficulties["ddr:SP" | "ddr:DP"],
|
||||
version: Versions["ddr:SP" | "ddr:DP"]
|
||||
): ChartDocument<"ddr:SP" | "ddr:DP"> {
|
||||
const splitDiff = music.diffLv.split(" ");
|
||||
const diffIndex = (playtype === "SP" ? 0 : 5) + DIFFICULTIES.indexOf(difficulty);
|
||||
return {
|
||||
chartID: CreateChartID(),
|
||||
songID: music.mcode,
|
||||
difficulty: difficulty,
|
||||
isPrimary: true,
|
||||
level: splitDiff[diffIndex]!,
|
||||
levelNum: parseInt(splitDiff[diffIndex]!, 10),
|
||||
playtype: playtype,
|
||||
versions: [version],
|
||||
data: {
|
||||
inGameID: music.mcode,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function parseGameData(version: Versions["ddr:SP" | "ddr:DP"], gameData: Music[]) {
|
||||
const songs = ReadCollection("songs-ddr.json");
|
||||
const existingChartDocs = ReadCollection("charts-ddr.json");
|
||||
const existingCharts = new Map<string, ChartDocument<"ddr:SP" | "ddr:DP">>();
|
||||
for (const chart of existingChartDocs) {
|
||||
existingCharts.set(`${chart.data.inGameID}-${chart.difficulty}-${chart.playtype}`, chart);
|
||||
}
|
||||
|
||||
const newSongs: SongDocument<"ddr">[] = [];
|
||||
const newCharts: ChartDocument<"ddr:SP" | "ddr:DP">[] = [];
|
||||
for (const music of gameData) {
|
||||
const song = songs.find((s: SongDocument<"ddr">) => s.data.inGameID === music.mcode);
|
||||
if (!song) {
|
||||
const newSong = buildSong(music);
|
||||
console.log(
|
||||
`New song detected: ${newSong.artist} - ${newSong.title} (inGameId ${newSong.id})`
|
||||
);
|
||||
newSongs.push(buildSong(music));
|
||||
}
|
||||
const splitDiff = music.diffLv.split(" ");
|
||||
for (let i = 0; i < 10; i++) {
|
||||
if (parseInt(splitDiff[i]!, 10) > 0) {
|
||||
const playtype = gameConfig.playtypes.at(Math.floor(i / 5))!;
|
||||
const difficulty = DIFFICULTIES.at(i % 5)!;
|
||||
const exists = existingCharts.get(`${music.mcode}-${difficulty}-${playtype}`);
|
||||
if (exists) {
|
||||
if (!exists.versions.includes(version)) {
|
||||
console.log(
|
||||
`Adding version ${version} to chart ${exists.playtype} ${exists.difficulty} of song ${song.artist} - ${song.title} (inGameId ${song.id})`
|
||||
);
|
||||
exists.versions.push(version);
|
||||
}
|
||||
|
||||
exists.level = splitDiff[i]!;
|
||||
exists.levelNum = parseInt(splitDiff[i]!, 10);
|
||||
} else {
|
||||
const newChart = buildChart(music, playtype, difficulty, version);
|
||||
console.log(
|
||||
`New chart detected: ${newChart.playtype} ${newChart.difficulty} for song ${music.artist} - ${music.title} (inGameId ${music.mcode})`
|
||||
);
|
||||
newCharts.push(newChart);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MutateCollection("songs-ddr.json", (songs: Array<SongDocument<"ddr">>) => [
|
||||
...songs,
|
||||
...newSongs,
|
||||
]);
|
||||
|
||||
// overwrite this collection instead of mutating it
|
||||
// we already know the existing chart docs and might have mutated them to
|
||||
// declare the new versions, or update chart constants.
|
||||
WriteCollection("charts-ddr.json", [...existingChartDocs, ...newCharts]);
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
# DDR (DP) Support
|
||||
|
||||
This game has the internal GPTString of `ddr:DP`.
|
||||
|
||||
!!! 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 |
|
||||
| :: | :: | :: |
|
||||
| `score` | Integer | The score value. This is between 0 and 1 million. |
|
||||
| `lamp` | "FAILED", "ASSIST", "CLEAR", "FULL COMBO", "GREAT FULL COMBO", "PERFECT FULL COMBO", "MARVELOUS FULL COMBO", "LIFE4" | The type of clear this user got. |
|
||||
|
||||
### Derived Metrics
|
||||
|
||||
| Metric Name | Type | Description |
|
||||
| :: | :: | :: |
|
||||
| `grade` | "E", "D", "D+", "C-", "C", "C+", "B-", "B", "B+", "A-", "A", "A+", "AA-", "AA", "AA+", "AAA" | The grade this score was. Note that grades are capped at F if this was a fail. |
|
||||
| `percent` | Decimal | The % value this score was worth. This is a number between 0 and 100. |
|
||||
|
||||
### Optional Metrics
|
||||
|
||||
| Metric Name | Type | Description |
|
||||
| :: | :: | :: |
|
||||
|
||||
## Judgements
|
||||
|
||||
The folowing judgements are defined:
|
||||
|
||||
- `MARVELOUS`
|
||||
- `PERFECT`
|
||||
- `GREAT`
|
||||
- `GOOD`
|
||||
- `MISS`
|
||||
- `OK`
|
||||
|
||||
## Rating Algorithms
|
||||
|
||||
### Score Rating Algorithms
|
||||
|
||||
| Name | Description |
|
||||
| :: | :: |
|
||||
| `flareSkill` | Flare Skill as it's implemented in DDR World. |
|
||||
|
||||
### Session Rating Algorithms
|
||||
|
||||
| Name | Description |
|
||||
| :: | :: |
|
||||
| `flareSkill` | Average of your 10 best Flare Points this session |
|
||||
|
||||
### Profile Rating Algorithms
|
||||
|
||||
| Name | Description |
|
||||
| :: | :: |
|
||||
| `flareSkill` | Flare Skill as it's implemented in DDR World, taking 30 best flare points from 3 different categories: CLASSIC (DDR 1st~X3 vs 2ndMIX), WHITE (DDR(2013)~DDR A), GOLD (DDR A20~WORLD). |
|
||||
|
||||
## Difficulties
|
||||
|
||||
- `BEGINNER`
|
||||
- `BASIC`
|
||||
- `DIFFICULT`
|
||||
- `EXPERT`
|
||||
- `CHALLENGE`
|
||||
|
||||
## Classes
|
||||
|
||||
| Name | Type | Values |
|
||||
| :: | :: | :: |
|
||||
|
||||
## Versions
|
||||
|
||||
| ID | Pretty Name |
|
||||
| :: | :: |
|
||||
| `a3` | A3 |
|
||||
| `konaste` | Konaste |
|
||||
|
||||
## Supported Match Types
|
||||
|
||||
- `inGameID`
|
||||
- `songTitle`
|
||||
- `tachiSongID`
|
||||
@@ -0,0 +1,86 @@
|
||||
# DDR (SP) Support
|
||||
|
||||
This game has the internal GPTString of `ddr:SP`.
|
||||
|
||||
!!! 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 |
|
||||
| :: | :: | :: |
|
||||
| `score` | Integer | The score value. This is between 0 and 1 million. |
|
||||
| `lamp` | "FAILED", "ASSIST", "CLEAR", "FULL COMBO", "GREAT FULL COMBO", "PERFECT FULL COMBO", "MARVELOUS FULL COMBO", "LIFE4" | The type of clear this user got. |
|
||||
|
||||
### Derived Metrics
|
||||
|
||||
| Metric Name | Type | Description |
|
||||
| :: | :: | :: |
|
||||
| `grade` | "E", "D", "D+", "C-", "C", "C+", "B-", "B", "B+", "A-", "A", "A+", "AA-", "AA", "AA+", "AAA" | The grade this score was. Note that grades are capped at F if this was a fail. |
|
||||
| `percent` | Decimal | The % value this score was worth. This is a number between 0 and 100. |
|
||||
|
||||
### Optional Metrics
|
||||
|
||||
| Metric Name | Type | Description |
|
||||
| :: | :: | :: |
|
||||
|
||||
## Judgements
|
||||
|
||||
The folowing judgements are defined:
|
||||
|
||||
- `MARVELOUS`
|
||||
- `PERFECT`
|
||||
- `GREAT`
|
||||
- `GOOD`
|
||||
- `MISS`
|
||||
- `OK`
|
||||
|
||||
## Rating Algorithms
|
||||
|
||||
### Score Rating Algorithms
|
||||
|
||||
| Name | Description |
|
||||
| :: | :: |
|
||||
| `flareSkill` | Flare Skill as it's implemented in DDR World. |
|
||||
|
||||
### Session Rating Algorithms
|
||||
|
||||
| Name | Description |
|
||||
| :: | :: |
|
||||
| `flareSkill` | Average of your 10 best Flare Points this session |
|
||||
|
||||
### Profile Rating Algorithms
|
||||
|
||||
| Name | Description |
|
||||
| :: | :: |
|
||||
| `flareSkill` | Flare Skill as it's implemented in DDR World, taking 30 best flare points from 3 different categories: CLASSIC (DDR 1st~X3 vs 2ndMIX), WHITE (DDR(2013)~DDR A), GOLD (DDR A20~WORLD). |
|
||||
|
||||
## Difficulties
|
||||
|
||||
- `BEGINNER`
|
||||
- `BASIC`
|
||||
- `DIFFICULT`
|
||||
- `EXPERT`
|
||||
- `CHALLENGE`
|
||||
|
||||
## Classes
|
||||
|
||||
| Name | Type | Values |
|
||||
| :: | :: | :: |
|
||||
|
||||
## Versions
|
||||
|
||||
| ID | Pretty Name |
|
||||
| :: | :: |
|
||||
| `a3` | A3 |
|
||||
| `konaste` | Konaste |
|
||||
|
||||
## Supported Match Types
|
||||
|
||||
- `inGameID`
|
||||
- `songTitle`
|
||||
- `tachiSongID`
|
||||
Generated
+6
-6
@@ -295,8 +295,8 @@ importers:
|
||||
specifier: ^5.6.1
|
||||
version: 5.6.1(@babel/core@7.1.0)(@types/react@17.0.13)(react-dom@17.0.2)(react@17.0.2)
|
||||
rg-stats:
|
||||
specifier: 0.5.6
|
||||
version: 0.5.6
|
||||
specifier: 0.5.8
|
||||
version: 0.5.8
|
||||
sync-fetch:
|
||||
specifier: ^0.3.1
|
||||
version: 0.3.1
|
||||
@@ -662,8 +662,8 @@ importers:
|
||||
specifier: 3.1.2
|
||||
version: 3.1.2
|
||||
rg-stats:
|
||||
specifier: 0.5.6
|
||||
version: 0.5.6
|
||||
specifier: 0.5.8
|
||||
version: 0.5.8
|
||||
rimraf:
|
||||
specifier: 3.0.2
|
||||
version: 3.0.2
|
||||
@@ -10162,8 +10162,8 @@ packages:
|
||||
resolution: {integrity: sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==}
|
||||
dev: false
|
||||
|
||||
/rg-stats@0.5.6:
|
||||
resolution: {integrity: sha512-4YG942ztdaXd27KUABxiQAA3aJuLvLcvjdLfoxKqn89P7967bfyG5Z5OWaAg9Sr8pvhsABA4bHZuBAKtSW90xA==}
|
||||
/rg-stats@0.5.8:
|
||||
resolution: {integrity: sha512-AqhksXLcwACA3Af1XQkJhVSeQtFlu6NggmTqxXvw7BLDiaOsTJDZn4+gnk4XlJtf+7U0nlrMKUFM8ZlHCNFjXA==}
|
||||
dev: false
|
||||
|
||||
/rimraf@2.4.5:
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
"maimai",
|
||||
"maimaidx",
|
||||
"itg",
|
||||
"ddr"
|
||||
],
|
||||
IMPORT_TYPES: [
|
||||
"file/eamusement-iidx-csv",
|
||||
|
||||
+2
-2
@@ -100,7 +100,7 @@
|
||||
"prudence": "0.10.0",
|
||||
"rate-limit-redis": "2.1.0",
|
||||
"redis": "3.1.2",
|
||||
"rg-stats": "0.5.6",
|
||||
"rg-stats": "0.5.8",
|
||||
"rimraf": "3.0.2",
|
||||
"safe-json-stringify": "1.2.0",
|
||||
"semver": "^7.3.7",
|
||||
@@ -129,4 +129,4 @@
|
||||
"node": "20",
|
||||
"pnpm": "8"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { ARCAEA_IMPL } from "./games/arcaea";
|
||||
import { BMS_14K_IMPL, BMS_7K_IMPL, PMS_CONTROLLER_IMPL, PMS_KEYBOARD_IMPL } from "./games/bms-pms";
|
||||
import { CHUNITHM_IMPL } from "./games/chunithm";
|
||||
import { DDR_IMPL } from "./games/ddr";
|
||||
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";
|
||||
@@ -45,4 +46,6 @@ export const GPT_SERVER_IMPLEMENTATIONS: GPTImplementations = {
|
||||
"sdvx:Single": SDVX_IMPL,
|
||||
"arcaea:Touch": ARCAEA_IMPL,
|
||||
"ongeki:Single": ONGEKI_IMPL,
|
||||
"ddr:SP": DDR_IMPL,
|
||||
"ddr:DP": DDR_IMPL,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,197 @@
|
||||
import { DDR_IMPL } from "./ddr";
|
||||
import { IIDX_DP_IMPL, IIDX_SP_IMPL } from "./iidx";
|
||||
import { TestSnapshot } from "../../test-utils/single-process-snapshot";
|
||||
import { DDR_GRADES, DDR_LAMPS } from "tachi-common";
|
||||
import t from "tap";
|
||||
import { dmf, mkFakePBDDRSP } from "test-utils/misc";
|
||||
import { Testing511SPA, TestingDDRSP, TestingDDRSPScore } from "test-utils/test-data";
|
||||
import type { ChartDocumentData, ProvidedMetrics, ScoreData } from "tachi-common";
|
||||
|
||||
const baseMetrics: ProvidedMetrics["ddr:DP" | "ddr:SP"] = {
|
||||
lamp: "CLEAR",
|
||||
score: 750_000,
|
||||
};
|
||||
|
||||
t.test("DDR Implementation", (t) => {
|
||||
t.test("Derivers", (t) => {
|
||||
const impl = DDR_IMPL;
|
||||
|
||||
t.test("Grade", (t) => {
|
||||
const f = (score: number, expected: any) =>
|
||||
t.equal(
|
||||
impl.derivers.grade(dmf(baseMetrics, { score }), TestingDDRSP as any),
|
||||
expected,
|
||||
`A score of ${score} should result in grade=${expected}.`
|
||||
);
|
||||
|
||||
f(990500, "AAA");
|
||||
f(950500, "AA+");
|
||||
f(900500, "AA");
|
||||
f(890500, "AA-");
|
||||
f(850500, "A+");
|
||||
f(800500, "A");
|
||||
f(790500, "A-");
|
||||
f(750500, "B+");
|
||||
f(700500, "B");
|
||||
f(690500, "B-");
|
||||
f(650500, "C+");
|
||||
f(600500, "C");
|
||||
f(590500, "C-");
|
||||
f(550500, "D+");
|
||||
f(500, "D");
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.end();
|
||||
});
|
||||
t.test("Class Derivers", (t) => {
|
||||
const impl = DDR_IMPL;
|
||||
|
||||
t.test("Flare", (t) => {
|
||||
const f = (ratings: number, expected: any) =>
|
||||
t.equal(
|
||||
impl.classDerivers.flare({ flareSkill: ratings }),
|
||||
expected,
|
||||
`A flare skill of ${ratings} should result in grade=${expected}.`
|
||||
);
|
||||
|
||||
f(1, "NONE");
|
||||
f(501, "NONE+");
|
||||
f(1001, "NONE++");
|
||||
f(1501, "NONE+++");
|
||||
f(2001, "MERCURY");
|
||||
f(3001, "MERCURY+");
|
||||
f(4001, "MERCURY++");
|
||||
f(5001, "MERCURY+++");
|
||||
f(6001, "VENUS");
|
||||
f(7001, "VENUS+");
|
||||
f(8001, "VENUS++");
|
||||
f(9001, "VENUS+++");
|
||||
f(10001, "EARTH");
|
||||
f(11501, "EARTH+");
|
||||
f(13001, "EARTH++");
|
||||
f(14501, "EARTH+++");
|
||||
f(16001, "MARS");
|
||||
f(18001, "MARS+");
|
||||
f(20001, "MARS++");
|
||||
f(22001, "MARS+++");
|
||||
f(24001, "JUPITER");
|
||||
f(26501, "JUPITER+");
|
||||
f(29001, "JUPITER++");
|
||||
f(31501, "JUPITER+++");
|
||||
f(34001, "SATURN");
|
||||
f(36751, "SATURN+");
|
||||
f(39501, "SATURN++");
|
||||
f(42251, "SATURN+++");
|
||||
f(45001, "URANUS");
|
||||
f(48751, "URANUS+");
|
||||
f(52501, "URANUS++");
|
||||
f(56251, "URANUS+++");
|
||||
f(60001, "NEPTUNE");
|
||||
f(63751, "NEPTUNE+");
|
||||
f(67501, "NEPTUNE++");
|
||||
f(71251, "NEPTUNE+++");
|
||||
f(75001, "SUN");
|
||||
f(78751, "SUN+");
|
||||
f(82501, "SUN++");
|
||||
f(86251, "SUN+++");
|
||||
f(90001, "WORLD");
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test("CalculatedData", (t) => {
|
||||
const impl = DDR_IMPL;
|
||||
|
||||
t.test("Flare Skill", (t) => {
|
||||
const f = (
|
||||
scoreData: Partial<ScoreData<"ddr:DP" | "ddr:SP">>,
|
||||
chartData: Partial<ChartDocumentData["ddr:DP" | "ddr:SP"]>,
|
||||
expected: any,
|
||||
msg: string
|
||||
) =>
|
||||
t.equal(
|
||||
impl.scoreCalcs.flareSkill(
|
||||
dmf(TestingDDRSPScore.scoreData, scoreData),
|
||||
dmf(TestingDDRSP, { data: chartData as any }) as any
|
||||
),
|
||||
expected,
|
||||
msg
|
||||
);
|
||||
|
||||
f(
|
||||
{ grade: "AA", lamp: "CLEAR", optional: { flare: "II", enumIndexes: {} } },
|
||||
{},
|
||||
257,
|
||||
"Level 6 song with Flare II should give 257 flare skill"
|
||||
);
|
||||
|
||||
f({ grade: "E", lamp: "FAILED" }, {}, 0, "Song failed: flare skill should be 0");
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test("Goal Formatters", (t) => {
|
||||
const impl = DDR_IMPL;
|
||||
|
||||
t.test("Criteria", (t) => {
|
||||
t.equal(impl.goalCriteriaFormatters.score(123456), "Get a score of 123,456 on");
|
||||
t.equal(impl.goalCriteriaFormatters.score(0), "Get a score of 0 on");
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test("Progress", (t) => {
|
||||
const f = (
|
||||
k: keyof typeof impl.goalProgressFormatters,
|
||||
modifant: Partial<ScoreData>,
|
||||
goalValue: any,
|
||||
expected: any
|
||||
) =>
|
||||
t.equal(
|
||||
impl.goalProgressFormatters[k](
|
||||
mkFakePBDDRSP({
|
||||
// @ts-expect-error lol deepmerge types
|
||||
scoreData: modifant,
|
||||
}),
|
||||
goalValue
|
||||
),
|
||||
expected
|
||||
);
|
||||
|
||||
f("score", { score: 123_456 }, 1_000_000, "123,456");
|
||||
f("score", { score: 0 }, 1_000_000, "0");
|
||||
|
||||
f("grade", { score: 955000, grade: "AA+" }, DDR_GRADES.AAA, "AAA-35,000");
|
||||
f("grade", { score: 995000, grade: "AAA" }, DDR_GRADES.AAA, "AAA+5,000");
|
||||
|
||||
f("lamp", { lamp: "CLEAR", optional: {} as any }, DDR_LAMPS.FULL_COMBO, "CLEAR");
|
||||
|
||||
f(
|
||||
"lamp",
|
||||
{ lamp: "FULL COMBO", optional: {} as any },
|
||||
DDR_LAMPS.FULL_COMBO,
|
||||
"FULL COMBO"
|
||||
);
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test("OutOf", (t) => {
|
||||
t.equal(impl.goalOutOfFormatters.score(123456), "123,456");
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.end();
|
||||
});
|
||||
@@ -0,0 +1,342 @@
|
||||
import {
|
||||
GoalFmtPercent,
|
||||
GoalFmtScore,
|
||||
GoalOutOfFmtPercent,
|
||||
GoalOutOfFmtScore,
|
||||
GradeGoalFormatter,
|
||||
} from "./_common";
|
||||
import db from "../../external/mongo/db";
|
||||
import { IsNullish } from "../../utils/misc";
|
||||
import { CreatePBMergeFor } from "../utils/pb-merge";
|
||||
import { SessionAvgBest10For } from "../utils/session-calc";
|
||||
import { DDRFlare } from "rg-stats";
|
||||
import {
|
||||
DDR_GBOUNDARIES,
|
||||
FmtNum,
|
||||
type Game,
|
||||
GetGrade,
|
||||
GetSpecificGPTConfig,
|
||||
type integer,
|
||||
type Playtype,
|
||||
} from "tachi-common";
|
||||
import type {
|
||||
GPTGoalFormatters,
|
||||
GPTGoalProgressFormatters,
|
||||
GPTServerImplementation,
|
||||
ScoreValidator,
|
||||
} from "game-implementations/types";
|
||||
import type { PBScoreDocument, SongDocument } from "tachi-common";
|
||||
|
||||
interface PBScoreDocumentWithSong extends PBScoreDocument<"ddr:DP" | "ddr:SP"> {
|
||||
song: SongDocument<"ddr">;
|
||||
top?: number;
|
||||
}
|
||||
|
||||
const FLARE_0_POINTS = [
|
||||
145, 155, 170, 185, 205, 230, 255, 290, 335, 400, 465, 510, 545, 575, 600, 620, 635, 650, 665,
|
||||
];
|
||||
|
||||
const DDR_GOAL_FMT: GPTGoalFormatters<"ddr:DP" | "ddr:SP"> = {
|
||||
score: GoalFmtScore,
|
||||
};
|
||||
|
||||
const DDR_GOAL_OO_FMT: GPTGoalFormatters<"ddr:DP" | "ddr:SP"> = {
|
||||
score: GoalOutOfFmtScore,
|
||||
};
|
||||
|
||||
const DDR_GOAL_PG_FMT: GPTGoalProgressFormatters<"ddr:DP" | "ddr:SP"> = {
|
||||
score: (pb) => FmtNum(pb.scoreData.score),
|
||||
lamp: (pb) => {
|
||||
return pb.scoreData.lamp;
|
||||
},
|
||||
grade: (pb, gradeIndex) =>
|
||||
GradeGoalFormatter(
|
||||
DDR_GBOUNDARIES,
|
||||
pb.scoreData.grade,
|
||||
pb.scoreData.score,
|
||||
DDR_GBOUNDARIES[gradeIndex]!.name,
|
||||
(delta) => {
|
||||
return FmtNum(delta);
|
||||
}
|
||||
),
|
||||
};
|
||||
|
||||
export const DDR_SCORE_VALIDATORS: Array<ScoreValidator<"ddr:DP" | "ddr:SP">> = [
|
||||
(s) => {
|
||||
const { MARVELOUS, PERFECT, GREAT, GOOD, OK, MISS } = s.scoreData.judgements;
|
||||
|
||||
if (
|
||||
IsNullish(MARVELOUS) ||
|
||||
IsNullish(PERFECT) ||
|
||||
IsNullish(GREAT) ||
|
||||
IsNullish(GOOD) ||
|
||||
IsNullish(OK) ||
|
||||
IsNullish(MISS)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const stepScore = 1_000_000 / (MARVELOUS + PERFECT + GREAT + GOOD + OK + MISS);
|
||||
const calculatedScore =
|
||||
Math.floor(
|
||||
(stepScore * (MARVELOUS + OK) +
|
||||
(stepScore - 10) * PERFECT +
|
||||
((stepScore * 3) / 5 - 10) * GREAT +
|
||||
(stepScore / 5 - 10) * GOOD) /
|
||||
10
|
||||
) * 10;
|
||||
|
||||
if (calculatedScore !== s.scoreData.score) {
|
||||
return `Expected calculated score from judgements of ${calculatedScore} to equal score of ${s.scoreData.score}.`;
|
||||
}
|
||||
},
|
||||
(s) => {
|
||||
const { MARVELOUS, PERFECT, GREAT, GOOD, MISS } = s.scoreData.judgements;
|
||||
|
||||
if (
|
||||
IsNullish(MARVELOUS) ||
|
||||
IsNullish(PERFECT) ||
|
||||
IsNullish(GREAT) ||
|
||||
IsNullish(GOOD) ||
|
||||
IsNullish(MISS)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (s.scoreData.lamp) {
|
||||
case "FULL COMBO": {
|
||||
if (MISS > 0) {
|
||||
return `Cannot have a FULL COMBO with more than 0 MISS`;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case "GREAT FULL COMBO": {
|
||||
if (MISS > 0 || GOOD > 0) {
|
||||
return `Cannot have a GREAT FULL COMBO with more than 0 MISS and GOOD`;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case "PERFECT FULL COMBO": {
|
||||
if (MISS > 0 || GOOD > 0 || GREAT > 0) {
|
||||
return `Cannot have a PERFECT FULL COMBO with more than 0 MISS, GOOD and GREAT`;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case "MARVELOUS FULL COMBO": {
|
||||
if (MISS > 0 || GOOD > 0 || GREAT > 0 || PERFECT > 0) {
|
||||
return `Cannot have a MARVELOUS FULL COMBO with anyhing else than MARVELOUS judgements`;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
export const DDR_IMPL: GPTServerImplementation<"ddr:DP" | "ddr:SP"> = {
|
||||
chartSpecificValidators: {},
|
||||
classDerivers: {
|
||||
flare: (ratings) => {
|
||||
const flarePoints = ratings.flareSkill;
|
||||
|
||||
if (IsNullish(flarePoints)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (true) {
|
||||
case flarePoints < 500:
|
||||
return "NONE";
|
||||
case flarePoints < 1000:
|
||||
return "NONE+";
|
||||
case flarePoints < 1500:
|
||||
return "NONE++";
|
||||
case flarePoints < 2000:
|
||||
return "NONE+++";
|
||||
case flarePoints < 3000:
|
||||
return "MERCURY";
|
||||
case flarePoints < 4000:
|
||||
return "MERCURY+";
|
||||
case flarePoints < 5000:
|
||||
return "MERCURY++";
|
||||
case flarePoints < 6000:
|
||||
return "MERCURY+++";
|
||||
case flarePoints < 7000:
|
||||
return "VENUS";
|
||||
case flarePoints < 8000:
|
||||
return "VENUS+";
|
||||
case flarePoints < 9000:
|
||||
return "VENUS++";
|
||||
case flarePoints < 10000:
|
||||
return "VENUS+++";
|
||||
case flarePoints < 11500:
|
||||
return "EARTH";
|
||||
case flarePoints < 13000:
|
||||
return "EARTH+";
|
||||
case flarePoints < 14500:
|
||||
return "EARTH++";
|
||||
case flarePoints < 16000:
|
||||
return "EARTH+++";
|
||||
case flarePoints < 18000:
|
||||
return "MARS";
|
||||
case flarePoints < 20000:
|
||||
return "MARS+";
|
||||
case flarePoints < 22000:
|
||||
return "MARS++";
|
||||
case flarePoints < 24000:
|
||||
return "MARS+++";
|
||||
case flarePoints < 26500:
|
||||
return "JUPITER";
|
||||
case flarePoints < 29000:
|
||||
return "JUPITER+";
|
||||
case flarePoints < 31500:
|
||||
return "JUPITER++";
|
||||
case flarePoints < 34000:
|
||||
return "JUPITER+++";
|
||||
case flarePoints < 36750:
|
||||
return "SATURN";
|
||||
case flarePoints < 39500:
|
||||
return "SATURN+";
|
||||
case flarePoints < 42250:
|
||||
return "SATURN++";
|
||||
case flarePoints < 45000:
|
||||
return "SATURN+++";
|
||||
case flarePoints < 48750:
|
||||
return "URANUS";
|
||||
case flarePoints < 52500:
|
||||
return "URANUS+";
|
||||
case flarePoints < 56250:
|
||||
return "URANUS++";
|
||||
case flarePoints < 60000:
|
||||
return "URANUS+++";
|
||||
case flarePoints < 63750:
|
||||
return "NEPTUNE";
|
||||
case flarePoints < 67500:
|
||||
return "NEPTUNE+";
|
||||
case flarePoints < 71250:
|
||||
return "NEPTUNE++";
|
||||
case flarePoints < 75000:
|
||||
return "NEPTUNE+++";
|
||||
case flarePoints < 78750:
|
||||
return "SUN";
|
||||
case flarePoints < 82500:
|
||||
return "SUN+";
|
||||
case flarePoints < 86250:
|
||||
return "SUN++";
|
||||
case flarePoints < 90000:
|
||||
return "SUN+++";
|
||||
}
|
||||
|
||||
return "WORLD";
|
||||
},
|
||||
},
|
||||
defaultMergeRefName: "Best Score",
|
||||
derivers: {
|
||||
grade: ({ score, lamp }) => {
|
||||
if (lamp === "FAILED") {
|
||||
return "E";
|
||||
}
|
||||
|
||||
return GetGrade(DDR_GBOUNDARIES, score);
|
||||
},
|
||||
},
|
||||
goalCriteriaFormatters: DDR_GOAL_FMT,
|
||||
goalProgressFormatters: DDR_GOAL_PG_FMT,
|
||||
goalOutOfFormatters: DDR_GOAL_OO_FMT,
|
||||
pbMergeFunctions: [
|
||||
CreatePBMergeFor("largest", "enumIndexes.lamp", "Best Lamp", (base, score) => {
|
||||
base.scoreData.lamp = score.scoreData.lamp;
|
||||
}),
|
||||
CreatePBMergeFor("largest", "score", "Best Score", (base, score) => {
|
||||
base.scoreData.score = score.scoreData.score;
|
||||
base.scoreData.grade = score.scoreData.grade;
|
||||
}),
|
||||
],
|
||||
profileCalcs: {
|
||||
flareSkill: async (game: Game, playtype: Playtype, userID: integer) => {
|
||||
const sc: Array<PBScoreDocumentWithSong> = await db["personal-bests"].aggregate([
|
||||
{
|
||||
$match: {
|
||||
userID,
|
||||
game,
|
||||
playtype,
|
||||
isPrimary: true,
|
||||
[`calculatedData.flareSkill`]: { $type: "number" },
|
||||
},
|
||||
},
|
||||
{
|
||||
$lookup: {
|
||||
from: "songs-ddr",
|
||||
localField: "songID",
|
||||
foreignField: "id",
|
||||
as: "song",
|
||||
},
|
||||
},
|
||||
{
|
||||
$unwind: {
|
||||
path: "$song",
|
||||
},
|
||||
},
|
||||
{
|
||||
$sort: {
|
||||
[`calculatedData.flareSkill`]: -1,
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
if (sc.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let classicIndex = 0;
|
||||
let goldIndex = 0;
|
||||
let whiteIndex = 0;
|
||||
|
||||
for (const score of sc) {
|
||||
if (score.song.data.flareCategory === "CLASSIC") {
|
||||
score.top = classicIndex++;
|
||||
} else if (score.song.data.flareCategory === "WHITE") {
|
||||
score.top = whiteIndex++;
|
||||
} else if (score.song.data.flareCategory === "GOLD") {
|
||||
score.top = goldIndex++;
|
||||
} else {
|
||||
score.top = 99; // Score will be filtered out
|
||||
}
|
||||
}
|
||||
|
||||
return sc
|
||||
.filter((score: PBScoreDocumentWithSong) => score.top! < 30)
|
||||
.reduce(
|
||||
(a: number, e: PBScoreDocumentWithSong) => a + e.calculatedData.flareSkill!,
|
||||
0
|
||||
);
|
||||
},
|
||||
},
|
||||
scoreCalcs: {
|
||||
flareSkill: (scoreData, chart) => {
|
||||
// No flare if the song is failed
|
||||
if (scoreData.lamp === "FAILED") {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const flareLevel = scoreData.optional.flare
|
||||
? GetSpecificGPTConfig("ddr:SP").optionalMetrics.flare.values.indexOf(
|
||||
scoreData.optional.flare
|
||||
)
|
||||
: 0;
|
||||
|
||||
return DDRFlare.calculate(chart.levelNum, flareLevel);
|
||||
},
|
||||
},
|
||||
scoreValidators: DDR_SCORE_VALIDATORS,
|
||||
sessionCalcs: {
|
||||
flareSkill: SessionAvgBest10For("flareSkill"),
|
||||
},
|
||||
};
|
||||
@@ -36,6 +36,7 @@ function perToScore(goal: GoalDocument) {
|
||||
case "wacca":
|
||||
case "chunithm":
|
||||
case "ongeki":
|
||||
case "ddr":
|
||||
case "museca":
|
||||
return v * 1_000_000;
|
||||
case "popn":
|
||||
|
||||
@@ -88,6 +88,8 @@ const scoreMovers: ScoreMovers = {
|
||||
"sdvx:Single": NEUTRAL_MV,
|
||||
"wacca:Single": NEUTRAL_MV,
|
||||
"museca:Single": NEUTRAL_MV,
|
||||
"ddr:SP": NEUTRAL_MV,
|
||||
"ddr:DP": NEUTRAL_MV,
|
||||
"maimai:Single": (old) => ({
|
||||
percent: old.percent,
|
||||
judgements: old.judgements,
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
FakeOtherUser,
|
||||
HC511Goal,
|
||||
HC511UserGoal,
|
||||
TestingDDRSPScorePB,
|
||||
TestingIIDXSPScore,
|
||||
TestingIIDXSPScorePB,
|
||||
TestingJubeatPB,
|
||||
@@ -101,6 +102,10 @@ export function mkFakePBIIDXSP(modifant: DeepPartial<PBScoreDocument<"iidx:SP">>
|
||||
return dmf(TestingIIDXSPScorePB, modifant);
|
||||
}
|
||||
|
||||
export function mkFakePBDDRSP(modifant: DeepPartial<PBScoreDocument<"ddr:SP">> = {}) {
|
||||
return dmf(TestingDDRSPScorePB, modifant);
|
||||
}
|
||||
|
||||
export function mkFakePBJubeat(modifant: DeepPartial<PBScoreDocument<"jubeat:Single">> = {}) {
|
||||
return dmf(TestingJubeatPB, modifant);
|
||||
}
|
||||
|
||||
@@ -101,6 +101,44 @@ export const TestingIIDXSPScorePB: PBScoreDocument<"iidx:SP"> = {
|
||||
timeAchieved: 10000,
|
||||
};
|
||||
|
||||
export const TestingDDRSPScorePB: PBScoreDocument<"ddr:SP"> = {
|
||||
calculatedData: {
|
||||
flareSkill: 545,
|
||||
},
|
||||
chartID: "f14bc72ad8336625ce64fc42571192e1ec168113",
|
||||
composedFrom: [
|
||||
{
|
||||
name: "Best Score",
|
||||
scoreID: "Te065000f08b49458f8b0aa3eabf0b857b79c562d7cf9eb34f6dabd7a1c3c3fa6",
|
||||
},
|
||||
],
|
||||
game: "ddr",
|
||||
highlight: false,
|
||||
isPrimary: true,
|
||||
playtype: "SP",
|
||||
rankingData: {
|
||||
rank: 1,
|
||||
outOf: 1,
|
||||
rivalRank: null,
|
||||
},
|
||||
scoreData: {
|
||||
score: 914400,
|
||||
lamp: "CLEAR",
|
||||
judgements: {},
|
||||
optional: {
|
||||
enumIndexes: {},
|
||||
},
|
||||
grade: "AA",
|
||||
enumIndexes: {
|
||||
lamp: 2,
|
||||
grade: 13,
|
||||
},
|
||||
},
|
||||
songID: 37691,
|
||||
timeAchieved: null,
|
||||
userID: 1,
|
||||
};
|
||||
|
||||
export const TestingJubeatPB: PBScoreDocument<"jubeat:Single"> = {
|
||||
chartID: "b90a319f18d1a746b330b8f4cd6f74874f664421",
|
||||
userID: 1,
|
||||
@@ -172,6 +210,39 @@ export const TestingIIDXSPScore: ScoreDocument<"iidx:SP"> = {
|
||||
importType: "ir/direct-manual",
|
||||
};
|
||||
|
||||
export const TestingDDRSPScore: ScoreDocument<"ddr:SP"> = {
|
||||
calculatedData: {
|
||||
flareSkill: 545,
|
||||
},
|
||||
chartID: "2fc2e0cfdda42addb7840b58be1df1f545310d66",
|
||||
comment: null,
|
||||
game: "ddr",
|
||||
highlight: false,
|
||||
importType: "file/batch-manual",
|
||||
isPrimary: true,
|
||||
playtype: "SP",
|
||||
scoreData: {
|
||||
score: 914400,
|
||||
lamp: "CLEAR",
|
||||
judgements: {},
|
||||
optional: {
|
||||
enumIndexes: {},
|
||||
},
|
||||
grade: "AA",
|
||||
enumIndexes: {
|
||||
lamp: 2,
|
||||
grade: 13,
|
||||
},
|
||||
},
|
||||
scoreID: "Te065000f08b49458f8b0aa3eabf0b857b79c562d7cf9eb34f6dabd7a1c3c3fa6",
|
||||
scoreMeta: {},
|
||||
service: "History (BATCH-MANUAL)",
|
||||
songID: 10,
|
||||
timeAchieved: null,
|
||||
timeAdded: 1722084133807,
|
||||
userID: 1,
|
||||
};
|
||||
|
||||
export const TestingSDVXScore: ScoreDocument<"sdvx:Single"> = {
|
||||
service: "foo (DIRECT-MANUAL)",
|
||||
game: "sdvx",
|
||||
@@ -475,6 +546,32 @@ export const Testing511Song: SongDocument<"iidx"> = {
|
||||
},
|
||||
};
|
||||
|
||||
export const TestingDDRSP: ChartDocument<"ddr:SP"> = {
|
||||
chartID: "2fc2e0cfdda42addb7840b58be1df1f545310d66",
|
||||
data: {
|
||||
inGameID: 10,
|
||||
},
|
||||
difficulty: "BASIC",
|
||||
isPrimary: true,
|
||||
level: "6",
|
||||
levelNum: 6,
|
||||
playtype: "SP",
|
||||
songID: 10,
|
||||
versions: ["a3"],
|
||||
};
|
||||
|
||||
export const TestingDDRSong: SongDocument<"ddr"> = {
|
||||
altTitles: [],
|
||||
artist: "UZI-LAY",
|
||||
data: {
|
||||
flareCategory: "CLASSIC",
|
||||
inGameID: 10,
|
||||
},
|
||||
id: 10,
|
||||
searchTerms: [],
|
||||
title: "PUT YOUR FAITH IN ME",
|
||||
};
|
||||
|
||||
export const TestingAlbidaADV: ChartDocument<"sdvx:Single"> = {
|
||||
chartID: "5088a4d0e1ee9d0cc2f625934306e45b1a60699b",
|
||||
difficulty: "ADV",
|
||||
|
||||
Reference in New Issue
Block a user