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); }