diff --git a/apps/discord-bot/src/commands/arcade/arcade.profile.tsx b/apps/discord-bot/src/commands/arcade/arcade.profile.tsx index bb7139c9a..b0c19e2e2 100644 --- a/apps/discord-bot/src/commands/arcade/arcade.profile.tsx +++ b/apps/discord-bot/src/commands/arcade/arcade.profile.tsx @@ -6,11 +6,10 @@ * https://github.com/Statsify/statsify/blob/main/LICENSE */ -import { ArcadeModes, FormattedGame, GameMode } from "@statsify/schemas"; +import { ArcadeModes, FormattedGame, type GameMode } from "@statsify/schemas"; import { BlockingDeadTable, BountyHuntersTable, - CaptureTheWoolTable, CreeperAttackTable, DragonWarsTable, DropperTable, @@ -29,7 +28,6 @@ import { SeasonalTable, ThrowOutTable, ZombiesTable, - captureTheWoolSiderbar, } from "./modes/index.js"; import { Container, Footer, Header, SidebarItem } from "#components"; import type { BaseProfileProps } from "#commands/base.hypixel-command"; @@ -51,7 +49,7 @@ export const ArcadeProfile = ({ }: ArcadeProfileProps) => { const { arcade } = player.stats; - let sidebar: SidebarItem[] = [ + const sidebar: SidebarItem[] = [ [t("stats.coins"), t(arcade.coins), "§6"], [t("stats.coinConversions"), t(arcade.coinConversions), "§e"], [t("stats.arcadeWins"), t(arcade.wins), "§b"], @@ -69,11 +67,6 @@ export const ArcadeProfile = ({ table = ; break; - case "captureTheWool": - table = ; - sidebar = captureTheWoolSiderbar(arcade, t); - break; - case "creeperAttack": table = ; break; diff --git a/apps/discord-bot/src/commands/arcade/dropper.profile.tsx b/apps/discord-bot/src/commands/arcade/dropper.profile.tsx index ded83fcef..d15a9ddb3 100644 --- a/apps/discord-bot/src/commands/arcade/dropper.profile.tsx +++ b/apps/discord-bot/src/commands/arcade/dropper.profile.tsx @@ -16,7 +16,7 @@ import { Container, Footer, Header } from "#components"; import { DropperMapsTable, DropperTable } from "./modes/index.js"; -import { DropperModes, FormattedGame, GameMode } from "@statsify/schemas"; +import { DropperModes, FormattedGame, type GameMode } from "@statsify/schemas"; import type { BaseProfileProps } from "#commands/base.hypixel-command"; export interface DropperProfileProps extends BaseProfileProps { diff --git a/apps/discord-bot/src/commands/arcade/modes/capture-the-wool.tsx b/apps/discord-bot/src/commands/arcade/modes/capture-the-wool.tsx deleted file mode 100644 index beeb13196..000000000 --- a/apps/discord-bot/src/commands/arcade/modes/capture-the-wool.tsx +++ /dev/null @@ -1,103 +0,0 @@ -/** - * 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 { Historical, If, type SidebarItem, Table } from "#components"; -import { formatTime } from "@statsify/util"; -import type { Arcade, CaptureTheWool } from "@statsify/schemas"; -import type { LocalizeFunction } from "@statsify/discord"; -import type { ProfileTime } from "#commands/base.hypixel-command"; - -interface CaptureTheWoolTableProps { - stats: CaptureTheWool; - t: LocalizeFunction; - time: ProfileTime; -} - -export const CaptureTheWoolTable = ({ stats, t, time }: CaptureTheWoolTableProps) => ( - - - - - - - - - - - - - - - - - - - - - 0}> - - - 0}> - - - 0}> - - - - - - - - - - - - - - - - - - - - -); - -export function captureTheWoolSiderbar(arcade: Arcade, t: LocalizeFunction): SidebarItem[] { - const captureTheWool = arcade.captureTheWool; - - return [ - [t("stats.coins"), t(arcade.coins), "§6"], - [t("stats.goldEarned"), t(captureTheWool.goldEarned), "§6"], - [t("stats.goldSpent"), t(captureTheWool.goldSpent), "§6"], - ]; -} diff --git a/apps/discord-bot/src/commands/arcade/modes/index.tsx b/apps/discord-bot/src/commands/arcade/modes/index.tsx index a31ceac50..98ecf6b7b 100644 --- a/apps/discord-bot/src/commands/arcade/modes/index.tsx +++ b/apps/discord-bot/src/commands/arcade/modes/index.tsx @@ -8,7 +8,6 @@ export * from "./blocking-dead.js"; export * from "./bounty-hunters.js"; -export * from "./capture-the-wool.js"; export * from "./creeper-attack.js"; export * from "./dragon-wars.js"; export * from "./dropper.js"; diff --git a/apps/discord-bot/src/commands/arcade/modes/overall-arcade.tsx b/apps/discord-bot/src/commands/arcade/modes/overall-arcade.tsx index dc7002edc..f074cae81 100644 --- a/apps/discord-bot/src/commands/arcade/modes/overall-arcade.tsx +++ b/apps/discord-bot/src/commands/arcade/modes/overall-arcade.tsx @@ -17,12 +17,11 @@ interface OverallArcadeTableProps { } export const OverallArcadeTable = ({ stats, t }: OverallArcadeTableProps) => { - const rowSize = 5; + const rowSize = 3; const games: [string, number][] = [ ["Blocking Dead", stats.blockingDead.wins], ["Bounty Hunters", stats.bountyHunters.wins], - ["Capture The Wool", stats.captureTheWool.wins], ["Dragon Wars", stats.dragonWars.wins], ["Dropper", stats.dropper.wins], ["Ender Spleef", stats.enderSpleef.wins], @@ -45,7 +44,7 @@ export const OverallArcadeTable = ({ stats, t }: OverallArcadeTableProps) => { const rows = arrayGroup(games, rowSize); - const colors = ["§a", "§e", "§6", "§c", "§4"]; + const colors = ["§d", "§b", "§a", "§e", "§6", "§c"]; return ( diff --git a/apps/discord-bot/src/commands/arenabrawl/arenabrawl.profile.tsx b/apps/discord-bot/src/commands/arenabrawl/arenabrawl.profile.tsx index 09080547e..97ccdc216 100644 --- a/apps/discord-bot/src/commands/arenabrawl/arenabrawl.profile.tsx +++ b/apps/discord-bot/src/commands/arenabrawl/arenabrawl.profile.tsx @@ -6,7 +6,7 @@ * https://github.com/Statsify/statsify/blob/main/LICENSE */ -import { ArenaBrawlModes, FormattedGame, GameMode } from "@statsify/schemas"; +import { ArenaBrawlModes, FormattedGame, type GameMode } from "@statsify/schemas"; import { Container, Footer, diff --git a/apps/discord-bot/src/commands/base.hypixel-command.ts b/apps/discord-bot/src/commands/base.hypixel-command.ts index ddd74be62..e425b48b0 100644 --- a/apps/discord-bot/src/commands/base.hypixel-command.ts +++ b/apps/discord-bot/src/commands/base.hypixel-command.ts @@ -15,6 +15,7 @@ import { Page, PaginateService, PlayerArgument, + SubPage, } from "@statsify/discord"; import { Container } from "typedi"; import { GamesWithBackgrounds, mapBackground } from "#constants"; @@ -23,7 +24,7 @@ import { getBackground, getLogo } from "@statsify/assets"; import { getTheme } from "#themes"; import { noop } from "@statsify/util"; import { render } from "@statsify/rendering"; -import type { GameMode, GameModes, Player, User } from "@statsify/schemas"; +import type { GameMode, GameModeWithSubModes, GameModes, Player, User } from "@statsify/schemas"; import type { Image } from "skia-canvas"; export type ProfileTime = "LIVE" | HistoricalTimeData; @@ -48,8 +49,8 @@ export type ModeEmoji = LocalizationString | false | undefined; export interface BaseHypixelCommand { getPreProfileData?(player: Player): K | Promise; - filterModes?(player: Player, modes: GameMode[]): GameMode[]; - getModeEmojis?(modes: GameMode[]): ModeEmoji[]; + filterModes?(player: Player, modes: GameModeWithSubModes[]): GameModeWithSubModes[]; + getModeEmojis?(modes: GameModeWithSubModes[]): ModeEmoji[]; } @Command({ @@ -83,29 +84,71 @@ export abstract class BaseHypixelCommand ({ - label: mode.formatted, - emoji: emojis[index], - generator: async (t) => { - const background = await getBackground(...mapBackground(this.modes, mode.api)); - - const profile = this.getProfile( - { - player, - skin, - background, - logo, - t, - user, - badge, - time: "LIVE", + const pages: Page[] = filteredModes.map((mode, index) => { + const pageInput = { + label: mode.formatted, + emoji: emojis[index], + }; + + if (mode.submodes.length === 0) { + const gameMode = { ...mode, submode: undefined } as unknown as GameMode; + + return { + ...pageInput, + generator: async (t) => { + const background = await getBackground(...mapBackground(this.modes, mode.api)); + + const profile = this.getProfile( + { + player, + skin, + background, + logo, + t, + user, + badge, + time: "LIVE", + }, + { mode: gameMode, data } + ); + + return render(profile, getTheme(user)); }, - { mode, data } - ); - - return render(profile, getTheme(user)); - }, - })); + }; + } + + const subPages = mode.submodes.map((submode): SubPage => ({ + label: submode.formatted, + generator: async (t) => { + const background = await getBackground(...mapBackground(this.modes, mode.api)); + + const gameMode = { + api: mode.api, + formatted: mode.formatted, + hypixel: mode.hypixel, + submode, + } as GameMode; + + const profile = this.getProfile( + { + player, + skin, + background, + logo, + t, + user, + badge, + time: "LIVE", + }, + { mode: gameMode, data } + ); + + return render(profile, getTheme(user)); + }, + })); + + return { ...pageInput, subPages }; + }); return this.paginateService.paginate(context, pages); } diff --git a/apps/discord-bot/src/commands/bedwars/bedwars-challenges.command.tsx b/apps/discord-bot/src/commands/bedwars/bedwars-challenges.command.tsx index af309b340..b7323b175 100644 --- a/apps/discord-bot/src/commands/bedwars/bedwars-challenges.command.tsx +++ b/apps/discord-bot/src/commands/bedwars/bedwars-challenges.command.tsx @@ -6,7 +6,7 @@ * https://github.com/Statsify/statsify/blob/main/LICENSE */ -import { BEDWARS_MODES, BedWarsModes, GameMode, Player } from "@statsify/schemas"; +import { BEDWARS_MODES, BedWarsModes, GameModeWithSubModes, Player } from "@statsify/schemas"; import { BaseHypixelCommand, BaseProfileProps } from "#commands/base.hypixel-command"; import { BedWarsChallengesProfile } from "./bedwars-challenges.profile.js"; import { Command } from "@statsify/discord"; @@ -19,8 +19,8 @@ export class BedWarsChallengesCommand extends BaseHypixelCommand { public filterModes( player: Player, - modes: GameMode[] - ): GameMode[] { + modes: GameModeWithSubModes[] + ): GameModeWithSubModes[] { return [modes[0]]; } diff --git a/apps/discord-bot/src/commands/bedwars/bedwars.profile.tsx b/apps/discord-bot/src/commands/bedwars/bedwars.profile.tsx index 1cabf35ef..452699499 100644 --- a/apps/discord-bot/src/commands/bedwars/bedwars.profile.tsx +++ b/apps/discord-bot/src/commands/bedwars/bedwars.profile.tsx @@ -6,7 +6,7 @@ * https://github.com/Statsify/statsify/blob/main/LICENSE */ -import { BedWarsModes, FormattedGame, GameMode } from "@statsify/schemas"; +import { BedWarsModes, FormattedGame, type GameMode } from "@statsify/schemas"; import { Container, Footer, diff --git a/apps/discord-bot/src/commands/blitzsg/blitzsg.command.tsx b/apps/discord-bot/src/commands/blitzsg/blitzsg.command.tsx index c2ba67077..951c0a0e9 100644 --- a/apps/discord-bot/src/commands/blitzsg/blitzsg.command.tsx +++ b/apps/discord-bot/src/commands/blitzsg/blitzsg.command.tsx @@ -6,7 +6,7 @@ * https://github.com/Statsify/statsify/blob/main/LICENSE */ -import { BLITZSG_MODES, BlitzSGModes, GameMode, Player } from "@statsify/schemas"; +import { BLITZSG_MODES, BlitzSGModes, GameModeWithSubModes, Player } from "@statsify/schemas"; import { BaseHypixelCommand, BaseProfileProps, @@ -23,8 +23,8 @@ export class BlitzSGCommand extends BaseHypixelCommand { public filterModes( player: Player, - modes: GameMode[] - ): GameMode[] { + modes: GameModeWithSubModes[] + ): GameModeWithSubModes[] { return filterBlitzKits(player, modes); } diff --git a/apps/discord-bot/src/commands/blitzsg/blitzsg.profile.tsx b/apps/discord-bot/src/commands/blitzsg/blitzsg.profile.tsx index 1de524cb1..a151d08af 100644 --- a/apps/discord-bot/src/commands/blitzsg/blitzsg.profile.tsx +++ b/apps/discord-bot/src/commands/blitzsg/blitzsg.profile.tsx @@ -11,7 +11,8 @@ import { BlitzSGKit, BlitzSGModes, FormattedGame, - GameMode, + type GameMode, + type GameModeWithSubModes, Player, } from "@statsify/schemas"; import { @@ -193,8 +194,8 @@ export const BlitzSGProfile = ({ export function filterBlitzKits( player: Player, - modes: GameMode[] -): GameMode[] { + modes: GameModeWithSubModes[] +): GameModeWithSubModes[] { const { blitzsg } = player.stats; const [overall, ...kits] = modes; diff --git a/apps/discord-bot/src/commands/challenges/challenges.command.tsx b/apps/discord-bot/src/commands/challenges/challenges.command.tsx index a9df48a0a..81b2135ce 100644 --- a/apps/discord-bot/src/commands/challenges/challenges.command.tsx +++ b/apps/discord-bot/src/commands/challenges/challenges.command.tsx @@ -12,7 +12,7 @@ import { ModeEmoji, ProfileData, } from "#commands/base.hypixel-command"; -import { CHALLENGE_MODES, ChallengeModes, GameId, GameMode } from "@statsify/schemas"; +import { CHALLENGE_MODES, ChallengeModes, GameId, GameModeWithSubModes } from "@statsify/schemas"; import { ChallengesProfile } from "./challenges.profile.js"; import { Command } from "@statsify/discord"; import { Image } from "skia-canvas"; @@ -35,7 +35,7 @@ export class ChallengesCommand extends BaseHypixelCommand< return { gameIcons: await getAllGameIcons() }; } - public getModeEmojis(modes: GameMode[]): ModeEmoji[] { + public getModeEmojis(modes: GameModeWithSubModes[]): ModeEmoji[] { return modes.map((m) => m.api !== "overall" && ((t) => t(`emojis:games.${m.api}`))); } diff --git a/apps/discord-bot/src/commands/challenges/challenges.profile.tsx b/apps/discord-bot/src/commands/challenges/challenges.profile.tsx index bd8193f06..c2b5e309c 100644 --- a/apps/discord-bot/src/commands/challenges/challenges.profile.tsx +++ b/apps/discord-bot/src/commands/challenges/challenges.profile.tsx @@ -7,15 +7,38 @@ */ import { + ArcadeChallenges, + ArenaBrawlChallenges, + BedWarsChallenges, + BlitzSGChallenges, + BuildBattleChallenges, ChallengeModes, Challenges, + CopsAndCrimsChallenges, + DuelsChallenges, FormattedGame, GameChallenges, - GameId, - GameMode, + type GameId, + type GameMode, + MegaWallsChallenges, + MetadataEntry, MetadataScanner, + MurderMysteryChallenges, + PaintballChallenges, + QuakeChallenges, + SkyWarsChallenges, + SmashHeroesChallenges, + SpeedUHCChallenges, + TNTGamesChallenges, + TurboKartRacersChallenges, + UHCChallenges, + VampireZChallenges, + WallsChallenges, + WarlordsChallenges, + WoolGamesChallenges, } from "@statsify/schemas"; import { Container, Footer, GameList, Header, SidebarItem, Table } from "#components"; + import { arrayGroup, prettify } from "@statsify/util"; import type { BaseProfileProps } from "#commands/base.hypixel-command"; import type { Image } from "skia-canvas"; @@ -44,12 +67,36 @@ const NormalTable = ({ challenges, t, gameIcons }: NormalTableProps) => { interface GameTableProps { gameChallenges: GameChallenges; - constructor: any; + mode: Exclude["api"], "overall">; t: LocalizeFunction; } -const GameTable = ({ gameChallenges, constructor, t }: GameTableProps) => { - const metadata = MetadataScanner.scan(constructor); +const METADATA: Record["api"], "overall">, MetadataEntry[]> = { + ARCADE: MetadataScanner.scan(ArcadeChallenges), + ARENA_BRAWL: MetadataScanner.scan(ArenaBrawlChallenges), + BEDWARS: MetadataScanner.scan(BedWarsChallenges), + BLITZSG: MetadataScanner.scan(BlitzSGChallenges), + BUILD_BATTLE: MetadataScanner.scan(BuildBattleChallenges), + COPS_AND_CRIMS: MetadataScanner.scan(CopsAndCrimsChallenges), + DUELS: MetadataScanner.scan(DuelsChallenges), + MEGAWALLS: MetadataScanner.scan(MegaWallsChallenges), + MURDER_MYSTERY: MetadataScanner.scan(MurderMysteryChallenges), + PAINTBALL: MetadataScanner.scan(PaintballChallenges), + QUAKE: MetadataScanner.scan(QuakeChallenges), + SKYWARS: MetadataScanner.scan(SkyWarsChallenges), + SMASH_HEROES: MetadataScanner.scan(SmashHeroesChallenges), + SPEED_UHC: MetadataScanner.scan(SpeedUHCChallenges), + TNT_GAMES: MetadataScanner.scan(TNTGamesChallenges), + TURBO_KART_RACERS: MetadataScanner.scan(TurboKartRacersChallenges), + UHC: MetadataScanner.scan(UHCChallenges), + VAMPIREZ: MetadataScanner.scan(VampireZChallenges), + WALLS: MetadataScanner.scan(WallsChallenges), + WARLORDS: MetadataScanner.scan(WarlordsChallenges), + WOOLGAMES: MetadataScanner.scan(WoolGamesChallenges), +}; + +const GameTable = ({ gameChallenges, mode, t }: GameTableProps) => { + const metadata = METADATA[mode]; const entries = Object.entries(gameChallenges); const GROUP_SIZE = entries.length < 5 ? 4 : (entries.length - 1) ** 0.5; @@ -59,8 +106,9 @@ const GameTable = ({ gameChallenges, constructor, t }: GameTableProps) => { .filter(([k]) => k !== "total") .sort((a, b) => b[1] - a[1]) .map(([challenge, completions]) => { - const field = metadata.find(([k]) => k === challenge); - const realName = field?.[1]?.leaderboard?.name ?? prettify(challenge); + const [_, field] = metadata.find(([k]) => k === challenge)!; + + const realName = field.leaderboard?.name ?? prettify(challenge); return [realName, t(completions)]; }), GROUP_SIZE @@ -107,7 +155,7 @@ export const ChallengesProfile = ({ table = ( ); diff --git a/apps/discord-bot/src/commands/copsandcrims/copsandcrims.profile.tsx b/apps/discord-bot/src/commands/copsandcrims/copsandcrims.profile.tsx index 7821a3e00..4ffe1ee16 100644 --- a/apps/discord-bot/src/commands/copsandcrims/copsandcrims.profile.tsx +++ b/apps/discord-bot/src/commands/copsandcrims/copsandcrims.profile.tsx @@ -7,7 +7,7 @@ */ import { Container, Footer, Header, Historical, SidebarItem, Table } from "#components"; -import { CopsAndCrimsModes, FormattedGame, GameMode } from "@statsify/schemas"; +import { CopsAndCrimsModes, FormattedGame, type GameMode } from "@statsify/schemas"; import { formatTime } from "@statsify/util"; import type { BaseProfileProps } from "#commands/base.hypixel-command"; diff --git a/apps/discord-bot/src/commands/duels/bridge.profile.tsx b/apps/discord-bot/src/commands/duels/bridge.profile.tsx index 7e88a1407..9de73e419 100644 --- a/apps/discord-bot/src/commands/duels/bridge.profile.tsx +++ b/apps/discord-bot/src/commands/duels/bridge.profile.tsx @@ -6,7 +6,7 @@ * https://github.com/Statsify/statsify/blob/main/LICENSE */ -import { BridgeModes, FormattedGame, GameMode } from "@statsify/schemas"; +import { BridgeModes, FormattedGame, type GameMode } from "@statsify/schemas"; import { Container, Footer, diff --git a/apps/discord-bot/src/commands/duels/duels.profile.tsx b/apps/discord-bot/src/commands/duels/duels.profile.tsx index 494499807..646f54b2e 100644 --- a/apps/discord-bot/src/commands/duels/duels.profile.tsx +++ b/apps/discord-bot/src/commands/duels/duels.profile.tsx @@ -13,7 +13,7 @@ import { UHCDuelsTable, } from "./tables/index.js"; import { Container, Footer, Header, SidebarItem, formatProgression } from "#components"; -import { DuelsModes, FormattedGame, GameMode } from "@statsify/schemas"; +import { DuelsModes, FormattedGame, type GameMode } from "@statsify/schemas"; import { prettify } from "@statsify/util"; import type { BaseProfileProps } from "#commands/base.hypixel-command"; diff --git a/apps/discord-bot/src/commands/historical/historical.base.tsx b/apps/discord-bot/src/commands/historical/historical.base.tsx index 4e14d758d..0930cbcda 100644 --- a/apps/discord-bot/src/commands/historical/historical.base.tsx +++ b/apps/discord-bot/src/commands/historical/historical.base.tsx @@ -7,93 +7,40 @@ */ import { - ARCADE_MODES, - ARENA_BRAWL_MODES, - BEDWARS_MODES, - BLITZSG_MODES, - BRIDGE_MODES, - BUILD_BATTLE_MODES, - COPS_AND_CRIMS_MODES, - DUELS_MODES, - GENERAL_MODES, - GameMode, - GameModes, - MEGAWALLS_MODES, - MURDER_MYSTERY_MODES, - PAINTBALL_MODES, - PIT_MODES, - Player, - QUAKE_MODES, - SKYWARS_MODES, - SMASH_HEROES_MODES, - SPEED_UHC_MODES, - TNT_GAMES_MODES, - TURBO_KART_RACERS_MODES, - UHC_MODES, - VAMPIREZ_MODES, - WALLS_MODES, - WARLORDS_MODES, - WOOLWARS_MODES, -} from "@statsify/schemas"; -import { - ApiService, Command, - CommandContext, EmbedBuilder, - IMessage, - PaginateService, PlayerArgument, SubCommand, } from "@statsify/discord"; -import { ArcadeProfile } from "../arcade/arcade.profile.js"; -import { ArenaBrawlProfile } from "../arenabrawl/arenabrawl.profile.js"; -import { BedWarsProfile } from "../bedwars/bedwars.profile.js"; -import { BlitzSGProfile, filterBlitzKits } from "../blitzsg/blitzsg.profile.js"; -import { BridgeProfile } from "../duels/bridge.profile.js"; -import { BuildBattleProfile } from "../buildbattle/buildbattle.profile.js"; -import { Container } from "typedi"; -import { CopsAndCrimsProfile } from "../copsandcrims/copsandcrims.profile.js"; -import { DuelsProfile } from "../duels/duels.profile.js"; -import { GamesWithBackgrounds } from "#constants"; -import { HistoricalGeneralProfile } from "../general/historical-general.profile.js"; -import { MegaWallsProfile, filterMegaWallsKits } from "../megawalls/megawalls.profile.js"; -import { MurderMysteryProfile } from "../murdermystery/murdermystery.profile.js"; -import { PaintballProfile } from "../paintball/paintball.profile.js"; -import { PitProfile } from "../pit/pit.profile.js"; -import { QuakeProfile } from "../quake/quake.profile.js"; + import { STATUS_COLORS } from "@statsify/logger"; -import { SkyWarsProfile } from "../skywars/skywars.profile.js"; -import { SmashHeroesProfile } from "../smashheroes/smashheroes.profile.js"; -import { SpeedUHCProfile } from "../speeduhc/speeduhc.profile.js"; -import { TNTGamesProfile } from "../tntgames/tntgames.profile.js"; -import { TurboKartRacersProfile } from "../turbokartracers/turbokartracers.profile.js"; -import { UHCProfile } from "../uhc/uhc.profile.js"; -import { VampireZProfile } from "../vampirez/vampirez.profile.js"; -import { WallsProfile } from "../walls/walls.profile.js"; -import { WarlordsProfile } from "../warlords/warlords.profile.js"; -import { WoolWarsProfile } from "../woolwars/woolwars.profile.js"; import { getAssetPath } from "@statsify/assets"; import { readFileSync } from "node:fs"; -import type { BaseProfileProps } from "#commands/base.hypixel-command"; import type { HistoricalType } from "@statsify/api-client"; +const preview = { + name: "preview.png", + data: readFileSync(getAssetPath("previews/session.png")), + type: "image/png", +}; + +const embed = new EmbedBuilder() + .color(STATUS_COLORS.info) + .title((t) => t("historical.disabledWarning.title")) + .description((t) => t("historical.disabledWarning.description")) + .image(`attachment://${preview.name}`); + +const message = { embeds: [embed], files: [preview] }; + const args = [PlayerArgument]; @Command({ description: "" }) export class HistoricalBase { - private readonly apiService: ApiService; - private readonly paginateService: PaginateService; - - public constructor(private readonly time: HistoricalType) { - this.apiService = Container.get(ApiService); - this.paginateService = Container.get(PaginateService); - } + public constructor(private readonly time: HistoricalType) {} @SubCommand({ description: (t) => t("commands.historical-arcade"), args }) - public arcade(context: CommandContext) { - return this.run(context, ARCADE_MODES, (base, mode) => ( - - )); + public arcade() { + return message; } @SubCommand({ @@ -101,79 +48,53 @@ export class HistoricalBase { args, group: "classic", }) - public arenabrawl(context: CommandContext) { - return this.run(context, ARENA_BRAWL_MODES, (base, mode) => ( - - )); + public arenabrawl() { + return message; } @SubCommand({ description: (t) => t("commands.historical-bedwars"), args }) - public bedwars(context: CommandContext) { - return this.run(context, BEDWARS_MODES, (base, mode) => ( - - )); + public bedwars() { + return message; } @SubCommand({ description: (t) => t("commands.historical-bridge"), args }) - public bridge(context: CommandContext) { - return this.run(context, BRIDGE_MODES, (base, mode) => ( - - )); + public bridge() { + return message; } @SubCommand({ description: (t) => t("commands.historical-blitzsg"), args }) - public blitzsg(context: CommandContext) { - return this.run( - context, - BLITZSG_MODES, - (base, mode) => , - filterBlitzKits - ); + public blitzsg() { + return message; } @SubCommand({ description: (t) => t("commands.historical-buildbattle"), args }) - public buildbattle(context: CommandContext) { - return this.run(context, BUILD_BATTLE_MODES, (base) => ( - - )); + public buildbattle() { + return message; } @SubCommand({ description: (t) => t("commands.historical-copsandcrims"), args }) - public copsandcrims(context: CommandContext) { - return this.run(context, COPS_AND_CRIMS_MODES, (base, mode) => ( - - )); + public copsandcrims() { + return message; } @SubCommand({ description: (t) => t("commands.historical-duels"), args }) - public duels(context: CommandContext) { - return this.run(context, DUELS_MODES, (base, mode) => ( - - )); + public duels() { + return message; } @SubCommand({ description: (t) => t("commands.historical-general"), args }) - public general(context: CommandContext) { - return this.run(context, GENERAL_MODES, (base) => ( - - )); + public general() { + return message; } @SubCommand({ description: (t) => t("commands.historical-megawalls"), args }) - public megawalls(context: CommandContext) { - return this.run( - context, - MEGAWALLS_MODES, - (base, mode) => , - filterMegaWallsKits - ); + public megawalls() { + return message; } @SubCommand({ description: (t) => t("commands.historical-murdermystery"), args }) - public murdermystery(context: CommandContext) { - return this.run(context, MURDER_MYSTERY_MODES, (base, mode) => ( - - )); + public murdermystery() { + return message; } @SubCommand({ @@ -181,16 +102,16 @@ export class HistoricalBase { args, group: "classic", }) - public paintball(context: CommandContext) { - return this.run(context, PAINTBALL_MODES, (base) => ); + public paintball() { + return message; } @SubCommand({ description: (t) => t("commands.historical-pit"), args, }) - public pit(context: CommandContext) { - return this.run(context, PIT_MODES, (base) => ); + public pit() { + return message; } @SubCommand({ @@ -198,36 +119,28 @@ export class HistoricalBase { args, group: "classic", }) - public quake(context: CommandContext) { - return this.run(context, QUAKE_MODES, (base, mode) => ( - - )); + public quake() { + return message; } @SubCommand({ description: (t) => t("commands.historical-skywars"), args }) - public skywars(context: CommandContext) { - return this.run(context, SKYWARS_MODES, (base, mode) => ( - - )); + public skywars() { + return message; } @SubCommand({ description: (t) => t("commands.historical-smashheroes"), args }) - public smashheroes(context: CommandContext) { - return this.run(context, SMASH_HEROES_MODES, (base, mode) => ( - - )); + public smashheroes() { + return message; } @SubCommand({ description: (t) => t("commands.historical-speeduhc"), args }) - public speeduhc(context: CommandContext) { - return this.run(context, SPEED_UHC_MODES, (base, mode) => ( - - )); + public speeduhc() { + return message; } @SubCommand({ description: (t) => t("commands.historical-tntgames"), args }) - public tntgames(context: CommandContext) { - return this.run(context, TNT_GAMES_MODES, (base) => ); + public tntgames() { + return message; } @SubCommand({ @@ -235,17 +148,13 @@ export class HistoricalBase { args, group: "classic", }) - public turbokartracers(context: CommandContext) { - return this.run(context, TURBO_KART_RACERS_MODES, (base) => ( - - )); + public turbokartracers() { + return message; } @SubCommand({ description: (t) => t("commands.historical-uhc"), args }) - public uhc(context: CommandContext) { - return this.run(context, UHC_MODES, (base, mode) => ( - - )); + public uhc() { + return message; } @SubCommand({ @@ -253,10 +162,8 @@ export class HistoricalBase { args, group: "classic", }) - public vampirez(context: CommandContext) { - return this.run(context, VAMPIREZ_MODES, (base, mode) => ( - - )); + public vampirez() { + return message; } @SubCommand({ @@ -264,42 +171,17 @@ export class HistoricalBase { args, group: "classic", }) - public walls(context: CommandContext) { - return this.run(context, WALLS_MODES, (base) => ); + public walls() { + return message; } @SubCommand({ description: (t) => t("commands.historical-warlords"), args }) - public warlords(context: CommandContext) { - return this.run(context, WARLORDS_MODES, (base, mode) => ( - - )); - } - - @SubCommand({ description: (t) => t("commands.historical-woolwars"), args }) - public woolwars(context: CommandContext) { - return this.run(context, WOOLWARS_MODES, (base, mode) => ( - - )); + public warlords() { + return message; } - protected run( - _context: CommandContext, - _modes: GameModes, - _getProfile: (base: BaseProfileProps, mode: GameMode) => JSX.Element, - _filterModes?: (player: Player, modes: GameMode[]) => GameMode[] - ): IMessage { - const preview = { - name: "preview.png", - data: readFileSync(getAssetPath("previews/session.png")), - type: "image/png", - }; - - const embed = new EmbedBuilder() - .color(STATUS_COLORS.info) - .title((t) => t("historical.disabledWarning.title")) - .description((t) => t("historical.disabledWarning.description")) - .image(`attachment://${preview.name}`); - - return { embeds: [embed], files: [preview] }; + @SubCommand({ description: (t) => t("commands.historical-woolgames"), args }) + public woolgames() { + return message; } } diff --git a/apps/discord-bot/src/commands/historical/session.command.tsx b/apps/discord-bot/src/commands/historical/session.command.tsx index d5e0f8b76..75f92aa49 100644 --- a/apps/discord-bot/src/commands/historical/session.command.tsx +++ b/apps/discord-bot/src/commands/historical/session.command.tsx @@ -17,6 +17,7 @@ import { DUELS_MODES, GENERAL_MODES, GameMode, + GameModeWithSubModes, GameModes, MEGAWALLS_MODES, MURDER_MYSTERY_MODES, @@ -33,7 +34,7 @@ import { VAMPIREZ_MODES, WALLS_MODES, WARLORDS_MODES, - WOOLWARS_MODES, + WOOLGAMES_MODES, } from "@statsify/schemas"; import { ApiService, @@ -43,6 +44,7 @@ import { PaginateService, PlayerArgument, SubCommand, + SubPage, } from "@statsify/discord"; import { ArcadeProfile } from "../arcade/arcade.profile.js"; import { ArenaBrawlProfile } from "../arenabrawl/arenabrawl.profile.js"; @@ -70,7 +72,7 @@ import { UHCProfile } from "../uhc/uhc.profile.js"; import { VampireZProfile } from "../vampirez/vampirez.profile.js"; import { WallsProfile } from "../walls/walls.profile.js"; import { WarlordsProfile } from "../warlords/warlords.profile.js"; -import { WoolWarsProfile } from "../woolwars/woolwars.profile.js"; +import { WoolGamesProfile } from "../woolgames/woolgames.profile.js"; import { getBackground, getLogo } from "@statsify/assets"; import { getTheme } from "#themes"; import { render } from "@statsify/rendering"; @@ -266,10 +268,10 @@ export class SessionCommand { )); } - @SubCommand({ description: (t) => t("commands.session-woolwars"), args: [PlayerArgument] }) - public woolwars(context: CommandContext) { - return this.run(context, WOOLWARS_MODES, (base, mode) => ( - + @SubCommand({ description: (t) => t("commands.session-woolgames"), args: [PlayerArgument] }) + public woolgames(context: CommandContext) { + return this.run(context, WOOLGAMES_MODES, (base, mode) => ( + )); } @@ -277,7 +279,7 @@ export class SessionCommand { context: CommandContext, modes: GameModes, getProfile: (base: BaseProfileProps, mode: GameMode) => JSX.Element, - filterModes?: (player: Player, modes: GameMode[]) => GameMode[] + filterModes?: (player: Player, modes: GameModeWithSubModes[]) => GameModeWithSubModes[] ) { const user = context.getUser(); @@ -296,50 +298,88 @@ export class SessionCommand { const allModes = modes.getModes(); const displayedModes = filterModes ? filterModes(player, allModes) : allModes; - const pages: Page[] = displayedModes.map((mode) => ({ - label: mode.formatted, - generator: async (t) => { - const background = await getBackground(...mapBackground(modes, mode.api)); - - const displayName = this.apiService.emojiDisplayName(t, player.displayName); - - let content: string | undefined = undefined; - - if (player.isNew) { - content = t("historical.newSession", { displayName }); - } else if (Math.random() < 0.1) { - content = t("tips.resetSession"); - } - - const profile = getProfile( - { - player, - skin, - background, - logo, - t, - user, - badge, - time: { - timeType: HistoricalTimes.SESSION, - sessionReset: player.sessionReset ? - DateTime.fromSeconds(player.sessionReset) : - DateTime.now(), + const pages: Page[] = displayedModes.map((mode) => { + if (mode.submodes.length === 0) return { + label: mode.formatted, + generator: async (t) => { + const background = await getBackground(...mapBackground(modes, mode.api)); + + const displayName = this.apiService.emojiDisplayName(t, player.displayName); + + let content: string | undefined = undefined; + + if (player.isNew) { + content = t("historical.newSession", { displayName }); + } else if (Math.random() < 0.1) { + content = t("tips.resetSession"); + } + + const profile = getProfile( + { + player, + skin, + background, + logo, + t, + user, + badge, + time: { + timeType: HistoricalTimes.SESSION, + sessionReset: player.sessionReset ? + DateTime.fromSeconds(player.sessionReset) : + DateTime.now(), + }, }, - }, - mode - ); - - const canvas = render(profile, getTheme(user)); - const buffer = await canvas.toBuffer("png"); - - return { - content, - files: [{ name: "session.png", data: buffer, type: "image/png" }], - attachments: [], - }; - }, - })); + { ...mode, submode: undefined } as unknown as GameMode + ); + + const canvas = render(profile, getTheme(user)); + const buffer = await canvas.toBuffer("png"); + + return { + content, + files: [{ name: "session.png", data: buffer, type: "image/png" }], + attachments: [], + }; + }, + }; + + const subPages = mode.submodes.map((submode): SubPage => ({ + label: submode.formatted, + generator: async (t) => { + const background = await getBackground(...mapBackground(modes, mode.api)); + + const profile = getProfile( + { + player, + skin, + background, + logo, + t, + user, + badge, + time: { + timeType: HistoricalTimes.SESSION, + sessionReset: player.sessionReset ? + DateTime.fromSeconds(player.sessionReset) : + DateTime.now(), + }, + }, + { api: mode.api, formatted: mode.formatted, hypixel: mode.hypixel, submode } as GameMode + ); + + const canvas = render(profile, getTheme(user)); + const buffer = await canvas.toBuffer("png"); + + return { + files: [{ name: "session.png", data: buffer, type: "image/png" }], + attachments: [], + }; + }, + })); + + return { label: mode.formatted, subPages }; + }); return this.paginateService.paginate(context, pages); } diff --git a/apps/discord-bot/src/commands/leaderboards/player-leaderboard.command.ts b/apps/discord-bot/src/commands/leaderboards/player-leaderboard.command.ts index 8f0225a0d..1a9159e80 100644 --- a/apps/discord-bot/src/commands/leaderboards/player-leaderboard.command.ts +++ b/apps/discord-bot/src/commands/leaderboards/player-leaderboard.command.ts @@ -34,7 +34,7 @@ import { VAMPIREZ_MODES, WALLS_MODES, WARLORDS_MODES, - WOOLWARS_MODES, + WOOLGAMES_MODES, } from "@statsify/schemas"; import { ApiService, @@ -268,11 +268,11 @@ export class PlayerLeaderboardCommand extends BaseLeaderboardCommand { } @SubCommand({ - description: (t) => t("commands.leaderboard-woolwars"), - args: [new PlayerLeaderboardArgument("woolwars")], + description: (t) => t("commands.leaderboard-woolgames"), + args: [new PlayerLeaderboardArgument("woolgames")], }) - public woolwars(context: CommandContext) { - return this.run(context, "woolwars", WOOLWARS_MODES); + public woolgames(context: CommandContext) { + return this.run(context, "woolgames", WOOLGAMES_MODES); } @SubCommand({ @@ -293,7 +293,7 @@ export class PlayerLeaderboardCommand extends BaseLeaderboardCommand { const field = `stats.${prefix}.${leaderboard.replaceAll(" ", ".")}`; const background = await getBackground( - ...mapBackground(modes, modes.getModes()[0].api) + ...mapBackground(modes, modes.getApiModes()[0]) ); const getLeaderboard = this.apiService.getPlayerLeaderboard.bind(this.apiService); diff --git a/apps/discord-bot/src/commands/megawalls/megawalls.command.tsx b/apps/discord-bot/src/commands/megawalls/megawalls.command.tsx index f30589015..52f39e02d 100644 --- a/apps/discord-bot/src/commands/megawalls/megawalls.command.tsx +++ b/apps/discord-bot/src/commands/megawalls/megawalls.command.tsx @@ -12,7 +12,7 @@ import { ProfileData, } from "#commands/base.hypixel-command"; import { Command } from "@statsify/discord"; -import { GameMode, MEGAWALLS_MODES, MegaWallsModes, Player } from "@statsify/schemas"; +import { GameModeWithSubModes, MEGAWALLS_MODES, MegaWallsModes, Player } from "@statsify/schemas"; import { MegaWallsProfile, filterMegaWallsKits } from "./megawalls.profile.js"; @Command({ description: (t) => t("commands.megawalls") }) @@ -23,8 +23,8 @@ export class MegaWallsCommand extends BaseHypixelCommand { public filterModes( player: Player, - modes: GameMode[] - ): GameMode[] { + modes: GameModeWithSubModes[] + ): GameModeWithSubModes[] { return filterMegaWallsKits(player, modes); } diff --git a/apps/discord-bot/src/commands/megawalls/megawalls.profile.tsx b/apps/discord-bot/src/commands/megawalls/megawalls.profile.tsx index c39c765a5..d882f9d0f 100644 --- a/apps/discord-bot/src/commands/megawalls/megawalls.profile.tsx +++ b/apps/discord-bot/src/commands/megawalls/megawalls.profile.tsx @@ -7,7 +7,7 @@ */ import { Container, Footer, Header, SidebarItem, Table } from "#components"; -import { FormattedGame, GameMode, MegaWallsModes, Player } from "@statsify/schemas"; +import { FormattedGame, type GameMode, type GameModeWithSubModes, MegaWallsModes, Player } from "@statsify/schemas"; import { formatTime, prettify } from "@statsify/util"; import type { BaseProfileProps } from "#commands/base.hypixel-command"; @@ -101,8 +101,8 @@ export const MegaWallsProfile = ({ export function filterMegaWallsKits( player: Player, - modes: GameMode[] -): GameMode[] { + modes: GameModeWithSubModes[] +): GameModeWithSubModes[] { const { megawalls } = player.stats; const [overall, ...kits] = modes; diff --git a/apps/discord-bot/src/commands/murdermystery/murdermystery.profile.tsx b/apps/discord-bot/src/commands/murdermystery/murdermystery.profile.tsx index 43e3f199d..28b8f06bc 100644 --- a/apps/discord-bot/src/commands/murdermystery/murdermystery.profile.tsx +++ b/apps/discord-bot/src/commands/murdermystery/murdermystery.profile.tsx @@ -7,7 +7,7 @@ */ import { Container, Footer, Header, Historical, SidebarItem, Table } from "#components"; -import { FormattedGame, GameMode, MurderMysteryModes } from "@statsify/schemas"; +import { FormattedGame, type GameMode, MurderMysteryModes } from "@statsify/schemas"; import { formatTime } from "@statsify/util"; import type { BaseProfileProps } from "#commands/base.hypixel-command"; diff --git a/apps/discord-bot/src/commands/quake/quake.profile.tsx b/apps/discord-bot/src/commands/quake/quake.profile.tsx index 16a79ada7..b47eca731 100644 --- a/apps/discord-bot/src/commands/quake/quake.profile.tsx +++ b/apps/discord-bot/src/commands/quake/quake.profile.tsx @@ -14,7 +14,7 @@ import { Table, formatProgression, } from "#components"; -import { FormattedGame, GameMode, QuakeModes } from "@statsify/schemas"; +import { FormattedGame, type GameMode, QuakeModes } from "@statsify/schemas"; import type { BaseProfileProps } from "#commands/base.hypixel-command"; export interface QuakeProfileProps extends BaseProfileProps { diff --git a/apps/discord-bot/src/commands/quests/quests.command.tsx b/apps/discord-bot/src/commands/quests/quests.command.tsx index 87b96d3b9..01c484c6a 100644 --- a/apps/discord-bot/src/commands/quests/quests.command.tsx +++ b/apps/discord-bot/src/commands/quests/quests.command.tsx @@ -88,7 +88,7 @@ export class QuestsCommand { user, badge, time, - mode, + mode: { ...mode, submode: undefined }, gameIcons, logos: [crossLogo, verifiedLogo], }); diff --git a/apps/discord-bot/src/commands/quests/quests.profile.tsx b/apps/discord-bot/src/commands/quests/quests.profile.tsx index 515485361..2e33dfd09 100644 --- a/apps/discord-bot/src/commands/quests/quests.profile.tsx +++ b/apps/discord-bot/src/commands/quests/quests.profile.tsx @@ -11,8 +11,8 @@ import { ClassMetadata, DailyQuests, FormattedGame, - GameId, - GameMode, + type GameId, + type GameMode, GameQuests, GenericQuestInstance, METADATA_KEY, @@ -62,7 +62,7 @@ const questMetadata = [DailyQuests, WeeklyQuests, OverallQuests].map((constructo export interface QuestProfileProps extends Omit { mode: GameMode; gameIcons: Record; - logos: [check: Image, cross: Image]; + logos: [cross: Image, check: Image]; time: QuestTime; } @@ -136,10 +136,10 @@ interface GameTableProps { t: LocalizeFunction; time: QuestTime; game: keyof typeof FormattedGame; - logos: [Image, Image]; + logos: [cross: Image, check: Image]; } -const GameTable = ({ quests, t, game, time, logos }: GameTableProps) => { +const GameTable = ({ quests, t, game, time, logos: [cross, check] }: GameTableProps) => { const isOverall = time === QuestTime.Overall; const entries = Object.entries(quests) @@ -156,7 +156,7 @@ const GameTable = ({ quests, t, game, time, logos }: GameTableProps) => {
{isOverall ? {t(completions)} : - } + } ); }); diff --git a/apps/discord-bot/src/commands/rankings/rankings.command.tsx b/apps/discord-bot/src/commands/rankings/rankings.command.tsx index dcec60feb..60cc34204 100644 --- a/apps/discord-bot/src/commands/rankings/rankings.command.tsx +++ b/apps/discord-bot/src/commands/rankings/rankings.command.tsx @@ -37,7 +37,7 @@ import { VAMPIREZ_MODES, WALLS_MODES, WARLORDS_MODES, - WOOLWARS_MODES, + WOOLGAMES_MODES, } from "@statsify/schemas"; import { ApiService, @@ -298,10 +298,10 @@ export class RankingsCommand { @SubCommand({ ...options, - description: (t) => t("commands.rankings-woolwars"), + description: (t) => t("commands.rankings-woolgames"), }) - public woolwars(context: CommandContext) { - return this.run(context, "woolwars", WOOLWARS_MODES); + public woolgames(context: CommandContext) { + return this.run(context, "woolgames", WOOLGAMES_MODES); } private async run( diff --git a/apps/discord-bot/src/commands/ratios/ratios.command.tsx b/apps/discord-bot/src/commands/ratios/ratios.command.tsx index e5d3536fd..43bb2a4fb 100644 --- a/apps/discord-bot/src/commands/ratios/ratios.command.tsx +++ b/apps/discord-bot/src/commands/ratios/ratios.command.tsx @@ -14,8 +14,8 @@ import { BlitzSGKit, COPS_AND_CRIMS_MODES, DUELS_MODES, - GameMode, - GameModes, + type GameModeWithSubModes, + type GameModes, LEADERBOARD_RATIOS, MEGAWALLS_MODES, MURDER_MYSTERY_MODES, @@ -31,7 +31,7 @@ import { VAMPIREZ_MODES, WALLS_MODES, WARLORDS_MODES, - WOOLWARS_MODES, + WOOLGAMES_MODES, } from "@statsify/schemas"; import { ApiService, @@ -42,6 +42,7 @@ import { PlayerArgument, SubCommand, } from "@statsify/discord"; +import { Constructor, prettify } from "@statsify/util"; import { GamesWithBackgrounds, MODES_TO_API, @@ -51,7 +52,6 @@ import { import { RatiosProfile, RatiosProfileProps } from "./ratios.profile.js"; import { getBackground, getLogo } from "@statsify/assets"; import { getTheme } from "#themes"; -import { prettify } from "@statsify/util"; import { render } from "@statsify/rendering"; const args = [PlayerArgument]; @@ -172,15 +172,15 @@ export class RatiosCommand { return this.run(context, WARLORDS_MODES); } - @SubCommand({ description: (t) => t("commands.ratios-woolwars"), args }) - public woolwars(context: CommandContext) { - return this.run(context, WOOLWARS_MODES); + @SubCommand({ description: (t) => t("commands.ratios-woolgames"), args }) + public woolgames(context: CommandContext) { + return this.run(context, WOOLGAMES_MODES); } private async run( context: CommandContext, modes: GameModes, - filterModes?: (player: Player, modes: GameMode[]) => GameMode[] + filterModes?: (player: Player, modes: GameModeWithSubModes[]) => GameModeWithSubModes[] ) { const user = context.getUser(); const player = await this.apiService.getPlayer(context.option("player"), user); @@ -218,7 +218,7 @@ export class RatiosCommand { t, user, badge, - mode, + mode: { ...mode, submode: mode.submodes.length === 0 ? undefined : mode.submodes[0] }, gameName: MODES_TO_FORMATTED.get(modes)!, ratios: ratios.map((r) => [ stats[r[0] as keyof typeof stats], @@ -241,7 +241,13 @@ export class RatiosCommand { return this.paginateService.paginate(context, pages); } - private getModeStats(game: PlayerStats[keyof PlayerStats], mode: GameMode) { + private getModeStats(game: PlayerStats[keyof PlayerStats], mode: GameModeWithSubModes) { + if (mode.submodes.length !== 0) { + let stats = game[mode.api as keyof typeof game]; + stats = stats[mode.submodes[0].api as keyof typeof game]; + return mode.submodes[0].api === "overall" ? stats || game : stats; + } + const stats = game[mode.api as keyof typeof game]; return mode.api === "overall" ? stats || game : stats; } @@ -252,18 +258,15 @@ export class RatiosCommand { ) { const gameClass = Reflect.getMetadata("design:type", PlayerStats.prototype, key); - const ratioModes: [mode: GameMode, ratios: Ratio[]][] = []; + const ratioModes: [mode: GameModeWithSubModes, ratios: Ratio[]][] = []; const gameModes = modes.getModes(); for (const mode of gameModes) { if (!mode.api) continue; - const modeClass = - mode.api === "overall" ? - Reflect.getMetadata("design:type", gameClass.prototype, mode.api) || gameClass : - Reflect.getMetadata("design:type", gameClass.prototype, mode.api); - + const modeClass = this.getModeClass(mode, gameClass); if (!modeClass) continue; + const ratios = LEADERBOARD_RATIOS.filter(([numerator, denominator]) => { const numeratorType = Reflect.getMetadata( "design:type", @@ -287,4 +290,16 @@ export class RatiosCommand { return ratioModes; } + + private getModeClass(mode: GameModeWithSubModes, gameClass: Constructor) { + const apiType = Reflect.getMetadata("design:type", gameClass.prototype, mode.api); + const modeType = mode.api === "overall" ? apiType || gameClass : apiType; + + if (mode.submodes.length === 0) return modeType; + + // @ts-expect-error TypeScript doesn't realize that an api field will always be present when submodes is not empty + const submode = mode.submodes[0].api as string; + const submodeType = Reflect.getMetadata("design:type", modeType.prototype, submode); + return submode === "overall" ? submodeType || modeType : submodeType; + } } diff --git a/apps/discord-bot/src/commands/ratios/ratios.profile.tsx b/apps/discord-bot/src/commands/ratios/ratios.profile.tsx index aac7c6177..a66ddb67f 100644 --- a/apps/discord-bot/src/commands/ratios/ratios.profile.tsx +++ b/apps/discord-bot/src/commands/ratios/ratios.profile.tsx @@ -7,7 +7,7 @@ */ import { Container, Footer, Header, List, Table } from "#components"; -import { FormattedGame, GameMode } from "@statsify/schemas"; +import { FormattedGame, type GameMode } from "@statsify/schemas"; import { LocalizeFunction } from "@statsify/discord"; import { ratio } from "@statsify/math"; import type { BaseProfileProps } from "#commands/base.hypixel-command"; diff --git a/apps/discord-bot/src/commands/skywars/skywars-challenges.command.tsx b/apps/discord-bot/src/commands/skywars/skywars-challenges.command.tsx index 77ba976ed..b556197b8 100644 --- a/apps/discord-bot/src/commands/skywars/skywars-challenges.command.tsx +++ b/apps/discord-bot/src/commands/skywars/skywars-challenges.command.tsx @@ -8,7 +8,7 @@ import { BaseHypixelCommand, BaseProfileProps } from "#commands/base.hypixel-command"; import { Command } from "@statsify/discord"; -import { GameMode, Player, SKYWARS_MODES, SkyWarsModes } from "@statsify/schemas"; +import { type GameModeWithSubModes, Player, SKYWARS_MODES, SkyWarsModes } from "@statsify/schemas"; import { SkyWarsChallengesProfile } from "./skywars-challenges.profile.js"; @Command({ description: (t) => t("commands.skywars-challenges") }) @@ -19,8 +19,8 @@ export class SkyWarsChallengesCommand extends BaseHypixelCommand { public filterModes( player: Player, - modes: GameMode[] - ): GameMode[] { + modes: GameModeWithSubModes[] + ): GameModeWithSubModes[] { return [modes[0]]; } diff --git a/apps/discord-bot/src/commands/skywars/skywars.profile.tsx b/apps/discord-bot/src/commands/skywars/skywars.profile.tsx index c82a2f7a8..0988d162e 100644 --- a/apps/discord-bot/src/commands/skywars/skywars.profile.tsx +++ b/apps/discord-bot/src/commands/skywars/skywars.profile.tsx @@ -15,7 +15,7 @@ import { Table, formatProgression, } from "#components"; -import { FormattedGame, GameMode, SkyWarsModes } from "@statsify/schemas"; +import { FormattedGame, type GameMode, SkyWarsModes } from "@statsify/schemas"; import { formatTime, prettify } from "@statsify/util"; import type { BaseProfileProps } from "#commands/base.hypixel-command"; diff --git a/apps/discord-bot/src/commands/smashheroes/smashheroes.profile.tsx b/apps/discord-bot/src/commands/smashheroes/smashheroes.profile.tsx index 1f0797dfc..d119a058f 100644 --- a/apps/discord-bot/src/commands/smashheroes/smashheroes.profile.tsx +++ b/apps/discord-bot/src/commands/smashheroes/smashheroes.profile.tsx @@ -7,7 +7,7 @@ */ import { Container, Footer, Header, SidebarItem, Table } from "#components"; -import { FormattedGame, GameMode, SmashHeroesModes } from "@statsify/schemas"; +import { FormattedGame, type GameMode, SmashHeroesModes } from "@statsify/schemas"; import { prettify } from "@statsify/util"; import type { BaseProfileProps } from "#commands/base.hypixel-command"; diff --git a/apps/discord-bot/src/commands/speeduhc/speeduhc.profile.tsx b/apps/discord-bot/src/commands/speeduhc/speeduhc.profile.tsx index c588985f7..56400dd49 100644 --- a/apps/discord-bot/src/commands/speeduhc/speeduhc.profile.tsx +++ b/apps/discord-bot/src/commands/speeduhc/speeduhc.profile.tsx @@ -15,7 +15,7 @@ import { Table, formatProgression, } from "#components"; -import { FormattedGame, GameMode, SpeedUHCMode, SpeedUHCModes } from "@statsify/schemas"; +import { FormattedGame, type GameMode, SpeedUHCMode, SpeedUHCModes } from "@statsify/schemas"; import { prettify } from "@statsify/util"; import type { BaseProfileProps } from "#commands/base.hypixel-command"; diff --git a/apps/discord-bot/src/commands/uhc/uhc.profile.tsx b/apps/discord-bot/src/commands/uhc/uhc.profile.tsx index 47e272ce8..44d67481d 100644 --- a/apps/discord-bot/src/commands/uhc/uhc.profile.tsx +++ b/apps/discord-bot/src/commands/uhc/uhc.profile.tsx @@ -14,7 +14,7 @@ import { Table, formatProgression, } from "#components"; -import { FormattedGame, GameMode, UHCModes } from "@statsify/schemas"; +import { FormattedGame, type GameMode, UHCModes } from "@statsify/schemas"; import { prettify } from "@statsify/util"; import type { BaseProfileProps } from "#commands/base.hypixel-command"; diff --git a/apps/discord-bot/src/commands/vampirez/vampirez.profile.tsx b/apps/discord-bot/src/commands/vampirez/vampirez.profile.tsx index 8fcdc3855..a22e8d699 100644 --- a/apps/discord-bot/src/commands/vampirez/vampirez.profile.tsx +++ b/apps/discord-bot/src/commands/vampirez/vampirez.profile.tsx @@ -14,7 +14,7 @@ import { Table, formatProgression, } from "#components"; -import { FormattedGame, GameMode, VampireZModes } from "@statsify/schemas"; +import { FormattedGame, type GameMode, VampireZModes } from "@statsify/schemas"; import type { BaseProfileProps } from "#commands/base.hypixel-command"; export interface VampireZProfileProps extends BaseProfileProps { diff --git a/apps/discord-bot/src/commands/warlords/warlords.profile.tsx b/apps/discord-bot/src/commands/warlords/warlords.profile.tsx index 40ec3e1b5..3d131e39e 100644 --- a/apps/discord-bot/src/commands/warlords/warlords.profile.tsx +++ b/apps/discord-bot/src/commands/warlords/warlords.profile.tsx @@ -7,7 +7,7 @@ */ import { Container, Footer, Header, SidebarItem, Table } from "#components"; -import { FormattedGame, GameMode, WarlordsModes } from "@statsify/schemas"; +import { FormattedGame, type GameMode, WarlordsModes } from "@statsify/schemas"; import { WarlordsClassTable } from "./tables/index.js"; import { prettify } from "@statsify/util"; import type { BaseProfileProps } from "#commands/base.hypixel-command"; diff --git a/apps/discord-bot/src/commands/woolgames/capture-the-wool.table.tsx b/apps/discord-bot/src/commands/woolgames/capture-the-wool.table.tsx new file mode 100644 index 000000000..3514b8815 --- /dev/null +++ b/apps/discord-bot/src/commands/woolgames/capture-the-wool.table.tsx @@ -0,0 +1,93 @@ +/** + * 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 { Historical, If, Table } from "#components"; +import { formatTime } from "@statsify/util"; +import type { CaptureTheWool } from "@statsify/schemas"; +import type { LocalizeFunction } from "@statsify/discord"; +import type { ProfileTime } from "#commands/base.hypixel-command"; + +interface CaptureTheWoolTableProps { + captureTheWool: CaptureTheWool; + t: LocalizeFunction; + time: ProfileTime; +} + +export const CaptureTheWoolTable = ({ captureTheWool, t, time }: CaptureTheWoolTableProps) => ( + + + + + + + + + + + + + + + + + + + + + 0}> + + + 0}> + + + 0}> + + + + + + + + + + + + + + + + + + + + +); diff --git a/apps/discord-bot/src/commands/woolgames/sheepwars.table.tsx b/apps/discord-bot/src/commands/woolgames/sheepwars.table.tsx new file mode 100644 index 000000000..d2171719a --- /dev/null +++ b/apps/discord-bot/src/commands/woolgames/sheepwars.table.tsx @@ -0,0 +1,40 @@ +/** + * 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 { prettify } from "@statsify/util"; +import type { LocalizeFunction } from "@statsify/discord"; +import type { SheepWars } from "@statsify/schemas"; + +export interface SheepWarsTableProps { + sheepwars: SheepWars; + t: LocalizeFunction; +} + +export const SheepWarsTable = ({ sheepwars, t }: SheepWarsTableProps) => { + const stats = sheepwars; + + return ( + <> + + + + + + + + + + + + + + + + ); +}; diff --git a/apps/discord-bot/src/commands/woolwars/woolwars.command.tsx b/apps/discord-bot/src/commands/woolgames/woolgames.command.tsx similarity index 55% rename from apps/discord-bot/src/commands/woolwars/woolwars.command.tsx rename to apps/discord-bot/src/commands/woolgames/woolgames.command.tsx index 72f11c538..dd02a1ae7 100644 --- a/apps/discord-bot/src/commands/woolwars/woolwars.command.tsx +++ b/apps/discord-bot/src/commands/woolgames/woolgames.command.tsx @@ -12,19 +12,19 @@ import { ProfileData, } from "#commands/base.hypixel-command"; import { Command } from "@statsify/discord"; -import { WOOLWARS_MODES, WoolWarsModes } from "@statsify/schemas"; -import { WoolWarsProfile } from "./woolwars.profile.js"; +import { WOOLGAMES_MODES, WoolGamesModes } from "@statsify/schemas"; +import { WoolGamesProfile } from "./woolgames.profile.js"; -@Command({ description: (t) => t("commands.woolwars") }) -export class WoolWarsCommand extends BaseHypixelCommand { +@Command({ description: (t) => t("commands.woolgames") }) +export class WoolGamesCommand extends BaseHypixelCommand { public constructor() { - super(WOOLWARS_MODES); + super(WOOLGAMES_MODES); } public getProfile( base: BaseProfileProps, - { mode }: ProfileData + { mode }: ProfileData ): JSX.Element { - return ; + return ; } } diff --git a/apps/discord-bot/src/commands/woolgames/woolgames.profile.tsx b/apps/discord-bot/src/commands/woolgames/woolgames.profile.tsx new file mode 100644 index 000000000..1cc74fd3e --- /dev/null +++ b/apps/discord-bot/src/commands/woolgames/woolgames.profile.tsx @@ -0,0 +1,115 @@ +/** + * 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 { CaptureTheWoolTable } from "./capture-the-wool.table.js"; +import { + Container, + Footer, + Header, + Historical, + SidebarItem, + Table, + formatProgression, +} from "#components"; +import { + FormattedGame, + type GameMode, + WoolGamesModes, +} from "@statsify/schemas"; +import { SheepWarsTable } from "./sheepwars.table.js"; +import { WoolWarsTable } from "./woolwars.table.js"; +import { formatTime } from "@statsify/util"; +import type { BaseProfileProps } from "#commands/base.hypixel-command"; + +export interface WoolGamesProfileProps extends BaseProfileProps { + mode: GameMode; +} + +export const WoolGamesProfile = ({ + skin, + player, + background, + logo, + user, + badge, + mode, + t, + time, +}: WoolGamesProfileProps) => { + const { woolgames } = player.stats; + + const sidebar: SidebarItem[] = [ + [t("stats.wool"), t(woolgames.coins), "§6"], + [t("stats.layers"), `${t(woolgames.layers)}§8/§a${t(100)}`, "§a"], + [t("stats.playtime"), formatTime(woolgames.playtime), "§d"], + ]; + + let table; + + switch (mode.api) { + case "woolwars": + table = ; + sidebar.push( + [t("stats.woolPlaced"), t(woolgames.woolwars.overall.woolPlaced), "§e"], + [t("stats.blocksBroken"), t(woolgames.woolwars.overall.blocksBroken), "§c"], + [t("stats.powerups"), t(woolgames.woolwars.overall.powerups), "§b"] + ); + break; + + case "captureTheWool": + table = ; + sidebar.push( + [t("stats.goldEarned"), t(woolgames.captureTheWool.goldEarned), "§6"], + [t("stats.goldSpent"), t(woolgames.captureTheWool.goldSpent), "§6"] + ); + break; + + case "sheepwars": + table = ; + sidebar.push( + [t("stats.magicWool"), t(woolgames.sheepwars.magicWool), "§5"], + [t("stats.sheepThrown"), t(woolgames.sheepwars.sheepThrown), "§c"] + ); + break; + } + + return ( + +
+ + {table} + + +