Beispiel #1
0
class BattlePassController(IBattlePassController, EventsHandler):
    __itemsCache = dependency.descriptor(IItemsCache)
    __lobbyContext = dependency.descriptor(ILobbyContext)
    __offersProvider = dependency.descriptor(IOffersDataProvider)

    def __init__(self):
        self.__oldPoints = 0
        self.__oldLevel = 0
        self.__currentMode = None
        self.__eventsManager = EventManager()
        self.__seasonChangeNotifier = SimpleNotifier(self.__getTimeToNotifySeasonChanged, self.__onNotifySeasonChanged)
        self.__extraChapterNotifier = SimpleNotifier(self.__getTimeToExtraChapterExpired, self.__onNotifyExtraChapterExpired)
        self.onPointsUpdated = Event(self.__eventsManager)
        self.onLevelUp = Event(self.__eventsManager)
        self.onBattlePassIsBought = Event(self.__eventsManager)
        self.onSelectTokenUpdated = Event(self.__eventsManager)
        self.onSeasonStateChanged = Event(self.__eventsManager)
        self.onExtraChapterExpired = Event(self.__eventsManager)
        self.onBattlePassSettingsChange = Event(self.__eventsManager)
        self.onFinalRewardStateChange = Event(self.__eventsManager)
        self.onOffersUpdated = Event(self.__eventsManager)
        self.onRewardSelectChange = Event(self.__eventsManager)
        self.onChapterChanged = Event(self.__eventsManager)
        self.__rewardLogic = None
        return

    def init(self):
        super(BattlePassController, self).init()
        g_playerEvents.onClientUpdated += self.__onTokensUpdate
        self.__rewardLogic = BattlePassRewardLogic(BattlePassStateMachine())
        BattlePassAwardsManager.init()

    def onLobbyInited(self, event):
        self._subscribe()
        self.__seasonChangeNotifier.startNotification()
        if any((self.isExtraChapter(chapterID) for chapterID in self.getChapterIDs())):
            self.__extraChapterNotifier.startNotification()
        self.__rewardLogic.start()
        self.onBattlePassSettingsChange(self.__getConfig().mode, self.__currentMode)
        self.__currentMode = self.__getConfig().mode

    def onAvatarBecomePlayer(self):
        self.__stop()

    def onDisconnected(self):
        self.__stop()
        self.__clearFields()
        self.__rewardLogic.stop()

    def fini(self):
        self.__stop()
        self.__rewardLogic.stop()
        self.__clearFields()
        self.__eventsManager.clear()
        g_playerEvents.onClientUpdated -= self.__onTokensUpdate
        super(BattlePassController, self).fini()

    def isBought(self, chapterID, seasonID=None):

        def getTokens(season, chapter):
            return self.__itemsCache.items.tokens.getTokens().get(getBattlePassPassTokenName(season, chapter))

        if seasonID is None:
            seasonID = self.getSeasonID()
        if chapterID is None:
            chapterID = self.getCurrentChapterID()
            if not chapterID:
                return False
        if not self.isExtraChapter(chapterID) and bool(getTokens(seasonID, 0)):
            return True
        else:
            token = getTokens(seasonID, chapterID)
            return token is not None

    def isOfferEnabled(self):
        return self.__lobbyContext.getServerSettings().isOffersEnabled()

    def isEnabled(self):
        return self.__getConfig().isEnabled()

    def isActive(self):
        return self.__getConfig().isActive(time_utils.getServerUTCTime())

    def isVisible(self):
        return self.isSeasonStarted() and not self.isDisabled() and not self.isSeasonFinished()

    def isDisabled(self):
        return not self.isActive() and not self.isPaused()

    def isPaused(self):
        return self.__getConfig().isPaused()

    def isSeasonStarted(self):
        return self.__getConfig().seasonStart <= time_utils.getServerUTCTime()

    def isSeasonFinished(self):
        return self.__getConfig().seasonFinish <= time_utils.getServerUTCTime()

    def isValidBattleType(self, prbEntity):
        return prbEntity.getQueueType() in (QUEUE_TYPE.RANDOMS, QUEUE_TYPE.MAPBOX)

    def isGameModeEnabled(self, arenaBonusType):
        return self.__getConfig().isGameModeEnabled(arenaBonusType)

    def isCompleted(self):
        return self.getState() == BattlePassState.COMPLETED

    def getSupportedArenaBonusTypes(self):
        return [ arenaBonusType for arenaBonusType in self.__getConfig().points ]

    def getMaxLevelInChapter(self, chapterId=None):
        if chapterId is None:
            chapterId = first(self.getChapterIDs())
        return self.__getConfig().getMaxChapterLevel(chapterId)

    def hasExtra(self):
        return any((self.isExtraChapter(chID) for chID in self.getChapterIDs()))

    def isRegularProgressionCompleted(self):
        chapterIDs = []
        for chapterID in self.__getConfig().getChapterIDs():
            if not self.isExtraChapter(chapterID):
                chapterIDs.append(chapterID)

        return all((self.getChapterState(chID) == ChapterState.COMPLETED for chID in chapterIDs))

    def getExtraChapterID(self):
        return findFirst(self.isExtraChapter, self.getChapterIDs(), 0)

    def getRewardType(self, chapterID):
        return self.__getConfig().getRewardType(chapterID)

    def isChapterExists(self, chapterID):
        return chapterID in self.getChapterIDs()

    def getChapterIDs(self):

        def isActive(chID):
            expireTimestamp = self.__getConfig().getChapterExpireTimestamp(chID)
            return not expireTimestamp or time_utils.getServerUTCTime() < expireTimestamp

        return [ chapterID for chapterID in self.__getConfig().getChapterIDs() if isActive(chapterID) ]

    def isExtraChapter(self, chapterID):
        return self.__getConfig().isExtraChapter(chapterID)

    def getBattlePassCost(self, chapterID):
        return Money(**self.__getConfig().getbattlePassCost(chapterID))

    def getChapterExpiration(self, chapterID):
        return self.__getConfig().getChapterExpireTimestamp(chapterID) if self.isExtraChapter(chapterID) else 0

    def getChapterRemainingTime(self, chapterID):
        remainingTime = 0
        if self.isExtraChapter(chapterID):
            remainingTime = max(0, self.getChapterExpiration(chapterID) - time_utils.getServerUTCTime())
        return remainingTime

    def isRareLevel(self, chapterID, level):
        realLevel = min(level, self.getMaxLevelInChapter(chapterID))
        tags = self.__getConfig().getTags(chapterID, realLevel, BattlePassConsts.REWARD_PAID)
        return BattlePassConsts.RARE_REWARD_TAG in tags

    def isFinalLevel(self, chapterID, level):
        return level >= self.getMaxLevelInChapter(chapterID)

    def getRewardLogic(self):
        return self.__rewardLogic

    def getSingleAward(self, chapterId, level, awardType=BattlePassConsts.REWARD_FREE, needSort=True):
        reward = {}
        if awardType in (BattlePassConsts.REWARD_FREE, BattlePassConsts.REWARD_PAID):
            reward = self.__getConfig().getRewardByType(chapterId, level, awardType)
        elif awardType == BattlePassConsts.REWARD_BOTH:
            rewards = [self.__getConfig().getFreeReward(chapterId, level), self.__getConfig().getPaidReward(chapterId, level)]
            return BattlePassAwardsManager.hideInvisible(BattlePassAwardsManager.composeBonuses(rewards))
        if needSort:
            rewards = BattlePassAwardsManager.composeBonuses([reward])
        else:
            rewards = awardsFactory(reward)
        return BattlePassAwardsManager.hideInvisible(rewards, needSplit=not needSort)

    def getAwardsInterval(self, chapterId, fromLevel, toLevel, awardType=BattlePassConsts.REWARD_FREE):
        result = {}
        for level in range(fromLevel, toLevel + 1):
            result[level] = self.getSingleAward(chapterId, level, awardType, True)

        return result

    def getPackedAwardsInterval(self, chapterId, fromLevel, toLevel, awardType=BattlePassConsts.REWARD_FREE):
        result = []
        for level in range(fromLevel, toLevel + 1):
            result.extend(self.getSingleAward(chapterId, level, awardType, False))

        return BattlePassAwardsManager.sortBonuses(result)

    def isNeedToTakeReward(self, chapterId, awardType, level):
        bonuses = self.getSingleAward(chapterId, level, awardType)
        if level > self.getLevelInChapter(chapterId):
            return False
        else:
            for bonus in bonuses:
                if bonus.getName() == BATTLE_PASS_SELECT_BONUS_NAME:
                    for tokenID in bonus.getTokens().iterkeys():
                        if self.__itemsCache.items.tokens.getToken(tokenID) is not None:
                            return True

            return False

    def replaceOfferByReward(self, bonuses):
        result = []
        for bonus in bonuses:
            if bonus.getName() == BATTLE_PASS_SELECT_BONUS_NAME:
                bonus.updateContext({'isReceived': False})
                hasGift = False
                for tokenID in bonus.getTokens().iterkeys():
                    offerToken = getOfferTokenByGift(tokenID)
                    offer = self.__offersProvider.getOfferByToken(offerToken)
                    if offer is not None:
                        receivedGifts = self.__offersProvider.getReceivedGifts(offer.id)
                        if receivedGifts:
                            for giftId, count in receivedGifts.iteritems():
                                if count > 0:
                                    gift = offer.getGift(giftId)
                                    if gift is not None:
                                        hasGift = True
                                        result.extend(gift.bonuses)

                if not hasGift:
                    result.append(bonus)
            result.append(bonus)

        return result

    def isChooseRewardEnabled(self, awardType, chapterId, level):
        if level > self.getLevelInChapter(chapterId):
            return False
        else:
            bonuses = self.getSingleAward(chapterId, level, awardType)
            for bonus in bonuses:
                if bonus.getName() == BATTLE_PASS_SELECT_BONUS_NAME:
                    for tokenID in bonus.getTokens().iterkeys():
                        if self.__itemsCache.items.tokens.getToken(tokenID) is not None:
                            return self.isOfferEnabled() and self.__offersProvider.getOfferByToken(getOfferTokenByGift(tokenID)) is not None

            return False

    def canChooseAnyReward(self):
        return self.isOfferEnabled() and any((token.startswith(BATTLE_PASS_CHOICE_REWARD_OFFER_GIFT_TOKENS) for token in self.__itemsCache.items.tokens.getTokens().iterkeys() if self.__offersProvider.getOfferByToken(getOfferTokenByGift(token)) is not None))

    def getChapterIndex(self, chapterID):
        sortedChapterIDs = sorted(self.getChapterIDs())
        return sortedChapterIDs.index(chapterID)

    def getLevelsConfig(self, chapterID):
        return self.__getConfig().getChapterLevels(chapterID)

    def getPointsInChapter(self, chapterID):
        return self.__itemsCache.items.battlePass.getPointsByChapterID(chapterID)

    def getLevelInChapter(self, chapterID):
        return self.__itemsCache.items.battlePass.getCurrentLevelByChapterID(chapterID)

    def getCurrentLevel(self):
        return self.getLevelInChapter(self.getCurrentChapterID())

    def getCurrentChapterID(self):
        activeChapter = self.__itemsCache.items.battlePass.getActiveChapterID()
        if activeChapter not in self.getChapterIDs():
            activeChapter = 0
        return activeChapter

    def hasActiveChapter(self):
        return bool(self.getCurrentChapterID())

    @process
    def activateChapter(self, chapterID, seasonID=None):
        yield BattlePassActivateChapterProcessor(chapterID, seasonID or self.getSeasonID()).request()

    def getFreePoints(self):
        return self.__itemsCache.items.battlePass.getNonChapterPoints()

    def getState(self):
        return self.__itemsCache.items.battlePass.getState()

    def getPrevSeasonsStats(self):
        packedStats = self.__itemsCache.items.battlePass.getPackedStats()
        if not packedStats:
            return None
        else:
            unpackStats, _ = BattlePassStatsCommon.unpackAllSeasonStats(packedStats)
            return unpackStats

    def getLastFinishedSeasonStats(self):
        allSeasonStats = self.getPrevSeasonsStats()
        if not allSeasonStats:
            seasons = sorted(self.getSeasonsHistory().keys(), reverse=True)
            return BattlePassStatsCommon.makeSeasonStats(first(seasons), {}, BattlePassStatsCommon.initialSeasonStatsData())
        return allSeasonStats[-1]

    def getSeasonsHistory(self):
        return {}

    def getLevelPoints(self, chapterID, level):
        levelsConfig = self.getLevelsConfig(chapterID)
        return levelsConfig[0] if level <= 0 else levelsConfig[level] - levelsConfig[level - 1]

    def getChapterState(self, chapterID):
        if self.getLevelInChapter(chapterID) >= self.getMaxLevelInChapter(chapterID):
            state = ChapterState.COMPLETED
        elif self.getCurrentChapterID() is not None and self.getCurrentChapterID() == chapterID:
            state = ChapterState.ACTIVE
        elif chapterID in self.__itemsCache.items.battlePass.getChapterStats():
            state = ChapterState.PAUSED
        else:
            state = ChapterState.NOT_STARTED
        return state

    def isChapterActive(self, chapterID):
        return self.getChapterState(chapterID) == ChapterState.ACTIVE

    def isChapterCompleted(self, chapterID):
        return self.getChapterState(chapterID) == ChapterState.COMPLETED

    def getFullChapterPoints(self, chapterID):
        levelsConfig = self.getLevelsConfig(chapterID)
        _, maxLevel = self.getChapterLevelInterval(chapterID)
        return levelsConfig[maxLevel - 1]

    def getLevelProgression(self, chapterID):
        if self.isDisabled():
            return (0, 0)
        if not chapterID:
            return (0, 0)
        level = self.getLevelInChapter(chapterID)
        points = self.getPointsInChapter(chapterID)
        return self.getProgressionByPoints(chapterID, points, level)

    def getLevelByPoints(self, chapterID, points):
        return self.getMaxLevelInChapter(chapterID) if points >= self.getLevelsConfig(chapterID)[-1] else bisect.bisect_right(self.getLevelsConfig(chapterID), points)

    def getProgressionByPoints(self, chapterID, points, level):
        levelsConfig = self.getLevelsConfig(chapterID)
        if level >= self.getMaxLevelInChapter(chapterID):
            points = levelsConfig[-1] - levelsConfig[-2]
            return (points, points)
        if level <= 0:
            basePoints = 0
            limitPoints = levelsConfig[0]
        else:
            basePoints = levelsConfig[level - 1]
            limitPoints = levelsConfig[level] - basePoints
        levelPoints = points - basePoints
        return (levelPoints, limitPoints)

    def getPerBattlePoints(self, gameMode=ARENA_BONUS_TYPE.REGULAR, vehCompDesc=None):
        winList = self.__getPackedBonusPointsList(vehTypeCompDescr=vehCompDesc, gameMode=gameMode)
        lostList = self.__getPackedBonusPointsList(vehTypeCompDescr=vehCompDesc, isWinner=False, gameMode=gameMode)
        count = 0
        result = []
        for winInfo, lostInfo in zip(winList, lostList):
            pointsWin, pointsCount = winInfo
            pointsLost, _ = lostInfo
            count += pointsCount
            if pointsWin > 0:
                item = TopPoints(count, pointsWin, pointsLost)
                result.append(item)

        return result

    def getPerBattleRoyalePoints(self, gameMode=ARENA_BONUS_TYPE.BATTLE_ROYALE_SOLO, vehCompDesc=None):
        winList = self.__getConfig().bonusPointsList(vehCompDesc, isWinner=True, gameMode=gameMode)
        pointsList = list(self.__getConfig().bonusPointsList(vehCompDesc, isWinner=False, gameMode=gameMode))
        pointsList[0] = winList[0]
        pointList = [ (key, len(list(group))) for key, group in groupby(pointsList) ]
        count = 0
        result = []
        if not winList or not pointList:
            _logger.error('Failed to get bonus points information! Check server settings are correct for Battle Royale.')
            return result
        for item in pointList:
            points, pointsCount = item
            count += pointsCount
            if points > 0:
                result.append(BattleRoyaleTopPoints(count, points))

        return result

    def getChapterConfig(self):
        return [ self.getMaxLevelInChapter(chapter) for chapter in self.getChapterIDs() ]

    def getChapterLevelInterval(self, chapterID):
        return self.__getConfig().getChapterBorders(chapterID)

    def isSpecialVehicle(self, intCD):
        return self.__getConfig().isSpecialVehicle(intCD)

    def getSpecialVehicles(self):
        return self.__getConfig().getSpecialVehicles()

    def getPointsDiffForVehicle(self, intCD, gameMode=ARENA_BONUS_TYPE.REGULAR):
        defaultWinList = self.__getPackedBonusPointsList(gameMode=gameMode)
        diffWinList = self.__getPackedBonusPointsList(vehTypeCompDescr=intCD, isDiff=True, gameMode=gameMode)
        if not defaultWinList or not diffWinList:
            _logger.error('Failed to get bonus points information! Check server settings are correct.')
            return PointsDifference(0, 0, 0)
        diffBlock = diffWinList[0]
        bonus = diffBlock[0]
        top = diffBlock[1]
        textID = getPointsInfoStringID()
        return PointsDifference(bonus, top, textID)

    def getVehicleProgression(self, intCD):
        points = self.__itemsCache.items.battlePass.getPointsForVehicle(intCD, 0)
        cap = self.__getConfig().vehicleCapacity(intCD)
        return (points, cap)

    def getSpecialVehicleCapBonus(self):
        return self.__getConfig().vehicleCapacity(first(self.getSpecialVehicles()))

    def getVehicleCapBonus(self, intCD):
        vehicle = self.__itemsCache.items.getItemByCD(intCD)
        return 0 if vehicle is None else self.__getConfig().capBonus(vehicle.level)

    def getSeasonTimeLeft(self):
        return max(0, self.getSeasonFinishTime() - time_utils.getServerUTCTime())

    def getFinalOfferTimeLeft(self):
        return max(0, self.getFinalOfferTime() - time_utils.getServerUTCTime())

    def getSeasonStartTime(self):
        return self.__getConfig().seasonStart

    def getSeasonFinishTime(self):
        return self.__getConfig().seasonFinish

    def hasMaxPointsOnVehicle(self, intCD):
        currentPoints, limitPoints = self.getVehicleProgression(intCD)
        return currentPoints >= limitPoints > 0

    def isProgressionOnVehiclePossible(self, intCD):
        return self.__getConfig().vehicleCapacity(intCD) > 0

    def getSeasonID(self):
        return self.__itemsCache.items.battlePass.getSeasonID()

    def getSeasonNum(self):
        return self.__getConfig().seasonNum

    def getFinalOfferTime(self):
        return self.__getConfig().finalOfferTime

    def getStylesConfig(self):
        return {chapterID:chapterInfo.get('styleId') for chapterID, chapterInfo in self.__getConfig().chapters.iteritems()}

    def getNotChosenRewardCount(self):
        return sum((token.startswith(BATTLE_PASS_CHOICE_REWARD_OFFER_GIFT_TOKENS) for token in self.__itemsCache.items.tokens.getTokens().iterkeys() if self.__offersProvider.getOfferByToken(getOfferTokenByGift(token)) is not None))

    def hasAnyOfferGiftToken(self):
        return any((token.startswith(BATTLE_PASS_CHOICE_REWARD_OFFER_GIFT_TOKENS) for token in self.__itemsCache.items.tokens.getTokens().iterkeys()))

    def takeRewardForLevel(self, chapterID, level):
        isBought = self.isBought(chapterID=chapterID)
        awardType = BattlePassConsts.REWARD_BOTH if isBought else BattlePassConsts.REWARD_FREE
        isOfferEnabled = self.isOfferEnabled()
        bonuses = self.getSingleAward(chapterID, level, awardType)
        rewardsToChoose = []
        stylesToChoose = []
        for bonus in bonuses:
            bonusName = bonus.getName()
            if bonusName == BATTLE_PASS_SELECT_BONUS_NAME and isOfferEnabled:
                for tokenID in bonus.getTokens().iterkeys():
                    if self.__itemsCache.items.tokens.getToken(tokenID) is not None and self.__offersProvider.getOfferByToken(getOfferTokenByGift(tokenID)) is not None:
                        rewardsToChoose.append(tokenID)

            if bonusName == BATTLE_PASS_STYLE_PROGRESS_BONUS_NAME:
                for tokenID in bonus.getTokens().iterkeys():
                    if self.__itemsCache.items.tokens.getToken(tokenID) is not None:
                        chapter = bonus.getChapter()
                        if chapter not in stylesToChoose:
                            stylesToChoose.append(chapter)

        rewardsToChoose.sort(key=lambda x: (int(x.split(':')[-1]), x.split(':')[-2]))
        self.getRewardLogic().startManualFlow(rewardsToChoose, chapterID, level)
        return

    def takeAllRewards(self):
        if self.isOfferEnabled():
            rewardsToChoose = [ token for token in self.__itemsCache.items.tokens.getTokens().iterkeys() if token.startswith(BATTLE_PASS_CHOICE_REWARD_OFFER_GIFT_TOKENS) and self.__offersProvider.getOfferByToken(getOfferTokenByGift(token)) is not None ]
            rewardsToChoose.sort(key=lambda x: (int(x.split(':')[-1]), x.split(':')[-2]))
        else:
            rewardsToChoose = []
        self.getRewardLogic().startManualFlow(rewardsToChoose, 0)
        return

    def getChapterStyleProgress(self, chapter):
        return getMaxAvalable3DStyleProgressInChapter(self.getSeasonID(), chapter, self.__itemsCache.items.tokens.getTokens().keys())

    def _getEvents(self):
        return ((self.__lobbyContext.getServerSettings().onServerSettingsChange, self.__onConfigChanged),
         (self.__lobbyContext.getServerSettings().onServerSettingsChange, self.__onOffersStateChanged),
         (self.__itemsCache.onSyncCompleted, self.__onSyncCompleted),
         (self.__offersProvider.onOffersUpdated, self.__onOffersUpdated))

    def __stop(self):
        self.__seasonChangeNotifier.stopNotification()
        self.__extraChapterNotifier.stopNotification()
        self._unsubscribe()

    def __getConfig(self):
        return self.__lobbyContext.getServerSettings().getBattlePassConfig()

    def __onTokensUpdate(self, diff, _):
        tokens = diff.get('tokens', {})
        if not tokens:
            return
        for chapter in self.getChapterIDs():
            if getBattlePassPassTokenName(self.getSeasonID(), chapter) in tokens:
                self.onBattlePassIsBought()
                break

        if any((tokenID.startswith(BATTLE_PASS_OFFER_TOKEN_PREFIX) for tokenID, token in tokens.iteritems())):
            self.onSelectTokenUpdated()

    def __getTimeUntilStart(self):
        return max(0, self.__getConfig().seasonStart - time_utils.getServerUTCTime())

    def __getTimeToNotifySeasonChanged(self):
        if not self.isPaused():
            if not self.isSeasonStarted():
                return self.__getTimeUntilStart()
            if not self.isSeasonFinished():
                return self.getSeasonTimeLeft()

    def __getTimeToExtraChapterExpired(self):
        extraChapterID = findFirst(self.isExtraChapter, self.getChapterIDs(), 0)
        return max(0, self.getChapterExpiration(extraChapterID) - time_utils.getServerUTCTime())

    def __onNotifySeasonChanged(self):
        self.onSeasonStateChanged()

    def __onNotifyExtraChapterExpired(self):
        self.onExtraChapterExpired()

    @serverSettingsChangeListener(BATTLE_PASS_CONFIG_NAME)
    def __onConfigChanged(self, diff):
        config = diff[BATTLE_PASS_CONFIG_NAME]
        self.__seasonChangeNotifier.startNotification()
        chapters = config.get('season', {}).get('chapters', {})
        if any((self.isExtraChapter(chapterID) for chapterID in chapters)):
            self.__extraChapterNotifier.stopNotification()
            self.__extraChapterNotifier = SimpleNotifier(self.__getTimeToExtraChapterExpired, self.__onNotifyExtraChapterExpired)
            self.__extraChapterNotifier.startNotification()
        else:
            self.__extraChapterNotifier.stopNotification()
        newMode = None
        oldMode = self.__currentMode
        if 'mode' in config:
            newMode = config['mode']
            self.__currentMode = newMode
        self.onBattlePassSettingsChange(newMode, oldMode)
        return

    @serverSettingsChangeListener(OFFERS_ENABLED_KEY)
    def __onOffersStateChanged(self, diff):
        self.__onOffersUpdated()

    def __onSyncCompleted(self, _, diff):
        if BATTLE_PASS_PDATA_KEY not in diff:
            return
        data = diff[BATTLE_PASS_PDATA_KEY]
        newPoints = data.get('sumPoints', self.__oldPoints)
        newLevel = data.get('level', self.__oldLevel)
        if newPoints != self.__oldPoints:
            self.onPointsUpdated()
        if newLevel != self.__oldLevel:
            self.onLevelUp()
        self.__oldPoints = newPoints
        self.__oldLevel = newLevel
        if 'chapterID' in data:
            self.onChapterChanged()

    def __onOffersUpdated(self):
        self.__validateOffers()
        self.onOffersUpdated()

    def __validateOffers(self):
        for offer in self.__offersProvider.iAvailableOffers(False):
            if not offer.token.startswith(BATTLE_PASS_OFFER_TOKEN_PREFIX):
                continue
            counts = {gift.giftCount for gift in offer.getAllGifts()}
            if len(counts) > 1:
                _logger.error('Wrong bonus count in gifts. Offer token %s', offer.token)

    @staticmethod
    def __bonusPointsDiffList(vehTypeCompDescr, config, gameMode):
        defaultPoints = config.points.get(gameMode, {})
        defaultDiff = [0] * len(defaultPoints.get('win', []))
        if vehTypeCompDescr in defaultPoints and 'win' in defaultPoints:
            specialPoints = defaultPoints[vehTypeCompDescr]
            defaultPoints = defaultPoints['win']
            specialPoints = specialPoints['win']
            return [ a - b for a, b in zip(specialPoints, defaultPoints) ]
        return defaultDiff

    def __getPackedBonusPointsList(self, vehTypeCompDescr=None, isWinner=True, isDiff=False, gameMode=ARENA_BONUS_TYPE.REGULAR):
        if isDiff:
            pointsList = self.__bonusPointsDiffList(vehTypeCompDescr, self.__getConfig(), gameMode)
        else:
            pointsList = self.__getConfig().bonusPointsList(vehTypeCompDescr, isWinner, gameMode)
        return [ (key, len(list(group))) for key, group in groupby(pointsList) ]

    @staticmethod
    def __checkIfRewardIsToken(bonusName, reward):
        if 'tokens' not in reward:
            return False
        bonuses = BattlePassAwardsManager.composeBonuses([reward])
        for bonus in bonuses:
            if bonus.getName() == bonusName:
                return True

        return False

    def __clearFields(self):
        self.__oldPoints = 0
        self.__oldLevel = 0
        self.__currentMode = None
        return
