Example #1
0
 def __loadVehicleCompound(self, vehicleDescr):
     vehicleName = vehicleDescr.name
     layout = self.__layouts.get(vehicleName, self.__layouts['default'])
     if vehicleName in self.__cachedCompound:
         _logger.debug('Loaded vehicle compound of "%s" from cache',
                       vehicleName)
         BigWorld.buildBlueprint(self.__cachedCompound[vehicleName],
                                 _BLUEPRINT_BG_TEXTURE, layout.projections)
         self.__inProgress = None
         return
     elif vehicleName in self.__pendingCompound:
         _logger.debug('Vehicle compound of "%s" is loading at the moment.',
                       vehicleName)
         return
     else:
         _logger.debug('Loading vehicle compound of "%s".', vehicleName)
         self.__pendingCompound.add(vehicleName)
         resources = (ma.prepareCompoundAssembler(vehicleDescr,
                                                  ModelsSetParams(
                                                      '', 'undamaged', []),
                                                  BigWorld.camera().spaceID,
                                                  lodIdx=layout.lodIdx,
                                                  skipMaterials=True), )
         BigWorld.loadResourceListBG(
             resources,
             makeCallbackWeak(self.__onResourcesLoaded, vehicleName))
         return
Example #2
0
 def __setupTrackAssembler(self, entity):
     modelNames = getPartModelsFromDesc(self.__vehicleDesc,
                                        ModelsSetParams(None, 'destroyed'))
     compoundAssembler = BigWorld.CompoundAssembler()
     compoundAssembler.addRootPart(modelNames.chassis,
                                   TankPartNames.CHASSIS,
                                   entity.filter.groundPlacingMatrix)
     compoundAssembler.assemblerName = TankPartNames.CHASSIS
     compoundAssembler.spaceID = entity.spaceID
     return compoundAssembler
    def __startBuild(self, vDesc, vState):
        self.__curBuildInd += 1
        self.__vState = vState
        self.__resources = {}
        self.__vehicleStickers = None
        cfg = hangarCFG()
        if vState == 'undamaged':
            self.__currentEmblemsAlpha = cfg['emblems_alpha_undamaged']
            self.__isVehicleDestroyed = False
        else:
            self.__currentEmblemsAlpha = cfg['emblems_alpha_damaged']
            self.__isVehicleDestroyed = True
        self.__vDesc = vDesc
        resources = camouflages.getCamoPrereqs(self.__outfit, vDesc)
        splineDesc = vDesc.chassis.splineDesc
        if splineDesc is not None:
            resources.append(splineDesc.segmentModelLeft)
            resources.append(splineDesc.segmentModelRight)
            if splineDesc.leftDesc is not None:
                resources.append(splineDesc.leftDesc)
            if splineDesc.rightDesc is not None:
                resources.append(splineDesc.rightDesc)
            if splineDesc.segment2ModelLeft is not None:
                resources.append(splineDesc.segment2ModelLeft)
            if splineDesc.segment2ModelRight is not None:
                resources.append(splineDesc.segment2ModelRight)
        from vehicle_systems import model_assembler
        resources.append(model_assembler.prepareCompoundAssembler(self.__vDesc, ModelsSetParams(self.__outfit.modelsSet, self.__vState), self.__spaceId))
        g_eventBus.handleEvent(CameraRelatedEvents(CameraRelatedEvents.VEHICLE_LOADING, ctx={'started': True,
         'vEntityId': self.__vEntity.id}), scope=EVENT_BUS_SCOPE.DEFAULT)
        cfg = hangarCFG()
        gunScale = Math.Vector3(1.0, 1.0, 1.1)
        capsuleScale = Math.Vector3(1.5, 1.5, 1.5)
        loadedGunScale = cfg.get('cam_capsule_gun_scale', gunScale)
        if loadedGunScale is not None:
            gunScale = loadedGunScale
        loadedCapsuleScale = cfg.get('cam_capsule_scale', capsuleScale)
        if loadedCapsuleScale is not None:
            capsuleScale = loadedCapsuleScale
        bspModels = ((TankPartNames.getIdx(TankPartNames.CHASSIS), vDesc.chassis.hitTester.bspModelName),
         (TankPartNames.getIdx(TankPartNames.HULL), vDesc.hull.hitTester.bspModelName),
         (TankPartNames.getIdx(TankPartNames.TURRET), vDesc.turret.hitTester.bspModelName),
         (TankPartNames.getIdx(TankPartNames.GUN), vDesc.gun.hitTester.bspModelName),
         (TankPartNames.getIdx(TankPartNames.GUN) + 1, vDesc.hull.hitTester.bspModelName, capsuleScale),
         (TankPartNames.getIdx(TankPartNames.GUN) + 2, vDesc.turret.hitTester.bspModelName, capsuleScale),
         (TankPartNames.getIdx(TankPartNames.GUN) + 3, vDesc.gun.hitTester.bspModelName, gunScale))
        collisionAssembler = BigWorld.CollisionAssembler(bspModels, self.__spaceId)
        resources.append(collisionAssembler)
        physicalTracksBuilders = vDesc.chassis.physicalTracks
        for name, builder in physicalTracksBuilders.iteritems():
            resources.append(builder.createLoader('{0}PhysicalTrack'.format(name)))

        BigWorld.loadResourceListBG(tuple(resources), makeCallbackWeak(self.__onResourcesLoaded, self.__curBuildInd))
        return
Example #4
0
 def __getModels(self):
     outfit = prepareBattleOutfit(self.outfitCD, self.__vehDescr,
                                  self.vehicleID)
     style = outfit.style
     if style is None:
         return (self.__vehDescr.turret.models.exploded,
                 self.__vehDescr.gun.models.exploded)
     else:
         modelsSetParams = ModelsSetParams(style.modelsSet,
                                           ModelStates.EXPLODED, [])
         _, _, turretModel, gunModel = getPartModelsFromDesc(
             self.__vehDescr, modelsSetParams)
         return (turretModel, gunModel)
Example #5
0
    def updateSpawnList(self, spawnListData):
        toAdd = spawnListData.difference(self._spawnList)
        toRemove = self._spawnList.difference(spawnListData)
        for data in toAdd:
            vDesc = VehicleDescriptor(compactDescr=data.vehicleCD)
            prereqs = set(vDesc.prerequisites())
            outfit = Outfit(component=getOutfitComponent(data.outfitCD),
                            vehicleCD=data.vehicleCD)
            modelsSetParams = ModelsSetParams(outfit.modelsSet,
                                              ModelStates.UNDAMAGED, [])
            compoundAssembler = model_assembler.prepareCompoundAssembler(
                vDesc, modelsSetParams,
                BigWorld.camera().spaceID)
            prereqs.add(compoundAssembler)
            self._appearanceCache.loadResources(data.vehicleCD, list(prereqs))

        for data in toRemove:
            self._appearanceCache.unloadResources(data.vehicleCD)

        self._spawnList = spawnListData
        _logger.debug('SpawnList cache updated=%s', spawnListData)
