class CombatEquipmentManager(object):

    def testArtyStrike(self, id = 33, offset = Vector3(0, 0, 0)):
        if not IS_DEVELOPMENT:
            return
        else:
            p = Vector3(BigWorld.camera().position)
            d = BigWorld.camera().direction
            collRes = BigWorld.wg_collideSegment(BigWorld.player().spaceID, p, p + d * 1000, 18, lambda matKind, collFlags, itemId, chunkId: collFlags & 8)
            if collRes is None:
                return
            strikePos = collRes[0]
            vDir = Vector2(d.x, d.z)
            vDir.normalise()
            self.setEquipmentApplicationPoint(id, strikePos + offset, vDir)
            return

    def __init__(self):
        self.__callbackDelayer = CallbackDelayer()
        self.__selectedAreas = {}
        self.__wings = {}
        if _ENABLE_DEBUG_DRAW:
            self.debugPolyLine = Flock.DebugPolyLine()
            self.debugPoints = []
            self.debugDirs = []

    def onBecomePlayer(self):
        pass

    def onBecomeNonPlayer(self):
        for area in self.__selectedAreas.itervalues():
            area.destroy()

        for wing in self.__wings.itervalues():
            wing.destroy()

        self.__callbackDelayer.destroy()
        self.__selectedAreas = {}
        self.__wings = {}

    def updateBomberTrajectory(self, equipmentID, team, curTime, curPos, curDir, nextTime, nextPos, nextDir, isDroppingPoint):
        if _ENABLE_DEBUG_LOG:
            LOG_DEBUG('===== updateBomberTrajectory =====')
            LOG_DEBUG(equipmentID, team)
            LOG_DEBUG(curPos, curDir, curTime)
            LOG_DEBUG(nextPos, nextDir, nextTime)
            LOG_DEBUG(isDroppingPoint)
        moveDir = nextPos - curPos
        moveDir.normalise()
        nextDir3d = Vector3(nextDir.x, moveDir.y, nextDir.y)
        nextDir3d.normalise()
        startP = BombersWing.CurveControlPoint(curPos, Vector3(curDir.x, 0, curDir.y), curTime)
        nextP = BombersWing.CurveControlPoint(nextPos, nextDir3d, nextTime)
        points = (startP, nextP)
        wingID = (team, equipmentID)
        wing = self.__wings.get(wingID)
        if wing is None or wing.withdrawn:
            if wing is not None:
                wing.destroy()
            self.__wings[wingID] = BombersWing.BombersWing(equipmentID, points)
            if _ENABLE_DEBUG_DRAW:
                self.debugPoints.append(curPos)
                self.debugDirs.append(curDir)
        else:
            wing.addControlPoint(points, isDroppingPoint)
        if _ENABLE_DEBUG_DRAW:
            self.debugPoints.append(nextPos)
            self.debugDirs.append(nextDir)
            self.debugPoints.append(nextPos + Vector3(nextDir.x, 0, nextDir.y) * 10)
            self.debugPoints.append(nextPos)
            self.debugPolyLine.set(self.debugPoints)
        return

    def showHittingArea(self, equipmentID, pos, dir, time):
        if _ENABLE_DEBUG_LOG:
            LOG_DEBUG('===== showHittingArea =====')
            LOG_DEBUG(equipmentID)
            LOG_DEBUG(pos, dir, time)
        correctedCoords = tuple((int(x * 1000.0) for x in pos.tuple()))
        areaUID = (int(equipmentID), correctedCoords)
        if areaUID in self.__selectedAreas:
            return
        eq = vehicles.g_cache.equipments()[equipmentID]
        if BattleReplay.isPlaying():
            BigWorld.callback(0.0, functools.partial(self.__showMarkerCallback, eq, pos, dir, time, areaUID))
        else:
            self.__showMarkerCallback(eq, pos, dir, time, areaUID)

    def __delayedAreaDestroy(self, areaUID):
        area = self.__selectedAreas.pop(areaUID, None)
        if area is not None:
            area.destroy()
        return

    def __showMarkerCallback(self, eq, pos, dir, time, areaUID):
        timer = round(time - BigWorld.serverTime())
        area = self.__selectedAreas.pop(areaUID, None)
        if area is not None:
            area.destroy()
        self.__selectedAreas[areaUID] = self.createEquipmentSelectedArea(pos, dir, eq)
        self.__callbackDelayer.delayCallback(timer, functools.partial(self.__delayedAreaDestroy, areaUID))
        g_sessionProvider.getEquipmentsCtrl().showMarker(eq, pos, dir, timer)
        return

    @staticmethod
    def __calcBombsDistribution(bombsCnt, areaWidth, areaLength):
        coeff = areaWidth / areaLength
        bombsPerWidth = math.sqrt(bombsCnt * coeff)
        bombsPerLength = bombsPerWidth / coeff
        return (bombsPerWidth, bombsPerLength)

    def showCarpetBombing(self, equipmentID, pos, dir, time):
        if _ENABLE_DEBUG_LOG:
            LOG_DEBUG('===== showCarpetBombing =====')
            LOG_DEBUG(equipmentID)
            LOG_DEBUG(pos, dir, time)
        bombEquipment = vehicles.g_cache.equipments()[equipmentID]
        shellDescr = items.vehicles.getDictDescr(bombEquipment.shellCompactDescr)
        shotEffect = vehicles.g_cache.shotEffects[shellDescr['effectsIndex']]
        airstrikeID = shotEffect.get('airstrikeID')
        if airstrikeID is None:
            LOG_ERROR('EquipmentID %s has no airstrike shot effect settings' % equipmentID)
            return
        else:
            areaWidth, areaLength = bombEquipment.areaWidth, bombEquipment.areaLength
            if _ENABLE_DEBUG_LOG:
                LOG_DEBUG('Ideal', areaWidth, areaLength)
            beginExplosionPos = BigWorld.wg_collideSegment(BigWorld.player().spaceID, pos, pos + dir * 1000.0, 18)
            if beginExplosionPos is None:
                return
            beginExplosionPos = beginExplosionPos[0]
            flatDir = Vector3(dir)
            flatDir.y = 0.0
            flatDir.normalise()
            endDropPoint = pos + flatDir * (areaLength * bombEquipment.waveFraction)
            endExplosionPos = BigWorld.wg_collideSegment(BigWorld.player().spaceID, endDropPoint, endDropPoint + dir * 1000.0, 18)
            if endExplosionPos is None:
                endExplosionPos = beginExplosionPos + flatDir * (areaLength * bombEquipment.waveFraction)
            else:
                endExplosionPos = endExplosionPos[0]
            areaLength = beginExplosionPos.flatDistTo(endExplosionPos)
            averageBombCount = bombEquipment.bombsNumber
            bombsPerWidth, bombsPerLength = CombatEquipmentManager.__calcBombsDistribution(averageBombCount, areaWidth, areaLength)
            delay = time - BigWorld.serverTime()
            explosionVelocity = flatDir * bombEquipment.speed
            partialAirstrikeFunc = functools.partial(BigWorld.PyGroundEffectManager().playAirstrike, airstrikeID, beginExplosionPos, explosionVelocity, areaWidth, areaLength, math.ceil(bombsPerWidth), math.ceil(bombsPerLength))
            if _ENABLE_DEBUG_LOG:
                LOG_DEBUG('delta', delay)
                LOG_DEBUG('pos, dir', pos, dir)
                LOG_DEBUG('Params for artyStrike effect', airstrikeID, beginExplosionPos, flatDir, areaWidth, areaLength, bombsPerWidth, bombsPerLength)
            if delay < 0.0:
                partialAirstrikeFunc()
            else:
                self.__callbackDelayer.delayCallback(delay, partialAirstrikeFunc)
            if _ENABLE_DEBUG_DRAW:
                self.debugStartLine = Flock.DebugLine(pos, beginExplosionPos)
                self.debugEndLine = Flock.DebugLine(endDropPoint, endExplosionPos)
                self.__callbackDelayer.delayCallback(delay, functools.partial(_DebugFrontLine.launch, beginExplosionPos, endExplosionPos, areaWidth, explosionVelocity))
            return

    def setEquipmentApplicationPoint(self, equipmentID, point, direction):
        myTeam = BigWorld.player().team
        wingID = (myTeam, equipmentID)
        wing = self.__wings.get(wingID)
        if wing is not None:
            wing.destroy()
            del self.__wings[wingID]
        self.cell.setEquipmentApplicationPoint(equipmentID, point, direction)
        return

    @staticmethod
    def createEquipmentSelectedArea(pos, dir, equipment):
        area = CombatSelectedArea.CombatSelectedArea()
        size = Math.Vector2(equipment.areaWidth, equipment.areaLength)
        visual = equipment.areaVisual
        color = equipment.areaColor
        marker = equipment.areaMarker
        if visual is None:
            visual = CombatSelectedArea.DEFAULT_RADIUS_MODEL
        if color is None:
            color = CombatSelectedArea.COLOR_WHITE
        if marker is None:
            pass
        area.setup(pos, dir, size, visual, color, marker)
        return area
