From 7d6cd879aacd47b74287c80901e878a7db7fe4e6 Mon Sep 17 00:00:00 2001 From: Kalle <38327916+Sendouc@users.noreply.github.com> Date: Sat, 1 Jul 2023 14:43:09 +0300 Subject: [PATCH] XP Leaderboards --- .../leaderboards/leaderboards-constants.ts | 15 ++- .../queries/XPLeaderboard.server.ts | 71 ++++++++++++++ .../leaderboards/routes/leaderboards.tsx | 93 ++++++++++++++++++- public/locales/en/common.json | 3 +- 4 files changed, 176 insertions(+), 6 deletions(-) create mode 100644 app/features/leaderboards/queries/XPLeaderboard.server.ts diff --git a/app/features/leaderboards/leaderboards-constants.ts b/app/features/leaderboards/leaderboards-constants.ts index 715d2a6db..b3db6539b 100644 --- a/app/features/leaderboards/leaderboards-constants.ts +++ b/app/features/leaderboards/leaderboards-constants.ts @@ -1,4 +1,17 @@ +import { mainWeaponIds } from "~/modules/in-game-lists"; +import { rankedModesShort } from "~/modules/in-game-lists/modes"; + export const MATCHES_COUNT_NEEDED_FOR_LEADERBOARD = 7; export const LEADERBOARD_MAX_SIZE = 250; -export const LEADERBOARD_TYPES = ["USER", "TEAM"] as const; +export const LEADERBOARD_TYPES = [ + "USER", + "TEAM", + "XP-ALL", + ...(rankedModesShort.map( + (mode) => `XP-MODE-${mode}` + ) as `XP-MODE-${(typeof rankedModesShort)[number]}`[]), + ...(mainWeaponIds.map( + (id) => `XP-WEAPON-${id}` + ) as `XP-WEAPON-${(typeof mainWeaponIds)[number]}`[]), +] as const; diff --git a/app/features/leaderboards/queries/XPLeaderboard.server.ts b/app/features/leaderboards/queries/XPLeaderboard.server.ts new file mode 100644 index 000000000..59d6bc412 --- /dev/null +++ b/app/features/leaderboards/queries/XPLeaderboard.server.ts @@ -0,0 +1,71 @@ +import { sql } from "~/db/sql"; +import { LEADERBOARD_MAX_SIZE } from "../leaderboards-constants"; +import type { User, XRankPlacement } from "~/db/types"; +import type { MainWeaponId } from "~/modules/in-game-lists"; + +const getStm = (where = "") => + sql.prepare(/* sql */ ` + select + "XRankPlacement"."id" as "entryId", + "XRankPlacement"."playerId", + "XRankPlacement"."weaponSplId", + "XRankPlacement"."name", + "User"."id", + "User"."discordName", + "User"."discordAvatar", + "User"."discordDiscriminator", + "User"."discordId", + "User"."customUrl", + max("XRankPlacement"."power") as "power", + rank () over ( + order by "power" desc + ) "placementRank" + from "XRankPlacement" + left join "SplatoonPlayer" on "SplatoonPlayer"."id" = "XRankPlacement"."playerId" + left join "User" on "User"."id" = "SplatoonPlayer"."userId" + ${where} + group by "XRankPlacement"."playerId" + order by "power" desc + limit ${LEADERBOARD_MAX_SIZE} +`); + +const allStm = getStm(); +const modeStm = getStm(/* sql */ ` + where + "XRankPlacement"."mode" = @mode +`); +const weaponStm = getStm(/* sql */ ` + where + "XRankPlacement"."weaponSplId" = @weaponSplId +`); + +export interface XPLeaderboardItem { + entryId: number; + power: number; + id: User["id"]; + name: XRankPlacement["name"]; + playerId: XRankPlacement["playerId"]; + discordName: User["discordName"] | null; + discordAvatar: User["discordAvatar"] | null; + discordDiscriminator: User["discordDiscriminator"] | null; + discordId: User["discordId"] | null; + customUrl: User["customUrl"] | null; + placementRank: number; + weaponSplId: MainWeaponId; +} + +export function allXPLeaderboard(): XPLeaderboardItem[] { + return allStm.all() as any[]; +} + +export function modeXPLeaderboard( + mode: XRankPlacement["mode"] +): XPLeaderboardItem[] { + return modeStm.all({ mode }) as any[]; +} + +export function weaponXPLeaderboard( + weaponSplId: MainWeaponId +): XPLeaderboardItem[] { + return weaponStm.all({ weaponSplId }) as any[]; +} diff --git a/app/features/leaderboards/routes/leaderboards.tsx b/app/features/leaderboards/routes/leaderboards.tsx index 837f864ff..badc4f7d3 100644 --- a/app/features/leaderboards/routes/leaderboards.tsx +++ b/app/features/leaderboards/routes/leaderboards.tsx @@ -12,6 +12,7 @@ import { LEADERBOARDS_PAGE, navIconUrl, teamPage, + topSearchPlayerPage, userPage, userSubmittedImage, } from "~/utils/urls"; @@ -29,6 +30,19 @@ import React from "react"; import { LEADERBOARD_TYPES } from "../leaderboards-constants"; import { useTranslation } from "~/hooks/useTranslation"; import { i18next } from "~/modules/i18n"; +import { + type XPLeaderboardItem, + allXPLeaderboard, + modeXPLeaderboard, + weaponXPLeaderboard, +} from "../queries/XPLeaderboard.server"; +import { WeaponImage } from "~/components/Image"; +import { + weaponCategories, + type MainWeaponId, + type RankedModeShort, +} from "~/modules/in-game-lists"; +import { rankedModesShort } from "~/modules/in-game-lists/modes"; export const handle: SendouRouteHandle = { i18n: ["vods"], @@ -73,12 +87,20 @@ export const loader = async ({ request }: LoaderArgs) => { return { userLeaderboard: type === "USER" ? userSPLeaderboard() : null, teamLeaderboard: type === "TEAM" ? teamSPLeaderboard() : null, + xpLeaderboard: + type === "XP-ALL" + ? allXPLeaderboard() + : type.startsWith("XP-MODE") + ? modeXPLeaderboard(type.split("-")[2] as RankedModeShort) + : type.startsWith("XP-WEAPON") + ? weaponXPLeaderboard(Number(type.split("-")[2]) as MainWeaponId) + : null, title: makeTitle(t("pages.leaderboards")), }; }; export default function LeaderboardsPage() { - const { t } = useTranslation(["common"]); + const { t } = useTranslation(["common", "game-misc", "weapons"]); const [searchParams, setSearchParams] = useSearchParams(); const data = useLoaderData(); @@ -92,14 +114,42 @@ export default function LeaderboardsPage() { } > - {LEADERBOARD_TYPES.map((type) => { + {LEADERBOARD_TYPES.filter((type) => !type.includes("XP")).map( + (type) => { + return ( + + ); + } + )} + + + + {rankedModesShort.map((mode) => { return ( - ); })} + {weaponCategories.map((category) => { + return ( + + {category.weaponIds.map((weaponId) => { + return ( + + ); + })} + + ); + })} {data.userLeaderboard ? ( @@ -107,6 +157,7 @@ export default function LeaderboardsPage() { {data.teamLeaderboard ? ( ) : null} + {data.xpLeaderboard ? : null} ); } @@ -178,3 +229,37 @@ function TeamTable({ entries }: { entries: TeamSPLeaderboardItem[] }) { ); } + +function XPTable({ entries }: { entries: XPLeaderboardItem[] }) { + return ( +
+ {entries.map((entry) => { + return ( + +
+
+ {entry.placementRank} +
+ {entry.discordId ? ( + + ) : null} + +
{entry.name}
+
{entry.power}
+
+ + ); + })} +
+ ); +} diff --git a/public/locales/en/common.json b/public/locales/en/common.json index 17810a727..6dadc81d9 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -187,5 +187,6 @@ "no": "No", "leaderboard.type.USER": "User", - "leaderboard.type.TEAM": "Team" + "leaderboard.type.TEAM": "Team", + "leaderboard.type.XP-ALL": "All" }