Updated murder mystery stats
This commit is contained in:
@@ -5,6 +5,7 @@ import { formatNumber } from "@/lib/formatters"
|
|||||||
import { NonNullStats } from "@/lib/schema/player"
|
import { NonNullStats } from "@/lib/schema/player"
|
||||||
import CollapsedStats from "../../_components/CollapsedStats"
|
import CollapsedStats from "../../_components/CollapsedStats"
|
||||||
import MurderMysteryGeneralStats from "./stats"
|
import MurderMysteryGeneralStats from "./stats"
|
||||||
|
import MurderMysteryStatTable from "./table"
|
||||||
|
|
||||||
export default function MurderMysteryStats({ stats }: { stats: NonNullStats["MurderMystery"] }) {
|
export default function MurderMysteryStats({ stats }: { stats: NonNullStats["MurderMystery"] }) {
|
||||||
if (!stats) return null
|
if (!stats) return null
|
||||||
@@ -33,6 +34,9 @@ export default function MurderMysteryStats({ stats }: { stats: NonNullStats["Mur
|
|||||||
<AccordionContent>
|
<AccordionContent>
|
||||||
<Separator className="my-4" />
|
<Separator className="my-4" />
|
||||||
<MurderMysteryGeneralStats statsChecked={stats} />
|
<MurderMysteryGeneralStats statsChecked={stats} />
|
||||||
|
<Separator className="my-4" />
|
||||||
|
<MurderMysteryStatTable stats={stats} />
|
||||||
|
<Separator className="my-4" />
|
||||||
</AccordionContent>
|
</AccordionContent>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
69
src/app/(stats)/player/[ign]/_stats/murder-mystery/table.tsx
Normal file
69
src/app/(stats)/player/[ign]/_stats/murder-mystery/table.tsx
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
|
||||||
|
import { formatNumber } from "@/lib/formatters"
|
||||||
|
import { getBestMurderMysteryMode, getModeTitle, getMurderMysteryModeStats } from "@/lib/hypixel/murder-mystery/general"
|
||||||
|
import { NonNullStats } from "@/lib/schema/player"
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
export default function MurderMysteryStatTable({ stats }: { stats: NonNullStats["MurderMystery"] }) {
|
||||||
|
return (
|
||||||
|
<Table>
|
||||||
|
<MurderMysteryTableHeader />
|
||||||
|
<TableBody>
|
||||||
|
<MurderMysteryStat modeId="MURDER_CLASSIC" stats={stats} />
|
||||||
|
<MurderMysteryStat modeId="MURDER_ASSASSINS" stats={stats} />
|
||||||
|
<MurderMysteryStat modeId="MURDER_DOUBLE_UP" stats={stats} />
|
||||||
|
<MurderMysteryStat modeId="MURDER_HARDCORE" stats={stats} />
|
||||||
|
<MurderMysteryStat modeId="MURDER_SHOWDOWN" stats={stats} />
|
||||||
|
<MurderMysteryStat modeId="all_modes" stats={stats} />
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function MurderMysteryTableHeader() {
|
||||||
|
const headerElements = [
|
||||||
|
"Mode",
|
||||||
|
"Kills",
|
||||||
|
"Bow Kills",
|
||||||
|
"Knife Kills",
|
||||||
|
"Thrown Knife Kills",
|
||||||
|
"Wins",
|
||||||
|
"Losses",
|
||||||
|
"WL",
|
||||||
|
"Gold Collected"
|
||||||
|
]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TableHeader>
|
||||||
|
<TableRow>
|
||||||
|
{headerElements.map((v, i) => {
|
||||||
|
return <TableHead key={i} className="font-bold">{v}</TableHead>
|
||||||
|
})}
|
||||||
|
</TableRow>
|
||||||
|
</TableHeader>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function MurderMysteryStat({ modeId, stats }: { modeId: Parameters<typeof getMurderMysteryModeStats>[0], stats: NonNullStats["MurderMystery"] }) {
|
||||||
|
if (!stats) return null
|
||||||
|
|
||||||
|
const title = getModeTitle(modeId)
|
||||||
|
const modeStats = getMurderMysteryModeStats(modeId, stats)
|
||||||
|
const bestMode = getBestMurderMysteryMode(stats) === modeId
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TableRow
|
||||||
|
className={cn(
|
||||||
|
modeId === "all_modes" ? "font-bold" : undefined,
|
||||||
|
bestMode === true ? "font-bold text-mc-light-purple" : undefined
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<TableCell>{title}</TableCell>
|
||||||
|
{modeStats.map((v, i) => (
|
||||||
|
<TableCell key={i}>
|
||||||
|
{formatNumber(v)}
|
||||||
|
</TableCell>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
export const TITLE = "Murder Mystery" as const
|
export const TITLE = "Murder Mystery" as const
|
||||||
export const MODES = [
|
export const MODES = [
|
||||||
{ id: "_MURDER_CLASSIC", name: "Classic" },
|
{ id: "MURDER_CLASSIC", name: "Classic" },
|
||||||
{ id: "_MURDER_ASSASSINS", name: "Assassins" },
|
{ id: "MURDER_ASSASSINS", name: "Assassins" },
|
||||||
{ id: "_MURDER_DOUBLE_UP", name: "Double Up" },
|
{ id: "MURDER_DOUBLE_UP", name: "Double Up" },
|
||||||
{ id: "_MURDER_HARDCORE", name: "Hardcore" },
|
{ id: "MURDER_HARDCORE", name: "Hardcore" },
|
||||||
{ id: "_MURDER_SHOWDOWN", name: "Showdown" },
|
{ id: "MURDER_SHOWDOWN", name: "Showdown" },
|
||||||
{ id: "", name: "Overall" }
|
{ id: "", name: "Overall" }
|
||||||
] as const
|
] as const
|
||||||
export const KNIFESKINS = {
|
export const KNIFESKINS = {
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import { KNIFESKINS } from "@/data/hypixel/murder-mystery"
|
import { KNIFESKINS, MODES } from "@/data/hypixel/murder-mystery"
|
||||||
|
import { NonNullStats } from "@/lib/schema/player"
|
||||||
|
import { devide } from "../general"
|
||||||
|
|
||||||
export function getKnifeName(name?: string) {
|
export function getKnifeName(name?: string) {
|
||||||
if (!name) return KNIFESKINS.undefined
|
if (!name) return KNIFESKINS.undefined
|
||||||
@@ -9,3 +11,60 @@ export function getKnifeName(name?: string) {
|
|||||||
|
|
||||||
return skin
|
return skin
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getModeTitle(mode: Exclude<typeof MODES[number]["id"], ""> | "all_modes") {
|
||||||
|
if (mode === "all_modes") return MODES.find(m => m.id === "")!.name
|
||||||
|
|
||||||
|
return MODES.find(m => m.id === mode)!.name
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getBestMurderMysteryMode(stats: NonNullable<NonNullStats["MurderMystery"]>) {
|
||||||
|
let best: typeof MODES[number] | null = null
|
||||||
|
let mostPlays = 0
|
||||||
|
for (const mode of MODES.filter(m => m.id !== "")) {
|
||||||
|
const [, , , wins, losses] = getMurderMysteryModeStats(mode.id, stats)
|
||||||
|
const plays = (wins as number || 0) + (losses as number || 0)
|
||||||
|
if (plays > mostPlays) {
|
||||||
|
mostPlays = plays
|
||||||
|
best = mode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return best === null ? null : best.id
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getMurderMysteryModeStats(
|
||||||
|
index: Exclude<typeof MODES[number]["id"], ""> | "all_modes",
|
||||||
|
stats: NonNullable<NonNullStats["MurderMystery"]>
|
||||||
|
) {
|
||||||
|
return murderMysteryModeStats(index, stats)
|
||||||
|
}
|
||||||
|
|
||||||
|
function murderMysteryModeStats(index: Exclude<typeof MODES[number]["id"], ""> | "all_modes", stats: NonNullable<NonNullStats["MurderMystery"]>) {
|
||||||
|
if (index === "all_modes") {
|
||||||
|
const losses = stats["games"] - stats["wins"]
|
||||||
|
|
||||||
|
return [
|
||||||
|
stats["kills"],
|
||||||
|
stats["bow_kills"],
|
||||||
|
stats["knife_kills"],
|
||||||
|
stats["thrown_knife_kills"],
|
||||||
|
stats["wins"],
|
||||||
|
losses,
|
||||||
|
devide(stats["wins"], losses),
|
||||||
|
stats["coins_pickedup"]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
const losses = stats[`games_${index}`] - stats[`wins_${index}`]
|
||||||
|
|
||||||
|
return [
|
||||||
|
stats[`kills_${index}`],
|
||||||
|
stats[`bow_kills_${index}`],
|
||||||
|
stats[`knife_kills_${index}`],
|
||||||
|
stats[`thrown_knife_kills_${index}`],
|
||||||
|
stats[`wins_${index}`],
|
||||||
|
losses,
|
||||||
|
devide(stats[`wins_${index}`], losses),
|
||||||
|
stats[`coins_pickedup_${index}`]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|||||||
@@ -331,15 +331,50 @@ export const duelsStatsSchema = z.looseObject({
|
|||||||
...duelsModeStats().bridge
|
...duelsModeStats().bridge
|
||||||
})
|
})
|
||||||
|
|
||||||
|
function murderMysteryModeStats() {
|
||||||
|
const ids = [
|
||||||
|
"MURDER_CLASSIC",
|
||||||
|
"MURDER_ASSASSINS",
|
||||||
|
"MURDER_DOUBLE_UP",
|
||||||
|
"MURDER_HARDCORE",
|
||||||
|
"MURDER_SHOWDOWN"
|
||||||
|
] as const
|
||||||
|
|
||||||
|
const stats = [
|
||||||
|
"kills",
|
||||||
|
"bow_kills",
|
||||||
|
"knife_kills",
|
||||||
|
"thrown_knife_kills",
|
||||||
|
"wins",
|
||||||
|
"games",
|
||||||
|
"coins_pickedup"
|
||||||
|
] as const
|
||||||
|
|
||||||
|
const entries = new Map<string, z.ZodDefault<z.ZodNumber>>()
|
||||||
|
|
||||||
|
for (const id of ids) {
|
||||||
|
for (const stat of stats) {
|
||||||
|
entries.set(`${stat}_${id}`, z.number().default(0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Object.fromEntries(entries) as Record<`${typeof stats[number]}_${typeof ids[number]}`, z.ZodDefault<z.ZodNumber>>
|
||||||
|
}
|
||||||
|
|
||||||
export const murderMysteryStatsSchema = z.looseObject({
|
export const murderMysteryStatsSchema = z.looseObject({
|
||||||
kills: z.number().default(0),
|
kills: z.number().default(0),
|
||||||
deaths: z.number().default(0),
|
deaths: z.number().default(0),
|
||||||
wins: z.number().default(0),
|
wins: z.number().default(0),
|
||||||
losses: z.number().default(0),
|
losses: z.number().default(0),
|
||||||
coins: z.number().default(0),
|
coins: z.number().default(0),
|
||||||
|
games: z.number().default(0),
|
||||||
|
bow_kills: z.number().default(0),
|
||||||
|
knife_kills: z.number().default(0),
|
||||||
|
coins_pickedup: z.number().default(0),
|
||||||
kills_as_murderer: z.number().default(0),
|
kills_as_murderer: z.number().default(0),
|
||||||
thrown_knife_kills: z.number().default(0),
|
thrown_knife_kills: z.number().default(0),
|
||||||
active_knife_skin: z.string().optional(),
|
active_knife_skin: z.string().optional(),
|
||||||
quickest_detective_win_time_seconds: z.number().default(0),
|
quickest_detective_win_time_seconds: z.number().default(0),
|
||||||
quickest_murderer_win_time_seconds: z.number().default(0)
|
quickest_murderer_win_time_seconds: z.number().default(0),
|
||||||
|
...murderMysteryModeStats()
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user