class EpicBattleMetaGameController(Notifiable, SeasonProvider,
                                   IEpicBattleMetaGameController,
                                   IGlobalListener):
    bootcampController = dependency.descriptor(IBootcampController)
    __itemsCache = dependency.descriptor(IItemsCache)
    __eventsCache = dependency.descriptor(IEventsCache)
    __settingsCore = dependency.descriptor(ISettingsCore)
    __lobbyContext = dependency.descriptor(ILobbyContext)
    __battleResultsService = dependency.descriptor(IBattleResultsService)
    __offersProvider = dependency.descriptor(IOffersDataProvider)
    MAX_STORED_ARENAS_RESULTS = 20
    DAILY_QUEST_ID = 'front_line'
    FINAL_BADGE_QUEST_ID = 'epicmetagame:progression_finish'

    def __init__(self):
        super(EpicBattleMetaGameController, self).__init__()
        self.onUpdated = Event.Event()
        self.onPrimeTimeStatusUpdated = Event.Event()
        self.onEventEnded = Event.Event()
        self.onGameModeStatusTick = Event.Event()
        self.__skillData = {}
        self.__playerMaxLevel = 0
        self.__levelProgress = tuple()
        self.__abilityPointsForLevel = list()
        self.__performanceGroup = None
        self.__isEpicSoundMode = False
        self.__rankSettings = {}
        self.__showedResultsForArenas = []
        self.__eventEndedNotifier = None
        self.__urlMacros = None
        return

    def init(self):
        super(EpicBattleMetaGameController, self).init()
        self.__urlMacros = URLMacros()
        self.addNotificator(SimpleNotifier(self.getTimer, self.__timerUpdate))
        self.addNotificator(PeriodicNotifier(self.getTimer, self.__timerTick))
        self.__eventEndedNotifier = SimpleNotifier(self.getEventTimeLeft,
                                                   self.__onEventEnded)
        self.addNotificator(self.__eventEndedNotifier)

    def fini(self):
        del self.__showedResultsForArenas[:]
        self.onUpdated.clear()
        self.onPrimeTimeStatusUpdated.clear()
        self.onGameModeStatusTick.clear()
        self.onEventEnded.clear()
        self.clearNotification()
        self.stopGlobalListening()
        self.__urlMacros = None
        super(EpicBattleMetaGameController, self).fini()
        return

    def onLobbyInited(self, ctx):
        self.__lobbyContext.getServerSettings(
        ).onServerSettingsChange += self.__updateEpicMetaGameSettings
        g_currentVehicle.onChanged += self.__invalidateBattleAbilities
        self.__itemsCache.onSyncCompleted += self.__invalidateBattleAbilities
        g_clientUpdateManager.addCallbacks({
            'epicMetaGame': self.__updateEpic,
            'inventory': self.__onInventoryUpdate,
            'tokens': self.__onTokensUpdate
        })
        self.startGlobalListening()
        self.__setData()
        self.__invalidateBattleAbilities()
        self.startNotification()
        if self.getPerformanceGroup() == EPIC_PERF_GROUP.HIGH_RISK:
            self.__lobbyContext.addFightButtonConfirmator(
                self.__confirmFightButtonPressEnabled)
        self.__isEpicSoundMode = False
        if self.prbEntity is not None:
            enableSound = bool(self.prbEntity.getModeFlags()
                               & FUNCTIONAL_FLAG.EPIC)
            self.__updateSounds(enableSound)
        return

    def onDisconnected(self):
        self.__clear()

    def onPrbEntitySwitching(self):
        if self.prbEntity is None:
            return
        else:
            switchedFromEpic = bool(self.prbEntity.getModeFlags()
                                    & FUNCTIONAL_FLAG.EPIC)
            if switchedFromEpic:
                self.__updateSounds(False)
            return

    def onPrbEntitySwitched(self):
        self.__invalidateBattleAbilities()
        if self.prbEntity is None:
            return
        else:
            isEpicSoundMode = bool(self.prbEntity.getModeFlags()
                                   & FUNCTIONAL_FLAG.EPIC)
            if isEpicSoundMode:
                self.__updateSounds(True)
            return

    def onAccountBecomePlayer(self):
        self.__battleResultsService.onResultPosted += self.__showBattleResults

    def onAvatarBecomePlayer(self):
        self.__clear()
        self.__battleResultsService.onResultPosted -= self.__showBattleResults

    def getModeSettings(self):
        return self.__lobbyContext.getServerSettings().epicBattles

    def isEnabled(self):
        return self.getModeSettings().isEnabled

    def isEpicPrbActive(self):
        return False if self.prbEntity is None else bool(
            self.prbEntity.getModeFlags() & FUNCTIONAL_FLAG.EPIC)

    def isCurrentCycleActive(self):
        season = self.getCurrentSeason()
        return season.hasActiveCycle(time_utils.getCurrentLocalServerTimestamp(
        )) if season is not None else False

    def isUnlockVehiclesInBattleEnabled(self):
        return any(self.getUnlockableInBattleVehLevels())

    def isDailyQuestsUnlocked(self):
        currrentLevel, _ = self.getPlayerLevelInfo()
        return currrentLevel >= self.getMaxPlayerLevel()

    def isDailyQuestsRefreshAvailable(self):
        if self.hasPrimeTimesLeftForCurrentCycle():
            return True
        primeTimePeriodsForDay = self.getPrimeTimesForDay(
            time_utils.getCurrentLocalServerTimestamp())
        if primeTimePeriodsForDay:
            _, periodTimeEnd = max(primeTimePeriodsForDay.values(),
                                   key=itemgetter(1))
            periodTimeLeft = periodTimeEnd - time_utils.getCurrentLocalServerTimestamp(
            )
            return periodTimeLeft > time_utils.getDayTimeLeft()
        return False

    def getPerformanceGroup(self):
        if not self.__performanceGroup:
            self.__analyzeClientSystem()
            _logger.debug('Current performance group %s',
                          self.__performanceGroup)
        return self.__performanceGroup

    def getMaxPlayerLevel(self):
        return self.__playerMaxLevel

    def getStageLimit(self):
        return self.__stageLimit

    def getAbilityPointsForLevel(self):
        return self.__abilityPointsForLevel

    def getValidVehicleLevels(self):
        return self.getModeSettings().validVehicleLevels

    def getUnlockableInBattleVehLevels(self):
        return self.getModeSettings().unlockableInBattleVehLevels

    def getSuitableForQueueVehicleLevels(self):
        return set(self.getValidVehicleLevels()) - set(
            self.getUnlockableInBattleVehLevels())

    def getPointsProgressForLevel(self, level):
        return self.__levelProgress[level]

    def getPointsForLevel(self, level):
        return sum(
            (self.__levelProgress[level] for level in xrange(level - 1)))

    def getLevelProgress(self):
        return self.__levelProgress

    def getLevelForPoints(self, points):
        lvl = 0
        while points >= 0 and lvl <= self.__playerMaxLevel:
            points -= self.__levelProgress[lvl]
            lvl += 1

        return lvl - 1

    def getAllSkillsInformation(self):
        return self.__skillData

    def getPlayerLevelInfo(self):
        return self.__itemsCache.items.epicMetaGame.playerLevelInfo

    def getPlayerRanksInfo(self):
        if not self.__rankSettings:
            famePtsByRank = self.__metaSettings.metaLevel.get(
                'famePtsByRank', {})
            rankSettings = getRankSettings()
            self.__rankSettings = {
                rankLvl:
                (extraFamePts, rankSettings.bonus.factor100ByRank[rankLvl])
                for rankLvl, extraFamePts in famePtsByRank.iteritems()
            }
        return self.__rankSettings

    def getSeasonData(self):
        return self.__itemsCache.items.epicMetaGame.seasonData

    def getSkillPoints(self):
        return self.__itemsCache.items.epicMetaGame.skillPoints

    def getSkillLevels(self):
        return self.__itemsCache.items.epicMetaGame.skillLevels

    def getSelectedSkills(self, vehicleCD):
        selected = self.__itemsCache.items.epicMetaGame.selectedSkills(
            vehicleCD)
        numSlots = self.getNumAbilitySlots(vehicles.getVehicleType(vehicleCD))
        while len(selected) < numSlots:
            selected.append(-1)

        return selected

    def hasSuitableVehicles(self):
        requiredLevel = self.getModeSettings().validVehicleLevels
        v = self.__itemsCache.items.getVehicles(
            REQ_CRITERIA.INVENTORY
            | REQ_CRITERIA.VEHICLE.LEVELS(requiredLevel))
        return len(v) > 0

    def increaseSkillLevel(self, skillID):
        BigWorld.player().epicMetaGame.increaseAbility(skillID)

    def changeEquippedSkills(self,
                             skillIDArray,
                             vehicleCD,
                             callback=None,
                             classVehs=False):
        if classVehs:
            if callback is None:
                BigWorld.player().epicMetaGame.setSelectedAbilitiesVehsClass(
                    skillIDArray, vehicleCD)
            else:
                BigWorld.player().epicMetaGame.setSelectedAbilitiesVehsClass(
                    skillIDArray, vehicleCD, callback)
        elif callback is None:
            BigWorld.player().epicMetaGame.setSelectedAbilities(
                skillIDArray, vehicleCD)
        else:
            BigWorld.player().epicMetaGame.setSelectedAbilities(
                skillIDArray, vehicleCD, callback)
        return

    def getCycleInfo(self, cycleID=None):
        season = self.getCurrentSeason()
        if season is not None:
            return season.getCycleInfo(cycleID)
        else:
            _logger.warning('No current season')
            return

    def getCycleOrdinalNumber(self, cycleID):
        cycleInfo = self.getCycleInfo(cycleID)
        return cycleInfo.ordinalNumber if cycleInfo else None

    def getSeasonTimeRange(self):
        season = self.getCurrentSeason() or self.getNextSeason()
        if season is not None:
            cycles = season.getAllCycles()
            if cycles:
                cycles = list(
                    sorted(cycles.values(), key=lambda c: c.ordinalNumber))
                return (cycles[0].startDate, cycles[-1].endDate)
        return (0, 0)

    def getAllUnlockedSkillInfoBySkillId(self):
        return {
            skillID: skill.getSkillInfo()
            for skillID, skill in self.__skillData.iteritems()
            if skill.isActivated
        }

    def getUnlockedAbilityIds(self):
        return (skill.getSkillInfo().eqID
                for skill in self.getAllSkillsInformation().itervalues()
                if skill.isActivated and skill.getSkillInfo() is not None)

    def getStoredEpicDiscount(self):
        return BigWorld.player().epicMetaGame.getStoredDiscount()

    def getEventTimeLeft(self):
        timeLeft = self.getSeasonTimeRange(
        )[1] - time_utils.getCurrentLocalServerTimestamp()
        return timeLeft + 1 if timeLeft > 0 else time_utils.ONE_MINUTE

    def getStats(self):
        return self.__itemsCache.items.epicMetaGame

    @process
    def openURL(self, url=None):
        requestUrl = url or self.getModeSettings().url
        if requestUrl:
            parsedUrl = yield self.__urlMacros.parse(requestUrl)
            if parsedUrl:
                self.__showBrowserView(parsedUrl)

    def showCustomScreen(self, screen):
        if self.getModeSettings().url and EpicBattleScreens.hasValue(screen):
            self.openURL('/'.join((self.getModeSettings().url.strip('/'),
                                   screen.value.strip('/'))))

    def getNumAbilitySlots(self, vehicleType):
        vehClass = getVehicleClassFromVehicleType(vehicleType)
        return self.__metaSettings.defaultSlots.get(vehClass, 0)

    def getAbilitySlotsOrder(self, vehicleType):
        vehClass = getVehicleClassFromVehicleType(vehicleType)
        return self.__metaSettings.slots.get(vehClass, (0, 0, 0))

    def getAbilitySlotsUnlockOrder(self, vehicleType):
        vehClass = getVehicleClassFromVehicleType(vehicleType)
        return self.__metaSettings.inBattleReservesByRank.get(
            'slotActions').get(vehClass, [[0], [0], [0]])

    def getAllLevelRewards(self):
        rewardsData = dict()
        allQuests = self.__eventsCache.getAllQuests()
        for questKey, questData in allQuests.iteritems():
            if LEVELUP_TOKEN_TEMPLATE in questKey:
                _, _, questNum = questKey.partition(LEVELUP_TOKEN_TEMPLATE)
                if questNum:
                    questLvl = int(questNum)
                    rewardsData[questLvl] = questData

        return rewardsData

    def isNeedToTakeReward(self):
        currentLevel, _ = self.getPlayerLevelInfo()
        rewardsData = self.getAllLevelRewards()
        for bonuses in (rewards.getBonuses()
                        for level, rewards in rewardsData.iteritems()
                        if level <= currentLevel):
            for bonus in bonuses:
                if bonus.getName() == EPIC_SELECT_BONUS_NAME:
                    for tokenID in bonus.getTokens().iterkeys():
                        if self.__itemsCache.items.tokens.getToken(tokenID):
                            return True

        return False

    def getNotChosenRewardCount(self):
        count = 0
        for token in self.__itemsCache.items.tokens.getTokens().iterkeys():
            if not token.startswith(EPIC_CHOICE_REWARD_OFFER_GIFT_TOKENS):
                continue
            if not self.__offersProvider.getOfferByToken(
                    token.replace('_gift', '')):
                continue
            if self.__itemsCache.items.tokens.isTokenAvailable(token):
                count += self.__itemsCache.items.tokens.getTokenCount(token)

        return count

    def hasAnyOfferGiftToken(self):
        return any((token.startswith(EPIC_CHOICE_REWARD_OFFER_GIFT_TOKENS)
                    for token in
                    self.__itemsCache.items.tokens.getTokens().iterkeys()))

    def replaceOfferByGift(self, bonuses):
        result = []
        for bonus in bonuses:
            gift = self.__getReceivedGift(bonus)
            if gift:
                result.extend(gift.bonuses)
            result.append(bonus)

        return result

    def replaceOfferByReward(self, bonuses):
        result = []
        for bonus in bonuses:
            if bool(self.__getReceivedGift(bonus)):
                bonus.updateContext({'isReceived': True})
            result.append(bonus)

        return result

    def __getReceivedGift(self, bonus):
        if bonus.getName() == EPIC_SELECT_BONUS_NAME:
            bonus.updateContext({'isReceived': False})
            for tokenID in bonus.getTokens().iterkeys():
                offer = self.__offersProvider.getOfferByToken(
                    tokenID.replace('_gift', ''))
                if offer:
                    receivedGifts = self.__offersProvider.getReceivedGifts(
                        offer.id)
                    if receivedGifts:
                        for giftId, count in receivedGifts.iteritems():
                            if count > 0:
                                return offer.getGift(giftId)

        return None

    def __invalidateBattleAbilities(self, *_):
        if not self.__itemsCache.isSynced():
            return
        self.__invalidateBattleAbilityItems()
        self.__invalidateBattleAbilitiesForVehicle()

    def __setData(self):
        self.__skillData = {}
        skills = self.__metaSettings.rewards.get('combatReserves', {})
        maxSkillLvl = self.__metaSettings.maxCombatReserveLevel
        eqs = vehicles.g_cache.equipments()
        if skills != {}:
            for key, value in skills.iteritems():
                self.__skillData[key] = EpicMetaGameSkill(
                    key, maxSkillLvl, value.get('tags'), value.get('price', 0))
                lvls = value['levels']
                lvlAmount = len(lvls)
                found = 0
                for eq in eqs.values():
                    if eq.name in lvls:
                        lvl = lvls.index(eq.name) + 1
                        self.__skillData[key].levels[
                            lvl] = EpicMetaGameSkillLevel(
                                lvl, eq.id[1], i18n.makeString(eq.userString),
                                i18n.makeString(eq.shortDescription),
                                i18n.makeString(eq.longDescription),
                                i18n.makeString(eq.shortFilterAlert),
                                i18n.makeString(eq.longFilterAlert),
                                eq.icon[0])
                        found += 1
                        if found == lvlAmount:
                            break

        metaLevel = self.__metaSettings.metaLevel
        self.__playerMaxLevel = metaLevel.get('maxLevel', 0)
        self.__stageLimit = metaLevel.get('stageLimit', -1)
        self.__abilityPointsForLevel = metaLevel.get('abilityPointsForLevel',
                                                     [])
        levelProgress = metaLevel.get('famePtsToProgress', [])[:]
        levelProgress.insert(0, 0)
        self.__levelProgress = tuple(levelProgress)

    def __clear(self):
        self.stopNotification()
        self.stopGlobalListening()
        self.__lobbyContext.getServerSettings(
        ).onServerSettingsChange -= self.__updateEpicMetaGameSettings
        g_currentVehicle.onChanged -= self.__invalidateBattleAbilities
        self.__itemsCache.onSyncCompleted -= self.__invalidateBattleAbilities
        g_clientUpdateManager.removeObjectCallbacks(self)
        if self.getPerformanceGroup() == EPIC_PERF_GROUP.HIGH_RISK:
            self.__lobbyContext.deleteFightButtonConfirmator(
                self.__confirmFightButtonPressEnabled)

    def __updateEpic(self, diff):
        changes = set(diff.keys())
        self.__invalidateBattleAbilities()
        if changes:
            self.onUpdated(diff)

    def __updateEpicMetaGameSettings(self, diff):
        if 'epic_config' in diff:
            self.__setData()
            self.onUpdated(diff['epic_config'])
            self.__resetTimer()

    def __resetTimer(self):
        self.startNotification()
        self.__timerUpdate()
        self.__timerTick()

    def __timerUpdate(self):
        status, _, _ = self.getPrimeTimeStatus()
        self.onPrimeTimeStatusUpdated(status)

    def __timerTick(self):
        self.onGameModeStatusTick()

    def __onEventEnded(self):
        self.onEventEnded()
        self.__eventEndedNotifier.stopNotification()
        self.__eventEndedNotifier.clear()

    def __showBattleResults(self, reusableInfo, _, resultsWindow):
        if reusableInfo.common.arenaBonusType == ARENA_BONUS_TYPE.EPIC_BATTLE:
            arenaUniqueID = reusableInfo.arenaUniqueID
            if arenaUniqueID not in self.__showedResultsForArenas:
                self.__showedResultsForArenas.append(arenaUniqueID)
                self.__showedResultsForArenas = self.__showedResultsForArenas[
                    -self.MAX_STORED_ARENAS_RESULTS:]
                extensionInfo = reusableInfo.personal.avatar.extensionInfo
                levelUpInfo = {
                    'metaLevel': extensionInfo.get('metaLevel'),
                    'prevMetaLevel': extensionInfo.get('prevMetaLevel'),
                    'playerRank': extensionInfo.get('playerRank'),
                    'originalFlXP': extensionInfo.get('originalFlXP'),
                    'boosterFlXP': extensionInfo.get('boosterFlXP')
                }
                event_dispatcher.showEpicBattlesAfterBattleWindow(
                    levelUpInfo, resultsWindow)

    def __isInValidPrebattle(self):
        if g_prbLoader and g_prbLoader.getDispatcher(
        ) and g_prbLoader.getDispatcher().getEntity():
            currentPrbEntity = g_prbLoader.getDispatcher().getEntity(
            ).getEntityType()
            return currentPrbEntity in (QUEUE_TYPE.EPIC, PREBATTLE_TYPE.EPIC,
                                        PREBATTLE_TYPE.EPIC_TRAINING)
        else:
            return None

    def __invalidateBattleAbilityItems(self):
        data = self.__itemsCache.items.getItems(GUI_ITEM_TYPE.BATTLE_ABILITY,
                                                REQ_CRITERIA.EMPTY)
        vehicle = g_currentVehicle.item
        for item in data.values():
            if self.__isInValidPrebattle():
                item.isUnlocked = item.innationID in self.getUnlockedAbilityIds(
                )
                if vehicle is not None:
                    mayInstall, _ = item.mayInstall(vehicle)
                    if not mayInstall:
                        item.isUnlocked = False
            item.isUnlocked = False

        return

    def __invalidateBattleAbilitiesForVehicle(self):
        vehicle = g_currentVehicle.item
        if vehicle is None or vehicle.descriptor.type.level not in self.__lobbyContext.getServerSettings(
        ).epicBattles.validVehicleLevels or not self.__isInValidPrebattle():
            return
        else:
            amountOfSlots = self.getNumAbilitySlots(vehicle.descriptor.type)
            selectedItems = [None] * amountOfSlots
            skillInfo = self.getAllSkillsInformation()
            selectedSkills = self.getSelectedSkills(vehicle.intCD)
            battleAbilities = self.__itemsCache.items.getItems(
                GUI_ITEM_TYPE.BATTLE_ABILITY, REQ_CRITERIA.EMPTY)
            for item in battleAbilities.values():
                for index, skillID in enumerate(selectedSkills):
                    if skillID is not None and skillID >= 0:
                        if skillInfo[skillID].getSkillInfo(
                        ) and item.innationID == skillInfo[
                                skillID].getSkillInfo().eqID:
                            selectedItems[index] = item

            vehicle.battleAbilities.setLayout(*selectedItems)
            vehicle.battleAbilities.setInstalled(*selectedItems)
            return

    def __analyzeClientSystem(self):
        stats = BigWorld.wg_getClientStatistics()
        stats['graphicsEngine'] = self.__settingsCore.getSetting(
            GRAPHICS.RENDER_PIPELINE)
        self.__performanceGroup = EPIC_PERF_GROUP.LOW_RISK
        for groupName, conditions in PERFORMANCE_GROUP_LIMITS.iteritems():
            for currentLimit in conditions:
                condValid = True
                systemStats = currentLimit.get(
                    EPIC_META_GAME_LIMIT_TYPE.SYSTEM_DATA, {})
                for key, limit in systemStats.iteritems():
                    currValue = stats.get(key, None)
                    if currValue is None or currValue != limit:
                        condValid = False

                hardwareParams = currentLimit.get(
                    EPIC_META_GAME_LIMIT_TYPE.HARDWARE_PARAMS, {})
                for key, limit in hardwareParams.iteritems():
                    currValue = BigWorld.getAutoDetectGraphicsSettingsScore(
                        key)
                    if currValue >= limit:
                        condValid = False

                if condValid:
                    self.__performanceGroup = groupName
                    return

        return

    def __onInventoryUpdate(self, invDiff):
        items = {
            GUI_ITEM_TYPE.VEHICLE, GUI_ITEM_TYPE.BATTLE_ABILITY,
            GUI_ITEM_TYPE.CUSTOMIZATION
        }
        if items.intersection(invDiff):
            self.__invalidateBattleAbilities()

    def __updateSounds(self, isEpicSoundMode):
        if isEpicSoundMode != self.__isEpicSoundMode:
            _FrontLineSounds.onChange(isEpicSoundMode)
            self.__isEpicSoundMode = isEpicSoundMode

    @async
    @process
    def __confirmFightButtonPressEnabled(self, callback):
        if not self.__isInValidPrebattle():
            callback(True)
            return
        defaults = AccountSettings.getFilterDefault(GUI_START_BEHAVIOR)
        filters = self.__settingsCore.serverSettings.getSection(
            GUI_START_BEHAVIOR, defaults)
        isEpicPerformanceWarningEnabled = not AccountSettings.getSettings(
            'isEpicPerformanceWarningClicked')
        if isEpicPerformanceWarningEnabled:
            result, checkboxChecked = yield DialogsInterface.showI18nCheckBoxDialog(
                'epicBattleConfirmDialog')
            filters['isEpicPerformanceWarningClicked'] = checkboxChecked
            AccountSettings.setSettings('isEpicPerformanceWarningClicked',
                                        checkboxChecked)
        else:
            result = True
        callback(result)

    def __showBrowserView(self, url):
        from gui.Scaleform.daapi.view.lobby.epicBattle.web_handlers import createFrontlineWebHandlers
        webHandlers = createFrontlineWebHandlers()
        alias = VIEW_ALIAS.BROWSER_VIEW
        g_eventBus.handleEvent(
            events.LoadViewEvent(SFViewLoadParams(alias,
                                                  getUniqueViewName(alias)),
                                 ctx={
                                     'url':
                                     url,
                                     'webHandlers':
                                     webHandlers,
                                     'returnAlias':
                                     VIEW_ALIAS.LOBBY_HANGAR,
                                     'onServerSettingsChange':
                                     self.__serverSettingsChangeBrowserHandler
                                 }), EVENT_BUS_SCOPE.LOBBY)

    def __serverSettingsChangeBrowserHandler(self, browser, diff):
        if not diff.get(Configs.EPIC_CONFIG.value, {}).get('isEnabled'):
            browser.onCloseView()

    @property
    def __metaSettings(self):
        return self.__lobbyContext.getServerSettings().epicMetaGame

    def __onTokensUpdate(self, diff):
        if any((key.startswith(EPIC_CHOICE_REWARD_OFFER_GIFT_TOKENS)
                for key in diff.keys())):
            pass
