diff --git a/src/app/(stats)/player/[ign]/_client.tsx b/src/app/(stats)/player/[ign]/_client.tsx index 1cb32ef..ec61e51 100644 --- a/src/app/(stats)/player/[ign]/_client.tsx +++ b/src/app/(stats)/player/[ign]/_client.tsx @@ -16,6 +16,7 @@ import ArcadeStats from "./_stats/arcade/arcade" import BedwarsStats from "./_stats/bedwars/bedwars" import BlitzStats from "./_stats/blitz/blitz" import BuildBattleStats from "./_stats/build-battle/build-battle" +import ClassicStats from "./_stats/classic/classic" import CopsAndCrimsStats from "./_stats/copsandcrims/copsandcrims" import DuelsStats from "./_stats/duels/duels" import MegaWallsStats from "./_stats/megawalls/megawalls" @@ -98,7 +99,8 @@ export function PlayerStats( "arcade": , "speeduhc": , "smashheros": , - "warlords": + "warlords": , + "classic": } as const const defaultOrder = Object.keys(statsComponents) diff --git a/src/app/(stats)/player/[ign]/_stats/classic/arenabrawl.tsx b/src/app/(stats)/player/[ign]/_stats/classic/arenabrawl.tsx new file mode 100644 index 0000000..6ccd46b --- /dev/null +++ b/src/app/(stats)/player/[ign]/_stats/classic/arenabrawl.tsx @@ -0,0 +1,46 @@ +import { Separator } from "@/components/ui/separator" +import { formatNumber } from "@/lib/formatters" +import { devide } from "@/lib/hypixel/general" +import { NonNullStats } from "@/lib/schema/player" +import GeneralStats from "../GeneralStats" +import { ArenaBrawlGeneralStats } from "./stats" +import { ArenaBrawlModeStatsTable } from "./table" + +export default function ArenaBrawlStats({ stats }: { stats: NonNullStats["ArenaBrawl"] }) { + if (!stats) return null + + const kills = stats.kills_1v1 + stats.kills_2v2 + stats.kills_4v4 + const deaths = stats.deaths_1v1 + stats.deaths_2v2 + stats.deaths_4v4 + const wins = stats.wins_1v1 + stats.wins_2v2 + stats.wins_4v4 + const losses = stats.losses_1v1 + stats.losses_2v2 + stats.losses_4v4 + const ws = stats.win_streaks_1v1 + stats.win_streaks_2v2 + stats.win_streaks_4v4 + const kd = formatNumber(devide(kills, deaths)) + const wl = formatNumber(devide(wins, losses)) + + return ( + KD

, + stat:

{kd}

+ }, + { + title:

Wins

, + stat:

{formatNumber(wins)}

+ }, + { + title:

WL

, + stat:

{wl}

+ } + ]} + > + + + + + +
+ ) +} diff --git a/src/app/(stats)/player/[ign]/_stats/classic/classic.tsx b/src/app/(stats)/player/[ign]/_stats/classic/classic.tsx new file mode 100644 index 0000000..46149d7 --- /dev/null +++ b/src/app/(stats)/player/[ign]/_stats/classic/classic.tsx @@ -0,0 +1,31 @@ +import { AccordionContent, AccordionItem, AccordionTrigger } from "@/components/ui/accordion" +import { Card, CardContent } from "@/components/ui/card" +import { NonNullStats } from "@/lib/schema/player" +import { cn } from "@/lib/utils" +import CollapsedStats from "../../_components/CollapsedStats" +import ArenaBrawlStats from "./arenabrawl" + +export default function ClassicStats({ arenaBrawlStats }: { arenaBrawlStats: NonNullStats["ArenaBrawl"] }) { + return ( + + + + +

Classic

+
+ Modes

, + stat:

Arena Brawl | Paintball | Quakecraft | TKR | VampireZ | Walls

+ }]} + /> +
+
+
+
+ + + +
+ ) +} diff --git a/src/app/(stats)/player/[ign]/_stats/classic/stats.tsx b/src/app/(stats)/player/[ign]/_stats/classic/stats.tsx new file mode 100644 index 0000000..28ab8fe --- /dev/null +++ b/src/app/(stats)/player/[ign]/_stats/classic/stats.tsx @@ -0,0 +1,12 @@ +import { formatNumber } from "@/lib/formatters" +import { BasicStat } from "../../_components/Stats" + +export function ArenaBrawlGeneralStats({ coins, keys, ws }: { coins: number, keys: number, ws: number }) { + return ( +
+ + + +
+ ) +} diff --git a/src/app/(stats)/player/[ign]/_stats/classic/table.tsx b/src/app/(stats)/player/[ign]/_stats/classic/table.tsx new file mode 100644 index 0000000..775bb49 --- /dev/null +++ b/src/app/(stats)/player/[ign]/_stats/classic/table.tsx @@ -0,0 +1,69 @@ +import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table" +import { formatNumber } from "@/lib/formatters" +import { getArenaBrawlModeName, getArenaBrawlModeStats, getArenaBrawlMostPlayedMode, getArenaBrawlOverallStats } from "@/lib/hypixel/classic/general" +import { NonNullStats } from "@/lib/schema/player" +import { cn } from "@/lib/utils" + +export function ArenaBrawlModeStatsTable({ stats }: { stats: NonNullable }) { + return ( + + + + + + + + +
+ ) +} + +function ArenaBrawlModeStat( + { modeId, stats }: { modeId: Parameters[0] | "all", stats: NonNullable } +) { + if (modeId === "all") { + const modeStats = getArenaBrawlOverallStats(stats) + return ( + + Overall + {modeStats.map((v, i) => { + return {formatNumber(v)} + })} + + ) + } + + const modeStats = getArenaBrawlModeStats(modeId, stats) + const modeName = getArenaBrawlModeName(modeId) + const mostPlayed = getArenaBrawlMostPlayedMode(stats) === modeId + + return ( + + {modeName} + {modeStats.map((v, i) => { + return {formatNumber(v)} + })} + + ) +} + +function ArenaBrawlModeStatsTableHeader() { + const headerElements = [ + "Mode", + "Kills", + "Deaths", + "KD", + "Wins", + "Losses", + "WL", + "Win Streaks" + ] + + return ( + + + {headerElements.map((v, i) => {v})} + + + ) +} diff --git a/src/app/(stats)/player/[ign]/_stats/warlords/table.tsx b/src/app/(stats)/player/[ign]/_stats/warlords/table.tsx index 0e3b7c8..363352c 100644 --- a/src/app/(stats)/player/[ign]/_stats/warlords/table.tsx +++ b/src/app/(stats)/player/[ign]/_stats/warlords/table.tsx @@ -35,7 +35,7 @@ function WarlordsClassStatsTableStat( const mostPlayed = getWarlordsMostPlayedClass(stats)?.id === classId return ( - + {classId === "all" ? ( diff --git a/src/data/hypixel/classic.ts b/src/data/hypixel/classic.ts new file mode 100644 index 0000000..c84d06e --- /dev/null +++ b/src/data/hypixel/classic.ts @@ -0,0 +1,5 @@ +export const ARENABRAWLMODES = [ + { id: "1v1", name: "1v1" }, + { id: "2v2", name: "2v2" }, + { id: "4v4", name: "4v4" } +] as const diff --git a/src/lib/hypixel/classic/general.ts b/src/lib/hypixel/classic/general.ts new file mode 100644 index 0000000..3f4a1fe --- /dev/null +++ b/src/lib/hypixel/classic/general.ts @@ -0,0 +1,56 @@ +import { ARENABRAWLMODES } from "@/data/hypixel/classic" +import { NonNullStats } from "@/lib/schema/player" +import { devide } from "../general" + +export function getArenaBrawlModeName(modeId: typeof ARENABRAWLMODES[number]["id"]) { + return ARENABRAWLMODES.find(m => m.id === modeId)!.name +} + +export function getArenaBrawlMostPlayedMode(stats: NonNullable) { + let mostPlayedMode: typeof ARENABRAWLMODES[number] | null = null + let maxGames = 0 + + for (const mode of ARENABRAWLMODES) { + const wins = stats[`wins_${mode.id}`] || 0 + const losses = stats[`losses_${mode.id}`] || 0 + const totalGames = wins + losses + + if (totalGames > maxGames) { + maxGames = totalGames + mostPlayedMode = mode + } + } + + return mostPlayedMode?.id || null +} + +export function getArenaBrawlOverallStats(stats: NonNullable) { + const allModeStats = ARENABRAWLMODES.map(mode => getArenaBrawlModeStats(mode.id, stats)) + const totalKills = allModeStats.reduce((sum, modeStats) => sum + (modeStats[0] || 0), 0) + const totalDeaths = allModeStats.reduce((sum, modeStats) => sum + (modeStats[1] || 0), 0) + const totalWins = allModeStats.reduce((sum, modeStats) => sum + (modeStats[3] || 0), 0) + const totalLosses = allModeStats.reduce((sum, modeStats) => sum + (modeStats[4] || 0), 0) + const maxWinStreak = Math.max(...allModeStats.map(modeStats => modeStats[6] || 0)) + + return [ + totalKills, + totalDeaths, + devide(totalKills, totalDeaths), + totalWins, + totalLosses, + devide(totalWins, totalLosses), + maxWinStreak + ] +} + +export function getArenaBrawlModeStats(modeId: typeof ARENABRAWLMODES[number]["id"], stats: NonNullable) { + return [ + stats[`kills_${modeId}`], + stats[`deaths_${modeId}`], + devide(stats[`kills_${modeId}`], stats[`deaths_${modeId}`]), + stats[`wins_${modeId}`], + stats[`losses_${modeId}`], + devide(stats[`wins_${modeId}`], stats[`losses_${modeId}`]), + stats[`win_streaks_${modeId}`] + ] +} diff --git a/src/lib/schema/player.ts b/src/lib/schema/player.ts index 6092b31..e81f3d6 100644 --- a/src/lib/schema/player.ts +++ b/src/lib/schema/player.ts @@ -3,6 +3,7 @@ import { arcadeStatsSchema } from "./stats/arcade" import { bedwarsStatsSchema } from "./stats/bedwars" import { blitzStatsSchema } from "./stats/blitz" import { buildBattleStatsSchema } from "./stats/build-battle" +import { arenaBrawlStatsSchema } from "./stats/classic" import { copsAndCrimsStatsSchema } from "./stats/copsandcrims" import { duelsStatsSchema } from "./stats/duels" import { megawallsStats } from "./stats/megawalls" @@ -44,14 +45,16 @@ export const playerSchema = z.looseObject({ Arcade: arcadeStatsSchema.optional(), SpeedUHC: speedUhcStatsSchema.optional(), SuperSmash: smashHerosStats.optional(), - Battleground: warlordsStatsSchema.optional() - }).transform(({ Walls3, MCGO, HungerGames, SuperSmash, Battleground, ...rest }) => { + Battleground: warlordsStatsSchema.optional(), + Arena: arenaBrawlStatsSchema.optional() + }).transform(({ Walls3, MCGO, HungerGames, SuperSmash, Battleground, Arena, ...rest }) => { return { MegaWalls: Walls3, CopsAndCrims: MCGO, Blitz: HungerGames, SmashHeros: SuperSmash, Warlords: Battleground, + ArenaBrawl: Arena, ...rest } }).optional(), diff --git a/src/lib/schema/stats/classic.ts b/src/lib/schema/stats/classic.ts new file mode 100644 index 0000000..a06de08 --- /dev/null +++ b/src/lib/schema/stats/classic.ts @@ -0,0 +1,21 @@ +import z from "zod" + +export const arenaBrawlStatsSchema = z.object({ + coins: z.number().default(0), + keys: z.number().default(0), + kills_1v1: z.number().default(0), + kills_2v2: z.number().default(0), + kills_4v4: z.number().default(0), + deaths_1v1: z.number().default(0), + deaths_2v2: z.number().default(0), + deaths_4v4: z.number().default(0), + wins_1v1: z.number().default(0), + wins_2v2: z.number().default(0), + wins_4v4: z.number().default(0), + losses_1v1: z.number().default(0), + losses_2v2: z.number().default(0), + losses_4v4: z.number().default(0), + win_streaks_1v1: z.number().default(0), + win_streaks_2v2: z.number().default(0), + win_streaks_4v4: z.number().default(0) +})