diff --git a/src/app/(stats)/player/[ign]/_stats/murder-mystery/murder-mystery.tsx b/src/app/(stats)/player/[ign]/_stats/murder-mystery/murder-mystery.tsx
index 3cf858d..904d87b 100644
--- a/src/app/(stats)/player/[ign]/_stats/murder-mystery/murder-mystery.tsx
+++ b/src/app/(stats)/player/[ign]/_stats/murder-mystery/murder-mystery.tsx
@@ -5,6 +5,7 @@ import { formatNumber } from "@/lib/formatters"
import { NonNullStats } from "@/lib/schema/player"
import CollapsedStats from "../../_components/CollapsedStats"
import MurderMysteryGeneralStats from "./stats"
+import MurderMysteryStatTable from "./table"
export default function MurderMysteryStats({ stats }: { stats: NonNullStats["MurderMystery"] }) {
if (!stats) return null
@@ -33,6 +34,9 @@ export default function MurderMysteryStats({ stats }: { stats: NonNullStats["Mur
+
+
+
diff --git a/src/app/(stats)/player/[ign]/_stats/murder-mystery/table.tsx b/src/app/(stats)/player/[ign]/_stats/murder-mystery/table.tsx
new file mode 100644
index 0000000..2d71406
--- /dev/null
+++ b/src/app/(stats)/player/[ign]/_stats/murder-mystery/table.tsx
@@ -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 (
+
+ )
+}
+
+function MurderMysteryTableHeader() {
+ const headerElements = [
+ "Mode",
+ "Kills",
+ "Bow Kills",
+ "Knife Kills",
+ "Thrown Knife Kills",
+ "Wins",
+ "Losses",
+ "WL",
+ "Gold Collected"
+ ]
+
+ return (
+
+
+ {headerElements.map((v, i) => {
+ return {v}
+ })}
+
+
+ )
+}
+
+function MurderMysteryStat({ modeId, stats }: { modeId: Parameters[0], stats: NonNullStats["MurderMystery"] }) {
+ if (!stats) return null
+
+ const title = getModeTitle(modeId)
+ const modeStats = getMurderMysteryModeStats(modeId, stats)
+ const bestMode = getBestMurderMysteryMode(stats) === modeId
+
+ return (
+
+ {title}
+ {modeStats.map((v, i) => (
+
+ {formatNumber(v)}
+
+ ))}
+
+ )
+}
diff --git a/src/data/hypixel/murder-mystery.ts b/src/data/hypixel/murder-mystery.ts
index d5323ba..08af9f5 100644
--- a/src/data/hypixel/murder-mystery.ts
+++ b/src/data/hypixel/murder-mystery.ts
@@ -1,10 +1,10 @@
export const TITLE = "Murder Mystery" as const
export const MODES = [
- { id: "_MURDER_CLASSIC", name: "Classic" },
- { id: "_MURDER_ASSASSINS", name: "Assassins" },
- { id: "_MURDER_DOUBLE_UP", name: "Double Up" },
- { id: "_MURDER_HARDCORE", name: "Hardcore" },
- { id: "_MURDER_SHOWDOWN", name: "Showdown" },
+ { id: "MURDER_CLASSIC", name: "Classic" },
+ { id: "MURDER_ASSASSINS", name: "Assassins" },
+ { id: "MURDER_DOUBLE_UP", name: "Double Up" },
+ { id: "MURDER_HARDCORE", name: "Hardcore" },
+ { id: "MURDER_SHOWDOWN", name: "Showdown" },
{ id: "", name: "Overall" }
] as const
export const KNIFESKINS = {
diff --git a/src/lib/hypixel/murder-mystery/general.ts b/src/lib/hypixel/murder-mystery/general.ts
index d8b0254..71b1b9b 100644
--- a/src/lib/hypixel/murder-mystery/general.ts
+++ b/src/lib/hypixel/murder-mystery/general.ts
@@ -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) {
if (!name) return KNIFESKINS.undefined
@@ -9,3 +11,60 @@ export function getKnifeName(name?: string) {
return skin
}
+
+export function getModeTitle(mode: Exclude | "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) {
+ 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 | "all_modes",
+ stats: NonNullable
+) {
+ return murderMysteryModeStats(index, stats)
+}
+
+function murderMysteryModeStats(index: Exclude | "all_modes", stats: NonNullable) {
+ 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}`]
+ ]
+}
diff --git a/src/lib/schema/stats.ts b/src/lib/schema/stats.ts
index f476967..a0e35b6 100644
--- a/src/lib/schema/stats.ts
+++ b/src/lib/schema/stats.ts
@@ -331,15 +331,50 @@ export const duelsStatsSchema = z.looseObject({
...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>()
+
+ 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>
+}
+
export const murderMysteryStatsSchema = z.looseObject({
kills: z.number().default(0),
deaths: z.number().default(0),
wins: z.number().default(0),
losses: 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),
thrown_knife_kills: z.number().default(0),
active_knife_skin: z.string().optional(),
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()
})