Beispiel #3
0
class BattlePassVotingRequester(object):
    __slots__ = ('__battlePassController', '__isAvailableService',
                 '__isStarted', '__requestNotifier', '__cache',
                 '__eventsManager', 'onVotingResultsUpdated')
    __webController = dependency.descriptor(IWebController)
    CALLBACK_REPEAT_TIME = 60

    def __init__(self, battlePassController):
        super(BattlePassVotingRequester, self).__init__()
        self.__battlePassController = weakref.proxy(battlePassController)
        self.__eventsManager = EventManager()
        self.__requestNotifier = SimpleNotifier(
            self.__getTimeToNotifyFailedRequest, self.__requestVotingData)
        self.__isStarted = False
        self.__isAvailableService = True
        self.__cache = {}
        self.onVotingResultsUpdated = Event(self.__eventsManager)

    def start(self):
        if not self.__isStarted:
            self.__isStarted = True
            self.__requestVotingData(self.__getDefaultSeasonIDs())

    def getVotingResult(self, seasonID):
        result = {}
        if self.__cache.get(seasonID):
            result = self.__cache[seasonID]
            if seasonID == self.__battlePassController.getSeasonID():
                self.__requestVotingData([seasonID])
        else:
            self.__requestVotingData([seasonID])
        return (self.__isAvailableService, result)

    def stop(self):
        self.__isStarted = False
        self.__requestNotifier.stopNotification()
        self.__eventsManager.clear()

    @process
    def __requestVotingData(self, seasons=None):
        if seasons is None:
            seasons = self.__getDefaultSeasonIDs()
        ctx = BattlePassGetVotingDataCtx(seasons)
        response = yield self.__webController.sendRequest(ctx=ctx)
        if not self.__isStarted:
            return
        else:
            self.__isAvailableService = response.isSuccess()
            result = {}
            if self.__isAvailableService:
                result = ctx.getDataObj(response.getData())
            else:
                _logger.warning(
                    'Bad response from Get Battle Pass Voting Data')
                self.__requestNotifier.startNotification()
            self.__updateCache(result)
            return

    def __updateCache(self, votingResults):
        isCacheUpdated = False
        for seasonID, votingResult in votingResults.iteritems():
            if not self.__cache.get(seasonID):
                self.__cache[seasonID] = votingResult
                isCacheUpdated = True
                continue
            for vehCD, countVoices in votingResult.iteritems():
                if self.__cache[seasonID].get(vehCD) != countVoices:
                    self.__cache[seasonID] = votingResult
                    isCacheUpdated = True
                    break

        if isCacheUpdated:
            self.onVotingResultsUpdated()

    def __getTimeToNotifyFailedRequest(self):
        return self.CALLBACK_REPEAT_TIME

    def __getDefaultSeasonIDs(self):
        result = [self.__battlePassController.getSeasonID()]
        allSeasonStats = self.__battlePassController.getPrevSeasonsStats()
        if not allSeasonStats:
            return result
        for seasonStats in allSeasonStats:
            result.append(seasonStats.seasonID)

        return result
