mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-21 02:37:05 +02:00
feat: cooler header
This commit is contained in:
1 parent
f0295079cc
commit
9d916a588d
10 files changed
+99
-38
No files matched your search
+7
-1
@@ -64,6 +64,7 @@ export function InfoWithDelta({
|
||||
: (stats[props.value] as number);
|
||||
const delta = useDelta(uhWhatDoICallThisVariable);
|
||||
const protagonistTank = Duel.use((state) => state.protagonist.tank);
|
||||
const model = modelDefinitions.models[protagonistTank.id];
|
||||
const others = useMemo(() => {
|
||||
const defaultSkills = createDefaultSkills(skillDefinitions);
|
||||
|
||||
@@ -78,7 +79,12 @@ export function InfoWithDelta({
|
||||
relativeAgainst === TankopediaRelativeAgainst.All,
|
||||
)
|
||||
.map((tank) => {
|
||||
const member = tankToDuelMember(tank, provisionDefinitions);
|
||||
const thisTankModel = modelDefinitions.models[tank.id];
|
||||
const member = tankToDuelMember(
|
||||
tank,
|
||||
thisTankModel,
|
||||
provisionDefinitions,
|
||||
);
|
||||
|
||||
return tankCharacteristics(
|
||||
{
|
||||
|
||||
+15
-15
@@ -1,7 +1,7 @@
|
||||
import { OrbitControls } from '@react-three/drei';
|
||||
import { invalidate, useThree } from '@react-three/fiber';
|
||||
import { clamp } from 'lodash-es';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { PerspectiveCamera, Vector3 } from 'three';
|
||||
import { OrbitControls as OrbitControlsClass } from 'three-stdlib';
|
||||
import { degToRad } from 'three/src/math/MathUtils.js';
|
||||
@@ -81,16 +81,14 @@ export function Controls({ naked }: ControlsProps) {
|
||||
protagonistTrackModelDefinition.origin.y +
|
||||
antagonistModelDefinition.turret_origin.y +
|
||||
antagonistTurretModelDefinition.gun_origin.y;
|
||||
const [autoRotate, setAutoRotate] = useState(
|
||||
display !== TankopediaDisplay.ShootingRange && !naked,
|
||||
);
|
||||
const disturbed = TankopediaEphemeral.use((state) => state.disturbed);
|
||||
const gunHeight =
|
||||
protagonistHullOrigin.y +
|
||||
protagonistTurretOrigin.y +
|
||||
protagonistGunOrigin.y;
|
||||
const inspectModeInitialPosition = [
|
||||
-8,
|
||||
gunHeight + (naked ? 10 : 4),
|
||||
gunHeight + (naked ? 10 : 2),
|
||||
-13,
|
||||
] as const;
|
||||
const protagonistGunOriginOnlyY = new Vector3(
|
||||
@@ -217,10 +215,6 @@ export function Controls({ naked }: ControlsProps) {
|
||||
useEffect(() => {
|
||||
let isInitialIteration = true;
|
||||
|
||||
function handleDisturbance() {
|
||||
setAutoRotate(false);
|
||||
}
|
||||
|
||||
function handleKeyDown(event: KeyboardEvent) {
|
||||
if (display !== TankopediaDisplay.ShootingRange || event.repeat) return;
|
||||
|
||||
@@ -254,8 +248,14 @@ export function Controls({ naked }: ControlsProps) {
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
poseEvent.on(handleDisturbance);
|
||||
canvas.addEventListener('pointerdown', handleDisturbance);
|
||||
function disturb() {
|
||||
mutateTankopediaEphemeral((draft) => {
|
||||
draft.disturbed = true;
|
||||
});
|
||||
}
|
||||
|
||||
poseEvent.on(disturb);
|
||||
canvas.addEventListener('pointerdown', disturb);
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
canvas.addEventListener('wheel', handleWheel);
|
||||
document.body.addEventListener('scroll', handleScroll);
|
||||
@@ -319,7 +319,7 @@ export function Controls({ naked }: ControlsProps) {
|
||||
const unsubscribeDisplay = tankopediaEphemeralStore.subscribe(
|
||||
(state) => state.display === TankopediaDisplay.ShootingRange,
|
||||
() => {
|
||||
handleDisturbance();
|
||||
disturb();
|
||||
updateCamera();
|
||||
},
|
||||
);
|
||||
@@ -330,8 +330,8 @@ export function Controls({ naked }: ControlsProps) {
|
||||
);
|
||||
|
||||
return () => {
|
||||
canvas.removeEventListener('pointerdown', handleDisturbance);
|
||||
poseEvent.off(handleDisturbance);
|
||||
canvas.removeEventListener('pointerdown', disturb);
|
||||
poseEvent.off(disturb);
|
||||
unsubscribeDisplay();
|
||||
unsubscribeShootingRangeZoom();
|
||||
window.removeEventListener('keydown', handleKeyDown);
|
||||
@@ -347,7 +347,7 @@ export function Controls({ naked }: ControlsProps) {
|
||||
ref={orbitControls}
|
||||
enabled={tankopediaEphemeralStore.getState().controlsEnabled}
|
||||
enableDamping={false}
|
||||
autoRotate={autoRotate}
|
||||
autoRotate={!disturbed}
|
||||
autoRotateSpeed={1 / 4}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -18,6 +18,7 @@ import { TankSandboxLoader } from './components/TankSandboxLoader';
|
||||
const tankDefinitions = await awaitableTankDefinitions;
|
||||
|
||||
export function HeroSection({ skeleton }: MaybeSkeletonComponentProps) {
|
||||
const disturbed = TankopediaEphemeral.use((state) => state.disturbed);
|
||||
const { unwrap } = useLocale();
|
||||
const canvas = useRef<HTMLCanvasElement>(null);
|
||||
const isFullScreen = useFullScreen();
|
||||
@@ -73,10 +74,13 @@ export function HeroSection({ skeleton }: MaybeSkeletonComponentProps) {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Flex justify="center" style={{ backgroundColor: 'black' }}>
|
||||
<Flex justify="center">
|
||||
<Flex
|
||||
direction={{ initial: 'column', md: 'row' }}
|
||||
style={{ zIndex: isFullScreen ? 2 : undefined }}
|
||||
style={{
|
||||
zIndex: isFullScreen ? 2 : undefined,
|
||||
backgroundColor: 'black',
|
||||
}}
|
||||
height={
|
||||
isFullScreen ? '100vh' : `calc(100vh - ${NAVBAR_HEIGHT}px - 8rem)`
|
||||
}
|
||||
@@ -94,22 +98,33 @@ export function HeroSection({ skeleton }: MaybeSkeletonComponentProps) {
|
||||
left={{ initial: '0', md: '5', lg: '9' }}
|
||||
py="4"
|
||||
top={{ initial: '8', md: '50%' }}
|
||||
style={{ transform: 'translateY(-50%)' }}
|
||||
style={{ transform: 'translateY(-50%)', transitionDuration: '200ms' }}
|
||||
direction="column"
|
||||
align={{ initial: 'center', md: 'start' }}
|
||||
gap={disturbed ? '0' : '2'}
|
||||
>
|
||||
<Flex align="center" gap="3">
|
||||
<Heading
|
||||
color={treeColor}
|
||||
trim="end"
|
||||
size={{ initial: '7', lg: '6' }}
|
||||
size={
|
||||
disturbed
|
||||
? { initial: '7', lg: '8' }
|
||||
: { initial: '8', lg: '9' }
|
||||
}
|
||||
style={{ transitionDuration: '200ms' }}
|
||||
>
|
||||
<Icon width="1em" height="1em" />
|
||||
</Heading>
|
||||
|
||||
<Heading
|
||||
weight="bold"
|
||||
size={{ initial: '7', lg: '8' }}
|
||||
size={
|
||||
disturbed
|
||||
? { initial: '7', lg: '8' }
|
||||
: { initial: '8', lg: '9' }
|
||||
}
|
||||
style={{ transitionDuration: '200ms' }}
|
||||
wrap="nowrap"
|
||||
color={treeColor}
|
||||
>
|
||||
@@ -117,14 +132,21 @@ export function HeroSection({ skeleton }: MaybeSkeletonComponentProps) {
|
||||
</Heading>
|
||||
</Flex>
|
||||
|
||||
<Text
|
||||
color="gray"
|
||||
size={{ initial: '3', lg: '4' }}
|
||||
weight="light"
|
||||
ml={{ initial: '0', md: '7', lg: '7' }}
|
||||
>
|
||||
BlitzKit Tankopedia
|
||||
</Text>
|
||||
<Box ml="3">
|
||||
<Text
|
||||
color="gray"
|
||||
size={{ initial: '3', lg: disturbed ? '4' : '5' }}
|
||||
weight="light"
|
||||
ml={{
|
||||
initial: '0',
|
||||
md: '8',
|
||||
lg: disturbed ? 'var(--font-size-8)' : 'var(--font-size-9)',
|
||||
}}
|
||||
style={{ transitionDuration: '200ms' }}
|
||||
>
|
||||
BlitzKit Tankopedia
|
||||
</Text>
|
||||
</Box>
|
||||
</Flex>
|
||||
|
||||
<Box
|
||||
@@ -134,8 +156,14 @@ export function HeroSection({ skeleton }: MaybeSkeletonComponentProps) {
|
||||
flexShrink="0"
|
||||
position="relative"
|
||||
>
|
||||
<Box position="absolute" width="100%" height="100%">
|
||||
<Box width="100%" height="100%">
|
||||
<Box position="absolute" width="100%" height="100%" overflow="hidden">
|
||||
<Box
|
||||
width="100%"
|
||||
height="100%"
|
||||
position="relative"
|
||||
left={disturbed ? '0' : '12.5%'}
|
||||
style={{ transitionDuration: '200ms' }}
|
||||
>
|
||||
{skeleton && <TankSandboxLoader id={protagonist.id} />}
|
||||
|
||||
<Suspense fallback={<TankSandboxLoader id={protagonist.id} />}>
|
||||
|
||||
@@ -95,6 +95,8 @@ export function MetaSection() {
|
||||
<TankSearch
|
||||
compact
|
||||
onSelect={(tank) => {
|
||||
const model = modelDefinitions.models[tank.id];
|
||||
|
||||
setShowSwapDialog(false);
|
||||
mutateTankopediaEphemeral((draft) => {
|
||||
draft.model = modelDefinitions.models[tank.id];
|
||||
@@ -102,6 +104,7 @@ export function MetaSection() {
|
||||
mutateDuel((draft) => {
|
||||
draft.protagonist = tankToDuelMember(
|
||||
tank,
|
||||
model,
|
||||
provisionDefinitions,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
import type { ProvisionDefinitions, TankDefinition } from '@blitzkit/core';
|
||||
import type {
|
||||
ModelDefinition,
|
||||
ProvisionDefinitions,
|
||||
TankDefinition,
|
||||
} from '@blitzkit/core';
|
||||
import { uniqueId } from 'lodash-es';
|
||||
import type { CompareMember } from '../../stores/compareEphemeral';
|
||||
import { tankToDuelMember } from './tankToDuelMember';
|
||||
|
||||
export function tankToCompareMember(
|
||||
tank: TankDefinition,
|
||||
model: ModelDefinition,
|
||||
provisionDefinitions: ProvisionDefinitions,
|
||||
) {
|
||||
return {
|
||||
...tankToDuelMember(tank, provisionDefinitions),
|
||||
...tankToDuelMember(tank, model, provisionDefinitions),
|
||||
key: uniqueId(),
|
||||
} satisfies CompareMember;
|
||||
}
|
||||
@@ -1,17 +1,30 @@
|
||||
import {
|
||||
createDefaultProvisions,
|
||||
ModelDefinition,
|
||||
type ProvisionDefinitions,
|
||||
type TankDefinition,
|
||||
} from '@blitzkit/core';
|
||||
import { degToRad } from 'three/src/math/MathUtils.js';
|
||||
import type { DuelMember } from '../../stores/duel';
|
||||
import { genericDefaultEquipmentMatrix } from '../../stores/duel/constants';
|
||||
import { applyPitchYawLimits } from '../blitz/applyPitchYawLimits';
|
||||
|
||||
export function tankToDuelMember(
|
||||
tank: TankDefinition,
|
||||
model: ModelDefinition,
|
||||
provisionDefinitions: ProvisionDefinitions,
|
||||
) {
|
||||
const turret = tank.turrets.at(-1)!;
|
||||
const turretModel = model.turrets[turret.id];
|
||||
const gun = turret.guns.at(-1)!;
|
||||
const gunModel = turretModel.guns[gun.gun_type!.value.base.id];
|
||||
|
||||
const [pitch, yaw] = applyPitchYawLimits(
|
||||
degToRad(8),
|
||||
degToRad(25),
|
||||
gunModel.pitch,
|
||||
turretModel.yaw,
|
||||
);
|
||||
|
||||
return {
|
||||
camouflage: true,
|
||||
@@ -24,8 +37,8 @@ export function tankToDuelMember(
|
||||
shell: tank.turrets.at(-1)!.guns.at(-1)!.gun_type!.value.base.shells[0],
|
||||
tank,
|
||||
track: tank.tracks.at(-1)!,
|
||||
yaw: 0,
|
||||
pitch: 0,
|
||||
yaw,
|
||||
pitch,
|
||||
turret,
|
||||
} satisfies DuelMember;
|
||||
}
|
||||
@@ -42,7 +42,7 @@ export function Page({ id, skeleton, locale }: PageProps) {
|
||||
<App.Provider>
|
||||
<TankopediaPersistent.Provider>
|
||||
<Duel.Provider
|
||||
data={{ tank, provisionDefinitions: provisionDefinitions }}
|
||||
data={{ tank, model, provisionDefinitions: provisionDefinitions }}
|
||||
>
|
||||
<PageWrapper p="0" maxWidth="unset" color="purple" gap="9" pb="9">
|
||||
<HeroSection skeleton={skeleton} />
|
||||
|
||||
@@ -27,7 +27,7 @@ export function Page({ id }: PageProps) {
|
||||
<LocaleProvider locale="en">
|
||||
<App.Provider>
|
||||
<TankopediaPersistent.Provider>
|
||||
<Duel.Provider data={{ provisionDefinitions, tank }}>
|
||||
<Duel.Provider data={{ provisionDefinitions, model, tank }}>
|
||||
<TankopediaEphemeral.Provider data={model}>
|
||||
<Content />
|
||||
</TankopediaEphemeral.Provider>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {
|
||||
ModelDefinition,
|
||||
type EngineDefinition,
|
||||
type GunDefinition,
|
||||
type ProvisionDefinitions,
|
||||
@@ -48,16 +49,19 @@ export interface DuelStore {
|
||||
export const Duel = createContextualStore(
|
||||
({
|
||||
tank,
|
||||
model,
|
||||
provisionDefinitions,
|
||||
}: {
|
||||
tank: TankDefinition;
|
||||
model: ModelDefinition;
|
||||
provisionDefinitions: ProvisionDefinitions;
|
||||
}) => {
|
||||
const protagonist = tankToDuelMember(tank, provisionDefinitions);
|
||||
const protagonist = tankToDuelMember(tank, model, provisionDefinitions);
|
||||
|
||||
return create<DuelStore>()(
|
||||
subscribeWithSelector<DuelStore>(() => ({
|
||||
protagonist,
|
||||
model,
|
||||
antagonist: protagonist,
|
||||
})),
|
||||
);
|
||||
|
||||
@@ -96,6 +96,7 @@ export const SHOOTING_RANGE_ZOOM_COEFFICIENTS: Record<
|
||||
};
|
||||
|
||||
interface TankopediaEphemeral {
|
||||
disturbed: boolean;
|
||||
shot?: Shot;
|
||||
shootingRangeZoom: ShootingRangeZoom;
|
||||
skills: Record<string, number>;
|
||||
@@ -128,6 +129,7 @@ export const TankopediaEphemeral = createContextualStore(
|
||||
(model: ModelDefinition) => {
|
||||
return create<TankopediaEphemeral>()(
|
||||
subscribeWithSelector<TankopediaEphemeral>(() => ({
|
||||
disturbed: false,
|
||||
shootingRangeZoom: ShootingRangeZoom.Arcade,
|
||||
relativeAgainst: TankopediaRelativeAgainst.Class,
|
||||
editStatic: false,
|
||||
|
||||
Reference in new issue
Block a user