The wheel rotation row is a KPI adjustment
VehicleParams.wheelsRotationSpeed reads a KPI adjustment when isWheeledVehicle or isWheeledOnSpotRotation is true; otherwise it returns None. It does not calculate an angular speed from the chassis. The KPI accumulator's getFactor converts a multiplicative aggregate to (coefficient - 1) * 100, and returns zero for a missing entry.
The crew skill definition declares this KPI as multiplicative. A supplied aggregate coefficient 1.05 therefore gives approximately 5 as a percentage adjustment, rather than 1.05 or five degrees per second. Actual KPI aggregation and effective crew level remain separate inputs; the declaration does not promise that every configured vehicle receives that adjustment.
The extra mobility-row builder includes this key, but can omit it when its comparison vehicle has a present, all-zero axle steering-angle list. The hangar generator also omits zero-valued extra rows and sends admitted values through the KPI formatting path. An eligible zero value, an unavailable None and an omitted row are different contracts; row absence alone does not identify vehicle capability.
Selected original accumulator, property and steering-filter functions were checked with inert coefficients and descriptor records. These checks establish value representation and the selected gates, not full crew/provider construction, native formatting, rendered rows, actual wheel motion or server turning behavior.
Rebuild and attribute each contribution once
Direct modifier application can accumulate: adding five to a duration of ten twice gives fifteen, then twenty. Reconstruct from the intended baseline and apply each contribution once. A mechanic origin can be absent; its getter returning None does not make the current object an independent origin.
Some progression damage is already in rebuilt shot descriptors; adding its KPI metadata again doubles the contribution. Equipment can also compensate for existing descriptor factors rather than stack independently. For example, base view range 400 with an existing 1.10 factor and a binocular target factor of 1.25 gives 500, not 550. A camouflage net similarly adds only its contribution beyond a competing static term.
Calculation aspect matters: a default/stationary characteristic can include a stationary-equipment projection without establishing that live equipment is active. Keep the configured aspect separate from replicated on/off state. Compare complete hypothetical loadouts instead of toggling independent booleans when slot compatibility, directives, progression or crew interact.
The basic-shell baseline is the first configured shot
getBasicShell returns vehicleDescriptor.gun.shots[0].shell. This baseline follows descriptor order; it does not select by the active shot index, ammunition stock or price. ShellParams.isBasic compares the queried shell's compact descriptor with that first shell's compact descriptor, rather than identifying the currently selected shot.
The named shellComparator builds parameters for the queried shell and that first-shell baseline using the same vehicle context. Without vehicle context it returns no comparator. The shared shell-tooltip path uses it, but other comparison strategies have their own baselines. Keep this contract separate from a configured vehicle calculation that deliberately changes activeGunShotIndex.
With synthetic shell IDs 10 and 20 and active index one, the selected helper/comparator checks still used 10 as the baseline for queried shell 20. Reordering the supplied shot list changed the baseline. Parameter construction was replaced by recording adapters; actual shell values, ammunition state, full providers, native code and tooltip rendering were not exercised.
Additional shell rows compare values before formatting
The current shell-switcher additional-row selector in getMechanicParameters compares each mapped parameter's value from the producer's nonempty list of state/parameter dictionaries. Its validator uses Python inequality against the first dictionary's value; it does not compare formatted text, use a numeric tolerance or inspect the state labels attached to those dictionaries.
The tank-setup shell provider combines the base parameter list with these selected additional keys. The specification model builder then formats each state's value and publishes the mechanic/state separately. Selection and display are different stages: an admitted row does not promise different displayed strings, and an omitted additional row does not establish that the mechanic is absent or inactive. Mechanics without an entry in this additional-row mapping produce no additional keys through this helper.
Selected original selector and model-builder functions were checked with inert parameter maps, model records and a deliberately constant formatting adapter. Unequal supplied values admitted a row even when that adapter emitted identical text; equal values did not. This checks the stage boundary, not the actual native formatter, supported shell configurations, rendering or live mechanic behavior.
Garage angle rows are display projections
VehicleParams converts configured yaw limits to absolute degree magnitudes. It routes them to the turret or gun row according to its turret-presence test; yaw hull aiming separately makes the gun row (0, 0). These rows do not preserve the descriptor's signed yaw bounds, and an unavailable row is distinct from a zero pair.
The pitch row converts its selected bounds to degrees, negates them and sorts the result. Without pitch hull aiming, it starts from the descriptor's absolute bounds. With pitch hull aiming, it uses wheel-correction bounds alone for a static-pitch gun; otherwise it combines the minimum lower pitch-table value and maximum upper value with their respective corrections. This is a configured projection, not a query at a particular turret yaw.
The Armor Inspector's drag path instead clamps yaw against the signed descriptor limits and obtains pitch bounds for the current turret yaw and mounting offsets through calcPitchLimitsFromDesc. Preserve that query boundary when implementing aiming constraints; do not use a garage display pair as a signed clamp interval.
Selected original garage getters were checked with inert descriptors and synthetic radian values. They confirmed absolute yaw magnitudes, sign-flipped sorted pitch output, hull-aiming branches and unavailable-row gates. Actual descriptor loading, yaw-dependent native calculation, model movement, rendering and live aiming were not exercised.