예제 #1
0
def ensure_have_account_player_profile() -> None:
    """
    Ensure the standard account-named player profile exists;
    creating if needed.
    """
    # This only applies when we're signed in.
    if _ba.get_account_state() != 'signed_in':
        return

    # If the short version of our account name currently cant be
    # displayed by the game, cancel.
    if not _ba.have_chars(_ba.get_account_display_string(full=False)):
        return

    config = _ba.app.config
    if ('Player Profiles' not in config
            or '__account__' not in config['Player Profiles']):

        # Create a spaz with a nice default purply color.
        _ba.add_transaction({
            'type': 'ADD_PLAYER_PROFILE',
            'name': '__account__',
            'profile': {
                'character': 'Spaz',
                'color': [0.5, 0.25, 1.0],
                'highlight': [0.5, 0.25, 1.0]
            }
        })
        _ba.run_transactions()
예제 #2
0
 def _refresh_account_name_text(self) -> None:
     if self._account_name_text is None:
         return
     try:
         name_str = _ba.get_account_display_string()
     except Exception:
         ba.print_exception()
         name_str = '??'
     ba.textwidget(edit=self._account_name_text, text=name_str)
예제 #3
0
    def _update_field(self, response: dict[str, Any]) -> None:
        if self._angry_computer_image is None:
            self._angry_computer_image = ba.imagewidget(
                parent=self._root_widget,
                position=(self._width - 180, self._height * 0.5 - 65),
                size=(150, 150),
                texture=self.lineup_tex,
                model_transparent=self._angry_computer_transparent_model)
        if self._line_image is None:
            self._line_image = ba.imagewidget(
                parent=self._root_widget,
                color=(0.0, 0.0, 0.0),
                opacity=0.2,
                position=(self._line_left, self._line_bottom - 2.0),
                size=(self._line_width, 4.0),
                texture=self._white_tex)

        # now go through the data they sent, creating dudes for us and our
        # enemies as needed and updating target positions on all of them..

        # mark all as unclaimed so we know which ones to kill off..
        for dude in self._dudes:
            dude.claimed = False

        # always have a dude for ourself..
        if -1 not in self._dudes_by_id:
            dude = self.Dude(
                self, response['d'], self._initial_offset, True,
                _ba.get_account_misc_read_val_2('resolvedAccountID', None),
                _ba.get_account_display_string())
            self._dudes_by_id[-1] = dude
            self._dudes.append(dude)
        else:
            self._dudes_by_id[-1].set_target_distance(response['d'])
        self._dudes_by_id[-1].claimed = True

        # now create/destroy enemies
        for (enemy_id, enemy_distance, enemy_account_id,
             enemy_name) in response['e']:
            if enemy_id not in self._dudes_by_id:
                dude = self.Dude(self, enemy_distance, self._initial_offset,
                                 False, enemy_account_id, enemy_name)
                self._dudes_by_id[enemy_id] = dude
                self._dudes.append(dude)
            else:
                self._dudes_by_id[enemy_id].set_target_distance(enemy_distance)
            self._dudes_by_id[enemy_id].claimed = True

        # remove unclaimed dudes from both of our lists
        # noinspection PyUnresolvedReferences
        self._dudes_by_id = dict([
            item for item in list(self._dudes_by_id.items()) if item[1].claimed
        ])
        self._dudes = [dude for dude in self._dudes if dude.claimed]
예제 #4
0
def get_last_player_name_from_input_device(device: ba.InputDevice) -> str:
    """Return a reasonable player name associated with a device.

    (generally the last one used there)
    """
    appconfig = _ba.app.config

    # Look for a default player profile name for them;
    # otherwise default to their current random name.
    profilename = '_random'
    key_name = device.name + ' ' + device.unique_identifier
    if ('Default Player Profiles' in appconfig
            and key_name in appconfig['Default Player Profiles']):
        profilename = appconfig['Default Player Profiles'][key_name]
    if profilename == '_random':
        profilename = device.get_default_player_name()
    if profilename == '__account__':
        profilename = _ba.get_account_display_string()
    return profilename
