def get_award_ticket_value(self, include_pro_bonus: bool = False) -> int: """Get the ticket award value for this achievement.""" val: int = (_ba.get_v1_account_misc_read_val('achAward.' + self._name, self._award) * _get_ach_mult(include_pro_bonus)) assert isinstance(val, int) return val
def _new_profile(self) -> None: # pylint: disable=cyclic-import from bastd.ui.profile.edit import EditProfileWindow from bastd.ui.purchase import PurchaseWindow # Limit to a handful profiles if they don't have pro-options. max_non_pro_profiles = _ba.get_v1_account_misc_read_val('mnpp', 5) assert self._profiles is not None if (not ba.app.accounts_v1.have_pro_options() and len(self._profiles) >= max_non_pro_profiles): PurchaseWindow(items=['pro'], header_text=ba.Lstr( resource='unlockThisProfilesText', subs=[('${NUM}', str(max_non_pro_profiles))])) return # Clamp at 100 profiles (otherwise the server will and that's less # elegant looking). if len(self._profiles) > 100: ba.screenmessage( ba.Lstr(translate=('serverResponses', 'Max number of profiles reached.')), color=(1, 0, 0)) ba.playsound(ba.getsound('error')) return self._save_state() ba.containerwidget(edit=self._root_widget, transition='out_left') ba.app.ui.set_main_menu_window( EditProfileWindow( existing_profile=None, in_main_menu=self._in_main_menu).get_root_widget())
def _fetch_news(self) -> None: ba.app.main_menu_last_news_fetch_time = time.time() # UPDATE - We now just pull news from MRVs. news = _ba.get_v1_account_misc_read_val('n', None) if news is not None: self._got_news(news)
def _update(self) -> None: # If they want us to close once we're signed in, do so. if self._close_once_signed_in and self._signed_in: self._back() return # Hmm should update this to use get_account_state_num. # Theoretically if we switch from one signed-in account to another # in the background this would break. account_state_num = _ba.get_v1_account_state_num() account_state = _ba.get_v1_account_state() show_linked = (self._signed_in and _ba.get_v1_account_misc_read_val( 'allowAccountLinking2', False)) if (account_state_num != self._account_state_num or self._show_linked != show_linked or self._needs_refresh): self._show_linked = show_linked self._account_state_num = account_state_num self._signed_in = (account_state == 'signed_in') self._refresh() # Go ahead and refresh some individual things # that may change under us. self._update_linked_accounts_text() self._update_unlink_accounts_button() self._refresh_campaign_progress_text() self._refresh_achievements() self._refresh_tickets_text() self._refresh_account_name_text()
def _get_ach_mult(include_pro_bonus: bool = False) -> int: """Return the multiplier for achievement pts. (just for display; changing this here won't affect actual rewards) """ val: int = _ba.get_v1_account_misc_read_val('achAwardMult', 5) assert isinstance(val, int) if include_pro_bonus and _ba.app.accounts_v1.have_pro(): val *= 2 return val
def _calc_count_for_tab(tabval: list[dict[str, Any]], our_tickets: int, count: int) -> int: for section in tabval: for item in section['items']: ticket_cost = _ba.get_v1_account_misc_read_val( 'price.' + item, None) if ticket_cost is not None: if (our_tickets >= ticket_cost and not _ba.get_purchased(item)): count += 1 return count
def buy(self, item: str) -> None: """Attempt to purchase the provided item.""" from ba.internal import (get_available_sale_time, get_store_item_name_translated) from bastd.ui import account from bastd.ui.confirm import ConfirmWindow from bastd.ui import getcurrency # Prevent pressing buy within a few seconds of the last press # (gives the buttons time to disable themselves and whatnot). curtime = ba.time(ba.TimeType.REAL) if self._last_buy_time is not None and (curtime - self._last_buy_time) < 2.0: ba.playsound(ba.getsound('error')) else: if _ba.get_v1_account_state() != 'signed_in': account.show_sign_in_prompt() else: self._last_buy_time = curtime # Pro is an actual IAP; the rest are ticket purchases. if item == 'pro': ba.playsound(ba.getsound('click01')) # Purchase either pro or pro_sale depending on whether # there is a sale going on. self._do_purchase_check('pro' if get_available_sale_time( 'extras') is None else 'pro_sale') else: price = _ba.get_v1_account_misc_read_val( 'price.' + item, None) our_tickets = _ba.get_v1_account_ticket_count() if price is not None and our_tickets < price: ba.playsound(ba.getsound('error')) getcurrency.show_get_tickets_prompt() else: def do_it() -> None: self._do_purchase_check(item, is_ticket_purchase=True) ba.playsound(ba.getsound('swish')) ConfirmWindow( ba.Lstr(resource='store.purchaseConfirmText', subs=[ ('${ITEM}', get_store_item_name_translated(item)) ]), width=400, height=120, action=do_it, ok_text=ba.Lstr(resource='store.purchaseText', fallback_resource='okText'))
def _on_activity_mult_press(self) -> None: from bastd.ui import confirm txt = ba.Lstr( resource='coopSelectWindow.activenessAllTimeInfoText' if self._season == 'a' else 'coopSelectWindow.activenessInfoText', subs=[('${MAX}', str(_ba.get_v1_account_misc_read_val('activenessMax', 1.0)))]) confirm.ConfirmWindow(txt, cancel_button=False, width=460, height=150, origin_widget=self._activity_mult_button)
def __init__(self, settings: dict): """Instantiate the Activity.""" super().__init__(settings) # Holds some flattened info about the player set at the point # when on_begin() is called. self.initialplayerinfos: Optional[list[ba.PlayerInfo]] = None # Go ahead and get our map loading. self._map_type = _map.get_map_class(self._calc_map_name(settings)) self._spawn_sound = _ba.getsound('spawn') self._map_type.preload() self._map: Optional[ba.Map] = None self._powerup_drop_timer: Optional[ba.Timer] = None self._tnt_spawners: Optional[dict[int, TNTSpawner]] = None self._tnt_drop_timer: Optional[ba.Timer] = None self._game_scoreboard_name_text: Optional[ba.Actor] = None self._game_scoreboard_description_text: Optional[ba.Actor] = None self._standard_time_limit_time: Optional[int] = None self._standard_time_limit_timer: Optional[ba.Timer] = None self._standard_time_limit_text: Optional[ba.NodeActor] = None self._standard_time_limit_text_input: Optional[ba.NodeActor] = None self._tournament_time_limit: Optional[int] = None self._tournament_time_limit_timer: Optional[ba.Timer] = None self._tournament_time_limit_title_text: Optional[ba.NodeActor] = None self._tournament_time_limit_text: Optional[ba.NodeActor] = None self._tournament_time_limit_text_input: Optional[ba.NodeActor] = None self._zoom_message_times: dict[int, float] = {} self._is_waiting_for_continue = False self._continue_cost = _ba.get_v1_account_misc_read_val( 'continueStartCost', 25) self._continue_cost_mult = _ba.get_v1_account_misc_read_val( 'continuesMult', 2) self._continue_cost_offset = _ba.get_v1_account_misc_read_val( 'continuesOffset', 0)
def _on_pro_mult_press(self) -> None: from bastd.ui import confirm txt = ba.Lstr(resource='coopSelectWindow.proMultInfoText', subs=[('${PERCENT}', str( _ba.get_v1_account_misc_read_val( 'proPowerRankingBoost', 10))), ('${PRO}', ba.Lstr(resource='store.bombSquadProNameText', subs=[('${APP_NAME}', ba.Lstr(resource='titleText'))]))]) confirm.ConfirmWindow(txt, cancel_button=False, width=460, height=130, origin_widget=self._pro_mult_button)
def update(self) -> None: """Update!""" if not self._root_widget: return # Update boost-price. if self._boost_price is not None: ba.textwidget(edit=self._boost_price, text=ba.charstr(ba.SpecialChar.TICKET) + str(self._boost_tickets)) # Update boost button color based on if we have enough moola. if self._boost_button is not None: can_boost = ( (_ba.get_v1_account_state() == 'signed_in' and _ba.get_v1_account_ticket_count() >= self._boost_tickets)) ba.buttonwidget(edit=self._boost_button, color=(0, 1, 0) if can_boost else (0.7, 0.7, 0.7)) # Update ticket-count. if self._tickets_text is not None: if self._boost_button is not None: if _ba.get_v1_account_state() == 'signed_in': val = ba.charstr(ba.SpecialChar.TICKET) + str( _ba.get_v1_account_ticket_count()) else: val = ba.charstr(ba.SpecialChar.TICKET) + '???' ba.textwidget(edit=self._tickets_text, text=val) else: ba.textwidget(edit=self._tickets_text, text='') current_time = ba.time(ba.TimeType.REAL) if (self._last_transaction_time is None or current_time - self._last_transaction_time > 0.001 * _ba.get_v1_account_misc_read_val('pqInt', 5000)): self._last_transaction_time = current_time _ba.add_transaction( { 'type': 'PARTY_QUEUE_QUERY', 'q': self._queue_id }, callback=ba.WeakCall(self.on_update_response)) _ba.run_transactions() # step our dudes for dude in self._dudes: dude.step(self._smoothing)
def _purchase_check_result(self, item: str, is_ticket_purchase: bool, result: Optional[dict[str, Any]]) -> None: if result is None: ba.playsound(ba.getsound('error')) ba.screenmessage( ba.Lstr(resource='internal.unavailableNoConnectionText'), color=(1, 0, 0)) else: if is_ticket_purchase: if result['allow']: price = _ba.get_v1_account_misc_read_val( 'price.' + item, None) if (price is None or not isinstance(price, int) or price <= 0): print('Error; got invalid local price of', price, 'for item', item) ba.playsound(ba.getsound('error')) else: ba.playsound(ba.getsound('click01')) _ba.in_game_purchase(item, price) else: if result['reason'] == 'versionTooOld': ba.playsound(ba.getsound('error')) ba.screenmessage(ba.Lstr( resource='getTicketsWindow.versionTooOldText'), color=(1, 0, 0)) else: ba.playsound(ba.getsound('error')) ba.screenmessage(ba.Lstr( resource='getTicketsWindow.unavailableText'), color=(1, 0, 0)) # Real in-app purchase. else: if result['allow']: _ba.purchase(item) else: if result['reason'] == 'versionTooOld': ba.playsound(ba.getsound('error')) ba.screenmessage(ba.Lstr( resource='getTicketsWindow.versionTooOldText'), color=(1, 0, 0)) else: ba.playsound(ba.getsound('error')) ba.screenmessage(ba.Lstr( resource='getTicketsWindow.unavailableText'), color=(1, 0, 0))
def continue_or_end_game(self) -> None: """If continues are allowed, prompts the player to purchase a continue and calls either end_game or continue_game depending on the result""" # pylint: disable=too-many-nested-blocks # pylint: disable=cyclic-import from bastd.ui.continues import ContinuesWindow from ba._coopsession import CoopSession from ba._generated.enums import TimeType try: if _ba.get_v1_account_misc_read_val('enableContinues', False): session = self.session # We only support continuing in non-tournament games. tournament_id = session.tournament_id if tournament_id is None: # We currently only support continuing in sequential # co-op campaigns. if isinstance(session, CoopSession): assert session.campaign is not None if session.campaign.sequential: gnode = self.globalsnode # Only attempt this if we're not currently paused # and there appears to be no UI. if (not gnode.paused and not _ba.app.ui.has_main_menu_window()): self._is_waiting_for_continue = True with _ba.Context('ui'): _ba.timer( 0.5, lambda: ContinuesWindow( self, self._continue_cost, continue_call=WeakCall( self._continue_choice, True), cancel_call=WeakCall( self._continue_choice, False)), timetype=TimeType.REAL) return except Exception: print_exception('Error handling continues.') self.end_game()
def _disabled_press(self) -> None: # if we're on a platform without purchases, inform the user they # can link their accounts and buy stuff elsewhere app = ba.app if ((app.test_build or (app.platform == 'android' and app.subplatform in ['oculus', 'cardboard'])) and _ba.get_v1_account_misc_read_val('allowAccountLinking2', False)): ba.screenmessage(ba.Lstr(resource=self._r + '.unavailableLinkAccountText'), color=(1, 0.5, 0)) else: ba.screenmessage(ba.Lstr(resource=self._r + '.unavailableText'), color=(1, 0.5, 0)) ba.playsound(ba.getsound('error'))
def get_league_rank_points(self, data: Optional[dict[str, Any]], subset: str = None) -> int: """(internal)""" if data is None: return 0 # If the data contains an achievement total, use that. otherwise calc # locally. if data['at'] is not None: total_ach_value = data['at'] else: total_ach_value = 0 for ach in _ba.app.ach.achievements: if ach.complete: total_ach_value += ach.power_ranking_value trophies_total: int = (data['t0a'] * data['t0am'] + data['t0b'] * data['t0bm'] + data['t1'] * data['t1m'] + data['t2'] * data['t2m'] + data['t3'] * data['t3m'] + data['t4'] * data['t4m']) if subset == 'trophyCount': val: int = (data['t0a'] + data['t0b'] + data['t1'] + data['t2'] + data['t3'] + data['t4']) assert isinstance(val, int) return val if subset == 'trophies': assert isinstance(trophies_total, int) return trophies_total if subset is not None: raise ValueError('invalid subset value: ' + str(subset)) if data['p']: pro_mult = 1.0 + float( _ba.get_v1_account_misc_read_val('proPowerRankingBoost', 0.0)) * 0.01 else: pro_mult = 1.0 # For final value, apply our pro mult and activeness-mult. return int( (total_ach_value + trophies_total) * (data['act'] if data['act'] is not None else 1.0) * pro_mult)
def _query_party_list_periodically(self) -> None: now = ba.time(ba.TimeType.REAL) # Fire off a new public-party query periodically. if (self._last_server_list_query_time is None or now - self._last_server_list_query_time > 0.001 * _ba.get_v1_account_misc_read_val('pubPartyRefreshMS', 10000)): self._last_server_list_query_time = now if DEBUG_SERVER_COMMUNICATION: print('REQUESTING SERVER LIST') if _ba.get_v1_account_state() == 'signed_in': _ba.add_transaction( { 'type': 'PUBLIC_PARTY_QUERY', 'proto': ba.app.protocol_version, 'lang': ba.app.lang.language }, callback=ba.WeakCall(self._on_public_party_query_result)) _ba.run_transactions() else: self._on_public_party_query_result(None)
def __init__(self, transition: str = 'in_right', from_modal_store: bool = False, modal: bool = False, origin_widget: ba.Widget = None, store_back_location: str = None): # pylint: disable=too-many-statements # pylint: disable=too-many-locals ba.set_analytics_screen('Get Tickets Window') self._transitioning_out = False self._store_back_location = store_back_location # ew. self._ad_button_greyed = False self._smooth_update_timer: Optional[ba.Timer] = None self._ad_button = None self._ad_label = None self._ad_image = None self._ad_time_text = None # If they provided an origin-widget, scale up from that. scale_origin: Optional[tuple[float, float]] if origin_widget is not None: self._transition_out = 'out_scale' scale_origin = origin_widget.get_screen_space_center() transition = 'in_scale' else: self._transition_out = 'out_right' scale_origin = None uiscale = ba.app.ui.uiscale self._width = 1000.0 if uiscale is ba.UIScale.SMALL else 800.0 x_inset = 100.0 if uiscale is ba.UIScale.SMALL else 0.0 self._height = 480.0 self._modal = modal self._from_modal_store = from_modal_store self._r = 'getTicketsWindow' top_extra = 20 if uiscale is ba.UIScale.SMALL else 0 super().__init__(root_widget=ba.containerwidget( size=(self._width, self._height + top_extra), transition=transition, scale_origin_stack_offset=scale_origin, color=(0.4, 0.37, 0.55), scale=(1.63 if uiscale is ba.UIScale.SMALL else 1.2 if uiscale is ba.UIScale.MEDIUM else 1.0), stack_offset=(0, -3) if uiscale is ba.UIScale.SMALL else (0, 0))) btn = ba.buttonwidget( parent=self._root_widget, position=(55 + x_inset, self._height - 79), size=(140, 60), scale=1.0, autoselect=True, label=ba.Lstr(resource='doneText' if modal else 'backText'), button_type='regular' if modal else 'back', on_activate_call=self._back) ba.containerwidget(edit=self._root_widget, cancel_button=btn) ba.textwidget(parent=self._root_widget, position=(self._width * 0.5, self._height - 55), size=(0, 0), color=ba.app.ui.title_color, scale=1.2, h_align='center', v_align='center', text=ba.Lstr(resource=self._r + '.titleText'), maxwidth=290) if not modal: ba.buttonwidget(edit=btn, button_type='backSmall', size=(60, 60), label=ba.charstr(ba.SpecialChar.BACK)) b_size = (220.0, 180.0) v = self._height - b_size[1] - 80 spacing = 1 self._ad_button = None def _add_button(item: str, position: tuple[float, float], size: tuple[float, float], label: ba.Lstr, price: str = None, tex_name: str = None, tex_opacity: float = 1.0, tex_scale: float = 1.0, enabled: bool = True, text_scale: float = 1.0) -> ba.Widget: btn2 = ba.buttonwidget( parent=self._root_widget, position=position, button_type='square', size=size, label='', autoselect=True, color=None if enabled else (0.5, 0.5, 0.5), on_activate_call=(ba.Call(self._purchase, item) if enabled else self._disabled_press)) txt = ba.textwidget(parent=self._root_widget, text=label, position=(position[0] + size[0] * 0.5, position[1] + size[1] * 0.3), scale=text_scale, maxwidth=size[0] * 0.75, size=(0, 0), h_align='center', v_align='center', draw_controller=btn2, color=(0.7, 0.9, 0.7, 1.0 if enabled else 0.2)) if price is not None and enabled: ba.textwidget(parent=self._root_widget, text=price, position=(position[0] + size[0] * 0.5, position[1] + size[1] * 0.17), scale=0.7, maxwidth=size[0] * 0.75, size=(0, 0), h_align='center', v_align='center', draw_controller=btn2, color=(0.4, 0.9, 0.4, 1.0)) i = None if tex_name is not None: tex_size = 90.0 * tex_scale i = ba.imagewidget( parent=self._root_widget, texture=ba.gettexture(tex_name), position=(position[0] + size[0] * 0.5 - tex_size * 0.5, position[1] + size[1] * 0.66 - tex_size * 0.5), size=(tex_size, tex_size), draw_controller=btn2, opacity=tex_opacity * (1.0 if enabled else 0.25)) if item == 'ad': self._ad_button = btn2 self._ad_label = txt assert i is not None self._ad_image = i self._ad_time_text = ba.textwidget( parent=self._root_widget, text='1m 10s', position=(position[0] + size[0] * 0.5, position[1] + size[1] * 0.5), scale=text_scale * 1.2, maxwidth=size[0] * 0.85, size=(0, 0), h_align='center', v_align='center', draw_controller=btn2, color=(0.4, 0.9, 0.4, 1.0)) return btn2 rsrc = self._r + '.ticketsText' c2txt = ba.Lstr( resource=rsrc, subs=[('${COUNT}', str(_ba.get_v1_account_misc_read_val('tickets2Amount', 500)))]) c3txt = ba.Lstr( resource=rsrc, subs=[ ('${COUNT}', str(_ba.get_v1_account_misc_read_val('tickets3Amount', 1500))) ]) c4txt = ba.Lstr( resource=rsrc, subs=[ ('${COUNT}', str(_ba.get_v1_account_misc_read_val('tickets4Amount', 5000))) ]) c5txt = ba.Lstr( resource=rsrc, subs=[ ('${COUNT}', str(_ba.get_v1_account_misc_read_val('tickets5Amount', 15000))) ]) h = 110.0 # enable buttons if we have prices.. tickets2_price = _ba.get_price('tickets2') tickets3_price = _ba.get_price('tickets3') tickets4_price = _ba.get_price('tickets4') tickets5_price = _ba.get_price('tickets5') # TEMP # tickets1_price = '$0.99' # tickets2_price = '$4.99' # tickets3_price = '$9.99' # tickets4_price = '$19.99' # tickets5_price = '$49.99' _add_button('tickets2', enabled=(tickets2_price is not None), position=(self._width * 0.5 - spacing * 1.5 - b_size[0] * 2.0 + h, v), size=b_size, label=c2txt, price=tickets2_price, tex_name='ticketsMore') # 0.99-ish _add_button('tickets3', enabled=(tickets3_price is not None), position=(self._width * 0.5 - spacing * 0.5 - b_size[0] * 1.0 + h, v), size=b_size, label=c3txt, price=tickets3_price, tex_name='ticketRoll') # 4.99-ish v -= b_size[1] - 5 _add_button('tickets4', enabled=(tickets4_price is not None), position=(self._width * 0.5 - spacing * 1.5 - b_size[0] * 2.0 + h, v), size=b_size, label=c4txt, price=tickets4_price, tex_name='ticketRollBig', tex_scale=1.2) # 9.99-ish _add_button('tickets5', enabled=(tickets5_price is not None), position=(self._width * 0.5 - spacing * 0.5 - b_size[0] * 1.0 + h, v), size=b_size, label=c5txt, price=tickets5_price, tex_name='ticketRolls', tex_scale=1.2) # 19.99-ish self._enable_ad_button = _ba.has_video_ads() h = self._width * 0.5 + 110.0 v = self._height - b_size[1] - 115.0 if self._enable_ad_button: h_offs = 35 b_size_3 = (150, 120) cdb = _add_button( 'ad', position=(h + h_offs, v), size=b_size_3, label=ba.Lstr(resource=self._r + '.ticketsFromASponsorText', subs=[('${COUNT}', str( _ba.get_v1_account_misc_read_val( 'sponsorTickets', 5)))]), tex_name='ticketsMore', enabled=self._enable_ad_button, tex_opacity=0.6, tex_scale=0.7, text_scale=0.7) ba.buttonwidget(edit=cdb, color=(0.65, 0.5, 0.7) if self._enable_ad_button else (0.5, 0.5, 0.5)) self._ad_free_text = ba.textwidget( parent=self._root_widget, text=ba.Lstr(resource=self._r + '.freeText'), position=(h + h_offs + b_size_3[0] * 0.5, v + b_size_3[1] * 0.5 + 25), size=(0, 0), color=(1, 1, 0, 1.0) if self._enable_ad_button else (1, 1, 1, 0.2), draw_controller=cdb, rotate=15, shadow=1.0, maxwidth=150, h_align='center', v_align='center', scale=1.0) v -= 125 else: v -= 20 if True: # pylint: disable=using-constant-test h_offs = 35 b_size_3 = (150, 120) cdb = _add_button( 'app_invite', position=(h + h_offs, v), size=b_size_3, label=ba.Lstr( resource='gatherWindow.earnTicketsForRecommendingText', subs=[('${COUNT}', str( _ba.get_v1_account_misc_read_val( 'sponsorTickets', 5)))]), tex_name='ticketsMore', enabled=True, tex_opacity=0.6, tex_scale=0.7, text_scale=0.7) ba.buttonwidget(edit=cdb, color=(0.65, 0.5, 0.7)) ba.textwidget(parent=self._root_widget, text=ba.Lstr(resource=self._r + '.freeText'), position=(h + h_offs + b_size_3[0] * 0.5, v + b_size_3[1] * 0.5 + 25), size=(0, 0), color=(1, 1, 0, 1.0), draw_controller=cdb, rotate=15, shadow=1.0, maxwidth=150, h_align='center', v_align='center', scale=1.0) tc_y_offs = 0 h = self._width - (185 + x_inset) v = self._height - 95 + tc_y_offs txt1 = (ba.Lstr( resource=self._r + '.youHaveText').evaluate().partition('${COUNT}')[0].strip()) txt2 = (ba.Lstr( resource=self._r + '.youHaveText').evaluate().rpartition('${COUNT}')[0].strip()) ba.textwidget(parent=self._root_widget, text=txt1, position=(h, v), size=(0, 0), color=(0.5, 0.5, 0.6), maxwidth=200, h_align='center', v_align='center', scale=0.8) v -= 30 self._ticket_count_text = ba.textwidget(parent=self._root_widget, position=(h, v), size=(0, 0), color=(0.2, 1.0, 0.2), maxwidth=200, h_align='center', v_align='center', scale=1.6) v -= 30 ba.textwidget(parent=self._root_widget, text=txt2, position=(h, v), size=(0, 0), color=(0.5, 0.5, 0.6), maxwidth=200, h_align='center', v_align='center', scale=0.8) # update count now and once per second going forward.. self._ticking_node: Optional[ba.Node] = None self._smooth_ticket_count: Optional[float] = None self._ticket_count = 0 self._update() self._update_timer = ba.Timer(1.0, ba.WeakCall(self._update), timetype=ba.TimeType.REAL, repeat=True) self._smooth_increase_speed = 1.0
def _update(self) -> None: # We may outlive our widgets. if not self.root_widget: return # If we've been foregrounded/backgrounded we need to re-grab data. if self._fg_state != ba.app.fg_state: self._fg_state = ba.app.fg_state self._have_valid_data = False # If we need to run another tournament query, do so. if not self._running_query and ( (self._last_query_time is None) or (not self._have_valid_data) or (ba.time(ba.TimeType.REAL) - self._last_query_time > 30.0)): _ba.tournament_query(args={ 'source': 'entry window' if self._tournament_activity is None else 'retry entry window' }, callback=ba.WeakCall( self._on_tournament_query_response)) self._last_query_time = ba.time(ba.TimeType.REAL) self._running_query = True # Grab the latest info on our tourney. self._tournament_info = ba.app.accounts_v1.tournament_info[ self._tournament_id] # If we don't have valid data always show a '-' for time. if not self._have_valid_data: ba.textwidget(edit=self._time_remaining_text, text='-') else: if self._seconds_remaining is not None: self._seconds_remaining = max(0, self._seconds_remaining - 1) ba.textwidget(edit=self._time_remaining_text, text=ba.timestring( self._seconds_remaining * 1000, centi=False, timeformat=ba.TimeFormat.MILLISECONDS)) # Keep price up-to-date and update the button with it. self._purchase_price = _ba.get_v1_account_misc_read_val( self._purchase_price_name, None) ba.textwidget( edit=self._ticket_cost_text, text=(ba.Lstr(resource='getTicketsWindow.freeText') if self._purchase_price == 0 else ba.Lstr( resource='getTicketsWindow.ticketsText', subs=[('${COUNT}', str(self._purchase_price) if self._purchase_price is not None else '?')])), position=self._ticket_cost_text_position_free if self._purchase_price == 0 else self._ticket_cost_text_position, scale=1.0 if self._purchase_price == 0 else 0.6) ba.textwidget( edit=self._free_plays_remaining_text, text='' if (self._tournament_info['freeTriesRemaining'] in [None, 0] or self._purchase_price != 0) else '' + str(self._tournament_info['freeTriesRemaining'])) ba.imagewidget(edit=self._ticket_img, opacity=0.2 if self._purchase_price == 0 else 1.0, position=self._ticket_img_pos_free if self._purchase_price == 0 else self._ticket_img_pos) if self._do_ad_btn: enabled = _ba.have_incentivized_ad() have_ad_tries_remaining = ( self._tournament_info['adTriesRemaining'] is not None and self._tournament_info['adTriesRemaining'] > 0) ba.textwidget(edit=self._ad_text, position=self._ad_text_position_remaining if have_ad_tries_remaining else self._ad_text_position, color=(0, 1, 0) if enabled else (0.5, 0.5, 0.5)) ba.imagewidget(edit=self._pay_with_ad_img, opacity=1.0 if enabled else 0.2) ba.buttonwidget(edit=self._pay_with_ad_btn, color=(0.5, 0.7, 0.2) if enabled else (0.5, 0.5, 0.5)) ad_plays_remaining_text = ( '' if not have_ad_tries_remaining else '' + str(self._tournament_info['adTriesRemaining'])) ba.textwidget(edit=self._ad_plays_remaining_text, text=ad_plays_remaining_text, color=(0, 0.8, 0) if enabled else (0.4, 0.4, 0.4)) try: t_str = str(_ba.get_v1_account_ticket_count()) except Exception: t_str = '?' if self._get_tickets_button: ba.buttonwidget(edit=self._get_tickets_button, label=ba.charstr(ba.SpecialChar.TICKET) + t_str) if self._ticket_count_text: ba.textwidget(edit=self._ticket_count_text, text=ba.charstr(ba.SpecialChar.TICKET) + t_str)
def on_activate( self, parent_widget: ba.Widget, tab_button: ba.Widget, region_width: float, region_height: float, region_left: float, region_bottom: float, ) -> ba.Widget: party_button_label = ('X' if ba.app.iircade_mode else ba.charstr( ba.SpecialChar.TOP_BUTTON)) message = ba.Lstr( resource='gatherWindow.aboutDescriptionText', subs=[('${PARTY}', ba.charstr(ba.SpecialChar.PARTY_ICON)), ('${BUTTON}', party_button_label)], ) # Let's not talk about sharing in vr-mode; its tricky to fit more # than one head in a VR-headset ;-) if not ba.app.vr_mode: message = ba.Lstr( value='${A}\n\n${B}', subs=[('${A}', message), ('${B}', ba.Lstr(resource='gatherWindow.' 'aboutDescriptionLocalMultiplayerExtraText'))]) string_height = 400 include_invite = True msc_scale = 1.1 c_height_2 = min(region_height, string_height * msc_scale + 100) try_tickets = _ba.get_v1_account_misc_read_val('friendTryTickets', None) if try_tickets is None: include_invite = False self._container = ba.containerwidget( parent=parent_widget, position=(region_left, region_bottom + (region_height - c_height_2) * 0.5), size=(region_width, c_height_2), background=False, selectable=include_invite) ba.widget(edit=self._container, up_widget=tab_button) ba.textwidget(parent=self._container, position=(region_width * 0.5, c_height_2 * (0.58 if include_invite else 0.5)), color=(0.6, 1.0, 0.6), scale=msc_scale, size=(0, 0), maxwidth=region_width * 0.9, max_height=c_height_2 * (0.7 if include_invite else 0.9), h_align='center', v_align='center', text=message) if include_invite: ba.textwidget(parent=self._container, position=(region_width * 0.57, 35), color=(0, 1, 0), scale=0.6, size=(0, 0), maxwidth=region_width * 0.5, h_align='right', v_align='center', flatness=1.0, text=ba.Lstr( resource='gatherWindow.inviteAFriendText', subs=[('${COUNT}', str(try_tickets))])) ba.buttonwidget( parent=self._container, position=(region_width * 0.59, 10), size=(230, 50), color=(0.54, 0.42, 0.56), textcolor=(0, 1, 0), label=ba.Lstr(resource='gatherWindow.inviteFriendsText', fallback_resource=( 'gatherWindow.getFriendInviteCodeText')), autoselect=True, on_activate_call=ba.WeakCall(self._invite_to_try_press), up_widget=tab_button) return self._container
def _get_custom_logo_tex_name(self) -> Optional[str]: if _ba.get_v1_account_misc_read_val('easter', False): return 'logoEaster' return None
def power_ranking_value(self) -> int: """Get the power-ranking award value for this achievement.""" val: int = _ba.get_v1_account_misc_read_val( 'achLeaguePoints.' + self._name, self._award) assert isinstance(val, int) return val
def _refresh(self) -> None: # pylint: disable=too-many-statements # (re)create the sub-container if need be.. if self._subcontainer is not None: self._subcontainer.delete() self._subcontainer = ba.containerwidget( parent=self._scrollwidget, size=(self._subcontainerwidth, self._subcontainerheight), background=False) w_parent = self._subcontainer v = self._subcontainerheight - 20 v -= 0 h2 = 80 v2 = v - 60 worth_color = (0.6, 0.6, 0.65) tally_color = (0.5, 0.6, 0.8) spc = 43 h_offs_tally = 150 tally_maxwidth = 120 v2 -= 70 ba.textwidget(parent=w_parent, position=(h2 - 60, v2 + 106), size=(0, 0), flatness=1.0, shadow=0.0, text=ba.Lstr(resource='coopSelectWindow.pointsText'), h_align='left', v_align='center', scale=0.8, color=(1, 1, 1, 0.3), maxwidth=200) self._power_ranking_achievements_button = ba.buttonwidget( parent=w_parent, position=(h2 - 60, v2 + 10), size=(200, 80), icon=ba.gettexture('achievementsIcon'), autoselect=True, on_activate_call=ba.WeakCall(self._on_achievements_press), up_widget=self._back_button, left_widget=self._back_button, color=(0.5, 0.5, 0.6), textcolor=(0.7, 0.7, 0.8), label='') self._power_ranking_achievement_total_text = ba.textwidget( parent=w_parent, position=(h2 + h_offs_tally, v2 + 45), size=(0, 0), flatness=1.0, shadow=0.0, text='-', h_align='left', v_align='center', scale=0.8, color=tally_color, maxwidth=tally_maxwidth) v2 -= 80 self._power_ranking_trophies_button = ba.buttonwidget( parent=w_parent, position=(h2 - 60, v2 + 10), size=(200, 80), icon=ba.gettexture('medalSilver'), autoselect=True, on_activate_call=ba.WeakCall(self._on_trophies_press), left_widget=self._back_button, color=(0.5, 0.5, 0.6), textcolor=(0.7, 0.7, 0.8), label='') self._power_ranking_trophies_total_text = ba.textwidget( parent=w_parent, position=(h2 + h_offs_tally, v2 + 45), size=(0, 0), flatness=1.0, shadow=0.0, text='-', h_align='left', v_align='center', scale=0.8, color=tally_color, maxwidth=tally_maxwidth) v2 -= 100 ba.textwidget( parent=w_parent, position=(h2 - 60, v2 + 86), size=(0, 0), flatness=1.0, shadow=0.0, text=ba.Lstr(resource='coopSelectWindow.multipliersText'), h_align='left', v_align='center', scale=0.8, color=(1, 1, 1, 0.3), maxwidth=200) self._activity_mult_button: Optional[ba.Widget] if _ba.get_v1_account_misc_read_val('act', False): self._activity_mult_button = ba.buttonwidget( parent=w_parent, position=(h2 - 60, v2 + 10), size=(200, 60), icon=ba.gettexture('heart'), icon_color=(0.5, 0, 0.5), label=ba.Lstr(resource='coopSelectWindow.activityText'), autoselect=True, on_activate_call=ba.WeakCall(self._on_activity_mult_press), left_widget=self._back_button, color=(0.5, 0.5, 0.6), textcolor=(0.7, 0.7, 0.8)) self._activity_mult_text = ba.textwidget( parent=w_parent, position=(h2 + h_offs_tally, v2 + 40), size=(0, 0), flatness=1.0, shadow=0.0, text='-', h_align='left', v_align='center', scale=0.8, color=tally_color, maxwidth=tally_maxwidth) v2 -= 65 else: self._activity_mult_button = None self._pro_mult_button = ba.buttonwidget( parent=w_parent, position=(h2 - 60, v2 + 10), size=(200, 60), icon=ba.gettexture('logo'), icon_color=(0.3, 0, 0.3), label=ba.Lstr(resource='store.bombSquadProNameText', subs=[('${APP_NAME}', ba.Lstr(resource='titleText')) ]), autoselect=True, on_activate_call=ba.WeakCall(self._on_pro_mult_press), left_widget=self._back_button, color=(0.5, 0.5, 0.6), textcolor=(0.7, 0.7, 0.8)) self._pro_mult_text = ba.textwidget(parent=w_parent, position=(h2 + h_offs_tally, v2 + 40), size=(0, 0), flatness=1.0, shadow=0.0, text='-', h_align='left', v_align='center', scale=0.8, color=tally_color, maxwidth=tally_maxwidth) v2 -= 30 v2 -= spc ba.textwidget(parent=w_parent, position=(h2 + h_offs_tally - 10 - 40, v2 + 35), size=(0, 0), flatness=1.0, shadow=0.0, text=ba.Lstr(resource='finalScoreText'), h_align='right', v_align='center', scale=0.9, color=worth_color, maxwidth=150) self._power_ranking_total_text = ba.textwidget( parent=w_parent, position=(h2 + h_offs_tally - 40, v2 + 35), size=(0, 0), flatness=1.0, shadow=0.0, text='-', h_align='left', v_align='center', scale=0.9, color=tally_color, maxwidth=tally_maxwidth) self._season_show_text = ba.textwidget( parent=w_parent, position=(390 - 15, v - 20), size=(0, 0), color=(0.6, 0.6, 0.7), maxwidth=200, text=ba.Lstr(resource='showText'), h_align='right', v_align='center', scale=0.8, shadow=0, flatness=1.0) self._league_title_text = ba.textwidget(parent=w_parent, position=(470, v - 97), size=(0, 0), color=(0.6, 0.6, 0.7), maxwidth=230, text='', h_align='center', v_align='center', scale=0.9, shadow=0, flatness=1.0) self._league_text_scale = 1.8 self._league_text_maxwidth = 210 self._league_text = ba.textwidget(parent=w_parent, position=(470, v - 140), size=(0, 0), color=(1, 1, 1), maxwidth=self._league_text_maxwidth, text='-', h_align='center', v_align='center', scale=self._league_text_scale, shadow=1.0, flatness=1.0) self._league_number_base_pos = (470, v - 140) self._league_number_text = ba.textwidget(parent=w_parent, position=(470, v - 140), size=(0, 0), color=(1, 1, 1), maxwidth=100, text='', h_align='left', v_align='center', scale=0.8, shadow=1.0, flatness=1.0) self._your_power_ranking_text = ba.textwidget(parent=w_parent, position=(470, v - 142 - 70), size=(0, 0), color=(0.6, 0.6, 0.7), maxwidth=230, text='', h_align='center', v_align='center', scale=0.9, shadow=0, flatness=1.0) self._to_ranked_text = ba.textwidget(parent=w_parent, position=(470, v - 250 - 70), size=(0, 0), color=(0.6, 0.6, 0.7), maxwidth=230, text='', h_align='center', v_align='center', scale=0.8, shadow=0, flatness=1.0) self._power_ranking_rank_text = ba.textwidget(parent=w_parent, position=(473, v - 210 - 70), size=(0, 0), big=False, text='-', h_align='center', v_align='center', scale=1.0) self._season_ends_text = ba.textwidget(parent=w_parent, position=(470, v - 380), size=(0, 0), color=(0.6, 0.6, 0.6), maxwidth=230, text='', h_align='center', v_align='center', scale=0.9, shadow=0, flatness=1.0) self._trophy_counts_reset_text = ba.textwidget( parent=w_parent, position=(470, v - 410), size=(0, 0), color=(0.5, 0.5, 0.5), maxwidth=230, text='Trophy counts will reset next season.', h_align='center', v_align='center', scale=0.8, shadow=0, flatness=1.0) self._power_ranking_score_widgets = [] self._power_ranking_score_v = v - 56 h = 707 v -= 451 self._see_more_button = ba.buttonwidget(parent=w_parent, label=self._rdict.seeMoreText, position=(h, v), color=(0.5, 0.5, 0.6), textcolor=(0.7, 0.7, 0.8), size=(230, 60), autoselect=True, on_activate_call=ba.WeakCall( self._on_more_press))
def _update_for_league_rank_data(self, data: Optional[dict[str, Any]]) -> None: # pylint: disable=too-many-statements # pylint: disable=too-many-branches # pylint: disable=too-many-locals if not self._root_widget: return accounts = ba.app.accounts_v1 in_top = (data is not None and data['rank'] is not None) eq_text = self._rdict.powerRankingPointsEqualsText pts_txt = self._rdict.powerRankingPointsText num_text = ba.Lstr(resource='numberText').evaluate() do_percent = False finished_season_unranked = False self._can_do_more_button = True extra_text = '' if _ba.get_v1_account_state() != 'signed_in': status_text = '(' + ba.Lstr( resource='notSignedInText').evaluate() + ')' elif in_top: assert data is not None status_text = num_text.replace('${NUMBER}', str(data['rank'])) elif data is not None: try: # handle old seasons where we didn't wind up ranked # at the end.. if not data['scores']: status_text = ( self._rdict.powerRankingFinishedSeasonUnrankedText) extra_text = '' finished_season_unranked = True self._can_do_more_button = False else: our_points = accounts.get_league_rank_points(data) progress = float(our_points) / max(1, data['scores'][-1][1]) status_text = str(int(progress * 100.0)) + '%' extra_text = ( '\n' + self._rdict.powerRankingPointsToRankedText.replace( '${CURRENT}', str(our_points)).replace( '${REMAINING}', str(data['scores'][-1][1]))) do_percent = True except Exception: ba.print_exception('Error updating power ranking.') status_text = self._rdict.powerRankingNotInTopText.replace( '${NUMBER}', str(data['listSize'])) extra_text = '' else: status_text = '-' self._season = data['s'] if data is not None else None v = self._subcontainerheight - 20 popup_was_selected = False if self._season_popup_menu is not None: btn = self._season_popup_menu.get_button() assert self._subcontainer if self._subcontainer.get_selected_child() == btn: popup_was_selected = True btn.delete() season_choices = [] season_choices_display = [] did_first = False self._is_current_season = False if data is not None: # build our list of seasons we have available for ssn in data['sl']: season_choices.append(ssn) if ssn != 'a' and not did_first: season_choices_display.append( ba.Lstr(resource='league.currentSeasonText', subs=[('${NUMBER}', ssn)])) did_first = True # if we either did not specify a season or specified the # first, we're looking at the current.. if self._season in [ssn, None]: self._is_current_season = True elif ssn == 'a': season_choices_display.append( ba.Lstr(resource='league.allTimeText')) else: season_choices_display.append( ba.Lstr(resource='league.seasonText', subs=[('${NUMBER}', ssn)])) assert self._subcontainer self._season_popup_menu = popup_ui.PopupMenu( parent=self._subcontainer, position=(390, v - 45), width=150, button_size=(200, 50), choices=season_choices, on_value_change_call=ba.WeakCall(self._on_season_change), choices_display=season_choices_display, current_choice=self._season) if popup_was_selected: ba.containerwidget( edit=self._subcontainer, selected_child=self._season_popup_menu.get_button()) ba.widget(edit=self._see_more_button, show_buffer_bottom=100) ba.widget(edit=self._season_popup_menu.get_button(), up_widget=self._back_button) ba.widget(edit=self._back_button, down_widget=self._power_ranking_achievements_button, right_widget=self._season_popup_menu.get_button()) ba.textwidget(edit=self._league_title_text, text='' if self._season == 'a' else ba.Lstr( resource='league.leagueText')) if data is None: lname = '' lnum = '' lcolor = (1, 1, 1) self._league_url_arg = '' elif self._season == 'a': lname = ba.Lstr(resource='league.allTimeText').evaluate() lnum = '' lcolor = (1, 1, 1) self._league_url_arg = '' else: lnum = ('[' + str(data['l']['i']) + ']') if data['l']['i2'] else '' lname = ba.Lstr(translate=('leagueNames', data['l']['n'])).evaluate() lcolor = data['l']['c'] self._league_url_arg = (data['l']['n'] + '_' + str(data['l']['i'])).lower() to_end_string: Union[ba.Lstr, str] if data is None or self._season == 'a' or data['se'] is None: to_end_string = '' show_season_end = False else: show_season_end = True days_to_end = data['se'][0] minutes_to_end = data['se'][1] if days_to_end > 0: to_end_string = ba.Lstr(resource='league.seasonEndsDaysText', subs=[('${NUMBER}', str(days_to_end))]) elif days_to_end == 0 and minutes_to_end >= 60: to_end_string = ba.Lstr(resource='league.seasonEndsHoursText', subs=[('${NUMBER}', str(minutes_to_end // 60))]) elif days_to_end == 0 and minutes_to_end >= 0: to_end_string = ba.Lstr( resource='league.seasonEndsMinutesText', subs=[('${NUMBER}', str(minutes_to_end))]) else: to_end_string = ba.Lstr( resource='league.seasonEndedDaysAgoText', subs=[('${NUMBER}', str(-(days_to_end + 1)))]) ba.textwidget(edit=self._season_ends_text, text=to_end_string) ba.textwidget(edit=self._trophy_counts_reset_text, text=ba.Lstr(resource='league.trophyCountsResetText') if self._is_current_season and show_season_end else '') ba.textwidget(edit=self._league_text, text=lname, color=lcolor) l_text_width = min( self._league_text_maxwidth, _ba.get_string_width(lname, suppress_warning=True) * self._league_text_scale) ba.textwidget( edit=self._league_number_text, text=lnum, color=lcolor, position=(self._league_number_base_pos[0] + l_text_width * 0.5 + 8, self._league_number_base_pos[1] + 10)) ba.textwidget( edit=self._to_ranked_text, text=ba.Lstr(resource='coopSelectWindow.toRankedText').evaluate() + '' + extra_text if do_percent else '') ba.textwidget( edit=self._your_power_ranking_text, text=ba.Lstr( resource='rankText', fallback_resource='coopSelectWindow.yourPowerRankingText') if (not do_percent) else '') ba.textwidget(edit=self._power_ranking_rank_text, position=(473, v - 70 - (170 if do_percent else 220)), text=status_text, big=(in_top or do_percent), scale=3.0 if (in_top or do_percent) else 0.7 if finished_season_unranked else 1.0) if self._activity_mult_button is not None: if data is None or data['act'] is None: ba.buttonwidget(edit=self._activity_mult_button, textcolor=(0.7, 0.7, 0.8, 0.5), icon_color=(0.5, 0, 0.5, 0.3)) ba.textwidget(edit=self._activity_mult_text, text=' -') else: ba.buttonwidget(edit=self._activity_mult_button, textcolor=(0.7, 0.7, 0.8, 1.0), icon_color=(0.5, 0, 0.5, 1.0)) # pylint: disable=consider-using-f-string ba.textwidget(edit=self._activity_mult_text, text='x ' + ('%.2f' % data['act'])) have_pro = False if data is None else data['p'] pro_mult = 1.0 + float( _ba.get_v1_account_misc_read_val('proPowerRankingBoost', 0.0)) * 0.01 # pylint: disable=consider-using-f-string ba.textwidget(edit=self._pro_mult_text, text=' -' if (data is None or not have_pro) else 'x ' + ('%.2f' % pro_mult)) ba.buttonwidget(edit=self._pro_mult_button, textcolor=(0.7, 0.7, 0.8, (1.0 if have_pro else 0.5)), icon_color=(0.5, 0, 0.5) if have_pro else (0.5, 0, 0.5, 0.2)) ba.buttonwidget(edit=self._power_ranking_achievements_button, label=('' if data is None else (str(data['a']) + ' ')) + ba.Lstr(resource='achievementsText').evaluate()) # for the achievement value, use the number they gave us for # non-current seasons; otherwise calc our own total_ach_value = 0 for ach in ba.app.ach.achievements: if ach.complete: total_ach_value += ach.power_ranking_value if self._season != 'a' and not self._is_current_season: if data is not None and 'at' in data: total_ach_value = data['at'] ba.textwidget(edit=self._power_ranking_achievement_total_text, text='-' if data is None else ('+ ' + pts_txt.replace('${NUMBER}', str(total_ach_value)))) total_trophies_count = (accounts.get_league_rank_points( data, 'trophyCount')) total_trophies_value = (accounts.get_league_rank_points( data, 'trophies')) ba.buttonwidget(edit=self._power_ranking_trophies_button, label=('' if data is None else (str(total_trophies_count) + ' ')) + ba.Lstr(resource='trophiesText').evaluate()) ba.textwidget( edit=self._power_ranking_trophies_total_text, text='-' if data is None else ('+ ' + pts_txt.replace('${NUMBER}', str(total_trophies_value)))) ba.textwidget( edit=self._power_ranking_total_text, text='-' if data is None else eq_text.replace( '${NUMBER}', str(accounts.get_league_rank_points(data)))) for widget in self._power_ranking_score_widgets: widget.delete() self._power_ranking_score_widgets = [] scores = data['scores'] if data is not None else [] tally_color = (0.5, 0.6, 0.8) w_parent = self._subcontainer v2 = self._power_ranking_score_v for score in scores: h2 = 680 is_us = score[3] self._power_ranking_score_widgets.append( ba.textwidget(parent=w_parent, position=(h2 - 20, v2), size=(0, 0), color=(1, 1, 1) if is_us else (0.6, 0.6, 0.7), maxwidth=40, flatness=1.0, shadow=0.0, text=num_text.replace('${NUMBER}', str(score[0])), h_align='right', v_align='center', scale=0.5)) self._power_ranking_score_widgets.append( ba.textwidget(parent=w_parent, position=(h2 + 20, v2), size=(0, 0), color=(1, 1, 1) if is_us else tally_color, maxwidth=60, text=str(score[1]), flatness=1.0, shadow=0.0, h_align='center', v_align='center', scale=0.7)) txt = ba.textwidget(parent=w_parent, position=(h2 + 60, v2 - (28 * 0.5) / 0.9), size=(210 / 0.9, 28), color=(1, 1, 1) if is_us else (0.6, 0.6, 0.6), maxwidth=210, flatness=1.0, shadow=0.0, autoselect=True, selectable=True, click_activate=True, text=score[2], h_align='left', v_align='center', scale=0.9) self._power_ranking_score_widgets.append(txt) ba.textwidget(edit=txt, on_activate_call=ba.Call(self._show_account_info, score[4], txt)) assert self._season_popup_menu is not None ba.widget(edit=txt, left_widget=self._season_popup_menu.get_button()) v2 -= 28
def _refresh(self) -> None: # pylint: disable=too-many-statements # pylint: disable=too-many-branches # pylint: disable=too-many-locals # pylint: disable=cyclic-import from bastd.ui import confirm account_state = _ba.get_v1_account_state() account_type = (_ba.get_v1_account_type() if account_state == 'signed_in' else 'unknown') is_google = account_type == 'Google Play' show_local_signed_in_as = False local_signed_in_as_space = 50.0 show_signed_in_as = self._signed_in signed_in_as_space = 95.0 show_sign_in_benefits = not self._signed_in sign_in_benefits_space = 80.0 show_signing_in_text = account_state == 'signing_in' signing_in_text_space = 80.0 show_google_play_sign_in_button = (account_state == 'signed_out' and 'Google Play' in self._show_sign_in_buttons) show_game_circle_sign_in_button = (account_state == 'signed_out' and 'Game Circle' in self._show_sign_in_buttons) show_device_sign_in_button = (account_state == 'signed_out' and 'Local' in self._show_sign_in_buttons) show_v2_sign_in_button = (account_state == 'signed_out' and 'V2' in self._show_sign_in_buttons) sign_in_button_space = 70.0 show_game_service_button = (self._signed_in and account_type in ['Game Center', 'Game Circle']) game_service_button_space = 60.0 show_linked_accounts_text = (self._signed_in and _ba.get_v1_account_misc_read_val( 'allowAccountLinking2', False)) linked_accounts_text_space = 60.0 show_achievements_button = ( self._signed_in and account_type in ('Google Play', 'Alibaba', 'Local', 'OUYA')) achievements_button_space = 60.0 show_achievements_text = (self._signed_in and not show_achievements_button) achievements_text_space = 27.0 show_leaderboards_button = (self._signed_in and is_google) leaderboards_button_space = 60.0 show_campaign_progress = self._signed_in campaign_progress_space = 27.0 show_tickets = self._signed_in tickets_space = 27.0 show_reset_progress_button = False reset_progress_button_space = 70.0 show_player_profiles_button = self._signed_in player_profiles_button_space = 100.0 show_link_accounts_button = (self._signed_in and _ba.get_v1_account_misc_read_val( 'allowAccountLinking2', False)) link_accounts_button_space = 70.0 show_unlink_accounts_button = show_link_accounts_button unlink_accounts_button_space = 90.0 show_sign_out_button = (self._signed_in and account_type in ['Local', 'Google Play', 'V2']) sign_out_button_space = 70.0 show_cancel_v2_sign_in_button = ( account_state == 'signing_in' and ba.app.accounts_v2.have_primary_credentials()) cancel_v2_sign_in_button_space = 70.0 if self._subcontainer is not None: self._subcontainer.delete() self._sub_height = 60.0 if show_local_signed_in_as: self._sub_height += local_signed_in_as_space if show_signed_in_as: self._sub_height += signed_in_as_space if show_signing_in_text: self._sub_height += signing_in_text_space if show_google_play_sign_in_button: self._sub_height += sign_in_button_space if show_game_circle_sign_in_button: self._sub_height += sign_in_button_space if show_device_sign_in_button: self._sub_height += sign_in_button_space if show_v2_sign_in_button: self._sub_height += sign_in_button_space if show_game_service_button: self._sub_height += game_service_button_space if show_linked_accounts_text: self._sub_height += linked_accounts_text_space if show_achievements_text: self._sub_height += achievements_text_space if show_achievements_button: self._sub_height += achievements_button_space if show_leaderboards_button: self._sub_height += leaderboards_button_space if show_campaign_progress: self._sub_height += campaign_progress_space if show_tickets: self._sub_height += tickets_space if show_sign_in_benefits: self._sub_height += sign_in_benefits_space if show_reset_progress_button: self._sub_height += reset_progress_button_space if show_player_profiles_button: self._sub_height += player_profiles_button_space if show_link_accounts_button: self._sub_height += link_accounts_button_space if show_unlink_accounts_button: self._sub_height += unlink_accounts_button_space if show_sign_out_button: self._sub_height += sign_out_button_space if show_cancel_v2_sign_in_button: self._sub_height += cancel_v2_sign_in_button_space self._subcontainer = ba.containerwidget(parent=self._scrollwidget, size=(self._sub_width, self._sub_height), background=False, claims_left_right=True, claims_tab=True, selection_loops_to_parent=True) first_selectable = None v = self._sub_height - 10.0 if show_local_signed_in_as: v -= local_signed_in_as_space * 0.6 ba.textwidget( parent=self._subcontainer, position=(self._sub_width * 0.5, v), size=(0, 0), text=ba.Lstr( resource='accountSettingsWindow.deviceSpecificAccountText', subs=[('${NAME}', _ba.get_v1_account_display_string())]), scale=0.7, color=(0.5, 0.5, 0.6), maxwidth=self._sub_width * 0.9, flatness=1.0, h_align='center', v_align='center') v -= local_signed_in_as_space * 0.4 self._account_name_text: Optional[ba.Widget] if show_signed_in_as: v -= signed_in_as_space * 0.2 txt = ba.Lstr( resource='accountSettingsWindow.youAreSignedInAsText', fallback_resource='accountSettingsWindow.youAreLoggedInAsText') ba.textwidget(parent=self._subcontainer, position=(self._sub_width * 0.5, v), size=(0, 0), text=txt, scale=0.9, color=ba.app.ui.title_color, maxwidth=self._sub_width * 0.9, h_align='center', v_align='center') v -= signed_in_as_space * 0.4 self._account_name_text = ba.textwidget( parent=self._subcontainer, position=(self._sub_width * 0.5, v), size=(0, 0), scale=1.5, maxwidth=self._sub_width * 0.9, res_scale=1.5, color=(1, 1, 1, 1), h_align='center', v_align='center') self._refresh_account_name_text() v -= signed_in_as_space * 0.4 else: self._account_name_text = None if self._back_button is None: bbtn = _ba.get_special_widget('back_button') else: bbtn = self._back_button if show_sign_in_benefits: v -= sign_in_benefits_space app = ba.app extra: Optional[Union[str, ba.Lstr]] if (app.platform in ['mac', 'ios'] and app.subplatform == 'appstore'): extra = ba.Lstr( value='\n${S}', subs=[('${S}', ba.Lstr(resource='signInWithGameCenterText'))]) else: extra = '' ba.textwidget(parent=self._subcontainer, position=(self._sub_width * 0.5, v + sign_in_benefits_space * 0.4), size=(0, 0), text=ba.Lstr(value='${A}${B}', subs=[('${A}', ba.Lstr(resource=self._r + '.signInInfoText')), ('${B}', extra)]), max_height=sign_in_benefits_space * 0.9, scale=0.9, color=(0.75, 0.7, 0.8), maxwidth=self._sub_width * 0.8, h_align='center', v_align='center') if show_signing_in_text: v -= signing_in_text_space ba.textwidget( parent=self._subcontainer, position=(self._sub_width * 0.5, v + signing_in_text_space * 0.5), size=(0, 0), text=ba.Lstr(resource='accountSettingsWindow.signingInText'), scale=0.9, color=(0, 1, 0), maxwidth=self._sub_width * 0.8, h_align='center', v_align='center') if show_google_play_sign_in_button: button_width = 350 v -= sign_in_button_space self._sign_in_google_play_button = btn = ba.buttonwidget( parent=self._subcontainer, position=((self._sub_width - button_width) * 0.5, v - 20), autoselect=True, size=(button_width, 60), label=ba.Lstr( value='${A}${B}', subs=[('${A}', ba.charstr(ba.SpecialChar.GOOGLE_PLAY_GAMES_LOGO)), ('${B}', ba.Lstr(resource=self._r + '.signInWithGooglePlayText'))]), on_activate_call=lambda: self._sign_in_press('Google Play')) if first_selectable is None: first_selectable = btn if ba.app.ui.use_toolbars: ba.widget(edit=btn, right_widget=_ba.get_special_widget('party_button')) ba.widget(edit=btn, left_widget=bbtn) ba.widget(edit=btn, show_buffer_bottom=40, show_buffer_top=100) self._sign_in_text = None if show_game_circle_sign_in_button: button_width = 350 v -= sign_in_button_space self._sign_in_game_circle_button = btn = ba.buttonwidget( parent=self._subcontainer, position=((self._sub_width - button_width) * 0.5, v - 20), autoselect=True, size=(button_width, 60), label=ba.Lstr(value='${A}${B}', subs=[('${A}', ba.charstr( ba.SpecialChar.GAME_CIRCLE_LOGO)), ('${B}', ba.Lstr(resource=self._r + '.signInWithGameCircleText'))]), on_activate_call=lambda: self._sign_in_press('Game Circle')) if first_selectable is None: first_selectable = btn if ba.app.ui.use_toolbars: ba.widget(edit=btn, right_widget=_ba.get_special_widget('party_button')) ba.widget(edit=btn, left_widget=bbtn) ba.widget(edit=btn, show_buffer_bottom=40, show_buffer_top=100) self._sign_in_text = None if show_v2_sign_in_button: button_width = 350 v -= sign_in_button_space self._sign_in_v2_button = btn = ba.buttonwidget( parent=self._subcontainer, position=((self._sub_width - button_width) * 0.5, v - 20), autoselect=True, size=(button_width, 60), label='', on_activate_call=self._v2_sign_in_press) ba.textwidget( parent=self._subcontainer, draw_controller=btn, h_align='center', v_align='center', size=(0, 0), position=(self._sub_width * 0.5, v + 17), text=ba.Lstr( value='${A}${B}', subs=[('${A}', ba.charstr(ba.SpecialChar.V2_LOGO)), ('${B}', ba.Lstr(resource=self._r + '.signInWithV2Text'))]), maxwidth=button_width * 0.8, color=(0.75, 1.0, 0.7)) ba.textwidget(parent=self._subcontainer, draw_controller=btn, h_align='center', v_align='center', size=(0, 0), position=(self._sub_width * 0.5, v - 4), text=ba.Lstr(resource=self._r + '.signInWithV2InfoText'), flatness=1.0, scale=0.57, maxwidth=button_width * 0.9, color=(0.55, 0.8, 0.5)) if first_selectable is None: first_selectable = btn if ba.app.ui.use_toolbars: ba.widget(edit=btn, right_widget=_ba.get_special_widget('party_button')) ba.widget(edit=btn, left_widget=bbtn) ba.widget(edit=btn, show_buffer_bottom=40, show_buffer_top=100) self._sign_in_text = None if show_device_sign_in_button: button_width = 350 v -= sign_in_button_space self._sign_in_device_button = btn = ba.buttonwidget( parent=self._subcontainer, position=((self._sub_width - button_width) * 0.5, v - 20), autoselect=True, size=(button_width, 60), label='', on_activate_call=lambda: self._sign_in_press('Local')) ba.textwidget(parent=self._subcontainer, draw_controller=btn, h_align='center', v_align='center', size=(0, 0), position=(self._sub_width * 0.5, v + 17), text=ba.Lstr( value='${A}${B}', subs=[('${A}', ba.charstr(ba.SpecialChar.LOCAL_ACCOUNT)), ('${B}', ba.Lstr(resource=self._r + '.signInWithDeviceText'))]), maxwidth=button_width * 0.8, color=(0.75, 1.0, 0.7)) ba.textwidget(parent=self._subcontainer, draw_controller=btn, h_align='center', v_align='center', size=(0, 0), position=(self._sub_width * 0.5, v - 4), text=ba.Lstr(resource=self._r + '.signInWithDeviceInfoText'), flatness=1.0, scale=0.57, maxwidth=button_width * 0.9, color=(0.55, 0.8, 0.5)) if first_selectable is None: first_selectable = btn if ba.app.ui.use_toolbars: ba.widget(edit=btn, right_widget=_ba.get_special_widget('party_button')) ba.widget(edit=btn, left_widget=bbtn) ba.widget(edit=btn, show_buffer_bottom=40, show_buffer_top=100) self._sign_in_text = None if show_player_profiles_button: button_width = 300 v -= player_profiles_button_space self._player_profiles_button = btn = ba.buttonwidget( parent=self._subcontainer, position=((self._sub_width - button_width) * 0.5, v + 30), autoselect=True, size=(button_width, 60), label=ba.Lstr(resource='playerProfilesWindow.titleText'), color=(0.55, 0.5, 0.6), icon=ba.gettexture('cuteSpaz'), textcolor=(0.75, 0.7, 0.8), on_activate_call=self._player_profiles_press) if first_selectable is None: first_selectable = btn if ba.app.ui.use_toolbars: ba.widget(edit=btn, right_widget=_ba.get_special_widget('party_button')) ba.widget(edit=btn, left_widget=bbtn, show_buffer_bottom=0) # the button to go to OS-Specific leaderboards/high-score-lists/etc. if show_game_service_button: button_width = 300 v -= game_service_button_space * 0.85 account_type = _ba.get_v1_account_type() if account_type == 'Game Center': account_type_name = ba.Lstr(resource='gameCenterText') elif account_type == 'Game Circle': account_type_name = ba.Lstr(resource='gameCircleText') else: raise ValueError("unknown account type: '" + str(account_type) + "'") self._game_service_button = btn = ba.buttonwidget( parent=self._subcontainer, position=((self._sub_width - button_width) * 0.5, v), color=(0.55, 0.5, 0.6), textcolor=(0.75, 0.7, 0.8), autoselect=True, on_activate_call=_ba.show_online_score_ui, size=(button_width, 50), label=account_type_name) if first_selectable is None: first_selectable = btn if ba.app.ui.use_toolbars: ba.widget(edit=btn, right_widget=_ba.get_special_widget('party_button')) ba.widget(edit=btn, left_widget=bbtn) v -= game_service_button_space * 0.15 else: self.game_service_button = None self._achievements_text: Optional[ba.Widget] if show_achievements_text: v -= achievements_text_space * 0.5 self._achievements_text = ba.textwidget( parent=self._subcontainer, position=(self._sub_width * 0.5, v), size=(0, 0), scale=0.9, color=(0.75, 0.7, 0.8), maxwidth=self._sub_width * 0.8, h_align='center', v_align='center') v -= achievements_text_space * 0.5 else: self._achievements_text = None self._achievements_button: Optional[ba.Widget] if show_achievements_button: button_width = 300 v -= achievements_button_space * 0.85 self._achievements_button = btn = ba.buttonwidget( parent=self._subcontainer, position=((self._sub_width - button_width) * 0.5, v), color=(0.55, 0.5, 0.6), textcolor=(0.75, 0.7, 0.8), autoselect=True, icon=ba.gettexture('googlePlayAchievementsIcon' if is_google else 'achievementsIcon'), icon_color=(0.8, 0.95, 0.7) if is_google else (0.85, 0.8, 0.9), on_activate_call=self._on_achievements_press, size=(button_width, 50), label='') if first_selectable is None: first_selectable = btn if ba.app.ui.use_toolbars: ba.widget(edit=btn, right_widget=_ba.get_special_widget('party_button')) ba.widget(edit=btn, left_widget=bbtn) v -= achievements_button_space * 0.15 else: self._achievements_button = None if show_achievements_text or show_achievements_button: self._refresh_achievements() self._leaderboards_button: Optional[ba.Widget] if show_leaderboards_button: button_width = 300 v -= leaderboards_button_space * 0.85 self._leaderboards_button = btn = ba.buttonwidget( parent=self._subcontainer, position=((self._sub_width - button_width) * 0.5, v), color=(0.55, 0.5, 0.6), textcolor=(0.75, 0.7, 0.8), autoselect=True, icon=ba.gettexture('googlePlayLeaderboardsIcon'), icon_color=(0.8, 0.95, 0.7), on_activate_call=self._on_leaderboards_press, size=(button_width, 50), label=ba.Lstr(resource='leaderboardsText')) if first_selectable is None: first_selectable = btn if ba.app.ui.use_toolbars: ba.widget(edit=btn, right_widget=_ba.get_special_widget('party_button')) ba.widget(edit=btn, left_widget=bbtn) v -= leaderboards_button_space * 0.15 else: self._leaderboards_button = None self._campaign_progress_text: Optional[ba.Widget] if show_campaign_progress: v -= campaign_progress_space * 0.5 self._campaign_progress_text = ba.textwidget( parent=self._subcontainer, position=(self._sub_width * 0.5, v), size=(0, 0), scale=0.9, color=(0.75, 0.7, 0.8), maxwidth=self._sub_width * 0.8, h_align='center', v_align='center') v -= campaign_progress_space * 0.5 self._refresh_campaign_progress_text() else: self._campaign_progress_text = None self._tickets_text: Optional[ba.Widget] if show_tickets: v -= tickets_space * 0.5 self._tickets_text = ba.textwidget(parent=self._subcontainer, position=(self._sub_width * 0.5, v), size=(0, 0), scale=0.9, color=(0.75, 0.7, 0.8), maxwidth=self._sub_width * 0.8, flatness=1.0, h_align='center', v_align='center') v -= tickets_space * 0.5 self._refresh_tickets_text() else: self._tickets_text = None # bit of spacing before the reset/sign-out section v -= 5 button_width = 250 if show_reset_progress_button: confirm_text = (ba.Lstr(resource=self._r + '.resetProgressConfirmText') if self._can_reset_achievements else ba.Lstr( resource=self._r + '.resetProgressConfirmNoAchievementsText')) v -= reset_progress_button_space self._reset_progress_button = btn = ba.buttonwidget( parent=self._subcontainer, position=((self._sub_width - button_width) * 0.5, v), color=(0.55, 0.5, 0.6), textcolor=(0.75, 0.7, 0.8), autoselect=True, size=(button_width, 60), label=ba.Lstr(resource=self._r + '.resetProgressText'), on_activate_call=lambda: confirm.ConfirmWindow( text=confirm_text, width=500, height=200, action=self._reset_progress)) if first_selectable is None: first_selectable = btn if ba.app.ui.use_toolbars: ba.widget(edit=btn, right_widget=_ba.get_special_widget('party_button')) ba.widget(edit=btn, left_widget=bbtn) self._linked_accounts_text: Optional[ba.Widget] if show_linked_accounts_text: v -= linked_accounts_text_space * 0.8 self._linked_accounts_text = ba.textwidget( parent=self._subcontainer, position=(self._sub_width * 0.5, v), size=(0, 0), scale=0.9, color=(0.75, 0.7, 0.8), maxwidth=self._sub_width * 0.95, h_align='center', v_align='center') v -= linked_accounts_text_space * 0.2 self._update_linked_accounts_text() else: self._linked_accounts_text = None if show_link_accounts_button: v -= link_accounts_button_space self._link_accounts_button = btn = ba.buttonwidget( parent=self._subcontainer, position=((self._sub_width - button_width) * 0.5, v), autoselect=True, size=(button_width, 60), label='', color=(0.55, 0.5, 0.6), on_activate_call=self._link_accounts_press) ba.textwidget(parent=self._subcontainer, draw_controller=btn, h_align='center', v_align='center', size=(0, 0), position=(self._sub_width * 0.5, v + 17 + 20), text=ba.Lstr(resource=self._r + '.linkAccountsText'), maxwidth=button_width * 0.8, color=(0.75, 0.7, 0.8)) ba.textwidget(parent=self._subcontainer, draw_controller=btn, h_align='center', v_align='center', size=(0, 0), position=(self._sub_width * 0.5, v - 4 + 20), text=ba.Lstr(resource=self._r + '.linkAccountsInfoText'), flatness=1.0, scale=0.5, maxwidth=button_width * 0.8, color=(0.75, 0.7, 0.8)) if first_selectable is None: first_selectable = btn if ba.app.ui.use_toolbars: ba.widget(edit=btn, right_widget=_ba.get_special_widget('party_button')) ba.widget(edit=btn, left_widget=bbtn, show_buffer_bottom=50) self._unlink_accounts_button: Optional[ba.Widget] if show_unlink_accounts_button: v -= unlink_accounts_button_space self._unlink_accounts_button = btn = ba.buttonwidget( parent=self._subcontainer, position=((self._sub_width - button_width) * 0.5, v + 25), autoselect=True, size=(button_width, 60), label='', color=(0.55, 0.5, 0.6), on_activate_call=self._unlink_accounts_press) self._unlink_accounts_button_label = ba.textwidget( parent=self._subcontainer, draw_controller=btn, h_align='center', v_align='center', size=(0, 0), position=(self._sub_width * 0.5, v + 55), text=ba.Lstr(resource=self._r + '.unlinkAccountsText'), maxwidth=button_width * 0.8, color=(0.75, 0.7, 0.8)) if first_selectable is None: first_selectable = btn if ba.app.ui.use_toolbars: ba.widget(edit=btn, right_widget=_ba.get_special_widget('party_button')) ba.widget(edit=btn, left_widget=bbtn, show_buffer_bottom=50) self._update_unlink_accounts_button() else: self._unlink_accounts_button = None if show_sign_out_button: v -= sign_out_button_space self._sign_out_button = btn = ba.buttonwidget( parent=self._subcontainer, position=((self._sub_width - button_width) * 0.5, v), size=(button_width, 60), label=ba.Lstr(resource=self._r + '.signOutText'), color=(0.55, 0.5, 0.6), textcolor=(0.75, 0.7, 0.8), autoselect=True, on_activate_call=self._sign_out_press) if first_selectable is None: first_selectable = btn if ba.app.ui.use_toolbars: ba.widget(edit=btn, right_widget=_ba.get_special_widget('party_button')) ba.widget(edit=btn, left_widget=bbtn, show_buffer_bottom=15) if show_cancel_v2_sign_in_button: v -= cancel_v2_sign_in_button_space self._cancel_v2_sign_in_button = btn = ba.buttonwidget( parent=self._subcontainer, position=((self._sub_width - button_width) * 0.5, v), size=(button_width, 60), label=ba.Lstr(resource='cancelText'), color=(0.55, 0.5, 0.6), textcolor=(0.75, 0.7, 0.8), autoselect=True, on_activate_call=self._cancel_v2_sign_in_press) if first_selectable is None: first_selectable = btn if ba.app.ui.use_toolbars: ba.widget(edit=btn, right_widget=_ba.get_special_widget('party_button')) ba.widget(edit=btn, left_widget=bbtn, show_buffer_bottom=15) # Whatever the topmost selectable thing is, we want it to scroll all # the way up when we select it. if first_selectable is not None: ba.widget(edit=first_selectable, up_widget=bbtn, show_buffer_top=400) # (this should re-scroll us to the top..) ba.containerwidget(edit=self._subcontainer, visible_child=first_selectable) self._needs_refresh = False
def update(self, index: int, party: PartyEntry, sub_scroll_width: float, sub_scroll_height: float, lineheight: float, columnwidget: ba.Widget, join_text: ba.Widget, filter_text: ba.Widget, existing_selection: Optional[Selection], tab: PublicGatherTab) -> None: """Update for the given data.""" # pylint: disable=too-many-locals # Quick-out: if we've been marked clean for a certain index and # we're still at that index, we're done. if party.clean_display_index == index: return ping_good = _ba.get_v1_account_misc_read_val('pingGood', 100) ping_med = _ba.get_v1_account_misc_read_val('pingMed', 500) self._clear() hpos = 20 vpos = sub_scroll_height - lineheight * index - 50 self._name_widget = ba.textwidget( text=ba.Lstr(value=party.name), parent=columnwidget, size=(sub_scroll_width * 0.63, 20), position=(0 + hpos, 4 + vpos), selectable=True, on_select_call=ba.WeakCall( tab.set_public_party_selection, Selection(party.get_key(), SelectionComponent.NAME)), on_activate_call=ba.WeakCall(tab.on_public_party_activate, party), click_activate=True, maxwidth=sub_scroll_width * 0.45, corner_scale=1.4, autoselect=True, color=(1, 1, 1, 0.3 if party.ping is None else 1.0), h_align='left', v_align='center') ba.widget(edit=self._name_widget, left_widget=join_text, show_buffer_top=64.0, show_buffer_bottom=64.0) if existing_selection == Selection(party.get_key(), SelectionComponent.NAME): ba.containerwidget(edit=columnwidget, selected_child=self._name_widget) if party.stats_addr: url = party.stats_addr.replace( '${ACCOUNT}', _ba.get_v1_account_misc_read_val_2('resolvedAccountID', 'UNKNOWN')) self._stats_button = ba.buttonwidget( color=(0.3, 0.6, 0.94), textcolor=(1.0, 1.0, 1.0), label=ba.Lstr(resource='statsText'), parent=columnwidget, autoselect=True, on_activate_call=ba.Call(ba.open_url, url), on_select_call=ba.WeakCall( tab.set_public_party_selection, Selection(party.get_key(), SelectionComponent.STATS_BUTTON)), size=(120, 40), position=(sub_scroll_width * 0.66 + hpos, 1 + vpos), scale=0.9) if existing_selection == Selection( party.get_key(), SelectionComponent.STATS_BUTTON): ba.containerwidget(edit=columnwidget, selected_child=self._stats_button) self._size_widget = ba.textwidget( text=str(party.size) + '/' + str(party.size_max), parent=columnwidget, size=(0, 0), position=(sub_scroll_width * 0.86 + hpos, 20 + vpos), scale=0.7, color=(0.8, 0.8, 0.8), h_align='right', v_align='center') if index == 0: ba.widget(edit=self._name_widget, up_widget=filter_text) if self._stats_button: ba.widget(edit=self._stats_button, up_widget=filter_text) self._ping_widget = ba.textwidget(parent=columnwidget, size=(0, 0), position=(sub_scroll_width * 0.94 + hpos, 20 + vpos), scale=0.7, h_align='right', v_align='center') if party.ping is None: ba.textwidget(edit=self._ping_widget, text='-', color=(0.5, 0.5, 0.5)) else: ba.textwidget(edit=self._ping_widget, text=str(int(party.ping)), color=(0, 1, 0) if party.ping <= ping_good else (1, 1, 0) if party.ping <= ping_med else (1, 0, 0)) party.clean_display_index = index
def __init__(self, items: list[str], transition: str = 'in_right', header_text: ba.Lstr = None): from ba.internal import get_store_item_display_size from bastd.ui.store import item as storeitemui if header_text is None: header_text = ba.Lstr(resource='unlockThisText', fallback_resource='unlockThisInTheStoreText') if len(items) != 1: raise ValueError('expected exactly 1 item') self._items = list(items) self._width = 580 self._height = 520 uiscale = ba.app.ui.uiscale super().__init__(root_widget=ba.containerwidget( size=(self._width, self._height), transition=transition, toolbar_visibility='menu_currency', scale=(1.2 if uiscale is ba.UIScale.SMALL else 1.1 if uiscale is ba.UIScale.MEDIUM else 1.0), stack_offset=(0, -15) if uiscale is ba.UIScale.SMALL else (0, 0))) self._is_double = False self._title_text = ba.textwidget(parent=self._root_widget, position=(self._width * 0.5, self._height - 30), size=(0, 0), text=header_text, h_align='center', v_align='center', maxwidth=self._width * 0.9 - 120, scale=1.2, color=(1, 0.8, 0.3, 1)) size = get_store_item_display_size(items[0]) display: dict[str, Any] = {} storeitemui.instantiate_store_item_display( items[0], display, parent_widget=self._root_widget, b_pos=(self._width * 0.5 - size[0] * 0.5 + 10 - ((size[0] * 0.5 + 30) if self._is_double else 0), self._height * 0.5 - size[1] * 0.5 + 30 + (20 if self._is_double else 0)), b_width=size[0], b_height=size[1], button=False) # Wire up the parts we need. if self._is_double: pass # not working else: if self._items == ['pro']: price_str = _ba.get_price(self._items[0]) pyoffs = -15 else: pyoffs = 0 price = self._price = _ba.get_v1_account_misc_read_val( 'price.' + str(items[0]), -1) price_str = ba.charstr(ba.SpecialChar.TICKET) + str(price) self._price_text = ba.textwidget(parent=self._root_widget, position=(self._width * 0.5, 150 + pyoffs), size=(0, 0), text=price_str, h_align='center', v_align='center', maxwidth=self._width * 0.9, scale=1.4, color=(0.2, 1, 0.2)) self._update_timer = ba.Timer(1.0, ba.WeakCall(self._update), timetype=ba.TimeType.REAL, repeat=True) self._cancel_button = ba.buttonwidget( parent=self._root_widget, position=(50, 40), size=(150, 60), scale=1.0, on_activate_call=self._cancel, autoselect=True, label=ba.Lstr(resource='cancelText')) self._purchase_button = ba.buttonwidget( parent=self._root_widget, position=(self._width - 200, 40), size=(150, 60), scale=1.0, on_activate_call=self._purchase, autoselect=True, label=ba.Lstr(resource='store.purchaseText')) ba.containerwidget(edit=self._root_widget, cancel_button=self._cancel_button, start_button=self._purchase_button, selected_child=self._purchase_button)
def instantiate_store_item_display(item_name: str, item: dict[str, Any], parent_widget: ba.Widget, b_pos: tuple[float, float], b_width: float, b_height: float, boffs_h: float = 0.0, boffs_h2: float = 0.0, boffs_v2: float = 0, delay: float = 0.0, button: bool = True) -> None: """(internal)""" # pylint: disable=too-many-statements # pylint: disable=too-many-branches # pylint: disable=too-many-locals from ba.internal import (get_store_item, get_store_item_name_translated, get_clean_price) del boffs_h # unused arg del boffs_h2 # unused arg del boffs_v2 # unused arg item_info = get_store_item(item_name) title_v = 0.24 price_v = 0.145 base_text_scale = 1.0 item['name'] = title = get_store_item_name_translated(item_name) btn: Optional[ba.Widget] if button: item['button'] = btn = ba.buttonwidget(parent=parent_widget, position=b_pos, transition_delay=delay, show_buffer_top=76.0, enable_sound=False, button_type='square', size=(b_width, b_height), autoselect=True, label='') ba.widget(edit=btn, show_buffer_bottom=76.0) else: btn = None b_offs_x = -0.015 * b_width check_pos = 0.76 icon_tex = None tint_tex = None tint_color = None tint2_color = None tex_name: Optional[str] = None desc: Optional[str] = None modes: Optional[ba.Lstr] = None if item_name.startswith('characters.'): character = ba.app.spaz_appearances[item_info['character']] tint_color = ( item_info['color'] if 'color' in item_info else character.default_color if character.default_color is not None else (1, 1, 1)) tint2_color = (item_info['highlight'] if 'highlight' in item_info else character.default_highlight if character.default_highlight is not None else (1, 1, 1)) icon_tex = character.icon_texture tint_tex = character.icon_mask_texture title_v = 0.255 price_v = 0.145 elif item_name in ['upgrades.pro', 'pro']: base_text_scale = 0.6 title_v = 0.85 price_v = 0.15 elif item_name.startswith('maps.'): map_type = item_info['map_type'] tex_name = map_type.get_preview_texture_name() title_v = 0.312 price_v = 0.17 elif item_name.startswith('games.'): gametype = item_info['gametype'] modes_l = [] if gametype.supports_session_type(ba.CoopSession): modes_l.append(ba.Lstr(resource='playModes.coopText')) if gametype.supports_session_type(ba.DualTeamSession): modes_l.append(ba.Lstr(resource='playModes.teamsText')) if gametype.supports_session_type(ba.FreeForAllSession): modes_l.append(ba.Lstr(resource='playModes.freeForAllText')) if len(modes_l) == 3: modes = ba.Lstr(value='${A}, ${B}, ${C}', subs=[('${A}', modes_l[0]), ('${B}', modes_l[1]), ('${C}', modes_l[2])]) elif len(modes_l) == 2: modes = ba.Lstr(value='${A}, ${B}', subs=[('${A}', modes_l[0]), ('${B}', modes_l[1])]) elif len(modes_l) == 1: modes = modes_l[0] else: raise Exception() desc = gametype.get_description_display_string(ba.CoopSession) tex_name = item_info['previewTex'] base_text_scale = 0.8 title_v = 0.48 price_v = 0.17 elif item_name.startswith('icons.'): base_text_scale = 1.5 price_v = 0.2 check_pos = 0.6 if item_name.startswith('characters.'): frame_size = b_width * 0.7 im_dim = frame_size * (100.0 / 113.0) im_pos = (b_pos[0] + b_width * 0.5 - im_dim * 0.5 + b_offs_x, b_pos[1] + b_height * 0.57 - im_dim * 0.5) mask_texture = ba.gettexture('characterIconMask') assert icon_tex is not None assert tint_tex is not None ba.imagewidget(parent=parent_widget, position=im_pos, size=(im_dim, im_dim), color=(1, 1, 1), transition_delay=delay, mask_texture=mask_texture, draw_controller=btn, texture=ba.gettexture(icon_tex), tint_texture=ba.gettexture(tint_tex), tint_color=tint_color, tint2_color=tint2_color) if item_name in ['pro', 'upgrades.pro']: frame_size = b_width * 0.5 im_dim = frame_size * (100.0 / 113.0) im_pos = (b_pos[0] + b_width * 0.5 - im_dim * 0.5 + b_offs_x, b_pos[1] + b_height * 0.5 - im_dim * 0.5) ba.imagewidget(parent=parent_widget, position=im_pos, size=(im_dim, im_dim), transition_delay=delay, draw_controller=btn, color=(0.3, 0.0, 0.3), opacity=0.3, texture=ba.gettexture('logo')) txt = ba.Lstr(resource='store.bombSquadProNewDescriptionText') # t = 'foo\nfoo\nfoo\nfoo\nfoo\nfoo' item['descriptionText'] = ba.textwidget( parent=parent_widget, text=txt, position=(b_pos[0] + b_width * 0.5, b_pos[1] + b_height * 0.69), transition_delay=delay, scale=b_width * (1.0 / 230.0) * base_text_scale * 0.75, maxwidth=b_width * 0.75, max_height=b_height * 0.2, size=(0, 0), h_align='center', v_align='center', draw_controller=btn, color=(0.3, 1, 0.3)) extra_backings = item['extra_backings'] = [] extra_images = item['extra_images'] = [] extra_texts = item['extra_texts'] = [] extra_texts_2 = item['extra_texts_2'] = [] backing_color = (0.5, 0.8, 0.3) if button else (0.6, 0.5, 0.65) b_square_texture = ba.gettexture('buttonSquare') char_mask_texture = ba.gettexture('characterIconMask') pos = (0.17, 0.43) tile_size = (b_width * 0.16 * 1.2, b_width * 0.2 * 1.2) tile_pos = (b_pos[0] + b_width * pos[0], b_pos[1] + b_height * pos[1]) extra_backings.append( ba.imagewidget(parent=parent_widget, position=(tile_pos[0] - tile_size[0] * 0.5, tile_pos[1] - tile_size[1] * 0.5), size=tile_size, transition_delay=delay, draw_controller=btn, color=backing_color, texture=b_square_texture)) im_size = tile_size[0] * 0.8 extra_images.append( ba.imagewidget(parent=parent_widget, position=(tile_pos[0] - im_size * 0.5, tile_pos[1] - im_size * 0.4), size=(im_size, im_size), transition_delay=delay, draw_controller=btn, color=(1, 1, 1), texture=ba.gettexture('ticketsMore'))) bonus_tickets = str( _ba.get_v1_account_misc_read_val('proBonusTickets', 100)) extra_texts.append( ba.textwidget(parent=parent_widget, draw_controller=btn, position=(tile_pos[0] - tile_size[0] * 0.03, tile_pos[1] - tile_size[1] * 0.25), size=(0, 0), color=(0.6, 1, 0.6), transition_delay=delay, h_align='center', v_align='center', maxwidth=tile_size[0] * 0.7, scale=0.55, text=ba.Lstr(resource='getTicketsWindow.ticketsText', subs=[('${COUNT}', bonus_tickets)]), flatness=1.0, shadow=0.0)) for charname, pos in [('Kronk', (0.32, 0.45)), ('Zoe', (0.425, 0.4)), ('Jack Morgan', (0.555, 0.45)), ('Mel', (0.645, 0.4))]: tile_size = (b_width * 0.16 * 0.9, b_width * 0.2 * 0.9) tile_pos = (b_pos[0] + b_width * pos[0], b_pos[1] + b_height * pos[1]) character = ba.app.spaz_appearances[charname] extra_backings.append( ba.imagewidget(parent=parent_widget, position=(tile_pos[0] - tile_size[0] * 0.5, tile_pos[1] - tile_size[1] * 0.5), size=tile_size, transition_delay=delay, draw_controller=btn, color=backing_color, texture=b_square_texture)) im_size = tile_size[0] * 0.7 extra_images.append( ba.imagewidget(parent=parent_widget, position=(tile_pos[0] - im_size * 0.53, tile_pos[1] - im_size * 0.35), size=(im_size, im_size), transition_delay=delay, draw_controller=btn, color=(1, 1, 1), texture=ba.gettexture(character.icon_texture), tint_texture=ba.gettexture( character.icon_mask_texture), tint_color=character.default_color, tint2_color=character.default_highlight, mask_texture=char_mask_texture)) extra_texts.append( ba.textwidget(parent=parent_widget, draw_controller=btn, position=(tile_pos[0] - im_size * 0.03, tile_pos[1] - im_size * 0.51), size=(0, 0), color=(0.6, 1, 0.6), transition_delay=delay, h_align='center', v_align='center', maxwidth=tile_size[0] * 0.7, scale=0.55, text=ba.Lstr(translate=('characterNames', charname)), flatness=1.0, shadow=0.0)) # If we have a 'total-worth' item-id for this id, show that price so # the user knows how much this is worth. total_worth_item = _ba.get_v1_account_misc_read_val('twrths', {}).get(item_name) total_worth_price: Optional[str] if total_worth_item is not None: price = _ba.get_price(total_worth_item) total_worth_price = (get_clean_price(price) if price is not None else '??') else: total_worth_price = None if total_worth_price is not None: total_worth_text = ba.Lstr(resource='store.totalWorthText', subs=[('${TOTAL_WORTH}', total_worth_price)]) extra_texts_2.append( ba.textwidget(parent=parent_widget, text=total_worth_text, position=(b_pos[0] + b_width * 0.5 + b_offs_x, b_pos[1] + b_height * 0.25), transition_delay=delay, scale=b_width * (1.0 / 230.0) * base_text_scale * 0.45, maxwidth=b_width * 0.5, size=(0, 0), h_align='center', v_align='center', shadow=1.0, flatness=1.0, draw_controller=btn, color=(0.3, 1, 1))) model_opaque = ba.getmodel('level_select_button_opaque') model_transparent = ba.getmodel('level_select_button_transparent') mask_tex = ba.gettexture('mapPreviewMask') for levelname, preview_tex_name, pos in [ ('Infinite Onslaught', 'doomShroomPreview', (0.80, 0.48)), ('Infinite Runaround', 'towerDPreview', (0.80, 0.32)) ]: tile_size = (b_width * 0.2, b_width * 0.13) tile_pos = (b_pos[0] + b_width * pos[0], b_pos[1] + b_height * pos[1]) im_size = tile_size[0] * 0.8 extra_backings.append( ba.imagewidget(parent=parent_widget, position=(tile_pos[0] - tile_size[0] * 0.5, tile_pos[1] - tile_size[1] * 0.5), size=tile_size, transition_delay=delay, draw_controller=btn, color=backing_color, texture=b_square_texture)) # Hack - gotta draw two transparent versions to avoid z issues. for mod in model_opaque, model_transparent: extra_images.append( ba.imagewidget(parent=parent_widget, position=(tile_pos[0] - im_size * 0.52, tile_pos[1] - im_size * 0.2), size=(im_size, im_size * 0.5), transition_delay=delay, model_transparent=mod, mask_texture=mask_tex, draw_controller=btn, texture=ba.gettexture(preview_tex_name))) extra_texts.append( ba.textwidget(parent=parent_widget, draw_controller=btn, position=(tile_pos[0] - im_size * 0.03, tile_pos[1] - im_size * 0.2), size=(0, 0), color=(0.6, 1, 0.6), transition_delay=delay, h_align='center', v_align='center', maxwidth=tile_size[0] * 0.7, scale=0.55, text=ba.Lstr(translate=('coopLevelNames', levelname)), flatness=1.0, shadow=0.0)) if item_name.startswith('icons.'): item['icon_text'] = ba.textwidget( parent=parent_widget, text=item_info['icon'], position=(b_pos[0] + b_width * 0.5, b_pos[1] + b_height * 0.5), transition_delay=delay, scale=b_width * (1.0 / 230.0) * base_text_scale * 2.0, maxwidth=b_width * 0.9, max_height=b_height * 0.9, size=(0, 0), h_align='center', v_align='center', draw_controller=btn) if item_name.startswith('maps.'): frame_size = b_width * 0.9 im_dim = frame_size * (100.0 / 113.0) im_pos = (b_pos[0] + b_width * 0.5 - im_dim * 0.5 + b_offs_x, b_pos[1] + b_height * 0.62 - im_dim * 0.25) model_opaque = ba.getmodel('level_select_button_opaque') model_transparent = ba.getmodel('level_select_button_transparent') mask_tex = ba.gettexture('mapPreviewMask') assert tex_name is not None ba.imagewidget(parent=parent_widget, position=im_pos, size=(im_dim, im_dim * 0.5), transition_delay=delay, model_opaque=model_opaque, model_transparent=model_transparent, mask_texture=mask_tex, draw_controller=btn, texture=ba.gettexture(tex_name)) if item_name.startswith('games.'): frame_size = b_width * 0.8 im_dim = frame_size * (100.0 / 113.0) im_pos = (b_pos[0] + b_width * 0.5 - im_dim * 0.5 + b_offs_x, b_pos[1] + b_height * 0.72 - im_dim * 0.25) model_opaque = ba.getmodel('level_select_button_opaque') model_transparent = ba.getmodel('level_select_button_transparent') mask_tex = ba.gettexture('mapPreviewMask') assert tex_name is not None ba.imagewidget(parent=parent_widget, position=im_pos, size=(im_dim, im_dim * 0.5), transition_delay=delay, model_opaque=model_opaque, model_transparent=model_transparent, mask_texture=mask_tex, draw_controller=btn, texture=ba.gettexture(tex_name)) item['descriptionText'] = ba.textwidget( parent=parent_widget, text=desc, position=(b_pos[0] + b_width * 0.5, b_pos[1] + b_height * 0.36), transition_delay=delay, scale=b_width * (1.0 / 230.0) * base_text_scale * 0.78, maxwidth=b_width * 0.8, max_height=b_height * 0.14, size=(0, 0), h_align='center', v_align='center', draw_controller=btn, flatness=1.0, shadow=0.0, color=(0.6, 1, 0.6)) item['gameModesText'] = ba.textwidget( parent=parent_widget, text=modes, position=(b_pos[0] + b_width * 0.5, b_pos[1] + b_height * 0.26), transition_delay=delay, scale=b_width * (1.0 / 230.0) * base_text_scale * 0.65, maxwidth=b_width * 0.8, size=(0, 0), h_align='center', v_align='center', draw_controller=btn, shadow=0, flatness=1.0, color=(0.6, 0.8, 0.6)) if not item_name.startswith('icons.'): item['title_text'] = ba.textwidget( parent=parent_widget, text=title, position=(b_pos[0] + b_width * 0.5 + b_offs_x, b_pos[1] + b_height * title_v), transition_delay=delay, scale=b_width * (1.0 / 230.0) * base_text_scale, maxwidth=b_width * 0.8, size=(0, 0), h_align='center', v_align='center', draw_controller=btn, color=(0.7, 0.9, 0.7, 1.0)) item['purchase_check'] = ba.imagewidget( parent=parent_widget, position=(b_pos[0] + b_width * check_pos, b_pos[1] + b_height * 0.05), transition_delay=delay, model_transparent=ba.getmodel('checkTransparent'), opacity=0.0, size=(60, 60), color=(0.6, 0.5, 0.8), draw_controller=btn, texture=ba.gettexture('uiAtlas')) item['price_widget'] = ba.textwidget( parent=parent_widget, text='', position=(b_pos[0] + b_width * 0.5 + b_offs_x, b_pos[1] + b_height * price_v), transition_delay=delay, scale=b_width * (1.0 / 300.0) * base_text_scale, maxwidth=b_width * 0.9, size=(0, 0), h_align='center', v_align='center', draw_controller=btn, color=(0.2, 1, 0.2, 1.0)) item['price_widget_left'] = ba.textwidget( parent=parent_widget, text='', position=(b_pos[0] + b_width * 0.33 + b_offs_x, b_pos[1] + b_height * price_v), transition_delay=delay, scale=b_width * (1.0 / 300.0) * base_text_scale, maxwidth=b_width * 0.3, size=(0, 0), h_align='center', v_align='center', draw_controller=btn, color=(0.2, 1, 0.2, 0.5)) item['price_widget_right'] = ba.textwidget( parent=parent_widget, text='', position=(b_pos[0] + b_width * 0.66 + b_offs_x, b_pos[1] + b_height * price_v), transition_delay=delay, scale=1.1 * b_width * (1.0 / 300.0) * base_text_scale, maxwidth=b_width * 0.3, size=(0, 0), h_align='center', v_align='center', draw_controller=btn, color=(0.2, 1, 0.2, 1.0)) item['price_slash_widget'] = ba.imagewidget( parent=parent_widget, position=(b_pos[0] + b_width * 0.33 + b_offs_x - 36, b_pos[1] + b_height * price_v - 35), transition_delay=delay, texture=ba.gettexture('slash'), opacity=0.0, size=(70, 70), draw_controller=btn, color=(1, 0, 0)) badge_rad = 44 badge_center = (b_pos[0] + b_width * 0.1 + b_offs_x, b_pos[1] + b_height * 0.87) item['sale_bg_widget'] = ba.imagewidget( parent=parent_widget, position=(badge_center[0] - badge_rad, badge_center[1] - badge_rad), opacity=0.0, transition_delay=delay, texture=ba.gettexture('circleZigZag'), draw_controller=btn, size=(badge_rad * 2, badge_rad * 2), color=(0.5, 0, 1)) item['sale_title_widget'] = ba.textwidget(parent=parent_widget, position=(badge_center[0], badge_center[1] + 12), transition_delay=delay, scale=1.0, maxwidth=badge_rad * 1.6, size=(0, 0), h_align='center', v_align='center', draw_controller=btn, shadow=0.0, flatness=1.0, color=(0, 1, 0)) item['sale_time_widget'] = ba.textwidget(parent=parent_widget, position=(badge_center[0], badge_center[1] - 12), transition_delay=delay, scale=0.7, maxwidth=badge_rad * 1.6, size=(0, 0), h_align='center', v_align='center', draw_controller=btn, shadow=0.0, flatness=1.0, color=(0.0, 1, 0.0, 1))
def __init__(self, origin_widget: ba.Widget = None): scale_origin: Optional[tuple[float, float]] if origin_widget is not None: self._transition_out = 'out_scale' scale_origin = origin_widget.get_screen_space_center() transition = 'in_scale' else: self._transition_out = 'out_right' scale_origin = None transition = 'in_right' bg_color = (0.4, 0.4, 0.5) self._width = 560 self._height = 420 uiscale = ba.app.ui.uiscale base_scale = (1.65 if uiscale is ba.UIScale.SMALL else 1.5 if uiscale is ba.UIScale.MEDIUM else 1.1) super().__init__(root_widget=ba.containerwidget( size=(self._width, self._height), transition=transition, scale=base_scale, scale_origin_stack_offset=scale_origin, stack_offset=(0, -10) if uiscale is ba.UIScale.SMALL else (0, 0))) self._cancel_button = ba.buttonwidget(parent=self._root_widget, position=(40, self._height - 45), size=(50, 50), scale=0.7, label='', color=bg_color, on_activate_call=self._cancel, autoselect=True, icon=ba.gettexture('crossOut'), iconscale=1.2) maxlinks = _ba.get_v1_account_misc_read_val('maxLinkAccounts', 5) ba.textwidget( parent=self._root_widget, position=(self._width * 0.5, self._height * 0.56), size=(0, 0), text=ba.Lstr(resource=( 'accountSettingsWindow.linkAccountsInstructionsNewText'), subs=[('${COUNT}', str(maxlinks))]), maxwidth=self._width * 0.9, color=ba.app.ui.infotextcolor, max_height=self._height * 0.6, h_align='center', v_align='center') ba.containerwidget(edit=self._root_widget, cancel_button=self._cancel_button) ba.buttonwidget( parent=self._root_widget, position=(40, 30), size=(200, 60), label=ba.Lstr( resource='accountSettingsWindow.linkAccountsGenerateCodeText'), autoselect=True, on_activate_call=self._generate_press) self._enter_code_button = ba.buttonwidget( parent=self._root_widget, position=(self._width - 240, 30), size=(200, 60), label=ba.Lstr( resource='accountSettingsWindow.linkAccountsEnterCodeText'), autoselect=True, on_activate_call=self._enter_code_press)
def __init__(self, transition: str = 'in_right', modal: bool = False, origin_widget: ba.Widget = None, close_once_signed_in: bool = False): # pylint: disable=too-many-statements self._sign_in_game_circle_button: Optional[ba.Widget] = None self._sign_in_v2_button: Optional[ba.Widget] = None self._sign_in_device_button: Optional[ba.Widget] = None self._close_once_signed_in = close_once_signed_in ba.set_analytics_screen('Account Window') # If they provided an origin-widget, scale up from that. scale_origin: Optional[tuple[float, float]] if origin_widget is not None: self._transition_out = 'out_scale' scale_origin = origin_widget.get_screen_space_center() transition = 'in_scale' else: self._transition_out = 'out_right' scale_origin = None self._r = 'accountSettingsWindow' self._modal = modal self._needs_refresh = False self._signed_in = (_ba.get_v1_account_state() == 'signed_in') self._account_state_num = _ba.get_v1_account_state_num() self._show_linked = (self._signed_in and _ba.get_v1_account_misc_read_val( 'allowAccountLinking2', False)) self._check_sign_in_timer = ba.Timer(1.0, ba.WeakCall(self._update), timetype=ba.TimeType.REAL, repeat=True) # Currently we can only reset achievements on game-center. account_type: Optional[str] if self._signed_in: account_type = _ba.get_v1_account_type() else: account_type = None self._can_reset_achievements = (account_type == 'Game Center') app = ba.app uiscale = app.ui.uiscale self._width = 760 if uiscale is ba.UIScale.SMALL else 660 x_offs = 50 if uiscale is ba.UIScale.SMALL else 0 self._height = (390 if uiscale is ba.UIScale.SMALL else 430 if uiscale is ba.UIScale.MEDIUM else 490) self._sign_in_button = None self._sign_in_text = None self._scroll_width = self._width - (100 + x_offs * 2) self._scroll_height = self._height - 120 self._sub_width = self._scroll_width - 20 # Determine which sign-in/sign-out buttons we should show. self._show_sign_in_buttons: list[str] = [] if app.platform == 'android' and app.subplatform == 'google': self._show_sign_in_buttons.append('Google Play') elif app.platform == 'android' and app.subplatform == 'amazon': self._show_sign_in_buttons.append('Game Circle') # Local accounts are generally always available with a few key # exceptions. self._show_sign_in_buttons.append('Local') # Ditto with shiny new V2 ones. if bool(True): self._show_sign_in_buttons.append('V2') top_extra = 15 if uiscale is ba.UIScale.SMALL else 0 super().__init__(root_widget=ba.containerwidget( size=(self._width, self._height + top_extra), transition=transition, toolbar_visibility='menu_minimal', scale_origin_stack_offset=scale_origin, scale=(2.09 if uiscale is ba.UIScale.SMALL else 1.4 if uiscale is ba.UIScale.MEDIUM else 1.0), stack_offset=(0, -19) if uiscale is ba.UIScale.SMALL else (0, 0))) if uiscale is ba.UIScale.SMALL and ba.app.ui.use_toolbars: self._back_button = None ba.containerwidget(edit=self._root_widget, on_cancel_call=self._back) else: self._back_button = btn = ba.buttonwidget( parent=self._root_widget, position=(51 + x_offs, self._height - 62), size=(120, 60), scale=0.8, text_scale=1.2, autoselect=True, label=ba.Lstr( resource='doneText' if self._modal else 'backText'), button_type='regular' if self._modal else 'back', on_activate_call=self._back) ba.containerwidget(edit=self._root_widget, cancel_button=btn) if not self._modal: ba.buttonwidget(edit=btn, button_type='backSmall', size=(60, 56), label=ba.charstr(ba.SpecialChar.BACK)) ba.textwidget(parent=self._root_widget, position=(self._width * 0.5, self._height - 41), size=(0, 0), text=ba.Lstr(resource=self._r + '.titleText'), color=ba.app.ui.title_color, maxwidth=self._width - 340, h_align='center', v_align='center') self._scrollwidget = ba.scrollwidget( parent=self._root_widget, highlight=False, position=((self._width - self._scroll_width) * 0.5, self._height - 65 - self._scroll_height), size=(self._scroll_width, self._scroll_height), claims_left_right=True, claims_tab=True, selection_loops_to_parent=True) self._subcontainer: Optional[ba.Widget] = None self._refresh() self._restore_state()
def call_after_ad(self, call: Callable[[], Any]) -> None: """Run a call after potentially showing an ad.""" # pylint: disable=too-many-statements # pylint: disable=too-many-branches # pylint: disable=too-many-locals from ba._generated.enums import TimeType app = _ba.app show = True # No ads without net-connections, etc. if not _ba.can_show_ad(): show = False if app.accounts_v1.have_pro(): show = False # Pro disables interstitials. try: session = _ba.get_foreground_host_session() assert session is not None is_tournament = session.tournament_id is not None except Exception: is_tournament = False if is_tournament: show = False # Never show ads during tournaments. if show: interval: Optional[float] launch_count = app.config.get('launchCount', 0) # If we're seeing short ads we may want to space them differently. interval_mult = (_ba.get_v1_account_misc_read_val( 'ads.shortIntervalMult', 1.0) if self.last_ad_was_short else 1.0) if self.ad_amt is None: if launch_count <= 1: self.ad_amt = _ba.get_v1_account_misc_read_val( 'ads.startVal1', 0.99) else: self.ad_amt = _ba.get_v1_account_misc_read_val( 'ads.startVal2', 1.0) interval = None else: # So far we're cleared to show; now calc our # ad-show-threshold and see if we should *actually* show # (we reach our threshold faster the longer we've been # playing). base = 'ads' if _ba.has_video_ads() else 'ads2' min_lc = _ba.get_v1_account_misc_read_val(base + '.minLC', 0.0) max_lc = _ba.get_v1_account_misc_read_val(base + '.maxLC', 5.0) min_lc_scale = (_ba.get_v1_account_misc_read_val( base + '.minLCScale', 0.25)) max_lc_scale = (_ba.get_v1_account_misc_read_val( base + '.maxLCScale', 0.34)) min_lc_interval = (_ba.get_v1_account_misc_read_val( base + '.minLCInterval', 360)) max_lc_interval = (_ba.get_v1_account_misc_read_val( base + '.maxLCInterval', 300)) if launch_count < min_lc: lc_amt = 0.0 elif launch_count > max_lc: lc_amt = 1.0 else: lc_amt = ((float(launch_count) - min_lc) / (max_lc - min_lc)) incr = (1.0 - lc_amt) * min_lc_scale + lc_amt * max_lc_scale interval = ((1.0 - lc_amt) * min_lc_interval + lc_amt * max_lc_interval) self.ad_amt += incr assert self.ad_amt is not None if self.ad_amt >= 1.0: self.ad_amt = self.ad_amt % 1.0 self.attempted_first_ad = True # After we've reached the traditional show-threshold once, # try again whenever its been INTERVAL since our last successful # show. elif ( self.attempted_first_ad and (self.last_ad_completion_time is None or (interval is not None and _ba.time(TimeType.REAL) - self.last_ad_completion_time > (interval * interval_mult)))): # Reset our other counter too in this case. self.ad_amt = 0.0 else: show = False # If we're *still* cleared to show, actually tell the system to show. if show: # As a safety-check, set up an object that will run # the completion callback if we've returned and sat for 10 seconds # (in case some random ad network doesn't properly deliver its # completion callback). class _Payload: def __init__(self, pcall: Callable[[], Any]): self._call = pcall self._ran = False def run(self, fallback: bool = False) -> None: """Run fallback call (and issue a warning about it).""" if not self._ran: if fallback: print( ('ERROR: relying on fallback ad-callback! ' 'last network: ' + app.ads.last_ad_network + ' (set ' + str( int(time.time() - app.ads.last_ad_network_set_time)) + 's ago); purpose=' + app.ads.last_ad_purpose)) _ba.pushcall(self._call) self._ran = True payload = _Payload(call) with _ba.Context('ui'): _ba.timer(5.0, lambda: payload.run(fallback=True), timetype=TimeType.REAL) self.show_ad('between_game', on_completion_call=payload.run) else: _ba.pushcall(call) # Just run the callback without the ad.