diff --git a/apps/discord-bot/src/commands/warlords/tables/capture-the-flag.table.tsx b/apps/discord-bot/src/commands/warlords/tables/capture-the-flag.table.tsx
new file mode 100644
index 000000000..af59ef4c5
--- /dev/null
+++ b/apps/discord-bot/src/commands/warlords/tables/capture-the-flag.table.tsx
@@ -0,0 +1,29 @@
+/**
+ * Copyright (c) Statsify
+ *
+ * This source code is licensed under the GNU GPL v3 license found in the
+ * LICENSE file in the root directory of this source tree.
+ * https://github.com/Statsify/statsify/blob/main/LICENSE
+ */
+
+import { Table } from "#components";
+import type { LocalizeFunction } from "@statsify/discord";
+import type { Warlords } from "@statsify/schemas";
+
+export interface WarlordsCaptureTheFlagTableProps {
+ warlords: Warlords;
+ t: LocalizeFunction;
+}
+
+export const WarlordsCaptureTheFlagTable = ({ warlords, t }: WarlordsCaptureTheFlagTableProps) => (
+
+
+
+
+
+
+
+
+
+
+);
diff --git a/apps/discord-bot/src/commands/warlords/tables/classes.table.tsx b/apps/discord-bot/src/commands/warlords/tables/classes.table.tsx
index cabb06f46..6ecab5786 100644
--- a/apps/discord-bot/src/commands/warlords/tables/classes.table.tsx
+++ b/apps/discord-bot/src/commands/warlords/tables/classes.table.tsx
@@ -6,17 +6,18 @@
* https://github.com/Statsify/statsify/blob/main/LICENSE
*/
+import { ClassMetadata, METADATA_KEY, WarlordsClass, WarlordsSpecification } from "@statsify/schemas";
import { LocalizeFunction } from "@statsify/discord";
import { Table } from "#components";
-import { Warlords, WarlordsClass } from "@statsify/schemas";
+import type { Constructor } from "@statsify/util";
interface WarlordsClassColumnProps {
title: string;
- stats: WarlordsClass;
+ stats: WarlordsSpecification;
t: LocalizeFunction;
}
-const WarlordsClassColumn = ({ title, stats, t }: WarlordsClassColumnProps) => (
+const WarlordsSpecificationColumn = ({ title, stats, t }: WarlordsClassColumnProps) => (
@@ -27,17 +28,33 @@ const WarlordsClassColumn = ({ title, stats, t }: WarlordsClassColumnProps) => (
);
export interface WarlordsClassTableProps {
- warlords: Warlords;
+ stats: WarlordsClass;
+ constructor: Constructor;
+ color: string;
t: LocalizeFunction;
}
-export const WarlordsClassTable = ({ warlords, t }: WarlordsClassTableProps) => (
-
-
-
-
-
-
-
-
-);
+export const WarlordsClassTable = ({ stats, t, constructor, color }: WarlordsClassTableProps) => {
+ const { attack, defense, healer } = getSpecificationNames(constructor);
+
+ return (
+
+
+
+
+
+
+
+
+ );
+};
+
+function getSpecificationNames(constructor: Constructor) {
+ const metadata = Reflect.getMetadata(METADATA_KEY, constructor.prototype) as ClassMetadata;
+
+ return {
+ attack: metadata.attack.leaderboard.name,
+ defense: metadata.defense.leaderboard.name,
+ healer: metadata.healer.leaderboard.name,
+ };
+}
diff --git a/apps/discord-bot/src/commands/warlords/tables/deathmatch.table.tsx b/apps/discord-bot/src/commands/warlords/tables/deathmatch.table.tsx
new file mode 100644
index 000000000..d3b76e87a
--- /dev/null
+++ b/apps/discord-bot/src/commands/warlords/tables/deathmatch.table.tsx
@@ -0,0 +1,25 @@
+/**
+ * Copyright (c) Statsify
+ *
+ * This source code is licensed under the GNU GPL v3 license found in the
+ * LICENSE file in the root directory of this source tree.
+ * https://github.com/Statsify/statsify/blob/main/LICENSE
+ */
+
+import { Table } from "#components";
+import type { LocalizeFunction } from "@statsify/discord";
+import type { Warlords } from "@statsify/schemas";
+
+export interface WarlordsDeathmatchTableProps {
+ warlords: Warlords;
+ t: LocalizeFunction;
+}
+
+export const WarlordsDeathmatchTable = ({ warlords, t }: WarlordsDeathmatchTableProps) => (
+
+
+
+
+
+
+);
diff --git a/apps/discord-bot/src/commands/warlords/tables/domination.table.tsx b/apps/discord-bot/src/commands/warlords/tables/domination.table.tsx
new file mode 100644
index 000000000..23fbebb6c
--- /dev/null
+++ b/apps/discord-bot/src/commands/warlords/tables/domination.table.tsx
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) Statsify
+ *
+ * This source code is licensed under the GNU GPL v3 license found in the
+ * LICENSE file in the root directory of this source tree.
+ * https://github.com/Statsify/statsify/blob/main/LICENSE
+ */
+
+import { Table } from "#components";
+import type { LocalizeFunction } from "@statsify/discord";
+import type { Warlords } from "@statsify/schemas";
+
+export interface WarlordsDominationTableProps {
+ warlords: Warlords;
+ t: LocalizeFunction;
+}
+
+export const WarlordsDominationTable = ({ warlords, t }: WarlordsDominationTableProps) => (
+
+
+
+
+
+
+
+
+
+
+
+);
diff --git a/apps/discord-bot/src/commands/warlords/tables/index.ts b/apps/discord-bot/src/commands/warlords/tables/index.ts
index f255ec2ab..21becef55 100644
--- a/apps/discord-bot/src/commands/warlords/tables/index.ts
+++ b/apps/discord-bot/src/commands/warlords/tables/index.ts
@@ -7,3 +7,7 @@
*/
export * from "./classes.table.js";
+export * from "./capture-the-flag.table.js";
+export * from "./domination.table.js";
+export * from "./overall.table.js";
+export * from "./deathmatch.table.js";
diff --git a/apps/discord-bot/src/commands/warlords/tables/overall.table.tsx b/apps/discord-bot/src/commands/warlords/tables/overall.table.tsx
new file mode 100644
index 000000000..4a275bea5
--- /dev/null
+++ b/apps/discord-bot/src/commands/warlords/tables/overall.table.tsx
@@ -0,0 +1,37 @@
+/**
+ * Copyright (c) Statsify
+ *
+ * This source code is licensed under the GNU GPL v3 license found in the
+ * LICENSE file in the root directory of this source tree.
+ * https://github.com/Statsify/statsify/blob/main/LICENSE
+ */
+
+import { Table } from "#components";
+import type { LocalizeFunction } from "@statsify/discord";
+import type { Warlords } from "@statsify/schemas";
+
+export interface WarlordsOverallTableProps {
+ warlords: Warlords;
+ t: LocalizeFunction;
+}
+
+export const WarlordsOverallTable = ({ warlords, t }: WarlordsOverallTableProps) => (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+);
diff --git a/apps/discord-bot/src/commands/warlords/warlords.profile.tsx b/apps/discord-bot/src/commands/warlords/warlords.profile.tsx
index 3d131e39e..5efd2fb13 100644
--- a/apps/discord-bot/src/commands/warlords/warlords.profile.tsx
+++ b/apps/discord-bot/src/commands/warlords/warlords.profile.tsx
@@ -6,9 +6,9 @@
* https://github.com/Statsify/statsify/blob/main/LICENSE
*/
-import { Container, Footer, Header, SidebarItem, Table } from "#components";
-import { FormattedGame, type GameMode, WarlordsModes } from "@statsify/schemas";
-import { WarlordsClassTable } from "./tables/index.js";
+import { Container, Footer, Header, type SidebarItem } from "#components";
+import { FormattedGame, type GameMode, WarlordsMage, WarlordsModes, WarlordsPaladin, WarlordsShaman, WarlordsWarrior } from "@statsify/schemas";
+import { WarlordsCaptureTheFlagTable, WarlordsClassTable, WarlordsDeathmatchTable, WarlordsDominationTable, WarlordsOverallTable } from "./tables/index.js";
import { prettify } from "@statsify/util";
import type { BaseProfileProps } from "#commands/base.hypixel-command";
@@ -31,37 +31,53 @@ export const WarlordsProfile = ({
const sidebar: SidebarItem[] = [
[t("stats.coins"), t(warlords.coins), "§6"],
- [t("stats.class"), prettify(warlords.class), "§e"],
];
+ if (mode.api !== "overall" && mode.api !== "captureTheFlag" && mode.api !== "domination" && mode.api !== "teamDeathmatch") {
+ sidebar.push(
+ [t("stats.spec"), prettify(warlords[mode.api].specification), "§a"],
+ [t("stats.level"), t(warlords[mode.api].level), "§a"]
+ );
+ } else {
+ sidebar.push([t("stats.class"), prettify(warlords.class), "§e"]);
+ const clazz = warlords.class as "mage" | "warrior" | "paladin" | "shaman";
+ // Verify that the cast is correct and the class is a valid class
+ if (clazz in warlords && typeof warlords[clazz] === "object") sidebar.push([t("stats.spec"), prettify(warlords[clazz].specification), "§a"]);
+ }
+
let table: JSX.Element;
switch (mode.api) {
case "overall":
- table = (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
+ table = ;
+ break;
+
+ case "captureTheFlag":
+ table = ;
+ break;
+
+ case "domination":
+ table = ;
+ break;
+
+ case "teamDeathmatch":
+ table = ;
+ break;
+
+ case "mage":
+ table = ;
+ break;
+
+ case "warrior":
+ table = ;
+ break;
+
+ case "paladin":
+ table = ;
break;
- case "classes":
- table = ;
+ case "shaman":
+ table = ;
break;
}
diff --git a/locales/en-US/default.json b/locales/en-US/default.json
index adb847ff1..135dd38d4 100644
--- a/locales/en-US/default.json
+++ b/locales/en-US/default.json
@@ -790,10 +790,15 @@
"fails": "Fails",
"mapsCompleted": "Maps Completed",
"arcadeWins": "Arcade Wins",
+ "spec": "Spec",
+ "capturePoints": "Capture Points",
+ "defendPoints": "Defend Points",
"draws": "Draws",
"sheepThrown": "Sheep Thrown",
"sheepKilled": "Sheep Killed",
"magicWool": "Magic Wool",
+ "flagCaptures": "Flag Captures",
+ "flagReturns": "Flag Returns",
"airTime": "Air Time",
"potionsSplashed": "Potions Splashed",
"powerOrbs": "Power Orbs"
@@ -842,4 +847,4 @@
"successfulUnverification": "Successfully unverified your account!",
"successfulVerification": "You are now verified to **{{displayName}}**"
}
-}
+}
\ No newline at end of file
diff --git a/packages/schemas/src/player/gamemodes/warlords/class.ts b/packages/schemas/src/player/gamemodes/warlords/class.ts
index 5cebeb547..8fe5f8f3e 100644
--- a/packages/schemas/src/player/gamemodes/warlords/class.ts
+++ b/packages/schemas/src/player/gamemodes/warlords/class.ts
@@ -10,7 +10,7 @@ import { Field } from "#metadata";
import { add } from "@statsify/math";
import type { APIData } from "@statsify/util";
-export class WarlordsClass {
+export class WarlordsSpecification {
@Field()
public wins: number;
@@ -36,3 +36,102 @@ export class WarlordsClass {
this.total = add(this.damage, this.prevent, this.healing);
}
}
+
+export class WarlordsClass extends WarlordsSpecification {
+ @Field({ store: { default: "none" } })
+ public specification: string;
+
+ // Warlords class level maxes out at 90
+ @Field({ leaderboard: { enabled: false } })
+ public level: number;
+
+ @Field()
+ public attack: WarlordsSpecification;
+
+ @Field()
+ public defense: WarlordsSpecification;
+
+ @Field()
+ public healer: WarlordsSpecification;
+
+ public constructor(data: APIData, clazz: string, attack: string, defense: string, tank: string) {
+ super(data, clazz);
+
+ this.specification = data[`${clazz}_spec`] ?? "none";
+ this.level = add(
+ data[`${clazz}_cooldown`],
+ data[`${clazz}_critchance`],
+ data[`${clazz}_critmultiplier`],
+ data[`${clazz}_energy`],
+ data[`${clazz}_health`],
+ data[`${clazz}_skill1`],
+ data[`${clazz}_skill2`],
+ data[`${clazz}_skill3`],
+ data[`${clazz}_skill4`],
+ data[`${clazz}_skill5`]
+ );
+ this.attack = new WarlordsSpecification(data, attack);
+ this.defense = new WarlordsSpecification(data, defense);
+ this.healer = new WarlordsSpecification(data, tank);
+ }
+}
+
+export class WarlordsShaman extends WarlordsClass {
+ @Field({ leaderboard: { name: "Thunderlord" } })
+ public declare attack: WarlordsSpecification;
+
+ @Field({ leaderboard: { name: "Spiritguard" } })
+ public declare defense: WarlordsSpecification;
+
+ @Field({ leaderboard: { name: "Earthwarden" } })
+ public declare healer: WarlordsSpecification;
+
+ public constructor(data: APIData) {
+ super(data, "shaman", "thunderlord", "spiritguard", "earthwarden");
+ }
+}
+
+export class WarlordsMage extends WarlordsClass {
+ @Field({ leaderboard: { name: "Pyromancer" } })
+ public declare attack: WarlordsSpecification;
+
+ @Field({ leaderboard: { name: "Cyromancer" } })
+ public declare defense: WarlordsSpecification;
+
+ @Field({ leaderboard: { name: "Aquamancer" } })
+ public declare healer: WarlordsSpecification;
+
+ public constructor(data: APIData) {
+ super(data, "mage", "pyromancer", "cyromancer", "aquamancer");
+ }
+}
+
+export class WarlordsWarrior extends WarlordsClass {
+ @Field({ leaderboard: { name: "Berserker" } })
+ public declare attack: WarlordsSpecification;
+
+ @Field({ leaderboard: { name: "Defender" } })
+ public declare defense: WarlordsSpecification;
+
+ @Field({ leaderboard: { name: "Revenant" } })
+ public declare healer: WarlordsSpecification;
+
+ public constructor(data: APIData) {
+ super(data, "warrior", "berserker", "defender", "revenant");
+ }
+}
+
+export class WarlordsPaladin extends WarlordsClass {
+ @Field({ leaderboard: { name: "Avenger" } })
+ public declare attack: WarlordsSpecification;
+
+ @Field({ leaderboard: { name: "Crusader" } })
+ public declare defense: WarlordsSpecification;
+
+ @Field({ leaderboard: { name: "Protector" } })
+ public declare healer: WarlordsSpecification;
+
+ public constructor(data: APIData) {
+ super(data, "paladin", "avenger", "crusader", "protector");
+ }
+}
diff --git a/packages/schemas/src/player/gamemodes/warlords/index.ts b/packages/schemas/src/player/gamemodes/warlords/index.ts
index 082861357..b23415276 100644
--- a/packages/schemas/src/player/gamemodes/warlords/index.ts
+++ b/packages/schemas/src/player/gamemodes/warlords/index.ts
@@ -8,39 +8,51 @@
import { type ExtractGameModes, GameModes } from "#game";
import { Field } from "#metadata";
-import { WarlordsClass } from "./class.js";
+import { WarlordsCaptureTheFlag, WarlordsDomination, WarlordsTeamDeathmatch } from "./mode.js";
+import { WarlordsMage, WarlordsPaladin, WarlordsShaman, WarlordsWarrior } from "./class.js";
import { add, ratio, sub } from "@statsify/math";
import type { APIData } from "@statsify/util";
export const WARLORDS_MODES = new GameModes([
{ api: "overall" },
- { api: "classes" },
-
- { hypixel: "ctf_mini", formatted: "CTF" },
- { hypixel: "domination", formatted: "Domination" },
- { hypixel: "team_deathmatch", formatted: "Deathmatch" },
+ { api: "captureTheFlag", hypixel: "ctf_mini", formatted: "Capture the Flag" },
+ { api: "domination", hypixel: "domination", formatted: "Domination" },
+ { api: "teamDeathmatch", hypixel: "team_deathmatch", formatted: "Team Deathmatch" },
+ { api: "mage" },
+ { api: "warrior" },
+ { api: "paladin" },
+ { api: "shaman" },
] as const);
export type WarlordsModes = ExtractGameModes;
export class Warlords {
+ @Field({ store: { default: "warrior" } })
+ public class: string;
+
+ @Field({ historical: { enabled: false } })
+ public coins: number;
+
@Field()
- public mage: WarlordsClass;
+ public mage: WarlordsMage;
@Field()
- public warrior: WarlordsClass;
+ public warrior: WarlordsWarrior;
@Field()
- public paladin: WarlordsClass;
+ public paladin: WarlordsPaladin;
@Field()
- public shaman: WarlordsClass;
+ public shaman: WarlordsShaman;
- @Field({ store: { default: "warrior" } })
- public class: string;
+ @Field()
+ public domination: WarlordsDomination;
- @Field({ historical: { enabled: false } })
- public coins: number;
+ @Field()
+ public captureTheFlag: WarlordsCaptureTheFlag;
+
+ @Field()
+ public teamDeathmatch: WarlordsTeamDeathmatch;
@Field()
public gamesPlayed: number;
@@ -67,14 +79,18 @@ export class Warlords {
public assists: number;
public constructor(data: APIData) {
- this.mage = new WarlordsClass(data, "mage");
- this.warrior = new WarlordsClass(data, "warrior");
- this.paladin = new WarlordsClass(data, "paladin");
- this.shaman = new WarlordsClass(data, "shaman");
-
this.class = data.chosen_class || "warrior";
this.coins = data.coins;
+ this.mage = new WarlordsMage(data);
+ this.warrior = new WarlordsWarrior(data);
+ this.paladin = new WarlordsPaladin(data);
+ this.shaman = new WarlordsShaman(data);
+
+ this.domination = new WarlordsDomination(data);
+ this.captureTheFlag = new WarlordsCaptureTheFlag(data);
+ this.teamDeathmatch = new WarlordsTeamDeathmatch(data);
+
this.gamesPlayed = Math.ceil(
add(
data.aquamancer_plays,
@@ -100,8 +116,8 @@ export class Warlords {
// Warlords Losses in API are bugged (~90% accurate)
// https://hypixel.net/threads/warlords-skillrating-website.1560046/post-12210896
this.losses = sub(this.gamesPlayed, this.wins);
-
this.wlr = ratio(this.wins, this.losses);
+
this.kills = data.kills;
this.deaths = data.deaths;
this.kdr = ratio(this.kills, this.deaths);
diff --git a/packages/schemas/src/player/gamemodes/warlords/mode.ts b/packages/schemas/src/player/gamemodes/warlords/mode.ts
new file mode 100644
index 000000000..7ea8a912e
--- /dev/null
+++ b/packages/schemas/src/player/gamemodes/warlords/mode.ts
@@ -0,0 +1,71 @@
+/**
+ * Copyright (c) Statsify
+ *
+ * This source code is licensed under the GNU GPL v3 license found in the
+ * LICENSE file in the root directory of this source tree.
+ * https://github.com/Statsify/statsify/blob/main/LICENSE
+ */
+
+import { Field } from "#metadata";
+import type { APIData } from "@statsify/util";
+
+export class WarlordsDomination {
+ @Field()
+ public capturePoints: number;
+
+ @Field()
+ public defendPoints: number;
+
+ @Field()
+ public kills: number;
+
+ @Field()
+ public score: number;
+
+ @Field()
+ public wins: number;
+
+ public constructor(data: APIData) {
+ this.capturePoints = data.dom_point_captures;
+ this.defendPoints = data.dom_point_defends;
+ this.kills = data.kills_domination;
+ this.score = data.total_domination_score;
+ this.wins = data.wins_domination;
+ }
+}
+
+export class WarlordsCaptureTheFlag {
+ @Field()
+ public wins: number;
+
+ @Field()
+ public kills: number;
+
+ @Field()
+ public flagCaptures: number;
+
+ @Field()
+ public flagReturns: number;
+
+ public constructor(data: APIData) {
+ this.wins = data.wins_capturetheflag;
+ this.kills = data.kills_capturetheflag;
+
+ this.flagCaptures = data.flag_conquer_self;
+ this.flagReturns = data.flag_returns;
+ }
+}
+
+export class WarlordsTeamDeathmatch {
+ @Field()
+ public wins: number;
+
+ @Field()
+ public kills: number;
+
+ public constructor(data: APIData) {
+ this.wins = data.wins_teamdeathmatch;
+ this.kills = data.kills_teamdeathmatch;
+ }
+}
+