예제 #5
0
    def __init__(self,
                 existing_profile: Optional[str],
                 in_main_menu: bool,
                 transition: str = 'in_right'):
        # FIXME: Tidy this up a bit.
        # pylint: disable=too-many-branches
        # pylint: disable=too-many-statements
        # pylint: disable=too-many-locals
        from ba.internal import get_player_profile_colors
        self._in_main_menu = in_main_menu
        self._existing_profile = existing_profile
        self._r = 'editProfileWindow'
        self._spazzes: List[str] = []
        self._icon_textures: List[ba.Texture] = []
        self._icon_tint_textures: List[ba.Texture] = []

        # Grab profile colors or pick random ones.
        self._color, self._highlight = get_player_profile_colors(
            existing_profile)
        self._width = width = 780.0 if ba.app.small_ui else 680.0
        self._x_inset = x_inset = 50.0 if ba.app.small_ui else 0.0
        self._height = height = (350.0 if ba.app.small_ui else
                                 400.0 if ba.app.med_ui else 450.0)
        spacing = 40
        self._base_scale = (2.05 if ba.app.small_ui else
                            1.5 if ba.app.med_ui else 1.0)
        top_extra = 15 if ba.app.small_ui else 15
        super().__init__(root_widget=ba.containerwidget(
            size=(width, height + top_extra),
            transition=transition,
            scale=self._base_scale,
            stack_offset=(0, 15) if ba.app.small_ui else (0, 0)))
        cancel_button = btn = ba.buttonwidget(
            parent=self._root_widget,
            position=(52 + x_inset, height - 60),
            size=(155, 60),
            scale=0.8,
            autoselect=True,
            label=ba.Lstr(resource='cancelText'),
            on_activate_call=self._cancel)
        ba.containerwidget(edit=self._root_widget, cancel_button=btn)
        save_button = btn = ba.buttonwidget(parent=self._root_widget,
                                            position=(width - (177 + x_inset),
                                                      height - 60),
                                            size=(155, 60),
                                            autoselect=True,
                                            scale=0.8,
                                            label=ba.Lstr(resource='saveText'))
        ba.widget(edit=save_button, left_widget=cancel_button)
        ba.widget(edit=cancel_button, right_widget=save_button)
        ba.containerwidget(edit=self._root_widget, start_button=btn)
        ba.textwidget(parent=self._root_widget,
                      position=(self._width * 0.5, height - 38),
                      size=(0, 0),
                      text=(ba.Lstr(resource=self._r + '.titleNewText')
                            if existing_profile is None else ba.Lstr(
                                resource=self._r + '.titleEditText')),
                      color=ba.app.title_color,
                      maxwidth=290,
                      scale=1.0,
                      h_align="center",
                      v_align="center")

        # Make a list of spaz icons.
        self.refresh_characters()
        profile = ba.app.config.get('Player Profiles',
                                    {}).get(self._existing_profile, {})

        if 'global' in profile:
            self._global = profile['global']
        else:
            self._global = False

        if 'icon' in profile:
            self._icon = profile['icon']
        else:
            self._icon = ba.charstr(ba.SpecialChar.LOGO)

        assigned_random_char = False

        # Look for existing character choice or pick random one otherwise.
        try:
            icon_index = self._spazzes.index(profile['character'])
        except Exception:
            # Let's set the default icon to spaz for our first profile; after
            # that we go random.
            # (SCRATCH THAT.. we now hard-code account-profiles to start with
            # spaz which has a similar effect)
            # try: p_len = len(ba.app.config['Player Profiles'])
            # except Exception: p_len = 0
            # if p_len == 0: icon_index = self._spazzes.index('Spaz')
            # else:
            random.seed()
            icon_index = random.randrange(len(self._spazzes))
            assigned_random_char = True
        self._icon_index = icon_index
        ba.buttonwidget(edit=save_button, on_activate_call=self.save)

        v = height - 115.0
        self._name = ('' if self._existing_profile is None else
                      self._existing_profile)
        self._is_account_profile = (self._name == '__account__')

        # If we just picked a random character, see if it has specific
        # colors/highlights associated with it and assign them if so.
        if assigned_random_char:
            clr = ba.app.spaz_appearances[
                self._spazzes[icon_index]].default_color
            if clr is not None:
                self._color = clr
            highlight = ba.app.spaz_appearances[
                self._spazzes[icon_index]].default_highlight
            if highlight is not None:
                self._highlight = highlight

        # Assign a random name if they had none.
        if self._name == '':
            names = _ba.get_random_names()
            self._name = names[random.randrange(len(names))]

        self._clipped_name_text = ba.textwidget(parent=self._root_widget,
                                                text='',
                                                position=(540 + x_inset,
                                                          v - 8),
                                                flatness=1.0,
                                                shadow=0.0,
                                                scale=0.55,
                                                size=(0, 0),
                                                maxwidth=100,
                                                h_align='center',
                                                v_align='center',
                                                color=(1, 1, 0, 0.5))

        if not self._is_account_profile and not self._global:
            ba.textwidget(parent=self._root_widget,
                          text=ba.Lstr(resource=self._r + '.nameText'),
                          position=(200 + x_inset, v - 6),
                          size=(0, 0),
                          h_align='right',
                          v_align='center',
                          color=(1, 1, 1, 0.5),
                          scale=0.9)

        self._upgrade_button = None
        if self._is_account_profile:
            if _ba.get_account_state() == 'signed_in':
                sval = _ba.get_account_display_string()
            else:
                sval = '??'
            ba.textwidget(parent=self._root_widget,
                          position=(self._width * 0.5, v - 7),
                          size=(0, 0),
                          scale=1.2,
                          text=sval,
                          maxwidth=270,
                          h_align='center',
                          v_align='center')
            txtl = ba.Lstr(
                resource='editProfileWindow.accountProfileText').evaluate()
            b_width = min(
                270.0,
                _ba.get_string_width(txtl, suppress_warning=True) * 0.6)
            ba.textwidget(parent=self._root_widget,
                          position=(self._width * 0.5, v - 39),
                          size=(0, 0),
                          scale=0.6,
                          color=ba.app.infotextcolor,
                          text=txtl,
                          maxwidth=270,
                          h_align='center',
                          v_align='center')
            self._account_type_info_button = ba.buttonwidget(
                parent=self._root_widget,
                label='?',
                size=(15, 15),
                text_scale=0.6,
                position=(self._width * 0.5 + b_width * 0.5 + 13, v - 47),
                button_type='square',
                color=(0.6, 0.5, 0.65),
                autoselect=True,
                on_activate_call=self.show_account_profile_info)
        elif self._global:

            b_size = 60
            self._icon_button = btn = ba.buttonwidget(
                parent=self._root_widget,
                autoselect=True,
                position=(self._width * 0.5 - 160 - b_size * 0.5, v - 38 - 15),
                size=(b_size, b_size),
                color=(0.6, 0.5, 0.6),
                label='',
                button_type='square',
                text_scale=1.2,
                on_activate_call=self._on_icon_press)
            self._icon_button_label = ba.textwidget(
                parent=self._root_widget,
                position=(self._width * 0.5 - 160, v - 35),
                draw_controller=btn,
                h_align='center',
                v_align='center',
                size=(0, 0),
                color=(1, 1, 1),
                text='',
                scale=2.0)

            ba.textwidget(parent=self._root_widget,
                          h_align='center',
                          v_align='center',
                          position=(self._width * 0.5 - 160, v - 55 - 15),
                          size=(0, 0),
                          draw_controller=btn,
                          text=ba.Lstr(resource=self._r + '.iconText'),
                          scale=0.7,
                          color=ba.app.title_color,
                          maxwidth=120)

            self._update_icon()

            ba.textwidget(parent=self._root_widget,
                          position=(self._width * 0.5, v - 7),
                          size=(0, 0),
                          scale=1.2,
                          text=self._name,
                          maxwidth=240,
                          h_align='center',
                          v_align='center')
            # FIXME hard coded strings are bad
            txtl = ba.Lstr(
                resource='editProfileWindow.globalProfileText').evaluate()
            b_width = min(
                240.0,
                _ba.get_string_width(txtl, suppress_warning=True) * 0.6)
            ba.textwidget(parent=self._root_widget,
                          position=(self._width * 0.5, v - 39),
                          size=(0, 0),
                          scale=0.6,
                          color=ba.app.infotextcolor,
                          text=txtl,
                          maxwidth=240,
                          h_align='center',
                          v_align='center')
            self._account_type_info_button = ba.buttonwidget(
                parent=self._root_widget,
                label='?',
                size=(15, 15),
                text_scale=0.6,
                position=(self._width * 0.5 + b_width * 0.5 + 13, v - 47),
                button_type='square',
                color=(0.6, 0.5, 0.65),
                autoselect=True,
                on_activate_call=self.show_global_profile_info)
        else:
            self._text_field = ba.textwidget(
                parent=self._root_widget,
                position=(220 + x_inset, v - 30),
                size=(265, 40),
                text=self._name,
                h_align='left',
                v_align='center',
                max_chars=16,
                description=ba.Lstr(resource=self._r + '.nameDescriptionText'),
                autoselect=True,
                editable=True,
                padding=4,
                color=(0.9, 0.9, 0.9, 1.0),
                on_return_press_call=ba.Call(save_button.activate))

            # FIXME hard coded strings are bad
            txtl = ba.Lstr(
                resource='editProfileWindow.localProfileText').evaluate()
            b_width = min(
                270.0,
                _ba.get_string_width(txtl, suppress_warning=True) * 0.6)
            ba.textwidget(parent=self._root_widget,
                          position=(self._width * 0.5, v - 43),
                          size=(0, 0),
                          scale=0.6,
                          color=ba.app.infotextcolor,
                          text=txtl,
                          maxwidth=270,
                          h_align='center',
                          v_align='center')
            self._account_type_info_button = ba.buttonwidget(
                parent=self._root_widget,
                label='?',
                size=(15, 15),
                text_scale=0.6,
                position=(self._width * 0.5 + b_width * 0.5 + 13, v - 50),
                button_type='square',
                color=(0.6, 0.5, 0.65),
                autoselect=True,
                on_activate_call=self.show_local_profile_info)
            self._upgrade_button = ba.buttonwidget(
                parent=self._root_widget,
                label=ba.Lstr(resource='upgradeText'),
                size=(40, 17),
                text_scale=1.0,
                button_type='square',
                position=(self._width * 0.5 + b_width * 0.5 + 13 + 43, v - 51),
                color=(0.6, 0.5, 0.65),
                autoselect=True,
                on_activate_call=self.upgrade_profile)

        self._update_clipped_name()
        self._clipped_name_timer = ba.Timer(0.333,
                                            ba.WeakCall(
                                                self._update_clipped_name),
                                            timetype=ba.TimeType.REAL,
                                            repeat=True)

        v -= spacing * 3.0
        b_size = 80
        b_size_2 = 100
        b_offs = 150
        self._color_button = btn = ba.buttonwidget(
            parent=self._root_widget,
            autoselect=True,
            position=(self._width * 0.5 - b_offs - b_size * 0.5, v - 50),
            size=(b_size, b_size),
            color=self._color,
            label='',
            button_type='square')
        origin = self._color_button.get_screen_space_center()
        ba.buttonwidget(edit=self._color_button,
                        on_activate_call=ba.WeakCall(self._make_picker,
                                                     'color', origin))
        ba.textwidget(parent=self._root_widget,
                      h_align='center',
                      v_align='center',
                      position=(self._width * 0.5 - b_offs, v - 65),
                      size=(0, 0),
                      draw_controller=btn,
                      text=ba.Lstr(resource=self._r + '.colorText'),
                      scale=0.7,
                      color=ba.app.title_color,
                      maxwidth=120)

        self._character_button = btn = ba.buttonwidget(
            parent=self._root_widget,
            autoselect=True,
            position=(self._width * 0.5 - b_size_2 * 0.5, v - 60),
            up_widget=self._account_type_info_button,
            on_activate_call=self._on_character_press,
            size=(b_size_2, b_size_2),
            label='',
            color=(1, 1, 1),
            mask_texture=ba.gettexture('characterIconMask'))
        if not self._is_account_profile and not self._global:
            ba.containerwidget(edit=self._root_widget,
                               selected_child=self._text_field)
        ba.textwidget(parent=self._root_widget,
                      h_align='center',
                      v_align='center',
                      position=(self._width * 0.5, v - 80),
                      size=(0, 0),
                      draw_controller=btn,
                      text=ba.Lstr(resource=self._r + '.characterText'),
                      scale=0.7,
                      color=ba.app.title_color,
                      maxwidth=130)

        self._highlight_button = btn = ba.buttonwidget(
            parent=self._root_widget,
            autoselect=True,
            position=(self._width * 0.5 + b_offs - b_size * 0.5, v - 50),
            up_widget=self._upgrade_button if self._upgrade_button is not None
            else self._account_type_info_button,
            size=(b_size, b_size),
            color=self._highlight,
            label='',
            button_type='square')

        if not self._is_account_profile and not self._global:
            ba.widget(edit=cancel_button, down_widget=self._text_field)
            ba.widget(edit=save_button, down_widget=self._text_field)
            ba.widget(edit=self._color_button, up_widget=self._text_field)
        ba.widget(edit=self._account_type_info_button,
                  down_widget=self._character_button)

        origin = self._highlight_button.get_screen_space_center()
        ba.buttonwidget(edit=self._highlight_button,
                        on_activate_call=ba.WeakCall(self._make_picker,
                                                     'highlight', origin))
        ba.textwidget(parent=self._root_widget,
                      h_align='center',
                      v_align='center',
                      position=(self._width * 0.5 + b_offs, v - 65),
                      size=(0, 0),
                      draw_controller=btn,
                      text=ba.Lstr(resource=self._r + '.highlightText'),
                      scale=0.7,
                      color=ba.app.title_color,
                      maxwidth=120)
        self._update_character()
