feat: more options on tierlist view (#1200)

* feat: more options on tierlist view

wait...

* fix: actually this was always clear tier

---------

Co-authored-by: zkldi <20380519+zkldi@users.noreply.github.com>
This commit is contained in:
zk
2024-10-28 20:30:22 +00:00
committed by GitHub
co-authored by zkldi
parent 21183ef6e7
commit 3c62d3d58b
6 changed files with 118 additions and 32 deletions
@@ -22,7 +22,7 @@ import ReferToUser from "components/util/ReferToUser";
import SelectLinkButton from "components/util/SelectLinkButton";
import useApiQuery from "components/util/query/useApiQuery";
import useUGPTBase from "components/util/useUGPTBase";
import { GPT_CLIENT_IMPLEMENTATIONS } from "lib/game-implementations";
import { GetEnumColour, GPT_CLIENT_IMPLEMENTATIONS } from "lib/game-implementations";
import { GPTRatingSystem } from "lib/types";
import React, { useContext, useEffect, useMemo, useState } from "react";
import { Col, Form, Row } from "react-bootstrap";
@@ -40,6 +40,7 @@ import {
SongDocument,
UserDocument,
integer,
GetGPTString,
} from "tachi-common";
import { ConfEnumScoreMetric } from "tachi-common/types/metrics";
import { UGPTFolderReturns } from "types/api-returns";
@@ -425,6 +426,8 @@ function TierlistBreakdown({ game, folderDataset, playtype, reqUser }: InfoProps
const gptImpl = GPT_CLIENT_IMPLEMENTATIONS[`${game}:${playtype}` as GPTString];
const [tierlist, setTierlist] = useState<string>(gptImpl.ratingSystems[0].name);
const [useFancyColour, setUseFancyColour] = useState(false);
const [forceGridView, setForceGridView] = useState(false);
const playerStats = useMemo(
() => FolderDatasetAchievedStatus(folderDataset, game, playtype, tierlist),
@@ -464,6 +467,30 @@ function TierlistBreakdown({ game, folderDataset, playtype, reqUser }: InfoProps
)}
<Col xs={12}>
<Divider />
<Form.Check
type="checkbox"
checked={!useFancyColour}
onChange={() => {
setUseFancyColour((e) => !e);
}}
label="Use simple clear/fail colours"
/>
<Form.Text>
<span>
If enabled, this will show green when you've achieved the tierlist
requirements, and red if you haven't, instead of fancier colours.
</span>
</Form.Text>
<Form.Check
className="d-block d-lg-none"
type="checkbox"
checked={forceGridView}
onChange={() => {
setForceGridView((e) => !e);
}}
label="Force desktop grid view"
/>
<Divider />
</Col>
<Col xs={12}>
<TierlistInfoLadder
@@ -473,6 +500,8 @@ function TierlistBreakdown({ game, folderDataset, playtype, reqUser }: InfoProps
reqUser={reqUser}
folderDataset={folderDataset}
tierlistImpl={tierlistImpl}
useFancyColour={useFancyColour}
forceGridView={forceGridView}
/>
</Col>
</Row>
@@ -486,6 +515,8 @@ function TierlistInfoLadder({
reqUser,
folderDataset,
tierlistImpl,
useFancyColour,
forceGridView,
}: {
playerStats: Record<string, { status: AchievedStatuses; score: string | null }>;
game: Game;
@@ -493,6 +524,8 @@ function TierlistInfoLadder({
reqUser: UserDocument;
tierlistImpl: GPTRatingSystem<GPTString>;
folderDataset: FolderDataset;
useFancyColour: boolean;
forceGridView: boolean;
}) {
const buckets: TierlistInfo[][] = useMemo(() => {
const buckets: TierlistInfo[][] = [];
@@ -569,7 +602,17 @@ function TierlistInfoLadder({
)
</div>
<TierlistBucket {...{ bucket, game, playtype, reqUser }} />
<TierlistBucket
{...{
bucket,
game,
playtype,
reqUser,
useFancyColour,
tierlistImpl,
forceGridView,
}}
/>
</div>
))}
</>
@@ -579,18 +622,25 @@ function TierlistInfoLadder({
function TierlistBucket({
bucket,
game,
playtype,
reqUser,
useFancyColour,
forceGridView,
tierlistImpl,
}: {
game: Game;
playtype: Playtype;
reqUser: UserDocument;
bucket: TierlistInfo[];
useFancyColour: boolean;
forceGridView: boolean;
tierlistImpl: GPTRatingSystem<GPTString>;
}) {
const {
breakpoint: { isLg },
} = useContext(WindowContext);
// xs view is tabular
if (!isLg) {
if (!isLg && !forceGridView) {
return (
<MiniTable>
{bucket.map((tierlistInfo, i) => (
@@ -598,9 +648,13 @@ function TierlistBucket({
tierlistInfo={tierlistInfo}
key={`${tierlistInfo.chart.chartID}-${tierlistInfo.text}`}
game={game}
playtype={playtype}
bucket={bucket}
i={i}
reqUser={reqUser}
useFancyColour={useFancyColour}
tierlistImpl={tierlistImpl}
forceGridView={forceGridView}
/>
))}
</MiniTable>
@@ -614,9 +668,13 @@ function TierlistBucket({
tierlistInfo={tierlistInfo}
key={`${tierlistInfo.chart.chartID}-${tierlistInfo.text}`}
game={game}
playtype={playtype}
bucket={bucket}
i={i}
reqUser={reqUser}
useFancyColour={useFancyColour}
forceGridView={forceGridView}
tierlistImpl={tierlistImpl}
/>
))}
</div>
@@ -626,13 +684,21 @@ function TierlistBucket({
function TierlistInfoBucketValues({
tierlistInfo,
game,
playtype,
reqUser,
useFancyColour,
forceGridView,
tierlistImpl,
}: {
tierlistInfo: TierlistInfo;
bucket: TierlistInfo[];
game: Game;
playtype: Playtype;
i: integer;
reqUser: UserDocument;
useFancyColour: boolean;
forceGridView: boolean;
tierlistImpl: GPTRatingSystem<GPTString>;
}) {
const { breakpoint } = useContext(WindowContext);
@@ -643,10 +709,26 @@ function TierlistInfoBucketValues({
[AchievedStatuses.SCORE_BASED]: "bg-transparent",
};
let colourClass: string | undefined;
let colourCss: string | undefined;
if (useFancyColour) {
const gptImpl = GPT_CLIENT_IMPLEMENTATIONS[GetGPTString(game, playtype)];
// @ts-expect-error lol
colourCss = gptImpl.enumColours[tierlistImpl.enumName][tierlistInfo.score];
} else {
colourClass = statusClasses[tierlistInfo.status];
}
if (tierlistInfo.status === AchievedStatuses.NOT_PLAYED) {
colourClass = "bg-body-tertiary";
}
const data = tierlistInfo.chart;
// xs view
if (!breakpoint.isLg) {
if (!breakpoint.isLg && !forceGridView) {
return (
<tr>
<DifficultyCell game={game} chart={tierlistInfo.chart} alwaysShort noTierlist />
@@ -664,7 +746,7 @@ function TierlistInfoBucketValues({
)}
</div>
</td>
<TierlistInfoCell tierlistInfo={tierlistInfo} />
<TierlistInfoCell tierlistInfo={tierlistInfo} colourCss={colourCss} />
</tr>
);
}
@@ -682,7 +764,10 @@ function TierlistInfoBucketValues({
) : undefined
}
>
<div className={`${statusClasses[tierlistInfo.status]} bg-opacity-50 rounded p-2`}>
<div
className={`${colourClass} bg-opacity-50 rounded p-2`}
style={{ backgroundColor: colourCss ? ChangeOpacity(colourCss, 0.5) : undefined }}
>
<Link className="text-decoration-none" to={CreateChartLink(data, game)}>
{data.__related.song.title}
</Link>{" "}
@@ -714,28 +799,35 @@ function TierlistInfoBucketValues({
);
}
function TierlistInfoCell({ tierlistInfo }: { tierlistInfo: TierlistInfo }) {
let colour;
const text = tierlistInfo.score ?? "NOT PLAYED";
function TierlistInfoCell({
tierlistInfo,
colourCss,
}: {
tierlistInfo: TierlistInfo;
colourCss: string | undefined;
}) {
let colour = colourCss;
if (tierlistInfo.status === AchievedStatuses.FAILED) {
colour = COLOUR_SET.red;
} else if (tierlistInfo.status === AchievedStatuses.NOT_PLAYED) {
colour = COLOUR_SET.red;
} else {
colour = COLOUR_SET.green;
if (!colour) {
if (tierlistInfo.status === AchievedStatuses.FAILED) {
colour = COLOUR_SET.red;
} else if (tierlistInfo.status === AchievedStatuses.NOT_PLAYED) {
colour = COLOUR_SET.red;
} else {
colour = COLOUR_SET.green;
}
}
return (
<td
style={{
backgroundColor: ChangeOpacity(colour, 0.2),
backgroundColor: colour ? ChangeOpacity(colour, 0.5) : undefined,
width: "60px",
minWidth: "60px",
maxWidth: "60px",
}}
>
{text}
{tierlistInfo.score ?? "NOT PLAYED"}
</td>
);
}
+1 -15
View File
@@ -672,21 +672,7 @@ export const GPT_CLIENT_IMPLEMENTATIONS: GPTClientImplementations = {
// this game has dynamic difficulties. Formatting is handled elsewhere.
},
classColours: {},
ratingSystems: [
CreateRatingSys(
"BPM",
"How fast are the streams in this chart?",
(c) => c.data.streamBPM,
(c) => c.data.streamBPM?.toString(),
() => undefined,
(s) => [
s.scoreData.lamp === "FAILED"
? `Failed ${s.scoreData.survivedPercent.toFixed(2)}%`
: s.scoreData.lamp,
s.scoreData.lamp !== "FAILED",
]
),
],
ratingSystems: [],
scoreHeaders: [
["Score", "Score", NumericSOV((x) => x.scoreData.finalPercent)],
["Judgements", "Hits", NumericSOV((x) => x.scoreData.scorePercent)],
+2
View File
@@ -5,6 +5,7 @@ import { GPTString } from "tachi-common";
export function CreateRatingSys<GPT extends GPTString>(
name: string,
description: string,
enumName: string,
toNumber: GPTRatingSystem<GPT>["toNumber"],
toString: GPTRatingSystem<GPT>["toString"],
idvDifference: GPTRatingSystem<GPT>["idvDifference"] = () => false,
@@ -12,6 +13,7 @@ export function CreateRatingSys<GPT extends GPTString>(
): GPTRatingSystem<GPT> {
return {
description,
enumName,
name,
toNumber,
toString,
+3
View File
@@ -139,6 +139,7 @@ export const IIDX_SP_IMPL: GPTClientImplementation<"iidx:SP"> = {
CreateRatingSys(
"NC Tier",
"Tierlist Ratings for Normal Clears.",
"lamp",
(c) => c.data.ncTier?.value,
(c) => c.data.ncTier?.text,
(c) => c.data.ncTier?.individualDifference,
@@ -147,6 +148,7 @@ export const IIDX_SP_IMPL: GPTClientImplementation<"iidx:SP"> = {
CreateRatingSys(
"HC Tier",
"Tierlist Ratings for Hard Clears.",
"lamp",
(c) => c.data.hcTier?.value,
(c) => c.data.hcTier?.text,
(c) => c.data.hcTier?.individualDifference,
@@ -155,6 +157,7 @@ export const IIDX_SP_IMPL: GPTClientImplementation<"iidx:SP"> = {
CreateRatingSys(
"EXHC Tier",
"Tierlist Ratings for EX-HARD Clears.",
"lamp",
(c) => c.data.exhcTier?.value,
(c) => c.data.exhcTier?.text,
(c) => c.data.exhcTier?.individualDifference,
+1
View File
@@ -154,6 +154,7 @@ export const SDVX_IMPL: GPTClientImplementation<"sdvx:Single"> = {
CreateRatingSys(
"Tierlist",
"The unofficial SDVX clearing tierlist",
"lamp",
(c) => c.data.clearTier?.value,
(c) => c.data.clearTier?.text,
(c) => c.data.clearTier?.individualDifference,
+2
View File
@@ -33,6 +33,8 @@ export type GPTDifficultyColours<GPT extends GPTString> = Record<Difficulties[GP
export type GPTRatingSystem<GPT extends GPTString> = {
name: string;
description: string;
enumName: string;
toNumber: (c: ChartDocument<GPT>) => number | null | undefined;
toString: (c: ChartDocument<GPT>) => string | null | undefined;