From e1bc11644d702bedbf5062ddbf623b64948087fc Mon Sep 17 00:00:00 2001 From: tresabhi <43380238+tresabhi@users.noreply.github.com> Date: Sun, 11 Oct 2026 01:36:34 -0500 Subject: [PATCH] hull traverse speeds --- packages/i18n/strings/en.json | 5 + .../website/src/config/characteristics.ts | 108 ++++++++--------- .../src/config/characteristicsGroups.ts | 10 +- .../website/src/hooks/useCharacteristics.ts | 24 ++-- packages/website/src/stores/tankopedia.ts | 9 +- .../src/tankopedia/hullTraverseSpeed.ts | 109 ++++++++++++++++++ .../src/tankopedia/rawTerrainCoefficient.ts | 12 +- packages/website/src/tankopedia/tankClass.ts | 9 ++ 8 files changed, 208 insertions(+), 78 deletions(-) create mode 100644 packages/website/src/tankopedia/hullTraverseSpeed.ts create mode 100644 packages/website/src/tankopedia/tankClass.ts diff --git a/packages/i18n/strings/en.json b/packages/i18n/strings/en.json index 7d53b3868..b097570ba 100644 --- a/packages/i18n/strings/en.json +++ b/packages/i18n/strings/en.json @@ -66,6 +66,7 @@ "azimuth_right": "Azimuth right", "speed_forward": "Speed forward", "speed_backward": "Speed backward", + "speeds": "Speeds", "engine_power": "Engine power", "brake_force": "Brake force", "weight": "Weight", @@ -79,6 +80,10 @@ "medium_terrain_coefficient_raw": "Medium", "soft_terrain_coefficient_raw": "Soft", "hull_traverse_speed": "Hull traverse speed", + "hull_traverse_speeds": "Hull traverse speeds", + "hull_traverse_speed_hard": "Hard", + "hull_traverse_speed_medium": "Medium", + "hull_traverse_speed_soft": "Soft", "turret_traverse_speed": "Turret traverse speed", "gun_traverse_speed": "Gun traverse speed", "health": "Health", diff --git a/packages/website/src/config/characteristics.ts b/packages/website/src/config/characteristics.ts index be7808709..b01d100a9 100644 --- a/packages/website/src/config/characteristics.ts +++ b/packages/website/src/config/characteristics.ts @@ -5,54 +5,12 @@ import { unionBoundingBox, } from "@blitzkit/core"; import { ShellType, TankClass } from "@blitzkit/protos"; +import { hullTraverseSpeed } from "../tankopedia/hullTraverseSpeed"; +import { rawTerrainCoefficient } from "../tankopedia/rawTerrainCoefficient"; import { CharacteristicType, type Characteristic, - type CharacteristicContext, } from "../types/characteristics"; -import { rawTerrainCoefficient } from "../tankopedia/rawTerrainCoefficient"; - -function intraClipCoefficient({ - script, -}: Pick) { - let coefficient = 1; - - script("protagonist", "NewAutoloader", (effect) => { - coefficient *= effect.clipReloadTimeFactor; - }); - - return coefficient; -} - -function aimTimeCoefficient({ script }: Pick) { - let coefficient = 1; - - script("protagonist", "miscAttrs/gunAimingTimeFactor", (effect) => { - coefficient += effect.bonusValues!.percent / 100; - }); - - script("protagonist", "FireControlSystem", (effect) => { - coefficient *= effect.aimingTimeFactor; - }); - - return coefficient; -} - -function dispersionCoefficient({ - script, -}: Pick) { - let coefficient = 1; - - script("protagonist", "miscAttrs/shotDispersionAngleFactor", (effect) => { - coefficient += effect.bonusValues!.percent / 100; - }); - - script("protagonist", "FireControlSystem", (effect) => { - coefficient *= effect.shotDispersionAngleFactor; - }); - - return coefficient; -} export const characteristics = { gun_type: { @@ -490,10 +448,20 @@ export const characteristics = { dispersion: { type: CharacteristicType.Number, compute({ gun, degressive, script }) { + let coefficient = 1; + + script("protagonist", "miscAttrs/shotDispersionAngleFactor", (effect) => { + coefficient += effect.bonusValues!.percent / 100; + }); + + script("protagonist", "FireControlSystem", (effect) => { + coefficient *= effect.shotDispersionAngleFactor; + }); + return ( gun.dispersion_base * degressive("gunner") * - dispersionCoefficient({ script }) + coefficient ); }, }, @@ -527,8 +495,9 @@ export const characteristics = { gun_frontal_depression: { type: CharacteristicType.Number, should_render({ state, turret, gun }) { - return state.model.turrets[turret.id].guns[gun.id].pitch!.front !== - undefined; + return ( + state.model.turrets[turret.id].guns[gun.id].pitch!.front !== undefined + ); }, compute({ state, turret, gun }) { return state.model.turrets[turret.id].guns[gun.id].pitch!.front!.max; @@ -538,8 +507,9 @@ export const characteristics = { gun_frontal_elevation: { type: CharacteristicType.Number, should_render({ state, turret, gun }) { - return state.model.turrets[turret.id].guns[gun.id].pitch!.front !== - undefined; + return ( + state.model.turrets[turret.id].guns[gun.id].pitch!.front !== undefined + ); }, compute({ state, turret, gun }) { return -state.model.turrets[turret.id].guns[gun.id].pitch!.front!.min; @@ -549,8 +519,9 @@ export const characteristics = { gun_rear_depression: { type: CharacteristicType.Number, should_render({ state, turret, gun }) { - return state.model.turrets[turret.id].guns[gun.id].pitch!.back !== - undefined; + return ( + state.model.turrets[turret.id].guns[gun.id].pitch!.back !== undefined + ); }, compute({ state, turret, gun }) { return state.model.turrets[turret.id].guns[gun.id].pitch!.back!.max; @@ -560,8 +531,9 @@ export const characteristics = { gun_rear_elevation: { type: CharacteristicType.Number, should_render({ state, turret, gun }) { - return state.model.turrets[turret.id].guns[gun.id].pitch!.back !== - undefined; + return ( + state.model.turrets[turret.id].guns[gun.id].pitch!.back !== undefined + ); }, compute({ state, turret, gun }) { return -state.model.turrets[turret.id].guns[gun.id].pitch!.back!.min; @@ -690,6 +662,36 @@ export const characteristics = { }, }, + hull_traverse_speed_hard: { + type: CharacteristicType.Number, + compute(context): number { + return hullTraverseSpeed( + context.characteristic("hard_terrain_coefficient_raw") as number, + context, + ); + }, + }, + + hull_traverse_speed_medium: { + type: CharacteristicType.Number, + compute(context): number { + return hullTraverseSpeed( + context.characteristic("medium_terrain_coefficient_raw") as number, + context, + ); + }, + }, + + hull_traverse_speed_soft: { + type: CharacteristicType.Number, + compute(context): number { + return hullTraverseSpeed( + context.characteristic("soft_terrain_coefficient_raw") as number, + context, + ); + }, + }, + turret_traverse_speed: { type: CharacteristicType.Number, compute({ turret, progressive }) { diff --git a/packages/website/src/config/characteristicsGroups.ts b/packages/website/src/config/characteristicsGroups.ts index 7dca410f4..3b4647754 100644 --- a/packages/website/src/config/characteristicsGroups.ts +++ b/packages/website/src/config/characteristicsGroups.ts @@ -155,8 +155,9 @@ export const characteristicsGroups: CharacteristicsGroup[] = [ name: "maneuverability", // render: CharacteristicsGroupRender.Statistical, order: [ - { name: "speed_forward", units: "km_h" }, - { name: "speed_backward", units: "km_h" }, + { label: "speeds" }, + { name: "speed_forward", units: "km_h", indent: true }, + { name: "speed_backward", units: "km_h", indent: true }, { name: "weight", decimals: 1, units: "t" }, { name: "engine_power", units: "bhp" }, @@ -173,7 +174,10 @@ export const characteristicsGroups: CharacteristicsGroup[] = [ { name: "medium_terrain_coefficient_raw", decimals: 2, indent: true }, { name: "soft_terrain_coefficient_raw", decimals: 2, indent: true }, - { name: "hull_traverse_speed", units: "deg_s" }, + { label: "hull_traverse_speeds" }, + { name: "hull_traverse_speed_hard", units: "deg_s", indent: true }, + { name: "hull_traverse_speed_medium", units: "deg_s", indent: true }, + { name: "hull_traverse_speed_soft", units: "deg_s", indent: true }, { name: "turret_traverse_speed", units: "deg_s" }, { name: "gun_traverse_speed", units: "deg_s" }, diff --git a/packages/website/src/hooks/useCharacteristics.ts b/packages/website/src/hooks/useCharacteristics.ts index 35dad845f..14f45a901 100644 --- a/packages/website/src/hooks/useCharacteristics.ts +++ b/packages/website/src/hooks/useCharacteristics.ts @@ -91,19 +91,17 @@ export function computeCharacteristics( } } - const protagonist = states.protagonist; - const protagonistEquipmentPreset = equipment.presets[tank.equipment_preset]; - const hasImprovedVentilation = Object.entries(protagonist.equipment).some( - ([index, choice]) => - protagonistEquipmentPreset.slots[Number(index)]?.options[choice] === 102, - ); - const provisionCrewBonus = - protagonist.provisions.reduce( - (total, id) => - total + - (scripts.provisions[id]?.bonusValues?.crewLevelIncrease ?? 0) / 100, - 0, - ) + (hasImprovedVentilation ? 0.08 : 0); + let provisionCrewBonus = 0; + script("protagonist", "Stimulator", (effect) => { + provisionCrewBonus += (effect.bonusValues?.crewLevelIncrease ?? 0) / 100; + }); + + script("protagonist", "StaticAdditiveDevice", (effect) => { + if (effect.attribute !== "miscAttrs/crewLevelIncrease") return; + + provisionCrewBonus += (effect.bonusValues?.value ?? 0) / 100; + }); + const commanderMastery = 1 + provisionCrewBonus; const crewMastery = { commander: commanderMastery, diff --git a/packages/website/src/stores/tankopedia.ts b/packages/website/src/stores/tankopedia.ts index e31d55298..f255c7712 100644 --- a/packages/website/src/stores/tankopedia.ts +++ b/packages/website/src/stores/tankopedia.ts @@ -79,6 +79,7 @@ export enum TankopediaRelativeAgainst { export interface TankEnvironment { equalize: boolean; distance: number; + skills: Record; } interface Tankopedia { @@ -91,7 +92,6 @@ interface Tankopedia { modelRequested: boolean; shot?: Shot; - skills: Record; relativeAgainst: TankopediaRelativeAgainst; editStatic: boolean; highlightArmor?: { @@ -127,13 +127,16 @@ export const Tankopedia = new Soapstone( antagonist: createTankState(tank), model: models.models[tank.id], - environment: { equalize: false, distance: 0 }, + environment: { + equalize: false, + distance: 0, + skills: createDefaultSkills(skills), + }, modelRequested: false, relativeAgainst: TankopediaRelativeAgainst.Class, editStatic: false, - skills: createDefaultSkills(skills), xpMultiplier: 1, requestedDisplay: TankopediaDisplay.Model, display: TankopediaDisplay.Model, diff --git a/packages/website/src/tankopedia/hullTraverseSpeed.ts b/packages/website/src/tankopedia/hullTraverseSpeed.ts new file mode 100644 index 000000000..c3445fd93 --- /dev/null +++ b/packages/website/src/tankopedia/hullTraverseSpeed.ts @@ -0,0 +1,109 @@ +import type { + CharacteristicContext, + CharacteristicScript, +} from "../types/characteristics"; +import { tankClassToBlitzTankClass } from "./tankClass"; + +function hullTraverseEnginePowerCoefficient( + tank: TankDefinition, + script: CharacteristicScript, +) { + let coefficient = 1; + + const enginePowerPercentage = tankClassToBlitzTankClass[tank.class]; + + script("protagonist", "RecycledEngine", (effect) => { + coefficient += + (effect.enginePowerPercentageBonus?.[enginePowerPercentage] ?? 0) / 100; + }); + + script("protagonist", "Afterburning", (effect) => { + coefficient += (effect.bonusValues?.enginePowerIncrease ?? 0) / 100; + }); + + script("protagonist", "ImprovedAfterburning", (effect) => { + coefficient += (effect.enginePowerIncrease ?? 1) - 1; + }); + + script("protagonist", "Fuel", (effect) => { + coefficient += (effect.bonusValues?.enginePowerIncrease ?? 0) / 100; + }); + + script("protagonist", "GearOil", (effect) => { + coefficient += (effect.bonusValues?.enginePowerIncrease ?? 0) / 100; + }); + + script("protagonist", "AttributesModifier", (effect) => { + coefficient *= effect.attributes?.enginePowerFactor ?? 1; + }); + + return coefficient; +} + +function hullTraverseRotationCoefficient( + driverCoefficient: number, + virtuosoSkill: number, + script: CharacteristicScript, +) { + let coefficient = 1 + virtuosoSkill / 100; + let rotationSpeedFactor = 1; + + script("protagonist", "StaticPhysicsAttrsModifier", (effect) => { + coefficient += (effect.bonusPercentage?.rotationSpeedFactor ?? 0) / 100; + }); + + script("protagonist", "ImprovedAfterburning", (effect) => { + rotationSpeedFactor *= effect.rotationSpeedFactor ?? 1; + }); + + script("protagonist", "AfterburnerAbility", (effect) => { + rotationSpeedFactor *= effect.bonusValues?.rotationSpeedFactor ?? 1; + }); + + return driverCoefficient * coefficient * rotationSpeedFactor; +} + +export function hullTraverseSpeed( + rawTerrainResistance: number, + context: CharacteristicContext, +) { + const { + tank, + engine, + track, + turret, + gun, + progressive, + script, + environment, + } = context; + const terrainResistance = track.resistance_hard; + const stockEngine = tank.engines[0]; + const stockTrack = tank.tracks[0]; + const stockTurret = tank.turrets[0]; + const stockGun = stockTurret.guns[0]; + const stockWeight = + tank.weight + + stockEngine.weight + + stockTrack.weight + + stockTurret.weight + + stockGun.weight; + const weight = + tank.weight + engine.weight + track.weight + turret.weight + gun.weight; + const enginePower = + (engine.power * hullTraverseEnginePowerCoefficient(tank, script)) / + stockEngine.power; + const rotationSpeed = hullTraverseRotationCoefficient( + progressive("driver"), + environment.skills.virtuoso ?? 0, + script, + ); + + return ( + enginePower * + track.traverse_speed * + rotationSpeed * + (terrainResistance / rawTerrainResistance) * + (stockWeight / weight) + ); +} diff --git a/packages/website/src/tankopedia/rawTerrainCoefficient.ts b/packages/website/src/tankopedia/rawTerrainCoefficient.ts index 55074caa4..2954b0fc0 100644 --- a/packages/website/src/tankopedia/rawTerrainCoefficient.ts +++ b/packages/website/src/tankopedia/rawTerrainCoefficient.ts @@ -6,17 +6,17 @@ export function rawTerrainCoefficient( driverCoefficient: number, script: CharacteristicScript, ) { - let passabilityIncrease = 0; + let passabilityFactor = 1; let terrainResistanceFactor = 1; script("protagonist", "Grousers", (effect) => { - passabilityIncrease += - (effect.bonusPercentage?.[passabilityAttribute] ?? 0) / 100; + passabilityFactor *= + 1 - (effect.bonusPercentage?.[passabilityAttribute] ?? 0) / 100; }); script("protagonist", "AttributesModifier", (effect) => { - passabilityIncrease += - (effect.attributes?.[passabilityAttribute] ?? 0) / 100; + passabilityFactor *= + 1 - (effect.attributes?.[passabilityAttribute] ?? 0) / 100; }); script("protagonist", "AfterburnerAbility", (effect) => { @@ -25,7 +25,7 @@ export function rawTerrainCoefficient( return ( resistance * - (1 - passabilityIncrease) * + passabilityFactor * terrainResistanceFactor * driverCoefficient ); diff --git a/packages/website/src/tankopedia/tankClass.ts b/packages/website/src/tankopedia/tankClass.ts new file mode 100644 index 000000000..2d9e94a72 --- /dev/null +++ b/packages/website/src/tankopedia/tankClass.ts @@ -0,0 +1,9 @@ +import type { BlitzTankClass } from "@blitzkit/core"; +import { TankClass } from "@blitzkit/protos"; + +export const tankClassToBlitzTankClass: Record = { + [TankClass.TANK_CLASS_LIGHT]: "lightTank", + [TankClass.TANK_CLASS_MEDIUM]: "mediumTank", + [TankClass.TANK_CLASS_HEAVY]: "heavyTank", + [TankClass.TANK_CLASS_TANK_DESTROYER]: "AT-SPG", +};