Legacy price tuples can omit a paid currency
ItemsPrices stores currency dictionaries, but indexed lookup, get() and items() project each price to a legacy credits/gold pair. A stored example such as {'crystal': 5} therefore appears as (0, 0) through those APIs. That projection is not evidence that the item is free or that no price was supplied.
Source excerpt from its tuple adapter:
@staticmethod
def _tuplePrice(priceInfo):
return (priceInfo.get('credits', 0), priceInfo.get('gold', 0))
Use getPrices(descriptor) or tryGetPrice(descriptor) when you need the stored dictionary, or getPrice(descriptor, currency) for one named amount. The shop quote provider builds Money from the stored dictionary when the item is present, preserving its supported currency fields. Resolve that provider only in its synchronized owning context and keep its missing-item policy; do not substitute a legacy tuple for a current full quote.
Full-price accessors return borrowed dictionaries. ItemsPrices.copy() creates another outer collection but reuses those inner dictionaries. Keep client shop data read-only, or copy the price dictionary before independent local edits. Use the Money presence checks to distinguish omitted fields from explicit zero; displaying a price still does not establish purchase eligibility or a completed transaction.
These are version-sensitive Python storage and adapter contracts verified against current public source, not a live shop or wallet test.
Live state
A vehicle entity reflects the current world object, not permanent configuration. Appearance may initialize after entity creation, and respawn modes can replace the entity while retaining the participant.
Common mistakes
- Using arena vehicle ID as though it were stable across battles.
- Reading inventory-only properties from a live entity.
- Persisting dropdown indexes instead of item identity.
- Keeping entity references after destruction, replay seek, or respawn.
- Assuming the configuration determines server-authoritative live state.
Verification trail
Follow conversion helpers between compact descriptors, item wrappers, arena records, and entity creation. Inspect the result schema separately; matching field names do not prove matching identity.
Preserve runtime selection on calculation copies
Version-sensitive: current 2.4 client source. ItemsRequester.getLayoutsVehicleCopy delegates to getVehicleCopy, which reconstructs a vehicle from its compact descriptor. The active shell index is runtime state and resets to zero during reconstruction. Restore activeGunShotIndex after configuring each isolated copy, including copies used for bonus attribution. Preserve the index rather than shell kind: two shots of the same kind can have different damage.
Keep the selected descriptor mode and shell consistent across configured values, baselines, and every marginal-removal step. Refresh copied crew bonuses through the current params_helper.updateCrewBonus path after loadout changes. Do not mutate the inventory descriptor to inspect another shell or mode.
Capture numeric results while each removal step is valid instead of retaining partially stripped vehicles for later reuse. A parameter calculator may reuse factors across shell indexes within one unchanged configuration; restore the original index afterward and discard the calculator when crew, equipment, or mode changes.
Progression identity and configuration caches
Conventional field modifications and vehicle skill trees have different choice contracts. Check the current progression object's isVehSkillTree() before presenting paired field modifications. Use technical node identity for attribution: localized names are presentation labels and distinct upgrades may share the same name. Make hypothetical progression edits on a copy and preserve dependency rules.
Cache compatibility against the complete candidate layout and the current vehicle context. Directive eligibility also depends on installed equipment or crew applicability, so equipment and crew changes must invalidate directive results. Keep exact compact item IDs at the Python/JavaScript boundary even when the UI groups equipment variants.
Evaluate all rows sharing the same ordered bonus-removal chain together. Group situational rows by their complete applicable skill-source set; a smaller key can mix scenarios. Cache names and artwork within one build, then release configuration-dependent results when their inputs change.
Configured values and mechanic metadata
VehicleMechanicItem.staticParams resolves reference metadata from vehicles.g_cache.vehicleMechanics by vehicle compact descriptor. It is not recalculated when an isolated loadout changes. Prefer configuration-aware calculations for configured values and identify metadata fallbacks explicitly. Preserve already formatted strings such as percentage text rather than sending them into a numeric formatter.
Module maxRegenHealth, when retained by the current parser, is the automatic-repair HP limit. It is distinct from maximum health, a damage threshold, or regeneration rate; zero is valid. Apply the same applicable strength factors as maximum health.
ShellParams.penetrationLoss converts piercingPowerLossFactorByDistance by multiplying by ten for post-impact HEAT jet loss per 100 mm. Flight-distance penetration uses the separate computePiercingPowerAtDist calculation. Trace the shell-kind branch and units before labeling a loss row; a shared field name does not make these presentation quantities interchangeable.
Configured calculation ownership
Fresh GUI vehicles can still share crew objects and descriptor templates, retain action-capable inventory identity, and compare equal by type despite different configuration. Getter units and first-match versus active-shot providers also differ. Use the dedicated Vehicle Calculation Contracts reference before building hypothetical setups or caches; it covers passive-input limits, copy-on-write, units, modifier reconstruction and attribution.