def __init__(self, sessiontype: type[ba.Session], playlist: str, scale_origin: tuple[float, float], delegate: Any = None): # FIXME: Tidy this up. # pylint: disable=too-many-branches # pylint: disable=too-many-statements # pylint: disable=too-many-locals from ba.internal import get_map_class, getclass, filter_playlist from bastd.ui.playlist import PlaylistTypeVars self._r = 'gameListWindow' self._delegate = delegate self._pvars = PlaylistTypeVars(sessiontype) self._transitioning_out = False # We behave differently if we're being used for playlist selection # vs starting a game directly (should make this more elegant). self._selecting_mode = ba.app.ui.selecting_private_party_playlist self._do_randomize_val = (ba.app.config.get( self._pvars.config_name + ' Playlist Randomize', 0)) self._sessiontype = sessiontype self._playlist = playlist self._width = 500.0 self._height = 330.0 - 50.0 # In teams games, show the custom names/colors button. if self._sessiontype is ba.DualTeamSession: self._height += 50.0 self._row_height = 45.0 # Grab our maps to display. model_opaque = ba.getmodel('level_select_button_opaque') model_transparent = ba.getmodel('level_select_button_transparent') mask_tex = ba.gettexture('mapPreviewMask') # Poke into this playlist and see if we can display some of its maps. map_textures = [] map_texture_entries = [] rows = 0 columns = 0 game_count = 0 scl = 0.35 c_width_total = 0.0 try: max_columns = 5 name = playlist if name == '__default__': plst = self._pvars.get_default_list_call() else: try: plst = ba.app.config[self._pvars.config_name + ' Playlists'][name] except Exception: print('ERROR INFO: self._config_name is:', self._pvars.config_name) print( 'ERROR INFO: playlist names are:', list(ba.app.config[self._pvars.config_name + ' Playlists'].keys())) raise plst = filter_playlist(plst, self._sessiontype, remove_unowned=False, mark_unowned=True) game_count = len(plst) for entry in plst: mapname = entry['settings']['map'] maptype: Optional[type[ba.Map]] try: maptype = get_map_class(mapname) except ba.NotFoundError: maptype = None if maptype is not None: tex_name = maptype.get_preview_texture_name() if tex_name is not None: map_textures.append(tex_name) map_texture_entries.append(entry) rows = (max(0, len(map_textures) - 1) // max_columns) + 1 columns = min(max_columns, len(map_textures)) if len(map_textures) == 1: scl = 1.1 elif len(map_textures) == 2: scl = 0.7 elif len(map_textures) == 3: scl = 0.55 else: scl = 0.35 self._row_height = 128.0 * scl c_width_total = scl * 250.0 * columns if map_textures: self._height += self._row_height * rows except Exception: ba.print_exception('Error listing playlist maps.') show_shuffle_check_box = game_count > 1 if show_shuffle_check_box: self._height += 40 # Creates our _root_widget. uiscale = ba.app.ui.uiscale scale = (1.69 if uiscale is ba.UIScale.SMALL else 1.1 if uiscale is ba.UIScale.MEDIUM else 0.85) super().__init__(position=scale_origin, size=(self._width, self._height), scale=scale) playlist_name: Union[str, ba.Lstr] = (self._pvars.default_list_name if playlist == '__default__' else playlist) self._title_text = ba.textwidget(parent=self.root_widget, position=(self._width * 0.5, self._height - 89 + 51), size=(0, 0), text=playlist_name, scale=1.4, color=(1, 1, 1), maxwidth=self._width * 0.7, h_align='center', v_align='center') self._cancel_button = ba.buttonwidget( parent=self.root_widget, position=(25, self._height - 53), size=(50, 50), scale=0.7, label='', color=(0.42, 0.73, 0.2), on_activate_call=self._on_cancel_press, autoselect=True, icon=ba.gettexture('crossOut'), iconscale=1.2) h_offs_img = self._width * 0.5 - c_width_total * 0.5 v_offs_img = self._height - 118 - scl * 125.0 + 50 bottom_row_buttons = [] self._have_at_least_one_owned = False for row in range(rows): for col in range(columns): tex_index = row * columns + col if tex_index < len(map_textures): tex_name = map_textures[tex_index] h = h_offs_img + scl * 250 * col v = v_offs_img - self._row_height * row entry = map_texture_entries[tex_index] owned = not (('is_unowned_map' in entry and entry['is_unowned_map']) or ('is_unowned_game' in entry and entry['is_unowned_game'])) if owned: self._have_at_least_one_owned = True try: desc = getclass(entry['type'], subclassof=ba.GameActivity ).get_settings_display_string(entry) if not owned: desc = ba.Lstr( value='${DESC}\n${UNLOCK}', subs=[ ('${DESC}', desc), ('${UNLOCK}', ba.Lstr( resource='unlockThisInTheStoreText')) ]) desc_color = (0, 1, 0) if owned else (1, 0, 0) except Exception: desc = ba.Lstr(value='(invalid)') desc_color = (1, 0, 0) btn = ba.buttonwidget( parent=self.root_widget, size=(scl * 240.0, scl * 120.0), position=(h, v), texture=ba.gettexture(tex_name if owned else 'empty'), model_opaque=model_opaque if owned else None, on_activate_call=ba.Call(ba.screenmessage, desc, desc_color), label='', color=(1, 1, 1), autoselect=True, extra_touch_border_scale=0.0, model_transparent=model_transparent if owned else None, mask_texture=mask_tex if owned else None) if row == 0 and col == 0: ba.widget(edit=self._cancel_button, down_widget=btn) if row == rows - 1: bottom_row_buttons.append(btn) if not owned: # Ewww; buttons don't currently have alpha so in this # case we draw an image over our button with an empty # texture on it. ba.imagewidget(parent=self.root_widget, size=(scl * 260.0, scl * 130.0), position=(h - 10.0 * scl, v - 4.0 * scl), draw_controller=btn, color=(1, 1, 1), texture=ba.gettexture(tex_name), model_opaque=model_opaque, opacity=0.25, model_transparent=model_transparent, mask_texture=mask_tex) ba.imagewidget(parent=self.root_widget, size=(scl * 100, scl * 100), draw_controller=btn, position=(h + scl * 70, v + scl * 10), texture=ba.gettexture('lock')) # Team names/colors. self._custom_colors_names_button: Optional[ba.Widget] if self._sessiontype is ba.DualTeamSession: y_offs = 50 if show_shuffle_check_box else 0 self._custom_colors_names_button = ba.buttonwidget( parent=self.root_widget, position=(100, 200 + y_offs), size=(290, 35), on_activate_call=ba.WeakCall(self._custom_colors_names_press), autoselect=True, textcolor=(0.8, 0.8, 0.8), label=ba.Lstr(resource='teamNamesColorText')) if not ba.app.accounts.have_pro(): ba.imagewidget( parent=self.root_widget, size=(30, 30), position=(95, 202 + y_offs), texture=ba.gettexture('lock'), draw_controller=self._custom_colors_names_button) else: self._custom_colors_names_button = None # Shuffle. def _cb_callback(val: bool) -> None: self._do_randomize_val = val cfg = ba.app.config cfg[self._pvars.config_name + ' Playlist Randomize'] = self._do_randomize_val cfg.commit() if show_shuffle_check_box: self._shuffle_check_box = ba.checkboxwidget( parent=self.root_widget, position=(110, 200), scale=1.0, size=(250, 30), autoselect=True, text=ba.Lstr(resource=self._r + '.shuffleGameOrderText'), maxwidth=300, textcolor=(0.8, 0.8, 0.8), value=self._do_randomize_val, on_value_change_call=_cb_callback) # Show tutorial. show_tutorial = bool(ba.app.config.get('Show Tutorial', True)) def _cb_callback_2(val: bool) -> None: cfg = ba.app.config cfg['Show Tutorial'] = val cfg.commit() self._show_tutorial_check_box = ba.checkboxwidget( parent=self.root_widget, position=(110, 151), scale=1.0, size=(250, 30), autoselect=True, text=ba.Lstr(resource=self._r + '.showTutorialText'), maxwidth=300, textcolor=(0.8, 0.8, 0.8), value=show_tutorial, on_value_change_call=_cb_callback_2) # Grumble: current autoselect doesn't do a very good job # with checkboxes. if self._custom_colors_names_button is not None: for btn in bottom_row_buttons: ba.widget(edit=btn, down_widget=self._custom_colors_names_button) if show_shuffle_check_box: ba.widget(edit=self._custom_colors_names_button, down_widget=self._shuffle_check_box) ba.widget(edit=self._shuffle_check_box, up_widget=self._custom_colors_names_button) else: ba.widget(edit=self._custom_colors_names_button, down_widget=self._show_tutorial_check_box) ba.widget(edit=self._show_tutorial_check_box, up_widget=self._custom_colors_names_button) self._ok_button = ba.buttonwidget( parent=self.root_widget, position=(70, 44), size=(200, 45), scale=1.8, text_res_scale=1.5, on_activate_call=self._on_ok_press, autoselect=True, label=ba.Lstr( resource='okText' if self._selecting_mode else 'playText')) ba.widget(edit=self._ok_button, up_widget=self._show_tutorial_check_box) ba.containerwidget(edit=self.root_widget, start_button=self._ok_button, cancel_button=self._cancel_button, selected_child=self._ok_button) # Update now and once per second. self._update_timer = ba.Timer(1.0, ba.WeakCall(self._update), timetype=ba.TimeType.REAL, repeat=True) self._update()
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 __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.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._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, transition: Optional[str] = 'in_right', origin_widget: ba.Widget = None): # pylint: disable=too-many-locals # pylint: disable=too-many-statements from bastd.ui.tabs import TabRow ba.set_analytics_screen('Watch Window') 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 ba.app.ui.set_main_menu_location('Watch') self._tab_data: Dict[str, Any] = {} self._my_replays_scroll_width: Optional[float] = None self._my_replays_watch_replay_button: Optional[ba.Widget] = None self._scrollwidget: Optional[ba.Widget] = None self._columnwidget: Optional[ba.Widget] = None self._my_replay_selected: Optional[str] = None self._my_replays_rename_window: Optional[ba.Widget] = None self._my_replay_rename_text: Optional[ba.Widget] = None self._r = 'watchWindow' uiscale = ba.app.ui.uiscale self._width = 1240 if uiscale is ba.UIScale.SMALL else 1040 x_inset = 100 if uiscale is ba.UIScale.SMALL else 0 self._height = (578 if uiscale is ba.UIScale.SMALL else 670 if uiscale is ba.UIScale.MEDIUM else 800) self._current_tab: Optional[WatchWindow.TabID] = None extra_top = 20 if uiscale is ba.UIScale.SMALL else 0 super().__init__(root_widget=ba.containerwidget( size=(self._width, self._height + extra_top), transition=transition, toolbar_visibility='menu_minimal', scale_origin_stack_offset=scale_origin, scale=(1.3 if uiscale is ba.UIScale.SMALL else 0.97 if uiscale is ba.UIScale.MEDIUM else 0.8), stack_offset=(0, -10) if uiscale is ba.UIScale.SMALL else ( 0, 15) if uiscale is ba.UIScale.MEDIUM else (0, 0))) if uiscale is ba.UIScale.SMALL and ba.app.ui.use_toolbars: ba.containerwidget(edit=self._root_widget, on_cancel_call=self._back) self._back_button = None else: self._back_button = btn = ba.buttonwidget( parent=self._root_widget, autoselect=True, position=(70 + x_inset, self._height - 74), size=(140, 60), scale=1.1, label=ba.Lstr(resource='backText'), button_type='back', on_activate_call=self._back) ba.containerwidget(edit=self._root_widget, cancel_button=btn) ba.buttonwidget(edit=btn, 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 - 38), size=(0, 0), color=ba.app.ui.title_color, scale=1.5, h_align='center', v_align='center', text=ba.Lstr(resource=self._r + '.titleText'), maxwidth=400) tabdefs = [ (self.TabID.MY_REPLAYS, ba.Lstr(resource=self._r + '.myReplaysText')), # (self.TabID.TEST_TAB, ba.Lstr(value='Testing')), ] scroll_buffer_h = 130 + 2 * x_inset tab_buffer_h = 750 + 2 * x_inset self._tab_row = TabRow(self._root_widget, tabdefs, pos=(tab_buffer_h * 0.5, self._height - 130), size=(self._width - tab_buffer_h, 50), on_select_call=self._set_tab) if ba.app.ui.use_toolbars: first_tab = self._tab_row.tabs[tabdefs[0][0]] last_tab = self._tab_row.tabs[tabdefs[-1][0]] ba.widget(edit=last_tab.button, right_widget=_ba.get_special_widget('party_button')) if uiscale is ba.UIScale.SMALL: bbtn = _ba.get_special_widget('back_button') ba.widget(edit=first_tab.button, up_widget=bbtn, left_widget=bbtn) self._scroll_width = self._width - scroll_buffer_h self._scroll_height = self._height - 180 # Not actually using a scroll widget anymore; just an image. scroll_left = (self._width - self._scroll_width) * 0.5 scroll_bottom = self._height - self._scroll_height - 79 - 48 buffer_h = 10 buffer_v = 4 ba.imagewidget(parent=self._root_widget, position=(scroll_left - buffer_h, scroll_bottom - buffer_v), size=(self._scroll_width + 2 * buffer_h, self._scroll_height + 2 * buffer_v), texture=ba.gettexture('scrollWidget'), model_transparent=ba.getmodel('softEdgeOutside')) self._tab_container: Optional[ba.Widget] = None self._restore_state()
def _draw_dude(self, i: int, btn: ba.Widget, hoffs: float, v: float, scl: float, position: Tuple[float, float], color: Tuple[float, float, float]) -> None: h_extra = -100 v_extra = 130 eye_color = (0.7 * 1.0 + 0.3 * color[0], 0.7 * 1.0 + 0.3 * color[1], 0.7 * 1.0 + 0.3 * color[2]) if i == 0: ba.imagewidget(parent=self._root_widget, draw_controller=btn, position=(hoffs + scl * (h_extra + position[0]), v + scl * (v_extra + position[1])), size=(scl * 60, scl * 80), color=color, texture=self._lineup_tex, model_transparent=self._lineup_1_transparent_model) ba.imagewidget( parent=self._root_widget, draw_controller=btn, position=(hoffs + scl * (h_extra + position[0] + 12), v + scl * (v_extra + position[1] + 53)), size=(scl * 36, scl * 18), texture=self._lineup_tex, color=eye_color, model_transparent=self._eyes_model) elif i == 1: ba.imagewidget(parent=self._root_widget, draw_controller=btn, position=(hoffs + scl * (h_extra + position[0]), v + scl * (v_extra + position[1])), size=(scl * 45, scl * 90), color=color, texture=self._lineup_tex, model_transparent=self._lineup_2_transparent_model) ba.imagewidget(parent=self._root_widget, draw_controller=btn, position=(hoffs + scl * (h_extra + position[0] + 5), v + scl * (v_extra + position[1] + 67)), size=(scl * 32, scl * 16), texture=self._lineup_tex, color=eye_color, model_transparent=self._eyes_model) elif i == 2: ba.imagewidget(parent=self._root_widget, draw_controller=btn, position=(hoffs + scl * (h_extra + position[0]), v + scl * (v_extra + position[1])), size=(scl * 45, scl * 90), color=color, texture=self._lineup_tex, model_transparent=self._lineup_3_transparent_model) ba.imagewidget(parent=self._root_widget, draw_controller=btn, position=(hoffs + scl * (h_extra + position[0] + 5), v + scl * (v_extra + position[1] + 59)), size=(scl * 34, scl * 17), texture=self._lineup_tex, color=eye_color, model_transparent=self._eyes_model) elif i == 3: ba.imagewidget(parent=self._root_widget, draw_controller=btn, position=(hoffs + scl * (h_extra + position[0]), v + scl * (v_extra + position[1])), size=(scl * 48, scl * 96), color=color, texture=self._lineup_tex, model_transparent=self._lineup_4_transparent_model) ba.imagewidget(parent=self._root_widget, draw_controller=btn, position=(hoffs + scl * (h_extra + position[0] + 2), v + scl * (v_extra + position[1] + 62)), size=(scl * 38, scl * 19), texture=self._lineup_tex, color=eye_color, model_transparent=self._eyes_model)
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' uiscale = ba.app.ui.uiscale self._width = 750.0 if uiscale is ba.UIScale.SMALL else 650.0 x_inset = 50.0 if uiscale is ba.UIScale.SMALL else 0.0 self._height = (380.0 if uiscale is ba.UIScale.SMALL else 420.0 if uiscale is ba.UIScale.MEDIUM else 500.0) top_extra = 20.0 if uiscale is ba.UIScale.SMALL 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 uiscale is ba.UIScale.SMALL else 1.5 if uiscale is ba.UIScale.MEDIUM else 1.0), stack_offset=(0, -10) if uiscale is ba.UIScale.SMALL 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.ui.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 uiscale is ba.UIScale.SMALL else 1.27 if uiscale is ba.UIScale.MEDIUM 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, border=2, margin=0) h = 145 self._do_randomize_val = ba.app.config.get( self._pvars.config_name + ' Playlist Randomize', 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.ui.use_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 ask_for_rating() -> Optional[ba.Widget]: """(internal)""" app = ba.app platform = app.platform subplatform = app.subplatform # FIXME: should whitelist platforms we *do* want this for. if ba.app.test_build: return None if not (platform == 'mac' or (platform == 'android' and subplatform in ['google', 'cardboard'])): return None width = 700 height = 400 spacing = 40 uiscale = ba.app.ui.uiscale dlg = ba.containerwidget( size=(width, height), transition='in_right', scale=(1.6 if uiscale is ba.UIScale.SMALL else 1.35 if uiscale is ba.UIScale.MEDIUM else 1.0)) v = height - 50 v -= spacing v -= 140 ba.imagewidget(parent=dlg, position=(width / 2 - 100, v + 10), size=(200, 200), texture=ba.gettexture('cuteSpaz')) ba.textwidget(parent=dlg, position=(15, v - 55), size=(width - 30, 30), color=ba.app.ui.infotextcolor, text=ba.Lstr(resource='pleaseRateText', subs=[('${APP_NAME}', ba.Lstr(resource='titleText'))]), maxwidth=width * 0.95, max_height=130, scale=0.85, h_align='center', v_align='center') def do_rating() -> None: import _ba if platform == 'android': appname = _ba.appname() if subplatform == 'google': url = f'market://details?id=net.froemling.{appname}' else: url = 'market://details?id=net.froemling.{appname}cb' else: url = 'macappstore://itunes.apple.com/app/id416482767?ls=1&mt=12' ba.open_url(url) ba.containerwidget(edit=dlg, transition='out_left') ba.buttonwidget(parent=dlg, position=(60, 20), size=(200, 60), label=ba.Lstr(resource='wellSureText'), autoselect=True, on_activate_call=do_rating) def close() -> None: ba.containerwidget(edit=dlg, transition='out_left') btn = ba.buttonwidget(parent=dlg, position=(width - 270, 20), size=(200, 60), label=ba.Lstr(resource='noThanksText'), autoselect=True, on_activate_call=close) ba.containerwidget(edit=dlg, cancel_button=btn, selected_child=btn) return dlg
def __init__(self, address: str): # in some cases we might want to show it as a qr code # (for long URLs especially) app = ba.app if app.platform == 'android' and app.subplatform == 'alibaba': self._width = 500 self._height = 500 super().__init__(root_widget=ba.containerwidget( size=(self._width, self._height), transition='in_right', scale=(1.25 if ba.app.small_ui else 1.25 if ba.app. med_ui else 1.25))) self._cancel_button = ba.buttonwidget( parent=self._root_widget, position=(50, self._height - 30), size=(50, 50), scale=0.6, label='', color=(0.6, 0.5, 0.6), on_activate_call=self._done, autoselect=True, icon=ba.gettexture('crossOut'), iconscale=1.2) qr_size = 400 ba.imagewidget(parent=self._root_widget, position=(self._width * 0.5 - qr_size * 0.5, self._height * 0.5 - qr_size * 0.5), size=(qr_size, qr_size), texture=_ba.get_qrcode_texture(address)) ba.containerwidget(edit=self._root_widget, cancel_button=self._cancel_button) else: # show it as a simple string... self._width = 800 self._height = 200 self._root_widget = ba.containerwidget( size=(self._width, self._height + 40), transition='in_right', scale=1.25 if ba.app.small_ui else 1.25 if ba.app.med_ui else 1.25) ba.textwidget(parent=self._root_widget, position=(self._width * 0.5, self._height - 10), size=(0, 0), color=ba.app.title_color, h_align='center', v_align='center', text=ba.Lstr(resource='directBrowserToURLText'), maxwidth=self._width * 0.95) ba.textwidget(parent=self._root_widget, position=(self._width * 0.5, self._height * 0.5 + 29), size=(0, 0), scale=1.3, color=ba.app.infotextcolor, h_align='center', v_align='center', text=address, maxwidth=self._width * 0.95) button_width = 200 btn = ba.buttonwidget(parent=self._root_widget, position=(self._width * 0.5 - button_width * 0.5, 20), size=(button_width, 65), label=ba.Lstr(resource='doneText'), on_activate_call=self._done) # we have no 'cancel' button but still want to be able to # hit back/escape/etc to leave.. ba.containerwidget(edit=self._root_widget, selected_child=btn, start_button=btn, on_cancel_call=btn.activate)
def _update(self) -> None: from ba.internal import have_pro_options have = have_pro_options() for lock in self._lock_images: ba.imagewidget(edit=lock, opacity=0.0 if have else 1.0)
def __init__(self, main_menu: bool = False, origin_widget: ba.Widget = None): # pylint: disable=too-many-statements # pylint: disable=too-many-locals from ba.internal import get_remote_app_name ba.set_analytics_screen('Help Window') # If they provided an origin-widget, scale up from that. scale_origin: Optional[Tuple[float, float]] if origin_widget is not None: self._transition_out = 'out_scale' scale_origin = origin_widget.get_screen_space_center() transition = 'in_scale' else: self._transition_out = 'out_right' scale_origin = None transition = 'in_right' self._r = 'helpWindow' getres = ba.app.lang.get_resource self._main_menu = main_menu uiscale = ba.app.ui.uiscale width = 950 if uiscale is ba.UIScale.SMALL else 750 x_offs = 100 if uiscale is ba.UIScale.SMALL else 0 height = (460 if uiscale is ba.UIScale.SMALL else 530 if uiscale is ba.UIScale.MEDIUM else 600) super().__init__(root_widget=ba.containerwidget( size=(width, height), transition=transition, toolbar_visibility='menu_minimal', scale_origin_stack_offset=scale_origin, scale=(1.77 if uiscale is ba.UIScale.SMALL else 1.25 if uiscale is ba.UIScale.MEDIUM else 1.0), stack_offset=(0, -30) if uiscale is ba.UIScale.SMALL else ( 0, 15) if uiscale is ba.UIScale.MEDIUM else (0, 0))) ba.textwidget(parent=self._root_widget, position=(0, height - (50 if uiscale is ba.UIScale.SMALL else 45)), size=(width, 25), text=ba.Lstr(resource=self._r + '.titleText', subs=[('${APP_NAME}', ba.Lstr(resource='titleText'))]), color=ba.app.ui.title_color, h_align='center', v_align='top') self._scrollwidget = ba.scrollwidget( parent=self._root_widget, position=(44 + x_offs, 55 if uiscale is ba.UIScale.SMALL else 55), simple_culling_v=100.0, size=(width - (88 + 2 * x_offs), height - 120 + (5 if uiscale is ba.UIScale.SMALL else 0)), capture_arrows=True) if ba.app.ui.use_toolbars: ba.widget(edit=self._scrollwidget, right_widget=_ba.get_special_widget('party_button')) ba.containerwidget(edit=self._root_widget, selected_child=self._scrollwidget) # ugly: create this last so it gets first dibs at touch events (since # we have it close to the scroll widget) if uiscale is ba.UIScale.SMALL and ba.app.ui.use_toolbars: ba.containerwidget(edit=self._root_widget, on_cancel_call=self._close) ba.widget(edit=self._scrollwidget, left_widget=_ba.get_special_widget('back_button')) else: btn = ba.buttonwidget( parent=self._root_widget, position=(x_offs + (40 + 0 if uiscale is ba.UIScale.SMALL else 70), height - (59 if uiscale is ba.UIScale.SMALL else 50)), size=(140, 60), scale=0.7 if uiscale is ba.UIScale.SMALL else 0.8, label=ba.Lstr( resource='backText') if self._main_menu else 'Close', button_type='back' if self._main_menu else None, extra_touch_border_scale=2.0, autoselect=True, on_activate_call=self._close) ba.containerwidget(edit=self._root_widget, cancel_button=btn) if self._main_menu: ba.buttonwidget(edit=btn, button_type='backSmall', size=(60, 55), label=ba.charstr(ba.SpecialChar.BACK)) self._sub_width = 660 self._sub_height = 1590 + ba.app.lang.get_resource( self._r + '.someDaysExtraSpace') + ba.app.lang.get_resource( self._r + '.orPunchingSomethingExtraSpace') self._subcontainer = ba.containerwidget(parent=self._scrollwidget, size=(self._sub_width, self._sub_height), background=False, claims_left_right=False, claims_tab=False) spacing = 1.0 h = self._sub_width * 0.5 v = self._sub_height - 55 logo_tex = ba.gettexture('logo') icon_buffer = 1.1 header = (0.7, 1.0, 0.7, 1.0) header2 = (0.8, 0.8, 1.0, 1.0) paragraph = (0.8, 0.8, 1.0, 1.0) txt = ba.Lstr(resource=self._r + '.welcomeText', subs=[('${APP_NAME}', ba.Lstr(resource='titleText')) ]).evaluate() txt_scale = 1.4 txt_maxwidth = 480 ba.textwidget(parent=self._subcontainer, position=(h, v), size=(0, 0), scale=txt_scale, flatness=0.5, res_scale=1.5, text=txt, h_align='center', color=header, v_align='center', maxwidth=txt_maxwidth) txt_width = min( txt_maxwidth, _ba.get_string_width(txt, suppress_warning=True) * txt_scale) icon_size = 70 hval2 = h - (txt_width * 0.5 + icon_size * 0.5 * icon_buffer) ba.imagewidget(parent=self._subcontainer, size=(icon_size, icon_size), position=(hval2 - 0.5 * icon_size, v - 0.45 * icon_size), texture=logo_tex) force_test = False app = ba.app if (app.platform == 'android' and app.subplatform == 'alibaba') or force_test: v -= 120.0 txtv = ( '\xe8\xbf\x99\xe6\x98\xaf\xe4\xb8\x80\xe4\xb8\xaa\xe5\x8f\xaf' '\xe4\xbb\xa5\xe5\x92\x8c\xe5\xae\xb6\xe4\xba\xba\xe6\x9c\x8b' '\xe5\x8f\x8b\xe4\xb8\x80\xe8\xb5\xb7\xe7\x8e\xa9\xe7\x9a\x84' '\xe6\xb8\xb8\xe6\x88\x8f,\xe5\x90\x8c\xe6\x97\xb6\xe6\x94\xaf' '\xe6\x8c\x81\xe8\x81\x94 \xe2\x80\xa8\xe7\xbd\x91\xe5\xaf\xb9' '\xe6\x88\x98\xe3\x80\x82\n' '\xe5\xa6\x82\xe6\xb2\xa1\xe6\x9c\x89\xe6\xb8\xb8\xe6\x88\x8f' '\xe6\x89\x8b\xe6\x9f\x84,\xe5\x8f\xaf\xe4\xbb\xa5\xe4\xbd\xbf' '\xe7\x94\xa8\xe7\xa7\xbb\xe5\x8a\xa8\xe8\xae\xbe\xe5\xa4\x87' '\xe6\x89\xab\xe7\xa0\x81\xe4\xb8\x8b\xe8\xbd\xbd\xe2\x80\x9c' '\xe9\x98\xbf\xe9\x87\x8c\xc2' '\xa0TV\xc2\xa0\xe5\x8a\xa9\xe6\x89' '\x8b\xe2\x80\x9d\xe7\x94\xa8 \xe6\x9d\xa5\xe4\xbb\xa3\xe6\x9b' '\xbf\xe5\xa4\x96\xe8\xae\xbe\xe3\x80\x82\n' '\xe6\x9c\x80\xe5\xa4\x9a\xe6\x94\xaf\xe6\x8c\x81\xe6\x8e\xa5' '\xe5\x85\xa5\xc2\xa08\xc2\xa0\xe4\xb8\xaa\xe5\xa4\x96\xe8' '\xae\xbe') ba.textwidget(parent=self._subcontainer, size=(0, 0), h_align='center', v_align='center', maxwidth=self._sub_width * 0.9, position=(self._sub_width * 0.5, v - 180), text=txtv) ba.imagewidget(parent=self._subcontainer, position=(self._sub_width - 320, v - 120), size=(200, 200), texture=ba.gettexture('aliControllerQR')) ba.imagewidget(parent=self._subcontainer, position=(90, v - 130), size=(210, 210), texture=ba.gettexture('multiplayerExamples')) v -= 120.0 else: v -= spacing * 50.0 txt = ba.Lstr(resource=self._r + '.someDaysText').evaluate() ba.textwidget(parent=self._subcontainer, position=(h, v), size=(0, 0), scale=1.2, maxwidth=self._sub_width * 0.9, text=txt, h_align='center', color=paragraph, v_align='center', flatness=1.0) v -= (spacing * 25.0 + getres(self._r + '.someDaysExtraSpace')) txt_scale = 0.66 txt = ba.Lstr(resource=self._r + '.orPunchingSomethingText').evaluate() ba.textwidget(parent=self._subcontainer, position=(h, v), size=(0, 0), scale=txt_scale, maxwidth=self._sub_width * 0.9, text=txt, h_align='center', color=paragraph, v_align='center', flatness=1.0) v -= (spacing * 27.0 + getres(self._r + '.orPunchingSomethingExtraSpace')) txt_scale = 1.0 txt = ba.Lstr(resource=self._r + '.canHelpText', subs=[('${APP_NAME}', ba.Lstr(resource='titleText')) ]).evaluate() ba.textwidget(parent=self._subcontainer, position=(h, v), size=(0, 0), scale=txt_scale, flatness=1.0, text=txt, h_align='center', color=paragraph, v_align='center') v -= spacing * 70.0 txt_scale = 1.0 txt = ba.Lstr(resource=self._r + '.toGetTheMostText').evaluate() ba.textwidget(parent=self._subcontainer, position=(h, v), size=(0, 0), scale=txt_scale, maxwidth=self._sub_width * 0.9, text=txt, h_align='center', color=header, v_align='center', flatness=1.0) v -= spacing * 40.0 txt_scale = 0.74 txt = ba.Lstr(resource=self._r + '.friendsText').evaluate() hval2 = h - 220 ba.textwidget(parent=self._subcontainer, position=(hval2, v), size=(0, 0), scale=txt_scale, maxwidth=100, text=txt, h_align='right', color=header, v_align='center', flatness=1.0) txt = ba.Lstr(resource=self._r + '.friendsGoodText', subs=[('${APP_NAME}', ba.Lstr(resource='titleText')) ]).evaluate() txt_scale = 0.7 ba.textwidget(parent=self._subcontainer, position=(hval2 + 10, v + 8), size=(0, 0), scale=txt_scale, maxwidth=500, text=txt, h_align='left', color=paragraph, flatness=1.0) app = ba.app v -= spacing * 45.0 txt = (ba.Lstr(resource=self._r + '.devicesText').evaluate() if app.vr_mode else ba.Lstr(resource=self._r + '.controllersText').evaluate()) txt_scale = 0.74 hval2 = h - 220 ba.textwidget(parent=self._subcontainer, position=(hval2, v), size=(0, 0), scale=txt_scale, maxwidth=100, text=txt, h_align='right', v_align='center', color=header, flatness=1.0) txt_scale = 0.7 if not app.vr_mode: infotxt = ('.controllersInfoTextRemoteOnly' if app.iircade_mode else '.controllersInfoText') txt = ba.Lstr( resource=self._r + infotxt, fallback_resource=self._r + '.controllersInfoText', subs=[('${APP_NAME}', ba.Lstr(resource='titleText')), ('${REMOTE_APP_NAME}', get_remote_app_name()) ]).evaluate() else: txt = ba.Lstr(resource=self._r + '.devicesInfoText', subs=[('${APP_NAME}', ba.Lstr(resource='titleText')) ]).evaluate() ba.textwidget(parent=self._subcontainer, position=(hval2 + 10, v + 8), size=(0, 0), scale=txt_scale, maxwidth=500, max_height=105, text=txt, h_align='left', color=paragraph, flatness=1.0) v -= spacing * 150.0 txt = ba.Lstr(resource=self._r + '.controlsText').evaluate() txt_scale = 1.4 txt_maxwidth = 480 ba.textwidget(parent=self._subcontainer, position=(h, v), size=(0, 0), scale=txt_scale, flatness=0.5, text=txt, h_align='center', color=header, v_align='center', res_scale=1.5, maxwidth=txt_maxwidth) txt_width = min( txt_maxwidth, _ba.get_string_width(txt, suppress_warning=True) * txt_scale) icon_size = 70 hval2 = h - (txt_width * 0.5 + icon_size * 0.5 * icon_buffer) ba.imagewidget(parent=self._subcontainer, size=(icon_size, icon_size), position=(hval2 - 0.5 * icon_size, v - 0.45 * icon_size), texture=logo_tex) v -= spacing * 45.0 txt_scale = 0.7 txt = ba.Lstr(resource=self._r + '.controlsSubtitleText', subs=[('${APP_NAME}', ba.Lstr(resource='titleText')) ]).evaluate() ba.textwidget(parent=self._subcontainer, position=(h, v), size=(0, 0), scale=txt_scale, maxwidth=self._sub_width * 0.9, flatness=1.0, text=txt, h_align='center', color=paragraph, v_align='center') v -= spacing * 160.0 sep = 70 icon_size = 100 # icon_size_2 = 30 hval2 = h - sep vval2 = v ba.imagewidget(parent=self._subcontainer, size=(icon_size, icon_size), position=(hval2 - 0.5 * icon_size, vval2 - 0.5 * icon_size), texture=ba.gettexture('buttonPunch'), color=(1, 0.7, 0.3)) txt_scale = getres(self._r + '.punchInfoTextScale') txt = ba.Lstr(resource=self._r + '.punchInfoText').evaluate() ba.textwidget(parent=self._subcontainer, position=(h - sep - 185 + 70, v + 120), size=(0, 0), scale=txt_scale, flatness=1.0, text=txt, h_align='center', color=(1, 0.7, 0.3, 1.0), v_align='top') hval2 = h + sep vval2 = v ba.imagewidget(parent=self._subcontainer, size=(icon_size, icon_size), position=(hval2 - 0.5 * icon_size, vval2 - 0.5 * icon_size), texture=ba.gettexture('buttonBomb'), color=(1, 0.3, 0.3)) txt = ba.Lstr(resource=self._r + '.bombInfoText').evaluate() txt_scale = getres(self._r + '.bombInfoTextScale') ba.textwidget(parent=self._subcontainer, position=(h + sep + 50 + 60, v - 35), size=(0, 0), scale=txt_scale, flatness=1.0, maxwidth=270, text=txt, h_align='center', color=(1, 0.3, 0.3, 1.0), v_align='top') hval2 = h vval2 = v + sep ba.imagewidget(parent=self._subcontainer, size=(icon_size, icon_size), position=(hval2 - 0.5 * icon_size, vval2 - 0.5 * icon_size), texture=ba.gettexture('buttonPickUp'), color=(0.5, 0.5, 1)) txtl = ba.Lstr(resource=self._r + '.pickUpInfoText') txt_scale = getres(self._r + '.pickUpInfoTextScale') ba.textwidget(parent=self._subcontainer, position=(h + 60 + 120, v + sep + 50), size=(0, 0), scale=txt_scale, flatness=1.0, text=txtl, h_align='center', color=(0.5, 0.5, 1, 1.0), v_align='top') hval2 = h vval2 = v - sep ba.imagewidget(parent=self._subcontainer, size=(icon_size, icon_size), position=(hval2 - 0.5 * icon_size, vval2 - 0.5 * icon_size), texture=ba.gettexture('buttonJump'), color=(0.4, 1, 0.4)) txt = ba.Lstr(resource=self._r + '.jumpInfoText').evaluate() txt_scale = getres(self._r + '.jumpInfoTextScale') ba.textwidget(parent=self._subcontainer, position=(h - 250 + 75, v - sep - 15 + 30), size=(0, 0), scale=txt_scale, flatness=1.0, text=txt, h_align='center', color=(0.4, 1, 0.4, 1.0), v_align='top') txt = ba.Lstr(resource=self._r + '.runInfoText').evaluate() txt_scale = getres(self._r + '.runInfoTextScale') ba.textwidget(parent=self._subcontainer, position=(h, v - sep - 100), size=(0, 0), scale=txt_scale, maxwidth=self._sub_width * 0.93, flatness=1.0, text=txt, h_align='center', color=(0.7, 0.7, 1.0, 1.0), v_align='center') v -= spacing * 280.0 txt = ba.Lstr(resource=self._r + '.powerupsText').evaluate() txt_scale = 1.4 txt_maxwidth = 480 ba.textwidget(parent=self._subcontainer, position=(h, v), size=(0, 0), scale=txt_scale, flatness=0.5, text=txt, h_align='center', color=header, v_align='center', maxwidth=txt_maxwidth) txt_width = min( txt_maxwidth, _ba.get_string_width(txt, suppress_warning=True) * txt_scale) icon_size = 70 hval2 = h - (txt_width * 0.5 + icon_size * 0.5 * icon_buffer) ba.imagewidget(parent=self._subcontainer, size=(icon_size, icon_size), position=(hval2 - 0.5 * icon_size, v - 0.45 * icon_size), texture=logo_tex) v -= spacing * 50.0 txt_scale = getres(self._r + '.powerupsSubtitleTextScale') txt = ba.Lstr(resource=self._r + '.powerupsSubtitleText').evaluate() ba.textwidget(parent=self._subcontainer, position=(h, v), size=(0, 0), scale=txt_scale, maxwidth=self._sub_width * 0.9, text=txt, h_align='center', color=paragraph, v_align='center', flatness=1.0) v -= spacing * 1.0 mm1 = -270 mm2 = -215 mm3 = 0 icon_size = 50 shadow_size = 80 shadow_offs_x = 3 shadow_offs_y = -4 t_big = 1.1 t_small = 0.65 shadow_tex = ba.gettexture('shadowSharp') for tex in [ 'powerupPunch', 'powerupShield', 'powerupBomb', 'powerupHealth', 'powerupIceBombs', 'powerupImpactBombs', 'powerupStickyBombs', 'powerupLandMines', 'powerupCurse' ]: name = ba.Lstr(resource=self._r + '.' + tex + 'NameText') desc = ba.Lstr(resource=self._r + '.' + tex + 'DescriptionText') v -= spacing * 60.0 ba.imagewidget( parent=self._subcontainer, size=(shadow_size, shadow_size), position=(h + mm1 + shadow_offs_x - 0.5 * shadow_size, v + shadow_offs_y - 0.5 * shadow_size), texture=shadow_tex, color=(0, 0, 0), opacity=0.5) ba.imagewidget(parent=self._subcontainer, size=(icon_size, icon_size), position=(h + mm1 - 0.5 * icon_size, v - 0.5 * icon_size), texture=ba.gettexture(tex)) txt_scale = t_big txtl = name ba.textwidget(parent=self._subcontainer, position=(h + mm2, v + 3), size=(0, 0), scale=txt_scale, maxwidth=200, flatness=1.0, text=txtl, h_align='left', color=header2, v_align='center') txt_scale = t_small txtl = desc ba.textwidget(parent=self._subcontainer, position=(h + mm3, v), size=(0, 0), scale=txt_scale, maxwidth=300, flatness=1.0, text=txtl, h_align='left', color=paragraph, v_align='center', res_scale=0.5)
def _refresh(self): if self._scrollwidget: self._scrollwidget.delete() if self._subcontainer: self._subcontainer.delete() self._scrollwidget = ba.scrollwidget( parent=self._root_widget, position=((self._width - self._scroll_width) * 0.5, self._height - self._scroll_height - 119), size=(self._scroll_width, self._scroll_height)) entries = bap.repo.get_available_packages() entry_height = 35 icon_size = 35 self._subcontainerheight = entry_height * len(entries) v = self._subcontainerheight self._subcontainer = ba.containerwidget( parent=self._scrollwidget, size=(self._scroll_width, self._subcontainerheight), background=False) ba.containerwidget(edit=self._scrollwidget, claims_left_right=False, claims_tab=False) ba.containerwidget(edit=self._subcontainer, claims_left_right=False, claims_tab=False, selection_loops=False, print_list_exit_instructions=False) ba.widget(edit=self._subcontainer, up_widget=self._back_button) for num, entry in enumerate(entries): cnt = ba.containerwidget(parent=self._subcontainer, position=(0, v - entry_height), size=(self._scroll_width, entry_height), root_selectable=True, background=False, click_activate=True, on_activate_call=ba.Call( self._on_entry_activated, entry)) if num == 0: ba.widget(edit=cnt, up_widget=self._back_button) ba.imagewidget(parent=cnt, size=(icon_size, icon_size), position=(10, 0.5 * entry_height - icon_size * 0.5), opacity=1.0, draw_controller=cnt, texture=ba.gettexture('file'), color=(0.1, 0.9, 0.1)) ba.textwidget(parent=cnt, draw_controller=cnt, text=entry.name, h_align='left', v_align='center', position=(10 + icon_size * 1.05, entry_height * 0.5), size=(0, 0), maxwidth=self._scroll_width * 0.93 - 50, color=(1, 1, 1, 1)) ba.textwidget(parent=cnt, draw_controller=cnt, text=entry.version.to_string(), h_align='left', v_align='center', position=(self._scroll_width * 0.93 - 50, entry_height * 0.5), size=(0, 0), maxwidth=self._scroll_width * 0.93 - 50, color=(1, 1, 1, 1)) v -= entry_height
def _refresh(self, file_names: List[str], error: Optional[str]) -> None: # pylint: disable=too-many-statements # pylint: disable=too-many-branches # pylint: disable=too-many-locals if not self._root_widget: return scrollwidget_selected = (self._scrollwidget is None or self._root_widget.get_selected_child() == self._scrollwidget) in_top_folder = (self._path == self._base_path) hide_top_folder = in_top_folder and self._show_base_path is False if hide_top_folder: folder_name = '' elif self._path == '/': folder_name = '/' else: assert self._path is not None folder_name = os.path.basename(self._path) b_color = (0.6, 0.53, 0.63) b_color_disabled = (0.65, 0.65, 0.65) if len(self._recent_paths) < 2: ba.buttonwidget(edit=self._back_button, color=b_color_disabled, textcolor=(0.5, 0.5, 0.5)) else: ba.buttonwidget(edit=self._back_button, color=b_color, textcolor=(0.75, 0.7, 0.8)) max_str_width = 300.0 str_width = min( max_str_width, _ba.get_string_width(folder_name, suppress_warning=True)) ba.textwidget(edit=self._path_text, text=folder_name, maxwidth=max_str_width) ba.imagewidget(edit=self._folder_icon, position=(self._folder_center - str_width * 0.5 - 40, self._height - 117), opacity=0.0 if hide_top_folder else 1.0) if self._scrollwidget is not None: self._scrollwidget.delete() if self._use_folder_button is not None: self._use_folder_button.delete() ba.widget(edit=self._cancel_button, right_widget=self._back_button) self._scrollwidget = ba.scrollwidget( parent=self._root_widget, position=((self._width - self._scroll_width) * 0.5, self._height - self._scroll_height - 119), size=(self._scroll_width, self._scroll_height)) if scrollwidget_selected: ba.containerwidget(edit=self._root_widget, selected_child=self._scrollwidget) # show error case.. if error is not None: self._subcontainer = ba.containerwidget(parent=self._scrollwidget, size=(self._scroll_width, self._scroll_height), background=False) ba.textwidget(parent=self._subcontainer, color=(1, 1, 0, 1), text=error, maxwidth=self._scroll_width * 0.9, position=(self._scroll_width * 0.48, self._scroll_height * 0.57), size=(0, 0), h_align='center', v_align='center') else: file_names = [f for f in file_names if not f.startswith('.')] file_names.sort(key=lambda x: x[0].lower()) entries = file_names entry_height = 35 folder_entry_height = 100 show_folder_entry = False show_use_folder_button = (self._allow_folders and not in_top_folder) self._subcontainerheight = entry_height * len(entries) + ( folder_entry_height if show_folder_entry else 0) v = self._subcontainerheight - (folder_entry_height if show_folder_entry else 0) self._subcontainer = ba.containerwidget( parent=self._scrollwidget, size=(self._scroll_width, self._subcontainerheight), background=False) ba.containerwidget(edit=self._scrollwidget, claims_left_right=False, claims_tab=False) ba.containerwidget(edit=self._subcontainer, claims_left_right=False, claims_tab=False, selection_loops=False, print_list_exit_instructions=False) ba.widget(edit=self._subcontainer, up_widget=self._back_button) if show_use_folder_button: self._use_folder_button = btn = ba.buttonwidget( parent=self._root_widget, position=(self._width - self._button_width - 35 - self._x_inset, self._height - 67), size=(self._button_width, 50), label=ba.Lstr(resource=self._r + '.useThisFolderButtonText'), on_activate_call=self._on_folder_entry_activated) ba.widget(edit=btn, left_widget=self._cancel_button, down_widget=self._scrollwidget) ba.widget(edit=self._cancel_button, right_widget=btn) ba.containerwidget(edit=self._root_widget, start_button=btn) folder_icon_size = 35 for num, entry in enumerate(entries): cnt = ba.containerwidget( parent=self._subcontainer, position=(0, v - entry_height), size=(self._scroll_width, entry_height), root_selectable=True, background=False, click_activate=True, on_activate_call=ba.Call(self._on_entry_activated, entry)) if num == 0: ba.widget(edit=cnt, up_widget=self._back_button) is_valid_file_path = self._is_valid_file_path(entry) assert self._path is not None is_dir = os.path.isdir(self._path + '/' + entry) if is_dir: ba.imagewidget(parent=cnt, size=(folder_icon_size, folder_icon_size), position=(10, 0.5 * entry_height - folder_icon_size * 0.5), draw_controller=cnt, texture=self._folder_tex, color=self._folder_color) else: ba.imagewidget(parent=cnt, size=(folder_icon_size, folder_icon_size), position=(10, 0.5 * entry_height - folder_icon_size * 0.5), opacity=1.0 if is_valid_file_path else 0.5, draw_controller=cnt, texture=self._file_tex, color=self._file_color) ba.textwidget(parent=cnt, draw_controller=cnt, text=entry, h_align='left', v_align='center', position=(10 + folder_icon_size * 1.05, entry_height * 0.5), size=(0, 0), maxwidth=self._scroll_width * 0.93 - 50, color=(1, 1, 1, 1) if (is_valid_file_path or is_dir) else (0.5, 0.5, 0.5, 1)) v -= entry_height
def __init__(self, path: str, callback: Callable[[Optional[str]], Any] = None, show_base_path: bool = True, valid_file_extensions: Sequence[str] = None, allow_folders: bool = False): if valid_file_extensions is None: valid_file_extensions = [] uiscale = ba.app.ui.uiscale self._width = 700 if uiscale is ba.UIScale.SMALL else 600 self._x_inset = x_inset = 50 if uiscale is ba.UIScale.SMALL else 0 self._height = 365 if uiscale is ba.UIScale.SMALL else 418 self._callback = callback self._base_path = path self._path: Optional[str] = None self._recent_paths: List[str] = [] self._show_base_path = show_base_path self._valid_file_extensions = [ '.' + ext for ext in valid_file_extensions ] self._allow_folders = allow_folders self._subcontainer: Optional[ba.Widget] = None self._subcontainerheight: Optional[float] = None self._scroll_width = self._width - (80 + 2 * x_inset) self._scroll_height = self._height - 170 self._r = 'fileSelectorWindow' super().__init__(root_widget=ba.containerwidget( size=(self._width, self._height), transition='in_right', scale=(2.23 if uiscale is ba.UIScale.SMALL else 1.4 if uiscale is ba.UIScale.MEDIUM else 1.0), stack_offset=(0, -35) if uiscale is ba.UIScale.SMALL else (0, 0))) ba.textwidget( parent=self._root_widget, position=(self._width * 0.5, self._height - 42), size=(0, 0), color=ba.app.ui.title_color, h_align='center', v_align='center', text=ba.Lstr(resource=self._r + '.titleFolderText') if (allow_folders and not valid_file_extensions) else ba.Lstr( resource=self._r + '.titleFileText') if not allow_folders else ba.Lstr( resource=self._r + '.titleFileFolderText'), maxwidth=210) self._button_width = 146 self._cancel_button = ba.buttonwidget( parent=self._root_widget, position=(35 + x_inset, self._height - 67), autoselect=True, size=(self._button_width, 50), label=ba.Lstr(resource='cancelText'), on_activate_call=self._cancel) ba.widget(edit=self._cancel_button, left_widget=self._cancel_button) b_color = (0.6, 0.53, 0.63) self._back_button = ba.buttonwidget( parent=self._root_widget, button_type='square', position=(43 + x_inset, self._height - 113), color=b_color, textcolor=(0.75, 0.7, 0.8), enable_sound=False, size=(55, 35), label=ba.charstr(ba.SpecialChar.LEFT_ARROW), on_activate_call=self._on_back_press) self._folder_tex = ba.gettexture('folder') self._folder_color = (1.1, 0.8, 0.2) self._file_tex = ba.gettexture('file') self._file_color = (1, 1, 1) self._use_folder_button: Optional[ba.Widget] = None self._folder_center = self._width * 0.5 + 15 self._folder_icon = ba.imagewidget(parent=self._root_widget, size=(40, 40), position=(40, self._height - 117), texture=self._folder_tex, color=self._folder_color) self._path_text = ba.textwidget(parent=self._root_widget, position=(self._folder_center, self._height - 98), size=(0, 0), color=ba.app.ui.title_color, h_align='center', v_align='center', text=self._path, maxwidth=self._width * 0.9) self._scrollwidget: Optional[ba.Widget] = None ba.containerwidget(edit=self._root_widget, cancel_button=self._cancel_button) self._set_path(path)
def __init__(self, gametype: type[ba.GameActivity], sessiontype: type[ba.Session], config: Optional[dict[str, Any]], completion_call: Callable[[Optional[dict[str, Any]]], Any], default_selection: str = None, transition: str = 'in_right', edit_info: dict[str, Any] = None): # pylint: disable=too-many-branches # pylint: disable=too-many-statements # pylint: disable=too-many-locals from ba.internal import (get_unowned_maps, get_filtered_map_name, get_map_class, get_map_display_string) self._gametype = gametype self._sessiontype = sessiontype # If we're within an editing session we get passed edit_info # (returning from map selection window, etc). if edit_info is not None: self._edit_info = edit_info # ..otherwise determine whether we're adding or editing a game based # on whether an existing config was passed to us. else: if config is None: self._edit_info = {'editType': 'add'} else: self._edit_info = {'editType': 'edit'} self._r = 'gameSettingsWindow' valid_maps = gametype.get_supported_maps(sessiontype) if not valid_maps: ba.screenmessage(ba.Lstr(resource='noValidMapsErrorText')) raise Exception('No valid maps') self._settings_defs = gametype.get_available_settings(sessiontype) self._completion_call = completion_call # To start with, pick a random map out of the ones we own. unowned_maps = get_unowned_maps() valid_maps_owned = [m for m in valid_maps if m not in unowned_maps] if valid_maps_owned: self._map = valid_maps[random.randrange(len(valid_maps_owned))] # Hmmm.. we own none of these maps.. just pick a random un-owned one # I guess.. should this ever happen? else: self._map = valid_maps[random.randrange(len(valid_maps))] is_add = (self._edit_info['editType'] == 'add') # If there's a valid map name in the existing config, use that. try: if (config is not None and 'settings' in config and 'map' in config['settings']): filtered_map_name = get_filtered_map_name( config['settings']['map']) if filtered_map_name in valid_maps: self._map = filtered_map_name except Exception: ba.print_exception('Error getting map for editor.') if config is not None and 'settings' in config: self._settings = config['settings'] else: self._settings = {} self._choice_selections: dict[str, int] = {} uiscale = ba.app.ui.uiscale width = 720 if uiscale is ba.UIScale.SMALL else 620 x_inset = 50 if uiscale is ba.UIScale.SMALL else 0 height = (365 if uiscale is ba.UIScale.SMALL else 460 if uiscale is ba.UIScale.MEDIUM else 550) spacing = 52 y_extra = 15 y_extra2 = 21 map_tex_name = (get_map_class(self._map).get_preview_texture_name()) if map_tex_name is None: raise Exception('no map preview tex found for' + self._map) map_tex = ba.gettexture(map_tex_name) top_extra = 20 if uiscale is ba.UIScale.SMALL else 0 super().__init__(root_widget=ba.containerwidget( size=(width, height + top_extra), transition=transition, scale=(2.19 if uiscale is ba.UIScale.SMALL else 1.35 if uiscale is ba.UIScale.MEDIUM else 1.0), stack_offset=(0, -17) if uiscale is ba.UIScale.SMALL else (0, 0))) btn = ba.buttonwidget( parent=self._root_widget, position=(45 + x_inset, height - 82 + y_extra2), size=(180, 70) if is_add else (180, 65), label=ba.Lstr(resource='backText') if is_add else ba.Lstr( resource='cancelText'), button_type='back' if is_add else None, autoselect=True, scale=0.75, text_scale=1.3, on_activate_call=ba.Call(self._cancel)) ba.containerwidget(edit=self._root_widget, cancel_button=btn) add_button = ba.buttonwidget( parent=self._root_widget, position=(width - (193 + x_inset), height - 82 + y_extra2), size=(200, 65), scale=0.75, text_scale=1.3, label=ba.Lstr(resource=self._r + '.addGameText') if is_add else ba.Lstr( resource='doneText')) if ba.app.ui.use_toolbars: pbtn = _ba.get_special_widget('party_button') ba.widget(edit=add_button, right_widget=pbtn, up_widget=pbtn) ba.textwidget(parent=self._root_widget, position=(-8, height - 70 + y_extra2), size=(width, 25), text=gametype.get_display_string(), color=ba.app.ui.title_color, maxwidth=235, scale=1.1, h_align='center', v_align='center') map_height = 100 scroll_height = map_height + 10 # map select and margin # Calc our total height we'll need scroll_height += spacing * len(self._settings_defs) scroll_width = width - (86 + 2 * x_inset) self._scrollwidget = ba.scrollwidget(parent=self._root_widget, position=(44 + x_inset, 35 + y_extra), size=(scroll_width, height - 116), highlight=False, claims_left_right=True, claims_tab=True, selection_loops_to_parent=True) self._subcontainer = ba.containerwidget(parent=self._scrollwidget, size=(scroll_width, scroll_height), background=False, claims_left_right=True, claims_tab=True, selection_loops_to_parent=True) v = scroll_height - 5 h = -40 # Keep track of all the selectable widgets we make so we can wire # them up conveniently. widget_column: list[list[ba.Widget]] = [] # Map select button. ba.textwidget(parent=self._subcontainer, position=(h + 49, v - 63), size=(100, 30), maxwidth=110, text=ba.Lstr(resource='mapText'), h_align='left', color=(0.8, 0.8, 0.8, 1.0), v_align='center') ba.imagewidget( parent=self._subcontainer, size=(256 * 0.7, 125 * 0.7), position=(h + 261 - 128 + 128.0 * 0.56, v - 90), texture=map_tex, model_opaque=ba.getmodel('level_select_button_opaque'), model_transparent=ba.getmodel('level_select_button_transparent'), mask_texture=ba.gettexture('mapPreviewMask')) map_button = btn = ba.buttonwidget( parent=self._subcontainer, size=(140, 60), position=(h + 448, v - 72), on_activate_call=ba.Call(self._select_map), scale=0.7, label=ba.Lstr(resource='mapSelectText')) widget_column.append([btn]) ba.textwidget(parent=self._subcontainer, position=(h + 363 - 123, v - 114), size=(100, 30), flatness=1.0, shadow=1.0, scale=0.55, maxwidth=256 * 0.7 * 0.8, text=get_map_display_string(self._map), h_align='center', color=(0.6, 1.0, 0.6, 1.0), v_align='center') v -= map_height for setting in self._settings_defs: value = setting.default value_type = type(value) # Now, if there's an existing value for it in the config, # override with that. try: if (config is not None and 'settings' in config and setting.name in config['settings']): value = value_type(config['settings'][setting.name]) except Exception: ba.print_exception() # Shove the starting value in there to start. self._settings[setting.name] = value name_translated = self._get_localized_setting_name(setting.name) mw1 = 280 mw2 = 70 # Handle types with choices specially: if isinstance(setting, ba.ChoiceSetting): for choice in setting.choices: if len(choice) != 2: raise ValueError( "Expected 2-member tuples for 'choices'; got: " + repr(choice)) if not isinstance(choice[0], str): raise TypeError( 'First value for choice tuple must be a str; got: ' + repr(choice)) if not isinstance(choice[1], value_type): raise TypeError( 'Choice type does not match default value; choice:' + repr(choice) + '; setting:' + repr(setting)) if value_type not in (int, float): raise TypeError( 'Choice type setting must have int or float default; ' 'got: ' + repr(setting)) # Start at the choice corresponding to the default if possible. self._choice_selections[setting.name] = 0 for index, choice in enumerate(setting.choices): if choice[1] == value: self._choice_selections[setting.name] = index break v -= spacing ba.textwidget(parent=self._subcontainer, position=(h + 50, v), size=(100, 30), maxwidth=mw1, text=name_translated, h_align='left', color=(0.8, 0.8, 0.8, 1.0), v_align='center') txt = ba.textwidget( parent=self._subcontainer, position=(h + 509 - 95, v), size=(0, 28), text=self._get_localized_setting_name(setting.choices[ self._choice_selections[setting.name]][0]), editable=False, color=(0.6, 1.0, 0.6, 1.0), maxwidth=mw2, h_align='right', v_align='center', padding=2) btn1 = ba.buttonwidget(parent=self._subcontainer, position=(h + 509 - 50 - 1, v), size=(28, 28), label='<', autoselect=True, on_activate_call=ba.Call( self._choice_inc, setting.name, txt, setting, -1), repeat=True) btn2 = ba.buttonwidget(parent=self._subcontainer, position=(h + 509 + 5, v), size=(28, 28), label='>', autoselect=True, on_activate_call=ba.Call( self._choice_inc, setting.name, txt, setting, 1), repeat=True) widget_column.append([btn1, btn2]) elif isinstance(setting, (ba.IntSetting, ba.FloatSetting)): v -= spacing min_value = setting.min_value max_value = setting.max_value increment = setting.increment ba.textwidget(parent=self._subcontainer, position=(h + 50, v), size=(100, 30), text=name_translated, h_align='left', color=(0.8, 0.8, 0.8, 1.0), v_align='center', maxwidth=mw1) txt = ba.textwidget(parent=self._subcontainer, position=(h + 509 - 95, v), size=(0, 28), text=str(value), editable=False, color=(0.6, 1.0, 0.6, 1.0), maxwidth=mw2, h_align='right', v_align='center', padding=2) btn1 = ba.buttonwidget(parent=self._subcontainer, position=(h + 509 - 50 - 1, v), size=(28, 28), label='-', autoselect=True, on_activate_call=ba.Call( self._inc, txt, min_value, max_value, -increment, value_type, setting.name), repeat=True) btn2 = ba.buttonwidget(parent=self._subcontainer, position=(h + 509 + 5, v), size=(28, 28), label='+', autoselect=True, on_activate_call=ba.Call( self._inc, txt, min_value, max_value, increment, value_type, setting.name), repeat=True) widget_column.append([btn1, btn2]) elif value_type == bool: v -= spacing ba.textwidget(parent=self._subcontainer, position=(h + 50, v), size=(100, 30), text=name_translated, h_align='left', color=(0.8, 0.8, 0.8, 1.0), v_align='center', maxwidth=mw1) txt = ba.textwidget( parent=self._subcontainer, position=(h + 509 - 95, v), size=(0, 28), text=ba.Lstr(resource='onText') if value else ba.Lstr( resource='offText'), editable=False, color=(0.6, 1.0, 0.6, 1.0), maxwidth=mw2, h_align='right', v_align='center', padding=2) cbw = ba.checkboxwidget(parent=self._subcontainer, text='', position=(h + 505 - 50 - 5, v - 2), size=(200, 30), autoselect=True, textcolor=(0.8, 0.8, 0.8), value=value, on_value_change_call=ba.Call( self._check_value_change, setting.name, txt)) widget_column.append([cbw]) else: raise Exception() # Ok now wire up the column. try: # pylint: disable=unsubscriptable-object prev_widgets: Optional[list[ba.Widget]] = None for cwdg in widget_column: if prev_widgets is not None: # Wire our rightmost to their rightmost. ba.widget(edit=prev_widgets[-1], down_widget=cwdg[-1]) ba.widget(cwdg[-1], up_widget=prev_widgets[-1]) # Wire our leftmost to their leftmost. ba.widget(edit=prev_widgets[0], down_widget=cwdg[0]) ba.widget(cwdg[0], up_widget=prev_widgets[0]) prev_widgets = cwdg except Exception: ba.print_exception( 'Error wiring up game-settings-select widget column.') ba.buttonwidget(edit=add_button, on_activate_call=ba.Call(self._add)) ba.containerwidget(edit=self._root_widget, selected_child=add_button, start_button=add_button) if default_selection == 'map': ba.containerwidget(edit=self._root_widget, selected_child=self._scrollwidget) ba.containerwidget(edit=self._subcontainer, selected_child=map_button)
def __init__(self, transition: Optional[str] = 'in_right', origin_widget: ba.Widget = None): # pylint: disable=too-many-statements # pylint: disable=too-many-locals ba.set_analytics_screen('Gather Window') 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 ba.app.ui.set_main_menu_location('Gather') _ba.set_party_icon_always_visible(True) uiscale = ba.app.ui.uiscale self._width = 1240 if uiscale is ba.UIScale.SMALL else 1040 x_offs = 100 if uiscale is ba.UIScale.SMALL else 0 self._height = (582 if uiscale is ba.UIScale.SMALL else 680 if uiscale is ba.UIScale.MEDIUM else 800) self._current_tab: Optional[GatherWindow.TabID] = None extra_top = 20 if uiscale is ba.UIScale.SMALL else 0 self._r = 'gatherWindow' super().__init__(root_widget=ba.containerwidget( size=(self._width, self._height + extra_top), transition=transition, toolbar_visibility='menu_minimal', scale_origin_stack_offset=scale_origin, scale=(1.3 if uiscale is ba.UIScale.SMALL else 0.97 if uiscale is ba.UIScale.MEDIUM else 0.8), stack_offset=(0, -11) if uiscale is ba.UIScale.SMALL else ( 0, 0) if uiscale is ba.UIScale.MEDIUM else (0, 0))) if uiscale is ba.UIScale.SMALL and ba.app.ui.use_toolbars: ba.containerwidget(edit=self._root_widget, on_cancel_call=self._back) self._back_button = None else: self._back_button = btn = ba.buttonwidget( parent=self._root_widget, position=(70 + x_offs, self._height - 74), size=(140, 60), scale=1.1, autoselect=True, label=ba.Lstr(resource='backText'), button_type='back', on_activate_call=self._back) ba.containerwidget(edit=self._root_widget, cancel_button=btn) ba.buttonwidget(edit=btn, button_type='backSmall', position=(70 + x_offs, self._height - 78), size=(60, 60), label=ba.charstr(ba.SpecialChar.BACK)) condensed = uiscale is not ba.UIScale.LARGE t_offs_y = (0 if not condensed else 25 if uiscale is ba.UIScale.MEDIUM else 17) ba.textwidget(parent=self._root_widget, position=(self._width * 0.5, self._height - 42 + t_offs_y), size=(0, 0), color=ba.app.ui.title_color, scale=(1.5 if not condensed else 1.0 if uiscale is ba.UIScale.MEDIUM else 0.6), h_align='center', v_align='center', text=ba.Lstr(resource=self._r + '.titleText'), maxwidth=550) platform = ba.app.platform subplatform = ba.app.subplatform scroll_buffer_h = 130 + 2 * x_offs tab_buffer_h = ((320 if condensed else 250) + 2 * x_offs) # Build up the set of tabs we want. tabdefs: List[Tuple[GatherWindow.TabID, ba.Lstr]] = [ (self.TabID.ABOUT, ba.Lstr(resource=self._r + '.aboutText')) ] if _ba.get_account_misc_read_val('enablePublicParties', True): tabdefs.append((self.TabID.INTERNET, ba.Lstr(resource=self._r + '.internetText'))) if platform == 'android' and subplatform == 'google': tabdefs.append((self.TabID.GOOGLE_PLAY, ba.Lstr(resource=self._r + '.googlePlayText'))) tabdefs.append((self.TabID.LOCAL_NETWORK, ba.Lstr(resource=self._r + '.localNetworkText'))) tabdefs.append( (self.TabID.MANUAL, ba.Lstr(resource=self._r + '.manualText'))) # On small UI, push our tabs up closer to the top of the screen to # save a bit of space. tabs_top_extra = 42 if condensed else 0 self._tab_row = TabRow(self._root_widget, tabdefs, pos=(tab_buffer_h * 0.5, self._height - 130 + tabs_top_extra), size=(self._width - tab_buffer_h, 50), on_select_call=ba.WeakCall(self._set_tab)) # Now instantiate handlers for these tabs. tabtypes: Dict[GatherWindow.TabID, Type[GatherTab]] = { self.TabID.ABOUT: AboutGatherTab, self.TabID.MANUAL: ManualGatherTab, self.TabID.GOOGLE_PLAY: GooglePlayGatherTab, self.TabID.INTERNET: PublicGatherTab, self.TabID.LOCAL_NETWORK: NearbyGatherTab } self._tabs: Dict[GatherWindow.TabID, GatherTab] = {} for tab_id in self._tab_row.tabs: tabtype = tabtypes.get(tab_id) if tabtype is not None: self._tabs[tab_id] = tabtype(self) if ba.app.ui.use_toolbars: ba.widget(edit=self._tab_row.tabs[tabdefs[-1][0]].button, right_widget=_ba.get_special_widget('party_button')) if uiscale is ba.UIScale.SMALL: ba.widget(edit=self._tab_row.tabs[tabdefs[0][0]].button, left_widget=_ba.get_special_widget('back_button')) self._scroll_width = self._width - scroll_buffer_h self._scroll_height = self._height - 180.0 + tabs_top_extra self._scroll_left = (self._width - self._scroll_width) * 0.5 self._scroll_bottom = (self._height - self._scroll_height - 79 - 48 + tabs_top_extra) buffer_h = 10 buffer_v = 4 # Not actually using a scroll widget anymore; just an image. ba.imagewidget(parent=self._root_widget, position=(self._scroll_left - buffer_h, self._scroll_bottom - buffer_v), size=(self._scroll_width + 2 * buffer_h, self._scroll_height + 2 * buffer_v), texture=ba.gettexture('scrollWidget'), model_transparent=ba.getmodel('softEdgeOutside')) self._tab_container: Optional[ba.Widget] = None self._restore_state()
def __init__(self, transition: str = 'in_right', origin_widget: ba.Widget = None): # pylint: disable=too-many-statements # pylint: disable=too-many-locals ba.set_analytics_screen('Settings Window') 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 width = 900 if ba.app.small_ui else 580 x_inset = 75 if ba.app.small_ui else 0 height = 435 # button_height = 42 self._r = 'settingsWindow' top_extra = 20 if ba.app.small_ui else 0 super().__init__(root_widget=ba.containerwidget( size=(width, height + top_extra), transition=transition, toolbar_visibility='menu_minimal', scale_origin_stack_offset=scale_origin, scale=( 1.75 if ba.app.small_ui else 1.35 if ba.app.med_ui else 1.0), stack_offset=(0, -8) if ba.app.small_ui else (0, 0))) if ba.app.toolbars and ba.app.small_ui: self._back_button = None ba.containerwidget(edit=self._root_widget, on_cancel_call=self._do_back) else: self._back_button = btn = ba.buttonwidget( parent=self._root_widget, autoselect=True, position=(40 + x_inset, height - 55), size=(130, 60), scale=0.8, text_scale=1.2, label=ba.Lstr(resource='backText'), button_type='back', on_activate_call=self._do_back) ba.containerwidget(edit=self._root_widget, cancel_button=btn) ba.textwidget(parent=self._root_widget, position=(0, height - 44), size=(width, 25), text=ba.Lstr(resource=self._r + '.titleText'), color=ba.app.title_color, h_align='center', v_align='center', maxwidth=130) if self._back_button is not None: ba.buttonwidget(edit=self._back_button, button_type='backSmall', size=(60, 60), label=ba.charstr(ba.SpecialChar.BACK)) v = height - 80 v -= 145 basew = 280 if ba.app.small_ui else 230 baseh = 170 x_offs = x_inset + (105 if ba.app.small_ui else 72) - basew # now unused x_offs2 = x_offs + basew - 7 x_offs3 = x_offs + 2 * (basew - 7) x_offs4 = x_offs2 x_offs5 = x_offs3 def _b_title(x: float, y: float, button: ba.Widget, text: Union[str, ba.Lstr]) -> None: ba.textwidget(parent=self._root_widget, text=text, position=(x + basew * 0.47, y + baseh * 0.22), maxwidth=basew * 0.7, size=(0, 0), h_align='center', v_align='center', draw_controller=button, color=(0.7, 0.9, 0.7, 1.0)) ctb = self._controllers_button = ba.buttonwidget( parent=self._root_widget, autoselect=True, position=(x_offs2, v), size=(basew, baseh), button_type='square', label='', on_activate_call=self._do_controllers) if ba.app.toolbars and self._back_button is None: bbtn = _ba.get_special_widget('back_button') ba.widget(edit=ctb, left_widget=bbtn) _b_title(x_offs2, v, ctb, ba.Lstr(resource=self._r + '.controllersText')) imgw = imgh = 130 ba.imagewidget(parent=self._root_widget, position=(x_offs2 + basew * 0.49 - imgw * 0.5, v + 35), size=(imgw, imgh), texture=ba.gettexture('controllerIcon'), draw_controller=ctb) gfxb = self._graphics_button = ba.buttonwidget( parent=self._root_widget, autoselect=True, position=(x_offs3, v), size=(basew, baseh), button_type='square', label='', on_activate_call=self._do_graphics) if ba.app.toolbars: pbtn = _ba.get_special_widget('party_button') ba.widget(edit=gfxb, up_widget=pbtn, right_widget=pbtn) _b_title(x_offs3, v, gfxb, ba.Lstr(resource=self._r + '.graphicsText')) imgw = imgh = 110 ba.imagewidget(parent=self._root_widget, position=(x_offs3 + basew * 0.49 - imgw * 0.5, v + 42), size=(imgw, imgh), texture=ba.gettexture('graphicsIcon'), draw_controller=gfxb) v -= (baseh - 5) abtn = self._audio_button = ba.buttonwidget( parent=self._root_widget, autoselect=True, position=(x_offs4, v), size=(basew, baseh), button_type='square', label='', on_activate_call=self._do_audio) _b_title(x_offs4, v, abtn, ba.Lstr(resource=self._r + '.audioText')) imgw = imgh = 120 ba.imagewidget(parent=self._root_widget, position=(x_offs4 + basew * 0.49 - imgw * 0.5 + 5, v + 35), size=(imgw, imgh), color=(1, 1, 0), texture=ba.gettexture('audioIcon'), draw_controller=abtn) avb = self._advanced_button = ba.buttonwidget( parent=self._root_widget, autoselect=True, position=(x_offs5, v), size=(basew, baseh), button_type='square', label='', on_activate_call=self._do_advanced) _b_title(x_offs5, v, avb, ba.Lstr(resource=self._r + '.advancedText')) imgw = imgh = 120 ba.imagewidget(parent=self._root_widget, position=(x_offs5 + basew * 0.49 - imgw * 0.5 + 5, v + 35), size=(imgw, imgh), color=(0.8, 0.95, 1), texture=ba.gettexture('advancedIcon'), draw_controller=avb)
def __init__(self, transition: str = 'in_right'): # pylint: disable=too-many-locals, too-many-statements from bastd.ui import confirm self._width = 720.0 self._height = 340.0 def _do_cancel() -> None: confirm.QuitWindow(swish=True, back=True) super().__init__( root_widget=ba.containerwidget(size=(self._width, self._height), transition=transition, on_cancel_call=_do_cancel, background=False, stack_offset=(0, -130))) self._r = 'kioskWindow' self._show_multiplayer = False # Let's reset all random player names every time we hit the main menu. _ba.reset_random_player_names() # Reset achievements too (at least locally). ba.app.config['Achievements'] = {} t_delay_base = 0.0 t_delay_scale = 0.0 if not ba.app.did_menu_intro: t_delay_base = 1.0 t_delay_scale = 1.0 model_opaque = ba.getmodel('level_select_button_opaque') model_transparent = ba.getmodel('level_select_button_transparent') mask_tex = ba.gettexture('mapPreviewMask') y_extra = 130.0 + (0.0 if self._show_multiplayer else -130.0) b_width = 250.0 b_height = 200.0 b_space = 280.0 b_v = 80.0 + y_extra label_height = 130.0 + y_extra img_width = 180.0 img_v = 158.0 + y_extra if self._show_multiplayer: tdelay = t_delay_base + t_delay_scale * 1.3 ba.textwidget( parent=self._root_widget, size=(0, 0), position=(self._width * 0.5, self._height + y_extra - 44), transition_delay=tdelay, text=ba.Lstr(resource=self._r + '.singlePlayerExamplesText'), flatness=1.0, scale=1.2, h_align='center', v_align='center', shadow=1.0) else: tdelay = t_delay_base + t_delay_scale * 0.7 ba.textwidget( parent=self._root_widget, size=(0, 0), position=(self._width * 0.5, self._height + y_extra - 34), transition_delay=tdelay, text=ba.Lstr(resource='demoText', fallback_resource='mainMenu.demoMenuText'), flatness=1.0, scale=1.2, h_align='center', v_align='center', shadow=1.0) h = self._width * 0.5 - b_space tdelay = t_delay_base + t_delay_scale * 0.7 self._b1 = btn = ba.buttonwidget(parent=self._root_widget, autoselect=True, size=(b_width, b_height), on_activate_call=ba.Call( self._do_game, 'easy'), transition_delay=tdelay, position=(h - b_width * 0.5, b_v), label='', button_type='square') ba.textwidget(parent=self._root_widget, draw_controller=btn, transition_delay=tdelay, size=(0, 0), position=(h, label_height), maxwidth=b_width * 0.7, text=ba.Lstr(resource=self._r + '.easyText'), scale=1.3, h_align='center', v_align='center') ba.imagewidget(parent=self._root_widget, draw_controller=btn, size=(img_width, 0.5 * img_width), transition_delay=tdelay, position=(h - img_width * 0.5, img_v), texture=ba.gettexture('doomShroomPreview'), model_opaque=model_opaque, model_transparent=model_transparent, mask_texture=mask_tex) h = self._width * 0.5 tdelay = t_delay_base + t_delay_scale * 0.65 self._b2 = btn = ba.buttonwidget(parent=self._root_widget, autoselect=True, size=(b_width, b_height), on_activate_call=ba.Call( self._do_game, 'medium'), position=(h - b_width * 0.5, b_v), label='', button_type='square', transition_delay=tdelay) ba.textwidget(parent=self._root_widget, draw_controller=btn, transition_delay=tdelay, size=(0, 0), position=(h, label_height), maxwidth=b_width * 0.7, text=ba.Lstr(resource=self._r + '.mediumText'), scale=1.3, h_align='center', v_align='center') ba.imagewidget(parent=self._root_widget, draw_controller=btn, size=(img_width, 0.5 * img_width), transition_delay=tdelay, position=(h - img_width * 0.5, img_v), texture=ba.gettexture('footballStadiumPreview'), model_opaque=model_opaque, model_transparent=model_transparent, mask_texture=mask_tex) h = self._width * 0.5 + b_space tdelay = t_delay_base + t_delay_scale * 0.6 self._b3 = btn = ba.buttonwidget(parent=self._root_widget, autoselect=True, size=(b_width, b_height), on_activate_call=ba.Call( self._do_game, 'hard'), transition_delay=tdelay, position=(h - b_width * 0.5, b_v), label='', button_type='square') ba.textwidget(parent=self._root_widget, draw_controller=btn, transition_delay=tdelay, size=(0, 0), position=(h, label_height), maxwidth=b_width * 0.7, text='Hard', scale=1.3, h_align='center', v_align='center') ba.imagewidget(parent=self._root_widget, draw_controller=btn, transition_delay=tdelay, size=(img_width, 0.5 * img_width), position=(h - img_width * 0.5, img_v), texture=ba.gettexture('courtyardPreview'), model_opaque=model_opaque, model_transparent=model_transparent, mask_texture=mask_tex) if not ba.app.did_menu_intro: ba.app.did_menu_intro = True self._b4: Optional[ba.Widget] self._b5: Optional[ba.Widget] self._b6: Optional[ba.Widget] if bool(False): ba.textwidget( parent=self._root_widget, size=(0, 0), position=(self._width * 0.5, self._height + y_extra - 44), transition_delay=tdelay, text=ba.Lstr(resource=self._r + '.versusExamplesText'), flatness=1.0, scale=1.2, h_align='center', v_align='center', shadow=1.0) h = self._width * 0.5 - b_space tdelay = t_delay_base + t_delay_scale * 0.7 self._b4 = btn = ba.buttonwidget(parent=self._root_widget, autoselect=True, size=(b_width, b_height), on_activate_call=ba.Call( self._do_game, 'ctf'), transition_delay=tdelay, position=(h - b_width * 0.5, b_v), label='', button_type='square') ba.textwidget(parent=self._root_widget, draw_controller=btn, transition_delay=tdelay, size=(0, 0), position=(h, label_height), maxwidth=b_width * 0.7, text=ba.Lstr(translate=('gameNames', 'Capture the Flag')), scale=1.3, h_align='center', v_align='center') ba.imagewidget(parent=self._root_widget, draw_controller=btn, size=(img_width, 0.5 * img_width), transition_delay=tdelay, position=(h - img_width * 0.5, img_v), texture=ba.gettexture('bridgitPreview'), model_opaque=model_opaque, model_transparent=model_transparent, mask_texture=mask_tex) h = self._width * 0.5 tdelay = t_delay_base + t_delay_scale * 0.65 self._b5 = btn = ba.buttonwidget(parent=self._root_widget, autoselect=True, size=(b_width, b_height), on_activate_call=ba.Call( self._do_game, 'hockey'), position=(h - b_width * 0.5, b_v), label='', button_type='square', transition_delay=tdelay) ba.textwidget(parent=self._root_widget, draw_controller=btn, transition_delay=tdelay, size=(0, 0), position=(h, label_height), maxwidth=b_width * 0.7, text=ba.Lstr(translate=('gameNames', 'Hockey')), scale=1.3, h_align='center', v_align='center') ba.imagewidget(parent=self._root_widget, draw_controller=btn, size=(img_width, 0.5 * img_width), transition_delay=tdelay, position=(h - img_width * 0.5, img_v), texture=ba.gettexture('hockeyStadiumPreview'), model_opaque=model_opaque, model_transparent=model_transparent, mask_texture=mask_tex) h = self._width * 0.5 + b_space tdelay = t_delay_base + t_delay_scale * 0.6 self._b6 = btn = ba.buttonwidget(parent=self._root_widget, autoselect=True, size=(b_width, b_height), on_activate_call=ba.Call( self._do_game, 'epic'), transition_delay=tdelay, position=(h - b_width * 0.5, b_v), label='', button_type='square') ba.textwidget(parent=self._root_widget, draw_controller=btn, transition_delay=tdelay, size=(0, 0), position=(h, label_height), maxwidth=b_width * 0.7, text=ba.Lstr(resource=self._r + '.epicModeText'), scale=1.3, h_align='center', v_align='center') ba.imagewidget(parent=self._root_widget, draw_controller=btn, transition_delay=tdelay, size=(img_width, 0.5 * img_width), position=(h - img_width * 0.5, img_v), texture=ba.gettexture('tipTopPreview'), model_opaque=model_opaque, model_transparent=model_transparent, mask_texture=mask_tex) else: self._b4 = self._b5 = self._b6 = None self._b7: Optional[ba.Widget] if bool(False): self._b7 = ba.buttonwidget( parent=self._root_widget, autoselect=True, size=(b_width, 50), color=(0.45, 0.55, 0.45), textcolor=(0.7, 0.8, 0.7), scale=0.5, position=((self._width * 0.5 - 37.5, y_extra + 120) if not self._show_multiplayer else (self._width + 100, y_extra + (140 if ba.app.small_ui else 120))), transition_delay=tdelay, label=ba.Lstr(resource=self._r + '.fullMenuText'), on_activate_call=self._do_full_menu) else: self._b7 = None self._restore_state() self._update() self._update_timer = ba.Timer(1.0, ba.WeakCall(self._update), timetype=ba.TimeType.REAL, repeat=True)
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 _update(self) -> None: have = ba.app.accounts.have_pro_options() for lock in self._lock_images: ba.imagewidget(edit=lock, opacity=0.0 if have else 1.0)
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-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: scale = (2.3 if ba.app.small_ui else 1.65 if ba.app.med_ui 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 _refresh(self) -> None: # FIXME: Should tidy this up. # pylint: disable=too-many-statements # pylint: disable=too-many-branches # pylint: disable=too-many-locals # pylint: disable=too-many-nested-blocks from ba.internal import (get_map_class, get_default_free_for_all_playlist, get_default_teams_playlist, filter_playlist) if not self._root_widget: return if self._subcontainer is not None: self._save_state() self._subcontainer.delete() # make sure config exists if self._config_name_full not in ba.app.config: ba.app.config[self._config_name_full] = {} items = list(ba.app.config[self._config_name_full].items()) # make sure everything is unicode items = [(i[0].decode(), i[1]) if not isinstance(i[0], str) else i for i in items] items.sort(key=lambda x2: x2[0].lower()) items = [['__default__', None]] + items # default is always first count = len(items) columns = 3 rows = int(math.ceil(float(count) / columns)) button_width = 230 button_height = 230 button_buffer_h = -3 button_buffer_v = 0 self._sub_width = self._scroll_width self._sub_height = 40 + rows * (button_height + 2 * button_buffer_v) + 90 assert self._sub_width is not None assert self._sub_height is not None self._subcontainer = ba.containerwidget(parent=self._scrollwidget, size=(self._sub_width, self._sub_height), background=False) children = self._subcontainer.get_children() for child in children: child.delete() ba.textwidget(parent=self._subcontainer, text=ba.Lstr(resource='playlistsText'), position=(40, self._sub_height - 26), size=(0, 0), scale=1.0, maxwidth=400, color=ba.app.title_color, h_align='left', v_align='center') index = 0 bs_config = ba.app.config model_opaque = ba.getmodel('level_select_button_opaque') model_transparent = ba.getmodel('level_select_button_transparent') mask_tex = ba.gettexture('mapPreviewMask') h_offs = 225 if count == 1 else 115 if count == 2 else 0 h_offs_bottom = 0 for y in range(rows): for x in range(columns): name = items[index][0] assert name is not None pos = (x * (button_width + 2 * button_buffer_h) + button_buffer_h + 8 + h_offs, self._sub_height - 47 - (y + 1) * (button_height + 2 * button_buffer_v)) btn = ba.buttonwidget(parent=self._subcontainer, button_type='square', size=(button_width, button_height), autoselect=True, label='', position=pos) if x == 0 and ba.app.toolbars and ba.app.small_ui: ba.widget( edit=btn, left_widget=_ba.get_special_widget('back_button')) if x == columns - 1 and ba.app.toolbars and ba.app.small_ui: ba.widget( edit=btn, right_widget=_ba.get_special_widget('party_button')) ba.buttonwidget( edit=btn, on_activate_call=ba.Call(self._on_playlist_press, btn, name), on_select_call=ba.Call(self._on_playlist_select, name)) ba.widget(edit=btn, show_buffer_top=50, show_buffer_bottom=50) if self._selected_playlist == name: ba.containerwidget(edit=self._subcontainer, selected_child=btn, visible_child=btn) if self._back_button is not None: if y == 0: ba.widget(edit=btn, up_widget=self._back_button) if x == 0: ba.widget(edit=btn, left_widget=self._back_button) print_name: Optional[Union[str, ba.Lstr]] if name == '__default__': print_name = self._pvars.default_list_name else: print_name = name ba.textwidget(parent=self._subcontainer, text=print_name, position=(pos[0] + button_width * 0.5, pos[1] + button_height * 0.79), size=(0, 0), scale=button_width * 0.003, maxwidth=button_width * 0.7, draw_controller=btn, h_align='center', v_align='center') # Poke into this playlist and see if we can display some of # its maps. map_images = [] try: map_textures = [] map_texture_entries = [] if name == '__default__': if self._sessiontype is ba.FreeForAllSession: playlist = (get_default_free_for_all_playlist()) elif self._sessiontype is ba.TeamsSession: playlist = get_default_teams_playlist() else: raise Exception("unrecognized session-type: " + str(self._sessiontype)) else: if name not in bs_config[self._pvars.config_name + ' Playlists']: print( 'NOT FOUND ERR', bs_config[self._pvars.config_name + ' Playlists']) playlist = bs_config[self._pvars.config_name + ' Playlists'][name] playlist = filter_playlist(playlist, self._sessiontype, remove_unowned=False, mark_unowned=True) for entry in playlist: mapname = entry['settings']['map'] maptype: Optional[Type[ba.Map]] try: maptype = get_map_class(mapname) except Exception: maptype = None if maptype is not None: tex_name = maptype.get_preview_texture_name() if tex_name is not None: map_textures.append(tex_name) map_texture_entries.append(entry) if len(map_textures) >= 6: break if len(map_textures) > 4: img_rows = 3 img_columns = 2 scl = 0.33 h_offs_img = 30 v_offs_img = 126 elif len(map_textures) > 2: img_rows = 2 img_columns = 2 scl = 0.35 h_offs_img = 24 v_offs_img = 110 elif len(map_textures) > 1: img_rows = 2 img_columns = 1 scl = 0.5 h_offs_img = 47 v_offs_img = 105 else: img_rows = 1 img_columns = 1 scl = 0.75 h_offs_img = 20 v_offs_img = 65 v = None for row in range(img_rows): for col in range(img_columns): tex_index = row * img_columns + col if tex_index < len(map_textures): entry = map_texture_entries[tex_index] owned = not (('is_unowned_map' in entry and entry['is_unowned_map']) or ('is_unowned_game' in entry and entry['is_unowned_game'])) tex_name = map_textures[tex_index] h = pos[0] + h_offs_img + scl * 250 * col v = pos[1] + v_offs_img - scl * 130 * row map_images.append( ba.imagewidget( parent=self._subcontainer, size=(scl * 250.0, scl * 125.0), position=(h, v), texture=ba.gettexture(tex_name), opacity=1.0 if owned else 0.25, draw_controller=btn, model_opaque=model_opaque, model_transparent=model_transparent, mask_texture=mask_tex)) if not owned: ba.imagewidget( parent=self._subcontainer, size=(scl * 100.0, scl * 100.0), position=(h + scl * 75, v + scl * 10), texture=ba.gettexture('lock'), draw_controller=btn) if v is not None: v -= scl * 130.0 except Exception: ba.print_exception("error listing playlist maps") if not map_images: ba.textwidget(parent=self._subcontainer, text='???', scale=1.5, size=(0, 0), color=(1, 1, 1, 0.5), h_align='center', v_align='center', draw_controller=btn, position=(pos[0] + button_width * 0.5, pos[1] + button_height * 0.5)) index += 1 if index >= count: break if index >= count: break self._customize_button = btn = ba.buttonwidget( parent=self._subcontainer, size=(100, 30), position=(34 + h_offs_bottom, 50), text_scale=0.6, label=ba.Lstr(resource='customizeText'), on_activate_call=self._on_customize_press, color=(0.54, 0.52, 0.67), textcolor=(0.7, 0.65, 0.7), autoselect=True) ba.widget(edit=btn, show_buffer_top=22, show_buffer_bottom=28) self._restore_state()
def _on_query_response(self, data: Optional[dict[str, Any]]) -> None: # FIXME: Tidy this up. # pylint: disable=too-many-locals # pylint: disable=too-many-branches # pylint: disable=too-many-statements # pylint: disable=too-many-nested-blocks if data is None: ba.textwidget( edit=self._loading_text, text=ba.Lstr(resource='internal.unavailableNoConnectionText')) else: try: self._loading_text.delete() trophystr = '' try: trophystr = data['trophies'] num = 10 chunks = [ trophystr[i:i + num] for i in range(0, len(trophystr), num) ] trophystr = ('\n\n'.join(chunks)) if trophystr == '': trophystr = '-' except Exception: ba.print_exception('Error displaying trophies.') account_name_spacing = 15 tscale = 0.65 ts_height = _ba.get_string_height(trophystr, suppress_warning=True) sub_width = self._width - 80 sub_height = 200 + ts_height * tscale + \ account_name_spacing * len(data['accountDisplayStrings']) self._subcontainer = ba.containerwidget( parent=self._scrollwidget, size=(sub_width, sub_height), background=False) v = sub_height - 20 title_scale = 0.37 center = 0.3 maxwidth_scale = 0.45 showing_character = False if data['profileDisplayString'] is not None: tint_color = (1, 1, 1) try: if data['profile'] is not None: profile = data['profile'] character = ba.app.spaz_appearances.get( profile['character'], None) if character is not None: tint_color = (profile['color'] if 'color' in profile else (1, 1, 1)) tint2_color = (profile['highlight'] if 'highlight' in profile else (1, 1, 1)) icon_tex = character.icon_texture tint_tex = character.icon_mask_texture mask_texture = ba.gettexture( 'characterIconMask') ba.imagewidget( parent=self._subcontainer, position=(sub_width * center - 40, v - 80), size=(80, 80), color=(1, 1, 1), mask_texture=mask_texture, texture=ba.gettexture(icon_tex), tint_texture=ba.gettexture(tint_tex), tint_color=tint_color, tint2_color=tint2_color) v -= 95 except Exception: ba.print_exception('Error displaying character.') ba.textwidget( parent=self._subcontainer, size=(0, 0), position=(sub_width * center, v), h_align='center', v_align='center', scale=0.9, color=ba.safecolor(tint_color, 0.7), shadow=1.0, text=ba.Lstr(value=data['profileDisplayString']), maxwidth=sub_width * maxwidth_scale * 0.75) showing_character = True v -= 33 center = 0.75 if showing_character else 0.5 maxwidth_scale = 0.45 if showing_character else 0.9 v = sub_height - 20 if len(data['accountDisplayStrings']) <= 1: account_title = ba.Lstr( resource='settingsWindow.accountText') else: account_title = ba.Lstr( resource='accountSettingsWindow.accountsText', fallback_resource='settingsWindow.accountText') ba.textwidget(parent=self._subcontainer, size=(0, 0), position=(sub_width * center, v), flatness=1.0, h_align='center', v_align='center', scale=title_scale, color=ba.app.ui.infotextcolor, text=account_title, maxwidth=sub_width * maxwidth_scale) draw_small = (showing_character or len(data['accountDisplayStrings']) > 1) v -= 14 if draw_small else 20 for account_string in data['accountDisplayStrings']: ba.textwidget(parent=self._subcontainer, size=(0, 0), position=(sub_width * center, v), h_align='center', v_align='center', scale=0.55 if draw_small else 0.8, text=account_string, maxwidth=sub_width * maxwidth_scale) v -= account_name_spacing v += account_name_spacing v -= 25 if showing_character else 29 ba.textwidget(parent=self._subcontainer, size=(0, 0), position=(sub_width * center, v), flatness=1.0, h_align='center', v_align='center', scale=title_scale, color=ba.app.ui.infotextcolor, text=ba.Lstr(resource='rankText'), maxwidth=sub_width * maxwidth_scale) v -= 14 if data['rank'] is None: rank_str = '-' suffix_offset = None else: str_raw = ba.Lstr( resource='league.rankInLeagueText').evaluate() # FIXME: Would be nice to not have to eval this. rank_str = ba.Lstr( resource='league.rankInLeagueText', subs=[('${RANK}', str(data['rank'][2])), ('${NAME}', ba.Lstr(translate=('leagueNames', data['rank'][0]))), ('${SUFFIX}', '')]).evaluate() rank_str_width = min( sub_width * maxwidth_scale, _ba.get_string_width(rank_str, suppress_warning=True) * 0.55) # Only tack our suffix on if its at the end and only for # non-diamond leagues. if (str_raw.endswith('${SUFFIX}') and data['rank'][0] != 'Diamond'): suffix_offset = rank_str_width * 0.5 + 2 else: suffix_offset = None ba.textwidget(parent=self._subcontainer, size=(0, 0), position=(sub_width * center, v), h_align='center', v_align='center', scale=0.55, text=rank_str, maxwidth=sub_width * maxwidth_scale) if suffix_offset is not None: assert data['rank'] is not None ba.textwidget(parent=self._subcontainer, size=(0, 0), position=(sub_width * center + suffix_offset, v + 3), h_align='left', v_align='center', scale=0.29, flatness=1.0, text='[' + str(data['rank'][1]) + ']') v -= 14 str_raw = ba.Lstr( resource='league.rankInLeagueText').evaluate() old_offs = -50 prev_ranks_shown = 0 for prev_rank in data['prevRanks']: rank_str = ba.Lstr( value='${S}: ${I}', subs=[ ('${S}', ba.Lstr(resource='league.seasonText', subs=[('${NUMBER}', str(prev_rank[0]))])), ('${I}', ba.Lstr(resource='league.rankInLeagueText', subs=[('${RANK}', str(prev_rank[3])), ('${NAME}', ba.Lstr(translate=('leagueNames', prev_rank[1]))), ('${SUFFIX}', '')])) ]).evaluate() rank_str_width = min( sub_width * maxwidth_scale, _ba.get_string_width(rank_str, suppress_warning=True) * 0.3) # Only tack our suffix on if its at the end and only for # non-diamond leagues. if (str_raw.endswith('${SUFFIX}') and prev_rank[1] != 'Diamond'): suffix_offset = rank_str_width + 2 else: suffix_offset = None ba.textwidget(parent=self._subcontainer, size=(0, 0), position=(sub_width * center + old_offs, v), h_align='left', v_align='center', scale=0.3, text=rank_str, flatness=1.0, maxwidth=sub_width * maxwidth_scale) if suffix_offset is not None: ba.textwidget(parent=self._subcontainer, size=(0, 0), position=(sub_width * center + old_offs + suffix_offset, v + 1), h_align='left', v_align='center', scale=0.20, flatness=1.0, text='[' + str(prev_rank[2]) + ']') prev_ranks_shown += 1 v -= 10 v -= 13 ba.textwidget(parent=self._subcontainer, size=(0, 0), position=(sub_width * center, v), flatness=1.0, h_align='center', v_align='center', scale=title_scale, color=ba.app.ui.infotextcolor, text=ba.Lstr(resource='achievementsText'), maxwidth=sub_width * maxwidth_scale) v -= 14 ba.textwidget(parent=self._subcontainer, size=(0, 0), position=(sub_width * center, v), h_align='center', v_align='center', scale=0.55, text=str(data['achievementsCompleted']) + ' / ' + str(len(ba.app.ach.achievements)), maxwidth=sub_width * maxwidth_scale) v -= 25 if prev_ranks_shown == 0 and showing_character: v -= 20 elif prev_ranks_shown == 1 and showing_character: v -= 10 center = 0.5 maxwidth_scale = 0.9 ba.textwidget(parent=self._subcontainer, size=(0, 0), position=(sub_width * center, v), h_align='center', v_align='center', scale=title_scale, color=ba.app.ui.infotextcolor, flatness=1.0, text=ba.Lstr(resource='trophiesThisSeasonText', fallback_resource='trophiesText'), maxwidth=sub_width * maxwidth_scale) v -= 19 ba.textwidget(parent=self._subcontainer, size=(0, ts_height), position=(sub_width * 0.5, v - ts_height * tscale), h_align='center', v_align='top', corner_scale=tscale, text=trophystr) except Exception: ba.print_exception('Error displaying account info.')
def __init__(self, transition: str = 'in_right', origin_widget: ba.Widget = None): # pylint: disable=too-many-statements # pylint: disable=too-many-locals import threading # Preload some modules we use in a background thread so we won't # have a visual hitch when the user taps them. threading.Thread(target=self._preload_modules).start() new_style = True uiscale = ba.app.uiscale width = 1000 if uiscale is ba.UIScale.SMALL else 800 x_offs = 100 if uiscale is ba.UIScale.SMALL else 0 height = 550 if new_style else 400 button_width = 400 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 = 'playWindow' super().__init__(root_widget=ba.containerwidget( size=(width, height), transition=transition, toolbar_visibility='menu_full', scale_origin_stack_offset=scale_origin, scale=((1.6 if new_style else 1.52) if uiscale is ba.UIScale.SMALL else 0.9 if uiscale is ba.UIScale.MEDIUM else 0.8), stack_offset=((0, 0) if new_style else ( 10, 7)) if uiscale is ba.UIScale.SMALL else (0, 0))) self._back_button = back_button = btn = ba.buttonwidget( parent=self._root_widget, position=(55 + x_offs, height - 132) if new_style else (55, height - 92), size=(120, 60), scale=1.1, text_res_scale=1.5, text_scale=1.2, autoselect=True, label=ba.Lstr(resource='backText'), button_type='back') txt = ba.textwidget(parent=self._root_widget, position=(width * 0.5, height - (101 if new_style else 61)), size=(0, 0), text=ba.Lstr(resource=self._r + '.titleText'), scale=1.7, res_scale=2.0, maxwidth=400, color=ba.app.ui.heading_color, h_align='center', v_align='center') ba.buttonwidget(edit=btn, button_type='backSmall', size=(60, 60), label=ba.charstr(ba.SpecialChar.BACK)) if ba.app.ui.use_toolbars and uiscale is ba.UIScale.SMALL: ba.textwidget(edit=txt, text='') v = height - (110 if new_style else 60) v -= 100 clr = (0.6, 0.7, 0.6, 1.0) v -= 280 if new_style else 180 v += (30 if ba.app.ui.use_toolbars and uiscale is ba.UIScale.SMALL else 0) hoffs = x_offs + 80 if new_style else 0 scl = 1.13 if new_style else 0.68 self._lineup_tex = ba.gettexture('playerLineup') angry_computer_transparent_model = ba.getmodel( 'angryComputerTransparent') 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._eyes_model = ba.getmodel('plasticEyesTransparent') self._coop_button = btn = ba.buttonwidget( parent=self._root_widget, position=(hoffs, v + (scl * 15 if new_style else 0)), size=(scl * button_width, scl * (300 if new_style else 360)), extra_touch_border_scale=0.1, autoselect=True, label='', button_type='square', text_scale=1.13, on_activate_call=self._coop) if ba.app.ui.use_toolbars and uiscale is ba.UIScale.SMALL: ba.widget(edit=btn, left_widget=_ba.get_special_widget('back_button')) ba.widget(edit=btn, up_widget=_ba.get_special_widget('account_button')) ba.widget(edit=btn, down_widget=_ba.get_special_widget('settings_button')) self._draw_dude(0, btn, hoffs, v, scl, position=(140, 30), color=(0.72, 0.4, 1.0)) self._draw_dude(1, btn, hoffs, v, scl, position=(185, 53), color=(0.71, 0.5, 1.0)) self._draw_dude(2, btn, hoffs, v, scl, position=(220, 27), color=(0.67, 0.44, 1.0)) self._draw_dude(3, btn, hoffs, v, scl, position=(255, 57), color=(0.7, 0.3, 1.0)) ba.imagewidget(parent=self._root_widget, draw_controller=btn, position=(hoffs + scl * 230, v + scl * 153), size=(scl * 115, scl * 115), texture=self._lineup_tex, model_transparent=angry_computer_transparent_model) ba.textwidget(parent=self._root_widget, draw_controller=btn, position=(hoffs + scl * (-10), v + scl * 95), size=(scl * button_width, scl * 50), text=ba.Lstr(resource='playModes.singlePlayerCoopText', fallback_resource='playModes.coopText'), maxwidth=scl * button_width * 0.7, res_scale=1.5, h_align='center', v_align='center', color=(0.7, 0.9, 0.7, 1.0), scale=scl * 2.3) ba.textwidget(parent=self._root_widget, draw_controller=btn, position=(hoffs + scl * (-10), v + (scl * 54)), size=(scl * button_width, scl * 30), text=ba.Lstr(resource=self._r + '.oneToFourPlayersText'), h_align='center', v_align='center', scale=0.83 * scl, flatness=1.0, maxwidth=scl * button_width * 0.7, color=clr) scl = 0.5 if new_style else 0.68 hoffs += 440 if new_style else 260 v += 180 if new_style else 0 self._teams_button = btn = ba.buttonwidget( parent=self._root_widget, position=(hoffs, v + (scl * 15 if new_style else 0)), size=(scl * button_width, scl * (300 if new_style else 360)), extra_touch_border_scale=0.1, autoselect=True, label='', button_type='square', text_scale=1.13, on_activate_call=self._team_tourney) if ba.app.ui.use_toolbars: ba.widget(edit=btn, up_widget=_ba.get_special_widget('tickets_plus_button'), right_widget=_ba.get_special_widget('party_button')) xxx = -14 self._draw_dude(2, btn, hoffs, v, scl, position=(xxx + 148, 30), color=(0.2, 0.4, 1.0)) self._draw_dude(3, btn, hoffs, v, scl, position=(xxx + 181, 53), color=(0.3, 0.4, 1.0)) self._draw_dude(1, btn, hoffs, v, scl, position=(xxx + 216, 33), color=(0.3, 0.5, 1.0)) self._draw_dude(0, btn, hoffs, v, scl, position=(xxx + 245, 57), color=(0.3, 0.5, 1.0)) xxx = 155 self._draw_dude(0, btn, hoffs, v, scl, position=(xxx + 151, 30), color=(1.0, 0.5, 0.4)) self._draw_dude(1, btn, hoffs, v, scl, position=(xxx + 189, 53), color=(1.0, 0.58, 0.58)) self._draw_dude(3, btn, hoffs, v, scl, position=(xxx + 223, 27), color=(1.0, 0.5, 0.5)) self._draw_dude(2, btn, hoffs, v, scl, position=(xxx + 257, 57), color=(1.0, 0.5, 0.5)) ba.textwidget(parent=self._root_widget, draw_controller=btn, position=(hoffs + scl * (-10), v + scl * 95), size=(scl * button_width, scl * 50), text=ba.Lstr(resource='playModes.teamsText', fallback_resource='teamsText'), res_scale=1.5, maxwidth=scl * button_width * 0.7, h_align='center', v_align='center', color=(0.7, 0.9, 0.7, 1.0), scale=scl * 2.3) ba.textwidget(parent=self._root_widget, draw_controller=btn, position=(hoffs + scl * (-10), v + (scl * 54)), size=(scl * button_width, scl * 30), text=ba.Lstr(resource=self._r + '.twoToEightPlayersText'), h_align='center', v_align='center', res_scale=1.5, scale=0.9 * scl, flatness=1.0, maxwidth=scl * button_width * 0.7, color=clr) hoffs += 0 if new_style else 260 v -= 155 if new_style else 0 self._free_for_all_button = btn = ba.buttonwidget( parent=self._root_widget, position=(hoffs, v + (scl * 15 if new_style else 0)), size=(scl * button_width, scl * (300 if new_style else 360)), extra_touch_border_scale=0.1, autoselect=True, label='', button_type='square', text_scale=1.13, on_activate_call=self._free_for_all) xxx = -5 self._draw_dude(0, btn, hoffs, v, scl, position=(xxx + 140, 30), color=(0.4, 1.0, 0.4)) self._draw_dude(3, btn, hoffs, v, scl, position=(xxx + 185, 53), color=(1.0, 0.4, 0.5)) self._draw_dude(1, btn, hoffs, v, scl, position=(xxx + 220, 27), color=(0.4, 0.5, 1.0)) self._draw_dude(2, btn, hoffs, v, scl, position=(xxx + 255, 57), color=(0.5, 1.0, 0.4)) xxx = 140 self._draw_dude(2, btn, hoffs, v, scl, position=(xxx + 148, 30), color=(1.0, 0.9, 0.4)) self._draw_dude(0, btn, hoffs, v, scl, position=(xxx + 182, 53), color=(0.7, 1.0, 0.5)) self._draw_dude(3, btn, hoffs, v, scl, position=(xxx + 233, 27), color=(0.7, 0.5, 0.9)) self._draw_dude(1, btn, hoffs, v, scl, position=(xxx + 266, 53), color=(0.4, 0.5, 0.8)) ba.textwidget(parent=self._root_widget, draw_controller=btn, position=(hoffs + scl * (-10), v + scl * 95), size=(scl * button_width, scl * 50), text=ba.Lstr(resource='playModes.freeForAllText', fallback_resource='freeForAllText'), maxwidth=scl * button_width * 0.7, h_align='center', v_align='center', color=(0.7, 0.9, 0.7, 1.0), scale=scl * 1.9) ba.textwidget(parent=self._root_widget, draw_controller=btn, position=(hoffs + scl * (-10), v + (scl * 54)), size=(scl * button_width, scl * 30), text=ba.Lstr(resource=self._r + '.twoToEightPlayersText'), h_align='center', v_align='center', scale=0.9 * scl, flatness=1.0, maxwidth=scl * button_width * 0.7, color=clr) if ba.app.ui.use_toolbars and uiscale is ba.UIScale.SMALL: back_button.delete() ba.containerwidget(edit=self._root_widget, on_cancel_call=self._back, selected_child=self._coop_button) else: ba.buttonwidget(edit=back_button, on_activate_call=self._back) ba.containerwidget(edit=self._root_widget, cancel_button=back_button, selected_child=self._coop_button) self._restore_state()
def __init__(self, offer: dict[str, Any], transition: str = 'in_right'): # pylint: disable=too-many-statements # pylint: disable=too-many-branches # pylint: disable=too-many-locals from ba.internal import (get_store_item_display_size, get_clean_price) from ba import SpecialChar from bastd.ui.store import item as storeitemui self._cancel_delay = offer.get('cancelDelay', 0) # First thing: if we're offering pro or an IAP, see if we have a # price for it. # If not, abort and go into zombie mode (the user should never see # us that way). real_price: Optional[str] # Misnomer: 'pro' actually means offer 'pro_sale'. if offer['item'] in ['pro', 'pro_fullprice']: real_price = _ba.get_price('pro' if offer['item'] == 'pro_fullprice' else 'pro_sale') if real_price is None and ba.app.debug_build: print('NOTE: Faking prices for debug build.') real_price = '$1.23' zombie = real_price is None elif isinstance(offer['price'], str): # (a string price implies IAP id) real_price = _ba.get_price(offer['price']) if real_price is None and ba.app.debug_build: print('NOTE: Faking price for debug build.') real_price = '$1.23' zombie = real_price is None else: real_price = None zombie = False if real_price is None: real_price = '?' if offer['item'] in ['pro', 'pro_fullprice']: self._offer_item = 'pro' else: self._offer_item = offer['item'] # If we wanted a real price but didn't find one, go zombie. if zombie: return # This can pop up suddenly, so lets block input for 1 second. _ba.lock_all_input() ba.timer(1.0, _ba.unlock_all_input, timetype=ba.TimeType.REAL) ba.playsound(ba.getsound('ding')) ba.timer(0.3, lambda: ba.playsound(ba.getsound('ooh')), timetype=ba.TimeType.REAL) self._offer = copy.deepcopy(offer) self._width = 580 self._height = 590 uiscale = ba.app.ui.uiscale super().__init__(root_widget=ba.containerwidget( size=(self._width, self._height), transition=transition, scale=(1.2 if uiscale is ba.UIScale.SMALL else 1.15 if uiscale is ba.UIScale.MEDIUM else 1.0), stack_offset=(0, -15) if uiscale is ba.UIScale.SMALL else (0, 0))) self._is_bundle_sale = False try: if offer['item'] in ['pro', 'pro_fullprice']: original_price_str = _ba.get_price('pro') if original_price_str is None: original_price_str = '?' new_price_str = _ba.get_price('pro_sale') if new_price_str is None: new_price_str = '?' percent_off_text = '' else: # If the offer includes bonus tickets, it's a bundle-sale. if ('bonusTickets' in offer and offer['bonusTickets'] is not None): self._is_bundle_sale = True original_price = _ba.get_v1_account_misc_read_val( 'price.' + self._offer_item, 9999) # For pure ticket prices we can show a percent-off. if isinstance(offer['price'], int): new_price = offer['price'] tchar = ba.charstr(SpecialChar.TICKET) original_price_str = tchar + str(original_price) new_price_str = tchar + str(new_price) percent_off = int( round(100.0 - (float(new_price) / original_price) * 100.0)) percent_off_text = ' ' + ba.Lstr( resource='store.salePercentText').evaluate().replace( '${PERCENT}', str(percent_off)) else: original_price_str = new_price_str = '?' percent_off_text = '' except Exception: print(f'Offer: {offer}') ba.print_exception('Error setting up special-offer') original_price_str = new_price_str = '?' percent_off_text = '' # If its a bundle sale, change the title. if self._is_bundle_sale: sale_text = ba.Lstr(resource='store.saleBundleText', fallback_resource='store.saleText').evaluate() else: # For full pro we say 'Upgrade?' since its not really a sale. if offer['item'] == 'pro_fullprice': sale_text = ba.Lstr( resource='store.upgradeQuestionText', fallback_resource='store.saleExclaimText').evaluate() else: sale_text = ba.Lstr( resource='store.saleExclaimText', fallback_resource='store.saleText').evaluate() self._title_text = ba.textwidget( parent=self._root_widget, position=(self._width * 0.5, self._height - 40), size=(0, 0), text=sale_text + ((' ' + ba.Lstr(resource='store.oneTimeOnlyText').evaluate()) if self._offer['oneTimeOnly'] else '') + percent_off_text, h_align='center', v_align='center', maxwidth=self._width * 0.9 - 220, scale=1.4, color=(0.3, 1, 0.3)) self._flash_on = False self._flashing_timer: Optional[ba.Timer] = ba.Timer( 0.05, ba.WeakCall(self._flash_cycle), repeat=True, timetype=ba.TimeType.REAL) ba.timer(0.6, ba.WeakCall(self._stop_flashing), timetype=ba.TimeType.REAL) size = get_store_item_display_size(self._offer_item) display: dict[str, Any] = {} storeitemui.instantiate_store_item_display( self._offer_item, display, parent_widget=self._root_widget, b_pos=(self._width * 0.5 - size[0] * 0.5 + 10 - ((size[0] * 0.5 + 30) if self._is_bundle_sale else 0), self._height * 0.5 - size[1] * 0.5 + 20 + (20 if self._is_bundle_sale else 0)), b_width=size[0], b_height=size[1], button=not self._is_bundle_sale) # Wire up the parts we need. if self._is_bundle_sale: self._plus_text = ba.textwidget(parent=self._root_widget, position=(self._width * 0.5, self._height * 0.5 + 50), size=(0, 0), text='+', h_align='center', v_align='center', maxwidth=self._width * 0.9, scale=1.4, color=(0.5, 0.5, 0.5)) self._plus_tickets = ba.textwidget( parent=self._root_widget, position=(self._width * 0.5 + 120, self._height * 0.5 + 50), size=(0, 0), text=ba.charstr(SpecialChar.TICKET_BACKING) + str(offer['bonusTickets']), h_align='center', v_align='center', maxwidth=self._width * 0.9, scale=2.5, color=(0.2, 1, 0.2)) self._price_text = ba.textwidget(parent=self._root_widget, position=(self._width * 0.5, 150), size=(0, 0), text=real_price, h_align='center', v_align='center', maxwidth=self._width * 0.9, scale=1.4, color=(0.2, 1, 0.2)) # Total-value if they supplied it. total_worth_item = offer.get('valueItem', None) if total_worth_item is not None: price = _ba.get_price(total_worth_item) total_worth_price = (get_clean_price(price) if price is not None else None) if total_worth_price is not None: total_worth_text = ba.Lstr(resource='store.totalWorthText', subs=[('${TOTAL_WORTH}', total_worth_price)]) self._total_worth_text = ba.textwidget( parent=self._root_widget, text=total_worth_text, position=(self._width * 0.5, 210), scale=0.9, maxwidth=self._width * 0.7, size=(0, 0), h_align='center', v_align='center', shadow=1.0, flatness=1.0, color=(0.3, 1, 1)) elif offer['item'] == 'pro_fullprice': # for full-price pro we simply show full price ba.textwidget(edit=display['price_widget'], text=real_price) ba.buttonwidget(edit=display['button'], on_activate_call=self._purchase) else: # Show old/new prices otherwise (for pro sale). ba.buttonwidget(edit=display['button'], on_activate_call=self._purchase) ba.imagewidget(edit=display['price_slash_widget'], opacity=1.0) ba.textwidget(edit=display['price_widget_left'], text=original_price_str) ba.textwidget(edit=display['price_widget_right'], text=new_price_str) # Add ticket button only if this is ticket-purchasable. if isinstance(offer.get('price'), int): self._get_tickets_button = ba.buttonwidget( parent=self._root_widget, position=(self._width - 125, self._height - 68), size=(90, 55), scale=1.0, button_type='square', color=(0.7, 0.5, 0.85), textcolor=(0.2, 1, 0.2), autoselect=True, label=ba.Lstr(resource='getTicketsWindow.titleText'), on_activate_call=self._on_get_more_tickets_press) 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() self._update_timer = ba.Timer(1.0, ba.WeakCall(self._update), timetype=ba.TimeType.REAL, repeat=True) self._cancel_button = ba.buttonwidget( parent=self._root_widget, position=(50, 40) if self._is_bundle_sale else (self._width * 0.5 - 75, 40), size=(150, 60), scale=1.0, on_activate_call=self._cancel, autoselect=True, label=ba.Lstr(resource='noThanksText')) self._cancel_countdown_text = ba.textwidget( parent=self._root_widget, text='', position=(50 + 150 + 20, 40 + 27) if self._is_bundle_sale else (self._width * 0.5 - 75 + 150 + 20, 40 + 27), scale=1.1, size=(0, 0), h_align='left', v_align='center', shadow=1.0, flatness=1.0, color=(0.6, 0.5, 0.5)) self._update_cancel_button_graphics() if self._is_bundle_sale: self._purchase_button = ba.buttonwidget( parent=self._root_widget, position=(self._width - 200, 40), size=(150, 60), scale=1.0, on_activate_call=self._purchase, autoselect=True, label=ba.Lstr(resource='store.purchaseText')) ba.containerwidget(edit=self._root_widget, cancel_button=self._cancel_button, start_button=self._purchase_button if self._is_bundle_sale else None, selected_child=self._purchase_button if self._is_bundle_sale else display['button'])
def instantiate_store_item_display(item_name: str, item: Dict[str, Any], parent_widget: ba.Widget, b_pos: Tuple[float, float], b_width: float, b_height: float, boffs_h: float = 0.0, boffs_h2: float = 0.0, boffs_v2: float = 0, delay: float = 0.0, button: bool = True) -> None: """(internal)""" # pylint: disable=too-many-statements # pylint: disable=too-many-branches # pylint: disable=too-many-locals from ba.internal import (get_store_item, get_store_item_name_translated, get_clean_price) del boffs_h # unused arg del boffs_h2 # unused arg del boffs_v2 # unused arg item_info = get_store_item(item_name) title_v = 0.24 price_v = 0.145 base_text_scale = 1.0 item['name'] = title = get_store_item_name_translated(item_name) btn: Optional[ba.Widget] if button: item['button'] = btn = ba.buttonwidget(parent=parent_widget, position=b_pos, transition_delay=delay, show_buffer_top=76.0, enable_sound=False, button_type='square', size=(b_width, b_height), autoselect=True, label='') ba.widget(edit=btn, show_buffer_bottom=76.0) else: btn = None b_offs_x = -0.015 * b_width check_pos = 0.76 icon_tex = None tint_tex = None tint_color = None tint2_color = None tex_name: Optional[str] = None desc: Optional[str] = None modes: Optional[ba.Lstr] = None if item_name.startswith('characters.'): character = ba.app.spaz_appearances[item_info['character']] tint_color = ( item_info['color'] if 'color' in item_info else character.default_color if character.default_color is not None else (1, 1, 1)) tint2_color = (item_info['highlight'] if 'highlight' in item_info else character.default_highlight if character.default_highlight is not None else (1, 1, 1)) icon_tex = character.icon_texture tint_tex = character.icon_mask_texture title_v = 0.255 price_v = 0.145 elif item_name in ['upgrades.pro', 'pro']: base_text_scale = 0.6 title_v = 0.85 price_v = 0.15 elif item_name.startswith('maps.'): map_type = item_info['map_type'] tex_name = map_type.get_preview_texture_name() title_v = 0.312 price_v = 0.17 elif item_name.startswith('games.'): gametype = item_info['gametype'] modes_l = [] if gametype.supports_session_type(ba.CoopSession): modes_l.append(ba.Lstr(resource='playModes.coopText')) if gametype.supports_session_type(ba.DualTeamSession): modes_l.append(ba.Lstr(resource='playModes.teamsText')) if gametype.supports_session_type(ba.FreeForAllSession): modes_l.append(ba.Lstr(resource='playModes.freeForAllText')) if len(modes_l) == 3: modes = ba.Lstr(value='${A}, ${B}, ${C}', subs=[('${A}', modes_l[0]), ('${B}', modes_l[1]), ('${C}', modes_l[2])]) elif len(modes_l) == 2: modes = ba.Lstr(value='${A}, ${B}', subs=[('${A}', modes_l[0]), ('${B}', modes_l[1])]) elif len(modes_l) == 1: modes = modes_l[0] else: raise Exception() desc = gametype.get_description_display_string(ba.CoopSession) tex_name = item_info['previewTex'] base_text_scale = 0.8 title_v = 0.48 price_v = 0.17 elif item_name.startswith('icons.'): base_text_scale = 1.5 price_v = 0.2 check_pos = 0.6 if item_name.startswith('characters.'): frame_size = b_width * 0.7 im_dim = frame_size * (100.0 / 113.0) im_pos = (b_pos[0] + b_width * 0.5 - im_dim * 0.5 + b_offs_x, b_pos[1] + b_height * 0.57 - im_dim * 0.5) mask_texture = ba.gettexture('characterIconMask') assert icon_tex is not None assert tint_tex is not None ba.imagewidget(parent=parent_widget, position=im_pos, size=(im_dim, im_dim), color=(1, 1, 1), transition_delay=delay, mask_texture=mask_texture, draw_controller=btn, texture=ba.gettexture(icon_tex), tint_texture=ba.gettexture(tint_tex), tint_color=tint_color, tint2_color=tint2_color) if item_name in ['pro', 'upgrades.pro']: frame_size = b_width * 0.5 im_dim = frame_size * (100.0 / 113.0) im_pos = (b_pos[0] + b_width * 0.5 - im_dim * 0.5 + b_offs_x, b_pos[1] + b_height * 0.5 - im_dim * 0.5) ba.imagewidget(parent=parent_widget, position=im_pos, size=(im_dim, im_dim), transition_delay=delay, draw_controller=btn, color=(0.3, 0.0, 0.3), opacity=0.3, texture=ba.gettexture('logo')) txt = ba.Lstr(resource='store.bombSquadProNewDescriptionText') # t = 'foo\nfoo\nfoo\nfoo\nfoo\nfoo' item['descriptionText'] = ba.textwidget( parent=parent_widget, text=txt, position=(b_pos[0] + b_width * 0.5, b_pos[1] + b_height * 0.69), transition_delay=delay, scale=b_width * (1.0 / 230.0) * base_text_scale * 0.75, maxwidth=b_width * 0.75, max_height=b_height * 0.2, size=(0, 0), h_align='center', v_align='center', draw_controller=btn, color=(0.3, 1, 0.3)) extra_backings = item['extra_backings'] = [] extra_images = item['extra_images'] = [] extra_texts = item['extra_texts'] = [] extra_texts_2 = item['extra_texts_2'] = [] backing_color = (0.5, 0.8, 0.3) if button else (0.6, 0.5, 0.65) b_square_texture = ba.gettexture('buttonSquare') char_mask_texture = ba.gettexture('characterIconMask') pos = (0.17, 0.43) tile_size = (b_width * 0.16 * 1.2, b_width * 0.2 * 1.2) tile_pos = (b_pos[0] + b_width * pos[0], b_pos[1] + b_height * pos[1]) extra_backings.append( ba.imagewidget(parent=parent_widget, position=(tile_pos[0] - tile_size[0] * 0.5, tile_pos[1] - tile_size[1] * 0.5), size=tile_size, transition_delay=delay, draw_controller=btn, color=backing_color, texture=b_square_texture)) im_size = tile_size[0] * 0.8 extra_images.append( ba.imagewidget(parent=parent_widget, position=(tile_pos[0] - im_size * 0.5, tile_pos[1] - im_size * 0.4), size=(im_size, im_size), transition_delay=delay, draw_controller=btn, color=(1, 1, 1), texture=ba.gettexture('ticketsMore'))) bonus_tickets = str( _ba.get_account_misc_read_val('proBonusTickets', 100)) extra_texts.append( ba.textwidget(parent=parent_widget, draw_controller=btn, position=(tile_pos[0] - tile_size[0] * 0.03, tile_pos[1] - tile_size[1] * 0.25), size=(0, 0), color=(0.6, 1, 0.6), transition_delay=delay, h_align='center', v_align='center', maxwidth=tile_size[0] * 0.7, scale=0.55, text=ba.Lstr(resource='getTicketsWindow.ticketsText', subs=[('${COUNT}', bonus_tickets)]), flatness=1.0, shadow=0.0)) for charname, pos in [('Kronk', (0.32, 0.45)), ('Zoe', (0.425, 0.4)), ('Jack Morgan', (0.555, 0.45)), ('Mel', (0.645, 0.4))]: tile_size = (b_width * 0.16 * 0.9, b_width * 0.2 * 0.9) tile_pos = (b_pos[0] + b_width * pos[0], b_pos[1] + b_height * pos[1]) character = ba.app.spaz_appearances[charname] extra_backings.append( ba.imagewidget(parent=parent_widget, position=(tile_pos[0] - tile_size[0] * 0.5, tile_pos[1] - tile_size[1] * 0.5), size=tile_size, transition_delay=delay, draw_controller=btn, color=backing_color, texture=b_square_texture)) im_size = tile_size[0] * 0.7 extra_images.append( ba.imagewidget(parent=parent_widget, position=(tile_pos[0] - im_size * 0.53, tile_pos[1] - im_size * 0.35), size=(im_size, im_size), transition_delay=delay, draw_controller=btn, color=(1, 1, 1), texture=ba.gettexture(character.icon_texture), tint_texture=ba.gettexture( character.icon_mask_texture), tint_color=character.default_color, tint2_color=character.default_highlight, mask_texture=char_mask_texture)) extra_texts.append( ba.textwidget(parent=parent_widget, draw_controller=btn, position=(tile_pos[0] - im_size * 0.03, tile_pos[1] - im_size * 0.51), size=(0, 0), color=(0.6, 1, 0.6), transition_delay=delay, h_align='center', v_align='center', maxwidth=tile_size[0] * 0.7, scale=0.55, text=ba.Lstr(translate=('characterNames', charname)), flatness=1.0, shadow=0.0)) # If we have a 'total-worth' item-id for this id, show that price so # the user knows how much this is worth. total_worth_item = _ba.get_account_misc_read_val('twrths', {}).get(item_name) total_worth_price: Optional[str] if total_worth_item is not None: price = _ba.get_price(total_worth_item) assert price is not None total_worth_price = get_clean_price(price) else: total_worth_price = None if total_worth_price is not None: total_worth_text = ba.Lstr(resource='store.totalWorthText', subs=[('${TOTAL_WORTH}', total_worth_price)]) extra_texts_2.append( ba.textwidget(parent=parent_widget, text=total_worth_text, position=(b_pos[0] + b_width * 0.5 + b_offs_x, b_pos[1] + b_height * 0.25), transition_delay=delay, scale=b_width * (1.0 / 230.0) * base_text_scale * 0.45, maxwidth=b_width * 0.5, size=(0, 0), h_align='center', v_align='center', shadow=1.0, flatness=1.0, draw_controller=btn, color=(0.3, 1, 1))) model_opaque = ba.getmodel('level_select_button_opaque') model_transparent = ba.getmodel('level_select_button_transparent') mask_tex = ba.gettexture('mapPreviewMask') for levelname, preview_tex_name, pos in [ ('Infinite Onslaught', 'doomShroomPreview', (0.80, 0.48)), ('Infinite Runaround', 'towerDPreview', (0.80, 0.32)) ]: tile_size = (b_width * 0.2, b_width * 0.13) tile_pos = (b_pos[0] + b_width * pos[0], b_pos[1] + b_height * pos[1]) im_size = tile_size[0] * 0.8 extra_backings.append( ba.imagewidget(parent=parent_widget, position=(tile_pos[0] - tile_size[0] * 0.5, tile_pos[1] - tile_size[1] * 0.5), size=tile_size, transition_delay=delay, draw_controller=btn, color=backing_color, texture=b_square_texture)) # hack - gotta draw two transparent versions to avoid z issues for mod in model_opaque, model_transparent: extra_images.append( ba.imagewidget(parent=parent_widget, position=(tile_pos[0] - im_size * 0.52, tile_pos[1] - im_size * 0.2), size=(im_size, im_size * 0.5), transition_delay=delay, model_transparent=mod, mask_texture=mask_tex, draw_controller=btn, texture=ba.gettexture(preview_tex_name))) extra_texts.append( ba.textwidget(parent=parent_widget, draw_controller=btn, position=(tile_pos[0] - im_size * 0.03, tile_pos[1] - im_size * 0.2), size=(0, 0), color=(0.6, 1, 0.6), transition_delay=delay, h_align='center', v_align='center', maxwidth=tile_size[0] * 0.7, scale=0.55, text=ba.Lstr(translate=('coopLevelNames', levelname)), flatness=1.0, shadow=0.0)) if item_name.startswith('icons.'): item['icon_text'] = ba.textwidget( parent=parent_widget, text=item_info['icon'], position=(b_pos[0] + b_width * 0.5, b_pos[1] + b_height * 0.5), transition_delay=delay, scale=b_width * (1.0 / 230.0) * base_text_scale * 2.0, maxwidth=b_width * 0.9, max_height=b_height * 0.9, size=(0, 0), h_align='center', v_align='center', draw_controller=btn) if item_name.startswith('maps.'): frame_size = b_width * 0.9 im_dim = frame_size * (100.0 / 113.0) im_pos = (b_pos[0] + b_width * 0.5 - im_dim * 0.5 + b_offs_x, b_pos[1] + b_height * 0.62 - im_dim * 0.25) model_opaque = ba.getmodel('level_select_button_opaque') model_transparent = ba.getmodel('level_select_button_transparent') mask_tex = ba.gettexture('mapPreviewMask') assert tex_name is not None ba.imagewidget(parent=parent_widget, position=im_pos, size=(im_dim, im_dim * 0.5), transition_delay=delay, model_opaque=model_opaque, model_transparent=model_transparent, mask_texture=mask_tex, draw_controller=btn, texture=ba.gettexture(tex_name)) if item_name.startswith('games.'): frame_size = b_width * 0.8 im_dim = frame_size * (100.0 / 113.0) im_pos = (b_pos[0] + b_width * 0.5 - im_dim * 0.5 + b_offs_x, b_pos[1] + b_height * 0.72 - im_dim * 0.25) model_opaque = ba.getmodel('level_select_button_opaque') model_transparent = ba.getmodel('level_select_button_transparent') mask_tex = ba.gettexture('mapPreviewMask') assert tex_name is not None ba.imagewidget(parent=parent_widget, position=im_pos, size=(im_dim, im_dim * 0.5), transition_delay=delay, model_opaque=model_opaque, model_transparent=model_transparent, mask_texture=mask_tex, draw_controller=btn, texture=ba.gettexture(tex_name)) item['descriptionText'] = ba.textwidget( parent=parent_widget, text=desc, position=(b_pos[0] + b_width * 0.5, b_pos[1] + b_height * 0.36), transition_delay=delay, scale=b_width * (1.0 / 230.0) * base_text_scale * 0.78, maxwidth=b_width * 0.8, max_height=b_height * 0.14, size=(0, 0), h_align='center', v_align='center', draw_controller=btn, flatness=1.0, shadow=0.0, color=(0.6, 1, 0.6)) item['gameModesText'] = ba.textwidget( parent=parent_widget, text=modes, position=(b_pos[0] + b_width * 0.5, b_pos[1] + b_height * 0.26), transition_delay=delay, scale=b_width * (1.0 / 230.0) * base_text_scale * 0.65, maxwidth=b_width * 0.8, size=(0, 0), h_align='center', v_align='center', draw_controller=btn, shadow=0, flatness=1.0, color=(0.6, 0.8, 0.6)) if not item_name.startswith('icons.'): item['title_text'] = ba.textwidget( parent=parent_widget, text=title, position=(b_pos[0] + b_width * 0.5 + b_offs_x, b_pos[1] + b_height * title_v), transition_delay=delay, scale=b_width * (1.0 / 230.0) * base_text_scale, maxwidth=b_width * 0.8, size=(0, 0), h_align='center', v_align='center', draw_controller=btn, color=(0.7, 0.9, 0.7, 1.0)) item['purchase_check'] = ba.imagewidget( parent=parent_widget, position=(b_pos[0] + b_width * check_pos, b_pos[1] + b_height * 0.05), transition_delay=delay, model_transparent=ba.getmodel('checkTransparent'), opacity=0.0, size=(60, 60), color=(0.6, 0.5, 0.8), draw_controller=btn, texture=ba.gettexture('uiAtlas')) item['price_widget'] = ba.textwidget( parent=parent_widget, text='', position=(b_pos[0] + b_width * 0.5 + b_offs_x, b_pos[1] + b_height * price_v), transition_delay=delay, scale=b_width * (1.0 / 300.0) * base_text_scale, maxwidth=b_width * 0.9, size=(0, 0), h_align='center', v_align='center', draw_controller=btn, color=(0.2, 1, 0.2, 1.0)) item['price_widget_left'] = ba.textwidget( parent=parent_widget, text='', position=(b_pos[0] + b_width * 0.33 + b_offs_x, b_pos[1] + b_height * price_v), transition_delay=delay, scale=b_width * (1.0 / 300.0) * base_text_scale, maxwidth=b_width * 0.3, size=(0, 0), h_align='center', v_align='center', draw_controller=btn, color=(0.2, 1, 0.2, 0.5)) item['price_widget_right'] = ba.textwidget( parent=parent_widget, text='', position=(b_pos[0] + b_width * 0.66 + b_offs_x, b_pos[1] + b_height * price_v), transition_delay=delay, scale=1.1 * b_width * (1.0 / 300.0) * base_text_scale, maxwidth=b_width * 0.3, size=(0, 0), h_align='center', v_align='center', draw_controller=btn, color=(0.2, 1, 0.2, 1.0)) item['price_slash_widget'] = ba.imagewidget( parent=parent_widget, position=(b_pos[0] + b_width * 0.33 + b_offs_x - 36, b_pos[1] + b_height * price_v - 35), transition_delay=delay, texture=ba.gettexture('slash'), opacity=0.0, size=(70, 70), draw_controller=btn, color=(1, 0, 0)) badge_rad = 44 badge_center = (b_pos[0] + b_width * 0.1 + b_offs_x, b_pos[1] + b_height * 0.87) item['sale_bg_widget'] = ba.imagewidget( parent=parent_widget, position=(badge_center[0] - badge_rad, badge_center[1] - badge_rad), opacity=0.0, transition_delay=delay, texture=ba.gettexture('circleZigZag'), draw_controller=btn, size=(badge_rad * 2, badge_rad * 2), color=(0.5, 0, 1)) item['sale_title_widget'] = ba.textwidget(parent=parent_widget, position=(badge_center[0], badge_center[1] + 12), transition_delay=delay, scale=1.0, maxwidth=badge_rad * 1.6, size=(0, 0), h_align='center', v_align='center', draw_controller=btn, shadow=0.0, flatness=1.0, color=(0, 1, 0)) item['sale_time_widget'] = ba.textwidget(parent=parent_widget, position=(badge_center[0], badge_center[1] - 12), transition_delay=delay, scale=0.7, maxwidth=badge_rad * 1.6, size=(0, 0), h_align='center', v_align='center', draw_controller=btn, shadow=0.0, flatness=1.0, color=(0.0, 1, 0.0, 1))
def __init__(self, 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.ui.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.ui.use_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.ui.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.ui.use_toolbars else self._scrollwidget) self._col = ba.columnwidget(parent=scrollwidget, border=2, margin=0) 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 __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.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._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 __init__(self, position: Tuple[float, float], scale: float = None): # pylint: disable=too-many-locals 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._transitioning_out = False self._width = 450 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 popup.PopupWindow.__init__(self, position=position, size=(self._width, self._height), scale=scale, bg_color=bg_color) 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) achievements = ba.app.achievements num_complete = len([a for a in achievements if a.complete]) txt_final = ba.Lstr( resource='accountSettingsWindow.achievementProgressText', subs=[('${COUNT}', str(num_complete)), ('${TOTAL}', str(len(achievements)))]) 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=txt_final, 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), capture_arrows=True, simple_culling_v=10) ba.widget(edit=self._scrollwidget, autoselect=True) ba.containerwidget(edit=self.root_widget, cancel_button=self._cancel_button) incr = 36 sub_width = self._width - 90 sub_height = 40 + len(achievements) * incr eq_rsrc = 'coopSelectWindow.powerRankingPointsEqualsText' pts_rsrc = 'coopSelectWindow.powerRankingPointsText' self._subcontainer = ba.containerwidget(parent=self._scrollwidget, size=(sub_width, sub_height), background=False) total_pts = 0 for i, ach in enumerate(achievements): complete = ach.complete ba.textwidget(parent=self._subcontainer, position=(sub_width * 0.08 - 5, sub_height - 20 - incr * i), maxwidth=20, scale=0.5, color=(0.6, 0.6, 0.7) if complete else (0.6, 0.6, 0.7, 0.2), flatness=1.0, shadow=0.0, text=str(i + 1), size=(0, 0), h_align='right', v_align='center') ba.imagewidget(parent=self._subcontainer, position=(sub_width * 0.10 + 1, sub_height - 20 - incr * i - 9) if complete else (sub_width * 0.10 - 4, sub_height - 20 - incr * i - 14), size=(18, 18) if complete else (27, 27), opacity=1.0 if complete else 0.3, color=ach.get_icon_color(complete)[:3], texture=ach.get_icon_texture(complete)) if complete: ba.imagewidget(parent=self._subcontainer, position=(sub_width * 0.10 - 4, sub_height - 25 - incr * i - 9), size=(28, 28), color=(2, 1.4, 0), texture=ba.gettexture('achievementOutline')) ba.textwidget(parent=self._subcontainer, position=(sub_width * 0.19, sub_height - 19 - incr * i + 3), maxwidth=sub_width * 0.62, scale=0.6, flatness=1.0, shadow=0.0, color=(1, 1, 1) if complete else (1, 1, 1, 0.2), text=ach.display_name, size=(0, 0), h_align='left', v_align='center') ba.textwidget(parent=self._subcontainer, position=(sub_width * 0.19, sub_height - 19 - incr * i - 10), maxwidth=sub_width * 0.62, scale=0.4, flatness=1.0, shadow=0.0, color=(0.83, 0.8, 0.85) if complete else (0.8, 0.8, 0.8, 0.2), text=ach.description_full_complete if complete else ach.description_full, size=(0, 0), h_align='left', v_align='center') pts = ach.power_ranking_value ba.textwidget(parent=self._subcontainer, position=(sub_width * 0.92, sub_height - 20 - incr * i), maxwidth=sub_width * 0.15, color=(0.7, 0.8, 1.0) if complete else (0.9, 0.9, 1.0, 0.3), flatness=1.0, shadow=0.0, scale=0.6, text=ba.Lstr(resource=pts_rsrc, subs=[('${NUMBER}', str(pts))]), size=(0, 0), h_align='center', v_align='center') if complete: total_pts += pts ba.textwidget(parent=self._subcontainer, position=(sub_width * 1.0, sub_height - 20 - incr * len(achievements)), maxwidth=sub_width * 0.5, scale=0.7, color=(0.7, 0.8, 1.0), flatness=1.0, shadow=0.0, text=ba.Lstr( value='${A} ${B}', subs=[ ('${A}', ba.Lstr(resource='coopSelectWindow.totalText')), ('${B}', ba.Lstr(resource=eq_rsrc, subs=[('${NUMBER}', str(total_pts))])) ]), size=(0, 0), h_align='right', v_align='center')
def _add_button(item: str, position: Tuple[float, float], size: Tuple[float, float], label: ba.Lstr, price: str = None, tex_name: str = None, tex_opacity: float = 1.0, tex_scale: float = 1.0, enabled: bool = True, text_scale: float = 1.0) -> ba.Widget: btn2 = ba.buttonwidget( parent=self._root_widget, position=position, button_type='square', size=size, label='', autoselect=True, color=None if enabled else (0.5, 0.5, 0.5), on_activate_call=(ba.Call(self._purchase, item) if enabled else self._disabled_press)) txt = ba.textwidget(parent=self._root_widget, text=label, position=(position[0] + size[0] * 0.5, position[1] + size[1] * 0.3), scale=text_scale, maxwidth=size[0] * 0.75, size=(0, 0), h_align='center', v_align='center', draw_controller=btn2, color=(0.7, 0.9, 0.7, 1.0 if enabled else 0.2)) if price is not None and enabled: ba.textwidget(parent=self._root_widget, text=price, position=(position[0] + size[0] * 0.5, position[1] + size[1] * 0.17), scale=0.7, maxwidth=size[0] * 0.75, size=(0, 0), h_align='center', v_align='center', draw_controller=btn2, color=(0.4, 0.9, 0.4, 1.0)) i = None if tex_name is not None: tex_size = 90.0 * tex_scale i = ba.imagewidget( parent=self._root_widget, texture=ba.gettexture(tex_name), position=(position[0] + size[0] * 0.5 - tex_size * 0.5, position[1] + size[1] * 0.66 - tex_size * 0.5), size=(tex_size, tex_size), draw_controller=btn2, opacity=tex_opacity * (1.0 if enabled else 0.25)) if item == 'ad': self._ad_button = btn2 self._ad_label = txt assert i is not None self._ad_image = i self._ad_time_text = ba.textwidget( parent=self._root_widget, text='1m 10s', position=(position[0] + size[0] * 0.5, position[1] + size[1] * 0.5), scale=text_scale * 1.2, maxwidth=size[0] * 0.85, size=(0, 0), h_align='center', v_align='center', draw_controller=btn2, color=(0.4, 0.9, 0.4, 1.0)) return btn2
def _update(self) -> None: import datetime # if we somehow get signed out, just die.. if _ba.get_account_state() != 'signed_in': self._back() return self._ticket_count = _ba.get_account_ticket_count() # update our incentivized ad button depending on whether ads are # available if self._ad_button is not None: next_reward_ad_time = _ba.get_account_misc_read_val_2( 'nextRewardAdTime', None) if next_reward_ad_time is not None: next_reward_ad_time = datetime.datetime.utcfromtimestamp( next_reward_ad_time) now = datetime.datetime.utcnow() if (_ba.have_incentivized_ad() and (next_reward_ad_time is None or next_reward_ad_time <= now)): self._ad_button_greyed = False ba.buttonwidget(edit=self._ad_button, color=(0.65, 0.5, 0.7)) ba.textwidget(edit=self._ad_label, color=(0.7, 0.9, 0.7, 1.0)) ba.textwidget(edit=self._ad_free_text, color=(1, 1, 0, 1)) ba.imagewidget(edit=self._ad_image, opacity=0.6) ba.textwidget(edit=self._ad_time_text, text='') else: self._ad_button_greyed = True ba.buttonwidget(edit=self._ad_button, color=(0.5, 0.5, 0.5)) ba.textwidget(edit=self._ad_label, color=(0.7, 0.9, 0.7, 0.2)) ba.textwidget(edit=self._ad_free_text, color=(1, 1, 0, 0.2)) ba.imagewidget(edit=self._ad_image, opacity=0.6 * 0.25) sval: Union[str, ba.Lstr] if (next_reward_ad_time is not None and next_reward_ad_time > now): sval = ba.timestring( (next_reward_ad_time - now).total_seconds() * 1000.0, centi=False, timeformat=ba.TimeFormat.MILLISECONDS) else: sval = '' ba.textwidget(edit=self._ad_time_text, text=sval) # if this is our first update, assign immediately; otherwise kick # off a smooth transition if the value has changed if self._smooth_ticket_count is None: self._smooth_ticket_count = float(self._ticket_count) self._smooth_update() # will set the text widget elif (self._ticket_count != int(self._smooth_ticket_count) and self._smooth_update_timer is None): self._smooth_update_timer = ba.Timer(0.05, ba.WeakCall( self._smooth_update), repeat=True, timetype=ba.TimeType.REAL) diff = abs(float(self._ticket_count) - self._smooth_ticket_count) self._smooth_increase_speed = (diff / 100.0 if diff >= 5000 else diff / 50.0 if diff >= 1500 else diff / 30.0 if diff >= 500 else diff / 15.0)