Beispiel #1
0
class OpenBrowserOnClickComponent(CGFComponent):
    urlProvider = ComponentProperty(type=CGFMetaTypes.STRING,
                                    editorName='url provider',
                                    value='MARATHON_VIDEO_URL_PROVIDER')

    def __init__(self):
        super(OpenBrowserOnClickComponent, self).__init__()
        self.__urlParser = URLMacros()

    def doAction(self):
        self.__openBrowser()

    @process
    def __openBrowser(self):
        getterFunc = URL_PROVIDERS[self.urlProvider]
        unparsedUrl = getterFunc()
        url = yield self.__urlParser.parse(unparsedUrl)
        showBrowserOverlayView(url, alias=VIEW_ALIAS.BROWSER_OVERLAY)
class EpicBattleMetaGameController(Notifiable, SeasonProvider,
                                   IEpicBattleMetaGameController,
                                   IGlobalListener):
    bootcampController = dependency.descriptor(IBootcampController)
    __itemsCache = dependency.descriptor(IItemsCache)
    __eventsCache = dependency.descriptor(IEventsCache)
    __settingsCore = dependency.descriptor(ISettingsCore)
    __lobbyContext = dependency.descriptor(ILobbyContext)
    __battleResultsService = dependency.descriptor(IBattleResultsService)
    __offersProvider = dependency.descriptor(IOffersDataProvider)
    MAX_STORED_ARENAS_RESULTS = 20
    DAILY_QUEST_ID = 'front_line'
    FINAL_BADGE_QUEST_ID = 'epicmetagame:progression_finish'

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

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

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

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

    def onDisconnected(self):
        self.__clear()

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

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

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

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

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

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

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

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

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

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

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

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

    def getMaxPlayerLevel(self):
        return self.__playerMaxLevel

    def getStageLimit(self):
        return self.__stageLimit

    def getAbilityPointsForLevel(self):
        return self.__abilityPointsForLevel

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

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

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

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

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

    def getLevelProgress(self):
        return self.__levelProgress

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

        return lvl - 1

    def getAllSkillsInformation(self):
        return self.__skillData

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

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

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

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

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

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

        return selected

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        return rewardsData

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

        return False

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

        return count

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

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

        return result

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

        return result

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

        return None

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

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

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

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

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

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

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

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

    def __timerTick(self):
        self.onGameModeStatusTick()

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

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

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

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

        return

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

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

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

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

                if condValid:
                    self.__performanceGroup = groupName
                    return

        return

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

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

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

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

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

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

    def __onTokensUpdate(self, diff):
        if any((key.startswith(EPIC_CHOICE_REWARD_OFFER_GIFT_TOKENS)
                for key in diff.keys())):
            pass
class ExternalLinksHandler(IExternalLinksController):
    def __init__(self):
        super(ExternalLinksHandler, self).__init__()
        self.__urlMacros = None
        return

    def init(self):
        self.__urlMacros = URLMacros()
        addListener = g_eventBus.addListener
        for eventType, handlerName in _LISTENERS.iteritems():
            handler = getattr(self, handlerName, None)
            if not handler:
                LOG_ERROR('Handler is not found', eventType, handlerName)
                continue
            if not callable(handler):
                LOG_ERROR('Handler is invalid', eventType, handlerName,
                          handler)
                continue
            addListener(eventType, handler)

        return

    def fini(self):
        if self.__urlMacros is not None:
            self.__urlMacros.clear()
            self.__urlMacros = None
        removeListener = g_eventBus.removeListener
        for eventType, handlerName in _LISTENERS.iteritems():
            handler = getattr(self, handlerName, None)
            if handler:
                removeListener(eventType, handler)

        super(ExternalLinksHandler, self).fini()
        return

    def open(self, url):
        if not url:
            LOG_ERROR('URL is empty', url)
            return
        try:
            BigWorld.wg_openWebBrowser(url)
        except Exception:
            LOG_ERROR('There is error while opening web browser at page:', url)
            LOG_CURRENT_EXCEPTION()

    @async
    @process
    def getURL(self, name, params=None, callback=lambda *args: None):
        urlSettings = GUI_SETTINGS.lookup(name)
        if urlSettings:
            url = yield self.__urlMacros.parse(str(urlSettings), params)
        else:
            url = yield lambda callback: callback('')
        callback(url)

    def _handleSpecifiedURL(self, event):
        self.open(event.url)

    @process
    def __openParsedUrl(self, urlName, params=None):
        parsedUrl = yield self.getURL(urlName, params)
        self.open(parsedUrl)

    def _handleParsedURL(self, event):
        self.__openParsedUrl(event.url)

    def _handleOpenRegistrationURL(self, _):
        self.__openParsedUrl('registrationURL')

    def _handleOpenRecoveryPasswordURL(self, _):
        self.__openParsedUrl('recoveryPswdURL')

    def _handleOpenPaymentURL(self, _):
        self.__openParsedUrl('paymentURL')

    def _handleSecuritySettingsURL(self, _):
        self.__openParsedUrl('securitySettingsURL')

    def _handleSupportURL(self, _):
        self.__openParsedUrl('supportURL')

    def _handleMigrationURL(self):
        self.__openParsedUrl('migrationURL')

    def _handleFortDescription(self, _):
        self.__openParsedUrl('fortDescription')

    def _handleClanSearch(self, _):
        self.__openParsedUrl('clanSearch')

    def _handleClanCreate(self, _):
        self.__openParsedUrl('clanCreate')

    def _handleInvitesManagementURL(self, _):
        self.__openParsedUrl('invitesManagementURL')

    def _handleGmSummaryURL(self, _):
        self.__openParsedUrl('globalMapSummary')

    def _handleGmPromoSummaryURL(self, _):
        self.__openParsedUrl('globalMapPromoSummary')

    def _handleGmCapURL(self, _):
        self.__openParsedUrl('globalMapCap')

    def _handleGmPromoURL(self, _):
        self.__openParsedUrl('globalMapPromo')

    def _handleOpenPremShopURL(self, _):
        self.__openParsedUrl('premShopURL')

    def _handleFrontlineChangesURL(self, _):
        self.__openParsedUrl('frontlineChangesURL')

    def _handleTokenShopURL(self, event):
        self.__openParsedUrl('tokenShopURL', event.params)

    def _handleTechTreeNewsURL(self, event):
        self.__openParsedUrl('techTreeUpdateNewsURL', event.params)
class PromoController(IPromoController):
    __browserCtrl = dependency.descriptor(IBrowserController)
    __notificationsCtrl = dependency.descriptor(IEventsNotificationsController)
    __webController = dependency.descriptor(IWebController)
    __settingsCore = dependency.descriptor(ISettingsCore)
    __lobbyContext = dependency.descriptor(ILobbyContext)
    __logger = dependency.descriptor(IPromoLogger)
    __bootcamp = dependency.descriptor(IBootcampController)

    def __init__(self):
        super(PromoController, self).__init__()
        self.__urlMacros = URLMacros()
        self.__externalCloseCallback = None
        self.__isLobbyInited = False
        self.__pendingPromo = None
        self.__promoCount = 0
        self.__lastUpdateTimeMark = 0
        self.__promoData = None
        self.__waitingForWebBridgeData = False
        self.__battlesFromLastTeaser = 0
        self.__wasInBattle = False
        self.__hasPendingTeaser = False
        self.__isPromoOpen = False
        self.__browserCreationCallbacks = {}
        self.__browserWatchers = {}
        self.__isInHangar = False
        self.__isTeaserOpen = False
        self.__checkIntervalInBattles = GUI_SETTINGS.checkPromoFrequencyInBattles
        self.__em = EventManager()
        self.onNewTeaserReceived = Event(self.__em)
        self.onPromoCountChanged = Event(self.__em)
        self.onTeaserShown = Event(self.__em)
        self.onTeaserClosed = Event(self.__em)
        return

    @sf_lobby
    def app(self):
        pass

    def fini(self):
        self.__stop()
        self.__pendingPromo = None
        self.__urlMacros.clear()
        self.__urlMacros = None
        self.__externalCloseCallback = None
        self.__em.clear()
        g_eventBus.removeListener(BrowserEvent.BROWSER_CREATED,
                                  self.__handleBrowserCreated)
        self.__browserCreationCallbacks = {}
        self.__browserWatchers = {}
        super(PromoController, self).fini()
        return

    def onLobbyInited(self, event):
        if not isPlayerAccount():
            return
        g_eventBus.addListener(BrowserEvent.BROWSER_CREATED,
                               self.__handleBrowserCreated)
        self.__isLobbyInited = True
        if self.__needToGetTeasersInfo():
            self.__updateWebBrgData()
        elif self.__hasPendingTeaser:
            self.__tryToShowTeaser()
        self.__notificationsCtrl.onEventNotificationsChanged += self.__onEventNotification
        if not isPopupsWindowsOpenDisabled():
            self.__processPromo(
                self.__notificationsCtrl.getEventsNotifications())
        self.app.loaderManager.onViewLoaded += self.__onViewLoaded

    @property
    def checkIntervalInBattles(self):
        return self.__checkIntervalInBattles

    def setNewTeaserData(self, teaserData):
        timestamp = teaserData['timestamp']
        if self.__lastUpdateTimeMark < timestamp and not self.__isPromoOpen and not self.__isTeaserOpen:
            self.__updateTeaserData(teaserData)

    def showFieldPost(self):
        url = GUI_SETTINGS.promoscreens
        loadingCallback = self.__logger.getLoggingFuture(
            action=PromoLogActions.OPEN_FROM_MENU,
            type=PromoLogSubjectType.INDEX,
            url=url)
        self.__showBrowserView(url, loadingCallback, soundSpaceID='field_post')

    @process
    def showLastTeaserPromo(self):
        rowUrl = self.__promoData.get('url', '')
        loadingCallback = self.__logger.getLoggingFuture(
            self.__promoData,
            action=PromoLogActions.OPEN_FROM_TEASER,
            type=PromoLogSubjectType.PROMO_SCREEN,
            url=rowUrl)
        url = yield self.__addAuthParams(rowUrl)
        self.__showBrowserView(url, loadingCallback)

    def setUnreadPromoCount(self, count):
        self.__updatePromoCount(count)

    def isTeaserOpen(self):
        return self.__isTeaserOpen

    def onAvatarBecomePlayer(self):
        if self.__isLobbyInited:
            self.__battlesFromLastTeaser += 1
        self.__stop()

    def onDisconnected(self):
        self.__stop()

    def isActive(self):
        return self.__lobbyContext.getServerSettings().isFieldPostEnabled(
        ) and not self.__bootcamp.isInBootcamp()

    def getPromoCount(self):
        return self.__promoCount

    def showPromo(self, url, closeCallback=None, source=None):
        if self.__isLobbyInited:
            if not self.__isPromoOpen and not self.__waitingForWebBridgeData:
                self.__registerAndShowPromoBrowser(
                    url, closeCallback,
                    self.__logger.getLoggingFuture(
                        action=PromoLogActions.OPEN_IN_OLD,
                        type=PromoLogSubjectType.PROMO_SCREEN,
                        source=source,
                        url=url))
        else:
            self.__pendingPromo = _PromoData(url, closeCallback, source)

    def __needToGetTeasersInfo(self):
        return True if self.__battlesFromLastTeaser == 0 else self.__checkIntervalInBattles > 0 and self.__battlesFromLastTeaser % self.__checkIntervalInBattles == 0

    def __onTeaserClosed(self, byUser=False):
        self.__isTeaserOpen = False
        self.onTeaserClosed()
        if byUser:
            self.__showBubbleTooltip()

    def __showBubbleTooltip(self):
        storageData = self.__settingsCore.serverSettings.getUIStorage()
        if not storageData.get(UI_STORAGE_KEYS.FIELD_POST_HINT_IS_SHOWN):
            showBubbleTooltip(i18n.makeString(TOOLTIPS.HEADER_VERSIONINFOHINT))
            self.__settingsCore.serverSettings.saveInUIStorage(
                {UI_STORAGE_KEYS.FIELD_POST_HINT_IS_SHOWN: True})

    @process
    def __updateWebBrgData(self):
        ctx = PromoGetTeaserRequestCtx()
        if self.__battlesFromLastTeaser == 0:
            sourceType = PromoLogSourceType.FIRST_LOGIN
        else:
            sourceType = PromoLogSourceType.AFTER_BATTLE
        answerCallback = self.__logger.getLoggingFuture(
            action=PromoLogActions.GET_MOST_IMPORTANT, source=sourceType)
        self.__waitingForWebBridgeData = True
        response = yield self.__webController.sendRequest(ctx=ctx)
        self.__waitingForWebBridgeData = False
        if response.isSuccess():
            self.__updateTeaserData(ctx.getDataObj(response.getData()))
        elif self.__hasPendingTeaser:
            self.__tryToShowTeaser()
        if answerCallback:
            answerCallback(success=response.extraCode)

    def __onPromoClosed(self, **kwargs):
        self.__isPromoOpen = False
        self.__showBubbleTooltip()
        if self.__externalCloseCallback:
            self.__externalCloseCallback()
        self.__requestPromoCount()
        actionType = PromoLogActions.CLOSED_BY_USER if kwargs.get(
            'byUser') else PromoLogActions.KILLED_BY_SYSTEM
        self.__logger.logAction(action=actionType,
                                type=PromoLogSubjectType.PROMO_SCREEN_OR_INDEX,
                                url=kwargs.get('url'))

    @process
    def __requestPromoCount(self):
        if not self.isActive():
            _logger.warning(
                'Trying to request unread promos count when promo functionality is disabled'
            )
            return
        ctx = PromoGetUnreadCountRequestCtx()
        response = yield self.__webController.sendRequest(ctx=ctx)
        if response.isSuccess():
            self.__updatePromoCount(ctx.getCount(response))

    def __updatePromoCount(self, count):
        countChanged = count != self.__promoCount
        self.__promoCount = count
        if countChanged:
            self.onPromoCountChanged()

    def __updateTeaserData(self, teaserData):
        self.__lastUpdateTimeMark = teaserData['timestamp']
        self.__promoData = teaserData['lastPromo']
        self.__updatePromoCount(teaserData['count'])
        if self.__promoData.get('url'):
            self.__tryToShowTeaser()

    def __showTeaser(self):
        if self.isActive():
            self.__battlesFromLastTeaser = 0
            self.__hasPendingTeaser = False
            self.onNewTeaserReceived(self.__promoData, self.__onTeaserShown,
                                     self.__onTeaserClosed)
        else:
            _logger.warning(
                'Impossible to show teaser, functionality is disabled')

    @process
    def __onTeaserShown(self, promoID):
        self.__isTeaserOpen = True
        self.onTeaserShown()
        yield self.__webController.sendRequest(
            PromoSendTeaserShownRequestCtx(promoID))

    def __tryToShowTeaser(self):
        if self.__isLobbyInited and self.__isInHangar and not self.__waitingForWebBridgeData:
            self.__showTeaser()
        else:
            self.__hasPendingTeaser = True

    def __stop(self):
        if self.app and self.app.loaderManager:
            self.app.loaderManager.onViewLoaded -= self.__onViewLoaded
        self.__isLobbyInited = False
        self.__isInHangar = False
        self.__isPromoOpen = False
        self.__externalCloseCallback = None
        self.__isTeaserOpen = False
        self.__notificationsCtrl.onEventNotificationsChanged -= self.__onEventNotification
        g_eventBus.removeListener(BrowserEvent.BROWSER_CREATED,
                                  self.__handleBrowserCreated)
        self.__browserCreationCallbacks = {}
        watcherKeys = self.__browserWatchers.keys()
        for browserID in watcherKeys:
            self.__clearWatcher(browserID)

        return

    def __processPromo(self, promos):
        if not self.__isPromoOpen and not self.__waitingForWebBridgeData:
            logData = {
                'action': PromoLogActions.OPEN_IN_OLD,
                'type': PromoLogSubjectType.PROMO_SCREEN
            }
            if self.__pendingPromo is not None:
                promoData = self.__pendingPromo
                logData.update({
                    'source': promoData.source,
                    'url': promoData.url
                })
                loadingCallback = self.__logger.getLoggingFuture(**logData)
                self.__registerAndShowPromoBrowser(promoData.url,
                                                   promoData.closeCallback,
                                                   loadingCallback)
                self.__pendingPromo = None
                return
            promo = findFirst(
                lambda item: item.eventType.startswith(
                    gc_constants.PROMO.TEMPLATE.ACTION), promos)
            if promo:
                logData.update({
                    'source': PromoLogSourceType.SSE,
                    'url': promo.data
                })
                self.__showBrowserView(
                    promo.data, self.__logger.getLoggingFuture(**logData))
        return

    def __onEventNotification(self, added, _):
        self.__processPromo(added)

    def __registerAndShowPromoBrowser(self, url, closeCallback,
                                      loadingCallback):
        self.__externalCloseCallback = closeCallback
        self.__showBrowserView(url, loadingCallback)

    @process
    def __showBrowserView(self, url, loadingCallback=None, soundSpaceID=None):
        promoUrl = yield self.__urlMacros.parse(url)
        self.__registerLoadingCallback(promoUrl, loadingCallback)
        _showBrowserView(promoUrl,
                         self.__onPromoClosed,
                         soundSpaceID=soundSpaceID)
        self.__isPromoOpen = True

    def __registerLoadingCallback(self, url, callback):
        if callback is not None:
            self.__browserCreationCallbacks[url] = callback
        return

    def __handleBrowserCreated(self, event):
        url = event.ctx.get('url')
        if url in self.__browserCreationCallbacks:
            callback = self.__browserCreationCallbacks.pop(url)
            browserID = event.ctx.get('browserID')
            browser = self.__browserCtrl.getBrowser(browserID)
            if browser is None:
                return

            def callbackWrapper(clbUrl, _, statusCode):
                if clbUrl == url:
                    callback(url=url, success=statusCode)
                    self.__clearWatcher(browserID)

            browser.onLoadEnd += callbackWrapper
            self.__browserWatchers[browserID] = callbackWrapper
        return

    def __clearWatcher(self, browserID):
        if browserID in self.__browserWatchers:
            watcher = self.__browserWatchers.pop(browserID)
            browser = self.__browserCtrl.getBrowser(browserID)
            if browser is not None:
                browser.onLoadEnd -= watcher
        return

    @async
    @process
    def __addAuthParams(self, url, callback):
        if not url or not self.__webController:
            callback(url)
        accessTokenData = yield self.__webController.getAccessTokenData(
            force=True)
        params = {
            'access_token': str(accessTokenData.accessToken),
            'spa_id': BigWorld.player().databaseID
        }
        callback(url_formatters.addParamsToUrlQuery(url, params))

    def __onViewLoaded(self, pyView, _):
        if self.__isLobbyInited:
            if pyView.alias == VIEW_ALIAS.LOBBY_HANGAR:
                self.__isInHangar = True
                if self.__hasPendingTeaser:
                    self.__tryToShowTeaser()
            elif pyView.viewType == ViewTypes.LOBBY_SUB:
                self.__isInHangar = False
