feat: migrate tanks

This commit is contained in:
tresabhi committed 2025-05-16 22:11:01 -05:00
1 parent a8eb751048
commit 2cfc65a990
19 files changed
+216 -155

No files matched your search

+4 -1
View File
@@ -741,7 +741,10 @@
},
"website": {
"redirect": "Redirecting to %s1... Didn't happen automatically? Click here.",
"redirect": {
"title": "301: moved permanently",
"body": "This page has moved. Redirecting..."
},
"settings": {
"dev_mode": "Development mode",
@@ -892,7 +892,13 @@ export async function definitions() {
const hullArmor: Armor = { spaced: [], thickness: {} };
const equipment = tankDefinition.root.optDevicePreset;
tankStringIdMap[`${nation}:${tankKey}`] = tankId;
const slug = slugs.get(tank.id)!;
const slug = slugs.get(tankId);
if (slug === undefined) {
throw new Error(
`Could not find slug for ${nation}/${tankKey} (${tankId})`,
);
}
if (tank.sellPrice) {
tankPrice = {
@@ -1,19 +0,0 @@
---
import { literals } from '@blitzkit/i18n';
import { getStrings } from '../core/i18n/getStrings';
export interface Props {
path: string;
}
const { path } = Astro.props;
const { locale } = Astro.params;
const strings = getStrings(locale);
const url = `/${locale === undefined ? '' : `${locale}/`}${path}`;
---
<meta http-equiv="refresh" content={`0; url=${url}`} />
<a href={url}>
{literals(strings.website.redirect, [url])}
</a>
+2 -1
View File
@@ -15,7 +15,8 @@ export interface Tool {
}
export const tankopediaTool: Tool = {
id: 'tankopedia',
id: 'tanks',
stringsId: 'tankopedia',
button: {
color: 'purple',
},
@@ -54,6 +54,8 @@ const locale = Astro.params.locale ?? DEFAULT_LOCALE;
: `/${bestLocale}${window.location.pathname}`;
}
</script>
<slot name="head" />
</head>
<body class={noNavbar ? 'no-navbar' : ''}>
@@ -0,0 +1,45 @@
---
import { DiscordLogoIcon, PaperPlaneIcon } from '@radix-ui/react-icons';
import { Button, Flex, Heading, Link, Text } from '@radix-ui/themes';
import { PageWrapper } from '../components/PageWrapper';
import { getStrings } from '../core/i18n/getStrings';
import BaseLayout from '../layouts/BaseLayout.astro';
export interface Props {
path: string;
}
const { path } = Astro.props;
const { locale } = Astro.params;
const strings = getStrings(locale);
const resolvedPath = `/${locale === undefined ? '' : `${locale}/`}${path}`;
---
<BaseLayout>
<link
slot="head"
rel="canonical"
href={`${Astro.url.origin}${resolvedPath}`}
/>
<meta slot="head" http-equiv="refresh" content={`0; url=${resolvedPath}`} />
<PageWrapper align="center" justify="center" gap="0">
<Heading>{strings.website.redirect.title}</Heading>
<Text color="gray">{strings.website.redirect.body}</Text>
<Flex mt="4" gap="2">
<Link href={resolvedPath}>
<Button>
<PaperPlaneIcon /> Redirect
</Button>
</Link>
<Link href="https://discord.gg/nDt7AjGJQH">
<Button variant="outline">
<DiscordLogoIcon /> Discord
</Button>
</Link>
</Flex>
</PageWrapper>
</BaseLayout>
+14 -16
View File
@@ -9,23 +9,21 @@ const { locale } = Astro.params;
---
<BaseLayout>
<PageWrapper>
<Flex direction="column" justify="center" align="center" flexGrow="1">
<Heading>404: page not found</Heading>
<Text color="gray">Where would you like to go?</Text>
<PageWrapper align="center" justify="center" gap="0">
<Heading>404: page not found</Heading>
<Text color="gray">Where would you like to go?</Text>
<Flex mt="4" gap="2">
<LinkI18n {locale} href="/">
<Button>
<HomeIcon /> Home
</Button>
</LinkI18n>
<Link href="https://discord.gg/nDt7AjGJQH">
<Button variant="outline">
<DiscordLogoIcon /> Discord
</Button>
</Link>
</Flex>
<Flex mt="4" gap="2">
<LinkI18n {locale} href="/">
<Button>
<HomeIcon /> Home
</Button>
</LinkI18n>
<Link href="https://discord.gg/nDt7AjGJQH">
<Button variant="outline">
<DiscordLogoIcon /> Discord
</Button>
</Link>
</Flex>
</PageWrapper>
</BaseLayout>
@@ -1,24 +1,24 @@
import { TankType } from '@blitzkit/core';
import { PageWrapper } from '../../../../../components/PageWrapper';
import { CalloutsSection } from '../../../../../components/Tankopedia/CalloutsSection';
import { CharacteristicsSection } from '../../../../../components/Tankopedia/CharacteristicsSection';
import { GameModeSection } from '../../../../../components/Tankopedia/GameModeSection';
import { HeroSection } from '../../../../../components/Tankopedia/HeroSection';
import { MetaSection } from '../../../../../components/Tankopedia/MetaSection';
import { TechTreeSection } from '../../../../../components/Tankopedia/TechTreeSection';
import { VideoSection } from '../../../../../components/Tankopedia/VideoSection';
import { awaitableModelDefinitions } from '../../../../../core/awaitables/modelDefinitions';
import { awaitableProvisionDefinitions } from '../../../../../core/awaitables/provisionDefinitions';
import { awaitableTankDefinitions } from '../../../../../core/awaitables/tankDefinitions';
import { PageWrapper } from '../../../../components/PageWrapper';
import { CalloutsSection } from '../../../../components/Tankopedia/CalloutsSection';
import { CharacteristicsSection } from '../../../../components/Tankopedia/CharacteristicsSection';
import { GameModeSection } from '../../../../components/Tankopedia/GameModeSection';
import { HeroSection } from '../../../../components/Tankopedia/HeroSection';
import { MetaSection } from '../../../../components/Tankopedia/MetaSection';
import { TechTreeSection } from '../../../../components/Tankopedia/TechTreeSection';
import { VideoSection } from '../../../../components/Tankopedia/VideoSection';
import { awaitableModelDefinitions } from '../../../../core/awaitables/modelDefinitions';
import { awaitableProvisionDefinitions } from '../../../../core/awaitables/provisionDefinitions';
import { awaitableTankDefinitions } from '../../../../core/awaitables/tankDefinitions';
import {
LocaleProvider,
type LocaleAcceptorProps,
} from '../../../../../hooks/useLocale';
import { App } from '../../../../../stores/app';
import { Duel } from '../../../../../stores/duel';
import { TankopediaEphemeral } from '../../../../../stores/tankopediaEphemeral';
import { TankopediaPersistent } from '../../../../../stores/tankopediaPersistent';
import type { MaybeSkeletonComponentProps } from '../../../../../types/maybeSkeletonComponentProps';
} from '../../../../hooks/useLocale';
import { App } from '../../../../stores/app';
import { Duel } from '../../../../stores/duel';
import { TankopediaEphemeral } from '../../../../stores/tankopediaEphemeral';
import { TankopediaPersistent } from '../../../../stores/tankopediaPersistent';
import type { MaybeSkeletonComponentProps } from '../../../../types/maybeSkeletonComponentProps';
type PageProps = MaybeSkeletonComponentProps &
LocaleAcceptorProps & {
@@ -0,0 +1,114 @@
---
import { AstroSeo } from '@astrolib/seo';
import {
asset,
createDefaultProvisions,
createDefaultSkills,
fetchEquipmentDefinitions,
fetchModelDefinitions,
fetchProvisionDefinitions,
fetchSkillDefinitions,
fetchTankDefinitions,
TIER_ROMAN_NUMERALS,
} from '@blitzkit/core';
import { DEFAULT_LOCALE, unwrapper } from '@blitzkit/i18n';
import { literals } from '@blitzkit/i18n/src/literals';
import type { GetStaticPaths } from 'astro';
import { tankCharacteristics } from '../../../../core/blitzkit/tankCharacteristics';
import { getStrings } from '../../../../core/i18n/getStrings';
import BaseLayout from '../../../../layouts/BaseLayout.astro';
import { genericDefaultEquipmentMatrix } from '../../../../stores/duel/constants';
import { getStaticPaths as parentGetStaticPaths } from '../index.astro';
import { Page } from './_index';
export const getStaticPaths = (async () => {
const tankDefinitions = await fetchTankDefinitions();
const parentPaths = parentGetStaticPaths();
return Object.values(tankDefinitions.tanks)
.map((tank) =>
parentPaths.map((parent) => ({
params: { ...parent.params, slug: tank.slug },
props: { id: tank.id },
})),
)
.flat();
}) satisfies GetStaticPaths;
const { id } = Astro.props;
const locale = Astro.params.locale ?? DEFAULT_LOCALE;
const strings = getStrings(locale);
const tankDefinitions = await fetchTankDefinitions();
const skillDefinitions = await fetchSkillDefinitions();
const provisionDefinitions = await fetchProvisionDefinitions();
const equipmentDefinitions = await fetchEquipmentDefinitions();
const tankModelDefinitions = await fetchModelDefinitions();
const tank = tankDefinitions.tanks[id];
const tankModelDefinition = tankModelDefinitions.models[tank.id];
const unwrap = unwrapper(locale);
const title = literals(strings.website.tools.tankopedia.tank_seo_title, [
unwrap(tank.name),
TIER_ROMAN_NUMERALS[tank.tier],
strings.common.nations_adjectives[
tank.nation as keyof typeof strings.common.nations_adjectives
],
strings.common.tank_class_short[tank.class],
'BlitzKit',
strings.common.nations[tank.nation as keyof typeof strings.common.nations],
]);
const url = import.meta.env.SITE + Astro.url.pathname;
const icon = asset(`icons/tanks/big/${id}.webp`);
const turret = tank.turrets.at(-1)!;
const gun = turret.guns.at(-1)!;
const characteristics = tankCharacteristics(
{
tank,
applyDynamicArmor: false,
applyReactiveArmor: false,
applySpallLiner: false,
camouflage: true,
consumables: [],
crewSkills: createDefaultSkills(skillDefinitions),
engine: tank.engines.at(-1)!,
equipmentMatrix: genericDefaultEquipmentMatrix,
turret,
gun,
provisions: createDefaultProvisions(tank, gun, provisionDefinitions),
shell: gun.gun_type!.value.base.shells[0],
stockEngine: tank.engines[0],
stockGun: tank.turrets[0].guns[0],
stockTurret: tank.turrets[0],
stockTrack: tank.tracks[0],
track: tank.tracks.at(-1)!,
},
{ equipmentDefinitions, provisionDefinitions, tankModelDefinition },
);
const description = literals(
strings.website.tools.tankopedia.tank_seo_description,
[
unwrap(tank.name),
Math.round(characteristics.dpm).toLocaleString(locale),
Math.round(characteristics.damage).toLocaleString(locale),
Math.round(characteristics.penetration).toLocaleString(locale),
Math.round(characteristics.speedForwards).toLocaleString(locale),
Math.round(characteristics.health).toLocaleString(locale),
],
);
---
<AstroSeo
{title}
{description}
openGraph={{ title, description, images: [{ url: icon }], url }}
/>
<meta name="twitter:title" content={title} />
<meta name="twitter:description" content={description} />
<meta name="twitter:card" content="summary" />
<meta name="twitter:image:src" content={icon} />
<BaseLayout integrated>
<Page {locale} id={id} client:only="react">
<Page {locale} id={id} skeleton slot="fallback" />
</Page>
</BaseLayout>
@@ -1,5 +1,5 @@
---
import LegacyRedirect from '../../../../components/LegacyRedirect.astro';
import LegacyRedirect from '../../../../layouts/LegacyRedirect.astro';
export { getStaticPaths } from '../../index.astro';
---
@@ -1,7 +1,7 @@
---
import type { GetStaticPaths } from 'astro';
import LegacyRedirect from '../../../../../components/LegacyRedirect.astro';
import { embedConfigurations } from '../../../../../constants/embeds';
import LegacyRedirect from '../../../../../layouts/LegacyRedirect.astro';
import { getStaticPaths as parentGetStaticPaths } from '../index.astro';
export const getStaticPaths = (() => {
@@ -1,5 +1,5 @@
---
import LegacyRedirect from '../../../../components/LegacyRedirect.astro';
import LegacyRedirect from '../../../../layouts/LegacyRedirect.astro';
export { getStaticPaths } from '../../index.astro';
---
@@ -1,5 +1,5 @@
---
import LegacyRedirect from '../../../../components/LegacyRedirect.astro';
import LegacyRedirect from '../../../../layouts/LegacyRedirect.astro';
export { getStaticPaths } from '../../index.astro';
---
@@ -1,5 +1,5 @@
---
import LegacyRedirect from '../../../../components/LegacyRedirect.astro';
import LegacyRedirect from '../../../../layouts/LegacyRedirect.astro';
export { getStaticPaths } from '../../index.astro';
---
@@ -1,5 +1,5 @@
---
import LegacyRedirect from '../../../../components/LegacyRedirect.astro';
import LegacyRedirect from '../../../../layouts/LegacyRedirect.astro';
export { getStaticPaths } from '../../index.astro';
---
@@ -1,25 +1,8 @@
---
import { AstroSeo } from '@astrolib/seo';
import {
asset,
createDefaultProvisions,
createDefaultSkills,
fetchEquipmentDefinitions,
fetchModelDefinitions,
fetchProvisionDefinitions,
fetchSkillDefinitions,
fetchTankDefinitions,
TIER_ROMAN_NUMERALS,
} from '@blitzkit/core';
import { DEFAULT_LOCALE, unwrapper } from '@blitzkit/i18n';
import { literals } from '@blitzkit/i18n/src/literals';
import { fetchTankDefinitions } from '@blitzkit/core';
import type { GetStaticPaths } from 'astro';
import { tankCharacteristics } from '../../../../../core/blitzkit/tankCharacteristics';
import { getStrings } from '../../../../../core/i18n/getStrings';
import BaseLayout from '../../../../../layouts/BaseLayout.astro';
import { genericDefaultEquipmentMatrix } from '../../../../../stores/duel/constants';
import LegacyRedirect from '../../../../../layouts/LegacyRedirect.astro';
import { getStaticPaths as parentGetStaticPaths } from '../index.astro';
import { Page } from './_index';
export const getStaticPaths = (async () => {
const tankDefinitions = await fetchTankDefinitions();
@@ -35,80 +18,8 @@ export const getStaticPaths = (async () => {
}) satisfies GetStaticPaths;
const { id } = Astro.params;
const locale = Astro.params.locale ?? DEFAULT_LOCALE;
const strings = getStrings(locale);
const tankDefinitions = await fetchTankDefinitions();
const skillDefinitions = await fetchSkillDefinitions();
const provisionDefinitions = await fetchProvisionDefinitions();
const equipmentDefinitions = await fetchEquipmentDefinitions();
const tankModelDefinitions = await fetchModelDefinitions();
const tank = tankDefinitions.tanks[id];
const tankModelDefinition = tankModelDefinitions.models[tank.id];
const name = tank.name_full ?? tank.name;
const unwrap = unwrapper(locale);
const title = literals(strings.website.tools.tankopedia.tank_seo_title, [
unwrap(name),
TIER_ROMAN_NUMERALS[tank.tier],
strings.common.nations_adjectives[
tank.nation as keyof typeof strings.common.nations_adjectives
],
strings.common.tank_class_short[tank.class],
'BlitzKit',
strings.common.nations[tank.nation as keyof typeof strings.common.nations],
]);
const url = import.meta.env.SITE + Astro.url.pathname;
const icon = asset(`icons/tanks/big/${id}.webp`);
const turret = tank.turrets.at(-1)!;
const gun = turret.guns.at(-1)!;
const characteristics = tankCharacteristics(
{
tank,
applyDynamicArmor: false,
applyReactiveArmor: false,
applySpallLiner: false,
camouflage: true,
consumables: [],
crewSkills: createDefaultSkills(skillDefinitions),
engine: tank.engines.at(-1)!,
equipmentMatrix: genericDefaultEquipmentMatrix,
turret,
gun,
provisions: createDefaultProvisions(tank, gun, provisionDefinitions),
shell: gun.gun_type!.value.base.shells[0],
stockEngine: tank.engines[0],
stockGun: tank.turrets[0].guns[0],
stockTurret: tank.turrets[0],
stockTrack: tank.tracks[0],
track: tank.tracks.at(-1)!,
},
{ equipmentDefinitions, provisionDefinitions, tankModelDefinition },
);
const description = literals(
strings.website.tools.tankopedia.tank_seo_description,
[
unwrap(name),
Math.round(characteristics.dpm).toLocaleString(locale),
Math.round(characteristics.damage).toLocaleString(locale),
Math.round(characteristics.penetration).toLocaleString(locale),
Math.round(characteristics.speedForwards).toLocaleString(locale),
Math.round(characteristics.health).toLocaleString(locale),
],
);
---
<AstroSeo
{title}
{description}
openGraph={{ title, description, images: [{ url: icon }], url }}
/>
<meta name="twitter:title" content={title} />
<meta name="twitter:description" content={description} />
<meta name="twitter:card" content="summary" />
<meta name="twitter:image:src" content={icon} />
<BaseLayout integrated>
<Page {locale} id={id} client:only="react">
<Page {locale} id={id} skeleton slot="fallback" />
</Page>
</BaseLayout>
<LegacyRedirect path={`tanks/${tank.slug}`} />
@@ -1,5 +1,5 @@
---
import LegacyRedirect from '../../../../components/LegacyRedirect.astro';
import LegacyRedirect from '../../../../layouts/LegacyRedirect.astro';
export { getStaticPaths } from '../../index.astro';
---
@@ -1,5 +1,5 @@
---
import LegacyRedirect from '../../../../components/LegacyRedirect.astro';
import LegacyRedirect from '../../../../layouts/LegacyRedirect.astro';
export { getStaticPaths } from '../../index.astro';
---