Пример #1
0
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()
Пример #2
0
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'])
Пример #3
0
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
Пример #4
0
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
Пример #5
0
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
Пример #6
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)
Пример #7
0
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
Пример #8
0
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)
Пример #9
0
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
Пример #11
0
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))
Пример #12
0
    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(