class ResourceWellController(IResourceWellController, EventsHandler):
    __itemsCache = dependency.descriptor(IItemsCache)
    __lobbyContext = dependency.descriptor(ILobbyContext)

    def __init__(self):
        self.__eventsManager = EventManager()
        self.onEventUpdated = Event(self.__eventsManager)
        self.onSettingsChanged = Event(self.__eventsManager)
        self.onNumberRequesterUpdated = Event(self.__eventsManager)
        self.__notifier = SimpleNotifier(self.__getTimeLeft, self.__onEventStateChange)
        self.__serialNumberRequester = ResourceWellNumberRequester(isSerial=True)
        self.__regularNumberRequester = ResourceWellNumberRequester(isSerial=False)

    def onLobbyInited(self, event):
        self._subscribe()
        self.__notifier.startNotification()

    def onAvatarBecomePlayer(self):
        self.__stop()

    def onDisconnected(self):
        self.__stop()

    def fini(self):
        self.__eventsManager.clear()
        self.__serialNumberRequester.clear()
        self.__regularNumberRequester.clear()
        self.__stop()

    def isEnabled(self):
        return self.__getConfig().isEnabled

    def isActive(self):
        return self.isEnabled() and self.isStarted() and not self.isFinished()

    def isStarted(self):
        return self.__getStartTime() <= time_utils.getServerUTCTime()

    def isFinished(self):
        return self.getFinishTime() <= time_utils.getServerUTCTime()

    def isPaused(self):
        return not self.isEnabled() and self.isStarted() and not self.isFinished()

    def getSeason(self):
        return self.__getConfig().season

    def getRewardLimit(self, isTop):
        return findFirst(lambda reward: reward.isSerial == isTop, self.__getConfig().rewards.itervalues()).limit

    def getFinishTime(self):
        return self.__getConfig().finishTime

    def getCurrentPoints(self):
        return self.__itemsCache.items.resourceWell.getCurrentPoints()

    def getMaxPoints(self):
        return self.__getConfig().points

    def getRewardVehicle(self):
        return first(first(self.__getConfig().rewards.itervalues()).bonus.get('vehicles', {}).keys())

    def getRewardStyleID(self):
        topReward = findFirst(lambda reward: reward.isSerial, self.__getConfig().rewards.itervalues())
        return first(topReward.bonus['vehicles'].values())['customization'].get('styleId', 0)

    def getRewardSequence(self, isTop):
        return findFirst(lambda reward: reward.isSerial == isTop, self.__getConfig().rewards.itervalues()).sequence

    def getRewardLeftCount(self, isTop):
        return self.__getSerialRewardLeftCount() if isTop else self.__getRegularRewardLeftCount()

    def isRewardEnabled(self, isTop):
        topRewardLeftCount = self.getRewardLeftCount(isTop=True)
        return isTop and topRewardLeftCount or not (isTop or topRewardLeftCount)

    def isRewardCountAvailable(self, isTop=True):
        requester = self.__serialNumberRequester if isTop else self.__regularNumberRequester
        return requester.isDataAvailable()

    def getReminderTime(self):
        return self.__getConfig().remindTime

    def isCompleted(self):
        return self.__itemsCache.items.resourceWell.getReward() is not None

    def getResources(self):
        return self.__getConfig().resources

    def getRewards(self):
        return self.__getConfig().rewards

    def getRewardID(self, isTop):
        return findFirst(lambda (rewardID, reward): reward.isSerial == isTop, self.getRewards().iteritems())[0]

    def startNumberRequesters(self):
        if self.isEnabled():
            self.__serialNumberRequester.start()
            self.__regularNumberRequester.start()

    def stopNumberRequesters(self):
        self.__serialNumberRequester.stop()
        self.__regularNumberRequester.stop()

    def _getEvents(self):
        return ((self.__lobbyContext.getServerSettings().onServerSettingsChange, self.__onServerSettingsChanged), (self.__serialNumberRequester.onUpdated, self.__onRequesterUpdated), (self.__regularNumberRequester.onUpdated, self.__onRequesterUpdated))

    def __getConfig(self):
        return self.__lobbyContext.getServerSettings().resourceWellConfig

    def __getTimeLeft(self):
        if not self.isStarted():
            return max(0, self.__getStartTime() - time_utils.getServerUTCTime())
        return max(0, self.getFinishTime() - time_utils.getServerUTCTime()) if not self.isFinished() else 0

    def __getStartTime(self):
        return self.__getConfig().startTime

    def __onEventStateChange(self):
        self.onEventUpdated()

    def __getRegularRewardLeftCount(self):
        return self.__regularNumberRequester.getRemainingValues() or 0

    def __getSerialRewardLeftCount(self):
        if not self.__serialNumberRequester.isDataAvailable():
            return 0
        remainingValuesCount = self.__serialNumberRequester.getRemainingValues()
        givenValuesCount = self.__serialNumberRequester.getGivenValues()
        rewardLimit = self.getRewardLimit(True)
        if remainingValuesCount > rewardLimit or givenValuesCount > rewardLimit:
            _logger.error('remainingValuesCount and givenValuesCount cannot exceed rewardLimit!')
            return 0
        return remainingValuesCount if remainingValuesCount < rewardLimit / 2.0 else rewardLimit - givenValuesCount

    @serverSettingsChangeListener(Configs.RESOURCE_WELL.value)
    def __onServerSettingsChanged(self, diff):
        resourceWellDiff = diff[Configs.RESOURCE_WELL.value]
        if 'finishTime' in resourceWellDiff or 'startTime' in resourceWellDiff:
            self.__notifier.startNotification()
            self.onEventUpdated()
        if 'isEnabled' in resourceWellDiff:
            self.__notifier.startNotification()
            self.onEventUpdated()
        if not self.isActive():
            self.stopNumberRequesters()
        self.onSettingsChanged()

    def __onRequesterUpdated(self):
        self.onNumberRequesterUpdated()

    def __stop(self):
        self._unsubscribe()
        self.__notifier.stopNotification()
        self.stopNumberRequesters()
class BattlePassVideoProvider(object):
    __slots__ = ('onVideosConfigUpdated', '__videosConfig', '__eventsManager',
                 '__battlePassController', '__isStarted',
                 '__unlockVideoNotifier', '__requestVideoNotifier',
                 '__failedRequestCount', '__webController')
    SIZE_STORAGE = 16
    CALLBACK_REPEAT_TIME = 20
    __settingsCore = dependency.descriptor(ISettingsCore)

    def __init__(self, battlePassController):
        super(BattlePassVideoProvider, self).__init__()
        self.__battlePassController = weakref.proxy(battlePassController)
        self.__eventsManager = EventManager()
        self.__videosConfig = {}
        self.__unlockVideoNotifier = SimpleNotifier(
            self.__getTimeToNotifyUnlockVideo, self.__onNotifyUnlockVideo)
        self.__requestVideoNotifier = SimpleNotifier(
            self.__getTimeToNotifyFailedRequest, self.__onMakeRepeatRequest)
        self.__isStarted = False
        self.__failedRequestCount = 0
        self.__webController = None
        self.onVideosConfigUpdated = Event(self.__eventsManager)
        return

    @property
    def isStarted(self):
        return self.__isStarted

    def start(self):
        self.__webController = BattlePassWebController()
        self.__webController.init()
        self.__webController.invalidate()
        self.__isStarted = True
        self.__requestVideoData()
        self.__battlePassController.onLevelUp += self.__onLevelUp

    def stop(self):
        self.__isStarted = False
        self.__failedRequestCount = 0
        self.__battlePassController.onLevelUp -= self.__onLevelUp
        self.__unlockVideoNotifier.stopNotification()
        self.__requestVideoNotifier.stopNotification()
        if self.__webController is not None:
            self.__webController.fini()
        self.__webController = None
        return

    def getVideosTotal(self):
        return len(self.__videosConfig)

    def getAvailableVideosCount(self):
        return len(self.__getAvailableVideos())

    def hasUnviewedVideo(self):
        return True if self.getAvailableVideosCount() > len(
            self.getViewedVideos()) else False

    def setShownVideo(self, videoID):
        self.__saveShownVideoInStorage(videoID)
        self.onVideosConfigUpdated()

    def getViewedVideos(self):
        shownVideoFlags = self.__settingsCore.serverSettings.getBPStorage(
        ).get(BattlePassStorageKeys.SHOWN_VIDEOS_FLAGS)
        result = []
        for videoID, video in self.__videosConfig.items():
            videoIndex = video.get('idx')
            if shownVideoFlags & 1 << videoIndex:
                result.append(videoID)

        return result

    def __saveShownVideoInStorage(self, videoID):
        offset = self.__getShownFlagOffset(self.__videosConfig, videoID)
        if offset is None:
            _logger.warning('Failed to save shown video')
            return
        else:
            shownVideoFlags = self.__settingsCore.serverSettings.getBPStorage(
            ).get(BattlePassStorageKeys.SHOWN_VIDEOS_FLAGS)
            self.__settingsCore.serverSettings.saveInBPStorage({
                BattlePassStorageKeys.SHOWN_VIDEOS_FLAGS:
                shownVideoFlags | 1 << offset
            })
            return

    def __resetShownVideoInStorage(self, videoID):
        offset = self.__getShownFlagOffset(self.__videosConfig, videoID)
        if offset is None:
            _logger.warning('Failed to clear save shown video')
            return
        else:
            shownVideoFlags = self.__settingsCore.serverSettings.getBPStorage(
            ).get(BattlePassStorageKeys.SHOWN_VIDEOS_FLAGS)
            if shownVideoFlags:
                self.__settingsCore.serverSettings.saveInBPStorage({
                    BattlePassStorageKeys.SHOWN_VIDEOS_FLAGS:
                    shownVideoFlags & ~(1 << offset)
                })
            return

    def __getAvailableVideos(self):
        result = []
        for video in self.__videosConfig.values():
            if video.get('is_available'):
                result.append(video)

        return result

    def __getTimeToNotifyUnlockVideo(self):
        if not self.__videosConfig:
            return 0
        serverVideosIDs = self.__getVideoIndexAndIDFromConfig(
            self.__videosConfig)
        for videoIndex in sorted(serverVideosIDs.keys()):
            video = self.__videosConfig[serverVideosIDs[videoIndex]]
            if not video.get('is_available'):
                publicationTime = video.get('condition',
                                            {}).get('publication_start', 0)
                return max(0, publicationTime - time_utils.getServerUTCTime())

    def __updateVideosConfig(self):
        for video in self.__videosConfig.values():
            if not video.get('is_available'):
                publicationTime = video.get('publication_start')
                needLevel = video.get('level')
                needVote = video.get('need_vote')
                if publicationTime > time_utils.getServerUTCTime():
                    continue
                if needLevel > self.__battlePassController.getCurrentLevel():
                    continue
                if needVote and not self.__battlePassController.isPlayerVoted(
                ):
                    continue
                self.__requestVideoData()
                break

    def __onLevelUp(self):
        self.__updateVideosConfig()

    def __onNotifyUnlockVideo(self):
        self.__updateVideosConfig()

    def __getTimeToNotifyFailedRequest(self):
        time = self.CALLBACK_REPEAT_TIME * 2**self.__failedRequestCount
        self.__failedRequestCount += 1
        return time

    def __onMakeRepeatRequest(self):
        self.__requestVideoData()

    @process
    def __requestVideoData(self):
        if self.__battlePassController.isPlayerVoted():
            voteID = self.__battlePassController.getVoteOption()
            isBought = self.__isVotedWithBoughtBP()
        else:
            voteID = None
            isBought = self.__battlePassController.isBought()
        ctx = BattlePassGetVideoDataCtx(
            self.__battlePassController.getSeasonID(),
            self.__battlePassController.getCurrentLevel(), int(isBought),
            voteID)
        response = yield self.__webController.sendRequest(ctx=ctx)
        if not self.__isStarted:
            return
        else:
            if response.isSuccess():
                self.__videosConfig = ctx.getDataObj(response.getData())
                self.__checkConflicts()
                self.__unlockVideoNotifier.startNotification()
                self.__failedRequestCount = 0
                self.onVideosConfigUpdated()
            else:
                self.__requestVideoNotifier.startNotification()
            return

    def __checkConflicts(self):
        localConfig = AccountSettings.getSettings(BATTLE_PASS_VIDEOS_CONFIG)
        localVideosIDs = self.__getVideoIndexAndIDFromConfig(localConfig)
        serverVideosIDs = self.__getVideoIndexAndIDFromConfig(
            self.__videosConfig)
        if not localConfig:
            AccountSettings.setSettings(BATTLE_PASS_VIDEOS_CONFIG,
                                        self.__videosConfig)
            localVideosIDs = self.__getVideoIndexAndIDFromConfig(
                self.__videosConfig)
        for videoIdx in serverVideosIDs.keys():
            localVideoID = localVideosIDs.get(videoIdx)
            serverVideoID = serverVideosIDs[videoIdx]
            if localVideoID is None or localVideoID != serverVideoID:
                self.__resetShownVideoInStorage(serverVideoID)
                localConfig.pop(localVideoID, None)
                localConfig[serverVideoID] = self.__videosConfig[serverVideoID]
                AccountSettings.setSettings(BATTLE_PASS_VIDEOS_CONFIG,
                                            localConfig)

        return

    def __isVotedWithBoughtBP(self):
        return self.__settingsCore.serverSettings.getBPStorage().get(
            BattlePassStorageKeys.VOTED_WITH_BOUGHT_BP)

    @staticmethod
    def __getVideoIndexAndIDFromConfig(config):
        return {video.get('idx'): videoID for videoID, video in config.items()}

    @staticmethod
    def __getShownFlagOffset(config, videoID):
        video = config.get(videoID, None)
        if video is None:
            _logger.error(
                'Failed to get video! Check videos config are correct.')
            return
        else:
            return video.get('idx')
