Added first skywars stats
This commit is contained in:
@@ -18,7 +18,7 @@ type SidebarProps = {
|
||||
|
||||
export default function Sidebar({ level, ign, player, guild }: SidebarProps) {
|
||||
return (
|
||||
<Card className="overflow-hidden w-1/4 max-h-fit">
|
||||
<Card className="w-1/4">
|
||||
<CardContent>
|
||||
<div className="flex justify-between px-8">
|
||||
<div className="text-center">
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
import { getSkyWarsIcon, getTextColor } from "@/lib/hypixel/skywars"
|
||||
import { getSkywarsLevel } from "@/lib/hypixel/skyWarsLevel"
|
||||
|
||||
export function SkywarsLevel({ xp, icon }: { xp: number, icon: string | undefined }) {
|
||||
const level = getSkywarsLevel(xp)
|
||||
const colors = getTextColor(Math.floor(level))
|
||||
const swIcon = getSkyWarsIcon(icon)
|
||||
const val = `${Math.floor(level)}${swIcon}`
|
||||
|
||||
if (level > 150) {
|
||||
return (
|
||||
<p className="font-bold">
|
||||
<span className={`text-mc-${colors.brackets}`}>[</span>
|
||||
<span
|
||||
style={{
|
||||
backgroundImage: "linear-gradient(to left,#a0a,#f5f,#5ff,#5f5,#ff5,#fa0,#f55)",
|
||||
WebkitBackgroundClip: "text",
|
||||
color: "transparent"
|
||||
}}
|
||||
>
|
||||
{`${val}`}
|
||||
</span>
|
||||
<span className={`text-mc-${colors.brackets}`}>]</span>
|
||||
</p>
|
||||
)
|
||||
}
|
||||
|
||||
if (level === 50) {
|
||||
<p>
|
||||
<span className={`text-mc-${colors.brackets}`}>[</span>
|
||||
<span
|
||||
style={{
|
||||
backgroundImage: "linear-gradient(to left,#a0a,#f5f,#5ff,#5f5,#ff5,#fa0,#f55)",
|
||||
WebkitBackgroundClip: "text",
|
||||
color: "transparent"
|
||||
}}
|
||||
>
|
||||
{`${val}`}
|
||||
</span>
|
||||
<span className={`text-mc-${colors.brackets}`}>]</span>
|
||||
</p>
|
||||
}
|
||||
|
||||
return (
|
||||
<p>
|
||||
<span className={`text-mc-${colors.brackets}`}>[</span>
|
||||
<span className={`text-mc-${colors.text}`}>{val}</span>
|
||||
<span className={`text-mc-${colors.brackets}`}>]</span>
|
||||
</p>
|
||||
)
|
||||
}
|
||||
81
src/app/(stats)/player/[ign]/_stats/skywars/skywars.tsx
Normal file
81
src/app/(stats)/player/[ign]/_stats/skywars/skywars.tsx
Normal file
@@ -0,0 +1,81 @@
|
||||
"use client"
|
||||
|
||||
import { Card, CardContent } from "@/components/ui/card"
|
||||
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible"
|
||||
import { Separator } from "@/components/ui/separator"
|
||||
import { formatNumber } from "@/lib/formatters"
|
||||
import { Player } from "@/lib/schema/player"
|
||||
import { ChevronDown, ChevronUp } from "lucide-react"
|
||||
import { useEffect, useRef, useState } from "react"
|
||||
import CollapsedStats from "../../_components/CollapsedStats"
|
||||
import { SkywarsLevel } from "./skywars-components"
|
||||
|
||||
export default function SkyWarsStats({ stats }: { stats: Player["player"]["stats"]["SkyWars"] }) {
|
||||
const ref = useRef<HTMLDivElement>(null)
|
||||
const [opened, setOpened] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
if (!ref.current) return
|
||||
|
||||
const observer = new MutationObserver((mutations) => {
|
||||
mutations.forEach((mutation) => {
|
||||
if (mutation.type === "attributes" && mutation.attributeName === "data-state") {
|
||||
const dataState = ref.current?.getAttribute("data-state")
|
||||
setOpened(dataState === "open")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
observer.observe(ref.current, {
|
||||
attributes: true,
|
||||
attributeFilter: ["data-state"]
|
||||
})
|
||||
|
||||
return () => observer.disconnect()
|
||||
}, [])
|
||||
|
||||
if (!stats) return null
|
||||
|
||||
const kd = (stats.kills / stats.deaths).toFixed(2)
|
||||
const wl = (stats.wins / stats.losses).toFixed(2)
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardContent>
|
||||
<Collapsible ref={ref}>
|
||||
<div className="flex justify-between">
|
||||
<h1 className="text-xl font-bold">SkyWars</h1>
|
||||
<div className="flex gap-4">
|
||||
<CollapsedStats
|
||||
stats={[
|
||||
{
|
||||
title: <p>Level</p>,
|
||||
stat: <SkywarsLevel xp={stats.skywars_experience} icon={stats.selected_prestige_icon} />
|
||||
},
|
||||
{
|
||||
title: <p>KD</p>,
|
||||
stat: <p>{kd}</p>
|
||||
},
|
||||
{
|
||||
title: <p>Wins</p>,
|
||||
stat: <p>{formatNumber(stats.wins)}</p>
|
||||
},
|
||||
{
|
||||
title: <p>WL</p>,
|
||||
stat: <p>{wl}</p>
|
||||
}
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
<CollapsibleTrigger className="transition-all">
|
||||
{opened === false ? <ChevronDown /> : <ChevronUp />}
|
||||
</CollapsibleTrigger>
|
||||
</div>
|
||||
<CollapsibleContent>
|
||||
<Separator className="my-4" />
|
||||
</CollapsibleContent>
|
||||
</Collapsible>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import { Loader2Icon } from "lucide-react"
|
||||
import { Suspense } from "react"
|
||||
import Sidebar from "./_components/Sidebar"
|
||||
import BedwarsStats from "./_stats/bedwars/bedwars"
|
||||
import SkyWarsStats from "./_stats/skywars/skywars"
|
||||
|
||||
export default async function PlayerPage({
|
||||
params
|
||||
@@ -73,6 +74,7 @@ async function SuspendedPage({ ign: pign }: { ign: string }) {
|
||||
<Sidebar level={level} ign={pign} player={player} guild={guild ?? undefined} />
|
||||
<div className="space-y-4 w-3/4">
|
||||
<BedwarsStats stats={player.stats.Bedwars} />
|
||||
<SkyWarsStats stats={player.stats.SkyWars} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
109
src/data/hypixel/skywars.ts
Normal file
109
src/data/hypixel/skywars.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
export const TITLE = "SkyWars"
|
||||
export const INITIAL_XP = [0, 20, 70, 150, 250, 500, 1000, 2000, 3500, 6000, 10000, 15000]
|
||||
export const RECURRING_XP = 10000
|
||||
|
||||
export const PRESTIGES = [
|
||||
{ level: 0, color: "gray", b_color: "gray", name: "None" },
|
||||
{ level: 5, color: "white", b_color: "white", name: "Iron" },
|
||||
{ level: 10, color: "gold", b_color: "gold", name: "Gold" },
|
||||
{ level: 15, color: "aqua", b_color: "aqua", name: "Diamond" },
|
||||
{ level: 20, color: "dark-green", b_color: "dark-green", name: "Emerald" },
|
||||
{ level: 25, color: "dark-aqua", b_color: "dark-aqua", name: "Sapphire" },
|
||||
{ level: 30, color: "dark-red", b_color: "dark-red", name: "Ruby" },
|
||||
{ level: 35, color: "light-purple", b_color: "light-purple", name: "Crystal" },
|
||||
{ level: 40, color: "blue", b_color: "blue", name: "Opal" },
|
||||
{ level: 45, color: "purple", b_color: "purple", name: "Amethyst" },
|
||||
{ level: 50, color: "rainbow", b_color: "red", name: "Rainbow" },
|
||||
{ level: 55, color: "white", b_color: "gray", name: "First Class" },
|
||||
{ level: 60, color: "red", b_color: "dark-red", name: "Assassin" },
|
||||
{ level: 65, color: "white", b_color: "red", name: "Veteran" },
|
||||
{ level: 70, color: "gold", b_color: "yellow", name: "God Like" },
|
||||
{ level: 75, color: "blue", b_color: "white", name: "Warrior" },
|
||||
{ level: 80, color: "aqua", b_color: "white", name: "Captain" },
|
||||
{ level: 85, color: "dark-aqua", b_color: "white", name: "Soldier" },
|
||||
{ level: 90, color: "dark-aqua", b_color: "green", name: "Infantry" },
|
||||
{ level: 95, color: "yellow", b_color: "red", name: "Sergeant" },
|
||||
{ level: 100, color: "dark-blue", b_color: "blue", name: "Lieutenant" },
|
||||
{ level: 105, color: "dark-red", b_color: "gold", name: "Admiral" },
|
||||
{ level: 110, color: "aqua", b_color: "dark-blue", name: "General" },
|
||||
{ level: 115, color: "gray", b_color: "dark-gray", name: "Villain" },
|
||||
{ level: 120, color: "purple", b_color: "light-purple", name: "Skilled" },
|
||||
{ level: 125, color: "yellow", b_color: "white", name: "Sneaky" },
|
||||
{ level: 130, color: "yellow", b_color: "red", name: "Overlord" },
|
||||
{ level: 135, color: "red", b_color: "gold", name: "War Chief" },
|
||||
{ level: 140, color: "red", b_color: "green", name: "Warlock" },
|
||||
{ level: 145, color: "aqua", b_color: "green", name: "Emperor" },
|
||||
{ level: 150, color: "rainbow", b_color: "red", name: "Mythic" }
|
||||
]
|
||||
|
||||
export const ICONS = {
|
||||
default: "\u22c6", // ⋆
|
||||
angel_1: "\u2605", // ★
|
||||
angel_2: "\u2606", // ☆
|
||||
angel_3: "\u2055", // ⁕
|
||||
angel_4: "\u2736", // ✶
|
||||
angel_5: "\u2733", // ✳
|
||||
angel_6: "\u2734", // ✴
|
||||
angel_7: "\u2737", // ✷
|
||||
angel_8: "\u274b", // ❋
|
||||
angel_9: "\u273c", // ✼
|
||||
angel_10: "\u2742", // ❂
|
||||
angel_11: "\u2741", // ❁
|
||||
angel_12: "\u262c", // ☬
|
||||
iron_prestige: "\u2719", // ✙
|
||||
gold_prestige: "\u2764", // ❤
|
||||
diamond_prestige: "\u2620", // ☠
|
||||
emerald_prestige: "\u2726", // ✦
|
||||
sapphire_prestige: "\u270c", // ✌
|
||||
ruby_prestige: "\u2766", // ❦
|
||||
crystal_prestige: "\u2735", // ✵
|
||||
opal_prestige: "\u2763", // ❣
|
||||
amethyst_prestige: "\u262f", // ☯
|
||||
rainbow_prestige: "\u273a", // ✺
|
||||
first_class_prestige: "\u2708", // ✈
|
||||
assassin_prestige: "\u26b0", // ⚰
|
||||
veteran_prestige: "\u2720", // ✠
|
||||
god_like_prestige: "\u2655", // ♕
|
||||
warrior_prestige: "\u26a1", // ⚡
|
||||
captain_prestige: "\u2042", // ⁂
|
||||
soldier_prestige: "\u2730", // ✰
|
||||
infantry_prestige: "\u2051", // ⁑
|
||||
sergeant_prestige: "\u2622", // ☢
|
||||
lieutenant_prestige: "\u2725", // ✥
|
||||
admiral_prestige: "\u265d", // ♝
|
||||
general_prestige: "\u2646", // ♆
|
||||
villain_prestige: "\u2601", // ☁
|
||||
skilled_prestige: "\u235f", // ⍟
|
||||
sneaky_prestige: "\u2657", // ♗
|
||||
overlord_prestige: "<Error icon=\"overlord\"/>", //
|
||||
war_chief_prestige: "\u265e", // ♞
|
||||
warlock_prestige: "<Error icon=\"warlock\"/>", //
|
||||
emperor_prestige: "\u2748", // ❈
|
||||
mythic_prestige: "\u0ca0_\u0ca0", // ಠ_ಠ
|
||||
favor_icon: "\u2694", // ⚔
|
||||
omega_icon: "\u03a9" // Ω
|
||||
}
|
||||
|
||||
export const MODES = [
|
||||
{ id: "_ranked", name: "Ranked" },
|
||||
{ id: "_solo_normal", name: "Solo Normal" },
|
||||
{ id: "_solo_insane", name: "Solo Insane" },
|
||||
{ id: "_team_normal", name: "Teams Normal" },
|
||||
{ id: "_team_insane", name: "Teams Insane" },
|
||||
{ id: "_mega_normal", name: "Mega" },
|
||||
{ id: "_mega_doubles", name: "Mega Doubles" },
|
||||
{ id: "", name: "Overall" }
|
||||
]
|
||||
|
||||
export const HEADS = [
|
||||
{ id: "eww", name: "Eww!", color: "darkgray" },
|
||||
{ id: "yucky", name: "Yucky!", color: "gray" },
|
||||
{ id: "meh", name: "Meh", color: "white" },
|
||||
{ id: "decent", name: "Decent", color: "yellow" },
|
||||
{ id: "salty", name: "Salty", color: "green" },
|
||||
{ id: "tasty", name: "Tasty", color: "darkaqua" },
|
||||
{ id: "succulent", name: "Succulent", color: "pink" },
|
||||
{ id: "sweet", name: "Sweet", color: "aqua" },
|
||||
{ id: "divine", name: "Divine", color: "gold" },
|
||||
{ id: "heavenly", name: "Heavenly", color: "purple" }
|
||||
]
|
||||
@@ -12,7 +12,11 @@ export async function getPlayer(uuid: string) {
|
||||
|
||||
if (!res.ok) return null
|
||||
|
||||
const { success, data } = playerSchema.safeParse(await res.json())
|
||||
const { success, data, error } = playerSchema.safeParse(await res.json())
|
||||
|
||||
if (error) {
|
||||
console.log(error)
|
||||
}
|
||||
|
||||
if (!success) return null
|
||||
|
||||
|
||||
13
src/lib/hypixel/skyWarsLevel.ts
Normal file
13
src/lib/hypixel/skyWarsLevel.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
export function getSkywarsLevel(xp: number) {
|
||||
const xps = [0, 20, 70, 150, 250, 500, 1000, 2000, 3500, 6000, 10000, 15000]
|
||||
if (xp >= 15000) {
|
||||
return (xp - 15000) / 10000 + 12
|
||||
}
|
||||
for (let i = 0; i < xps.length; i += 1) {
|
||||
if (xp < xps[i]) {
|
||||
return i + (xp - xps[i - 1]) / (xps[i] - xps[i - 1])
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
30
src/lib/hypixel/skywars.ts
Normal file
30
src/lib/hypixel/skywars.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { ICONS, PRESTIGES } from "@/data/hypixel/skywars"
|
||||
import { floorLevel } from "./formatters"
|
||||
|
||||
export function getTextColor(level: number) {
|
||||
const floored = floorLevel(level, 5)
|
||||
|
||||
if (level > 150) {
|
||||
return {
|
||||
text: PRESTIGES.at(-1)?.color ?? "gray",
|
||||
brackets: PRESTIGES.at(-1)?.b_color ?? "gray"
|
||||
}
|
||||
}
|
||||
|
||||
const pres = PRESTIGES.find(p => p.level === floored)
|
||||
|
||||
return {
|
||||
text: pres?.color ?? "gray",
|
||||
brackets: pres?.b_color ?? "gray"
|
||||
}
|
||||
}
|
||||
|
||||
export function getSkyWarsIcon(icon?: string) {
|
||||
if (!icon) {
|
||||
return ICONS.default
|
||||
}
|
||||
|
||||
const icons = ICONS as Record<string, string>
|
||||
|
||||
return icons[icon] ?? ICONS.default
|
||||
}
|
||||
@@ -189,4 +189,11 @@ export const bedwarsStatsSchema = z.looseObject({
|
||||
castle_beds_lost_bedwars: z.number().default(0)
|
||||
})
|
||||
|
||||
export const skywarsStatsSchema = z.looseObject({})
|
||||
export const skywarsStatsSchema = z.looseObject({
|
||||
skywars_experience: z.number().default(0),
|
||||
selected_prestige_icon: z.string().optional(),
|
||||
kills: z.number().default(0),
|
||||
deaths: z.number().default(0),
|
||||
wins: z.number().default(0),
|
||||
losses: z.number().default(0)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user