diff --git a/client/package.json b/client/package.json index 3a8ed93c7..f9f7002bf 100644 --- a/client/package.json +++ b/client/package.json @@ -75,7 +75,7 @@ "resolve": "1.12.0", "resolve-url-loader": "3.1.0", "socicon": "3.0.5", - "tachi-common": "0.2.12" + "tachi-common": "0.2.15" }, "devDependencies": { "@types/humanize-duration": "3.25.1", diff --git a/client/pnpm-lock.yaml b/client/pnpm-lock.yaml index 4f279ba86..bc8896253 100644 --- a/client/pnpm-lock.yaml +++ b/client/pnpm-lock.yaml @@ -77,7 +77,7 @@ specifiers: serve: 11.2.0 socicon: 3.0.5 style-loader: 1.0.0 - tachi-common: 0.2.12 + tachi-common: 0.2.15 terser-webpack-plugin: 1.4.1 ts-pnp: 1.1.4 typescript: 4.3.5 @@ -142,7 +142,7 @@ dependencies: resolve: 1.12.0 resolve-url-loader: 3.1.0 socicon: 3.0.5 - tachi-common: 0.2.12 + tachi-common: 0.2.15 devDependencies: '@types/humanize-duration': 3.25.1 @@ -11178,8 +11178,8 @@ packages: strip-ansi: 6.0.0 dev: true - /tachi-common/0.2.12: - resolution: {integrity: sha512-xwbQUZefLUs1sus3nrrys84qJyRMWLl/Mb4UzFYd74n0ZmOejrxpwIb18ILWww4J/IxQurPd4Id+DCP12SEW1w==} + /tachi-common/0.2.15: + resolution: {integrity: sha512-p7nt7pupl2JWNb20K7n18YOBtxUYeoyDsE6YbPVVyPvY1oj74a2YxALpmajGV2pbVAkdywrJS94In2sQUGzJtw==} dependencies: monk: 7.3.4 transitivePeerDependencies: diff --git a/client/src/app/pages/dashboard/users/games/_game/_playtype/folders/SpecificFolderPage.tsx b/client/src/app/pages/dashboard/users/games/_game/_playtype/folders/SpecificFolderPage.tsx index d7a7cb229..049d72ac7 100644 --- a/client/src/app/pages/dashboard/users/games/_game/_playtype/folders/SpecificFolderPage.tsx +++ b/client/src/app/pages/dashboard/users/games/_game/_playtype/folders/SpecificFolderPage.tsx @@ -1,22 +1,38 @@ import { ResponsiveBar } from "@nivo/bar"; import { BarChartTooltip } from "components/charts/ChartTooltip"; +import QuickTooltip from "components/layout/misc/QuickTooltip"; import Card from "components/layout/page/Card"; import FolderTable from "components/tables/folders/FolderTable"; import ApiError from "components/util/ApiError"; import Divider from "components/util/Divider"; +import Icon from "components/util/Icon"; import Loading from "components/util/Loading"; +import Muted from "components/util/Muted"; import useApiQuery from "components/util/query/useApiQuery"; -import React, { useMemo } from "react"; +import SelectButton from "components/util/SelectButton"; +import { useFormik } from "formik"; +import React, { useMemo, useState } from "react"; +import { Col, Form, Row } from "react-bootstrap"; import { useParams } from "react-router-dom"; -import { Game, GetGamePTConfig, PublicUserDocument } from "tachi-common"; +import { + ChartTierlistInfo, + FormatDifficultyShort, + Game, + GetGamePTConfig, + GPTTierlists, + IDStrings, + PublicUserDocument, +} from "tachi-common"; import { UGPTFolderReturns } from "types/api-returns"; import { FolderDataset } from "types/tables"; import { Playtype } from "types/tachi"; import { DEFAULT_BAR_PROPS } from "util/charts"; import { ChangeOpacity } from "util/color-opacity"; import { CreateChartIDMap, CreateSongMap } from "util/data"; -import { ComposeExpFn, ComposeInverseExpFn } from "util/misc"; +import { ComposeExpFn, ComposeInverseExpFn, IsNullish, NO_OP } from "util/misc"; +import { GetGradeChartExpScale } from "util/scales"; import { NumericSOV, StrSOV } from "util/sorts"; +import { GetScaleAchievedFn } from "util/tierlist"; interface Props { reqUser: PublicUserDocument; @@ -31,6 +47,10 @@ export default function SpecificFolderPage({ reqUser, game, playtype }: Props) { `/users/${reqUser.id}/games/${game}/${playtype}/folders/${folderID}` ); + const [mode, setMode] = useState<"normal" | "ladder">("normal"); + + const gptConfig = GetGamePTConfig(game, playtype); + const folderDataset = useMemo(() => { if (!data) { return null; @@ -56,6 +76,22 @@ export default function SpecificFolderPage({ reqUser, game, playtype }: Props) { return folderDataset; }, [data]); + const folderInfoHeader = useMemo(() => { + if (!folderDataset || !data) { + return ; + } + + return ( + + ); + }, [folderDataset]); + if (isLoading || !data || !folderDataset) { return ; } @@ -69,35 +105,326 @@ export default function SpecificFolderPage({ reqUser, game, playtype }: Props) {
+
{folderInfoHeader}
- + +
+
+
+ + + Normal View + + {gptConfig.tierlists.length && ( + + + Tierlist View + + )} +
- + {mode === "normal" ? ( + + ) : ( + + )}
); } -function FolderInfoHeader({ +type InfoProps = Props & { + folderDataset: FolderDataset; + data: UGPTFolderReturns; +}; + +function TierlistBreakdown({ game, folderDataset, playtype, reqUser }: InfoProps) { + const gptConfig = GetGamePTConfig(game, playtype); + + const formik = useFormik({ + initialValues: { + __hideAchieved: false, + ...Object.fromEntries(gptConfig.tierlists.map(e => [e, true])), + } as { __hideAchieved: boolean } & Partial>, + onSubmit: NO_OP, + }); + + const tierlistInfo = FolderDatasetToTierlistInfo(folderDataset, game, playtype, formik.values); + + const dataMap = CreateChartIDMap(folderDataset); + + return ( + + + + Here you can select what tierlist blocks to show! + + {gptConfig.tierlists.length > 1 && + gptConfig.tierlists.map(e => ( + <> + + {gptConfig.tierlistDescriptions[e]} + + ))} + + + Hide achieved elements of the tierlist. + + + + + + + + + + ); +} + +function TierlistInfoLadder({ + tierlistInfo, + dataMap, game, playtype, - reqUser, - folderDataset, - data, -}: Props & { folderDataset: FolderDataset; data: UGPTFolderReturns }) { +}: { + tierlistInfo: TierlistInfo[]; + dataMap: Map; + game: Game; + playtype: Playtype; +}) { + const buckets: TierlistInfo[][] = useMemo(() => { + const buckets: TierlistInfo[][] = []; + let currentBucket: TierlistInfo[] = []; + + let lastValue; + for (const tl of tierlistInfo) { + if (lastValue && tl.data.value !== lastValue) { + buckets.push(currentBucket); + currentBucket = [tl]; + } else { + currentBucket.push(tl); + } + lastValue = tl.data.value; + } + + if (currentBucket.length) { + buckets.push(currentBucket); + } + + return buckets; + }, tierlistInfo); + + const gptConfig = GetGamePTConfig(game, playtype); + + for (const bucket of buckets) { + bucket.sort(NumericSOV(x => gptConfig.tierlists.indexOf(x.key), true)); + } + + if (tierlistInfo.length === 0) { + return Got no tierlist data to show you!; + } + + return ( + + {buckets.map((bucket, i) => ( + + + {bucket[0].data.value} + + + {bucket.map((tierlistInfo, i) => { + const data = dataMap.get(tierlistInfo.chartID)!; + + const lastKey = bucket[i - 1]; + + let statusClass; + + switch (tierlistInfo.achieved) { + case AchievedStatuses.ACHIEVED: + statusClass = "achieved"; + break; + case AchievedStatuses.FAILED: + statusClass = "unachieved"; + break; + case AchievedStatuses.NOT_PLAYED: + case AchievedStatuses.SCORE_BASED: + statusClass = ""; + } + + return ( + <> + {lastKey && lastKey.key !== tierlistInfo.key && ( + + )} + + {data.__related.song.title} {FormatDifficultyShort(data, game)} + + {tierlistInfo.key}{" "} + {tierlistInfo.data.individualDifference && ( + <> +
+ +
+ + + + + +
+ + )} + + + {tierlistInfo.achieved === AchievedStatuses.NOT_PLAYED + ? "Not Played" + : tierlistInfo.achieved === AchievedStatuses.SCORE_BASED + ? `You have: ${data.__related.pb!.scoreData.grade}` + : `You have: ${data.__related.pb!.scoreData.lamp}`} + + + + ); + })} +
+ ))} +
+ ); +} + +interface TierlistInfo { + chartID: string; + key: GPTTierlists[IDStrings]; + data: ChartTierlistInfo; + achieved: AchievedStatuses; +} + +enum AchievedStatuses { + NOT_PLAYED, + FAILED, + ACHIEVED, + SCORE_BASED, +} + +function FolderDatasetToTierlistInfo( + folderDataset: FolderDataset, + game: Game, + playtype: Playtype, + options: Partial> & { __hideAchieved: boolean } +) { + const tierlistInfo: TierlistInfo[] = []; + + const tierlistKeys: GPTTierlists[IDStrings][] = []; + + for (const k in options) { + const key = k as GPTTierlists[IDStrings]; + if (options[key]) { + tierlistKeys.push(key); + } + } + + for (const data of folderDataset) { + for (const key of tierlistKeys) { + if (IsNullish(data.tierlistInfo[key])) { + continue; + } + + let achieved: AchievedStatuses; + + if (!data.__related.pb) { + achieved = AchievedStatuses.NOT_PLAYED; + } else { + const fn = GetScaleAchievedFn(game, playtype, key); + + if (fn) { + achieved = fn(data.__related.pb) + ? AchievedStatuses.ACHIEVED + : AchievedStatuses.FAILED; + } else { + achieved = AchievedStatuses.SCORE_BASED; + } + } + + if (options.__hideAchieved && achieved === AchievedStatuses.ACHIEVED) { + continue; + } + + tierlistInfo.push({ + chartID: data.chartID, + key, + data: data.tierlistInfo[key]!, + achieved, + }); + } + } + + return tierlistInfo.sort(NumericSOV(x => x.data.value, true)); +} + +function FolderInfoHeader({ game, playtype, reqUser, folderDataset, data }: InfoProps) { + return ( + + + + ); +} + +function ScoreDistributionChart({ game, playtype, reqUser, folderDataset, data }: InfoProps) { + const dataMap = CreateChartIDMap(folderDataset); + const gptConfig = GetGamePTConfig(game, playtype); + const dataset = []; - const expScale = 1; + const expScale = GetGradeChartExpScale(game); const expFn = ComposeExpFn(expScale); const invExpFn = ComposeInverseExpFn(expScale); @@ -114,70 +441,69 @@ function FolderInfoHeader({ dataset.sort(NumericSOV(x => x.expValue)); - const dataMap = CreateChartIDMap(folderDataset); - - const gptConfig = GetGamePTConfig(game, playtype); - return ( - -
- ( - { - const data = dataMap.get(d.indexValue as string)!; +
+ ( + { + const data = dataMap.get(d.indexValue as string)!; - return ( -
- {data.__related.song.title} -
- {data.__related.pb?.scoreData.percent.toFixed(2)}% -
- ); - }} - /> - )} - key={"value"} + return ( +
+ {data.__related.song.title} +
+ {data.__related.pb?.scoreData.percent.toFixed(2)}% +
+ ); + }} + /> + )} + key={"value"} + colors={k => { + if (!k.data.grade) { + return "black"; + } // @ts-expect-error temp + return ChangeOpacity(gptConfig.gradeColours[k.data.grade], 0.5); + }} + // @ts-expect-error temp + borderColor={k => gptConfig.gradeColours[k.data.grade]} + borderWidth={1} + padding={0.2} + // @ts-expect-error temp + data={dataset} + minValue={0} + maxValue={expFn(100)} + margin={{ left: 50, top: 20, bottom: 20 }} + valueFormat={e => `${invExpFn(e).toFixed(2)}%`} + axisLeft={{ + tickValues: gptConfig.gradeBoundaries.map(e => (e === 0 ? 0 : expFn(e))), + format: x => { + let nearest; - colors={k => ChangeOpacity(gptConfig.gradeColours[k.data.grade], 0.5)} - // @ts-expect-error temp + const lgv = invExpFn(x); - borderColor={k => gptConfig.gradeColours[k.data.grade]} - borderWidth={1} - padding={0.2} - // @ts-expect-error temp - data={dataset} - minValue={0} - maxValue={expFn(100)} - margin={{ left: 50, top: 20, bottom: 20 }} - axisLeft={{ - tickValues: gptConfig.gradeBoundaries.map(e => (e === 0 ? 0 : expFn(e))), - format: x => { - let nearest; - - const lgv = invExpFn(x); - - for (const [i, gradeBnd] of gptConfig.gradeBoundaries.entries()) { - if (Math.abs(gradeBnd - lgv) < 0.00005) { - nearest = i; - break; - } + for (const [i, gradeBnd] of gptConfig.gradeBoundaries.entries()) { + if (Math.abs(gradeBnd - lgv) < 0.00005) { + nearest = i; + break; } + } - if (nearest === undefined) { - return null; - } + if (nearest === undefined) { + return null; + } - return gptConfig.grades[nearest]; - }, - }} - axisBottom={null} - {...DEFAULT_BAR_PROPS} - /> -
- + return gptConfig.grades[nearest]; + }, + }} + axisBottom={null} + {...DEFAULT_BAR_PROPS} + labelSkipWidth={40} + /> +
); } diff --git a/client/src/components/layout/header/SearchBar.tsx b/client/src/components/layout/header/SearchBar.tsx index 95bea41bf..8cd1817e7 100644 --- a/client/src/components/layout/header/SearchBar.tsx +++ b/client/src/components/layout/header/SearchBar.tsx @@ -68,7 +68,9 @@ function GPTSearchResult({ playtype )} profile, if they have one.`} > - + + + diff --git a/client/src/components/tables/cells/BMSDifficultyCell.tsx b/client/src/components/tables/cells/BMSDifficultyCell.tsx index 988028bdb..d115dc4a8 100644 --- a/client/src/components/tables/cells/BMSDifficultyCell.tsx +++ b/client/src/components/tables/cells/BMSDifficultyCell.tsx @@ -21,7 +21,7 @@ export default function BMSDifficultyCell({ }} > {levelText} - + {!chart.isPrimary && (
diff --git a/client/src/components/tables/cells/DifficultyCell.tsx b/client/src/components/tables/cells/DifficultyCell.tsx index f68e7390e..c122323b7 100644 --- a/client/src/components/tables/cells/DifficultyCell.tsx +++ b/client/src/components/tables/cells/DifficultyCell.tsx @@ -30,16 +30,14 @@ export default function DifficultyCell({ }} > {!alwaysShort && ( - <> - {FormatDifficulty(chart, game)} - {Object.keys(chart.tierlistInfo).length > 0 && ( - - )} - + {FormatDifficulty(chart, game)} )} {FormatDifficultyShort(chart, game)} + {Object.keys(chart.tierlistInfo).length > 0 && ( + + )} {!chart.isPrimary && (
diff --git a/client/src/components/tables/cells/TierlistInfoPart.tsx b/client/src/components/tables/cells/TierlistInfoPart.tsx index 4689bd1a6..c44d9bc17 100644 --- a/client/src/components/tables/cells/TierlistInfoPart.tsx +++ b/client/src/components/tables/cells/TierlistInfoPart.tsx @@ -2,47 +2,61 @@ import QuickTooltip from "components/layout/misc/QuickTooltip"; import Icon from "components/util/Icon"; import Muted from "components/util/Muted"; import React from "react"; -import { ChartDocument } from "tachi-common"; +import { ChartDocument, Game, GetGamePTConfig } from "tachi-common"; import MiniTable from "../components/MiniTable"; // I hate the fact object.keys doesnt infer types. type K = keyof ChartDocument["tierlistInfo"]; -export default function TierlistInfoPart({ chart }: { chart: ChartDocument }) { - const keys = Object.keys(chart.tierlistInfo) as K[]; +export default function TierlistInfoPart({ chart, game }: { chart: ChartDocument; game: Game }) { + const keys = GetGamePTConfig(game, chart.playtype).tierlists.filter(e => chart.tierlistInfo[e]); return ( - - - - Tierlist - Value - - - - {keys.map(k => ( - - {k} - - {chart.tierlistInfo[k]!.text} ({chart.tierlistInfo[k]!.value}) - {chart.tierlistInfo[k]!.individualDifference && ( - <> -
- - - )} - - - ))} - - - } - > -
- {keys.map(k => chart.tierlistInfo[k]!.text).join(" / ")} + <> +
+ + + + Tierlist + Value + + + + {keys.map(k => ( + + {k} + + {chart.tierlistInfo[k]!.text} ( + {chart.tierlistInfo[k]!.value}) + {chart.tierlistInfo[k]!.individualDifference && ( + <> +
+ + + )} + + + ))} + + + } + > +
+ {keys.map(k => chart.tierlistInfo[k]!.text).join(" / ")} +
+
- +
+ + {keys.map(k => ( + <> + {chart.tierlistInfo[k]!.text}
+ + ))} +
+
+ ); } diff --git a/client/src/components/tables/components/FilterDirectivesIndicator.tsx b/client/src/components/tables/components/FilterDirectivesIndicator.tsx index aba5df0a4..3a3dc5de9 100644 --- a/client/src/components/tables/components/FilterDirectivesIndicator.tsx +++ b/client/src/components/tables/components/FilterDirectivesIndicator.tsx @@ -56,7 +56,10 @@ export default function FilterDirectivesIndicator({ component={ <> You can use{" "} - + Filter Directives {" "} inside this filter box. @@ -79,7 +82,10 @@ export default function FilterDirectivesIndicator({

This is a slightly advanced feature for{" "} power users. You can read about it{" "} - here.
+ + here + + .
The quick explanation is that you can use directives like{" "} Title:conflict Score:>=100 to perform advanced filters!

diff --git a/client/src/components/tables/folders/BMSFolderTable.tsx b/client/src/components/tables/folders/BMSFolderTable.tsx index 6366b6179..8915eb053 100644 --- a/client/src/components/tables/folders/BMSFolderTable.tsx +++ b/client/src/components/tables/folders/BMSFolderTable.tsx @@ -31,7 +31,7 @@ export default function BMSFolderTable({ const headers: Header[0]>[] = [ [ "Chart", - "Ch.", + "Chart", NumericSOV( x => x.tierlistInfo["sgl-EC"]?.value ?? x.tierlistInfo["sgl-HC"]?.value ?? x.levelNum diff --git a/client/src/components/tables/folders/GenericFolderTable.tsx b/client/src/components/tables/folders/GenericFolderTable.tsx index 9aa80d8ef..231db1d03 100644 --- a/client/src/components/tables/folders/GenericFolderTable.tsx +++ b/client/src/components/tables/folders/GenericFolderTable.tsx @@ -35,7 +35,7 @@ export default function GenericFolderTable({ const gptConfig = GetGamePTConfig(game, playtype); const headers: Header[] = [ - ["Chart", "Ch.", NumericSOV(x => x.levelNum)], + ["Chart", "Chart", NumericSOV(x => x.levelNum)], FolderIndicatorHeader, ["Song", "Song", StrSOV(x => x.__related.song.title)], ["Score", "Score", NumericSOV(x => x.__related.pb?.scoreData.percent ?? -Infinity)], diff --git a/client/src/components/tables/folders/IIDXFolderTable.tsx b/client/src/components/tables/folders/IIDXFolderTable.tsx index c5dc9bd76..a109dfb56 100644 --- a/client/src/components/tables/folders/IIDXFolderTable.tsx +++ b/client/src/components/tables/folders/IIDXFolderTable.tsx @@ -39,7 +39,7 @@ export default function IIDXFolderTable({ const headers: Header[0]>[] = [ [ "Chart", - "Ch.", + "Chart", NumericSOV( x => x.tierlistInfo["kt-NC"]?.value ?? x.tierlistInfo["kt-HC"]?.value ?? x.levelNum ), diff --git a/client/src/components/tables/folders/MusecaFolderTable.tsx b/client/src/components/tables/folders/MusecaFolderTable.tsx index 9dfaafed6..e64ed2624 100644 --- a/client/src/components/tables/folders/MusecaFolderTable.tsx +++ b/client/src/components/tables/folders/MusecaFolderTable.tsx @@ -31,7 +31,7 @@ export default function MusecaFolderTable({ playtype: Playtype; }) { const headers: Header[0]>[] = [ - ["Chart", "Ch.", NumericSOV(x => x.levelNum)], + ["Chart", "Chart", NumericSOV(x => x.levelNum)], FolderIndicatorHeader, ["Song", "Song", StrSOV(x => x.__related.song.title)], ["Score", "Score", NumericSOV(x => x.__related.pb?.scoreData.percent ?? -Infinity)], diff --git a/client/src/components/tables/folders/SDVXFolderTable.tsx b/client/src/components/tables/folders/SDVXFolderTable.tsx index 7c1765691..e4c8d458b 100644 --- a/client/src/components/tables/folders/SDVXFolderTable.tsx +++ b/client/src/components/tables/folders/SDVXFolderTable.tsx @@ -30,7 +30,7 @@ export default function SDVXFolderTable({ playtype: Playtype; }) { const headers: Header[0]>[] = [ - ["Chart", "Ch.", NumericSOV(x => x.levelNum)], + ["Chart", "Chart", NumericSOV(x => x.levelNum)], FolderIndicatorHeader, ["Song", "Song", StrSOV(x => x.__related.song.title)], ["Score", "Score", NumericSOV(x => x.__related.pb?.scoreData.percent ?? -Infinity)], diff --git a/client/src/components/tables/pbs/BMSPBTable.tsx b/client/src/components/tables/pbs/BMSPBTable.tsx index 094bb5608..813c7e28f 100644 --- a/client/src/components/tables/pbs/BMSPBTable.tsx +++ b/client/src/components/tables/pbs/BMSPBTable.tsx @@ -48,7 +48,7 @@ export default function BMSPBTable({ const headers: Header[0]>[] = [ [ "Chart", - "Ch.", + "Chart", NumericSOV( x => x.__related.chart.tierlistInfo["sgl-EC"]?.value ?? diff --git a/client/src/components/tables/pbs/GenericPBTable.tsx b/client/src/components/tables/pbs/GenericPBTable.tsx index 522dfe368..a9cae2516 100644 --- a/client/src/components/tables/pbs/GenericPBTable.tsx +++ b/client/src/components/tables/pbs/GenericPBTable.tsx @@ -40,7 +40,7 @@ export default function GenericPBTable({ const gptConfig = GetGamePTConfig(game, playtype); const headers: Header[] = [ - ["Chart", "Ch.", NumericSOV(x => x.__related.chart.levelNum)], + ["Chart", "Chart", NumericSOV(x => x.__related.chart.levelNum)], IndicatorHeader, ["Song", "Song", StrSOV(x => x.__related.song.title)], ["Score", "Score", NumericSOV(x => x.scoreData.percent)], diff --git a/client/src/components/tables/pbs/IIDXPBTable.tsx b/client/src/components/tables/pbs/IIDXPBTable.tsx index fcf856faa..6f04da17c 100644 --- a/client/src/components/tables/pbs/IIDXPBTable.tsx +++ b/client/src/components/tables/pbs/IIDXPBTable.tsx @@ -48,7 +48,7 @@ export default function IIDXPBTable({ const headers: Header[0]>[] = [ [ "Chart", - "Ch.", + "Chart", NumericSOV( x => x.__related.chart.tierlistInfo["kt-NC"]?.value ?? diff --git a/client/src/components/tables/pbs/MusecaPBTable.tsx b/client/src/components/tables/pbs/MusecaPBTable.tsx index 19f64330b..0e7fb6af9 100644 --- a/client/src/components/tables/pbs/MusecaPBTable.tsx +++ b/client/src/components/tables/pbs/MusecaPBTable.tsx @@ -34,7 +34,7 @@ export default function MusecaPBTable({ playtype: "Single"; }) { const headers: Header[0]>[] = [ - ["Chart", "Ch.", NumericSOV(x => x.__related.chart.levelNum)], + ["Chart", "Chart", NumericSOV(x => x.__related.chart.levelNum)], IndicatorHeader, ["Song", "Song", StrSOV(x => x.__related.song.title)], ["Score", "Score", NumericSOV(x => x.scoreData.percent)], diff --git a/client/src/components/tables/pbs/SDVXPBTable.tsx b/client/src/components/tables/pbs/SDVXPBTable.tsx index b54ec7307..aad0faa92 100644 --- a/client/src/components/tables/pbs/SDVXPBTable.tsx +++ b/client/src/components/tables/pbs/SDVXPBTable.tsx @@ -33,7 +33,7 @@ export default function SDVXPBTable({ playtype: "7K" | "14K"; }) { const headers: Header[0]>[] = [ - ["Chart", "Ch.", NumericSOV(x => x.__related.chart.levelNum)], + ["Chart", "Chart", NumericSOV(x => x.__related.chart.levelNum)], IndicatorHeader, ["Song", "Song", StrSOV(x => x.__related.song.title)], ["Score", "Score", NumericSOV(x => x.scoreData.percent)], diff --git a/client/src/components/tables/scores/BMSScoreTable.tsx b/client/src/components/tables/scores/BMSScoreTable.tsx index 1d2099298..eae13e668 100644 --- a/client/src/components/tables/scores/BMSScoreTable.tsx +++ b/client/src/components/tables/scores/BMSScoreTable.tsx @@ -34,7 +34,7 @@ export default function BMSScoreTable({ [ [ "Chart", - "Ch.", + "Chart", NumericSOV( x => x.__related.chart.tierlistInfo["sgl-EC"]?.value ?? diff --git a/client/src/components/tables/scores/GenericScoreTable.tsx b/client/src/components/tables/scores/GenericScoreTable.tsx index c6f25635c..af1eee4eb 100644 --- a/client/src/components/tables/scores/GenericScoreTable.tsx +++ b/client/src/components/tables/scores/GenericScoreTable.tsx @@ -36,7 +36,7 @@ export default function GenericScoreTable({ dataset={dataset} pageLen={pageLen} headers={[ - ["Chart", "Ch.", NumericSOV(x => x.__related.chart.levelNum)], + ["Chart", "Chart", NumericSOV(x => x.__related.chart.levelNum)], IndicatorHeader, ["Song", "Song", StrSOV(x => x.__related.song.title)], ["Score", "Score", NumericSOV(x => x.scoreData.percent)], diff --git a/client/src/components/tables/scores/IIDXScoreTable.tsx b/client/src/components/tables/scores/IIDXScoreTable.tsx index 09e009d29..e36aee557 100644 --- a/client/src/components/tables/scores/IIDXScoreTable.tsx +++ b/client/src/components/tables/scores/IIDXScoreTable.tsx @@ -42,7 +42,7 @@ export default function IIDXScoreTable({ [ [ "Chart", - "Ch.", + "Chart", NumericSOV( x => x.__related.chart.tierlistInfo["kt-NC"]?.value ?? diff --git a/client/src/components/tables/scores/MusecaScoreTable.tsx b/client/src/components/tables/scores/MusecaScoreTable.tsx index 0b6b70e8f..4cd7a98e0 100644 --- a/client/src/components/tables/scores/MusecaScoreTable.tsx +++ b/client/src/components/tables/scores/MusecaScoreTable.tsx @@ -30,7 +30,7 @@ export default function MusecaScoreTable({ pageLen={pageLen} headers={ [ - ["Chart", "Ch.", NumericSOV(x => x.__related.chart.levelNum)], + ["Chart", "Chart", NumericSOV(x => x.__related.chart.levelNum)], IndicatorHeader, ["Song", "Song", StrSOV(x => x.__related.song.title)], ["Score", "Score", NumericSOV(x => x.scoreData.percent)], diff --git a/client/src/components/tables/scores/SDVXScoreTable.tsx b/client/src/components/tables/scores/SDVXScoreTable.tsx index 300a72119..8dd8a6abf 100644 --- a/client/src/components/tables/scores/SDVXScoreTable.tsx +++ b/client/src/components/tables/scores/SDVXScoreTable.tsx @@ -30,7 +30,7 @@ export default function SDVXScoreTable({ pageLen={pageLen} headers={ [ - ["Chart", "Ch.", NumericSOV(x => x.__related.chart.levelNum)], + ["Chart", "Chart", NumericSOV(x => x.__related.chart.levelNum)], IndicatorHeader, ["Song", "Song", StrSOV(x => x.__related.song.title)], ["Score", "Score", NumericSOV(x => x.scoreData.percent)], diff --git a/client/src/components/user/UserHeader.tsx b/client/src/components/user/UserHeader.tsx index f11112937..78f004429 100644 --- a/client/src/components/user/UserHeader.tsx +++ b/client/src/components/user/UserHeader.tsx @@ -64,7 +64,7 @@ export function UserHeaderBody({ reqUser }: { reqUser: PublicUserDocument }) { href="https://steamcommunity.com/id/" mode="steam" /> - + = { + iidx: 2, + bms: 2, + chunithm: 1, + ddr: 1, + gitadora: 1, + maimai: 1, + museca: 5, + sdvx: 5, + usc: 5, +}; + +export function GetGradeChartExpScale(game: Game) { + return scales[game]; +} diff --git a/client/src/util/splashes.ts b/client/src/util/splashes.ts index e893c9f08..e957f3d7f 100644 --- a/client/src/util/splashes.ts +++ b/client/src/util/splashes.ts @@ -63,6 +63,7 @@ export const loggedInSplashes = [ "Play ULTRAKILL.", "A Cutting-Edge Score Tracker (heh).", "Winnipeg is a warm, nice place to live.", + "I accidentally the score tracker.", ]; // Stuff that's bad for the kids, you know? @@ -70,4 +71,5 @@ export const contentiousSplashes = [ "Formerly Chucks", "Node.js Is Bad Ass Rock Star Tech", "i'm obligated to ask for you to seek immediate psychological attention", + "Is IIDX still a good game or are you all basically stupid?", ]; diff --git a/client/src/util/tierlist.ts b/client/src/util/tierlist.ts new file mode 100644 index 000000000..595296daf --- /dev/null +++ b/client/src/util/tierlist.ts @@ -0,0 +1,51 @@ +import { Game, GPTTierlists, IDStrings, IIDX_LAMPS, ScoreDocument } from "tachi-common"; +import { Playtype } from "types/tachi"; + +type ScaleAchievedFns = { + [I in IDStrings]: { + [K in GPTTierlists[I]]: ((k: ScoreDocument) => boolean) | null; + }; +}; + +const ScaleNameAchievedFns: ScaleAchievedFns = { + "bms:14K": { + "sgl-EC": k => k.scoreData.lampIndex >= IIDX_LAMPS.EASY_CLEAR, + "sgl-HC": k => k.scoreData.lampIndex >= IIDX_LAMPS.HARD_CLEAR, + }, + "bms:7K": { + "sgl-EC": k => k.scoreData.lampIndex >= IIDX_LAMPS.EASY_CLEAR, + "sgl-HC": k => k.scoreData.lampIndex >= IIDX_LAMPS.HARD_CLEAR, + }, + "chunithm:Single": {}, + "ddr:DP": {}, + "ddr:SP": {}, + "gitadora:Dora": {}, + "gitadora:Gita": {}, + "iidx:DP": {}, + "iidx:SP": { + "kt-NC": k => k.scoreData.lampIndex >= IIDX_LAMPS.CLEAR, + "kt-HC": k => k.scoreData.lampIndex >= IIDX_LAMPS.HARD_CLEAR, + "kt-EXHC": k => k.scoreData.lampIndex >= IIDX_LAMPS.EX_HARD_CLEAR, + }, + "maimai:Single": {}, + "museca:Single": { + "tachi-score": null, + }, + "sdvx:Single": {}, + "usc:Single": {}, +}; + +export function GetScaleAchievedFn( + game: Game, + playtype: Playtype, + tierlist: GPTTierlists[IDStrings] +) { + const scl = ScaleNameAchievedFns[`${game}:${playtype}` as IDStrings]; + + if (!scl) { + throw new Error(`Invalid game + pt combination ${game}:${playtype}. Can't find scale.`); + } + + // @ts-expect-error hack haha + return scl[tierlist]; +}