diff --git a/client/src/components/tables/cells/BPICell.tsx b/client/src/components/tables/cells/BPICell.tsx
index 4a9b97684..f873a516c 100644
--- a/client/src/components/tables/cells/BPICell.tsx
+++ b/client/src/components/tables/cells/BPICell.tsx
@@ -2,8 +2,7 @@ import QuickTooltip from "components/layout/misc/QuickTooltip";
import Divider from "components/util/Divider";
import Muted from "components/util/Muted";
import useUGPTSettings from "components/util/useUGPTSettings";
-import { UGPTSettingsContext } from "context/UGPTSettingsContext";
-import React, { useContext, useState } from "react";
+import React from "react";
import { PoyashiBPI } from "rg-stats";
import { ChartDocument, integer, PBScoreDocument, ScoreDocument } from "tachi-common";
import { GetGradeFromPercent, IsNullish } from "util/misc";
@@ -73,7 +72,7 @@ export default function BPICell({
return
N/A | ;
}
- const bpiTarget = settings.preferences.gameSpecific.bpiTarget;
+ const bpiTarget = settings?.preferences.gameSpecific.bpiTarget ?? 0;
let bpiTargetScore = 0;
let targetDelta: number | null = 0;
diff --git a/client/src/components/tables/cells/VF6Cell.tsx b/client/src/components/tables/cells/VF6Cell.tsx
new file mode 100644
index 000000000..823bfed9b
--- /dev/null
+++ b/client/src/components/tables/cells/VF6Cell.tsx
@@ -0,0 +1,148 @@
+import QuickTooltip from "components/layout/misc/QuickTooltip";
+import useUGPTSettings from "components/util/useUGPTSettings";
+import React from "react";
+import { Volforce } from "rg-stats";
+import { ChartDocument, GetGamePTConfig, PBScoreDocument, ScoreDocument } from "tachi-common";
+import { IsNullish } from "util/misc";
+import MiniTable from "../components/MiniTable";
+
+type VF6IDStrings = "sdvx:Single" | "usc:Keyboard" | "usc:Controller";
+
+const SHORT_LAMPS = {
+ CLEAR: "CLR",
+ "EXCESSIVE CLEAR": "EXC",
+ "ULTIMATE CHAIN": "UC",
+} as const;
+
+export default function VF6Cell({
+ score,
+ chart,
+}: {
+ score: ScoreDocument | PBScoreDocument;
+ chart: ChartDocument;
+}) {
+ const { settings } = useUGPTSettings();
+
+ if (IsNullish(score.calculatedData.VF6)) {
+ return N/A | ;
+ }
+
+ const vf6Target = settings?.preferences.gameSpecific.vf6Target ?? 0.3;
+
+ const gptConfig = GetGamePTConfig(score.game, score.playtype);
+
+ const targets: Record = {};
+
+ if (vf6Target) {
+ for (const lamp of ["CLEAR", "EXCESSIVE CLEAR", "ULTIMATE CHAIN"] as const) {
+ if (score.scoreData.lampIndex <= gptConfig.lamps.indexOf(lamp)) {
+ const expectedScore = InverseVF6(vf6Target, lamp, chart.levelNum);
+
+ if (expectedScore === null) {
+ targets[SHORT_LAMPS[lamp]] = null;
+ } else {
+ targets[SHORT_LAMPS[lamp]] = expectedScore - score.scoreData.score;
+ break;
+ }
+ }
+ }
+ }
+
+ console.log(targets);
+
+ return (
+ <>
+
+
+
+
+
+ >
+ }
+ >
+
+ {score.calculatedData.VF6}
+
+
+
+ {score.calculatedData.VF6! >= vf6Target ? (
+ {vf6Target}VF Target Achieved!
+ ) : Object.values(targets).every(k => k === null) ? (
+ {vf6Target}VF Not Possible
+ ) : (
+ Object.entries(targets).map(([k, v], i) => (
+
+
+
+
+ ))
+ )}
+
+ |
+
+ >
+ );
+}
+
+function InverseVF6(
+ vf6: number,
+ lamp: "CLEAR" | "EXCESSIVE CLEAR" | "ULTIMATE CHAIN",
+ level: number
+) {
+ try {
+ return Volforce.inverseVF6(vf6, lamp, level);
+ } catch (err) {
+ return null;
+ }
+}
+
+function VF6TargetCell({
+ targetDelta,
+ vf6Target,
+ clearType,
+ showClearType,
+}: {
+ targetDelta: number | null;
+ vf6Target: number;
+ clearType: string;
+ showClearType: boolean;
+}) {
+ if (targetDelta === null) {
+ return (
+
+ {vf6Target}VF w/ {clearType}: Not Possible
+
+ );
+ }
+
+ if (targetDelta < 0) {
+ return (
+
+ Score is {vf6Target}VF w/ {clearType}
+
+ );
+ }
+
+ const div = targetDelta / 1_000;
+
+ let fmt;
+ if (div >= 1_000) {
+ fmt = `${(div / 1_000).toFixed(3)}m`;
+ } else {
+ fmt = `${div.toFixed(2)}k`;
+ }
+
+ return (
+
+ {vf6Target}VF{showClearType ? ` w/ ${clearType}` : ""}: +{fmt}
+
+ );
+}
diff --git a/client/src/components/tables/game-core-cells/SDVXCoreCells.tsx b/client/src/components/tables/game-core-cells/SDVXCoreCells.tsx
index 381d36a7e..33fe865b3 100644
--- a/client/src/components/tables/game-core-cells/SDVXCoreCells.tsx
+++ b/client/src/components/tables/game-core-cells/SDVXCoreCells.tsx
@@ -1,20 +1,24 @@
import React from "react";
-import { PBScoreDocument, ScoreDocument } from "tachi-common";
+import { ChartDocument, PBScoreDocument, ScoreDocument } from "tachi-common";
import { IsNullish } from "util/misc";
import LampCell from "../cells/LampCell";
import MillionsScoreCell from "../cells/MillionsScoreCell";
import SDVXJudgementCell from "../cells/SDVXJudgementCell";
+import VF6Cell from "../cells/VF6Cell";
export default function SDVXScoreCoreCells({
sc,
+ chart,
}: {
sc: ScoreDocument<"sdvx:Single"> | PBScoreDocument<"sdvx:Single">;
+ chart: ChartDocument<"sdvx:Single" | "usc:Controller" | "usc:Keyboard">;
}) {
return (
<>
+
{!IsNullish(sc.calculatedData.VF6) ? sc.calculatedData.VF6!.toFixed(3) : "N/A"} |
>
);
diff --git a/client/src/components/tables/game-core-cells/ScoreCoreCells.tsx b/client/src/components/tables/game-core-cells/ScoreCoreCells.tsx
index b448f27e8..8f5c1dd68 100644
--- a/client/src/components/tables/game-core-cells/ScoreCoreCells.tsx
+++ b/client/src/components/tables/game-core-cells/ScoreCoreCells.tsx
@@ -40,7 +40,12 @@ export default function ScoreCoreCells({
} else if (game === "bms") {
return ;
} else if (game === "sdvx" || game === "usc") {
- return ;
+ return (
+ }
+ />
+ );
} else if (game === "maimai") {
return ;
} else if (game === "museca") {
diff --git a/client/src/components/util/useUGPTSettings.tsx b/client/src/components/util/useUGPTSettings.tsx
index 18cb04573..c62002d58 100644
--- a/client/src/components/util/useUGPTSettings.tsx
+++ b/client/src/components/util/useUGPTSettings.tsx
@@ -4,7 +4,8 @@ import { IDStrings, UGPTSettings } from "tachi-common";
import { SetState } from "types/react";
export default function useUGPTSettings() {
- return useContext<{ settings: UGPTSettings; setSettings: SetState> }>(
- UGPTSettingsContext as any
- );
+ return useContext<{
+ settings: UGPTSettings | null;
+ setSettings: SetState | null>;
+ }>(UGPTSettingsContext as any);
}