Ejemplo n.º 2
0
class CombatEquipmentManager(object):
    def testArtyStrike(self, id=33, offset=Vector3(0, 0, 0)):
        if not IS_DEVELOPMENT:
            return
        p = Vector3(BigWorld.camera().position)
        d = BigWorld.camera().direction
        collRes = BigWorld.wg_collideSegment(
            BigWorld.player().spaceID, p, p + d * 1000, 18,
            lambda matKind, collFlags, itemId, chunkId: collFlags & 8)
        if collRes is None:
            return
        strikePos = collRes[0]
        vDir = Vector2(d.x, d.z)
        vDir.normalise()
        self.setEquipmentApplicationPoint(id, strikePos + offset, vDir)

    def __init__(self):
        self.__callbackDelayer = CallbackDelayer()
        self.__selectedAreas = {}
        self.__wings = {}
        if _ENABLE_DEBUG_DRAW:
            self.debugPolyLine = Flock.DebugPolyLine()
            self.debugPoints = []
            self.debugDirs = []

    def onBecomePlayer(self):
        pass

    def onBecomeNonPlayer(self):
        for area in self.__selectedAreas.itervalues():
            area.destroy()

        for wing in self.__wings.itervalues():
            wing.destroy()

        self.__callbackDelayer.destroy()
        self.__selectedAreas = {}
        self.__wings = {}

    def updateBomberTrajectory(self, equipmentID, team, curTime, curPos,
                               curDir, nextTime, nextPos, nextDir,
                               isDroppingPoint):
        if _ENABLE_DEBUG_LOG:
            LOG_DEBUG('===== updateBomberTrajectory =====')
            LOG_DEBUG(equipmentID, team)
            LOG_DEBUG(curPos, curDir, curTime)
            LOG_DEBUG(nextPos, nextDir, nextTime)
            LOG_DEBUG(isDroppingPoint)
        moveDir = nextPos - curPos
        moveDir.normalise()
        nextDir3d = Vector3(nextDir.x, moveDir.y, nextDir.y)
        nextDir3d.normalise()
        startP = BombersWing.CurveControlPoint(curPos,
                                               Vector3(curDir.x, 0, curDir.y),
                                               curTime)
        nextP = BombersWing.CurveControlPoint(nextPos, nextDir3d, nextTime)
        points = (startP, nextP)
        wingID = (team, equipmentID)
        wing = self.__wings.get(wingID)
        if wing is None or wing.withdrawn:
            if wing is not None:
                wing.destroy()
            self.__wings[wingID] = BombersWing.BombersWing(equipmentID, points)
            if _ENABLE_DEBUG_DRAW:
                self.debugPoints.append(curPos)
                self.debugDirs.append(curDir)
        else:
            wing.addControlPoint(points, isDroppingPoint)
        if _ENABLE_DEBUG_DRAW:
            self.debugPoints.append(nextPos)
            self.debugDirs.append(nextDir)
            self.debugPoints.append(nextPos +
                                    Vector3(nextDir.x, 0, nextDir.y) * 10)
            self.debugPoints.append(nextPos)
            self.debugPolyLine.set(self.debugPoints)

    def showHittingArea(self, equipmentID, pos, dir, time):
        if _ENABLE_DEBUG_LOG:
            LOG_DEBUG('===== showHittingArea =====')
            LOG_DEBUG(equipmentID)
            LOG_DEBUG(pos, dir, time)
        correctedCoords = tuple((int(x * 1000.0) for x in pos.tuple()))
        areaUID = (int(equipmentID), correctedCoords)
        if areaUID in self.__selectedAreas:
            return
        eq = vehicles.g_cache.equipments()[equipmentID]
        if BattleReplay.isPlaying():
            BigWorld.callback(
                0.0,
                functools.partial(self.__showMarkerCallback, eq, pos, dir,
                                  time, areaUID))
        else:
            self.__showMarkerCallback(eq, pos, dir, time, areaUID)

    def __delayedAreaDestroy(self, areaUID):
        area = self.__selectedAreas.pop(areaUID, None)
        if area is not None:
            area.destroy()

    def __showMarkerCallback(self, eq, pos, dir, time, areaUID):
        timer = round(time - BigWorld.serverTime())
        area = self.__selectedAreas.pop(areaUID, None)
        if area is not None:
            area.destroy()
        self.__selectedAreas[areaUID] = self.createEquipmentSelectedArea(
            pos, dir, eq)
        self.__callbackDelayer.delayCallback(
            timer, functools.partial(self.__delayedAreaDestroy, areaUID))
        g_sessionProvider.getEquipmentsCtrl().showMarker(eq, pos, dir, timer)

    @staticmethod
    def __calcBombsDistribution(bombsCnt, areaWidth, areaLength):
        coeff = areaWidth / areaLength
        bombsPerWidth = math.sqrt(bombsCnt * coeff)
        bombsPerLength = bombsPerWidth / coeff
        return (bombsPerWidth, bombsPerLength)

    def showCarpetBombing(self, equipmentID, pos, dir, time):
        if _ENABLE_DEBUG_LOG:
            LOG_DEBUG('===== showCarpetBombing =====')
            LOG_DEBUG(equipmentID)
            LOG_DEBUG(pos, dir, time)
        bombEquipment = vehicles.g_cache.equipments()[equipmentID]
        shellDescr = items.vehicles.getDictDescr(
            bombEquipment.shellCompactDescr)
        shotEffect = vehicles.g_cache.shotEffects[shellDescr['effectsIndex']]
        airstrikeID = shotEffect.get('airstrikeID')
        if airstrikeID is None:
            LOG_ERROR('EquipmentID %s has no airstrike shot effect settings' %
                      equipmentID)
            return
        areaWidth, areaLength = bombEquipment.areaWidth, bombEquipment.areaLength
        if _ENABLE_DEBUG_LOG:
            LOG_DEBUG('Ideal', areaWidth, areaLength)
        beginExplosionPos = BigWorld.wg_collideSegment(
            BigWorld.player().spaceID, pos, pos + dir * 1000.0, 18)
        if beginExplosionPos is None:
            return
        beginExplosionPos = beginExplosionPos[0]
        flatDir = Vector3(dir)
        flatDir.y = 0.0
        flatDir.normalise()
        endDropPoint = pos + flatDir * (areaLength *
                                        bombEquipment.waveFraction)
        endExplosionPos = BigWorld.wg_collideSegment(
            BigWorld.player().spaceID, endDropPoint,
            endDropPoint + dir * 1000.0, 18)
        if endExplosionPos is None:
            endExplosionPos = beginExplosionPos + flatDir * (
                areaLength * bombEquipment.waveFraction)
        else:
            endExplosionPos = endExplosionPos[0]
        areaLength = beginExplosionPos.flatDistTo(endExplosionPos)
        averageBombCount = bombEquipment.bombsNumber
        bombsPerWidth, bombsPerLength = CombatEquipmentManager.__calcBombsDistribution(
            averageBombCount, areaWidth, areaLength)
        delay = time - BigWorld.serverTime()
        explosionVelocity = flatDir * bombEquipment.speed
        partialAirstrikeFunc = functools.partial(
            BigWorld.PyGroundEffectManager().playAirstrike, airstrikeID,
            beginExplosionPos, explosionVelocity, areaWidth, areaLength,
            math.ceil(bombsPerWidth), math.ceil(bombsPerLength))
        if _ENABLE_DEBUG_LOG:
            LOG_DEBUG('delta', delay)
            LOG_DEBUG('pos, dir', pos, dir)
            LOG_DEBUG('Params for artyStrike effect', airstrikeID,
                      beginExplosionPos, flatDir, areaWidth, areaLength,
                      bombsPerWidth, bombsPerLength)
        if delay < 0.0:
            partialAirstrikeFunc()
        else:
            self.__callbackDelayer.delayCallback(delay, partialAirstrikeFunc)
        if _ENABLE_DEBUG_DRAW:
            self.debugStartLine = Flock.DebugLine(pos, beginExplosionPos)
            self.debugEndLine = Flock.DebugLine(endDropPoint, endExplosionPos)
            self.__callbackDelayer.delayCallback(
                delay,
                functools.partial(_DebugFrontLine.launch, beginExplosionPos,
                                  endExplosionPos, areaWidth,
                                  explosionVelocity))

    def setEquipmentApplicationPoint(self, equipmentID, point, direction):
        myTeam = BigWorld.player().team
        wingID = (myTeam, equipmentID)
        wing = self.__wings.get(wingID)
        if wing is not None:
            wing.destroy()
            del self.__wings[wingID]
        self.cell.setEquipmentApplicationPoint(equipmentID, point, direction)

    @staticmethod
    def createEquipmentSelectedArea(pos, dir, equipment):
        area = CombatSelectedArea.CombatSelectedArea()
        size = Math.Vector2(equipment.areaWidth, equipment.areaLength)
        visual = equipment.areaVisual
        color = equipment.areaColor
        marker = equipment.areaMarker
        if visual is None:
            visual = CombatSelectedArea.DEFAULT_RADIUS_MODEL
        if color is None:
            color = CombatSelectedArea.COLOR_WHITE
        if marker is None:
            pass
        area.setup(pos, dir, size, visual, color, marker)
        return area
