diff --git a/app/constants.ts b/app/constants.ts index cd0cb542d..0e8b0f0a4 100644 --- a/app/constants.ts +++ b/app/constants.ts @@ -73,3 +73,16 @@ export const CUSTOMIZED_CSS_VARS_NAME = "css"; export const MAX_AP = 57; export const ONE_HOUR_IN_MS = 60 * 60 * 1000; + +export const SPLATOON_3_XP_BADGE_VALUES = [ + 3400, 3300, 3200, 3100, 3000, 2900, 2800, 2700, 2600, +] as const; +export const findSplatoon3XpBadgeValue = (xPower: number) => { + for (const value of SPLATOON_3_XP_BADGE_VALUES) { + if (xPower >= value) { + return value; + } + } + + return null; +}; diff --git a/app/db/models/users/queries.server.ts b/app/db/models/users/queries.server.ts index 900916e9f..f056aa00f 100644 --- a/app/db/models/users/queries.server.ts +++ b/app/db/models/users/queries.server.ts @@ -34,6 +34,7 @@ import forcePatronSql from "./forcePatron.sql"; import makeVideoAdderSql from "./makeVideoAdder.sql"; import linkPlayerSql from "./linkPlayer.sql"; import unlinkPlayerSql from "./unlinkPlayer.sql"; +import { syncXPBadges } from "~/features/badges"; const upsertStm = sql.prepare(upsertSql); export function upsert( @@ -279,4 +280,5 @@ export function linkPlayer({ }) { unlinkPlayerStm.run({ userId }); linkPlayerStm.run({ userId, playerId }); + syncXPBadges(); } diff --git a/app/features/badges/index.ts b/app/features/badges/index.ts new file mode 100644 index 000000000..6691b2ef4 --- /dev/null +++ b/app/features/badges/index.ts @@ -0,0 +1 @@ +export { syncXPBadges } from "./queries/syncXPBadges.server"; diff --git a/app/features/badges/queries/syncXPBadges.server.ts b/app/features/badges/queries/syncXPBadges.server.ts new file mode 100644 index 000000000..0fb265be2 --- /dev/null +++ b/app/features/badges/queries/syncXPBadges.server.ts @@ -0,0 +1,60 @@ +import invariant from "tiny-invariant"; +import { + SPLATOON_3_XP_BADGE_VALUES, + findSplatoon3XpBadgeValue, +} from "~/constants"; +import { sql } from "~/db/sql"; + +const badgeCodeToIdStm = sql.prepare(/* sql */ ` + select "id" + from "Badge" + where "code" = @code +`); + +const deleteBadgeOwnerStm = sql.prepare(/* sql */ ` + delete from "TournamentBadgeOwner" + where "badgeId" = @badgeId +`); + +const userTopXPowersStm = sql.prepare(/* sql */ ` + select + "SplatoonPlayer"."userId", + max("XRankPlacement"."power") as "xPower" + from + "SplatoonPlayer" + left join "XRankPlacement" on "XRankPlacement"."playerId" = "SplatoonPlayer"."id" + where "SplatoonPlayer"."userId" is not null + group by "SplatoonPlayer"."userId" +`); + +const addXPBadgeStm = sql.prepare(/* sql */ ` + insert into "TournamentBadgeOwner" ("badgeId", "userId") + values ( + (select "id" from "Badge" where "code" = @code), + @userId + ) +`); + +export const syncXPBadges = sql.transaction(() => { + for (const value of SPLATOON_3_XP_BADGE_VALUES) { + const badgeId = badgeCodeToIdStm + .pluck() + .get({ code: String(value) }) as number; + + invariant(badgeId, `Badge ${value} not found`); + + deleteBadgeOwnerStm.run({ badgeId }); + } + + const userTopXPowers = userTopXPowersStm.all() as Array<{ + userId: number; + xPower: number; + }>; + + for (const { userId, xPower } of userTopXPowers) { + const badgeValue = findSplatoon3XpBadgeValue(xPower); + if (!badgeValue) continue; + + addXPBadgeStm.run({ code: String(badgeValue), userId }); + } +}); diff --git a/app/routes/badges/$id.tsx b/app/routes/badges/$id.tsx index 4cd40dda3..63a976726 100644 --- a/app/routes/badges/$id.tsx +++ b/app/routes/badges/$id.tsx @@ -18,6 +18,7 @@ import { BADGES_PAGE } from "~/utils/urls"; import { type BadgesLoaderData } from "../badges"; import { type TFunction } from "react-i18next"; import { useTranslation } from "~/hooks/useTranslation"; +import { SPLATOON_3_XP_BADGE_VALUES } from "~/constants"; export interface BadgeDetailsContext { badgeName: string; @@ -104,7 +105,10 @@ export function badgeExplanationText( if (badge.code === "patreon_plus") { return t("patreon+"); } - if (badge.code.startsWith("xp")) { + if ( + badge.code.startsWith("xp") || + SPLATOON_3_XP_BADGE_VALUES.includes(Number(badge.code) as any) + ) { return t("xp", { xpText: badge.displayName }); } diff --git a/package.json b/package.json index f61e63dda..7b75cfd8b 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "replace-weapon-names": "node --experimental-specifier-resolution=node --loader ts-node/esm -r tsconfig-paths/register scripts/replace-weapon-names.ts", "placements": "node --experimental-specifier-resolution=node --loader ts-node/esm -r tsconfig-paths/register scripts/placements/index.ts", "hex-to-filter": "node --experimental-specifier-resolution=node --loader ts-node/esm -r tsconfig-paths/register scripts/hex-to-filter.ts", + "sync-xp-badges": "node --experimental-specifier-resolution=node --loader ts-node/esm -r tsconfig-paths/register scripts/sync-xp-badges.ts", "lint:ts": "eslint . --ext .ts,.tsx", "lint:css": "stylelint \"app/styles/**/*.css\"", "prettier:check": "prettier --check . --loglevel warn", diff --git a/public/static-assets/badges/2600.avif b/public/static-assets/badges/2600.avif new file mode 100644 index 000000000..98a7e870c Binary files /dev/null and b/public/static-assets/badges/2600.avif differ diff --git a/public/static-assets/badges/2600.gif b/public/static-assets/badges/2600.gif new file mode 100644 index 000000000..c80efec64 Binary files /dev/null and b/public/static-assets/badges/2600.gif differ diff --git a/public/static-assets/badges/2600.png b/public/static-assets/badges/2600.png new file mode 100644 index 000000000..41d27f38d Binary files /dev/null and b/public/static-assets/badges/2600.png differ diff --git a/public/static-assets/badges/2700.avif b/public/static-assets/badges/2700.avif new file mode 100644 index 000000000..efe5de411 Binary files /dev/null and b/public/static-assets/badges/2700.avif differ diff --git a/public/static-assets/badges/2700.gif b/public/static-assets/badges/2700.gif new file mode 100644 index 000000000..6d1ea3448 Binary files /dev/null and b/public/static-assets/badges/2700.gif differ diff --git a/public/static-assets/badges/2700.png b/public/static-assets/badges/2700.png new file mode 100644 index 000000000..97299547e Binary files /dev/null and b/public/static-assets/badges/2700.png differ diff --git a/public/static-assets/badges/2800.avif b/public/static-assets/badges/2800.avif new file mode 100644 index 000000000..e27082b7c Binary files /dev/null and b/public/static-assets/badges/2800.avif differ diff --git a/public/static-assets/badges/2800.gif b/public/static-assets/badges/2800.gif new file mode 100644 index 000000000..e696846df Binary files /dev/null and b/public/static-assets/badges/2800.gif differ diff --git a/public/static-assets/badges/2800.png b/public/static-assets/badges/2800.png new file mode 100644 index 000000000..64c16a389 Binary files /dev/null and b/public/static-assets/badges/2800.png differ diff --git a/public/static-assets/badges/2900.avif b/public/static-assets/badges/2900.avif new file mode 100644 index 000000000..1a7e2359b Binary files /dev/null and b/public/static-assets/badges/2900.avif differ diff --git a/public/static-assets/badges/2900.gif b/public/static-assets/badges/2900.gif new file mode 100644 index 000000000..d6e575678 Binary files /dev/null and b/public/static-assets/badges/2900.gif differ diff --git a/public/static-assets/badges/2900.png b/public/static-assets/badges/2900.png new file mode 100644 index 000000000..a6f9da2dc Binary files /dev/null and b/public/static-assets/badges/2900.png differ diff --git a/public/static-assets/badges/3000.avif b/public/static-assets/badges/3000.avif new file mode 100644 index 000000000..533a78a23 Binary files /dev/null and b/public/static-assets/badges/3000.avif differ diff --git a/public/static-assets/badges/3000.gif b/public/static-assets/badges/3000.gif new file mode 100644 index 000000000..b4e84229c Binary files /dev/null and b/public/static-assets/badges/3000.gif differ diff --git a/public/static-assets/badges/3000.png b/public/static-assets/badges/3000.png new file mode 100644 index 000000000..a944eb7fa Binary files /dev/null and b/public/static-assets/badges/3000.png differ diff --git a/public/static-assets/badges/3100.avif b/public/static-assets/badges/3100.avif new file mode 100644 index 000000000..a60202783 Binary files /dev/null and b/public/static-assets/badges/3100.avif differ diff --git a/public/static-assets/badges/3100.gif b/public/static-assets/badges/3100.gif new file mode 100644 index 000000000..3205fd2d2 Binary files /dev/null and b/public/static-assets/badges/3100.gif differ diff --git a/public/static-assets/badges/3100.png b/public/static-assets/badges/3100.png new file mode 100644 index 000000000..3e717214c Binary files /dev/null and b/public/static-assets/badges/3100.png differ diff --git a/public/static-assets/badges/3200.avif b/public/static-assets/badges/3200.avif new file mode 100644 index 000000000..89510a5c9 Binary files /dev/null and b/public/static-assets/badges/3200.avif differ diff --git a/public/static-assets/badges/3200.gif b/public/static-assets/badges/3200.gif new file mode 100644 index 000000000..d763e3d58 Binary files /dev/null and b/public/static-assets/badges/3200.gif differ diff --git a/public/static-assets/badges/3200.png b/public/static-assets/badges/3200.png new file mode 100644 index 000000000..f99e51fc1 Binary files /dev/null and b/public/static-assets/badges/3200.png differ diff --git a/public/static-assets/badges/3300.avif b/public/static-assets/badges/3300.avif new file mode 100644 index 000000000..a2ba81173 Binary files /dev/null and b/public/static-assets/badges/3300.avif differ diff --git a/public/static-assets/badges/3300.gif b/public/static-assets/badges/3300.gif new file mode 100644 index 000000000..880dded77 Binary files /dev/null and b/public/static-assets/badges/3300.gif differ diff --git a/public/static-assets/badges/3300.png b/public/static-assets/badges/3300.png new file mode 100644 index 000000000..53dce2cca Binary files /dev/null and b/public/static-assets/badges/3300.png differ diff --git a/public/static-assets/badges/3400.avif b/public/static-assets/badges/3400.avif new file mode 100644 index 000000000..301839cbe Binary files /dev/null and b/public/static-assets/badges/3400.avif differ diff --git a/public/static-assets/badges/3400.gif b/public/static-assets/badges/3400.gif new file mode 100644 index 000000000..f2232f075 Binary files /dev/null and b/public/static-assets/badges/3400.gif differ diff --git a/public/static-assets/badges/3400.png b/public/static-assets/badges/3400.png new file mode 100644 index 000000000..8690272fc Binary files /dev/null and b/public/static-assets/badges/3400.png differ diff --git a/scripts/placements/index.ts b/scripts/placements/index.ts index fccd0f19b..a206806d8 100644 --- a/scripts/placements/index.ts +++ b/scripts/placements/index.ts @@ -6,6 +6,7 @@ import { sql } from "~/db/sql"; import type { XRankPlacement } from "~/db/types"; import { type MainWeaponId, mainWeaponIds } from "~/modules/in-game-lists"; import { xRankSchema } from "./schemas"; +import { syncXPBadges } from "~/features/badges"; const rawJsonNumber = process.argv[2]?.trim(); invariant(rawJsonNumber, "jsonNumber is required (argument 1)"); @@ -50,6 +51,7 @@ async function main() { } addPlacements(placements); + syncXPBadges(); console.log(`done reading in ${placements.length} placements`); } diff --git a/scripts/sync-xp-badges.ts b/scripts/sync-xp-badges.ts new file mode 100644 index 000000000..ac349ec1b --- /dev/null +++ b/scripts/sync-xp-badges.ts @@ -0,0 +1,7 @@ +/* eslint-disable no-console */ +import "dotenv/config"; +import { syncXPBadges } from "~/features/badges"; + +syncXPBadges(); + +console.log("Synced XP badges");