class CommonTankAppearance(ScriptGameObject):
    compoundModel = property(lambda self: self._compoundModel)
    boundEffects = property(lambda self: self.__boundEffects)
    fashions = property(lambda self: self.__fashions)
    fashion = property(lambda self: self.fashions.chassis)
    typeDescriptor = property(lambda self: self.__typeDesc if self._vehicle is
                              None else self._vehicle.typeDescriptor)
    id = property(lambda self: self.__vID)
    isAlive = property(lambda self: self.__isAlive)
    isObserver = property(lambda self: self.__isObserver)
    outfit = property(lambda self: self.__outfit)
    renderMode = property(lambda self: self.__renderMode)

    def _setFashions(self, fashions, isTurretDetached=False):
        if IS_EDITOR and self.__fashions:
            for fashion in self.__fashions:
                if fashion:
                    fashion.disowned()

        self.__fashions = fashions
        if isTurretDetached:
            self.compoundModel.setupFashions((fashions.chassis, fashions.hull))
        else:
            self.compoundModel.setupFashions(fashions)

    def _setOutfit(self, outfitCD):
        self.__outfit = self._prepareOutfit(outfitCD)

    terrainMatKind = property(lambda self: self.__currTerrainMatKind)
    terrainGroundType = property(lambda self: self.__currTerrainGroundType)
    terrainEffectMaterialNames = property(
        lambda self: self.__terrainEffectMaterialNames)
    isInWater = property(lambda self: self.waterSensor.isInWater)
    isUnderwater = property(lambda self: self.waterSensor.isUnderWater)
    waterHeight = property(lambda self: self.waterSensor.waterHeight)
    damageState = property(lambda self: self.__currentDamageState)
    modelsSetParams = property(
        lambda self: ModelsSetParams(self.outfit.modelsSet, self.damageState.
                                     modelState, self.__attachments))
    splineTracks = property(lambda self: self._splineTracks)
    isFlying = property(lambda self: self.flyingInfoProvider is not None and
                        self.flyingInfoProvider.isFlying)
    isLeftSideFlying = property(
        lambda self: self.flyingInfoProvider is not None and self.
        flyingInfoProvider.isLeftSideFlying)
    isRightSideFlying = property(
        lambda self: self.flyingInfoProvider is not None and self.
        flyingInfoProvider.isRightSideFlying)
    trackScrollController = property(lambda self: self.__trackScrollCtl)
    wheelsState = property(lambda self: 0)
    burnoutLevel = property(lambda self: 0.0)
    wheelsGameObject = property(lambda self: self.__wheelsGameObject)
    filterRetrievers = property(lambda self: self.__filterRetrievers)
    filterRetrieverGameObjects = property(
        lambda self: self.__filterRetrieverGameObjects)
    allLodCalculators = property(lambda self: self.__allLodCalculators)
    transmissionSlip = property(lambda self: self._commonSlip)
    transmissionScroll = property(lambda self: self._commonScroll)
    vehicleStickers = property(lambda self: self._vehicleStickers)
    isTurretDetached = property(lambda self: self._isTurretDetached)
    _weaponEnergy = property(lambda self: self.__weaponEnergy)
    filter = AutoProperty()
    areaTriggerTarget = ComponentDescriptor()
    burnoutProcessor = ComponentDescriptor()
    c11nComponent = ComponentDescriptor()
    collisionObstaclesCollector = ComponentDescriptor()
    collisions = ComponentDescriptor()
    crashedTracksController = ComponentDescriptor()
    customEffectManager = ComponentDescriptor()
    detailedEngineState = ComponentDescriptor()
    dirtComponent = ComponentDescriptor()
    engineAudition = ComponentDescriptor()
    flyingInfoProvider = ComponentDescriptor()
    frictionAudition = ComponentDescriptor()
    gearbox = ComponentDescriptor()
    gunLinkedNodesAnimator = ComponentDescriptor()
    gunRecoil = ComponentDescriptor()
    gunRotatorAudition = ComponentDescriptor()
    hullAimingController = ComponentDescriptor()
    leveredSuspension = ComponentDescriptor()
    lodCalculator = ComponentDescriptor()
    shadowManager = ComponentDescriptor()
    siegeEffects = ComponentDescriptor()
    suspension = ComponentDescriptor()
    suspensionSound = ComponentDescriptor()
    swingingAnimator = ComponentDescriptor()
    terrainMatKindSensor = ComponentDescriptor()
    tessellationCollisionSensor = ComponentDescriptor()
    trackNodesAnimator = ComponentDescriptor()
    tracks = ComponentDescriptor()
    transform = ComponentDescriptor()
    vehicleTraces = ComponentDescriptor()
    waterSensor = ComponentDescriptor()
    wheelsAnimator = ComponentDescriptor()
    flagComponent = ComponentDescriptor()

    def __init__(self, spaceID):
        ScriptGameObject.__init__(self, spaceID, CgfTankNodes.TANK_ROOT)
        self._vehicle = None
        self.__wheelsGameObject = ScriptGameObject(spaceID, 'Tank.Wheels.Root')
        self.__filter = None
        self.__typeDesc = None
        self.crashedTracksController = None
        self.__currentDamageState = VehicleDamageState()
        self.__currTerrainMatKind = [-1] * MATKIND_COUNT
        self.__currTerrainGroundType = [-1] * MATKIND_COUNT
        self.__terrainEffectMaterialNames = [''] * MATKIND_COUNT
        self._chassisDecal = VehicleDecal(self)
        self.__splodge = None
        self.__boundEffects = None
        self._splineTracks = None
        self.flyingInfoProvider = self.createComponent(
            Vehicular.FlyingInfoProvider)
        self.__trackScrollCtl = BigWorld.PyTrackScroll()
        self.__trackScrollCtl.setFlyingInfo(
            DataLinks.createBoolLink(self.flyingInfoProvider,
                                     'isLeftSideFlying'),
            DataLinks.createBoolLink(self.flyingInfoProvider,
                                     'isRightSideFlying'))
        self.__weaponEnergy = 0.0
        self.__outfit = None
        self.__systemStarted = False
        self.__isAlive = True
        self._isTurretDetached = False
        self.__isObserver = False
        self.__attachments = []
        self.__modelAnimators = []
        self.turretMatrix = None
        self.gunMatrix = None
        self.__allLodCalculators = []
        self._commonScroll = 0.0
        self._commonSlip = 0.0
        self._compoundModel = None
        self.__fashions = None
        self.__filterRetrievers = []
        self.__filterRetrieverGameObjects = []
        self._vehicleStickers = None
        self._vehicleInfo = {}
        self.__vID = 0
        self.__renderMode = None
        self.__frameTimestamp = 0
        self.__periodicTimerID = None
        self.undamagedStateChildren = []
        self.createComponent(VehicleAppearanceComponent, self)
        self._loadingQueue = []
        return

    def prerequisites(self,
                      typeDescriptor,
                      vID,
                      health,
                      isCrewActive,
                      isTurretDetached,
                      outfitCD,
                      renderMode=None):
        self.damageState.update(health, isCrewActive, False)
        self.__typeDesc = typeDescriptor
        self.__vID = vID
        self._isTurretDetached = isTurretDetached
        self.__updateModelStatus()
        self.__outfit = self._prepareOutfit(outfitCD)
        if self.damageState.isCurrentModelUndamaged:
            self.__attachments = camouflages.getAttachments(
                self.outfit, self.typeDescriptor)
        self.__renderMode = renderMode
        prereqs = self.typeDescriptor.prerequisites(True)
        prereqs.extend(
            camouflages.getCamoPrereqs(self.outfit, self.typeDescriptor))
        prereqs.extend(
            camouflages.getModelAnimatorsPrereqs(self.outfit, self.spaceID))
        prereqs.extend(
            camouflages.getAttachmentsAnimatorsPrereqs(self.__attachments,
                                                       self.spaceID))
        splineDesc = self.typeDescriptor.chassis.splineDesc
        modelsSet = self.outfit.modelsSet
        if IS_EDITOR:
            modelsSet = self.currentModelsSet
        if splineDesc is not None:
            for _, trackDesc in splineDesc.trackPairs.iteritems():
                prereqs += trackDesc.prerequisites(modelsSet)

        modelsSetParams = self.modelsSetParams
        compoundAssembler = model_assembler.prepareCompoundAssembler(
            self.typeDescriptor,
            modelsSetParams,
            self.spaceID,
            self.isTurretDetached,
            renderMode=self.renderMode)
        prereqs.append(compoundAssembler)
        if renderMode == TankRenderMode.OVERLAY_COLLISION:
            self.damageState.update(0, isCrewActive, False)
        collisionAssembler = model_assembler.prepareCollisionAssembler(
            self.typeDescriptor, self.isTurretDetached, self.spaceID)
        prereqs.append(collisionAssembler)
        skin = modelsSetParams.skin
        if IS_EDITOR:
            skin = self.currentModelsSet
        physicalTracksBuilders = self.typeDescriptor.chassis.physicalTracks
        for name, builders in physicalTracksBuilders.iteritems():
            for index, builder in enumerate(builders):
                prereqs.append(
                    builder.createLoader(
                        self.spaceID,
                        '{0}{1}PhysicalTrack'.format(name, index), skin))

        return prereqs

    def construct(self, isPlayer, resourceRefs):
        self.__isObserver = 'observer' in self.typeDescriptor.type.tags
        self._compoundModel = resourceRefs[self.typeDescriptor.name]
        self.removeComponentByType(GenericComponents.DynamicModelComponent)
        self.createComponent(GenericComponents.DynamicModelComponent,
                             self._compoundModel)
        if not self._compoundModel.isValid():
            _logger.error('compoundModel is not valid')
        if self.typeDescriptor.gun.edgeByVisualModel:
            self._compoundModel.setPartProperties(
                TankPartIndexes.GUN, PartProperties.HIGHLIGHTABLE
                | PartProperties.HIGHLIGHTBYVISUAL)
        self._compoundModel.setPartProperties(
            TankPartIndexes.CHASSIS,
            PartProperties.HIGHLIGHTABLE | PartProperties.HIGHLIGHTBYVISUAL)
        self.__boundEffects = bound_effects.ModelBoundEffects(
            self.compoundModel)
        isCurrentModelDamaged = self.damageState.isCurrentModelDamaged
        fashions = camouflages.prepareFashions(isCurrentModelDamaged)
        if not isCurrentModelDamaged:
            model_assembler.setupTracksFashion(self.typeDescriptor,
                                               fashions.chassis)
        self.collisions = self.createComponent(
            BigWorld.CollisionComponent, resourceRefs['collisionAssembler'])
        model_assembler.setupCollisions(self.typeDescriptor, self.collisions)
        self._setFashions(fashions, self.isTurretDetached)
        self._setupModels()
        if not isCurrentModelDamaged:
            modelsSet = self.outfit.modelsSet
            if IS_EDITOR:
                modelsSet = self.currentModelsSet
            self._splineTracks = model_assembler.setupSplineTracks(
                self.fashion, self.typeDescriptor, self.compoundModel,
                resourceRefs, modelsSet)
            self.crashedTracksController = CrashedTrackController(
                self.typeDescriptor, self.fashion, modelsSet)
        else:
            self.__trackScrollCtl = None
        self._chassisDecal.create()
        if self.modelsSetParams.state == 'undamaged':
            self.__modelAnimators = camouflages.getModelAnimators(
                self.outfit, self.typeDescriptor, self.spaceID, resourceRefs,
                self.compoundModel)
            self.__modelAnimators.extend(
                camouflages.getAttachmentsAnimators(self.__attachments,
                                                    self.spaceID, resourceRefs,
                                                    self.compoundModel))
        self.transform = self.createComponent(
            GenericComponents.TransformComponent, Math.Vector3(0, 0, 0))
        self.areaTriggerTarget = self.createComponent(
            Triggers.AreaTriggerTarget)
        self.__filter = model_assembler.createVehicleFilter(
            self.typeDescriptor)
        compoundModel = self.compoundModel
        if self.isAlive:
            self.detailedEngineState, self.gearbox = model_assembler.assembleDrivetrain(
                self, isPlayer)
            if not gEffectsDisabled():
                self.customEffectManager = CustomEffectManager(self)
                if self.typeDescriptor.hasSiegeMode:
                    self.siegeEffects = SiegeEffectsController(self, isPlayer)
                model_assembler.assembleVehicleAudition(isPlayer, self)
                self.detailedEngineState.onEngineStart = self._onEngineStart
                self.detailedEngineState.onStateChanged = self.engineAudition.onEngineStateChanged
            if isPlayer:
                turret = self.typeDescriptor.turret
                gunRotatorAudition = self.createComponent(
                    Vehicular.GunRotatorAudition,
                    turret.turretRotatorSoundManual, turret.weight / 1000.0,
                    compoundModel.node(TankPartNames.TURRET))
                gunRotatorAudition.vehicleMatrixLink = self.compoundModel.root
                gunRotatorAudition.damaged = lambda: self.turretDamaged()
                gunRotatorAudition.maxTurretRotationSpeed = lambda: self.maxTurretRotationSpeed(
                )
                self.gunRotatorAudition = gunRotatorAudition
                self.frictionAudition = self.createComponent(
                    Vehicular.FrictionAudition, TANK_FRICTION_EVENT)
        isLodTopPriority = isPlayer
        lodCalcInst = self.createComponent(
            Vehicular.LodCalculator,
            DataLinks.linkMatrixTranslation(compoundModel.matrix), True,
            VEHICLE_PRIORITY_GROUP, isLodTopPriority)
        self.lodCalculator = lodCalcInst
        self.allLodCalculators.append(lodCalcInst)
        lodLink = DataLinks.createFloatLink(lodCalcInst, 'lodDistance')
        lodStateLink = lodCalcInst.lodStateLink
        if IS_EDITOR:
            matrixBinding = None
            changeCamera = None
        else:
            matrixBinding = BigWorld.player(
            ).consistentMatrices.onVehicleMatrixBindingChanged
            changeCamera = BigWorld.player().inputHandler.onCameraChanged
        self.shadowManager = VehicleShadowManager(compoundModel, matrixBinding,
                                                  changeCamera)
        if not self.damageState.isCurrentModelDamaged:
            self.__assembleNonDamagedOnly(resourceRefs, isPlayer, lodLink,
                                          lodStateLink)
            dirtEnabled = BigWorld.WG_dirtEnabled(
            ) and 'HD' in self.typeDescriptor.type.tags
            if dirtEnabled and self.fashions is not None:
                dirtHandlers = [
                    BigWorld.PyDirtHandler(
                        True,
                        compoundModel.node(TankPartNames.CHASSIS).position.y),
                    BigWorld.PyDirtHandler(
                        False,
                        compoundModel.node(TankPartNames.HULL).position.y),
                    BigWorld.PyDirtHandler(
                        False,
                        compoundModel.node(TankPartNames.TURRET).position.y),
                    BigWorld.PyDirtHandler(
                        False,
                        compoundModel.node(TankPartNames.GUN).position.y)
                ]
                modelHeight, _ = self.computeVehicleHeight()
                self.dirtComponent = self.createComponent(
                    Vehicular.DirtComponent, dirtHandlers, modelHeight)
                for fashionIdx, _ in enumerate(TankPartNames.ALL):
                    self.fashions[fashionIdx].addMaterialHandler(
                        dirtHandlers[fashionIdx])
                    self.fashions[fashionIdx].addTrackMaterialHandler(
                        dirtHandlers[fashionIdx])

        model_assembler.setupTurretRotations(self)
        self.waterSensor = model_assembler.assembleWaterSensor(
            self.typeDescriptor, self, lodStateLink, self.spaceID)
        if self.engineAudition is not None:
            self.engineAudition.setIsUnderwaterInfo(
                DataLinks.createBoolLink(self.waterSensor, 'isUnderWater'))
            self.engineAudition.setIsInWaterInfo(
                DataLinks.createBoolLink(self.waterSensor, 'isInWater'))
        self.__postSetupFilter()
        compoundModel.setPartBoundingBoxAttachNode(
            TankPartIndexes.GUN, TankNodeNames.GUN_INCLINATION)
        camouflages.updateFashions(self)
        model_assembler.assembleCustomLogicComponents(self,
                                                      self.typeDescriptor,
                                                      self.__attachments,
                                                      self.__modelAnimators)
        self._createStickers()
        while self._loadingQueue:
            prefab, go, vector, callback = self._loadingQueue.pop()
            CGF.loadGameObjectIntoHierarchy(prefab, go, vector, callback)

        return

    def destroy(self):
        self._vehicleInfo = {}
        self.flagComponent = None
        self._destroySystems()
        fashions = VehiclePartsTuple(None, None, None, None)
        self._setFashions(fashions, self._isTurretDetached)
        self.shadowManager.unregisterCompoundModel(self.compoundModel)
        for go in self.filterRetrieverGameObjects:
            go.destroy()

        self.wheelsGameObject.destroy()
        super(CommonTankAppearance, self).destroy()
        self.__typeDesc = None
        if self.boundEffects is not None:
            self.boundEffects.destroy()
        self._vehicleStickers = None
        self._chassisDecal.destroy()
        self._chassisDecal = None
        self._compoundModel = None
        self._destroyStickers()
        self._loadingQueue = []
        return

    def activate(self):
        typeDescr = self.typeDescriptor
        wheelConfig = typeDescr.chassis.generalWheelsAnimatorConfig
        if self.wheelsAnimator is not None and wheelConfig is not None:
            self.wheelsAnimator.createCollision(wheelConfig, self.collisions)
        super(CommonTankAppearance, self).activate()
        self.wheelsGameObject.activate()
        for go in self.filterRetrieverGameObjects:
            go.activate()

        if not self.isObserver:
            self._chassisDecal.attach()
        if not self.isObserver:
            self._startSystems()
            self.filter.enableLagDetection(
                not self.damageState.isCurrentModelDamaged)
            if self.__periodicTimerID is not None:
                BigWorld.cancelCallback(self.__periodicTimerID)
            self.__periodicTimerID = BigWorld.callback(PERIODIC_UPDATE_TIME,
                                                       self.__onPeriodicTimer)
        self.setupGunMatrixTargets(self.filter)
        for lodCalculator in self.allLodCalculators:
            lodCalculator.setupPosition(
                DataLinks.linkMatrixTranslation(self.compoundModel.matrix))

        for modelAnimator in self.__modelAnimators:
            modelAnimator.animator.setEnabled(True)
            modelAnimator.animator.start()

        if hasattr(self.filter, 'placingCompensationMatrix'
                   ) and self.swingingAnimator is not None:
            self.swingingAnimator.placingCompensationMatrix = self.filter.placingCompensationMatrix
            self.swingingAnimator.worldMatrix = self.compoundModel.matrix
        if self.isObserver:
            self.compoundModel.visible = False
        self._connectCollider()
        self._attachStickers()
        return

    def deactivate(self):
        for modelAnimator in self.__modelAnimators:
            modelAnimator.animator.setEnabled(False)

        super(CommonTankAppearance, self).deactivate()
        self.shadowManager.unregisterCompoundModel(self.compoundModel)
        self._stopSystems()
        self.wheelsGameObject.deactivate()
        for go in self.filterRetrieverGameObjects:
            go.deactivate()

        self._chassisDecal.detach()
        self._detachStickers()

    def setVehicleInfo(self, vehInfo):
        self._vehicleInfo = vehInfo

    def setupGunMatrixTargets(self, target):
        self.turretMatrix = target.turretMatrix
        self.gunMatrix = target.gunMatrix

    def receiveShotImpulse(self, direction, impulse):
        if not VehicleDamageState.isDamagedModel(self.damageState.modelState):
            self.swingingAnimator.receiveShotImpulse(direction, impulse)
            if self.crashedTracksController is not None:
                self.crashedTracksController.receiveShotImpulse(
                    direction, impulse)
        return

    def recoil(self):
        self._initiateRecoil(TankNodeNames.GUN_INCLINATION, 'HP_gunFire',
                             self.gunRecoil)

    def multiGunRecoil(self, indexes):
        if self.gunAnimators is None:
            return
        else:
            for index in indexes:
                typeDescr = self.typeDescriptor
                gunNodeName = typeDescr.turret.multiGun[index].node
                gunFireNodeName = typeDescr.turret.multiGun[index].gunFire
                gunAnimator = self.gunAnimators[index].findComponentByType(
                    Vehicular.RecoilAnimator)
                self._initiateRecoil(gunNodeName, gunFireNodeName, gunAnimator)

            return

    def computeFullVehicleLength(self):
        vehicleLength = 0.0
        if self.compoundModel is not None:
            hullBB = Math.Matrix(
                self.compoundModel.getBoundsForPart(TankPartIndexes.HULL))
            vehicleLength = hullBB.applyVector(Math.Vector3(0.0, 0.0,
                                                            1.0)).length
        return vehicleLength

    def _initiateRecoil(self, gunNodeName, gunFireNodeName, gunAnimator):
        gunNode = self.compoundModel.node(gunNodeName)
        impulseDir = Math.Matrix(gunNode).applyVector(Math.Vector3(0, 0, -1))
        impulseValue = self.typeDescriptor.gun.impulse
        self.receiveShotImpulse(impulseDir, impulseValue)
        gunAnimator.recoil()
        return impulseDir

    def _connectCollider(self):
        if self.collisions is not None:
            chassisColisionMatrix, gunNodeName = self._vehicleColliderInfo
            if self.isTurretDetached:
                self.collisions.removeAttachment(
                    TankPartNames.getIdx(TankPartNames.TURRET))
                self.collisions.removeAttachment(
                    TankPartNames.getIdx(TankPartNames.GUN))
                collisionData = ((TankPartNames.getIdx(TankPartNames.HULL),
                                  self.compoundModel.node(TankPartNames.HULL)),
                                 (TankPartNames.getIdx(TankPartNames.CHASSIS),
                                  chassisColisionMatrix))
            else:
                collisionData = ((TankPartNames.getIdx(TankPartNames.HULL),
                                  self.compoundModel.node(TankPartNames.HULL)),
                                 (TankPartNames.getIdx(TankPartNames.TURRET),
                                  self.compoundModel.node(
                                      TankPartNames.TURRET)),
                                 (TankPartNames.getIdx(TankPartNames.CHASSIS),
                                  chassisColisionMatrix),
                                 (TankPartNames.getIdx(TankPartNames.GUN),
                                  self.compoundModel.node(gunNodeName)))
            defaultPartLength = len(TankPartNames.ALL)
            additionalChassisParts = []
            trackPairs = self.typeDescriptor.chassis.trackPairs
            if not trackPairs:
                trackPairs = [None]
            for x in xrange(len(trackPairs) - 1):
                additionalChassisParts.append(
                    (defaultPartLength + x, chassisColisionMatrix))

            if additionalChassisParts:
                collisionData += tuple(additionalChassisParts)
            self.collisions.connect(self.id, ColliderTypes.VEHICLE_COLLIDER,
                                    collisionData)
        return

    def computeVehicleHeight(self):
        gunLength = 0.0
        height = 0.0
        if self.collisions is not None:
            desc = self.typeDescriptor
            hullBB = self.collisions.getBoundingBox(
                TankPartNames.getIdx(TankPartNames.HULL))
            turretBB = self.collisions.getBoundingBox(
                TankPartNames.getIdx(TankPartNames.TURRET))
            gunBB = self.collisions.getBoundingBox(
                TankPartNames.getIdx(TankPartNames.GUN))
            hullTopY = desc.chassis.hullPosition[1] + hullBB[1][1]
            turretTopY = desc.chassis.hullPosition[
                1] + desc.hull.turretPositions[0][1] + turretBB[1][1]
            gunTopY = desc.chassis.hullPosition[1] + desc.hull.turretPositions[
                0][1] + desc.turret.gunPosition[1] + gunBB[1][1]
            gunLength = math.fabs(gunBB[1][2] - gunBB[0][2])
            height = max(hullTopY, max(turretTopY, gunTopY))
        return (height, gunLength)

    def onWaterSplash(self, waterHitPoint, isHeavySplash):
        pass

    def onUnderWaterSwitch(self, isUnderWater):
        pass

    def getWheelsSteeringMax(self):
        pass

    def _prepareOutfit(self, outfitCD):
        outfitComponent = camouflages.getOutfitComponent(outfitCD)
        return Outfit(component=outfitComponent,
                      vehicleCD=self.typeDescriptor.makeCompactDescr())

    def _setupModels(self):
        self.__isAlive = not self.damageState.isCurrentModelDamaged
        if self.isAlive:
            _, gunLength = self.computeVehicleHeight()
            self.__weaponEnergy = gunLength * self.typeDescriptor.shot.shell.caliber
        if MAX_DISTANCE > 0 and not self.isObserver:
            transform = self.typeDescriptor.chassis.AODecals[0]
            splodge = BigWorld.Splodge(
                transform, MAX_DISTANCE,
                self.typeDescriptor.chassis.hullPosition.y)
            if splodge:
                self.__splodge = splodge
                node = self.compoundModel.node(TankPartNames.HULL)
                node.attach(splodge)

    def _createStickers(self):
        _logger.debug('Creating VehicleStickers for vehicleType: %s',
                      self.typeDescriptor)
        isCurrentModelDamaged = self.damageState.isCurrentModelDamaged
        if isCurrentModelDamaged:
            return
        else:
            if self.vehicleStickers is not None:
                self._destroyStickers()
            self._vehicleStickers = VehicleStickers(self.spaceID,
                                                    self.typeDescriptor,
                                                    outfit=self.outfit)
            return

    def _destroyStickers(self):
        _logger.debug('Attaching VehicleStickers for vehicleType: %s',
                      self.typeDescriptor)
        self._detachStickers()
        self._vehicleStickers = None
        return

    def _attachStickers(self):
        _logger.debug('Attaching VehicleStickers for vehicle: %s',
                      self._vehicle)
        if self.vehicleStickers is None:
            _logger.error(
                'Failed to attach VehicleStickers. Missing VehicleStickers. Vehicle: %s',
                self._vehicle)
            return
        else:
            isCurrentModelDamaged = self.damageState.isCurrentModelDamaged
            self.vehicleStickers.alpha = DEFAULT_STICKERS_ALPHA
            self.vehicleStickers.attach(
                compoundModel=self.compoundModel,
                isDamaged=isCurrentModelDamaged,
                showDamageStickers=not isCurrentModelDamaged)
            return

    def _detachStickers(self):
        _logger.debug('Detaching VehicleStickers for vehicle: %s',
                      self._vehicle)
        if self.vehicleStickers is not None:
            self.vehicleStickers.detach()
        return

    @property
    def _vehicleColliderInfo(self):
        chassisColisionMatrix = self.compoundModel.matrix
        if self.damageState.isCurrentModelDamaged:
            gunNodeName = 'gun'
        else:
            gunNodeName = TankNodeNames.GUN_INCLINATION
        return (chassisColisionMatrix, gunNodeName)

    def _startSystems(self):
        if self.flyingInfoProvider is not None:
            self.flyingInfoProvider.setData(self.filter, self.suspension)
        if self.damageState.isCurrentModelDamaged or self.__systemStarted:
            return
        else:
            self.__systemStarted = True
            if self.trackScrollController is not None:
                self.trackScrollController.activate()
                self.trackScrollController.setData(self.filter)
            if self.engineAudition is not None:
                self.engineAudition.setWeaponEnergy(self._weaponEnergy)
                self.engineAudition.attachToModel(self.compoundModel)
            if self.hullAimingController is not None:
                self.hullAimingController.setData(self.filter,
                                                  self.typeDescriptor)
            if self.detailedEngineState is not None:
                self.detailedEngineState.onGearUpCbk = self.__onEngineStateGearUp
            return

    def _stopSystems(self):
        if self.flyingInfoProvider is not None:
            self.flyingInfoProvider.setData(None, None)
        if self.__systemStarted:
            self.__systemStarted = False
        if self.trackScrollController is not None:
            self.trackScrollController.deactivate()
            self.trackScrollController.setData(None)
        if self.__periodicTimerID is not None:
            BigWorld.cancelCallback(self.__periodicTimerID)
            self.__periodicTimerID = None
        for modelAnimator in self.__modelAnimators:
            modelAnimator.animator.stop()

        self.filter.enableLagDetection(False)
        return

    def _destroySystems(self):
        self.__systemStarted = False
        if self.trackScrollController is not None:
            self.trackScrollController.deactivate()
            self.__trackScrollCtl = None
        for modelAnimator in self.__modelAnimators:
            modelAnimator.animator.stop()

        if self.__periodicTimerID is not None:
            BigWorld.cancelCallback(self.__periodicTimerID)
            self.__periodicTimerID = None
        self.__modelAnimators = []
        self.filter.enableLagDetection(False)
        for go in self.undamagedStateChildren:
            CGF.removeGameObject(go)

        self.undamagedStateChildren = []
        return

    def _onRequestModelsRefresh(self):
        self.flagComponent = None
        self.__updateModelStatus()
        return

    def __updateModelStatus(self):
        if self.damageState.isCurrentModelUndamaged:
            modelStatus = ModelStatus.NORMAL
        else:
            modelStatus = ModelStatus.CRASHED
        for htManager in self.typeDescriptor.getHitTesterManagers():
            htManager.setStatus(modelStatus)

    def _onEngineStart(self):
        if self.engineAudition is not None:
            self.engineAudition.onEngineStart()
        return

    def __assembleNonDamagedOnly(self, resourceRefs, isPlayer, lodLink,
                                 lodStateLink):
        model_assembler.assembleTerrainMatKindSensor(self, lodStateLink,
                                                     self.spaceID)
        model_assembler.assembleRecoil(self, lodLink)
        model_assembler.assembleMultiGunRecoil(self, lodLink)
        model_assembler.assembleGunLinkedNodesAnimator(self)
        model_assembler.assembleCollisionObstaclesCollector(
            self, lodStateLink, self.typeDescriptor)
        model_assembler.assembleTessellationCollisionSensor(self, lodStateLink)
        wheelsScroll = None
        wheelsSteering = None
        generalWheelsAnimatorConfig = self.typeDescriptor.chassis.generalWheelsAnimatorConfig
        if generalWheelsAnimatorConfig is not None:
            scrollableWheelsCount = generalWheelsAnimatorConfig.getNonTrackWheelsCount(
            )
            wheelsScroll = []
            for _ in xrange(scrollableWheelsCount):
                retrieverGameObject = ScriptGameObject(self.spaceID)
                retriever = retrieverGameObject.createComponent(
                    NetworkFilters.FloatFilterRetriever)
                wheelsScroll.append(
                    DataLinks.createFloatLink(retriever, 'value'))
                self.filterRetrievers.append(retriever)
                self.filterRetrieverGameObjects.append(retrieverGameObject)

            steerableWheelsCount = generalWheelsAnimatorConfig.getSteerableWheelsCount(
            )
            wheelsSteering = []
            for _ in xrange(steerableWheelsCount):
                retrieverGameObject = ScriptGameObject(self.spaceID)
                retriever = retrieverGameObject.createComponent(
                    NetworkFilters.FloatFilterRetriever)
                wheelsSteering.append(
                    DataLinks.createFloatLink(retriever, 'value'))
                self.filterRetrievers.append(retriever)
                self.filterRetrieverGameObjects.append(retrieverGameObject)

        self.wheelsAnimator = model_assembler.createWheelsAnimator(
            self, ColliderTypes.VEHICLE_COLLIDER, self.typeDescriptor,
            lambda: self.wheelsState, wheelsScroll, wheelsSteering,
            self.splineTracks, lodStateLink)
        if self.customEffectManager is not None:
            self.customEffectManager.setWheelsData(self)
        suspensionLodLink = lodStateLink
        if 'wheeledVehicle' in self.typeDescriptor.type.tags:
            wheeledLodCalculator = self.wheelsGameObject.createComponent(
                Vehicular.LodCalculator,
                DataLinks.linkMatrixTranslation(self.compoundModel.matrix),
                True, WHEELED_CHASSIS_PRIORITY_GROUP, isPlayer)
            self.allLodCalculators.append(wheeledLodCalculator)
            suspensionLodLink = wheeledLodCalculator.lodStateLink
        model_assembler.assembleSuspensionIfNeed(self, suspensionLodLink)
        model_assembler.assembleLeveredSuspensionIfNeed(
            self, suspensionLodLink)
        self.__assembleSwinging(lodLink)
        model_assembler.assembleBurnoutProcessor(self)
        model_assembler.assembleSuspensionSound(self, lodLink, isPlayer)
        model_assembler.assembleHullAimingController(self)
        self.trackNodesAnimator = model_assembler.createTrackNodesAnimator(
            self, self.typeDescriptor, lodStateLink)
        model_assembler.assembleTracks(resourceRefs, self.typeDescriptor, self,
                                       self.splineTracks, False, lodStateLink)
        model_assembler.assembleVehicleTraces(self, self.filter, lodStateLink)
        return

    def __assembleSwinging(self, lodLink):
        hullNode = self.compoundModel.node(TankPartNames.HULL)
        if hullNode is None:
            _logger.error(
                'Could not create SwingingAnimator: failed to find hull node')
            return
        else:
            self.swingingAnimator = model_assembler.createSwingingAnimator(
                self, self.typeDescriptor, hullNode.localMatrix,
                self.compoundModel.matrix, lodLink)
            self.compoundModel.node(TankPartNames.HULL,
                                    self.swingingAnimator.animatedMProv)
            if hasattr(self.filter, 'placingCompensationMatrix'):
                self.swingingAnimator.placingCompensationMatrix = self.filter.placingCompensationMatrix
            return

    def __postSetupFilter(self):
        suspensionWorking = self.suspension is not None and self.suspension.hasGroundNodes
        placingOnGround = not (suspensionWorking
                               or self.leveredSuspension is not None)
        self.filter.placingOnGround = placingOnGround
        return

    def __onPeriodicTimer(self):
        timeStamp = BigWorld.wg_getFrameTimestamp()
        if self.__frameTimestamp >= timeStamp:
            self.__periodicTimerID = BigWorld.callback(0.0,
                                                       self.__onPeriodicTimer)
        else:
            self.__frameTimestamp = timeStamp
            self.__periodicTimerID = BigWorld.callback(PERIODIC_UPDATE_TIME,
                                                       self.__onPeriodicTimer)
            self._periodicUpdate()

    def _periodicUpdate(self):
        if self._vehicle is None or not self._vehicle.isAlive():
            return
        else:
            self._updateCurrTerrainMatKinds()
            self.__updateEffectsLOD()
            if self.siegeEffects:
                self.siegeEffects.tick()
            if self.customEffectManager:
                self.customEffectManager.update()
            return

    def __updateEffectsLOD(self):
        if self.customEffectManager:
            distanceFromPlayer = self.lodCalculator.lodDistance
            enableExhaust = distanceFromPlayer <= _LOD_DISTANCE_EXHAUST and not self.isUnderwater
            enableTrails = distanceFromPlayer <= _LOD_DISTANCE_TRAIL_PARTICLES and BigWorld.wg_isVehicleDustEnabled(
            )
            self.customEffectManager.enable(enableTrails,
                                            EffectSettings.SETTING_DUST)
            self.customEffectManager.enable(enableExhaust,
                                            EffectSettings.SETTING_EXHAUST)

    def _stopEffects(self):
        self.boundEffects.stop()

    def playEffectWithStopCallback(self, effects):
        self._stopEffects()
        vehicle = self._vehicle
        return self.boundEffects.addNew(
            None,
            effects[1],
            effects[0],
            isPlayerVehicle=vehicle.isPlayerVehicle,
            showShockWave=vehicle.isPlayerVehicle,
            showFlashBang=vehicle.isPlayerVehicle,
            entity_id=vehicle.id,
            isPlayer=vehicle.isPlayerVehicle,
            showDecal=True,
            start=vehicle.position + Math.Vector3(0.0, 1.0, 0.0),
            end=vehicle.position + Math.Vector3(0.0, -1.0, 0.0)).stop

    def playEffect(self, kind, *modifs):
        self._stopEffects()
        if kind == 'empty' or self._vehicle is None:
            return
        else:
            enableDecal = True
            if kind in ('explosion', 'destruction') and self.isFlying:
                enableDecal = False
            if self.isUnderwater:
                if kind not in ('submersionDeath', ):
                    return
            effects = self.typeDescriptor.type.effects[kind]
            if not effects:
                return
            vehicle = self._vehicle
            effects = random.choice(effects)
            args = dict(isPlayerVehicle=vehicle.isPlayerVehicle,
                        showShockWave=vehicle.isPlayerVehicle,
                        showFlashBang=vehicle.isPlayerVehicle,
                        entity_id=vehicle.id,
                        isPlayer=vehicle.isPlayerVehicle,
                        showDecal=enableDecal,
                        start=vehicle.position + Math.Vector3(0.0, 1.0, 0.0),
                        end=vehicle.position + Math.Vector3(0.0, -1.0, 0.0))
            if isSpawnedBot(
                    self.typeDescriptor.type.tags) and kind in ('explosion',
                                                                'destruction'):
                if isPlayerAvatar():
                    if self.isFlying:
                        instantExplosionEff = self.typeDescriptor.type.effects[
                            'instantExplosion']
                        if instantExplosionEff:
                            effects = random.choice(instantExplosionEff)
                    BigWorld.player().terrainEffects.addNew(
                        self._vehicle.position, effects[1], effects[0], None,
                        **args)
            else:
                self.boundEffects.addNew(None, effects[1], effects[0], **args)
            return

    def _updateCurrTerrainMatKinds(self):
        if self.terrainMatKindSensor is None:
            return
        else:
            matKinds = self.terrainMatKindSensor.matKinds
            groundTypes = self.terrainMatKindSensor.groundTypes
            materialsCount = len(matKinds)
            for i in xrange(MATKIND_COUNT):
                matKind = matKinds[i] if i < materialsCount else 0
                groundType = groundTypes[i] if i < materialsCount else 0
                self.terrainMatKind[i] = matKind
                self.terrainGroundType[i] = groundType
                effectIndex = calcEffectMaterialIndex(matKind)
                effectMaterialName = ''
                if effectIndex is not None:
                    effectMaterialName = material_kinds.EFFECT_MATERIALS[
                        effectIndex]
                self.terrainEffectMaterialNames[i] = effectMaterialName

            if self.vehicleTraces is not None:
                self.vehicleTraces.setCurrTerrainMatKinds(
                    self.terrainMatKind[0], self.terrainMatKind[1])
            return

    def onSiegeStateChanged(self, newState, timeToNextMode):
        if self.engineAudition is not None:
            self.engineAudition.onSiegeStateChanged(newState)
        if self.hullAimingController is not None:
            self.hullAimingController.onSiegeStateChanged(newState)
        if self.suspensionSound is not None:
            self.suspensionSound.vehicleState = newState
        if self.siegeEffects is not None:
            self.siegeEffects.onSiegeStateChanged(newState, timeToNextMode)
        enabled = newState == VEHICLE_SIEGE_STATE.ENABLED or newState == VEHICLE_SIEGE_STATE.SWITCHING_ON
        if self.suspension is not None:
            self.suspension.setLiftMode(enabled)
        if self.leveredSuspension is not None:
            self.leveredSuspension.setLiftMode(enabled)
        if self.vehicleTraces is not None:
            self.vehicleTraces.setLiftMode(enabled)
        return

    def changeEngineMode(self, mode, forceSwinging=False):
        if self.detailedEngineState is not None:
            self.detailedEngineState.mode = mode[0]
        if self.trackScrollController is not None:
            self.trackScrollController.setMode(mode)
        return

    def changeSiegeState(self, siegeState):
        if self.engineAudition is not None:
            self.engineAudition.onSiegeStateChanged(siegeState)
        return

    def turretDamaged(self):
        pass

    def maxTurretRotationSpeed(self):
        pass

    def pushToLoadingQueue(self, prefab, go, vector, callback):
        self._loadingQueue.append((prefab, go, vector, callback))

    def _onCameraChanged(self, cameraName, currentVehicleId=None):
        if self.id != BigWorld.player().playerVehicleID:
            return
        isEnabled = not cameraName == 'sniper'
        for modelAnimator in self.__modelAnimators:
            modelAnimator.animator.setEnabled(isEnabled)

    def __onEngineStateGearUp(self):
        if self.customEffectManager is not None:
            self.customEffectManager.onGearUp()
        if self.engineAudition is not None:
            self.engineAudition.onEngineGearUp()
        return

    def __animatorCallback(self, name, time):
        _logger.debug('Callback aquired %s %f', name, time)
        if self.shellAnimator is not None:
            self.shellAnimator.throwShell(
                self.typeDescriptor.shot.shell.animation)
        return

    def __isRequireTrackDebrisGeneration(self, isLeft, pairIndex):
        tracks = self.typeDescriptor.chassis.tracks
        return tracks is not None and tracks.trackPairs[
            pairIndex].tracksDebris is not None

    def _addCrashedTrack(self, isLeft, pairIndex, isSideFlying):
        if not self.__isRequireTrackDebrisGeneration(isLeft, pairIndex):
            if self.crashedTracksController is not None:
                self.crashedTracksController.addTrack(isLeft, isSideFlying)
            return
        else:
            track = self.tracks.getTrackGameObject(isLeft, pairIndex)
            debris = track.createComponent(TrackCrashWithDebrisComponent,
                                           isLeft, pairIndex,
                                           self.typeDescriptor,
                                           self.gameObject, self.boundEffects)
            debris.isTopPriority = self._vehicle.isPlayerVehicle
            debris.isPlayer = self._vehicle.isPlayerVehicle
            debris.isFlying = isSideFlying
            return

    def _delCrashedTrack(self, isLeft, pairIndex):
        if not self.__isRequireTrackDebrisGeneration(isLeft, pairIndex):
            if self.crashedTracksController is not None:
                self.crashedTracksController.delTrack(isLeft)
            return
        elif self.tracks is None:
            return
        else:
            track = self.tracks.getTrackGameObject(isLeft, pairIndex)
            debris = track.findComponentByType(TrackCrashWithDebrisComponent)
            if debris is not None:
                debris.markAsRepaired()
                track.removeComponent(debris)
            return
