diff --git a/app/modules/in-game-lists/index.ts b/app/modules/in-game-lists/index.ts index 7765f4009..1b8c1e776 100644 --- a/app/modules/in-game-lists/index.ts +++ b/app/modules/in-game-lists/index.ts @@ -51,3 +51,4 @@ export type { SpecialWeaponId, } from "./types"; export { isAbility } from "./utils"; +export { stageIds } from "./stage-ids"; diff --git a/app/modules/in-game-lists/stage-ids.ts b/app/modules/in-game-lists/stage-ids.ts new file mode 100644 index 000000000..5b53ba46f --- /dev/null +++ b/app/modules/in-game-lists/stage-ids.ts @@ -0,0 +1,15 @@ +// when adding new stage id also create new language dicts via create-misc.json.ts +export const stageIds = [ + 0, // Scorch Gorge + 1, // Eeltail Alley + 2, // Hagglefish Market + 3, // Undertow Spillway + 4, // Mincemeat Metalworks + 5, // Hammerhead Bridge + 6, // Museum d'Alfonsino + 7, // Mahi-Mahi Resort + 8, // Inkblot Art Academy + 9, // Sturgeon Shipyard + 10, // MakoMart + 11, // Wahoo World +] as const; diff --git a/app/routes/maps.tsx b/app/routes/maps.tsx new file mode 100644 index 000000000..ce9ae032a --- /dev/null +++ b/app/routes/maps.tsx @@ -0,0 +1,55 @@ +import type { LinksFunction } from "@remix-run/node"; +import { useTranslation } from "react-i18next"; +import { Image } from "~/components/Image"; +import { Main } from "~/components/Main"; +import { modes, stageIds } from "~/modules/in-game-lists"; +import styles from "~/styles/maps.css"; +import { modeImageUrl } from "~/utils/urls"; + +export const links: LinksFunction = () => { + return [{ rel: "stylesheet", href: styles }]; +}; + +export const handle = { + i18n: "game-misc", +}; + +export default function MapListPage() { + return ( +
+ +
+ ); +} + +function MapPoolSelector() { + const { t } = useTranslation(["game-misc"]); + + return ( +
+ {stageIds.map((stageId) => ( +
+
{t(`game-misc:STAGE_${stageId}`)}
+
+ {modes + .filter((mode) => mode.short !== "TW") + .map((mode) => ( + + ))} +
+
+ ))} +
+ ); +} diff --git a/app/styles/maps.css b/app/styles/maps.css new file mode 100644 index 000000000..fbc32bce1 --- /dev/null +++ b/app/styles/maps.css @@ -0,0 +1,22 @@ +.maps__stage-row { + display: flex; + align-items: center; + justify-content: space-between; + background-color: var(--bg-lighter); + border-radius: var(--rounded); + font-size: var(--fonts-xs); + font-weight: var(--semi-bold); + gap: var(--s-0-5); + padding-block: var(--s-1-5); + padding-inline: var(--s-3); +} + +.maps__mode-button { + padding: 0; + padding: var(--s-1-5); + border: none; + background-color: var(--bg-darker); + border-radius: var(--rounded); + color: var(--theme); + outline: initial; +} diff --git a/package.json b/package.json index 6cbd6bd6f..7098ebb8c 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "rename-badge": "node --experimental-specifier-resolution=node --loader ts-node/esm -r tsconfig-paths/register scripts/rename-badge.ts", "create-weapon-json": "node --experimental-specifier-resolution=node --loader ts-node/esm -r tsconfig-paths/register scripts/create-weapon-json.ts", "create-gear-json": "node --experimental-specifier-resolution=node --loader ts-node/esm -r tsconfig-paths/register scripts/create-gear-json.ts", + "create-misc-json": "node --experimental-specifier-resolution=node --loader ts-node/esm -r tsconfig-paths/register scripts/create-misc-json.ts", "create-analyzer-json": "node --experimental-specifier-resolution=node --loader ts-node/esm -r tsconfig-paths/register scripts/create-analyzer-json.ts", "check-translation-jsons": "node --experimental-specifier-resolution=node --loader ts-node/esm -r tsconfig-paths/register scripts/check-translation-jsons.ts && npm run prettier:write", "replace-img-names": "node --experimental-specifier-resolution=node --loader ts-node/esm -r tsconfig-paths/register scripts/replace-img-names.ts", diff --git a/public/locales/da/game-misc.json b/public/locales/da/game-misc.json new file mode 100644 index 000000000..de7ff391b --- /dev/null +++ b/public/locales/da/game-misc.json @@ -0,0 +1,14 @@ +{ + "STAGE_0": "Scorch Gorge", + "STAGE_1": "Eeltail Alley", + "STAGE_2": "Hagglefish Market", + "STAGE_3": "Undertow Spillway", + "STAGE_4": "Mincemeat Metalworks", + "STAGE_5": "Hammerhead Bridge", + "STAGE_6": "Museum d'Alfonsino", + "STAGE_7": "Mahi-Mahi Resort", + "STAGE_8": "Inkblot Art Academy", + "STAGE_9": "Sturgeon Shipyard", + "STAGE_10": "MakoMart", + "STAGE_11": "Wahoo World" +} diff --git a/public/locales/de/game-misc.json b/public/locales/de/game-misc.json new file mode 100644 index 000000000..446fe7423 --- /dev/null +++ b/public/locales/de/game-misc.json @@ -0,0 +1,14 @@ +{ + "STAGE_0": "Sengkluft", + "STAGE_1": "Streifenaal-Straße", + "STAGE_2": "Schnapperchen-Basar", + "STAGE_3": "Schwertmuschel-Reservoir", + "STAGE_4": "Aalstahl-Metallwerk", + "STAGE_5": "Makrelenbrücke", + "STAGE_6": "Pinakoithek", + "STAGE_7": "Mahi-Mahi-Resort", + "STAGE_8": "Perlmutt-Akademie", + "STAGE_9": "Störwerft", + "STAGE_10": "Cetacea-Markt", + "STAGE_11": "Flunder-Funpark" +} diff --git a/public/locales/en/game-misc.json b/public/locales/en/game-misc.json new file mode 100644 index 000000000..de7ff391b --- /dev/null +++ b/public/locales/en/game-misc.json @@ -0,0 +1,14 @@ +{ + "STAGE_0": "Scorch Gorge", + "STAGE_1": "Eeltail Alley", + "STAGE_2": "Hagglefish Market", + "STAGE_3": "Undertow Spillway", + "STAGE_4": "Mincemeat Metalworks", + "STAGE_5": "Hammerhead Bridge", + "STAGE_6": "Museum d'Alfonsino", + "STAGE_7": "Mahi-Mahi Resort", + "STAGE_8": "Inkblot Art Academy", + "STAGE_9": "Sturgeon Shipyard", + "STAGE_10": "MakoMart", + "STAGE_11": "Wahoo World" +} diff --git a/public/locales/es/game-misc.json b/public/locales/es/game-misc.json new file mode 100644 index 000000000..119f38f0d --- /dev/null +++ b/public/locales/es/game-misc.json @@ -0,0 +1,14 @@ +{ + "STAGE_0": "Desfiladero Fumarola", + "STAGE_1": "Callejones Crustáceo", + "STAGE_2": "Mercado Lenguado", + "STAGE_3": "Cisterna Navajuela", + "STAGE_4": "Desguace Mero", + "STAGE_5": "Puente Salmón", + "STAGE_6": "Museo del Pargo", + "STAGE_7": "Spa Cala Bacalao", + "STAGE_8": "Instituto Coralino", + "STAGE_9": "Astillero Beluga", + "STAGE_10": "Ultramarinos Orca", + "STAGE_11": "Pirañalandia" +} diff --git a/public/locales/fr/game-misc.json b/public/locales/fr/game-misc.json new file mode 100644 index 000000000..70daa96d9 --- /dev/null +++ b/public/locales/fr/game-misc.json @@ -0,0 +1,14 @@ +{ + "STAGE_0": "Canyon aux colonnes", + "STAGE_1": "Banlieue Balibot", + "STAGE_2": "Marché Grefin", + "STAGE_3": "Réservoir Rigadelle", + "STAGE_4": "Casse Rascasse", + "STAGE_5": "Pont Esturgeon", + "STAGE_6": "Galeries Guppy", + "STAGE_7": "Club Ca$halot", + "STAGE_8": "Institut Calam'arts", + "STAGE_9": "Chantier Narval", + "STAGE_10": "Supermarché Cétacé", + "STAGE_11": "Parc Carapince" +} diff --git a/public/locales/it/game-misc.json b/public/locales/it/game-misc.json new file mode 100644 index 000000000..d8a2e4c9d --- /dev/null +++ b/public/locales/it/game-misc.json @@ -0,0 +1,14 @@ +{ + "STAGE_0": "Grank Canyon", + "STAGE_1": "Sobborgo Siluriano", + "STAGE_2": "Mercato Fruttato", + "STAGE_3": "Cisterna Cernia", + "STAGE_4": "Discarica Tritatutto", + "STAGE_5": "Ponte Sgombro", + "STAGE_6": "Museo di Cefalò", + "STAGE_7": "Villanguilla", + "STAGE_8": "Campus Hippocampus", + "STAGE_9": "Cantiere Pinnenere", + "STAGE_10": "Mercatotano", + "STAGE_11": "Soglioland" +} diff --git a/public/locales/ja/game-misc.json b/public/locales/ja/game-misc.json new file mode 100644 index 000000000..467c2f8e8 --- /dev/null +++ b/public/locales/ja/game-misc.json @@ -0,0 +1,14 @@ +{ + "STAGE_0": "ユノハナ大渓谷", + "STAGE_1": "ゴンズイ地区", + "STAGE_2": "ヤガラ市場", + "STAGE_3": "マテガイ放水路", + "STAGE_4": "ナメロウ金属", + "STAGE_5": "マサバ海峡大橋", + "STAGE_6": "キンメダイ美術館", + "STAGE_7": "マヒマヒリゾート&スパ", + "STAGE_8": "海女美術大学", + "STAGE_9": "チョウザメ造船", + "STAGE_10": "ザトウマーケット", + "STAGE_11": "スメーシーワールド" +} diff --git a/public/locales/ko/game-misc.json b/public/locales/ko/game-misc.json new file mode 100644 index 000000000..5595a0ddd --- /dev/null +++ b/public/locales/ko/game-misc.json @@ -0,0 +1,14 @@ +{ + "STAGE_0": "석순 대협곡", + "STAGE_1": "메기 지구", + "STAGE_2": "대치 시장", + "STAGE_3": "맛조개 방수로", + "STAGE_4": "나메로우 금속", + "STAGE_5": "고등어 해협 대교", + "STAGE_6": "도미 미술관", + "STAGE_7": "만새기 리조트&스파", + "STAGE_8": "해녀 미술 대학", + "STAGE_9": "철갑상어 조선소", + "STAGE_10": "혹등 마켓", + "STAGE_11": "초밥 월드" +} diff --git a/public/locales/nl/game-misc.json b/public/locales/nl/game-misc.json new file mode 100644 index 000000000..0b3e2c7c0 --- /dev/null +++ b/public/locales/nl/game-misc.json @@ -0,0 +1,14 @@ +{ + "STAGE_0": "Roggentrog", + "STAGE_1": "Forelviaduct", + "STAGE_2": "Boter-bij-de-vismarkt", + "STAGE_3": "Baarsreservoir", + "STAGE_4": "Zilversmelt-hoogovens", + "STAGE_5": "Brandingbrug", + "STAGE_6": "Galerie Le Guppy", + "STAGE_7": "El Dorade-resort", + "STAGE_8": "Koraalcampus", + "STAGE_9": "Walruswerf", + "STAGE_10": "Bultrugbazaar", + "STAGE_11": "Waterwonderland" +} diff --git a/public/locales/ru/game-misc.json b/public/locales/ru/game-misc.json new file mode 100644 index 000000000..74168c87a --- /dev/null +++ b/public/locales/ru/game-misc.json @@ -0,0 +1,14 @@ +{ + "STAGE_0": "Опаленное ущелье", + "STAGE_1": "Угрево-Скатово", + "STAGE_2": "Рынок «Свисторыб»", + "STAGE_3": "Приливослив", + "STAGE_4": "Цех «Тартар»", + "STAGE_5": "Акулий мост", + "STAGE_6": "Галерея «Де Берикс»", + "STAGE_7": "Спа-курорт «Золотая рыбка»", + "STAGE_8": "Академия «Лепота»", + "STAGE_9": "Осетровые верфи", + "STAGE_10": "«Горбуша-Маркет»", + "STAGE_11": "Луна-парк «Язь»" +} diff --git a/public/locales/zh/game-misc.json b/public/locales/zh/game-misc.json new file mode 100644 index 000000000..0638f6429 --- /dev/null +++ b/public/locales/zh/game-misc.json @@ -0,0 +1,14 @@ +{ + "STAGE_0": "温泉花大峡谷", + "STAGE_1": "鳗鲶区", + "STAGE_2": "烟管鱼市场", + "STAGE_3": "竹蛏疏洪道", + "STAGE_4": "鱼肉碎金属", + "STAGE_5": "真鲭跨海大桥", + "STAGE_6": "金眼鲷美术馆", + "STAGE_7": "鬼头刀SPA度假区", + "STAGE_8": "海女美术大学", + "STAGE_9": "鲟鱼造船厂", + "STAGE_10": "座头购物中心", + "STAGE_11": "醋饭海洋世界" +} diff --git a/scripts/create-misc-json.ts b/scripts/create-misc-json.ts new file mode 100644 index 000000000..85ccbe00d --- /dev/null +++ b/scripts/create-misc-json.ts @@ -0,0 +1,69 @@ +import { LANG_JSONS_TO_CREATE, loadLangDicts } from "./utils"; +import fs from "fs"; +import path from "path"; +import invariant from "tiny-invariant"; + +// ⚠️ keep same order as https://github.com/IPLSplatoon/IPLMapGen2/blob/splat3/data.js +const stages = [ + "Scorch Gorge", + "Eeltail Alley", + "Hagglefish Market", + "Undertow Spillway", + "Mincemeat Metalworks", + "Hammerhead Bridge", + "Museum d'Alfonsino", + "Mahi-Mahi Resort", + "Inkblot Art Academy", + "Sturgeon Shipyard", + "MakoMart", + "Wahoo World", +] as const; + +async function main() { + const langDicts = await loadLangDicts(); + + const englishLangDict = langDicts.find( + ([langCode]) => langCode === "EUen" + )?.[1]; + invariant(englishLangDict); + + const codeNames = stages.map((stage) => { + const codeName = Object.entries( + englishLangDict["CommonMsg/VS/VSStageName"] + ).find(([_key, value]) => value === stage)?.[0]; + + invariant(codeName, `Could not find code name for stage ${stage}`); + + return codeName; + }); + + for (const langCode of LANG_JSONS_TO_CREATE) { + const langDict = langDicts.find(([code]) => code === langCode)?.[1]; + invariant(langDict, `Missing translations for ${langCode}`); + + const translationsMap = Object.fromEntries( + stages.map((_, i) => { + const codeName = codeNames[ + i + ] as keyof typeof langDict["CommonMsg/VS/VSStageName"]; + invariant(codeName); + + return [`STAGE_${i}`, langDict["CommonMsg/VS/VSStageName"][codeName]]; + }) + ); + + fs.writeFileSync( + path.join( + __dirname, + "..", + "public", + "locales", + langCode.slice(2), + `game-misc.json` + ), + JSON.stringify(translationsMap, null, 2) + "\n" + ); + } +} + +void main(); diff --git a/scripts/utils.ts b/scripts/utils.ts index 35486fff6..6f7d7f45a 100644 --- a/scripts/utils.ts +++ b/scripts/utils.ts @@ -1,5 +1,6 @@ import path from "node:path"; import fs from "node:fs"; +import type euEn from "./dicts/langs/EUen.json"; const LANG_DICTS_PATH = path.join(__dirname, "dicts", "langs"); @@ -17,9 +18,7 @@ export const LANG_JSONS_TO_CREATE = [ ]; export async function loadLangDicts() { - const result: Array< - [langCode: string, translations: Record>] - > = []; + const result: Array<[langCode: string, translations: typeof euEn]> = []; const files = await fs.promises.readdir(LANG_DICTS_PATH); for (const file of files) { diff --git a/types/react-i18next.d.ts b/types/react-i18next.d.ts index 607c96ac4..33855f602 100644 --- a/types/react-i18next.d.ts +++ b/types/react-i18next.d.ts @@ -11,6 +11,7 @@ import type weapons from "../public/locales/en/weapons.json"; import type gear from "../public/locales/en/gear.json"; import type builds from "../public/locales/en/builds.json"; import type analyzer from "../public/locales/en/analyzer.json"; +import type gameMisc from "../public/locales/en/game-misc.json"; declare module "react-i18next" { interface CustomTypeOptions { @@ -27,6 +28,7 @@ declare module "react-i18next" { gear: typeof gear; builds: typeof builds; analyzer: typeof analyzer; + "game-misc": typeof gameMisc; }; } }