diff --git a/.github/workflows/build-website.yml b/.github/workflows/website.yml similarity index 63% rename from .github/workflows/build-website.yml rename to .github/workflows/website.yml index cfa126071..9d1915491 100644 --- a/.github/workflows/build-website.yml +++ b/.github/workflows/website.yml @@ -1,8 +1,7 @@ -name: Build Website +name: Website on: push: - branches: main workflow_dispatch: schedule: @@ -15,7 +14,7 @@ permissions: concurrency: group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true + cancel-in-progress: ${{ github.ref == 'refs/heads/main' }} env: PUBLIC_WARGAMING_APPLICATION_ID: ${{ secrets.PUBLIC_WARGAMING_APPLICATION_ID }} @@ -52,14 +51,41 @@ jobs: with: path: dist/website - deploy: - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - needs: build + deploy-github-pages: + if: github.ref == 'refs/heads/main' + + name: Deploy to GitHub Pages runs-on: ubuntu-latest - name: Deploy + needs: build + environment: + name: Production + url: ${{ steps.deployment.outputs.page_url }} + steps: - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v4 + + deploy-vercel: + name: Deploy to Vercel + needs: build + runs-on: ubuntu-latest + environment: + name: Preview + + steps: + # vercel begs for the .git dir so here it is my guy + - uses: actions/checkout@v4 + + - name: Download artifact + uses: actions/download-artifact@v4 + with: + path: ./dist/website + + - uses: amondnet/vercel-action@v25 + with: + vercel-token: ${{ secrets.VERCEL_TOKEN }} + github-token: ${{ secrets.GITHUB_TOKEN }} + vercel-org-id: ${{ secrets.VERCEL_ORG_ID}} + vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }} + working-directory: ./dist/website diff --git a/.gitignore b/.gitignore index 7cb0c5661..8834744dc 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,4 @@ target # !packages/core/src/protos/*.proto /.vercel /.astro +.vercel diff --git a/.vscode/settings.json b/.vscode/settings.json index a263f4dd4..6be5a5ada 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -189,6 +189,7 @@ "unpickled", "Unregistering", "unrotate", + "vercel", "vickers", "vitepress", "Vovko", diff --git a/docs/changelogs/1.26.4.md b/docs/changelogs/1.26.4.md new file mode 100644 index 000000000..f79137bcd --- /dev/null +++ b/docs/changelogs/1.26.4.md @@ -0,0 +1,15 @@ +# BlitzKit 1.26.4 + +## Features + +- `/auction` now warns if a tank stock is at or below 500 and now strikes through the tank name if its out +- Added advanced armor highlighting mode + - Green: penetration + - Red: blocked + - Pink: ricochet + - Cyan: penetration with 3 calibers rule +- Optimized the armor highlighting for about a 15% increase in frame rate + +## Fixes + +- Added 5000 doses of adrenaline, incase the BOT DECIDES TO DIE AGAIN FOR NO REASON diff --git a/package.json b/package.json index de13f3b0a..6ea76e4e5 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "blitzkit", "private": true, - "version": "1.26.3", + "version": "1.26.4", "description": "🎉 Tools for everything World of Tanks Blitz", "type": "module", "types": "./types.d.ts", diff --git a/packages/bot/src/commands/auction.ts b/packages/bot/src/commands/auction.ts index d9c97a330..cc8da1920 100644 --- a/packages/bot/src/commands/auction.ts +++ b/packages/bot/src/commands/auction.ts @@ -27,7 +27,7 @@ type Result = available_from?: Date; available_before?: Date; price?: Price; - next_price?: Price | null; + next_price: Price | null; available: true; display?: boolean; next_price_datetime: Date; @@ -68,6 +68,8 @@ interface Currency { type: 'currency'; } +const WARNING_COUNT = 500; + export const auctionCommand = new Promise((resolve) => { resolve({ command: createLocalizedCommand('auction'), @@ -107,21 +109,27 @@ export const auctionCommand = new Promise((resolve) => { const name = `[${tank.name}]()`; - const next = translate('bot.commands.auction.body.next', [ - `<:gold:1317173197082333244> ${data.next_price!.value.toLocaleString( - interaction.locale, - )}`, - ]); + const next = + data.next_price === null + ? '' + : `\n-# ${translate('bot.commands.auction.body.next', [ + `<:gold:1317173197082333244> ${data.next_price.value.toLocaleString( + interaction.locale, + )}`, + ])}`; const available = translate('bot.commands.auction.body.available', [ data.current_count.toLocaleString(interaction.locale), data.initial_count.toLocaleString(interaction.locale), ]); + const isOut = data.current_count === 0; + const outString = isOut ? '~~' : ''; + const isLow = data.current_count <= WARNING_COUNT; - return `${ - index + 1 - }. ${name} <:gold:1317173197082333244> ${data.price!.value.toLocaleString( + return `${index + 1}. ${ + outString + }${name} <:gold:1317173197082333244> ${data.price!.value.toLocaleString( interaction.locale, - )}\n-# ${available}\n-# ${next}`; + )}${outString}\n-#${isLow ? ' ‼️ ' : ' '}${available}${isOut ? '' : next}`; }); const header = `${title}\n${subtitle}`; const lines = [header, ...body]; diff --git a/packages/bot/src/commands/rating.tsx b/packages/bot/src/commands/rating.tsx index 29ddaf439..1188c3706 100644 --- a/packages/bot/src/commands/rating.tsx +++ b/packages/bot/src/commands/rating.tsx @@ -7,6 +7,7 @@ import { getLeagueFromScore, getRatingInfo, getRatingNeighbors, + imgur, } from '@blitzkit/core'; import { SlashCommandSubcommandBuilder } from 'discord.js'; import markdownEscape from 'markdown-escape'; @@ -119,6 +120,19 @@ export const ratingCommand = new Promise((resolve) => { statsB2, ); const leagueTheme = LEAGUE_ACCENT[league.index]; + let rewardImage = reward + ? reward.type === 'vehicle' + ? reward.vehicle.preview_image_url + : reward.stuff.image_url + : undefined; + + if (rewardImage) { + const { ok } = await fetch(rewardImage); + + if (!ok) { + rewardImage = imgur('kP88vqr'); + } + } return ( @@ -325,11 +339,7 @@ export const ratingCommand = new Promise((resolve) => { height: 64, objectFit: 'cover', }} - src={await iconPng( - reward.type === 'vehicle' - ? reward.vehicle.preview_image_url - : reward.stuff.image_url, - )} + src={await iconPng(rewardImage!)} /> )} diff --git a/packages/bot/src/main.ts b/packages/bot/src/main.ts index c6ccd605b..6857c5b32 100644 --- a/packages/bot/src/main.ts +++ b/packages/bot/src/main.ts @@ -7,8 +7,19 @@ import { manager } from './core/discord/manager'; console.log('pre manager event listeners and spawn'); +let isFirst = true; +const interval = setInterval(() => { + if (!isFirst) { + console.warn('respawning all shards because manager died'); + manager.respawnAll(); + } + + isFirst = false; +}, 5000); + const shards = await manager .on('shardCreate', (shard) => { + clearInterval(interval); console.log(`🟡 Launching shard ${shard.id}`); shard.on('ready', () => console.log(`🟢 Launched shard ${shard.id}`)); }) diff --git a/packages/website/src/components/Armor/components/PrimaryArmorSceneComponent/index.tsx b/packages/website/src/components/Armor/components/PrimaryArmorSceneComponent/index.tsx index 519646c10..45a326318 100644 --- a/packages/website/src/components/Armor/components/PrimaryArmorSceneComponent/index.tsx +++ b/packages/website/src/components/Armor/components/PrimaryArmorSceneComponent/index.tsx @@ -49,6 +49,7 @@ export function PrimaryArmorSceneComponent({ damage: { value: null }, explosionRadius: { value: null }, greenPenetration: { value: null }, + advancedHighlighting: { value: null }, opaque: { value: null }, inverseProjectionMatrix: { value: null }, @@ -90,6 +91,10 @@ export function PrimaryArmorSceneComponent({ function handleGreenPenetrationChange(greenPenetration: boolean) { material.uniforms.greenPenetration.value = greenPenetration; } + function handleAdvancedHighlightingChange(advancedHighlighting: boolean) { + material.uniforms.advancedHighlighting.value = advancedHighlighting; + invalidate(); + } function handleOpaqueChange(opaque: boolean) { material.uniforms.opaque.value = opaque; } @@ -133,12 +138,13 @@ export function PrimaryArmorSceneComponent({ if (!noInvalidate) invalidate(); } + const initialState = tankopediaPersistentStore.getState(); + handleShellChange(); - handleGreenPenetrationChange( - tankopediaPersistentStore.getState().greenPenetration, - ); - handleOpaqueChange(tankopediaPersistentStore.getState().opaque); - handleWireframeChange(tankopediaPersistentStore.getState().wireframe); + handleGreenPenetrationChange(initialState.greenPenetration); + handleAdvancedHighlightingChange(initialState.advancedHighlighting); + handleOpaqueChange(initialState.opaque); + handleWireframeChange(initialState.wireframe); handleProtagonistEquipmentChange( duelStore.getState().protagonist.equipmentMatrix, ); @@ -156,6 +162,10 @@ export function PrimaryArmorSceneComponent({ (state) => state.greenPenetration, handleGreenPenetrationChange, ), + tankopediaPersistentStore.subscribe( + (state) => state.advancedHighlighting, + handleAdvancedHighlightingChange, + ), tankopediaPersistentStore.subscribe( (state) => state.opaque, handleOpaqueChange, diff --git a/packages/website/src/components/Armor/components/PrimaryArmorSceneComponent/shaders/fragment.glsl b/packages/website/src/components/Armor/components/PrimaryArmorSceneComponent/shaders/fragment.glsl index 045d49a80..97f0f6a39 100644 --- a/packages/website/src/components/Armor/components/PrimaryArmorSceneComponent/shaders/fragment.glsl +++ b/packages/website/src/components/Armor/components/PrimaryArmorSceneComponent/shaders/fragment.glsl @@ -12,82 +12,102 @@ uniform bool canSplash; uniform float damage; uniform float explosionRadius; uniform bool greenPenetration; +uniform bool advancedHighlighting; uniform bool opaque; uniform mat4 inverseProjectionMatrix; uniform highp sampler2D spacedArmorBuffer; uniform highp sampler2D spacedArmorDepth; -float depthToDistance(float depth) { - vec4 clipPosition = vec4(gl_FragCoord.xy / resolution * 2.0 - 1.0, depth * 2.0 - 1.0, 1.0); - vec4 eyePosition = inverseProjectionMatrix * clipPosition; - return length(eyePosition.xyz / eyePosition.w); +const float HALF = 0.5; +const float EXPLOSION_DAMAGE_FACTOR = 1.1; +const float RANDOMIZATION_FACTOR = 0.05; +const float GREEN_VALUE = 0.392; + +float getDistance(vec2 coord, float depth) { + vec4 clipPos = vec4(coord * 2.0 - 1.0, depth * 2.0 - 1.0, 1.0); + vec4 eyePos = inverseProjectionMatrix * clipPos; + return length(eyePos.xyz / eyePos.w); +} + +vec3 getPenetrationColor(bool isThreeCalibersRule) { + if (advancedHighlighting) { + return vec3(0.0, 1.0, isThreeCalibersRule ? 1.0 : 0.0); + } + + return vec3(0.0, 1.0, 0.0); } void main() { - float penetrationChance = -1.0; - float splashChance = -1.0; + vec2 screenCoord = gl_FragCoord.xy / resolution; + vec4 spacedArmorData = texture2D(spacedArmorBuffer, screenCoord); + bool isUnderSpacedArmor = spacedArmorData.a != 0.0; - vec2 screenCoordinates = gl_FragCoord.xy / resolution; - float angle = acos(dot(vNormal, -vViewPosition) / length(vViewPosition)); - - vec4 spacedArmorBufferFragment = texture2D(spacedArmorBuffer, screenCoordinates); - bool isUnderSpacedArmor = spacedArmorBufferFragment.a != 0.0; + float viewDistance = length(vViewPosition); + float angle = acos(dot(vNormal, -vViewPosition) / viewDistance); bool threeCalibersRule = caliber > thickness * 3.0 || isUnderSpacedArmor; + float penetrationChance = -1.0; + float splashChance = 0.0; + bool didRicochet = false; + if (!threeCalibersRule && angle >= ricochet) { penetrationChance = 0.0; + didRicochet = true; } else { - bool twoCalibersRule = caliber > thickness * 2.0 && thickness > 0.0; - float finalNormalization = twoCalibersRule ? ((1.4 * normalization * caliber) / (2.0 * thickness)) : normalization; - float finalThickness = thickness / cos(max(0.0, angle - finalNormalization)); + float thicknessRatio = thickness > 0.0 ? caliber / thickness : 0.0; + bool twoCalibersRule = thicknessRatio > 2.0; + float finalNorm = twoCalibersRule ? ((1.4 * normalization * caliber) / (2.0 * thickness)) : normalization; + float finalThickness = thickness / cos(max(0.0, angle - finalNorm)); - // world space in meters - vec4 spacedArmorDepthFragment = texture2D(spacedArmorDepth, screenCoordinates); - float spacedArmorDistance = depthToDistance(spacedArmorDepthFragment.r); - float primaryArmorDistance = depthToDistance(gl_FragCoord.z); - float distanceFromSpacedArmor = primaryArmorDistance - spacedArmorDistance; + float remainingPen = penetration; - if (canSplash && isUnderSpacedArmor) { - float spacedArmorThickness = spacedArmorBufferFragment.r * penetration; - float finalDamage = 0.5 * damage * (1.0 - distanceFromSpacedArmor / explosionRadius) - 1.1 * (finalThickness + spacedArmorThickness); + if (isUnderSpacedArmor) { + float spacedArmorThickness = spacedArmorData.r * penetration; + remainingPen -= spacedArmorThickness; - penetrationChance = 0.0; - splashChance = finalDamage > 0.0 ? 1.0 : 0.0; - } else { - float remainingPenetration = penetration; + if (isExplosive && remainingPen > 0.0) { + float spacedDist = getDistance(screenCoord, texture2D(spacedArmorDepth, screenCoord).r); + float primaryDist = getDistance(screenCoord, gl_FragCoord.z); + float distFromArmor = primaryDist - spacedDist; - if (isUnderSpacedArmor) { - float spacedArmorThickness = spacedArmorBufferFragment.r * penetration; - remainingPenetration -= spacedArmorThickness; + if (canSplash) { + float finalDamage = HALF * damage * (1.0 - distFromArmor / explosionRadius) - EXPLOSION_DAMAGE_FACTOR * (finalThickness + spacedArmorThickness); + splashChance = step(0.0, finalDamage); + penetrationChance = 0.0; - if (isExplosive && remainingPenetration > 0.0) { - remainingPenetration -= 0.5 * remainingPenetration * distanceFromSpacedArmor; + } else { + remainingPen -= HALF * remainingPen * distFromArmor; } } + } - remainingPenetration = max(0.0, remainingPenetration); - float delta = finalThickness - remainingPenetration; - float randomization = remainingPenetration * 0.05; + if (penetrationChance < 0.0) { + remainingPen = max(0.0, remainingPen); + float delta = finalThickness - remainingPen; + float randomization = remainingPen * RANDOMIZATION_FACTOR; penetrationChance = clamp(1.0 - (delta + randomization) / (2.0 * randomization), 0.0, 1.0); if (canSplash) { - float splashDamage = 0.5 * damage - 1.1 * finalThickness; - splashChance = splashDamage > 0.0 ? 1.0 : 0.0; - } else { - splashChance = 0.0; + float splashDamage = HALF * damage - EXPLOSION_DAMAGE_FACTOR * finalThickness; + splashChance = step(0.0, splashDamage); } } } - float alpha = opaque ? 1.0 : 0.5; - vec3 color = vec3(1.0, splashChance * 0.392, 0.0); - if (greenPenetration) { - // non-trigonometric red-green mixing: https://www.desmos.com/calculator/ceo1oq7l67 - float falling = -pow(penetrationChance, 2.0) + 1.0; - float gaining = -pow(penetrationChance - 1.0, 2.0) + 1.0; - gl_FragColor = vec4(falling * color + gaining * vec3(0.0, 1.0, 0.0), alpha); + float alpha = opaque ? 1.0 : HALF; + vec3 baseColor = vec3(1.0, splashChance * GREEN_VALUE, 0.0); + + if (advancedHighlighting && didRicochet) { + baseColor = vec3(1.0, baseColor.g, 1.0); + } + + if (greenPenetration || advancedHighlighting) { + float falling = -penetrationChance * penetrationChance + 1.0; + float gaining = -(penetrationChance - 1.0) * (penetrationChance - 1.0) + 1.0; + vec3 penColor = getPenetrationColor(threeCalibersRule); + gl_FragColor = vec4(falling * baseColor + gaining * penColor, alpha); } else { - gl_FragColor = vec4(color, (1.0 - penetrationChance) * alpha); + gl_FragColor = vec4(baseColor, (1.0 - penetrationChance) * alpha); } } \ No newline at end of file diff --git a/packages/website/src/components/Tankopedia/HeroSection/components/Options/index.tsx b/packages/website/src/components/Tankopedia/HeroSection/components/Options/index.tsx index 20dad8563..1e4baa024 100644 --- a/packages/website/src/components/Tankopedia/HeroSection/components/Options/index.tsx +++ b/packages/website/src/components/Tankopedia/HeroSection/components/Options/index.tsx @@ -17,6 +17,7 @@ import { import { Box, Button, + Checkbox, Dialog, DropdownMenu, Flex, @@ -68,6 +69,9 @@ export function Options({ thicknessRange, canvas }: OptionsProps) { const greenPenetration = TankopediaPersistent.use( (state) => state.greenPenetration, ); + const advancedHighlighting = TankopediaPersistent.use( + (state) => state.advancedHighlighting, + ); const wireframe = TankopediaPersistent.use((state) => state.wireframe); const opaque = TankopediaPersistent.use((state) => state.opaque); const fullScreenAvailable = useFullscreenAvailability(); @@ -325,53 +329,71 @@ export function Options({ thicknessRange, canvas }: OptionsProps) { style={{ transform: 'translateX(-50%)' }} > {display === TankopediaDisplay.DynamicArmor && ( - - - Shooter: - - + { + mutateTankopediaPersistent((draft) => { + draft.advancedHighlighting = !draft.advancedHighlighting; + }); + }} > - - - + + + Advanced highlighting + + - - Select tank + + + Shooter: + + + + + - - { - mutateDuel((draft) => { - draft.antagonist.tank = tank; - draft.antagonist.engine = tank.engines.at(-1)!; - draft.antagonist.track = tank.tracks.at(-1)!; - draft.antagonist.turret = tank.turrets.at(-1)!; - draft.antagonist.gun = - draft.antagonist.turret.guns.at(-1)!; - draft.antagonist.shell = - draft.antagonist.gun.gun_type!.value.base.shells[0]; - }); - setAntagonistSelectorOpen(false); - }} - /> - - - - + + Select tank + + + { + mutateDuel((draft) => { + draft.antagonist.tank = tank; + draft.antagonist.engine = tank.engines.at(-1)!; + draft.antagonist.track = tank.tracks.at(-1)!; + draft.antagonist.turret = tank.turrets.at(-1)!; + draft.antagonist.gun = + draft.antagonist.turret.guns.at(-1)!; + draft.antagonist.shell = + draft.antagonist.gun.gun_type!.value.base.shells[0]; + }); + setAntagonistSelectorOpen(false); + }} + /> + + + + + )} - state.display); const duelStore = Duel.useStore(); @@ -82,7 +82,7 @@ export function Controls() { antagonistModelDefinition.turret_origin.y + antagonistTurretModelDefinition.gun_origin.y; const [autoRotate, setAutoRotate] = useState( - display !== TankopediaDisplay.ShootingRange, + display !== TankopediaDisplay.ShootingRange && !naked, ); const gunHeight = protagonistHullOrigin.y + @@ -249,10 +249,6 @@ export function Controls() { canvas.addEventListener('wheel', handleWheel); document.body.addEventListener('scroll', handleScroll); - const sniperModeCameraOffset = new Vector3(); - let pitch = 0; - let yaw = 0; - function updateCamera() { if (!orbitControls.current) return; @@ -307,13 +303,6 @@ export function Controls() { camera.updateProjectionMatrix(); } - function handleModelTransform(event: ModelTransformEventData) { - pitch = event.pitch; - yaw = event.yaw ?? yaw; - } - - modelTransformEvent.on(handleModelTransform); - updateCamera(); const unsubscribeDisplay = tankopediaEphemeralStore.subscribe( @@ -337,7 +326,6 @@ export function Controls() { window.removeEventListener('keydown', handleKeyDown); canvas.removeEventListener('wheel', handleWheel); document.body.removeEventListener('scroll', handleScroll); - modelTransformEvent.off(handleModelTransform); }; }, [display === TankopediaDisplay.ShootingRange]); @@ -348,7 +336,6 @@ export function Controls() { ref={orbitControls} enabled={tankopediaEphemeralStore.getState().controlsEnabled} enableDamping={false} - maxPolarAngle={degToRad(100)} autoRotate={autoRotate} autoRotateSpeed={1 / 4} /> diff --git a/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/components/SceneProps/components/TargetCircle.tsx b/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/components/SceneProps/components/TargetCircle.tsx index dfdc6589a..151045d70 100644 --- a/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/components/SceneProps/components/TargetCircle.tsx +++ b/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/components/SceneProps/components/TargetCircle.tsx @@ -3,7 +3,7 @@ import { Box, type BoxProps } from '@radix-ui/themes'; import { forwardRef } from 'react'; import { Var } from '../../../../../../../../core/radix/var'; -const DOTS = 32; +// const DOTS = 32; type TargetCircleProps = BoxProps & { variant: 'client' | 'server'; diff --git a/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/index.tsx b/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/index.tsx index 0c920375c..511d3c09c 100644 --- a/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/index.tsx +++ b/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/index.tsx @@ -31,10 +31,11 @@ import { TankModel } from './components/TankModel'; interface TankSandboxProps { thicknessRange: ThicknessRange; + naked?: boolean; } export const TankSandbox = forwardRef( - ({ thicknessRange }, ref) => { + ({ thicknessRange, naked }, ref) => { const mutateTankopediaEphemeral = TankopediaEphemeral.useMutation(); const canvas = useRef(null); const hasImprovedVerticalStabilizer = useEquipment(122); @@ -179,10 +180,15 @@ export const TankSandbox = forwardRef( draft.highlightArmor = undefined; }); }} - style={{ userSelect: 'none' }} + style={{ + userSelect: 'none', + width: naked ? 640 : '100%', + height: naked ? 480 : '100%', + outline: naked ? '1rem red solid' : undefined, + }} > - - + + {!naked && } {display !== TankopediaDisplay.StaticArmor && } diff --git a/packages/website/src/components/TierList/Tile.tsx b/packages/website/src/components/TierList/Tile.tsx index 6e3ef7736..c569f19b6 100644 --- a/packages/website/src/components/TierList/Tile.tsx +++ b/packages/website/src/components/TierList/Tile.tsx @@ -68,7 +68,7 @@ export function TierListTile(props: TierListTileProps) { lastPosition.current = { x: event.clientX, y: event.clientY }; }, []); - const handlePointerUp = useCallback((event: PointerEvent) => { + const handlePointerUp = useCallback(() => { if (!card.current) return; const cardRect = card.current.getBoundingClientRect(); diff --git a/packages/website/src/pages/api/tankopedia/tank-icon/[id]/_index.tsx b/packages/website/src/pages/api/tankopedia/tank-icon/[id]/_index.tsx new file mode 100644 index 000000000..213149ea1 --- /dev/null +++ b/packages/website/src/pages/api/tankopedia/tank-icon/[id]/_index.tsx @@ -0,0 +1,40 @@ +import { TankSandbox } from '../../../../../components/Tankopedia/HeroSection/components/TankSandbox'; +import { awaitableModelDefinitions } from '../../../../../core/awaitables/modelDefinitions'; +import { awaitableProvisionDefinitions } from '../../../../../core/awaitables/provisionDefinitions'; +import { awaitableTankDefinitions } from '../../../../../core/awaitables/tankDefinitions'; +import { App } from '../../../../../stores/app'; +import { Duel } from '../../../../../stores/duel'; +import { TankopediaEphemeral } from '../../../../../stores/tankopediaEphemeral'; +import { TankopediaPersistent } from '../../../../../stores/tankopediaPersistent'; + +const [provisionDefinitions, tankDefinitions, modelDefinitions] = + await Promise.all([ + awaitableProvisionDefinitions, + awaitableTankDefinitions, + awaitableModelDefinitions, + ]); + +interface PageProps { + id: number; +} + +export function Page({ id }: PageProps) { + const tank = tankDefinitions.tanks[id]; + const model = modelDefinitions.models[id]; + + return ( + + + + + + + + + + ); +} + +function Content() { + return ; +} diff --git a/packages/website/src/pages/api/tankopedia/tank-icon/[id]/index.astro b/packages/website/src/pages/api/tankopedia/tank-icon/[id]/index.astro new file mode 100644 index 000000000..7aa817f2b --- /dev/null +++ b/packages/website/src/pages/api/tankopedia/tank-icon/[id]/index.astro @@ -0,0 +1,30 @@ +--- +import { assertSecret } from '@blitzkit/core'; +import type { GetStaticPaths } from 'astro'; +import { getStaticPaths as tankopediaGetStaticPaths } from '../../../../tools/tankopedia/[id]/index.astro'; +import { Page } from './_index'; + +export const getStaticPaths = (async () => { + if (assertSecret(import.meta.env.MODE) === 'development') { + return await tankopediaGetStaticPaths(); + } else { + return []; + } +}) satisfies GetStaticPaths; + +const { id } = Astro.params; +--- + + + + + + + + + + diff --git a/packages/website/src/stores/tankopediaPersistent/index.ts b/packages/website/src/stores/tankopediaPersistent/index.ts index c70dd4607..d8054b75a 100644 --- a/packages/website/src/stores/tankopediaPersistent/index.ts +++ b/packages/website/src/stores/tankopediaPersistent/index.ts @@ -12,6 +12,7 @@ export type TankopediaTestTankDisplay = 'include' | 'exclude' | 'only'; export interface TankopediaPersistentStore { wireframe: boolean; opaque: boolean; + advancedHighlighting: boolean; environment: (typeof ENVIRONMENTS)[number]; showGrid: boolean; greenPenetration: boolean; @@ -33,6 +34,7 @@ export const TankopediaPersistent = createContextualStore(() => subscribeWithSelector(() => ({ wireframe: false, opaque: false, + advancedHighlighting: false, environment: ENVIRONMENTS[0], showGrid: true, greenPenetration: false, diff --git a/vercel.json b/vercel.json new file mode 100644 index 000000000..80e57248a --- /dev/null +++ b/vercel.json @@ -0,0 +1,5 @@ +{ + "github": { + "enabled": false + } +}