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
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
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
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