class ArcadeCamera(ICamera, CallbackDelayer, TimeDeltaMeter): REASONS_AFFECT_CAMERA_DIRECTLY = (ImpulseReason.MY_SHOT, ImpulseReason.OTHER_SHOT, ImpulseReason.VEHICLE_EXPLOSION, ImpulseReason.HE_EXPLOSION) _DYNAMIC_ENABLED = True settingsCore = dependency.descriptor(ISettingsCore) @staticmethod def enableDynamicCamera(enable): ArcadeCamera._DYNAMIC_ENABLED = enable @staticmethod def isCameraDynamic(): return ArcadeCamera._DYNAMIC_ENABLED _FILTER_LENGTH = 5 _DEFAULT_MAX_ACCELERATION_DURATION = 1.5 _FOCAL_POINT_CHANGE_SPEED = 100.0 _FOCAL_POINT_MIN_DIFF = 5.0 _MIN_REL_SPEED_ACC_SMOOTHING = 0.7 def getReasonsAffectCameraDirectly(self): return ArcadeCamera.REASONS_AFFECT_CAMERA_DIRECTLY def __getVehicleMProv(self): refinedMProv = self.__aimingSystem.vehicleMProv return refinedMProv.b.source def __setVehicleMProv(self, vehicleMProv): prevAimRel = self.__cam.aimPointProvider.a if self.__cam.aimPointProvider is not None else None refinedVehicleMProv = self.__refineVehicleMProv(vehicleMProv) self.__aimingSystem.vehicleMProv = refinedVehicleMProv self.__setupCameraProviders(refinedVehicleMProv) self.__cam.speedTreeTarget = self.__aimingSystem.vehicleMProv self.__aimingSystem.update(0.0) if prevAimRel is not None: self.__cam.aimPointProvider.a = prevAimRel baseTranslation = Matrix(refinedVehicleMProv).translation relativePosition = self.__aimingSystem.matrix.translation - baseTranslation if self.__cameraInterpolator.active: self.setYawPitch(self.__previousAimVector.yaw, -self.__previousAimVector.pitch) self.__transitionYawDirty = True else: self.__setCameraPosition(relativePosition) return camera = property(lambda self: self.__cam) angles = property(lambda self: (self.__aimingSystem.yaw, self.__aimingSystem.pitch)) aimingSystem = property(lambda self: self.__aimingSystem) vehicleMProv = property(__getVehicleMProv, __setVehicleMProv) __aimOffset = aih_global_binding.bindRW( aih_global_binding.BINDING_ID.AIM_OFFSET) def __init__(self, dataSec, defaultOffset=None): CallbackDelayer.__init__(self) TimeDeltaMeter.__init__(self) self.__shiftKeySensor = None self.__movementOscillator = None self.__impulseOscillator = None self.__noiseOscillator = None self.__dynamicCfg = CameraDynamicConfig() self.__accelerationSmoother = None self.__readCfg(dataSec) self.__onChangeControlMode = None self.__aimingSystem = None self.__curSense = 0 self.__curScrollSense = 0 self.__postmortemMode = False self.__focalPointDist = 1.0 self.__autoUpdateDxDyDz = Vector3(0.0) self.__updatedByKeyboard = False if defaultOffset is not None: self.__defaultAimOffset = defaultOffset self.__cam = BigWorld.HomingCamera(self.__adCfg['enable']) if self.__adCfg['enable']: self.__cam.initAdvancedCollider( self.__adCfg['fovRatio'], self.__adCfg['rollbackSpeed'], self.__adCfg['minimalCameraDistance'], self.__adCfg['speedThreshold'], self.__adCfg['minimalVolume']) for group_name in VOLUME_GROUPS_NAMES: self.__cam.addVolumeGroup( self.__adCfg['volumeGroups'][group_name]) self.__cam.aimPointClipCoords = defaultOffset else: self.__defaultAimOffset = Vector2() self.__cam = None self.__cameraInterpolator = DynamicCameraInterpolator() self.__resetCameraTransition() return def create(self, pivotPos, onChangeControlMode=None, postmortemMode=False): self.__onChangeControlMode = onChangeControlMode self.__postmortemMode = postmortemMode targetMat = self.getTargetMProv() aimingSystemClass = ArcadeAimingSystemRemote if BigWorld.player( ).isObserver() else ArcadeAimingSystem self.__aimingSystem = aimingSystemClass( self.__refineVehicleMProv(targetMat), pivotPos.y, pivotPos.z, self.__calcAimMatrix(), self.__cfg['angleRange'], not postmortemMode) if self.__adCfg['enable']: self.__aimingSystem.initAdvancedCollider( self.__adCfg['fovRatio'], self.__adCfg['rollbackSpeed'], self.__adCfg['minimalCameraDistance'], self.__adCfg['speedThreshold'], self.__adCfg['minimalVolume']) for group_name in VOLUME_GROUPS_NAMES: self.__aimingSystem.addVolumeGroup( self.__adCfg['volumeGroups'][group_name]) self.setCameraDistance(self.__cfg['startDist']) self.__aimingSystem.pitch = self.__cfg['startAngle'] self.__aimingSystem.yaw = Math.Matrix(targetMat).yaw self.__updateAngles(0, 0) cameraPosProvider = Math.Vector4Translation(self.__aimingSystem.matrix) self.__cam.cameraPositionProvider = cameraPosProvider self.settingsCore.onSettingsChanged += self.__handleSettingsChange def getTargetMProv(self): replayCtrl = BattleReplay.g_replayCtrl if replayCtrl.isPlaying and replayCtrl.playerVehicleID != 0: vehicleID = replayCtrl.playerVehicleID else: vehicleID = BigWorld.player().playerVehicleID return BigWorld.entity(vehicleID).matrix def reinitMatrix(self): self.__setVehicleMProv(self.getTargetMProv()) def setToVehicleDirection(self): matrix = Math.Matrix(self.getTargetMProv()) self.setYawPitch(matrix.yaw, matrix.pitch) def destroy(self): self.settingsCore.onSettingsChanged -= self.__handleSettingsChange CallbackDelayer.destroy(self) self.disable() self.__onChangeControlMode = None self.__cam = None if self.__aimingSystem is not None: self.__aimingSystem.destroy() self.__aimingSystem = None return def getPivotSettings(self): return self.__aimingSystem.getPivotSettings() def setPivotSettings(self, heightAboveBase, focusRadius): self.__aimingSystem.setPivotSettings(heightAboveBase, focusRadius) def __setDynamicCollisions(self, enable): if self.__cam is not None: self.__cam.setDynamicCollisions(enable) if self.__aimingSystem is not None: self.__aimingSystem.setDynamicCollisions(enable) return def focusOnPos(self, preferredPos): self.__aimingSystem.focusOnPos(preferredPos) def shiftCamPos(self, shift=None): matrixProduct = self.__aimingSystem.vehicleMProv shiftMat = matrixProduct.a if shift is not None: camDirection = Math.Vector3(math.sin(self.angles[0]), 0, math.cos(self.angles[0])) normal = Math.Vector3(camDirection) normal.x = camDirection.z normal.z = -camDirection.x shiftMat.translation += camDirection * shift.z + Math.Vector3( 0, 1, 0) * shift.y + normal * shift.x else: shiftMat.setIdentity() return def enable(self, preferredPos=None, closesDist=False, postmortemParams=None, turretYaw=None, gunPitch=None, camTransitionParams=None): replayCtrl = BattleReplay.g_replayCtrl if replayCtrl.isRecording: replayCtrl.setAimClipPosition(self.__aimOffset) self.measureDeltaTime() camDist = None vehicle = BigWorld.player().getVehicleAttached() initialVehicleMatrix = BigWorld.player().getOwnVehicleMatrix( ) if vehicle is None else vehicle.matrix vehicleMProv = initialVehicleMatrix if not self.__postmortemMode: if closesDist: camDist = self.__cfg['distRange'][0] elif postmortemParams is not None: self.__aimingSystem.yaw = postmortemParams[0][0] self.__aimingSystem.pitch = postmortemParams[0][1] camDist = postmortemParams[1] else: camDist = self.__cfg['distRange'][1] replayCtrl = BattleReplay.g_replayCtrl if replayCtrl.isPlaying: camDist = None vehicle = BigWorld.entity(replayCtrl.playerVehicleID) if vehicle is not None: vehicleMProv = vehicle.matrix if camDist is not None: self.setCameraDistance(camDist) else: self.__inputInertia.teleport(self.__calcRelativeDist()) if camTransitionParams is not None: cameraTransitionDuration = camTransitionParams.get( 'cameraTransitionDuration', -1) if cameraTransitionDuration > 0: previousCamMatrix = camTransitionParams.get('camMatrix', None) self.__setupCameraTransition(cameraTransitionDuration, previousCamMatrix) self.vehicleMProv = vehicleMProv self.__setDynamicCollisions(True) BigWorld.camera(self.__cam) self.__aimingSystem.enable(preferredPos, turretYaw, gunPitch) self.__aimingSystem.aimMatrix = self.__calcAimMatrix() self.__cameraUpdate() self.delayCallback(0.0, self.__cameraUpdate) from gui import g_guiResetters g_guiResetters.add(self.__onRecreateDevice) return def __handleSettingsChange(self, diff): if 'fov' in diff or 'dynamicFov' in diff: self.__inputInertia.teleport(self.__calcRelativeDist(), self.__calculateInputInertiaMinMax()) def __calculateInputInertiaMinMax(self): if self.settingsCore.getSetting('dynamicFov'): _, minFov, maxFov = self.settingsCore.getSetting('fov') kMin = minFov / maxFov else: kMin = 1.0 return MinMax(kMin, 1.0) def __setupCameraTransition(self, duration, previousMatrix): self.__endCamPosition = previousMatrix.translation - Math.Vector3( self.__cam.cameraPositionProvider.b.value[0:3]) self.__lastTransitionEndAimYaw = 0 positionInterpolation = DynamicCameraInterpolator.DataProvider( start=lambda worldStartingPoint=previousMatrix.translation: worldStartingPoint - Math.Vector3(self.__cam.cameraPositionProvider .b.value[0:3]), end=lambda: self.__endCamPosition, interpolate=None, set=lambda interpolatedPosition: self.__setCameraPosition( interpolatedPosition)) previousWorldAimPoint = previousMatrix.translation + previousMatrix.applyToAxis( 2) * 100 self.__previousAimVector = previousMatrix.applyToAxis(2) self.__targetAimVector = previousWorldAimPoint - Math.Vector3( self.__cam.cameraPositionProvider.b.value[0:3]) aimInterpolation = DynamicCameraInterpolator.DataProvider( start=lambda startingWorldAim=previousWorldAimPoint: startingWorldAim, end=lambda: self.__targetAimVector + Math.Vector3( self.__cam.cameraPositionProvider.b.value[0:3]), interpolate=partial( self.__interpolateAim, previousMatrix.translation, lambda: self.__endCamPosition + Math.Vector3( self.__cam.cameraPositionProvider.b.value[0:3])), set=lambda interpolatedAim: self.__setCameraAimPoint( interpolatedAim - Math.Vector3( self.__cam.cameraPositionProvider.b.value[0:3]))) pivotMatrix = Math.Matrix() self.__cam.pivotPositionProvider = Math.Vector4Translation(pivotMatrix) pivotInterpolation = DynamicCameraInterpolator.DataProvider( start=lambda startingPivot=(previousMatrix.translation + previousWorldAimPoint ) * 0.5: startingPivot, end=lambda: Math.Vector3(self.__cam.pivotPositionProvider.value[0:3 ]), interpolate=None, set=lambda interpolatedPivot, matrix=pivotMatrix: mathUtils. setTranslation(pivotMatrix, interpolatedPivot)) self.__cameraInterpolator.setup( duration, (positionInterpolation, aimInterpolation, pivotInterpolation)) self.__cameraInterpolator.start() return def __interpolateAim(self, worldStartPosition, worldEndPositionGetter, worldStartAim, worldEndAim, t): relativeStartAim = worldStartAim - worldStartPosition relativeEndAim = worldEndAim - worldEndPositionGetter() deltaAngle = self.__lastTransitionEndAimYaw - relativeEndAim.yaw if math.fabs(deltaAngle) > math.pi and not self.__transitionYawDirty: self.__tansitionYawRevolutions += math.copysign(1, deltaAngle) self.__lastTransitionEndAimYaw = self.__lastTransitionEndAimYaw - deltaAngle + math.copysign( math.pi, deltaAngle) else: self.__lastTransitionEndAimYaw = relativeEndAim.yaw self.__transitionYawDirty = False targetYaw = self.__lastTransitionEndAimYaw + self.__tansitionYawRevolutions * 2 * math.pi angles = mathUtils.lerp( Math.Vector2(relativeStartAim.pitch, relativeStartAim.yaw), Math.Vector2(relativeEndAim.pitch, targetYaw), t) result = Math.Vector3() result.setPitchYaw(angles.x, angles.y) result = result.scale(1000) return Math.Vector3( self.__cam.cameraPositionProvider.value[0:3]) + result def __refineVehicleMProv(self, vehicleMProv): vehicleTranslationOnly = Math.WGTranslationOnlyMP() vehicleTranslationOnly.source = vehicleMProv refinedMatrixProvider = Math.MatrixProduct() refinedMatrixProvider.a = mathUtils.createIdentityMatrix() refinedMatrixProvider.b = vehicleTranslationOnly return refinedMatrixProvider def __setupCameraProviders(self, vehicleMProv): vehiclePos = Math.Vector4Translation(vehicleMProv) cameraPositionProvider = Math.Vector4Combiner() cameraPositionProvider.fn = 'ADD' cameraPositionProvider.a = Vector4(0, 0, 0, 0) cameraPositionProvider.b = vehiclePos cameraAimPointProvider = Math.Vector4Combiner() cameraAimPointProvider.fn = 'ADD' cameraAimPointProvider.a = Vector4(0, 0, 1, 0) cameraAimPointProvider.b = vehiclePos self.__cam.cameraPositionProvider = cameraPositionProvider self.__cam.aimPointProvider = cameraAimPointProvider self.__cam.pivotPositionProvider = self.__aimingSystem.positionAboveVehicleProv def __setCameraPosition(self, relativeToVehiclePosition): self.__cam.cameraPositionProvider.a = Vector4( relativeToVehiclePosition.x, relativeToVehiclePosition.y, relativeToVehiclePosition.z, 1.0) def __setCameraAimPoint(self, relativeToVehiclePosition): self.__cam.aimPointProvider.a = Vector4(relativeToVehiclePosition.x, relativeToVehiclePosition.y, relativeToVehiclePosition.z, 1.0) def disable(self): from gui import g_guiResetters if self.__onRecreateDevice in g_guiResetters: g_guiResetters.remove(self.__onRecreateDevice) self.__setDynamicCollisions(False) self.__cam.speedTreeTarget = None self.__resetCameraTransition() if self.__shiftKeySensor is not None: self.__shiftKeySensor.reset(Math.Vector3()) self.stopCallback(self.__cameraUpdate) self.__movementOscillator.reset() self.__impulseOscillator.reset() self.__noiseOscillator.reset() self.__accelerationSmoother.reset() self.__autoUpdateDxDyDz.set(0) self.__updatedByKeyboard = False dist = self.__calcRelativeDist() if dist is not None: self.__inputInertia.teleport(dist) FovExtended.instance().resetFov() return def update(self, dx, dy, dz, rotateMode=True, zoomMode=True, updatedByKeyboard=False): self.__curSense = self.__cfg[ 'keySensitivity'] if updatedByKeyboard else self.__cfg[ 'sensitivity'] self.__curScrollSense = self.__cfg[ 'keySensitivity'] if updatedByKeyboard else self.__cfg[ 'scrollSensitivity'] self.__updatedByKeyboard = updatedByKeyboard if updatedByKeyboard: self.__autoUpdateDxDyDz.set(dx, dy, dz) else: self.__autoUpdateDxDyDz.set(0) self.__update(dx, dy, dz, rotateMode, zoomMode, False) def restoreDefaultsState(self): LOG_ERROR('ArcadeCamera::restoreDefaultState is obsolete!') def getConfigValue(self, name): return self.__cfg.get(name) def getUserConfigValue(self, name): return self.__userCfg.get(name) def setUserConfigValue(self, name, value): if name not in self.__userCfg: return else: self.__userCfg[name] = value if name not in ('keySensitivity', 'sensitivity', 'scrollSensitivity'): self.__cfg[name] = self.__userCfg[name] if name == 'fovMultMinMaxDist' and getattr( self, '_ArcadeCamera__aimingSystem', None) is not None: self.__inputInertia.teleport(self.__calcRelativeDist(), value) else: self.__cfg[name] = self.__baseCfg[name] * self.__userCfg[name] return def setCameraDistance(self, distance): distRange = self.__cfg['distRange'] clampedDist = mathUtils.clamp(distRange[0], distRange[1], distance) self.__aimingSystem.distanceFromFocus = clampedDist self.__inputInertia.teleport(self.__calcRelativeDist()) def getCameraDistance(self): return self.__aimingSystem.distanceFromFocus def setYawPitch(self, yaw, pitch): self.__aimingSystem.yaw = yaw self.__aimingSystem.pitch = pitch def calcYawPitchDelta(self, dx, dy): return (dx * self.__curSense * (-1 if self.__cfg['horzInvert'] else 1), dy * self.__curSense * (-1 if self.__cfg['vertInvert'] else 1)) def __resetCameraTransition(self): if self.__cameraInterpolator.isInitialized: self.__cameraInterpolator.reset() self.__endCamPosition = None self.__targetAimVector = None self.__previousAimVector = None self.__lastTransitionEndAimYaw = 0 self.__tansitionYawRevolutions = 0 self.__transitionYawDirty = False return def __updateAngles(self, dx, dy): yawDelta, pitchDelta = self.calcYawPitchDelta(dx, dy) self.__aimingSystem.handleMovement(yawDelta, -pitchDelta) return (self.__aimingSystem.yaw, self.__aimingSystem.pitch, 0) def __update(self, dx, dy, dz, rotateMode=True, zoomMode=True, isCallback=False): if not self.__aimingSystem: return else: prevPos = self.__inputInertia.calcWorldPos( self.__aimingSystem.matrix) distChanged = False if zoomMode and dz != 0: prevDist = self.__aimingSystem.distanceFromFocus distDelta = dz * float(self.__curScrollSense) distMinMax = self.__cfg['distRange'] newDist = mathUtils.clamp(distMinMax.min, distMinMax.max, prevDist - distDelta) floatEps = 0.001 if abs(newDist - prevDist) > floatEps: self.__aimingSystem.distanceFromFocus = newDist self.__userCfg['startDist'] = newDist self.__inputInertia.glideFov(self.__calcRelativeDist()) self.__aimingSystem.aimMatrix = self.__calcAimMatrix() distChanged = True if not self.__updatedByKeyboard: if abs(newDist - prevDist) < floatEps and mathUtils.almostZero( newDist - distMinMax.min): if self.__onChangeControlMode is not None: self.__onChangeControlMode() return if rotateMode: self.__updateAngles(dx, dy) if ENABLE_INPUT_ROTATION_INERTIA and not distChanged: self.__aimingSystem.update(0.0) if ENABLE_INPUT_ROTATION_INERTIA or distChanged: worldDeltaPos = prevPos - self.__aimingSystem.matrix.translation matInv = Matrix(self.__aimingSystem.matrix) matInv.invert() self.__inputInertia.glide(matInv.applyVector(worldDeltaPos)) return def __cameraUpdate(self): if not (self.__autoUpdateDxDyDz.x == 0.0 and self.__autoUpdateDxDyDz.y == 0.0 and self.__autoUpdateDxDyDz.z == 0.0): self.__update(self.__autoUpdateDxDyDz.x, self.__autoUpdateDxDyDz.y, self.__autoUpdateDxDyDz.z) inertDt = deltaTime = self.measureDeltaTime() replayCtrl = BattleReplay.g_replayCtrl if replayCtrl.isPlaying: repSpeed = replayCtrl.playbackSpeed if repSpeed == 0.0: inertDt = 0.01 deltaTime = 0.0 else: inertDt = deltaTime = deltaTime / repSpeed self.__aimingSystem.update(deltaTime) virginShotPoint = self.__aimingSystem.getThirdPersonShotPoint() delta = self.__inputInertia.positionDelta sign = delta.dot(Vector3(0, 0, 1)) self.__inputInertia.update(inertDt) delta = (delta - self.__inputInertia.positionDelta).length if delta != 0.0: self.__cam.setScrollDelta(math.copysign(delta, sign)) FovExtended.instance().setFovByMultiplier( self.__inputInertia.fovZoomMultiplier) unshakenPos = self.__inputInertia.calcWorldPos( self.__aimingSystem.matrix) vehMatrix = Math.Matrix(self.__aimingSystem.vehicleMProv) vehiclePos = vehMatrix.translation fromVehicleToUnshakedPos = unshakenPos - vehiclePos deviationBasis = mathUtils.createRotationMatrix( Vector3(self.__aimingSystem.yaw, 0, 0)) impulseDeviation, movementDeviation, oscillationsZoomMultiplier = self.__updateOscillators( deltaTime) relCamPosMatrix = mathUtils.createTranslationMatrix(impulseDeviation + movementDeviation) relCamPosMatrix.postMultiply(deviationBasis) relCamPosMatrix.translation += fromVehicleToUnshakedPos upRotMat = mathUtils.createRotationMatrix( Vector3( 0, 0, -impulseDeviation.x * self.__dynamicCfg['sideImpulseToRollRatio'] - self.__noiseOscillator.deviation.z)) upRotMat.postMultiply(relCamPosMatrix) self.__cam.up = upRotMat.applyVector(Vector3(0, 1, 0)) relTranslation = relCamPosMatrix.translation shotPoint = self.__calcFocalPoint(virginShotPoint, deltaTime) vehToShotPoint = shotPoint - vehiclePos if self.__cameraInterpolator.active: self.__targetAimVector = vehToShotPoint self.__endCamPosition = relTranslation if not self.__cameraInterpolator.tick(): self.__cam.pivotPositionProvider = self.__aimingSystem.positionAboveVehicleProv else: self.__setCameraAimPoint(vehToShotPoint) self.__setCameraPosition(relTranslation) replayCtrl = BattleReplay.g_replayCtrl if replayCtrl.isPlaying and replayCtrl.isControllingCamera: aimOffset = replayCtrl.getAimClipPosition() if not BigWorld.player().isForcedGuiControlMode() and GUI.mcursor( ).inFocus: GUI.mcursor().position = aimOffset else: aimOffset = self.__calcAimOffset(oscillationsZoomMultiplier) if replayCtrl.isRecording: replayCtrl.setAimClipPosition(aimOffset) self.__cam.aimPointClipCoords = aimOffset self.__aimOffset = aimOffset if self.__shiftKeySensor is not None: self.__shiftKeySensor.update(1.0) if self.__shiftKeySensor.currentVelocity.lengthSquared > 0.0: self.shiftCamPos(self.__shiftKeySensor.currentVelocity) self.__shiftKeySensor.currentVelocity = Math.Vector3() return 0.0 def __calcFocalPoint(self, shotPoint, deltaTime): aimStartPoint = self.__aimingSystem.matrix.translation aimDir = shotPoint - aimStartPoint distToShotPoint = aimDir.length if distToShotPoint < 0.001: return shotPoint aimDir /= distToShotPoint absDiff = abs(distToShotPoint - self.__focalPointDist) if absDiff < ArcadeCamera._FOCAL_POINT_MIN_DIFF or absDiff <= ArcadeCamera._FOCAL_POINT_CHANGE_SPEED * deltaTime: self.__focalPointDist = distToShotPoint return shotPoint changeDir = (distToShotPoint - self.__focalPointDist) / absDiff self.__focalPointDist += changeDir * ArcadeCamera._FOCAL_POINT_CHANGE_SPEED * deltaTime return aimStartPoint + aimDir * self.__focalPointDist def __calcAimOffset(self, oscillationsZoomMultiplier): fov = BigWorld.projection().fov aspect = cameras.getScreenAspectRatio() yTan = math.tan(fov * 0.5) xTan = yTan * aspect defaultX = self.__defaultAimOffset[0] defaultY = self.__defaultAimOffset[1] yawFromImpulse = self.__impulseOscillator.deviation.x * self.__dynamicCfg[ 'sideImpulseToYawRatio'] xImpulseDeviationTan = math.tan( -(yawFromImpulse + self.__noiseOscillator.deviation.x) * oscillationsZoomMultiplier) pitchFromImpulse = self.__impulseOscillator.deviation.z * self.__dynamicCfg[ 'frontImpulseToPitchRatio'] yImpulseDeviationTan = math.tan( (pitchFromImpulse + self.__noiseOscillator.deviation.y) * oscillationsZoomMultiplier) totalOffset = Vector2( (defaultX * xTan + xImpulseDeviationTan) / (xTan * (1 - defaultX * xTan * xImpulseDeviationTan)), (defaultY * yTan + yImpulseDeviationTan) / (yTan * (1 - defaultY * yTan * yImpulseDeviationTan))) return totalOffset def __calcRelativeDist(self): if self.__aimingSystem is not None: distRange = self.__cfg['distRange'] curDist = self.__aimingSystem.distanceFromFocus return (curDist - distRange[0]) / (distRange[1] - distRange[0]) else: return def __calcCurOscillatorAcceleration(self, deltaTime): vehicle = BigWorld.player().getVehicleAttached() if vehicle is None: return Vector3(0, 0, 0) else: vehFilter = vehicle.filter curVelocity = getattr(vehFilter, 'velocity', Vector3(0.0)) relativeSpeed = curVelocity.length / vehicle.typeDescriptor.physics[ 'speedLimits'][0] if relativeSpeed >= ArcadeCamera._MIN_REL_SPEED_ACC_SMOOTHING: self.__accelerationSmoother.maxAllowedAcceleration = self.__dynamicCfg[ 'accelerationThreshold'] else: self.__accelerationSmoother.maxAllowedAcceleration = self.__dynamicCfg[ 'accelerationMax'] acceleration = self.__accelerationSmoother.update( vehicle, deltaTime) yawMat = mathUtils.createRotationMatrix( (-self.__aimingSystem.yaw, 0, 0)) acceleration = yawMat.applyVector(-acceleration) oscillatorAcceleration = Vector3(acceleration.x, acceleration.y, acceleration.z) return oscillatorAcceleration * self.__dynamicCfg[ 'accelerationSensitivity'] def __updateOscillators(self, deltaTime): if not ArcadeCamera.isCameraDynamic(): self.__impulseOscillator.reset() self.__movementOscillator.reset() self.__noiseOscillator.reset() return (Vector3(0), Vector3(0), 1.0) oscillatorAcceleration = self.__calcCurOscillatorAcceleration( deltaTime) self.__movementOscillator.externalForce += oscillatorAcceleration self.__impulseOscillator.update(deltaTime) self.__movementOscillator.update(deltaTime) self.__noiseOscillator.update(deltaTime) self.__impulseOscillator.externalForce = Vector3(0) self.__movementOscillator.externalForce = Vector3(0) self.__noiseOscillator.externalForce = Vector3(0) relDist = self.__calcRelativeDist() zoomMultiplier = mathUtils.lerp(1.0, self.__dynamicCfg['zoomExposure'], relDist) impulseDeviation = Vector3(self.__impulseOscillator.deviation) impulseDeviation.set(impulseDeviation.x * zoomMultiplier, impulseDeviation.y * zoomMultiplier, impulseDeviation.z * zoomMultiplier) movementDeviation = Vector3(self.__movementOscillator.deviation) movementDeviation.set(movementDeviation.x * zoomMultiplier, movementDeviation.y * zoomMultiplier, movementDeviation.z * zoomMultiplier) return (impulseDeviation, movementDeviation, zoomMultiplier) def applyImpulse(self, position, impulse, reason=ImpulseReason.ME_HIT): adjustedImpulse, noiseMagnitude = self.__dynamicCfg.adjustImpulse( impulse, reason) yawMat = mathUtils.createRotationMatrix( (-self.__aimingSystem.yaw, 0, 0)) impulseLocal = yawMat.applyVector(adjustedImpulse) self.__impulseOscillator.applyImpulse(impulseLocal) self.__applyNoiseImpulse(noiseMagnitude) def applyDistantImpulse(self, position, impulseValue, reason=ImpulseReason.ME_HIT): applicationPosition = self.__cam.position if reason == ImpulseReason.SPLASH: applicationPosition = Matrix(self.vehicleMProv).translation impulse = applicationPosition - position distance = impulse.length if distance < 1.0: distance = 1.0 impulse.normalise() if reason == ImpulseReason.OTHER_SHOT and distance <= self.__dynamicCfg[ 'maxShotImpulseDistance']: impulse *= impulseValue / distance elif reason == ImpulseReason.SPLASH or reason == ImpulseReason.HE_EXPLOSION: impulse *= impulseValue / distance elif reason == ImpulseReason.VEHICLE_EXPLOSION and distance <= self.__dynamicCfg[ 'maxExplosionImpulseDistance']: impulse *= impulseValue / distance else: return self.applyImpulse(position, impulse, reason) def __applyNoiseImpulse(self, noiseMagnitude): noiseImpulse = mathUtils.RandomVectors.random3(noiseMagnitude) self.__noiseOscillator.applyImpulse(noiseImpulse) def handleKeyEvent(self, isDown, key, mods, event=None): if self.__shiftKeySensor is None: return False elif BigWorld.isKeyDown(Keys.KEY_CAPSLOCK) and mods & 4: if key == Keys.KEY_C: self.shiftCamPos() return self.__shiftKeySensor.handleKeyEvent(key, isDown) else: self.__shiftKeySensor.reset(Math.Vector3()) return False def reload(self): if not constants.IS_DEVELOPMENT: return import ResMgr ResMgr.purge('gui/avatar_input_handler.xml') cameraSec = ResMgr.openSection( 'gui/avatar_input_handler.xml/arcadeMode/camera/') self.__readCfg(cameraSec) def __calcAimMatrix(self): endMult = self.__inputInertia.endZoomMultiplier fov = FovExtended.instance().actualDefaultVerticalFov * endMult offset = self.__defaultAimOffset return cameras.getAimMatrix(-offset[0], -offset[1], fov) def __readCfg(self, dataSec): if dataSec is None: LOG_WARNING( 'Invalid section <arcadeMode/camera> in avatar_input_handler.xml' ) self.__baseCfg = dict() bcfg = self.__baseCfg bcfg['keySensitivity'] = readFloat(dataSec, 'keySensitivity', 0, 10, 0.01) bcfg['sensitivity'] = readFloat(dataSec, 'sensitivity', 0, 10, 0.01) bcfg['scrollSensitivity'] = readFloat(dataSec, 'scrollSensitivity', 0, 10, 0.01) bcfg['angleRange'] = readVec2(dataSec, 'angleRange', (0, 0), (180, 180), (10, 110)) distRangeVec = readVec2(dataSec, 'distRange', (1, 1), (100, 100), (2, 20)) bcfg['distRange'] = MinMax(distRangeVec.x, distRangeVec.y) bcfg['minStartDist'] = readFloat(dataSec, 'minStartDist', bcfg['distRange'][0], bcfg['distRange'][1], bcfg['distRange'][0]) bcfg['optimalStartDist'] = readFloat(dataSec, 'optimalStartDist', bcfg['distRange'][0], bcfg['distRange'][1], bcfg['distRange'][0]) bcfg['angleRange'][0] = math.radians( bcfg['angleRange'][0]) - math.pi * 0.5 bcfg['angleRange'][1] = math.radians( bcfg['angleRange'][1]) - math.pi * 0.5 bcfg['fovMultMinMaxDist'] = MinMax( readFloat(dataSec, 'fovMultMinDist', 0.1, 100, 1.0), readFloat(dataSec, 'fovMultMaxDist', 0.1, 100, 1.0)) ds = Settings.g_instance.userPrefs[Settings.KEY_CONTROL_MODE] if ds is not None: ds = ds['arcadeMode/camera'] self.__userCfg = dict() ucfg = self.__userCfg ucfg['horzInvert'] = self.settingsCore.getSetting('mouseHorzInvert') ucfg['vertInvert'] = self.settingsCore.getSetting('mouseVertInvert') ucfg['sniperModeByShift'] = self.settingsCore.getSetting( 'sniperModeByShift') ucfg['keySensitivity'] = readFloat(ds, 'keySensitivity', 0.0, 10.0, 1.0) ucfg['sensitivity'] = readFloat(ds, 'sensitivity', 0.0, 10.0, 1.0) ucfg['scrollSensitivity'] = readFloat(ds, 'scrollSensitivity', 0.0, 10.0, 1.0) ucfg['startDist'] = readFloat(ds, 'startDist', bcfg['distRange'][0], 500, bcfg['optimalStartDist']) if ucfg['startDist'] < bcfg['minStartDist']: ucfg['startDist'] = bcfg['optimalStartDist'] ucfg['startAngle'] = readFloat(ds, 'startAngle', 5, 180, 60) ucfg['startAngle'] = math.radians(ucfg['startAngle']) - math.pi * 0.5 ucfg['fovMultMinMaxDist'] = MinMax( readFloat(ds, 'fovMultMinDist', 0.1, 100, bcfg['fovMultMinMaxDist'].min), readFloat(ds, 'fovMultMaxDist', 0.1, 100, bcfg['fovMultMinMaxDist'].max)) self.__cfg = dict() cfg = self.__cfg cfg['keySensitivity'] = bcfg['keySensitivity'] cfg['sensitivity'] = bcfg['sensitivity'] cfg['scrollSensitivity'] = bcfg['scrollSensitivity'] cfg['angleRange'] = bcfg['angleRange'] cfg['distRange'] = bcfg['distRange'] cfg['minStartDist'] = bcfg['minStartDist'] cfg['horzInvert'] = ucfg['horzInvert'] cfg['vertInvert'] = ucfg['vertInvert'] cfg['keySensitivity'] *= ucfg['keySensitivity'] cfg['sensitivity'] *= ucfg['sensitivity'] cfg['scrollSensitivity'] *= ucfg['scrollSensitivity'] cfg['startDist'] = ucfg['startDist'] cfg['startAngle'] = ucfg['startAngle'] cfg['fovMultMinMaxDist'] = ucfg['fovMultMinMaxDist'] cfg['sniperModeByShift'] = ucfg['sniperModeByShift'] enableShift = dataSec.readBool('shift', False) if enableShift: movementMappings = dict() movementMappings[Keys.KEY_A] = Math.Vector3(-1, 0, 0) movementMappings[Keys.KEY_D] = Math.Vector3(1, 0, 0) movementMappings[Keys.KEY_Q] = Math.Vector3(0, 1, 0) movementMappings[Keys.KEY_E] = Math.Vector3(0, -1, 0) movementMappings[Keys.KEY_W] = Math.Vector3(0, 0, 1) movementMappings[Keys.KEY_S] = Math.Vector3(0, 0, -1) shiftSensitivity = dataSec.readFloat('shiftSensitivity', 0.5) self.__shiftKeySensor = KeySensor(movementMappings, shiftSensitivity) self.__shiftKeySensor.reset(Math.Vector3()) dynamicsSection = dataSec['dynamics'] self.__impulseOscillator = createOscillatorFromSection( dynamicsSection['impulseOscillator'], False) self.__movementOscillator = createOscillatorFromSection( dynamicsSection['movementOscillator'], False) self.__movementOscillator = Math.PyCompoundOscillator( self.__movementOscillator, Math.PyOscillator(1.0, Vector3(50), Vector3(20), Vector3(0.01, 0.0, 0.01))) self.__noiseOscillator = createOscillatorFromSection( dynamicsSection['randomNoiseOscillatorSpherical']) self.__dynamicCfg.readImpulsesConfig(dynamicsSection) self.__dynamicCfg['accelerationSensitivity'] = readFloat( dynamicsSection, 'accelerationSensitivity', -1000, 1000, 0.1) self.__dynamicCfg['frontImpulseToPitchRatio'] = math.radians( readFloat(dynamicsSection, 'frontImpulseToPitchRatio', -1000, 1000, 0.1)) self.__dynamicCfg['sideImpulseToRollRatio'] = math.radians( readFloat(dynamicsSection, 'sideImpulseToRollRatio', -1000, 1000, 0.1)) self.__dynamicCfg['sideImpulseToYawRatio'] = math.radians( readFloat(dynamicsSection, 'sideImpulseToYawRatio', -1000, 1000, 0.1)) accelerationThreshold = readFloat(dynamicsSection, 'accelerationThreshold', 0.0, 1000.0, 0.1) self.__dynamicCfg['accelerationThreshold'] = accelerationThreshold self.__dynamicCfg['accelerationMax'] = readFloat( dynamicsSection, 'accelerationMax', 0.0, 1000.0, 0.1) self.__dynamicCfg['maxShotImpulseDistance'] = readFloat( dynamicsSection, 'maxShotImpulseDistance', 0.0, 1000.0, 10.0) self.__dynamicCfg['maxExplosionImpulseDistance'] = readFloat( dynamicsSection, 'maxExplosionImpulseDistance', 0.0, 1000.0, 10.0) self.__dynamicCfg['zoomExposure'] = readFloat(dynamicsSection, 'zoomExposure', 0.0, 1000.0, 0.25) accelerationFilter = mathUtils.RangeFilter( self.__dynamicCfg['accelerationThreshold'], self.__dynamicCfg['accelerationMax'], 100, mathUtils.SMAFilter(ArcadeCamera._FILTER_LENGTH)) maxAccelerationDuration = readFloat( dynamicsSection, 'maxAccelerationDuration', 0.0, 10000.0, ArcadeCamera._DEFAULT_MAX_ACCELERATION_DURATION) self.__accelerationSmoother = AccelerationSmoother( accelerationFilter, maxAccelerationDuration) self.__inputInertia = _InputInertia( self.__calculateInputInertiaMinMax(), 0.0) advancedCollider = dataSec['advancedCollider'] self.__adCfg = dict() cfg = self.__adCfg if advancedCollider is None: LOG_ERROR('<advancedCollider> dataSection is not found!') cfg['enable'] = False else: cfg['enable'] = advancedCollider.readBool('enable', False) cfg['fovRatio'] = advancedCollider.readFloat('fovRatio', 2.0) cfg['rollbackSpeed'] = advancedCollider.readFloat( 'rollbackSpeed', 1.0) cfg['minimalCameraDistance'] = self.__cfg['distRange'][0] cfg['speedThreshold'] = advancedCollider.readFloat( 'speedThreshold', 0.1) cfg['minimalVolume'] = advancedCollider.readFloat( 'minimalVolume', 200.0) cfg['volumeGroups'] = dict() for group in VOLUME_GROUPS_NAMES: groups = advancedCollider['volumeGroups'] cfg['volumeGroups'][group] = CollisionVolumeGroup.fromSection( groups[group]) return def writeUserPreferences(self): ds = Settings.g_instance.userPrefs if not ds.has_key(Settings.KEY_CONTROL_MODE): ds.write(Settings.KEY_CONTROL_MODE, '') ucfg = self.__userCfg ds = ds[Settings.KEY_CONTROL_MODE] ds.writeBool('arcadeMode/camera/horzInvert', ucfg['horzInvert']) ds.writeBool('arcadeMode/camera/vertInvert', ucfg['vertInvert']) ds.writeFloat('arcadeMode/camera/keySensitivity', ucfg['keySensitivity']) ds.writeFloat('arcadeMode/camera/sensitivity', ucfg['sensitivity']) ds.writeFloat('arcadeMode/camera/scrollSensitivity', ucfg['scrollSensitivity']) ds.writeFloat('arcadeMode/camera/startDist', ucfg['startDist']) ds.writeFloat('arcadeMode/camera/fovMultMinDist', ucfg['fovMultMinMaxDist'].min) ds.writeFloat('arcadeMode/camera/fovMultMaxDist', ucfg['fovMultMinMaxDist'].max) ucfg['startAngle'] = math.degrees(ucfg['startAngle'] + math.pi * 0.5) ds.writeFloat('arcadeMode/camera/startAngle', ucfg['startAngle']) def __onRecreateDevice(self): self.__aimingSystem.aimMatrix = self.__calcAimMatrix()
class SniperCamera(ICamera, CallbackDelayer): _DYNAMIC_ENABLED = True @staticmethod def enableDynamicCamera(enable): SniperCamera._DYNAMIC_ENABLED = enable @staticmethod def isCameraDynamic(): return SniperCamera._DYNAMIC_ENABLED _FILTER_LENGTH = 5 _DEFAULT_MAX_ACCELERATION_DURATION = 1.5 _MIN_REL_SPEED_ACC_SMOOTHING = 0.7 camera = property(lambda self: self.__cam) aimingSystem = property(lambda self: self.__aimingSystem) settingsCore = dependency.descriptor(ISettingsCore) __aimOffset = aih_global_binding.bindRW( aih_global_binding.BINDING_ID.AIM_OFFSET) __zoomFactor = aih_global_binding.bindRW( aih_global_binding.BINDING_ID.ZOOM_FACTOR) def __init__(self, dataSec, defaultOffset=None, binoculars=None): CallbackDelayer.__init__(self) self.__impulseOscillator = None self.__movementOscillator = None self.__noiseOscillator = None self.__dynamicCfg = CameraDynamicConfig() self.__accelerationSmoother = None self.__readCfg(dataSec) if binoculars is None: return else: self.__cam = BigWorld.FreeCamera() self.__zoom = self.__cfg['zoom'] self.__curSense = 0 self.__curScrollSense = 0 self.__waitVehicleCallbackId = None self.__onChangeControlMode = None self.__aimingSystem = None self.__binoculars = binoculars self.__defaultAimOffset = defaultOffset or Vector2() self.__crosshairMatrix = createCrosshairMatrix( offsetFromNearPlane=self.__dynamicCfg['aimMarkerDistance']) self.__prevTime = BigWorld.time() self.__autoUpdateDxDyDz = Vector3(0, 0, 0) if BattleReplay.g_replayCtrl.isPlaying: BattleReplay.g_replayCtrl.setDataCallback( 'applyZoom', self.__applySerializedZoom) return def __onSettingsChanged(self, diff): if 'increasedZoom' in diff: self.__cfg['increasedZoom'] = diff['increasedZoom'] if not self.__cfg['increasedZoom']: self.__cfg['zoom'] = self.__zoom = self.__cfg['zooms'][:3][-1] if self.camera is BigWorld.camera(): self.delayCallback(0.0, self.__applyZoom, self.__cfg['zoom']) if ('fov' in diff or 'dynamicFov' in diff) and self.camera is BigWorld.camera(): self.delayCallback(0.01, self.__applyZoom, self.__cfg['zoom']) def create(self, onChangeControlMode=None): self.__onChangeControlMode = onChangeControlMode self.settingsCore.onSettingsChanged += self.__onSettingsChanged aimingSystemClass = SniperAimingSystemRemote if BigWorld.player( ).isObserver() else SniperAimingSystem self.__aimingSystem = aimingSystemClass() def destroy(self): self.settingsCore.onSettingsChanged -= self.__onSettingsChanged self.disable() self.__onChangeControlMode = None self.__cam = None if self.__aimingSystem is not None: self.__aimingSystem.destroy() self.__aimingSystem = None CallbackDelayer.destroy(self) return def enable(self, targetPos, saveZoom): self.__prevTime = BigWorld.time() player = BigWorld.player() if saveZoom: self.__zoom = self.__cfg['zoom'] else: self.__cfg['zoom'] = self.__zoom = self.__cfg['zooms'][0] self.__applyZoom(self.__zoom) self.__setupCamera(targetPos) vehicle = player.getVehicleAttached() if self.__waitVehicleCallbackId is not None: BigWorld.cancelCallback(self.__waitVehicleCallbackId) if vehicle is None: self.__waitVehicleCallbackId = BigWorld.callback( 0.1, self.__waitVehicle) else: self.__showVehicle(False) BigWorld.camera(self.__cam) if self.__cameraUpdate(False) >= 0.0: self.delayCallback(0.0, self.__cameraUpdate) return def disable(self): if self.__waitVehicleCallbackId is not None: BigWorld.cancelCallback(self.__waitVehicleCallbackId) self.__waitVehicleCallbackId = None self.__showVehicle(True) self.stopCallback(self.__cameraUpdate) if self.__aimingSystem is not None: self.__aimingSystem.disable() self.__movementOscillator.reset() self.__impulseOscillator.reset() self.__noiseOscillator.reset() self.__accelerationSmoother.reset() self.__autoUpdateDxDyDz.set(0) FovExtended.instance().resetFov() return def getConfigValue(self, name): return self.__cfg.get(name) def getUserConfigValue(self, name): return self.__userCfg.get(name) def setUserConfigValue(self, name, value): if name not in self.__userCfg: return self.__userCfg[name] = value if name not in ('keySensitivity', 'sensitivity', 'scrollSensitivity'): self.__cfg[name] = self.__userCfg[name] else: self.__cfg[name] = self.__baseCfg[name] * self.__userCfg[name] def update(self, dx, dy, dz, updatedByKeyboard=False): self.__curSense = self.__cfg[ 'keySensitivity'] if updatedByKeyboard else self.__cfg[ 'sensitivity'] self.__curScrollSense = self.__cfg[ 'keySensitivity'] if updatedByKeyboard else self.__cfg[ 'scrollSensitivity'] self.__curSense *= 1.0 / self.__zoom if updatedByKeyboard: self.__autoUpdateDxDyDz.set(dx, dy, dz) else: self.__autoUpdateDxDyDz.set(0, 0, 0) self.__rotateAndZoom(dx, dy, dz) def onRecreateDevice(self): self.__applyZoom(self.__zoom) def applyImpulse(self, position, impulse, reason=ImpulseReason.ME_HIT): adjustedImpulse, noiseMagnitude = self.__dynamicCfg.adjustImpulse( impulse, reason) camMatrix = Matrix(self.__cam.matrix) impulseLocal = camMatrix.applyVector(adjustedImpulse) impulseAsYPR = Vector3(impulseLocal.x, -impulseLocal.y + impulseLocal.z, 0) rollPart = self.__dynamicCfg['impulsePartToRoll'] impulseAsYPR.z = -rollPart * impulseAsYPR.x impulseAsYPR.x *= 1 - rollPart self.__impulseOscillator.applyImpulse(impulseAsYPR) self.__applyNoiseImpulse(noiseMagnitude) def applyDistantImpulse(self, position, impulseValue, reason=ImpulseReason.ME_HIT): impulse = self.__cam.position - position distance = impulse.length if distance < 1.0: distance = 1.0 impulse.normalise() if reason == ImpulseReason.OTHER_SHOT and distance <= self.__dynamicCfg[ 'maxShotImpulseDistance']: impulse *= impulseValue / distance elif reason == ImpulseReason.SPLASH or reason == ImpulseReason.HE_EXPLOSION: impulse *= impulseValue / distance elif reason == ImpulseReason.VEHICLE_EXPLOSION and distance <= self.__dynamicCfg[ 'maxExplosionImpulseDistance']: impulse *= impulseValue / distance else: return self.applyImpulse(position, impulse, reason) def setMaxZoom(self): zooms = self.__getZooms() self.__zoom = zooms[-1] self.__applyZoom(self.__zoom) def __applyNoiseImpulse(self, noiseMagnitude): noiseImpulse = mathUtils.RandomVectors.random3(noiseMagnitude) self.__noiseOscillator.applyImpulse(noiseImpulse) def __rotateAndZoom(self, dx, dy, dz): self.__aimingSystem.handleMovement(*self.__calcYawPitchDelta(dx, dy)) self.__setupZoom(dz) def __calcYawPitchDelta(self, dx, dy): return (dx * self.__curSense * (-1 if self.__cfg['horzInvert'] else 1), dy * self.__curSense * (-1 if self.__cfg['vertInvert'] else 1)) def __showVehicle(self, show): player = BigWorld.player() vehicle = player.getVehicleAttached() if vehicle is not None: LOG_DEBUG('__showVehicle: ', vehicle.id, show) vehicle.show(show) return def __setupCamera(self, targetPos): vehicleTypeDescriptor = BigWorld.player().vehicleTypeDescriptor playerGunMatFunction = AimingSystems.getPlayerGunMat if vehicleTypeDescriptor: if vehicleTypeDescriptor.isYawHullAimingAvailable: playerGunMatFunction = AimingSystems.getCenteredPlayerGunMat elif vehicleTypeDescriptor.turret.gunCamPosition: playerGunMatFunction = AimingSystems.getSniperCameraPlayerGunMat self.__aimingSystem.enable(targetPos, playerGunMatFunction) def __waitVehicle(self): player = BigWorld.player() vehicle = player.getVehicleAttached() if vehicle is not None and vehicle.isStarted: self.__waitVehicleCallbackId = None else: self.__waitVehicleCallbackId = BigWorld.callback( 0.1, self.__waitVehicle) return self.__showVehicle(False) return def __applySerializedZoom(self, zoomFactor): if BattleReplay.g_replayCtrl.isPlaying: if BattleReplay.g_replayCtrl.isControllingCamera: self.__applyZoom(zoomFactor) def __applyZoom(self, zoomFactor): self.__zoomFactor = zoomFactor if BattleReplay.g_replayCtrl.isRecording: BattleReplay.g_replayCtrl.serializeCallbackData( 'applyZoom', (zoomFactor, )) FovExtended.instance().setFovByMultiplier(1 / zoomFactor) def __getZooms(self): zooms = self.__cfg['zooms'] if not self.__cfg['increasedZoom']: zooms = zooms[:3] return zooms def __setupZoom(self, dz): if dz == 0: return else: zooms = self.__getZooms() prevZoom = self.__zoom if self.__zoom == zooms[ 0] and dz < 0 and self.__onChangeControlMode is not None: self.__onChangeControlMode(True) if dz > 0: for elem in zooms: if self.__zoom < elem: self.__zoom = elem self.__cfg['zoom'] = self.__zoom break elif dz < 0: for i in range(len(zooms) - 1, -1, -1): if self.__zoom > zooms[i]: self.__zoom = zooms[i] self.__cfg['zoom'] = self.__zoom break if prevZoom != self.__zoom: self.__applyZoom(self.__zoom) return def __cameraUpdate(self, allowModeChange=True): curTime = BigWorld.time() deltaTime = curTime - self.__prevTime self.__prevTime = curTime if not self.__autoUpdateDxDyDz.x == self.__autoUpdateDxDyDz.y == self.__autoUpdateDxDyDz.z == 0.0: self.__rotateAndZoom(self.__autoUpdateDxDyDz.x, self.__autoUpdateDxDyDz.y, self.__autoUpdateDxDyDz.z) self.__aimingSystem.update(deltaTime) localTransform, impulseTransform = self.__updateOscillators(deltaTime) zoom = self.__aimingSystem.overrideZoom(self.__zoom) aimMatrix = cameras.getAimMatrix(self.__defaultAimOffset.x, self.__defaultAimOffset.y) camMat = Matrix(aimMatrix) rodMat = mathUtils.createTranslationMatrix( -self.__dynamicCfg['pivotShift']) antiRodMat = mathUtils.createTranslationMatrix( self.__dynamicCfg['pivotShift']) camMat.postMultiply(rodMat) camMat.postMultiply(localTransform) camMat.postMultiply(antiRodMat) camMat.postMultiply(self.__aimingSystem.matrix) camMat.invert() self.__cam.set(camMat) if zoom != self.__zoom: self.__zoom = zoom self.__applyZoom(self.__zoom) replayCtrl = BattleReplay.g_replayCtrl if replayCtrl.isPlaying and replayCtrl.isControllingCamera: aimOffset = replayCtrl.getAimClipPosition() binocularsOffset = aimOffset if not BigWorld.player().isForcedGuiControlMode() and GUI.mcursor( ).inFocus: GUI.mcursor().position = aimOffset else: aimOffset = self.__calcAimOffset(impulseTransform) binocularsOffset = self.__calcAimOffset() if replayCtrl.isRecording: replayCtrl.setAimClipPosition(aimOffset) self.__aimOffset = aimOffset self.__binoculars.setMaskCenter(binocularsOffset.x, binocularsOffset.y) player = BigWorld.player() if allowModeChange and (self.__isPositionUnderwater( self.__aimingSystem.matrix.translation) or player.isGunLocked and not player.isObserverFPV): self.__onChangeControlMode(False) return -1 def __calcAimOffset(self, aimLocalTransform=None): aimingSystemMatrix = self.__aimingSystem.matrix worldCrosshair = Matrix(self.__crosshairMatrix) if aimLocalTransform is not None: worldCrosshair.postMultiply(aimLocalTransform) worldCrosshair.postMultiply(aimingSystemMatrix) aimOffset = cameras.projectPoint(worldCrosshair.translation) return Vector2(mathUtils.clamp(-0.95, 0.95, aimOffset.x), mathUtils.clamp(-0.95, 0.95, aimOffset.y)) def __calcCurOscillatorAcceleration(self, deltaTime): vehicle = BigWorld.player().vehicle if vehicle is None or not vehicle.isAlive(): return Vector3(0, 0, 0) else: curVelocity = vehicle.filter.velocity relativeSpeed = curVelocity.length / vehicle.typeDescriptor.physics[ 'speedLimits'][0] if relativeSpeed >= SniperCamera._MIN_REL_SPEED_ACC_SMOOTHING: self.__accelerationSmoother.maxAllowedAcceleration = self.__dynamicCfg[ 'accelerationThreshold'] else: self.__accelerationSmoother.maxAllowedAcceleration = self.__dynamicCfg[ 'accelerationMax'] acceleration = self.__accelerationSmoother.update( vehicle, deltaTime) camMat = Matrix(self.__cam.matrix) acceleration = camMat.applyVector(-acceleration) accelSensitivity = self.__dynamicCfg['accelerationSensitivity'] acceleration.x *= accelSensitivity.x acceleration.y *= accelSensitivity.y acceleration.z *= accelSensitivity.z oscillatorAcceleration = Vector3(0, -acceleration.y + acceleration.z, -acceleration.x) return oscillatorAcceleration def __updateOscillators(self, deltaTime): if not SniperCamera.isCameraDynamic(): self.__impulseOscillator.reset() self.__movementOscillator.reset() self.__noiseOscillator.reset() return (mathUtils.createRotationMatrix(Vector3(0, 0, 0)), mathUtils.createRotationMatrix(Vector3(0, 0, 0))) oscillatorAcceleration = self.__calcCurOscillatorAcceleration( deltaTime) self.__movementOscillator.externalForce += oscillatorAcceleration self.__impulseOscillator.update(deltaTime) self.__movementOscillator.update(deltaTime) self.__noiseOscillator.update(deltaTime) noiseDeviation = Vector3(self.__noiseOscillator.deviation) deviation = self.__impulseOscillator.deviation + self.__movementOscillator.deviation + noiseDeviation oscVelocity = self.__impulseOscillator.velocity + self.__movementOscillator.velocity + self.__noiseOscillator.velocity if abs(deviation.x) < 1e-05 and abs(oscVelocity.x) < 0.0001: deviation.x = 0 if abs(deviation.y) < 1e-05 and abs(oscVelocity.y) < 0.0001: deviation.y = 0 if abs(deviation.z) < 1e-05 and abs(oscVelocity.z) < 0.0001: deviation.z = 0 curZoomIdx = 0 zooms = self.__cfg['zooms'] for idx in xrange(len(zooms)): if self.__zoom == zooms[idx]: curZoomIdx = idx break zoomExposure = self.__zoom * self.__dynamicCfg['zoomExposure'][ curZoomIdx] deviation /= zoomExposure impulseDeviation = (self.__impulseOscillator.deviation + noiseDeviation) / zoomExposure self.__impulseOscillator.externalForce = Vector3(0) self.__movementOscillator.externalForce = Vector3(0) self.__noiseOscillator.externalForce = Vector3(0) return (mathUtils.createRotationMatrix( Vector3(deviation.x, deviation.y, deviation.z)), mathUtils.createRotationMatrix(impulseDeviation)) def __isPositionUnderwater(self, position): return BigWorld.wg_collideWater(position, position + Vector3(0, 1, 0), False) > -1.0 def reload(self): if not constants.IS_DEVELOPMENT: return import ResMgr ResMgr.purge('gui/avatar_input_handler.xml') cameraSec = ResMgr.openSection( 'gui/avatar_input_handler.xml/sniperMode/camera/') self.__readCfg(cameraSec) def __readCfg(self, dataSec): if not dataSec: LOG_WARNING( 'Invalid section <sniperMode/camera> in avatar_input_handler.xml' ) self.__baseCfg = dict() bcfg = self.__baseCfg bcfg['keySensitivity'] = readFloat(dataSec, 'keySensitivity', 0, 10, 0.005) bcfg['sensitivity'] = readFloat(dataSec, 'sensitivity', 0, 10, 0.005) bcfg['scrollSensitivity'] = readFloat(dataSec, 'scrollSensitivity', 0, 10, 0.005) zooms = dataSec.readString('zooms', '2 4 8 16 25') bcfg['zooms'] = [float(x) for x in zooms.split()] ds = Settings.g_instance.userPrefs[Settings.KEY_CONTROL_MODE] if ds is not None: ds = ds['sniperMode/camera'] self.__userCfg = dict() ucfg = self.__userCfg ucfg['horzInvert'] = self.settingsCore.getSetting('mouseHorzInvert') ucfg['vertInvert'] = self.settingsCore.getSetting('mouseVertInvert') ucfg['increasedZoom'] = self.settingsCore.getSetting('increasedZoom') maxZoom = bcfg['zooms'][-1] if ucfg['increasedZoom'] else bcfg[ 'zooms'][:3][-1] ucfg['zoom'] = readFloat(ds, 'zoom', 0.0, maxZoom, bcfg['zooms'][0]) ucfg['sniperModeByShift'] = self.settingsCore.getSetting( 'sniperModeByShift') ucfg['keySensitivity'] = readFloat(ds, 'keySensitivity', 0.0, 10.0, 1.0) ucfg['sensitivity'] = readFloat(ds, 'sensitivity', 0.0, 10.0, 1.0) ucfg['scrollSensitivity'] = readFloat(ds, 'scrollSensitivity', 0.0, 10.0, 1.0) self.__cfg = dict() cfg = self.__cfg cfg['keySensitivity'] = bcfg['keySensitivity'] cfg['sensitivity'] = bcfg['sensitivity'] cfg['scrollSensitivity'] = bcfg['scrollSensitivity'] cfg['zooms'] = bcfg['zooms'] cfg['keySensitivity'] *= ucfg['keySensitivity'] cfg['sensitivity'] *= ucfg['sensitivity'] cfg['scrollSensitivity'] *= ucfg['scrollSensitivity'] cfg['horzInvert'] = ucfg['horzInvert'] cfg['vertInvert'] = ucfg['vertInvert'] cfg['zoom'] = ucfg['zoom'] cfg['increasedZoom'] = ucfg['increasedZoom'] cfg['sniperModeByShift'] = ucfg['sniperModeByShift'] dynamicsSection = dataSec['dynamics'] self.__impulseOscillator = createOscillatorFromSection( dynamicsSection['impulseOscillator']) self.__movementOscillator = createOscillatorFromSection( dynamicsSection['movementOscillator']) self.__noiseOscillator = createOscillatorFromSection( dynamicsSection['randomNoiseOscillatorSpherical']) self.__dynamicCfg.readImpulsesConfig(dynamicsSection) self.__dynamicCfg['accelerationSensitivity'] = readVec3( dynamicsSection, 'accelerationSensitivity', (-1000, -1000, -1000), (1000, 1000, 1000), (0.5, 0.5, 0.5)) accelerationThreshold = readFloat(dynamicsSection, 'accelerationThreshold', 0.0, 1000.0, 0.1) self.__dynamicCfg['accelerationThreshold'] = accelerationThreshold self.__dynamicCfg['accelerationMax'] = readFloat( dynamicsSection, 'accelerationMax', 0.0, 1000.0, 0.1) self.__dynamicCfg['maxShotImpulseDistance'] = readFloat( dynamicsSection, 'maxShotImpulseDistance', 0.0, 1000.0, 10.0) self.__dynamicCfg['maxExplosionImpulseDistance'] = readFloat( dynamicsSection, 'maxExplosionImpulseDistance', 0.0, 1000.0, 10.0) self.__dynamicCfg['impulsePartToRoll'] = readFloat( dynamicsSection, 'impulsePartToRoll', 0.0, 1000.0, 0.3) self.__dynamicCfg['pivotShift'] = Vector3( 0, readFloat(dynamicsSection, 'pivotShift', -1000, 1000, -0.5), 0) self.__dynamicCfg['aimMarkerDistance'] = readFloat( dynamicsSection, 'aimMarkerDistance', -1000, 1000, 1.0) rawZoomExposure = dynamicsSection.readString('zoomExposure', '0.6 0.5 0.4 0.3 0.2') self.__dynamicCfg['zoomExposure'] = [ float(x) for x in rawZoomExposure.split() ] accelerationFilter = mathUtils.RangeFilter( self.__dynamicCfg['accelerationThreshold'], self.__dynamicCfg['accelerationMax'], 100, mathUtils.SMAFilter(SniperCamera._FILTER_LENGTH)) maxAccelerationDuration = readFloat( dynamicsSection, 'maxAccelerationDuration', 0.0, 10000.0, SniperCamera._DEFAULT_MAX_ACCELERATION_DURATION) self.__accelerationSmoother = AccelerationSmoother( accelerationFilter, maxAccelerationDuration) return def writeUserPreferences(self): ds = Settings.g_instance.userPrefs if not ds.has_key(Settings.KEY_CONTROL_MODE): ds.write(Settings.KEY_CONTROL_MODE, '') ucfg = self.__userCfg ds = ds[Settings.KEY_CONTROL_MODE] ds.writeBool('sniperMode/camera/horzInvert', ucfg['horzInvert']) ds.writeBool('sniperMode/camera/vertInvert', ucfg['vertInvert']) ds.writeFloat('sniperMode/camera/keySensitivity', ucfg['keySensitivity']) ds.writeFloat('sniperMode/camera/sensitivity', ucfg['sensitivity']) ds.writeFloat('sniperMode/camera/scrollSensitivity', ucfg['scrollSensitivity']) ds.writeFloat('sniperMode/camera/zoom', self.__cfg['zoom'])
class AvatarInputHandler(CallbackDelayer, ComponentSystem): bootcampCtrl = dependency.descriptor(IBootcampController) ctrl = property(lambda self: self.__curCtrl) ctrls = property(lambda self: self.__ctrls) isSPG = property(lambda self: self.__isSPG) isATSPG = property(lambda self: self.__isATSPG) isFlashBangAllowed = property(lambda self: self.__ctrls['video'] != self.__curCtrl) isDetached = property(lambda self: self.__isDetached) isGuiVisible = property(lambda self: self.__isGUIVisible) isStarted = property(lambda self: self.__isStarted) isObserverFPV = property(lambda self: BigWorld.player().isObserver() and BigWorld.player().isObserverFPV) remoteCameraSender = property(lambda self: self.__remoteCameraSender) __ctrlModeName = aih_global_binding.bindRW(_BINDING_ID.CTRL_MODE_NAME) __aimOffset = aih_global_binding.bindRW(_BINDING_ID.AIM_OFFSET) _DYNAMIC_CAMERAS_ENABLED_KEY = 'global/dynamicCameraEnabled' settingsCore = dependency.descriptor(ISettingsCore) @staticmethod def enableDynamicCamera(enable, useHorizontalStabilizer=True): for dynamicCameraClass in _DYNAMIC_CAMERAS: dynamicCameraClass.enableDynamicCamera(enable) SniperAimingSystem.setStabilizerSettings(useHorizontalStabilizer, True) @staticmethod def isCameraDynamic(): for dynamicCameraClass in _DYNAMIC_CAMERAS: if not dynamicCameraClass.isCameraDynamic(): return False return True @staticmethod def isSniperStabilized(): return SniperAimingSystem.getStabilizerSettings() @property def ctrlModeName(self): return self.__ctrlModeName siegeModeControl = ComponentDescriptor() siegeModeSoundNotifications = ComponentDescriptor() steadyVehicleMatrixCalculator = ComponentDescriptor() def __init__(self): CallbackDelayer.__init__(self) ComponentSystem.__init__(self) self.__alwaysShowAimKey = None self.__showMarkersKey = None sec = self._readCfg() self.onCameraChanged = Event() self.onPostmortemVehicleChanged = Event() self.onPostmortemKillerVisionEnter = Event() self.onPostmortemKillerVisionExit = Event() self.__isArenaStarted = False self.__isStarted = False self.__targeting = _Targeting() self.__vertScreenshotCamera = _VertScreenshotCamera() self.__ctrls = dict() self.__killerVehicleID = None self.__isAutorotation = True self.__prevModeAutorotation = None self.__isSPG = False self.__isATSPG = False self.__setupCtrls(sec) self.__curCtrl = self.__ctrls[_CTRLS_FIRST] self.__ctrlModeName = _CTRLS_FIRST self.__isDetached = False self.__waitObserverCallback = None self.__observerVehicle = None self.__observerIsSwitching = False self.__commands = [] self.__remoteCameraSender = None self.__isGUIVisible = False return def __constructComponents(self): player = BigWorld.player() if player.vehicleTypeDescriptor.hasSiegeMode: if not self.siegeModeControl: self.siegeModeControl = SiegeModeControl() self.__commands.append(self.siegeModeControl) self.siegeModeControl.onSiegeStateChanged += lambda *args: self.steadyVehicleMatrixCalculator.relinkSources() self.siegeModeSoundNotifications = SiegeModeSoundNotifications() self.siegeModeControl.onSiegeStateChanged += self.siegeModeSoundNotifications.onSiegeStateChanged self.siegeModeControl.onRequestFail += self.__onRequestFail self.siegeModeControl.onSiegeStateChanged += SiegeModeCameraShaker.shake if self.bootcampCtrl.isInBootcamp() and constants.HAS_DEV_RESOURCES: self.__commands.append(BootcampModeControl()) def prerequisites(self): out = [] for ctrl in self.__ctrls.itervalues(): out += ctrl.prerequisites() return out def handleKeyEvent(self, event): import game isDown, key, mods, isRepeat = game.convertKeyEvent(event) if isRepeat: return False elif self.__isStarted and self.__isDetached: if self.__curCtrl.alwaysReceiveKeyEvents() and not self.isObserverFPV or CommandMapping.g_instance.isFired(CommandMapping.CMD_CM_LOCK_TARGET, key): self.__curCtrl.handleKeyEvent(isDown, key, mods, event) return BigWorld.player().handleKey(isDown, key, mods) elif not self.__isStarted or self.__isDetached: return False for command in self.__commands: if command.handleKeyEvent(isDown, key, mods, event): return True if isDown and BigWorld.isKeyDown(Keys.KEY_CAPSLOCK): if self.__alwaysShowAimKey is not None and key == self.__alwaysShowAimKey: gui_event_dispatcher.toggleCrosshairVisibility() return True if self.__showMarkersKey is not None and key == self.__showMarkersKey and not self.__isGUIVisible: gui_event_dispatcher.toggleMarkers2DVisibility() return True if key == Keys.KEY_F5 and constants.HAS_DEV_RESOURCES: self.__vertScreenshotCamera.enable(not self.__vertScreenshotCamera.isEnabled) return True if key == Keys.KEY_SPACE and isDown and BigWorld.player().isObserver(): BigWorld.player().cell.switchObserverFPV(not BigWorld.player().isObserverFPV) return True else: return True if not self.isObserverFPV and self.__curCtrl.handleKeyEvent(isDown, key, mods, event) else BigWorld.player().handleKey(isDown, key, mods) def handleMouseEvent(self, dx, dy, dz): return False if not self.__isStarted or self.__isDetached else self.__curCtrl.handleMouseEvent(dx, dy, dz) def setForcedGuiControlMode(self, flags): result = False detached = flags & GUI_CTRL_MODE_FLAG.CURSOR_ATTACHED > 0 if detached ^ self.__isDetached: self.__isDetached = detached self.__targeting.detach(self.__isDetached) if detached: g_appLoader.attachCursor(settings.APP_NAME_SPACE.SF_BATTLE, flags=flags) result = True if flags & GUI_CTRL_MODE_FLAG.AIMING_ENABLED > 0: self.setAimingMode(False, AIMING_MODE.USER_DISABLED) else: g_appLoader.detachCursor(settings.APP_NAME_SPACE.SF_BATTLE) result = True self.__curCtrl.setForcedGuiControlMode(detached) elif detached: g_appLoader.syncCursor(settings.APP_NAME_SPACE.SF_BATTLE, flags=flags) return result def updateShootingStatus(self, canShoot): return None if self.__isDetached else self.__curCtrl.updateShootingStatus(canShoot) def getDesiredShotPoint(self, ignoreAimingMode=False): return None if self.__isDetached else self.__curCtrl.getDesiredShotPoint(ignoreAimingMode) def getMarkerPoint(self): point = None if self.__ctrlModeName in (_CTRL_MODE.ARCADE, _CTRL_MODE.STRATEGIC, _CTRL_MODE.ARTY): AimingSystems.shootInSkyPoint.has_been_called = False point = self.getDesiredShotPoint(ignoreAimingMode=True) if AimingSystems.shootInSkyPoint.has_been_called: point = None return point def showGunMarker(self, isShown): self.__curCtrl.setGunMarkerFlag(isShown, _GUN_MARKER_FLAG.CLIENT_MODE_ENABLED) def showGunMarker2(self, isShown): if not BattleReplay.isPlaying(): self.__curCtrl.setGunMarkerFlag(isShown, _GUN_MARKER_FLAG.SERVER_MODE_ENABLED) if gun_marker_ctrl.useDefaultGunMarkers(): self.__curCtrl.setGunMarkerFlag(not isShown, _GUN_MARKER_FLAG.CLIENT_MODE_ENABLED) replayCtrl = BattleReplay.g_replayCtrl replayCtrl.setUseServerAim(isShown) def updateGunMarker(self, pos, direction, size, relaxTime, collData): self.__curCtrl.updateGunMarker(_GUN_MARKER_TYPE.CLIENT, pos, direction, size, relaxTime, collData) def updateGunMarker2(self, pos, direction, size, relaxTime, collData): self.__curCtrl.updateGunMarker(_GUN_MARKER_TYPE.SERVER, pos, direction, size, relaxTime, collData) def setAimingMode(self, enable, mode): self.__curCtrl.setAimingMode(enable, mode) def getAimingMode(self, mode): return self.__curCtrl.getAimingMode(mode) def setAutorotation(self, bValue): if not self.__curCtrl.enableSwitchAutorotationMode(): return elif not BigWorld.player().isOnArena: return else: if self.__isAutorotation != bValue: self.__isAutorotation = bValue BigWorld.player().enableOwnVehicleAutorotation(self.__isAutorotation) self.__prevModeAutorotation = None return def getAutorotation(self): return self.__isAutorotation def switchAutorotation(self): self.setAutorotation(not self.__isAutorotation) def activatePostmortem(self, isRespawn): if self.siegeModeSoundNotifications is not None: self.siegeModeSoundNotifications = None BigWorld.player().autoAim(None) for ctlMode in self.__ctrls.itervalues(): ctlMode.resetAimingMode() try: params = self.__curCtrl.postmortemCamParams except Exception: params = None onPostmortemActivation = getattr(self.__curCtrl, 'onPostmortemActivation', None) if onPostmortemActivation is not None: onPostmortemActivation(_CTRL_MODE.POSTMORTEM, postmortemParams=params, bPostmortemDelay=True, respawn=isRespawn) else: self.onControlModeChanged(_CTRL_MODE.POSTMORTEM, postmortemParams=params, bPostmortemDelay=True, respawn=isRespawn) return def deactivatePostmortem(self): self.onControlModeChanged('arcade') arcadeMode = self.__ctrls['arcade'] arcadeMode.camera.setToVehicleDirection() self.__identifySPG() self.__constructComponents() def setKillerVehicleID(self, killerVehicleID): self.__killerVehicleID = killerVehicleID def getKillerVehicleID(self): return self.__killerVehicleID def start(self): g_guiResetters.add(self.__onRecreateDevice) self.steadyVehicleMatrixCalculator = SteadyVehicleMatrixCalculator() self.__identifySPG() self.__constructComponents() for control in self.__ctrls.itervalues(): control.create() avatar = BigWorld.player() if not self.__curCtrl.isManualBind(): avatar.positionControl.bindToVehicle(True) self.__curCtrl.enable() tmp = self.__curCtrl.getPreferredAutorotationMode() if tmp is not None: self.__isAutorotation = tmp self.__prevModeAutorotation = True else: self.__isAutorotation = True self.__prevModeAutorotation = None avatar.enableOwnVehicleAutorotation(self.__isAutorotation) self.__targeting.enable(True) self.__isStarted = True self.__isGUIVisible = True self.__killerVehicleID = None arena = avatar.arena arena.onPeriodChange += self.__onArenaStarted self.settingsCore.onSettingsChanged += self.__onSettingsChanged avatar.consistentMatrices.onVehicleMatrixBindingChanged += self.__onVehicleChanged self.__onArenaStarted(arena.period) if not avatar.isObserver() and arena.hasObservers: self.__remoteCameraSender = RemoteCameraSender(self) self.onCameraChanged('arcade') return def stop(self): self.__isStarted = False import SoundGroups SoundGroups.g_instance.changePlayMode(0) aih_global_binding.clear() for control in self.__ctrls.itervalues(): control.destroy() replayCtrl = BattleReplay.g_replayCtrl if replayCtrl.isRecording: replayCtrl.setPlayerVehicleID(0) if self.__remoteCameraSender is not None: self.__remoteCameraSender.destroy() self.__remoteCameraSender = None self.onCameraChanged.clear() self.onCameraChanged = None self.onPostmortemVehicleChanged.clear() self.onPostmortemVehicleChanged = None self.onPostmortemKillerVisionEnter.clear() self.onPostmortemKillerVisionEnter = None self.onPostmortemKillerVisionExit.clear() self.onPostmortemKillerVisionExit = None self.__targeting.enable(False) self.__killerVehicleID = None if self.__onRecreateDevice in g_guiResetters: g_guiResetters.remove(self.__onRecreateDevice) BigWorld.player().arena.onPeriodChange -= self.__onArenaStarted self.settingsCore.onSettingsChanged -= self.__onSettingsChanged BigWorld.player().consistentMatrices.onVehicleMatrixBindingChanged -= self.__onVehicleChanged ComponentSystem.destroy(self) CallbackDelayer.destroy(self) return def __onVehicleChanged(self, isStatic): self.steadyVehicleMatrixCalculator.relinkSources() self.__identifySPG() if self.__waitObserverCallback is not None and self.__observerVehicle is not None: player = BigWorld.player() ownVehicle = BigWorld.entity(player.playerVehicleID) vehicle = player.getVehicleAttached() if vehicle != ownVehicle: self.__waitObserverCallback() self.__observerIsSwitching = False self.__observerVehicle = None return def setObservedVehicle(self, vehicleID): for control in self.__ctrls.itervalues(): control.setObservedVehicle(vehicleID) def onControlModeChanged(self, eMode, **args): if self.steadyVehicleMatrixCalculator is not None: self.steadyVehicleMatrixCalculator.relinkSources() if not self.__isArenaStarted and eMode != _CTRL_MODE.POSTMORTEM: return else: player = BigWorld.player() isObserverMode = 'observer' in player.vehicleTypeDescriptor.type.tags if player is not None else True if self.__waitObserverCallback is not None: self.__waitObserverCallback = None if isObserverMode and eMode == _CTRL_MODE.POSTMORTEM: if self.__observerVehicle is not None and not self.__observerIsSwitching: self.__waitObserverCallback = partial(self.onControlModeChanged, eMode, **args) self.__observerIsSwitching = True player.positionControl.followCamera(False) player.positionControl.bindToVehicle(True, self.__observerVehicle) return if isObserverMode and self.__ctrlModeName == _CTRL_MODE.POSTMORTEM: player = BigWorld.player() self.__observerVehicle = player.vehicle.id if player.vehicle else None self.__observerIsSwitching = False replayCtrl = BattleReplay.g_replayCtrl if replayCtrl.isRecording: replayCtrl.setControlMode(eMode) self.__curCtrl.disable() prevCtrl = self.__curCtrl self.__curCtrl = self.__ctrls[eMode] self.__ctrlModeName = eMode if player is not None: if not prevCtrl.isManualBind() and self.__curCtrl.isManualBind(): if isObserverMode: player.positionControl.bindToVehicle(False, -1) else: player.positionControl.bindToVehicle(False) elif prevCtrl.isManualBind() and not self.__curCtrl.isManualBind(): if isObserverMode: player.positionControl.followCamera(False) player.positionControl.bindToVehicle(True, self.__observerVehicle) else: player.positionControl.bindToVehicle(True) elif not prevCtrl.isManualBind() and not self.__curCtrl.isManualBind(): if isObserverMode and not self.isObserverFPV: player.positionControl.bindToVehicle(True) newAutoRotationMode = self.__curCtrl.getPreferredAutorotationMode() if newAutoRotationMode is not None: if prevCtrl.getPreferredAutorotationMode() is None: self.__prevModeAutorotation = self.__isAutorotation if self.__isAutorotation != newAutoRotationMode: self.__isAutorotation = newAutoRotationMode BigWorld.player().enableOwnVehicleAutorotation(self.__isAutorotation) elif prevCtrl.getPreferredAutorotationMode() is not None: if self.__prevModeAutorotation is None: self.__prevModeAutorotation = True if self.__isAutorotation != self.__prevModeAutorotation: self.__isAutorotation = self.__prevModeAutorotation BigWorld.player().enableOwnVehicleAutorotation(self.__isAutorotation) self.__prevModeAutorotation = None if not isObserverMode and self.__ctrlModeName in (_CTRL_MODE.ARCADE, _CTRL_MODE.SNIPER): lockEnabled = prevCtrl.getAimingMode(AIMING_MODE.TARGET_LOCK) self.__curCtrl.setAimingMode(lockEnabled, AIMING_MODE.TARGET_LOCK) self.__targeting.onRecreateDevice() self.__curCtrl.setGUIVisible(self.__isGUIVisible) if isObserverMode: args.update(vehicleID=self.__observerVehicle) self.__curCtrl.enable(**args) else: self.__curCtrl.enable(**args) isReplayPlaying = replayCtrl.isPlaying vehicleID = None vehicle = player.getVehicleAttached() if isObserverMode: vehicleID = self.__observerVehicle elif vehicle is not None and isReplayPlaying: vehicleID = vehicle.id self.onCameraChanged(eMode, vehicleID) if not isReplayPlaying: self.__curCtrl.handleMouseEvent(0.0, 0.0, 0.0) return def onVehicleControlModeChanged(self, eMode): LOG_DEBUG('onVehicleControlModeChanged: ', eMode, self.isObserverFPV) if not self.isObserverFPV: self.onControlModeChanged(_CTRL_MODE.POSTMORTEM) return else: if eMode is None: eMode = _CTRL_MODES[BigWorld.player().observerFPVControlMode] targetPos = self.getDesiredShotPoint() or Math.Vector3(0, 0, 0) LOG_DEBUG('onVehicleControlModeChanged: ', eMode, targetPos) self.onControlModeChanged(eMode, preferredPos=targetPos, aimingMode=0, saveZoom=False, saveDist=True, equipmentID=None, curVehicleID=BigWorld.player().getVehicleAttached()) return def getTargeting(self): return self.__targeting def setGUIVisible(self, isVisible): self.__isGUIVisible = isVisible self.__curCtrl.setGUIVisible(isVisible) def selectPlayer(self, vehId): self.__curCtrl.selectPlayer(vehId) def onMinimapClicked(self, worldPos): self.__curCtrl.onMinimapClicked(worldPos) def onVehicleShaken(self, vehicle, impulsePosition, impulseDir, caliber, shakeReason): if shakeReason == _ShakeReason.OWN_SHOT_DELAYED: shakeFuncBound = functools.partial(self.onVehicleShaken, vehicle, impulsePosition, impulseDir, caliber, _ShakeReason.OWN_SHOT) delayTime = self.__dynamicCameraSettings.settings['ownShotImpulseDelay'] self.delayCallback(delayTime, shakeFuncBound) return else: camera = getattr(self.ctrl, 'camera', None) if camera is None: return impulseValue = self.__dynamicCameraSettings.getGunImpulse(caliber) vehicleSensitivity = 0.0 avatarVehicle = BigWorld.player().getVehicleAttached() if avatarVehicle is None or not avatarVehicle.isAlive(): return avatarVehicleTypeDesc = getattr(avatarVehicle, 'typeDescriptor', None) if avatarVehicleTypeDesc is not None: avatarVehWeightTons = avatarVehicleTypeDesc.physics['weight'] / 1000.0 vehicleSensitivity = self.__dynamicCameraSettings.getSensitivityToImpulse(avatarVehWeightTons) vehicleSensitivity *= avatarVehicleTypeDesc.hull.swinging.sensitivityToImpulse impulseReason = None isDistant = False if shakeReason == _ShakeReason.OWN_SHOT: if vehicle is avatarVehicle: impulseReason = cameras.ImpulseReason.MY_SHOT isDistant = False else: impulseReason = cameras.ImpulseReason.OTHER_SHOT isDistant = True elif vehicle is avatarVehicle: if shakeReason == _ShakeReason.HIT or shakeReason == _ShakeReason.HIT_NO_DAMAGE: impulseValue *= 1.0 if shakeReason == _ShakeReason.HIT else self.__dynamicCameraSettings.settings['zeroDamageHitSensitivity'] impulseReason = cameras.ImpulseReason.ME_HIT isDistant = False else: impulseReason = cameras.ImpulseReason.SPLASH isDistant = True impulseDir, impulseValue = self.__adjustImpulse(impulseDir, impulseValue, camera, impulsePosition, vehicleSensitivity, impulseReason) if isDistant: camera.applyDistantImpulse(impulsePosition, impulseValue, impulseReason) else: camera.applyImpulse(impulsePosition, impulseDir * impulseValue, impulseReason) return def onVehicleCollision(self, vehicle, impactVelocity): if impactVelocity < self.__dynamicCameraSettings.settings['minCollisionSpeed']: return else: camera = getattr(self.ctrl, 'camera', None) if camera is None: return avatarVehicle = BigWorld.player().getVehicleAttached() if avatarVehicle is None or not avatarVehicle.isAlive(): return if vehicle is avatarVehicle: impulse = Math.Vector3(0, impactVelocity * self.__dynamicCameraSettings.settings['collisionSpeedToImpulseRatio'], 0) camera.applyImpulse(vehicle.position, impulse, cameras.ImpulseReason.COLLISION) return def onVehicleDeath(self, vehicle, exploded): if not exploded: return else: camera = getattr(self.ctrl, 'camera', None) if camera is None: return avatarVehicle = BigWorld.player().getVehicleAttached() if avatarVehicle is None or avatarVehicle is vehicle: return caliber = vehicle.typeDescriptor.shot.shell.caliber impulseValue = self.__dynamicCameraSettings.getGunImpulse(caliber) avatarVehicleWeightInTons = avatarVehicle.typeDescriptor.physics['weight'] / 1000.0 vehicleSensitivity = self.__dynamicCameraSettings.getSensitivityToImpulse(avatarVehicleWeightInTons) vehicleSensitivity *= avatarVehicle.typeDescriptor.hull.swinging.sensitivityToImpulse _, impulseValue = self.__adjustImpulse(Math.Vector3(0, 0, 0), impulseValue, camera, vehicle.position, vehicleSensitivity, cameras.ImpulseReason.VEHICLE_EXPLOSION) camera.applyDistantImpulse(vehicle.position, impulseValue, cameras.ImpulseReason.VEHICLE_EXPLOSION) return def onExplosionImpulse(self, position, impulseValue): camera = getattr(self.ctrl, 'camera', None) if camera is None: return else: avatarVehicle = BigWorld.player().getVehicleAttached() if avatarVehicle is None: return avatarVehicleWeightInTons = avatarVehicle.typeDescriptor.physics['weight'] / 1000.0 vehicleSensitivity = self.__dynamicCameraSettings.getSensitivityToImpulse(avatarVehicleWeightInTons) vehicleSensitivity *= avatarVehicle.typeDescriptor.hull.swinging.sensitivityToImpulse _, impulseValue = self.__adjustImpulse(Math.Vector3(0, 0, 0), impulseValue, camera, position, vehicleSensitivity, cameras.ImpulseReason.HE_EXPLOSION) camera.applyDistantImpulse(position, impulseValue, cameras.ImpulseReason.HE_EXPLOSION) return def onProjectileHit(self, position, caliber, isOwnShot): if not isOwnShot: return else: camera = getattr(self.ctrl, 'camera', None) if camera is None: return impulseValue = self.__dynamicCameraSettings.getGunImpulse(caliber) vehicleSensitivity = 1.0 avatarVehicle = BigWorld.player().getVehicleAttached() if avatarVehicle is not None: avatarVehicleWeightInTons = avatarVehicle.typeDescriptor.physics['weight'] / 1000.0 vehicleSensitivity = self.__dynamicCameraSettings.getSensitivityToImpulse(avatarVehicleWeightInTons) vehicleSensitivity *= avatarVehicle.typeDescriptor.hull.swinging.sensitivityToImpulse _, impulseValue = self.__adjustImpulse(Math.Vector3(0, 0, 0), impulseValue, camera, position, vehicleSensitivity, cameras.ImpulseReason.VEHICLE_EXPLOSION) camera.applyDistantImpulse(position, impulseValue, cameras.ImpulseReason.PROJECTILE_HIT) return def onSpecificImpulse(self, position, impulse, specificCtrl=None): if specificCtrl is None: camera = getattr(self.ctrl, 'camera', None) else: camera = self.ctrls[specificCtrl].camera if camera is None: return else: camera.applyImpulse(position, impulse, cameras.ImpulseReason.MY_SHOT) return def __adjustImpulse(self, impulseDir, impulseValue, camera, impulsePosition, vehicleSensitivity, impulseReason): if impulseReason in camera.getReasonsAffectCameraDirectly(): dirToCamera = camera.camera.position - impulsePosition dirToCamera.normalise() impulseDir = dirToCamera else: impulseValue *= vehicleSensitivity return (impulseDir, impulseValue) def __identifySPG(self): veh = BigWorld.entity(BigWorld.player().playerVehicleID) if veh is None: return else: vehTypeDesc = veh.typeDescriptor.type self.__isSPG = 'SPG' in vehTypeDesc.tags self.__isATSPG = 'AT-SPG' in vehTypeDesc.tags return def reloadDynamicSettings(self): if not constants.HAS_DEV_RESOURCES: return ResMgr.purge(INPUT_HANDLER_CFG) sec = ResMgr.openSection(INPUT_HANDLER_CFG) self.__dynamicCameraSettings = DynamicCameraSettings(sec['dynamicCameraCommon']) try: self.__ctrls['sniper'].camera.aimingSystem.reloadConfig(sec['sniperMode']['camera']) except Exception: pass def _readCfg(self): sec = ResMgr.openSection(INPUT_HANDLER_CFG) if sec is None: LOG_ERROR('can not open <%s>.' % INPUT_HANDLER_CFG) return else: self.__checkSections(sec) keySec = sec['keys'] if keySec is not None: self.__showMarkersKey = getattr(Keys, keySec.readString('showMarkersKey', ''), None) self.__alwaysShowAimKey = getattr(Keys, keySec.readString('alwaysShowAimKey', ''), None) self.__dynamicCameraSettings = DynamicCameraSettings(sec['dynamicCameraCommon']) return sec def __setupCtrls(self, section): modules = (control_modes, MapCaseMode, RespawnDeathMode, epic_battle_death_mode) bonusType = BigWorld.player().arenaBonusType bonusTypeCtrlsMap = _OVERWRITE_CTRLS_DESC_MAP.get(bonusType, {}) for name, desc in _CTRLS_DESC_MAP.items(): if bonusTypeCtrlsMap.has_key(name): desc = bonusTypeCtrlsMap.get(name) try: if desc[2] != _CTRL_TYPE.DEVELOPMENT or desc[2] == _CTRL_TYPE.DEVELOPMENT and constants.HAS_DEV_RESOURCES: if name not in self.__ctrls: for module in modules: classType = getattr(module, desc[0], None) if classType is None: pass self.__ctrls[name] = classType(section[desc[1]] if desc[1] else None, self) break except Exception: LOG_DEBUG('Error while setting ctrls', name, desc, constants.HAS_DEV_RESOURCES) LOG_CURRENT_EXCEPTION() return def __checkSections(self, section): for _, desc in _CTRLS_DESC_MAP.items(): if desc[1] is None or desc[2] == _CTRL_TYPE.OPTIONAL or desc[2] == _CTRL_TYPE.DEVELOPMENT and not constants.HAS_DEV_RESOURCES: continue if not section.has_key(desc[1]): LOG_ERROR('Invalid section <%s> in <%s>.' % (desc[1], INPUT_HANDLER_CFG)) return def __onArenaStarted(self, period, *args): self.__isArenaStarted = period == ARENA_PERIOD.BATTLE self.__curCtrl.setGunMarkerFlag(self.__isArenaStarted, _GUN_MARKER_FLAG.CONTROL_ENABLED) self.showGunMarker2(gun_marker_ctrl.useServerGunMarker()) self.showGunMarker(gun_marker_ctrl.useClientGunMarker()) def __onRecreateDevice(self): self.__curCtrl.onRecreateDevice() self.__targeting.onRecreateDevice() def __onSettingsChanged(self, diff): if 'dynamicCamera' in diff or 'horStabilizationSnp' in diff: dynamicCamera = self.settingsCore.getSetting('dynamicCamera') horStabilizationSnp = self.settingsCore.getSetting('horStabilizationSnp') self.enableDynamicCamera(dynamicCamera, horStabilizationSnp) def __onRequestFail(self): player = BigWorld.player() if player is not None: player.showVehicleError('cantSwitchEngineDestroyed') return
class _GunMarkersDecorator(IGunMarkerController): __gunMarkersFlags = aih_global_binding.bindRW( _BINDING_ID.GUN_MARKERS_FLAGS) __clientState = aih_global_binding.bindRW( _BINDING_ID.CLIENT_GUN_MARKER_STATE) __serverState = aih_global_binding.bindRW( _BINDING_ID.SERVER_GUN_MARKER_STATE) def __init__(self, clientMarker, serverMarker): super(_GunMarkersDecorator, self).__init__() self.__clientMarker = clientMarker self.__serverMarker = serverMarker def create(self): self.__clientMarker.create() self.__serverMarker.create() def destroy(self): self.__clientMarker.destroy() self.__serverMarker.destroy() def enable(self): self.__clientMarker.enable() self.__clientMarker.setPosition(self.__clientState[0]) self.__serverMarker.enable() self.__serverMarker.setPosition(self.__serverState[0]) def disable(self): self.__clientMarker.disable() self.__serverMarker.disable() def reset(self): self.__clientMarker.reset() self.__serverMarker.reset() def onRecreateDevice(self): self.__clientMarker.onRecreateDevice() self.__serverMarker.onRecreateDevice() def getPosition(self, markerType=_MARKER_TYPE.CLIENT): if markerType == _MARKER_TYPE.CLIENT: return self.__clientMarker.getPosition() if markerType == _MARKER_TYPE.SERVER: return self.__serverMarker.getPosition() _logger.warning('Gun maker control is not found by type: %d', markerType) return Math.Vector3() def setPosition(self, position, markerType=_MARKER_TYPE.CLIENT): if markerType == _MARKER_TYPE.CLIENT: self.__clientMarker.setPosition(position) elif markerType == _MARKER_TYPE.SERVER: self.__serverMarker.setPosition(position) else: _logger.warning('Gun maker control is not found by type: %d', markerType) def setFlag(self, positive, bit): if positive: self.__gunMarkersFlags |= bit if bit == _MARKER_FLAG.SERVER_MODE_ENABLED: self.__serverMarker.setPosition( self.__clientMarker.getPosition()) self.__serverMarker.setSize(self.__clientMarker.getSize()) else: self.__gunMarkersFlags &= ~bit def update(self, markerType, position, direction, size, relaxTime, collData): if markerType == _MARKER_TYPE.CLIENT: self.__clientState = (position, direction, collData) if self.__gunMarkersFlags & _MARKER_FLAG.CLIENT_MODE_ENABLED: self.__clientMarker.update(markerType, position, direction, size, relaxTime, collData) elif markerType == _MARKER_TYPE.SERVER: self.__serverState = (position, direction, collData) if self.__gunMarkersFlags & _MARKER_FLAG.SERVER_MODE_ENABLED: self.__serverMarker.update(markerType, position, direction, size, relaxTime, collData) else: _logger.warning('Gun maker control is not found by type: %d', markerType) def setVisible(self, flag): pass def getSize(self): pass def setSize(self, newSize): pass
class _GunMarkerController(IGunMarkerController): _gunMarkersFlags = aih_global_binding.bindRW(_BINDING_ID.GUN_MARKERS_FLAGS) def __init__(self, gunMakerType, dataProvider, enabledFlag=_MARKER_FLAG.UNDEFINED): super(_GunMarkerController, self).__init__() self._gunMarkerType = gunMakerType self._dataProvider = dataProvider self._enabledFlag = enabledFlag self._position = Math.Vector3() def create(self): pass def destroy(self): self._dataProvider = None return def enable(self): if self._enabledFlag != _MARKER_FLAG.UNDEFINED: self.setFlag(True, self._enabledFlag) def disable(self): if self._enabledFlag != _MARKER_FLAG.UNDEFINED: self.setFlag(False, self._enabledFlag) def reset(self): pass def update(self, markerType, position, direction, size, relaxTime, collData): if self._gunMarkerType == markerType: self._position = position else: _logger.warning( 'Position can not be defined, type of marker does not equal: required = %d, received = %d', self._gunMarkerType, markerType) def setFlag(self, positive, bit): if positive: self._gunMarkersFlags |= bit else: self._gunMarkersFlags &= ~bit def onRecreateDevice(self): pass def getPosition(self): return self._position def setPosition(self, position): self._position = position positionMatrix = Math.Matrix() positionMatrix.setTranslate(position) self._updateMatrixProvider(positionMatrix) def setVisible(self, flag): pass def getSize(self): pass def setSize(self, newSize): pass def _updateMatrixProvider(self, positionMatrix, relaxTime=0.0): animationMatrix = self._dataProvider.positionMatrixProvider animationMatrix.keyframes = ((0.0, Math.Matrix(animationMatrix)), (relaxTime, positionMatrix)) animationMatrix.time = 0.0
class _GunMarkersDecorator(IGunMarkerController): """Decorator that contains implementation of markers on client-side and server-side.""" __gunMarkersFlags = aih_global_binding.bindRW( _BINDING_ID.GUN_MARKERS_FLAGS) __clientState = aih_global_binding.bindRW( _BINDING_ID.CLIENT_GUN_MARKER_STATE) __serverState = aih_global_binding.bindRW( _BINDING_ID.SERVER_GUN_MARKER_STATE) def __init__(self, clientMarker, serverMarker): super(_GunMarkersDecorator, self).__init__() self.__clientMarker = clientMarker self.__serverMarker = serverMarker def create(self): self.__clientMarker.create() self.__serverMarker.create() def destroy(self): self.__clientMarker.destroy() self.__serverMarker.destroy() def enable(self): self.__clientMarker.enable() self.__clientMarker.setPosition(self.__clientState[0]) self.__serverMarker.enable() self.__serverMarker.setPosition(self.__serverState[0]) def disable(self): self.__clientMarker.disable() self.__serverMarker.disable() def reset(self): self.__clientMarker.reset() self.__serverMarker.reset() def onRecreateDevice(self): self.__clientMarker.onRecreateDevice() self.__serverMarker.onRecreateDevice() def getPosition(self, markerType=_MARKER_TYPE.CLIENT): if markerType == _MARKER_TYPE.CLIENT: return self.__clientMarker.getPosition() elif markerType == _MARKER_TYPE.SERVER: return self.__serverMarker.getPosition() else: LOG_UNEXPECTED('Gun maker control is not found by type', markerType) return Math.Vector3() def setPosition(self, position, markerType=_MARKER_TYPE.CLIENT): if markerType == _MARKER_TYPE.CLIENT: self.__clientMarker.setPosition(position) elif markerType == _MARKER_TYPE.SERVER: self.__serverMarker.setPosition(position) else: LOG_UNEXPECTED('Gun maker control is not found by type', markerType) def setFlag(self, positive, bit): if positive: self.__gunMarkersFlags |= bit if bit == _MARKER_FLAG.SERVER_MODE_ENABLED: self.__serverMarker.setPosition( self.__clientMarker.getPosition()) else: self.__gunMarkersFlags &= ~bit def update(self, markerType, position, dir, size, relaxTime, collData): if markerType == _MARKER_TYPE.CLIENT: self.__clientState = (position, dir, collData) if self.__gunMarkersFlags & _MARKER_FLAG.CLIENT_MODE_ENABLED: self.__clientMarker.update(markerType, position, dir, size, relaxTime, collData) elif markerType == _MARKER_TYPE.SERVER: self.__serverState = (position, dir, collData) if self.__gunMarkersFlags & _MARKER_FLAG.SERVER_MODE_ENABLED: self.__serverMarker.update(markerType, position, dir, size, relaxTime, collData) else: LOG_UNEXPECTED('Gun maker control is not found by type', markerType)
class ArtyCamera(ICamera, CallbackDelayer): _DYNAMIC_ENABLED = True @staticmethod def enableDynamicCamera(enable): ArtyCamera._DYNAMIC_ENABLED = enable @staticmethod def isCameraDynamic(): return ArtyCamera._DYNAMIC_ENABLED camera = property(lambda self: self.__cam) aimingSystem = property(lambda self: self.__aimingSystem) settingsCore = dependency.descriptor(ISettingsCore) __aimOffset = aih_global_binding.bindRW( aih_global_binding.BINDING_ID.AIM_OFFSET) def __init__(self, dataSec): CallbackDelayer.__init__(self) self.__positionOscillator = None self.__positionNoiseOscillator = None self.__dynamicCfg = CameraDynamicConfig() self.__readCfg(dataSec) self.__cam = BigWorld.CursorCamera() self.__curSense = self.__cfg['sensitivity'] self.__onChangeControlMode = None self.__camDist = 0.0 self.__desiredCamDist = 0.0 self.__aimingSystem = None self.__prevTime = 0.0 self.__dxdydz = Vector3(0.0, 0.0, 0.0) self.__autoUpdatePosition = False self.__needReset = 0 self.__sourceMatrix = None self.__targetMatrix = None self.__rotation = 0.0 self.__positionHysteresis = None self.__timeHysteresis = None return def create(self, onChangeControlMode=None): self.__onChangeControlMode = onChangeControlMode aimingSystemClass = ArtyAimingSystemRemote if BigWorld.player( ).isObserver() else ArtyAimingSystem self.__aimingSystem = aimingSystemClass() self.__camDist = self.__cfg['camDist'] self.__desiredCamDist = self.__camDist self.__positionHysteresis = PositionHysteresis( self.__cfg['hPositionThresholdSq']) self.__timeHysteresis = TimeHysteresis(self.__cfg['hTimeThreshold']) self.__cam.pivotMaxDist = 0.0 self.__cam.maxDistHalfLife = 0.01 self.__cam.movementHalfLife = 0.0 self.__cam.turningHalfLife = -1 self.__cam.pivotPosition = Vector3(0.0, 0.0, 0.0) self.__sourceMatrix = Matrix() self.__targetMatrix = Matrix() self.__rotation = 0.0 def destroy(self): CallbackDelayer.destroy(self) self.disable() self.__onChangeControlMode = None self.__cam = None if self.__aimingSystem is not None: self.__aimingSystem.destroy() self.__aimingSystem = None return def enable(self, targetPos, saveDist): self.__prevTime = 0.0 self.__aimingSystem.enable(targetPos) self.__positionHysteresis.update(Vector3(0.0, 0.0, 0.0)) self.__timeHysteresis.update(BigWorld.timeExact()) camTarget = MatrixProduct() self.__cam.target = camTarget self.__cam.source = self.__sourceMatrix self.__cam.wg_applyParams() BigWorld.camera(self.__cam) BigWorld.player().positionControl.moveTo(self.__aimingSystem.hitPoint) BigWorld.player().positionControl.followCamera(False) self.__rotation = 0.0 self.__cameraUpdate() self.delayCallback(0.0, self.__cameraUpdate) self.__needReset = 1 def disable(self): if self.__aimingSystem is not None: self.__aimingSystem.disable() self.stopCallback(self.__cameraUpdate) BigWorld.camera(None) self.__positionOscillator.reset() return def teleport(self, pos): self.__aimingSystem.updateTargetPos(pos) self.update(0.0, 0.0, 0.0) def getConfigValue(self, name): return self.__cfg.get(name) def getUserConfigValue(self, name): return self.__userCfg.get(name) def setUserConfigValue(self, name, value): if name not in self.__userCfg: return self.__userCfg[name] = value if name not in ('keySensitivity', 'sensitivity', 'scrollSensitivity'): self.__cfg[name] = self.__userCfg[name] else: self.__cfg[name] = self.__baseCfg[name] * self.__userCfg[name] def update(self, dx, dy, dz, updateByKeyboard=False): self.__curSense = self.__cfg[ 'keySensitivity'] if updateByKeyboard else self.__cfg['sensitivity'] self.__autoUpdatePosition = updateByKeyboard self.__dxdydz = Vector3(dx if not self.__cfg['horzInvert'] else -dx, dy if not self.__cfg['vertInvert'] else -dy, dz) def applyImpulse(self, position, impulse, reason=ImpulseReason.ME_HIT): adjustedImpulse, noiseMagnitude = self.__dynamicCfg.adjustImpulse( impulse, reason) impulseFlatDir = Vector3(adjustedImpulse.x, 0, adjustedImpulse.z) impulseFlatDir.normalise() impulseLocal = self.__sourceMatrix.applyVector( impulseFlatDir * (-1 * adjustedImpulse.length)) self.__positionOscillator.applyImpulse(impulseLocal) self.__applyNoiseImpulse(noiseMagnitude) def applyDistantImpulse(self, position, impulseValue, reason=ImpulseReason.ME_HIT): if reason != ImpulseReason.SPLASH and reason != ImpulseReason.PROJECTILE_HIT: return impulse = BigWorld.player().getOwnVehiclePosition() - position distance = impulse.length if distance <= 1.0: distance = 1.0 impulse.normalise() if reason == ImpulseReason.PROJECTILE_HIT: if not cameras.isPointOnScreen(position): return distance = 1.0 impulse *= impulseValue / distance self.applyImpulse(position, impulse, reason) def writeUserPreferences(self): ds = Settings.g_instance.userPrefs if not ds.has_key(Settings.KEY_CONTROL_MODE): ds.write(Settings.KEY_CONTROL_MODE, '') ucfg = self.__userCfg ds = ds[Settings.KEY_CONTROL_MODE] ds.writeBool('artyMode/camera/horzInvert', ucfg['horzInvert']) ds.writeBool('artyMode/camera/vertInvert', ucfg['vertInvert']) ds.writeFloat('artyMode/camera/keySensitivity', ucfg['keySensitivity']) ds.writeFloat('artyMode/camera/sensitivity', ucfg['sensitivity']) ds.writeFloat('artyMode/camera/scrollSensitivity', ucfg['scrollSensitivity']) ds.writeFloat('artyMode/camera/camDist', self.__cfg['camDist']) def __applyNoiseImpulse(self, noiseMagnitude): noiseImpulse = mathUtils.RandomVectors.random3Flat(noiseMagnitude) self.__positionNoiseOscillator.applyImpulse(noiseImpulse) def __calculateAimOffset(self, aimWorldPos): replayCtrl = BattleReplay.g_replayCtrl if replayCtrl.isPlaying and replayCtrl.isControllingCamera: aimOffset = replayCtrl.getAimClipPosition() else: aimOffset = cameras.projectPoint(aimWorldPos) aimOffset = Vector2(mathUtils.clamp(-0.95, 0.95, aimOffset.x), mathUtils.clamp(-0.95, 0.95, aimOffset.y)) if replayCtrl.isRecording: replayCtrl.setAimClipPosition(aimOffset) return aimOffset def __handleMovement(self): replayCtrl = BattleReplay.g_replayCtrl if replayCtrl.isPlaying: if self.__needReset != 0: if self.__needReset > 1: from helpers import isPlayerAvatar player = BigWorld.player() if isPlayerAvatar(): if player.inputHandler.ctrl is not None: player.inputHandler.ctrl.resetGunMarkers() self.__needReset = 0 else: self.__needReset += 1 if replayCtrl.isControllingCamera: self.__aimingSystem.updateTargetPos( replayCtrl.getGunRotatorTargetPoint()) else: self.__aimingSystem.handleMovement( self.__dxdydz.x * self.__curSense, -self.__dxdydz.y * self.__curSense) if self.__dxdydz.x != 0 or self.__dxdydz.y != 0 or self.__dxdydz.z != 0: self.__needReset = 2 else: self.__aimingSystem.handleMovement( self.__dxdydz.x * self.__curSense, -self.__dxdydz.y * self.__curSense) return def __getCameraDirection(self): direction = Vector3() direction.setPitchYaw(self.__rotation, self.__aimingSystem.direction.yaw) direction.normalise() return direction def __getDesiredCameraDistance(self): distRange = self.__cfg['distRange'] self.__desiredCamDist -= self.__dxdydz.z * self.__curSense self.__desiredCamDist = mathUtils.clamp(distRange[0], distRange[1], self.__desiredCamDist) self.__desiredCamDist = self.__aimingSystem.overrideCamDist( self.__desiredCamDist) self.__cfg['camDist'] = self.__camDist return self.__desiredCamDist def __updateTrajectoryDrawer(self): shotDescr = BigWorld.player().getVehicleDescriptor().shot BigWorld.wg_trajectory_drawer().setParams( shotDescr.maxDistance, Vector3(0, -shotDescr.gravity, 0), self.__aimOffset) def __updateTime(self): curTime = BigWorld.timeExact() deltaTime = curTime - self.__prevTime self.__prevTime = curTime return deltaTime def __choosePitchLevel(self, aimPoint): useHighPitch = (aimPoint - self.__aimingSystem.hitPoint ).lengthSquared > self.__cfg['highPitchThresholdSq'] if useHighPitch: useHighPitch = self.__positionHysteresis.check( aimPoint) or self.__timeHysteresis.check(self.__prevTime) else: self.__positionHysteresis.update(aimPoint) self.__timeHysteresis.update(self.__prevTime) return useHighPitch def __getCollisionTestOrigin(self, aimPoint, direction): distRange = self.__cfg['distRange'] vehiclePosition = BigWorld.player().getVehicleAttached().position collisionTestOrigin = Vector3(vehiclePosition) if direction.x * direction.x > direction.z * direction.z and not mathUtils.almostZero( direction.x): collisionTestOrigin.y = direction.y / direction.x * ( vehiclePosition.x - aimPoint.x) + aimPoint.y elif not mathUtils.almostZero(direction.z): collisionTestOrigin.y = direction.y / direction.z * ( vehiclePosition.z - aimPoint.z) + aimPoint.y else: collisionTestOrigin = aimPoint - direction.scale( (distRange[1] - distRange[0]) / 2.0) LOG_WARNING( "Can't find point on direction ray. Using half point as collision test origin" ) return collisionTestOrigin def __resolveCollisions(self, aimPoint, distance, direction): distRange = self.__cfg['distRange'] collisionTestOrigin = self.__getCollisionTestOrigin( aimPoint, direction) sign = copysign( 1.0, distance * distance - (aimPoint - collisionTestOrigin).lengthSquared) srcPoint = collisionTestOrigin endPoint = aimPoint - direction.scale(distance + sign * distRange[0]) collision = collideDynamicAndStatic( srcPoint, endPoint, (BigWorld.player().playerVehicleID, )) if collision: collisionDistance = (aimPoint - collision[0]).length if sign * (collisionDistance - distance) < distRange[0]: distance = collisionDistance - sign * distRange[0] if mathUtils.almostZero(self.__rotation): srcPoint = aimPoint - direction.scale(distance) endPoint = aimPoint collision = collideDynamicAndStatic( srcPoint, endPoint, (BigWorld.player().playerVehicleID, )) if collision: self.__aimingSystem.aimPoint = collision[0] if collision[1]: self.__positionHysteresis.update(aimPoint) self.__timeHysteresis.update(self.__prevTime) return distance def __calculateIdealState(self, deltaTime): aimPoint = self.__aimingSystem.aimPoint direction = self.__aimingSystem.direction impactPitch = max(direction.pitch, self.__cfg['minimalPitch']) self.__rotation = max(self.__rotation, impactPitch) distRange = self.__cfg['distRange'] distanceCurSq = ( aimPoint - BigWorld.player().getVehicleAttached().position).lengthSquared distanceMinSq = distRange[0]**2.0 forcedPitch = impactPitch if distanceCurSq < distanceMinSq: forcedPitch = atan2(sqrt(distanceMinSq - distanceCurSq), sqrt(distanceCurSq)) angularShift = self.__cfg['angularSpeed'] * deltaTime angularShift = angularShift if self.__choosePitchLevel( aimPoint) else -angularShift minPitch = max(forcedPitch, impactPitch) maxPitch = max(forcedPitch, self.__cfg['maximalPitch']) self.__rotation = mathUtils.clamp(minPitch, maxPitch, self.__rotation + angularShift) desiredDistance = self.__getDesiredCameraDistance() cameraDirection = self.__getCameraDirection() desiredDistance = self.__resolveCollisions(aimPoint, desiredDistance, cameraDirection) desiredDistance = mathUtils.clamp(distRange[0], distRange[1], desiredDistance) translation = aimPoint - cameraDirection.scale(desiredDistance) rotation = Vector3(cameraDirection.yaw, -cameraDirection.pitch, 0.0) return (translation, rotation) def __interpolateStates(self, deltaTime, translation, rotation): lerpParam = mathUtils.clamp( 0.0, 1.0, deltaTime * self.__cfg['interpolationSpeed']) self.__sourceMatrix = slerp(self.__sourceMatrix, rotation, lerpParam) self.__targetMatrix.translation = mathUtils.lerp( self.__targetMatrix.translation, translation, lerpParam) return (self.__sourceMatrix, self.__targetMatrix) def __cameraUpdate(self): deltaTime = self.__updateTime() self.__handleMovement() aimPoint = Vector3(self.__aimingSystem.aimPoint) self.__aimOffset = self.__calculateAimOffset(aimPoint) self.__updateTrajectoryDrawer() translation, rotation = self.__calculateIdealState(deltaTime) self.__interpolateStates(deltaTime, translation, mathUtils.createRotationMatrix(rotation)) self.__camDist = aimPoint - self.__targetMatrix.translation self.__camDist = self.__camDist.length self.__cam.source = self.__sourceMatrix self.__cam.target.b = self.__targetMatrix self.__cam.pivotPosition = Vector3(0, 0, 0) BigWorld.player().positionControl.moveTo(aimPoint) self.__updateOscillator(deltaTime) self.__aimingSystem.update(deltaTime) if not self.__autoUpdatePosition: self.__dxdydz = Vector3(0, 0, 0) return 0.0 def __updateOscillator(self, deltaTime): if ArtyCamera.isCameraDynamic(): self.__positionOscillator.update(deltaTime) self.__positionNoiseOscillator.update(deltaTime) else: self.__positionOscillator.reset() self.__positionNoiseOscillator.reset() self.__cam.target.a = mathUtils.createTranslationMatrix( self.__positionOscillator.deviation + self.__positionNoiseOscillator.deviation) def __readCfg(self, dataSec): if not dataSec: LOG_WARNING( 'Invalid section <artyMode/camera> in avatar_input_handler.xml' ) self.__baseCfg = dict() bcfg = self.__baseCfg bcfg['keySensitivity'] = readFloat(dataSec, 'keySensitivity', 0.005, 10.0, 0.025) bcfg['sensitivity'] = readFloat(dataSec, 'sensitivity', 0.005, 10.0, 0.025) bcfg['scrollSensitivity'] = readFloat(dataSec, 'scrollSensitivity', 0.005, 10.0, 0.025) bcfg['angularSpeed'] = readFloat(dataSec, 'angularSpeed', pi / 720.0, pi / 0.5, pi / 360.0) bcfg['distRange'] = readVec2(dataSec, 'distRange', (1.0, 1.0), (10000.0, 10000.0), (2.0, 30.0)) bcfg['minimalPitch'] = readFloat(dataSec, 'minimalPitch', pi / 36.0, pi / 3.0, pi / 18.0) bcfg['maximalPitch'] = readFloat(dataSec, 'maximalPitch', pi / 6.0, pi / 3.0, pi / 3.5) bcfg['interpolationSpeed'] = readFloat(dataSec, 'interpolationSpeed', 0.0, 10.0, 5.0) bcfg['highPitchThreshold'] = readFloat(dataSec, 'highPitchThreshold', 0.1, 10.0, 3.0) bcfg['hTimeThreshold'] = readFloat(dataSec, 'hysteresis/timeThreshold', 0.0, 10.0, 0.5) bcfg['hPositionThreshold'] = readFloat(dataSec, 'hysteresis/positionThreshold', 0.0, 100.0, 7.0) ds = Settings.g_instance.userPrefs[Settings.KEY_CONTROL_MODE] if ds is not None: ds = ds['artyMode/camera'] self.__userCfg = dict() ucfg = self.__userCfg ucfg['horzInvert'] = self.settingsCore.getSetting('mouseHorzInvert') ucfg['vertInvert'] = self.settingsCore.getSetting('mouseVertInvert') ucfg['keySensitivity'] = readFloat(ds, 'keySensitivity', 0.0, 10.0, 1.0) ucfg['sensitivity'] = readFloat(ds, 'sensitivity', 0.0, 10.0, 1.0) ucfg['scrollSensitivity'] = readFloat(ds, 'scrollSensitivity', 0.0, 10.0, 1.0) ucfg['camDist'] = readFloat(ds, 'camDist', 0.0, 60.0, 0.0) self.__cfg = dict() cfg = self.__cfg cfg['keySensitivity'] = bcfg['keySensitivity'] cfg['sensitivity'] = bcfg['sensitivity'] cfg['scrollSensitivity'] = bcfg['scrollSensitivity'] cfg['distRange'] = bcfg['distRange'] cfg['angularSpeed'] = bcfg['angularSpeed'] cfg['minimalPitch'] = bcfg['minimalPitch'] cfg['maximalPitch'] = bcfg['maximalPitch'] cfg['interpolationSpeed'] = bcfg['interpolationSpeed'] cfg['highPitchThresholdSq'] = bcfg['highPitchThreshold'] * bcfg[ 'highPitchThreshold'] cfg['hTimeThreshold'] = bcfg['hTimeThreshold'] cfg['hPositionThresholdSq'] = bcfg['hPositionThreshold'] * bcfg[ 'hPositionThreshold'] cfg['keySensitivity'] *= ucfg['keySensitivity'] cfg['sensitivity'] *= ucfg['sensitivity'] cfg['scrollSensitivity'] *= ucfg['scrollSensitivity'] cfg['camDist'] = ucfg['camDist'] cfg['horzInvert'] = ucfg['horzInvert'] cfg['vertInvert'] = ucfg['vertInvert'] dynamicSection = dataSec['dynamics'] self.__dynamicCfg.readImpulsesConfig(dynamicSection) self.__positionOscillator = createOscillatorFromSection( dynamicSection['oscillator'], False) self.__positionNoiseOscillator = createOscillatorFromSection( dynamicSection['randomNoiseOscillatorFlat'], False) return
class ArtyCamera(CameraWithSettings, CallbackDelayer): _DYNAMIC_ENABLED = True @staticmethod def enableDynamicCamera(enable): ArtyCamera._DYNAMIC_ENABLED = enable @staticmethod def isCameraDynamic(): return ArtyCamera._DYNAMIC_ENABLED camera = property(lambda self: self.__cam) aimingSystem = property(lambda self: self.__aimingSystem) __aimOffset = aih_global_binding.bindRW( aih_global_binding.BINDING_ID.AIM_OFFSET) def __init__(self, dataSec): super(ArtyCamera, self).__init__() CallbackDelayer.__init__(self) self.isAimOffsetEnabled = True self.__positionOscillator = None self.__positionNoiseOscillator = None self.__switchers = CameraSwitcherCollection(cameraSwitchers=[ CameraSwitcher(switchType=SwitchTypes.FROM_TRANSITION_DIST_AS_MAX, switchToName=CTRL_MODE_NAME.STRATEGIC, switchToPos=0.0) ], isEnabled=True) self.__dynamicCfg = CameraDynamicConfig() self._readConfigs(dataSec) self.__cam = BigWorld.CursorCamera() self.__curSense = self._cfg['sensitivity'] self.__onChangeControlMode = None self.__camDist = 0.0 self.__desiredCamDist = 0.0 self.__aimingSystem = None self.__prevTime = 0.0 self.__prevAimPoint = Vector3() self.__dxdydz = Vector3(0.0, 0.0, 0.0) self.__autoUpdatePosition = False self.__needReset = 0 self.__sourceMatrix = None self.__targetMatrix = None self.__rotation = 0.0 self.__positionHysteresis = None self.__timeHysteresis = None self.__transitionEnabled = True self.__scrollSmoother = SPGScrollSmoother(0.3) self.__collisionDist = 0.0 self.__camViewPoint = Vector3() return @staticmethod def _getConfigsKey(): return ArtyCamera.__name__ def create(self, onChangeControlMode=None): super(ArtyCamera, self).create() self.__onChangeControlMode = onChangeControlMode aimingSystemClass = ArtyAimingSystemRemote if BigWorld.player( ).isObserver() else ArtyAimingSystem self.__aimingSystem = aimingSystemClass() self.__camDist = self._cfg['camDist'] self.__desiredCamDist = self.__camDist self.__positionHysteresis = PositionHysteresis( self._cfg['hPositionThresholdSq']) self.__timeHysteresis = TimeHysteresis(self._cfg['hTimeThreshold']) self.__cam.pivotMaxDist = 0.0 self.__cam.maxDistHalfLife = 0.01 self.__cam.movementHalfLife = 0.0 self.__cam.turningHalfLife = -1 self.__cam.pivotPosition = Vector3(0.0, 0.0, 0.0) self.__sourceMatrix = Matrix() self.__targetMatrix = Matrix() self.__rotation = 0.0 self.__enableSwitchers() self.__scrollSmoother.setTime(self._cfg['scrollSmoothingTime']) def destroy(self): self.disable() self.__onChangeControlMode = None self.__cam = None if self.__aimingSystem is not None: self.__aimingSystem.destroy() self.__aimingSystem = None CallbackDelayer.destroy(self) CameraWithSettings.destroy(self) return def enable(self, targetPos, saveDist, switchToPos=None, switchToPlace=None): BigWorld.wg_trajectory_drawer().setStrategicMode(False) self.__prevTime = 0.0 if switchToPlace == SwitchToPlaces.TO_TRANSITION_DIST: self.__camDist = math_utils.clamp(self._cfg['distRange'][0], self._cfg['distRange'][1], self._cfg['transitionDist']) elif switchToPlace == SwitchToPlaces.TO_RELATIVE_POS and switchToPos is not None: minDist, maxDist = self._cfg['distRange'] self.__camDist = (maxDist - minDist) * switchToPos + minDist elif switchToPlace == SwitchToPlaces.TO_NEAR_POS and switchToPos is not None: minDist, maxDist = self._cfg['distRange'] self.__camDist = math_utils.clamp(minDist, maxDist, switchToPos) elif self.settingsCore.getSetting(SPGAim.AUTO_CHANGE_AIM_MODE): self.__camDist = math_utils.clamp(self._cfg['distRange'][0], self._cfg['transitionDist'], self.__camDist) self.__desiredCamDist = self.__camDist self.__scrollSmoother.start(self.__desiredCamDist) self.__enableSwitchers() self.__aimingSystem.enable(targetPos) self.__positionHysteresis.update(Vector3(0.0, 0.0, 0.0)) self.__timeHysteresis.update(BigWorld.timeExact()) camTarget = MatrixProduct() self.__rotation = max(self.__aimingSystem.direction.pitch, self._cfg['minimalPitch']) cameraDirection = self.__getCameraDirection() self.__targetMatrix.translation = self.aimingSystem.aimPoint - cameraDirection.scale( self.__camDist) self.__cam.target = camTarget self.__cam.target.b = self.__targetMatrix self.__sourceMatrix = math_utils.createRotationMatrix( (cameraDirection.yaw, -cameraDirection.pitch, 0.0)) self.__cam.source = self.__sourceMatrix self.__cam.forceUpdate() BigWorld.camera(self.__cam) BigWorld.player().positionControl.moveTo(self.__aimingSystem.hitPoint) BigWorld.player().positionControl.followCamera(False) self.__cameraUpdate() self.delayCallback(0.01, self.__cameraUpdate) self.__needReset = 1 return def disable(self): self.__scrollSmoother.stop() if self.__aimingSystem is not None: self.__aimingSystem.disable() self.stopCallback(self.__cameraUpdate) self.__switchers.clear() self.__positionOscillator.reset() return def teleport(self, pos): self.__aimingSystem.updateTargetPos(pos) self.update(0.0, 0.0, 0.0) def getDistRatio(self): minDist, maxDist = self._cfg['distRange'] return (self.__desiredCamDist - minDist) / (maxDist - minDist) def getCurrentCamDist(self): return self.__desiredCamDist def getCamDistRange(self): return self._cfg['distRange'] def getCamTransitionDist(self): return self._cfg['transitionDist'] def update(self, dx, dy, dz, updateByKeyboard=False): self.__curSense = self._cfg[ 'keySensitivity'] if updateByKeyboard else self._cfg['sensitivity'] self.__autoUpdatePosition = updateByKeyboard self.__dxdydz = Vector3(dx if not self._cfg['horzInvert'] else -dx, dy if not self._cfg['vertInvert'] else -dy, dz) def applyImpulse(self, position, impulse, reason=ImpulseReason.ME_HIT): adjustedImpulse, noiseMagnitude = self.__dynamicCfg.adjustImpulse( impulse, reason) impulseFlatDir = Vector3(adjustedImpulse.x, 0, adjustedImpulse.z) impulseFlatDir.normalise() impulseLocal = self.__sourceMatrix.applyVector( impulseFlatDir * (-1 * adjustedImpulse.length)) self.__positionOscillator.applyImpulse(impulseLocal) self.__applyNoiseImpulse(noiseMagnitude) def applyDistantImpulse(self, position, impulseValue, reason=ImpulseReason.ME_HIT): if reason != ImpulseReason.SPLASH and reason != ImpulseReason.PROJECTILE_HIT: return impulse = BigWorld.player().getOwnVehiclePosition() - position distance = impulse.length if distance <= 1.0: distance = 1.0 impulse.normalise() if reason == ImpulseReason.PROJECTILE_HIT: if not cameras.isPointOnScreen(position): return distance = 1.0 impulse *= impulseValue / distance self.applyImpulse(position, impulse, reason) def writeUserPreferences(self): ds = Settings.g_instance.userPrefs if not ds.has_key(Settings.KEY_CONTROL_MODE): ds.write(Settings.KEY_CONTROL_MODE, '') ucfg = self._userCfg ds = ds[Settings.KEY_CONTROL_MODE] ds.writeBool('artyMode/camera/horzInvert', ucfg['horzInvert']) ds.writeBool('artyMode/camera/vertInvert', ucfg['vertInvert']) ds.writeFloat('artyMode/camera/keySensitivity', ucfg['keySensitivity']) ds.writeFloat('artyMode/camera/sensitivity', ucfg['sensitivity']) ds.writeFloat('artyMode/camera/scrollSensitivity', ucfg['scrollSensitivity']) ds.writeFloat('artyMode/camera/camDist', self._cfg['camDist']) def __applyNoiseImpulse(self, noiseMagnitude): noiseImpulse = math_utils.RandomVectors.random3Flat(noiseMagnitude) self.__positionNoiseOscillator.applyImpulse(noiseImpulse) def __calculateAimOffset(self, aimWorldPos): if not self.isAimOffsetEnabled: return Vector2(0.0, 0.0) replayCtrl = BattleReplay.g_replayCtrl if replayCtrl.isPlaying and replayCtrl.isControllingCamera: aimOffset = replayCtrl.getAimClipPosition() else: proj = BigWorld.projection() aimLocalPos = Matrix(self.__cam.matrix).applyPoint(aimWorldPos) if aimLocalPos.z < 0: aimLocalPos.z = max(0.0, proj.nearPlane - _OFFSET_FROM_NEAR_PLANE) aimWorldPos = Matrix( self.__cam.invViewMatrix).applyPoint(aimLocalPos) aimOffset = cameras.projectPoint(aimWorldPos) aimOffset = Vector2(math_utils.clamp(-0.95, 0.95, aimOffset.x), math_utils.clamp(-0.95, 0.95, aimOffset.y)) if replayCtrl.isRecording: replayCtrl.setAimClipPosition(aimOffset) return aimOffset def __handleMovement(self): replayCtrl = BattleReplay.g_replayCtrl if replayCtrl.isPlaying: if self.__needReset != 0: if self.__needReset > 1: from helpers import isPlayerAvatar player = BigWorld.player() if isPlayerAvatar(): if player.inputHandler.ctrl is not None: player.inputHandler.ctrl.resetGunMarkers() self.__needReset = 0 else: self.__needReset += 1 if replayCtrl.isControllingCamera: self.__aimingSystem.updateTargetPos( replayCtrl.getGunRotatorTargetPoint()) else: self.__aimingSystem.handleMovement( self.__dxdydz.x * self.__curSense, -self.__dxdydz.y * self.__curSense) if self.__dxdydz.x != 0 or self.__dxdydz.y != 0 or self.__dxdydz.z != 0: self.__needReset = 2 else: self.__aimingSystem.handleMovement( self.__dxdydz.x * self.__curSense, -self.__dxdydz.y * self.__curSense) return def __getCameraDirection(self): direction = Vector3() direction.setPitchYaw(self.__rotation, self.__aimingSystem.direction.yaw) direction.normalise() return direction def __getDesiredCameraDistance(self): distRange = self.__switchersDistRange() self.__desiredCamDist -= self.__dxdydz.z * self.__curSense self.__desiredCamDist = math_utils.clamp(distRange[0], distRange[1], self.__desiredCamDist) self.__desiredCamDist = self.__aimingSystem.overrideCamDist( self.__desiredCamDist) self._cfg['camDist'] = self.__camDist self.__scrollSmoother.moveTo(self.__desiredCamDist, distRange) return self.__desiredCamDist def __updateTrajectoryDrawer(self): shotDescr = BigWorld.player().getVehicleDescriptor().shot BigWorld.wg_trajectory_drawer().setParams( shotDescr.maxDistance, Vector3(0, -shotDescr.gravity, 0), self.__aimOffset) def __updateTime(self): curTime = BigWorld.timeExact() deltaTime = curTime - self.__prevTime self.__prevTime = curTime return deltaTime def __choosePitchLevel(self, aimPoint): useHighPitch = (aimPoint - self.__aimingSystem.hitPoint ).lengthSquared > self._cfg['highPitchThresholdSq'] if useHighPitch: useHighPitch = self.__positionHysteresis.check( aimPoint) or self.__timeHysteresis.check(self.__prevTime) else: self.__positionHysteresis.update(aimPoint) self.__timeHysteresis.update(self.__prevTime) return useHighPitch def __getCollisionTestOrigin(self, aimPoint, direction): distRange = self.__switchersDistRange() vehiclePosition = BigWorld.player().getVehicleAttached().position collisionTestOrigin = Vector3(vehiclePosition) if direction.x * direction.x > direction.z * direction.z and not math_utils.almostZero( direction.x): collisionTestOrigin.y = direction.y / direction.x * ( vehiclePosition.x - aimPoint.x) + aimPoint.y elif not math_utils.almostZero(direction.z): collisionTestOrigin.y = direction.y / direction.z * ( vehiclePosition.z - aimPoint.z) + aimPoint.y else: collisionTestOrigin = aimPoint - direction.scale( (distRange[1] - distRange[0]) / 2.0) LOG_WARNING( "Can't find point on direction ray. Using half point as collision test origin" ) return collisionTestOrigin def __resolveCollisions(self, aimPoint, distance, direction): distRange = self.__switchersDistRange() collisionDist = 0.0 collisionTestOrigin = self.__getCollisionTestOrigin( aimPoint, direction) sign = copysign( 1.0, distance * distance - (aimPoint - collisionTestOrigin).lengthSquared) srcPoint = collisionTestOrigin endPoint = aimPoint - direction.scale( distance + sign * (distRange[0] + _SEARCH_COLLISION_DEPTH)) collision = collideDynamicAndStatic( srcPoint, endPoint, (BigWorld.player().playerVehicleID, )) if collision: collisionDistance = (aimPoint - collision[0]).length collisionDist = collisionDistance - sign * distRange[0] if sign * (collisionDistance - distance) < distRange[0]: distance = collisionDist if math_utils.almostZero(self.__rotation): srcPoint = aimPoint - direction.scale(distance) endPoint = aimPoint collision = collideDynamicAndStatic( srcPoint, endPoint, (BigWorld.player().playerVehicleID, )) if collision: self.__aimingSystem.aimPoint = collision[0] if collision[1]: self.__positionHysteresis.update(aimPoint) self.__timeHysteresis.update(self.__prevTime) return collisionDist def __calculateIdealState(self, deltaTime): aimPoint = self.__aimingSystem.aimPoint direction = self.__aimingSystem.direction impactPitch = max(direction.pitch, self._cfg['minimalPitch']) self.__rotation = max(self.__rotation, impactPitch) distRange = self.__switchersDistRange() distanceCurSq = ( aimPoint - BigWorld.player().getVehicleAttached().position).lengthSquared distanceMinSq = distRange[0]**2.0 forcedPitch = impactPitch if distanceCurSq < distanceMinSq: forcedPitch = atan2(sqrt(distanceMinSq - distanceCurSq), sqrt(distanceCurSq)) angularShift = self._cfg['angularSpeed'] * deltaTime angularShift = angularShift if self.__choosePitchLevel( aimPoint) else -angularShift minPitch = max(forcedPitch, impactPitch) maxPitch = max(forcedPitch, self._cfg['maximalPitch']) self.__rotation = math_utils.clamp(minPitch, maxPitch, self.__rotation + angularShift) desiredDistance = self.__getDesiredCameraDistance() cameraDirection = self.__getCameraDirection() collisionDist = self.__resolveCollisions(aimPoint, desiredDistance, cameraDirection) collisionDist = max(distRange[0], collisionDist) desiredDistance = self.__scrollSmoother.update(deltaTime) rotation = Vector3(cameraDirection.yaw, -cameraDirection.pitch, 0.0) return (rotation, desiredDistance, collisionDist) def __interpolateStates(self, deltaTime, rotation, desiredDistance, collisionDist): lerpParam = math_utils.clamp( 0.0, 1.0, deltaTime * self._cfg['interpolationSpeed']) collisionLerpParam = math_utils.clamp( 0.0, 1.0, deltaTime * self._cfg['distInterpolationSpeed']) self.__sourceMatrix = slerp(self.__sourceMatrix, rotation, lerpParam) camDirection = Vector3() camDirection.setPitchYaw(-self.__sourceMatrix.pitch, self.__sourceMatrix.yaw) camDirection.normalise() self.__camViewPoint = math_utils.lerp(self.__camViewPoint, self.__aimingSystem.aimPoint, lerpParam) self.__collisionDist = math_utils.lerp(self.__collisionDist, collisionDist, collisionLerpParam) desiredDistance = max(desiredDistance, self.__collisionDist) self.__targetMatrix.translation = self.__camViewPoint - camDirection.scale( desiredDistance) return (self.__sourceMatrix, self.__targetMatrix) def __cameraUpdate(self): deltaTime = self.__updateTime() self.__aimOffset = self.__calculateAimOffset( self.__aimingSystem.aimPoint) self.__handleMovement() aimPoint = Vector3(self.__aimingSystem.aimPoint) self.__updateTrajectoryDrawer() rotation, desiredDistance, collisionDist = self.__calculateIdealState( deltaTime) self.__interpolateStates(deltaTime, math_utils.createRotationMatrix(rotation), desiredDistance, collisionDist) self.__camDist = aimPoint - self.__targetMatrix.translation self.__camDist = self.__camDist.length self.__cam.source = self.__sourceMatrix self.__cam.target.b = self.__targetMatrix self.__cam.pivotPosition = Vector3(0, 0, 0) if aimPoint.distSqrTo(self.__prevAimPoint) > 0.010000000000000002: BigWorld.player().positionControl.moveTo(aimPoint) self.__prevAimPoint = aimPoint self.__updateOscillator(deltaTime) self.__aimingSystem.update(deltaTime) if self.__onChangeControlMode is not None and self.__switchers.needToSwitch( self.__dxdydz.z, self.__desiredCamDist, self._cfg['distRange'][0], self._cfg['distRange'][1], self._cfg['transitionDist']): self.__onChangeControlMode(*self.__switchers.getSwitchParams()) if not self.__transitionEnabled and self.__desiredCamDist - TRANSITION_DIST_HYSTERESIS <= self._cfg[ 'transitionDist']: self.__transitionEnabled = True self.__enableSwitchers(False) if not self.__autoUpdatePosition: self.__dxdydz = Vector3(0, 0, 0) return 0.01 def __updateOscillator(self, deltaTime): if ArtyCamera.isCameraDynamic(): self.__positionOscillator.update(deltaTime) self.__positionNoiseOscillator.update(deltaTime) else: self.__positionOscillator.reset() self.__positionNoiseOscillator.reset() self.__cam.target.a = math_utils.createTranslationMatrix( self.__positionOscillator.deviation + self.__positionNoiseOscillator.deviation) def __switchersDistRange(self): distRange = self._cfg['distRange'] if self.__switchers.isEnabled(): distRange = self.__switchers.getDistLimitationForSwitch( distRange[0], distRange[1], self._cfg['transitionDist']) return distRange def _readConfigs(self, dataSec): if not dataSec: LOG_WARNING( 'Invalid section <artyMode/camera> in avatar_input_handler.xml' ) super(ArtyCamera, self)._readConfigs(dataSec) dynamicSection = dataSec['dynamics'] self.__dynamicCfg.readImpulsesConfig(dynamicSection) self.__positionOscillator = createOscillatorFromSection( dynamicSection['oscillator'], False) self.__positionNoiseOscillator = createOscillatorFromSection( dynamicSection['randomNoiseOscillatorFlat']) def _readBaseCfg(self, dataSec): bcfg = self._baseCfg bcfg['keySensitivity'] = readFloat(dataSec, 'keySensitivity', 0.005, 10.0, 0.025) bcfg['sensitivity'] = readFloat(dataSec, 'sensitivity', 0.005, 10.0, 0.025) bcfg['scrollSensitivity'] = readFloat(dataSec, 'scrollSensitivity', 0.005, 10.0, 0.025) bcfg['angularSpeed'] = readFloat(dataSec, 'angularSpeed', pi / 720.0, pi / 0.5, pi / 360.0) bcfg['distRange'] = readVec2(dataSec, 'distRange', (1.0, 1.0), (10000.0, 10000.0), (2.0, 30.0)) bcfg['transitionDist'] = readFloat(dataSec, 'transitionDist', 1.0, 10000.0, 60.0) bcfg['minimalPitch'] = readFloat(dataSec, 'minimalPitch', pi / 36.0, pi / 3.0, pi / 18.0) bcfg['maximalPitch'] = readFloat(dataSec, 'maximalPitch', pi / 6.0, pi / 3.0, pi / 3.5) bcfg['interpolationSpeed'] = readFloat(dataSec, 'interpolationSpeed', 0.0, 100.0, 5.0) bcfg['distInterpolationSpeed'] = readFloat(dataSec, 'distInterpolationSpeed', 0.0, 10.0, 5.0) bcfg['highPitchThreshold'] = readFloat(dataSec, 'highPitchThreshold', 0.1, 10.0, 3.0) bcfg['hTimeThreshold'] = readFloat(dataSec, 'hysteresis/timeThreshold', 0.0, 10.0, 0.5) bcfg['hPositionThreshold'] = readFloat(dataSec, 'hysteresis/positionThreshold', 0.0, 100.0, 7.0) bcfg['scrollSmoothingTime'] = readFloat(dataSec, 'scrollSmoothingTime', 0.0, 1.0, 0.3) def _readUserCfg(self): ucfg = self._userCfg dataSec = Settings.g_instance.userPrefs[Settings.KEY_CONTROL_MODE] if dataSec is not None: dataSec = dataSec['artyMode/camera'] ucfg['horzInvert'] = False ucfg['vertInvert'] = False ucfg['keySensitivity'] = readFloat(dataSec, 'keySensitivity', 0.0, 10.0, 1.0) ucfg['sensitivity'] = readFloat(dataSec, 'sensitivity', 0.0, 10.0, 1.0) ucfg['scrollSensitivity'] = readFloat(dataSec, 'scrollSensitivity', 0.0, 10.0, 1.0) ucfg['camDist'] = readFloat(dataSec, 'camDist', 0.0, 60.0, 0.0) return def _makeCfg(self): bcfg = self._baseCfg ucfg = self._userCfg cfg = self._cfg cfg['keySensitivity'] = bcfg['keySensitivity'] cfg['sensitivity'] = bcfg['sensitivity'] cfg['scrollSensitivity'] = bcfg['scrollSensitivity'] cfg['distRange'] = bcfg['distRange'] cfg['transitionDist'] = bcfg['transitionDist'] cfg['angularSpeed'] = bcfg['angularSpeed'] cfg['minimalPitch'] = bcfg['minimalPitch'] cfg['maximalPitch'] = bcfg['maximalPitch'] cfg['interpolationSpeed'] = bcfg['interpolationSpeed'] cfg['distInterpolationSpeed'] = bcfg['distInterpolationSpeed'] cfg['highPitchThresholdSq'] = bcfg['highPitchThreshold'] * bcfg[ 'highPitchThreshold'] cfg['hTimeThreshold'] = bcfg['hTimeThreshold'] cfg['hPositionThresholdSq'] = bcfg['hPositionThreshold'] * bcfg[ 'hPositionThreshold'] cfg['scrollSmoothingTime'] = bcfg['scrollSmoothingTime'] cfg['keySensitivity'] *= ucfg['keySensitivity'] cfg['sensitivity'] *= ucfg['sensitivity'] cfg['scrollSensitivity'] *= ucfg['scrollSensitivity'] cfg['camDist'] = ucfg['camDist'] cfg['horzInvert'] = ucfg['horzInvert'] cfg['vertInvert'] = ucfg['vertInvert'] def _handleSettingsChange(self, diff): super(ArtyCamera, self)._handleSettingsChange(diff) if SPGAim.AUTO_CHANGE_AIM_MODE in diff: self.__enableSwitchers() if GAME.SCROLL_SMOOTHING in diff: self.__scrollSmoother.setIsEnabled( self.settingsCore.getSetting(GAME.SCROLL_SMOOTHING)) def _updateSettingsFromServer(self): super(ArtyCamera, self)._updateSettingsFromServer() if self.settingsCore.isReady: self.__enableSwitchers() self.__scrollSmoother.setIsEnabled( self.settingsCore.getSetting(GAME.SCROLL_SMOOTHING)) def __enableSwitchers(self, updateTransitionEnabled=True): if updateTransitionEnabled and self.__desiredCamDist - TRANSITION_DIST_HYSTERESIS >= self._cfg[ 'transitionDist']: self.__transitionEnabled = False if self.settingsCore.isReady: self.__switchers.setIsEnabled( self.settingsCore.getSetting(SPGAim.AUTO_CHANGE_AIM_MODE) and self.__transitionEnabled)
class RadialMenu(RadialMenuMeta, BattleGUIKeyHandler, CallbackDelayer): sessionProvider = dependency.descriptor(IBattleSessionProvider) _aimOffset = aih_global_binding.bindRW( aih_global_binding.BINDING_ID.AIM_OFFSET) _REFRESH_TIME_IN_SECONDS = 0.3 def __init__(self): super(RadialMenu, self).__init__() IBCLogger.__init__(self) self.__crosshairData = None self.__stateData = None self.__isVisible = False return def handleEscKey(self, isDown): return True def onAction(self, action): chatCommands = self.sessionProvider.shared.chatCommands if chatCommands is None: return elif action == RADIAL_MENU_CONSTS.EMPTY_BUTTON_STATE or self.__crosshairData is None: self.__setVisibility(False) return else: if action == BATTLE_CHAT_COMMAND_NAMES.REPLY: if self.__crosshairData.replyState == ReplyState.CAN_CONFIRM and self.__crosshairData.replyToAction in ONE_SHOT_COMMANDS_TO_REPLIES.keys( ): chatCommands.handleChatCommand( ONE_SHOT_COMMANDS_TO_REPLIES[ self.__crosshairData.replyToAction], targetID=self.__crosshairData.targetID) else: chatCommands.sendReplyChatCommand( self.__crosshairData.targetID, self.__crosshairData.replyToAction) elif action == BATTLE_CHAT_COMMAND_NAMES.CANCEL_REPLY: chatCommands.sendCancelReplyChatCommand( self.__crosshairData.targetID, self.__crosshairData.replyToAction) elif action in (BATTLE_CHAT_COMMAND_NAMES.GOING_THERE, BATTLE_CHAT_COMMAND_NAMES.ATTENTION_TO_POSITION, BATTLE_CHAT_COMMAND_NAMES.SPG_AIM_AREA): chatCommands.sendAdvancedPositionPing(action, isInRadialMenu=True) else: chatCommands.handleChatCommand( action, targetID=self.__crosshairData.targetID, isInRadialMenu=True) self.__crosshairData = None self.__setVisibility(False) return def onSelect(self): self.__playSound(SoundEffectsId.SELECT_RADIAL_BUTTON) def onHideCompleted(self): self.__setVisibility(False) def onRefresh(self): if self.__isVisible: self.show() @loggerEntry def show(self, reshowPreviousState=False): chatCommands = self.sessionProvider.shared.chatCommands if chatCommands is None: return else: if reshowPreviousState: targetID, targetMarkerType, crosshairType, _, _ = self.__crosshairData replyState, replyToAction = chatCommands.getReplyStateForTargetIDAndMarkerType( targetID, targetMarkerType) else: targetID, targetMarkerType, crosshairType, replyState, replyToAction = chatCommands.getAimedAtTargetData( ) menuState = self.__getRadialMenuState(targetID, targetMarkerType, crosshairType, replyState, replyToAction) replyStateDiff = self.__generateDiffStateDict( menuState, replyState, replyToAction, targetID) ctrl = self.sessionProvider.shared.crosshair if ctrl is not None: position = ctrl.getDisaredPosition() else: guiScreenWidth, guiScreenHeight = GUI.screenResolution() position = (guiScreenWidth * 0.5, guiScreenHeight * 0.5) if self.app is not None: self.app.registerGuiKeyHandler(self) self.__setVisibility(True) self._showInternal(menuState, replyStateDiff, position) if RadialMenu.__isMarkerEmptyLocationOrOutOfBorder( self.__crosshairData.targetMarkerType, self.__crosshairData.targetMarkerSubtype): self.delayCallback(self._REFRESH_TIME_IN_SECONDS, self.__checkForValidLocationMarkerLoop) if RadialMenu.__isCanRespondToAlly( self.__crosshairData.targetMarkerType, self.__crosshairData.targetMarkerSubtype, self.__crosshairData.replyState): self.delayCallback(self._REFRESH_TIME_IN_SECONDS, self.__checkForTemporaryRespondUpdateLoop) return @simpleLog(action='radial_menu_open') def hide(self, allowAction=True): if self.app is not None: self.app.unregisterGuiKeyHandler(self) if self.__isVisible is False: return else: self.as_hideS(allowAction) self.stopCallback(self.__checkForValidLocationMarkerLoop) return def _populate(self): super(RadialMenu, self)._populate() CommandMapping.g_instance.onMappingChanged += self.__onMappingChanged self.__refreshShortcutsAndState() ctrl = self.sessionProvider.shared.feedback if ctrl is not None: ctrl.onReplyFeedbackReceived += self.__onReplyFeedbackReceived ctrl.onRemoveCommandReceived += self.__onRemoveCommandReceived ctrl.onVehicleMarkerRemoved += self.__onVehicleMarkerRemoved ctrl.onVehicleFeedbackReceived += self.__onVehicleFeedbackReceived ctrl.onAddCommandReceived += self.__onAddCommandReceived return def _dispose(self): CommandMapping.g_instance.onMappingChanged -= self.__onMappingChanged ctrl = self.sessionProvider.shared.feedback if ctrl is not None: ctrl.onReplyFeedbackReceived -= self.__onReplyFeedbackReceived ctrl.onRemoveCommandReceived -= self.__onRemoveCommandReceived ctrl.onVehicleMarkerRemoved -= self.__onVehicleMarkerRemoved ctrl.onVehicleFeedbackReceived -= self.__onVehicleFeedbackReceived ctrl.onAddCommandReceived -= self.__onAddCommandReceived super(RadialMenu, self)._dispose() return def _showInternal(self, radialState, diff, position): cursorX, cursorY = GUI.mcursor().position self.as_showS(cursorX, cursorY, radialState, diff, position) def __setVisibility(self, newState): if newState == self.__isVisible: return self.__isVisible = newState gui_event_dispatcher.toggleCrosshairVisibility() def __onMappingChanged(self, *args): self.__refreshShortcutsAndState() def __refreshShortcutsAndState(self): self.__stateData = [] def createShortcut(shortcutData): return { 'title': shortcutData.title, 'action': shortcutData.action, 'icon': shortcutData.icon, 'bState': shortcutData.bState, 'indexInGroup': shortcutData.indexInGroup, 'key': getKeyFromAction(shortcutData.action) } for state in RADIAL_MENU_CONSTS.ALL_TARGET_STATES: bottomShortcuts = map(createShortcut, BOTTOM_SHORTCUT_SETS[state]) if state == RADIAL_MENU_CONSTS.TARGET_STATE_ALLY: regularShortcuts = map(createShortcut, ALLY_UPPER_SHORTCUTS_DEFAULT) else: regularShortcuts = map(createShortcut, UPPER_SHORTCUT_SETS[state]) self.__stateData.append({ 'state': state, 'bottomShortcuts': bottomShortcuts, 'regularShortcuts': regularShortcuts }) self.as_buildDataS(self.__stateData) def __getRadialMenuState(self, targetID, targetMarkerType, targetMarkerSubtype, replyState, replyToAction): if targetMarkerType in _MARKERS_TYPE_TO_SUBTYPE_MAP and targetMarkerSubtype in _MARKERS_TYPE_TO_SUBTYPE_MAP[ targetMarkerType]: viewState = _MARKERS_TYPE_TO_SUBTYPE_MAP[targetMarkerType][ targetMarkerSubtype] else: _logger.warning( "Marker subtype name '%s' is not defined for '%s'.", targetMarkerSubtype, targetMarkerType) viewState = RADIAL_MENU_CONSTS.TARGET_STATE_DEFAULT if self.sessionProvider.getArenaDP().getVehicleInfo().isSPG(): if viewState == RADIAL_MENU_CONSTS.TARGET_STATE_ENEMY: viewState = RADIAL_MENU_CONSTS.TARGET_STATE_SPG_ENEMY elif viewState == RADIAL_MENU_CONSTS.TARGET_STATE_DEFAULT: if replyState == ReplyState.CAN_REPLY: viewState = RADIAL_MENU_CONSTS.TARGET_STATE_DEFAULT else: viewState = RADIAL_MENU_CONSTS.TARGET_STATE_SPG_DEFAULT self.__crosshairData = CrosshairData(targetID, targetMarkerType, targetMarkerSubtype, replyState, replyToAction) return viewState if viewState in RADIAL_MENU_CONSTS.ALL_TARGET_STATES else RADIAL_MENU_CONSTS.TARGET_STATE_DEFAULT def __playSound(self, soundName): if self.app.soundManager is not None: self.app.soundManager.playEffectSound(soundName) return def __generateDiffStateDict(self, targetState, replyState, replyAction, targetID): resultingDiffList = [] if targetState not in UPPER_SHORTCUT_SETS and targetState != RADIAL_MENU_CONSTS.TARGET_STATE_ALLY or targetState not in BOTTOM_SHORTCUT_SETS or self.__stateData is None: return resultingDiffList else: if targetState == RADIAL_MENU_CONSTS.TARGET_STATE_ALLY: self.__populateWithAllyData(replyAction, replyState, resultingDiffList, targetID) else: if replyState in (ReplyState.CAN_CANCEL_REPLY, ReplyState.CAN_REPLY, ReplyState.CAN_CONFIRM): self.__populateWithNonAllyData(replyState, resultingDiffList, targetState) self.__handleSpgView(resultingDiffList, targetState) return resultingDiffList def __populateWithNonAllyData(self, replyState, resultingDiffList, targetState): for shortcut in UPPER_SHORTCUT_SETS[targetState]: buttonData = defaultdict() RadialMenu.__copyShortcutData(buttonData=buttonData, shortcut=shortcut) if shortcut.indexInGroup == RADIAL_MENU_CONSTS.ELEMENT_INDEX_FIRST: defaultShortcut = self.__adjustPrimaryRadialButton( replyState, shortcut, BATTLE_CHAT_COMMAND_NAMES.REPLY) RadialMenu.__copyShortcutData(buttonData=buttonData, shortcut=defaultShortcut) buttonData['key'] = getKeyFromAction( BATTLE_CHAT_COMMAND_NAMES.REPLY) elif shortcut.bState != RADIAL_MENU_CONSTS.EMPTY_BUTTON_STATE: buttonData['bState'] = RADIAL_MENU_CONSTS.DISABLED_BUTTON_STATE if 'key' not in buttonData: buttonData['key'] = getKeyFromAction(shortcut.action) resultingDiffList.append(buttonData) def __populateWithAllyData(self, replyAction, replyState, resultingDiffList, targetID): buttonDataTemplate = ALLY_UPPER_SHORTCUTS_DEFAULT chatCommands = self.sessionProvider.shared.chatCommands if chatCommands is not None and chatCommands.isTargetAllyCommittedToMe( targetID): buttonDataTemplate = ALLY_UPPER_SHORTCUTS_ONE_DISABLED if (replyState == ReplyState.CAN_CONFIRM or replyState == ReplyState.CAN_RESPOND ) and replyAction != BATTLE_CHAT_COMMAND_NAMES.RELOADINGGUN: buttonDataTemplate = ALLY_UPPER_SHORTCUTS_THREE_DISABLED for shortcut in buttonDataTemplate: buttonData = defaultdict() RadialMenu.__copyShortcutData(buttonData=buttonData, shortcut=shortcut) if shortcut.indexInGroup == RADIAL_MENU_CONSTS.ELEMENT_INDEX_FIRST: canReplyAction = BATTLE_CHAT_COMMAND_NAMES.SUPPORTING_ALLY if replyAction in ( BATTLE_CHAT_COMMAND_NAMES.HELPME, BATTLE_CHAT_COMMAND_NAMES.SOS ) else BATTLE_CHAT_COMMAND_NAMES.REPLY if replyState == ReplyState.CAN_RESPOND and replyAction is BATTLE_CHAT_COMMAND_NAMES.SUPPORTING_ALLY: defaultShortcut = _THANKS_SHORTCUT else: defaultShortcut = self.__adjustPrimaryRadialButton( replyState, shortcut, canReplyAction) RadialMenu.__copyShortcutData(buttonData=buttonData, shortcut=defaultShortcut) if replyState in (ReplyState.CAN_CANCEL_REPLY, ReplyState.CAN_REPLY, ReplyState.CAN_CONFIRM): buttonData['key'] = getKeyFromAction( BATTLE_CHAT_COMMAND_NAMES.REPLY) elif shortcut.indexInGroup == RADIAL_MENU_CONSTS.ELEMENT_INDEX_SIXTH and replyState in ( ReplyState.CAN_REPLY, ReplyState.CAN_CANCEL_REPLY, ReplyState.CAN_CONFIRM): self.__unbindKeyShortcutFromButton(buttonData) if 'key' not in buttonData: buttonData['key'] = getKeyFromAction(shortcut.action) resultingDiffList.append(buttonData) return def __adjustPrimaryRadialButton(self, replyState, shortcut, canReplyAction): if replyState == ReplyState.CAN_REPLY: defaultShortcut = Shortcut( title=shortcut.title, action=canReplyAction, icon=shortcut.icon, groups=RADIAL_MENU_CONSTS.ALL_TARGET_STATES, bState=RADIAL_MENU_CONSTS.NORMAL_BUTTON_STATE, indexInGroup=shortcut.indexInGroup) elif replyState == ReplyState.CAN_CANCEL_REPLY: defaultShortcut = _CAN_CANCEL_REPLY_SHORTCUT elif replyState == ReplyState.CAN_CONFIRM: defaultShortcut = _CONFIRM_SHORTCUT else: defaultShortcut = shortcut return defaultShortcut def __handleSpgView(self, resultingDiffList, targetState): if self.sessionProvider.getArenaDP().getVehicleInfo().isSPG( ) and avatar_getter.getInputHandler().ctrlModeName in ( CTRL_MODE_NAME.STRATEGIC, CTRL_MODE_NAME.ARTY, CTRL_MODE_NAME.MAP_CASE) and targetState in ( RADIAL_MENU_CONSTS.TARGET_STATE_SPG_DEFAULT, RADIAL_MENU_CONSTS.TARGET_STATE_SPG_ENEMY ) and not resultingDiffList: for shortcut in UPPER_SHORTCUT_SETS[targetState]: buttonData = defaultdict() RadialMenu.__copyShortcutData(buttonData, shortcut) if shortcut.indexInGroup == RADIAL_MENU_CONSTS.ELEMENT_INDEX_SIXTH: self.__unbindKeyShortcutFromButton(buttonData) elif shortcut.indexInGroup == RADIAL_MENU_CONSTS.ELEMENT_INDEX_FIRST: buttonData['key'] = getKeyFromAction( BATTLE_CHAT_COMMAND_NAMES.REPLY) resultingDiffList.append(buttonData) def __disableButton(self, buttonData): buttonData['bState'] = RADIAL_MENU_CONSTS.DISABLED_BUTTON_STATE self.__unbindKeyShortcutFromButton(buttonData) def __unbindKeyShortcutFromButton(self, buttonData): buttonData['key'] = BW_TO_SCALEFORM[Keys.KEY_NONE] @staticmethod def __copyShortcutData(buttonData, shortcut): buttonData['title'] = shortcut.title buttonData['action'] = shortcut.action buttonData['icon'] = shortcut.icon buttonData['bState'] = shortcut.bState buttonData['indexInGroup'] = shortcut.indexInGroup def __onReplyFeedbackReceived(self, uniqueTargetID, replierID, markerType, oldReplyCount, newReplyCount): if oldReplyCount != 0 and newReplyCount != 0: return self.__reshow(uniqueTargetID, markerType, True) def __onRemoveCommandReceived(self, removedID, markerType): self.__reshow(removedID, markerType, markerType != MarkerType.LOCATION_MARKER_TYPE) def __reshow(self, removedID, markerType, reshowPreviousState): if self.__isVisible is False: return else: if self.__crosshairData is not None: if self.__crosshairData.targetID == removedID and markerType == self.__crosshairData.targetMarkerType: self.hide(allowAction=False) self.show(reshowPreviousState) return def __onVehicleMarkerRemoved(self, vehicleID): if self.__crosshairData is not None and self.__crosshairData.targetID == vehicleID: self.delayCallback(self._REFRESH_TIME_IN_SECONDS, self.__reshow, vehicleID, MarkerType.VEHICLE_MARKER_TYPE, False) return def __onVehicleFeedbackReceived(self, eventID, vehicleID, value): if eventID == FEEDBACK_EVENT_ID.VEHICLE_DEAD and self.__crosshairData is not None and self.__crosshairData.targetID == vehicleID: self.delayCallback(self._REFRESH_TIME_IN_SECONDS, self.__reshow, vehicleID, MarkerType.VEHICLE_MARKER_TYPE, False) return def __onAddCommandReceived(self, addedID, markerType): if markerType != MarkerType.LOCATION_MARKER_TYPE: self.__reshow(addedID, markerType, True) def __checkForValidLocationMarkerLoop(self): if self.__crosshairData is None or self.__isVisible is False: return else: chatCommands = self.sessionProvider.shared.chatCommands _, targetMarkerType, targetMarkerSubtype, _, _ = chatCommands.getAimedAtTargetData( ) if RadialMenu.__isMarkerEmptyLocationOrOutOfBorder( targetMarkerType, targetMarkerSubtype ) and targetMarkerType != self.__crosshairData.targetMarkerType: self.hide(allowAction=False) self.show(reshowPreviousState=False) hasDelayedCallback = self.hasDelayedCallback( self.__checkForValidLocationMarkerLoop) return self._REFRESH_TIME_IN_SECONDS if not hasDelayedCallback else None def __checkForTemporaryRespondUpdateLoop(self): if self.__crosshairData is None or self.__isVisible is False: return else: chatCommands = self.sessionProvider.shared.chatCommands _, targetMarkerType, targetMarkerSubtype, replyState, _ = chatCommands.getAimedAtTargetData( ) if RadialMenu.__isCanRespondToAlly(targetMarkerType, targetMarkerSubtype, replyState): return self._REFRESH_TIME_IN_SECONDS self.hide(allowAction=False) self.show(reshowPreviousState=False) return -1 @staticmethod def __isMarkerEmptyLocationOrOutOfBorder(markerType, markerSubType): return markerType in (MarkerType.INVALID_MARKER_TYPE, MarkerType.LOCATION_MARKER_TYPE ) and markerSubType == INVALID_MARKER_SUBTYPE @staticmethod def __isCanRespondToAlly(markerType, markerSubType, replyState): return replyState == ReplyState.CAN_RESPOND and markerType == MarkerType.VEHICLE_MARKER_TYPE and markerSubType == DefaultMarkerSubType.ALLY_MARKER_SUBTYPE
class StrategicCamera(CameraWithSettings, CallbackDelayer): _DYNAMIC_ENABLED = True ABSOLUTE_VERTICAL_FOV = math.radians(60.0) _SMOOTHING_PIVOT_DELTA_FACTOR = 6.0 @staticmethod def enableDynamicCamera(enable): StrategicCamera._DYNAMIC_ENABLED = enable @staticmethod def isCameraDynamic(): return StrategicCamera._DYNAMIC_ENABLED camera = property(lambda self: self.__cam) aimingSystem = property(lambda self: self.__aimingSystem) __aimOffset = aih_global_binding.bindRW(aih_global_binding.BINDING_ID.AIM_OFFSET) def __init__(self, dataSec): super(StrategicCamera, self).__init__() CallbackDelayer.__init__(self) self.__positionOscillator = None self.__positionNoiseOscillator = None self.__activeDistRangeSettings = None self.__dynamicCfg = CameraDynamicConfig() self.__cameraYaw = 0.0 self.__readCfg(dataSec) self.__cam = BigWorld.CursorCamera() self.__cam.isHangar = False self.__curSense = self._cfg['sensitivity'] self.__onChangeControlMode = None self.__aimingSystem = None self.__prevTime = BigWorld.time() self.__autoUpdatePosition = False self.__dxdydz = Vector3(0, 0, 0) self.__needReset = 0 self.__smoothingPivotDelta = 0 return def create(self, onChangeControlMode=None): super(StrategicCamera, self).create() self.__onChangeControlMode = onChangeControlMode self.__camDist = self._cfg['camDist'] self.__cam.pivotMaxDist = 0.0 self.__cam.maxDistHalfLife = 0.01 self.__cam.movementHalfLife = 0.0 self.__cam.turningHalfLife = -1 self.__cam.pivotPosition = Math.Vector3(0.0, self.__camDist, 0.0) aimingSystemClass = StrategicAimingSystemRemote if BigWorld.player().isObserver() else StrategicAimingSystem self.__aimingSystem = aimingSystemClass(self._cfg['distRange'][0], self.__cameraYaw) def destroy(self): self.disable() self.__onChangeControlMode = None self.__cam = None if self.__aimingSystem is not None: self.__aimingSystem.destroy() self.__aimingSystem = None CallbackDelayer.destroy(self) CameraWithSettings.destroy(self) return def enable(self, targetPos, saveDist): self.__prevTime = BigWorld.time() self.__aimingSystem.enable(targetPos) self.__activeDistRangeSettings = self.__getActiveDistRangeForArena() if self.__activeDistRangeSettings is not None: self.__aimingSystem.height = self.__getDistRange()[0] srcMat = math_utils.createRotationMatrix((self.__cameraYaw, -math.pi * 0.499, 0.0)) self.__cam.source = srcMat if not saveDist: self.__updateCamDistCfg() self.__camDist = self._cfg['camDist'] self.__cam.pivotPosition = Math.Vector3(0.0, self.__camDist, 0.0) camTarget = Math.MatrixProduct() camTarget.b = self.__aimingSystem.matrix self.__cam.target = camTarget self.__cam.forceUpdate() BigWorld.camera(self.__cam) BigWorld.player().positionControl.moveTo(self.__aimingSystem.matrix.translation) BigWorld.player().positionControl.followCamera(True) FovExtended.instance().enabled = False BigWorld.projection().fov = StrategicCamera.ABSOLUTE_VERTICAL_FOV self.__cameraUpdate() self.delayCallback(0.0, self.__cameraUpdate) self.__needReset = 1 return def disable(self): if self.__aimingSystem is not None: self.__aimingSystem.disable() self.stopCallback(self.__cameraUpdate) positionControl = BigWorld.player().positionControl if positionControl is not None: positionControl.followCamera(False) self.__positionOscillator.reset() FovExtended.instance().resetFov() FovExtended.instance().enabled = True return def teleport(self, pos): self.moveToPosition(pos) def setMaxDist(self): distRange = self.__getDistRange() if len(distRange) > 1: self.__camDist = distRange[1] def update(self, dx, dy, dz, updatedByKeyboard=False): self.__curSense = self._cfg['keySensitivity'] if updatedByKeyboard else self._cfg['sensitivity'] standardMaxDist = self._cfg['distRange'][1] if self.__camDist > standardMaxDist: self.__curSense *= 1.0 + (self.__camDist - self._cfg['distRange'][1]) * self.__getCameraAcceleration() self.__autoUpdatePosition = updatedByKeyboard self.__dxdydz = Vector3(dx, dy, dz) def moveToPosition(self, pos): self.__aimingSystem.enable(pos) self.update(0.0, 0.0, 0.0) def calcVisibleAreaRatio(self): points = [Math.Vector2(1, 1), Math.Vector2(1, -1), Math.Vector2(-1, -1), Math.Vector2(-1, 1)] dirsPos = [ getWorldRayAndPoint(point.x, point.y) for point in points ] planeXZ = Plane(Math.Vector3(0, 1, 0), 0) collisionPoints = [] for direction, begPos in dirsPos: endPos = begPos + direction * 1000 testResult = BigWorld.wg_collideSegment(BigWorld.player().spaceID, begPos, endPos, 3) collPoint = Math.Vector3(0, 0, 0) if collPoint is not None: collPoint = testResult.closestPoint else: collPoint = planeXZ.intersectSegment(begPos, endPos) collisionPoints.append(collPoint) x0 = abs(collisionPoints[1].x - collisionPoints[2].x) x1 = abs(collisionPoints[0].x - collisionPoints[3].x) z0 = abs(collisionPoints[0].z - collisionPoints[1].z) z1 = abs(collisionPoints[3].z - collisionPoints[2].z) bb = BigWorld.player().arena.arenaType.boundingBox arenaBottomLeft = bb[0] arenaUpperRight = bb[1] arenaX = arenaUpperRight[0] - arenaBottomLeft[0] arenaZ = arenaUpperRight[1] - arenaBottomLeft[1] return ((x0 + x1) * 0.5 / arenaX, (z0 + z1) * 0.5 / arenaZ) def applyImpulse(self, position, impulse, reason=ImpulseReason.ME_HIT): adjustedImpulse, noiseMagnitude = self.__dynamicCfg.adjustImpulse(impulse, reason) impulseFlatDir = Vector3(adjustedImpulse.x, 0, adjustedImpulse.z) impulseFlatDir.normalise() cameraYawMat = math_utils.createRotationMatrix(Vector3(-self.__cameraYaw, 0.0, 0.0)) impulseLocal = cameraYawMat.applyVector(impulseFlatDir * (-1 * adjustedImpulse.length)) self.__positionOscillator.applyImpulse(impulseLocal) self.__applyNoiseImpulse(noiseMagnitude) def applyDistantImpulse(self, position, impulseValue, reason=ImpulseReason.ME_HIT): if reason != ImpulseReason.SPLASH and reason != ImpulseReason.PROJECTILE_HIT: return impulse = BigWorld.player().getOwnVehiclePosition() - position distance = impulse.length if distance <= 1.0: distance = 1.0 impulse.normalise() if reason == ImpulseReason.PROJECTILE_HIT: if not cameras.isPointOnScreen(position): return distance = 1.0 impulse *= impulseValue / distance self.applyImpulse(position, impulse, reason) def __applyNoiseImpulse(self, noiseMagnitude): noiseImpulse = math_utils.RandomVectors.random3Flat(noiseMagnitude) self.__positionNoiseOscillator.applyImpulse(noiseImpulse) def writeUserPreferences(self): ds = Settings.g_instance.userPrefs if not ds.has_key(Settings.KEY_CONTROL_MODE): ds.write(Settings.KEY_CONTROL_MODE, '') ucfg = self._userCfg ds = ds[Settings.KEY_CONTROL_MODE] ds.writeFloat('strategicMode/camera/keySensitivity', ucfg['keySensitivity']) ds.writeFloat('strategicMode/camera/sensitivity', ucfg['sensitivity']) ds.writeFloat('strategicMode/camera/scrollSensitivity', ucfg['scrollSensitivity']) ds.writeFloat('strategicMode/camera/camDist', self._cfg['camDist']) def __cameraUpdate(self): replayCtrl = BattleReplay.g_replayCtrl if replayCtrl.isPlaying and replayCtrl.isControllingCamera: aimOffset = replayCtrl.getAimClipPosition() else: aimOffset = self.__calcAimOffset() if replayCtrl.isRecording: replayCtrl.setAimClipPosition(aimOffset) self.__aimOffset = aimOffset shotDescr = BigWorld.player().getVehicleDescriptor().shot BigWorld.wg_trajectory_drawer().setParams(shotDescr.maxDistance, Math.Vector3(0, -shotDescr.gravity, 0), aimOffset) curTime = BigWorld.time() deltaTime = curTime - self.__prevTime self.__prevTime = curTime self.__aimingSystem.update(deltaTime) if replayCtrl.isPlaying: if self.__needReset != 0: if self.__needReset > 1: from helpers import isPlayerAvatar player = BigWorld.player() if isPlayerAvatar(): if player.inputHandler.ctrl is not None: player.inputHandler.ctrl.resetGunMarkers() self.__needReset = 0 else: self.__needReset += 1 if replayCtrl.isControllingCamera: self.__aimingSystem.updateTargetPos(replayCtrl.getGunRotatorTargetPoint()) else: self.__aimingSystem.handleMovement(self.__dxdydz.x * self.__curSense, -self.__dxdydz.y * self.__curSense) if self.__dxdydz.x != 0 or self.__dxdydz.y != 0 or self.__dxdydz.z != 0: self.__needReset = 2 else: self.__aimingSystem.handleMovement(self.__dxdydz.x * self.__curSense, -self.__dxdydz.y * self.__curSense) distRange = self.__getDistRange() self.__calcSmoothingPivotDelta(deltaTime) self.__camDist -= self.__dxdydz.z * float(self.__curSense) self.__camDist = self.__aimingSystem.overrideCamDist(self.__camDist) distRange = self.__getDistRange() maxPivotHeight = distRange[1] - distRange[0] self.__camDist = math_utils.clamp(0, maxPivotHeight, self.__camDist) self._cfg['camDist'] = self.__camDist camDistWithSmoothing = self.__camDist + self.__smoothingPivotDelta - self.__aimingSystem.heightFromPlane self.__cam.pivotPosition = Math.Vector3(0.0, camDistWithSmoothing, 0.0) if self.__dxdydz.z != 0 and self.__onChangeControlMode is not None and math_utils.almostZero(self.__camDist - maxPivotHeight): self.__onChangeControlMode() self.__updateOscillator(deltaTime) if not self.__autoUpdatePosition: self.__dxdydz = Vector3(0, 0, 0) return 0.0 def __calcSmoothingPivotDelta(self, deltaTime): heightsDy = self.__aimingSystem.heightFromPlane - self.__smoothingPivotDelta smoothFactor = math_utils.clamp(0, 1, StrategicCamera._SMOOTHING_PIVOT_DELTA_FACTOR * deltaTime) smoothingPivotDeltaDy = smoothFactor * heightsDy self.__smoothingPivotDelta += smoothingPivotDeltaDy def __calcAimOffset(self): aimWorldPos = self.__aimingSystem.matrix.applyPoint(Vector3(0, -self.__aimingSystem.height, 0)) aimOffset = cameras.projectPoint(aimWorldPos) return Vector2(math_utils.clamp(-0.95, 0.95, aimOffset.x), math_utils.clamp(-0.95, 0.95, aimOffset.y)) def __updateOscillator(self, deltaTime): if StrategicCamera.isCameraDynamic(): self.__positionOscillator.update(deltaTime) self.__positionNoiseOscillator.update(deltaTime) else: self.__positionOscillator.reset() self.__positionNoiseOscillator.reset() self.__cam.target.a = math_utils.createTranslationMatrix(self.__positionOscillator.deviation + self.__positionNoiseOscillator.deviation) def reload(self): if not constants.IS_DEVELOPMENT: return import ResMgr ResMgr.purge('gui/avatar_input_handler.xml') cameraSec = ResMgr.openSection('gui/avatar_input_handler.xml/strategicMode/camera/') self.__readCfg(cameraSec) def __readCfg(self, dataSec): if not dataSec or dataSec['strategic']: LOG_WARNING('Invalid section <strategicMode/camera> in avatar_input_handler.xml') self._baseCfg = dict() bcfg = self._baseCfg bcfg['keySensitivity'] = readFloat(dataSec, 'keySensitivity', 0.005, 10, 0.025) bcfg['sensitivity'] = readFloat(dataSec, 'sensitivity', 0.005, 10, 0.025) bcfg['scrollSensitivity'] = readFloat(dataSec, 'scrollSensitivity', 0.005, 10, 0.025) bcfg['distRange'] = readVec2(dataSec, 'distRange', (1, 1), (10000, 10000), (2, 30)) bcfg['distRangeForArenaSize'] = self.__readDynamicDistRangeData(dataSec) ds = Settings.g_instance.userPrefs[Settings.KEY_CONTROL_MODE] if ds is not None: ds = ds['strategicMode/camera'] self._userCfg = dict() ucfg = self._userCfg ucfg['horzInvert'] = False ucfg['vertInvert'] = False ucfg['keySensitivity'] = readFloat(ds, 'keySensitivity', 0.0, 10.0, 1.0) ucfg['sensitivity'] = readFloat(ds, 'sensitivity', 0.0, 10.0, 1.0) ucfg['scrollSensitivity'] = readFloat(ds, 'scrollSensitivity', 0.0, 10.0, 1.0) ucfg['camDist'] = readFloat(ds, 'camDist', 0.0, 60.0, 0) self._cfg = dict() cfg = self._cfg cfg['keySensitivity'] = bcfg['keySensitivity'] cfg['sensitivity'] = bcfg['sensitivity'] cfg['scrollSensitivity'] = bcfg['scrollSensitivity'] cfg['distRange'] = bcfg['distRange'] cfg['distRangeForArenaSize'] = bcfg['distRangeForArenaSize'] cfg['camDist'] = ucfg['camDist'] cfg['keySensitivity'] *= ucfg['keySensitivity'] cfg['sensitivity'] *= ucfg['sensitivity'] cfg['scrollSensitivity'] *= ucfg['scrollSensitivity'] cfg['horzInvert'] = ucfg['horzInvert'] cfg['vertInvert'] = ucfg['vertInvert'] dynamicsSection = dataSec['dynamics'] self.__dynamicCfg.readImpulsesConfig(dynamicsSection) self.__positionOscillator = createOscillatorFromSection(dynamicsSection['oscillator'], False) self.__positionNoiseOscillator = createOscillatorFromSection(dynamicsSection['randomNoiseOscillatorFlat'], False) return def __readDynamicDistRangeData(self, dataSec): section = dataSec['dynamicDistRanges'] dynamicDistRanges = [] if section is None: return dynamicDistRanges else: value = section['dynamicDistRange'] minArenaSize = readFloat(dataSec, 'minArenaSize', 0.1, 2000, 2000.0) distRange = readVec2(value, 'distRangeOverride', 0.1, 100, (40, 300)) acceleration = readFloat(dataSec, 'acceleration', 0.1, 100, 0.1) dynamicDistRanges.append(_DistRangeSetting(minArenaSize, distRange, acceleration)) return dynamicDistRanges def __getActiveDistRangeForArena(self): bb = BigWorld.player().arena.arenaType.boundingBox arenaBottomLeft = bb[0] arenaUpperRight = bb[1] arenaX = arenaUpperRight[0] - arenaBottomLeft[0] arenaZ = arenaUpperRight[1] - arenaBottomLeft[1] arenaSize = min(arenaX, arenaZ) availableDistRanges = self._cfg['distRangeForArenaSize'] currentDistRange = None choosenArenaMinSize = 0 for pt in availableDistRanges: if arenaSize >= pt.minArenaSize and pt.minArenaSize > choosenArenaMinSize: choosenArenaMinSize = pt.minArenaSize currentDistRange = pt return currentDistRange def __getDistRange(self): return self._cfg['distRange'] if not self.__activeDistRangeSettings else self.__activeDistRangeSettings.distRange def __getCameraAcceleration(self): return 0 if not self.__activeDistRangeSettings else self.__activeDistRangeSettings.acceleration def __updateCamDistCfg(self): ds = Settings.g_instance.userPrefs[Settings.KEY_CONTROL_MODE] if ds is not None: ds = ds['strategicMode/camera'] distRange = self.__getDistRange() self._cfg['camDist'] = self._userCfg['camDist'] = readFloat(ds, 'camDist', 0, distRange[1] - distRange[0], 0) return
class ChatCommandsController(IBattleController): __slots__ = ('__isEnabled', '__arenaDP', '__feedback', '__ammo', '__markersManager') sessionProvider = dependency.descriptor(IBattleSessionProvider) settingsCore = dependency.descriptor(ISettingsCore) _aimOffset = aih_global_binding.bindRW( aih_global_binding.BINDING_ID.AIM_OFFSET) def __init__(self, setup, feedback, ammo): super(ChatCommandsController, self).__init__() self.__arenaDP = weakref.proxy(setup.arenaDP) self.__feedback = weakref.proxy(feedback) self.__ammo = weakref.proxy(ammo) self.__markersManager = None self.__isEnabled = False return @proto_getter(PROTO_TYPE.BW_CHAT2) def proto(self): return None def getControllerID(self): return BATTLE_CTRL_ID.CHAT_COMMANDS def startControl(self): self.settingsCore.onSettingsChanged += self.__onSettingsChanged g_eventBus.addListener(events.MarkersManagerEvent.MARKERS_CREATED, self.__onMarkersManagerMarkersCreated, EVENT_BUS_SCOPE.BATTLE) def stopControl(self): self.__arenaDP = None self.__feedback = None self.__ammo = None self.__markersManager = None self.settingsCore.onSettingsChanged -= self.__onSettingsChanged g_eventBus.removeListener(events.MarkersManagerEvent.MARKERS_CREATED, self.__onMarkersManagerMarkersCreated, EVENT_BUS_SCOPE.BATTLE) return def getAimedAtTargetData(self): advChatCmp = getattr( self.sessionProvider.arenaVisitor.getComponentSystem(), 'advancedChatComponent', None) if advChatCmp is None: return else: targetID, targetMarkerType, markerSubType = self.__getAimedAtVehicleOrObject( ) if targetMarkerType == MarkerType.INVALID_MARKER_TYPE: targetID, targetMarkerType, markerSubType = self.__markersManager.getCurrentlyAimedAtMarkerIDAndType( ) if targetMarkerType == MarkerType.INVALID_MARKER_TYPE and getAimedAtPositionWithinBorders( self._aimOffset[0], self._aimOffset[1]) is not None: targetMarkerType = MarkerType.LOCATION_MARKER_TYPE markerSubType = INVALID_MARKER_SUBTYPE replyState, replyToAction = self.getReplyStateForTargetIDAndMarkerType( targetID, targetMarkerType) return (targetID, targetMarkerType, markerSubType, replyState, replyToAction) def getReplyStateForTargetIDAndMarkerType(self, targetID, targetMarkerType): advChatCmp = getattr( self.sessionProvider.arenaVisitor.getComponentSystem(), 'advancedChatComponent', None) return None if advChatCmp is None else advChatCmp.getReplyStateForTargetIDAndMarkerType( targetID, targetMarkerType) def isTargetAllyCommittedToMe(self, targetID): advChatCmp = getattr( self.sessionProvider.arenaVisitor.getComponentSystem(), 'advancedChatComponent', None) return False if advChatCmp is None else advChatCmp.isTargetAllyCommittedToMe( targetID) def handleContexChatCommand(self, key): cmdMap = CommandMapping.g_instance advChatCmp = getattr( self.sessionProvider.arenaVisitor.getComponentSystem(), 'advancedChatComponent', None) if advChatCmp is None: return else: for chatCmd, keyboardCmd in KB_MAPPING.iteritems(): if cmdMap.isFired(keyboardCmd, key): if chatCmd in DIRECT_ACTION_BATTLE_CHAT_COMMANDS: self.handleChatCommand(chatCmd) return targetID, targetMarkerType, markerSubType, replyState, replyToAction = self.getAimedAtTargetData( ) if chatCmd in (BATTLE_CHAT_COMMAND_NAMES.THANKS, BATTLE_CHAT_COMMAND_NAMES.TURNBACK): replyState = ReplyState.NO_REPLY if replyState == ReplyState.NO_REPLY: if chatCmd in TARGET_TYPE_TRANSLATION_MAPPING and targetMarkerType in TARGET_TYPE_TRANSLATION_MAPPING[ chatCmd] and markerSubType in TARGET_TYPE_TRANSLATION_MAPPING[ chatCmd][targetMarkerType]: action = TARGET_TYPE_TRANSLATION_MAPPING[chatCmd][ targetMarkerType][markerSubType] else: _logger.debug( 'Action %s (at key %s) is not supported for target type %s (cut type: %s)', chatCmd, key, targetMarkerType, markerSubType) return if action == BATTLE_CHAT_COMMAND_NAMES.HELPME: if advChatCmp.isTargetAllyCommittedToMe(targetID): action = BATTLE_CHAT_COMMAND_NAMES.THANKS self.handleChatCommand(action, targetID=targetID) else: if chatCmd == CONTEXTCOMMAND2 and replyState != ReplyState.CAN_REPLY: return if replyState == ReplyState.CAN_CANCEL_REPLY: self.sendCancelReplyChatCommand( targetID, replyToAction) elif replyState == ReplyState.CAN_REPLY: if replyToAction in ( BATTLE_CHAT_COMMAND_NAMES.HELPME, BATTLE_CHAT_COMMAND_NAMES.SOS): self.handleChatCommand( BATTLE_CHAT_COMMAND_NAMES.SUPPORTING_ALLY, targetID=targetID) else: self.sendReplyChatCommand( targetID, replyToAction) elif replyState == ReplyState.CAN_CONFIRM and replyToAction in ONE_SHOT_COMMANDS_TO_REPLIES.keys( ): self.handleChatCommand( ONE_SHOT_COMMANDS_TO_REPLIES[replyToAction], targetID=targetID) elif replyState == ReplyState.CAN_RESPOND and replyToAction in COMMAND_RESPONDING_MAPPING.keys( ): self.handleChatCommand( COMMAND_RESPONDING_MAPPING[replyToAction], targetID=targetID) return def sendAdvancedPositionPing(self, action, isInRadialMenu=False): aimedAtPosition = getAimedAtPositionWithinBorders( self._aimOffset[0], self._aimOffset[1]) if aimedAtPosition is not None: if self.__isSPG( ) and action == BATTLE_CHAT_COMMAND_NAMES.GOING_THERE or self.__isSPGAndInStrategicOrArtyMode( ) and isInRadialMenu is False and action == BATTLE_CHAT_COMMAND_NAMES.ATTENTION_TO_POSITION: action = BATTLE_CHAT_COMMAND_NAMES.SPG_AIM_AREA self.sendAttentionToPosition3D(position=aimedAtPosition, name=action) return def sendReplyChatCommand(self, targetID, action): if not avatar_getter.isVehicleAlive() or self.sessionProvider.getCtx( ).isPlayerObserver() or self.__isEnabled is False: return player = BigWorld.player() command = self.proto.battleCmd.createReplyByName( targetID, action, player.id) if command: self.__sendChatCommand(command) else: _logger.error('Reply command not valid for command id: %d', targetID) def sendCancelReplyChatCommand(self, targetID, action): if not avatar_getter.isVehicleAlive(): return player = BigWorld.player() command = self.proto.battleCmd.createCancelReplyByName( targetID, action, player.id) if command: self.__sendChatCommand(command) else: _logger.error('Cancel reply command not valid for command id: %d', targetID) def sendClearChatCommandsFromTarget(self, targetID, targetMarkerType): command = self.proto.battleCmd.createClearChatCommandsFromTarget( targetID, targetMarkerType) if command: self.__sendChatCommand(command) else: _logger.error( 'Clear chat commands not valid for command id: %d and marker type: %s', targetID, targetMarkerType) def handleChatCommand(self, action, targetID=None, isInRadialMenu=False): player = BigWorld.player() targetType = _COMMAND_NAME_TRANSFORM_MARKER_TYPE[action] if targetType == MarkerType.HEADQUARTER_MARKER_TYPE: self.sendAttentionToObjective( targetID, player.team == EPIC_BATTLE_TEAM_ID.TEAM_ATTACKER, action) elif targetType == MarkerType.BASE_MARKER_TYPE: self.sendCommandToBase(targetID, action) elif action in LOCATION_CMD_NAMES: self.sendAdvancedPositionPing(action, isInRadialMenu) elif action in TARGETED_VEHICLE_CMD_NAMES: self.sendTargetedCommand(action, targetID, isInRadialMenu) elif action in EPIC_GLOBAL_CMD_NAMES: self.sendEpicGlobalCommand(action) elif action == BATTLE_CHAT_COMMAND_NAMES.RELOADINGGUN: self.sendReloadingCommand() else: self.sendCommand(action) def sendAttentionToPosition3D(self, position, name): if self.__isProhibitedToSendIfDeadOrObserver( name) or self.__isEnabled is False: return positionVec = Math.Vector3(position[0], position[1], position[2]) if name == BATTLE_CHAT_COMMAND_NAMES.SPG_AIM_AREA and self.__isSPG(): time = self.__getReloadTime( ) if self.__ammo.getAllShellsQuantityLeft() > 0 else -1 command = self.proto.battleCmd.createByPosition( positionVec, name, time) else: command = self.proto.battleCmd.createByPosition(positionVec, name) if command: self.__sendChatCommand(command) else: _logger.error( 'Minimap command for position (%d, %d, %d) not found', positionVec.x, positionVec.y, positionVec.z) def sendAttentionToObjective(self, hqIdx, isAtk, action=None): if self.__isEnabled is False: return else: if action is None: action = BATTLE_CHAT_COMMAND_NAMES.ATTACK_OBJECTIVE if not isAtk: action = BATTLE_CHAT_COMMAND_NAMES.DEFEND_OBJECTIVE command = self.proto.battleCmd.createByObjectiveIndex( hqIdx, isAtk, action) if command: self.__sendChatCommand(command) else: _logger.error( 'Minimap command for objective with id: %d not found', hqIdx) return def sendCommandToBase(self, baseIdx, cmdName, baseName=''): if self.__isProhibitedToSendIfDeadOrObserver( cmdName) or self.__isEnabled is False: return if self.sessionProvider.arenaVisitor.gui.isInEpicRange(): baseName = ID_TO_BASENAME[baseIdx] command = self.proto.battleCmd.createByBaseIndexAndName( baseIdx, cmdName, baseName) if command: self.__sendChatCommand(command) else: _logger.error('Command not found: %s', cmdName) def sendCommand(self, cmdName): if self.__isProhibitedToSendIfDeadOrObserver( cmdName) or self.__isEnabled is False: return command = self.proto.battleCmd.createByName(cmdName) if command: self.__sendChatCommand(command) else: _logger.error('Command not found: %s', cmdName) def sendEpicGlobalCommand(self, cmdName, baseName=''): if self.__isProhibitedToSendIfDeadOrObserver( cmdName) or self.__isEnabled is False: return command = self.proto.battleCmd.createByGlobalMsgName(cmdName, baseName) if command: self.__sendChatCommand(command) else: _logger.error('Command not found: %s', cmdName) def sendTargetedCommand(self, cmdName, targetID, isInRadialMenu=False): if self.__isProhibitedToSendIfDeadOrObserver( cmdName ) or self.__isEnabled is False or not self.__arenaDP.getVehicleInfo( targetID).isAlive(): return if self.__isSPG( ) and cmdName == BATTLE_CHAT_COMMAND_NAMES.ATTACKING_ENEMY or self.__isSPGAndInStrategicOrArtyMode( ) and cmdName == BATTLE_CHAT_COMMAND_NAMES.ATTACK_ENEMY and isInRadialMenu is False: time = self.__getReloadTime( ) if self.__ammo.getShellsQuantityLeft() > 0 else -1 command = self.proto.battleCmd.createSPGAimTargetCommand( targetID, time) else: command = self.proto.battleCmd.createByNameTarget( cmdName, targetID) if command: self.__sendChatCommand(command) else: _logger.error( 'Targeted command(%s) was not found or targetID(%d) is not valid', cmdName, targetID) def sendReloadingCommand(self): if not avatar_getter.isPlayerOnArena() or self.__isEnabled is False: return state = self.__ammo.getGunReloadingState() command = self.proto.battleCmd.create4Reload( self.__ammo.getGunSettings().isCassetteClip(), math.ceil(state.getTimeLeft()), self.__ammo.getShellsQuantityLeft()) if command: self.__sendChatCommand(command) else: _logger.error('Can not create reloading command') def __isProhibitedToSendIfDeadOrObserver(self, name): return not avatar_getter.isVehicleAlive( ) and name in PROHIBITED_IF_DEAD or self.sessionProvider.getCtx( ).isPlayerObserver() and name in PROHIBITED_IF_SPECTATOR def __isSPG(self): return self.sessionProvider.getArenaDP().getVehicleInfo().isSPG() def __isSPGAndInStrategicOrArtyMode(self): return self.__isSPG() and avatar_getter.getInputHandler( ).ctrlModeName in (CTRL_MODE_NAME.STRATEGIC, CTRL_MODE_NAME.ARTY, CTRL_MODE_NAME.MAP_CASE) def __getReloadTime(self): reloadState = self.__ammo.getGunReloadingState() reloadTime = reloadState.getTimeLeft() return reloadTime def __sendChatCommand(self, command): controller = MessengerEntry.g_instance.gui.channelsCtrl.getController( command.getClientID()) if controller: controller.sendCommand(command) def __onSettingsChanged(self, diff): enableBattleCommunication = diff.get( BattleCommStorageKeys.ENABLE_BATTLE_COMMUNICATION) if enableBattleCommunication is None: return else: self.__isEnabled = enableBattleCommunication return def __getAimedAtVehicleOrObject(self): player = BigWorld.player() target = BigWorld.target() targetID = -1 markerSubType = INVALID_MARKER_SUBTYPE targetMarkerType = MarkerType.INVALID_MARKER_TYPE if self.__isTargetCorrect(player, target): if isinstance(target, DestructibleEntity.DestructibleEntity): targetID = target.destructibleEntityID targetMarkerType = MarkerType.HEADQUARTER_MARKER_TYPE markerSubType = DefaultMarkerSubType.ENEMY_MARKER_SUBTYPE if avatar_getter.getPlayerTeam( ) == EPIC_BATTLE_TEAM_ID.TEAM_ATTACKER else DefaultMarkerSubType.ALLY_MARKER_SUBTYPE elif target.publicInfo['team'] == avatar_getter.getPlayerTeam(): targetID = target.id targetMarkerType = MarkerType.VEHICLE_MARKER_TYPE markerSubType = DefaultMarkerSubType.ALLY_MARKER_SUBTYPE else: targetID = target.id targetMarkerType = MarkerType.VEHICLE_MARKER_TYPE markerSubType = DefaultMarkerSubType.ENEMY_MARKER_SUBTYPE return (targetID, targetMarkerType, markerSubType) def __isTargetCorrect(self, player, target): if target is not None and isinstance( target, DestructibleEntity.DestructibleEntity): if target.isAlive(): if player is not None and isPlayerAvatar(): return True if target is not None and isinstance(target, Vehicle.Vehicle): if target.isAlive(): if player is not None and isPlayerAvatar(): vInfo = self.__arenaDP.getVehicleInfo(target.id) isAlly = target.publicInfo['team'] == player.team return not vInfo.isChatCommandsDisabled(isAlly) return False def __onMarkersManagerMarkersCreated(self, event): self.__markersManager = event.getMarkersManager() self.__isEnabled = bool( self.settingsCore.getSetting( BattleCommStorageKeys.ENABLE_BATTLE_COMMUNICATION))
CLIENT_SPG_GUN_MARKER_FOCUS_DATA_PROVIDER: lambda: _Observable(None), SERVER_GUN_MARKER_FOCUS_DATA_PROVIDER: lambda: _Observable(None), SERVER_SPG_GUN_MARKER_FOCUS_DATA_PROVIDER: lambda: _Observable(None) }) # crosshair_proxy _GUN_MARKERS_SET_IDS += (CLIENT_GUN_MARKER_FOCUS_DATA_PROVIDER, CLIENT_SPG_GUN_MARKER_FOCUS_DATA_PROVIDER, SERVER_GUN_MARKER_FOCUS_DATA_PROVIDER, SERVER_SPG_GUN_MARKER_FOCUS_DATA_PROVIDER) # gun_marker_ctrl _GunMarkersDPFactory._clientFocusDataProvider = aih_global_binding.bindRW( CLIENT_GUN_MARKER_FOCUS_DATA_PROVIDER) _GunMarkersDPFactory._serverFocusDataProvider = aih_global_binding.bindRW( SERVER_GUN_MARKER_FOCUS_DATA_PROVIDER) _GunMarkersDPFactory._clientSPGFocusDataProvider = aih_global_binding.bindRW( CLIENT_SPG_GUN_MARKER_FOCUS_DATA_PROVIDER) _GunMarkersDPFactory._serverSPGFocusDataProvider = aih_global_binding.bindRW( SERVER_SPG_GUN_MARKER_FOCUS_DATA_PROVIDER) # crosshair_proxy GunMarkersSetInfo.clientMarkerFocusDataProvider = aih_global_binding.bindRO( CLIENT_GUN_MARKER_FOCUS_DATA_PROVIDER) GunMarkersSetInfo.clientSPGMarkerFocusDataProvider = aih_global_binding.bindRO( CLIENT_SPG_GUN_MARKER_FOCUS_DATA_PROVIDER) GunMarkersSetInfo.serverMarkerFocusDataProvider = aih_global_binding.bindRO( SERVER_GUN_MARKER_FOCUS_DATA_PROVIDER) GunMarkersSetInfo.serverSPGMarkerFocusDataProvider = aih_global_binding.bindRO(