Beispiel #6
0
class EpicBattleMetaGameController(Notifiable, SeasonProvider, IEpicBattleMetaGameController, IGlobalListener):
    bootcampController = dependency.descriptor(IBootcampController)
    __itemsCache = dependency.descriptor(IItemsCache)
    __settingsCore = dependency.descriptor(ISettingsCore)
    __lobbyContext = dependency.descriptor(ILobbyContext)
    __battleResultsService = dependency.descriptor(IBattleResultsService)
    MAX_STORED_ARENAS_RESULTS = 20
    TOKEN_QUEST_ID = 'epicmetagame:levelup:'
    DAILY_QUEST_ID = 'front_line'
    MODE_ALIAS = 'frontline'

    def __init__(self):
        super(EpicBattleMetaGameController, self).__init__()
        self._setSeasonSettingsProvider(self.getModeSettings)
        self._setPrimeTimesIteratorGetter(self.getPrimeTimesIter)
        self.onUpdated = Event.Event()
        self.onPrimeTimeStatusUpdated = Event.Event()
        self.onEventEnded = Event.Event()
        self.__skillData = {}
        self.__playerMaxLevel = 0
        self.__playerMaxPrestigeLevel = 0
        self.__levelProgress = tuple()
        self.__isNow = False
        self.__inEpicPrebattle = False
        self.__performanceGroup = None
        self.__isFrSoundMode = False
        self.__rankSettings = {}
        self.__showedResultsForArenas = []
        self.__eventEndedNotifier = None
        return

    def init(self):
        super(EpicBattleMetaGameController, self).init()
        self.addNotificator(SimpleNotifier(self.getTimer, self.__timerUpdate))
        self.__eventEndedNotifier = SimpleNotifier(self.getEventTimeLeft, self.__onEventEnded)
        self.addNotificator(self.__eventEndedNotifier)

    def fini(self):
        del self.__showedResultsForArenas[:]
        self.onUpdated.clear()
        self.onPrimeTimeStatusUpdated.clear()
        self.clearNotification()
        self.stopGlobalListening()
        super(EpicBattleMetaGameController, self).fini()

    def onLobbyInited(self, ctx):
        self.__lobbyContext.getServerSettings().onServerSettingsChange += self.__updateEpicMetaGameSettings
        g_currentVehicle.onChanged += self.__invalidateBattleAbilities
        self.__itemsCache.onSyncCompleted += self.__invalidateBattleAbilities
        g_clientUpdateManager.addCallbacks({'epicMetaGame': self.__updateEpic,
         'inventory': self.__onInventoryUpdate})
        self.startGlobalListening()
        self.__setData()
        self.__invalidateBattleAbilities()
        self.startNotification()
        if self.getPerformanceGroup() == EPIC_PERF_GROUP.HIGH_RISK:
            self.__lobbyContext.addFightButtonConfirmator(self.__confirmFightButtonPressEnabled)
        self.__isFrSoundMode = False
        self.__updateSounds()

    def onDisconnected(self):
        self.__clear()

    def onPrbEntitySwitched(self):
        self.__invalidateBattleAbilities()
        self.__updateSounds()

    def onAccountBecomePlayer(self):
        self.__battleResultsService.onResultPosted += self.__showBattleResults

    def onAvatarBecomePlayer(self):
        self.__clear()
        self.__battleResultsService.onResultPosted -= self.__showBattleResults

    def isEnabled(self):
        return self.getModeSettings().isEnabled

    def isReservesAvailableInFLMenu(self):
        return self.getModeSettings().reservesAvailableInFLMenu

    def getPerformanceGroup(self):
        if not self.__performanceGroup:
            self.__analyzeClientSystem()
            _logger.debug('Current performance group %s', self.__performanceGroup)
        return self.__performanceGroup

    def getMaxPlayerLevel(self):
        return self.__playerMaxLevel

    def getMaxPlayerPrestigeLevel(self):
        return self.__playerMaxPrestigeLevel

    def getStageLimit(self):
        return self.__stageLimit

    def getPointsProgressForLevel(self, level):
        return self.__levelProgress[level]

    def getPointsForLevel(self, level):
        return sum((self.__levelProgress[level] for level in xrange(level - 1)))

    def getLevelProgress(self):
        return self.__levelProgress

    def getLevelForPoints(self, points):
        lvl = 0
        while points >= 0 and lvl <= self.__playerMaxLevel:
            points -= self.__levelProgress[lvl]
            lvl += 1

        return lvl - 1

    def getAllSkillsInformation(self):
        return self.__skillData

    def getPlayerLevelInfo(self):
        return self.__itemsCache.items.epicMetaGame.playerLevelInfo

    def getPlayerRanksInfo(self):
        if not self.__rankSettings:
            famePtsByRank = self.__getSettings().metaLevel.get('famePtsByRank', {})
            rankSettings = getRankSettings()
            self.__rankSettings = {rankLvl:(extraFamePts, rankSettings.bonus.factor100ByRank[rankLvl]) for rankLvl, extraFamePts in famePtsByRank.iteritems()}
        return self.__rankSettings

    def getSeasonData(self):
        return self.__itemsCache.items.epicMetaGame.seasonData

    def getSkillPoints(self):
        return self.__itemsCache.items.epicMetaGame.skillPoints

    def getSkillLevels(self):
        return self.__itemsCache.items.epicMetaGame.skillLevels

    def getSelectedSkills(self, vehicleCD):
        selected = self.__itemsCache.items.epicMetaGame.selectedSkills(vehicleCD)
        numSlots = self.getNumAbilitySlots(vehicles.getVehicleType(vehicleCD))
        while len(selected) < numSlots:
            selected.append(-1)

        return selected

    def hasSuitableVehicles(self):
        requiredLevel = self.getModeSettings().validVehicleLevels
        v = self.__itemsCache.items.getVehicles(REQ_CRITERIA.INVENTORY | REQ_CRITERIA.VEHICLE.LEVELS(requiredLevel))
        return len(v) > 0

    def increaseSkillLevel(self, skillID):
        BigWorld.player().epicMetaGame.increaseAbility(skillID)

    def changeEquippedSkills(self, skillIDArray, vehicleCD, callback=None):
        if callback is None:
            BigWorld.player().epicMetaGame.setSelectedAbilities(skillIDArray, vehicleCD)
        else:
            BigWorld.player().epicMetaGame.setSelectedAbilities(skillIDArray, vehicleCD, callback)
        return

    def getCycleInfo(self, cycleID=None):
        season = self.getCurrentSeason()
        if season is not None:
            cycleInfo = season.getCycleInfo(cycleID)
            if cycleInfo is not None:
                return cycleInfo
            _logger.warning('Cycle with id "%s" not found', cycleID)
        _logger.warning('No current season')
        return

    def getCycleOrdinalNumber(self, cycleID):
        cycleInfo = self.getCycleInfo(cycleID)
        return cycleInfo.ordinalNumber if cycleInfo else None

    def getSeasonTimeRange(self):
        season = self.getCurrentSeason() or self.getNextSeason()
        if season is not None:
            cycles = season.getAllCycles()
            if cycles:
                cycles = list(sorted(cycles.values(), key=lambda c: c.ordinalNumber))
                return (cycles[0].startDate, cycles[-1].endDate)
        return (0, 0)

    def getAllUnlockedSkillLevels(self):
        return chain.from_iterable((skill.getAllUnlockedSkillLevels() for skill in self.__skillData.itervalues()))

    def getAllUnlockedSkillLevelsBySkillId(self):
        return {skillID:skill.getAllUnlockedSkillLevels() for skillID, skill in self.__skillData.iteritems()}

    def getUnlockedAbilityIds(self):
        return (lvl.eqID for lvl in (skill.getMaxUnlockedSkillLevel() for skill in self.getAllSkillsInformation().itervalues()) if lvl is not None)

    def getStoredEpicDiscount(self):
        return BigWorld.player().epicMetaGame.getStoredDiscount()

    def isWelcomeScreenUpToDate(self, serverSettings):
        lastSeen = serverSettings.getSectionSettings(GUI_START_BEHAVIOR, 'lastShownEpicWelcomeScreen')
        currentVersion = getCurrentWelcomeScreenVersion()
        return lastSeen >= currentVersion

    def getEventTimeLeft(self):
        timeLeft = self.getSeasonTimeRange()[1] - time_utils.getCurrentLocalServerTimestamp()
        return timeLeft + 1 if timeLeft > 0 else time_utils.ONE_MINUTE

    def getStats(self):
        return self.__itemsCache.items.epicMetaGame

    def getNumAbilitySlots(self, vehicleType):
        config = self.__lobbyContext.getServerSettings().epicMetaGame
        vehClass = getVehicleClassFromVehicleType(vehicleType)
        return config.defaultSlots.get(vehClass, 0)

    def __invalidateBattleAbilities(self, *_):
        if not self.__itemsCache.isSynced():
            return
        self.__invalidateBattleAbilityItems()
        self.__invalidateBattleAbilitiesForVehicle()

    def __setData(self):
        self.__skillData = {}
        skills = self.__getSettings().rewards.get('combatReserves', {})
        maxSkillLvl = self.__getSettings().maxCombatReserveLevel
        eqs = vehicles.g_cache.equipments()
        if skills != {}:
            for key, value in skills.iteritems():
                self.__skillData[key] = EpicMetaGameSkill(key, maxSkillLvl)
                lvls = value['levels']
                lvlAmount = len(lvls)
                found = 0
                for eq in eqs.values():
                    if eq.name in lvls:
                        lvl = lvls.index(eq.name) + 1
                        self.__skillData[key].levels[lvl] = EpicMetaGameSkillLevel(lvl, eq.id[1], i18n.makeString(eq.userString), i18n.makeString(eq.shortDescription), i18n.makeString(eq.longDescription), i18n.makeString(eq.shortFilterAlert), i18n.makeString(eq.longFilterAlert), eq.icon[0])
                        found += 1
                        if found == lvlAmount:
                            break

        metaLevel = self.__getSettings().metaLevel
        self.__playerMaxLevel = metaLevel.get('maxLevel', 0)
        self.__playerMaxPrestigeLevel = metaLevel.get('maxPrestigeLevel', 0)
        self.__stageLimit = metaLevel.get('stageLimit', -1)
        levelProgress = metaLevel.get('famePtsToProgress', [])[:]
        levelProgress.insert(0, 0)
        self.__levelProgress = tuple(levelProgress)

    def __clear(self):
        self.stopNotification()
        self.stopGlobalListening()
        self.__lobbyContext.getServerSettings().onServerSettingsChange -= self.__updateEpicMetaGameSettings
        g_currentVehicle.onChanged -= self.__invalidateBattleAbilities
        self.__itemsCache.onSyncCompleted -= self.__invalidateBattleAbilities
        g_clientUpdateManager.removeObjectCallbacks(self)
        if self.getPerformanceGroup() == EPIC_PERF_GROUP.HIGH_RISK:
            self.__lobbyContext.deleteFightButtonConfirmator(self.__confirmFightButtonPressEnabled)

    def __updateEpic(self, diff):
        changes = set(diff.keys())
        self.__invalidateBattleAbilities()
        if changes:
            self.onUpdated(diff)

    def __updateEpicMetaGameSettings(self, diff):
        if 'epic_config' in diff:
            self.__setData()
            self.onUpdated(diff)
            self.__resetTimer()

    def __resetTimer(self):
        self.startNotification()
        self.__timerUpdate()

    def __timerUpdate(self):
        status, _, _ = self.getPrimeTimeStatus()
        self.onPrimeTimeStatusUpdated(status)

    def __onEventEnded(self):
        self.onEventEnded()
        self.__eventEndedNotifier.stopNotification()
        self.__eventEndedNotifier.clear()

    def __showBattleResults(self, reusableInfo, _):
        if reusableInfo.common.arenaBonusType == ARENA_BONUS_TYPE.EPIC_BATTLE:
            arenaUniqueID = reusableInfo.arenaUniqueID
            if arenaUniqueID not in self.__showedResultsForArenas:
                self.__showedResultsForArenas.append(arenaUniqueID)
                self.__showedResultsForArenas = self.__showedResultsForArenas[-self.MAX_STORED_ARENAS_RESULTS:]
                event_dispatcher.showEpicBattlesAfterBattleWindow(reusableInfo)

    def __isInValidPrebattle(self):
        if g_prbLoader and g_prbLoader.getDispatcher() and g_prbLoader.getDispatcher().getEntity():
            currentPrbEntity = g_prbLoader.getDispatcher().getEntity().getEntityType()
            return currentPrbEntity in (QUEUE_TYPE.EPIC, PREBATTLE_TYPE.EPIC, PREBATTLE_TYPE.EPIC_TRAINING)
        else:
            return None

    def __invalidateBattleAbilityItems(self):
        data = self.__itemsCache.items.getItems(GUI_ITEM_TYPE.BATTLE_ABILITY, REQ_CRITERIA.EMPTY)
        for item in data.values():
            if self.__isInValidPrebattle():
                newLevel = next((lvl.level for lvl in chain.from_iterable((skillInfo.levels.itervalues() for skillInfo in self.getAllSkillsInformation().itervalues())) if lvl.eqID == item.innationID), 0)
                item.setLevel(newLevel)
                item.isUnlocked = item.innationID in self.getUnlockedAbilityIds()
            item.setLevel(0)
            item.isUnlocked = False

    def __invalidateBattleAbilitiesForVehicle(self):
        vehicle = g_currentVehicle.item
        if vehicle is None or vehicle.descriptor.type.level not in self.__lobbyContext.getServerSettings().epicBattles.validVehicleLevels or not self.__isInValidPrebattle():
            return
        else:
            amountOfSlots = self.getNumAbilitySlots(vehicle.descriptor.type)
            selectedItems = [None] * amountOfSlots
            skillInfo = self.getAllSkillsInformation()
            selectedSkills = self.getSelectedSkills(vehicle.intCD)
            battleAbilities = self.__itemsCache.items.getItems(GUI_ITEM_TYPE.BATTLE_ABILITY, REQ_CRITERIA.EMPTY)
            for item in battleAbilities.values():
                for index, skillID in enumerate(selectedSkills):
                    if skillID is not None and skillID >= 0:
                        if skillInfo[skillID].getMaxUnlockedSkillLevel() and item.innationID == skillInfo[skillID].getMaxUnlockedSkillLevel().eqID:
                            selectedItems[index] = item

            vehicle.battleAbilities.setLayout(*selectedItems)
            vehicle.battleAbilities.setInstalled(*selectedItems)
            return

    def __analyzeClientSystem(self):
        stats = BigWorld.wg_getClientStatistics()
        stats['graphicsEngine'] = self.__settingsCore.getSetting(GRAPHICS.RENDER_PIPELINE)
        self.__performanceGroup = EPIC_PERF_GROUP.LOW_RISK
        for groupName, conditions in PERFORMANCE_GROUP_LIMITS.iteritems():
            for currentLimit in conditions:
                condValid = True
                systemStats = currentLimit.get(EPIC_META_GAME_LIMIT_TYPE.SYSTEM_DATA, {})
                for key, limit in systemStats.iteritems():
                    currValue = stats.get(key, None)
                    if currValue is None or currValue != limit:
                        condValid = False

                hardwareParams = currentLimit.get(EPIC_META_GAME_LIMIT_TYPE.HARDWARE_PARAMS, {})
                for key, limit in hardwareParams.iteritems():
                    currValue = BigWorld.getAutoDetectGraphicsSettingsScore(key)
                    if currValue >= limit:
                        condValid = False

                if condValid:
                    self.__performanceGroup = groupName
                    return

        return

    def __onInventoryUpdate(self, invDiff):
        if GUI_ITEM_TYPE.VEHICLE or GUI_ITEM_TYPE.BATTLE_ABILITY or GUI_ITEM_TYPE.CUSTOMIZATION in invDiff:
            self.__invalidateBattleAbilities()

    def __updateSounds(self):
        if self.prbEntity is None:
            return
        else:
            isFrSoundMode = bool(self.prbEntity.getModeFlags() & FUNCTIONAL_FLAG.EPIC)
            if isFrSoundMode != self.__isFrSoundMode:
                _FrontLineSounds.onChange(isFrSoundMode)
                self.__isFrSoundMode = isFrSoundMode
            return

    def getPrimeTimesIter(self, primeTimes):
        for primeTime in primeTimes:
            yield primeTime

    @async
    @process
    def __confirmFightButtonPressEnabled(self, callback):
        if not self.__isInValidPrebattle():
            callback(True)
            return
        defaults = AccountSettings.getFilterDefault(GUI_START_BEHAVIOR)
        filters = self.__settingsCore.serverSettings.getSection(GUI_START_BEHAVIOR, defaults)
        isEpicPerformanceWarningEnabled = not AccountSettings.getSettings('isEpicPerformanceWarningClicked')
        if isEpicPerformanceWarningEnabled:
            result, checkboxChecked = yield DialogsInterface.showI18nCheckBoxDialog('epicBattleConfirmDialog')
            filters['isEpicPerformanceWarningClicked'] = checkboxChecked
            AccountSettings.setSettings('isEpicPerformanceWarningClicked', checkboxChecked)
        else:
            result = True
        callback(result)

    @staticmethod
    def __getSettings():
        lobbyContext = dependency.instance(ILobbyContext)
        generalSettings = lobbyContext.getServerSettings().epicMetaGame
        return generalSettings

    @staticmethod
    def getModeSettings():
        lobbyContext = dependency.instance(ILobbyContext)
        generalSettings = lobbyContext.getServerSettings().epicBattles
        return generalSettings
