feat: stat parity

This commit is contained in:
TrèsAbhi committed 2024-02-17 09:57:52 -06:00
1 parent b7558c33da
commit 09ba8663e6
6 files changed
+66 -51

No files matched your search

+1
View File
@@ -33,6 +33,7 @@
"deburr", "deburr",
"decalmask", "decalmask",
"defence", "defence",
"degressive",
"diacriticless", "diacriticless",
"donotremove", "donotremove",
"drei", "drei",
@@ -14,25 +14,25 @@ export function Miscellaneous() {
const cooldownBooster = useTankopediaTemporary( const cooldownBooster = useTankopediaTemporary(
(state) => state.cooldownBooster, (state) => state.cooldownBooster,
); );
const crew = useTankopediaTemporary((state) => state.crew); const crewMastery = useTankopediaTemporary((state) => state.crewMastery);
const crewInput = useRef<HTMLInputElement>(null); const crewInput = useRef<HTMLInputElement>(null);
const [crewDraft, setCrewDraft] = useState(crew); const [crewMasteryDraft, setCrewMasteryDraft] = useState(crewMastery);
const debouncedApplyDraft = useMemo( const debouncedApplyDraft = useMemo(
() => () =>
debounce((value) => { debounce((value) => {
mutateTankopediaTemporary((draft) => { mutateTankopediaTemporary((draft) => {
draft.crew = value; draft.crewMastery = value;
}); });
}, 500), }, 500),
[], [],
); );
useEffect(() => { useEffect(() => {
setCrewDraft(crew); setCrewMasteryDraft(crewMastery);
if (!crewInput.current) return; if (!crewInput.current) return;
crewInput.current.value = `${Math.round(crew * 100)}`; crewInput.current.value = `${Math.round(crewMastery * 100)}`;
}, [crew]); }, [crewMastery]);
return ( return (
<ConfigurationChildWrapper> <ConfigurationChildWrapper>
@@ -91,18 +91,18 @@ export function Miscellaneous() {
<TextField.Slot>Crew</TextField.Slot> <TextField.Slot>Crew</TextField.Slot>
<TextField.Input <TextField.Input
ref={crewInput} ref={crewInput}
defaultValue={Math.round(crew * 100)} defaultValue={Math.round(crewMastery * 100)}
style={{ textAlign: 'right' }} style={{ textAlign: 'right' }}
onBlur={() => { onBlur={() => {
const newValue = Number(crewInput.current!.value) / 100; const newValue = Number(crewInput.current!.value) / 100;
if (isNaN(newValue)) { if (isNaN(newValue)) {
crewInput.current!.value = `${Math.round(crew * 100)}`; crewInput.current!.value = `${Math.round(crewMastery * 100)}`;
return; return;
} }
mutateTankopediaTemporary((draft) => { mutateTankopediaTemporary((draft) => {
draft.crew = clamp(newValue / 100, 0.5, 1); draft.crewMastery = clamp(newValue / 100, 0.5, 1);
}); });
}} }}
onKeyUp={(event) => { onKeyUp={(event) => {
@@ -117,10 +117,10 @@ export function Miscellaneous() {
<Slider <Slider
min={50} min={50}
max={100} max={100}
value={[crewDraft * 100]} value={[crewMasteryDraft * 100]}
style={{ width: '100%' }} style={{ width: '100%' }}
onValueChange={([value]) => { onValueChange={([value]) => {
setCrewDraft(value / 100); setCrewMasteryDraft(value / 100);
debouncedApplyDraft(value / 100); debouncedApplyDraft(value / 100);
}} }}
/> />
@@ -6,8 +6,10 @@ import { lerp } from 'three/src/math/MathUtils';
import { isExplosive } from '../../../../../../core/blitz/isExplosive'; import { isExplosive } from '../../../../../../core/blitz/isExplosive';
import { resolveNearPenetration } from '../../../../../../core/blitz/resolveNearPenetration'; import { resolveNearPenetration } from '../../../../../../core/blitz/resolveNearPenetration';
import { coefficient } from '../../../../../../core/blitzkrieg/coefficient'; import { coefficient } from '../../../../../../core/blitzkrieg/coefficient';
import { degressiveStat } from '../../../../../../core/blitzkrieg/degressiveStat';
import { modelDefinitions } from '../../../../../../core/blitzkrieg/modelDefinitions'; import { modelDefinitions } from '../../../../../../core/blitzkrieg/modelDefinitions';
import { normalizeBoundingBox } from '../../../../../../core/blitzkrieg/normalizeBoundingBox'; import { normalizeBoundingBox } from '../../../../../../core/blitzkrieg/normalizeBoundingBox';
import { progressiveStat } from '../../../../../../core/blitzkrieg/progressiveStat';
import { resolveDpm } from '../../../../../../core/blitzkrieg/resolveDpm'; import { resolveDpm } from '../../../../../../core/blitzkrieg/resolveDpm';
import { sum } from '../../../../../../core/blitzkrieg/sum'; import { sum } from '../../../../../../core/blitzkrieg/sum';
import { import {
@@ -29,7 +31,15 @@ export function Characteristics() {
const { tank, turret, gun, engine, track, shell } = useDuel( const { tank, turret, gun, engine, track, shell } = useDuel(
(state) => state.protagonist!, (state) => state.protagonist!,
); );
const crew = useTankopediaTemporary((state) => state.crew); const provisions = useProvisions();
const crewMastery = useTankopediaTemporary((state) => state.crewMastery);
const provisionCrewBonus = provisions.reduce(
(total, provision) =>
provision.crew ? total + provision.crew / 100 : total,
0,
);
const commanderMastery = crewMastery + provisionCrewBonus;
const crewMemberMastery = commanderMastery * 1.1;
const stockEngine = tank.engines[0]; const stockEngine = tank.engines[0];
const stockTrack = tank.tracks[0]; const stockTrack = tank.tracks[0];
const stockTurret = tank.turrets[0]; const stockTurret = tank.turrets[0];
@@ -53,7 +63,6 @@ export function Characteristics() {
stockTurret.weight + stockTurret.weight +
stockGun.weight; stockGun.weight;
const [penetrationDistance, setPenetrationDistance] = useState(250); const [penetrationDistance, setPenetrationDistance] = useState(250);
const provisions = useProvisions();
const camouflage = useTankopediaTemporary((state) => state.camouflage); const camouflage = useTankopediaTemporary((state) => state.camouflage);
const hasRammer = useEquipment(100); const hasRammer = useEquipment(100);
const hasCalibratedShells = useEquipment(103); const hasCalibratedShells = useEquipment(103);
@@ -89,10 +98,10 @@ export function Characteristics() {
[hasEnhancedSandbagArmor, 0.06], [hasEnhancedSandbagArmor, 0.06],
[hasImprovedAssembly, 0.06], [hasImprovedAssembly, 0.06],
); );
const reloadCoefficient = coefficient( const reloadCoefficient =
[hasRammer, -0.07], (coefficient([hasRammer, -0.07]) *
[hasAdrenaline && gun.type === 'regular', -0.2], coefficient([true, degressiveStat(crewMemberMastery)])) /
); coefficient([hasAdrenaline && gun.type === 'regular', 0.2]);
const penetrationCoefficient = coefficient([ const penetrationCoefficient = coefficient([
hasCalibratedShells, hasCalibratedShells,
isExplosive(shell.type) ? 0.1 : 0.05, isExplosive(shell.type) ? 0.1 : 0.05,
@@ -110,15 +119,18 @@ export function Characteristics() {
hasSupercharger, hasSupercharger,
-0.5, -0.5,
]); ]);
const aimTimeCoefficient = coefficient( const aimTimeCoefficient =
coefficient(
[hasEnhancedGunLayingDrive, -0.1], [hasEnhancedGunLayingDrive, -0.1],
[hasReticleCalibration, -0.4], [hasReticleCalibration, -0.4],
[hasVerticalStabilizer, -0.15], ) * coefficient([true, degressiveStat(crewMemberMastery)]);
); const dispersionStillCoefficient =
const dispersionCoefficient = coefficient( coefficient([hasRefinedGun, -0.1], [hasReticleCalibration, -0.4]) *
[hasRefinedGun, -0.1], coefficient([true, degressiveStat(crewMemberMastery)]);
[hasReticleCalibration, -0.4], const dispersionMovingCoefficient = coefficient([
); hasVerticalStabilizer,
-0.15,
]);
const enginePowerCoefficient = coefficient( const enginePowerCoefficient = coefficient(
[ [
hasEngineAccelerator && (tank.type === 'light' || tank.type === 'medium'), hasEngineAccelerator && (tank.type === 'light' || tank.type === 'medium'),
@@ -136,19 +148,20 @@ export function Characteristics() {
[hasGearOil, 0.03], [hasGearOil, 0.03],
[hasImprovedGearOil, 0.06], [hasImprovedGearOil, 0.06],
); );
const turretTraverseCoefficient = coefficient( const turretTraverseCoefficient =
[hasStandardFuel, 0.03], coefficient([hasStandardFuel, 0.03], [hasImprovedFuel, 0.1]) *
[hasImprovedFuel, 0.1], coefficient([true, progressiveStat(crewMemberMastery)]);
); const hullTraverseCoefficient =
const hullTraverseCoefficient = coefficient( coefficient(
[hasImprovedControl, 0.1], [hasImprovedControl, 0.1],
[hasImprovedEnginePowerBoost, 0.05], [hasImprovedEnginePowerBoost, 0.05],
); ) * coefficient([true, progressiveStat(crewMemberMastery)]);
const resistanceCoefficient = coefficient([hasImprovedSuspension, -0.25]); const resistanceCoefficient = coefficient([hasImprovedSuspension, -0.25]);
const viewRangeCoefficient = coefficient([ const viewRangeCoefficient =
coefficient([
hasImprovedOptics, hasImprovedOptics,
tank.type === 'tankDestroyer' ? 0.05 : tank.type === 'heavy' ? 0.07 : 0.1, tank.type === 'tankDestroyer' ? 0.05 : tank.type === 'heavy' ? 0.07 : 0.1,
]); ]) * coefficient([true, progressiveStat(commanderMastery)]);
const fireChanceCoefficient = coefficient([hasProtectiveKit, -0.2]); const fireChanceCoefficient = coefficient([hasProtectiveKit, -0.2]);
const speedForwardsSum = sum( const speedForwardsSum = sum(
@@ -199,11 +212,6 @@ export function Characteristics() {
], ],
); );
const provisionCrewBonus = provisions.reduce(
(total, provision) =>
provision.crew ? total + provision.crew / 100 : total,
0,
);
const resolvedEnginePower = engine.power * enginePowerCoefficient; const resolvedEnginePower = engine.power * enginePowerCoefficient;
const dpm = resolveDpm( const dpm = resolveDpm(
gun, gun,
@@ -363,7 +371,7 @@ export function Characteristics() {
<InfoWithDelta name="Module damage" unit="hp" decimals={0}> <InfoWithDelta name="Module damage" unit="hp" decimals={0}>
{shell.damage.module * damageCoefficient} {shell.damage.module * damageCoefficient}
</InfoWithDelta> </InfoWithDelta>
<InfoWithDelta name="Shell velocity" unit="m/s"> <InfoWithDelta name="Shell velocity" unit="m/s" decimals={0}>
{shell.speed * shellVelocityCoefficient} {shell.speed * shellVelocityCoefficient}
</InfoWithDelta> </InfoWithDelta>
<InfoWithDelta <InfoWithDelta
@@ -382,7 +390,7 @@ export function Characteristics() {
unit="m" unit="m"
deltaType="lowerIsBetter" deltaType="lowerIsBetter"
> >
{gun.dispersion.base * dispersionCoefficient} {gun.dispersion.base * dispersionStillCoefficient}
</InfoWithDelta> </InfoWithDelta>
<InfoWithDelta <InfoWithDelta
prefix="+ " prefix="+ "
@@ -392,7 +400,7 @@ export function Characteristics() {
unit="s" unit="s"
deltaType="lowerIsBetter" deltaType="lowerIsBetter"
> >
{track.dispersion.move * dispersionCoefficient} {track.dispersion.move * dispersionMovingCoefficient}
</InfoWithDelta> </InfoWithDelta>
<InfoWithDelta <InfoWithDelta
decimals={3} decimals={3}
@@ -402,7 +410,7 @@ export function Characteristics() {
unit="°" unit="°"
deltaType="lowerIsBetter" deltaType="lowerIsBetter"
> >
{track.dispersion.traverse * dispersionCoefficient} {track.dispersion.traverse * dispersionMovingCoefficient}
</InfoWithDelta> </InfoWithDelta>
<InfoWithDelta <InfoWithDelta
decimals={3} decimals={3}
@@ -412,7 +420,7 @@ export function Characteristics() {
unit="°" unit="°"
deltaType="lowerIsBetter" deltaType="lowerIsBetter"
> >
{gun.dispersion.traverse * dispersionCoefficient} {gun.dispersion.traverse * dispersionMovingCoefficient}
</InfoWithDelta> </InfoWithDelta>
<InfoWithDelta <InfoWithDelta
decimals={3} decimals={3}
@@ -422,17 +430,17 @@ export function Characteristics() {
unit="m" unit="m"
deltaType="lowerIsBetter" deltaType="lowerIsBetter"
> >
{gun.dispersion.shot * dispersionCoefficient} {gun.dispersion.shot * dispersionMovingCoefficient}
</InfoWithDelta> </InfoWithDelta>
<InfoWithDelta <InfoWithDelta
decimals={3} decimals={1}
prefix="x " prefix="x "
indent indent
name="Gun damaged" name="Gun damaged"
unit="scalar" unit="scalar"
deltaType="lowerIsBetter" deltaType="lowerIsBetter"
> >
{gun.dispersion.damaged * dispersionCoefficient} {gun.dispersion.damaged * dispersionMovingCoefficient}
</InfoWithDelta> </InfoWithDelta>
<Info name="Gun flexibility" unit="°" /> <Info name="Gun flexibility" unit="°" />
<InfoWithDelta decimals={1} indent name="Depression"> <InfoWithDelta decimals={1} indent name="Depression">
@@ -549,7 +557,7 @@ export function Characteristics() {
<InfoWithDelta name="Fire chance" unit="%" deltaType="lowerIsBetter"> <InfoWithDelta name="Fire chance" unit="%" deltaType="lowerIsBetter">
{Math.round(engine.fireChance * fireChanceCoefficient * 100)} {Math.round(engine.fireChance * fireChanceCoefficient * 100)}
</InfoWithDelta> </InfoWithDelta>
<InfoWithDelta name="View range" unit="m"> <InfoWithDelta name="View range" unit="m" decimals={0}>
{turret.viewRange * viewRangeCoefficient} {turret.viewRange * viewRangeCoefficient}
</InfoWithDelta> </InfoWithDelta>
<Info name="Camouflage" unit="%" /> <Info name="Camouflage" unit="%" />
@@ -590,9 +598,9 @@ export function Characteristics() {
unit="%" unit="%"
decimals={0} decimals={0}
> >
{(crew + provisionCrewBonus) * {(member.type === 'commander'
(member.type === 'commander' ? 1 : 1.1) * ? commanderMastery
100} : crewMemberMastery) * 100}
</InfoWithDelta> </InfoWithDelta>
{member.substitute && ( {member.substitute && (
<Info <Info
+3
View File
@@ -0,0 +1,3 @@
export function degressiveStat(crew: number) {
return 0.875 / (0.00375 * 100 * crew + 0.5) - 1;
}
+3
View File
@@ -0,0 +1,3 @@
export function progressiveStat(crew: number) {
return (0.00375 * 100 * crew + 0.5) / 0.875 - 1;
}
+2 -2
View File
@@ -67,7 +67,7 @@ interface TankopediaTemporary {
equipmentMatrix: EquipmentMatrix; equipmentMatrix: EquipmentMatrix;
mode: TankopediaMode; mode: TankopediaMode;
consumables: number[]; consumables: number[];
crew: number; crewMastery: number;
provisions: number[]; provisions: number[];
camouflage: boolean; camouflage: boolean;
cooldownBooster: number; cooldownBooster: number;
@@ -176,7 +176,7 @@ export const useTankopediaTemporary = create<TankopediaTemporary>()(
[0, 0, 0], [0, 0, 0],
[0, 0, 0], [0, 0, 0],
], ],
crew: 0.75, crewMastery: 0.75,
mode: 'model', mode: 'model',
consumables: [], consumables: [],
provisions: [], provisions: [],