mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-21 02:37:05 +02:00
feat: auto reloader compatibility
This commit is contained in:
1 parent
8033e8f7cc
commit
bdbfce0e18
3 files changed
+49
-123
No files matched your search
+1
-77
@@ -5,18 +5,9 @@ import {
|
||||
resolvePenetrationCoefficient,
|
||||
} from '@blitzkit/core';
|
||||
import { literals } from '@blitzkit/i18n';
|
||||
import {
|
||||
Flex,
|
||||
Heading,
|
||||
IconButton,
|
||||
Slider,
|
||||
Text,
|
||||
TextField,
|
||||
Tooltip,
|
||||
} from '@radix-ui/themes';
|
||||
import { Flex, Heading, IconButton, Tooltip } from '@radix-ui/themes';
|
||||
import { debounce } from 'lodash-es';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { lerp } from 'three/src/math/MathUtils.js';
|
||||
import { useEquipment } from '../../../../../../hooks/useEquipment';
|
||||
import { useLocale } from '../../../../../../hooks/useLocale';
|
||||
import { useTankModelDefinition } from '../../../../../../hooks/useTankModelDefinition';
|
||||
@@ -187,73 +178,6 @@ export function Firepower({
|
||||
)}
|
||||
<ReloadVisualizer stats={stats} />
|
||||
|
||||
{typeof shell.penetration !== 'number' && (
|
||||
<>
|
||||
<InfoWithDelta
|
||||
stats={stats}
|
||||
indent
|
||||
decimals={0}
|
||||
name={literals(
|
||||
strings.website.tools.tankopedia.characteristics.values
|
||||
.at_distance,
|
||||
[`${penetrationDistance}`],
|
||||
)}
|
||||
noRanking
|
||||
value={() =>
|
||||
lerp(
|
||||
shell.penetration.near,
|
||||
shell.penetration.far,
|
||||
(penetrationDistance * penetrationLossOverDistanceCoefficient) /
|
||||
500,
|
||||
) * penetrationCoefficient
|
||||
}
|
||||
/>
|
||||
<Flex align="center" gap="2" style={{ paddingLeft: 24 }}>
|
||||
<Text color="gray">
|
||||
{
|
||||
strings.website.tools.tankopedia.characteristics.values
|
||||
.at_distance_slider_label
|
||||
}
|
||||
</Text>
|
||||
<Slider
|
||||
variant="classic"
|
||||
key={penetrationDistance}
|
||||
min={0}
|
||||
max={500}
|
||||
style={{ flex: 1 }}
|
||||
defaultValue={[penetrationDistance]}
|
||||
onValueChange={([value]) => {
|
||||
setPenetrationDistanceDebounced(value);
|
||||
|
||||
if (!penetrationDistanceInput.current) return;
|
||||
|
||||
penetrationDistanceInput.current.value = value.toString();
|
||||
}}
|
||||
/>
|
||||
<TextField.Root
|
||||
variant="classic"
|
||||
style={{ width: 64 }}
|
||||
ref={penetrationDistanceInput}
|
||||
defaultValue={penetrationDistance}
|
||||
onBlur={() => {
|
||||
setPenetrationDistance(
|
||||
Math.min(
|
||||
parseInt(penetrationDistanceInput.current!.value),
|
||||
500,
|
||||
),
|
||||
);
|
||||
}}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === 'Enter') {
|
||||
event.currentTarget.blur();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<TextField.Slot side="right">m</TextField.Slot>
|
||||
</TextField.Root>
|
||||
</Flex>
|
||||
</>
|
||||
)}
|
||||
{isExplosive(shell.type) && (
|
||||
<InfoWithDelta
|
||||
stats={stats}
|
||||
|
||||
+48
-44
@@ -24,21 +24,7 @@ export function ReloadVisualizer({ stats }: StatsAcceptorProps) {
|
||||
const totalTime = useRef<HTMLSpanElement>(null);
|
||||
const progressTime = useRef<HTMLSpanElement>(null);
|
||||
|
||||
/**
|
||||
* 3-shell gun example:
|
||||
*
|
||||
* 0-1: clip reload
|
||||
* ~~~: shell shot
|
||||
* 1-2: intra-clip
|
||||
* ~~~: shell shot
|
||||
* 2-3: intra-clip
|
||||
* ~~~: shell shot & reset to 0
|
||||
*
|
||||
* intra-clip time = intraClip * (n - 1)
|
||||
*/
|
||||
const state = useRef(0);
|
||||
const dpm = useRef(0);
|
||||
const shotPool = useRef(new Set<number>());
|
||||
|
||||
useEffect(() => {
|
||||
let cancel = false;
|
||||
@@ -46,55 +32,73 @@ export function ReloadVisualizer({ stats }: StatsAcceptorProps) {
|
||||
let lastState = state.current;
|
||||
const shellArray = new Array(stats.shells);
|
||||
|
||||
function shoot() {
|
||||
shotPool.current.add(Date.now() / 1000);
|
||||
dpm.current += stats.damage;
|
||||
}
|
||||
|
||||
function frame() {
|
||||
if (!totalTime.current || !progressTime.current) return;
|
||||
|
||||
const reloadThreshold = stats.shellReloads
|
||||
? stats.shellReloads.length
|
||||
: 1;
|
||||
const now = Date.now() / 1000;
|
||||
const dt = now - lastT;
|
||||
let reload;
|
||||
let shells;
|
||||
|
||||
shotPool.current.forEach((t) => {
|
||||
if (now - t > 60) shotPool.current.delete(t);
|
||||
});
|
||||
if (state.current < reloadThreshold) {
|
||||
const shellReload =
|
||||
reloadThreshold === 1
|
||||
? stats.shellReload!
|
||||
: stats.shellReloads![Math.floor(state.current)];
|
||||
|
||||
dpm.current = shotPool.current.size * stats.damage;
|
||||
|
||||
if (state.current < 1) {
|
||||
state.current = Math.min(1, state.current + dt / stats.shellReload!);
|
||||
reload = state.current;
|
||||
state.current = Math.min(
|
||||
reloadThreshold,
|
||||
state.current + dt / shellReload,
|
||||
);
|
||||
reload = state.current % 1;
|
||||
shells = shellArray.fill(reload);
|
||||
|
||||
totalTime.current.innerHTML = stats.shellReload!.toFixed(PRECISION);
|
||||
progressTime.current.innerHTML = (
|
||||
(1 - reload) *
|
||||
stats.shellReload!
|
||||
).toFixed(PRECISION);
|
||||
if (reloadThreshold > 1) {
|
||||
shells = shells.map((_, index) =>
|
||||
Math.max(0, state.current - shellArray.length + index + 1),
|
||||
);
|
||||
}
|
||||
|
||||
if (state.current >= 1) shoot();
|
||||
} else if (state.current < stats.shells) {
|
||||
const interval =
|
||||
stats.burstShells === 1
|
||||
? stats.intraClip!
|
||||
: Math.floor(state.current) % stats.burstShells === 0
|
||||
? stats.intraClip!
|
||||
: stats.burstInterShell!;
|
||||
totalTime.current.innerHTML = shellReload.toFixed(PRECISION);
|
||||
progressTime.current.innerHTML = ((1 - reload) * shellReload).toFixed(
|
||||
PRECISION,
|
||||
);
|
||||
} else if (state.current < stats.shells + reloadThreshold - 1) {
|
||||
let interval;
|
||||
|
||||
state.current = Math.min(stats.shells, state.current + dt / interval);
|
||||
shells = times(stats.shells, (index) => index > state.current - 1);
|
||||
if (
|
||||
stats.burstShells === 1 ||
|
||||
Math.floor(state.current) % stats.burstShells === 0
|
||||
) {
|
||||
interval = stats.intraClip!;
|
||||
} else {
|
||||
interval = stats.burstInterShell!;
|
||||
}
|
||||
|
||||
state.current = Math.min(
|
||||
stats.shells + reloadThreshold,
|
||||
state.current + dt / interval,
|
||||
);
|
||||
reload = state.current % 1;
|
||||
|
||||
if (reloadThreshold > 1) {
|
||||
shells = shellArray.map((_, index) =>
|
||||
Math.max(
|
||||
0,
|
||||
Math.floor(index + 1 + reloadThreshold - state.current),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
shells = times(stats.shells, (index) => index > state.current - 1);
|
||||
}
|
||||
|
||||
totalTime.current.innerHTML = interval.toFixed(PRECISION);
|
||||
progressTime.current.innerHTML = ((1 - reload) * interval).toFixed(
|
||||
PRECISION,
|
||||
);
|
||||
|
||||
if (lastState % 1 > state.current % 1) shoot();
|
||||
} else {
|
||||
state.current = 0;
|
||||
reload = 0;
|
||||
|
||||
-2
@@ -87,8 +87,6 @@ export function Characteristics({ skeleton }: MaybeSkeletonComponentProps) {
|
||||
modelTransformEvent.dispatch({ pitch, yaw });
|
||||
}, [hasImprovedVerticalStabilizer, hasDownImprovedVerticalStabilizer]);
|
||||
|
||||
console.log(tank.slug, gun.burst, stats.burstShells);
|
||||
|
||||
return (
|
||||
<Flex
|
||||
gap="8"
|
||||
|
||||
Reference in new issue
Block a user