Beispiel #7
0
class BattlePassController(IBattlePassController):
    __itemsCache = dependency.descriptor(IItemsCache)
    __lobbyContext = dependency.descriptor(ILobbyContext)
    __offersProvider = dependency.descriptor(IOffersDataProvider)

    def __init__(self):
        self.__oldPoints = 0
        self.__oldLevel = 0
        self.__currentMode = None
        self.__eventsManager = EventManager()
        self.__seasonChangeNotifier = SimpleNotifier(
            self.__getTimeToNotifySeasonChange, self.__onNotifySeasonChange)
        self.onPointsUpdated = Event(self.__eventsManager)
        self.onLevelUp = Event(self.__eventsManager)
        self.onBattlePassIsBought = Event(self.__eventsManager)
        self.onSeasonStateChange = Event(self.__eventsManager)
        self.onBattlePassSettingsChange = Event(self.__eventsManager)
        self.onFinalRewardStateChange = Event(self.__eventsManager)
        self.onDeviceSelectChange = Event(self.__eventsManager)
        self.onOffersUpdated = Event(self.__eventsManager)
        self.onRewardSelectChange = Event(self.__eventsManager)
        self.__nonSelectedOldTrophyDeviceNotifier = NonSelectedOldTrophyDeviceNotifier(
            self)
        self.__rewardLogic = None
        return

    def init(self):
        super(BattlePassController, self).init()
        g_clientUpdateManager.addCallbacks({'tokens': self.__onTokensUpdate})
        self.__rewardLogic = BattlePassRewardLogic(BattlePassStateMachine())
        BattlePassAwardsManager.init()

    def onLobbyInited(self, event):
        self.__lobbyContext.getServerSettings(
        ).onServerSettingsChange += self.__onServerSettingsChange
        self.__itemsCache.onSyncCompleted += self.__onSyncCompleted
        self.__offersProvider.onOffersUpdated += self.__onOffersUpdated
        self.__seasonChangeNotifier.startNotification()
        self.__rewardLogic.start()
        if self.__currentMode is None:
            self.__currentMode = self.__getConfig().mode
        else:
            self.onBattlePassSettingsChange(self.__getConfig().mode,
                                            self.__currentMode)
            self.__currentMode = self.__getConfig().mode
        self.__nonSelectedOldTrophyDeviceNotifier.start()
        return

    def onAvatarBecomePlayer(self):
        self.__stop()

    def onDisconnected(self):
        self.__stop()
        self.__clearFields()
        self.__rewardLogic.stop()

    def fini(self):
        self.__stop()
        self.__rewardLogic.stop()
        self.__clearFields()
        self.__eventsManager.clear()
        g_clientUpdateManager.removeObjectCallbacks(self)
        super(BattlePassController, self).fini()

    def isBought(self, seasonID=None, chapter=None):
        if seasonID is None:
            seasonID = self.getSeasonID()
        if chapter is None:
            chapter = self.getCurrentChapter()
        tokenForAllBP = self.__itemsCache.items.tokens.getTokens().get(
            getBattlePassPassTokenName(seasonID, 0))
        if tokenForAllBP is not None:
            return True
        else:
            token = self.__itemsCache.items.tokens.getTokens().get(
                getBattlePassPassTokenName(seasonID, chapter))
            return token is not None

    def isOfferEnabled(self):
        return self.__lobbyContext.getServerSettings().isOffersEnabled()

    def isEnabled(self):
        return self.__getConfig().isEnabled()

    def isActive(self):
        return self.__getConfig().isActive(time_utils.getServerUTCTime())

    def isVisible(self):
        return self.isSeasonStarted(
        ) and not self.isDisabled() and not self.isSeasonFinished()

    def isOffSeasonEnable(self):
        return False

    def isDisabled(self):
        return not self.isActive() and not self.isPaused()

    def isPaused(self):
        return self.__getConfig().isPaused()

    def isSeasonStarted(self):
        return self.__getConfig().seasonStart <= time_utils.getServerUTCTime()

    def isSeasonFinished(self):
        return self.__getConfig().seasonFinish <= time_utils.getServerUTCTime()

    def isValidBattleType(self, prbEntity):
        return prbEntity.getQueueType() in (constants.QUEUE_TYPE.RANDOMS,
                                            constants.QUEUE_TYPE.MAPBOX)

    def isGameModeEnabled(self, arenaBonusType):
        return self.__getConfig().isGameModeEnabled(arenaBonusType)

    def getSupportedArenaBonusTypes(self):
        return [arenaBonusType for arenaBonusType in self.__getConfig().points]

    def getMaxLevel(self):
        return self.__getConfig().maxBaseLevel

    def isRareLevel(self, level):
        realLevel = min(level, self.getMaxLevel())
        tags = self.__getConfig().getTags(realLevel,
                                          BattlePassConsts.REWARD_PAID)
        return BattlePassConsts.RARE_REWARD_TAG in tags

    def isFinalLevel(self, level):
        realLevel = min(level, self.getMaxLevel())
        return realLevel in self.getChapterConfig()

    def getOldTrophySelectTokensCount(self):
        return self.__itemsCache.items.tokens.getTokenCount(
            BATTLE_PASS_TOKEN_TROPHY_GIFT_OFFER_2020)

    def getOldNewDeviceSelectTokensCount(self):
        return self.__itemsCache.items.tokens.getTokenCount(
            BATTLE_PASS_TOKEN_NEW_DEVICE_GIFT_OFFER_2020)

    def getRewardLogic(self):
        return self.__rewardLogic

    def getSingleAward(self,
                       level,
                       awardType=BattlePassConsts.REWARD_FREE,
                       needSort=True):
        reward = {}
        if awardType in (BattlePassConsts.REWARD_FREE,
                         BattlePassConsts.REWARD_PAID):
            reward = self.__getConfig().getRewardByType(level, awardType)
        elif awardType == BattlePassConsts.REWARD_BOTH:
            rewards = [
                self.__getConfig().getFreeReward(level),
                self.__getConfig().getPaidReward(level)
            ]
            return BattlePassAwardsManager.hideInvisible(
                BattlePassAwardsManager.composeBonuses(rewards))
        if needSort:
            rewards = BattlePassAwardsManager.composeBonuses([reward])
        else:
            rewards = awardsFactory(reward)
        return BattlePassAwardsManager.hideInvisible(rewards,
                                                     needSplit=not needSort)

    def getAwardsInterval(self,
                          fromLevel,
                          toLevel,
                          awardType=BattlePassConsts.REWARD_FREE):
        result = {}
        for level in range(fromLevel, toLevel + 1):
            result[level] = self.getSingleAward(level, awardType, True)

        return result

    def getPackedAwardsInterval(self,
                                fromLevel,
                                toLevel,
                                awardType=BattlePassConsts.REWARD_FREE):
        result = []
        for level in range(fromLevel, toLevel + 1):
            result.extend(self.getSingleAward(level, awardType, False))

        return BattlePassAwardsManager.sortBonuses(result)

    def isNeedToTakeReward(self, awardType, level):
        bonuses = self.getSingleAward(level, awardType)
        if level > self.getCurrentLevel():
            return False
        else:
            for bonus in bonuses:
                if bonus.getName() in (BATTLE_PASS_SELECT_BONUS_NAME,
                                       BATTLE_PASS_STYLE_PROGRESS_BONUS_NAME):
                    for tokenID in bonus.getTokens().iterkeys():
                        if self.__itemsCache.items.tokens.getToken(
                                tokenID) is not None:
                            return True

            return False

    def replaceOfferByReward(self, bonuses):
        result = []
        for bonus in bonuses:
            if bonus.getName() == BATTLE_PASS_SELECT_BONUS_NAME:
                hasGift = False
                for tokenID in bonus.getTokens().iterkeys():
                    offerToken = getOfferTokenByGift(tokenID)
                    offer = self.__offersProvider.getOfferByToken(offerToken)
                    if offer is not None:
                        receivedGifts = self.__offersProvider.getReceivedGifts(
                            offer.id)
                        if receivedGifts:
                            for giftId, count in receivedGifts.iteritems():
                                if count > 0:
                                    gift = offer.getGift(giftId)
                                    if gift is not None:
                                        hasGift = True
                                        result.extend(gift.bonuses)

                if not hasGift:
                    result.append(bonus)
            result.append(bonus)

        return result

    def isChooseRewardEnabled(self, awardType, level):
        if level > self.getCurrentLevel():
            return False
        else:
            bonuses = self.getSingleAward(level, awardType)
            for bonus in bonuses:
                if bonus.getName() == BATTLE_PASS_STYLE_PROGRESS_BONUS_NAME:
                    return True
                if bonus.getName() == BATTLE_PASS_SELECT_BONUS_NAME:
                    for tokenID in bonus.getTokens().iterkeys():
                        if self.__itemsCache.items.tokens.getToken(
                                tokenID) is not None:
                            return self.isOfferEnabled(
                            ) and self.__offersProvider.getOfferByToken(
                                getOfferTokenByGift(tokenID)) is not None

            return False

    def canChooseAnyReward(self):
        return False if not self.isOfferEnabled() else any((
            token.startswith(BATTLE_PASS_CHOICE_REWARD_OFFER_GIFT_TOKENS)
            for token in self.__itemsCache.items.tokens.getTokens().iterkeys()
            if self.__offersProvider.getOfferByToken(getOfferTokenByGift(
                token)) is not None))

    def getLevelsConfig(self):
        return self.__getConfig().basePoints

    def getFinalRewards(self):
        return {}

    def getFreeFinalRewardDict(self):
        return self.__getConfig().getRewardByType(self.getMaxLevel(),
                                                  BattlePassConsts.REWARD_FREE)

    def getCurrentPoints(self):
        return self.__itemsCache.items.battlePass.getPoints()

    def getMaxPoints(self):
        return self.__getConfig().maxBasePoints

    def getCurrentLevel(self):
        return self.__itemsCache.items.battlePass.getCurrentLevel()

    def getCurrentChapter(self):
        return self.__getConfig().getChapter(self.getCurrentLevel())

    def getChapterByLevel(self, level):
        return self.__getConfig().getChapter(level)

    def getState(self):
        return self.__itemsCache.items.battlePass.getState()

    def getPrevSeasonsStats(self):
        packedStats = self.__itemsCache.items.battlePass.getPackedStats()
        if not packedStats:
            return None
        else:
            unpackStats, _ = BattlePassStatsCommon.unpackAllSeasonStats(
                packedStats)
            return unpackStats

    def getLastFinishedSeasonStats(self):
        allSeasonStats = self.getPrevSeasonsStats()
        if not allSeasonStats:
            seasons = sorted(self.getSeasonsHistory().keys(), reverse=True)
            return BattlePassStatsCommon.makeSeasonStats(
                first(seasons), {},
                BattlePassStatsCommon.initialSeasonStatsData())
        return allSeasonStats[-1]

    def getSeasonsHistory(self):
        return self.__getConfig().seasonsHistory

    def getLevelPoints(self, level):
        levelsConfig = self.getLevelsConfig()
        return levelsConfig[
            0] if level <= 0 else levelsConfig[level] - levelsConfig[level - 1]

    def getFullChapterPoints(self, chapter, includeCurrent):
        levelsConfig = self.getLevelsConfig()
        minLevel, maxLevel = self.getChapterLevelInterval(chapter)
        if minLevel == maxLevel == 0:
            return 0
        if includeCurrent:
            return levelsConfig[maxLevel - 1]
        return 0 if minLevel <= 1 else levelsConfig[minLevel - 2]

    def getLevelProgression(self):
        if self.isDisabled():
            return (0, 0)
        level = self.getCurrentLevel()
        if level >= self.getMaxLevel():
            levelsConfig = self.getLevelsConfig()
            points = levelsConfig[-1] - levelsConfig[-2]
            return (points, points)
        points = self.getCurrentPoints()
        levelsConfig = self.getLevelsConfig()
        return getLevelProgression(level, points, levelsConfig)

    def getLevelByPoints(self, points):
        if points >= self.getMaxPoints():
            level = self.getMaxLevel()
        else:
            levelsConfig = self.getLevelsConfig()
            level = getLevel(curPoints=points, levelPoints=levelsConfig)
        chapter = self.__getConfig().getChapter(level)
        return (chapter, level)

    def getProgressionByPoints(self, points, level):
        levelsConfig = self.getLevelsConfig()
        if level >= self.getMaxLevel():
            levelPoints = fullLevelPoints = levelsConfig[-1] - levelsConfig[-2]
        else:
            levelPoints, fullLevelPoints = getLevelProgression(
                level, points, levelsConfig)
        return (levelPoints, fullLevelPoints)

    def getPerBattlePoints(self,
                           gameMode=constants.ARENA_BONUS_TYPE.REGULAR,
                           vehCompDesc=None):
        winList = self.__getPackedBonusPointsList(vehTypeCompDescr=vehCompDesc,
                                                  gameMode=gameMode)
        lostList = self.__getPackedBonusPointsList(
            vehTypeCompDescr=vehCompDesc, isWinner=False, gameMode=gameMode)
        count = 0
        result = []
        for winInfo, lostInfo in zip(winList, lostList):
            pointsWin, pointsCount = winInfo
            pointsLost, _ = lostInfo
            count += pointsCount
            if pointsWin > 0:
                item = TopPoints(count, pointsWin, pointsLost)
                result.append(item)

        return result

    def getPerBattleRoyalePoints(
            self,
            gameMode=constants.ARENA_BONUS_TYPE.BATTLE_ROYALE_SOLO,
            vehCompDesc=None):
        winList = self.__getPackedBonusPointsList(vehTypeCompDescr=vehCompDesc,
                                                  gameMode=gameMode)
        pointList = self.__getPackedBonusPointsList(
            vehTypeCompDescr=vehCompDesc, isWinner=False, gameMode=gameMode)
        count = 0
        result = []
        if not winList or not pointList:
            _logger.error(
                'Failed to get bonus points information! Check server settings are correct for Battle Royale.'
            )
            return result
        pointList[0] = winList[0]
        for item in pointList:
            points, pointsCount = item
            count += pointsCount
            if points > 0:
                result.append(BattleRoyaleTopPoints(count, points))

        return result

    def getChapterConfig(self):
        return self.__getConfig().finalLevelsInChapter

    def getChapterLevelInterval(self, chapter):
        chapterConfig = self.getChapterConfig()
        if chapter < BattlePassConsts.MINIMAL_CHAPTER_NUMBER or chapter > len(
                chapterConfig):
            return (0, 0)
        fromLevel = 1 if chapter == BattlePassConsts.MINIMAL_CHAPTER_NUMBER else chapterConfig[
            chapter - 2] + 1
        toLevel = chapterConfig[chapter - 1]
        return (fromLevel, toLevel)

    def isSpecialVehicle(self, intCD):
        return self.__getConfig().isSpecialVehicle(intCD)

    def getSpecialVehicles(self):
        return self.__getConfig().getSpecialVehicles()

    def getPointsDiffForVehicle(self,
                                intCD,
                                gameMode=constants.ARENA_BONUS_TYPE.REGULAR):
        defaultWinList = self.__getPackedBonusPointsList(gameMode=gameMode)
        diffWinList = self.__getPackedBonusPointsList(vehTypeCompDescr=intCD,
                                                      isDiff=True,
                                                      gameMode=gameMode)
        if not defaultWinList or not diffWinList:
            _logger.error(
                'Failed to get bonus points information! Check server settings are correct.'
            )
            return PointsDifference(0, 0, 0)
        diffBlock = diffWinList[0]
        bonus = diffBlock[0]
        top = diffBlock[1]
        textID = getPointsInfoStringID()
        return PointsDifference(bonus, top, textID)

    def getVehicleProgression(self, intCD):
        points = self.__itemsCache.items.battlePass.getPointsForVehicle(
            intCD, 0)
        cap = self.__getConfig().vehicleCapacity(intCD)
        return (points, cap)

    def getVehicleCapBonus(self, intCD):
        vehicle = self.__itemsCache.items.getItemByCD(intCD)
        if vehicle is None:
            return 0
        else:
            bonus = self.__getConfig().capBonus(vehicle.level)
            return bonus

    def getCapacityList(self):
        capacities = self.__getConfig().capacityList()
        return enumerate(capacities, 1)

    def getSeasonTimeLeft(self):
        return max(0,
                   self.getSeasonFinishTime() - time_utils.getServerUTCTime())

    def getFinalOfferTimeLeft(self):
        return max(0, self.getFinalOfferTime() - time_utils.getServerUTCTime())

    def getSeasonStartTime(self):
        return self.__getConfig().seasonStart

    def getSeasonFinishTime(self):
        return self.__getConfig().seasonFinish

    def hasMaxPointsOnVehicle(self, intCD):
        currentPoints, limitPoints = self.getVehicleProgression(intCD)
        return currentPoints >= limitPoints > 0

    def isProgressionOnVehiclePossible(self, intCD):
        cap = self.__getConfig().vehicleCapacity(intCD)
        return cap > 0

    def getSeasonID(self):
        return self.__itemsCache.items.battlePass.getSeasonID()

    def getSeasonNum(self):
        return self.__getConfig().seasonNum

    def getFinalOfferTime(self):
        return self.__getConfig().finalOfferTime

    def getStylesConfig(self):
        rewards = self.__getConfig().selectedReward
        return BattlePassAwardsManager.composeBonuses([
            rewards
        ]) if rewards else BattlePassAwardsManager.composeBonuses([])

    def getNotChosenRewardCount(self):
        return sum((
            token.startswith(BATTLE_PASS_CHOICE_REWARD_OFFER_GIFT_TOKENS)
            for token in self.__itemsCache.items.tokens.getTokens().iterkeys()
            if self.__offersProvider.getOfferByToken(getOfferTokenByGift(
                token)) is not None))

    def hasAnyOfferGiftToken(self):
        return any(
            (token.startswith(BATTLE_PASS_CHOICE_REWARD_OFFER_GIFT_TOKENS) for
             token in self.__itemsCache.items.tokens.getTokens().iterkeys()))

    def takeRewardForLevel(self, level):
        chapter = self.getChapterByLevel(level - 1)
        isBought = self.isBought(chapter=chapter)
        awardType = BattlePassConsts.REWARD_BOTH if isBought else BattlePassConsts.REWARD_FREE
        isOfferEnabled = self.isOfferEnabled()
        bonuses = self.getSingleAward(level, awardType)
        rewardsToChoose = []
        stylesToChoose = []
        for bonus in bonuses:
            bonusName = bonus.getName()
            if bonusName == BATTLE_PASS_SELECT_BONUS_NAME and isOfferEnabled:
                for tokenID in bonus.getTokens().iterkeys():
                    if self.__itemsCache.items.tokens.getToken(
                            tokenID
                    ) is not None and self.__offersProvider.getOfferByToken(
                            getOfferTokenByGift(tokenID)) is not None:
                        rewardsToChoose.append(tokenID)

            if bonusName == BATTLE_PASS_STYLE_PROGRESS_BONUS_NAME:
                for tokenID in bonus.getTokens().iterkeys():
                    if self.__itemsCache.items.tokens.getToken(
                            tokenID) is not None:
                        chapter = bonus.getChapter()
                        if chapter not in stylesToChoose:
                            stylesToChoose.append(chapter)

        rewardsToChoose.sort(
            key=lambda x: (int(x.split(':')[-1]), x.split(':')[-2]))
        self.getRewardLogic().startManualFlow(rewardsToChoose, stylesToChoose)
        return

    def takeAllRewards(self):
        if self.isOfferEnabled():
            rewardsToChoose = [
                token for token in
                self.__itemsCache.items.tokens.getTokens().iterkeys()
                if token.startswith(BATTLE_PASS_CHOICE_REWARD_OFFER_GIFT_TOKENS
                                    ) and self.__offersProvider.
                getOfferByToken(getOfferTokenByGift(token)) is not None
            ]
            rewardsToChoose.sort(
                key=lambda x: (int(x.split(':')[-1]), x.split(':')[-2]))
        else:
            rewardsToChoose = []
        chapter = self.getCurrentChapter()
        stylesToChoose = getStylesToChooseUntilChapter(chapter + 1)
        self.getRewardLogic().startManualFlow(rewardsToChoose, stylesToChoose)
        return

    def getChapterStyleProgress(self, chapter):
        return getMaxAvalable3DStyleProgressInChapter(
            self.getSeasonID(), chapter,
            self.__itemsCache.items.tokens.getTokens().keys())

    def __stop(self):
        self.__seasonChangeNotifier.stopNotification()
        self.__itemsCache.onSyncCompleted -= self.__onSyncCompleted
        self.__lobbyContext.getServerSettings(
        ).onServerSettingsChange -= self.__onServerSettingsChange
        self.__offersProvider.onOffersUpdated -= self.__onOffersUpdated
        self.__nonSelectedOldTrophyDeviceNotifier.stop()

    def __getConfig(self):
        return self.__lobbyContext.getServerSettings().getBattlePassConfig()

    def __onTokensUpdate(self, diff):
        for chapter, _ in enumerate(self.getChapterConfig(),
                                    BattlePassConsts.MINIMAL_CHAPTER_NUMBER):
            if getBattlePassPassTokenName(self.getSeasonID(), chapter) in diff:
                self.onBattlePassIsBought()
                break

        if BATTLE_PASS_TOKEN_TROPHY_GIFT_OFFER_2020 in diff or BATTLE_PASS_TOKEN_NEW_DEVICE_GIFT_OFFER_2020 in diff:
            self.onDeviceSelectChange()

    def __getTimeUntilStart(self):
        return max(
            0,
            self.__getConfig().seasonStart - time_utils.getServerUTCTime())

    def __getTimeToNotifySeasonChange(self):
        if not self.isPaused():
            if not self.isSeasonStarted():
                return self.__getTimeUntilStart()
            if not self.isSeasonFinished():
                return self.getSeasonTimeLeft()

    def __onNotifySeasonChange(self):
        self.onSeasonStateChange()

    def __onServerSettingsChange(self, diff):
        if BATTLE_PASS_CONFIG_NAME in diff:
            self.__seasonChangeNotifier.startNotification()
            newMode = None
            oldMode = self.__currentMode
            if 'mode' in diff[BATTLE_PASS_CONFIG_NAME]:
                newMode = diff[BATTLE_PASS_CONFIG_NAME]['mode']
                self.__currentMode = newMode
            self.onBattlePassSettingsChange(newMode, oldMode)
        if 'isOffersEnabled' in diff:
            self.__onOffersUpdated()
            self.onDeviceSelectChange()
        return

    def __onSyncCompleted(self, _, diff):
        if 'battlePass' in diff:
            newPoints = diff['battlePass'].get('sumPoints', self.__oldPoints)
            newLevel = diff['battlePass'].get('level', self.__oldLevel)
            if newPoints != self.__oldPoints:
                self.onPointsUpdated()
            if newLevel != self.__oldLevel:
                self.onLevelUp()
            self.__oldPoints = newPoints
            self.__oldLevel = newLevel

    def __onOffersUpdated(self):
        self.__validateOffers()
        self.onOffersUpdated()

    def __validateOffers(self):
        for offer in self.__offersProvider.iAvailableOffers(False):
            if not offer.token.startswith(BATTLE_PASS_OFFER_TOKEN_PREFIX):
                continue
            counts = {gift.giftCount for gift in offer.getAllGifts()}
            if len(counts) > 1:
                _logger.error('Wrong bonus count in gifts. Offer token %s',
                              offer.token)

    @staticmethod
    def __bonusPointsDiffList(vehTypeCompDescr, config, gameMode):
        defaultPoints = config.points.get(gameMode, {})
        defaultDiff = [0] * len(defaultPoints.get('win', []))
        if vehTypeCompDescr in defaultPoints and 'win' in defaultPoints:
            specialPoints = defaultPoints[vehTypeCompDescr]
            defaultPoints = defaultPoints['win']
            specialPoints = specialPoints['win']
            return [a - b for a, b in zip(specialPoints, defaultPoints)]
        return defaultDiff

    def __getPackedBonusPointsList(
            self,
            vehTypeCompDescr=None,
            isWinner=True,
            isDiff=False,
            gameMode=constants.ARENA_BONUS_TYPE.REGULAR):
        if isDiff:
            pointsList = self.__bonusPointsDiffList(
                vehTypeCompDescr=vehTypeCompDescr,
                config=self.__getConfig(),
                gameMode=gameMode)
        else:
            pointsList = self.__getConfig().bonusPointsList(
                vehTypeCompDescr=vehTypeCompDescr,
                isWinner=isWinner,
                gameMode=gameMode)
        return [(key, len(list(group))) for key, group in groupby(pointsList)]

    @staticmethod
    def __checkIfRewardIsToken(bonusName, reward):
        if 'tokens' not in reward:
            return False
        bonuses = BattlePassAwardsManager.composeBonuses([reward])
        for bonus in bonuses:
            if bonus.getName() == bonusName:
                return True

        return False

    def __clearFields(self):
        self.__oldPoints = 0
        self.__oldLevel = 0
        self.__currentMode = None
        return