class CompoundAppearance(ComponentSystem, CallbackDelayer):
    compoundModel = property(lambda self: self.__compoundModel)
    boundEffects = property(lambda self: self.__boundEffects)
    fashion = property(lambda self: self.__fashions.chassis)
    typeDescriptor = property(lambda self: self.__typeDesc)
    id = property(lambda self: self.__vID)
    isAlive = property(lambda self: self.__isAlive)
    isObserver = property(lambda self: self.__isObserver)

    def __setFashions(self, fashions, isTurretDetached=False):
        self.__fashions = fashions
        self.__fashion = fashions.chassis
        if isTurretDetached:
            self.__compoundModel.setupFashions(
                (fashions.chassis, fashions.hull))
        else:
            self.__compoundModel.setupFashions(fashions)

    fashions = property(lambda self: self.__fashions, __setFashions)
    terrainMatKind = property(lambda self: self.__currTerrainMatKind)
    terrainEffectMaterialNames = property(
        lambda self: self.__terrainEffectMaterialNames)
    isInWater = property(lambda self: self.waterSensor.isInWater)
    isUnderwater = property(lambda self: self.waterSensor.isUnderWater)
    waterHeight = property(lambda self: self.waterSensor.waterHeight)
    damageState = property(lambda self: self.__currentDamageState)
    modelsSetParams = property(lambda self: ModelsSetParams(
        self.__outfit.modelsSet, self.__currentDamageState.modelState))
    frameTimeStamp = 0
    rightTrackScroll = property(lambda self: self.__rightTrackScroll)
    leftTrackScroll = property(lambda self: self.__leftTrackScroll)
    splineTracks = property(lambda self: self.__splineTracks)
    activated = property(lambda self: self.__activated)
    isFlying = property(lambda self: self.flyingInfoProvider is not None and
                        self.flyingInfoProvider.isFlying)
    isLeftSideFlying = property(
        lambda self: self.flyingInfoProvider is not None and self.
        flyingInfoProvider.isLeftSideFlying)
    isRightSideFlying = property(
        lambda self: self.flyingInfoProvider is not None and self.
        flyingInfoProvider.isRightSideFlying)

    @property
    def rpm(self):
        return self.detailedEngineState.rpm if self.detailedEngineState is not None else 0.0

    @property
    def gear(self):
        return self.detailedEngineState.gearNum if self.detailedEngineState is not None else 0.0

    trackScrollController = property(lambda self: self.__trackScrollCtl)
    filter = AutoProperty()
    detailedEngineState = ComponentDescriptor()
    engineAudition = ComponentDescriptor()
    trackCrashAudition = ComponentDescriptor()
    customEffectManager = ComponentDescriptor()
    highlighter = ComponentDescriptor()
    shadowManager = ComponentDescriptor()
    gunRecoil = ComponentDescriptor()
    gunLinkedNodesAnimator = ComponentDescriptor()
    swingingAnimator = ComponentDescriptor()
    dirtComponent = ComponentDescriptor()
    suspensionSound = ComponentDescriptor()
    siegeEffects = ComponentDescriptor()
    lodCalculator = ComponentDescriptor()
    frictionAudition = ComponentDescriptor()
    suspension = ComponentDescriptor()
    leveredSuspension = ComponentDescriptor()
    suspensionController = ComponentDescriptor()
    collisions = ComponentDescriptor()
    wheelsAnimator = ComponentDescriptor()
    trackNodesAnimator = ComponentDescriptor()
    vehicleTraces = ComponentDescriptor()
    flyingInfoProvider = ComponentDescriptor()
    terrainMatKindSensor = ComponentDescriptor()
    waterSensor = ComponentDescriptor()
    peripheralsController = ComponentDescriptor()
    tracks = ComponentDescriptor()
    crashedTrackController = ComponentDescriptor()
    collisionObstaclesCollector = ComponentDescriptor()
    tessellationCollisionSensor = ComponentDescriptor()

    def __init__(self):
        CallbackDelayer.__init__(self)
        ComponentSystem.__init__(self)
        self.turretMatrix = Math.WGAdaptiveMatrixProvider()
        self.gunMatrix = Math.WGAdaptiveMatrixProvider()
        self.__vehicle = None
        self.__filter = None
        self.__originalFilter = None
        self.__typeDesc = None
        self.__fashion = None
        self.crashedTracksController = None
        self.__currentDamageState = VehicleDamageState()
        self.__effectsPlayer = None
        self.__engineMode = (0, 0)
        self.__currTerrainMatKind = [-1] * _MATKIND_COUNT
        self.__terrainEffectMaterialNames = [''] * _MATKIND_COUNT
        self.__terrainMatKindLodDistance = _LOD_DISTANCE_TERRAIN_MATKIND_UPDATE
        self.__chassisDecal = VehicleDecal(self)
        self.__splodge = None
        self.__vehicleStickers = None
        self.onModelChanged = Event()
        self.__leftTrackScroll = 0.0
        self.__rightTrackScroll = 0.0
        self.__fashions = None
        self.__compoundModel = None
        self.__boundEffects = None
        self.__splineTracks = None
        self.flyingInfoProvider = Vehicular.FlyingInfoProvider()
        self.__trackScrollCtl = BigWorld.PyTrackScroll()
        self.__trackScrollCtl.setFlyingInfo(
            DataLinks.createBoolLink(self.flyingInfoProvider,
                                     'isLeftSideFlying'),
            DataLinks.createBoolLink(self.flyingInfoProvider,
                                     'isRightSideFlying'))
        self.__weaponEnergy = 0.0
        self.__activated = False
        self.__systemStarted = False
        self.__outfit = None
        self.__vID = 0
        self.__isAlive = True
        self.__isTurretDetached = False
        self.__periodicTimerID = None
        self.__wasDeactivated = False
        self.__dirtUpdateTime = 0.0
        self.__inSpeedTreeCollision = False
        self.__isObserver = False
        self.__chassisColisionMatrix = Math.WGAdaptiveMatrixProvider()
        return

    def prerequisites(self, typeDescriptor, vID, health, isCrewActive,
                      isTurretDetached, outfitCD):
        self.__currentDamageState.update(health, isCrewActive, False)
        self.__vID = vID
        self.__typeDesc = typeDescriptor
        self.__isTurretDetached = isTurretDetached
        self.__prepareOutfit(outfitCD)
        out = camouflages.getCamoPrereqs(self.__outfit, self.__typeDesc)
        splineDesc = typeDescriptor.chassis.splineDesc
        if splineDesc is not None:
            out.append(splineDesc.segmentModelLeft)
            out.append(splineDesc.segmentModelRight)
            if splineDesc.segment2ModelLeft is not None:
                out.append(splineDesc.segment2ModelLeft)
            if splineDesc.segment2ModelRight is not None:
                out.append(splineDesc.segment2ModelRight)
        self.__terrainMatKindLodDistance = typeDescriptor.chassis.traces.lodDist
        return out

    def setVehicle(self, vehicle):
        self.__vehicle = vehicle
        if self.customEffectManager is not None:
            self.customEffectManager.setVehicle(vehicle)
        if self.crashedTracksController is not None:
            self.crashedTracksController.setVehicle(vehicle)
        if self.frictionAudition is not None:
            self.frictionAudition.setVehicleMatrix(vehicle.matrix)
        self.highlighter.setVehicle(vehicle)
        self.__applyVehicleOutfit()
        return

    def __arenaPeriodChanged(self, period, *otherArgs):
        if self.detailedEngineState is None:
            return
        else:
            periodEndTime = BigWorld.player().arena.periodEndTime
            serverTime = BigWorld.serverTime()
            engine_state.notifyEngineOnArenaPeriodChange(
                self.detailedEngineState, period, periodEndTime, serverTime)
            return

    def activate(self):
        if self.__activated or self.__vehicle is None:
            return
        else:
            if self.collisions is not None and self.__vehicle.isTurretDetached:
                self.collisions.removeAttachment(
                    TankPartNames.getIdx(TankPartNames.TURRET))
                self.collisions.removeAttachment(
                    TankPartNames.getIdx(TankPartNames.GUN))
            super(CompoundAppearance, self).activate()
            isPlayerVehicle = self.__vehicle.isPlayerVehicle
            self.__isObserver = 'observer' in self.__typeDesc.type.tags
            player = BigWorld.player()
            self.__originalFilter = self.__vehicle.filter
            self.__vehicle.filter = self.__filter
            self.__vehicle.filter.enableStabilisedMatrix(isPlayerVehicle)
            self.__filter.isStrafing = self.__vehicle.isStrafing
            self.__filter.vehicleCollisionCallback = player.handleVehicleCollidedVehicle
            if isPlayerVehicle and self.collisions is not None:
                colliderData = (self.collisions.getColliderID(),
                                (TankPartNames.getIdx(TankPartNames.HULL),
                                 TankPartNames.getIdx(TankPartNames.TURRET)))
                BigWorld.appendCameraCollider(colliderData)
                self.__inSpeedTreeCollision = True
                BigWorld.setSpeedTreeCollisionBody(
                    self.__compoundModel.getBoundsForPart(
                        TankPartIndexes.HULL))
            self.__linkCompound()
            if not self.__isObserver:
                self.__chassisDecal.attach()
            self.__createAndAttachStickers()
            if self.__currentDamageState.isCurrentModelDamaged:
                self.__chassisColisionMatrix.target = self.__compoundModel.matrix
                gunNodeName = 'gun'
            else:
                self.__chassisColisionMatrix.target = self.__vehicle.filter.groundPlacingMatrix
                gunNodeName = TankNodeNames.GUN_INCLINATION
            if not self.__isObserver:
                self.__startSystems()
            self.setupGunMatrixTargets()
            if not self.__isObserver:
                self.__vehicle.filter.enableLagDetection(
                    not self.__currentDamageState.isCurrentModelDamaged)
            self.onModelChanged()
            if self.lodCalculator is not None:
                self.lodCalculator.setupPosition(
                    DataLinks.linkMatrixTranslation(
                        self.__compoundModel.matrix))
            if hasattr(self.filter, 'placingCompensationMatrix'
                       ) and self.swingingAnimator is not None:
                self.swingingAnimator.placingCompensationMatrix = self.filter.placingCompensationMatrix
                self.swingingAnimator.worldMatrix = self.__compoundModel.matrix
            if not self.__isObserver:
                if self.__periodicTimerID is not None:
                    BigWorld.cancelCallback(self.__periodicTimerID)
                self.__periodicTimerID = BigWorld.callback(
                    _PERIODIC_TIME, self.__onPeriodicTimer)
                self.__dirtUpdateTime = BigWorld.time()
            if self.fashion is not None:
                self.fashion.activate()
            if self.__isObserver:
                self.__compoundModel.visible = False
            BigWorld.player().arena.onPeriodChange += self.__arenaPeriodChanged
            BigWorld.player(
            ).inputHandler.onCameraChanged += self.__onCameraChanged
            if self.detailedEngineState is not None:
                engine_state.checkEngineStart(self.detailedEngineState,
                                              BigWorld.player().arena.period)
            if self.collisions is not None:
                collisionData = ((TankPartNames.getIdx(TankPartNames.HULL),
                                  self.compoundModel.node(TankPartNames.HULL)),
                                 (TankPartNames.getIdx(TankPartNames.TURRET),
                                  self.compoundModel.node(
                                      TankPartNames.TURRET)),
                                 (TankPartNames.getIdx(TankPartNames.CHASSIS),
                                  self.__chassisColisionMatrix),
                                 (TankPartNames.getIdx(TankPartNames.GUN),
                                  self.compoundModel.node(gunNodeName)))
                self.collisions.connect(self.id,
                                        ColliderTypes.VEHICLE_COLLIDER,
                                        collisionData)
            self.__activated = True
            return

    def deactivate(self, stopEffects=True):
        if not self.__activated:
            return
        else:
            self.shadowManager.unregisterCompoundModel(self.__compoundModel)
            self.__activated = False
            self.__wasDeactivated = True
            if self.fashion is not None:
                self.fashion.deactivate()
            self.__stopSystems()
            super(CompoundAppearance, self).deactivate()
            self.__chassisDecal.detach()
            if self.__inSpeedTreeCollision:
                BigWorld.setSpeedTreeCollisionBody(None)
            if self.collisions is not None:
                BigWorld.removeCameraCollider(self.collisions.getColliderID())
            self.__vehicle.filter.enableLagDetection(False)
            self.turretMatrix.target = None
            self.gunMatrix.target = None
            self.__vehicle.filter = self.__originalFilter
            self.__filter.reset()
            self.__originalFilter = None
            self.__vehicleStickers.detach()
            if stopEffects:
                self.__stopEffects()
                self.__boundEffects.stop()
            self.__vehicle.model = None
            self.__compoundModel.matrix = Math.Matrix()
            self.__vehicle = None
            BigWorld.player().arena.onPeriodChange -= self.__arenaPeriodChanged
            BigWorld.player(
            ).inputHandler.onCameraChanged -= self.__onCameraChanged
            return

    def __startSystems(self):
        if self.__systemStarted or self.__currentDamageState.isCurrentModelDamaged:
            return
        else:
            if self.flyingInfoProvider is not None:
                self.flyingInfoProvider.setData(self.__vehicle.filter,
                                                self.suspension)
            if self.__trackScrollCtl is not None:
                self.__trackScrollCtl.activate()
                self.__trackScrollCtl.setData(self.__vehicle.filter)
            if self.__vehicle.isPlayerVehicle:
                self.delayCallback(_PERIODIC_TIME_ENGINE,
                                   self.__onPeriodicTimerEngine)
                self.highlighter.highlight(True)
            if self.engineAudition is not None:
                self.engineAudition.setWeaponEnergy(self.__weaponEnergy)
                self.engineAudition.attachToModel(self.__compoundModel)
            if self.peripheralsController is not None:
                self.peripheralsController.attachToVehicle(self.__vehicle)
            if self.suspensionController is not None:
                self.suspensionController.setData(
                    self.__vehicle.filter, self.__vehicle.typeDescriptor)
            if self.detailedEngineState is not None:
                self.detailedEngineState.onGearUpCbk = self.__onEngineStateGearUp
            self.delayCallback(_PERIODIC_TIME_DIRT[0][0],
                               self.__onPeriodicTimerDirt)
            self.__systemStarted = True
            return

    def __stopSystems(self):
        if not self.__systemStarted:
            return
        else:
            self.__systemStarted = False
            if self.__periodicTimerID is not None:
                BigWorld.cancelCallback(self.__periodicTimerID)
                self.__periodicTimerID = None
            if self.flyingInfoProvider is not None:
                self.flyingInfoProvider.setData(None, None)
            if self.__vehicle.isPlayerVehicle:
                self.highlighter.highlight(False)
                self.stopCallback(self.__onPeriodicTimerEngine)
            self.stopCallback(self.__onPeriodicTimerDirt)
            if self.__trackScrollCtl is not None:
                self.__trackScrollCtl.deactivate()
                self.__trackScrollCtl.setData(None)
            if self.detailedEngineState is not None:
                self.detailedEngineState.onGearUpCbk = None
            return

    def __destroySystems(self):
        if self.__periodicTimerID is not None:
            BigWorld.cancelCallback(self.__periodicTimerID)
            self.__periodicTimerID = None
        if self.__trackScrollCtl is not None:
            self.__trackScrollCtl.deactivate()
            self.__trackScrollCtl = None
        if self.crashedTracksController is not None:
            self.crashedTracksController.destroy()
            self.crashedTracksController = None
        self.__systemStarted = False
        return

    def __destroyEngineAudition(self):
        self.engineAudition = None
        if self.detailedEngineState is not None:
            self.detailedEngineState.onEngineStart = None
            self.detailedEngineState.onStateChanged = None
        return

    def __prepareSystemsForDamagedVehicle(self, vehicle, isTurretDetached):
        if self.flyingInfoProvider is not None:
            self.flyingInfoProvider.setData(vehicle.filter, None)
        self.vehicleTraces = None
        self.suspensionSound = None
        self.swingingAnimator = None
        self.gunRecoil = None
        self.gunLinkedNodesAnimator = None
        self.suspension = None
        self.leveredSuspension = None
        self.trackNodesAnimator = None
        self.wheelsAnimator = None
        fashions = VehiclePartsTuple(None, None, None, None)
        self.__setFashions(fashions, isTurretDetached)
        self.customEffectManager = None
        self.__destroyEngineAudition()
        self.detailedEngineState = None
        self.trackCrashAudition = None
        self.frictionAudition = None
        self.terrainMatKindSensor = None
        self.__splineTracks = None
        model = self.compoundModel
        self.waterSensor.sensorPlaneLink = model.root
        self.peripheralsController = None
        self.dirtComponent = None
        self.tracks = None
        self.collisionObstaclesCollector = None
        self.tessellationCollisionSensor = None
        self.__destroySystems()
        return

    def destroy(self):
        if self.__vehicle is not None:
            self.deactivate()
        self.__destroySystems()
        ComponentSystem.destroy(self)
        self.__typeDesc = None
        if self.__boundEffects is not None:
            self.__boundEffects.destroy()
        self.__vehicleStickers = None
        self.onModelChanged = None
        self.__chassisDecal.destroy()
        self.__chassisDecal = None
        self.__compoundModel = None
        CallbackDelayer.destroy(self)
        return

    def start(self, prereqs=None):
        self.collisions = prereqs['collisionAssembler']
        self.__typeDesc.chassis.hitTester.bbox = self.collisions.getBoundingBox(
            TankPartNames.getIdx(TankPartNames.CHASSIS))
        self.__typeDesc.hull.hitTester.bbox = self.collisions.getBoundingBox(
            TankPartNames.getIdx(TankPartNames.HULL))
        self.__typeDesc.turret.hitTester.bbox = self.collisions.getBoundingBox(
            TankPartNames.getIdx(TankPartNames.TURRET))
        self.__typeDesc.gun.hitTester.bbox = self.collisions.getBoundingBox(
            TankPartNames.getIdx(TankPartNames.GUN))
        self.__compoundModel = prereqs[self.__typeDesc.name]
        self.__boundEffects = bound_effects.ModelBoundEffects(
            self.__compoundModel)
        isCurrentModelDamaged = self.__currentDamageState.isCurrentModelDamaged
        fashions = camouflages.prepareFashions(isCurrentModelDamaged)
        if not isCurrentModelDamaged:
            model_assembler.setupTracksFashion(self.__typeDesc,
                                               fashions.chassis)
        self.__setFashions(fashions, self.__isTurretDetached)
        self.__setupModels()
        if not isCurrentModelDamaged:
            self.__splineTracks = model_assembler.setupSplineTracks(
                self.__fashion, self.__typeDesc, self.__compoundModel, prereqs)
            self.crashedTracksController = CrashedTrackController(
                self.__typeDesc, self.__fashion)
        else:
            self.__trackScrollCtl = None
        if self.__currentDamageState.effect is not None:
            self.__playEffect(self.__currentDamageState.effect,
                              SpecialKeyPointNames.STATIC)
        self.__chassisDecal.create()
        return

    def showStickers(self, show):
        self.__vehicleStickers.show = show

    def updateTurretVisibility(self):
        self.__requestModelsRefresh()

    def changeVisibility(self, modelVisible):
        self.compoundModel.visible = modelVisible
        self.showStickers(modelVisible)
        if self.crashedTracksController is not None:
            self.crashedTracksController.setVisible(modelVisible)
        return

    def changeDrawPassVisibility(self, visibilityMask):
        colorPassEnabled = visibilityMask & BigWorld.ColorPassBit != 0
        self.compoundModel.visible = visibilityMask
        self.compoundModel.skipColorPass = not colorPassEnabled
        self.showStickers(colorPassEnabled)
        if self.crashedTracksController is not None:
            self.crashedTracksController.setVisible(visibilityMask)
        return

    def onVehicleHealthChanged(self, showEffects=True):
        vehicle = self.__vehicle
        if not vehicle.isAlive() and vehicle.health > 0:
            self.changeEngineMode((0, 0))
        currentState = self.__currentDamageState
        previousState = currentState.state
        isUnderWater = self.waterSensor.isUnderWater
        currentState.update(vehicle.health, vehicle.isCrewActive, isUnderWater)
        if previousState != currentState.state:
            if currentState.effect is not None and showEffects:
                self.__playEffect(currentState.effect)
            if vehicle.health <= 0:
                BigWorld.player().inputHandler.onVehicleDeath(
                    vehicle, currentState.state == 'ammoBayExplosion')
                self.processVehicleDeath(currentState)
                self.__requestModelsRefresh()
            elif not vehicle.isCrewActive:
                self.__onCrewKilled()
        return

    @ComponentSystem.groupCall
    def processVehicleDeath(self, vehicleDamageState):
        pass

    def showAmmoBayEffect(self, mode, fireballVolume):
        if mode == constants.AMMOBAY_DESTRUCTION_MODE.POWDER_BURN_OFF:
            self.__playEffect('ammoBayBurnOff')
            return
        volumes = items.vehicles.g_cache.commonConfig['miscParams'][
            'explosionCandleVolumes']
        candleIdx = 0
        for idx, volume in enumerate(volumes):
            if volume >= fireballVolume:
                break
            candleIdx = idx + 1

        if candleIdx > 0:
            self.__playEffect('explosionCandle%d' % candleIdx)
        else:
            self.__playEffect('explosion')

    def changeEngineMode(self, mode, forceSwinging=False):
        self.__engineMode = mode
        if self.detailedEngineState is not None:
            self.detailedEngineState.mode = self.__engineMode[0]
        if self.__trackScrollCtl is not None:
            self.__trackScrollCtl.setMode(self.__engineMode)
        return None if BattleReplay.isPlaying(
        ) and BattleReplay.g_replayCtrl.isTimeWarpInProgress else None

    def stopSwinging(self):
        if self.swingingAnimator is not None:
            self.swingingAnimator.accelSwingingPeriod = 0.0
        return

    def removeDamageSticker(self, code):
        self.__vehicleStickers.delDamageSticker(code)

    def addDamageSticker(self, code, componentIdx, stickerID, segStart,
                         segEnd):
        self.__vehicleStickers.addDamageSticker(code, componentIdx, stickerID,
                                                segStart, segEnd)

    def receiveShotImpulse(self, direction, impulse):
        if BattleReplay.isPlaying(
        ) and BattleReplay.g_replayCtrl.isTimeWarpInProgress:
            return
        else:
            if not VehicleDamageState.isDamagedModel(
                    self.__currentDamageState.modelState):
                self.swingingAnimator.receiveShotImpulse(direction, impulse)
                if self.crashedTracksController is not None:
                    self.crashedTracksController.receiveShotImpulse(
                        direction, impulse)
            return

    def recoil(self):
        gunNode = self.compoundModel.node(TankNodeNames.GUN_INCLINATION)
        impulseDir = Math.Matrix(gunNode).applyVector(Math.Vector3(0, 0, -1))
        impulseValue = self.__typeDesc.gun.impulse
        self.receiveShotImpulse(impulseDir, impulseValue)
        self.gunRecoil.recoil()
        node = self.compoundModel.node('HP_gunFire')
        gunPos = Math.Matrix(node).translation
        BigWorld.player().inputHandler.onVehicleShaken(
            self.__vehicle, gunPos, impulseDir,
            self.__typeDesc.shot.shell.caliber, ShakeReason.OWN_SHOT_DELAYED)

    def addCrashedTrack(self, isLeft):
        if not self.__vehicle.isAlive():
            return
        else:
            if self.crashedTracksController is not None:
                self.crashedTracksController.addTrack(
                    isLeft, self.isLeftSideFlying
                    if isLeft else self.isRightSideFlying)
            if not self.__vehicle.isEnteringWorld and self.trackCrashAudition:
                self.trackCrashAudition.playCrashSound(isLeft)
            return

    def delCrashedTrack(self, isLeft):
        if self.crashedTracksController is not None:
            self.crashedTracksController.delTrack(isLeft)
        if not self.__vehicle.isEnteringWorld and self.trackCrashAudition and self.__vehicle.isPlayerVehicle:
            self.trackCrashAudition.playCrashSound(isLeft, True)
        return

    def __prepareOutfit(self, outfitCD):
        if self.__outfit:
            return
        outfit = Outfit(outfitCD)
        player = BigWorld.player()
        forceHistorical = player.isHistoricallyAccurate and player.playerVehicleID != self.__vID and not outfit.isHistorical(
        )
        self.__outfit = Outfit() if forceHistorical else outfit

    def __applyVehicleOutfit(self):
        camouflages.updateFashions(
            self.__fashions, self.__typeDesc,
            self.__currentDamageState.isCurrentModelDamaged, self.__outfit)

    def getBounds(self, partIdx):
        return self.collisions.getBoundingBox(
            partIdx) if self.collisions is not None else (Math.Vector3(
                0.0, 0.0, 0.0), Math.Vector3(0.0, 0.0, 0.0), 0)

    def __requestModelsRefresh(self):
        modelsSetParams = self.modelsSetParams
        assembler = model_assembler.prepareCompoundAssembler(
            self.__typeDesc, modelsSetParams, self.__vehicle.spaceID,
            self.__vehicle.isTurretDetached)
        BigWorld.loadResourceListBG((assembler, ),
                                    makeCallbackWeak(self.__onModelsRefresh,
                                                     modelsSetParams.state),
                                    loadingPriority(self.__vehicle.id))

    def __onModelsRefresh(self, modelState, resourceList):
        if BattleReplay.isFinished():
            return
        elif self.__vehicle is None:
            return
        else:
            prevTurretYaw = Math.Matrix(self.turretMatrix).yaw
            prevGunPitch = Math.Matrix(self.gunMatrix).pitch
            vehicle = self.__vehicle
            newCompoundModel = resourceList[self.__typeDesc.name]
            self.deactivate(False)
            self.shadowManager.reattachCompoundModel(vehicle,
                                                     self.__compoundModel,
                                                     newCompoundModel)
            self.__compoundModel = newCompoundModel
            self.__isTurretDetached = vehicle.isTurretDetached
            self.__prepareSystemsForDamagedVehicle(vehicle,
                                                   self.__isTurretDetached)
            self.__setupModels()
            self.setVehicle(vehicle)
            self.activate()
            self.__reattachComponents(self.__compoundModel,
                                      self.__weaponEnergy)
            self.__filter.syncGunAngles(prevTurretYaw, prevGunPitch)
            model_assembler.setupTurretRotations(self)
            return

    def __setupModels(self):
        self.__isAlive = not self.__currentDamageState.isCurrentModelDamaged
        if self.__isAlive:
            _, gunLength = self.computeVehicleHeight()
            self.__weaponEnergy = gunLength * self.__typeDesc.shot.shell.caliber
        if MAX_DISTANCE > 0:
            transform = self.__typeDesc.chassis.AODecals[0]
            self.__attachSplodge(
                BigWorld.Splodge(transform, MAX_DISTANCE,
                                 self.__typeDesc.chassis.hullPosition.y))

    def __reattachComponents(self, model, weaponEnergy):
        self.__boundEffects.reattachTo(model)
        if self.__effectsPlayer is not None:
            self.__effectsPlayer.reattachTo(model)
        if self.engineAudition is not None:
            self.engineAudition.setWeaponEnergy(weaponEnergy)
            self.engineAudition.attachToModel(model)
        return

    def __playEffect(self, kind, *modifs):
        self.__stopEffects()
        if kind == 'empty' or self.__vehicle is None:
            return
        else:
            enableDecal = True
            if kind in ('explosion', 'destruction') and self.isFlying:
                enableDecal = False
            if self.isUnderwater:
                if kind not in ('submersionDeath', ):
                    return
            effects = self.typeDescriptor.type.effects[kind]
            if not effects:
                return
            vehicle = self.__vehicle
            effects = random.choice(effects)
            self.__effectsPlayer = EffectsListPlayer(
                effects[1],
                effects[0],
                showShockWave=vehicle.isPlayerVehicle,
                showFlashBang=vehicle.isPlayerVehicle,
                isPlayer=vehicle.isPlayerVehicle,
                showDecal=enableDecal,
                start=vehicle.position + Math.Vector3(0.0, 1.0, 0.0),
                end=vehicle.position + Math.Vector3(0.0, -1.0, 0.0),
                entity_id=vehicle.id)
            self.__effectsPlayer.play(self.__compoundModel, *modifs)
            return

    def __stopEffects(self):
        if self.__effectsPlayer is not None:
            self.__effectsPlayer.stop()
        self.__effectsPlayer = None
        return

    def __onCrewKilled(self):
        self.__destroyEngineAudition()
        if self.customEffectManager is not None:
            self.customEffectManager = None
        return

    def onWaterSplash(self, waterHitPoint, isHeavySplash):
        effectName = 'waterCollisionHeavy' if isHeavySplash else 'waterCollisionLight'
        self.__vehicle.showCollisionEffect(waterHitPoint, effectName,
                                           Math.Vector3(0.0, 1.0, 0.0))

    def onUnderWaterSwitch(self, isUnderWater):
        if isUnderWater and self.__effectsPlayer is not None and self.__currentDamageState.effect not in (
                'submersionDeath', ):
            self.__stopEffects()
        extra = self.__vehicle.typeDescriptor.extrasDict['fire']
        if extra.isRunningFor(self.__vehicle):
            extra.checkUnderwater(self.__vehicle, isUnderWater)
        return

    def updateTracksScroll(self, leftScroll, rightScroll):
        self.__leftTrackScroll = leftScroll
        self.__rightTrackScroll = rightScroll
        if self.__trackScrollCtl is not None:
            self.__trackScrollCtl.setExternal(leftScroll, rightScroll)
        return

    def __onPeriodicTimerEngine(self):
        if self.detailedEngineState is None or self.engineAudition is None:
            return
        else:
            if self.siegeEffects is not None:
                self.siegeEffects.tick(_PERIODIC_TIME_ENGINE)
            return _PERIODIC_TIME_ENGINE

    def __onPeriodicTimer(self):
        timeStamp = BigWorld.wg_getFrameTimestamp()
        if CompoundAppearance.frameTimeStamp >= timeStamp:
            self.__periodicTimerID = BigWorld.callback(0.0,
                                                       self.__onPeriodicTimer)
            return
        else:
            CompoundAppearance.frameTimeStamp = timeStamp
            self.__periodicTimerID = BigWorld.callback(_PERIODIC_TIME,
                                                       self.__onPeriodicTimer)
            if self.__vehicle is None:
                return
            vehicle = self.__vehicle
            if self.peripheralsController is not None:
                self.peripheralsController.update(vehicle,
                                                  self.crashedTracksController)
            if not vehicle.isAlive():
                return
            distanceFromPlayer = self.lodCalculator.lodDistance
            self.__updateCurrTerrainMatKinds()
            if not self.__vehicle.isPlayerVehicle:
                if self.siegeEffects is not None:
                    self.siegeEffects.tick(_PERIODIC_TIME)
            self.__updateEffectsLOD(distanceFromPlayer)
            if self.customEffectManager:
                self.customEffectManager.update()
            return

    def __onPeriodicTimerDirt(self):
        if self.__fashion is None:
            return
        else:
            dt = 1.0
            distanceFromPlayer = self.lodCalculator.lodDistance
            if 0.0 <= distanceFromPlayer < _PERIODIC_TIME_DIRT[1][1]:
                time = BigWorld.time()
                simDt = time - self.__dirtUpdateTime
                if simDt > 0.0:
                    if self.dirtComponent:
                        roll = Math.Matrix(self.__compoundModel.matrix).roll
                        hasContact = 0
                        waterHeight = self.waterHeight
                        if math.fabs(roll) > math.radians(120.0):
                            hasContact = 2
                            if self.waterSensor.isInWater:
                                waterHeight = 1.0
                        elif self.__trackScrollCtl is not None:
                            hasContact = 0 if self.__trackScrollCtl.hasContact(
                            ) else 1
                        self.dirtComponent.update(
                            self.filter.averageSpeed, waterHeight,
                            self.waterSensor.waterHeightWorld,
                            self.__currTerrainMatKind[2], hasContact, simDt)
                    self.__dirtUpdateTime = time
                if distanceFromPlayer <= _PERIODIC_TIME_DIRT[1][0]:
                    dt = _PERIODIC_TIME_DIRT[0][0]
                else:
                    dt = _PERIODIC_TIME_DIRT[0][
                        0] + _DIRT_ALPHA * distanceFromPlayer
            return dt

    def __updateEffectsLOD(self, distanceFromPlayer):
        if self.customEffectManager:
            enableExhaust = distanceFromPlayer <= _LOD_DISTANCE_EXHAUST and not self.isUnderwater
            enableTrails = distanceFromPlayer <= _LOD_DISTANCE_TRAIL_PARTICLES and BigWorld.wg_isVehicleDustEnabled(
            )
            self.customEffectManager.enable(enableTrails,
                                            EffectSettings.SETTING_DUST)
            self.customEffectManager.enable(enableExhaust,
                                            EffectSettings.SETTING_EXHAUST)

    def __updateCurrTerrainMatKinds(self):
        if self.terrainMatKindSensor is None:
            return
        else:
            matKinds = self.terrainMatKindSensor.matKinds
            matKindsCount = len(matKinds)
            for i in xrange(_MATKIND_COUNT):
                matKind = matKinds[i] if i < matKindsCount else 0
                self.__currTerrainMatKind[i] = matKind
                effectIndex = calcEffectMaterialIndex(matKind)
                effectMaterialName = ''
                if effectIndex is not None:
                    effectMaterialName = material_kinds.EFFECT_MATERIALS[
                        effectIndex]
                self.__terrainEffectMaterialNames[i] = effectMaterialName

            if self.vehicleTraces is not None:
                self.vehicleTraces.setCurrTerrainMatKinds(
                    self.__currTerrainMatKind[0], self.__currTerrainMatKind[1])
            return

    def switchFireVibrations(self, bStart):
        if self.peripheralsController is not None:
            self.peripheralsController.switchFireVibrations(bStart)
        return

    def executeHitVibrations(self, hitEffectCode):
        if self.peripheralsController is not None:
            self.peripheralsController.executeHitVibrations(hitEffectCode)
        return

    def executeRammingVibrations(self, matKind=None):
        if self.peripheralsController is not None:
            self.peripheralsController.executeRammingVibrations(
                self.__vehicle, matKind)
        return

    def executeShootingVibrations(self, caliber):
        if self.peripheralsController is not None:
            self.peripheralsController.executeShootingVibrations(caliber)
        return

    def executeCriticalHitVibrations(self, vehicle, extrasName):
        if self.peripheralsController is not None:
            self.peripheralsController.executeCriticalHitVibrations(
                vehicle, extrasName)
        return

    def deviceStateChanged(self, deviceName, state):
        if not self.isUnderwater and self.detailedEngineState is not None and deviceName == 'engine':
            engineState = engine_state.getEngineStateFromName(state)
            self.detailedEngineState.engineState = engineState
        return

    def __linkCompound(self):
        vehicle = self.__vehicle
        vehicle.model = None
        vehicle.model = self.__compoundModel
        vehicleMatrix = vehicle.matrix
        self.__compoundModel.matrix = vehicleMatrix
        return

    def __createStickers(self):
        if self.__vehicleStickers is not None:
            return
        else:
            insigniaRank = self.__vehicle.publicInfo['marksOnGun']
            self.__vehicleStickers = VehicleStickers(self.__typeDesc,
                                                     insigniaRank,
                                                     self.__outfit)
            clanID = BigWorld.player().arena.vehicles[
                self.__vehicle.id]['clanDBID']
            self.__vehicleStickers.setClanID(clanID)
            return

    def __attachStickers(self,
                         alpha=_DEFAULT_STICKERS_ALPHA,
                         emblemsOnly=False):
        self.__vehicleStickers.alpha = alpha
        self.__vehicleStickers.attach(
            self.compoundModel,
            self.__currentDamageState.isCurrentModelDamaged, not emblemsOnly)

    def __createAndAttachStickers(self):
        isCurrentModelDamaged = self.__currentDamageState.isCurrentModelDamaged
        stickersAlpha = _DEFAULT_STICKERS_ALPHA
        if isCurrentModelDamaged:
            stickersAlpha = items.vehicles.g_cache.commonConfig['miscParams'][
                'damageStickerAlpha']
        self.__createStickers()
        self.__attachStickers(stickersAlpha, isCurrentModelDamaged)

    def __attachSplodge(self, splodge):
        node = self.__compoundModel.node(TankPartNames.HULL)
        if splodge:
            self.__splodge = splodge
            node.attach(splodge)

    def __disableStipple(self):
        self.compoundModel.stipple = False

    def computeVehicleHeight(self):
        gunLength = 0.0
        height = 0.0
        if self.collisions is not None:
            desc = self.__typeDesc
            hullBB = self.collisions.getBoundingBox(
                TankPartNames.getIdx(TankPartNames.HULL))
            turretBB = self.collisions.getBoundingBox(
                TankPartNames.getIdx(TankPartNames.TURRET))
            gunBB = self.collisions.getBoundingBox(
                TankPartNames.getIdx(TankPartNames.GUN))
            hullTopY = desc.chassis.hullPosition[1] + hullBB[1][1]
            turretTopY = desc.chassis.hullPosition[
                1] + desc.hull.turretPositions[0][1] + turretBB[1][1]
            gunTopY = desc.chassis.hullPosition[1] + desc.hull.turretPositions[
                0][1] + desc.turret.gunPosition[1] + gunBB[1][1]
            gunLength = math.fabs(gunBB[1][2] - gunBB[0][2])
            height = max(hullTopY, max(turretTopY, gunTopY))
        return (height, gunLength)

    def setupGunMatrixTargets(self, target=None):
        if target is None:
            target = self.__filter
        self.turretMatrix.target = target.turretMatrix
        self.gunMatrix.target = target.gunMatrix
        return

    def onFriction(self, otherID, frictionPoint, state):
        if self.frictionAudition is not None:
            self.frictionAudition.processFriction(otherID, frictionPoint,
                                                  state)
        return

    def assembleStipple(self):
        compound = self.compoundModel
        compound.matrix = Math.Matrix(compound.matrix)
        hullNode = compound.node(TankPartNames.HULL)
        compound.node(TankPartNames.HULL, hullNode.localMatrix)
        turretRotation = compound.node(TankPartNames.TURRET)
        if turretRotation is not None:
            compound.node(TankPartNames.TURRET, turretRotation.localMatrix)
        gunInclination = compound.node(TankNodeNames.GUN_INCLINATION)
        if gunInclination is not None:
            compound.node(TankNodeNames.GUN_INCLINATION,
                          gunInclination.localMatrix)
        gunRecoil = compound.node(TankNodeNames.GUN_RECOIL)
        if gunRecoil is not None:
            compound.node(TankNodeNames.GUN_RECOIL, gunRecoil.localMatrix)
        self.fashions = VehiclePartsTuple(None, None, None, None)
        return

    def onSiegeStateChanged(self, newState):
        if self.engineAudition is not None:
            self.engineAudition.onSiegeStateChanged(newState)
        if self.suspensionController is not None:
            self.suspensionController.onSiegeStateChanged(newState)
        if self.suspensionSound is not None:
            self.__suspensionSound.vehicleState = newState
        if self.siegeEffects is not None:
            self.siegeEffects.onSiegeStateChanged(newState)
        return

    def __onCameraChanged(self, cameraName, currentVehicleId=None):
        if self.engineAudition is not None:
            self.engineAudition.onCameraChanged(
                cameraName,
                currentVehicleId if currentVehicleId is not None else 0)
        return

    def __onEngineStateGearUp(self):
        if self.customEffectManager is not None:
            self.customEffectManager.onGearUp()
        if self.engineAudition is not None:
            self.engineAudition.onEngineGearUp()
        return

    def addCameraCollider(self):
        collider = self.collisions
        if collider is not None:
            colliderData = (collider.getColliderID(),
                            (TankPartNames.getIdx(TankPartNames.HULL),
                             TankPartNames.getIdx(TankPartNames.TURRET)))
            BigWorld.appendCameraCollider(colliderData)
        return

    def removeCameraCollider(self):
        collider = self.collisions
        if collider is not None:
            BigWorld.removeCameraCollider(collider.getColliderID())
        return