class ExternalLinksHandler(IExternalLinksController):
    __loginManager = dependency.descriptor(ILoginManager)

    def __init__(self):
        super(ExternalLinksHandler, self).__init__()
        self.__urlMacros = None
        self.__linksHandlers = None
        return

    def init(self):
        self.__urlMacros = URLMacros()
        addListener = g_eventBus.addListener
        for eventType, handlerName in _LISTENERS.iteritems():
            handler = getattr(self, handlerName, None)
            if not handler:
                _logger.error('Handler is not found %s %s', eventType,
                              handlerName)
                continue
            if not callable(handler):
                _logger.error('Handler is invalid %s %s %r', eventType,
                              handlerName, handler)
                continue
            addListener(eventType, handler)

        return

    def fini(self):
        if self.__urlMacros is not None:
            self.__urlMacros.clear()
            self.__urlMacros = None
        removeListener = g_eventBus.removeListener
        for eventType, handlerName in _LISTENERS.iteritems():
            handler = getattr(self, handlerName, None)
            if handler:
                removeListener(eventType, handler)

        super(ExternalLinksHandler, self).fini()
        return

    def open(self, url):
        if not url:
            _logger.error('URL is empty %r', url)
            return
        handled = False
        for handler in self._getHandlers():
            handled = handler.handle(url)
            if handled:
                break

        if not handled:
            _logger.error('Cant handle external link: %s', url)

    @async
    @process
    def getURL(self, name, params=None, callback=lambda *args: None):
        urlSettings = GUI_SETTINGS.lookup(name)
        if urlSettings:
            url = yield self.__urlMacros.parse(str(urlSettings), params)
        else:
            url = yield lambda callback: callback('')
        callback(url)

    def externalAllowed(self, url):
        for handler in self._getHandlers():
            result = handler.checkHandle(url)
            if result.handled:
                return result.externalAllowed

        return False

    def _handleSpecifiedURL(self, event):
        self.open(event.url)

    @process
    def __openParsedUrl(self, urlName, params=None):
        parsedUrl = yield self.getURL(urlName, params)
        self.open(parsedUrl)

    def _handleParsedURL(self, event):
        self.__openParsedUrl(event.url)

    def _handleOpenRegistrationURL(self, _):
        self.__openParsedUrl('registrationURL')

    def _handleOpenRecoveryPasswordURL(self, _):
        self.__openParsedUrl('recoveryPswdURL')

    def _handleOpenPaymentURL(self, _):
        self.__openParsedUrl('paymentURL')

    def _handleSecuritySettingsURL(self, _):
        self.__openParsedUrl('securitySettingsURL')

    def _handleClanRulesURL(self, _):
        self.__openParsedUrl('clanRulesURL')

    def _handleSupportURL(self, _):
        self.__openParsedUrl('supportURL')

    def _handleMigrationURL(self):
        self.__openParsedUrl('migrationURL')

    def _handleFortDescription(self, _):
        self.__openParsedUrl('fortDescription')

    def _handleClanSearch(self, _):
        self.__openParsedUrl('clanSearch')

    def _handleClanCreate(self, _):
        self.__openParsedUrl('clanCreate')

    def _handleInvitesManagementURL(self, _):
        self.__openParsedUrl('invitesManagementURL')

    def _handleGmSummaryURL(self, _):
        self.__openParsedUrl('globalMapSummary')

    def _handleGmPromoSummaryURL(self, _):
        self.__openParsedUrl('globalMapPromoSummary')

    def _handleGmCapURL(self, _):
        self.__openParsedUrl('globalMapCap')

    def _handleGmPromoURL(self, _):
        self.__openParsedUrl('globalMapPromo')

    def _handleOpenPremShopURL(self, _):
        self.__openParsedUrl('premShopURL')

    def _handleFrontlineChangesURL(self, _):
        self.__openParsedUrl('frontlineChangesURL')

    def _handleTokenShopURL(self, event):
        self.__openParsedUrl('tokenShopURL', event.params)

    def _getHandlers(self):
        if not self.__linksHandlers:
            self.__linksHandlers = []
            if self.__loginManager.isWgcSteam:
                self.__linksHandlers.append(external.PremShopLinksHandler())
                self.__linksHandlers.append(
                    external.AddPlatformTagLinksHandler())
                self.__linksHandlers.append(
                    external.PremShopLinksForArgsUrlHandler())
                self.__linksHandlers.append(
                    external.AddPlatformTagLinksToArgsUrlHandler())
            self.__linksHandlers.append(external.OpenBrowserHandler())
        return self.__linksHandlers
class PersonalMissionFirstEntryView(LobbySubView,
                                    PersonalMissionFirstEntryViewMeta):
    _COMMON_SOUND_SPACE = PERSONAL_MISSIONS_SOUND_SPACE
    __settingsCore = dependency.descriptor(ISettingsCore)
    __CARDS = (3, 3, 4, 4)
    __R_PERSONAL_MISSION_FIRST_ENTRY_VIEW = R.strings.personal_missions.PersonalMissionFirstEntryView

    def __init__(self, ctx):
        super(PersonalMissionFirstEntryView, self).__init__(ctx)
        self.__urlMacros = URLMacros()
        self.__settings = GUI_SETTINGS.personalMissions.get('welcomeVideo', {})
        self.__cardsLen = len(self.__CARDS)

    def playVideo(self):
        if self.__settings.get('isEnabled', False):
            self.__showVideo()

    def backBtnClicked(self):
        self.fireEvent(events.LoadViewEvent(
            SFViewLoadParams(VIEW_ALIAS.LOBBY_PERSONAL_MISSIONS)),
                       scope=EVENT_BUS_SCOPE.LOBBY)

    def onViewClose(self, isAcceptBtnClick=False):
        if isAcceptBtnClick:
            self.__settingsCore.serverSettings.saveInUIStorage(
                {PM_TUTOR_FIELDS.GREETING_SCREEN_SHOWN: True})
            self.fireEvent(events.LoadViewEvent(
                SFViewLoadParams(VIEW_ALIAS.LOBBY_PERSONAL_MISSIONS)),
                           scope=EVENT_BUS_SCOPE.LOBBY)
        else:
            self.fireEvent(events.LoadViewEvent(
                SFViewLoadParams(VIEW_ALIAS.LOBBY_HANGAR)),
                           scope=EVENT_BUS_SCOPE.LOBBY)

    def onCardClick(self, cardIndex):
        self.__updateDetailedData(cardIndex)

    def onNextCardClick(self, cardIndex):
        self.__updateDetailedData(cardIndex + 1)

    def onPrevCardClick(self, cardIndex):
        self.__updateDetailedData(cardIndex - 1)

    def _populate(self):
        super(PersonalMissionFirstEntryView, self)._populate()
        infoBlocks = [
            self.__makeTileData(cardIndex)
            for cardIndex in xrange(0, self.__cardsLen)
        ]
        firstEntry = not self.__settingsCore.serverSettings.getUIStorage().get(
            PM_TUTOR_FIELDS.GREETING_SCREEN_SHOWN)
        self.as_setInitDataS({
            'titleLabel':
            PERSONAL_MISSIONS.PERSONALMISSIONFIRSTENTRYVIEW_TITLE,
            'bigBtnLabel':
            PERSONAL_MISSIONS.PERSONALMISSIONFIRSTENTRYVIEW_ACKNOWLEDGEBTN,
            'playVideoBtnLabel':
            text_styles.concatStylesToSingleLine(
                icons.makeImageTag(
                    RES_ICONS.MAPS_ICONS_PERSONALMISSIONS_PLAYICON,
                    width=14,
                    height=15,
                    vSpace=-2),
                i18n.makeString(PERSONAL_MISSIONS.
                                PERSONALMISSIONFIRSTENTRYVIEW_VIDEOBTNLABEL)),
            'playVideoBtnVisible':
            self.__settings.get('isEnabled', False),
            'bgSource':
            RES_ICONS.MAPS_ICONS_PERSONALMISSIONS_INFOSCREENBG,
            'infoBlocks':
            infoBlocks,
            'backBtnLabel':
            PERSONAL_MISSIONS.HEADER_BACKBTN_LABEL,
            'isFirstEntry':
            firstEntry
        })
        self.soundManager.setRTPC(SOUNDS.RTCP_OVERLAY,
                                  SOUNDS.MAX_MISSIONS_ZOOM)

    def _dispose(self):
        self.soundManager.setRTPC(SOUNDS.RTCP_OVERLAY,
                                  SOUNDS.MIN_MISSIONS_ZOOM)
        super(PersonalMissionFirstEntryView, self)._dispose()

    def __updateDetailedData(self, cardIndex):
        cardIndex = self.__normalizeSlotIndex(cardIndex)
        blocksLen = self.__CARDS[cardIndex]
        blocks = []
        for blockIndex in xrange(0, blocksLen):
            blocks.append({
                'title':
                PERSONAL_MISSIONS.getBlockTitle(cardIndex, blockIndex),
                'description':
                PERSONAL_MISSIONS.getBlockDescription(cardIndex, blockIndex),
                'image':
                RES_ICONS.getBlockImageByStep(cardIndex, blockIndex)
            })

        item = self.__R_PERSONAL_MISSION_FIRST_ENTRY_VIEW.dyn(
            'item{}'.format(cardIndex))
        data = {
            'index':
            cardIndex,
            'icon':
            RES_ICONS.getInfoIcon(cardIndex),
            'title':
            PERSONAL_MISSIONS.getCardHeader(cardIndex),
            'description':
            PERSONAL_MISSIONS.getCardInnerDescription(cardIndex),
            'blocks':
            blocks,
            'notificationIcon':
            RES_ICONS.MAPS_ICONS_LIBRARY_WARNINGICON_1,
            'notificationLabel':
            backport.text(item.warning())
            if item and 'warning' in item.keys() else ''
        }
        self.as_setDetailedCardDataS(data)

    @process
    def __showVideo(self):
        url = yield self.__urlMacros.parse(self.__settings.get('url'))
        webHandlers = webApiCollection(ui_web_api.CloseViewWebApi,
                                       sound_web_api.SoundWebApi,
                                       sound_web_api.HangarSoundWebApi)
        ctx = {
            'url': url,
            'webHandlers': webHandlers,
            'returnAlias': self.alias
        }
        showPersonalMissionBrowserView(ctx)

    @staticmethod
    def __makeTileData(cardIndex):
        return {
            'index': cardIndex,
            'iconSource': RES_ICONS.getInfoIcon(cardIndex),
            'titleLabel': PERSONAL_MISSIONS.getCardHeader(cardIndex),
            'descriptionLabel': PERSONAL_MISSIONS.getCardDescription(cardIndex)
        }

    def __normalizeSlotIndex(self, slotIndex):
        if slotIndex >= self.__cardsLen:
            return 0
        return self.__cardsLen - 1 if slotIndex < 0 else slotIndex
