def _handle_hit(self, msg: ba.HitMessage) -> None: ispunched = (msg.srcnode and msg.srcnode.getnodetype() == 'spaz') # Normal bombs are triggered by non-punch impacts; # impact-bombs by all impacts. if (not self._exploded and (not ispunched or self.bomb_type in ['impact', 'land_mine'])): # Also lets change the owner of the bomb to whoever is setting # us off. (this way points for big chain reactions go to the # person causing them). source_player = msg.get_source_player(ba.Player) if source_player is not None: self._source_player = source_player # Also inherit the hit type (if a landmine sets off by a bomb, # the credit should go to the mine) # the exception is TNT. TNT always gets credit. # UPDATE (July 2020): not doing this anymore. Causes too much # weird logic such as bombs acting like punches. Holler if # anything is noticeably broken due to this. # if self.bomb_type != 'tnt': # self.hit_type = msg.hit_type # self.hit_subtype = msg.hit_subtype ba.timer(0.1 + random.random() * 0.1, ba.WeakCall(self.handlemessage, ExplodeMessage())) assert self.node self.node.handlemessage('impulse', msg.pos[0], msg.pos[1], msg.pos[2], msg.velocity[0], msg.velocity[1], msg.velocity[2], msg.magnitude, msg.velocity_magnitude, msg.radius, 0, msg.velocity[0], msg.velocity[1], msg.velocity[2]) if msg.srcnode: pass
def _drop_powerups(self, standard_points: bool = False, force_first: str = None) -> None: """Generic powerup drop.""" from bastd.actor import powerupbox if standard_points: pts = self.map.powerup_spawn_points for i in range(len(pts)): ba.timer( 1.0 + i * 0.5, ba.WeakCall(self._drop_powerup, i, force_first if i == 0 else None)) else: drop_pt = (self._powerup_center[0] + random.uniform( -1.0 * self._powerup_spread[0], 1.0 * self._powerup_spread[0]), self._powerup_center[1], self._powerup_center[2] + random.uniform( -self._powerup_spread[1], self._powerup_spread[1])) # Drop one random one somewhere. powerupbox.PowerupBox( position=drop_pt, poweruptype=powerupbox.get_factory().get_random_powerup_type( excludetypes=self._excludepowerups)).autoretain()
def _refresh_in_game( self, positions: List[Tuple[float, float, float]]) -> Tuple[float, float, float]: # pylint: disable=too-many-branches # pylint: disable=too-many-locals # pylint: disable=too-many-statements custom_menu_entries: List[Dict[str, Any]] = [] session = _ba.get_foreground_host_session() if session is not None: try: custom_menu_entries = session.get_custom_menu_entries() for cme in custom_menu_entries: if (not isinstance(cme, dict) or 'label' not in cme or not isinstance(cme['label'], (str, ba.Lstr)) or 'call' not in cme or not callable(cme['call'])): raise ValueError('invalid custom menu entry: ' + str(cme)) except Exception: custom_menu_entries = [] ba.print_exception( f'Error getting custom menu entries for {session}') self._width = 250.0 self._height = 250.0 if self._input_player else 180.0 if (self._is_demo or self._is_arcade) and self._input_player: self._height -= 40 if not self._have_settings_button: self._height -= 50 if self._connected_to_remote_player: # In this case we have a leave *and* a disconnect button. self._height += 50 self._height += 50 * (len(custom_menu_entries)) uiscale = ba.app.ui.uiscale ba.containerwidget( edit=self._root_widget, size=(self._width, self._height), scale=(2.15 if uiscale is ba.UIScale.SMALL else 1.6 if uiscale is ba.UIScale.MEDIUM else 1.0)) h = 125.0 v = (self._height - 80.0 if self._input_player else self._height - 60) h_offset = 0 d_h_offset = 0 v_offset = -50 for _i in range(6 + len(custom_menu_entries)): positions.append((h, v, 1.0)) v += v_offset h += h_offset h_offset += d_h_offset self._start_button = None ba.app.pause() # Player name if applicable. if self._input_player: player_name = self._input_player.getname() h, v, scale = positions[self._p_index] v += 35 ba.textwidget(parent=self._root_widget, position=(h - self._button_width / 2, v), size=(self._button_width, self._button_height), color=(1, 1, 1, 0.5), scale=0.7, h_align='center', text=ba.Lstr(value=player_name)) else: player_name = '' h, v, scale = positions[self._p_index] self._p_index += 1 btn = ba.buttonwidget(parent=self._root_widget, position=(h - self._button_width / 2, v), size=(self._button_width, self._button_height), scale=scale, label=ba.Lstr(resource=self._r + '.resumeText'), autoselect=self._use_autoselect, on_activate_call=self._resume) ba.containerwidget(edit=self._root_widget, cancel_button=btn) # Add any custom options defined by the current game. for entry in custom_menu_entries: h, v, scale = positions[self._p_index] self._p_index += 1 # Ask the entry whether we should resume when we call # it (defaults to true). resume = bool(entry.get('resume_on_call', True)) if resume: call = ba.Call(self._resume_and_call, entry['call']) else: call = ba.Call(entry['call'], ba.WeakCall(self._resume)) ba.buttonwidget(parent=self._root_widget, position=(h - self._button_width / 2, v), size=(self._button_width, self._button_height), scale=scale, on_activate_call=call, label=entry['label'], autoselect=self._use_autoselect) # Add a 'leave' button if the menu-owner has a player. if ((self._input_player or self._connected_to_remote_player) and not (self._is_demo or self._is_arcade)): h, v, scale = positions[self._p_index] self._p_index += 1 btn = ba.buttonwidget(parent=self._root_widget, position=(h - self._button_width / 2, v), size=(self._button_width, self._button_height), scale=scale, on_activate_call=self._leave, label='', autoselect=self._use_autoselect) if (player_name != '' and player_name[0] != '<' and player_name[-1] != '>'): txt = ba.Lstr(resource=self._r + '.justPlayerText', subs=[('${NAME}', player_name)]) else: txt = ba.Lstr(value=player_name) ba.textwidget(parent=self._root_widget, position=(h, v + self._button_height * (0.64 if player_name != '' else 0.5)), size=(0, 0), text=ba.Lstr(resource=self._r + '.leaveGameText'), scale=(0.83 if player_name != '' else 1.0), color=(0.75, 1.0, 0.7), h_align='center', v_align='center', draw_controller=btn, maxwidth=self._button_width * 0.9) ba.textwidget(parent=self._root_widget, position=(h, v + self._button_height * 0.27), size=(0, 0), text=txt, color=(0.75, 1.0, 0.7), h_align='center', v_align='center', draw_controller=btn, scale=0.45, maxwidth=self._button_width * 0.9) return h, v, scale
def __init__(self, transition: str = 'in_right', origin_widget: ba.Widget = None): # pylint: disable=too-many-locals # pylint: disable=too-many-statements # 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 = 'editSoundtrackWindow' uiscale = ba.app.uiscale self._width = 800 if uiscale is ba.UIScale.SMALL else 600 x_inset = 100 if uiscale is ba.UIScale.SMALL else 0 self._height = (340 if uiscale is ba.UIScale.SMALL else 370 if uiscale is ba.UIScale.MEDIUM else 440) spacing = 40.0 v = self._height - 40.0 v -= spacing * 1.0 super().__init__(root_widget=ba.containerwidget( size=(self._width, self._height), transition=transition, toolbar_visibility='menu_minimal', scale_origin_stack_offset=scale_origin, scale=(2.3 if uiscale is ba.UIScale.SMALL else 1.6 if uiscale is ba.UIScale.MEDIUM else 1.0), stack_offset=(0, -18) if uiscale is ba.UIScale.SMALL else (0, 0))) if ba.app.toolbars and uiscale is ba.UIScale.SMALL: self._back_button = None else: self._back_button = ba.buttonwidget( parent=self._root_widget, position=(45 + x_inset, self._height - 60), size=(120, 60), scale=0.8, label=ba.Lstr(resource='backText'), button_type='back', autoselect=True) ba.buttonwidget(edit=self._back_button, button_type='backSmall', size=(60, 60), label=ba.charstr(ba.SpecialChar.BACK)) ba.textwidget(parent=self._root_widget, position=(self._width * 0.5, self._height - 35), size=(0, 0), maxwidth=300, text=ba.Lstr(resource=self._r + '.titleText'), color=ba.app.title_color, h_align='center', v_align='center') h = 43 + x_inset v = self._height - 60 b_color = (0.6, 0.53, 0.63) b_textcolor = (0.75, 0.7, 0.8) lock_tex = ba.gettexture('lock') self._lock_images: List[ba.Widget] = [] scl = (1.0 if uiscale is ba.UIScale.SMALL else 1.13 if uiscale is ba.UIScale.MEDIUM else 1.4) v -= 60.0 * scl self._new_button = btn = ba.buttonwidget( parent=self._root_widget, position=(h, v), size=(100, 55.0 * scl), on_activate_call=self._new_soundtrack, color=b_color, button_type='square', autoselect=True, textcolor=b_textcolor, text_scale=0.7, label=ba.Lstr(resource=self._r + '.newText')) self._lock_images.append( ba.imagewidget(parent=self._root_widget, size=(30, 30), draw_controller=btn, position=(h - 10, v + 55.0 * scl - 28), texture=lock_tex)) if self._back_button is None: ba.widget(edit=btn, left_widget=_ba.get_special_widget('back_button')) v -= 60.0 * scl self._edit_button = btn = ba.buttonwidget( parent=self._root_widget, position=(h, v), size=(100, 55.0 * scl), on_activate_call=self._edit_soundtrack, color=b_color, button_type='square', autoselect=True, textcolor=b_textcolor, text_scale=0.7, label=ba.Lstr(resource=self._r + '.editText')) self._lock_images.append( ba.imagewidget(parent=self._root_widget, size=(30, 30), draw_controller=btn, position=(h - 10, v + 55.0 * scl - 28), texture=lock_tex)) if self._back_button is None: ba.widget(edit=btn, left_widget=_ba.get_special_widget('back_button')) v -= 60.0 * scl self._duplicate_button = btn = ba.buttonwidget( parent=self._root_widget, position=(h, v), size=(100, 55.0 * scl), on_activate_call=self._duplicate_soundtrack, button_type='square', autoselect=True, color=b_color, textcolor=b_textcolor, text_scale=0.7, label=ba.Lstr(resource=self._r + '.duplicateText')) self._lock_images.append( ba.imagewidget(parent=self._root_widget, size=(30, 30), draw_controller=btn, position=(h - 10, v + 55.0 * scl - 28), texture=lock_tex)) if self._back_button is None: ba.widget(edit=btn, left_widget=_ba.get_special_widget('back_button')) v -= 60.0 * scl self._delete_button = btn = ba.buttonwidget( parent=self._root_widget, position=(h, v), size=(100, 55.0 * scl), on_activate_call=self._delete_soundtrack, color=b_color, button_type='square', autoselect=True, textcolor=b_textcolor, text_scale=0.7, label=ba.Lstr(resource=self._r + '.deleteText')) self._lock_images.append( ba.imagewidget(parent=self._root_widget, size=(30, 30), draw_controller=btn, position=(h - 10, v + 55.0 * scl - 28), texture=lock_tex)) if self._back_button is None: ba.widget(edit=btn, left_widget=_ba.get_special_widget('back_button')) # Keep our lock images up to date/etc. self._update_timer = ba.Timer(1.0, ba.WeakCall(self._update), timetype=ba.TimeType.REAL, repeat=True) self._update() v = self._height - 65 scroll_height = self._height - 105 v -= scroll_height self._scrollwidget = scrollwidget = ba.scrollwidget( parent=self._root_widget, position=(152 + x_inset, v), highlight=False, size=(self._width - (205 + 2 * x_inset), scroll_height)) ba.widget(edit=self._scrollwidget, left_widget=self._new_button, right_widget=_ba.get_special_widget('party_button') if ba.app.toolbars else self._scrollwidget) self._col = ba.columnwidget(parent=scrollwidget) self._soundtracks: Optional[Dict[str, Any]] = None self._selected_soundtrack: Optional[str] = None self._selected_soundtrack_index: Optional[int] = None self._soundtrack_widgets: List[ba.Widget] = [] self._allow_changing_soundtracks = False self._refresh() if self._back_button is not None: ba.buttonwidget(edit=self._back_button, on_activate_call=self._back) ba.containerwidget(edit=self._root_widget, cancel_button=self._back_button) else: ba.containerwidget(edit=self._root_widget, on_cancel_call=self._back)
def _start_powerup_drops(self) -> None: self._powerup_drop_timer = ba.Timer(3.0, ba.WeakCall(self._drop_powerups), repeat=True)
def __init__(self, window: StoreBrowserWindow): self._window = weakref.ref(window) data = {'tab': tab} ba.timer(0.1, ba.WeakCall(self._on_response, data), timetype=ba.TimeType.REAL)
def update_buttons(self) -> None: """Update our buttons.""" # pylint: disable=too-many-statements # pylint: disable=too-many-branches # pylint: disable=too-many-locals from ba.internal import have_pro, get_available_sale_time from ba import SpecialChar if not self._root_widget: return import datetime sales_raw = _ba.get_account_misc_read_val('sales', {}) sales = {} try: # Look at the current set of sales; filter any with time remaining. for sale_item, sale_info in list(sales_raw.items()): to_end = (datetime.datetime.utcfromtimestamp(sale_info['e']) - datetime.datetime.utcnow()).total_seconds() if to_end > 0: sales[sale_item] = { 'to_end': to_end, 'original_price': sale_info['op'] } except Exception: ba.print_exception('Error parsing sales.') assert self.button_infos is not None for b_type, b_info in self.button_infos.items(): if b_type in ['upgrades.pro', 'pro']: purchased = have_pro() else: purchased = _ba.get_purchased(b_type) sale_opacity = 0.0 sale_title_text: Union[str, ba.Lstr] = '' sale_time_text: Union[str, ba.Lstr] = '' if purchased: title_color = (0.8, 0.7, 0.9, 1.0) color = (0.63, 0.55, 0.78) extra_image_opacity = 0.5 call = ba.WeakCall(self._print_already_own, b_info['name']) price_text = '' price_text_left = '' price_text_right = '' show_purchase_check = True description_color: Sequence[float] = (0.4, 1.0, 0.4, 0.4) description_color2: Sequence[float] = (0.0, 0.0, 0.0, 0.0) price_color = (0.5, 1, 0.5, 0.3) else: title_color = (0.7, 0.9, 0.7, 1.0) color = (0.4, 0.8, 0.1) extra_image_opacity = 1.0 call = b_info['call'] if 'call' in b_info else None if b_type in ['upgrades.pro', 'pro']: sale_time = get_available_sale_time('extras') if sale_time is not None: priceraw = _ba.get_price('pro') price_text_left = (priceraw if priceraw is not None else '?') priceraw = _ba.get_price('pro_sale') price_text_right = (priceraw if priceraw is not None else '?') sale_opacity = 1.0 price_text = '' sale_title_text = ba.Lstr(resource='store.saleText') sale_time_text = ba.timestring( sale_time, centi=False, timeformat=ba.TimeFormat.MILLISECONDS) else: priceraw = _ba.get_price('pro') price_text = priceraw if priceraw is not None else '?' price_text_left = '' price_text_right = '' else: price = _ba.get_account_misc_read_val('price.' + b_type, 0) # Color the button differently if we cant afford this. if _ba.get_account_state() == 'signed_in': if _ba.get_account_ticket_count() < price: color = (0.6, 0.61, 0.6) price_text = ba.charstr(ba.SpecialChar.TICKET) + str( _ba.get_account_misc_read_val('price.' + b_type, '?')) price_text_left = '' price_text_right = '' # TESTING: if b_type in sales: sale_opacity = 1.0 price_text_left = ba.charstr(SpecialChar.TICKET) + str( sales[b_type]['original_price']) price_text_right = price_text price_text = '' sale_title_text = ba.Lstr(resource='store.saleText') sale_time_text = ba.timestring( int(sales[b_type]['to_end'] * 1000), centi=False, timeformat=ba.TimeFormat.MILLISECONDS) description_color = (0.5, 1.0, 0.5) description_color2 = (0.3, 1.0, 1.0) price_color = (0.2, 1, 0.2, 1.0) show_purchase_check = False if 'title_text' in b_info: ba.textwidget(edit=b_info['title_text'], color=title_color) if 'purchase_check' in b_info: ba.imagewidget(edit=b_info['purchase_check'], opacity=1.0 if show_purchase_check else 0.0) if 'price_widget' in b_info: ba.textwidget(edit=b_info['price_widget'], text=price_text, color=price_color) if 'price_widget_left' in b_info: ba.textwidget(edit=b_info['price_widget_left'], text=price_text_left) if 'price_widget_right' in b_info: ba.textwidget(edit=b_info['price_widget_right'], text=price_text_right) if 'price_slash_widget' in b_info: ba.imagewidget(edit=b_info['price_slash_widget'], opacity=sale_opacity) if 'sale_bg_widget' in b_info: ba.imagewidget(edit=b_info['sale_bg_widget'], opacity=sale_opacity) if 'sale_title_widget' in b_info: ba.textwidget(edit=b_info['sale_title_widget'], text=sale_title_text) if 'sale_time_widget' in b_info: ba.textwidget(edit=b_info['sale_time_widget'], text=sale_time_text) if 'button' in b_info: ba.buttonwidget(edit=b_info['button'], color=color, on_activate_call=call) if 'extra_backings' in b_info: for bck in b_info['extra_backings']: ba.imagewidget(edit=bck, color=color, opacity=extra_image_opacity) if 'extra_images' in b_info: for img in b_info['extra_images']: ba.imagewidget(edit=img, opacity=extra_image_opacity) if 'extra_texts' in b_info: for etxt in b_info['extra_texts']: ba.textwidget(edit=etxt, color=description_color) if 'extra_texts_2' in b_info: for etxt in b_info['extra_texts_2']: ba.textwidget(edit=etxt, color=description_color2) if 'descriptionText' in b_info: ba.textwidget(edit=b_info['descriptionText'], color=description_color)
def _select_with_delay(self, map_name: str) -> None: _ba.lock_all_input() ba.timer(0.1, _ba.unlock_all_input, timetype=ba.TimeType.REAL) ba.timer(0.1, ba.WeakCall(self._select, map_name), timetype=ba.TimeType.REAL)
def __init__(self, tournament_id: str, tournament_activity: ba.Activity = None, position: Tuple[float, float] = (0.0, 0.0), delegate: Any = None, scale: float = None, offset: Tuple[float, float] = (0.0, 0.0), on_close_call: Callable[[], Any] = None): # Needs some tidying. # pylint: disable=too-many-branches # pylint: disable=too-many-locals # pylint: disable=too-many-statements ba.set_analytics_screen('Tournament Entry Window') self._tournament_id = tournament_id self._tournament_info = (ba.app.tournament_info[self._tournament_id]) # Set a few vars depending on the tourney fee. self._fee = self._tournament_info['fee'] self._allow_ads = self._tournament_info['allowAds'] if self._fee == 4: self._purchase_name = 'tournament_entry_4' self._purchase_price_name = 'price.tournament_entry_4' elif self._fee == 3: self._purchase_name = 'tournament_entry_3' self._purchase_price_name = 'price.tournament_entry_3' elif self._fee == 2: self._purchase_name = 'tournament_entry_2' self._purchase_price_name = 'price.tournament_entry_2' elif self._fee == 1: self._purchase_name = 'tournament_entry_1' self._purchase_price_name = 'price.tournament_entry_1' else: if self._fee != 0: raise ValueError('invalid fee: ' + str(self._fee)) self._purchase_name = 'tournament_entry_0' self._purchase_price_name = 'price.tournament_entry_0' self._purchase_price: Optional[int] = None self._on_close_call = on_close_call if scale is None: uiscale = ba.app.uiscale scale = (2.3 if uiscale is ba.UIScale.SMALL else 1.65 if uiscale is ba.UIScale.MEDIUM else 1.23) self._delegate = delegate self._transitioning_out = False self._tournament_activity = tournament_activity self._width = 340 self._height = 220 bg_color = (0.5, 0.4, 0.6) # Creates our root_widget. popup.PopupWindow.__init__(self, position=position, size=(self._width, self._height), scale=scale, bg_color=bg_color, offset=offset, toolbar_visibility='menu_currency') self._last_ad_press_time = -9999.0 self._last_ticket_press_time = -9999.0 self._entering = False self._launched = False # Show the ad button only if we support ads *and* it has a level 1 fee. self._do_ad_btn = (_ba.has_video_ads() and self._allow_ads) x_offs = 0 if self._do_ad_btn else 85 self._cancel_button = ba.buttonwidget(parent=self.root_widget, position=(20, self._height - 30), size=(50, 50), scale=0.5, label='', color=bg_color, on_activate_call=self._on_cancel, autoselect=True, icon=ba.gettexture('crossOut'), iconscale=1.2) self._title_text = ba.textwidget( parent=self.root_widget, position=(self._width * 0.5, self._height - 20), size=(0, 0), h_align='center', v_align='center', scale=0.6, text=ba.Lstr(resource='tournamentEntryText'), maxwidth=200, color=(1, 1, 1, 0.4)) btn = self._pay_with_tickets_button = ba.buttonwidget( parent=self.root_widget, position=(30 + x_offs, 60), autoselect=True, button_type='square', size=(120, 120), label='', on_activate_call=self._on_pay_with_tickets_press) self._ticket_img_pos = (50 + x_offs, 94) self._ticket_img_pos_free = (50 + x_offs, 80) self._ticket_img = ba.imagewidget(parent=self.root_widget, draw_controller=btn, size=(80, 80), position=self._ticket_img_pos, texture=ba.gettexture('tickets')) self._ticket_cost_text_position = (87 + x_offs, 88) self._ticket_cost_text_position_free = (87 + x_offs, 120) self._ticket_cost_text = ba.textwidget( parent=self.root_widget, draw_controller=btn, position=self._ticket_cost_text_position, size=(0, 0), h_align='center', v_align='center', scale=0.6, text='', maxwidth=95, color=(0, 1, 0)) self._free_plays_remaining_text = ba.textwidget( parent=self.root_widget, draw_controller=btn, position=(87 + x_offs, 78), size=(0, 0), h_align='center', v_align='center', scale=0.33, text='', maxwidth=95, color=(0, 0.8, 0)) self._pay_with_ad_btn: Optional[ba.Widget] if self._do_ad_btn: btn = self._pay_with_ad_btn = ba.buttonwidget( parent=self.root_widget, position=(190, 60), autoselect=True, button_type='square', size=(120, 120), label='', on_activate_call=self._on_pay_with_ad_press) self._pay_with_ad_img = ba.imagewidget(parent=self.root_widget, draw_controller=btn, size=(80, 80), position=(210, 94), texture=ba.gettexture('tv')) self._ad_text_position = (251, 88) self._ad_text_position_remaining = (251, 92) have_ad_tries_remaining = ( self._tournament_info['adTriesRemaining'] is not None) self._ad_text = ba.textwidget( parent=self.root_widget, draw_controller=btn, position=self._ad_text_position_remaining if have_ad_tries_remaining else self._ad_text_position, size=(0, 0), h_align='center', v_align='center', scale=0.6, text=ba.Lstr(resource='watchAVideoText', fallback_resource='watchAnAdText'), maxwidth=95, color=(0, 1, 0)) ad_plays_remaining_text = ( '' if not have_ad_tries_remaining else '' + str(self._tournament_info['adTriesRemaining'])) self._ad_plays_remaining_text = ba.textwidget( parent=self.root_widget, draw_controller=btn, position=(251, 78), size=(0, 0), h_align='center', v_align='center', scale=0.33, text=ad_plays_remaining_text, maxwidth=95, color=(0, 0.8, 0)) ba.textwidget(parent=self.root_widget, position=(self._width * 0.5, 120), size=(0, 0), h_align='center', v_align='center', scale=0.6, text=ba.Lstr(resource='orText', subs=[('${A}', ''), ('${B}', '')]), maxwidth=35, color=(1, 1, 1, 0.5)) else: self._pay_with_ad_btn = None self._get_tickets_button: Optional[ba.Widget] if not ba.app.toolbars: self._get_tickets_button = ba.buttonwidget( parent=self.root_widget, position=(self._width - 190 + 110, 15), autoselect=True, scale=0.6, size=(120, 60), textcolor=(0.2, 1, 0.2), label=ba.charstr(ba.SpecialChar.TICKET), color=(0.6, 0.4, 0.7), on_activate_call=self._on_get_tickets_press) else: self._get_tickets_button = None self._seconds_remaining = None ba.containerwidget(edit=self.root_widget, cancel_button=self._cancel_button) # Let's also ask the server for info about this tournament # (time remaining, etc) so we can show the user time remaining, # disallow entry if time has run out, etc. xoffs = 104 if ba.app.toolbars else 0 self._time_remaining_text = ba.textwidget(parent=self.root_widget, position=(70 + xoffs, 23), size=(0, 0), h_align='center', v_align='center', text='-', scale=0.65, maxwidth=100, flatness=1.0, color=(0.7, 0.7, 0.7)) self._time_remaining_label_text = ba.textwidget( parent=self.root_widget, position=(70 + xoffs, 40), size=(0, 0), h_align='center', v_align='center', text=ba.Lstr(resource='coopSelectWindow.timeRemainingText'), scale=0.45, flatness=1.0, maxwidth=100, color=(0.7, 0.7, 0.7)) self._last_query_time: Optional[float] = None # If there seems to be a relatively-recent valid cached info for this # tournament, use it. Otherwise we'll kick off a query ourselves. if (self._tournament_id in ba.app.tournament_info and ba.app.tournament_info[self._tournament_id]['valid'] and (ba.time(ba.TimeType.REAL, ba.TimeFormat.MILLISECONDS) - ba.app.tournament_info[self._tournament_id]['timeReceived'] < 1000 * 60 * 5)): try: info = ba.app.tournament_info[self._tournament_id] self._seconds_remaining = max( 0, info['timeRemaining'] - int( (ba.time(ba.TimeType.REAL, ba.TimeFormat.MILLISECONDS) - info['timeReceived']) / 1000)) self._have_valid_data = True self._last_query_time = ba.time(ba.TimeType.REAL) except Exception: ba.print_exception('error using valid tourney data') self._have_valid_data = False else: self._have_valid_data = False self._fg_state = ba.app.fg_state self._running_query = False self._update_timer = ba.Timer(1.0, ba.WeakCall(self._update), repeat=True, timetype=ba.TimeType.REAL) self._update() self._restore_state()
def end_game(self) -> None: # Tell our bots to celebrate just to rub it in. self._bots.final_celebrate() ba.setmusic(None) ba.pushcall(ba.WeakCall(self.do_end, 'defeat'))
def __init__(self, parent: ba.Widget, position: Tuple[float, float], initial_color: Sequence[float] = (1.0, 1.0, 1.0), delegate: Any = None, scale: float = None, offset: Tuple[float, float] = (0.0, 0.0), tag: Any = ''): # pylint: disable=too-many-locals del parent # Unused var. from ba.internal import get_player_colors c_raw = get_player_colors() assert len(c_raw) == 16 self.colors = [c_raw[0:4], c_raw[4:8], c_raw[8:12], c_raw[12:16]] uiscale = ba.app.uiscale if scale is None: scale = (2.3 if uiscale is ba.UIScale.SMALL else 1.65 if uiscale is ba.UIScale.MEDIUM else 1.23) self._delegate = delegate self._transitioning_out = False self._tag = tag self._color = list(initial_color) self._last_press_time = ba.time(ba.TimeType.REAL, ba.TimeFormat.MILLISECONDS) self._last_press_color_name: Optional[str] = None self._last_press_increasing: Optional[bool] = None self._change_speed = 1.0 width = 180.0 height = 240.0 # Creates our _root_widget. PopupWindow.__init__(self, position=position, size=(width, height), scale=scale, focus_position=(10, 10), focus_size=(width - 20, height - 20), bg_color=(0.5, 0.5, 0.5), offset=offset) self._swatch = ba.imagewidget(parent=self.root_widget, position=(width * 0.5 - 50, height - 70), size=(100, 70), texture=ba.gettexture('buttonSquare'), color=(1, 0, 0)) x = 50 y = height - 90 self._label_r: ba.Widget self._label_g: ba.Widget self._label_b: ba.Widget for color_name, color_val in [('r', (1, 0.15, 0.15)), ('g', (0.15, 1, 0.15)), ('b', (0.15, 0.15, 1))]: txt = ba.textwidget(parent=self.root_widget, position=(x - 10, y), size=(0, 0), h_align='center', color=color_val, v_align='center', text='0.12') setattr(self, '_label_' + color_name, txt) for b_label, bhval, binc in [('-', 30, False), ('+', 75, True)]: ba.buttonwidget(parent=self.root_widget, position=(x + bhval, y - 15), scale=0.8, repeat=True, text_scale=1.3, size=(40, 40), label=b_label, autoselect=True, enable_sound=False, on_activate_call=ba.WeakCall( self._color_change_press, color_name, binc)) y -= 42 btn = ba.buttonwidget(parent=self.root_widget, position=(width * 0.5 - 40, 10), size=(80, 30), text_scale=0.6, color=(0.6, 0.6, 0.6), textcolor=(0.7, 0.7, 0.7), label=ba.Lstr(resource='doneText'), on_activate_call=ba.WeakCall( self._transition_out), autoselect=True) ba.containerwidget(edit=self.root_widget, start_button=btn) # Unlike the swatch picker, we stay open and constantly push our # color to the delegate, so start doing that. self._update_for_color()
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 from ba.internal import get_league_rank_points if not self._root_widget: return 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_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 = 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)) 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_account_misc_read_val('proPowerRankingBoost', 0.0)) * 0.01 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.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 = (get_league_rank_points(data, 'trophyCount')) total_trophies_value = (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(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-branches # pylint: disable=too-many-locals # pylint: disable=too-many-statements from bastd.ui.confirm import QuitWindow from bastd.ui.store.button import StoreButton # Clear everything that was there. children = self._root_widget.get_children() for child in children: child.delete() self._tdelay = 0.0 self._t_delay_inc = 0.0 self._t_delay_play = 0.0 self._button_width = 200.0 self._button_height = 45.0 self._r = 'mainMenu' app = ba.app self._have_quit_button = (app.ui.uiscale is ba.UIScale.LARGE or (app.platform == 'windows' and app.subplatform == 'oculus')) self._have_store_button = not self._in_game self._have_settings_button = ( (not self._in_game or not app.toolbar_test) and not (self._is_demo or self._is_arcade or self._is_iircade)) self._input_device = input_device = _ba.get_ui_input_device() self._input_player = input_device.player if input_device else None self._connected_to_remote_player = ( input_device.is_connected_to_remote_player() if input_device else False) positions: List[Tuple[float, float, float]] = [] self._p_index = 0 if self._in_game: h, v, scale = self._refresh_in_game(positions) else: h, v, scale = self._refresh_not_in_game(positions) if self._have_settings_button: h, v, scale = positions[self._p_index] self._p_index += 1 self._settings_button = ba.buttonwidget( parent=self._root_widget, position=(h - self._button_width * 0.5 * scale, v), size=(self._button_width, self._button_height), scale=scale, autoselect=self._use_autoselect, label=ba.Lstr(resource=self._r + '.settingsText'), transition_delay=self._tdelay, on_activate_call=self._settings) # Scattered eggs on easter. if _ba.get_account_misc_read_val('easter', False) and not self._in_game: icon_size = 34 ba.imagewidget(parent=self._root_widget, position=(h - icon_size * 0.5 - 15, v + self._button_height * scale - icon_size * 0.24 + 1.5), transition_delay=self._tdelay, size=(icon_size, icon_size), texture=ba.gettexture('egg3'), tilt_scale=0.0) self._tdelay += self._t_delay_inc if self._in_game: h, v, scale = positions[self._p_index] self._p_index += 1 # If we're in a replay, we have a 'Leave Replay' button. if _ba.is_in_replay(): ba.buttonwidget(parent=self._root_widget, position=(h - self._button_width * 0.5 * scale, v), scale=scale, size=(self._button_width, self._button_height), autoselect=self._use_autoselect, label=ba.Lstr(resource='replayEndText'), on_activate_call=self._confirm_end_replay) elif _ba.get_foreground_host_session() is not None: ba.buttonwidget( parent=self._root_widget, position=(h - self._button_width * 0.5 * scale, v), scale=scale, size=(self._button_width, self._button_height), autoselect=self._use_autoselect, label=ba.Lstr(resource=self._r + '.endGameText'), on_activate_call=self._confirm_end_game) # Assume we're in a client-session. else: ba.buttonwidget( parent=self._root_widget, position=(h - self._button_width * 0.5 * scale, v), scale=scale, size=(self._button_width, self._button_height), autoselect=self._use_autoselect, label=ba.Lstr(resource=self._r + '.leavePartyText'), on_activate_call=self._confirm_leave_party) self._store_button: Optional[ba.Widget] if self._have_store_button: this_b_width = self._button_width h, v, scale = positions[self._p_index] self._p_index += 1 sbtn = self._store_button_instance = StoreButton( parent=self._root_widget, position=(h - this_b_width * 0.5 * scale, v), size=(this_b_width, self._button_height), scale=scale, on_activate_call=ba.WeakCall(self._on_store_pressed), sale_scale=1.3, transition_delay=self._tdelay) self._store_button = store_button = sbtn.get_button() uiscale = ba.app.ui.uiscale icon_size = (55 if uiscale is ba.UIScale.SMALL else 55 if uiscale is ba.UIScale.MEDIUM else 70) ba.imagewidget( parent=self._root_widget, position=(h - icon_size * 0.5, v + self._button_height * scale - icon_size * 0.23), transition_delay=self._tdelay, size=(icon_size, icon_size), texture=ba.gettexture(self._store_char_tex), tilt_scale=0.0, draw_controller=store_button) self._tdelay += self._t_delay_inc else: self._store_button = None self._quit_button: Optional[ba.Widget] if not self._in_game and self._have_quit_button: h, v, scale = positions[self._p_index] self._p_index += 1 self._quit_button = quit_button = ba.buttonwidget( parent=self._root_widget, autoselect=self._use_autoselect, position=(h - self._button_width * 0.5 * scale, v), size=(self._button_width, self._button_height), scale=scale, label=ba.Lstr(resource=self._r + ('.quitText' if 'Mac' in ba.app.user_agent_string else '.exitGameText')), on_activate_call=self._quit, transition_delay=self._tdelay) # Scattered eggs on easter. if _ba.get_account_misc_read_val('easter', False): icon_size = 30 ba.imagewidget(parent=self._root_widget, position=(h - icon_size * 0.5 + 25, v + self._button_height * scale - icon_size * 0.24 + 1.5), transition_delay=self._tdelay, size=(icon_size, icon_size), texture=ba.gettexture('egg1'), tilt_scale=0.0) ba.containerwidget(edit=self._root_widget, cancel_button=quit_button) self._tdelay += self._t_delay_inc else: self._quit_button = None # If we're not in-game, have no quit button, and this is android, # we want back presses to quit our activity. if (not self._in_game and not self._have_quit_button and ba.app.platform == 'android'): def _do_quit() -> None: QuitWindow(swish=True, back=True) ba.containerwidget(edit=self._root_widget, on_cancel_call=_do_quit) # Add speed-up/slow-down buttons for replays. # (ideally this should be part of a fading-out playback bar like most # media players but this works for now). if _ba.is_in_replay(): b_size = 50.0 b_buffer = 10.0 t_scale = 0.75 uiscale = ba.app.ui.uiscale if uiscale is ba.UIScale.SMALL: b_size *= 0.6 b_buffer *= 1.0 v_offs = -40 t_scale = 0.5 elif uiscale is ba.UIScale.MEDIUM: v_offs = -70 else: v_offs = -100 self._replay_speed_text = ba.textwidget( parent=self._root_widget, text=ba.Lstr(resource='watchWindow.playbackSpeedText', subs=[('${SPEED}', str(1.23))]), position=(h, v + v_offs + 7 * t_scale), h_align='center', v_align='center', size=(0, 0), scale=t_scale) # Update to current value. self._change_replay_speed(0) # Keep updating in a timer in case it gets changed elsewhere. self._change_replay_speed_timer = ba.Timer( 0.25, ba.WeakCall(self._change_replay_speed, 0), timetype=ba.TimeType.REAL, repeat=True) btn = ba.buttonwidget(parent=self._root_widget, position=(h - b_size - b_buffer, v - b_size - b_buffer + v_offs), button_type='square', size=(b_size, b_size), label='', autoselect=True, on_activate_call=ba.Call( self._change_replay_speed, -1)) ba.textwidget( parent=self._root_widget, draw_controller=btn, text='-', position=(h - b_size * 0.5 - b_buffer, v - b_size * 0.5 - b_buffer + 5 * t_scale + v_offs), h_align='center', v_align='center', size=(0, 0), scale=3.0 * t_scale) btn = ba.buttonwidget( parent=self._root_widget, position=(h + b_buffer, v - b_size - b_buffer + v_offs), button_type='square', size=(b_size, b_size), label='', autoselect=True, on_activate_call=ba.Call(self._change_replay_speed, 1)) ba.textwidget( parent=self._root_widget, draw_controller=btn, text='+', position=(h + b_size * 0.5 + b_buffer, v - b_size * 0.5 - b_buffer + 5 * t_scale + v_offs), h_align='center', v_align='center', size=(0, 0), scale=3.0 * t_scale)
def __init__(self, transition: str = 'in_right', modal: bool = False, origin_widget: ba.Widget = None): from ba.internal import get_cached_league_rank_data from ba.deprecated import get_resource ba.set_analytics_screen('League Rank Window') self._league_rank_data: Optional[Dict[str, Any]] = None self._modal = modal # 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._width = 1320 if ba.app.small_ui else 1120 x_inset = 100 if ba.app.small_ui else 0 self._height = (657 if ba.app.small_ui else 710 if ba.app.med_ui else 800) self._r = 'coopSelectWindow' self._rdict = get_resource(self._r) top_extra = 20 if ba.app.small_ui else 0 self._league_url_arg = '' self._is_current_season = False self._can_do_more_button = True super().__init__(root_widget=ba.containerwidget( size=(self._width, self._height + top_extra), stack_offset=(0, -15) if ba.app.small_ui else ( 0, 10) if ba.app.med_ui else (0, 0), transition=transition, scale_origin_stack_offset=scale_origin, scale=( 1.2 if ba.app.small_ui else 0.93 if ba.app.med_ui else 0.8))) self._back_button = btn = ba.buttonwidget( parent=self._root_widget, position=(75 + x_inset, self._height - 87 - (4 if ba.app.small_ui else 0)), size=(120, 60), scale=1.2, autoselect=True, label=ba.Lstr(resource='doneText' if self._modal else 'backText'), button_type=None if self._modal else 'back', on_activate_call=self._back) self._title_text = ba.textwidget( parent=self._root_widget, position=(self._width * 0.5, self._height - 56), size=(0, 0), text=ba.Lstr( resource='league.leagueRankText', fallback_resource='coopSelectWindow.powerRankingText'), h_align="center", color=ba.app.title_color, scale=1.4, maxwidth=600, v_align="center") ba.buttonwidget(edit=btn, button_type='backSmall', position=(75 + x_inset, self._height - 87 - (2 if ba.app.small_ui else 0)), size=(60, 55), label=ba.charstr(ba.SpecialChar.BACK)) self._scroll_width = self._width - (130 + 2 * x_inset) self._scroll_height = self._height - 160 self._scrollwidget = ba.scrollwidget(parent=self._root_widget, highlight=False, position=(65 + x_inset, 70), size=(self._scroll_width, self._scroll_height), center_small_content=True) ba.widget(edit=self._scrollwidget, autoselect=True) ba.containerwidget(edit=self._scrollwidget, claims_left_right=True) ba.containerwidget(edit=self._root_widget, cancel_button=self._back_button, selected_child=self._back_button) self._last_power_ranking_query_time: Optional[float] = None self._doing_power_ranking_query = False self._subcontainer: Optional[ba.Widget] = None self._subcontainerwidth = 800 self._subcontainerheight = 483 self._power_ranking_score_widgets: List[ba.Widget] = [] self._season_popup_menu: Optional[popup_ui.PopupMenu] = None self._requested_season: Optional[str] = None self._season: Optional[str] = None # take note of our account state; we'll refresh later if this changes self._account_state = _ba.get_account_state() self._refresh() self._restore_state() # if we've got cached power-ranking data already, display it info = get_cached_league_rank_data() if info is not None: self._update_for_league_rank_data(info) self._update_timer = ba.Timer(1.0, ba.WeakCall(self._update), timetype=ba.TimeType.REAL, repeat=True) self._update(show=(info is None))
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_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 __init__(self, origin: Sequence[float] = (0, 0)): _ba.set_party_window_open(True) self._r = 'partyWindow' self._popup_type: Optional[str] = None self._popup_party_member_client_id: Optional[int] = None self._popup_party_member_is_host: Optional[bool] = None self._width = 500 uiscale = ba.app.ui.uiscale self._height = (365 if uiscale is ba.UIScale.SMALL else 480 if uiscale is ba.UIScale.MEDIUM else 600) super().__init__(root_widget=ba.containerwidget( size=(self._width, self._height), transition='in_scale', color=(0.40, 0.55, 0.20), parent=_ba.get_special_widget('overlay_stack'), on_outside_click_call=self.close_with_sound, scale_origin_stack_offset=origin, scale=(2.0 if uiscale is ba.UIScale.SMALL else 1.35 if uiscale is ba.UIScale.MEDIUM else 1.0), stack_offset=(0, -10) if uiscale is ba.UIScale.SMALL else ( 240, 0) if uiscale is ba.UIScale.MEDIUM else (330, 20))) self._cancel_button = ba.buttonwidget(parent=self._root_widget, scale=0.7, position=(30, self._height - 47), size=(50, 50), label='', on_activate_call=self.close, autoselect=True, color=(0.45, 0.63, 0.15), icon=ba.gettexture('crossOut'), iconscale=1.2) ba.containerwidget(edit=self._root_widget, cancel_button=self._cancel_button) self._menu_button = ba.buttonwidget( parent=self._root_widget, scale=0.7, position=(self._width - 60, self._height - 47), size=(50, 50), label='...', autoselect=True, button_type='square', on_activate_call=ba.WeakCall(self._on_menu_button_press), color=(0.55, 0.73, 0.25), iconscale=1.2) info = _ba.get_connection_to_host_info() if info.get('name', '') != '': title = ba.Lstr(value=info['name']) else: title = ba.Lstr(resource=self._r + '.titleText') self._title_text = ba.textwidget(parent=self._root_widget, scale=0.9, color=(0.5, 0.7, 0.5), text=title, size=(0, 0), position=(self._width * 0.5, self._height - 29), maxwidth=self._width * 0.7, h_align='center', v_align='center') self._empty_str = ba.textwidget(parent=self._root_widget, scale=0.75, size=(0, 0), position=(self._width * 0.5, self._height - 65), maxwidth=self._width * 0.85, h_align='center', v_align='center') self._scroll_width = self._width - 50 self._scrollwidget = ba.scrollwidget(parent=self._root_widget, size=(self._scroll_width, self._height - 200), position=(30, 80), color=(0.4, 0.6, 0.3)) self._columnwidget = ba.columnwidget(parent=self._scrollwidget, border=2, margin=0) ba.widget(edit=self._menu_button, down_widget=self._columnwidget) self._muted_text = ba.textwidget( parent=self._root_widget, position=(self._width * 0.5, self._height * 0.5), size=(0, 0), h_align='center', v_align='center', text=ba.Lstr(resource='chatMutedText')) self._chat_texts: List[ba.Widget] = [] # add all existing messages if chat is not muted if not ba.app.config.resolve('Chat Muted'): msgs = _ba.get_chat_messages() for msg in msgs: self._add_msg(msg) self._text_field = txt = ba.textwidget( parent=self._root_widget, editable=True, size=(530, 40), position=(44, 39), text='', maxwidth=494, shadow=0.3, flatness=1.0, description=ba.Lstr(resource=self._r + '.chatMessageText'), autoselect=True, v_align='center', corner_scale=0.7) ba.widget(edit=self._scrollwidget, autoselect=True, left_widget=self._cancel_button, up_widget=self._cancel_button, down_widget=self._text_field) ba.widget(edit=self._columnwidget, autoselect=True, up_widget=self._cancel_button, down_widget=self._text_field) ba.containerwidget(edit=self._root_widget, selected_child=txt) btn = ba.buttonwidget(parent=self._root_widget, size=(50, 35), label=ba.Lstr(resource=self._r + '.sendText'), button_type='square', autoselect=True, position=(self._width - 70, 35), on_activate_call=self._send_chat_message) ba.textwidget(edit=txt, on_return_press_call=btn.activate) self._name_widgets: List[ba.Widget] = [] self._roster: Optional[List[Dict[str, Any]]] = None self._update_timer = ba.Timer(1.0, ba.WeakCall(self._update), repeat=True, timetype=ba.TimeType.REAL) self._update()
def __init__(self, position: Sequence[float] = (0.0, 1.0, 0.0), velocity: Sequence[float] = (0.0, 0.0, 0.0), bomb_type: str = 'normal', blast_radius: float = 2.0, source_player: ba.Player = None, owner: ba.Node = None): """Create a new Bomb. bomb_type can be 'ice','impact','land_mine','normal','sticky', or 'tnt'. Note that for impact or land_mine bombs you have to call arm() before they will go off. """ super().__init__() factory = get_factory() if bomb_type not in ('ice', 'impact', 'land_mine', 'normal', 'sticky', 'tnt'): raise ValueError('invalid bomb type: ' + bomb_type) self.bomb_type = bomb_type self._exploded = False self.texture_sequence: Optional[ba.Node] = None if self.bomb_type == 'sticky': self._last_sticky_sound_time = 0.0 self.blast_radius = blast_radius if self.bomb_type == 'ice': self.blast_radius *= 1.2 elif self.bomb_type == 'impact': self.blast_radius *= 0.7 elif self.bomb_type == 'land_mine': self.blast_radius *= 0.7 elif self.bomb_type == 'tnt': self.blast_radius *= 1.45 self._explode_callbacks: List[Callable[[Bomb, Blast], Any]] = [] # the player this came from self.source_player = source_player # by default our hit type/subtype is our own, but we pick up types of # whoever sets us off so we know what caused a chain reaction self.hit_type = 'explosion' self.hit_subtype = self.bomb_type # if no owner was provided, use an unconnected node ref # (nevermind; trying to use None in these type cases instead) # if owner is None: # owner = ba.Node(None) # the node this came from self.owner = owner # adding footing-materials to things can screw up jumping and flying # since players carrying those things # and thus touching footing objects will think they're on solid # ground.. perhaps we don't wanna add this even in the tnt case?.. materials: Tuple[ba.Material, ...] if self.bomb_type == 'tnt': materials = (factory.bomb_material, ba.sharedobj('footing_material'), ba.sharedobj('object_material')) else: materials = (factory.bomb_material, ba.sharedobj('object_material')) if self.bomb_type == 'impact': materials = materials + (factory.impact_blast_material, ) elif self.bomb_type == 'land_mine': materials = materials + (factory.land_mine_no_explode_material, ) if self.bomb_type == 'sticky': materials = materials + (factory.sticky_material, ) else: materials = materials + (factory.normal_sound_material, ) if self.bomb_type == 'land_mine': fuse_time = None self.node = ba.newnode('prop', delegate=self, attrs={ 'position': position, 'velocity': velocity, 'model': factory.land_mine_model, 'light_model': factory.land_mine_model, 'body': 'landMine', 'shadow_size': 0.44, 'color_texture': factory.land_mine_tex, 'reflection': 'powerup', 'reflection_scale': [1.0], 'materials': materials }) elif self.bomb_type == 'tnt': fuse_time = None self.node = ba.newnode('prop', delegate=self, attrs={ 'position': position, 'velocity': velocity, 'model': factory.tnt_model, 'light_model': factory.tnt_model, 'body': 'crate', 'shadow_size': 0.5, 'color_texture': factory.tnt_tex, 'reflection': 'soft', 'reflection_scale': [0.23], 'materials': materials }) elif self.bomb_type == 'impact': fuse_time = 20.0 self.node = ba.newnode('prop', delegate=self, attrs={ 'position': position, 'velocity': velocity, 'body': 'sphere', 'model': factory.impact_bomb_model, 'shadow_size': 0.3, 'color_texture': factory.impact_tex, 'reflection': 'powerup', 'reflection_scale': [1.5], 'materials': materials }) self.arm_timer = ba.Timer( 0.2, ba.WeakCall(self.handlemessage, ArmMessage())) self.warn_timer = ba.Timer( fuse_time - 1.7, ba.WeakCall(self.handlemessage, WarnMessage())) else: fuse_time = 3.0 if self.bomb_type == 'sticky': sticky = True model = factory.sticky_bomb_model rtype = 'sharper' rscale = 1.8 else: sticky = False model = factory.bomb_model rtype = 'sharper' rscale = 1.8 if self.bomb_type == 'ice': tex = factory.ice_tex elif self.bomb_type == 'sticky': tex = factory.sticky_tex else: tex = factory.regular_tex self.node = ba.newnode('bomb', delegate=self, attrs={ 'position': position, 'velocity': velocity, 'model': model, 'shadow_size': 0.3, 'color_texture': tex, 'sticky': sticky, 'owner': owner, 'reflection': rtype, 'reflection_scale': [rscale], 'materials': materials }) sound = ba.newnode('sound', owner=self.node, attrs={ 'sound': factory.fuse_sound, 'volume': 0.25 }) self.node.connectattr('position', sound, 'position') ba.animate(self.node, 'fuse_length', {0.0: 1.0, fuse_time: 0.0}) # Light the fuse!!! if self.bomb_type not in ('land_mine', 'tnt'): assert fuse_time is not None ba.timer(fuse_time, ba.WeakCall(self.handlemessage, ExplodeMessage())) ba.animate(self.node, 'model_scale', {0: 0, 0.2: 1.3, 0.26: 1})
def _refresh(self, select_soundtrack: str = None) -> None: self._allow_changing_soundtracks = False old_selection = self._selected_soundtrack # If there was no prev selection, look in prefs. if old_selection is None: old_selection = ba.app.config.get('Soundtrack') old_selection_index = self._selected_soundtrack_index # Delete old. while self._soundtrack_widgets: self._soundtrack_widgets.pop().delete() self._soundtracks = ba.app.config.get('Soundtracks', {}) assert self._soundtracks is not None items = list(self._soundtracks.items()) items.sort(key=lambda x: x[0].lower()) items = [('__default__', None)] + items # default is always first index = 0 for pname, _pval in items: assert pname is not None txtw = ba.textwidget( parent=self._col, size=(self._width - 40, 24), text=self._get_soundtrack_display_name(pname), h_align='left', v_align='center', maxwidth=self._width - 110, always_highlight=True, on_select_call=ba.WeakCall(self._select, pname, index), on_activate_call=self._edit_soundtrack_with_sound, selectable=True) if index == 0: ba.widget(edit=txtw, up_widget=self._back_button) self._soundtrack_widgets.append(txtw) # Select this one if the user requested it if select_soundtrack is not None: if pname == select_soundtrack: ba.columnwidget(edit=self._col, selected_child=txtw, visible_child=txtw) else: # Select this one if it was previously selected. # Go by index if there's one. if old_selection_index is not None: if index == old_selection_index: ba.columnwidget(edit=self._col, selected_child=txtw, visible_child=txtw) else: # Otherwise look by name. if pname == old_selection: ba.columnwidget(edit=self._col, selected_child=txtw, visible_child=txtw) index += 1 # Explicitly run select callback on current one and re-enable # callbacks. # Eww need to run this in a timer so it happens after our select # callbacks. With a small-enough time sometimes it happens before # anyway. Ew. need a way to just schedule a callable i guess. ba.timer(0.1, ba.WeakCall(self._set_allow_changing), timetype=ba.TimeType.REAL)
def __init__(self, parent: ba.Widget, position: Sequence[float], size: Sequence[float], scale: float, on_activate_call: Callable[[], Any] = None, transition_delay: float = None, color: Sequence[float] = None, textcolor: Sequence[float] = None, show_tickets: bool = False, button_type: str = None, sale_scale: float = 1.0): self._position = position self._size = size self._scale = scale if on_activate_call is None: on_activate_call = ba.WeakCall(self._default_on_activate_call) self._on_activate_call = on_activate_call self._button = ba.buttonwidget( parent=parent, size=size, label='' if show_tickets else ba.Lstr(resource='storeText'), scale=scale, autoselect=True, on_activate_call=self._on_activate, transition_delay=transition_delay, color=color, button_type=button_type) self._title_text: Optional[ba.Widget] self._ticket_text: Optional[ba.Widget] if show_tickets: self._title_text = ba.textwidget( parent=parent, position=(position[0] + size[0] * 0.5 * scale, position[1] + size[1] * 0.65 * scale), size=(0, 0), h_align='center', v_align='center', maxwidth=size[0] * scale * 0.65, text=ba.Lstr(resource='storeText'), draw_controller=self._button, scale=scale, transition_delay=transition_delay, color=textcolor) self._ticket_text = ba.textwidget( parent=parent, size=(0, 0), h_align='center', v_align='center', maxwidth=size[0] * scale * 0.85, text='', color=(0.2, 1.0, 0.2), flatness=1.0, shadow=0.0, scale=scale * 0.6, transition_delay=transition_delay) else: self._title_text = None self._ticket_text = None self._circle_rad = 12 * scale self._circle_center = (0.0, 0.0) self._sale_circle_center = (0.0, 0.0) self._available_purchase_backing = ba.imagewidget( parent=parent, color=(1, 0, 0), draw_controller=self._button, size=(2.2 * self._circle_rad, 2.2 * self._circle_rad), texture=ba.gettexture('circleShadow'), transition_delay=transition_delay) self._available_purchase_text = ba.textwidget( parent=parent, size=(0, 0), h_align='center', v_align='center', text='', draw_controller=self._button, color=(1, 1, 1), flatness=1.0, shadow=1.0, scale=0.6 * scale, maxwidth=self._circle_rad * 1.4, transition_delay=transition_delay) self._sale_circle_rad = 18 * scale * sale_scale self._sale_backing = ba.imagewidget( parent=parent, color=(0.5, 0, 1.0), draw_controller=self._button, size=(2 * self._sale_circle_rad, 2 * self._sale_circle_rad), texture=ba.gettexture('circleZigZag'), transition_delay=transition_delay) self._sale_title_text = ba.textwidget( parent=parent, size=(0, 0), h_align='center', v_align='center', draw_controller=self._button, color=(0, 1, 0), flatness=1.0, shadow=0.0, scale=0.5 * scale * sale_scale, maxwidth=self._sale_circle_rad * 1.5, transition_delay=transition_delay) self._sale_time_text = ba.textwidget(parent=parent, size=(0, 0), h_align='center', v_align='center', draw_controller=self._button, color=(0, 1, 0), flatness=1.0, shadow=0.0, scale=0.4 * scale * sale_scale, maxwidth=self._sale_circle_rad * 1.5, transition_delay=transition_delay) self.set_position(position) self._update_timer = ba.Timer(1.0, ba.WeakCall(self._update), repeat=True, timetype=ba.TimeType.REAL) self._update()
def __init__(self, existing_profile: Optional[str], in_main_menu: bool, transition: str = 'in_right'): # FIXME: Tidy this up a bit. # pylint: disable=too-many-branches # pylint: disable=too-many-statements # pylint: disable=too-many-locals from ba.internal import get_player_profile_colors self._in_main_menu = in_main_menu self._existing_profile = existing_profile self._r = 'editProfileWindow' self._spazzes: List[str] = [] self._icon_textures: List[ba.Texture] = [] self._icon_tint_textures: List[ba.Texture] = [] # Grab profile colors or pick random ones. self._color, self._highlight = get_player_profile_colors( existing_profile) self._width = width = 780.0 if ba.app.small_ui else 680.0 self._x_inset = x_inset = 50.0 if ba.app.small_ui else 0.0 self._height = height = (350.0 if ba.app.small_ui else 400.0 if ba.app.med_ui else 450.0) spacing = 40 self._base_scale = (2.05 if ba.app.small_ui else 1.5 if ba.app.med_ui else 1.0) top_extra = 15 if ba.app.small_ui else 15 super().__init__(root_widget=ba.containerwidget( size=(width, height + top_extra), transition=transition, scale=self._base_scale, stack_offset=(0, 15) if ba.app.small_ui else (0, 0))) cancel_button = btn = ba.buttonwidget( parent=self._root_widget, position=(52 + x_inset, height - 60), size=(155, 60), scale=0.8, autoselect=True, label=ba.Lstr(resource='cancelText'), on_activate_call=self._cancel) ba.containerwidget(edit=self._root_widget, cancel_button=btn) save_button = btn = ba.buttonwidget(parent=self._root_widget, position=(width - (177 + x_inset), height - 60), size=(155, 60), autoselect=True, scale=0.8, label=ba.Lstr(resource='saveText')) ba.widget(edit=save_button, left_widget=cancel_button) ba.widget(edit=cancel_button, right_widget=save_button) ba.containerwidget(edit=self._root_widget, start_button=btn) ba.textwidget(parent=self._root_widget, position=(self._width * 0.5, height - 38), size=(0, 0), text=(ba.Lstr(resource=self._r + '.titleNewText') if existing_profile is None else ba.Lstr( resource=self._r + '.titleEditText')), color=ba.app.title_color, maxwidth=290, scale=1.0, h_align="center", v_align="center") # Make a list of spaz icons. self.refresh_characters() profile = ba.app.config.get('Player Profiles', {}).get(self._existing_profile, {}) if 'global' in profile: self._global = profile['global'] else: self._global = False if 'icon' in profile: self._icon = profile['icon'] else: self._icon = ba.charstr(ba.SpecialChar.LOGO) assigned_random_char = False # Look for existing character choice or pick random one otherwise. try: icon_index = self._spazzes.index(profile['character']) except Exception: # Let's set the default icon to spaz for our first profile; after # that we go random. # (SCRATCH THAT.. we now hard-code account-profiles to start with # spaz which has a similar effect) # try: p_len = len(ba.app.config['Player Profiles']) # except Exception: p_len = 0 # if p_len == 0: icon_index = self._spazzes.index('Spaz') # else: random.seed() icon_index = random.randrange(len(self._spazzes)) assigned_random_char = True self._icon_index = icon_index ba.buttonwidget(edit=save_button, on_activate_call=self.save) v = height - 115.0 self._name = ('' if self._existing_profile is None else self._existing_profile) self._is_account_profile = (self._name == '__account__') # If we just picked a random character, see if it has specific # colors/highlights associated with it and assign them if so. if assigned_random_char: clr = ba.app.spaz_appearances[ self._spazzes[icon_index]].default_color if clr is not None: self._color = clr highlight = ba.app.spaz_appearances[ self._spazzes[icon_index]].default_highlight if highlight is not None: self._highlight = highlight # Assign a random name if they had none. if self._name == '': names = _ba.get_random_names() self._name = names[random.randrange(len(names))] self._clipped_name_text = ba.textwidget(parent=self._root_widget, text='', position=(540 + x_inset, v - 8), flatness=1.0, shadow=0.0, scale=0.55, size=(0, 0), maxwidth=100, h_align='center', v_align='center', color=(1, 1, 0, 0.5)) if not self._is_account_profile and not self._global: ba.textwidget(parent=self._root_widget, text=ba.Lstr(resource=self._r + '.nameText'), position=(200 + x_inset, v - 6), size=(0, 0), h_align='right', v_align='center', color=(1, 1, 1, 0.5), scale=0.9) self._upgrade_button = None if self._is_account_profile: if _ba.get_account_state() == 'signed_in': sval = _ba.get_account_display_string() else: sval = '??' ba.textwidget(parent=self._root_widget, position=(self._width * 0.5, v - 7), size=(0, 0), scale=1.2, text=sval, maxwidth=270, h_align='center', v_align='center') txtl = ba.Lstr( resource='editProfileWindow.accountProfileText').evaluate() b_width = min( 270.0, _ba.get_string_width(txtl, suppress_warning=True) * 0.6) ba.textwidget(parent=self._root_widget, position=(self._width * 0.5, v - 39), size=(0, 0), scale=0.6, color=ba.app.infotextcolor, text=txtl, maxwidth=270, h_align='center', v_align='center') self._account_type_info_button = ba.buttonwidget( parent=self._root_widget, label='?', size=(15, 15), text_scale=0.6, position=(self._width * 0.5 + b_width * 0.5 + 13, v - 47), button_type='square', color=(0.6, 0.5, 0.65), autoselect=True, on_activate_call=self.show_account_profile_info) elif self._global: b_size = 60 self._icon_button = btn = ba.buttonwidget( parent=self._root_widget, autoselect=True, position=(self._width * 0.5 - 160 - b_size * 0.5, v - 38 - 15), size=(b_size, b_size), color=(0.6, 0.5, 0.6), label='', button_type='square', text_scale=1.2, on_activate_call=self._on_icon_press) self._icon_button_label = ba.textwidget( parent=self._root_widget, position=(self._width * 0.5 - 160, v - 35), draw_controller=btn, h_align='center', v_align='center', size=(0, 0), color=(1, 1, 1), text='', scale=2.0) ba.textwidget(parent=self._root_widget, h_align='center', v_align='center', position=(self._width * 0.5 - 160, v - 55 - 15), size=(0, 0), draw_controller=btn, text=ba.Lstr(resource=self._r + '.iconText'), scale=0.7, color=ba.app.title_color, maxwidth=120) self._update_icon() ba.textwidget(parent=self._root_widget, position=(self._width * 0.5, v - 7), size=(0, 0), scale=1.2, text=self._name, maxwidth=240, h_align='center', v_align='center') # FIXME hard coded strings are bad txtl = ba.Lstr( resource='editProfileWindow.globalProfileText').evaluate() b_width = min( 240.0, _ba.get_string_width(txtl, suppress_warning=True) * 0.6) ba.textwidget(parent=self._root_widget, position=(self._width * 0.5, v - 39), size=(0, 0), scale=0.6, color=ba.app.infotextcolor, text=txtl, maxwidth=240, h_align='center', v_align='center') self._account_type_info_button = ba.buttonwidget( parent=self._root_widget, label='?', size=(15, 15), text_scale=0.6, position=(self._width * 0.5 + b_width * 0.5 + 13, v - 47), button_type='square', color=(0.6, 0.5, 0.65), autoselect=True, on_activate_call=self.show_global_profile_info) else: self._text_field = ba.textwidget( parent=self._root_widget, position=(220 + x_inset, v - 30), size=(265, 40), text=self._name, h_align='left', v_align='center', max_chars=16, description=ba.Lstr(resource=self._r + '.nameDescriptionText'), autoselect=True, editable=True, padding=4, color=(0.9, 0.9, 0.9, 1.0), on_return_press_call=ba.Call(save_button.activate)) # FIXME hard coded strings are bad txtl = ba.Lstr( resource='editProfileWindow.localProfileText').evaluate() b_width = min( 270.0, _ba.get_string_width(txtl, suppress_warning=True) * 0.6) ba.textwidget(parent=self._root_widget, position=(self._width * 0.5, v - 43), size=(0, 0), scale=0.6, color=ba.app.infotextcolor, text=txtl, maxwidth=270, h_align='center', v_align='center') self._account_type_info_button = ba.buttonwidget( parent=self._root_widget, label='?', size=(15, 15), text_scale=0.6, position=(self._width * 0.5 + b_width * 0.5 + 13, v - 50), button_type='square', color=(0.6, 0.5, 0.65), autoselect=True, on_activate_call=self.show_local_profile_info) self._upgrade_button = ba.buttonwidget( parent=self._root_widget, label=ba.Lstr(resource='upgradeText'), size=(40, 17), text_scale=1.0, button_type='square', position=(self._width * 0.5 + b_width * 0.5 + 13 + 43, v - 51), color=(0.6, 0.5, 0.65), autoselect=True, on_activate_call=self.upgrade_profile) self._update_clipped_name() self._clipped_name_timer = ba.Timer(0.333, ba.WeakCall( self._update_clipped_name), timetype=ba.TimeType.REAL, repeat=True) v -= spacing * 3.0 b_size = 80 b_size_2 = 100 b_offs = 150 self._color_button = btn = ba.buttonwidget( parent=self._root_widget, autoselect=True, position=(self._width * 0.5 - b_offs - b_size * 0.5, v - 50), size=(b_size, b_size), color=self._color, label='', button_type='square') origin = self._color_button.get_screen_space_center() ba.buttonwidget(edit=self._color_button, on_activate_call=ba.WeakCall(self._make_picker, 'color', origin)) ba.textwidget(parent=self._root_widget, h_align='center', v_align='center', position=(self._width * 0.5 - b_offs, v - 65), size=(0, 0), draw_controller=btn, text=ba.Lstr(resource=self._r + '.colorText'), scale=0.7, color=ba.app.title_color, maxwidth=120) self._character_button = btn = ba.buttonwidget( parent=self._root_widget, autoselect=True, position=(self._width * 0.5 - b_size_2 * 0.5, v - 60), up_widget=self._account_type_info_button, on_activate_call=self._on_character_press, size=(b_size_2, b_size_2), label='', color=(1, 1, 1), mask_texture=ba.gettexture('characterIconMask')) if not self._is_account_profile and not self._global: ba.containerwidget(edit=self._root_widget, selected_child=self._text_field) ba.textwidget(parent=self._root_widget, h_align='center', v_align='center', position=(self._width * 0.5, v - 80), size=(0, 0), draw_controller=btn, text=ba.Lstr(resource=self._r + '.characterText'), scale=0.7, color=ba.app.title_color, maxwidth=130) self._highlight_button = btn = ba.buttonwidget( parent=self._root_widget, autoselect=True, position=(self._width * 0.5 + b_offs - b_size * 0.5, v - 50), up_widget=self._upgrade_button if self._upgrade_button is not None else self._account_type_info_button, size=(b_size, b_size), color=self._highlight, label='', button_type='square') if not self._is_account_profile and not self._global: ba.widget(edit=cancel_button, down_widget=self._text_field) ba.widget(edit=save_button, down_widget=self._text_field) ba.widget(edit=self._color_button, up_widget=self._text_field) ba.widget(edit=self._account_type_info_button, down_widget=self._character_button) origin = self._highlight_button.get_screen_space_center() ba.buttonwidget(edit=self._highlight_button, on_activate_call=ba.WeakCall(self._make_picker, 'highlight', origin)) ba.textwidget(parent=self._root_widget, h_align='center', v_align='center', position=(self._width * 0.5 + b_offs, v - 65), size=(0, 0), draw_controller=btn, text=ba.Lstr(resource=self._r + '.highlightText'), scale=0.7, color=ba.app.title_color, maxwidth=120) self._update_character()
def __init__(self, parent: ba.Widget, position: Tuple[float, float], initial_color: Sequence[float] = (1.0, 1.0, 1.0), delegate: Any = None, scale: float = None, offset: Tuple[float, float] = (0.0, 0.0), tag: Any = ''): # pylint: disable=too-many-locals from ba.internal import have_pro, get_player_colors c_raw = get_player_colors() assert len(c_raw) == 16 self.colors = [c_raw[0:4], c_raw[4:8], c_raw[8:12], c_raw[12:16]] uiscale = ba.app.uiscale if scale is None: scale = (2.3 if uiscale is ba.UIScale.SMALL else 1.65 if uiscale is ba.UIScale.MEDIUM else 1.23) self._parent = parent self._position = position self._scale = scale self._offset = offset self._delegate = delegate self._transitioning_out = False self._tag = tag self._initial_color = initial_color # Create our _root_widget. PopupWindow.__init__(self, position=position, size=(210, 240), scale=scale, focus_position=(10, 10), focus_size=(190, 220), bg_color=(0.5, 0.5, 0.5), offset=offset) rows: List[List[ba.Widget]] = [] closest_dist = 9999.0 closest = (0, 0) for y in range(4): row: List[ba.Widget] = [] rows.append(row) for x in range(4): color = self.colors[y][x] dist = (abs(color[0] - initial_color[0]) + abs(color[1] - initial_color[1]) + abs(color[2] - initial_color[2])) if dist < closest_dist: closest = (x, y) closest_dist = dist btn = ba.buttonwidget(parent=self.root_widget, position=(22 + 45 * x, 185 - 45 * y), size=(35, 40), label='', button_type='square', on_activate_call=ba.WeakCall( self._select, x, y), autoselect=True, color=color, extra_touch_border_scale=0.0) row.append(btn) other_button = ba.buttonwidget( parent=self.root_widget, position=(105 - 60, 13), color=(0.7, 0.7, 0.7), text_scale=0.5, textcolor=(0.8, 0.8, 0.8), size=(120, 30), label=ba.Lstr(resource='otherText', fallback_resource='coopSelectWindow.customText'), autoselect=True, on_activate_call=ba.WeakCall(self._select_other)) # Custom colors are limited to pro currently. if not have_pro(): ba.imagewidget(parent=self.root_widget, position=(50, 12), size=(30, 30), texture=ba.gettexture('lock'), draw_controller=other_button) # If their color is close to one of our swatches, select it. # Otherwise select 'other'. if closest_dist < 0.03: ba.containerwidget(edit=self.root_widget, selected_child=rows[closest[1]][closest[0]]) else: ba.containerwidget(edit=self.root_widget, selected_child=other_button)
def __init__(self, queue_id: str, address: str, port: int): ba.app.ui.have_party_queue_window = True self._address = address self._port = port self._queue_id = queue_id self._width = 800 self._height = 400 self._last_connect_attempt_time: Optional[float] = None self._last_transaction_time: Optional[float] = None self._boost_button: Optional[ba.Widget] = None self._boost_price: Optional[ba.Widget] = None self._boost_label: Optional[ba.Widget] = None self._field_shown = False self._dudes: list[PartyQueueWindow.Dude] = [] self._dudes_by_id: dict[int, PartyQueueWindow.Dude] = {} self._line_left = 40.0 self._line_width = self._width - 190 self._line_bottom = self._height * 0.4 self.lineup_tex = ba.gettexture('playerLineup') self._smoothing = 0.0 self._initial_offset = 0.0 self._boost_tickets = 0 self._boost_strength = 0.0 self._angry_computer_transparent_model = ba.getmodel( 'angryComputerTransparent') self._angry_computer_image: Optional[ba.Widget] = None self.lineup_1_transparent_model = ba.getmodel( 'playerLineup1Transparent') self._lineup_2_transparent_model = ba.getmodel( 'playerLineup2Transparent') self._lineup_3_transparent_model = ba.getmodel( 'playerLineup3Transparent') self._lineup_4_transparent_model = ba.getmodel( 'playerLineup4Transparent') self._line_image: Optional[ba.Widget] = None self.eyes_model = ba.getmodel('plasticEyesTransparent') self._white_tex = ba.gettexture('white') uiscale = ba.app.ui.uiscale super().__init__(root_widget=ba.containerwidget( size=(self._width, self._height), color=(0.45, 0.63, 0.15), transition='in_scale', scale=(1.4 if uiscale is ba.UIScale.SMALL else 1.2 if uiscale is ba.UIScale.MEDIUM else 1.0))) self._cancel_button = ba.buttonwidget(parent=self._root_widget, scale=1.0, position=(60, self._height - 80), size=(50, 50), label='', on_activate_call=self.close, autoselect=True, color=(0.45, 0.63, 0.15), icon=ba.gettexture('crossOut'), iconscale=1.2) ba.containerwidget(edit=self._root_widget, cancel_button=self._cancel_button) self._title_text = ba.textwidget( parent=self._root_widget, position=(self._width * 0.5, self._height * 0.55), size=(0, 0), color=(1.0, 3.0, 1.0), scale=1.3, h_align='center', v_align='center', text=ba.Lstr(resource='internal.connectingToPartyText'), maxwidth=self._width * 0.65) self._tickets_text = ba.textwidget(parent=self._root_widget, position=(self._width - 180, self._height - 20), size=(0, 0), color=(0.2, 1.0, 0.2), scale=0.7, h_align='center', v_align='center', text='') # update at roughly 30fps self._update_timer = ba.Timer(0.033, ba.WeakCall(self.update), repeat=True, timetype=ba.TimeType.REAL) self.update()
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.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_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_account_ticket_count()) except Exception: t_str = '?' if self._get_tickets_button is not None: ba.buttonwidget(edit=self._get_tickets_button, label=ba.charstr(ba.SpecialChar.TICKET) + t_str)
def __init__(self, parent: PartyQueueWindow, distance: float, initial_offset: float, is_player: bool, account_id: str, name: str): self.claimed = False self._line_left = parent.get_line_left() self._line_width = parent.get_line_width() self._line_bottom = parent.get_line_bottom() self._target_distance = distance self._distance = distance + initial_offset self._boost_brightness = 0.0 self._debug = False self._sc = sc = 1.1 if is_player else 0.6 + random.random() * 0.2 self._y_offs = -30.0 if is_player else -47.0 * sc self._last_boost_time = 0.0 self._color = (0.2, 1.0, 0.2) if is_player else (0.5 + 0.3 * random.random(), 0.4 + 0.2 * random.random(), 0.5 + 0.3 * random.random()) self._eye_color = (0.7 * 1.0 + 0.3 * self._color[0], 0.7 * 1.0 + 0.3 * self._color[1], 0.7 * 1.0 + 0.3 * self._color[2]) self._body_image = ba.buttonwidget( parent=parent.get_root_widget(), selectable=True, label='', size=(sc * 60, sc * 80), color=self._color, texture=parent.lineup_tex, model_transparent=parent.lineup_1_transparent_model) ba.buttonwidget(edit=self._body_image, on_activate_call=ba.WeakCall( parent.on_account_press, account_id, self._body_image)) ba.widget(edit=self._body_image, autoselect=True) self._eyes_image = ba.imagewidget( parent=parent.get_root_widget(), size=(sc * 36, sc * 18), texture=parent.lineup_tex, color=self._eye_color, model_transparent=parent.eyes_model) self._name_text = ba.textwidget(parent=parent.get_root_widget(), size=(0, 0), shadow=0, flatness=1.0, text=name, maxwidth=100, h_align='center', v_align='center', scale=0.75, color=(1, 1, 1, 0.6)) self._update_image() # DEBUG: vis target pos.. self._body_image_target: Optional[ba.Widget] self._eyes_image_target: Optional[ba.Widget] if self._debug: self._body_image_target = ba.imagewidget( parent=parent.get_root_widget(), size=(sc * 60, sc * 80), color=self._color, texture=parent.lineup_tex, model_transparent=parent.lineup_1_transparent_model) self._eyes_image_target = ba.imagewidget( parent=parent.get_root_widget(), size=(sc * 36, sc * 18), texture=parent.lineup_tex, color=self._eye_color, model_transparent=parent.eyes_model) # (updates our image positions) self.set_target_distance(self._target_distance) else: self._body_image_target = self._eyes_image_target = None
def on_begin(self) -> None: # pylint: disable=too-many-branches # pylint: disable=too-many-locals # pylint: disable=too-many-statements from bastd.actor.text import Text from bastd.actor.image import Image from ba.deprecated import get_resource ba.set_analytics_screen('FreeForAll Series Victory Screen' if self. _is_ffa else 'Teams Series Victory Screen') if ba.app.uiscale is ba.UIScale.LARGE: sval = ba.Lstr(resource='pressAnyKeyButtonPlayAgainText') else: sval = ba.Lstr(resource='pressAnyButtonPlayAgainText') self._show_up_next = False self._custom_continue_message = sval super().on_begin() winning_sessionteam = self.settings_raw['winner'] # Pause a moment before playing victory music. ba.timer(0.6, ba.WeakCall(self._play_victory_music)) ba.timer(4.4, ba.WeakCall(self._show_winner, self.settings_raw['winner'])) ba.timer(4.6, ba.Call(ba.playsound, self._score_display_sound)) # Score / Name / Player-record. player_entries: List[Tuple[int, str, ba.PlayerRecord]] = [] # Note: for ffa, exclude players who haven't entered the game yet. if self._is_ffa: for _pkey, prec in self.stats.get_records().items(): if prec.player.in_game: player_entries.append( (prec.player.sessionteam.customdata['score'], prec.getname(full=True), prec)) player_entries.sort(reverse=True, key=lambda x: x[0]) else: for _pkey, prec in self.stats.get_records().items(): player_entries.append((prec.score, prec.name_full, prec)) player_entries.sort(reverse=True, key=lambda x: x[0]) ts_height = 300.0 ts_h_offs = -390.0 tval = 6.4 t_incr = 0.12 always_use_first_to = get_resource('bestOfUseFirstToInstead') session = self.session if self._is_ffa: assert isinstance(session, ba.FreeForAllSession) txt = ba.Lstr( value='${A}:', subs=[('${A}', ba.Lstr(resource='firstToFinalText', subs=[('${COUNT}', str(session.get_ffa_series_length()))])) ]) else: assert isinstance(session, ba.MultiTeamSession) # Some languages may prefer to always show 'first to X' instead of # 'best of X'. # FIXME: This will affect all clients connected to us even if # they're not using this language. Should try to come up # with a wording that works everywhere. if always_use_first_to: txt = ba.Lstr( value='${A}:', subs=[ ('${A}', ba.Lstr(resource='firstToFinalText', subs=[ ('${COUNT}', str(session.get_series_length() / 2 + 1)) ])) ]) else: txt = ba.Lstr( value='${A}:', subs=[('${A}', ba.Lstr(resource='bestOfFinalText', subs=[('${COUNT}', str(session.get_series_length()))])) ]) Text(txt, v_align=Text.VAlign.CENTER, maxwidth=300, color=(0.5, 0.5, 0.5, 1.0), position=(0, 220), scale=1.2, transition=Text.Transition.IN_TOP_SLOW, h_align=Text.HAlign.CENTER, transition_delay=t_incr * 4).autoretain() win_score = (session.get_series_length() - 1) // 2 + 1 lose_score = 0 for team in self.teams: if team.sessionteam.customdata['score'] != win_score: lose_score = team.sessionteam.customdata['score'] if not self._is_ffa: Text(ba.Lstr(resource='gamesToText', subs=[('${WINCOUNT}', str(win_score)), ('${LOSECOUNT}', str(lose_score))]), color=(0.5, 0.5, 0.5, 1.0), maxwidth=160, v_align=Text.VAlign.CENTER, position=(0, -215), scale=1.8, transition=Text.Transition.IN_LEFT, h_align=Text.HAlign.CENTER, transition_delay=4.8 + t_incr * 4).autoretain() if self._is_ffa: v_extra = 120 else: v_extra = 0 mvp: Optional[ba.PlayerRecord] = None mvp_name: Optional[str] = None # Show game MVP. if not self._is_ffa: mvp, mvp_name = None, None for entry in player_entries: if entry[2].team == winning_sessionteam: mvp = entry[2] mvp_name = entry[1] break if mvp is not None: Text(ba.Lstr(resource='mostValuablePlayerText'), color=(0.5, 0.5, 0.5, 1.0), v_align=Text.VAlign.CENTER, maxwidth=300, position=(180, ts_height / 2 + 15), transition=Text.Transition.IN_LEFT, h_align=Text.HAlign.LEFT, transition_delay=tval).autoretain() tval += 4 * t_incr Image(mvp.get_icon(), position=(230, ts_height / 2 - 55 + 14 - 5), scale=(70, 70), transition=Image.Transition.IN_LEFT, transition_delay=tval).autoretain() assert mvp_name is not None Text(ba.Lstr(value=mvp_name), position=(280, ts_height / 2 - 55 + 15 - 5), h_align=Text.HAlign.LEFT, v_align=Text.VAlign.CENTER, maxwidth=170, scale=1.3, color=ba.safecolor(mvp.team.color + (1, )), transition=Text.Transition.IN_LEFT, transition_delay=tval).autoretain() tval += 4 * t_incr # Most violent. most_kills = 0 for entry in player_entries: if entry[2].kill_count >= most_kills: mvp = entry[2] mvp_name = entry[1] most_kills = entry[2].kill_count if mvp is not None: Text(ba.Lstr(resource='mostViolentPlayerText'), color=(0.5, 0.5, 0.5, 1.0), v_align=Text.VAlign.CENTER, maxwidth=300, position=(180, ts_height / 2 - 150 + v_extra + 15), transition=Text.Transition.IN_LEFT, h_align=Text.HAlign.LEFT, transition_delay=tval).autoretain() Text(ba.Lstr(value='(${A})', subs=[('${A}', ba.Lstr(resource='killsTallyText', subs=[('${COUNT}', str(most_kills))])) ]), position=(260, ts_height / 2 - 150 - 15 + v_extra), color=(0.3, 0.3, 0.3, 1.0), scale=0.6, h_align=Text.HAlign.LEFT, transition=Text.Transition.IN_LEFT, transition_delay=tval).autoretain() tval += 4 * t_incr Image(mvp.get_icon(), position=(233, ts_height / 2 - 150 - 30 - 46 + 25 + v_extra), scale=(50, 50), transition=Image.Transition.IN_LEFT, transition_delay=tval).autoretain() assert mvp_name is not None Text(ba.Lstr(value=mvp_name), position=(270, ts_height / 2 - 150 - 30 - 36 + v_extra + 15), h_align=Text.HAlign.LEFT, v_align=Text.VAlign.CENTER, maxwidth=180, color=ba.safecolor(mvp.team.color + (1, )), transition=Text.Transition.IN_LEFT, transition_delay=tval).autoretain() tval += 4 * t_incr # Most killed. most_killed = 0 mkp, mkp_name = None, None for entry in player_entries: if entry[2].killed_count >= most_killed: mkp = entry[2] mkp_name = entry[1] most_killed = entry[2].killed_count if mkp is not None: Text(ba.Lstr(resource='mostViolatedPlayerText'), color=(0.5, 0.5, 0.5, 1.0), v_align=Text.VAlign.CENTER, maxwidth=300, position=(180, ts_height / 2 - 300 + v_extra + 15), transition=Text.Transition.IN_LEFT, h_align=Text.HAlign.LEFT, transition_delay=tval).autoretain() Text(ba.Lstr(value='(${A})', subs=[('${A}', ba.Lstr(resource='deathsTallyText', subs=[('${COUNT}', str(most_killed))])) ]), position=(260, ts_height / 2 - 300 - 15 + v_extra), h_align=Text.HAlign.LEFT, scale=0.6, color=(0.3, 0.3, 0.3, 1.0), transition=Text.Transition.IN_LEFT, transition_delay=tval).autoretain() tval += 4 * t_incr Image(mkp.get_icon(), position=(233, ts_height / 2 - 300 - 30 - 46 + 25 + v_extra), scale=(50, 50), transition=Image.Transition.IN_LEFT, transition_delay=tval).autoretain() assert mkp_name is not None Text(ba.Lstr(value=mkp_name), position=(270, ts_height / 2 - 300 - 30 - 36 + v_extra + 15), h_align=Text.HAlign.LEFT, v_align=Text.VAlign.CENTER, color=ba.safecolor(mkp.team.color + (1, )), maxwidth=180, transition=Text.Transition.IN_LEFT, transition_delay=tval).autoretain() tval += 4 * t_incr # Now show individual scores. tdelay = tval Text(ba.Lstr(resource='finalScoresText'), color=(0.5, 0.5, 0.5, 1.0), position=(ts_h_offs, ts_height / 2), transition=Text.Transition.IN_RIGHT, transition_delay=tdelay).autoretain() tdelay += 4 * t_incr v_offs = 0.0 tdelay += len(player_entries) * 8 * t_incr for _score, name, prec in player_entries: tdelay -= 4 * t_incr v_offs -= 40 Text(str(prec.team.customdata['score']) if self._is_ffa else str(prec.score), color=(0.5, 0.5, 0.5, 1.0), position=(ts_h_offs + 230, ts_height / 2 + v_offs), h_align=Text.HAlign.RIGHT, transition=Text.Transition.IN_RIGHT, transition_delay=tdelay).autoretain() tdelay -= 4 * t_incr Image(prec.get_icon(), position=(ts_h_offs - 72, ts_height / 2 + v_offs + 15), scale=(30, 30), transition=Image.Transition.IN_LEFT, transition_delay=tdelay).autoretain() Text(ba.Lstr(value=name), position=(ts_h_offs - 50, ts_height / 2 + v_offs + 15), h_align=Text.HAlign.LEFT, v_align=Text.VAlign.CENTER, maxwidth=180, color=ba.safecolor(prec.team.color + (1, )), transition=Text.Transition.IN_RIGHT, transition_delay=tdelay).autoretain() ba.timer(15.0, ba.WeakCall(self._show_tips))
def __init__(self, sessiontype: Type[ba.Session], transition: str = 'in_right', select_playlist: str = None, origin_widget: ba.Widget = None): # Yes this needs tidying. # pylint: disable=too-many-locals # pylint: disable=too-many-statements # pylint: disable=cyclic-import from bastd.ui import playlist 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._sessiontype = sessiontype self._pvars = playlist.PlaylistTypeVars(sessiontype) self._max_playlists = 30 self._r = 'gameListWindow' self._width = 750.0 if ba.app.small_ui else 650.0 x_inset = 50.0 if ba.app.small_ui else 0.0 self._height = (380.0 if ba.app.small_ui else 420.0 if ba.app.med_ui else 500.0) top_extra = 20.0 if ba.app.small_ui else 0.0 super().__init__(root_widget=ba.containerwidget( size=(self._width, self._height + top_extra), transition=transition, scale_origin_stack_offset=scale_origin, scale=(2.05 if ba.app.small_ui else 1.5 if ba.app.med_ui else 1.0), stack_offset=(0, -10) if ba.app.small_ui else (0, 0))) self._back_button = back_button = btn = ba.buttonwidget( parent=self._root_widget, position=(43 + x_inset, self._height - 60), size=(160, 68), scale=0.77, autoselect=True, text_scale=1.3, label=ba.Lstr(resource='backText'), button_type='back') ba.textwidget(parent=self._root_widget, position=(0, self._height - 47), size=(self._width, 25), text=ba.Lstr(resource=self._r + '.titleText', subs=[('${TYPE}', self._pvars.window_title_name)]), color=ba.app.heading_color, maxwidth=290, h_align='center', v_align='center') ba.buttonwidget(edit=btn, button_type='backSmall', size=(60, 60), label=ba.charstr(ba.SpecialChar.BACK)) v = self._height - 59.0 h = 41 + x_inset b_color = (0.6, 0.53, 0.63) b_textcolor = (0.75, 0.7, 0.8) self._lock_images: List[ba.Widget] = [] lock_tex = ba.gettexture('lock') scl = (1.1 if ba.app.small_ui else 1.27 if ba.app.med_ui else 1.57) scl *= 0.63 v -= 65.0 * scl new_button = btn = ba.buttonwidget( parent=self._root_widget, position=(h, v), size=(90, 58.0 * scl), on_activate_call=self._new_playlist, color=b_color, autoselect=True, button_type='square', textcolor=b_textcolor, text_scale=0.7, label=ba.Lstr(resource='newText', fallback_resource=self._r + '.newText')) self._lock_images.append( ba.imagewidget(parent=self._root_widget, size=(30, 30), draw_controller=btn, position=(h - 10, v + 58.0 * scl - 28), texture=lock_tex)) v -= 65.0 * scl self._edit_button = edit_button = btn = ba.buttonwidget( parent=self._root_widget, position=(h, v), size=(90, 58.0 * scl), on_activate_call=self._edit_playlist, color=b_color, autoselect=True, textcolor=b_textcolor, button_type='square', text_scale=0.7, label=ba.Lstr(resource='editText', fallback_resource=self._r + '.editText')) self._lock_images.append( ba.imagewidget(parent=self._root_widget, size=(30, 30), draw_controller=btn, position=(h - 10, v + 58.0 * scl - 28), texture=lock_tex)) v -= 65.0 * scl duplicate_button = btn = ba.buttonwidget( parent=self._root_widget, position=(h, v), size=(90, 58.0 * scl), on_activate_call=self._duplicate_playlist, color=b_color, autoselect=True, textcolor=b_textcolor, button_type='square', text_scale=0.7, label=ba.Lstr(resource='duplicateText', fallback_resource=self._r + '.duplicateText')) self._lock_images.append( ba.imagewidget(parent=self._root_widget, size=(30, 30), draw_controller=btn, position=(h - 10, v + 58.0 * scl - 28), texture=lock_tex)) v -= 65.0 * scl delete_button = btn = ba.buttonwidget( parent=self._root_widget, position=(h, v), size=(90, 58.0 * scl), on_activate_call=self._delete_playlist, color=b_color, autoselect=True, textcolor=b_textcolor, button_type='square', text_scale=0.7, label=ba.Lstr(resource='deleteText', fallback_resource=self._r + '.deleteText')) self._lock_images.append( ba.imagewidget(parent=self._root_widget, size=(30, 30), draw_controller=btn, position=(h - 10, v + 58.0 * scl - 28), texture=lock_tex)) v -= 65.0 * scl self._import_button = ba.buttonwidget( parent=self._root_widget, position=(h, v), size=(90, 58.0 * scl), on_activate_call=self._import_playlist, color=b_color, autoselect=True, textcolor=b_textcolor, button_type='square', text_scale=0.7, label=ba.Lstr(resource='importText')) v -= 65.0 * scl btn = ba.buttonwidget(parent=self._root_widget, position=(h, v), size=(90, 58.0 * scl), on_activate_call=self._share_playlist, color=b_color, autoselect=True, textcolor=b_textcolor, button_type='square', text_scale=0.7, label=ba.Lstr(resource='shareText')) self._lock_images.append( ba.imagewidget(parent=self._root_widget, size=(30, 30), draw_controller=btn, position=(h - 10, v + 58.0 * scl - 28), texture=lock_tex)) v = self._height - 75 self._scroll_height = self._height - 119 scrollwidget = ba.scrollwidget(parent=self._root_widget, position=(140 + x_inset, v - self._scroll_height), size=(self._width - (180 + 2 * x_inset), self._scroll_height + 10), highlight=False) ba.widget(edit=back_button, right_widget=scrollwidget) self._columnwidget = ba.columnwidget(parent=scrollwidget) h = 145 try: self._do_randomize_val = ba.app.config[self._pvars.config_name + ' Playlist Randomize'] except Exception: self._do_randomize_val = 0 h += 210 for btn in [new_button, delete_button, edit_button, duplicate_button]: ba.widget(edit=btn, right_widget=scrollwidget) ba.widget(edit=scrollwidget, left_widget=new_button, right_widget=_ba.get_special_widget('party_button') if ba.app.toolbars else None) # make sure config exists self._config_name_full = self._pvars.config_name + ' Playlists' if self._config_name_full not in ba.app.config: ba.app.config[self._config_name_full] = {} self._selected_playlist_name: Optional[str] = None self._selected_playlist_index: Optional[int] = None self._playlist_widgets: List[ba.Widget] = [] self._refresh(select_playlist=select_playlist) ba.buttonwidget(edit=back_button, on_activate_call=self._back) ba.containerwidget(edit=self._root_widget, cancel_button=back_button) ba.containerwidget(edit=self._root_widget, selected_child=scrollwidget) # Keep our lock images up to date/etc. self._update_timer = ba.Timer(1.0, ba.WeakCall(self._update), timetype=ba.TimeType.REAL, repeat=True) self._update()
def __init__(self, transition: str = 'in_right', modal: bool = False, show_tab: str = None, on_close_call: Callable[[], Any] = None, back_location: str = None, origin_widget: ba.Widget = None): # pylint: disable=too-many-statements # pylint: disable=too-many-locals from bastd.ui import tabs from ba import SpecialChar app = ba.app uiscale = app.uiscale ba.set_analytics_screen('Store Window') scale_origin: Optional[Tuple[float, float]] # If they provided an origin-widget, scale up from that. 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.button_infos: Optional[Dict[str, Dict[str, Any]]] = None self.update_buttons_timer: Optional[ba.Timer] = None self._status_textwidget_update_timer = None self._back_location = back_location self._on_close_call = on_close_call self._show_tab = show_tab self._modal = modal self._width = 1240 if uiscale is ba.UIScale.SMALL else 1040 self._x_inset = x_inset = 100 if uiscale is ba.UIScale.SMALL else 0 self._height = (578 if uiscale is ba.UIScale.SMALL else 645 if uiscale is ba.UIScale.MEDIUM else 800) self._current_tab: Optional[str] = None extra_top = 30 if uiscale is ba.UIScale.SMALL else 0 self._request: Any = None self._r = 'store' self._last_buy_time: Optional[float] = None super().__init__(root_widget=ba.containerwidget( size=(self._width, self._height + extra_top), transition=transition, toolbar_visibility='menu_full', scale=(1.3 if uiscale is ba.UIScale.SMALL else 0.9 if uiscale is ba.UIScale.MEDIUM else 0.8), scale_origin_stack_offset=scale_origin, stack_offset=((0, -5) if uiscale is ba.UIScale.SMALL else ( 0, 0) if uiscale is ba.UIScale.MEDIUM else (0, 0)))) self._back_button = btn = ba.buttonwidget( parent=self._root_widget, position=(70 + x_inset, self._height - 74), size=(140, 60), scale=1.1, autoselect=True, label=ba.Lstr(resource='doneText' if self._modal else 'backText'), button_type=None if self._modal else 'back', on_activate_call=self._back) ba.containerwidget(edit=self._root_widget, cancel_button=btn) self._get_tickets_button = ba.buttonwidget( parent=self._root_widget, size=(210, 65), on_activate_call=self._on_get_more_tickets_press, autoselect=True, scale=0.9, text_scale=1.4, left_widget=self._back_button, color=(0.7, 0.5, 0.85), textcolor=(0.2, 1.0, 0.2), label=ba.Lstr(resource='getTicketsWindow.titleText')) # Move this dynamically to keep it out of the way of the party icon. self._update_get_tickets_button_pos() self._get_ticket_pos_update_timer = ba.Timer( 1.0, ba.WeakCall(self._update_get_tickets_button_pos), repeat=True, timetype=ba.TimeType.REAL) ba.widget(edit=self._back_button, right_widget=self._get_tickets_button) self._ticket_text_update_timer = ba.Timer( 1.0, ba.WeakCall(self._update_tickets_text), timetype=ba.TimeType.REAL, repeat=True) self._update_tickets_text() app = ba.app if app.platform in ['mac', 'ios'] and app.subplatform == 'appstore': ba.buttonwidget( parent=self._root_widget, position=(self._width * 0.5 - 70, 16), size=(230, 50), scale=0.65, on_activate_call=ba.WeakCall(self._restore_purchases), color=(0.35, 0.3, 0.4), selectable=False, textcolor=(0.55, 0.5, 0.6), label=ba.Lstr( resource='getTicketsWindow.restorePurchasesText')) ba.textwidget(parent=self._root_widget, position=(self._width * 0.5, self._height - 44), size=(0, 0), color=app.title_color, scale=1.5, h_align='center', v_align='center', text=ba.Lstr(resource='storeText'), maxwidth=420) if not self._modal: ba.buttonwidget(edit=self._back_button, button_type='backSmall', size=(60, 60), label=ba.charstr(SpecialChar.BACK)) scroll_buffer_h = 130 + 2 * x_inset tab_buffer_h = 250 + 2 * x_inset tabs_def = [ ('extras', ba.Lstr(resource=self._r + '.extrasText')), ('maps', ba.Lstr(resource=self._r + '.mapsText')), ('minigames', ba.Lstr(resource=self._r + '.miniGamesText')), ('characters', ba.Lstr(resource=self._r + '.charactersText')), ('icons', ba.Lstr(resource=self._r + '.iconsText')), ] tab_results = tabs.create_tab_buttons(self._root_widget, tabs_def, pos=(tab_buffer_h * 0.5, self._height - 130), size=(self._width - tab_buffer_h, 50), on_select_call=self._set_tab, return_extra_info=True) self._purchasable_count_widgets: Dict[str, Dict[str, Any]] = {} # Create our purchasable-items tags and have them update over time. for i, tab in enumerate(tabs_def): pos = tab_results['positions'][i] size = tab_results['sizes'][i] button = tab_results['buttons_indexed'][i] rad = 10 center = (pos[0] + 0.1 * size[0], pos[1] + 0.9 * size[1]) img = ba.imagewidget(parent=self._root_widget, position=(center[0] - rad * 1.04, center[1] - rad * 1.15), size=(rad * 2.2, rad * 2.2), texture=ba.gettexture('circleShadow'), color=(1, 0, 0)) txt = ba.textwidget(parent=self._root_widget, position=center, size=(0, 0), h_align='center', v_align='center', maxwidth=1.4 * rad, scale=0.6, shadow=1.0, flatness=1.0) rad = 20 sale_img = ba.imagewidget(parent=self._root_widget, position=(center[0] - rad, center[1] - rad), size=(rad * 2, rad * 2), draw_controller=button, texture=ba.gettexture('circleZigZag'), color=(0.5, 0, 1.0)) sale_title_text = ba.textwidget(parent=self._root_widget, position=(center[0], center[1] + 0.24 * rad), size=(0, 0), h_align='center', v_align='center', draw_controller=button, maxwidth=1.4 * rad, scale=0.6, shadow=0.0, flatness=1.0, color=(0, 1, 0)) sale_time_text = ba.textwidget(parent=self._root_widget, position=(center[0], center[1] - 0.29 * rad), size=(0, 0), h_align='center', v_align='center', draw_controller=button, maxwidth=1.4 * rad, scale=0.4, shadow=0.0, flatness=1.0, color=(0, 1, 0)) self._purchasable_count_widgets[tab[0]] = { 'img': img, 'text': txt, 'sale_img': sale_img, 'sale_title_text': sale_title_text, 'sale_time_text': sale_time_text } self._tab_update_timer = ba.Timer(1.0, ba.WeakCall(self._update_tabs), timetype=ba.TimeType.REAL, repeat=True) self._update_tabs() self._tab_buttons = tab_results['buttons'] if self._get_tickets_button is not None: last_tab_button = self._tab_buttons[tabs_def[-1][0]] ba.widget(edit=self._get_tickets_button, down_widget=last_tab_button) ba.widget(edit=last_tab_button, up_widget=self._get_tickets_button, right_widget=self._get_tickets_button) self._scroll_width = self._width - scroll_buffer_h self._scroll_height = self._height - 180 self._scrollwidget: Optional[ba.Widget] = None self._status_textwidget: Optional[ba.Widget] = None self._restore_state()
def __init__(self, account_id: str, profile_id: str = None, position: Tuple[float, float] = (0.0, 0.0), scale: float = None, offset: Tuple[float, float] = (0.0, 0.0)): from ba.internal import is_browser_likely_available, master_server_get self._account_id = account_id self._profile_id = profile_id uiscale = ba.app.uiscale if scale is None: scale = (2.6 if uiscale is ba.UIScale.SMALL else 1.8 if uiscale is ba.UIScale.MEDIUM else 1.4) self._transitioning_out = False self._width = 400 self._height = (300 if uiscale is ba.UIScale.SMALL else 400 if uiscale is ba.UIScale.MEDIUM else 450) self._subcontainer: Optional[ba.Widget] = None bg_color = (0.5, 0.4, 0.6) # Creates our _root_widget. popup.PopupWindow.__init__(self, position=position, size=(self._width, self._height), scale=scale, bg_color=bg_color, offset=offset) self._cancel_button = ba.buttonwidget( parent=self.root_widget, position=(50, self._height - 30), size=(50, 50), scale=0.5, label='', color=bg_color, on_activate_call=self._on_cancel_press, autoselect=True, icon=ba.gettexture('crossOut'), iconscale=1.2) self._title_text = ba.textwidget( parent=self.root_widget, position=(self._width * 0.5, self._height - 20), size=(0, 0), h_align='center', v_align='center', scale=0.6, text=ba.Lstr(resource='playerInfoText'), maxwidth=200, color=(0.7, 0.7, 0.7, 0.7)) self._scrollwidget = ba.scrollwidget(parent=self.root_widget, size=(self._width - 60, self._height - 70), position=(30, 30), capture_arrows=True, simple_culling_v=10) ba.widget(edit=self._scrollwidget, autoselect=True) self._loading_text = ba.textwidget( parent=self._scrollwidget, scale=0.5, text=ba.Lstr(value='${A}...', subs=[('${A}', ba.Lstr(resource='loadingText'))]), size=(self._width - 60, 100), h_align='center', v_align='center') # In cases where the user most likely has a browser/email, lets # offer a 'report this user' button. if (is_browser_likely_available() and _ba.get_account_misc_read_val( 'showAccountExtrasMenu', False)): self._extras_menu_button = ba.buttonwidget( parent=self.root_widget, size=(20, 20), position=(self._width - 60, self._height - 30), autoselect=True, label='...', button_type='square', color=(0.64, 0.52, 0.69), textcolor=(0.57, 0.47, 0.57), on_activate_call=self._on_extras_menu_press) ba.containerwidget(edit=self.root_widget, cancel_button=self._cancel_button) master_server_get('bsAccountInfo', { 'buildNumber': ba.app.build_number, 'accountID': self._account_id, 'profileID': self._profile_id }, callback=ba.WeakCall(self._on_query_response))
def instantiate(self, scrollwidget: ba.Widget, tab_button: ba.Widget) -> None: """Create the store.""" # pylint: disable=too-many-locals # pylint: disable=too-many-branches # pylint: disable=too-many-nested-blocks from bastd.ui.store import item as storeitemui title_spacing = 40 button_border = 20 button_spacing = 4 boffs_h = 40 self._height = 80.0 # Calc total height. for i, section in enumerate(self._sections): if section['title'] != '': assert self._height is not None self._height += title_spacing b_width, b_height = section['button_size'] b_column_count = int( math.floor((self._width - boffs_h - 20) / (b_width + button_spacing))) b_row_count = int( math.ceil( float(len(section['items'])) / b_column_count)) b_height_total = ( 2 * button_border + b_row_count * b_height + (b_row_count - 1) * section['v_spacing']) self._height += b_height_total assert self._height is not None cnt2 = ba.containerwidget(parent=scrollwidget, scale=1.0, size=(self._width, self._height), background=False) ba.containerwidget(edit=cnt2, claims_left_right=True, claims_tab=True, selection_loop_to_parent=True) v = self._height - 20 if self._tab == 'characters': txt = ba.Lstr( resource='store.howToSwitchCharactersText', subs=[ ('${SETTINGS}', ba.Lstr( resource='accountSettingsWindow.titleText' )), ('${PLAYER_PROFILES}', ba.Lstr( resource='playerProfilesWindow.titleText') ) ]) ba.textwidget(parent=cnt2, text=txt, size=(0, 0), position=(self._width * 0.5, self._height - 28), h_align='center', v_align='center', color=(0.7, 1, 0.7, 0.4), scale=0.7, shadow=0, flatness=1.0, maxwidth=700, transition_delay=0.4) elif self._tab == 'icons': txt = ba.Lstr( resource='store.howToUseIconsText', subs=[ ('${SETTINGS}', ba.Lstr(resource='mainMenu.settingsText')), ('${PLAYER_PROFILES}', ba.Lstr( resource='playerProfilesWindow.titleText') ) ]) ba.textwidget(parent=cnt2, text=txt, size=(0, 0), position=(self._width * 0.5, self._height - 28), h_align='center', v_align='center', color=(0.7, 1, 0.7, 0.4), scale=0.7, shadow=0, flatness=1.0, maxwidth=700, transition_delay=0.4) elif self._tab == 'maps': assert self._width is not None assert self._height is not None txt = ba.Lstr(resource='store.howToUseMapsText') ba.textwidget(parent=cnt2, text=txt, size=(0, 0), position=(self._width * 0.5, self._height - 28), h_align='center', v_align='center', color=(0.7, 1, 0.7, 0.4), scale=0.7, shadow=0, flatness=1.0, maxwidth=700, transition_delay=0.4) prev_row_buttons: Optional[List] = None this_row_buttons = [] delay = 0.3 for section in self._sections: if section['title'] != '': ba.textwidget( parent=cnt2, position=(60, v - title_spacing * 0.8), size=(0, 0), scale=1.0, transition_delay=delay, color=(0.7, 0.9, 0.7, 1), h_align='left', v_align='center', text=ba.Lstr(resource=section['title']), maxwidth=self._width * 0.7) v -= title_spacing delay = max(0.100, delay - 0.100) v -= button_border b_width, b_height = section['button_size'] b_count = len(section['items']) b_column_count = int( math.floor((self._width - boffs_h - 20) / (b_width + button_spacing))) col = 0 item: Dict[str, Any] assert self._store_window.button_infos is not None for i, item_name in enumerate(section['items']): item = self._store_window.button_infos[ item_name] = {} item['call'] = ba.WeakCall(self._store_window.buy, item_name) if 'x_offs' in section: boffs_h2 = section['x_offs'] else: boffs_h2 = 0 if 'y_offs' in section: boffs_v2 = section['y_offs'] else: boffs_v2 = 0 b_pos = (boffs_h + boffs_h2 + (b_width + button_spacing) * col, v - b_height + boffs_v2) storeitemui.instantiate_store_item_display( item_name, item, parent_widget=cnt2, b_pos=b_pos, boffs_h=boffs_h, b_width=b_width, b_height=b_height, boffs_h2=boffs_h2, boffs_v2=boffs_v2, delay=delay) btn = item['button'] delay = max(0.1, delay - 0.1) this_row_buttons.append(btn) # Wire this button to the equivalent in the # previous row. if prev_row_buttons is not None: # pylint: disable=unsubscriptable-object if len(prev_row_buttons) > col: ba.widget(edit=btn, up_widget=prev_row_buttons[col]) ba.widget(edit=prev_row_buttons[col], down_widget=btn) # If we're the last button in our row, # wire any in the previous row past # our position to go to us if down is # pressed. if (col + 1 == b_column_count or i == b_count - 1): for b_prev in prev_row_buttons[col + 1:]: ba.widget(edit=b_prev, down_widget=btn) else: ba.widget(edit=btn, up_widget=prev_row_buttons[-1]) else: ba.widget(edit=btn, up_widget=tab_button) col += 1 if col == b_column_count or i == b_count - 1: prev_row_buttons = this_row_buttons this_row_buttons = [] col = 0 v -= b_height if i < b_count - 1: v -= section['v_spacing'] v -= button_border # Set a timer to update these buttons periodically as long # as we're alive (so if we buy one it will grey out, etc). self._store_window.update_buttons_timer = ba.Timer( 0.5, ba.WeakCall(self._store_window.update_buttons), repeat=True, timetype=ba.TimeType.REAL) # Also update them immediately. self._store_window.update_buttons()
def __init__(self, tournament_id: str, tournament_activity: ba.GameActivity = None, position: Tuple[float, float] = (0.0, 0.0), scale: float = None, offset: Tuple[float, float] = (0.0, 0.0), tint_color: Sequence[float] = (1.0, 1.0, 1.0), tint2_color: Sequence[float] = (1.0, 1.0, 1.0), selected_character: str = None, on_close_call: Callable[[], Any] = None): del tournament_activity # unused arg del tint_color # unused arg del tint2_color # unused arg del selected_character # unused arg self._tournament_id = tournament_id self._subcontainer: Optional[ba.Widget] = None self._on_close_call = on_close_call uiscale = ba.app.ui.uiscale if scale is None: scale = (2.3 if uiscale is ba.UIScale.SMALL else 1.65 if uiscale is ba.UIScale.MEDIUM else 1.23) self._transitioning_out = False self._width = 400 self._height = (300 if uiscale is ba.UIScale.SMALL else 370 if uiscale is ba.UIScale.MEDIUM else 450) bg_color = (0.5, 0.4, 0.6) # creates our _root_widget super().__init__(position=position, size=(self._width, self._height), scale=scale, bg_color=bg_color, offset=offset) # app = ba.app self._cancel_button = ba.buttonwidget( parent=self.root_widget, position=(50, self._height - 30), size=(50, 50), scale=0.5, label='', color=bg_color, on_activate_call=self._on_cancel_press, autoselect=True, icon=ba.gettexture('crossOut'), iconscale=1.2) self._title_text = ba.textwidget( parent=self.root_widget, position=(self._width * 0.5, self._height - 20), size=(0, 0), h_align='center', v_align='center', scale=0.6, text=ba.Lstr(resource='tournamentStandingsText'), maxwidth=200, color=(1, 1, 1, 0.4)) self._scrollwidget = ba.scrollwidget(parent=self.root_widget, size=(self._width - 60, self._height - 70), position=(30, 30), highlight=False, simple_culling_v=10) ba.widget(edit=self._scrollwidget, autoselect=True) self._loading_text = ba.textwidget( parent=self._scrollwidget, scale=0.5, text=ba.Lstr(value='${A}...', subs=[('${A}', ba.Lstr(resource='loadingText'))]), size=(self._width - 60, 100), h_align='center', v_align='center') ba.containerwidget(edit=self.root_widget, cancel_button=self._cancel_button) _ba.tournament_query(args={ 'tournamentIDs': [tournament_id], 'numScores': 50, 'source': 'scores window' }, callback=ba.WeakCall( self._on_tournament_query_response))