class CommonTankAppearance(ScriptGameObject):
    compoundModel = property(lambda self: self._compoundModel)
    boundEffects = property(lambda self: self.__boundEffects)
    fashions = property(lambda self: self.__fashions)
    fashion = property(lambda self: self.fashions.chassis)
    typeDescriptor = property(lambda self: self.__typeDesc)
    id = property(lambda self: self.__vID)
    isAlive = property(lambda self: self.__isAlive)
    isObserver = property(lambda self: self.__isObserver)
    outfit = property(lambda self: self.__outfit)
    renderState = property(lambda self: self.__renderState)

    def _setFashions(self, fashions, isTurretDetached=False):
        self.__fashions = fashions
        if isTurretDetached:
            self.compoundModel.setupFashions((fashions.chassis, fashions.hull))
        else:
            self.compoundModel.setupFashions(fashions)

    terrainMatKind = property(lambda self: self.__currTerrainMatKind)
    terrainGroundType = property(lambda self: self.__currTerrainGroundType)
    terrainEffectMaterialNames = property(lambda self: self.__terrainEffectMaterialNames)
    isInWater = property(lambda self: self.waterSensor.isInWater)
    isUnderwater = property(lambda self: self.waterSensor.isUnderWater)
    waterHeight = property(lambda self: self.waterSensor.waterHeight)
    damageState = property(lambda self: self.__currentDamageState)
    modelsSetParams = property(lambda self: ModelsSetParams(self.outfit.modelsSet, self.damageState.modelState, self.__attachments))
    splineTracks = property(lambda self: self._splineTracks)
    isFlying = property(lambda self: self.flyingInfoProvider is not None and self.flyingInfoProvider.isFlying)
    isLeftSideFlying = property(lambda self: self.flyingInfoProvider is not None and self.flyingInfoProvider.isLeftSideFlying)
    isRightSideFlying = property(lambda self: self.flyingInfoProvider is not None and self.flyingInfoProvider.isRightSideFlying)
    trackScrollController = property(lambda self: self.__trackScrollCtl)
    wheelsState = property(lambda self: 0)
    burnoutLevel = property(lambda self: 0.0)
    filterRetrievers = property(lambda self: self.__filterRetrievers)
    allLodCalculators = property(lambda self: self.__allLodCalculators)
    transmissionSlip = property(lambda self: self._commonSlip)
    transmissionScroll = property(lambda self: self._commonScroll)
    vehicleStickers = property(lambda self: self._vehicleStickers)
    isTurretDetached = property(lambda self: self._isTurretDetached)
    _weaponEnergy = property(lambda self: self.__weaponEnergy)
    filter = AutoProperty()
    areaTriggerTarget = ComponentDescriptor()
    burnoutProcessor = ComponentDescriptor()
    c11nComponent = ComponentDescriptor()
    collisionObstaclesCollector = ComponentDescriptor()
    collisions = ComponentDescriptor()
    crashedTracksController = ComponentDescriptor()
    customEffectManager = ComponentDescriptor()
    detailedEngineState = ComponentDescriptor()
    dirtComponent = ComponentDescriptor()
    engineAudition = ComponentDescriptor()
    flyingInfoProvider = ComponentDescriptor()
    frictionAudition = ComponentDescriptor()
    gearbox = ComponentDescriptor()
    gunLinkedNodesAnimator = ComponentDescriptor()
    gunRecoil = ComponentDescriptor()
    gunAnimators = [ComponentDescriptor()]
    gunRotatorAudition = ComponentDescriptor()
    hullAimingController = ComponentDescriptor()
    leveredSuspension = ComponentDescriptor()
    lodCalculator = ComponentDescriptor()
    shadowManager = ComponentDescriptor()
    siegeEffects = ComponentDescriptor()
    suspension = ComponentDescriptor()
    suspensionSound = ComponentDescriptor()
    swingingAnimator = ComponentDescriptor()
    terrainMatKindSensor = ComponentDescriptor()
    tessellationCollisionSensor = ComponentDescriptor()
    trackNodesAnimator = ComponentDescriptor()
    tracks = ComponentDescriptor()
    transform = ComponentDescriptor()
    vehicleTraces = ComponentDescriptor()
    waterSensor = ComponentDescriptor()
    wheeledLodCalculator = ComponentDescriptor()
    wheelsAnimator = ComponentDescriptor()
    flagComponent = ComponentDescriptor()

    def __init__(self, spaceID):
        ScriptGameObject.__init__(self, spaceID)
        self._vehicle = None
        self.__filter = None
        self.__typeDesc = None
        self.crashedTracksController = None
        self.__currentDamageState = VehicleDamageState()
        self.__currTerrainMatKind = [-1] * MATKIND_COUNT
        self.__currTerrainGroundType = [-1] * MATKIND_COUNT
        self.__terrainEffectMaterialNames = [''] * MATKIND_COUNT
        self._chassisDecal = VehicleDecal(self)
        self.__splodge = None
        self.__boundEffects = None
        self._splineTracks = None
        self.flyingInfoProvider = self.createComponent(Vehicular.FlyingInfoProvider)
        self.__trackScrollCtl = BigWorld.PyTrackScroll()
        self.__trackScrollCtl.setFlyingInfo(DataLinks.createBoolLink(self.flyingInfoProvider, 'isLeftSideFlying'), DataLinks.createBoolLink(self.flyingInfoProvider, 'isRightSideFlying'))
        self.__weaponEnergy = 0.0
        self.__outfit = None
        self.__systemStarted = False
        self.__isAlive = True
        self._isTurretDetached = False
        self.__isObserver = False
        self.__attachments = []
        self.__modelAnimators = []
        self.turretMatrix = None
        self.gunMatrix = None
        self.__allLodCalculators = []
        self._commonScroll = 0.0
        self._commonSlip = 0.0
        self._compoundModel = None
        self.__fashions = None
        self.__filterRetrievers = []
        self._vehicleStickers = None
        self.__vID = 0
        self.__renderState = None
        self.__frameTimestamp = 0
        self.__periodicTimerID = None
        return

    def prerequisites(self, typeDescriptor, vID, health, isCrewActive, isTurretDetached, outfitCD, renderState=None):
        self.damageState.update(health, isCrewActive, False)
        self.__typeDesc = typeDescriptor
        self.__vID = vID
        self._isTurretDetached = isTurretDetached
        self.__outfit = self._prepareOutfit(outfitCD)
        if self.damageState.isCurrentModelUndamaged:
            self.__attachments = camouflages.getAttachments(self.outfit, self.typeDescriptor)
        self.__renderState = renderState
        prereqs = self.typeDescriptor.prerequisites(True)
        prereqs.extend(camouflages.getCamoPrereqs(self.outfit, self.typeDescriptor))
        prereqs.extend(camouflages.getModelAnimatorsPrereqs(self.outfit, self.worldID))
        prereqs.extend(camouflages.getAttachmentsAnimatorsPrereqs(self.__attachments, self.worldID))
        splineDesc = self.typeDescriptor.chassis.splineDesc
        if splineDesc is not None:
            modelsSet = self.outfit.modelsSet
            prereqs.append(splineDesc.segmentModelLeft(modelsSet))
            prereqs.append(splineDesc.segmentModelRight(modelsSet))
            segment2ModelLeft = splineDesc.segment2ModelLeft(modelsSet)
            if segment2ModelLeft is not None:
                prereqs.append(segment2ModelLeft)
            segment2ModelRight = splineDesc.segment2ModelRight(modelsSet)
            if segment2ModelRight is not None:
                prereqs.append(segment2ModelRight)
        modelsSetParams = self.modelsSetParams
        compoundAssembler = model_assembler.prepareCompoundAssembler(self.typeDescriptor, modelsSetParams, self.worldID, self.isTurretDetached, renderState=self.renderState)
        prereqs.append(compoundAssembler)
        if renderState == RenderStates.OVERLAY_COLLISION:
            self.damageState.update(0, isCrewActive, False)
        if not isTurretDetached:
            bspModels = ((TankPartNames.getIdx(TankPartNames.CHASSIS), typeDescriptor.chassis.hitTester.bspModelName),
             (TankPartNames.getIdx(TankPartNames.HULL), typeDescriptor.hull.hitTester.bspModelName),
             (TankPartNames.getIdx(TankPartNames.TURRET), typeDescriptor.turret.hitTester.bspModelName),
             (TankPartNames.getIdx(TankPartNames.GUN), typeDescriptor.gun.hitTester.bspModelName))
        else:
            bspModels = ((TankPartNames.getIdx(TankPartNames.CHASSIS), typeDescriptor.chassis.hitTester.bspModelName), (TankPartNames.getIdx(TankPartNames.HULL), typeDescriptor.hull.hitTester.bspModelName))
        collisionAssembler = BigWorld.CollisionAssembler(bspModels, self.worldID)
        prereqs.append(collisionAssembler)
        physicalTracksBuilders = self.typeDescriptor.chassis.physicalTracks
        for name, builders in physicalTracksBuilders.iteritems():
            for index, builder in enumerate(builders):
                prereqs.append(builder.createLoader(self.worldID, '{0}{1}PhysicalTrack'.format(name, index), modelsSetParams.skin))

        return prereqs

    def construct(self, isPlayer, resourceRefs):
        self.collisions = resourceRefs['collisionAssembler']
        self.typeDescriptor.chassis.hitTester.bbox = self.collisions.getBoundingBox(TankPartNames.getIdx(TankPartNames.CHASSIS))
        self.typeDescriptor.hull.hitTester.bbox = self.collisions.getBoundingBox(TankPartNames.getIdx(TankPartNames.HULL))
        self.typeDescriptor.turret.hitTester.bbox = self.collisions.getBoundingBox(TankPartNames.getIdx(TankPartNames.TURRET))
        self.typeDescriptor.gun.hitTester.bbox = self.collisions.getBoundingBox(TankPartNames.getIdx(TankPartNames.GUN))
        self.__isObserver = 'observer' in self.typeDescriptor.type.tags
        self._compoundModel = resourceRefs[self.typeDescriptor.name]
        self.__boundEffects = bound_effects.ModelBoundEffects(self.compoundModel)
        isCurrentModelDamaged = self.damageState.isCurrentModelDamaged
        fashions = camouflages.prepareFashions(isCurrentModelDamaged)
        if not isCurrentModelDamaged:
            model_assembler.setupTracksFashion(self.typeDescriptor, fashions.chassis)
        self._setFashions(fashions, self.isTurretDetached)
        self._setupModels()
        if not isCurrentModelDamaged:
            modelsSet = self.outfit.modelsSet
            self._splineTracks = model_assembler.setupSplineTracks(self.fashion, self.typeDescriptor, self.compoundModel, resourceRefs, modelsSet)
            self.crashedTracksController = CrashedTrackController(self.typeDescriptor, self.fashion, modelsSet)
        else:
            self.__trackScrollCtl = None
        self._chassisDecal.create()
        self.__modelAnimators = camouflages.getModelAnimators(self.outfit, self.typeDescriptor, self.worldID, resourceRefs, self.compoundModel)
        if self.modelsSetParams.state == 'undamaged':
            self.__modelAnimators.extend(camouflages.getAttachmentsAnimators(self.__attachments, self.worldID, resourceRefs, self.compoundModel))
        self.transform = self.createComponent(GenericComponents.TransformComponent, Math.Vector3(0, 0, 0))
        self.areaTriggerTarget = self.createComponent(Triggers.AreaTriggerTarget)
        self.__filter = model_assembler.createVehicleFilter(self.typeDescriptor)
        compoundModel = self.compoundModel
        if self.isAlive:
            self.detailedEngineState, self.gearbox = model_assembler.assembleDrivetrain(self, isPlayer)
            if not gEffectsDisabled():
                self.customEffectManager = CustomEffectManager(self)
                if self.typeDescriptor.hasSiegeMode:
                    self.siegeEffects = SiegeEffectsController(self, isPlayer)
                model_assembler.assembleVehicleAudition(isPlayer, self)
                self.detailedEngineState.onEngineStart = self._onEngineStart
                self.detailedEngineState.onStateChanged = self.engineAudition.onEngineStateChanged
            if isPlayer:
                turret = self.typeDescriptor.turret
                gunRotatorAudition = self.createComponent(Vehicular.GunRotatorAudition, turret.turretRotatorSoundManual, turret.weight / 1000.0, compoundModel.node(TankPartNames.TURRET))
                gunRotatorAudition.vehicleMatrixLink = self.compoundModel.root
                gunRotatorAudition.damaged = lambda : self.turretDamaged()
                gunRotatorAudition.maxTurretRotationSpeed = lambda : self.maxTurretRotationSpeed()
                self.gunRotatorAudition = gunRotatorAudition
                self.frictionAudition = self.createComponent(Vehicular.FrictionAudition, TANK_FRICTION_EVENT)
        isLodTopPriority = isPlayer
        lodCalcInst = self.createComponent(Vehicular.LodCalculator, DataLinks.linkMatrixTranslation(compoundModel.matrix), True, VEHICLE_PRIORITY_GROUP, isLodTopPriority)
        self.lodCalculator = lodCalcInst
        self.allLodCalculators.append(lodCalcInst)
        lodLink = DataLinks.createFloatLink(lodCalcInst, 'lodDistance')
        lodStateLink = lodCalcInst.lodStateLink
        if IS_EDITOR:
            matrixBinding = None
            changeCamera = None
        else:
            matrixBinding = BigWorld.player().consistentMatrices.onVehicleMatrixBindingChanged
            changeCamera = BigWorld.player().inputHandler.onCameraChanged
        self.shadowManager = VehicleShadowManager(compoundModel, matrixBinding, changeCamera)
        if not self.damageState.isCurrentModelDamaged:
            self.__assembleNonDamagedOnly(resourceRefs, isPlayer, lodLink, lodStateLink)
            dirtEnabled = BigWorld.WG_dirtEnabled() and 'HD' in self.typeDescriptor.type.tags
            if dirtEnabled and self.fashions is not None:
                dirtHandlers = [BigWorld.PyDirtHandler(True, compoundModel.node(TankPartNames.CHASSIS).position.y),
                 BigWorld.PyDirtHandler(False, compoundModel.node(TankPartNames.HULL).position.y),
                 BigWorld.PyDirtHandler(False, compoundModel.node(TankPartNames.TURRET).position.y),
                 BigWorld.PyDirtHandler(False, compoundModel.node(TankPartNames.GUN).position.y)]
                modelHeight, _ = self.computeVehicleHeight()
                self.dirtComponent = self.createComponent(Vehicular.DirtComponent, dirtHandlers, modelHeight)
                for fashionIdx, _ in enumerate(TankPartNames.ALL):
                    self.fashions[fashionIdx].addMaterialHandler(dirtHandlers[fashionIdx])
                    self.fashions[fashionIdx].addTrackMaterialHandler(dirtHandlers[fashionIdx])

        model_assembler.setupTurretRotations(self)
        self.waterSensor = model_assembler.assembleWaterSensor(self.typeDescriptor, self, lodStateLink, self.worldID)
        if self.engineAudition is not None:
            self.engineAudition.setIsUnderwaterInfo(DataLinks.createBoolLink(self.waterSensor, 'isUnderWater'))
            self.engineAudition.setIsInWaterInfo(DataLinks.createBoolLink(self.waterSensor, 'isInWater'))
        self.__postSetupFilter()
        compoundModel.setPartBoundingBoxAttachNode(TankPartIndexes.GUN, TankNodeNames.GUN_INCLINATION)
        camouflages.updateFashions(self)
        model_assembler.assembleCustomLogicComponents(self, self.__attachments, self.__modelAnimators)
        return

    def destroy(self):
        self.flagComponent = None
        self.__modelAnimators = []
        self._destroySystems()
        fashions = VehiclePartsTuple(None, None, None, None)
        self._setFashions(fashions, self._isTurretDetached)
        super(CommonTankAppearance, self).destroy()
        self.__typeDesc = None
        if self.boundEffects is not None:
            self.boundEffects.destroy()
        self._vehicleStickers = None
        self._chassisDecal.destroy()
        self._chassisDecal = None
        self._compoundModel = None
        return

    def activate(self):
        if self.collisions is not None and self.isTurretDetached:
            self.collisions.removeAttachment(TankPartNames.getIdx(TankPartNames.TURRET))
            self.collisions.removeAttachment(TankPartNames.getIdx(TankPartNames.GUN))
        super(CommonTankAppearance, self).activate()
        if not self.isObserver:
            self._chassisDecal.attach()
        self._createAndAttachStickers()
        if not self.isObserver:
            if not self.damageState.isCurrentModelDamaged and not self.__systemStarted:
                self._startSystems()
            self.filter.enableLagDetection(not self.damageState.isCurrentModelDamaged)
            if self.__periodicTimerID is not None:
                BigWorld.cancelCallback(self.__periodicTimerID)
            self.__periodicTimerID = BigWorld.callback(PERIODIC_UPDATE_TIME, self.__onPeriodicTimer)
        self.setupGunMatrixTargets(self.filter)
        for lodCalculator in self.allLodCalculators:
            lodCalculator.setupPosition(DataLinks.linkMatrixTranslation(self.compoundModel.matrix))

        for modelAnimator in self.__modelAnimators:
            modelAnimator.animator.start()

        if hasattr(self.filter, 'placingCompensationMatrix') and self.swingingAnimator is not None:
            self.swingingAnimator.placingCompensationMatrix = self.filter.placingCompensationMatrix
            self.swingingAnimator.worldMatrix = self.compoundModel.matrix
        if self.isObserver:
            self.compoundModel.visible = False
        if self.collisions is not None:
            chassisColisionMatrix, gunNodeName = self._vehicleColliderInfo
            collisionData = ((TankPartNames.getIdx(TankPartNames.HULL), self.compoundModel.node(TankPartNames.HULL)),
             (TankPartNames.getIdx(TankPartNames.TURRET), self.compoundModel.node(TankPartNames.TURRET)),
             (TankPartNames.getIdx(TankPartNames.CHASSIS), chassisColisionMatrix),
             (TankPartNames.getIdx(TankPartNames.GUN), self.compoundModel.node(gunNodeName)))
            self.collisions.connect(self.id, ColliderTypes.VEHICLE_COLLIDER, collisionData)
        return

    def deactivate(self):
        for modelAnimator in self.__modelAnimators:
            modelAnimator.animator.stop()

        if self.damageState and self.damageState.isCurrentModelDamaged:
            self.__modelAnimators = []
        self.shadowManager.unregisterCompoundModel(self.compoundModel)
        if self.__systemStarted:
            self._stopSystems()
        super(CommonTankAppearance, self).deactivate()
        self._chassisDecal.detach()
        self.filter.enableLagDetection(False)
        if self.vehicleStickers:
            self.vehicleStickers.detach()

    def setupGunMatrixTargets(self, target):
        self.turretMatrix = target.turretMatrix
        self.gunMatrix = target.gunMatrix

    def receiveShotImpulse(self, direction, impulse):
        if not VehicleDamageState.isDamagedModel(self.damageState.modelState):
            self.swingingAnimator.receiveShotImpulse(direction, impulse)
            if self.crashedTracksController is not None:
                self.crashedTracksController.receiveShotImpulse(direction, impulse)
        return

    def computeVehicleHeight(self):
        gunLength = 0.0
        height = 0.0
        if self.collisions is not None:
            desc = self.typeDescriptor
            hullBB = self.collisions.getBoundingBox(TankPartNames.getIdx(TankPartNames.HULL))
            turretBB = self.collisions.getBoundingBox(TankPartNames.getIdx(TankPartNames.TURRET))
            gunBB = self.collisions.getBoundingBox(TankPartNames.getIdx(TankPartNames.GUN))
            hullTopY = desc.chassis.hullPosition[1] + hullBB[1][1]
            turretTopY = desc.chassis.hullPosition[1] + desc.hull.turretPositions[0][1] + turretBB[1][1]
            gunTopY = desc.chassis.hullPosition[1] + desc.hull.turretPositions[0][1] + desc.turret.gunPosition[1] + gunBB[1][1]
            gunLength = math.fabs(gunBB[1][2] - gunBB[0][2])
            height = max(hullTopY, max(turretTopY, gunTopY))
        return (height, gunLength)

    def onWaterSplash(self, waterHitPoint, isHeavySplash):
        pass

    def onUnderWaterSwitch(self, isUnderWater):
        pass

    def getWheelsSteeringMax(self):
        pass

    def _prepareOutfit(self, outfitCD):
        outfitComponent = camouflages.getOutfitComponent(outfitCD)
        return Outfit(component=outfitComponent, vehicleCD=self.typeDescriptor.makeCompactDescr())

    def _setupModels(self):
        self.__isAlive = not self.damageState.isCurrentModelDamaged
        if self.isAlive:
            _, gunLength = self.computeVehicleHeight()
            self.__weaponEnergy = gunLength * self.typeDescriptor.shot.shell.caliber
        if MAX_DISTANCE > 0 and not self.isObserver:
            transform = self.typeDescriptor.chassis.AODecals[0]
            splodge = BigWorld.Splodge(transform, MAX_DISTANCE, self.typeDescriptor.chassis.hullPosition.y)
            if splodge:
                self.__splodge = splodge
                node = self.compoundModel.node(TankPartNames.HULL)
                node.attach(splodge)

    def _createStickers(self):
        return VehicleStickers(self.typeDescriptor, 0, self.outfit)

    @property
    def _vehicleColliderInfo(self):
        chassisColisionMatrix = self.compoundModel.matrix
        if self.damageState.isCurrentModelDamaged:
            gunNodeName = 'gun'
        else:
            gunNodeName = TankNodeNames.GUN_INCLINATION
        return (chassisColisionMatrix, gunNodeName)

    def _startSystems(self):
        self.__systemStarted = True
        if self.flyingInfoProvider is not None:
            self.flyingInfoProvider.setData(self.filter, self.suspension)
        if self.trackScrollController is not None:
            self.trackScrollController.activate()
            self.trackScrollController.setData(self.filter)
        if self.engineAudition is not None:
            self.engineAudition.setWeaponEnergy(self._weaponEnergy)
            self.engineAudition.attachToModel(self.compoundModel)
        if self.hullAimingController is not None:
            self.hullAimingController.setData(self.filter, self.typeDescriptor)
        return

    def _stopSystems(self):
        self.__systemStarted = False
        if self.flyingInfoProvider is not None:
            self.flyingInfoProvider.setData(None, None)
        if self.trackScrollController is not None:
            self.trackScrollController.deactivate()
            self.trackScrollController.setData(None)
        if self.__periodicTimerID is not None:
            BigWorld.cancelCallback(self.__periodicTimerID)
            self.__periodicTimerID = None
        return

    def _destroySystems(self):
        self.__systemStarted = False
        if self.trackScrollController is not None:
            self.trackScrollController.deactivate()
            self.__trackScrollCtl = None
        if self.crashedTracksController is not None:
            self.crashedTracksController.destroy()
            self.crashedTracksController = None
        if self.__periodicTimerID is not None:
            BigWorld.cancelCallback(self.__periodicTimerID)
            self.__periodicTimerID = None
        return

    def _onRequestModelsRefresh(self):
        self.flagComponent = None
        return

    def _onEngineStart(self):
        if self.engineAudition is not None:
            self.engineAudition.onEngineStart()
        return

    def __assembleNonDamagedOnly(self, resourceRefs, isPlayer, lodLink, lodStateLink):
        model_assembler.assembleTerrainMatKindSensor(self, lodStateLink, self.worldID)
        model_assembler.assembleRecoil(self, lodLink)
        model_assembler.assembleMultiGunRecoil(self, lodLink)
        model_assembler.assembleGunLinkedNodesAnimator(self)
        model_assembler.assembleCollisionObstaclesCollector(self, lodStateLink, self.typeDescriptor, self.worldID)
        model_assembler.assembleTessellationCollisionSensor(self, lodStateLink)
        wheelsScroll = None
        wheelsSteering = None
        generalWheelsAnimatorConfig = self.typeDescriptor.chassis.generalWheelsAnimatorConfig
        if generalWheelsAnimatorConfig is not None:
            scrollableWheelsCount = generalWheelsAnimatorConfig.getNonTrackWheelsCount()
            wheelsScroll = []
            for _ in xrange(scrollableWheelsCount):
                retriever = self.createComponent(NetworkFilters.FloatFilterRetriever)
                wheelsScroll.append(DataLinks.createFloatLink(retriever, 'value'))
                self.filterRetrievers.append(retriever)

            steerableWheelsCount = generalWheelsAnimatorConfig.getSteerableWheelsCount()
            wheelsSteering = []
            for _ in xrange(steerableWheelsCount):
                retriever = self.createComponent(NetworkFilters.FloatFilterRetriever)
                wheelsSteering.append(DataLinks.createFloatLink(retriever, 'value'))
                self.filterRetrievers.append(retriever)

        self.wheelsAnimator = model_assembler.createWheelsAnimator(self, ColliderTypes.VEHICLE_COLLIDER, self.typeDescriptor, lambda : self.wheelsState, wheelsScroll, wheelsSteering, self.splineTracks, lodStateLink)
        if self.customEffectManager is not None:
            self.customEffectManager.setWheelsData(self)
        suspensionLodLink = lodStateLink
        if 'wheeledVehicle' in self.typeDescriptor.type.tags:
            wheeledLodCalculator = Vehicular.LodCalculator(self.worldID, DataLinks.linkMatrixTranslation(self.compoundModel.matrix), True, WHEELED_CHASSIS_PRIORITY_GROUP, isPlayer)
            self.wheeledLodCalculator = wheeledLodCalculator
            self.allLodCalculators.append(wheeledLodCalculator)
            suspensionLodLink = wheeledLodCalculator.lodStateLink
        model_assembler.assembleSuspensionIfNeed(self, suspensionLodLink)
        model_assembler.assembleLeveredSuspensionIfNeed(self, suspensionLodLink)
        self.__assembleSwinging(lodLink)
        model_assembler.assembleBurnoutProcessor(self)
        model_assembler.assembleSuspensionSound(self, lodLink, isPlayer)
        model_assembler.assembleHullAimingController(self)
        self.trackNodesAnimator = model_assembler.createTrackNodesAnimator(self, self.typeDescriptor, lodStateLink)
        model_assembler.assembleTracks(resourceRefs, self.typeDescriptor, self, self.splineTracks, False, lodStateLink)
        model_assembler.assembleVehicleTraces(self, self.filter, lodStateLink)
        return

    def __assembleSwinging(self, lodLink):
        self.swingingAnimator = model_assembler.createSwingingAnimator(self, self.typeDescriptor, self.compoundModel.node(TankPartNames.HULL).localMatrix, self.compoundModel.matrix, lodLink)
        self.compoundModel.node(TankPartNames.HULL, self.swingingAnimator.animatedMProv)
        if hasattr(self.filter, 'placingCompensationMatrix'):
            self.swingingAnimator.placingCompensationMatrix = self.filter.placingCompensationMatrix

    def __postSetupFilter(self):
        suspensionWorking = self.suspension is not None and self.suspension.hasGroundNodes
        placingOnGround = not (suspensionWorking or self.leveredSuspension is not None)
        self.filter.placingOnGround = placingOnGround
        return

    def _createAndAttachStickers(self):
        isCurrentModelDamaged = self.damageState.isCurrentModelDamaged
        stickersAlpha = DEFAULT_STICKERS_ALPHA
        if isCurrentModelDamaged:
            stickersAlpha = items.vehicles.g_cache.commonConfig['miscParams']['damageStickerAlpha']
        if self.vehicleStickers is None:
            self._vehicleStickers = self._createStickers()
        self.vehicleStickers.alpha = stickersAlpha
        self.vehicleStickers.attach(compoundModel=self.compoundModel, isDamaged=self.damageState.isCurrentModelDamaged, showDamageStickers=not isCurrentModelDamaged)
        return

    def __onPeriodicTimer(self):
        timeStamp = BigWorld.wg_getFrameTimestamp()
        if self.__frameTimestamp >= timeStamp:
            self.__periodicTimerID = BigWorld.callback(0.0, self.__onPeriodicTimer)
        else:
            self.__frameTimestamp = timeStamp
            self.__periodicTimerID = BigWorld.callback(PERIODIC_UPDATE_TIME, self.__onPeriodicTimer)
            self._periodicUpdate()

    def _periodicUpdate(self):
        if self._vehicle is None or not self._vehicle.isAlive():
            return
        else:
            self._updateCurrTerrainMatKinds()
            self.__updateEffectsLOD()
            if self.customEffectManager:
                self.customEffectManager.update()
            return

    def __updateEffectsLOD(self):
        if self.customEffectManager:
            distanceFromPlayer = self.lodCalculator.lodDistance
            enableExhaust = distanceFromPlayer <= _LOD_DISTANCE_EXHAUST and not self.isUnderwater
            enableTrails = distanceFromPlayer <= _LOD_DISTANCE_TRAIL_PARTICLES and BigWorld.wg_isVehicleDustEnabled()
            self.customEffectManager.enable(enableTrails, EffectSettings.SETTING_DUST)
            self.customEffectManager.enable(enableExhaust, EffectSettings.SETTING_EXHAUST)

    def _stopEffects(self):
        self.boundEffects.stop()

    def playEffect(self, kind, *modifs):
        self._stopEffects()
        if kind == 'empty' or self._vehicle is None:
            return
        else:
            enableDecal = True
            if kind in ('explosion', 'destruction') and self.isFlying:
                enableDecal = False
            if self.isUnderwater:
                if kind not in ('submersionDeath',):
                    return
            effects = self.typeDescriptor.type.effects[kind]
            if not effects:
                return
            vehicle = self._vehicle
            effects = random.choice(effects)
            args = dict(isPlayerVehicle=vehicle.isPlayerVehicle, showShockWave=vehicle.isPlayerVehicle, showFlashBang=vehicle.isPlayerVehicle, entity_id=vehicle.id, isPlayer=vehicle.isPlayerVehicle, showDecal=enableDecal, start=vehicle.position + Math.Vector3(0.0, 1.0, 0.0), end=vehicle.position + Math.Vector3(0.0, -1.0, 0.0))
            if isSpawnedBot(self.typeDescriptor.type.tags) and kind in ('explosion', 'destruction'):
                player = BigWorld.player()
                if player is not None and isPlayerAvatar():
                    player.terrainEffects.addNew(self._vehicle.position, effects[1], effects[0], None, **args)
            else:
                self.boundEffects.addNew(None, effects[1], effects[0], **args)
            return

    def _updateCurrTerrainMatKinds(self):
        if self.terrainMatKindSensor is None:
            return
        else:
            matKinds = self.terrainMatKindSensor.matKinds
            groundTypes = self.terrainMatKindSensor.groundTypes
            materialsCount = len(matKinds)
            for i in xrange(MATKIND_COUNT):
                matKind = matKinds[i] if i < materialsCount else 0
                groundType = groundTypes[i] if i < materialsCount else 0
                self.terrainMatKind[i] = matKind
                self.terrainGroundType[i] = groundType
                effectIndex = calcEffectMaterialIndex(matKind)
                effectMaterialName = ''
                if effectIndex is not None:
                    effectMaterialName = material_kinds.EFFECT_MATERIALS[effectIndex]
                self.terrainEffectMaterialNames[i] = effectMaterialName

            if self.vehicleTraces is not None:
                self.vehicleTraces.setCurrTerrainMatKinds(self.terrainMatKind[0], self.terrainMatKind[1])
            return

    def changeEngineMode(self, mode, forceSwinging=False):
        if self.detailedEngineState is not None:
            self.detailedEngineState.mode = mode[0]
        if self.trackScrollController is not None:
            self.trackScrollController.setMode(mode)
        return

    def changeSiegeState(self, siegeState):
        if self.engineAudition is not None:
            self.engineAudition.onSiegeStateChanged(siegeState)
        return

    def turretDamaged(self):
        pass

    def maxTurretRotationSpeed(self):
        pass
