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
121 changes: 83 additions & 38 deletions apps/discord-bot/src/commands/ratios/ratios.command.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ import { render } from "@statsify/rendering";

const args = [PlayerArgument];

type RatioMode<T extends GamesWithBackgrounds> = {
mode: GameModeWithSubModes<T>;
formatted: string;
submode?: GameModeWithSubModes<T>["submodes"][number];
};

@Command({ description: (t) => t("commands.ratios") })
export class RatiosCommand {
public constructor(
Expand Down Expand Up @@ -92,7 +98,7 @@ export class RatiosCommand {
const filteredKits = kits
.sort(
(a, b) =>
(blitzsg[b.api] as BlitzSGKit).exp - (blitzsg[a.api] as BlitzSGKit).exp
(blitzsg[b.mode.api] as BlitzSGKit).exp - (blitzsg[a.mode.api] as BlitzSGKit).exp
)
.slice(0, 24);

Expand Down Expand Up @@ -186,7 +192,7 @@ export class RatiosCommand {
private async run<T extends GamesWithBackgrounds>(
context: CommandContext,
modes: GameModes<T>,
filterModes?: (player: Player, modes: GameModeWithSubModes<T>[]) => GameModeWithSubModes<T>[]
filterModes?: (player: Player, modes: RatioMode<T>[]) => RatioMode<T>[]
) {
const user = context.getUser();
const player = await this.apiService.getPlayer(context.option("player"), user);
Expand All @@ -203,18 +209,20 @@ export class RatiosCommand {

const ratiosPerMode = this.getRatiosPerMode(key, modes);

const allModes = ratiosPerMode.map(([mode]) => mode);
const allModes = ratiosPerMode.map(({ ratioMode }) => ratioMode);
const displayedModes = filterModes ? filterModes(player, allModes) : allModes;
const displayedModeKeys = new Set(displayedModes.map((mode) => this.getRatioModeKey(mode)));
const displayedRatiosPerMode = ratiosPerMode.filter(({ ratioMode }) =>
displayedModeKeys.has(this.getRatioModeKey(ratioMode))
);

const pages: Page[] = displayedModes.map((mode, index) => ({
label: mode.formatted,
const pages: Page[] = displayedRatiosPerMode.map(({ ratioMode, ratios }) => ({
label: ratioMode.formatted,
generator: async (t) => {
const background = await getBackground(...mapBackground(modes, mode.api));
const background = await getBackground(...mapBackground(modes, ratioMode.mode.api));

const game = player.stats[key];
const stats = this.getModeStats(game, mode);

const ratios = ratiosPerMode[index][1];
const stats = this.getModeStats(game, ratioMode);

const props: RatiosProfileProps = {
player,
Expand All @@ -224,7 +232,11 @@ export class RatiosCommand {
t,
user,
badge,
mode: { ...mode, submode: mode.submodes.length === 0 ? undefined : mode.submodes[0] },
mode: {
...ratioMode.mode,
formatted: ratioMode.formatted,
submode: ratioMode.submode,
},
gameName: MODES_TO_FORMATTED.get(modes)!,
ratios: ratios.map((r) => [
stats[r[0] as keyof typeof stats],
Expand All @@ -247,11 +259,13 @@ export class RatiosCommand {
return this.paginateService.paginate(context, pages);
}

private getModeStats(game: PlayerStats[keyof PlayerStats], mode: GameModeWithSubModes<any>) {
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;
private getModeStats(game: PlayerStats[keyof PlayerStats], ratioMode: RatioMode<any>) {
const { mode, submode } = ratioMode;

if (submode) {
const modeStats = game[mode.api as keyof typeof game];
const submodeStats = modeStats?.[submode.api as keyof typeof modeStats];
return submode.api === "overall" ? submodeStats || modeStats : submodeStats;
}

const stats = game[mode.api as keyof typeof game];
Expand All @@ -264,47 +278,78 @@ export class RatiosCommand {
) {
const gameClass = Reflect.getMetadata("design:type", PlayerStats.prototype, key);

const ratioModes: [mode: GameModeWithSubModes<T>, ratios: Ratio[]][] = [];
const ratioModes: { ratioMode: RatioMode<T>; ratios: Ratio[] }[] = [];
const gameModes = modes.getModes();

for (const mode of gameModes) {
if (!mode.api) continue;

const modeClass = this.getModeClass(mode, gameClass);
if (!modeClass) continue;
for (const ratioMode of this.getRatioModes(mode)) {
const modeClass = this.getModeClass(ratioMode, gameClass);
if (!modeClass) continue;

const ratios = LEADERBOARD_RATIOS.filter(([numerator, denominator]) => {
const numeratorType = Reflect.getMetadata(
"design:type",
modeClass.prototype,
numerator
);
const ratios = LEADERBOARD_RATIOS.filter(([numerator, denominator]) => {
const numeratorType = Reflect.getMetadata(
"design:type",
modeClass.prototype,
numerator
);

const denominatorType = Reflect.getMetadata(
"design:type",
modeClass.prototype,
denominator
);
const denominatorType = Reflect.getMetadata(
"design:type",
modeClass.prototype,
denominator
);

return numeratorType === Number && denominatorType === Number;
});
return numeratorType === Number && denominatorType === Number;
});

if (!ratios.length) continue;
if (!ratios.length) continue;

ratioModes.push([mode, ratios]);
ratioModes.push({ ratioMode, ratios });
}
}

return ratioModes;
}

private getModeClass<T extends GamesWithBackgrounds>(mode: GameModeWithSubModes<T>, gameClass: Constructor<any>) {
private getModeClass<T extends GamesWithBackgrounds>(ratioMode: RatioMode<T>, gameClass: Constructor<any>) {
const { mode, submode } = ratioMode;
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;
if (!submode) return modeType;

const submodeType = Reflect.getMetadata("design:type", modeType.prototype, submode.api);
return submode.api === "overall" ? submodeType || modeType : submodeType;
}

private getRatioModes<T extends GamesWithBackgrounds>(mode: GameModeWithSubModes<T>): RatioMode<T>[] {
const baseMode = {
mode,
formatted: mode.formatted,
};

if (mode.submodes.length === 0) return [baseMode];

const submodes = mode.submodes
.filter((submode) => submode.api !== "stats" && submode.api !== "titles")
.map((submode) => ({
mode,
submode,
formatted: this.formatSubmode(mode.formatted, submode),
}));

return mode.api === "overall" ? [baseMode, ...submodes] : submodes;
}

private formatSubmode(mode: string, submode: { api: string; formatted: string }) {
if (submode.api === "overall") return `${mode} Overall`;
if (submode.formatted.startsWith(mode)) return submode.formatted;
return `${mode} ${submode.formatted}`;
}

const submode = mode.submodes[0].api;
const submodeType = Reflect.getMetadata("design:type", modeType.prototype, submode);
return submode === "overall" ? submodeType || modeType : submodeType;
private getRatioModeKey<T extends GamesWithBackgrounds>(ratioMode: RatioMode<T>) {
return `${ratioMode.mode.api}:${ratioMode.submode?.api ?? ""}`;
}
}
43 changes: 39 additions & 4 deletions packages/schemas/src/player/gamemodes/duels/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,16 +54,51 @@ export const DUELS_MODES = new GameModes([
{ api: "doubles" },
{ api: "threes" },
{ api: "fours" },
{ api: "twoVTwoVTwoVTwo", formatted: "2v2v2v2" },
{ api: "threeVThreeVThreeVThree", formatted: "3v3v3v3" },
{ api: "capture", formatted: "CTF" },
],
},
{
api: "classic",
hypixel: "DUELS_CLASSIC_DUEL",
submodes: [
{ api: "overall" },
{ api: "solo" },
{ api: "doubles" },
],
},
{ api: "classic", hypixel: "DUELS_CLASSIC_DUEL" },
{ api: "combo", hypixel: "DUELS_COMBO_DUEL" },
{ api: "megawalls", formatted: "MegaWalls" },
{
api: "megawalls",
formatted: "MegaWalls",
submodes: [
{ api: "overall" },
{ api: "solo" },
{ api: "doubles" },
],
},
{ api: "nodebuff", hypixel: "DUELS_POTION_DUEL", formatted: "NoDebuff" },
{ api: "op", formatted: "OP" },
{
api: "op",
formatted: "OP",
submodes: [
{ api: "overall" },
{ api: "solo" },
{ api: "doubles" },
],
},
{ api: "quake", hypixel: "DUELS_QUAKE_DUEL" },
{ api: "parkour", hypixel: "DUELS_PARKOUR_EIGHT" },
{ api: "skywars", formatted: "SkyWars" },
{
api: "skywars",
formatted: "SkyWars",
submodes: [
{ api: "overall" },
{ api: "solo" },
{ api: "doubles" },
],
},
{
api: "spleef",
submodes: [
Expand Down
38 changes: 29 additions & 9 deletions packages/schemas/src/player/gamemodes/duels/mode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,20 +143,32 @@ export class BridgeDuels {
@Field()
public fours: BridgeDuelsMode;

@Field()
public twoVTwoVTwoVTwo: BridgeDuelsMode;

@Field()
public threeVThreeVThreeVThree: BridgeDuelsMode;

@Field()
public capture: BridgeDuelsMode;

public constructor(data: APIData) {
this.solo = new BridgeDuelsMode(data, "bridge_duel");
this.doubles = new BridgeDuelsMode(data, "bridge_doubles");
this.threes = new BridgeDuelsMode(data, "bridge_threes");
this.fours = new BridgeDuelsMode(data, "bridge_four");
this.twoVTwoVTwoVTwo = new BridgeDuelsMode(data, "bridge_2v2v2v2");
this.threeVThreeVThreeVThree = new BridgeDuelsMode(data, "bridge_3v3v3v3");
this.capture = new BridgeDuelsMode(data, "capture_threes");

this.overall = deepAdd(
this.solo,
this.doubles,
this.threes,
this.fours,
new BridgeDuelsMode(data, "bridge_2v2v2v2"),
new BridgeDuelsMode(data, "bridge_3v3v3v3"),
new BridgeDuelsMode(data, "capture_threes")
this.twoVTwoVTwoVTwo,
this.threeVThreeVThreeVThree,
this.capture
);

this.overall.winstreak = data.current_bridge_winstreak;
Expand Down Expand Up @@ -591,16 +603,24 @@ export class ParkourDuels extends SingleDuelsGameMode {
}

export class MegaWallsDuels extends SinglePVPDuelsGameMode {
@Field()
public solo: PVPBaseDuelsGameMode;

@Field()
public doubles: PVPBaseDuelsGameMode;

public constructor(data: APIData) {
super(data, "Mega Walls", "mw_duel", "half");

// add back doubles stats
const doubles = new PVPBaseDuelsGameMode(data, "mw_doubles");
this.wins = add(this.wins, doubles.wins);
this.losses = add(this.losses, doubles.losses);
this.kills = add(this.kills, doubles.kills);
this.deaths = add(this.deaths, doubles.deaths);
this.blocksPlaced = add(this.blocksPlaced, doubles.blocksPlaced);
this.solo = new PVPBaseDuelsGameMode(data, "mw_duel");
this.doubles = new PVPBaseDuelsGameMode(data, "mw_doubles");

this.wins = add(this.solo.wins, this.doubles.wins);
this.losses = add(this.solo.losses, this.doubles.losses);
this.kills = add(this.solo.kills, this.doubles.kills);
this.deaths = add(this.solo.deaths, this.doubles.deaths);
this.blocksPlaced = add(this.solo.blocksPlaced, this.doubles.blocksPlaced);

PVPBaseDuelsGameMode.applyRatios(this);

Expand Down
Loading