feat: implement most equalizer

This commit is contained in:
TrèsAbhi committed 2026-04-03 12:08:47 -05:00
1 parent 6835004945
commit 77c516a6eb
10 files changed
+58 -41

No files matched your search

+3
View File
@@ -168,6 +168,7 @@ if (changedTanks.length > 0) {
) )
.map((id) => currentEquipmentDefinitions.equipments[id]); .map((id) => currentEquipmentDefinitions.equipments[id]);
const equalize = false;
const currentCharacteristics = tankCharacteristics( const currentCharacteristics = tankCharacteristics(
{ {
tank: current, tank: current,
@@ -193,6 +194,7 @@ if (changedTanks.length > 0) {
stockTurret: current.turrets[0], stockTurret: current.turrets[0],
track: currentTracks, track: currentTracks,
turret: currentTurret, turret: currentTurret,
equalize,
}, },
{ {
equipmentDefinitions: currentEquipmentDefinitions, equipmentDefinitions: currentEquipmentDefinitions,
@@ -225,6 +227,7 @@ if (changedTanks.length > 0) {
stockTurret: preview.turrets[0], stockTurret: preview.turrets[0],
track: previewTracks, track: previewTracks,
turret: previewTurret, turret: previewTurret,
equalize,
}, },
{ {
equipmentDefinitions: previewEquipmentDefinitions, equipmentDefinitions: previewEquipmentDefinitions,
@@ -59,11 +59,12 @@ export const InfoWithDelta = memo<InfoWithDeltaProps>(
const delta = useDelta(uhWhatDoICallThisVariable); const delta = useDelta(uhWhatDoICallThisVariable);
const protagonistTank = Duel.use((state) => state.protagonist.tank); const protagonistTank = Duel.use((state) => state.protagonist.tank);
const shellIndex = Duel.use((state) => const shellIndex = Duel.use((state) =>
state.protagonist.gun.shells.indexOf(state.protagonist.shell) state.protagonist.gun.shells.indexOf(state.protagonist.shell),
); );
const equipmentMatrix = Duel.use( const equipmentMatrix = Duel.use(
(state) => state.protagonist.equipmentMatrix (state) => state.protagonist.equipmentMatrix,
); );
const equalize = Duel.use((state) => state.equalize);
const others = useMemo(() => { const others = useMemo(() => {
const defaultSkills = createDefaultSkills(skillDefinitions); const defaultSkills = createDefaultSkills(skillDefinitions);
@@ -75,7 +76,7 @@ export const InfoWithDelta = memo<InfoWithDeltaProps>(
tank.class === protagonistTank.class) || tank.class === protagonistTank.class) ||
(relativeAgainst === TankopediaRelativeAgainst.Tier && (relativeAgainst === TankopediaRelativeAgainst.Tier &&
tank.tier === protagonistTank.tier) || tank.tier === protagonistTank.tier) ||
relativeAgainst === TankopediaRelativeAgainst.All relativeAgainst === TankopediaRelativeAgainst.All,
) )
.map((tank) => { .map((tank) => {
const member = tankToDuelMember(tank, provisionDefinitions); const member = tankToDuelMember(tank, provisionDefinitions);
@@ -105,12 +106,13 @@ export const InfoWithDelta = memo<InfoWithDeltaProps>(
track: member.track, track: member.track,
turret: member.turret, turret: member.turret,
assaultDistance: member.assaultDistance, assaultDistance: member.assaultDistance,
equalize,
}, },
{ {
equipmentDefinitions, equipmentDefinitions,
provisionDefinitions, provisionDefinitions,
tankModelDefinition: modelDefinitions.models[tank.id], tankModelDefinition: modelDefinitions.models[tank.id],
} },
); );
}) })
.filter((tank) => { .filter((tank) => {
@@ -123,7 +125,7 @@ export const InfoWithDelta = memo<InfoWithDeltaProps>(
return othersValue !== undefined; return othersValue !== undefined;
}) as TankCharacteristics[]; }) as TankCharacteristics[];
}, [relativeAgainst, shellIndex, equipmentMatrix]); }, [relativeAgainst, shellIndex, equipmentMatrix, equalize]);
const betterTanks = others.filter((tank) => { const betterTanks = others.filter((tank) => {
const othersValue = const othersValue =
typeof props.value === "function" typeof props.value === "function"
@@ -183,5 +185,5 @@ export const InfoWithDelta = memo<InfoWithDeltaProps>(
(a, b) => (a, b) =>
(typeof a.value === "function" ? a.value(a.stats) : a.stats[a.value]) === (typeof a.value === "function" ? a.value(a.stats) : a.stats[a.value]) ===
(typeof b.value === "function" ? b.value(b.stats) : b.stats[b.value]) (typeof b.value === "function" ? b.value(b.stats) : b.stats[b.value]),
); );
@@ -31,6 +31,7 @@ export function Characteristics({ skeleton }: MaybeSkeletonComponentProps) {
const equipmentMatrix = Duel.use( const equipmentMatrix = Duel.use(
(state) => state.protagonist.equipmentMatrix, (state) => state.protagonist.equipmentMatrix,
); );
const equalize = Duel.use((state) => state.equalize);
const { tank, turret, gun, engine, track, shell } = Duel.use( const { tank, turret, gun, engine, track, shell } = Duel.use(
(state) => state.protagonist, (state) => state.protagonist,
); );
@@ -62,6 +63,7 @@ export function Characteristics({ skeleton }: MaybeSkeletonComponentProps) {
applyDynamicArmor: false, applyDynamicArmor: false,
applySpallLiner: false, applySpallLiner: false,
assaultDistance, assaultDistance,
equalize,
}, },
{ {
tankModelDefinition, tankModelDefinition,
@@ -16,7 +16,7 @@ import { ConfigurationChildWrapper } from "./ConfigurationChildWrapper";
export function Equalizer() { export function Equalizer() {
const { strings } = useLocale(); const { strings } = useLocale();
const equalize = Duel.use((state) => state.protagonist.equalize); const equalize = Duel.use((state) => state.equalize);
return ( return (
<ConfigurationChildWrapper> <ConfigurationChildWrapper>
@@ -33,7 +33,7 @@ export function Equalizer() {
mt="2" mt="2"
onClick={() => { onClick={() => {
Duel.mutate((draft) => { Duel.mutate((draft) => {
draft.protagonist.equalize = !draft.protagonist.equalize; draft.equalize = !draft.equalize;
}); });
}} }}
width="100%" width="100%"
@@ -47,6 +47,7 @@ export function tankCharacteristics(
applyDynamicArmor, applyDynamicArmor,
applySpallLiner, applySpallLiner,
assaultDistance, assaultDistance,
equalize,
}: { }: {
tank: TankDefinition; tank: TankDefinition;
turret: TurretDefinition; turret: TurretDefinition;
@@ -67,6 +68,7 @@ export function tankCharacteristics(
applyDynamicArmor: boolean; applyDynamicArmor: boolean;
applySpallLiner: boolean; applySpallLiner: boolean;
assaultDistance: number; assaultDistance: number;
equalize: boolean;
}, },
{ {
tankModelDefinition, tankModelDefinition,
@@ -81,6 +83,13 @@ export function tankCharacteristics(
const preset = equipmentDefinitions.presets[tank.equipment_preset]; const preset = equipmentDefinitions.presets[tank.equipment_preset];
const turretModelDefinition = tankModelDefinition.turrets[turret.id]; const turretModelDefinition = tankModelDefinition.turrets[turret.id];
const gunModelDefinition = turretModelDefinition.guns[gun.id]; const gunModelDefinition = turretModelDefinition.guns[gun.id];
const equalizer = (equalize ? tank.equalizer : undefined) ?? {
armor: 1,
damage: 1,
health: 1,
module_health: 1,
penetration: 1,
};
function equipment(id: number) { function equipment(id: number) {
return preset.slots.some((slot, index) => { return preset.slots.some((slot, index) => {
@@ -177,15 +186,17 @@ export function tankCharacteristics(
(coefficient([hasGunRammer, -0.05]) * (coefficient([hasGunRammer, -0.05]) *
coefficient([true, degressiveStat(loaderMastery)])) / coefficient([true, degressiveStat(loaderMastery)])) /
coefficient([hasAdrenaline && gun.gun_type!.$case === "regular", 0.17]); coefficient([hasAdrenaline && gun.gun_type!.$case === "regular", 0.17]);
const penetrationCoefficient = coefficient([ const penetrationCoefficient =
hasCalibratedShells, coefficient([
resolvePenetrationCoefficient(true, shell.type) - 1, hasCalibratedShells,
]); resolvePenetrationCoefficient(true, shell.type) - 1,
const healthCoefficient = coefficient( ]) * equalizer.penetration;
[hasSandbagArmor, 0.03], const healthCoefficient =
[hasEnhancedSandbagArmor, 0.06], coefficient(
[hasImprovedAssembly, 0.04], [hasSandbagArmor, 0.03],
); [hasEnhancedSandbagArmor, 0.06],
[hasImprovedAssembly, 0.04],
) * equalizer.health;
const shellVelocityCoefficient = coefficient( const shellVelocityCoefficient = coefficient(
[hasSupercharger, 0.35], [hasSupercharger, 0.35],
[hasImprovedGunPowder, 0.3], [hasImprovedGunPowder, 0.3],
@@ -345,7 +356,9 @@ export function tankCharacteristics(
const resolvedEnginePower = engine.power * enginePowerCoefficient; const resolvedEnginePower = engine.power * enginePowerCoefficient;
const damageCoefficientWithoutAssault = armorDamageCoefficient; const damageCoefficientWithoutAssault = armorDamageCoefficient;
const damageCoefficient = const damageCoefficient =
damageCoefficientWithoutAssault * assaultDamageCoefficient; damageCoefficientWithoutAssault *
assaultDamageCoefficient *
equalizer.damage;
const dpm = resolveDpm( const dpm = resolveDpm(
gun, gun,
shell, shell,
@@ -654,13 +667,3 @@ export const characteristicsOrder: {
items: ["crewCount"], items: ["crewCount"],
}, },
]; ];
// export const characteristicsStyling: Record<
// TankCharacteristicsKey,
// {
// name: string;
// indent?: boolean;
// condition?: TankCharacteristicsCondition[];
// }
// > = {
// };
@@ -26,6 +26,5 @@ export function tankToDuelMember(
track: tank.tracks.at(-1)!, track: tank.tracks.at(-1)!,
turret, turret,
assaultDistance: (gun.assault_ranges?.ranges[0].distance ?? 0) / 2, assaultDistance: (gun.assault_ranges?.ranges[0].distance ?? 0) / 2,
equalize: false,
} satisfies DuelMember; } satisfies DuelMember;
} }
@@ -45,6 +45,7 @@ export function Page({ locale }: LocaleAcceptorProps) {
function Content() { function Content() {
const members = CompareEphemeral.use((state) => state.members); const members = CompareEphemeral.use((state) => state.members);
const equalize = CompareEphemeral.use((state) => state.equalize);
const [addTankDialogOpen, setAddTankDialogOpen] = useState(false); const [addTankDialogOpen, setAddTankDialogOpen] = useState(false);
const crewSkills = CompareEphemeral.use((state) => state.crewSkills); const crewSkills = CompareEphemeral.use((state) => state.crewSkills);
const { strings } = useLocale(); const { strings } = useLocale();
@@ -61,26 +62,30 @@ function Content() {
stockTurret: thisMember.tank.turrets[0], stockTurret: thisMember.tank.turrets[0],
applyReactiveArmor: members.some( applyReactiveArmor: members.some(
(member) => (member) =>
member.key !== thisMember.key && member.consumables.includes(33) member.key !== thisMember.key &&
member.consumables.includes(33),
), ),
applyDynamicArmor: members.some( applyDynamicArmor: members.some(
(member) => (member) =>
member.key !== thisMember.key && member.consumables.includes(73) member.key !== thisMember.key &&
member.consumables.includes(73),
), ),
applySpallLiner: members.some( applySpallLiner: members.some(
(member) => (member) =>
member.key !== thisMember.key && member.provisions.includes(101) member.key !== thisMember.key &&
member.provisions.includes(101),
), ),
assaultDistance: thisMember.assaultDistance, assaultDistance: thisMember.assaultDistance,
equalize,
}, },
{ {
equipmentDefinitions: equipmentDefinitions, equipmentDefinitions: equipmentDefinitions,
provisionDefinitions: provisionDefinitions, provisionDefinitions: provisionDefinitions,
tankModelDefinition: modelDefinitions.models[thisMember.tank.id], tankModelDefinition: modelDefinitions.models[thisMember.tank.id],
} },
) ),
), ),
[members, crewSkills] [members, crewSkills],
); );
useEffect(() => { useEffect(() => {
@@ -112,7 +117,7 @@ function Content() {
window.history.replaceState( window.history.replaceState(
{}, {},
"", "",
`${window.location.pathname}?${search.toString()}` `${window.location.pathname}?${search.toString()}`,
); );
}, [members]); }, [members]);
@@ -33,7 +33,7 @@ export const getStaticPaths = (async () => {
parentPaths.map((parent) => ({ parentPaths.map((parent) => ({
params: { ...parent.params, slug: tank.slug }, params: { ...parent.params, slug: tank.slug },
props: { ...parent.props, id: tank.id }, props: { ...parent.props, id: tank.id },
})) })),
) )
.flat(); .flat();
}) satisfies GetStaticPaths; }) satisfies GetStaticPaths;
@@ -86,8 +86,9 @@ const characteristics = tankCharacteristics(
stockTrack: tank.tracks[0], stockTrack: tank.tracks[0],
track: tank.tracks.at(-1)!, track: tank.tracks.at(-1)!,
assaultDistance: 0, assaultDistance: 0,
equalize: false,
}, },
{ equipmentDefinitions, provisionDefinitions, tankModelDefinition } { equipmentDefinitions, provisionDefinitions, tankModelDefinition },
); );
const description = literals( const description = literals(
strings.website.tools.tankopedia.tank_seo_description, strings.website.tools.tankopedia.tank_seo_description,
@@ -98,7 +99,7 @@ const description = literals(
penetration: Math.round(characteristics.penetration).toLocaleString(locale), penetration: Math.round(characteristics.penetration).toLocaleString(locale),
speed: Math.round(characteristics.speedForwards).toLocaleString(locale), speed: Math.round(characteristics.speedForwards).toLocaleString(locale),
health: Math.round(characteristics.health).toLocaleString(locale), health: Math.round(characteristics.health).toLocaleString(locale),
} },
); );
const guideUrl = `${assertSecret(import.meta.env.PUBLIC_GUIDE_WEBSITE)}/${tank.slug}`; const guideUrl = `${assertSecret(import.meta.env.PUBLIC_GUIDE_WEBSITE)}/${tank.slug}`;
@@ -7,6 +7,7 @@ export interface CompareMember extends DuelMember {
export interface CompareEphemeral { export interface CompareEphemeral {
crewSkills: Record<string, number>; crewSkills: Record<string, number>;
equalize: boolean;
members: CompareMember[]; members: CompareMember[];
sorting?: { sorting?: {
direction: "ascending" | "descending"; direction: "ascending" | "descending";
@@ -19,5 +20,6 @@ export const CompareEphemeral = new Varuna<
Record<string, number> Record<string, number>
>((crewSkills) => ({ >((crewSkills) => ({
crewSkills, crewSkills,
equalize: false,
members: [], members: [],
})); }));
+2 -2
View File
@@ -36,12 +36,12 @@ export interface DuelMember {
camouflage: boolean; camouflage: boolean;
cooldownBooster: number; cooldownBooster: number;
assaultDistance: number; assaultDistance: number;
equalize: boolean;
} }
export interface DuelStore { export interface DuelStore {
protagonist: DuelMember; protagonist: DuelMember;
antagonist: DuelMember; antagonist: DuelMember;
equalize: boolean;
} }
const provisionDefinitions = await awaitableProvisionDefinitions; const provisionDefinitions = await awaitableProvisionDefinitions;
@@ -52,5 +52,5 @@ export const Duel = new Varuna<
>(({ tank, model }) => { >(({ tank, model }) => {
const protagonist = tankToDuelMember(tank, provisionDefinitions); const protagonist = tankToDuelMember(tank, provisionDefinitions);
return { protagonist, antagonist: protagonist, model }; return { protagonist, antagonist: protagonist, model, equalize: false };
}); });