예제 #6
0
    def _refresh_not_in_game(
        self, positions: List[Tuple[float, float,
                                    float]]) -> Tuple[float, float, float]:
        # pylint: disable=too-many-branches
        # pylint: disable=too-many-locals
        # pylint: disable=too-many-statements
        if not ba.app.did_menu_intro:
            self._tdelay = 2.0
            self._t_delay_inc = 0.02
            self._t_delay_play = 1.7
            ba.app.did_menu_intro = True
        self._width = 400.0
        self._height = 200.0
        enable_account_button = True
        account_type_name: Union[str, ba.Lstr]
        if _ba.get_account_state() == 'signed_in':
            account_type_name = _ba.get_account_display_string()
            account_type_icon = None
            account_textcolor = (1.0, 1.0, 1.0)
        else:
            account_type_name = ba.Lstr(
                resource='notSignedInText',
                fallback_resource='accountSettingsWindow.titleText')
            account_type_icon = None
            account_textcolor = (1.0, 0.2, 0.2)
        account_type_icon_color = (1.0, 1.0, 1.0)
        account_type_call = self._show_account_window
        account_type_enable_button_sound = True
        b_count = 3  # play, help, credits
        if self._have_settings_button:
            b_count += 1
        if enable_account_button:
            b_count += 1
        if self._have_quit_button:
            b_count += 1
        if self._have_store_button:
            b_count += 1
        uiscale = ba.app.ui.uiscale
        if uiscale is ba.UIScale.SMALL:
            root_widget_scale = 1.6
            play_button_width = self._button_width * 0.65
            play_button_height = self._button_height * 1.1
            small_button_scale = 0.51 if b_count > 6 else 0.63
            button_y_offs = -20.0
            button_y_offs2 = -60.0
            self._button_height *= 1.3
            button_spacing = 1.04
        elif uiscale is ba.UIScale.MEDIUM:
            root_widget_scale = 1.3
            play_button_width = self._button_width * 0.65
            play_button_height = self._button_height * 1.1
            small_button_scale = 0.6
            button_y_offs = -55.0
            button_y_offs2 = -75.0
            self._button_height *= 1.25
            button_spacing = 1.1
        else:
            root_widget_scale = 1.0
            play_button_width = self._button_width * 0.65
            play_button_height = self._button_height * 1.1
            small_button_scale = 0.75
            button_y_offs = -80.0
            button_y_offs2 = -100.0
            self._button_height *= 1.2
            button_spacing = 1.1
        spc = self._button_width * small_button_scale * button_spacing
        ba.containerwidget(edit=self._root_widget,
                           size=(self._width, self._height),
                           background=False,
                           scale=root_widget_scale)
        assert not positions
        positions.append((self._width * 0.5, button_y_offs, 1.7))
        x_offs = self._width * 0.5 - (spc * (b_count - 1) * 0.5) + (spc * 0.5)
        for i in range(b_count - 1):
            positions.append(
                (x_offs + spc * i - 1.0, button_y_offs + button_y_offs2,
                 small_button_scale))
        # In kiosk mode, provide a button to get back to the kiosk menu.
        if ba.app.demo_mode or ba.app.arcade_mode:
            h, v, scale = positions[self._p_index]
            this_b_width = self._button_width * 0.4 * scale
            demo_menu_delay = 0.0 if self._t_delay_play == 0.0 else max(
                0, self._t_delay_play + 0.1)
            self._demo_menu_button = ba.buttonwidget(
                parent=self._root_widget,
                position=(self._width * 0.5 - this_b_width * 0.5, v + 90),
                size=(this_b_width, 45),
                autoselect=True,
                color=(0.45, 0.55, 0.45),
                textcolor=(0.7, 0.8, 0.7),
                label=ba.Lstr(resource='modeArcadeText' if ba.app.
                              arcade_mode else 'modeDemoText'),
                transition_delay=demo_menu_delay,
                on_activate_call=self._demo_menu_press)
        else:
            self._demo_menu_button = None
        uiscale = ba.app.ui.uiscale
        foof = (-1 if uiscale is ba.UIScale.SMALL else
                1 if uiscale is ba.UIScale.MEDIUM else 3)
        h, v, scale = positions[self._p_index]
        v = v + foof
        gather_delay = 0.0 if self._t_delay_play == 0.0 else max(
            0.0, self._t_delay_play + 0.1)
        assert play_button_width is not None
        assert play_button_height is not None
        this_h = h - play_button_width * 0.5 * scale - 40 * scale
        this_b_width = self._button_width * 0.25 * scale
        this_b_height = self._button_height * 0.82 * scale
        self._gather_button = btn = ba.buttonwidget(
            parent=self._root_widget,
            position=(this_h - this_b_width * 0.5, v),
            size=(this_b_width, this_b_height),
            autoselect=self._use_autoselect,
            button_type='square',
            label='',
            transition_delay=gather_delay,
            on_activate_call=self._gather_press)
        ba.textwidget(parent=self._root_widget,
                      position=(this_h, v + self._button_height * 0.33),
                      size=(0, 0),
                      scale=0.75,
                      transition_delay=gather_delay,
                      draw_controller=btn,
                      color=(0.75, 1.0, 0.7),
                      maxwidth=self._button_width * 0.33,
                      text=ba.Lstr(resource='gatherWindow.titleText'),
                      h_align='center',
                      v_align='center')
        icon_size = this_b_width * 0.6
        ba.imagewidget(parent=self._root_widget,
                       size=(icon_size, icon_size),
                       draw_controller=btn,
                       transition_delay=gather_delay,
                       position=(this_h - 0.5 * icon_size,
                                 v + 0.31 * this_b_height),
                       texture=ba.gettexture('usersButton'))

        # Play button.
        h, v, scale = positions[self._p_index]
        self._p_index += 1
        self._start_button = start_button = ba.buttonwidget(
            parent=self._root_widget,
            position=(h - play_button_width * 0.5 * scale, v),
            size=(play_button_width, play_button_height),
            autoselect=self._use_autoselect,
            scale=scale,
            text_res_scale=2.0,
            label=ba.Lstr(resource='playText'),
            transition_delay=self._t_delay_play,
            on_activate_call=self._play_press)
        ba.containerwidget(edit=self._root_widget,
                           start_button=start_button,
                           selected_child=start_button)
        v = v + foof
        watch_delay = 0.0 if self._t_delay_play == 0.0 else max(
            0.0, self._t_delay_play - 0.1)
        this_h = h + play_button_width * 0.5 * scale + 40 * scale
        this_b_width = self._button_width * 0.25 * scale
        this_b_height = self._button_height * 0.82 * scale
        self._watch_button = btn = ba.buttonwidget(
            parent=self._root_widget,
            position=(this_h - this_b_width * 0.5, v),
            size=(this_b_width, this_b_height),
            autoselect=self._use_autoselect,
            button_type='square',
            label='',
            transition_delay=watch_delay,
            on_activate_call=self._watch_press)
        ba.textwidget(parent=self._root_widget,
                      position=(this_h, v + self._button_height * 0.33),
                      size=(0, 0),
                      scale=0.75,
                      transition_delay=watch_delay,
                      color=(0.75, 1.0, 0.7),
                      draw_controller=btn,
                      maxwidth=self._button_width * 0.33,
                      text=ba.Lstr(resource='watchWindow.titleText'),
                      h_align='center',
                      v_align='center')
        icon_size = this_b_width * 0.55
        ba.imagewidget(parent=self._root_widget,
                       size=(icon_size, icon_size),
                       draw_controller=btn,
                       transition_delay=watch_delay,
                       position=(this_h - 0.5 * icon_size,
                                 v + 0.33 * this_b_height),
                       texture=ba.gettexture('tv'))
        if not self._in_game and enable_account_button:
            this_b_width = self._button_width
            h, v, scale = positions[self._p_index]
            self._p_index += 1
            self._gc_button = ba.buttonwidget(
                parent=self._root_widget,
                position=(h - this_b_width * 0.5 * scale, v),
                size=(this_b_width, self._button_height),
                scale=scale,
                label=account_type_name,
                autoselect=self._use_autoselect,
                on_activate_call=account_type_call,
                textcolor=account_textcolor,
                icon=account_type_icon,
                icon_color=account_type_icon_color,
                transition_delay=self._tdelay,
                enable_sound=account_type_enable_button_sound)

            # Scattered eggs on easter.
            if _ba.get_account_misc_read_val('easter',
                                             False) and not self._in_game:
                icon_size = 32
                ba.imagewidget(parent=self._root_widget,
                               position=(h - icon_size * 0.5 + 35,
                                         v + self._button_height * scale -
                                         icon_size * 0.24 + 1.5),
                               transition_delay=self._tdelay,
                               size=(icon_size, icon_size),
                               texture=ba.gettexture('egg2'),
                               tilt_scale=0.0)
            self._tdelay += self._t_delay_inc
        else:
            self._gc_button = None

        # How-to-play button.
        h, v, scale = positions[self._p_index]
        self._p_index += 1
        btn = ba.buttonwidget(
            parent=self._root_widget,
            position=(h - self._button_width * 0.5 * scale, v),
            scale=scale,
            autoselect=self._use_autoselect,
            size=(self._button_width, self._button_height),
            label=ba.Lstr(resource=self._r + '.howToPlayText'),
            transition_delay=self._tdelay,
            on_activate_call=self._howtoplay)
        self._how_to_play_button = btn

        # Scattered eggs on easter.
        if _ba.get_account_misc_read_val('easter',
                                         False) and not self._in_game:
            icon_size = 28
            ba.imagewidget(parent=self._root_widget,
                           position=(h - icon_size * 0.5 + 30,
                                     v + self._button_height * scale -
                                     icon_size * 0.24 + 1.5),
                           transition_delay=self._tdelay,
                           size=(icon_size, icon_size),
                           texture=ba.gettexture('egg4'),
                           tilt_scale=0.0)
        # Credits button.
        self._tdelay += self._t_delay_inc
        h, v, scale = positions[self._p_index]
        self._p_index += 1
        self._credits_button = ba.buttonwidget(
            parent=self._root_widget,
            position=(h - self._button_width * 0.5 * scale, v),
            size=(self._button_width, self._button_height),
            autoselect=self._use_autoselect,
            label=ba.Lstr(resource=self._r + '.creditsText'),
            scale=scale,
            transition_delay=self._tdelay,
            on_activate_call=self._credits)
        self._tdelay += self._t_delay_inc
        return h, v, scale