Example #9
0
    def __startBuild(self, vDesc, vState):
        self.__curBuildInd += 1
        self.__vState = vState
        self.__resources = {}
        self.__vehicleStickers = None
        cfg = hangarCFG()
        if vState == 'undamaged':
            self.__currentEmblemsAlpha = cfg['emblems_alpha_undamaged']
            self.__isVehicleDestroyed = False
        else:
            self.__currentEmblemsAlpha = cfg['emblems_alpha_damaged']
            self.__isVehicleDestroyed = True
        self.__vDesc = vDesc
        resources = camouflages.getCamoPrereqs(self.__outfit, vDesc)
        if not self.__isVehicleDestroyed:
            self.__attachments = camouflages.getAttachments(self.__outfit, vDesc)
        modelsSet = self.__outfit.modelsSet
        splineDesc = vDesc.chassis.splineDesc
        if splineDesc is not None:
            for _, trackDesc in splineDesc.trackPairs.iteritems():
                resources += trackDesc.prerequisites(modelsSet)

        from vehicle_systems import model_assembler
        resources.append(model_assembler.prepareCompoundAssembler(self.__vDesc, ModelsSetParams(modelsSet, self.__vState, self.__attachments), self.__spaceId))
        g_eventBus.handleEvent(CameraRelatedEvents(CameraRelatedEvents.VEHICLE_LOADING, ctx={'started': True,
         'vEntityId': self.__vEntity.id,
         'intCD': self.__vDesc.type.compactDescr}), scope=EVENT_BUS_SCOPE.DEFAULT)
        cfg = hangarCFG()
        gunScale = Math.Vector3(1.0, 1.0, 1.1)
        capsuleScale = Math.Vector3(1.5, 1.5, 1.5)
        loadedGunScale = cfg.get('cam_capsule_gun_scale', gunScale)
        if loadedGunScale is not None:
            gunScale = loadedGunScale
        loadedCapsuleScale = cfg.get('cam_capsule_scale', capsuleScale)
        if loadedCapsuleScale is not None:
            capsuleScale = loadedCapsuleScale
        hitTesterManagers = {TankPartNames.CHASSIS: vDesc.chassis.hitTesterManager,
         TankPartNames.HULL: vDesc.hull.hitTesterManager,
         TankPartNames.TURRET: vDesc.turret.hitTesterManager,
         TankPartNames.GUN: vDesc.gun.hitTesterManager}
        bspModels = ()
        crashedBspModels = ()
        for partName, htManager in hitTesterManagers.iteritems():
            partId = TankPartNames.getIdx(partName)
            bspModel = (partId, htManager.modelHitTester.bspModelName)
            bspModels = bspModels + (bspModel,)
            if htManager.crashedModelHitTester:
                crashedBspModel = (partId, htManager.crashedModelHitTester.bspModelName)
                crashedBspModels = crashedBspModels + (crashedBspModel,)

        bspModels = bspModels + ((TankPartNames.getIdx(TankPartNames.GUN) + 1, vDesc.hull.hitTesterManager.modelHitTester.bspModelName, capsuleScale), (TankPartNames.getIdx(TankPartNames.GUN) + 2, vDesc.turret.hitTesterManager.modelHitTester.bspModelName, capsuleScale), (TankPartNames.getIdx(TankPartNames.GUN) + 3, vDesc.gun.hitTesterManager.modelHitTester.bspModelName, gunScale))
        if vDesc.hull.hitTesterManager.crashedModelHitTester:
            crashedBspModels = crashedBspModels + ((TankPartNames.getIdx(TankPartNames.GUN) + 1, vDesc.hull.hitTesterManager.crashedModelHitTester.bspModelName, capsuleScale),)
        if vDesc.turret.hitTesterManager.crashedModelHitTester:
            crashedBspModels = crashedBspModels + ((TankPartNames.getIdx(TankPartNames.GUN) + 2, vDesc.turret.hitTesterManager.crashedModelHitTester.bspModelName, capsuleScale),)
        if vDesc.gun.hitTesterManager.crashedModelHitTester:
            crashedBspModels = crashedBspModels + ((TankPartNames.getIdx(TankPartNames.GUN) + 3, vDesc.gun.hitTesterManager.crashedModelHitTester.bspModelName, gunScale),)
        modelCA = BigWorld.CollisionAssembler(bspModels, self.__spaceId)
        modelCA.name = 'ModelCollisions'
        resources.append(modelCA)
        if crashedBspModels:
            crashedModelCA = BigWorld.CollisionAssembler(crashedBspModels, self.__spaceId)
            crashedModelCA.name = 'CrashedModelCollisions'
            resources.append(crashedModelCA)
        physicalTracksBuilders = vDesc.chassis.physicalTracks
        for name, builders in physicalTracksBuilders.iteritems():
            for index, builder in enumerate(builders):
                resources.append(builder.createLoader(self.__spaceId, '{0}{1}PhysicalTrack'.format(name, index), modelsSet))

        BigWorld.loadResourceListBG(tuple(resources), makeCallbackWeak(self.__onResourcesLoaded, self.__curBuildInd))
        return