Esempio n. 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 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()
Esempio n. 3
0
class LowTierMMController(ILowTierMMController):
    __eventsCache = dependency.descriptor(IEventsCache)

    def __init__(self):
        super(LowTierMMController, self).__init__()
        self.onEventStateChanged = Event.Event()
        self.__isEventActive = False
        self.__dateFinish = None
        self.__notifier = None
        return

    def fini(self):
        self.__clear()
        self.onEventStateChanged.clear()
        self.__clearNotifier()

    def isEnabled(self):
        return self.__isEventActive

    def getDateFinish(self):
        return self.__dateFinish

    def onLobbyInited(self, event):
        self.__eventsCache.onSyncCompleted += self.__update
        self.__update()

    def onAvatarBecomePlayer(self):
        self.__clear()
        self.__clearNotifier()

    def onDisconnected(self):
        self.__clear()
        self.__clearNotifier()

    def __getTimeDelta(self):
        return self.__dateFinish - time_utils.getServerUTCTime()

    def __clear(self):
        self.__isEventActive = False
        self.__dateFinish = None
        self.__eventsCache.onSyncCompleted -= self.__update
        return

    def __clearNotifier(self):
        if self.__notifier:
            self.__notifier.stopNotification()
            self.__notifier.clear()
            self.__notifier = None
        return

    def __update(self):
        actions = self.__eventsCache.getActions()
        eventState = self.__getEventParams(actions, _EVENT_STATE_NAME)
        try:
            dateFinishDT = datetime.strptime(eventState.strip(), FORMAT_DATE)
            self.__dateFinish = calendar.timegm(dateFinishDT.timetuple())
        except (AttributeError, TypeError):
            self.__updateEvent(False)
            self.__dateFinish = None
            return

        currtime = time_utils.getServerUTCTime()
        eventState = self.__dateFinish > currtime
        self.__updateEvent(eventState)
        self.__clearNotifier()
        if eventState:
            self.__notifier = SimpleNotifier(self.__getTimeDelta,
                                             self.__update)
            self.__notifier.startNotification()
        else:
            self.__dateFinish = None
        return

    @staticmethod
    def __getEventParams(actions, eventName):
        for action in actions.itervalues():
            steps = action.getData()['steps']
            if steps is None:
                continue
            for step in steps:
                if step.get('name') != eventName:
                    continue
                return step.get('params').get('date')

        return

    def __updateEvent(self, eventState):
        if self.__isEventActive != eventState:
            self.__isEventActive = eventState
            setEnabledMMEvent(self.__isEventActive)
            self.onEventStateChanged()
Esempio n. 4
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 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')
Esempio n. 6
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