예제 #7
0
    def on_transition_in(self) -> None:
        super().on_transition_in()
        random.seed(123)
        self._logo_node: Optional[ba.Node] = None
        self._custom_logo_tex_name: Optional[str] = None
        self._word_actors: List[ba.Actor] = []
        app = ba.app

        # FIXME: We shouldn't be doing things conditionally based on whether
        #  the host is VR mode or not (clients may differ in that regard).
        #  Any differences need to happen at the engine level so everyone
        #  sees things in their own optimal way.
        vr_mode = ba.app.vr_mode

        if not ba.app.toolbar_test:
            color = ((1.0, 1.0, 1.0, 1.0) if vr_mode else (0.5, 0.6, 0.5, 0.6))
            # FIXME: Need a node attr for vr-specific-scale.
            scale = (0.9 if
                     (app.ui.uiscale is ba.UIScale.SMALL or vr_mode) else 0.7)
            self.my_name = ba.NodeActor(
                ba.newnode('text',
                           attrs={
                               'v_attach': 'bottom',
                               'h_align': 'center',
                               'color': color,
                               'flatness': 1.0,
                               'shadow': 1.0 if vr_mode else 0.5,
                               'scale': scale,
                               'position': (0, 10),
                               'vr_depth': -10,
                               'text': '\xa9 2011-2020 Eric Froemling'
                           }))

        # Throw up some text that only clients can see so they know that the
        # host is navigating menus while they're just staring at an
        # empty-ish screen.
        tval = ba.Lstr(resource='hostIsNavigatingMenusText',
                       subs=[('${HOST}', _ba.get_account_display_string())])
        self._host_is_navigating_text = ba.NodeActor(
            ba.newnode('text',
                       attrs={
                           'text': tval,
                           'client_only': True,
                           'position': (0, -200),
                           'flatness': 1.0,
                           'h_align': 'center'
                       }))
        if not ba.app.main_menu_did_initial_transition and hasattr(
                self, 'my_name'):
            assert self.my_name.node
            ba.animate(self.my_name.node, 'opacity', {2.3: 0, 3.0: 1.0})

        # FIXME: We shouldn't be doing things conditionally based on whether
        #  the host is vr mode or not (clients may not be or vice versa).
        #  Any differences need to happen at the engine level so everyone sees
        #  things in their own optimal way.
        vr_mode = app.vr_mode
        uiscale = app.ui.uiscale

        # In cases where we're doing lots of dev work lets always show the
        # build number.
        force_show_build_number = False

        if not ba.app.toolbar_test:
            if app.debug_build or app.test_build or force_show_build_number:
                if app.debug_build:
                    text = ba.Lstr(value='${V} (${B}) (${D})',
                                   subs=[
                                       ('${V}', app.version),
                                       ('${B}', str(app.build_number)),
                                       ('${D}', ba.Lstr(resource='debugText')),
                                   ])
                else:
                    text = ba.Lstr(value='${V} (${B})',
                                   subs=[
                                       ('${V}', app.version),
                                       ('${B}', str(app.build_number)),
                                   ])
            else:
                text = ba.Lstr(value='${V}', subs=[('${V}', app.version)])
            scale = 0.9 if (uiscale is ba.UIScale.SMALL or vr_mode) else 0.7
            color = (1, 1, 1, 1) if vr_mode else (0.5, 0.6, 0.5, 0.7)
            self.version = ba.NodeActor(
                ba.newnode(
                    'text',
                    attrs={
                        'v_attach': 'bottom',
                        'h_attach': 'right',
                        'h_align': 'right',
                        'flatness': 1.0,
                        'vr_depth': -10,
                        'shadow': 1.0 if vr_mode else 0.5,
                        'color': color,
                        'scale': scale,
                        'position': (-260, 10) if vr_mode else (-10, 10),
                        'text': text
                    }))
            if not ba.app.main_menu_did_initial_transition:
                assert self.version.node
                ba.animate(self.version.node, 'opacity', {2.3: 0, 3.0: 1.0})

        # Show the iircade logo on our iircade build.
        if app.iircade_mode:
            img = ba.NodeActor(
                ba.newnode('image',
                           attrs={
                               'texture': ba.gettexture('iircadeLogo'),
                               'attach': 'center',
                               'scale': (250, 250),
                               'position': (0, 0),
                               'tilt_translate': 0.21,
                               'absolute_scale': True
                           })).autoretain()
            imgdelay = 0.0 if app.main_menu_did_initial_transition else 1.0
            ba.animate(img.node, 'opacity', {
                imgdelay + 1.5: 0.0,
                imgdelay + 2.5: 1.0
            })

        # Throw in test build info.
        self.beta_info = self.beta_info_2 = None
        if app.test_build and not (app.demo_mode or app.arcade_mode):
            pos = ((230, 125) if (app.demo_mode or app.arcade_mode) else
                   (230, 35))
            self.beta_info = ba.NodeActor(
                ba.newnode('text',
                           attrs={
                               'v_attach': 'center',
                               'h_align': 'center',
                               'color': (1, 1, 1, 1),
                               'shadow': 0.5,
                               'flatness': 0.5,
                               'scale': 1,
                               'vr_depth': -60,
                               'position': pos,
                               'text': ba.Lstr(resource='testBuildText')
                           }))
            if not ba.app.main_menu_did_initial_transition:
                assert self.beta_info.node
                ba.animate(self.beta_info.node, 'opacity', {1.3: 0, 1.8: 1.0})

        model = ba.getmodel('thePadLevel')
        trees_model = ba.getmodel('trees')
        bottom_model = ba.getmodel('thePadLevelBottom')
        color_texture = ba.gettexture('thePadLevelColor')
        trees_texture = ba.gettexture('treesColor')
        bgtex = ba.gettexture('menuBG')
        bgmodel = ba.getmodel('thePadBG')

        # Load these last since most platforms don't use them.
        vr_bottom_fill_model = ba.getmodel('thePadVRFillBottom')
        vr_top_fill_model = ba.getmodel('thePadVRFillTop')

        gnode = self.globalsnode
        gnode.camera_mode = 'rotate'

        tint = (1.14, 1.1, 1.0)
        gnode.tint = tint
        gnode.ambient_color = (1.06, 1.04, 1.03)
        gnode.vignette_outer = (0.45, 0.55, 0.54)
        gnode.vignette_inner = (0.99, 0.98, 0.98)

        self.bottom = ba.NodeActor(
            ba.newnode('terrain',
                       attrs={
                           'model': bottom_model,
                           'lighting': False,
                           'reflection': 'soft',
                           'reflection_scale': [0.45],
                           'color_texture': color_texture
                       }))
        self.vr_bottom_fill = ba.NodeActor(
            ba.newnode('terrain',
                       attrs={
                           'model': vr_bottom_fill_model,
                           'lighting': False,
                           'vr_only': True,
                           'color_texture': color_texture
                       }))
        self.vr_top_fill = ba.NodeActor(
            ba.newnode('terrain',
                       attrs={
                           'model': vr_top_fill_model,
                           'vr_only': True,
                           'lighting': False,
                           'color_texture': bgtex
                       }))
        self.terrain = ba.NodeActor(
            ba.newnode('terrain',
                       attrs={
                           'model': model,
                           'color_texture': color_texture,
                           'reflection': 'soft',
                           'reflection_scale': [0.3]
                       }))
        self.trees = ba.NodeActor(
            ba.newnode('terrain',
                       attrs={
                           'model': trees_model,
                           'lighting': False,
                           'reflection': 'char',
                           'reflection_scale': [0.1],
                           'color_texture': trees_texture
                       }))
        self.bgterrain = ba.NodeActor(
            ba.newnode('terrain',
                       attrs={
                           'model': bgmodel,
                           'color': (0.92, 0.91, 0.9),
                           'lighting': False,
                           'background': True,
                           'color_texture': bgtex
                       }))

        self._ts = 0.86

        self._language: Optional[str] = None
        self._update_timer = ba.Timer(1.0, self._update, repeat=True)
        self._update()

        # Hopefully this won't hitch but lets space these out anyway.
        _ba.add_clean_frame_callback(ba.WeakCall(self._start_preloads))

        random.seed()

        # On the main menu, also show our news.
        class News:
            """Wrangles news display."""

            def __init__(self, activity: ba.Activity):
                self._valid = True
                self._message_duration = 10.0
                self._message_spacing = 2.0
                self._text: Optional[ba.NodeActor] = None
                self._activity = weakref.ref(activity)

                # If we're signed in, fetch news immediately.
                # Otherwise wait until we are signed in.
                self._fetch_timer: Optional[ba.Timer] = ba.Timer(
                    1.0, ba.WeakCall(self._try_fetching_news), repeat=True)
                self._try_fetching_news()

            # We now want to wait until we're signed in before fetching news.
            def _try_fetching_news(self) -> None:
                if _ba.get_account_state() == 'signed_in':
                    self._fetch_news()
                    self._fetch_timer = None

            def _fetch_news(self) -> None:
                ba.app.main_menu_last_news_fetch_time = time.time()

                # UPDATE - We now just pull news from MRVs.
                news = _ba.get_account_misc_read_val('n', None)
                if news is not None:
                    self._got_news(news)

            def _change_phrase(self) -> None:
                from bastd.actor.text import Text

                # If our news is way out of date, lets re-request it;
                # otherwise, rotate our phrase.
                assert ba.app.main_menu_last_news_fetch_time is not None
                if time.time() - ba.app.main_menu_last_news_fetch_time > 600.0:
                    self._fetch_news()
                    self._text = None
                else:
                    if self._text is not None:
                        if not self._phrases:
                            for phr in self._used_phrases:
                                self._phrases.insert(0, phr)
                        val = self._phrases.pop()
                        if val == '__ACH__':
                            vrmode = app.vr_mode
                            Text(ba.Lstr(resource='nextAchievementsText'),
                                 color=((1, 1, 1, 1) if vrmode else
                                        (0.95, 0.9, 1, 0.4)),
                                 host_only=True,
                                 maxwidth=200,
                                 position=(-300, -35),
                                 h_align=Text.HAlign.RIGHT,
                                 transition=Text.Transition.FADE_IN,
                                 scale=0.9 if vrmode else 0.7,
                                 flatness=1.0 if vrmode else 0.6,
                                 shadow=1.0 if vrmode else 0.5,
                                 h_attach=Text.HAttach.CENTER,
                                 v_attach=Text.VAttach.TOP,
                                 transition_delay=1.0,
                                 transition_out_delay=self._message_duration
                                 ).autoretain()
                            achs = [
                                a for a in app.achievements if not a.complete
                            ]
                            if achs:
                                ach = achs.pop(
                                    random.randrange(min(4, len(achs))))
                                ach.create_display(
                                    -180,
                                    -35,
                                    1.0,
                                    outdelay=self._message_duration,
                                    style='news')
                            if achs:
                                ach = achs.pop(
                                    random.randrange(min(8, len(achs))))
                                ach.create_display(
                                    180,
                                    -35,
                                    1.25,
                                    outdelay=self._message_duration,
                                    style='news')
                        else:
                            spc = self._message_spacing
                            keys = {
                                spc: 0.0,
                                spc + 1.0: 1.0,
                                spc + self._message_duration - 1.0: 1.0,
                                spc + self._message_duration: 0.0
                            }
                            assert self._text.node
                            ba.animate(self._text.node, 'opacity', keys)
                            # {k: v
                            #  for k, v in list(keys.items())})
                            self._text.node.text = val

            def _got_news(self, news: str) -> None:
                # Run this stuff in the context of our activity since we
                # need to make nodes and stuff.. should fix the serverget
                # call so it.
                activity = self._activity()
                if activity is None or activity.expired:
                    return
                with ba.Context(activity):

                    self._phrases: List[str] = []

                    # Show upcoming achievements in non-vr versions
                    # (currently too hard to read in vr).
                    self._used_phrases = (
                        ['__ACH__'] if not ba.app.vr_mode else
                        []) + [s for s in news.split('<br>\n') if s != '']
                    self._phrase_change_timer = ba.Timer(
                        (self._message_duration + self._message_spacing),
                        ba.WeakCall(self._change_phrase),
                        repeat=True)

                    scl = 1.2 if (ba.app.ui.uiscale is ba.UIScale.SMALL
                                  or ba.app.vr_mode) else 0.8

                    color2 = ((1, 1, 1, 1) if ba.app.vr_mode else
                              (0.7, 0.65, 0.75, 1.0))
                    shadow = (1.0 if ba.app.vr_mode else 0.4)
                    self._text = ba.NodeActor(
                        ba.newnode('text',
                                   attrs={
                                       'v_attach': 'top',
                                       'h_attach': 'center',
                                       'h_align': 'center',
                                       'vr_depth': -20,
                                       'shadow': shadow,
                                       'flatness': 0.8,
                                       'v_align': 'top',
                                       'color': color2,
                                       'scale': scl,
                                       'maxwidth': 900.0 / scl,
                                       'position': (0, -10)
                                   }))
                    self._change_phrase()

        if not (app.demo_mode or app.arcade_mode) and not app.toolbar_test:
            self._news = News(self)

        # Bring up the last place we were, or start at the main menu otherwise.
        with ba.Context('ui'):
            from bastd.ui import specialoffer
            if bool(False):
                uicontroller = ba.app.ui.controller
                assert uicontroller is not None
                uicontroller.show_main_menu()
            else:
                main_menu_location = ba.app.ui.get_main_menu_location()

                # When coming back from a kiosk-mode game, jump to
                # the kiosk start screen.
                if ba.app.demo_mode or ba.app.arcade_mode:
                    # pylint: disable=cyclic-import
                    from bastd.ui.kiosk import KioskWindow
                    ba.app.ui.set_main_menu_window(
                        KioskWindow().get_root_widget())
                # ..or in normal cases go back to the main menu
                else:
                    if main_menu_location == 'Gather':
                        # pylint: disable=cyclic-import
                        from bastd.ui.gather import GatherWindow
                        ba.app.ui.set_main_menu_window(
                            GatherWindow(transition=None).get_root_widget())
                    elif main_menu_location == 'Watch':
                        # pylint: disable=cyclic-import
                        from bastd.ui.watch import WatchWindow
                        ba.app.ui.set_main_menu_window(
                            WatchWindow(transition=None).get_root_widget())
                    elif main_menu_location == 'Team Game Select':
                        # pylint: disable=cyclic-import
                        from bastd.ui.playlist.browser import (
                            PlaylistBrowserWindow)
                        ba.app.ui.set_main_menu_window(
                            PlaylistBrowserWindow(
                                sessiontype=ba.DualTeamSession,
                                transition=None).get_root_widget())
                    elif main_menu_location == 'Free-for-All Game Select':
                        # pylint: disable=cyclic-import
                        from bastd.ui.playlist.browser import (
                            PlaylistBrowserWindow)
                        ba.app.ui.set_main_menu_window(
                            PlaylistBrowserWindow(
                                sessiontype=ba.FreeForAllSession,
                                transition=None).get_root_widget())
                    elif main_menu_location == 'Coop Select':
                        # pylint: disable=cyclic-import
                        from bastd.ui.coop.browser import CoopBrowserWindow
                        ba.app.ui.set_main_menu_window(
                            CoopBrowserWindow(
                                transition=None).get_root_widget())
                    else:
                        # pylint: disable=cyclic-import
                        from bastd.ui.mainmenu import MainMenuWindow
                        ba.app.ui.set_main_menu_window(
                            MainMenuWindow(transition=None).get_root_widget())

                # attempt to show any pending offers immediately.
                # If that doesn't work, try again in a few seconds
                # (we may not have heard back from the server)
                # ..if that doesn't work they'll just have to wait
                # until the next opportunity.
                if not specialoffer.show_offer():

                    def try_again() -> None:
                        if not specialoffer.show_offer():
                            # Try one last time..
                            ba.timer(2.0,
                                     specialoffer.show_offer,
                                     timetype=ba.TimeType.REAL)

                    ba.timer(2.0, try_again, timetype=ba.TimeType.REAL)
        ba.app.main_menu_did_initial_transition = True
