mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-28 21:19:52 +01:00
feat: some stas
This commit is contained in:
1 parent
ee74c5ea5b
commit
eea2024549
5 files changed
+229
-68
No files matched your search
@@ -68,6 +68,7 @@ interface VehicleDefinitions {
|
|||||||
armor: VehicleDefinitionArmor;
|
armor: VehicleDefinitionArmor;
|
||||||
turretPositions: { turret: string };
|
turretPositions: { turret: string };
|
||||||
turretInitialRotation?: { yaw: 0; pitch: 6.5; roll: 0 };
|
turretInitialRotation?: { yaw: 0; pitch: 6.5; roll: 0 };
|
||||||
|
maxHealth: number;
|
||||||
};
|
};
|
||||||
chassis: {
|
chassis: {
|
||||||
[key: string]: {
|
[key: string]: {
|
||||||
@@ -92,6 +93,7 @@ interface VehicleDefinitions {
|
|||||||
};
|
};
|
||||||
turrets0: {
|
turrets0: {
|
||||||
[key: string]: {
|
[key: string]: {
|
||||||
|
maxHealth: number;
|
||||||
armor: VehicleDefinitionArmor;
|
armor: VehicleDefinitionArmor;
|
||||||
userString: number;
|
userString: number;
|
||||||
level: number;
|
level: number;
|
||||||
@@ -345,6 +347,7 @@ export async function definitions(production: boolean) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
tankDefinitions[tankId] = {
|
tankDefinitions[tankId] = {
|
||||||
|
health: tankDefinition.root.hull.maxHealth,
|
||||||
id: tankId,
|
id: tankId,
|
||||||
equipment,
|
equipment,
|
||||||
consumables: tankDefinition.root.consumableSlots,
|
consumables: tankDefinition.root.consumableSlots,
|
||||||
@@ -452,6 +455,7 @@ export async function definitions(production: boolean) {
|
|||||||
.replace(/^(Turret ([0-9] )?)+/, ''),
|
.replace(/^(Turret ([0-9] )?)+/, ''),
|
||||||
tier: turret.level as Tier,
|
tier: turret.level as Tier,
|
||||||
guns: [],
|
guns: [],
|
||||||
|
health: turret.maxHealth,
|
||||||
});
|
});
|
||||||
|
|
||||||
modelDefinitions[tankId].turrets[turretId] = {
|
modelDefinitions[tankId].turrets[turretId] = {
|
||||||
@@ -591,7 +595,7 @@ export async function definitions(production: boolean) {
|
|||||||
speed: gunShellEntry.speed,
|
speed: gunShellEntry.speed,
|
||||||
damage: {
|
damage: {
|
||||||
armor: shell.damage.armor,
|
armor: shell.damage.armor,
|
||||||
devices: shell.damage.devices,
|
module: shell.damage.devices,
|
||||||
},
|
},
|
||||||
caliber: shell.caliber,
|
caliber: shell.caliber,
|
||||||
normalization: shell.normalizationAngle,
|
normalization: shell.normalizationAngle,
|
||||||
|
|||||||
@@ -1,21 +1,29 @@
|
|||||||
import { Flex, Text } from '@radix-ui/themes';
|
import { Flex, Text } from '@radix-ui/themes';
|
||||||
|
import { ReactNode } from 'react';
|
||||||
import { theme } from '../../../../../../../stitches.config';
|
import { theme } from '../../../../../../../stitches.config';
|
||||||
|
|
||||||
interface InfoProps {
|
interface InfoProps {
|
||||||
name: string;
|
name: string;
|
||||||
value: string;
|
children: ReactNode;
|
||||||
unit: string;
|
unit: string;
|
||||||
indent?: boolean;
|
indent?: boolean;
|
||||||
|
highlight?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Info({ name, value, unit, indent = false }: InfoProps) {
|
export function Info({
|
||||||
|
name,
|
||||||
|
children,
|
||||||
|
unit,
|
||||||
|
indent = false,
|
||||||
|
highlight,
|
||||||
|
}: InfoProps) {
|
||||||
return (
|
return (
|
||||||
<Flex
|
<Flex
|
||||||
align="center"
|
align="center"
|
||||||
style={{ width: '100%', paddingLeft: indent ? 24 : 0 }}
|
style={{ width: '100%', paddingLeft: indent ? 24 : 0 }}
|
||||||
gap="4"
|
gap="4"
|
||||||
>
|
>
|
||||||
<Text>
|
<Text color={highlight ? 'amber' : undefined}>
|
||||||
{name}{' '}
|
{name}{' '}
|
||||||
<Text color="gray" size="1">
|
<Text color="gray" size="1">
|
||||||
{unit}
|
{unit}
|
||||||
@@ -26,11 +34,13 @@ export function Info({ name, value, unit, indent = false }: InfoProps) {
|
|||||||
style={{
|
style={{
|
||||||
flex: 1,
|
flex: 1,
|
||||||
height: 1,
|
height: 1,
|
||||||
backgroundColor: theme.colors.componentCallToActionInteractive_alpha,
|
backgroundColor: highlight
|
||||||
|
? theme.colors.componentCallToActionInteractive_amberAlpha
|
||||||
|
: theme.colors.componentCallToActionInteractive_alpha,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Text>{value}</Text>
|
<Text color={highlight ? 'amber' : undefined}>{children}</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,9 @@
|
|||||||
import { Flex, Heading, Tabs } from '@radix-ui/themes';
|
import { Flex, Heading, Tabs } from '@radix-ui/themes';
|
||||||
|
import { use } from 'react';
|
||||||
|
import { resolveNearPenetration } from '../../../../../../core/blitz/resolveNearPenetration';
|
||||||
|
import { modelDefinitions } from '../../../../../../core/blitzkrieg/modelDefinitions';
|
||||||
|
import { SHELL_NAMES } from '../../../../../../core/blitzkrieg/tankDefinitions';
|
||||||
|
import { useDuel } from '../../../../../../stores/duel';
|
||||||
import { Camouflage } from './components/Camouflage';
|
import { Camouflage } from './components/Camouflage';
|
||||||
import { Consumables } from './components/Consumables';
|
import { Consumables } from './components/Consumables';
|
||||||
import { Crew } from './components/Crew';
|
import { Crew } from './components/Crew';
|
||||||
@@ -8,6 +13,29 @@ import { Modules } from './components/Modules';
|
|||||||
import { Provisions } from './components/Provisions';
|
import { Provisions } from './components/Provisions';
|
||||||
|
|
||||||
export function Characteristics() {
|
export function Characteristics() {
|
||||||
|
const awaitedModelDefinitions = use(modelDefinitions);
|
||||||
|
const { tank, turret, gun, shell } = useDuel((state) => state.protagonist!);
|
||||||
|
const tankModelDefinition = awaitedModelDefinitions[tank.id];
|
||||||
|
const turretModelDefinition = tankModelDefinition.turrets[turret.id];
|
||||||
|
const gunModelDefinition = turretModelDefinition.guns[gun.id];
|
||||||
|
|
||||||
|
let dpm: number;
|
||||||
|
|
||||||
|
if (gun.type === 'regular') {
|
||||||
|
dpm = (shell.damage.armor / gun.reload) * 60;
|
||||||
|
} else if (gun.type === 'auto_loader') {
|
||||||
|
dpm =
|
||||||
|
((shell.damage.armor * gun.count) /
|
||||||
|
(gun.reload + (gun.count - 1) * gun.interClip)) *
|
||||||
|
60;
|
||||||
|
} else {
|
||||||
|
dpm =
|
||||||
|
((shell.damage.armor * gun.count) /
|
||||||
|
(gun.reload.reduce((a, b) => a + b, 0) +
|
||||||
|
(gun.count - 1) * gun.interClip)) *
|
||||||
|
60;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tabs.Root defaultValue="configure" style={{ width: '100%' }}>
|
<Tabs.Root defaultValue="configure" style={{ width: '100%' }}>
|
||||||
<Flex gap="4" direction="column">
|
<Flex gap="4" direction="column">
|
||||||
@@ -20,75 +48,190 @@ export function Characteristics() {
|
|||||||
<Flex direction="column" gap="4">
|
<Flex direction="column" gap="4">
|
||||||
<Flex direction="column" gap="2">
|
<Flex direction="column" gap="2">
|
||||||
<Heading size="5">Survivability</Heading>
|
<Heading size="5">Survivability</Heading>
|
||||||
<Info name="Health" value="2014" unit="hp" />
|
<Info name="Health" unit="hp">
|
||||||
<Info name="Fire chance" value="20" unit="%" />
|
{tank.health + turret.health}
|
||||||
<Info name="View range" value="302" unit="m" />
|
</Info>
|
||||||
<Info name="Camouflage when still" value="30.00" unit="%" />
|
<Info name="Fire chance" unit="%">
|
||||||
<Info indent name="Moving" value="30.00" unit="%" />
|
TODO
|
||||||
<Info indent name="Shooting" value="30.00" unit="%" />
|
</Info>
|
||||||
<Info indent name="Shooting and moving" value="30.00" unit="%" />
|
<Info name="View range" unit="m">
|
||||||
<Info indent name="Size" value="6.21 x 2.43 x 2.00" unit="m" />
|
TODO
|
||||||
|
</Info>
|
||||||
|
<Info name="Camouflage when still" unit="%">
|
||||||
|
TODO
|
||||||
|
</Info>
|
||||||
|
<Info indent name="Moving" unit="%">
|
||||||
|
TODO
|
||||||
|
</Info>
|
||||||
|
<Info indent name="Shooting" unit="%">
|
||||||
|
TODO
|
||||||
|
</Info>
|
||||||
|
<Info indent name="Shooting and moving" unit="%">
|
||||||
|
TODO
|
||||||
|
</Info>
|
||||||
|
<Info indent name="On fire" unit="%">
|
||||||
|
TODO
|
||||||
|
</Info>
|
||||||
|
<Info indent name="Size" unit="m">
|
||||||
|
TODO
|
||||||
|
</Info>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<Flex direction="column" gap="2">
|
<Flex direction="column" gap="2">
|
||||||
<Heading size="5">Fire</Heading>
|
<Heading size="5">Fire</Heading>
|
||||||
<Info name="Damage per minute" value="2316" unit="hp / min" />
|
<Info name="Damage per minute" unit="hp / min">
|
||||||
<Info indent name="Maximum" value="2316" unit="hp / min" />
|
{dpm.toFixed(0)}
|
||||||
<Info indent name="Effective" value="2316" unit="hp / min" />
|
</Info>
|
||||||
<Info name="Reload" value="5.73" unit="s" />
|
{gun.type === 'auto_reloader' && (
|
||||||
<Info name="AP penetration" value="273" unit="mm" />
|
<>
|
||||||
<Info indent name="AP at 500m" value="253" unit="mm" />
|
<Info indent name="Maximum" unit="hp / min">
|
||||||
<Info indent name="APCR" value="312" unit="mm" />
|
TODO
|
||||||
<Info indent name="APCR at 500m" value="300" unit="mm" />
|
</Info>
|
||||||
<Info indent name="HE" value="90" unit="mm" />
|
<Info indent name="Effective" unit="hp / min">
|
||||||
<Info indent name="Caliber" value="100" unit="mm" />
|
TODO
|
||||||
<Info name="AP damage" value="400" unit="hp" />
|
</Info>
|
||||||
<Info name="Shell velocity" value="1535" unit="m/s" />
|
</>
|
||||||
<Info indent name="AP module damage" value="130" unit="hp" />
|
)}
|
||||||
<Info indent name="APCR" value="340" unit="hp" />
|
{gun.type === 'auto_reloader' ? (
|
||||||
<Info indent name="APCR module" value="110" unit="hp" />
|
gun.reload.map((reload, index) => (
|
||||||
<Info indent name="HE" value="520" unit="hp" />
|
<Info
|
||||||
<Info indent name="HE module" value="200" unit="hp" />
|
indent={index > 0}
|
||||||
<Info name="Aim time" value="1.72" unit="s" />
|
name={
|
||||||
<Info name="Dispersion at 100m" value="0.326" unit="m" />
|
index > 0 ? `Shell ${index + 1}` : 'Reload on shell 1'
|
||||||
<Info indent name="On move" value="0.100" unit="m" />
|
}
|
||||||
<Info indent name="On hull traverse" value="0.100" unit="m" />
|
unit="s"
|
||||||
<Info indent name="On turret traverse" value="0.070" unit="m" />
|
>
|
||||||
<Info indent name="On shoot" value="3.500" unit="m" />
|
{reload.toFixed(2)}
|
||||||
<Info name="Gun depression" value="20" unit="°" />
|
</Info>
|
||||||
<Info indent name="Elevation" value="7" unit="°" />
|
))
|
||||||
<Info indent name="Frontal depression" value="7" unit="°" />
|
) : (
|
||||||
<Info indent name="Frontal elevation" value="7" unit="°" />
|
<Info name="Reload" unit="s">
|
||||||
<Info indent name="Rear depression" value="7" unit="°" />
|
{gun.reload.toFixed(2)}
|
||||||
<Info indent name="Rear elevation" value="7" unit="°" />
|
</Info>
|
||||||
<Info indent name="Azimuth" value="30, 30" unit="°" />
|
)}
|
||||||
|
<Info name="Caliber" unit="mm">
|
||||||
|
{shell.caliber}
|
||||||
|
</Info>
|
||||||
|
{gun.shells.map((shell, index) => (
|
||||||
|
<Info
|
||||||
|
indent={index > 0}
|
||||||
|
name={`${SHELL_NAMES[shell.type]}${index === 0 ? ' penetration' : ''}`}
|
||||||
|
unit="mm"
|
||||||
|
>
|
||||||
|
{resolveNearPenetration(shell.penetration)}
|
||||||
|
</Info>
|
||||||
|
))}
|
||||||
|
{gun.shells.map((shell, index) => (
|
||||||
|
<Info
|
||||||
|
indent={index > 0}
|
||||||
|
name={`${SHELL_NAMES[shell.type]}${index === 0 ? ' damage' : ''}`}
|
||||||
|
unit="hp"
|
||||||
|
>
|
||||||
|
{shell.damage.armor}
|
||||||
|
</Info>
|
||||||
|
))}
|
||||||
|
{gun.shells.map((shell, index) => (
|
||||||
|
<Info
|
||||||
|
indent={index > 0}
|
||||||
|
name={`${SHELL_NAMES[shell.type]}${index === 0 ? ' module damage' : ''}`}
|
||||||
|
unit="hp"
|
||||||
|
>
|
||||||
|
{shell.damage.module}
|
||||||
|
</Info>
|
||||||
|
))}
|
||||||
|
{gun.shells.map((shell, index) => (
|
||||||
|
<Info
|
||||||
|
indent={index > 0}
|
||||||
|
name={`${SHELL_NAMES[shell.type]}${index === 0 ? ' velocity' : ''}`}
|
||||||
|
unit="m/s"
|
||||||
|
>
|
||||||
|
{shell.speed}
|
||||||
|
</Info>
|
||||||
|
))}
|
||||||
|
<Info name="Aim time" unit="s">
|
||||||
|
TODO
|
||||||
|
</Info>
|
||||||
|
<Info name="Dispersion at 100m" unit="m">
|
||||||
|
TODO
|
||||||
|
</Info>
|
||||||
|
<Info indent name="On move" unit="m">
|
||||||
|
TODO
|
||||||
|
</Info>
|
||||||
|
<Info indent name="On hull traverse" unit="m">
|
||||||
|
TODO
|
||||||
|
</Info>
|
||||||
|
<Info indent name="On turret traverse" unit="m">
|
||||||
|
TODO
|
||||||
|
</Info>
|
||||||
|
<Info indent name="On shoot" unit="m">
|
||||||
|
TODO
|
||||||
|
</Info>
|
||||||
|
<Info name="Gun depression" unit="°">
|
||||||
|
{gunModelDefinition.pitch.max}
|
||||||
|
</Info>
|
||||||
|
<Info indent name="Elevation" unit="°">
|
||||||
|
{-gunModelDefinition.pitch.min}
|
||||||
|
</Info>
|
||||||
|
{gunModelDefinition.pitch.front && (
|
||||||
|
<>
|
||||||
|
<Info indent name="Frontal depression" unit="°">
|
||||||
|
{gunModelDefinition.pitch.front.max}
|
||||||
|
</Info>
|
||||||
|
<Info indent name="Frontal elevation" unit="°">
|
||||||
|
{-gunModelDefinition.pitch.front.min}
|
||||||
|
</Info>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{gunModelDefinition.pitch.back && (
|
||||||
|
<>
|
||||||
|
<Info indent name="Rear depression" unit="°">
|
||||||
|
{gunModelDefinition.pitch.back.max}
|
||||||
|
</Info>
|
||||||
|
<Info indent name="Rear elevation" unit="°">
|
||||||
|
{-gunModelDefinition.pitch.back.min}
|
||||||
|
</Info>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{turretModelDefinition.yaw && (
|
||||||
|
<Info indent name="Azimuth" unit="°">
|
||||||
|
{-turretModelDefinition.yaw.min},{' '}
|
||||||
|
{turretModelDefinition.yaw.max}
|
||||||
|
</Info>
|
||||||
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<Flex direction="column" gap="2">
|
<Flex direction="column" gap="2">
|
||||||
<Heading size="5">Maneuverability</Heading>
|
<Heading size="5">Maneuverability</Heading>
|
||||||
<Info name="Speed forwards" value="55" unit="km/s" />
|
<Info name="Speed forwards" unit="km/s">
|
||||||
<Info indent name="Backward" value="20" unit="km/s" />
|
TODO
|
||||||
{/* TODO: average speed? */}
|
</Info>
|
||||||
<Info
|
<Info indent name="Average" unit="km/s">
|
||||||
name="Effective power on hard terrain"
|
TODO
|
||||||
value="36.86"
|
</Info>
|
||||||
unit="hp/kg"
|
<Info indent name="Backwards" unit="km/s">
|
||||||
/>
|
TODO
|
||||||
<Info
|
</Info>
|
||||||
indent
|
<Info name="Effective power on hard terrain" unit="hp/kg">
|
||||||
name="On medium terrain"
|
TODO
|
||||||
value="36.86"
|
</Info>
|
||||||
unit="hp/kg"
|
<Info indent name="On medium terrain" unit="hp/kg">
|
||||||
/>
|
TODO
|
||||||
<Info indent name="On soft terrain" value="36.86" unit="hp/kg" />
|
</Info>
|
||||||
<Info name="Weight" value="45" unit="mt" />
|
<Info indent name="On soft terrain" unit="hp/kg">
|
||||||
<Info
|
TODO
|
||||||
name="Traverse speed on hard terrain"
|
</Info>
|
||||||
value="36.86"
|
<Info name="Weight" unit="mt">
|
||||||
unit="°/s"
|
TODO
|
||||||
/>
|
</Info>
|
||||||
<Info indent name="On medium terrain" value="36.86" unit="°/s" />
|
<Info name="Traverse speed on hard terrain" unit="°/s">
|
||||||
<Info indent name="On soft terrain" value="36.86" unit="°/s" />
|
TODO
|
||||||
|
</Info>
|
||||||
|
<Info indent name="On medium terrain" unit="°/s">
|
||||||
|
TODO
|
||||||
|
</Info>
|
||||||
|
<Info indent name="On soft terrain" unit="°/s">
|
||||||
|
TODO
|
||||||
|
</Info>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Tabs.Content>
|
</Tabs.Content>
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ export const SHELL_NAMES: Record<ShellType, string> = {
|
|||||||
export type TankDefinitions = Record<number, TankDefinition>;
|
export type TankDefinitions = Record<number, TankDefinition>;
|
||||||
export interface TankDefinition {
|
export interface TankDefinition {
|
||||||
id: number;
|
id: number;
|
||||||
|
health: number;
|
||||||
nation: string;
|
nation: string;
|
||||||
name: string;
|
name: string;
|
||||||
name_full?: string;
|
name_full?: string;
|
||||||
@@ -33,6 +34,7 @@ export type TankDefinitionPrice =
|
|||||||
| { type: 'credits'; value: number }
|
| { type: 'credits'; value: number }
|
||||||
| { type: 'gold'; value: number };
|
| { type: 'gold'; value: number };
|
||||||
export interface TurretDefinition {
|
export interface TurretDefinition {
|
||||||
|
health: number;
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
tier: Tier;
|
tier: Tier;
|
||||||
@@ -66,7 +68,7 @@ export type ShellDefinition = {
|
|||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
speed: number;
|
speed: number;
|
||||||
damage: { armor: number; devices: number };
|
damage: { armor: number; module: number };
|
||||||
caliber: number;
|
caliber: number;
|
||||||
icon: string;
|
icon: string;
|
||||||
penetration: number | Vector2Tuple;
|
penetration: number | Vector2Tuple;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
amberDark,
|
amberDark,
|
||||||
|
amberDarkA,
|
||||||
blueDark,
|
blueDark,
|
||||||
orangeDark,
|
orangeDark,
|
||||||
purpleDark,
|
purpleDark,
|
||||||
@@ -20,6 +21,7 @@ export const theme = {
|
|||||||
...createColors(slate, 'light'),
|
...createColors(slate, 'light'),
|
||||||
|
|
||||||
...createColors(amberDark, 'amber'),
|
...createColors(amberDark, 'amber'),
|
||||||
|
...createColors(amberDarkA, 'amberAlpha'),
|
||||||
...createColors(orangeDark, 'orange'),
|
...createColors(orangeDark, 'orange'),
|
||||||
...createColors(purpleDark, 'purple'),
|
...createColors(purpleDark, 'purple'),
|
||||||
...createColors(blueDark, 'blue'),
|
...createColors(blueDark, 'blue'),
|
||||||
|
|||||||
Reference in new issue
Block a user