class ProgressionView(ViewImpl):
    __slots__ = ('__notifier', '__backCallback')
    _COMMON_SOUND_SPACE = RESOURCE_WELL_SOUND_SPACE
    __resourceWell = dependency.descriptor(IResourceWellController)

    def __init__(self, layoutID, backCallback):
        settings = ViewSettings(R.views.lobby.resource_well.ProgressionView())
        settings.flags = ViewFlags.LOBBY_SUB_VIEW
        settings.model = ProgressionViewModel()
        self.__backCallback = backCallback
        super(ProgressionView, self).__init__(settings)
        self.__notifier = None
        return

    @property
    def viewModel(self):
        return super(ProgressionView, self).getViewModel()

    def createToolTipContent(self, event, contentID):
        if contentID == R.views.lobby.resource_well.tooltips.SerialNumberTooltip():
            return SerialNumberTooltip(parentLayout=self.layoutID)
        if contentID == R.views.lobby.resource_well.tooltips.RefundResourcesTooltip():
            return RefundResourcesTooltip()
        return ProgressTooltip(progress=self.viewModel.getProgression()) if contentID == R.views.lobby.resource_well.tooltips.ProgressTooltip() else super(ProgressionView, self).createToolTipContent(event, contentID)

    def _onLoading(self, *args, **kwargs):
        super(ProgressionView, self)._onLoading(*args, **kwargs)
        self.__resourceWell.startNumberRequesters()
        self.__notifier = SimpleNotifier(self.__getReminderTimeLeft, self.__updateEventTime)
        self.__updateModel()
        ResourceWellMainScreenLogger().onViewOpened(getProgressionState())

    def _finalize(self):
        self.__notifier.stopNotification()
        self.__resourceWell.stopNumberRequesters()
        super(ProgressionView, self)._finalize()

    def _getEvents(self):
        return ((self.viewModel.onAboutClick, self.__showEventInfo),
         (self.viewModel.onPreview, self.__showPreview),
         (self.viewModel.onHangarShow, self.__showHangar),
         (self.viewModel.onResourcesContribute, self.__contributeResources),
         (self.viewModel.onResourcesReturn, self.__extractResources),
         (self.viewModel.onClose, self.__close),
         (g_playerEvents.onClientUpdated, self.__onClientUpdated),
         (self.__resourceWell.onNumberRequesterUpdated, self.__onNumberRequesterUpdated),
         (self.__resourceWell.onEventUpdated, self.__onEventStateUpdated))

    def __updateModel(self):
        with self.viewModel.transaction() as model:
            self.__fillEventInfo(model=model)
            self.__fillProgression(model=model)
            self.__updateEventTime(model=model)
            self.__fillVehicleName(model=model)
            self.__fillRewards(model.getRewards())

    @replaceNoneKwargsModel
    def __updateEventTime(self, model=None):
        model.setEndDate(round(time_utils.makeLocalServerTime(self.__resourceWell.getFinishTime()), -1))
        isEventEnding = isEventEndingsSoon(resourceWell=self.__resourceWell)
        model.setIsEventEndingSoon(isEventEnding)
        model.setTimeLeft(self.__resourceWell.getFinishTime() - time_utils.getServerUTCTime())
        if isEventEnding:
            self.__notifier.stopNotification()
        else:
            self.__notifier.startNotification()

    @replaceNoneKwargsModel
    def __fillEventInfo(self, model=None):
        model.setTopRewardPlayersCount(self.__resourceWell.getRewardLimit(isTop=True))
        model.setRegularRewardVehiclesCount(self.__resourceWell.getRewardLimit(isTop=False))

    @replaceNoneKwargsModel
    def __fillProgression(self, model=None):
        model.setProgressionState(_PROGRESSION_STATE_MAPPING.get(getProgressionState()))
        currentPoints = self.__resourceWell.getCurrentPoints()
        maxPoints = self.__resourceWell.getMaxPoints()
        model.setProgression(_FULL_PROGRESS * currentPoints / (maxPoints or _FULL_PROGRESS))

    def __fillVehicleName(self, model=None):
        vehicle = getVehicleByIntCD(self.__resourceWell.getRewardVehicle())
        model.setVehicleName(vehicle.userName)

    def __fillRewards(self, rewards):
        rewards.clear()
        rewards.addViewModel(self.__getRewardModel(isTop=True))
        rewards.addViewModel(self.__getRewardModel(isTop=False))
        rewards.invalidate()

    def __getRewardModel(self, isTop):
        model = RewardModel()
        model.setIsTop(isTop)
        model.setIsEnabled(self.__resourceWell.isRewardEnabled(isTop))
        model.setVehiclesLeftCount(self.__resourceWell.getRewardLeftCount(isTop))
        model.setIsCountAvailable(self.__resourceWell.isRewardCountAvailable(isTop))
        fillVehicleInfo(model.vehicleInfo, getVehicleByIntCD(self.__resourceWell.getRewardVehicle()))
        return model

    def __close(self):
        if callable(self.__backCallback):
            self.__backCallback()
        else:
            showHangar()

    def __showHangar(self):
        showHangar()
        self.destroyWindow()

    def __showPreview(self, args):
        isStyled = args.get('isStyled', False)
        style = getRewardStyle(resourceWell=self.__resourceWell)
        vehicleCD = self.__resourceWell.getRewardVehicle()
        topPanelData = {'linkage': VEHPREVIEW_CONSTANTS.TOP_PANEL_TABS_LINKAGE,
         'tabIDs': PERSONAL_NUMBER_STYLE_TABS,
         'currentTabID': TabID.PERSONAL_NUMBER_VEHICLE if isStyled else TabID.BASE_VEHICLE}
        showResourceWellVehiclePreview(vehicleCD, style, self.__previewCallback, topPanelData)

    def __previewCallback(self):
        showResourceWellProgressionWindow(backCallback=self.__backCallback)

    def __contributeResources(self):
        showResourcesLoadingWindow(ParentScreens.MAIN_SCREEN)

    @process
    def __extractResources(self):
        yield ResourceWellTakeBackProcessor().request()

    def __showEventInfo(self):
        showBrowserOverlayView(GUI_SETTINGS.resourceWell.get('infoPage'), VIEW_ALIAS.RESOURCE_WELL_BROWSER_VIEW)

    def __onEventStateUpdated(self):
        if not self.__resourceWell.isActive():
            showHangar()
        else:
            self.__updateEventTime()

    def __onEventSettingsUpdated(self):
        if not self.__resourceWell.isActive():
            self.__close()
            return
        self.__updateModel()

    def __getReminderTimeLeft(self):
        return max(0, self.__resourceWell.getReminderTime() - time_utils.getServerUTCTime())

    def __onClientUpdated(self, diff, _):
        if RESOURCE_WELL_PDATA_KEY in diff:
            if not self.__resourceWell.isCompleted():
                self.__fillProgression()

    def __onNumberRequesterUpdated(self):
        with self.viewModel.transaction() as model:
            self.__fillProgression(model=model)
            self.__fillRewards(model.getRewards())