Beispiel #7
0
class BattleRoyaleController(Notifiable, SeasonProvider,
                             IBattleRoyaleController, IGlobalListener):
    __lobbyContext = dependency.descriptor(ILobbyContext)
    __eventsCache = dependency.descriptor(IEventsCache)
    __itemsCache = dependency.descriptor(IItemsCache)
    __hangarsSpace = dependency.descriptor(IHangarSpace)
    __settingsCore = dependency.descriptor(ISettingsCore)
    __hangarSpacesSwitcher = dependency.descriptor(IHangarSpacesSwitcher)
    __hangarSpaceReloader = dependency.descriptor(IHangarSpaceReloader)
    __notificationsCtrl = dependency.descriptor(IEventsNotificationsController)
    __sessionProvider = dependency.descriptor(IBattleSessionProvider)
    __c11nService = dependency.descriptor(ICustomizationService)
    __connectionMgr = dependency.descriptor(IConnectionManager)
    __battleRoyaleTournamentController = dependency.descriptor(
        IBattleRoyaleTournamentController)
    __bootcamp = dependency.descriptor(IBootcampController)
    TOKEN_QUEST_ID = 'token:br:title:'
    MAX_STORED_ARENAS_RESULTS = 20

    def __init__(self):
        super(BattleRoyaleController, self).__init__()
        self.onUpdated = Event.Event()
        self.onPrimeTimeStatusUpdated = Event.Event()
        self.onSpaceUpdated = Event.Event()
        self.__clientValuesInited = False
        self.__clientShields = {}
        self.__performanceGroup = None
        self.__serverSettings = None
        self.__battleRoyaleSettings = None
        self.__wasInLobby = False
        self.__equipmentCount = {}
        self.__equipmentSlots = tuple()
        self.__voControl = None
        self.__defaultHangars = {}
        self.__c11nVisible = False
        self.__urlMacros = None
        self.__isNeedToUpdateHeroTank = False
        return

    def init(self):
        super(BattleRoyaleController, self).init()
        self.__voControl = BRVoiceOverController()
        self.__voControl.init()
        self.__urlMacros = URLMacros()
        self.addNotificator(SimpleNotifier(self.getTimer, self.__timerUpdate))

    def fini(self):
        self.__voControl.fini()
        self.__voControl = None
        self.__equipmentCount = None
        self.__defaultHangars = None
        self.__urlMacros = None
        self.onUpdated.clear()
        self.onPrimeTimeStatusUpdated.clear()
        self.clearNotification()
        super(BattleRoyaleController, self).fini()
        return

    def wasInLobby(self):
        return self.__wasInLobby

    def onLobbyInited(self, event):
        super(BattleRoyaleController, self).onLobbyInited(event)
        if not self.__clientValuesInited:
            self.__clientValuesInited = True
        g_clientUpdateManager.addCallbacks(
            {'battleRoyale': self.__updateRoyale})
        self.startNotification()
        self.startGlobalListening()
        self.__hangarsSpace.onSpaceChangedByAction += self.__onSpaceChanged
        self.__hangarsSpace.onSpaceChanged += self.__onSpaceChanged
        self.__hangarsSpace.onVehicleChanged += self.__onVehicleChanged
        self.__c11nService.onVisibilityChanged += self.__onC11nVisibilityChanged
        self.__notificationsCtrl.onEventNotificationsChanged += self.__onEventNotification
        self.__onEventNotification(
            self.__notificationsCtrl.getEventsNotifications())
        self.__battleRoyaleTournamentController.onSelectBattleRoyaleTournament += self.__selectBattleRoyaleTournament
        self.__updateMode()
        self.__wasInLobby = True
        if not self.__hangarsSpace.spaceInited or self.__hangarsSpace.spaceLoading(
        ):
            self.__hangarsSpace.onSpaceCreate += self.__onSpaceCreate
        else:
            self.__updateSpace()
        nextTick(self.__eventAvailabilityUpdate)()

    def __onEventNotification(self, added, removed=()):
        for evNotification in removed:
            if evNotification.eventType in (SERVER_CMD_CHANGE_HANGAR,
                                            SERVER_CMD_CHANGE_HANGAR_PREM):
                self.__defaultHangars[evNotification.eventType ==
                                      SERVER_CMD_CHANGE_HANGAR_PREM] = None

        for evNotification in added:
            if evNotification.eventType in (SERVER_CMD_CHANGE_HANGAR,
                                            SERVER_CMD_CHANGE_HANGAR_PREM):
                try:
                    data = json.loads(evNotification.data)
                    path = data['hangar']
                except Exception:
                    path = evNotification.data

                self.__defaultHangars[evNotification.eventType ==
                                      SERVER_CMD_CHANGE_HANGAR_PREM] = path

        return

    def onDisconnected(self):
        self.__wasInLobby = False
        self.__clearClientValues()
        self.__clear()
        super(BattleRoyaleController, self).onDisconnected()

    def onAccountBecomePlayer(self):
        self.__onServerSettingsChanged(self.__lobbyContext.getServerSettings())
        super(BattleRoyaleController, self).onAccountBecomePlayer()

    def onAvatarBecomePlayer(self):
        self.__clear()
        if self.__sessionProvider.arenaVisitor.getArenaBonusType(
        ) in ARENA_BONUS_TYPE.BATTLE_ROYALE_RANGE:
            self.__voControl.activate()
        else:
            self.__voControl.deactivate()
        self.__voControl.onAvatarBecomePlayer()
        super(BattleRoyaleController, self).onAvatarBecomePlayer()

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

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

    def getEndTime(self):
        return self.getCurrentSeason().getCycleEndDate()

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

    def getDefaultAmmoCount(self, itemKey, intCD=None, vehicleName=None):
        if itemKey in AmmoTypes.SHELLS or itemKey in AmmoTypes.CHARGES:
            return self.__equipmentCount.get(itemKey, 0)
        else:
            if itemKey == AmmoTypes.ITEM:
                if intCD is None:
                    _logger.error(
                        'Cannot get count of equipment. Equipment id not specified'
                    )
                    return 0
                if vehicleName is None:
                    _logger.error(
                        "Cannot get count of '%r' equipment. Vehicle not specified",
                        intCD)
                    return 0
                vehiclesSlotsConfig = self.getModeSettings(
                ).vehiclesSlotsConfig
                if vehicleName not in vehiclesSlotsConfig:
                    _logger.error(
                        "Cannot get count of '%r' equipment. Vehicle '%r' not found",
                        intCD, vehicleName)
                    return 0
                for chargeId, equipmentId in vehiclesSlotsConfig[
                        vehicleName].iteritems():
                    if equipmentId == intCD:
                        return self.getDefaultAmmoCount(chargeId)

            else:
                _logger.warning(
                    "Can't get default ammo count. Unknown item key: '%r'.",
                    itemKey)
            return 0

    def getVehicleSlots(self):
        return self.__equipmentSlots

    def getBrVehicleEquipmentIds(self, vehicleName):
        vehiclesSlotsConfig = self.getModeSettings().vehiclesSlotsConfig
        if vehicleName not in vehiclesSlotsConfig:
            _logger.error(
                "Get equipment for vehicle '%r' failed. Vehicle not found. Slots config %s",
                vehicleName, vehiclesSlotsConfig)
            return None
        else:
            vehiclesSlots = vehiclesSlotsConfig[vehicleName]
            result = []
            for chargeName in sorted(vehiclesSlots.keys()):
                result.append(vehiclesSlots[chargeName])

            return result

    @staticmethod
    def getBrCommanderSkills():
        result = []
        if g_currentVehicle.isPresent():
            vehicle = g_currentVehicle.item
            for _, tankman in vehicle.crew:
                if tankman is not None:
                    for skill in tankman.skills:
                        result.append(skill)

        return result

    def isActive(self):
        _, isCycleActive = self.getCurrentCycleInfo()
        return self.isEnabled() and self.getCurrentSeason(
        ) is not None and isCycleActive

    def isBattleRoyaleMode(self):
        if self.prbDispatcher is None:
            return False
        else:
            state = self.prbDispatcher.getFunctionalState()
            return self.__isBattleRoyaleMode(
                state) or self.__isBattleRoyaleTournamentMode(state)

    def __isBattleRoyaleMode(self, state):
        return state.isInPreQueue(
            queueType=QUEUE_TYPE.BATTLE_ROYALE) or state.isInUnit(
                PREBATTLE_TYPE.BATTLE_ROYALE)

    def __isBattleRoyaleTournamentMode(self, state):
        return state.isInPreQueue(
            queueType=QUEUE_TYPE.BATTLE_ROYALE_TOURNAMENT) or state.isInUnit(
                PREBATTLE_TYPE.BATTLE_ROYALE_TOURNAMENT)

    def isBattlePassAvailable(self, bonusType):
        battlePassConfig = self.__lobbyContext.getServerSettings(
        ).getBattlePassConfig()
        return battlePassConfig.isEnabled(
        ) and battlePassConfig.isGameModeEnabled(bonusType)

    def isInBattleRoyaleSquad(self):
        dispatcher = self.prbDispatcher
        if dispatcher is not None:
            state = dispatcher.getFunctionalState()
            return state.isInUnit(PREBATTLE_TYPE.BATTLE_ROYALE)
        else:
            return False

    def onPrbEntitySwitched(self):
        self.__updateMode()
        self.__updateSpace()

    def selectRoyaleBattle(self):
        if not self.isEnabled():
            return
        self.__selectRoyaleBattle()

    def setDefaultHangarEntryPoint(self):
        if self.__battleRoyaleTournamentController.isSelected():
            self.__battleRoyaleTournamentController.leaveBattleRoyaleTournament(
                isChangingToBattleRoyaleHangar=True)

    def isGeneralHangarEntryPoint(self):
        return not self.__battleRoyaleTournamentController.isSelected()

    def selectRandomBattle(self):
        dispatcher = g_prbLoader.getDispatcher()
        if dispatcher is None:
            _logger.error('Prebattle dispatcher is not defined')
            return
        else:
            self.__doSelectRandomPrb(dispatcher)
            return

    def getPlayerLevelInfo(self):
        return self.__itemsCache.items.battleRoyale.accTitle

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

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

    def getQuests(self):
        _, isCycleActive = self.getCurrentCycleInfo()
        return {
            k: v
            for k, v in self.__eventsCache.getQuests().items()
            if v.getGroupID() == BATTLE_ROYALE_GROUPS_ID
            and self.__tokenIsValid(v)
        } if self.isGeneralHangarEntryPoint() and isCycleActive else {}

    def isDailyQuestsRefreshAvailable(self):
        if self.hasPrimeTimesLeftForCurrentCycle():
            return True
        serversPeriodsMapping = self.getPrimeTimesForDay(
            time_utils.getCurrentLocalServerTimestamp())
        periods = []
        for _, dayPeriods in serversPeriodsMapping.items():
            periods.append(max([periodEnd for _, periodEnd in dayPeriods]))

        if periods:
            periodTimeLeft = max(
                periods) - time_utils.getCurrentLocalServerTimestamp()
            return periodTimeLeft > time_utils.getDayTimeLeft()
        return False

    def __selectRoyaleBattle(self):
        dispatcher = g_prbLoader.getDispatcher()
        if dispatcher is None:
            _logger.error('Prebattle dispatcher is not defined')
            return
        else:
            self.__doSelectBattleRoyalePrb(dispatcher)
            return

    def __selectBattleRoyaleTournament(self):
        dispatcher = g_prbLoader.getDispatcher()
        if dispatcher is None:
            _logger.error('Prebattle dispatcher is not defined')
            return
        else:
            self.__doSelectBattleRoyaleTournamentPrb(dispatcher)
            return

    def __showBrowserView(self, url):
        webHandlers = createBattleRoyaleWebHanlders()
        alias = VIEW_ALIAS.BROWSER_VIEW
        g_eventBus.handleEvent(
            events.LoadViewEvent(SFViewLoadParams(alias,
                                                  getUniqueViewName(alias)),
                                 ctx={
                                     'url':
                                     url,
                                     'webHandlers':
                                     webHandlers,
                                     'returnAlias':
                                     VIEW_ALIAS.LOBBY_HANGAR,
                                     'onServerSettingsChange':
                                     self.__serverSettingsChangeBrowserHandler
                                 }), EVENT_BUS_SCOPE.LOBBY)

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

    def _createSeason(self, cycleInfo, seasonData):
        return BattleRoyaleSeason(cycleInfo, seasonData)

    def __onSpaceChanged(self):
        switchItems = self.__hangarSpacesSwitcher.itemsToSwitch
        if self.isBattleRoyaleMode(
        ) and self.__hangarSpacesSwitcher.currentItem != switchItems.BATTLE_ROYALE:
            self.selectRandomBattle()

    def __eventAvailabilityUpdate(self, *_):
        if g_prbLoader.getDispatcher() is None:
            return
        elif self.__battleRoyaleTournamentController.isSelected():
            return
        else:
            battleRoyaleEnabled = self.isEnabled() and self.getCurrentSeason(
            ) is not None
            isSelectRandom = not battleRoyaleEnabled and self.isBattleRoyaleMode(
            )
            if battleRoyaleEnabled and not self.isActive():
                currTime = time_utils.getCurrentLocalServerTimestamp()
                cycle = self.getCurrentSeason().getNextByTimeCycle(currTime)
                isSelectRandom = cycle is None
            if isSelectRandom:
                self.selectRandomBattle()
            return

    def __onSpaceCreate(self):
        nextTick(self.__updateSpace)()

    def __updateSpace(self):
        if not self.__c11nVisible and self.__hangarsSpace.spaceInited and not self.__hangarsSpace.spaceLoading(
        ):
            switchItems = self.__hangarSpacesSwitcher.itemsToSwitch
            isBrSpace = self.__hangarSpacesSwitcher.currentItem == switchItems.BATTLE_ROYALE
            isBrMode = self.isBattleRoyaleMode()
            if isBrMode and not isBrSpace:
                g_eventBus.handleEvent(events.HangarSpacesSwitcherEvent(
                    events.HangarSpacesSwitcherEvent.SWITCH_TO_HANGAR_SPACE,
                    ctx={'switchItemName': switchItems.BATTLE_ROYALE}),
                                       scope=EVENT_BUS_SCOPE.LOBBY)
                self.__isNeedToUpdateHeroTank = True
            elif not isBrMode and isBrSpace:
                defaultHangarPath = self.__defaultHangars.get(
                    self.__hangarsSpace.isPremium)
                if defaultHangarPath is not None:
                    self.__hangarSpaceReloader.changeHangarSpace(
                        defaultHangarPath)
                else:
                    g_eventBus.handleEvent(events.HangarSpacesSwitcherEvent(
                        events.HangarSpacesSwitcherEvent.
                        SWITCH_TO_HANGAR_SPACE,
                        ctx={'switchItemName': switchItems.DEFAULT}),
                                           scope=EVENT_BUS_SCOPE.LOBBY)
            self.onSpaceUpdated()
        return

    def __updateMode(self):
        if self.isBattleRoyaleMode():
            self.__enableRoyaleMode()
        elif not self.__bootcamp.isInBootcamp():
            self.__disableRoyaleMode()

    def __enableRoyaleMode(self):
        royaleVehicleID = AccountSettings.getFavorites(ROYALE_VEHICLE)
        if not royaleVehicleID or self.__itemsCache.items.getVehicle(
                royaleVehicleID) is None:
            criteria = REQ_CRITERIA.VEHICLE.HAS_TAGS(
                [VEHICLE_TAGS.BATTLE_ROYALE]) | REQ_CRITERIA.INVENTORY
            vehicles = self.__itemsCache.items.getVehicles
            royaleVehicle = first(
                sorted(vehicles(criteria=criteria).values(),
                       key=lambda item: item.intCD))
            if royaleVehicle:
                royaleVehicleID = royaleVehicle.invID
        if self.__c11nVisible:
            pass
        elif royaleVehicleID:
            g_currentVehicle.selectVehicle(royaleVehicleID)
        else:
            g_currentVehicle.selectNoVehicle()
        self.__voControl.activate()
        return

    def __disableRoyaleMode(self):
        storedVehInvID = AccountSettings.getFavorites(CURRENT_VEHICLE)
        if not storedVehInvID:
            criteria = REQ_CRITERIA.INVENTORY | ~REQ_CRITERIA.VEHICLE.HAS_TAGS(
                [VEHICLE_TAGS.BATTLE_ROYALE])
            vehicle = first(
                self.__itemsCache.items.getVehicles(
                    criteria=criteria).values())
            if vehicle:
                storedVehInvID = vehicle.invID
        if storedVehInvID:
            g_currentVehicle.selectVehicle(storedVehInvID)
        else:
            g_currentVehicle.selectNoVehicle()
        self.__voControl.deactivate()

    @process
    def __doSelectBattleRoyaleTournamentPrb(self, dispatcher):
        yield dispatcher.doSelectAction(
            PrbAction(PREBATTLE_ACTION_NAME.BATTLE_ROYALE_TOURNAMENT))

    @process
    def __doSelectBattleRoyalePrb(self, dispatcher):
        yield dispatcher.doSelectAction(
            PrbAction(PREBATTLE_ACTION_NAME.BATTLE_ROYALE))

    @process
    def __doSelectRandomPrb(self, dispatcher):
        yield dispatcher.doSelectAction(PrbAction(
            PREBATTLE_ACTION_NAME.RANDOM))

    @process
    def fightClick(self):
        dispatcher = g_prbLoader.getDispatcher()
        if not dispatcher:
            return
        lobbyContext = dependency.instance(ILobbyContext)
        navigationPossible = yield lobbyContext.isHeaderNavigationPossible()
        fightButtonPressPossible = yield lobbyContext.isFightButtonPressPossible(
        )
        if navigationPossible and fightButtonPressPossible:
            if dispatcher:
                dispatcher.doAction(
                    PrbAction(PREBATTLE_ACTION_NAME.BATTLE_ROYALE))
            else:
                _logger.error('Prebattle dispatcher is not defined')

    @process
    def _doLeaveBattleRoyalePrb(self, dispatcher):
        if dispatcher is None:
            return
        else:
            yield dispatcher.doLeaveAction(LeavePrbAction())
            return

    def __getBattleRoyaleSettings(self):
        generalSettings = self.__serverSettings.battleRoyale
        cycleID = None
        now = time_utils.getCurrentLocalServerTimestamp()
        _, cycleInfo = season_common.getSeason(generalSettings.asDict(), now)
        if cycleInfo:
            _, _, _, cycleID = cycleInfo
        for season in generalSettings.seasons.values():
            if cycleID in season.get('cycles', {}):
                return generalSettings.replace(season).replace(
                    season['cycles'][cycleID])

        return generalSettings

    def __getCachedSettings(self):
        return self.__battleRoyaleSettings

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

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

    def __onServerSettingsChanged(self, serverSettings):
        if self.__serverSettings is not None:
            self.__serverSettings.onServerSettingsChange -= self.__updateRoyaleSettings
        self.__serverSettings = serverSettings
        self.__battleRoyaleSettings = self.__getBattleRoyaleSettings()
        self.__updateEquipmentCount()
        self.__serverSettings.onServerSettingsChange += self.__updateRoyaleSettings
        return

    def __updateRoyaleSettings(self, diff):
        if Configs.BATTLE_ROYALE_CONFIG.value not in diff:
            return
        else:
            self.__eventAvailabilityUpdate()
            self.__battleRoyaleSettings = self.__getBattleRoyaleSettings()
            self.__updateEquipmentCount()
            self.__divisions = None
            self.onUpdated()
            self.__resetTimer()
            return

    def __clear(self):
        self.stopNotification()
        self.stopGlobalListening()
        self.__hangarsSpace.onSpaceChangedByAction -= self.__onSpaceChanged
        self.__hangarsSpace.onSpaceChanged -= self.__onSpaceChanged
        self.__hangarsSpace.onSpaceCreate -= self.__onSpaceCreate
        self.__hangarsSpace.onVehicleChanged -= self.__onVehicleChanged
        self.__notificationsCtrl.onEventNotificationsChanged -= self.__onEventNotification
        self.__c11nService.onVisibilityChanged -= self.__onC11nVisibilityChanged
        self.__battleRoyaleTournamentController.onSelectBattleRoyaleTournament -= self.__selectBattleRoyaleTournament
        self.__defaultHangars = {}
        g_clientUpdateManager.removeObjectCallbacks(self)

    def __clearClientValues(self):
        if self.__serverSettings is not None:
            self.__serverSettings.onServerSettingsChange -= self.__updateRoyaleSettings
        self.__serverSettings = None
        self.__clientValuesInited = False
        return

    def __updateRoyale(self, _):
        self.onUpdated()
        self.__resetTimer()

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

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

                if condValid:
                    self.__performanceGroup = groupName
                    return

        return

    def __updateEquipmentCount(self):
        if self.__equipmentCount:
            self.__equipmentCount = None
        self.__equipmentCount = {}
        items = self.__battleRoyaleSettings.defaultAmmo
        for itemGroup in items:
            groupKey, groupItems = itemGroup
            self.__equipmentCount[groupKey] = groupItems[0]

        return

    def __onC11nVisibilityChanged(self, isVisible):
        self.__c11nVisible = isVisible
        self.__updateSpace()

    def __onVehicleChanged(self):
        if self.__isNeedToUpdateHeroTank:
            self.__hangarsSpace.onHeroTankReady()
            self.__isNeedToUpdateHeroTank = False

    def __tokenIsValid(self, quest):
        tokens = quest.accountReqs.getTokens()
        return False if tokens and not tokens[0].isAvailable() else True
class GameWindowController(IGameWindowController):
    eventsCache = dependency.descriptor(IEventsCache)
    lobbyContext = dependency.descriptor(ILobbyContext)

    def __init__(self):
        self.__urlMacros = URLMacros()
        self.__isLobbyInited = False
        super(GameWindowController, self).__init__()

    def fini(self):
        self.__urlMacros.clear()
        self.__urlMacros = None
        self.hideWindow()
        super(GameWindowController, self).fini()
        return

    def onLobbyInited(self, event):
        self.__isLobbyInited = True
        self._addListeners()

    def onAvatarBecomePlayer(self):
        self._removeListeners()
        if self.__isLobbyInited:
            self.hideWindow()
        self.__isLobbyInited = False
        super(GameWindowController, self).onAvatarBecomePlayer()

    def onDisconnected(self):
        self.__isLobbyInited = False
        self._removeListeners()
        self.hideWindow()
        super(GameWindowController, self).onDisconnected()

    def hideWindow(self):
        raise NotImplementedError

    def showWindow(self, url=None, invokedFrom=None):
        self.hideWindow()
        self._showWindow(url, invokedFrom)

    @async
    @process
    def getUrl(self, callback=lambda *args: None):
        url = yield self.__urlMacros.parse(self._getUrl())
        callback(url)

    def _addListeners(self):
        self.eventsCache.onSyncCompleted += self._onSyncCompleted

    def _removeListeners(self):
        self.eventsCache.onSyncCompleted -= self._onSyncCompleted

    def _onSyncCompleted(self, *_):
        pass

    @process
    def _showWindow(self, url, invokedFrom=None):
        if url is None:
            url = yield self.getUrl()
            if not url:
                return
        self._openWindow(url, invokedFrom)
        return

    def _openWindow(self, url, invokedFrom=None):
        raise NotImplementedError
Beispiel #9
0
class ProgressiveItemsView(ViewImpl):
    __slots__ = ('__c11nView', '_itemsProgressData', '_possibleItems',
                 '_vehicle', '__blur', '__layoutID', '__urlMacros',
                 '__guiSettings')
    __lobbyContext = dependency.descriptor(ILobbyContext)
    __itemsCache = dependency.descriptor(IItemsCache)
    __customizationService = dependency.descriptor(ICustomizationService)
    __appLoader = dependency.descriptor(IAppLoader)
    __settingsCore = dependency.descriptor(ISettingsCore)

    def __init__(self, layoutID, c11nView, *args, **kwargs):
        settings = ViewSettings(layoutID)
        settings.args = args
        settings.kwargs = kwargs
        settings.flags = ViewFlags.LOBBY_TOP_SUB_VIEW
        settings.model = ProgressiveItemsViewModel()
        super(ProgressiveItemsView, self).__init__(settings)
        self._itemsProgressData = None
        self._possibleItems = None
        self._vehicle = None
        self.__blur = CachedBlur()
        self.__layoutID = layoutID
        self.__c11nView = c11nView
        self.__urlMacros = URLMacros()
        self.__guiSettings = GUI_SETTINGS.progressiveItems.get(
            'tutorialVideo', {})
        return

    def createToolTip(self, event):
        if event.contentID == R.views.common.tooltip_window.backport_tooltip_content.BackportTooltipContent(
        ):
            intCD = int(event.getArgument('id'))
            level = int(event.getArgument('level'))
            window = BackportTooltipWindow(
                self.__getTooltipData(intCD, event.getArgument('tooltip'),
                                      level), self.getParentWindow())
            window.load()
            return window
        return super(ProgressiveItemsView, self).createToolTip(event)

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

    def _initialize(self, *args, **kwargs):
        super(ProgressiveItemsView, self)._initialize(*args, **kwargs)
        if self.__c11nView is not None:
            self.__c11nView.changeVisible(False)
        self.viewModel.onSelectItem += self._onSelectItem
        self.viewModel.tutorial.showVideo += self._showVideoPage
        return

    def _finalize(self):
        super(ProgressiveItemsView, self)._finalize()
        if self.__c11nView is not None:
            self.__c11nView.changeVisible(True)
            self.__c11nView = None
        self.__blur.fini()
        self.viewModel.onSelectItem -= self._onSelectItem
        self.viewModel.tutorial.showVideo -= self._showVideoPage
        return

    def _onLoading(self, *args, **kwargs):
        self._vehicle = g_currentVehicle.item
        self._possibleItems = self._getPossibleItemsForVehicle()
        self._itemsProgressData = self.__itemsCache.items.inventory.getC11nProgressionDataForVehicle(
            self._vehicle.intCD)
        itemIntCD = kwargs.get('itemIntCD')
        with self.getViewModel().transaction() as model:
            model.setTankName(self._vehicle.userName)
            model.setTankLevel(int2roman(self._vehicle.level))
            model.setTankType(self._vehicle.typeBigIconResource())
            self.__setItems(model)
            model.setIsRendererPipelineDeferred(isRendererPipelineDeferred())
            model.setItemToScroll(0 if itemIntCD is None else itemIntCD)
        return

    def _onLoaded(self, *args, **kwargs):
        self.__blur.enable()
        self.__settingsCore.serverSettings.setOnceOnlyHintsSettings(
            {OnceOnlyHints.C11N_PROGRESSION_VIEW_HINT: HINT_SHOWN_STATUS})

    def _onSelectItem(self, args=None):
        if args is not None:
            intCD = int(args['intCD'])
            level = int(args['level'])
            item = self.__customizationService.getItemByCD(intCD)
            ctx = self.__customizationService.getCtx()

            def changeTabAndGetItemToHand():
                ctx.changeModeWithProgressionDecal(intCD)
                ctx.events.onGetItemBackToHand(item, level, scrollToItem=True)
                noveltyCount = self._vehicle.getC11nItemNoveltyCounter(
                    proxy=self.__itemsCache.items, item=item)
                if noveltyCount:
                    BigWorld.callback(
                        0.0, lambda: ctx.resetItemsNovelty([item.intCD]))

            BigWorld.callback(0.0, changeTabAndGetItemToHand)
        self.destroyWindow()
        return

    def _showVideoPage(self, args=None):
        self.__showVideo()

    @process
    def __showVideo(self):
        url = yield self.__urlMacros.parse(self.__guiSettings.get('url'))
        webHandlers = webApiCollection(ui_web_api.CloseViewWebApi,
                                       sound_web_api.SoundWebApi,
                                       sound_web_api.HangarSoundWebApi)
        ctx = {'url': url, 'webHandlers': webHandlers}
        showProgressiveItemsBrowserView(ctx)

    def _getPossibleItemsForVehicle(self):
        customizationCache = vehicles.g_cache.customization20()
        vehicleType = self._vehicle.descriptor.type
        sortedItems = sorted(
            customizationCache.customizationWithProgression.itervalues(),
            key=lambda i: i.id)
        return [
            item.compactDescr for item in sortedItems
            if item.filter.matchVehicleType(vehicleType)
        ]

    def __setItems(self, model):
        for intCD in self._possibleItems:
            itemModel = ItemModel()
            item = self.__customizationService.getItemByCD(intCD)
            itemModel.setItemId(intCD)
            itemModel.setItemUserString(item.userName)
            itemModel.setMaxLevel(item.getMaxProgressionLevel())
            itemModel.setScaleFactor(item.formfactor)
            latestOpenedLevel = item.getLatestOpenedProgressionLevel(
                self._vehicle)
            itemModel.setCurrentLevel(1 if latestOpenedLevel ==
                                      -1 else latestOpenedLevel + 1)
            self.__setEachLevelInfo(itemModel, item)
            model.progressiveItems.addViewModel(itemModel)

    def __setEachLevelInfo(self, model, item):
        for level in xrange(1, model.getMaxLevel() + 1):
            levelInfo = ItemLevelInfoModel()
            levelInfo.setLevel(level)
            levelInfo.setLevelText(getProgressionItemStatusText(level))
            levelInfo.setUnlocked(level < model.getCurrentLevel())
            icon = item.iconUrlByProgressionLevel(
                level, _PREVIEW_ICON_SIZE,
                _PREVIEW_ICON_INNER_SIZE.get(item.formfactor))
            levelInfo.setIcon(icon)
            if level == model.getCurrentLevel():
                levelInfo.setInProgress(True)
                levelInfo.progressBlock.setUnlockCondition(
                    _ms(
                        item.progressionConditions.get(level, {})[0].get(
                            'description', '')))
                currProgress = int(
                    item.getCurrentProgressOnCurrentLevel(self._vehicle))
                currProgress = currProgress if currProgress > 0 else 0
                maxProgress = int(
                    item.progressionConditions.get(level,
                                                   {})[0].get('value', '1'))
                if maxProgress > 1:
                    levelInfo.progressBlock.setProgressionVal(currProgress)
                    levelInfo.progressBlock.setMaxProgressionVal(maxProgress)
                else:
                    levelInfo.progressBlock.setHideProgressBarAndString(True)
            model.eachLevelInfo.addViewModel(levelInfo)

    @staticmethod
    def __getTooltipData(intCD, tooltip, level):
        return createTooltipData(isSpecial=True,
                                 specialAlias=tooltip,
                                 specialArgs=CustomizationTooltipContext(
                                     itemCD=intCD,
                                     level=level,
                                     showOnlyProgressBlock=True))
