feat: burst data

This commit is contained in:
TrèsAbhi committed 2025-08-18 12:06:14 -05:00
1 parent 79c450798c
commit c20f8c5040
37 files changed
+973 -1378

No files matched your search

@@ -26,6 +26,7 @@ import { AssaultRangesVisualizer } from './AssaultRangesVisualizer';
import { GunFlexibilityVisualizer } from './GunFlexibilityVisualizer';
import { Info } from './Info';
import { InfoWithDelta } from './InfoWithDelta';
import { ReloadVisualizer } from './ReloadVisualizer';
import { RicochetVisualizer } from './RicochetVisualizer';
import { StatsTableWrapper } from './StatsTableWrapper';
import type { StatsAcceptorProps } from './TraverseVisualizer';
@@ -54,8 +55,7 @@ export function Firepower({
]);
const tankModelDefinition = useTankModelDefinition();
const turretModelDefinition = tankModelDefinition.turrets[turret.id];
const gunModelDefinition =
turretModelDefinition.guns[gun.gun_type!.value.base.id];
const gunModelDefinition = turretModelDefinition.guns[gun.id];
useEffect(() => {
if (penetrationDistanceInput.current) {
@@ -71,7 +71,7 @@ export function Firepower({
</Heading>
<Flex>
{gun.gun_type!.value.base.shells.map((thisShell, shellIndex) => {
{gun.shells.map((thisShell, shellIndex) => {
const selected = thisShell.id === shell.id;
return (
@@ -84,13 +84,9 @@ export function Firepower({
borderTopLeftRadius: shellIndex === 0 ? undefined : 0,
borderBottomLeftRadius: shellIndex === 0 ? undefined : 0,
borderTopRightRadius:
shellIndex === gun.gun_type!.value.base.shells.length - 1
? undefined
: 0,
shellIndex === gun.shells.length - 1 ? undefined : 0,
borderBottomRightRadius:
shellIndex === gun.gun_type!.value.base.shells.length - 1
? undefined
: 0,
shellIndex === gun.shells.length - 1 ? undefined : 0,
marginLeft: shellIndex === 0 ? 0 : -1,
}}
onClick={() => {
@@ -125,11 +121,8 @@ export function Firepower({
<InfoWithDelta stats={stats} decimals={0} value="moduleDamage" />
<InfoWithDelta stats={stats} decimals={0} value="penetration" />
{gun.gun_type!.value.base.assault_ranges?.types.includes(shell.type) && (
<AssaultRangesVisualizer
stats={stats}
ranges={gun.gun_type!.value.base.assault_ranges}
/>
{gun.assault_ranges?.types.includes(shell.type) && (
<AssaultRangesVisualizer stats={stats} ranges={gun.assault_ranges} />
)}
{gun.gun_type!.$case === 'auto_reloader' && (
@@ -190,7 +183,7 @@ export function Firepower({
value="intraClip"
/>
)}
{/* <ReloadVisualizer stats={stats} /> */}
<ReloadVisualizer stats={stats} />
{typeof shell.penetration !== 'number' && (
<>
@@ -51,7 +51,7 @@ export function ModelChunk({ only }: Props) {
const tankModel = modelDefinitions.models[tank.id];
const turretModel = tankModel.turrets[turret.id];
const gunModel = turretModel.guns[gun.gun_type!.value.base.id];
const gunModel = turretModel.guns[gun.id];
const originZ = tankModel.turret_origin.z + turretModel.gun_origin.z;
const originY = tankModel.turret_origin.y + turretModel.gun_origin.y;
@@ -47,7 +47,7 @@ export function GunFlexibilityVisualizer({
const tankModel = modelDefinition.models[tank.id];
const turretModel = tankModel.turrets[turret.id];
const gunModel = turretModel.guns[gun.gun_type!.value.base.id];
const gunModel = turretModel.guns[gun.id];
const [yaw, setYaw] = useState(0);
const [pitch, setPitch] = useState(0);
@@ -61,9 +61,7 @@ export const InfoWithDelta = memo<InfoWithDeltaProps>(
const delta = useDelta(uhWhatDoICallThisVariable);
const protagonistTank = Duel.use((state) => state.protagonist.tank);
const shellIndex = Duel.use((state) =>
state.protagonist.gun.gun_type!.value.base.shells.indexOf(
state.protagonist.shell,
),
state.protagonist.gun.shells.indexOf(state.protagonist.shell),
);
const equipmentMatrix = Duel.use(
(state) => state.protagonist.equipmentMatrix,
@@ -89,9 +87,7 @@ export const InfoWithDelta = memo<InfoWithDeltaProps>(
provisionDefinitions,
);
if (
member.gun.gun_type!.value.base.shells[shellIndex] === undefined
) {
if (member.gun.shells[shellIndex] === undefined) {
return undefined;
}
@@ -107,7 +103,7 @@ export const InfoWithDelta = memo<InfoWithDeltaProps>(
engine: member.engine,
gun: member.gun,
equipmentMatrix: equipmentMatrix,
shell: member.gun.gun_type!.value.base.shells[shellIndex],
shell: member.gun.shells[shellIndex],
stockEngine: tank.engines[0],
stockGun: tank.turrets[0].guns[0],
stockTrack: tank.tracks[0],
@@ -11,7 +11,7 @@ export function ShellSwitcher() {
return (
<Flex>
{gun.gun_type!.value.base.shells.map((thisShell, shellIndex) => {
{gun.shells.map((thisShell, shellIndex) => {
const selected = thisShell.id === shell.id;
return (
@@ -26,9 +26,7 @@ export function ShellSwitcher() {
borderBottomLeftRadius: shellIndex === 0 ? '50%' : 0,
borderTopRightRadius: 0,
borderBottomRightRadius:
shellIndex === gun.gun_type!.value.base.shells.length - 1
? '50%'
: 0,
shellIndex === gun.shells.length - 1 ? '50%' : 0,
marginLeft: shellIndex === 0 ? 0 : -1,
}}
onClick={() => {
@@ -41,8 +41,7 @@ export function Characteristics({ skeleton }: MaybeSkeletonComponentProps) {
const stockGun = stockTurret.guns[0];
const tankModelDefinition = useTankModelDefinition();
const turretModelDefinition = tankModelDefinition.turrets[turret.id];
const gunModelDefinition =
turretModelDefinition.guns[gun.gun_type!.value.base.id];
const gunModelDefinition = turretModelDefinition.guns[gun.id];
const stats = tankCharacteristics(
{
tank,
@@ -33,7 +33,7 @@ type ModuleDefinition =
function extractModule(raw: ModuleDefinition | TankDefinition) {
if ('gun_type' in raw) {
return raw.gun_type!.value.base;
return raw;
} else
return raw as Exclude<ModuleDefinition | TankDefinition, GunDefinition>;
}
@@ -179,37 +179,31 @@ export function Modules() {
if (
!draft.protagonist.turret.guns.some(
(gun) =>
gun.gun_type!.value.base.id ===
draft.protagonist.gun.gun_type!.value.base.id,
(gun) => gun.id === draft.protagonist.gun.id,
)
) {
draft.protagonist.gun = draft.protagonist.turret.guns.at(-1)!;
draft.protagonist.shell =
draft.protagonist.gun.gun_type!.value.base.shells[0];
draft.protagonist.shell = draft.protagonist.gun.shells[0];
}
} else if (unlock.type === ModuleType.GUN) {
const gunInTurret = draft.protagonist.turret.guns.find(
(gun) => gun.gun_type!.value.base.id === unlock.id,
(gun) => gun.id === unlock.id,
);
if (gunInTurret) {
draft.protagonist.gun = gunInTurret;
draft.protagonist.shell = gunInTurret.gun_type!.value.base.shells[0];
draft.protagonist.shell = gunInTurret.shells[0];
} else {
// TODO: warn somehow?
const suitableTurret = draft.protagonist.tank.turrets.find((turret) =>
turret.guns.some(
(gun) => gun.gun_type!.value.base.id === unlock.id,
),
turret.guns.some((gun) => gun.id === unlock.id),
)!;
const gunInSuitableTurret = suitableTurret.guns.find(
(gun) => gun.gun_type!.value.base.id === unlock.id,
(gun) => gun.id === unlock.id,
)!;
draft.protagonist.turret = suitableTurret;
draft.protagonist.gun = gunInSuitableTurret;
draft.protagonist.shell =
gunInSuitableTurret.gun_type!.value.base.shells[0];
draft.protagonist.shell = gunInSuitableTurret.shells[0];
}
} else if (unlock.type === ModuleType.ENGINE) {
draft.protagonist.engine = draft.protagonist.tank.engines.find(
@@ -241,11 +235,9 @@ export function Modules() {
} else if (unlock.type === ModuleType.GUN) {
module = tank.turrets
.find((turret) =>
turret.guns.some(
(gun) => gun.gun_type!.value.base.id === unlock.id,
),
turret.guns.some((gun) => gun.id === unlock.id),
)!
.guns.find((gun) => gun.gun_type!.value.base.id === unlock.id)!;
.guns.find((gun) => gun.id === unlock.id)!;
} else if (unlock.type === ModuleType.VEHICLE) {
module = tankDefinitions.tanks[unlock.id];
}
@@ -308,7 +300,7 @@ export function Modules() {
(unlock.type === ModuleType.TURRET
? turret.id
: unlock.type === ModuleType.GUN
? gun.gun_type!.value.base.id
? gun.id
: unlock.type === ModuleType.ENGINE
? engine.id
: unlock.type === ModuleType.TRACKS
@@ -319,8 +311,7 @@ export function Modules() {
unlock.type === ModuleType.TURRET
? extractModule(module).id == topTurret.id
: unlock.type === ModuleType.GUN
? extractModule(module).id ===
topGun.gun_type!.value.base.id
? extractModule(module).id === topGun.id
: unlock.type === ModuleType.ENGINE
? extractModule(module).id === topEngine.id
: unlock.type === ModuleType.TRACKS
@@ -371,8 +362,7 @@ export function Modules() {
mutateDuel((draft) => {
draft.protagonist.turret = draft.protagonist.tank.turrets[0];
draft.protagonist.gun = draft.protagonist.turret.guns[0];
draft.protagonist.shell =
draft.protagonist.gun.gun_type!.value.base.shells[0];
draft.protagonist.shell = draft.protagonist.gun.shells[0];
draft.protagonist.engine = draft.protagonist.tank.engines[0];
draft.protagonist.track = draft.protagonist.tank.tracks[0];
});
@@ -387,8 +377,7 @@ export function Modules() {
draft.protagonist.turret =
draft.protagonist.tank.turrets.at(-1)!;
draft.protagonist.gun = draft.protagonist.turret.guns.at(-1)!;
draft.protagonist.shell =
draft.protagonist.gun.gun_type!.value.base.shells[0];
draft.protagonist.shell = draft.protagonist.gun.shells[0];
draft.protagonist.engine =
draft.protagonist.tank.engines.at(-1)!;
draft.protagonist.track =
@@ -427,7 +416,7 @@ export function Modules() {
{tree(ModuleType.GUN, [
{
cost: { type: 'xp', value: 0 },
id: gun0.gun_type!.value.base.id,
id: gun0.id,
type: ModuleType.GUN,
},
])}
@@ -90,7 +90,7 @@ export function CustomShellButton() {
modified.normalization = undefined;
modified.ricochet = undefined;
modified.explosion_radius =
gun.gun_type!.value.base.shells.find(
gun.shells.find(
(shell) => shell.type === ShellType.HE,
)?.explosion_radius ?? 1;
break;
@@ -21,8 +21,7 @@ export function QuickInputs() {
const tankModelDefinition = useTankModelDefinition();
const turretModelDefinition =
tankModelDefinition.turrets[protagonist.turret.id];
const gunModelDefinition =
turretModelDefinition.guns[protagonist.gun.gun_type!.value.base.id];
const gunModelDefinition = turretModelDefinition.guns[protagonist.gun.id];
const initialGunPitch =
tankModelDefinition.initial_turret_rotation?.pitch ?? 0;
const hasImprovedVerticalStabilizer = useEquipment(122);
@@ -141,7 +141,7 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
mr="2"
mb="1"
>
{TIER_ROMAN_NUMERALS[antagonistGun.gun_type!.value.base.tier]}
{TIER_ROMAN_NUMERALS[antagonistGun.tier]}
</Text>
<img
@@ -161,26 +161,20 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
{[...antagonistUniqueGuns].reverse().map(({ gun, turret }) => (
<ModuleButton
module="gun"
key={gun.gun_type!.value.base.id}
key={gun.id}
onClick={() =>
mutateDuel((draft) => {
draft.antagonist.turret = turret;
draft.antagonist.gun = gun;
draft.antagonist.shell =
gun.gun_type!.value.base.shells[0];
draft.antagonist.shell = gun.shells[0];
})
}
selected={
gun.gun_type!.value.base.id ===
antagonistGun.gun_type!.value.base.id
}
discriminator={
TIER_ROMAN_NUMERALS[gun.gun_type!.value.base.tier]
}
selected={gun.id === antagonistGun.id}
discriminator={TIER_ROMAN_NUMERALS[gun.tier]}
secondaryDiscriminator={
<Text style={{ fontSize: '0.75em' }}>
{literals(strings.common.units.mm, [
gun.gun_type!.value.base.shells[0].caliber.toFixed(0),
gun.shells[0].caliber.toFixed(0),
])}
</Text>
}
@@ -198,7 +192,7 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
}}
overflow="hidden"
>
{antagonistGun.gun_type!.value.base.shells.map((thisShell) => (
{antagonistGun.shells.map((thisShell) => (
<IconButton
color={
thisShell.id === antagonistShell.id && !hasCustomShell
@@ -367,8 +361,7 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
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];
draft.antagonist.shell = draft.antagonist.gun.shells[0];
});
setAntagonistSelectorOpen(false);
}}
@@ -56,9 +56,7 @@ export function Controls({
const antagonistTurretModelDefinition =
antagonistModelDefinition.turrets[antagonistTurret.id];
const protagonistGunModelDefinition =
protagonistTurretModelDefinition.guns[
protagonistGun.gun_type!.value.base.id
];
protagonistTurretModelDefinition.guns[protagonistGun.id];
const protagonistHullOrigin = new Vector3(
protagonistTrackModelDefinition.origin.x,
protagonistTrackModelDefinition.origin.y,
@@ -14,7 +14,7 @@ export function InitialAligner() {
const tankModel = modelDefinitions.models[tank.id];
const turretModel = tankModel.turrets[turret.id];
const gunModel = turretModel.guns[gun.gun_type!.value.base.id];
const gunModel = turretModel.guns[gun.id];
useEffect(() => {
const [pitch, yaw] = applyPitchYawLimits(
@@ -49,7 +49,7 @@ export function SceneProps() {
const trackModelDefinition =
modelDefinitions.models[tank.id].tracks[track.id];
const gunModelDefinition =
tankModelDefinition.turrets[turret.id].guns[gun.gun_type!.value.base.id];
tankModelDefinition.turrets[turret.id].guns[gun.id];
const turretModelDefinition = tankModelDefinition.turrets[turret.id];
const hullOrigin = new Vector3(
trackModelDefinition.origin.x,
@@ -28,8 +28,7 @@ export function TankModel() {
const tankModelDefinition = useTankModelDefinition();
const turretModelDefinition =
tankModelDefinition.turrets[protagonist.turret.id];
const gunModelDefinition =
turretModelDefinition.guns[protagonist.gun.gun_type!.value.base.id];
const gunModelDefinition = turretModelDefinition.guns[protagonist.gun.id];
const { gltf } = useModel(protagonist.tank.id);
const nodes = Object.values(gltf.nodes);
@@ -31,8 +31,7 @@ export function TransitionSkeleton() {
const tankModelDefinition = useTankModelDefinition();
const turretModelDefinition =
tankModelDefinition.turrets[protagonist.turret.id];
const gunModelDefinition =
turretModelDefinition.guns[protagonist.gun.gun_type!.value.base.id];
const gunModelDefinition = turretModelDefinition.guns[protagonist.gun.id];
const { gltf } = useModel(protagonist.tank.id);
const nodes = Object.values(gltf.nodes);
const revealed = tankopediaEphemeralStore((state) => state.revealed);
@@ -59,8 +59,7 @@ export const TankSandbox = forwardRef<HTMLCanvasElement, TankSandboxProps>(
const tankModelDefinition = useTankModelDefinition();
const turretModelDefinition =
tankModelDefinition.turrets[protagonistTurret.id];
const gunModelDefinition =
turretModelDefinition.guns[protagonistGun.gun_type!.value.base.id];
const gunModelDefinition = turretModelDefinition.guns[protagonistGun.id];
const rawDisplay = TankopediaEphemeral.use((state) => state.display);
const [display, setDisplay] = useState(rawDisplay);
const hideTankModelUnderArmor = TankopediaPersistent.use(
@@ -29,8 +29,7 @@ export function HeroSection({ skeleton }: MaybeSkeletonComponentProps) {
(filtered.reduce((accumulator, thisTank) => {
return (
accumulator +
thisTank.turrets.at(-1)!.guns.at(-1)!.gun_type!.value.base.shells[0]
.penetration.near
thisTank.turrets.at(-1)!.guns.at(-1)!.shells[0].penetration.near
);
}, 0) /
filtered.length) *
@@ -51,8 +50,7 @@ export function HeroSection({ skeleton }: MaybeSkeletonComponentProps) {
useEffect(() => {
function handleKeyDown(event: KeyboardEvent) {
const { shells } =
duelStore.getState().antagonist.gun.gun_type!.value.base;
const { shells } = duelStore.getState().antagonist.gun;
times(3, (index) => {
if (event.key === `${index + 1}` && shells.length > index) {