-
+
Lamps Only
-
All
-
Grades Only
diff --git a/client/src/components/tables/cells/OngekiPlatinumCell.tsx b/client/src/components/tables/cells/OngekiPlatinumCell.tsx
new file mode 100644
index 000000000..9c833cabb
--- /dev/null
+++ b/client/src/components/tables/cells/OngekiPlatinumCell.tsx
@@ -0,0 +1,34 @@
+import React from "react";
+import { Difficulties, integer } from "tachi-common";
+
+export default function OngekiPlatinumCell({
+ platScore: platScore,
+ maxPlatScore: maxPlatScore,
+ difficulty,
+}: {
+ platScore: integer | null | undefined;
+ maxPlatScore: integer;
+ difficulty: Difficulties["ongeki:Single"];
+}) {
+ if (difficulty !== "MASTER" && difficulty !== "LUNATIC") {
+ return N/A | ;
+ }
+
+ if (platScore === null || platScore === undefined) {
+ return Unknown | ;
+ }
+
+ return (
+
+ MAX-{maxPlatScore - platScore}
+ {platScore !== undefined && (
+ <>
+
+
+ [{platScore}/{maxPlatScore}]
+
+ >
+ )}
+ |
+ );
+}
diff --git a/client/src/lib/game-implementations.tsx b/client/src/lib/game-implementations.tsx
index aaa0e08f0..1b76d8076 100644
--- a/client/src/lib/game-implementations.tsx
+++ b/client/src/lib/game-implementations.tsx
@@ -20,6 +20,7 @@ import WaccaJudgementCell from "components/tables/cells/WACCAJudgementCell";
import OngekiJudgementCell from "components/tables/cells/OngekiJudgementCell";
import React from "react";
import OngekiLampCell from "components/tables/cells/OngekiLampCell";
+import OngekiPlatinumCell from "components/tables/cells/OngekiPlatinumCell";
import { CreateRatingSys, bgc } from "./games/_util";
import { BMS_14K_IMPL, BMS_7K_IMPL, PMS_IMPL } from "./games/bms-pms";
import { IIDX_DP_IMPL, IIDX_SP_IMPL } from "./games/iidx";
@@ -786,6 +787,8 @@ export const GPT_CLIENT_IMPLEMENTATIONS: GPTClientImplementations = {
(x) => x.scoreData.score * 10000 + (x.scoreData.optional.platScore ?? 0)
),
],
+ // TODO: this should be sorted by %
+ ["Platinum Score", "Score", NumericSOV((x) => x.scoreData.optional.platScore ?? 0)],
["Judgements", "Hits", NumericSOV((x) => x.scoreData.score)],
[
"Lamp",
@@ -798,22 +801,16 @@ export const GPT_CLIENT_IMPLEMENTATIONS: GPTClientImplementations = {
],
scoreCoreCells: ({ sc, chart }) => (
<>
-
- {sc.scoreData.grade}
-
- {FormatMillions(sc.scoreData.score)}
- {typeof sc.scoreData.optional.platScore === "number" &&
- (chart.difficulty === "MASTER" || chart.difficulty === "LUNATIC") && (
- <>
-
- [Plat: {sc.scoreData.optional.platScore}]
- >
- )}
- |
+
+
;
-type OngekiSong = SongDocument<"ongeki">;
-
-interface SxgaEntry {
- date: string;
- title: string;
- artist: string;
- category: string;
- lunatic: "" | "1";
-}
-
-// Wikiwiki really sucks
-const unnormalizeTitle = (title: string): string =>
- title
- .replace("...", "…")
- .replace(/:/gu, ":")
- .replace(/\(/gu, "(")
- .replace(/\)/gu, ")")
- .replace(/!/gu, "!")
- .replace(/\?/gu, "?")
- .replace(/~/gu, "~");
-
-const scrapeWikiwiki = async (
- charts: OngekiChart[],
- songID: number,
- title: string,
- lunatic: boolean,
- unnormalized = false
-) => {
- const res = await fetch(`https://wikiwiki.jp/gameongeki/${title.replace(/ /gu, "%20")}`);
- const $ = cheerio.load(await res.text());
- const tables = $("#content tbody").toArray();
- if (tables.length < 2) {
- if (unnormalized) {
- throw new Error(`${title}: Invalid wikipage: expected 2 tables, got ${tables.length}`);
- } else {
- return scrapeWikiwiki(charts, songID, unnormalizeTitle(title), lunatic, true);
- }
- }
- const diffTable = tables![1];
- const diffRows = $(diffTable).find("tr").toArray();
-
- const diffsToParse: Difficulties["ongeki:Single"][] = lunatic
- ? ["LUNATIC"]
- : ["BASIC", "ADVANCED", "EXPERT", "MASTER"];
- for (const diff of diffsToParse) {
- const row = diffRows.find((row) => $(row.firstChild!).html() === diff);
-
- const level = $(row?.children[1]).html();
- const noteCountRaw = $(row?.children[2]).html();
- const bellCountRaw = $(row?.children[3]).html();
- const internalLevelRaw = $(row?.children[4]).html();
-
- let noteCount: number | undefined;
- let bellCount: number | undefined;
- let internalLevel: number;
-
- if (!level) {
- throw new Error(`${title} ${diff}: Invalid wikipage: ${diff} has no level`);
- }
-
- if (!noteCountRaw) {
- if (diff === "MASTER" || diff === "LUNATIC") {
- // This value is necessary for platinum delta calculation
- throw new Error(`${title} ${diff}: unknown note count`);
- } else {
- console.log(`warn: ${title} ${diff}: unknown note count`);
- }
- } else {
- noteCount = parseInt(noteCountRaw, 10);
- }
- if (!bellCountRaw) {
- console.log(`warn: ${title} ${diff}: unknown bell count`);
- } else {
- bellCount = parseInt(bellCountRaw, 10);
- }
- if (!internalLevelRaw) {
- console.log(`warn: ${title} ${diff}: unknown internal level (will be deduced)`);
- internalLevel = parseInt(level, 10);
- if (level.endsWith("+")) {
- internalLevel += 0.7;
- }
- } else {
- internalLevel = parseFloat(internalLevelRaw);
- }
-
- const newChart: OngekiChart = {
- difficulty: diff,
- chartID: CreateChartID(),
- songID,
- level,
- levelNum: internalLevel,
- isPrimary: true,
- playtype: "Single",
- data: {
- displayVersion: CURRENT_VERSION_PRETTY,
- },
- versions: [CURRENT_VERSION, `${CURRENT_VERSION}Omni`],
- };
-
- charts.push(newChart);
- }
-};
-
-const main = async ({ date }) => {
- const charts: OngekiChart[] = ReadCollection("charts-ongeki.json");
- const songs: OngekiSong[] = ReadCollection("songs-ongeki.json");
-
- const sxgaEntriesRaw = await fetch(SXGA_DATA_URL);
- const sxgaEntries: SxgaEntry[] = (await sxgaEntriesRaw.json()).filter(
- (o: SxgaEntry) => o.date >= date
- );
- for (const entry of sxgaEntries) {
- const queriedDiff = entry.lunatic ? "LUNATIC" : "MASTER";
- const song = songs.find((song) => song.title === entry.title);
-
- console.log(`${entry.title} ${queriedDiff}`);
-
- if (song) {
- const chart = charts.find(
- (chart) => chart.songID === song.id && chart.difficulty === queriedDiff
- );
- if (chart) {
- console.log(`\tskipping`);
- } else {
- console.log(`\t\x1b[34mnew chart\x1b[0m`);
-
- scrapeWikiwiki(charts, song.id, entry.title, entry.lunatic ? true : false);
- }
- } else {
- console.log(`\t\x1b[31mnew song\x1b[0m`);
-
- songs.push({
- title: entry.title,
- artist: entry.artist,
- id: songs[songs.length - 1]!.id + 1,
- searchTerms: [],
- altTitles: [],
- data: {
- genre: entry.category,
- },
- });
-
- scrapeWikiwiki(
- charts,
- songs[songs.length - 1]!.id,
- entry.title,
- entry.lunatic ? true : false
- );
- }
-
- console.log("");
- }
-
- WriteCollection("songs-ongeki.json", songs);
- WriteCollection("charts-ongeki.json", charts);
-};
-
-new Command()
- .name("Fetch Ongeki")
- .description("Fetch new Ongeki charts.")
- .requiredOption("--date ", "Start date as YYYYMMDD, e.g. 20231228")
- .action(main)
- .parse();
diff --git a/docs/docs/game-support/games/ongeki-Single.md b/docs/docs/game-support/games/ongeki-Single.md
index 4311de3a6..4c5faff46 100644
--- a/docs/docs/game-support/games/ongeki-Single.md
+++ b/docs/docs/game-support/games/ongeki-Single.md
@@ -85,7 +85,6 @@ Note: since bright MEMORY Act.3, "crazy" charts and "Re:MASTER" charts are in se
| ID | Pretty Name |
| :: | :: |
-| `brightMemory2` | bright MEMORY Act.2 |
| `brightMemory3` | bright MEMORY Act.3 |
## Supported Match Types
diff --git a/server/src/game-implementations/games/ongeki.ts b/server/src/game-implementations/games/ongeki.ts
index 256861971..3f14fe5d1 100644
--- a/server/src/game-implementations/games/ongeki.ts
+++ b/server/src/game-implementations/games/ongeki.ts
@@ -7,23 +7,20 @@ import { ONGEKIRating } from "rg-stats";
import { ONGEKI_GBOUNDARIES, FmtNum, GetGrade } from "tachi-common";
import { IsNullish } from "utils/misc";
import type { GPTServerImplementation } from "game-implementations/types";
-import type { Difficulties } from "tachi-common";
-export const OngekiPlatDiff = (diff: Difficulties["ongeki:Single"]) =>
- diff === "MASTER" || diff === "LUNATIC";
+const isBonusTrack = (inGameID: number) => inGameID >= 7000 && inGameID < 8000;
export const ONGEKI_IMPL: GPTServerImplementation<"ongeki:Single"> = {
chartSpecificValidators: {
platScore: (platScore, chart) => {
- if (!OngekiPlatDiff(chart.difficulty)) {
- // We don't care about other difficulties
- return true;
- }
-
if (platScore < 0) {
return `Platinum Score must be non-negative. Got ${platScore}`;
}
+ if (platScore > chart.data.maxPlatScore) {
+ return `Platinum Score is too large: ${platScore}>${chart.data.maxPlatScore}`;
+ }
+
return true;
},
bellCount: (bellCount) => {
@@ -53,7 +50,7 @@ export const ONGEKI_IMPL: GPTServerImplementation<"ongeki:Single"> = {
},
scoreCalcs: {
rating: (scoreData, chart) =>
- chart.data.isUnranked || chart.levelNum === 0.0
+ isBonusTrack(chart.data.inGameID) || chart.levelNum === 0.0
? 0
: ONGEKIRating.calculate(scoreData.score, chart.levelNum),
},
diff --git a/server/src/test-utils/test-data.ts b/server/src/test-utils/test-data.ts
index 6326b8938..e8e269de6 100644
--- a/server/src/test-utils/test-data.ts
+++ b/server/src/test-utils/test-data.ts
@@ -1269,6 +1269,8 @@ export const TestingOngekiChart: ChartDocument<"ongeki:Single"> = {
chartID: "213796bdb6150f80ba6412ce69df1249e16c0cb0",
data: {
displayVersion: "bright MEMORY Act.3",
+ inGameID: 2137,
+ maxPlatScore: 1000,
},
difficulty: "MASTER",
isPrimary: true,