def _update(self) -> None: """Periodic updating.""" now = ba.time(ba.TimeType.REAL) self._update_currency_ui() if self._state.sub_tab is SubTabType.HOST: # If we're not signed in, just refresh to show that. if (_ba.get_v1_account_state() != 'signed_in' and self._showing_not_signed_in_screen): self._refresh_sub_tab() else: # Query an updated state periodically. if (self._last_hosting_state_query_time is None or now - self._last_hosting_state_query_time > 15.0): self._debug_server_comm('querying private party state') if _ba.get_v1_account_state() == 'signed_in': _ba.add_transaction( { 'type': 'PRIVATE_PARTY_QUERY', 'expire_time': time.time() + 20, }, callback=ba.WeakCall( self._hosting_state_idle_response), ) _ba.run_transactions() else: self._hosting_state_idle_response(None) self._last_hosting_state_query_time = now
def _on_continue_press(self) -> None: from bastd.ui import getcurrency # Disallow for first second. if self._start_count - self._count < 2: ba.playsound(ba.getsound('error')) else: # If somehow we got signed out... if _ba.get_v1_account_state() != 'signed_in': ba.screenmessage(ba.Lstr(resource='notSignedInText'), color=(1, 0, 0)) ba.playsound(ba.getsound('error')) return # If it appears we don't have enough tickets, offer to buy more. tickets = _ba.get_v1_account_ticket_count() if tickets < self._cost: # FIXME: Should we start the timer back up again after? self._counting_down = False ba.textwidget(edit=self._counter_text, text='') ba.playsound(ba.getsound('error')) getcurrency.show_get_tickets_prompt() return if not self._transitioning_out: ba.playsound(ba.getsound('swish')) self._transitioning_out = True ba.containerwidget(edit=self._root_widget, transition='out_scale') self._continue_call()
def announce_completion(self, sound: bool = True) -> None: """Kick off an announcement for this achievement's completion.""" from ba._generated.enums import TimeType app = _ba.app # Even though there are technically achievements when we're not # signed in, lets not show them (otherwise we tend to get # confusing 'controller connected' achievements popping up while # waiting to log in which can be confusing). if _ba.get_v1_account_state() != 'signed_in': return # If we're being freshly complete, display/report it and whatnot. if (self, sound) not in app.ach.achievements_to_display: app.ach.achievements_to_display.append((self, sound)) # If there's no achievement display timer going, kick one off # (if one's already running it will pick this up before it dies). # Need to check last time too; its possible our timer wasn't able to # clear itself if an activity died and took it down with it. if ((app.ach.achievement_display_timer is None or _ba.time(TimeType.REAL) - app.ach.last_achievement_display_time > 2.0) and _ba.getactivity(doraise=False) is not None): app.ach.achievement_display_timer = _ba.Timer( 1.0, _display_next_achievement, repeat=True, timetype=TimeType.BASE) # Show the first immediately. _display_next_achievement()
def on_boost_press(self) -> None: """Boost was pressed.""" from bastd.ui import account from bastd.ui import getcurrency if _ba.get_v1_account_state() != 'signed_in': account.show_sign_in_prompt() return if _ba.get_v1_account_ticket_count() < self._boost_tickets: ba.playsound(ba.getsound('error')) getcurrency.show_get_tickets_prompt() return ba.playsound(ba.getsound('laserReverse')) _ba.add_transaction( { 'type': 'PARTY_QUEUE_BOOST', 't': self._boost_tickets, 'q': self._queue_id }, callback=ba.WeakCall(self.on_update_response)) # lets not run these immediately (since they may be rapid-fire, # just bucket them until the next tick) # the transaction handles the local ticket change, but we apply our # local boost vis manually here.. # (our visualization isn't really wired up to be transaction-based) our_dude = self._dudes_by_id.get(-1) if our_dude is not None: our_dude.boost(self._boost_strength, self._smoothing)
def _on_start_advertizing_press(self) -> None: from bastd.ui.account import show_sign_in_prompt if _ba.get_v1_account_state() != 'signed_in': show_sign_in_prompt() return name = cast(str, ba.textwidget(query=self._host_name_text)) if name == '': ba.screenmessage(ba.Lstr(resource='internal.invalidNameErrorText'), color=(1, 0, 0)) ba.playsound(ba.getsound('error')) return _ba.set_public_party_name(name) cfg = ba.app.config cfg['Public Party Name'] = name cfg.commit() ba.playsound(ba.getsound('shieldUp')) _ba.set_public_party_enabled(True) # In GUI builds we want to authenticate clients only when hosting # public parties. _ba.set_authenticate_clients(True) self._do_status_check() ba.buttonwidget( edit=self._host_toggle_button, label=ba.Lstr( resource='gatherWindow.makePartyPrivateText', fallback_resource='gatherWindow.stopAdvertisingText'), on_activate_call=self._on_stop_advertising_press)
def _on_get_more_tickets_press(self) -> None: from bastd.ui import account from bastd.ui import getcurrency if _ba.get_v1_account_state() != 'signed_in': account.show_sign_in_prompt() return getcurrency.GetCurrencyWindow(modal=True).get_root_widget()
def _invite_to_try_press(self) -> None: from bastd.ui.account import show_sign_in_prompt from bastd.ui.appinvite import handle_app_invites_press if _ba.get_v1_account_state() != 'signed_in': show_sign_in_prompt() return handle_app_invites_press()
def _share_playlist(self) -> None: # pylint: disable=cyclic-import from bastd.ui.purchase import PurchaseWindow if not ba.app.accounts_v1.have_pro_options(): PurchaseWindow(items=['pro']) return # Gotta be signed in for this to work. if _ba.get_v1_account_state() != 'signed_in': ba.screenmessage(ba.Lstr(resource='notSignedInErrorText'), color=(1, 0, 0)) ba.playsound(ba.getsound('error')) return if self._selected_playlist_name == '__default__': ba.playsound(ba.getsound('error')) ba.screenmessage(ba.Lstr(resource=self._r + '.cantShareDefaultText'), color=(1, 0, 0)) return if self._selected_playlist_name is None: return _ba.add_transaction( { 'type': 'SHARE_PLAYLIST', 'expire_time': time.time() + 5, 'playlistType': self._pvars.config_name, 'playlistName': self._selected_playlist_name }, callback=ba.WeakCall(self._on_share_playlist_response, self._selected_playlist_name)) _ba.run_transactions() ba.screenmessage(ba.Lstr(resource='sharingText'))
def _on_friend_promo_code_press(self) -> None: from bastd.ui import appinvite from bastd.ui import account if _ba.get_v1_account_state() != 'signed_in': account.show_sign_in_prompt() return appinvite.handle_app_invites_press()
def _tick(self) -> None: # if our target activity is gone or has ended, go away activity = self._activity() if activity is None or activity.has_ended(): self._on_cancel() return if _ba.get_v1_account_state() == 'signed_in': sval = (ba.charstr(ba.SpecialChar.TICKET) + str(_ba.get_v1_account_ticket_count())) else: sval = '?' if self._tickets_text is not None: assert self._tickets_text_base is not None ba.textwidget(edit=self._tickets_text, text=self._tickets_text_base.replace( '${COUNT}', sval)) if self._counting_down: self._count -= 1 ba.playsound(ba.getsound('tick')) if self._count <= 0: self._on_cancel() else: ba.textwidget(edit=self._counter_text, text=str(self._count))
def ensure_have_account_player_profile(self) -> None: """ Ensure the standard account-named player profile exists; creating if needed. (internal) """ # This only applies when we're signed in. if _ba.get_v1_account_state() != 'signed_in': return # If the short version of our account name currently cant be # displayed by the game, cancel. if not _ba.have_chars(_ba.get_v1_account_display_string(full=False)): return config = _ba.app.config if ('Player Profiles' not in config or '__account__' not in config['Player Profiles']): # Create a spaz with a nice default purply color. _ba.add_transaction({ 'type': 'ADD_PLAYER_PROFILE', 'name': '__account__', 'profile': { 'character': 'Spaz', 'color': [0.5, 0.25, 1.0], 'highlight': [0.5, 0.25, 1.0] } }) _ba.run_transactions()
def add_pending_promo_code(self, code: str) -> None: """(internal)""" from ba._language import Lstr from ba._generated.enums import TimeType # If we're not signed in, queue up the code to run the next time we # are and issue a warning if we haven't signed in within the next # few seconds. if _ba.get_v1_account_state() != 'signed_in': def check_pending_codes() -> None: """(internal)""" # If we're still not signed in and have pending codes, # inform the user that they need to sign in to use them. if self.pending_promo_codes: _ba.screenmessage(Lstr(resource='signInForPromoCodeText'), color=(1, 0, 0)) _ba.playsound(_ba.getsound('error')) self.pending_promo_codes.append(code) _ba.timer(6.0, check_pending_codes, timetype=TimeType.REAL) return _ba.screenmessage(Lstr(resource='submittingPromoCodeText'), color=(0, 1, 0)) _ba.add_transaction({ 'type': 'PROMO_CODE', 'expire_time': time.time() + 5, 'code': code }) _ba.run_transactions()
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 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 __init__(self, transition: Optional[str] = 'in_right'): # pylint: disable=cyclic-import import threading from bastd.mainmenu import MainMenuSession self._in_game = not isinstance(_ba.get_foreground_host_session(), MainMenuSession) # Preload some modules we use in a background thread so we won't # have a visual hitch when the user taps them. threading.Thread(target=self._preload_modules).start() if not self._in_game: ba.set_analytics_screen('Main Menu') self._show_remote_app_info_on_first_launch() # Make a vanilla container; we'll modify it to our needs in refresh. super().__init__(root_widget=ba.containerwidget( transition=transition, toolbar_visibility='menu_minimal_no_back' if self. _in_game else 'menu_minimal_no_back')) # Grab this stuff in case it changes. self._is_demo = ba.app.demo_mode self._is_arcade = ba.app.arcade_mode self._is_iircade = ba.app.iircade_mode self._tdelay = 0.0 self._t_delay_inc = 0.02 self._t_delay_play = 1.7 self._p_index = 0 self._use_autoselect = True self._button_width = 200.0 self._button_height = 45.0 self._width = 100.0 self._height = 100.0 self._demo_menu_button: Optional[ba.Widget] = None self._gather_button: Optional[ba.Widget] = None self._start_button: Optional[ba.Widget] = None self._watch_button: Optional[ba.Widget] = None self._gc_button: Optional[ba.Widget] = None self._how_to_play_button: Optional[ba.Widget] = None self._credits_button: Optional[ba.Widget] = None self._settings_button: Optional[ba.Widget] = None self._store_char_tex = self._get_store_char_tex() self._refresh() self._restore_state() # Keep an eye on a few things and refresh if they change. self._account_state = _ba.get_v1_account_state() self._account_state_num = _ba.get_v1_account_state_num() self._account_type = (_ba.get_v1_account_type() if self._account_state == 'signed_in' else None) self._refresh_timer = ba.Timer(1.0, ba.WeakCall(self._check_refresh), repeat=True, timetype=ba.TimeType.REAL)
def _start_stop_button_press(self) -> None: if (self._waiting_for_start_stop_response or self._waiting_for_initial_state): return if _ba.get_v1_account_state() != 'signed_in': ba.screenmessage(ba.Lstr(resource='notSignedInErrorText')) ba.playsound(ba.getsound('error')) self._refresh_sub_tab() return if self._hostingstate.unavailable_error is not None: ba.playsound(ba.getsound('error')) return ba.playsound(ba.getsound('click01')) # If we're not hosting, start. if self._hostingstate.party_code is None: # If there's a ticket cost, make sure we have enough tickets. if self._hostingstate.tickets_to_host_now > 0: ticket_count: Optional[int] try: ticket_count = _ba.get_v1_account_ticket_count() except Exception: # FIXME: should add a ba.NotSignedInError we can use here. ticket_count = None ticket_cost = self._hostingstate.tickets_to_host_now if ticket_count is not None and ticket_count < ticket_cost: getcurrency.show_get_tickets_prompt() ba.playsound(ba.getsound('error')) return self._last_action_send_time = time.time() _ba.add_transaction( { 'type': 'PRIVATE_PARTY_START', 'config': dataclass_to_dict(self._hostingconfig), 'region_pings': ba.app.net.zone_pings, 'expire_time': time.time() + 20, }, callback=ba.WeakCall(self._hosting_state_response)) _ba.run_transactions() else: self._last_action_send_time = time.time() _ba.add_transaction( { 'type': 'PRIVATE_PARTY_STOP', 'expire_time': time.time() + 20, }, callback=ba.WeakCall(self._hosting_state_response)) _ba.run_transactions() ba.playsound(ba.getsound('click01')) self._waiting_for_start_stop_response = True self._refresh_sub_tab()
def upgrade_profile(self) -> None: """Attempt to ugrade the profile to global.""" from bastd.ui import account from bastd.ui.profile import upgrade as pupgrade if _ba.get_v1_account_state() != 'signed_in': account.show_sign_in_prompt() return pupgrade.ProfileUpgradeWindow(self)
def _on_store_press(self) -> None: from bastd.ui import account from bastd.ui.store.browser import StoreBrowserWindow if _ba.get_v1_account_state() != 'signed_in': account.show_sign_in_prompt() return StoreBrowserWindow(modal=True, show_tab=StoreBrowserWindow.TabID.MAPS, on_close_call=self._on_store_close, origin_widget=self._get_more_maps_button)
def _on_store_press(self) -> None: from bastd.ui.account import show_sign_in_prompt from bastd.ui.store.browser import StoreBrowserWindow if _ba.get_v1_account_state() != 'signed_in': show_sign_in_prompt() return self._transition_out() StoreBrowserWindow(modal=True, show_tab=StoreBrowserWindow.TabID.ICONS, origin_widget=self._get_more_icons_button)
def _update_tickets_text(self) -> None: from ba import SpecialChar if not self._root_widget: return sval: Union[str, ba.Lstr] if _ba.get_v1_account_state() == 'signed_in': sval = (ba.charstr(SpecialChar.TICKET) + str(_ba.get_v1_account_ticket_count())) else: sval = ba.Lstr(resource='getTicketsWindow.titleText') ba.buttonwidget(edit=self._get_tickets_button, label=sval)
def _update(self) -> None: # Kiosk-mode is designed to be used signed-out... try for force # the issue. if _ba.get_v1_account_state() == 'signed_in': # _bs.sign_out() # FIXME: Try to delete player profiles here too. pass else: # Also make sure there's no player profiles. appconfig = ba.app.config appconfig['Player Profiles'] = {}
def _coop(self) -> None: # pylint: disable=cyclic-import from bastd.ui.account import show_sign_in_prompt from bastd.ui.coop.browser import CoopBrowserWindow if _ba.get_v1_account_state() != 'signed_in': show_sign_in_prompt() return self._save_state() ba.containerwidget(edit=self._root_widget, transition='out_left') ba.app.ui.set_main_menu_window( CoopBrowserWindow( origin_widget=self._coop_button).get_root_widget())
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 get_purchased_icons(self) -> list[str]: """(internal)""" # pylint: disable=cyclic-import from ba import _store if _ba.get_v1_account_state() != 'signed_in': return [] icons = [] store_items = _store.get_store_items() for item_name, item in list(store_items.items()): if item_name.startswith('icons.') and _ba.get_purchased(item_name): icons.append(item['icon']) return icons
def _update_clipped_name(self) -> None: if not self._clipped_name_text: return name = self.getname() if name == '__account__': name = (_ba.get_v1_account_name() if _ba.get_v1_account_state() == 'signed_in' else '???') if len(name) > 10 and not (self._global or self._is_account_profile): ba.textwidget(edit=self._clipped_name_text, text=ba.Lstr(resource='inGameClippedNameText', subs=[('${NAME}', name[:10] + '...')])) else: ba.textwidget(edit=self._clipped_name_text, text='')
def _on_promo_code_press(self) -> None: from bastd.ui.promocode import PromoCodeWindow from bastd.ui.account import show_sign_in_prompt # We have to be logged in for promo-codes to work. if _ba.get_v1_account_state() != 'signed_in': show_sign_in_prompt() return self._save_state() ba.containerwidget(edit=self._root_widget, transition='out_left') ba.app.ui.set_main_menu_window( PromoCodeWindow( origin_widget=self._promo_code_button).get_root_widget())
def _generate_press(self) -> None: from bastd.ui import account if _ba.get_v1_account_state() != 'signed_in': account.show_sign_in_prompt() return ba.screenmessage( ba.Lstr(resource='gatherWindow.requestingAPromoCodeText'), color=(0, 1, 0)) _ba.add_transaction({ 'type': 'ACCOUNT_LINK_CODE_REQUEST', 'expire_time': time.time() + 5 }) _ba.run_transactions()
def _on_get_more_tickets_press(self) -> None: # pylint: disable=cyclic-import from bastd.ui.account import show_sign_in_prompt from bastd.ui.getcurrency import GetCurrencyWindow if _ba.get_v1_account_state() != 'signed_in': show_sign_in_prompt() return self._save_state() ba.containerwidget(edit=self._root_widget, transition='out_left') window = GetCurrencyWindow( from_modal_store=self._modal, store_back_location=self._back_location).get_root_widget() if not self._modal: ba.app.ui.set_main_menu_window(window)
def _custom_colors_names_press(self) -> None: from bastd.ui.account import show_sign_in_prompt from bastd.ui.teamnamescolors import TeamNamesColorsWindow from bastd.ui.purchase import PurchaseWindow if not ba.app.accounts_v1.have_pro(): if _ba.get_v1_account_state() != 'signed_in': show_sign_in_prompt() else: PurchaseWindow(items=['pro']) self._transition_out() return assert self._custom_colors_names_button TeamNamesColorsWindow(scale_origin=self._custom_colors_names_button. get_screen_space_center())
def _import_playlist(self) -> None: # pylint: disable=cyclic-import from bastd.ui.playlist import share # Gotta be signed in for this to work. if _ba.get_v1_account_state() != 'signed_in': ba.screenmessage(ba.Lstr(resource='notSignedInErrorText'), color=(1, 0, 0)) ba.playsound(ba.getsound('error')) return share.SharePlaylistImportWindow(origin_widget=self._import_button, on_success_callback=ba.WeakCall( self._on_playlist_import_success))