Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/discord-bot/src/commands/duels/duels.profile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ export const DuelsProfile = <T extends ProfileTime>({
duels[mode.api].titleFormatted
}\n${formatProgression({
t,
label: t("stats.progression.win"),
label: t(mode.api === "arena" ? "stats.progression.kill" : "stats.progression.win"),
progression: duels[mode.api].progression,
currentLevel: duels[mode.api].titleLevelFormatted,
nextLevel: duels[mode.api].nextTitleLevelFormatted,
Expand Down
81 changes: 54 additions & 27 deletions apps/discord-bot/src/commands/duels/tables/titles.table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,70 +6,97 @@
* https://github.com/Statsify/statsify/blob/main/LICENSE
*/

import * as Schemas from "@statsify/schemas";
import { arrayGroup } from "@statsify/util";
import type { Duels } from "@statsify/schemas";
import type { DuelsModeIcons } from "../duels.command.js";
import type { Image } from "skia-canvas";
import type { LocalizeFunction } from "@statsify/discord";

interface TitlesTableProps {
duels: Duels;
duels: Schemas.Duels;
t: LocalizeFunction;
modeIcons: DuelsModeIcons;
}

function ModeTitle({ icon, title, wins, t }: { icon: Image; title: string; wins: number; t: LocalizeFunction }) {
function ModeTitle({ icon, title, score, t }: { icon: Image; title: string; score: number; t: LocalizeFunction }) {
return (
<box width="100%" padding={{ left: 8, right: 8, top: 4, bottom: 4 }}>
<img image={icon} width={32} height={32} />
<text margin={{ left: 8 }}>
{title}
</text>
<div width="remaining" margin={{ left: 4, right: 4 }} />
<text>{t(wins)}</text>
<text>{t(score)}</text>
</box>
);
}

export const TitlesTable = ({ duels, t, modeIcons }: TitlesTableProps) => {
const titleData = { active_prefix_icon: duels.icon, active_prefix_scheme: duels.scheme };
const getBaseTitle = (score: number, mode: string, titleRequirement: Schemas.TitleRequirement) =>
Schemas.getTitleAndProgression({
score,
mode,
data: titleData,
titleRequirement,
scheme: "default",
}).titleFormatted;

const titleSortScore = (score: number, titleRequirement: Schemas.TitleRequirement) => {
if (titleRequirement === "half") return score * 2;
if (titleRequirement === "overall") return score / 2;
return score;
};

const games = [
{ icon: modeIcons.bedwars, title: duels.bedwars.titleFormatted, wins: duels.bedwars.overall.wins },
{ icon: modeIcons.blitzsg, title: duels.blitzsg.titleFormatted, wins: duels.blitzsg.wins },
{ icon: modeIcons.bow, title: duels.bow.titleFormatted, wins: duels.bow.wins },
{ icon: modeIcons.spleef, title: duels.spleef.titleFormatted, wins: duels.spleef.overallWins },
{ icon: modeIcons.boxing, title: duels.boxing.titleFormatted, wins: duels.boxing.wins },
{ icon: modeIcons.bridge, title: duels.bridge.titleFormatted, wins: duels.bridge.overall.wins },
{ icon: modeIcons.classic, title: duels.classic.titleFormatted, wins: duels.classic.overall.wins },
{ icon: modeIcons.combo, title: duels.combo.titleFormatted, wins: duels.combo.wins },
{ icon: modeIcons.megawalls, title: duels.megawalls.titleFormatted, wins: duels.megawalls.wins },
{ icon: modeIcons.nodebuff, title: duels.nodebuff.titleFormatted, wins: duels.nodebuff.wins },
{ icon: modeIcons.op, title: duels.op.titleFormatted, wins: duels.op.overall.wins },
{ icon: modeIcons.quake, title: duels.quake.titleFormatted, wins: duels.quake.wins },
{ icon: modeIcons.parkour, title: duels.parkour.titleFormatted, wins: duels.parkour.wins },
{ icon: modeIcons.skywars, title: duels.skywars.titleFormatted, wins: duels.skywars.overall.wins },
{ icon: modeIcons.sumo, title: duels.sumo.titleFormatted, wins: duels.sumo.wins },
{ icon: modeIcons.uhc, title: duels.uhc.titleFormatted, wins: duels.uhc.overall.wins },
];
{ icon: modeIcons.arena, mode: "Duel Arena", score: duels.arena.kills, titleRequirement: "default" },
{ icon: modeIcons.bedwars, mode: "Bed Wars", score: duels.bedwars.overall.wins, titleRequirement: "default" },
{ icon: modeIcons.blitzsg, mode: "Blitz", score: duels.blitzsg.wins, titleRequirement: "default" },
{ icon: modeIcons.bow, mode: "Bow", score: duels.bow.wins, titleRequirement: "default" },
{ icon: modeIcons.spleef, mode: "Spleef", score: duels.spleef.overallWins, titleRequirement: "default" },
{ icon: modeIcons.boxing, mode: "Boxing", score: duels.boxing.wins, titleRequirement: "half" },
{ icon: modeIcons.bridge, mode: "Bridge", score: duels.bridge.overall.wins, titleRequirement: "half" },
{ icon: modeIcons.classic, mode: "Classic", score: duels.classic.overall.wins, titleRequirement: "default" },
{ icon: modeIcons.combo, mode: "Combo", score: duels.combo.wins, titleRequirement: "default" },
{ icon: modeIcons.megawalls, mode: "Mega Walls", score: duels.megawalls.wins, titleRequirement: "half" },
{ icon: modeIcons.nodebuff, mode: "NoDebuff", score: duels.nodebuff.wins, titleRequirement: "half" },
{ icon: modeIcons.op, mode: "OP", score: duels.op.overall.wins, titleRequirement: "default" },
{ icon: modeIcons.quake, mode: "Quakecraft", score: duels.quake.wins, titleRequirement: "default" },
{ icon: modeIcons.parkour, mode: "Parkour", score: duels.parkour.wins, titleRequirement: "half" },
{ icon: modeIcons.skywars, mode: "SkyWars", score: duels.skywars.overall.wins, titleRequirement: "default" },
{ icon: modeIcons.sumo, mode: "Sumo", score: duels.sumo.wins, titleRequirement: "default" },
{ icon: modeIcons.uhc, mode: "UHC", score: duels.uhc.overall.wins, titleRequirement: "default" },
] satisfies {
icon: Image;
mode: string;
score: number;
titleRequirement: Schemas.TitleRequirement;
}[];

games.sort((a, b) => b.wins - a.wins);
games.sort(
(a, b) =>
titleSortScore(b.score, b.titleRequirement) -
titleSortScore(a.score, a.titleRequirement)
);
const groups = arrayGroup(games, games.length / 2);
const overallTitle = getBaseTitle(duels.overall.wins, "", "overall");

return (
<div width="100%" direction="column">
<ModeTitle
icon={modeIcons.overall}
title={duels.overall.titleFormatted}
wins={duels.overall.wins}
title={overallTitle}
score={duels.overall.wins}
t={t}
/>
<div width="100%">
{groups.map((group) => (
<div width={`1/${groups.length}`} direction="column">
{group.map(({ icon, title, wins }) => (
{group.map(({ icon, mode, score, titleRequirement }) => (
<ModeTitle
icon={icon}
title={title}
wins={wins}
title={getBaseTitle(score, mode, titleRequirement)}
score={score}
t={t}
/>
))}
Expand Down
5 changes: 3 additions & 2 deletions packages/schemas/src/player/gamemodes/duels/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export const DUELS_MODES = new GameModes([
{ api: "titles" },
],
},
{ api: "arena", hypixel: "DUELS_DUEL_ARENA" },
{ api: "arena", hypixel: "DUELS_DUEL_ARENA", formatted: "Duel Arena" },
{
api: "bedwars",
formatted: "BedWars",
Expand Down Expand Up @@ -122,7 +122,7 @@ export class Duels {
@Field({ leaderboard: { extraDisplay: "this.overall.titleFormatted" } })
public overall: SingleBowPVPDuelsGameMode;

@Field({ leaderboard: { extraDisplay: "this.arena.titleFormatted" } })
@Field({ leaderboard: { name: "Duel Arena", extraDisplay: "this.arena.titleFormatted" } })
public arena: ArenaDuels;

@Field({
Expand Down Expand Up @@ -236,3 +236,4 @@ export class Duels {
}

export * from "./mode.js";
export * from "./util.js";
16 changes: 14 additions & 2 deletions packages/schemas/src/player/gamemodes/duels/mode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -295,13 +295,25 @@ export class SingleDuelsGameMode extends BaseDuelsGameMode {
}
}

export class ArenaDuels extends SingleDuelsGameMode {
export class ArenaDuels extends SinglePVPDuelsGameMode {
@Field()
public shotsFired: number;

public constructor(data: APIData) {
super(data, "Arena", "duel_arena", "default");
super(data, "Duel Arena", "duel_arena", "default");
this.shotsFired = data[`duel_arena_bow_shots`];

const { titleFormatted, titleLevelFormatted, nextTitleLevelFormatted, progression } = getTitleAndProgression({
score: this.kills,
mode: "Duel Arena",
data,
titleRequirement: "default",
});

this.titleFormatted = titleFormatted;
this.titleLevelFormatted = titleLevelFormatted;
this.nextTitleLevelFormatted = nextTitleLevelFormatted;
this.progression = progression;
}
}

Expand Down
10 changes: 6 additions & 4 deletions packages/schemas/src/player/gamemodes/duels/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,13 @@ export const getTitleAndProgression = ({
mode,
data,
titleRequirement,
scheme: schemeOption = "active",
}: {
score: number;
mode: string;
data: APIData;
titleRequirement: TitleRequirement;
scheme?: "active" | "default";
}) => {
mode = mode ? `${mode} ` : mode;

Expand All @@ -98,9 +100,10 @@ export const getTitleAndProgression = ({

const icon = iconKey in ICON_MAP ? ICON_MAP[iconKey as keyof typeof ICON_MAP] : ICON_MAP.none;

const scheme = schemeKey in SCHEME_MAP ?
SCHEME_MAP[schemeKey as keyof typeof SCHEME_MAP] :
SCHEME_MAP.default;
let scheme = SCHEME_MAP.default;

if (schemeOption === "active" && schemeKey in SCHEME_MAP)
scheme = SCHEME_MAP[schemeKey as keyof typeof SCHEME_MAP];

const titleFormatted = scheme.title(icon, `${mode}${title}${division > 1 ? ` ${romanNumeral(division)}` : ""}`, bold, defaultColor);
const titleLevelFormatted = scheme.level(romanNumeral(division), bold, defaultColor);
Expand Down Expand Up @@ -269,4 +272,3 @@ const SCHEME_MAP: Record<string, Scheme> = {
variety_values: gradientColorScheme(["§b", "§f", "§f", "§f", "§b"]),
og_fade: gradientColorScheme(["§6", "§e", "§f", "§7", "§8"]),
};

Loading