Added first skywars stats
This commit is contained in:
@@ -18,7 +18,7 @@ type SidebarProps = {
|
|||||||
|
|
||||||
export default function Sidebar({ level, ign, player, guild }: SidebarProps) {
|
export default function Sidebar({ level, ign, player, guild }: SidebarProps) {
|
||||||
return (
|
return (
|
||||||
<Card className="overflow-hidden w-1/4 max-h-fit">
|
<Card className="w-1/4">
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<div className="flex justify-between px-8">
|
<div className="flex justify-between px-8">
|
||||||
<div className="text-center">
|
<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 { Suspense } from "react"
|
||||||
import Sidebar from "./_components/Sidebar"
|
import Sidebar from "./_components/Sidebar"
|
||||||
import BedwarsStats from "./_stats/bedwars/bedwars"
|
import BedwarsStats from "./_stats/bedwars/bedwars"
|
||||||
|
import SkyWarsStats from "./_stats/skywars/skywars"
|
||||||
|
|
||||||
export default async function PlayerPage({
|
export default async function PlayerPage({
|
||||||
params
|
params
|
||||||
@@ -73,6 +74,7 @@ async function SuspendedPage({ ign: pign }: { ign: string }) {
|
|||||||
<Sidebar level={level} ign={pign} player={player} guild={guild ?? undefined} />
|
<Sidebar level={level} ign={pign} player={player} guild={guild ?? undefined} />
|
||||||
<div className="space-y-4 w-3/4">
|
<div className="space-y-4 w-3/4">
|
||||||
<BedwarsStats stats={player.stats.Bedwars} />
|
<BedwarsStats stats={player.stats.Bedwars} />
|
||||||
|
<SkyWarsStats stats={player.stats.SkyWars} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</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
|
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
|
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)
|
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