class BattlePassController(IBattlePassController):
    __itemsCache = dependency.descriptor(IItemsCache)
    __lobbyContext = dependency.descriptor(ILobbyContext)

    def __init__(self):
        self.__oldPoints = 0
        self.__oldLevel = 0
        self.__oldVoteOption = 0
        self.__badge = None
        self.__currentMode = None
        self.__eventsManager = EventManager()
        self.__seasonChangeNotifier = SimpleNotifier(
            self.__getTimeToNotifySeasonChange, self.__onNotifySeasonChange)
        self.__purchaseUnlockNotifier = SimpleNotifier(
            self.__getTimeToNotifyPurchaseUnlock, self.__onNotifyUnlock)
        self.onPointsUpdated = Event(self.__eventsManager)
        self.onLevelUp = Event(self.__eventsManager)
        self.onVoted = Event(self.__eventsManager)
        self.onBattlePassIsBought = Event(self.__eventsManager)
        self.onSeasonStateChange = Event(self.__eventsManager)
        self.onUnlimitedPurchaseUnlocked = Event(self.__eventsManager)
        self.onBattlePassSettingsChange = Event(self.__eventsManager)
        self.onFinalRewardStateChange = Event(self.__eventsManager)
        self.__votingRequester = BattlePassVotingRequester(self)
        self.__finalRewardStateMachine = FinalRewardStateMachine(self)
        return

    def init(self):
        super(BattlePassController, self).init()
        g_clientUpdateManager.addCallbacks({'tokens': self.__onTokensUpdate})
        BattlePassAwardsManager.init()

    def onLobbyInited(self, event):
        self.__lobbyContext.getServerSettings(
        ).onServerSettingsChange += self.__onServerSettingsChange
        self.__itemsCache.onSyncCompleted += self.__onSyncCompleted
        self.__seasonChangeNotifier.startNotification()
        self.__purchaseUnlockNotifier.startNotification()
        self.__finalRewardStateMachine.init()
        if self.__currentMode is None:
            self.__currentMode = self.__getConfig().mode
        else:
            self.onBattlePassSettingsChange(self.__getConfig().mode,
                                            self.__currentMode)
            self.__currentMode = self.__getConfig().mode
        self.__votingRequester.start()
        return

    def onAvatarBecomePlayer(self):
        self.__stop()

    def onDisconnected(self):
        self.__finalRewardStateMachine.interruptFlow()
        self.__stop()
        self.__clearFields()

    def fini(self):
        self.__stop()
        self.__clearFields()
        self.__eventsManager.clear()
        g_clientUpdateManager.removeObjectCallbacks(self)
        super(BattlePassController, self).fini()

    def isBought(self, seasonID=None):
        if seasonID is None:
            seasonID = self.getSeasonID()
        token = self.__itemsCache.items.tokens.getTokens().get(
            getBattlePassPassTokenName(seasonID))
        return True if token else False

    def isEnabled(self):
        return self.__getConfig().isEnabled()

    def isActive(self):
        return self.__getConfig().isActive(time_utils.getServerUTCTime())

    def isVisible(self):
        return self.isSeasonStarted(
        ) and not self.isDisabled() and not self.isSeasonFinished()

    def isOffSeasonEnable(self):
        return (not self.isSeasonStarted()
                or self.isSeasonFinished()) and not self.isDisabled()

    def isDisabled(self):
        return self.__getConfig().isDisabled()

    def isPaused(self):
        return self.__getConfig().isPaused()

    def isPlayerVoted(self):
        return self.getVoteOption() != 0

    def isSeasonStarted(self):
        return self.__getConfig().seasonStart <= time_utils.getServerUTCTime()

    def isSeasonFinished(self):
        return self.__getConfig().seasonFinish <= time_utils.getServerUTCTime()

    def isSellAnyLevelsUnlocked(self):
        return self.__getConfig().isSellAnyUnlocked(
            time_utils.getServerUTCTime())

    def isValidBattleType(self, prbEntity):
        return prbEntity.getEntityType() == constants.ARENA_GUI_TYPE.RANDOM

    def getMaxLevel(self, isBase=True):
        return len(self.getLevelsConfig(isBase))

    def isRareLevel(self, level, isBase=True):
        realLevel = min(level, self.getMaxLevel(isBase))
        rewardType = BattlePassConsts.REWARD_PAID if isBase else BattlePassConsts.REWARD_POST
        tags = self.__getConfig().getTags(realLevel, rewardType)
        return BattlePassConsts.RARE_REWARD_TAG in tags

    def getVotingRequester(self):
        return self.__votingRequester

    def getFinalFlowSM(self):
        return self.__finalRewardStateMachine

    def getSplitFinalAwards(self):
        freeReward = []
        paidReward = []
        awardsSettings = sorted(self.getFinalRewards().iteritems(),
                                key=itemgetter(0))
        voteOption = self.getVoteOption()
        if voteOption != 0:
            for vote, config in awardsSettings:
                shared = BattlePassAwardsManager.composeBonuses(
                    [config.get('shared', {})])
                if vote == voteOption:
                    unique = BattlePassAwardsManager.composeBonuses(
                        [config.get('unique', {})])
                    freeReward.extend(shared)
                    freeReward.extend(unique)
                paidReward.extend(shared)

        else:
            configs = [config for _, config in awardsSettings]
            if len(configs) != 2:
                return (freeReward, paidReward)
            sharedA = BattlePassAwardsManager.composeBonuses(
                [configs[0].get('shared', {})])
            sharedB = BattlePassAwardsManager.composeBonuses(
                [configs[1].get('shared', {})])
            uniqueA = BattlePassAwardsManager.composeBonuses(
                [configs[0].get('unique', {})])
            uniqueB = BattlePassAwardsManager.composeBonuses(
                [configs[1].get('unique', {})])
            for optionA, optionB in zip(sharedA, sharedB):
                bonus = makeUndefinedBonus(optionA, optionB)
                freeReward.append(bonus)
                paidReward.append(bonus)

        for optionA, optionB in zip(uniqueA, uniqueB):
            bonus = makeUndefinedBonus(optionA, optionB)
            freeReward.append(bonus)

        return (freeReward, paidReward)

    def getFinalAwardsForPurchaseLevels(self):
        awards = []
        awardsSettings = sorted(self.getFinalRewards().iteritems(),
                                key=itemgetter(0))
        configs = [config for _, config in awardsSettings]
        if len(configs) != 2:
            return awards
        awards.extend(
            BattlePassAwardsManager.composeBonuses(
                [configs[0].get('shared', {})]))
        awards.extend(
            BattlePassAwardsManager.composeBonuses(
                [configs[1].get('shared', {})]))
        uniqueA = BattlePassAwardsManager.composeBonuses(
            [configs[0].get('unique', {})])
        uniqueB = BattlePassAwardsManager.composeBonuses(
            [configs[1].get('unique', {})])
        for optionA, optionB in zip(uniqueA, uniqueB):
            bonus = makeUndefinedBonus(optionA, optionB)
            awards.append(bonus)

        return awards

    def getSingleAward(self,
                       level,
                       awardType=BattlePassConsts.REWARD_FREE,
                       needSort=True):
        reward = {}
        if awardType in (BattlePassConsts.REWARD_FREE,
                         BattlePassConsts.REWARD_PAID,
                         BattlePassConsts.REWARD_POST):
            reward = self.__getConfig().getRewardByType(level, awardType)
        elif awardType == BattlePassConsts.REWARD_BOTH:
            rewards = [
                self.__getConfig().getFreeReward(level),
                self.__getConfig().getPaidReward(level)
            ]
            return BattlePassAwardsManager.hideInvisible(
                BattlePassAwardsManager.composeBonuses(rewards))
        if needSort:
            rewards = BattlePassAwardsManager.composeBonuses([reward])
        else:
            rewards = awardsFactory(reward)
        return BattlePassAwardsManager.hideInvisible(rewards,
                                                     needSplit=not needSort)

    def getAwardsInterval(self,
                          fromLevel,
                          toLevel,
                          awardType=BattlePassConsts.REWARD_FREE):
        result = {}
        for level in range(fromLevel, toLevel + 1):
            result[level] = self.getSingleAward(level, awardType, True)

        return result

    def getAwardsList(self, awardType=BattlePassConsts.REWARD_FREE):
        maxLevel = self.getMaxLevel(False if awardType ==
                                    BattlePassConsts.REWARD_POST else True)
        return self.getAwardsInterval(1, maxLevel, awardType)

    def getPackedAwardsInterval(self,
                                fromLevel,
                                toLevel,
                                awardType=BattlePassConsts.REWARD_FREE):
        result = []
        for level in range(fromLevel, toLevel + 1):
            result.extend(self.getSingleAward(level, awardType, False))

        return BattlePassAwardsManager.sortBonuses(result)

    def getLevelsConfig(self, isBase=True):
        return self.__getConfig().basePoints if isBase else self.__getConfig(
        ).postPoints

    def getFinalRewards(self):
        return self.__getConfig().finalReward

    def getFreeFinalRewardDict(self):
        return self.__getConfig().getRewardByType(self.getMaxLevel(),
                                                  BattlePassConsts.REWARD_FREE)

    def getCurrentPoints(self, aligned=False):
        points = self.__itemsCache.items.battlePass.getPoints()
        return self.__getConfig().alignedPointsFromSumPoints(
            points) if aligned else points

    def getMaxPoints(self, isBase=True):
        return self.__getConfig(
        ).maxBasePoints if isBase else self.__getConfig().maxPostPoints

    def getCurrentLevel(self):
        return self.__itemsCache.items.battlePass.getCurrentLevel()

    def getState(self):
        return self.__itemsCache.items.battlePass.getState()

    def getMaxSoldLevelsBeforeUnlock(self):
        return self.__getConfig().maxSoldLevelsBeforeUnlock

    def getBoughtLevels(self):
        return self.__itemsCache.items.battlePass.getBoughtLevels()

    def getVoteOption(self):
        return self.__itemsCache.items.battlePass.getVoteOption()

    def getPrevSeasonsStats(self):
        packedStats = self.__itemsCache.items.battlePass.getPackedStats()
        if not packedStats:
            return None
        else:
            unpackStats, _ = BattlePassStatsCommon.unpackAllSeasonStats(
                packedStats)
            return unpackStats

    def getLastFinishedSeasonStats(self):
        allSeasonStats = self.getPrevSeasonsStats()
        if not allSeasonStats:
            seasons = sorted(self.getSeasonsHistory().keys(), reverse=True)
            return BattlePassStatsCommon.makeSeasonStats(
                first(seasons), {},
                BattlePassStatsCommon.initialSeasonStatsData())
        return allSeasonStats[-1]

    def getSeasonsHistory(self):
        return self.__getConfig().seasonsHistory

    def getAlternativeVoteOption(self):
        voteOption = self.getVoteOption()
        if voteOption == 0:
            return 0
        voteOptions = self.getFinalRewards().keys()
        alternativeOption = findFirst(lambda option: option != voteOption,
                                      voteOptions, 0)
        return alternativeOption

    def getLevelPoints(self, level, isBase=True):
        levelsConfig = self.getLevelsConfig(isBase)
        return levelsConfig[
            0] if level <= 0 else levelsConfig[level] - levelsConfig[level - 1]

    def getLevelProgression(self):
        if self.isDisabled():
            return (0, 0)
        state = self.getState()
        if state == BattlePassState.COMPLETED:
            levelsConfig = self.getLevelsConfig(False)
            points = levelsConfig[-1] - levelsConfig[-2]
            return (points, points)
        level = self.getCurrentLevel()
        points = self.getCurrentPoints(aligned=True)
        levelsConfig = self.getLevelsConfig(state == BattlePassState.BASE)
        return getLevelProgression(level, points, levelsConfig)

    def getLevelByPoints(self, points):
        if points >= self.__getConfig().maxPostPoints + self.__getConfig(
        ).maxBasePoints:
            state = BattlePassState.COMPLETED
        elif points >= self.__getConfig().maxBasePoints:
            state = BattlePassState.POST
        else:
            state = BattlePassState.BASE
        if state == BattlePassState.COMPLETED:
            level = self.getMaxLevel(isBase=False)
        else:
            alignedPoints = points - self.__getConfig(
            ).maxBasePoints if state == BattlePassState.POST else points
            levelsConfig = self.getLevelsConfig(
                isBase=state == BattlePassState.BASE)
            level = getLevel(curPoints=alignedPoints, levelPoints=levelsConfig)
        return (state, level)

    def getProgressionByPoints(self, points, state, level):
        if state == BattlePassState.COMPLETED:
            levelsConfig = self.getLevelsConfig(isBase=False)
            levelPoints = fullLevelPoints = levelsConfig[-1] - levelsConfig[-2]
        else:
            alignedPoints = points - self.__getConfig(
            ).maxBasePoints if state == BattlePassState.POST else points
            levelsConfig = self.getLevelsConfig(
                isBase=state == BattlePassState.BASE)
            levelPoints, fullLevelPoints = getLevelProgression(
                level, alignedPoints, levelsConfig)
        return (levelPoints, fullLevelPoints)

    def getPerBattlePoints(self, vehCompDesc=None):
        winList = self.__getPackedBonusPointsList(vehTypeCompDescr=vehCompDesc)
        lostList = self.__getPackedBonusPointsList(
            vehTypeCompDescr=vehCompDesc, isWinner=False)
        count = 0
        result = []
        for winInfo, lostInfo in zip(winList, lostList):
            pointsWin, pointsCount = winInfo
            pointsLost, _ = lostInfo
            count += pointsCount
            if pointsWin > 0:
                item = TopPoints(count, pointsWin, pointsLost)
                result.append(item)

        return result

    def getBadgeData(self):
        if self.__badge is None:
            self.__extractBadgeInfo()
        return self.__badge

    def isSpecialVehicle(self, intCD):
        return self.__getConfig().isSpecialVehicle(intCD)

    def getSpecialVehicles(self):
        return self.__getConfig().getSpecialVehicles()

    def getPointsDiffForVehicle(self, intCD):
        defaultWinList = self.__getPackedBonusPointsList()
        diffWinList = self.__getPackedBonusPointsList(vehTypeCompDescr=intCD,
                                                      isDiff=True)
        if not defaultWinList or not diffWinList:
            _logger.error(
                'Failed to get bonus points information! Check server settings are correct.'
            )
            return PointsDifference(0, 0, 0)
        defaultBlock = defaultWinList[-1]
        diffBlock = diffWinList[0]
        bonus = diffBlock[0]
        top = 0 if diffBlock[1] == BattlePassConfig.MAX_RANKS - defaultBlock[
            1] else diffBlock[1]
        textID = getPointsInfoStringID(top != 0)
        return PointsDifference(bonus, top, textID)

    def getVehicleProgression(self, intCD):
        points = self.__itemsCache.items.battlePass.getPointsForVehicle(
            intCD, 0)
        cap = self.__getConfig().vehicleCapacity(intCD)
        return (points, cap)

    def getVehicleCapBonus(self, intCD):
        vehicle = self.__itemsCache.items.getItemByCD(intCD)
        if vehicle is None:
            return 0
        else:
            bonus = self.__getConfig().capBonus(vehicle.level)
            return bonus

    def getCapacityList(self):
        capacities = self.__getConfig().capacityList()
        return enumerate(capacities, 1)

    def getSeasonTimeLeft(self):
        return max(0,
                   self.getSeasonFinishTime() - time_utils.getServerUTCTime())

    def getSellAnyLevelsUnlockTimeLeft(self):
        return max(
            0,
            self.getSellAnyLevelsUnlockTime() - time_utils.getServerUTCTime())

    def getFinalOfferTimeLeft(self):
        return max(0, self.getFinalOfferTime() - time_utils.getServerUTCTime())

    def getSeasonStartTime(self):
        return self.__getConfig().seasonStart

    def getSeasonFinishTime(self):
        return self.__getConfig().seasonFinish

    def getSellAnyLevelsUnlockTime(self):
        return self.__getConfig().sellAnyLevelsUnlockTime

    def hasMaxPointsOnVehicle(self, intCD):
        currentPoints, limitPoints = self.getVehicleProgression(intCD)
        return currentPoints >= limitPoints > 0

    def isProgressionOnVehiclePossible(self, intCD):
        cap = self.__getConfig().vehicleCapacity(intCD)
        return cap > 0

    def canPlayerParticipate(self):
        criteria = REQ_CRITERIA.INVENTORY
        criteria |= ~REQ_CRITERIA.VEHICLE.EPIC_BATTLE
        criteria |= REQ_CRITERIA.CUSTOM(
            lambda veh: not self.hasMaxPointsOnVehicle(veh.intCD))
        criteria |= REQ_CRITERIA.CUSTOM(
            lambda veh: self.isProgressionOnVehiclePossible(veh.intCD))
        availableVehiclesToProgression = self.__itemsCache.items.getVehicles(
            criteria)
        return len(availableVehiclesToProgression) > 0

    def getSeasonID(self):
        return self.__itemsCache.items.battlePass.getSeasonID()

    def getFinalOfferTime(self):
        return self.__getConfig().finalOfferTime

    def __stop(self):
        self.__seasonChangeNotifier.stopNotification()
        self.__purchaseUnlockNotifier.stopNotification()
        self.__itemsCache.onSyncCompleted -= self.__onSyncCompleted
        self.__lobbyContext.getServerSettings(
        ).onServerSettingsChange -= self.__onServerSettingsChange
        self.__votingRequester.stop()

    def __getFrontierStep(self):
        return self.__getConfig().postPoints

    def __getConfig(self):
        return self.__lobbyContext.getServerSettings().getBattlePassConfig()

    def __onTokensUpdate(self, diff):
        if getBattlePassPassTokenName(self.getSeasonID()) in diff:
            self.onBattlePassIsBought()

    def __getTimeUntilStart(self):
        return max(
            0,
            self.__getConfig().seasonStart - time_utils.getServerUTCTime())

    def __getTimeToNotifySeasonChange(self):
        if self.isEnabled() or self.isPaused():
            if not self.isSeasonStarted():
                return self.__getTimeUntilStart()
            if not self.isSeasonFinished():
                return self.getSeasonTimeLeft()

    def __onNotifySeasonChange(self):
        self.onSeasonStateChange()

    def __getTimeToNotifyPurchaseUnlock(self):
        return self.getSellAnyLevelsUnlockTimeLeft(
        ) if self.isEnabled() or self.isPaused() else 0

    def __onNotifyUnlock(self):
        self.onUnlimitedPurchaseUnlocked()

    def __onServerSettingsChange(self, diff):
        if BATTLE_PASS_CONFIG_NAME in diff:
            self.__seasonChangeNotifier.startNotification()
            self.__purchaseUnlockNotifier.startNotification()
            newMode = None
            oldMode = self.__currentMode
            if 'mode' in diff[BATTLE_PASS_CONFIG_NAME]:
                newMode = diff[BATTLE_PASS_CONFIG_NAME]['mode']
                self.__currentMode = newMode
            self.__extractBadgeInfo()
            self.onBattlePassSettingsChange(newMode, oldMode)
        return

    def __onSyncCompleted(self, _, diff):
        if 'battlePass' in diff:
            newPoints = diff['battlePass'].get('sumPoints', self.__oldPoints)
            newLevel = diff['battlePass'].get('level', self.__oldLevel)
            newVoteOption = diff['battlePass'].get('voteOption',
                                                   self.__oldVoteOption)
            if newPoints != self.__oldPoints:
                self.onPointsUpdated()
            if newLevel != self.__oldLevel:
                self.onLevelUp()
            if newVoteOption != self.__oldVoteOption:
                self.onVoted()
            self.__oldPoints = newPoints
            self.__oldLevel = newLevel
            self.__oldVoteOption = newVoteOption

    def __getPackedBonusPointsList(self,
                                   vehTypeCompDescr=None,
                                   isWinner=True,
                                   isDiff=False):
        if isDiff:
            pointsList = self.__getConfig().bonusPointsDiffList(
                vehTypeCompDescr=vehTypeCompDescr)
        else:
            pointsList = self.__getConfig().bonusPointsList(
                vehTypeCompDescr=vehTypeCompDescr, isWinner=isWinner)
        return [(key, len(list(group))) for key, group in groupby(pointsList)]

    def __extractBadgeInfo(self):
        config = self.__getConfig()
        for level in range(1, self.getMaxLevel() + 1):
            if self.__processRewardIsBadge(config.getFreeReward(level)):
                return

        for level in range(1, self.getMaxLevel(False) + 1):
            if self.__processRewardIsBadge(config.getPostReward(level)):
                return

        self.__badge = None
        return

    def __processRewardIsBadge(self, reward):
        if 'dossier' not in reward:
            return False
        bonuses = BattlePassAwardsManager.composeBonuses([reward])
        for bonus in bonuses:
            if bonus.getName() != 'dossier':
                continue
            if bonus.getBadges():
                self.__badge = bonus
                return True

        return False

    def __clearFields(self):
        self.__oldPoints = 0
        self.__oldLevel = 0
        self.__oldVoteOption = 0
        self.__badge = None
        self.__currentMode = None
        return