Use explicit configuration cache identity
GUI vehicle equality/hash use type identity, not inventory identity, selected shot or complete configuration. A hypothetical copy can compare equal to its source and replace the same dictionary entry. Explicitly key calculations by the setup, mode, shell/shot, crew and relevant modifier revision.
A shell item ID can also stay unchanged while a local gun-shot override changes damage, velocity, angles or range. Shell-card providers can select the first matching shot or an envelope across matches, whereas vehicle DPM reads the active shot. Do not silently combine first-match damage with active-shot rate and call it one coherent configuration.
Units belong to the provider
| Value | Boundary | Consequence |
|---|---|---|
| Engine power | Component XML is converted to watts; VehicleParams exposes horsepower. | Select the formula matching the getter, not the component field name. |
| reloadTime | Descriptor gun value is a duration; VehicleParams/gun parameter can be shots per minute. | Use the seconds interface when available; unavailable magazine durations are not zero. |
| Magazine rate | Reload and intra-clip intervals both enter the cycle. | Four shots, 30-second reload and 2-second gaps give 4×60/(30+3×2). |
| Shell speed/gravity | Shot parsing applies projectileSpeedFactor; display reverses the speed conversion. | Do not mix scaled descriptor speed with unscaled gravity/display units. |
| Angles | Normalization is radians; ricochet data is a cosine. | Convert deliberately and clamp cosine before acos. |
| Module damage | A descriptor damage-pair mean. | Not hit probability or a confirmed battle outcome. |
Accuracy rows preserve provider-specific calculation stages
The shared gun reader stores atan(shotDispersionRadius / 100.0) as shotDispersionAngle. The module aggregator rounds each descriptor's angle times 100 to two decimal places, then collects minimum/maximum values. The ordinary GunParams.dispertionRadius branch returns (None, minimumRawValue). That module minimum is not a complete configured-vehicle baseline.
The vehicle dispersion helper instead applies the configured gun's miscellaneous and supplied dispersion factors to its angle. VehicleParams rounds each component times 100 to four decimal places, then applies its skill-factor combiner. These ordinary paths do not apply a tangent conversion; some special module branches do, using their own selected angle. Preserve the selected provider and branch rather than replacing all accuracy getters with one geometric formula.
The module tooltip consumes dispertionRadius, while the vehicle tooltip can select shotDispersionAngle. Both use the shared list formatter; common formatting does not make their input provenance or rounding stages interchangeable.
Selected original getter/helper checks used inert descriptors, supplied raw maps and skill factors, and the original decimal-rounding helper under host Python 3. A synthetic radius number 0.456789 converted through a numeric atan adapter gave a vehicle pre-skill value 0.4568; a supplied ordinary module map returned its minimum 0.46. This isolates calculation stages, not actual XML parsing, full aggregation, supported tuning, visible text differences, native aiming-circle geometry or execution in the client Python 2 runtime.
Configured health has a baseline and later rounding stages
The vehicle descriptor rebuild assembles health from hull and turret contributions. Rounded positive miscellaneous overrides replace the corresponding contribution; otherwise it uses hull health and the sum of health from its installed turret entries. It saves that sum as defaultMaxHealth, then applies the VEHICLE_HEALTH battle modifier. The saved baseline can already include overrides; it is not automatically an untouched stock value.
Only when healthFactor != 1.0 does the rebuild multiply the modified result and apply int(ceilTo(..., VEHICLE_HEALTH_DECIMALS)). The constant is currently one, but ceilTo uses 10 ** decimals as its scale. This selects a tens scale, not one fractional decimal place. Its epsilon and intermediate decimal rounding are part of the contract; preserve the helper rather than replacing it with a blanket mathematical ceiling.
A selected-source check with assembled baseline 1000 and a recording modifier that adds 25 produced maximum 1025 with factor one. Factor 1.1 instead took 1127.5 through the rounding path to 1130, while the saved baseline stayed 1000. These synthetic values illustrate stage order, not actual vehicle tuning or server health.
VehicleParams.maxHealth forwards the descriptor's configured maximum. It does not itself redo the calculation or report a battle entity's current hit points, module durability or a repair amount. The check selected the health region and original numeric helpers with inert descriptor fields and a modifier adapter; complete rebuilds, modifier collection, live configurations, native/client behavior and the client Python 2 runtime were not exercised.
Rocket garage rows use a declared KPI projection
The rocket descriptor reader stores modifiers and client KPI entries separately. The garage rocket helpers aggregate the KPI entries, then read getCoeff: this is the raw coefficient, with one for a missing entry. It is distinct from getFactor, which expresses a multiplicative KPI as a percentage change. The helper does not derive these coefficients from the modifier list.
VehicleParams multiplies its already-computed engine power by the engine KPI coefficient. Its rocket speed row scales its configured forward and reverse speed values by their respective coefficients, rounding the forward result to two decimal places and converting the reverse result to a float. Without the mechanic, these rocket fields are unavailable. A missing KPI entry on an enabled mechanic instead leaves that component unchanged.
The module engine provider uses the same power helper with its separately rounded module horsepower as the base; the engine tooltip selects the normal and rocket power rows together. Preserve the provider's base and stage order. A built-in KPI row and an active-state snapshot assembled from dynamic modifiers are separate calculation paths; applying both to the same baseline can count a contribution twice. Source inspection alone does not establish that their results differ or agree for every supported configuration.
Selected original helpers and vehicle properties, with inert KPI records and supplied rounding, mapped power 100 through multiplicative coefficients 1.2 and 1.5 to approximately 180. Changing only the inert modifier list did not change that projection; removing the KPI entries restored the base. These checks establish input provenance, not actual configuration loading, current activation, achieved speed, native boost physics, rendering or execution in the client Python 2 runtime.