예제 #8
0
    def _refresh(self) -> None:
        # pylint: disable=too-many-locals
        from ba.internal import (PlayerProfilesChangedMessage,
                                 get_player_profile_colors,
                                 get_player_profile_icon)
        old_selection = self._selected_profile

        # Delete old.
        while self._profile_widgets:
            self._profile_widgets.pop().delete()
        try:
            self._profiles = ba.app.config['Player Profiles']
        except Exception:
            self._profiles = {}
        assert self._profiles is not None
        items = list(self._profiles.items())
        items.sort(key=lambda x: x[0].lower())
        index = 0
        account_name: Optional[str]
        if _ba.get_account_state() == 'signed_in':
            account_name = _ba.get_account_display_string()
        else:
            account_name = None
        widget_to_select = None
        for p_name, _ in items:
            if p_name == '__account__' and account_name is None:
                continue
            color, _highlight = get_player_profile_colors(p_name)
            scl = 1.1
            txtw = ba.textwidget(
                parent=self._columnwidget,
                position=(0, 32),
                size=((self._width - 40) / scl, 28),
                text=ba.Lstr(
                    value=account_name if p_name ==
                    '__account__' else get_player_profile_icon(p_name) +
                    p_name),
                h_align='left',
                v_align='center',
                on_select_call=ba.WeakCall(self._select, p_name, index),
                maxwidth=self._scroll_width * 0.92,
                corner_scale=scl,
                color=ba.safecolor(color, 0.4),
                always_highlight=True,
                on_activate_call=ba.Call(self._edit_button.activate),
                selectable=True)
            if index == 0:
                ba.widget(edit=txtw, up_widget=self._back_button)
            ba.widget(edit=txtw, show_buffer_top=40, show_buffer_bottom=40)
            self._profile_widgets.append(txtw)

            # Select/show this one if it was previously selected
            # (but defer till after this loop since our height is
            # still changing).
            if p_name == old_selection:
                widget_to_select = txtw

            index += 1

        if widget_to_select is not None:
            ba.columnwidget(edit=self._columnwidget,
                            selected_child=widget_to_select,
                            visible_child=widget_to_select)

        # If there's a team-chooser in existence, tell it the profile-list
        # has probably changed.
        session = _ba.get_foreground_host_session()
        if session is not None:
            session.handlemessage(PlayerProfilesChangedMessage())