Ejemplo n.º 3
0
class DetachedTurret(BigWorld.Entity):
    allTurrets = list()

    def __init__(self):
        self.__vehDescr = vehicles.VehicleDescr(compactDescr=self.vehicleCompDescr)
        self.filter = BigWorld.WGTurretFilter()
        self.__detachConfirmationTimer = SynchronousDetachment(self)
        self.__detachConfirmationTimer.onInit()
        self.__detachmentEffects = None
        self.__hitEffects = {TankComponentNames.TURRET: None,
         TankComponentNames.GUN: None}
        self.__reactors = []
        self.targetFullBounds = True
        self.targetCaps = [1]
        self.__isBeingPulledCallback = None



    def reload(self):
        pass



    def prerequisites(self):
        prereqs = [self.__vehDescr.turret['models']['exploded'], self.__vehDescr.gun['models']['exploded']]
        prereqs += self.__vehDescr.prerequisites()
        return prereqs



    def onEnterWorld(self, prereqs):
        self.model = prereqs[self.__vehDescr.turret['models']['exploded']]
        self.__gunModel = prereqs[self.__vehDescr.gun['models']['exploded']]
        node = self.model.node('HP_gunJoint', Math.Matrix())
        node.attach(self.__gunModel)
        self.__detachConfirmationTimer.onEnterWorld()
        self.__vehDescr.keepPrereqs(prereqs)
        turretDescr = self.__vehDescr.turret
        if self.isUnderWater == 0:
            self.__detachmentEffects = _TurretDetachmentEffects(self.model, turretDescr['turretDetachmentEffects'], self.isCollidingWithWorld == 1)
            self.__reactors.append(self.__detachmentEffects)
        else:
            self.__detachmentEffects = None
        self.__hitEffects[TankComponentNames.TURRET] = turretHitEffects = _HitEffects(self.model)
        self.__hitEffects[TankComponentNames.GUN] = gunHitEffects = _HitEffects(self.__gunModel)
        self.__reactors.append(turretHitEffects)
        self.__reactors.append(gunHitEffects)
        self.__componentsDesc = (self.__vehDescr.turret, self.__vehDescr.gun)
        for desc in self.__componentsDesc:
            desc['hitTester'].loadBspModel()

        from AvatarInputHandler.CallbackDelayer import CallbackDelayer
        self.__isBeingPulledCallback = CallbackDelayer()
        self.__isBeingPulledCallback.delayCallback(self.__checkIsBeingPulled(), self.__checkIsBeingPulled)
        DetachedTurret.allTurrets.append(self)



    def onLeaveWorld(self):
        DetachedTurret.allTurrets.remove(self)
        self.__detachConfirmationTimer.cancel()
        self.__detachConfirmationTimer = None
        for reactor in self.__reactors:
            if reactor is not None:
                reactor.destroy()

        self.__isBeingPulledCallback.destroy()
        self.__isBeingPulledCallback = None



    def onStaticCollision(self, energy, point, normal):
        if self.__detachmentEffects is not None:
            surfaceMaterial = calcSurfaceMaterialNearPoint(point, normal, self.spaceID)
            effectIdx = surfaceMaterial.effectIdx
            groundEffect = True
            distToWater = BigWorld.wg_collideWater(self.position, surfaceMaterial.point)
            if distToWater != -1:
                vel = Math.Vector3(self.velocity).length
                if vel < _MIN_COLLISION_SPEED:
                    groundEffect = False
                effectIdx = material_kinds.EFFECT_MATERIAL_INDEXES_BY_NAMES['water']
            self.__detachmentEffects.notifyAboutCollision(energy, point, effectIdx, groundEffect, self.isUnderWater)



    def showDamageFromShot(self, points, effectsIndex):
        (maxHitEffectCode, decodedPoints,) = DamageFromShotDecoder.decodeHitPoints(points, self.__vehDescr)
        for shotPoint in decodedPoints:
            hitEffects = self.__hitEffects.get(shotPoint.componentName)
            if hitEffects is not None:
                hitEffects.showHit(shotPoint, effectsIndex)
            else:
                LOG_ERROR("Detached turret got hit into %s component, but it's impossible" % shotPoint.componentName)




    def collideSegment(self, startPoint, endPoint, skipGun = False):
        res = None
        filterMethod = getattr(self.filter, 'segmentMayHitEntity', lambda : True)
        if not filterMethod(startPoint, endPoint):
            return res
        modelsToCheck = (self.model,) if skipGun else (self.model, self.__gunModel)
        for (model, desc,) in zip(modelsToCheck, self.__componentsDesc):
            toModel = Matrix(model.matrix)
            toModel.invert()
            collisions = desc['hitTester'].localHitTest(toModel.applyPoint(startPoint), toModel.applyPoint(endPoint))
            if collisions is None:
                continue
            for (dist, _, hitAngleCos, matKind,) in collisions:
                if res is None or res.dist >= dist:
                    matInfo = desc['materials'].get(matKind)
                    res = SegmentCollisionResult(dist, hitAngleCos, matInfo.armor if matInfo is not None else 0)


        return res



    def set_isUnderWater(self, prev):
        if self.__detachmentEffects is not None:
            if self.isUnderWater:
                self.__detachmentEffects.stopEffects()



    def set_isCollidingWithWorld(self, prev):
        pass



    def changeAppearanceVisibility(self, isVisible):
        self.model.visible = isVisible
        self.model.visibleAttachments = isVisible



    def __checkIsBeingPulled(self):
        if self.__detachmentEffects is not None:
            if self.isCollidingWithWorld and not self.isUnderWater and self.velocity.lengthSquared > 0.1:
                extent = Math.Matrix(self.model.bounds).applyVector(Math.Vector3(0.5, 0.5, 0.5)).length
                surfaceMaterial = calcSurfaceMaterialNearPoint(self.position, Math.Vector3(0, extent, 0), self.spaceID)
                self.__detachmentEffects.notifyAboutBeingPulled(True, surfaceMaterial.effectIdx)
                if surfaceMaterial.matKind == 0:
                    LOG_ERROR('calcSurfaceMaterialNearPoint failed to find the collision point')
            else:
                self.__detachmentEffects.notifyAboutBeingPulled(False, None)
        return SERVER_TICK_LENGTH