class MarathonEvent(IMarathonEvent, MarathonEventDataProvider):
    _eventsCache = dependency.descriptor(IEventsCache)
    _bootcamp = dependency.descriptor(IBootcampController)
    _lobbyContext = dependency.descriptor(ILobbyContext)

    def __init__(self):
        super(MarathonEvent, self).__init__()
        self.__isEnabled = False
        self.__isAvailable = False
        self.__rewardObtained = False
        self.__state = ''
        self.__suspendFlag = False
        self.__quest = None
        self.__group = None
        self.__urlMacros = URLMacros()
        return

    @async
    @process
    def getUrl(self, callback):
        url = yield self.__urlMacros.parse(self._getUrl())
        callback(url)

    @async
    @process
    def getMarathonVehicleUrl(self, callback):
        url = yield self.__urlMacros.parse(self._getUrl(urlType=MarathonConfig.REWARD_VEHICLE_URL))
        callback(url)

    def getFinishSaleTime(self):
        return self._lobbyContext.getServerSettings().getMarathonConfig()[MarathonConfig.FINISH_SALE_TIME]

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

    def getHangarFlag(self, state=None):
        return backport.image(R.images.gui.maps.icons.library.hangarFlag.dyn(self.hangarFlags)())

    @prbEntityProperty
    def prbEntity(self):
        return None

    def isEnabled(self):
        return self.__isEnabled and not self._bootcamp.isInBootcamp()

    def isAvailable(self):
        return self.__isAvailable

    def getQuestsData(self, prefix=None, postfix=None):
        return self.__getProgress('quests', prefix, postfix)

    def getTokensData(self, prefix=None, postfix=None):
        return self.__getProgress('tokens', prefix, postfix)

    def getMarathonProgress(self, byCompletedTokensCount=False):
        tokens = self.getTokensData(prefix=self.tokenPrefix, postfix=self.completedTokenPostfix)
        if byCompletedTokensCount:
            return (len(tokens), self.questsInChain)
        tokenPrefixLen = len(self.tokenPrefix)
        res = []
        for tokenNames in tokens.keys():
            name = str(tokenNames[tokenPrefixLen:])
            res.append(int(filter(str.isdigit, name)))

        currentStep = sorted(res)[-1] if res else 0
        return (currentStep, self.questsInChain)

    def getState(self):
        return self.__state

    def getTooltipHeader(self):
        if self.tooltipHeaderType == MARATHON_FLAG_TOOLTIP_HEADERS.PROGRESS:
            return self.getFormattedDaysStatus()
        if self.tooltipHeaderType == MARATHON_FLAG_TOOLTIP_HEADERS.COUNTDOWN:
            return self.getFormattedRemainingTime()
        return self.getFormattedStartFinishText() if self.tooltipHeaderType == MARATHON_FLAG_TOOLTIP_HEADERS.TEXT else ('', '')

    def getMarathonFlagState(self, vehicle):
        return {'flagHeaderIcon': self.__getHangarFlagHeaderIcon(),
         'flagStateIcon': self.__getHangarFlagStateIcon(vehicle),
         'flagMain': self.getHangarFlag(self.__state),
         'tooltip': self.__getTooltip(),
         'enable': self.isAvailable(),
         'visible': self.isEnabled()}

    def checkForWarnings(self, vehicle):
        wrongBattleType = self.prbEntity.getEntityType() != constants.ARENA_GUI_TYPE.RANDOM
        if wrongBattleType:
            return MARATHON_WARNING.WRONG_BATTLE_TYPE
        wrongVehicleLevel = vehicle.level < self.minVehicleLevel
        return MARATHON_WARNING.WRONG_VEH_TYPE if wrongVehicleLevel else ''

    def isRewardObtained(self):
        return self.__rewardObtained

    def getMarathonQuests(self):
        return self._eventsCache.getHiddenQuests(self.__marathonFilterFunc)

    def getFormattedDaysStatus(self):
        icon = self.icons.iconFlag
        text = self.__getProgressInDays(self.__getTimeFromGroupStart())
        return (icon, text)

    def getFormattedRemainingTime(self):
        firstQuestStartTimeLeft, firstQuestFinishTimeLeft = self.__getQuestTimeInterval()
        if self.__state == MARATHON_STATE.NOT_STARTED:
            icon = self.icons.timeIconGlow
            text = self.__getTillTimeStart(firstQuestStartTimeLeft)
        elif self.__state == MARATHON_STATE.IN_PROGRESS:
            text = self.__getTillTimeEnd(firstQuestFinishTimeLeft)
            if firstQuestFinishTimeLeft > ONE_DAY:
                icon = self.icons.iconFlag
            else:
                icon = self.icons.timeIconGlow
        else:
            icon = self.icons.iconFlag
            text = text_styles.main(backport.text(self.tooltips.stateComplete))
        return (icon, text)

    def getFormattedStartFinishText(self):
        startDate, finishDate = self.__getGroupStartFinishTime()
        startDateStruct = getTimeStructInLocal(startDate)
        finishDateStruct = getTimeStructInLocal(finishDate)
        startDateText = text_styles.main(backport.text(TOOLTIPS.MARATHON_DATE, day=startDateStruct.tm_mday, month=i18n.makeString(MENU.datetime_months(startDateStruct.tm_mon)), hour=startDateStruct.tm_hour, minutes=i18n.makeString('%02d', startDateStruct.tm_min)))
        finishDateText = text_styles.main(backport.text(TOOLTIPS.MARATHON_DATE, day=finishDateStruct.tm_mday, month=i18n.makeString(MENU.datetime_months(finishDateStruct.tm_mon)), hour=finishDateStruct.tm_hour, minutes=i18n.makeString('%02d', finishDateStruct.tm_min)))
        text = text_styles.main(backport.text(TOOLTIPS.MARATHON_SUBTITLE, startDate=startDateText, finishDate=finishDateText))
        return ('', text)

    def getExtraTimeToBuy(self, timeFormat=TIME_FORMAT_DAYS):
        if self.__state == MARATHON_STATE.FINISHED:
            _, groupFinishTimeLeft = self.__getGroupTimeInterval()
            timeUnit = TIME_FORMAT_TO_TIME_UNIT[timeFormat]
            if groupFinishTimeLeft < timeUnit:
                groupFinishTimeLeft += timeUnit
            if timeFormat == TIME_FORMAT_HOURS:
                templateKey = self.tooltips.hoursShort
            else:
                templateKey = self.tooltips.daysShort
            fmtText = text_styles.neutral(backport.text(templateKey, value=str(int(groupFinishTimeLeft // timeUnit))))
            return fmtText

    def getMarathonDiscount(self):
        passQuests, allQuests = self.getMarathonProgress()
        return int(passQuests * 100) / allQuests if passQuests and self.questsInChain else 0

    def setState(self):
        if not self.__group:
            self.__isEnabled = False
            self.__isAvailable = False
            self.__state = MARATHON_STATE.UNKNOWN
            return self.__state
        if self.__suspendFlag:
            self.__state = MARATHON_STATE.SUSPENDED
            self.__isEnabled = True
            self.__isAvailable = False
            return self.__state
        groupStartTimeLeft, groupFinishTimeLeft = self.__getGroupTimeInterval()
        zeroTime = ZERO_TIME
        if groupStartTimeLeft > zeroTime or groupFinishTimeLeft <= zeroTime:
            self.__isEnabled = False
            self.__isAvailable = False
            self.__state = MARATHON_STATE.DISABLED
            return self.__state
        if groupStartTimeLeft <= zeroTime < groupFinishTimeLeft:
            self.__isEnabled = True
            self.__isAvailable = True
        firstQuestStartTimeLeft, firstQuestFinishTimeLeft = self.__getQuestTimeInterval()
        if firstQuestStartTimeLeft > zeroTime:
            self.__state = MARATHON_STATE.NOT_STARTED
        elif firstQuestStartTimeLeft <= zeroTime < firstQuestFinishTimeLeft:
            self.__state = MARATHON_STATE.IN_PROGRESS
        elif firstQuestFinishTimeLeft <= zeroTime < groupFinishTimeLeft:
            self.__state = MARATHON_STATE.FINISHED
        return self.__state

    def updateQuestsData(self):
        self.__suspendFlag = False
        quests = self._eventsCache.getHiddenQuests(self.__marathonFilterFunc)
        if quests:
            sortedQuests = sorted(quests.itervalues(), key=self.__findEarliestQuest)
            for q in quests:
                if self.suspendPrefix in q:
                    self.__suspendFlag = True
                    break

            self.__quest = sortedQuests[0]
        else:
            self.__quest = None
        groups = self._eventsCache.getGroups(self.__marathonFilterFunc)
        if groups:
            sortedGroups = sorted(groups)
            self.__group = groups[sortedGroups[0]]
        else:
            self.__group = None
        tokens = self.getTokensData(prefix=self.tokenPrefix)
        rewardObtained = False
        for key in self.awardTokens:
            if key in tokens and tokens[key][_TOKEN_COUNT_INDEX] > 0:
                rewardObtained = True
                break

        self.__setRewardObtained(rewardObtained)
        return

    def getClosestStatusUpdateTime(self):
        if self.__state == MARATHON_STATE.NOT_STARTED:
            timeLeft, _ = self.__getQuestTimeInterval()
            return timeLeft + 1
        if self.__state == MARATHON_STATE.IN_PROGRESS:
            _, timeLeft = self.__getQuestTimeInterval()
            return timeLeft + 1
        if self.__state == MARATHON_STATE.FINISHED:
            _, timeLeft = self.__getGroupTimeInterval()
            return timeLeft

    def showRewardVideo(self):
        videoShownKey = self.__getRewardShownMarkKey(MARATHON_VIDEO_WAS_SHOWN_PREFIX)
        if self.isRewardObtained() and self.doesShowRewardVideo() and not AccountSettings.getUIFlag(videoShownKey):
            showMarathonReward(self.vehicleID, videoShownKey)

    def showRewardScreen(self):
        if not self.doesShowRewardScreen():
            return
        if self.__state in MARATHON_STATE.DISABLED_STATE:
            return
        if self.isRewardObtained() and not AccountSettings.getUIFlag(self.__getRewardShownMarkKey(MARATHON_REWARD_WAS_SHOWN_PREFIX)):
            showBrowserOverlayView(self._getUrl() + self.marathonCompleteUrlAdd, alias=VIEW_ALIAS.BROWSER_OVERLAY, callbackOnLoad=partial(self.__setScreenWasShown, MARATHON_REWARD_WAS_SHOWN_PREFIX))

    def createMarathonWebHandlers(self):
        from gui.marathon.web_handlers import createDefaultMarathonWebHandlers
        return createDefaultMarathonWebHandlers()

    def getVehiclePreviewTitleTooltip(self):
        finishSaleTime = self.__getDateTimeText(self.getFinishSaleTime())
        questStartTime, _ = self.__getQuestStartFinishTime()
        questStartTimeText = self.__getDateTimeText(questStartTime)
        body = self.infoBody
        addInfo = self.bodyAddInfo
        if self.__state == MARATHON_STATE.NOT_STARTED:
            tooltipBody = body.announce()
            addInfo = backport.text(addInfo.announce(), addInfo=backport.text(self.tooltips.previewAnnounce, marathonStartDate=text_styles.neutral(questStartTimeText)))
            return self.__getPreviewInfoTooltip(tooltipBody, addInfo)
        if self.__state in (MARATHON_STATE.IN_PROGRESS, MARATHON_STATE.FINISHED):
            tooltipBody = body.progress.withDiscount() if self.getMarathonDiscount() else body.progress()
            endVehicleSellDate = text_styles.neutral(finishSaleTime)
            addInfo = backport.text(addInfo.progress(), endVehicleSellDate=endVehicleSellDate, addInfo=backport.text(self.tooltips.previewInProgress))
            return self.__getPreviewInfoTooltip(tooltipBody, addInfo)
        return makeTooltip()

    def getPreviewBuyBtnData(self):
        buyImage = backport.image(R.images.gui.maps.icons.library.buyInWeb())
        label = backport.text(_R_BUYING_PANEL.buyBtn.label.buy())
        enabled = False
        questStartTime, _ = self.__getQuestStartFinishTime()
        questStartTimeText = self.__getDateTimeText(questStartTime)
        customOffer = None
        addInfo = backport.text(self.tooltips.previewAnnounce, marathonStartDate=text_styles.neutral(questStartTimeText))
        tooltip = makeTooltip(header=backport.text(_R_BUYING_PANEL.buyBtn.tooltip.inactive.header()), body=backport.text(_R_BUYING_PANEL.buyBtn.tooltip.inactive.body(), addInfo=addInfo))
        if self.__state == MARATHON_STATE.IN_PROGRESS or self.__state == MARATHON_STATE.FINISHED:
            enabled = True
            tooltip = makeTooltip(body=backport.text(_R_BUYING_PANEL.buyBtn.tooltip.active.body()))
            if self.getMarathonDiscount():
                label = backport.text(_R_BUYING_PANEL.buyBtn.label.buyWithDiscount())
                discountText = text_styles.stats(backport.text(_R_BUYING_PANEL.customOffer.discount()))
                discountValue = text_styles.promoTitle(' {}'.format(backport.text(R.strings.quests.action.discount.percent(), value=backport.getIntegralFormat(self.getMarathonDiscount()))))
                customOffer = ''.join((discountText, discountValue))
        return {'enabled': enabled,
         'label': label,
         'btnIcon': buyImage,
         'btnIconAlign': _BUYING_BUTTON_ICON_ALIGN,
         'btnTooltip': tooltip,
         'customOffer': customOffer}

    def _getUrl(self, urlType=MarathonConfig.URL):
        baseUrl = self._lobbyContext.getServerSettings().getMarathonConfig().get(urlType, MarathonConfig.EMPTY_PATH)
        if not baseUrl:
            _logger.warning('Marathon url from marathon_config.xml is absent or invalid: %s', baseUrl)
        return baseUrl

    def __getDateTimeText(self, dateTime):
        localDateTime = getTimeStructInLocal(dateTime)
        monthName = backport.text(R.strings.menu.dateTime.months.dyn('c_{}'.format(localDateTime.tm_mon))())
        dateTimeText = backport.text(R.strings.marathon.vehiclePreview.tooltip.dateTime(), day=localDateTime.tm_mday, monthName=monthName, year=localDateTime.tm_year, hour=localDateTime.tm_hour, min='{min:02d}'.format(min=localDateTime.tm_min))
        return self.__noWrapTextFormat(dateTimeText)

    def __getPreviewInfoTooltip(self, tooltipBody, addInfo):
        return makeTooltip(header=backport.text(R.strings.marathon.vehiclePreview.title.tooltip.header()), body=backport.text(tooltipBody, addInfo=addInfo))

    def __getRewardShownMarkKey(self, key):
        return '_'.join([key, self.tokenPrefix])

    def __setScreenWasShown(self, key):
        AccountSettings.setUIFlag(self.__getRewardShownMarkKey(key), True)

    def __setRewardObtained(self, obtained):
        self.__rewardObtained = obtained

    def __findEarliestQuest(self, quest):
        return quest.getStartTimeLeft()

    def __marathonFilterFunc(self, q):
        return q.getID().startswith(self.prefix)

    def __getProgress(self, progressType, prefix=None, postfix=None):
        progress = {}
        if progressType == 'quests':
            progress = self._eventsCache.questsProgress.getQuestsData()
        elif progressType == 'tokens':
            progress = self._eventsCache.questsProgress.getTokensData()
        if prefix:
            progress = {k:v for k, v in progress.iteritems() if k.startswith(prefix)}
        if postfix:
            progress = {k:v for k, v in progress.iteritems() if k.endswith(postfix)}
        return progress

    def __getHangarFlagHeaderIcon(self):
        for key, imgPath in self.icons.mapFlagHeaderIcon.iteritems():
            if self.__state in key:
                return imgPath

    def __getHangarFlagStateIcon(self, vehicle):
        if self.__state not in MARATHON_STATE.ENABLED_STATE:
            return ''
        if self.isRewardObtained():
            return self.icons.okIcon
        if self.__state == MARATHON_STATE.NOT_STARTED:
            return self.icons.timeIcon
        if self.__state == MARATHON_STATE.IN_PROGRESS:
            warning = self.checkForWarnings(vehicle)
            if warning:
                return self.icons.alertIcon
            _, firstQuestFinishTimeLeft = self.__getQuestTimeInterval()
            if firstQuestFinishTimeLeft > ONE_DAY:
                return self.icons.iconFlag
            if firstQuestFinishTimeLeft <= ONE_DAY:
                return self.icons.timeIcon
        return self.icons.saleIcon if self.__state == MARATHON_STATE.FINISHED else ''

    def __noWrapTextFormat(self, text):
        return text.replace(' ', '&nbsp;')

    def __getTillTimeEnd(self, value):
        return self.__getFormattedTillTimeString(value, self.tooltips.stateEnd)

    def __getTillTimeStart(self, value):
        return self.__getFormattedTillTimeString(value, self.tooltips.stateStart, extraFmt=True)

    def __getProgressInDays(self, value):
        return self.__getTextInDays(value, self.tooltips.stateProgress)

    def __getTextInDays(self, timeValue, keyNamespace):
        gmtime = time.gmtime(timeValue)
        text = text_styles.stats(backport.text(self.tooltips.daysShort, value=str(time.struct_time(gmtime).tm_yday)))
        return text_styles.main(backport.text(keyNamespace, value=text))

    def __getFormattedTillTimeString(self, timeValue, keyNamespace, extraFmt=False):
        gmtime = time.gmtime(timeValue)
        if timeValue >= ONE_DAY:
            text = backport.text(self.tooltips.daysShort, value=str(gmtime.tm_yday))
        elif timeValue >= ONE_HOUR:
            text = backport.text(self.tooltips.hoursShort, value=str(gmtime.tm_hour + 1))
        else:
            text = backport.text(self.tooltips.minutesShort, value=str(gmtime.tm_min + 1))
        return text_styles.main(backport.text(keyNamespace, value=text_styles.stats(text))) if extraFmt or timeValue >= ONE_DAY else text_styles.tutorial(backport.text(keyNamespace, value=text))

    def __getTooltip(self):
        return self.flagTooltip if self.isAvailable() else self.disabledFlagTooltip

    def __getTimeFromGroupStart(self):
        return self.__group.getTimeFromStartTillNow() if self.__group else ZERO_TIME

    def __getGroupTimeInterval(self):
        return (self.__group.getStartTimeLeft(), self.__group.getFinishTimeLeft()) if self.__group else (ZERO_TIME, ZERO_TIME)

    def __getQuestTimeInterval(self):
        return (self.__quest.getStartTimeLeft(), self.__quest.getFinishTimeLeft()) if self.__quest else (ZERO_TIME, ZERO_TIME)

    def __getQuestStartFinishTime(self):
        return (self.__quest.getStartTimeRaw(), self.__quest.getFinishTimeRaw()) if self.__quest else (ZERO_TIME, ZERO_TIME)

    def __getGroupStartFinishTime(self):
        return (self.__group.getStartTimeRaw(), self.__group.getFinishTimeRaw()) if self.__group else (ZERO_TIME, ZERO_TIME)
Beispiel #11
0
 def externalBrowser(self, cmd):
     linkCtrl = dependency.instance(IExternalLinksController)
     urlParser = URLMacros(allowedMacroses=['DB_ID'])
     url = yield urlParser.parse(url=cmd.url)
     linkCtrl.open(url)
Beispiel #12
0
class BrowserController(IBrowserController):
    def __init__(self):
        super(BrowserController, self).__init__()
        self.__browsers = {}
        self.__browsersCallbacks = {}
        self.__browserCreationCallbacks = {}
        self.__browserIDGenerator = SequenceIDGenerator()
        self.__eventMgr = Event.EventManager()
        self.onBrowserAdded = Event.Event(self.__eventMgr)
        self.onBrowserDeleted = Event.Event(self.__eventMgr)
        self.__urlMacros = URLMacros()
        self.__pendingBrowsers = {}
        self.__creatingBrowserID = None
        self.__filters = _getGlobalFilters()
        return

    def fini(self):
        self.__filters = None
        self.__eventMgr.clear()
        self.__eventMgr = None
        self.__urlMacros.clear()
        self.__urlMacros = None
        self.__browsersCallbacks.clear()
        self.__browsersCallbacks = None
        self.__browsers.clear()
        self.__browsers = None
        self.__pendingBrowsers.clear()
        self.__pendingBrowsers = None
        self.__browserIDGenerator = None
        super(BrowserController, self).fini()
        return

    def onAvatarBecomePlayer(self):
        self.__stop()
        BigWorld.destroyBrowser()

    def onDisconnected(self):
        self.__stop()
        BigWorld.destroyBrowser()

    def onLobbyStarted(self, ctx):
        BigWorld.createBrowser()

    def addFilterHandler(self, handler):
        self.__filters.add(handler)

    def removeFilterHandler(self, handler):
        self.__filters.discard(handler)

    @async
    @process
    def load(self,
             url=None,
             title=None,
             showActionBtn=True,
             showWaiting=True,
             browserID=None,
             isAsync=False,
             browserSize=None,
             isDefault=True,
             callback=None,
             showCloseBtn=False,
             useBrowserWindow=True,
             isModal=False,
             showCreateWaiting=False,
             handlers=None,
             showBrowserCallback=None,
             isSolidBorder=False):
        if showCreateWaiting:
            Waiting.show('browser/init')
        url = yield self.__urlMacros.parse(url or GUI_SETTINGS.browser.url)
        suffix = yield self.__urlMacros.parse(GUI_SETTINGS.browser.params)
        concatenator = '&' if '?' in url else '?'
        if suffix not in url:
            url = concatenator.join([url, suffix])
        size = browserSize or BROWSER.SIZE
        webBrowserID = browserID
        if browserID is None:
            browserID = self.__browserIDGenerator.next()
            webBrowserID = browserID
        elif not isinstance(browserID, int):
            webBrowserID = self.__browserIDGenerator.next()
        ctx = {
            'url': url,
            'title': title,
            'showActionBtn': showActionBtn,
            'showWaiting': showWaiting,
            'browserID': browserID,
            'size': size,
            'isAsync': isAsync,
            'showCloseBtn': showCloseBtn,
            'showWindow': useBrowserWindow,
            'alias': VIEW_ALIAS.BROWSER_WINDOW_MODAL
            if isModal else VIEW_ALIAS.BROWSER_WINDOW,
            'showCreateWaiting': showCreateWaiting,
            'handlers': handlers,
            'showBrowserCallback': showBrowserCallback,
            'isSolidBorder': isSolidBorder
        }
        if browserID not in self.__browsers and browserID not in self.__pendingBrowsers:
            appLoader = dependency.instance(IAppLoader)
            app = appLoader.getApp()
            if app is None:
                raise SoftException('Application can not be None')
            browser = WebBrowser(webBrowserID,
                                 app,
                                 size,
                                 url,
                                 handlers=self.__filters)
            self.__browsers[browserID] = browser
            if self.__isCreatingBrowser():
                _logger.info('CTRL: Queueing a browser creation: %r - %s',
                             browserID, url)
                self.__pendingBrowsers[browserID] = ctx
            else:
                self.__createBrowser(ctx)
        elif browserID in self.__pendingBrowsers:
            _logger.info(
                'CTRL: Re-queuing a browser creation, overriding: %r - %s',
                browserID, url)
            self.__pendingBrowsers[browserID] = ctx
        elif browserID in self.__browsers:
            _logger.info('CTRL: Re-navigating an existing browser: %r - %s',
                         browserID, url)
            browser = self.__browsers[browserID]
            browser.navigate(url)
            browser.changeTitle(title)
        callback(browserID)
        return

    def getAllBrowsers(self):
        return self.__browsers

    def getBrowser(self, browserID):
        return self.__browsers.get(browserID)

    def delBrowser(self, browserID):
        if browserID in self.__browsers:
            _logger.info('CTRL: Deleting a browser: %s', browserID)
            browser = self.__browsers.pop(browserID)
            self.__clearCallbacks(browserID, browser, True)
            browser.destroy()
            if self.__creatingBrowserID == browserID:
                self.__creatingBrowserID = None
                self.__tryCreateNextPendingBrowser()
            if browserID in self.__pendingBrowsers:
                del self.__pendingBrowsers[browserID]
        self.onBrowserDeleted(browserID)
        return

    def __isCreatingBrowser(self):
        return self.__creatingBrowserID is not None

    def __createDone(self, ctx):
        _logger.info('CTRL: Finished creating a browser: %r',
                     self.__creatingBrowserID)
        if ctx['showCreateWaiting']:
            Waiting.hide('browser/init')

    def __tryCreateNextPendingBrowser(self):
        self.__creatingBrowserID = None
        if self.__pendingBrowsers:
            nextCtx = self.__pendingBrowsers.popitem()[1]
            self.__createBrowser(nextCtx)
        return

    def __createBrowser(self, ctx):
        browserID = ctx['browserID']
        _logger.info('CTRL: Creating a browser: %r - %s', browserID,
                     ctx['url'])
        self.__creatingBrowserID = browserID
        browser = self.__browsers[browserID]
        if not browser.create():
            _logger.info('CTRL: Failed the create step: %r', browserID)
            self.delBrowser(browserID)
            self.__tryCreateNextPendingBrowser()
            return
        else:
            self.onBrowserAdded(browserID)

            def createNextBrowser():
                _logger.info(
                    'CTRL: Triggering create of next browser from: %r',
                    browserID)
                creation = self.__browserCreationCallbacks.pop(browserID, None)
                if creation is not None:
                    self.__browsers[
                        browserID].onCanCreateNewBrowser -= creation
                self.__tryCreateNextPendingBrowser()
                return

            def failedCreationCallback(url):
                _logger.info('CTRL: Failed a creation: %r - %s', browserID,
                             url)
                self.__clearCallbacks(browserID, self.__browsers[browserID],
                                      False)
                self.delBrowser(browserID)

            def successfulCreationCallback(url, isLoaded, httpStatusCode=None):
                _logger.info('CTRL: Ready to show: %r isLoaded: %r %s',
                             browserID, isLoaded, url)
                self.__clearCallbacks(browserID, self.__browsers[browserID],
                                      False)
                if isLoaded:
                    self.__showBrowser(browserID, ctx)
                else:
                    _logger.warning('Browser request url %s was not loaded!',
                                    url)
                g_eventBus.handleEvent(
                    BrowserEvent(BrowserEvent.BROWSER_CREATED, ctx=ctx))
                self.__createDone(ctx)

            def titleUpdateCallback(title):
                ctx['title'] = title

            browser.onCanCreateNewBrowser += createNextBrowser
            self.__browserCreationCallbacks[browserID] = createNextBrowser
            browser.onFailedCreation += failedCreationCallback
            browser.onTitleChange += titleUpdateCallback
            if ctx['isAsync']:
                self.__browsersCallbacks[browserID] = (
                    None, successfulCreationCallback, failedCreationCallback,
                    titleUpdateCallback)
                browser.onLoadEnd += successfulCreationCallback
            else:
                self.__browsersCallbacks[browserID] = (
                    successfulCreationCallback, None, failedCreationCallback,
                    titleUpdateCallback)
                browser.onReady += successfulCreationCallback
            return

    def __stop(self):
        while self.__browsers:
            browserID, browser = self.__browsers.popitem()
            self.__clearCallbacks(browserID, browser, True)
            browser.destroy()

    def __clearCallbacks(self, browserID, browser, incDelayedCreation):
        ready, loadEnd, failed, title = self.__browsersCallbacks.pop(
            browserID, (None, None, None, None))
        if browser is not None:
            if failed is not None:
                browser.onFailedCreation -= failed
            if ready is not None:
                browser.onReady -= ready
            if loadEnd is not None:
                browser.onLoadEnd -= loadEnd
            if title is not None:
                browser.onTitleChange -= title
        if incDelayedCreation:
            creation = self.__browserCreationCallbacks.pop(browserID, None)
            if browser is not None and creation is not None:
                browser.onCanCreateNewBrowser -= creation
        return

    def __showBrowser(self, browserID, ctx):
        _logger.info('CTRL: Showing a browser: %r - %s', browserID, ctx['url'])
        if ctx.get('showWindow'):
            alias = ctx['alias']
            g_eventBus.handleEvent(
                LoadViewEvent(SFViewLoadParams(alias,
                                               getViewName(alias, browserID)),
                              ctx=ctx), EVENT_BUS_SCOPE.LOBBY)
        showBrowserCallback = ctx.get('showBrowserCallback')
        if showBrowserCallback:
            showBrowserCallback()
Beispiel #13
0
class EventProgressionController(IEventProgressionController):
    __lobbyContext = dependency.descriptor(ILobbyContext)
    __itemsCache = dependency.descriptor(IItemsCache)
    __eventsCache = dependency.descriptor(IEventsCache)
    __connectionMgr = dependency.descriptor(IConnectionManager)
    __questController = dependency.descriptor(IQuestsController)
    onUpdated = Event.Event()

    def __init__(self):
        self.__urlMacros = URLMacros()
        self.__isEnabled = False
        self.__isFrontLine = False
        self.__isSteelHunter = False
        self.__url = ''
        self.__actualRewardPoints = 0
        self.__seasonRewardPoints = 0
        self.__maxRewardPoints = 0
        self.__rewardPointsTokenID = ''
        self.__seasonPointsTokenID = ''
        self.__rewardVehicles = []
        self.__currentMode = None
        self.__questCardLevelTxtId = 0
        self.__progressionNameCycleTxtId = 0
        self.__progressionIconId = 0
        self.__selectorLabelTxtId = 0
        self.__selectorRibbonResId = 0
        self.__allCyclesWasEndedResId = 0
        self.__selectorData = 0
        self.__selectorType = 0
        self.__selectorQueueType = 0
        self.__questPrefix = ''
        self.__activeQuestIDs = []
        self.__primeTimeTitle = ''
        self.__primeTimeBg = ''
        return

    @property
    def isEnabled(self):
        return self.__isEnabled

    @property
    def isFrontLine(self):
        return self.__isEnabled and self.__isFrontLine

    @property
    def isSteelHunter(self):
        return self.__isEnabled and self.__isSteelHunter

    @property
    def url(self):
        return self.__url

    @property
    def questPrefix(self):
        return self.__questPrefix

    @property
    def actualRewardPoints(self):
        return self.__actualRewardPoints

    @property
    def seasonRewardPoints(self):
        dossier = self.__itemsCache.items.getAccountDossier()
        achievements = dossier.getDossierDescr().expand('epicSeasons')
        achievements = chain(
            achievements.items(),
            dossier.getDossierDescr().expand('battleRoyaleSeasons').items())
        seasonRewardPoints = self.__seasonRewardPoints
        for (seasonID, cycleID), cycleAchievements in achievements:
            if not self.validateSeasonData(seasonID, cycleID):
                continue
            _, _, awardPoints, _ = cycleAchievements
            seasonRewardPoints += awardPoints

        return seasonRewardPoints

    @property
    def maxRewardPoints(self):
        return self.__maxRewardPoints

    @property
    def rewardPointsTokenID(self):
        return self.__rewardPointsTokenID

    @property
    def rewardVehicles(self):
        return self.__rewardVehicles

    @property
    def questCardLevelTxtId(self):
        return self.__questCardLevelTxtId

    @property
    def flagIconId(self):
        return self.__flagIconId

    @property
    def questTooltipHeaderIconId(self):
        return self.__questTooltipHeaderIconId

    @property
    def questTooltipHeaderTxtId(self):
        return self.__questTooltipHeaderTxtId

    @property
    def selectorLabelTxtId(self):
        return self.__selectorLabelTxtId

    @property
    def selectorRibbonResId(self):
        return self.__selectorRibbonResId

    @property
    def aboutEventProgressionResId(self):
        if self.__currentMode:
            if self.__currentMode.getCurrentSeason(
            ) or self.__currentMode.getNextSeason():
                self.__allCyclesWasEndedResId = R.strings.event_progression.selectorTooltip.eventProgression.waitNext(
                )
            else:
                self.__allCyclesWasEndedResId = R.strings.event_progression.selectorTooltip.eventProgression.ended(
                )
        return self.__allCyclesWasEndedResId

    @property
    def selectorData(self):
        return self.__selectorData

    @property
    def selectorType(self):
        return self.__selectorType

    @property
    def selectorQueueType(self):
        return self.__selectorQueueType

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

    def getProgressionXPTokenID(self):
        return self.__currentController.PROGRESSION_XP_TOKEN if self.__currentController else ''

    def isAvailable(self):
        return self.isFrontLine or self.isSteelHunter

    def modeIsEnabled(self):
        return self.__currentController.getModeSettings(
        ).isEnabled if self.__currentController else False

    def modeIsAvailable(self):
        return self.isAvailable() and self.isInPrimeTime() and self.isActive()

    def isFrozen(self):
        if self.__currentController:
            for primeTime in self.getPrimeTimes().values():
                if primeTime.hasAnyPeriods():
                    return False

        return True

    def isActive(self):
        return self.modeIsEnabled(
        ) and self.__currentController.getCurrentSeason(
        ) is not None and self.__currentController.getCurrentCycleInfo(
        )[1] if self.__currentController else False

    def isDailyQuestsRefreshAvailable(self):
        dayTimeLeft = time_utils.getDayTimeLeft()
        cycleTimeLeft = self.getCurrentCycleTimeLeft()
        currentPrimeTimeEnd = self.getCurrentPrimeTimeEnd()
        if currentPrimeTimeEnd is None:
            return False
        else:
            primeTimeTimeLeft = currentPrimeTimeEnd - time_utils.getCurrentLocalServerTimestamp(
            )
            state1 = self.hasPrimeTimesLeft(
            ) or primeTimeTimeLeft > dayTimeLeft
            state2 = cycleTimeLeft > dayTimeLeft
            return state1 and state2

    def getPlayerLevelInfo(self):
        if not (self.isSteelHunter or self.isFrontLine):
            return PlayerLevelInfo(None, None)
        else:
            levelInfo = self.__currentController.getPlayerLevelInfo()
            if self.isFrontLine:
                levelInfo = self.__currentController.getPlayerLevelInfo()[1:]
            return PlayerLevelInfo(*levelInfo)

    def getActiveQuestIDs(self):
        self.__activeQuestIDs = [
            q.getID() for q in self.getActiveQuestsAsDict().values()
        ]
        return self.__activeQuestIDs

    def getActiveQuestsAsDict(self):
        if not (self.__isEnabled and self.modeIsEnabled()):
            return {}
        quests = {
            k: v
            for k, v in self.__eventsCache.getQuests().items()
            if self.__isActiveQuest(v)
        }
        return quests

    def getQuestForVehicle(self, vehicle, sortByPriority=False, questIDs=None):
        questIDs = questIDs if questIDs is not None else self.getActiveQuestIDs(
        )
        quests = [
            q for q in self.__questController.getQuestForVehicle(vehicle)
            if q.getID() in questIDs
        ]
        if sortByPriority:
            quests.sort(key=lambda _q: _q.getPriority(), reverse=True)
        return quests

    def isUnavailableQuestByID(self, questID):
        if questID not in self.__activeQuestIDs:
            return False
        if self.__isFrontLine:
            if not self.__isMaxLevel():
                return True
        return False

    def getUnavailableQuestMessage(self, questID):
        if questID not in self.__activeQuestIDs:
            return ''
        if self.__isFrontLine:
            if not self.__isMaxLevel():
                idMsg = R.strings.event_progression.questsTooltip.frontLine.notReachLevel(
                )
                return backport.text(
                    idMsg, level=self.__currentController.getMaxPlayerLevel())

    def getRewardVehiclePrice(self, vehicleCD):
        return {intCD: price
                for intCD, price in self.__rewardVehicles}.get(vehicleCD, 0)

    def getAllLevelAwards(self):
        awardsData = dict()
        abilityPts = None
        if self.isFrontLine:
            abilityPts = self.__lobbyContext.getServerSettings(
            ).epicMetaGame.metaLevel['abilityPointsForLevel']
        allQuests = self.__eventsCache.getAllQuests()
        for questKey, questData in allQuests.iteritems():
            if self.__currentController.TOKEN_QUEST_ID in questKey:
                _, _, questNum = questKey.partition(
                    self.__currentController.TOKEN_QUEST_ID)
                if questNum:
                    questLvl = int(questNum)
                    questBonuses = questData.getBonuses()
                    awardsData[questLvl] = self.__packBonuses(
                        questBonuses, questLvl, abilityPts)

        return awardsData

    def getLevelAwards(self, level):
        allAwards = self.getAllLevelAwards()
        return allAwards[level] if level in allAwards else []

    def showCustomScreen(self, screen):
        if self.__url and EventProgressionScreens.hasValue(screen):
            self.openURL('/'.join(
                (self.__url.strip('/'), screen.value.strip('/'))))

    def getPrimeTimeTitle(self):
        return self.__primeTimeTitle

    def getPrimeTimeBg(self):
        return self.__primeTimeBg

    def onPrimeTimeStatusUpdatedAddEvent(self, event):
        if self.__currentController:
            self.__currentController.onPrimeTimeStatusUpdated += event

    def onPrimeTimeStatusUpdatedRemoveEvent(self, event):
        if self.__currentController:
            self.__currentController.onPrimeTimeStatusUpdated -= event

    def getTimer(self):
        return self.__currentController.getTimer()

    def isInPrimeTime(self):
        _, _, isNow = self.getPrimeTimeStatus()
        return isNow

    @classmethod
    def validateSeasonData(cls, seasonID, cycleID):
        seasonValidationData = {
            season.getSeasonID():
            [cycle.ID for cycle in season.getAllCycles().values()]
            for season in cls.getMostRelevantSeasons().itervalues()
        }
        return seasonID in seasonValidationData and cycleID in seasonValidationData.get(
            seasonID, [])

    @classmethod
    def getMostRelevantSeasons(cls):
        epicMetaGameCtrl = dependency.instance(IEpicBattleMetaGameController)
        battleRoyaleCtrl = dependency.instance(IBattleRoyaleController)
        seasons = {
            key: value
            for key, value in zip(('frontline', 'battle_royale'), (first(
                filter(None, (mode.getCurrentSeason(), mode.getNextSeason(),
                              mode.getPreviousSeason()))) for mode in (
                                  epicMetaGameCtrl, battleRoyaleCtrl)))
        }
        return seasons

    @classmethod
    def getCalendarInfo(cls):
        calendarData = dict()
        for key, selectedSeason in cls.getMostRelevantSeasons().iteritems():
            if selectedSeason is not None:
                calendarData[key] = {}
                calendarData[key]['season'] = {
                    'id': selectedSeason.getSeasonID(),
                    'start': selectedSeason.getStartDate(),
                    'end': selectedSeason.getEndDate()
                }
                calendarData[key]['cycles'] = [{
                    'id':
                    cycle.ID,
                    'start':
                    cycle.startDate,
                    'end':
                    cycle.endDate,
                    'announce_only':
                    cycle.announceOnly
                } for cycle in selectedSeason.getAllCycles().values()]

        return calendarData

    def getCurrentSeason(self):
        return self.__currentController.getCurrentSeason(
        ) if self.__currentController else None

    def getNextSeason(self):
        return self.__currentController.getNextSeason(
        ) if self.__currentController else None

    def getPreviousSeason(self):
        return self.__currentController.getPreviousSeason(
        ) if self.__currentController else None

    def hasAnySeason(self):
        return self.__currentController.hasAnySeason(
        ) if self.__currentController else False

    def getCurrentOrNextActiveCycleNumber(self, season):
        return self.__currentController.getCurrentOrNextActiveCycleNumber(
            season) if self.__currentController else 0

    def getMaxPlayerLevel(self):
        return self.__currentController.getMaxPlayerLevel()

    def isNeedAchieveMaxLevelForDailyQuest(self):
        level, _ = self.getPlayerLevelInfo()
        return True if self.isFrontLine and level < self.getMaxPlayerLevel(
        ) else False

    def getCurrentCycleInfo(self):
        return self.__currentController.getCurrentCycleInfo(
        ) if self.__currentController else (None, False)

    def getPrimeTimeStatus(self, peripheryID=None):
        return self.__currentController.getPrimeTimeStatus(
            peripheryID) if self.__currentController else (
                PrimeTimeStatus.NOT_SET, 0, False)

    def hasAvailablePrimeTimeServers(self):
        if self.__connectionMgr.isStandalone():
            allPeripheryIDs = {self.__connectionMgr.peripheryID}
        else:
            allPeripheryIDs = set([
                host.peripheryID
                for host in g_preDefinedHosts.hostsWithRoaming()
            ])
        for peripheryID in allPeripheryIDs:
            primeTimeStatus, _, _ = self.getPrimeTimeStatus(peripheryID)
            if primeTimeStatus == PrimeTimeStatus.AVAILABLE:
                return True

        return False

    def getPrimeTimesForDay(self, selectedTime, groupIdentical=False):
        return self.__currentController.getPrimeTimesForDay(
            selectedTime, groupIdentical)

    def getPrimeTimes(self):
        return self.__currentController.getPrimeTimes(
        ) if self.__currentController else (0, 0, False)

    def getPerformanceGroup(self):
        return self.__currentController.getPerformanceGroup(
        ) if self.__currentController else None

    def getCurrentCycleTimeLeft(self):
        currentCycleEndTime, isCycleActive = self.getCurrentCycleInfo()
        cycleTimeLeft = currentCycleEndTime - time_utils.getCurrentLocalServerTimestamp(
        ) if isCycleActive else None
        return cycleTimeLeft

    def getCurrentPrimeTimeEnd(self):
        primeTimes = self.getPrimeTimes()
        currentPrimeTimeEnd = None
        for primeTime in primeTimes.values():
            periods = primeTime.getPeriodsActiveForTime(
                time_utils.getCurrentLocalServerTimestamp())
            for period in periods:
                _, endTime = period
                currentPrimeTimeEnd = max(endTime, currentPrimeTimeEnd)

        return currentPrimeTimeEnd

    def hasPrimeTimesLeft(self):
        currentCycleEndTime, isCycleActive = self.getCurrentCycleInfo()
        if not isCycleActive:
            return False
        primeTimes = self.getPrimeTimes()
        return any([
            primeTime.getNextPeriodStart(
                time_utils.getCurrentLocalServerTimestamp(),
                currentCycleEndTime) for primeTime in primeTimes.values()
        ])

    def getStats(self):
        return self.__currentController.getStats()

    def getPointsProgressForLevel(self, level):
        return self.__currentController.getPointsProgressForLevel(level)

    def getEpicMetascreenData(self):
        if self.isFrontLine:
            metaGameStats = self.getStats()
            data = {
                'average_xp':
                metaGameStats.averageXP,
                'is_reserves_available_in_fl_menu':
                self.__currentController.isReservesAvailableInFLMenu()
            }
            return data
        elif self.isSteelHunter:
            metaGameStats = self.getStats()
            data = {'kill_count': metaGameStats.killCount}
            return data
        else:
            return None

    def onLobbyInited(self, ctx):
        self.__lobbyContext.getServerSettings(
        ).onServerSettingsChange += self.__onServerSettingsChange
        self.__itemsCache.onSyncCompleted += self.__updatePlayerData
        self.__updateSettings()
        self.__updatePlayerData()

    def isCurrentSeasonInPrimeTime(self):
        season = self.getCurrentSeason()
        if season is None or season.getCycleInfo() is None:
            return False
        else:
            isInPrime = not self.isInPrimeTime()
            isEnable = self.modeIsEnabled()
            isAvailable = self.modeIsAvailable()
            return isInPrime and isEnable and not isAvailable and season.getCycleInfo(
            )

    def getHeaderTooltipPack(self):
        items = [self.__getTopBackgroundTooltipWithTextData()]
        bottom = -30
        if not self.isCurrentSeasonInPrimeTime() and not self.modeIsAvailable(
        ):
            items.append(self.__getRewardVehiclesData())
            bottom = 0
        return formatters.packBuildUpBlockData(
            items, padding=formatters.packPadding(bottom=bottom))

    def getSeasonInfoTooltipPack(self):
        season = self.getCurrentSeason() or self.getNextSeason()
        currentTime = time_utils.getCurrentLocalServerTimestamp()
        isPrimeTime = self.isCurrentSeasonInPrimeTime()
        if isPrimeTime or self.modeIsAvailable():
            cycle = season.getCycleInfo()
            getDate = lambda c: c.endDate
            getTimeToStr = getTimeToLeftStr
        else:
            cycle = season.getNextByTimeCycle(currentTime) if season else None
            getDate = lambda c: c.startDate
            getTimeToStr = getTimeToStartStr
        if cycle is not None:
            cycleNumber = self.getCurrentOrNextActiveCycleNumber(season)
            if self.modeIsEnabled():
                title = backport.text(self.__progressionNameCycleTxtId,
                                      season=int2roman(cycleNumber))
            else:
                title = backport.text(self.__selectorLabelTxtId)
            description = getTimeToStr(
                getDate(cycle) -
                currentTime) if self.modeIsEnabled() else backport.text(
                    R.strings.tooltips.eventProgression.disabled())
        else:
            title = ''
            description = ''
            if self.isFrontLine:
                title = backport.text(
                    R.strings.tooltips.eventProgression.allSeasonsAreOver())
                description = backport.text(
                    R.strings.tooltips.eventProgression.allSeasonsAreOver())
            elif self.isSteelHunter:
                title = backport.text(self.__allCyclesWasEndedResId)
        return formatters.packBuildUpBlockData([
            formatters.packImageTextBlockData(
                title=text_styles.middleTitle(title),
                txtPadding=formatters.packPadding(top=8, left=94),
                desc=text_styles.main(description),
                descPadding=formatters.packPadding(top=8, left=94),
                txtOffset=1,
                txtGap=-1,
                img=backport.image(self.__progressionIconId),
                imgPadding=formatters.packPadding(top=1, left=18))
        ])

    def getCurrentModeAlias(self):
        return self.__currentController.MODE_ALIAS

    def getCycleStatusTooltipPack(self):
        items = []
        season = self.__currentController.getCurrentSeason(
        ) or self.__currentController.getNextSeason()
        levelInfo = self.getPlayerLevelInfo()
        cycleNumber = self.getCurrentOrNextActiveCycleNumber(season)
        seasonStr = backport.text(self.__progressionNameCycleTxtId,
                                  season=int2roman(cycleNumber))
        seasonDescr = text_styles.middleTitle(seasonStr)
        items.append(
            formatters.packTextBlockData(text=seasonDescr,
                                         useHtml=True,
                                         padding=formatters.packPadding(
                                             left=20, right=20)))
        currentCycle = season.getCycleInfo()
        tDiff = currentCycle.endDate - time_utils.getCurrentLocalServerTimestamp(
        ) if currentCycle is not None else 0
        timeLeft = text_styles.main(getTimeToLeftStr(tDiff))
        items.append(
            formatters.packTextBlockData(text=timeLeft,
                                         useHtml=True,
                                         padding=formatters.packPadding(
                                             left=20, right=20)))
        linkage = BLOCKS_TOOLTIP_TYPES.TOOLTIP_BATTLE_ROYALE_META_LEVEL_BLOCK_LINKAGE if self.isSteelHunter else BLOCKS_TOOLTIP_TYPES.TOOLTIP_EPIC_BATTLE_META_LEVEL_BLOCK_LINKAGE
        items.append(
            formatters.packBuildUpBlockData(
                blocks=[
                    formatters.packBlockDataItem(
                        linkage=linkage,
                        data=getProgressionIconVODict(
                            cycleNumber=cycleNumber,
                            playerLevel=levelInfo.currentLevel))
                ],
                layout=BLOCKS_TOOLTIP_TYPES.LAYOUT_HORIZONTAL,
                align=BLOCKS_TOOLTIP_TYPES.ALIGN_CENTER))
        items.append(getLevelData(levelInfo.currentLevel))
        if levelInfo.currentLevel < self.getMaxPlayerLevel():
            items.append(
                self.__getCurrentMaxProgress(
                    playerLevel=levelInfo.currentLevel,
                    playerFamePts=levelInfo.levelProgress))
            items.append(
                self.__getPlayerProgressToLevelBlock(
                    playerLevel=levelInfo.currentLevel,
                    playerFamePts=levelInfo.levelProgress))
        else:
            unlockedStr = backport.text(
                R.strings.tooltips.eventProgression.unlockedDailyMissions())
            items.append(
                formatters.packTextBlockData(
                    text=text_styles.main(unlockedStr),
                    useHtml=True,
                    padding=formatters.packPadding(left=20, right=20, top=-7)))
        return formatters.packBuildUpBlockData(items)

    def getExchangeInfo(self):
        exchange = self.__lobbyContext.getServerSettings(
        ).eventProgression.exchange
        return {
            'expireTimestamp': exchange['expireTimestamp'],
            'exchangeRate': exchange['exchangeRate'],
            'loginDeadlineTimestamp': exchange['loginDeadlineTimestamp']
        }

    @property
    def __currentController(self):
        if self.__currentMode is None:
            self.__updateSettings()
        return self.__currentMode

    def __isMaxLevel(self):
        modeCtrl = self.__currentController
        levelInfo = self.getPlayerLevelInfo()
        maxLevel = modeCtrl.getMaxPlayerLevel()
        return levelInfo.currentLevel >= maxLevel

    def __isActiveQuest(self, q):
        if self.__questPrefix not in q.getID():
            return False
        if self.__isFrontLine:
            return True
        if self.__isSteelHunter:
            validationResult = q.isAvailable()
            isReqsAvailable = q.accountReqs.isAvailable()
            return (validationResult.isValid or validationResult.reason
                    == 'dailyComplete') and isReqsAvailable
        return False

    def __packBonuses(self, bonuses, level, abilityPts):
        result = [{
            'id': 0,
            'type': ItemPackType.CUSTOM_SUPPLY_POINT,
            'value': abilityPts[level - 1],
            'icon': {
                AWARDS_SIZES.SMALL:
                getRelativeUrl(
                    backport.image(R.images.gui.maps.icons.epicBattles.awards.
                                   c_48x48.abilityToken())),
                AWARDS_SIZES.BIG:
                getRelativeUrl(
                    backport.image(R.images.gui.maps.icons.epicBattles.awards.
                                   c_80x80.abilityToken()))
            }
        }] if abilityPts else []
        for bonus in bonuses:
            bonusList = bonus.getWrappedEpicBonusList()
            for bonusEntry in bonusList:
                bonusEntry['icon'] = {
                    size: getRelativeUrl(path)
                    for size, path in bonusEntry['icon'].iteritems()
                }

            result.extend(bonusList)

        return result

    def __getPlayerProgressToLevelBlock(self, playerLevel, playerFamePts):
        famePtsToProgress = self.getPointsProgressForLevel(playerLevel)
        data = EpicBattlesWidgetTooltipVO(progressBarData={
            'value': playerFamePts,
            'maxValue': famePtsToProgress
        })._asdict()
        res = formatters.packBlockDataItem(
            linkage=BLOCKS_TOOLTIP_TYPES.
            TOOLTIP_META_LEVEL_PROGRESS_BLOCK_LINKAGE,
            data=data,
            padding=formatters.packPadding(left=20))
        return res

    def __getCurrentMaxProgress(self, playerLevel, playerFamePts):
        items = []
        famePtsToProgress = self.getPointsProgressForLevel(playerLevel)
        currentPoint = text_styles.stats(str(playerFamePts))
        fameTo = text_styles.main(str(famePtsToProgress))
        currentPointMaxPoint = text_styles.concatStylesWithSpace(
            currentPoint, text_styles.main('/'), fameTo)
        text = text_styles.main(currentPointMaxPoint)
        marginTop = 7
        icon = None
        if self.isFrontLine:
            marginTop = 0
            iconSrc = backport.image(
                R.images.gui.maps.icons.epicBattles.fame_point_tiny())
            icon = icons.makeImageTag(source=iconSrc, width=24, height=24)
        elif self.isSteelHunter:
            marginTop = 6
            iconSrc = backport.image(
                R.images.gui.maps.icons.battleRoyale.progression_point())
            icon = icons.makeImageTag(source=iconSrc, width=16, height=16)
        if icon is not None:
            text = text_styles.concatStylesWithSpace(text, icon)
        items.append(
            formatters.packAlignedTextBlockData(
                text=text,
                align=BLOCKS_TOOLTIP_TYPES.ALIGN_RIGHT,
                padding=formatters.packPadding(left=20, right=20, top=-35)))
        return formatters.packBuildUpBlockData(
            items, padding=formatters.packPadding(top=marginTop))

    def __getRewardVehiclesData(self):
        getRewards = self.__getRewardVehicles
        rewardVehiclesNames = [
            text_styles.stats(v.shortUserName) for v in getRewards()
        ]
        promo = R.strings.event_progression.selectorTooltip.eventProgression.promo
        text = backport.text(
            promo.multi() if len(rewardVehiclesNames) > 1 else promo.single(),
            vehicles=', '.join(rewardVehiclesNames[:-1]),
            vehicle=rewardVehiclesNames[-1])
        return formatters.packTextBlockData(text_styles.main(text),
                                            padding=formatters.packPadding(
                                                top=-10, left=20, right=25))

    def __getTopBackgroundTooltipWithTextData(self):
        header = text_styles.bonusLocalText(
            backport.text(R.strings.tooltips.eventProgression.header()))
        iconSrc = backport.image(
            R.images.gui.maps.icons.epicBattles.rewardPoints.c_16x16())
        iconCurrency = icons.makeImageTag(source=iconSrc,
                                          width=16,
                                          height=16,
                                          vSpace=-3)
        currency = text_styles.concatStylesWithSpace(
            self.__getCurrencyCurrentStr(), iconCurrency)
        background = R.images.gui.maps.icons.epicBattles.backgrounds.widget_tooltip_background(
        )
        return formatters.packImageTextBlockData(
            title=header,
            txtPadding=formatters.packPadding(top=16, left=20),
            desc=currency,
            descPadding=formatters.packPadding(top=6, left=20),
            txtOffset=1,
            txtGap=-1,
            img=backport.image(background))

    def __getCurrencyCurrentStr(self):
        res = text_styles.main(
            backport.text(R.strings.tooltips.eventProgression.currency()) +
            ' ') + text_styles.stats(int(self.actualRewardPoints))
        return res

    def __getRewardVehicles(self):
        rewardVehiclesIds = [intCD for intCD, _ in self.rewardVehicles]
        rewardVehicles = self.__itemsCache.items.getVehicles(
            REQ_CRITERIA.IN_CD_LIST(rewardVehiclesIds))
        return [rewardVehicles[intCD] for intCD in rewardVehiclesIds]

    def __clear(self):
        self.__itemsCache.onSyncCompleted -= self.__updatePlayerData
        self.__lobbyContext.getServerSettings(
        ).onServerSettingsChange -= self.__onServerSettingsChange

    def __onServerSettingsChange(self, diff):
        if Configs.EVENT_PROGRESSION_CONFIG.value in diff:
            self.__updateSettings()
            self.onUpdated(diff)

    def __onSyncCompleted(self, *args, **kwargs):
        self.__updatePlayerData()
        self.onUpdated(args, kwargs)

    def __updateSettings(self):
        s = self.__lobbyContext.getServerSettings().eventProgression
        self.__isEnabled = s.isEnabled
        self.__isFrontLine = s.isFrontLine
        self.__isSteelHunter = s.isSteelHunter
        self.__url = s.url
        self.__maxRewardPoints = s.maxRewardPoints
        self.__rewardPointsTokenID = s.rewardPointsTokenID
        self.__seasonPointsTokenID = s.seasonPointsTokenID
        self.__rewardVehicles = s.rewardVehicles
        self.__questPrefix = s.questPrefix
        if self.isSteelHunter is True:
            self.__updateSteelHunterData()
        elif self.isFrontLine is True:
            self.__updateFrontLineData()
        else:
            self.__currentMode = None
        return

    def __updateFrontLineData(self):
        self.__currentMode = dependency.instance(IEpicBattleMetaGameController)
        self.__flagIconId = R.images.gui.maps.icons.library.hangarFlag.flag_epic(
        )
        self.__questTooltipHeaderIconId = R.images.gui.maps.icons.quests.epic_quests_infotip(
        )
        self.__questTooltipHeaderTxtId = R.strings.epic_battle.questsTooltip.epicBattle.header(
        )
        self.__questCardLevelTxtId = R.strings.event_progression.questsCard.frontLine.getLevel(
        )
        self.__progressionNameCycleTxtId = R.strings.tooltips.eventProgression.season(
        )
        self.__progressionIconId = R.images.gui.maps.icons.battleTypes.c_64x64.frontline(
        )
        self.__selectorLabelTxtId = R.strings.menu.headerButtons.battle.types.epic(
        )
        self.__selectorRibbonResId = R.images.gui.maps.icons.epicBattles.ribbon_small(
        )
        self.__selectorData = PREBATTLE_ACTION_NAME.EPIC
        self.__selectorType = SELECTOR_BATTLE_TYPES.EVENT_PROGRESSION
        self.__selectorQueueType = QUEUE_TYPE.EPIC
        self.__primeTimeTitle = R.strings.epic_battle.primeTime.title()
        self.__primeTimeBg = R.images.gui.maps.icons.epicBattles.primeTime.prime_time_back_default(
        )
        self.__allCyclesWasEndedResId = R.strings.event_progression.selectorTooltip.eventProgression.waitNext(
        )

    def __updateSteelHunterData(self):
        self.__currentMode = dependency.instance(IBattleRoyaleController)
        self.__flagIconId = R.images.gui.maps.icons.library.hangarFlag.flag_epic_steelhunter(
        )
        self.__questTooltipHeaderIconId = R.images.gui.maps.icons.quests.epic_steelhunter_quests_infotip(
        )
        self.__questTooltipHeaderTxtId = R.strings.epic_battle.questsTooltip.epicBattle.steelhunter.header(
        )
        self.__questCardLevelTxtId = R.strings.event_progression.questsCard.steelHunter.getLevel(
        )
        self.__progressionNameCycleTxtId = R.strings.tooltips.eventProgression.steelHunter.season(
        )
        self.__progressionIconId = R.images.gui.maps.icons.battleTypes.c_64x64.steelhunt(
        )
        self.__selectorLabelTxtId = R.strings.menu.headerButtons.battle.types.battleRoyale(
        )
        self.__selectorRibbonResId = R.images.gui.maps.icons.battleRoyale.ribbon_small(
        )
        self.__selectorData = PREBATTLE_ACTION_NAME.BATTLE_ROYALE
        self.__selectorType = SELECTOR_BATTLE_TYPES.BATTLE_ROYALE
        self.__selectorQueueType = QUEUE_TYPE.BATTLE_ROYALE
        self.__primeTimeTitle = R.strings.epic_battle.primeTime.steelhunter.title(
        )
        self.__primeTimeBg = R.images.gui.maps.icons.battleRoyale.primeTime.prime_time_back_default(
        )
        self.__allCyclesWasEndedResId = R.strings.event_progression.selectorTooltip.eventProgression.ended(
        )

    def __updatePlayerData(self, *_):
        t = self.__itemsCache.items.tokens.getTokens()
        self.__actualRewardPoints = t.get(self.__rewardPointsTokenID,
                                          (0, 0))[1]
        self.__seasonRewardPoints = t.get(self.__seasonPointsTokenID,
                                          (0, 0))[1]
class RssNewsFeed(RssNewsFeedMeta):
    UPDATE_INTERVAL = 60
    DESCRIPTION_MAX_LENGTH = 250
    DESCRIPTION_TAIL = '...'
    DESCRIPTION_CUT_LENGTH = DESCRIPTION_MAX_LENGTH - len(DESCRIPTION_TAIL)
    SHOW_NEWS_COUNT = 3
    externalBrowser = dependency.descriptor(IExternalLinksController)
    internalBrowser = dependency.descriptor(IBrowserController)

    def __init__(self):
        super(RssNewsFeed, self).__init__()
        self.__requestCbID = None
        self.__feed = []
        self.__urlMacros = URLMacros()
        return

    def getFeed(self):
        return self.__feed

    def openBrowser(self, linkToOpen):
        if linkToOpen:
            openBrowser = self.externalBrowser.open
            if GUI_SETTINGS.loginRssFeed.internalBrowser:
                browser = self.internalBrowser
                if browser is not None:
                    openBrowser = browser.load
                else:
                    _logger.error(
                        'Attempting to open internal browser, but browseris not exist. External browser will be opened: %r',
                        linkToOpen)
            _logger.debug('Open browser: %r', linkToOpen)
            openBrowser(linkToOpen)
        return

    def _populate(self):
        super(RssNewsFeed, self)._populate()
        self.__updateCallback()

    def _dispose(self):
        self.__urlMacros.clear()
        self.__urlMacros = None
        self.__feed = []
        self.__clearCallback()
        super(RssNewsFeed, self)._dispose()
        return

    @process
    def __requestFeed(self):
        yield lambda callback: callback(True)
        if GUI_SETTINGS.loginRssFeed.show:
            requestUrl = yield self.__getRssUrl()
            from helpers.RSSDownloader import g_downloader as g_rss
            if g_rss is not None:
                g_rss.download(self.__onRssFeedReceived, url=requestUrl)
            _logger.debug('Requesting login RSS news: %s', requestUrl)
        return

    def __onRssFeedReceived(self, data):
        if self.isDisposed():
            return
        else:
            self.__feed = []
            for entry in reversed(data.get('entries', [])):
                data = self.__prepareData(entry)
                if data is not None:
                    self.__feed.append(data)

            _logger.debug('RSS feed received, entries count %d',
                          len(self.__feed))
            self.as_updateFeedS(self.__feed[:self.SHOW_NEWS_COUNT])
            return

    def __clearCallback(self):
        if self.__requestCbID is not None:
            BigWorld.cancelCallback(self.__requestCbID)
            self.__requestCbID = None
        return

    def __updateCallback(self):
        self.__requestFeed()
        self.__clearCallback()
        self.__requestCbID = BigWorld.callback(self.UPDATE_INTERVAL,
                                               self.__updateCallback)

    @async
    @process
    def __getRssUrl(self, callback):
        url = yield self.__urlMacros.parse(str(GUI_SETTINGS.loginRssFeed.url))
        callback(url)

    def __prepareData(self, entryData):
        description = entryData.get('description')
        if description is not None:
            try:
                section = ResMgr.DataSection()
                section.createSectionFromString(encodeUtf8(description))
                _, section = findFirst(lambda (name, _): name == 'div',
                                       section.items())
                description, _ = unicode_from_utf8(section.asString)
                if len(description) > self.DESCRIPTION_MAX_LENGTH:
                    description = description[:self.
                                              DESCRIPTION_CUT_LENGTH] + self.DESCRIPTION_TAIL
            except Exception:
                _logger.exception('Invalid RSS entry description: %r, %r',
                                  entryData, description)
                return

        return {
            'id': entryData.get('id', str(uuid.uuid4())),
            'link': entryData.get('link'),
            'description': encodeUtf8(description)
        }
Beispiel #15
0
class VehiclePreviewBuyingPanel(VehiclePreviewBuyingPanelMeta):
    appLoader = dependency.descriptor(IAppLoader)
    _itemsCache = dependency.descriptor(IItemsCache)
    _goodiesCache = dependency.descriptor(IGoodiesCache)
    _comparisonBasket = dependency.descriptor(IVehicleComparisonBasket)
    _tradeIn = dependency.descriptor(ITradeInController)
    _restores = dependency.descriptor(IRestoreController)
    _heroTanks = dependency.descriptor(IHeroTankController)
    _lobbyContext = dependency.descriptor(ILobbyContext)
    _marathonsCtrl = dependency.descriptor(IMarathonEventsController)
    __calendarController = dependency.descriptor(ICalendarController)
    __linksCtrl = dependency.descriptor(IExternalLinksController)

    def __init__(self, skipConfirm=False):
        super(VehiclePreviewBuyingPanel, self).__init__()
        heroTankCD = self._heroTanks.getCurrentTankCD()
        self._vehicleCD = g_currentPreviewVehicle.item.intCD
        self._vehicleLevel = g_currentPreviewVehicle.item.level
        self._actionType = None
        self._skipConfirm = skipConfirm
        self._disableBuyButton = False
        self._marathonEvent = None
        self.__previewDP = DefaultVehPreviewDataProvider()
        self.__isHeroTank = heroTankCD and heroTankCD == self._vehicleCD
        self.__price = None
        self.__title = None
        self.__description = None
        self.__items = None
        self.__offers = None
        self.__currentOffer = None
        self.__styleByGroup = {}
        self.__vehicleByGroup = {}
        self.__endTime = None
        self.__oldPrice = MONEY_UNDEFINED
        self.__buyParams = None
        self.__backAlias = None
        self.__backCallback = None
        self.__timeCallbackID = None
        self.__timeLeftIcon = icons.makeImageTag(
            RES_ICONS.MAPS_ICONS_LIBRARY_TIME_ICON, 16, 16)
        self.__cachedVehiclesVOs = None
        self.__cachedItemsVOs = None
        self.__cachedCollapsedItemsVOs = None
        self.__couponInfo = None
        self.__hasSSEDiscount = False
        self.__urlMacros = URLMacros()
        g_techTreeDP.load()
        return

    def onBuyOrResearchClick(self):
        vehicle = g_currentPreviewVehicle.item
        shopPackage = self.__items is not None and self.__couponInfo is None
        frontlineCouponPackage = self.__couponInfo is not None and self.__couponInfo.selected
        if self._marathonEvent:
            self.__purchaseMarathonPackage()
        elif shopPackage or frontlineCouponPackage:
            self.__purchasePackage()
        elif self.__offers is not None:
            self.__purchaseOffer()
        elif self.__isHeroTank:
            self.__purchaseHeroTank()
        elif canBuyGoldForVehicleThroughWeb(vehicle):
            self.__purchaseSingleVehicle(vehicle)
        else:
            self.__research()
        return

    def onCouponSelected(self, isActive):
        if self.__couponInfo:
            self.__couponInfo.selected = isActive
            if isActive:
                self.__title = backport.text(
                    R.strings.vehicle_preview.buyingPanel.frontlinePack.
                    titleLabel.active())
            elif self.__hasSSEDiscount:
                self.__title = backport.text(
                    R.strings.vehicle_preview.buyingPanel.frontlinePack.
                    titleLabel.inactive_add_discount())
            else:
                self.__title = backport.text(
                    R.strings.vehicle_preview.buyingPanel.frontlinePack.
                    titleLabel.inactive())
            self.__update()

    def setMarathonEvent(self, prefix):
        self._marathonEvent = self._marathonsCtrl.getMarathon(prefix)

    def setTimerData(self, endTime):
        if self.__couponInfo is not None:
            return
        else:
            if endTime is not None:
                self.__endTime = endTime
                self.__onLeftTimeUpdated()
            self.__updateBtnState()
            return

    def setInfoTooltip(self):
        tooltip = self._marathonEvent.getVehiclePreviewTitleTooltip()
        self.as_setSetTitleTooltipS(tooltip)

    def setBuyParams(self, buyParams):
        self.__buyParams = buyParams

    def setBackAlias(self, backAlias):
        self.__backAlias = backAlias

    def setBackCallback(self, backCallback):
        self.__backCallback = backCallback

    def setIsHeroTank(self, isHero):
        self.__isHeroTank = isHero

    def setPackItems(self, packItems, price, oldPrice, title):
        self.__title = title if title is not None else ''
        self.__price = price
        self.__hasSSEDiscount = oldPrice != MONEY_UNDEFINED
        self.__oldPrice = oldPrice
        self.__items = packItems
        self.__styleByGroup.clear()
        self.__vehicleByGroup.clear()
        vehiclesItems, items = self.__previewDP.separateItemsPack(self.__items)
        for item in items:
            if item.type in ItemPackTypeGroup.STYLE and item.groupID not in self.__styleByGroup:
                self.__styleByGroup[item.groupID] = item.id
            if item.type in ItemPackTypeGroup.DISCOUNT:
                self.__title = backport.text(
                    R.strings.vehicle_preview.buyingPanel.frontlinePack.
                    titleLabel.active())
                self.__couponInfo = _CouponData(item=item, selected=True)
                self.as_setCouponS(
                    self.__previewDP.packCouponData(
                        self.__items, self.__price.get(Currency.GOLD)))
                if not self.__oldPrice:
                    self.__oldPrice = self.__price

        for vehicleItem in vehiclesItems:
            self.__vehicleByGroup[vehicleItem.id] = vehicleItem.groupID

        vehiclesVOs, itemsVOs, collapseItemsVOs = self.__previewDP.getItemsPackData(
            g_currentPreviewVehicle.item, items, vehiclesItems)
        self.__cachedVehiclesVOs = vehiclesVOs
        self.__cachedItemsVOs = itemsVOs
        self.__cachedCollapsedItemsVOs = collapseItemsVOs
        self.__update()
        return

    def onCarouselVehicleSelected(self, intCD):
        self._vehicleCD = intCD
        g_currentPreviewVehicle.selectVehicle(intCD)

    def setOffers(self, offers, title, description):
        self.__offers = offers
        self.__title = title
        self.__description = description
        selectedID = getActiveOffer(self.__offers).id
        offersData = self.__previewDP.getOffersData(
            self.__offers, selectedID) if len(self.__offers) > 1 else []
        self.as_setOffersDataS(offersData)
        self.onOfferSelected(selectedID)

    def onOfferSelected(self, offerID):
        self.__currentOffer = findFirst(lambda o: o.id == offerID,
                                        self.__offers)
        if self.__currentOffer:
            vehicle = g_currentPreviewVehicle.item
            crew = self.__currentOffer.crew
            g_eventBus.handleEvent(
                HasCtxEvent(ctx={
                    'vehicleItems':
                    [ItemPackEntry(id=vehicle.intCD, groupID=crew.groupID)],
                    'crewItems': [crew],
                    'offer':
                    self.__currentOffer
                },
                            eventType=OFFER_CHANGED_EVENT))
            self.__buyParams = self.__currentOffer.buyParams
            self.__price = self.__currentOffer.buyPrice
            self.as_setBuyDataS(
                self.__previewDP.getOffersBuyingPanelData(self.__getBtnData()))
            description = self.__description or self.__getCurrentOfferDescription(
            ) or {}
            self.as_setSetTitleTooltipS(makeTooltip(**description))

    def showTooltip(self, intCD, itemType):
        toolTipMgr = self.appLoader.getApp().getToolTipMgr()
        if itemType == BOX_TYPE:
            toolTipMgr.onCreateComplexTooltip(
                makeTooltip(TOOLTIPS.VEHICLEPREVIEW_BOXTOOLTIP_HEADER,
                            TOOLTIPS.VEHICLEPREVIEW_BOXTOOLTIP_BODY), 'INFO')
            return
        try:
            try:
                itemId = int(intCD)
            except ValueError:
                itemId = intCD

            rawItem = [
                item for item in self.__items
                if item.id == itemId and item.type == itemType
            ][0]
            item = lookupItem(rawItem, self._itemsCache, self._goodiesCache)
            showItemTooltip(toolTipMgr, rawItem, item)
        except IndexError:
            return

    def updateData(self, useCompactData):
        self.__update(collapseItems=False)

    def _populate(self):
        super(VehiclePreviewBuyingPanel, self)._populate()
        g_clientUpdateManager.addMoneyCallback(self.__updateBtnState)
        g_clientUpdateManager.addCallbacks({
            'stats.freeXP':
            self.__updateBtnState,
            'inventory':
            self.__updateBtnState,
            'serverSettings.blueprints_config':
            self.__onBlueprintsModeChanged
        })
        g_currentPreviewVehicle.onVehicleUnlocked += self.__updateBtnState
        g_currentPreviewVehicle.onChanged += self.__onVehicleChanged
        self._heroTanks.onUpdated += self.__updateBtnState
        self._restores.onRestoreChangeNotify += self.__onRestoreChanged
        self._lobbyContext.getServerSettings(
        ).onServerSettingsChange += self.__onServerSettingsChanged
        self.addListener(CameraRelatedEvents.VEHICLE_LOADING,
                         self.__onVehicleLoading)

    def _dispose(self):
        g_clientUpdateManager.removeObjectCallbacks(self)
        g_currentPreviewVehicle.onVehicleUnlocked -= self.__updateBtnState
        g_currentPreviewVehicle.onChanged -= self.__onVehicleChanged
        self._heroTanks.onUpdated -= self.__updateBtnState
        self._restores.onRestoreChangeNotify -= self.__onRestoreChanged
        self._lobbyContext.getServerSettings(
        ).onServerSettingsChange -= self.__onServerSettingsChanged
        self.removeListener(CameraRelatedEvents.VEHICLE_LOADING,
                            self.__onVehicleLoading)
        self.__stopTimer()
        self.__styleByGroup.clear()
        self.__vehicleByGroup.clear()
        self.__urlMacros.clear()
        self.__urlMacros = None
        super(VehiclePreviewBuyingPanel, self)._dispose()
        return

    def __update(self, collapseItems=False):
        if self.__cachedVehiclesVOs:
            g_currentPreviewVehicle.selectVehicle(
                self.__cachedVehiclesVOs[0]['intCD'])
            self.as_setSetVehiclesDataS({'vehicles': self.__cachedVehiclesVOs})
        if self.__couponInfo:
            self.__updateEnabledState(self.__cachedCollapsedItemsVOs,
                                      self.__couponInfo.selected)
            self.__updateEnabledState(self.__cachedItemsVOs,
                                      self.__couponInfo.selected)
        if collapseItems and self.__cachedCollapsedItemsVOs:
            self.as_setSetItemsDataS({'items': self.__cachedCollapsedItemsVOs})
        elif self.__cachedItemsVOs:
            self.as_setSetItemsDataS({'items': self.__cachedItemsVOs})
        self.__updateBtnState()

    def __getOfferByID(self, offerID):
        return findFirst(lambda o: o.buy_params['transactionID'] == offerID,
                         self.__offers)

    def __isReferralWindow(self):
        return self.__backAlias == VIEW_ALIAS.REFERRAL_PROGRAM_WINDOW

    def __getConfirmationDialogKey(self):
        key = 'buyConfirmation'
        if self.__isReferralWindow():
            key = 'referralReward'
        return key

    def __buyRequestConfirmation(self, key='buyConfirmation'):
        product = self.__title if self.__couponInfo is None else g_currentPreviewVehicle.item.shortUserName
        return DialogsInterface.showDialog(meta=I18nConfirmDialogMeta(
            key=key,
            messageCtx={
                'product':
                product,
                'price':
                formatPrice(self.__getPackPrice(), reverse=True, useIcon=True)
            },
            focusedID=DIALOG_BUTTON_ID.SUBMIT))

    def __onVehicleLoading(self, ctxEvent):
        vehicle = g_currentPreviewVehicle.item
        if vehicle is None:
            return
        else:
            groupID = self.__vehicleByGroup.get(vehicle.intCD)
            if not ctxEvent.ctx.get(
                    'started') and groupID in self.__styleByGroup:
                customizationStyle = self.__styleByGroup[groupID]
                style = self._itemsCache.items.getItemByCD(customizationStyle)
                if style is not None and not style.isRentable:
                    g_currentPreviewVehicle.previewStyle(style)
            return

    @process
    def __updateBtnState(self, *_):
        item = g_currentPreviewVehicle.item
        if item is None:
            return
        else:
            btnData = self.__getBtnData()
            self._actionType = self.__previewDP.getBuyType(item)
            if self.__items:
                buyingPanelData = self.__previewDP.getItemPackBuyingPanelData(
                    btnData, self.__items,
                    self.__couponInfo.selected if self.__couponInfo else False,
                    self.__price.get(Currency.GOLD))
            elif self.__offers:
                buyingPanelData = self.__previewDP.getOffersBuyingPanelData(
                    btnData)
            else:
                buyingPanelData = self.__previewDP.getBuyingPanelData(
                    item, btnData, self.__isHeroTank)
            buyingPanelData.update(
                {'isReferralEnabled': self.__isReferralWindow()})
            hasExternalLink = yield self.__hasExternalLink()
            if hasExternalLink:
                btnIcon = backport.image(
                    R.images.gui.maps.icons.library.buyInWeb())
                buyingPanelData.update({
                    'buyButtonIcon': btnIcon,
                    'buyButtonIconAlign': 'right'
                })
            self.as_setBuyDataS(buyingPanelData)
            return

    def __onVehicleChanged(self, *_):
        if g_currentPreviewVehicle.isPresent():
            self._vehicleCD = g_currentPreviewVehicle.item.intCD
            if not self.__price:
                self.__updateBtnState()

    def __onRestoreChanged(self, vehicles):
        if g_currentPreviewVehicle.isPresent():
            if self._vehicleCD in vehicles:
                self.__updateBtnState()

    def __onServerSettingsChanged(self, diff):
        if self._lobbyContext.getServerSettings().isShopDataChangedInDiff(
                diff,
                'isEnabled') or CollectorVehicleConsts.CONFIG_NAME in diff:
            self.__updateBtnState()

    def __onBlueprintsModeChanged(self, _):
        self.__updateBtnState()

    def __getBtnData(self):
        if self.__price is not None:
            return self.__getBtnDataPack()
        else:
            vehicle = g_currentPreviewVehicle.item
            if vehicle.isCollectible:
                return self.__getBtnDataCollectibleVehicle(vehicle)
            return self.__getBtnDataUnlockedVehicle(
                vehicle
            ) if vehicle.isUnlocked else self.__getBtnDataLockedVehicle(
                vehicle)

    def __getBtnDataPack(self):
        buyButtonTooltip = ''
        actionTooltip = None
        customOffer = None
        price = self.__getPackPrice()
        currency = price.getCurrency()
        walletAvailable = self.__walletAvailableForCurrency(currency)
        enabled = False
        if not walletAvailable:
            buyButtonTooltip = _buildBuyButtonTooltip('walletUnavailable')
        elif self._disableBuyButton:
            buyButtonTooltip = _buildBuyButtonTooltip('endTime')
        elif self.__price.isSet(currency):
            enabled = currency == Currency.GOLD or mayObtainForMoney(
                price) or mayObtainWithMoneyExchange(price)
        else:
            enabled = True
        if self.__currentOffer and self.__currentOffer.bestOffer and self.__currentOffer.eventType:
            actionTooltip = self.__getBestOfferTooltipData(
                self.__currentOffer.eventType)
        buttonIcon = None
        buttonIconAlign = None
        itemPrices = ItemPrice(price=price, defPrice=self.__oldPrice)
        specialData = getHeroTankPreviewParams() if self.__isHeroTank else None
        if specialData is not None and specialData.buyButtonLabel:
            buttonLabel = backport.text(specialData.buyButtonLabel)
        elif self.__isReferralWindow():
            buttonLabel = backport.text(
                R.strings.vehicle_preview.buyingPanel.buyBtn.label.obtain())
        elif self._marathonEvent is not None:
            itemPrices = ITEM_PRICE_EMPTY
            buttonData = self._marathonEvent.getPreviewBuyBtnData()
            buttonLabel = buttonData['label']
            enabled = buttonData['enabled']
            buttonIcon = buttonData['btnIcon']
            buttonIconAlign = buttonData['btnIconAlign']
            buyButtonTooltip = buttonData['btnTooltip']
            customOffer = buttonData['customOffer']
        elif self.__items and self.__couponInfo is None:
            buttonLabel = backport.text(R.strings.vehicle_preview.buyingPanel.
                                        buyBtn.label.buyItemPack())
        elif self.__offers and self.__currentOffer:
            buttonLabel = backport.text(
                R.strings.vehicle_preview.buyingPanel.buyBtn.label.rent())
            self.__title = self.__getCurrentOfferTitle()
        else:
            buttonLabel = backport.text(
                R.strings.vehicle_preview.buyingPanel.buyBtn.label.buy())
        isAction = self.__oldPrice.isDefined(
        ) and self.__oldPrice != self.__price or actionTooltip is not None or self.__couponInfo and self.__couponInfo.selected
        return _ButtonState(enabled=enabled,
                            itemPrice=getItemPricesVO(itemPrices),
                            label=buttonLabel,
                            icon=buttonIcon,
                            iconAlign=buttonIconAlign,
                            isAction=isAction,
                            actionTooltip=actionTooltip,
                            tooltip=buyButtonTooltip,
                            title=self.__title,
                            isMoneyEnough=True,
                            isUnlock=False,
                            isPrevItemsUnlock=True,
                            customOffer=customOffer,
                            isShowSpecial=False)

    def __getPackPrice(self):
        if self.__couponInfo and self.__couponInfo.selected:
            discount = self.__couponInfo.discount
            currency = self.__price.getCurrency()
            if currency == Currency.GOLD:
                discountPrice = self.__price - discount
                return discountPrice.toNonNegative()
        return self.__price

    def __getBtnDataUnlockedVehicle(self, vehicle):
        money = self._itemsCache.items.stats.money
        money = self._tradeIn.addTradeInPriceIfNeeded(vehicle, money)
        buyButtonTooltip = ''
        actionTooltip = getActionPriceData(vehicle)
        exchangeRate = self._itemsCache.items.shop.exchangeRate
        priceType, price = getPriceTypeAndValue(vehicle, money, exchangeRate)
        itemPrice = chooseItemPriceVO(priceType, price)
        currency = price.getCurrency(byWeight=True)
        walletAvailable = self.__walletAvailableForCurrency(currency)
        buttonLabel = self.__getUnlockedVehicleBtnLabel(priceType)
        buttonIcon = None
        buttonIconAlign = None
        isAction = False
        minRentPricePackage = vehicle.getRentPackage()
        if minRentPricePackage:
            isAction = minRentPricePackage['rentPrice'] != minRentPricePackage[
                'defaultRentPrice']
        elif not vehicle.isRestoreAvailable():
            isAction = vehicle.buyPrices.getSum().isActionPrice()
        mayObtain = self.__isHeroTank or walletAvailable and vehicle.mayObtainWithMoneyExchange(
            money, exchangeRate)
        isBuyingAvailable = not vehicle.isHidden or vehicle.isRentable or vehicle.isRestorePossible(
        )
        isMoneyEnough = True
        if not walletAvailable:
            buyButtonTooltip = _buildBuyButtonTooltip('walletUnavailable')
        elif not mayObtain and isBuyingAvailable:
            if currency == Currency.GOLD:
                mayObtain = True
            else:
                buyButtonTooltip = _buildBuyButtonTooltip('notEnoughCredits')
                isMoneyEnough = False
        if self._disableBuyButton or self.__isHeroTank and self._vehicleCD != self._heroTanks.getCurrentTankCD(
        ):
            mayObtain = False
            isMoneyEnough = False
        return _ButtonState(enabled=mayObtain,
                            itemPrice=itemPrice,
                            label=buttonLabel,
                            icon=buttonIcon,
                            iconAlign=buttonIconAlign,
                            isAction=isAction,
                            actionTooltip=actionTooltip,
                            tooltip=buyButtonTooltip,
                            title=self.__title,
                            isMoneyEnough=isMoneyEnough,
                            isUnlock=False,
                            isPrevItemsUnlock=True,
                            customOffer=None,
                            isShowSpecial=False)

    def __getBtnDataCollectibleVehicle(self, vehicle):
        isVehicleCollectorEnabled = self._lobbyContext.getServerSettings(
        ).isCollectorVehicleEnabled()
        isNationUnlocked = vehicle_collector_helper.isAvailableForPurchase(
            vehicle)
        resultVO = self.__getBtnDataUnlockedVehicle(vehicle)
        if isVehicleCollectorEnabled and isNationUnlocked:
            return resultVO
        if not isVehicleCollectorEnabled:
            tooltip = TOOLTIPS_CONSTANTS.VEHICLE_COLLECTOR_DISABLED
            isSpecialTooltip = True
        else:
            key = 'notUnlockedNation'
            tooltip = makeTooltip(
                header=TOOLTIPS.vehiclepreview_buybutton_all(key, 'header'),
                body=_getCollectibleWarningStr(
                    TOOLTIPS.vehiclepreview_buybutton_all(key, 'body'),
                    vehicle)) if resultVO.isMoneyEnough else resultVO.tooltip
            isSpecialTooltip = False
        resultVO = resultVO._replace(enabled=False,
                                     tooltip=tooltip,
                                     isShowSpecial=isSpecialTooltip)
        return resultVO

    def __getBtnDataLockedVehicle(self, vehicle):
        stats = self._itemsCache.items.stats
        tooltip = ''
        buttonIcon = None
        buttonIconAlign = None
        nodeCD = vehicle.intCD
        _, isXpEnough = g_techTreeDP.isVehicleAvailableToUnlock(
            nodeCD, self._vehicleLevel)
        unlocks = self._itemsCache.items.stats.unlocks
        isNext2Unlock, unlockProps = g_techTreeDP.isNext2Unlock(
            nodeCD,
            unlocked=set(unlocks),
            xps=stats.vehiclesXPs,
            freeXP=stats.freeXP,
            level=self._vehicleLevel)
        isAvailableToUnlock = isXpEnough and isNext2Unlock
        if not isAvailableToUnlock:
            if not isXpEnough:
                tooltip = _buildBuyButtonTooltip('notEnoughXp')
            elif any((bool(cd in unlocks)
                      for cd in g_techTreeDP.getTopLevel(nodeCD))):
                tooltip = _buildBuyButtonTooltip('parentModuleIsLocked')
            else:
                tooltip = _buildBuyButtonTooltip('parentVehicleIsLocked')
        specialData = getHeroTankPreviewParams() if self.__isHeroTank else None
        if specialData is not None and specialData.buyButtonLabel:
            buyLabel = backport.text(specialData.buyButtonLabel)
        else:
            buyLabel = backport.text(
                R.strings.vehicle_preview.buyingPanel.buyBtn.label.research())
        return _ButtonState(enabled=isAvailableToUnlock,
                            itemPrice=getItemUnlockPricesVO(unlockProps),
                            label=buyLabel,
                            icon=buttonIcon,
                            iconAlign=buttonIconAlign,
                            isAction=unlockProps.discount > 0,
                            actionTooltip=None,
                            tooltip=tooltip,
                            title=self.__title,
                            isMoneyEnough=isXpEnough,
                            isUnlock=True,
                            isPrevItemsUnlock=isNext2Unlock,
                            customOffer=None,
                            isShowSpecial=False)

    @staticmethod
    def __getBestOfferTooltipData(eventType=None):
        return VEHICLE_PREVIEW.BUYINGPANEL_OFFER_RENT_FRONTLINE_TOOLTIP_BEST_OFFER if eventType == 'frontline' else None

    def __getCurrentOfferTitle(self):
        if self.__offers and self.__currentOffer:
            if self.__currentOffer.eventType == 'frontline':
                firstRent = first(self.__currentOffer.rent)
                if len(self.__offers) > 1 or firstRent and firstRent.get(
                        'season') is not None:
                    return _ms(
                        backport.text(R.strings.vehicle_preview.buyingPanel.
                                      offer.rent.title.frontline.ordinal()))
                return _ms(
                    backport.text(R.strings.vehicle_preview.buyingPanel.offer.
                                  rent.title.frontline.single_cycle()),
                    cycles=self.__currentOffer.name)
        return self.__title

    def __getCurrentOfferDescription(self):
        return {
            'header':
            backport.text(R.strings.vehicle_preview.buyingPanel.offer.rent.
                          frontline.description.header()),
            'body':
            backport.text(R.strings.vehicle_preview.buyingPanel.offer.rent.
                          frontline.description.body.credits())
        } if self.__currentOffer and self.__currentOffer.eventType == 'frontline' else None

    def __startTimer(self, interval):
        self.__stopTimer()
        self.__timeCallbackID = BigWorld.callback(interval,
                                                  self.__onLeftTimeUpdated)

    def __stopTimer(self):
        if self.__timeCallbackID is not None:
            BigWorld.cancelCallback(self.__timeCallbackID)
            self.__timeCallbackID = None
        return

    def __setUsageLeftTime(self, leftTime):
        self.as_updateLeftTimeS(formattedTime='{} {}'.format(
            self.__timeLeftIcon,
            text_styles.tutorial(
                time_utils.getTillTimeString(leftTime,
                                             MENU.VEHICLEPREVIEW_TIMELEFT))),
                                hasHoursAndMinutes=True)

    def __setShortLeftTime(self, leftTime):
        self.as_updateLeftTimeS(formattedTime='{} {}'.format(
            self.__timeLeftIcon,
            text_styles.tutorial(
                time_utils.getTillTimeString(
                    leftTime, MENU.VEHICLEPREVIEW_TIMELEFTSHORT))),
                                hasHoursAndMinutes=True)

    def __setDateLeftTime(self):
        tm = time_utils.getTimeStructInLocal(self.__endTime)
        monthName = backport.text(
            R.strings.menu.dateTime.months.num(tm.tm_mon)())
        fmtValues = backport.text(R.strings.menu.dateTime.order(),
                                  day=tm.tm_mday,
                                  month=monthName,
                                  year=tm.tm_year)
        tooltip = makeTooltip(
            header=backport.text(R.strings.tooltips.vehiclePreview.shopPack.
                                 dateTimeTooltip.header()),
            body=backport.text(R.strings.tooltips.vehiclePreview.shopPack.
                               dateTimeTooltip.body(),
                               namePack=text_styles.neutral(self.__title),
                               date=fmtValues))
        self.as_setSetTitleTooltipS(tooltip)
        self.as_updateLeftTimeS(formattedTime='')

    def __timeOver(self):
        self.__endTime = None
        self._disableBuyButton = True
        formattedTime = '{} {}'.format(
            icons.makeImageTag(RES_ICONS.MAPS_ICONS_LIBRARY_ALERTICON2,
                               vSpace=-2),
            text_styles.alert(MENU.VEHICLEPREVIEW_ENDTIME))
        self.as_updateLeftTimeS(formattedTime=formattedTime)
        self.__updateBtnState()
        return

    def __onLeftTimeUpdated(self):
        leftTime = self.__endTime - time_utils.getServerUTCTime()
        self.__timeCallbackID = None
        if leftTime < 0:
            self.__timeOver()
        elif leftTime > time_utils.ONE_DAY:
            self.__setDateLeftTime()
            self.__startTimer(leftTime - time_utils.ONE_DAY)
        else:
            gmTime = time.gmtime(leftTime)
            if gmTime.tm_min == 0:
                self.__setShortLeftTime(leftTime)
            else:
                self.__setUsageLeftTime(leftTime)
            self.__startTimer(gmTime.tm_sec + 1)
        return

    @process
    def __purchasePackage(self):
        if self.__items is not None:
            product = self.__title if self.__couponInfo is None else g_currentPreviewVehicle.item.shortUserName
            price = self.__getPackPrice()
            if not mayObtainForMoney(price) and mayObtainWithMoneyExchange(
                    price):
                isOk, _ = yield DialogsInterface.showDialog(
                    ExchangeCreditsWebProductMeta(name=product,
                                                  count=1,
                                                  price=price.get(
                                                      Currency.CREDITS)))
                if isOk:
                    self.__purchasePackage()
                    return
                return
            requestConfirmed = yield self.__buyRequestConfirmation(
                self.__getConfirmationDialogKey())
            if requestConfirmed:
                if self.__isReferralWindow():
                    inventoryVehicle = self._itemsCache.items.getItemByCD(
                        g_currentPreviewVehicle.item.intCD)
                    showGetVehiclePage(inventoryVehicle, self.__buyParams)
                    return
                if mayObtainForMoney(price):
                    showBuyVehicleOverlay(self.__buyParams)
                elif price.get(Currency.GOLD,
                               0) > self._itemsCache.items.stats.gold:
                    showBuyGoldForBundle(price.get(Currency.GOLD, 0),
                                         self.__buyParams)
        return

    def __purchaseOffer(self):
        rent = self.__currentOffer.rent
        cycles = [r['cycle'] for r in rent if r.get('cycle')]
        seasons = [r['season'] for r in rent if r.get('season')]
        showVehicleRentDialog(
            g_currentPreviewVehicle.item.intCD,
            RentType.SEASON_CYCLE_RENT if cycles else RentType.SEASON_RENT,
            cycles if cycles else seasons, GameSeasonType.EPIC
            if self.__currentOffer.eventType == 'frontline' else None,
            self.__currentOffer.buyPrice, self.__currentOffer.buyParams)
        return

    def __purchaseSingleVehicle(self, vehicle):
        event_dispatcher.showVehicleBuyDialog(
            vehicle,
            returnAlias=self.__backAlias,
            returnCallback=self.__backCallback)

    @process
    def __purchaseHeroTank(self):
        if self._heroTanks.isAdventHero():
            self.__calendarController.showWindow(
                invokedFrom=CalendarInvokeOrigin.HANGAR)
            return
        shopUrl = self._heroTanks.getCurrentShopUrl()
        if shopUrl:
            event_dispatcher.showShop(shopUrl)
        else:
            url = yield self.__urlMacros.parse(
                self._heroTanks.getCurrentRelatedURL())
            self.fireEvent(
                events.OpenLinkEvent(events.OpenLinkEvent.SPECIFIED, url=url))

    @async
    @process
    def __hasExternalLink(self, callback=None):
        url = ''
        if self._marathonEvent:
            url = yield self._marathonEvent.getMarathonVehicleUrl()
        elif self.__isHeroTank:
            if not self._heroTanks.isAdventHero(
            ) and not self._heroTanks.getCurrentShopUrl():
                url = self._heroTanks.getCurrentRelatedURL()
        callback(self.__linksCtrl.externalAllowed(url) if url else False)

    @process
    def __purchaseMarathonPackage(self):
        if self._marathonEvent.hasIgbLink():
            url = yield self._marathonEvent.getMarathonVehicleUrlIgb()
            event_dispatcher.showShop(url)
        else:
            url = yield self._marathonEvent.getMarathonVehicleUrl()
            self.fireEvent(
                events.OpenLinkEvent(events.OpenLinkEvent.SPECIFIED, url=url))

    def __research(self):
        if self._actionType == factory.UNLOCK_ITEM:
            unlockProps = g_techTreeDP.getUnlockProps(self._vehicleCD,
                                                      self._vehicleLevel)
            factory.doAction(factory.UNLOCK_ITEM,
                             self._vehicleCD,
                             unlockProps,
                             skipConfirm=self._skipConfirm)
        else:
            factory.doAction(factory.BUY_VEHICLE,
                             self._vehicleCD,
                             False,
                             None,
                             VIEW_ALIAS.VEHICLE_PREVIEW,
                             self.__backAlias,
                             self.__backCallback,
                             skipConfirm=self._skipConfirm)
        return

    def __walletAvailableForCurrency(self, currency):
        return self._itemsCache.items.stats.currencyStatuses.get(
            currency) == WalletController.STATUS.AVAILABLE

    def __getUnlockedVehicleBtnLabel(self, priceType):
        specialData = getHeroTankPreviewParams() if self.__isHeroTank else None
        if specialData is not None and specialData.buyButtonLabel:
            buttonLabel = backport.text(specialData.buyButtonLabel)
        elif priceType == ActualPrice.RESTORE_PRICE:
            buttonLabel = backport.text(
                R.strings.vehicle_preview.buyingPanel.buyBtn.label.restore())
        elif priceType == ActualPrice.RENT_PRICE:
            buttonLabel = backport.text(
                R.strings.vehicle_preview.buyingPanel.buyBtn.label.rent())
        elif self.__isHeroTank and self._heroTanks.isAdventHero():
            buttonLabel = backport.text(R.strings.vehicle_preview.buyingPanel.
                                        buyBtn.label.showAdventCalendar())
        else:
            buttonLabel = backport.text(
                R.strings.vehicle_preview.buyingPanel.buyBtn.label.buy())
        return buttonLabel

    @staticmethod
    def __updateEnabledState(collection, enabled):
        if collection is None:
            return
        else:
            for item in collection:
                if 'isEnabled' in item:
                    item['isEnabled'] = enabled

            return
Beispiel #16
0
class InternalLinksHandler(IInternalLinksController):
    browserCtrl = dependency.descriptor(IBrowserController)

    def __init__(self):
        super(InternalLinksHandler, self).__init__()
        self.__urlMacros = None
        self._browserID = None
        return

    def init(self):
        self.__urlMacros = URLMacros()
        addListener = g_eventBus.addListener
        for eventType, handlerName in _LISTENERS.iteritems():
            handler = getattr(self, handlerName, None)
            if not handler:
                LOG_ERROR('Handler is not found', eventType, handlerName)
                continue
            if not callable(handler):
                LOG_ERROR('Handler is invalid', eventType, handlerName,
                          handler)
                continue
            addListener(eventType, handler)

        return

    def fini(self):
        if self.__urlMacros is not None:
            self.__urlMacros.clear()
            self.__urlMacros = None
        self._browserID = None
        removeListener = g_eventBus.removeListener
        for eventType, handlerName in _LISTENERS.iteritems():
            handler = getattr(self, handlerName, None)
            if handler:
                removeListener(eventType, handler)

        super(InternalLinksHandler, self).fini()
        return

    @async
    @process
    def getURL(self, name, callback):
        urlSettings = GUI_SETTINGS.lookup(name)
        if urlSettings:
            url = yield self.__urlMacros.parse(str(urlSettings))
        else:
            url = yield lambda callback: callback('')
        callback(url)

    @process
    def __openInternalBrowse(self,
                             urlName,
                             title='',
                             browserSize=None,
                             showActionBtn=True,
                             showCloseBtn=False):
        parsedUrl = yield self.getURL(urlName)
        if parsedUrl:
            self._browserID = yield self.browserCtrl.load(
                parsedUrl,
                browserID=self._browserID,
                title=title,
                browserSize=browserSize,
                showActionBtn=showActionBtn,
                showCloseBtn=showCloseBtn)