예제 #9
0
    def _refresh(self) -> None:
        # pylint: disable=too-many-statements
        # pylint: disable=too-many-branches
        # pylint: disable=too-many-locals
        # pylint: disable=cyclic-import
        from bastd.ui import confirm

        account_state = _ba.get_account_state()
        account_type = (_ba.get_account_type()
                        if account_state == 'signed_in' else 'unknown')

        is_google = account_type == 'Google Play'

        show_local_signed_in_as = False
        local_signed_in_as_space = 50.0

        show_signed_in_as = self._signed_in
        signed_in_as_space = 95.0

        show_sign_in_benefits = not self._signed_in
        sign_in_benefits_space = 80.0

        show_signing_in_text = account_state == 'signing_in'
        signing_in_text_space = 80.0

        show_google_play_sign_in_button = (account_state == 'signed_out'
                                           and 'Google Play'
                                           in self._show_sign_in_buttons)
        show_game_circle_sign_in_button = (account_state == 'signed_out'
                                           and 'Game Circle'
                                           in self._show_sign_in_buttons)
        show_ali_sign_in_button = (account_state == 'signed_out'
                                   and 'Ali' in self._show_sign_in_buttons)
        show_test_sign_in_button = (account_state == 'signed_out'
                                    and 'Test' in self._show_sign_in_buttons)
        show_device_sign_in_button = (account_state == 'signed_out' and 'Local'
                                      in self._show_sign_in_buttons)
        sign_in_button_space = 70.0

        show_game_service_button = (self._signed_in and account_type
                                    in ['Game Center', 'Game Circle'])
        game_service_button_space = 60.0

        show_linked_accounts_text = (self._signed_in
                                     and _ba.get_account_misc_read_val(
                                         'allowAccountLinking2', False))
        linked_accounts_text_space = 60.0

        show_achievements_button = (self._signed_in and account_type
                                    in ('Google Play', 'Alibaba', 'Local',
                                        'OUYA', 'Test'))
        achievements_button_space = 60.0

        show_achievements_text = (self._signed_in
                                  and not show_achievements_button)
        achievements_text_space = 27.0

        show_leaderboards_button = (self._signed_in and is_google)
        leaderboards_button_space = 60.0

        show_campaign_progress = self._signed_in
        campaign_progress_space = 27.0

        show_tickets = self._signed_in
        tickets_space = 27.0

        show_reset_progress_button = False
        reset_progress_button_space = 70.0

        show_player_profiles_button = self._signed_in
        player_profiles_button_space = 100.0

        show_link_accounts_button = (self._signed_in
                                     and _ba.get_account_misc_read_val(
                                         'allowAccountLinking2', False))
        link_accounts_button_space = 70.0

        show_unlink_accounts_button = show_link_accounts_button
        unlink_accounts_button_space = 90.0

        show_sign_out_button = (self._signed_in and account_type
                                in ['Test', 'Local', 'Google Play'])
        sign_out_button_space = 70.0

        if self._subcontainer is not None:
            self._subcontainer.delete()
        self._sub_height = 60.0
        if show_local_signed_in_as:
            self._sub_height += local_signed_in_as_space
        if show_signed_in_as:
            self._sub_height += signed_in_as_space
        if show_signing_in_text:
            self._sub_height += signing_in_text_space
        if show_google_play_sign_in_button:
            self._sub_height += sign_in_button_space
        if show_game_circle_sign_in_button:
            self._sub_height += sign_in_button_space
        if show_ali_sign_in_button:
            self._sub_height += sign_in_button_space
        if show_test_sign_in_button:
            self._sub_height += sign_in_button_space
        if show_device_sign_in_button:
            self._sub_height += sign_in_button_space
        if show_game_service_button:
            self._sub_height += game_service_button_space
        if show_linked_accounts_text:
            self._sub_height += linked_accounts_text_space
        if show_achievements_text:
            self._sub_height += achievements_text_space
        if show_achievements_button:
            self._sub_height += achievements_button_space
        if show_leaderboards_button:
            self._sub_height += leaderboards_button_space
        if show_campaign_progress:
            self._sub_height += campaign_progress_space
        if show_tickets:
            self._sub_height += tickets_space
        if show_sign_in_benefits:
            self._sub_height += sign_in_benefits_space
        if show_reset_progress_button:
            self._sub_height += reset_progress_button_space
        if show_player_profiles_button:
            self._sub_height += player_profiles_button_space
        if show_link_accounts_button:
            self._sub_height += link_accounts_button_space
        if show_unlink_accounts_button:
            self._sub_height += unlink_accounts_button_space
        if show_sign_out_button:
            self._sub_height += sign_out_button_space
        self._subcontainer = ba.containerwidget(parent=self._scrollwidget,
                                                size=(self._sub_width,
                                                      self._sub_height),
                                                background=False)
        ba.containerwidget(edit=self._scrollwidget,
                           claims_left_right=True,
                           claims_tab=True,
                           selection_loop_to_parent=True)
        ba.containerwidget(edit=self._subcontainer,
                           claims_left_right=True,
                           claims_tab=True,
                           selection_loop_to_parent=True)

        first_selectable = None
        v = self._sub_height - 10.0

        if show_local_signed_in_as:
            v -= local_signed_in_as_space * 0.6
            ba.textwidget(
                parent=self._subcontainer,
                position=(self._sub_width * 0.5, v),
                size=(0, 0),
                text=ba.Lstr(
                    resource='accountSettingsWindow.deviceSpecificAccountText',
                    subs=[('${NAME}', _ba.get_account_display_string())]),
                scale=0.7,
                color=(0.5, 0.5, 0.6),
                maxwidth=self._sub_width * 0.9,
                flatness=1.0,
                h_align='center',
                v_align='center')
            v -= local_signed_in_as_space * 0.4

        self._account_name_text: Optional[ba.Widget]
        if show_signed_in_as:
            v -= signed_in_as_space * 0.2
            txt = ba.Lstr(
                resource='accountSettingsWindow.youAreSignedInAsText',
                fallback_resource='accountSettingsWindow.youAreLoggedInAsText')
            ba.textwidget(parent=self._subcontainer,
                          position=(self._sub_width * 0.5, v),
                          size=(0, 0),
                          text=txt,
                          scale=0.9,
                          color=ba.app.title_color,
                          maxwidth=self._sub_width * 0.9,
                          h_align='center',
                          v_align='center')
            v -= signed_in_as_space * 0.4
            self._account_name_text = ba.textwidget(
                parent=self._subcontainer,
                position=(self._sub_width * 0.5, v),
                size=(0, 0),
                scale=1.5,
                maxwidth=self._sub_width * 0.9,
                res_scale=1.5,
                color=(1, 1, 1, 1),
                h_align='center',
                v_align='center')
            self._refresh_account_name_text()
            v -= signed_in_as_space * 0.4
        else:
            self._account_name_text = None

        if self._back_button is None:
            bbtn = _ba.get_special_widget('back_button')
        else:
            bbtn = self._back_button

        if show_sign_in_benefits:
            v -= sign_in_benefits_space
            app = ba.app
            extra: Optional[Union[str, ba.Lstr]]
            if (app.platform in ['mac', 'ios']
                    and app.subplatform == 'appstore'):
                extra = ba.Lstr(
                    value='\n${S}',
                    subs=[('${S}',
                           ba.Lstr(resource='signInWithGameCenterText'))])
            else:
                extra = ''

            ba.textwidget(parent=self._subcontainer,
                          position=(self._sub_width * 0.5,
                                    v + sign_in_benefits_space * 0.4),
                          size=(0, 0),
                          text=ba.Lstr(value='${A}${B}',
                                       subs=[('${A}',
                                              ba.Lstr(resource=self._r +
                                                      '.signInInfoText')),
                                             ('${B}', extra)]),
                          max_height=sign_in_benefits_space * 0.9,
                          scale=0.9,
                          color=(0.75, 0.7, 0.8),
                          maxwidth=self._sub_width * 0.8,
                          h_align='center',
                          v_align='center')

        if show_signing_in_text:
            v -= signing_in_text_space

            ba.textwidget(
                parent=self._subcontainer,
                position=(self._sub_width * 0.5,
                          v + signing_in_text_space * 0.5),
                size=(0, 0),
                text=ba.Lstr(resource='accountSettingsWindow.signingInText'),
                scale=0.9,
                color=(0, 1, 0),
                maxwidth=self._sub_width * 0.8,
                h_align='center',
                v_align='center')

        if show_google_play_sign_in_button:
            button_width = 350
            v -= sign_in_button_space
            self._sign_in_google_play_button = btn = ba.buttonwidget(
                parent=self._subcontainer,
                position=((self._sub_width - button_width) * 0.5, v - 20),
                autoselect=True,
                size=(button_width, 60),
                label=ba.Lstr(
                    value='${A}${B}',
                    subs=[('${A}',
                           ba.charstr(ba.SpecialChar.GOOGLE_PLAY_GAMES_LOGO)),
                          ('${B}',
                           ba.Lstr(resource=self._r +
                                   '.signInWithGooglePlayText'))]),
                on_activate_call=lambda: self._sign_in_press('Google Play'))
            if first_selectable is None:
                first_selectable = btn
            if ba.app.toolbars:
                ba.widget(edit=btn,
                          right_widget=_ba.get_special_widget('party_button'))
            ba.widget(edit=btn, left_widget=bbtn)
            ba.widget(edit=btn, show_buffer_bottom=40, show_buffer_top=100)
            self._sign_in_text = None

        if show_game_circle_sign_in_button:
            button_width = 350
            v -= sign_in_button_space
            self._sign_in_game_circle_button = btn = ba.buttonwidget(
                parent=self._subcontainer,
                position=((self._sub_width - button_width) * 0.5, v - 20),
                autoselect=True,
                size=(button_width, 60),
                label=ba.Lstr(value='${A}${B}',
                              subs=[('${A}',
                                     ba.charstr(
                                         ba.SpecialChar.GAME_CIRCLE_LOGO)),
                                    ('${B}',
                                     ba.Lstr(resource=self._r +
                                             '.signInWithGameCircleText'))]),
                on_activate_call=lambda: self._sign_in_press('Game Circle'))
            if first_selectable is None:
                first_selectable = btn
            if ba.app.toolbars:
                ba.widget(edit=btn,
                          right_widget=_ba.get_special_widget('party_button'))
            ba.widget(edit=btn, left_widget=bbtn)
            ba.widget(edit=btn, show_buffer_bottom=40, show_buffer_top=100)
            self._sign_in_text = None

        if show_ali_sign_in_button:
            button_width = 350
            v -= sign_in_button_space
            self._sign_in_ali_button = btn = ba.buttonwidget(
                parent=self._subcontainer,
                position=((self._sub_width - button_width) * 0.5, v - 20),
                autoselect=True,
                size=(button_width, 60),
                label=ba.Lstr(value='${A}${B}',
                              subs=[('${A}',
                                     ba.charstr(ba.SpecialChar.ALIBABA_LOGO)),
                                    ('${B}',
                                     ba.Lstr(resource=self._r + '.signInText'))
                                    ]),
                on_activate_call=lambda: self._sign_in_press('Ali'))
            if first_selectable is None:
                first_selectable = btn
            if ba.app.toolbars:
                ba.widget(edit=btn,
                          right_widget=_ba.get_special_widget('party_button'))
            ba.widget(edit=btn, left_widget=bbtn)
            ba.widget(edit=btn, show_buffer_bottom=40, show_buffer_top=100)
            self._sign_in_text = None

        if show_device_sign_in_button:
            button_width = 350
            v -= sign_in_button_space
            self._sign_in_device_button = btn = ba.buttonwidget(
                parent=self._subcontainer,
                position=((self._sub_width - button_width) * 0.5, v - 20),
                autoselect=True,
                size=(button_width, 60),
                label='',
                on_activate_call=lambda: self._sign_in_press('Local'))
            ba.textwidget(parent=self._subcontainer,
                          draw_controller=btn,
                          h_align='center',
                          v_align='center',
                          size=(0, 0),
                          position=(self._sub_width * 0.5, v + 17),
                          text=ba.Lstr(
                              value='${A}${B}',
                              subs=[('${A}',
                                     ba.charstr(ba.SpecialChar.LOCAL_ACCOUNT)),
                                    ('${B}',
                                     ba.Lstr(resource=self._r +
                                             '.signInWithDeviceText'))]),
                          maxwidth=button_width * 0.8,
                          color=(0.75, 1.0, 0.7))
            ba.textwidget(parent=self._subcontainer,
                          draw_controller=btn,
                          h_align='center',
                          v_align='center',
                          size=(0, 0),
                          position=(self._sub_width * 0.5, v - 4),
                          text=ba.Lstr(resource=self._r +
                                       '.signInWithDeviceInfoText'),
                          flatness=1.0,
                          scale=0.57,
                          maxwidth=button_width * 0.9,
                          color=(0.55, 0.8, 0.5))
            if first_selectable is None:
                first_selectable = btn
            if ba.app.toolbars:
                ba.widget(edit=btn,
                          right_widget=_ba.get_special_widget('party_button'))
            ba.widget(edit=btn, left_widget=bbtn)
            ba.widget(edit=btn, show_buffer_bottom=40, show_buffer_top=100)
            self._sign_in_text = None

        # Old test-account option.
        if show_test_sign_in_button:
            button_width = 350
            v -= sign_in_button_space
            self._sign_in_test_button = btn = ba.buttonwidget(
                parent=self._subcontainer,
                position=((self._sub_width - button_width) * 0.5, v - 20),
                autoselect=True,
                size=(button_width, 60),
                label='',
                on_activate_call=lambda: self._sign_in_press('Test'))
            ba.textwidget(parent=self._subcontainer,
                          draw_controller=btn,
                          h_align='center',
                          v_align='center',
                          size=(0, 0),
                          position=(self._sub_width * 0.5, v + 17),
                          text=ba.Lstr(
                              value='${A}${B}',
                              subs=[('${A}',
                                     ba.charstr(ba.SpecialChar.TEST_ACCOUNT)),
                                    ('${B}',
                                     ba.Lstr(resource=self._r +
                                             '.signInWithTestAccountText'))]),
                          maxwidth=button_width * 0.8,
                          color=(0.75, 1.0, 0.7))
            ba.textwidget(parent=self._subcontainer,
                          draw_controller=btn,
                          h_align='center',
                          v_align='center',
                          size=(0, 0),
                          position=(self._sub_width * 0.5, v - 4),
                          text=ba.Lstr(resource=self._r +
                                       '.signInWithTestAccountInfoText'),
                          flatness=1.0,
                          scale=0.57,
                          maxwidth=button_width * 0.9,
                          color=(0.55, 0.8, 0.5))
            if first_selectable is None:
                first_selectable = btn
            if ba.app.toolbars:
                ba.widget(edit=btn,
                          right_widget=_ba.get_special_widget('party_button'))
            ba.widget(edit=btn, left_widget=bbtn)
            ba.widget(edit=btn, show_buffer_bottom=40, show_buffer_top=100)
            self._sign_in_text = None

        if show_player_profiles_button:
            button_width = 300
            v -= player_profiles_button_space
            self._player_profiles_button = btn = ba.buttonwidget(
                parent=self._subcontainer,
                position=((self._sub_width - button_width) * 0.5, v + 30),
                autoselect=True,
                size=(button_width, 60),
                label=ba.Lstr(resource='playerProfilesWindow.titleText'),
                color=(0.55, 0.5, 0.6),
                icon=ba.gettexture('cuteSpaz'),
                textcolor=(0.75, 0.7, 0.8),
                on_activate_call=self._player_profiles_press)
            if first_selectable is None:
                first_selectable = btn
            if ba.app.toolbars:
                ba.widget(edit=btn,
                          right_widget=_ba.get_special_widget('party_button'))
            ba.widget(edit=btn, left_widget=bbtn, show_buffer_bottom=0)

        # the button to go to OS-Specific leaderboards/high-score-lists/etc.
        if show_game_service_button:
            button_width = 300
            v -= game_service_button_space * 0.85
            account_type = _ba.get_account_type()
            if account_type == 'Game Center':
                account_type_name = ba.Lstr(resource='gameCenterText')
            elif account_type == 'Game Circle':
                account_type_name = ba.Lstr(resource='gameCircleText')
            else:
                raise Exception("unknown account type: '" + str(account_type) +
                                "'")
            self._game_service_button = btn = ba.buttonwidget(
                parent=self._subcontainer,
                position=((self._sub_width - button_width) * 0.5, v),
                color=(0.55, 0.5, 0.6),
                textcolor=(0.75, 0.7, 0.8),
                autoselect=True,
                on_activate_call=_ba.show_online_score_ui,
                size=(button_width, 50),
                label=account_type_name)
            if first_selectable is None:
                first_selectable = btn
            if ba.app.toolbars:
                ba.widget(edit=btn,
                          right_widget=_ba.get_special_widget('party_button'))
            ba.widget(edit=btn, left_widget=bbtn)
            v -= game_service_button_space * 0.15
        else:
            self.game_service_button = None

        self._achievements_text: Optional[ba.Widget]
        if show_achievements_text:
            v -= achievements_text_space * 0.5
            self._achievements_text = ba.textwidget(
                parent=self._subcontainer,
                position=(self._sub_width * 0.5, v),
                size=(0, 0),
                scale=0.9,
                color=(0.75, 0.7, 0.8),
                maxwidth=self._sub_width * 0.8,
                h_align='center',
                v_align='center')
            v -= achievements_text_space * 0.5
        else:
            self._achievements_text = None

        self._achievements_button: Optional[ba.Widget]
        if show_achievements_button:
            button_width = 300
            v -= achievements_button_space * 0.85
            self._achievements_button = btn = ba.buttonwidget(
                parent=self._subcontainer,
                position=((self._sub_width - button_width) * 0.5, v),
                color=(0.55, 0.5, 0.6),
                textcolor=(0.75, 0.7, 0.8),
                autoselect=True,
                icon=ba.gettexture('googlePlayAchievementsIcon'
                                   if is_google else 'achievementsIcon'),
                icon_color=(0.8, 0.95, 0.7) if is_google else (0.85, 0.8, 0.9),
                on_activate_call=self._on_achievements_press,
                size=(button_width, 50),
                label='')
            if first_selectable is None:
                first_selectable = btn
            if ba.app.toolbars:
                ba.widget(edit=btn,
                          right_widget=_ba.get_special_widget('party_button'))
            ba.widget(edit=btn, left_widget=bbtn)
            v -= achievements_button_space * 0.15
        else:
            self._achievements_button = None

        if show_achievements_text or show_achievements_button:
            self._refresh_achievements()

        self._leaderboards_button: Optional[ba.Widget]
        if show_leaderboards_button:
            button_width = 300
            v -= leaderboards_button_space * 0.85
            self._leaderboards_button = btn = ba.buttonwidget(
                parent=self._subcontainer,
                position=((self._sub_width - button_width) * 0.5, v),
                color=(0.55, 0.5, 0.6),
                textcolor=(0.75, 0.7, 0.8),
                autoselect=True,
                icon=ba.gettexture('googlePlayLeaderboardsIcon'),
                icon_color=(0.8, 0.95, 0.7),
                on_activate_call=self._on_leaderboards_press,
                size=(button_width, 50),
                label=ba.Lstr(resource='leaderboardsText'))
            if first_selectable is None:
                first_selectable = btn
            if ba.app.toolbars:
                ba.widget(edit=btn,
                          right_widget=_ba.get_special_widget('party_button'))
            ba.widget(edit=btn, left_widget=bbtn)
            v -= leaderboards_button_space * 0.15
        else:
            self._leaderboards_button = None

        self._campaign_progress_text: Optional[ba.Widget]
        if show_campaign_progress:
            v -= campaign_progress_space * 0.5
            self._campaign_progress_text = ba.textwidget(
                parent=self._subcontainer,
                position=(self._sub_width * 0.5, v),
                size=(0, 0),
                scale=0.9,
                color=(0.75, 0.7, 0.8),
                maxwidth=self._sub_width * 0.8,
                h_align='center',
                v_align='center')
            v -= campaign_progress_space * 0.5
            self._refresh_campaign_progress_text()
        else:
            self._campaign_progress_text = None

        self._tickets_text: Optional[ba.Widget]
        if show_tickets:
            v -= tickets_space * 0.5
            self._tickets_text = ba.textwidget(parent=self._subcontainer,
                                               position=(self._sub_width * 0.5,
                                                         v),
                                               size=(0, 0),
                                               scale=0.9,
                                               color=(0.75, 0.7, 0.8),
                                               maxwidth=self._sub_width * 0.8,
                                               flatness=1.0,
                                               h_align='center',
                                               v_align='center')
            v -= tickets_space * 0.5
            self._refresh_tickets_text()

        else:
            self._tickets_text = None

        # bit of spacing before the reset/sign-out section
        v -= 5

        button_width = 250
        if show_reset_progress_button:
            confirm_text = (ba.Lstr(resource=self._r +
                                    '.resetProgressConfirmText')
                            if self._can_reset_achievements else ba.Lstr(
                                resource=self._r +
                                '.resetProgressConfirmNoAchievementsText'))
            v -= reset_progress_button_space
            self._reset_progress_button = btn = ba.buttonwidget(
                parent=self._subcontainer,
                position=((self._sub_width - button_width) * 0.5, v),
                color=(0.55, 0.5, 0.6),
                textcolor=(0.75, 0.7, 0.8),
                autoselect=True,
                size=(button_width, 60),
                label=ba.Lstr(resource=self._r + '.resetProgressText'),
                on_activate_call=lambda: confirm.ConfirmWindow(
                    text=confirm_text,
                    width=500,
                    height=200,
                    action=self._reset_progress))
            if first_selectable is None:
                first_selectable = btn
            if ba.app.toolbars:
                ba.widget(edit=btn,
                          right_widget=_ba.get_special_widget('party_button'))
            ba.widget(edit=btn, left_widget=bbtn)

        self._linked_accounts_text: Optional[ba.Widget]
        if show_linked_accounts_text:
            v -= linked_accounts_text_space * 0.8
            self._linked_accounts_text = ba.textwidget(
                parent=self._subcontainer,
                position=(self._sub_width * 0.5, v),
                size=(0, 0),
                scale=0.9,
                color=(0.75, 0.7, 0.8),
                maxwidth=self._sub_width * 0.95,
                h_align='center',
                v_align='center')
            v -= linked_accounts_text_space * 0.2
            self._update_linked_accounts_text()
        else:
            self._linked_accounts_text = None

        if show_link_accounts_button:
            v -= link_accounts_button_space
            self._link_accounts_button = btn = ba.buttonwidget(
                parent=self._subcontainer,
                position=((self._sub_width - button_width) * 0.5, v),
                autoselect=True,
                size=(button_width, 60),
                label='',
                color=(0.55, 0.5, 0.6),
                on_activate_call=self._link_accounts_press)
            ba.textwidget(parent=self._subcontainer,
                          draw_controller=btn,
                          h_align='center',
                          v_align='center',
                          size=(0, 0),
                          position=(self._sub_width * 0.5, v + 17 + 20),
                          text=ba.Lstr(resource=self._r + '.linkAccountsText'),
                          maxwidth=button_width * 0.8,
                          color=(0.75, 0.7, 0.8))
            ba.textwidget(parent=self._subcontainer,
                          draw_controller=btn,
                          h_align='center',
                          v_align='center',
                          size=(0, 0),
                          position=(self._sub_width * 0.5, v - 4 + 20),
                          text=ba.Lstr(resource=self._r +
                                       '.linkAccountsInfoText'),
                          flatness=1.0,
                          scale=0.5,
                          maxwidth=button_width * 0.8,
                          color=(0.75, 0.7, 0.8))
            if first_selectable is None:
                first_selectable = btn
            if ba.app.toolbars:
                ba.widget(edit=btn,
                          right_widget=_ba.get_special_widget('party_button'))
            ba.widget(edit=btn, left_widget=bbtn, show_buffer_bottom=50)

        self._unlink_accounts_button: Optional[ba.Widget]
        if show_unlink_accounts_button:
            v -= unlink_accounts_button_space
            self._unlink_accounts_button = btn = ba.buttonwidget(
                parent=self._subcontainer,
                position=((self._sub_width - button_width) * 0.5, v + 25),
                autoselect=True,
                size=(button_width, 60),
                label='',
                color=(0.55, 0.5, 0.6),
                on_activate_call=self._unlink_accounts_press)
            self._unlink_accounts_button_label = ba.textwidget(
                parent=self._subcontainer,
                draw_controller=btn,
                h_align='center',
                v_align='center',
                size=(0, 0),
                position=(self._sub_width * 0.5, v + 55),
                text=ba.Lstr(resource=self._r + '.unlinkAccountsText'),
                maxwidth=button_width * 0.8,
                color=(0.75, 0.7, 0.8))
            if first_selectable is None:
                first_selectable = btn
            if ba.app.toolbars:
                ba.widget(edit=btn,
                          right_widget=_ba.get_special_widget('party_button'))
            ba.widget(edit=btn, left_widget=bbtn, show_buffer_bottom=50)
            self._update_unlink_accounts_button()
        else:
            self._unlink_accounts_button = None

        if show_sign_out_button:
            v -= sign_out_button_space
            self._sign_out_button = btn = ba.buttonwidget(
                parent=self._subcontainer,
                position=((self._sub_width - button_width) * 0.5, v),
                size=(button_width, 60),
                label=ba.Lstr(resource=self._r + '.signOutText'),
                color=(0.55, 0.5, 0.6),
                textcolor=(0.75, 0.7, 0.8),
                autoselect=True,
                on_activate_call=self._sign_out_press)
            if first_selectable is None:
                first_selectable = btn
            if ba.app.toolbars:
                ba.widget(edit=btn,
                          right_widget=_ba.get_special_widget('party_button'))
            ba.widget(edit=btn, left_widget=bbtn, show_buffer_bottom=15)

        # Whatever the topmost selectable thing is, we want it to scroll all
        # the way up when we select it.
        if first_selectable is not None:
            ba.widget(edit=first_selectable,
                      up_widget=bbtn,
                      show_buffer_top=400)
            # (this should re-scroll us to the top..)
            ba.containerwidget(edit=self._subcontainer,
                               visible_child=first_selectable)
        self._needs_refresh = False