Ejemplo n.º 4
0
class DetachedTurret(BigWorld.Entity):
    allTurrets = list()

    def __init__(self):
        self.__vehDescr = vehicles.VehicleDescr(
            compactDescr=self.vehicleCompDescr)
        self.filter = BigWorld.WGTurretFilter()
        self.__detachConfirmationTimer = SynchronousDetachment(self)
        self.__detachConfirmationTimer.onInit()
        self.__detachmentEffects = None
        self.__hitEffects = {
            TankComponentNames.TURRET: None,
            TankComponentNames.GUN: None
        }
        self.__reactors = []
        self.targetFullBounds = True
        self.targetCaps = [1]
        self.__isBeingPulledCallback = None

    def reload(self):
        pass

    def prerequisites(self):
        prereqs = [
            self.__vehDescr.turret['models']['exploded'],
            self.__vehDescr.gun['models']['exploded']
        ]
        prereqs += self.__vehDescr.prerequisites()
        return prereqs

    def onEnterWorld(self, prereqs):
        self.model = prereqs[self.__vehDescr.turret['models']['exploded']]
        self.__gunModel = prereqs[self.__vehDescr.gun['models']['exploded']]
        node = self.model.node('HP_gunJoint', Math.Matrix())
        node.attach(self.__gunModel)
        self.__detachConfirmationTimer.onEnterWorld()
        self.__vehDescr.keepPrereqs(prereqs)
        turretDescr = self.__vehDescr.turret
        if self.isUnderWater == 0:
            self.__detachmentEffects = _TurretDetachmentEffects(
                self.model, turretDescr['turretDetachmentEffects'],
                self.isCollidingWithWorld == 1)
            self.__reactors.append(self.__detachmentEffects)
        else:
            self.__detachmentEffects = None
        self.__hitEffects[
            TankComponentNames.TURRET] = turretHitEffects = _HitEffects(
                self.model)
        self.__hitEffects[
            TankComponentNames.GUN] = gunHitEffects = _HitEffects(
                self.__gunModel)
        self.__reactors.append(turretHitEffects)
        self.__reactors.append(gunHitEffects)
        self.__componentsDesc = (self.__vehDescr.turret, self.__vehDescr.gun)
        for desc in self.__componentsDesc:
            desc['hitTester'].loadBspModel()

        from AvatarInputHandler.CallbackDelayer import CallbackDelayer
        self.__isBeingPulledCallback = CallbackDelayer()
        self.__isBeingPulledCallback.delayCallback(self.__checkIsBeingPulled(),
                                                   self.__checkIsBeingPulled)
        DetachedTurret.allTurrets.append(self)

    def onLeaveWorld(self):
        DetachedTurret.allTurrets.remove(self)
        self.__detachConfirmationTimer.cancel()
        self.__detachConfirmationTimer = None
        for reactor in self.__reactors:
            if reactor is not None:
                reactor.destroy()

        self.__isBeingPulledCallback.destroy()
        self.__isBeingPulledCallback = None

    def onStaticCollision(self, energy, point, normal):
        if self.__detachmentEffects is not None:
            surfaceMaterial = calcSurfaceMaterialNearPoint(
                point, normal, self.spaceID)
            effectIdx = surfaceMaterial.effectIdx
            groundEffect = True
            distToWater = BigWorld.wg_collideWater(self.position,
                                                   surfaceMaterial.point)
            if distToWater != -1:
                vel = Math.Vector3(self.velocity).length
                if vel < _MIN_COLLISION_SPEED:
                    groundEffect = False
                effectIdx = material_kinds.EFFECT_MATERIAL_INDEXES_BY_NAMES[
                    'water']
            self.__detachmentEffects.notifyAboutCollision(
                energy, point, effectIdx, groundEffect, self.isUnderWater)

    def showDamageFromShot(self, points, effectsIndex):
        maxHitEffectCode, decodedPoints = DamageFromShotDecoder.decodeHitPoints(
            points, self.__vehDescr)
        for shotPoint in decodedPoints:
            hitEffects = self.__hitEffects.get(shotPoint.componentName)
            if hitEffects is not None:
                hitEffects.showHit(shotPoint, effectsIndex)
            else:
                LOG_ERROR(
                    "Detached turret got hit into %s component, but it's impossible"
                    % shotPoint.componentName)

    def collideSegment(self, startPoint, endPoint, skipGun=False):
        res = None
        filterMethod = getattr(self.filter, 'segmentMayHitEntity',
                               lambda: True)
        if not filterMethod(startPoint, endPoint, 0):
            return res
        modelsToCheck = (self.model, ) if skipGun else (self.model,
                                                        self.__gunModel)
        for model, desc in zip(modelsToCheck, self.__componentsDesc):
            toModel = Matrix(model.matrix)
            toModel.invert()
            collisions = desc['hitTester'].localHitTest(
                toModel.applyPoint(startPoint), toModel.applyPoint(endPoint))
            if collisions is None:
                continue
            for dist, _, hitAngleCos, matKind in collisions:
                if res is None or res.dist >= dist:
                    matInfo = desc['materials'].get(matKind)
                    res = SegmentCollisionResult(
                        dist, hitAngleCos,
                        matInfo.armor if matInfo is not None else 0)

        return res

    def set_isUnderWater(self, prev):
        if self.__detachmentEffects is not None:
            if self.isUnderWater:
                self.__detachmentEffects.stopEffects()

    def set_isCollidingWithWorld(self, prev):
        pass

    def changeAppearanceVisibility(self, isVisible):
        self.model.visible = isVisible
        self.model.visibleAttachments = isVisible

    def __checkIsBeingPulled(self):
        if self.__detachmentEffects is not None:
            if self.isCollidingWithWorld and not self.isUnderWater and self.velocity.lengthSquared > 0.1:
                extent = Math.Matrix(self.model.bounds).applyVector(
                    Math.Vector3(0.5, 0.5, 0.5)).length
                surfaceMaterial = calcSurfaceMaterialNearPoint(
                    self.position, Math.Vector3(0, extent, 0), self.spaceID)
                self.__detachmentEffects.notifyAboutBeingPulled(
                    True, surfaceMaterial.effectIdx)
                if surfaceMaterial.matKind == 0:
                    LOG_ERROR(
                        'calcSurfaceMaterialNearPoint failed to find the collision point'
                    )
            else:
                self.__detachmentEffects.notifyAboutBeingPulled(False, None)
        return SERVER_TICK_LENGTH