Added tnt games stats

This commit is contained in:
2025-09-06 20:46:00 +02:00
parent 45b2ae1763
commit 24e07e092a
9 changed files with 288 additions and 2 deletions

View File

@@ -18,7 +18,32 @@ type GenericProgressProps =
rainbow: true rainbow: true
}) })
export default function GenericProgress({ percent, tooltipId, tooltipContent, className, rainbow }: GenericProgressProps) { type GenericProgressNoTooltipProps = {
text: string
percent: number
className?: string
}
export function GenericProgressNoTooltip({ text, percent, className }: GenericProgressNoTooltipProps) {
return (
<div className="flex flex-1">
<div
className={cn("h-5 rounded-l-md flex items-center justify-center", percent >= 100 ? "rounded-r-md" : undefined, className)}
style={{ width: `${percent > 100 ? 100 : percent}%` }}
>
{text}
</div>
{percent < 100 && (
<div
className={cn("flex-1 h-5 rounded-r-md bg-background", percent === 0 ? "rounded-l-md" : undefined)}
>
</div>
)}
</div>
)
}
export function GenericProgress({ percent, tooltipId, tooltipContent, className, rainbow }: GenericProgressProps) {
const [pos, setPos] = useState({ x: 0, y: 0 }) const [pos, setPos] = useState({ x: 0, y: 0 })
useEffect(() => { useEffect(() => {

View File

@@ -0,0 +1,39 @@
import { formatNumber, formatSecondsToTime } from "@/lib/formatters"
import { devide } from "@/lib/hypixel/general"
import { NonNullStats } from "@/lib/schema/player"
import { BasicStat } from "../../_components/Stats"
export default function TNTGamesGeneralStats({ stats }: { stats: NonNullable<NonNullStats["TNTGames"]> }) {
return (
<div className="flex flex-col gap-6">
<div>
<BasicStat title="Token: " value={formatNumber(stats.coins)} className="text-mc-dark-green" />
<BasicStat title="Wins: " value={formatNumber(stats.wins)} />
</div>
<div className="grid grid-cols-2 grid-rows-2">
<div>
<h2 className="text-xl font-bold">TNT Tag</h2>
<BasicStat title="Kills: " value={formatNumber(stats.kills_tntag)} />
<BasicStat title="Wins: " value={formatNumber(stats.wins_tntag)} />
</div>
<div>
<h2 className="text-xl font-bold">TNT Run</h2>
<BasicStat title="Wins: " value={formatNumber(stats.wins_tntrun)} />
<BasicStat title="Record Time: " value={formatSecondsToTime(stats.record_tntrun)} />
</div>
<div>
<h2 className="text-xl font-bold">PvP Run</h2>
<BasicStat title="Kills: " value={formatNumber(stats.kills_pvprun)} />
<BasicStat title="Wins: " value={formatNumber(stats.wins_pvprun)} />
<BasicStat title="Record Time: " value={formatSecondsToTime(stats.record_pvprun)} />
</div>
<div>
<h2 className="text-xl font-bold">Bow Spleef</h2>
<BasicStat title="Wins: " value={formatNumber(stats.wins_bowspleef)} />
<BasicStat title="Losses: " value={formatNumber(stats.deaths_bowspleef)} />
<BasicStat title="Win/Loss Ratio: " value={formatNumber(devide(stats.wins_bowspleef, stats.deaths_bowspleef))} />
</div>
</div>
</div>
)
}

View File

@@ -0,0 +1,37 @@
import { AccordionContent, AccordionItem, AccordionTrigger } from "@/components/ui/accordion"
import { Card, CardContent } from "@/components/ui/card"
import { Separator } from "@/components/ui/separator"
import { NonNullStats } from "@/lib/schema/player"
import CollapsedStats from "../../_components/CollapsedStats"
import TNTGamesGeneralStats from "./stats"
import TNTWizardsStats from "./wizards"
export default function TNTGamesStats({ stats }: { stats: NonNullStats["TNTGames"] }) {
if (!stats) return null
return (
<AccordionItem value="tnt-games">
<Card className="py-0">
<CardContent>
<AccordionTrigger className="items-center py-2 hover:no-underline hover:cursor-pointer">
<h1 className="text-xl font-bold">TNT Games</h1>
<div className="flex gap-4">
<CollapsedStats
stats={[{
title: <p>Wins</p>,
stat: <p className="text-muted-foreground">{stats.wins}</p>
}]}
/>
</div>
</AccordionTrigger>
<AccordionContent>
<Separator className="my-4" />
<TNTGamesGeneralStats stats={stats} />
<Separator className="my-4" />
<TNTWizardsStats stats={stats} />
</AccordionContent>
</CardContent>
</Card>
</AccordionItem>
)
}

View File

@@ -0,0 +1,102 @@
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
import { formatNumber } from "@/lib/formatters"
import { devide, romanize } from "@/lib/hypixel/general"
import { getTNTGameMode, getTNTModeStats } from "@/lib/hypixel/tnt-games/general"
import { NonNullStats } from "@/lib/schema/player"
import { GenericProgressNoTooltip } from "../../_components/GenericProgress"
import { BasicStat } from "../../_components/Stats"
export default function TNTWizardsStats({ stats }: { stats: NonNullable<NonNullStats["TNTGames"]> }) {
return (
<div className="space-y-4">
<h2 className="text-xl font-bold">TNT Wizards</h2>
<div>
<BasicStat title="Wins: " value={formatNumber(stats.wins_capture)} />
<BasicStat title="Kills: " value={formatNumber(stats.kills_capture)} />
<BasicStat title="Deaths: " value={formatNumber(stats.deaths_capture)} />
<BasicStat title="Kill/Death Ratio: " value={formatNumber(devide(stats.kills_capture, stats.deaths_capture))} />
</div>
<Table>
<TNTWizardsTableHeader />
<TableBody>
<TableStats id="ancient" stats={stats} />
<TableStats id="blood" stats={stats} />
<TableStats id="fire" stats={stats} />
<TableStats id="hydro" stats={stats} />
<TableStats id="ice" stats={stats} />
<TableStats id="kinetic" stats={stats} />
<TableStats id="storm" stats={stats} />
<TableStats id="toxic" stats={stats} />
<TableStats id="wither" stats={stats} />
</TableBody>
</Table>
</div>
)
}
function TableStats({ id, stats }: { id: Parameters<typeof getTNTGameMode>[0], stats: NonNullable<NonNullStats["TNTGames"]> }) {
const gm = getTNTGameMode(id)
const modeStats = getTNTModeStats(id, stats)
return (
<TableRow>
<TableCell className={`text-mc-${gm.color}`}>{gm.name}</TableCell>
{modeStats.map((m, i) => {
if (i === modeStats.length - 2) {
if (m === 0) {
return <TableCell key={i}>?</TableCell>
}
return (
<TableCell key={i}>
{
<GenericProgressNoTooltip
text={romanize(m)}
percent={m / 7 * 100}
className={`bg-mc-${gm.color}`}
/>
}
</TableCell>
)
}
if (i === modeStats.length - 1) {
if (m === 0) {
return <TableCell key={i}>?</TableCell>
}
return (
<TableCell key={i}>
{
<GenericProgressNoTooltip
text={romanize(m)}
percent={m / 7 * 100}
className={`bg-mc-${gm.color}`}
/>
}
</TableCell>
)
}
return <TableCell key={i}>{formatNumber(m)}</TableCell>
})}
</TableRow>
)
}
function TNTWizardsTableHeader() {
const headerElements = [
"Wizard",
"Kills",
"Deaths",
"Assists",
"KD",
"Power",
"Regen"
]
return (
<TableHeader>
<TableRow>
{headerElements.map((v, i) => {
return <TableHead key={i} className="font-bold">{v}</TableHead>
})}
</TableRow>
</TableHeader>
)
}

View File

@@ -16,6 +16,7 @@ import DuelsStats from "./_stats/duels/duels"
import MurderMysteryStats from "./_stats/murder-mystery/murder-mystery" import MurderMysteryStats from "./_stats/murder-mystery/murder-mystery"
import PitStats from "./_stats/pit/pit" import PitStats from "./_stats/pit/pit"
import SkyWarsStats from "./_stats/skywars/skywars" import SkyWarsStats from "./_stats/skywars/skywars"
import TNTGamesStats from "./_stats/tnt-games/tnt-games"
import UHCStats from "./_stats/uhc/uhc" import UHCStats from "./_stats/uhc/uhc"
export async function generateMetadata({ params }: { params: Promise<{ ign: string }> }): Promise<Metadata> { export async function generateMetadata({ params }: { params: Promise<{ ign: string }> }): Promise<Metadata> {
@@ -116,6 +117,7 @@ async function SuspendedPage({ ign: pign }: { ign: string }) {
<BuildBattleStats stats={player.stats.BuildBattle} /> <BuildBattleStats stats={player.stats.BuildBattle} />
<UHCStats stats={player.stats.UHC} /> <UHCStats stats={player.stats.UHC} />
<PitStats stats={player.stats.Pit} /> <PitStats stats={player.stats.Pit} />
<TNTGamesStats stats={player.stats.TNTGames} />
</Accordion> </Accordion>
</div> </div>
) : ) :

View File

@@ -0,0 +1,11 @@
export const WIZARDS = [
{ id: "ancient", name: "Ancient", color: "gold" },
{ id: "blood", name: "Blood", color: "dark-red" },
{ id: "fire", name: "Fire", color: "red" },
{ id: "hydro", name: "Hydro", color: "dark-blue" },
{ id: "ice", name: "Ice", color: "blue" },
{ id: "kinetic", name: "Kinetic", color: "light-purple" },
{ id: "storm", name: "Storm", color: "yellow" },
{ id: "toxic", name: "Toxic", color: "green" },
{ id: "wither", name: "Wither", color: "black" }
] as const

View File

@@ -0,0 +1,18 @@
import { WIZARDS } from "@/data/hypixel/tnt-games"
import { NonNullStats } from "@/lib/schema/player"
import { devide } from "../general"
export function getTNTGameMode(id: typeof WIZARDS[number]["id"]) {
return WIZARDS.find(w => w.id === id)!
}
export function getTNTModeStats(id: typeof WIZARDS[number]["id"], stats: NonNullable<NonNullStats["TNTGames"]>) {
return [
stats[`new_${id}wizard_kills`],
stats[`new_${id}wizard_deaths`],
stats[`new_${id}wizard_assists`],
devide(stats[`new_${id}wizard_kills`], stats[`new_${id}wizard_deaths`]),
stats[`new_${id}wizard_explode`],
stats[`new_${id}wizard_regen`]
]
}

View File

@@ -6,6 +6,7 @@ import {
murderMysteryStatsSchema, murderMysteryStatsSchema,
pitStats, pitStats,
skywarsStatsSchema, skywarsStatsSchema,
tntGamesStatsSchema,
uhcSchema uhcSchema
} from "./stats" } from "./stats"
@@ -28,7 +29,8 @@ export const playerSchema = z.looseObject({
MurderMystery: murderMysteryStatsSchema.optional(), MurderMystery: murderMysteryStatsSchema.optional(),
BuildBattle: buildBattleStatsSchema.optional(), BuildBattle: buildBattleStatsSchema.optional(),
UHC: uhcSchema.optional(), UHC: uhcSchema.optional(),
Pit: pitStats.optional() Pit: pitStats.optional(),
TNTGames: tntGamesStatsSchema.optional()
}).optional(), }).optional(),
quests: z.record( quests: z.record(
z.string(), z.string(),

View File

@@ -503,3 +503,53 @@ export const pitStats = z.looseObject({
...pit_stats_ptl, ...pit_stats_ptl,
...rest ...rest
})) }))
function tntGamesModeStats() {
const ids = [
"ancient",
"blood",
"fire",
"hydro",
"ice",
"kinetic",
"storm",
"toxic",
"wither"
] as const
const stats = [
"kills",
"deaths",
"assists",
"explode",
"regen"
] as const
const entries = new Map<string, z.ZodDefault<z.ZodNumber>>()
for (const id of ids) {
for (const stat of stats) {
entries.set(`new_${id}wizard_${stat}`, z.number().default(0))
}
}
return Object.fromEntries(entries) as Record<`new_${typeof ids[number]}wizard_${typeof stats[number]}`, z.ZodDefault<z.ZodNumber>>
}
export const tntGamesStatsSchema = z.looseObject({
wins: z.number().default(0),
coins: z.number().default(0),
kills_tntag: z.number().default(0),
wins_tntag: z.number().default(0),
wins_tntrun: z.number().default(0),
record_tntrun: z.number().default(0),
kills_pvprun: z.number().default(0),
wins_pvprun: z.number().default(0),
record_pvprun: z.number().default(0),
wins_bowspleef: z.number().default(0),
deaths_bowspleef: z.number().default(0),
wins_capture: z.number().default(0),
kills_capture: z.number().default(0),
deaths_capture: z.number().default(0),
...tntGamesModeStats()
})