From eaf0e02d287deea7c4280d80b406e00509c60b25 Mon Sep 17 00:00:00 2001
From: Taken
Date: Sat, 20 Sep 2025 19:57:05 +0200
Subject: [PATCH] Added arena brawl stats card
---
src/app/(stats)/player/[ign]/_client.tsx | 4 +-
.../[ign]/_stats/classic/arenabrawl.tsx | 46 +++++++++++++
.../player/[ign]/_stats/classic/classic.tsx | 31 +++++++++
.../player/[ign]/_stats/classic/stats.tsx | 12 ++++
.../player/[ign]/_stats/classic/table.tsx | 69 +++++++++++++++++++
.../player/[ign]/_stats/warlords/table.tsx | 2 +-
src/data/hypixel/classic.ts | 5 ++
src/lib/hypixel/classic/general.ts | 56 +++++++++++++++
src/lib/schema/player.ts | 7 +-
src/lib/schema/stats/classic.ts | 21 ++++++
10 files changed, 249 insertions(+), 4 deletions(-)
create mode 100644 src/app/(stats)/player/[ign]/_stats/classic/arenabrawl.tsx
create mode 100644 src/app/(stats)/player/[ign]/_stats/classic/classic.tsx
create mode 100644 src/app/(stats)/player/[ign]/_stats/classic/stats.tsx
create mode 100644 src/app/(stats)/player/[ign]/_stats/classic/table.tsx
create mode 100644 src/data/hypixel/classic.ts
create mode 100644 src/lib/hypixel/classic/general.ts
create mode 100644 src/lib/schema/stats/classic.ts
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)
+})