Ejemplo n.º 1
0
    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_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'])
Ejemplo n.º 2
0
                def instantiate(self, scrollwidget: ba.Widget,
                                tab_button: ba.Widget) -> None:
                    """Create the store."""
                    # pylint: disable=too-many-locals
                    # pylint: disable=too-many-branches
                    # pylint: disable=too-many-nested-blocks
                    from bastd.ui.store import item as storeitemui
                    title_spacing = 40
                    button_border = 20
                    button_spacing = 4
                    boffs_h = 40
                    self._height = 80.0

                    # Calc total height.
                    for i, section in enumerate(self._sections):
                        if section['title'] != '':
                            assert self._height is not None
                            self._height += title_spacing
                        b_width, b_height = section['button_size']
                        b_column_count = int(
                            math.floor((self._width - boffs_h - 20) /
                                       (b_width + button_spacing)))
                        b_row_count = int(
                            math.ceil(
                                float(len(section['items'])) / b_column_count))
                        b_height_total = (
                            2 * button_border + b_row_count * b_height +
                            (b_row_count - 1) * section['v_spacing'])
                        self._height += b_height_total

                    assert self._height is not None
                    cnt2 = ba.containerwidget(parent=scrollwidget,
                                              scale=1.0,
                                              size=(self._width, self._height),
                                              background=False)
                    ba.containerwidget(edit=cnt2,
                                       claims_left_right=True,
                                       claims_tab=True,
                                       selection_loop_to_parent=True)
                    v = self._height - 20

                    if self._tab == 'characters':
                        txt = ba.Lstr(
                            resource='store.howToSwitchCharactersText',
                            subs=[
                                ('${SETTINGS}',
                                 ba.Lstr(
                                     resource='accountSettingsWindow.titleText'
                                 )),
                                ('${PLAYER_PROFILES}',
                                 ba.Lstr(
                                     resource='playerProfilesWindow.titleText')
                                 )
                            ])
                        ba.textwidget(parent=cnt2,
                                      text=txt,
                                      size=(0, 0),
                                      position=(self._width * 0.5,
                                                self._height - 28),
                                      h_align='center',
                                      v_align='center',
                                      color=(0.7, 1, 0.7, 0.4),
                                      scale=0.7,
                                      shadow=0,
                                      flatness=1.0,
                                      maxwidth=700,
                                      transition_delay=0.4)
                    elif self._tab == 'icons':
                        txt = ba.Lstr(
                            resource='store.howToUseIconsText',
                            subs=[
                                ('${SETTINGS}',
                                 ba.Lstr(resource='mainMenu.settingsText')),
                                ('${PLAYER_PROFILES}',
                                 ba.Lstr(
                                     resource='playerProfilesWindow.titleText')
                                 )
                            ])
                        ba.textwidget(parent=cnt2,
                                      text=txt,
                                      size=(0, 0),
                                      position=(self._width * 0.5,
                                                self._height - 28),
                                      h_align='center',
                                      v_align='center',
                                      color=(0.7, 1, 0.7, 0.4),
                                      scale=0.7,
                                      shadow=0,
                                      flatness=1.0,
                                      maxwidth=700,
                                      transition_delay=0.4)
                    elif self._tab == 'maps':
                        assert self._width is not None
                        assert self._height is not None
                        txt = ba.Lstr(resource='store.howToUseMapsText')
                        ba.textwidget(parent=cnt2,
                                      text=txt,
                                      size=(0, 0),
                                      position=(self._width * 0.5,
                                                self._height - 28),
                                      h_align='center',
                                      v_align='center',
                                      color=(0.7, 1, 0.7, 0.4),
                                      scale=0.7,
                                      shadow=0,
                                      flatness=1.0,
                                      maxwidth=700,
                                      transition_delay=0.4)

                    prev_row_buttons: Optional[List] = None
                    this_row_buttons = []

                    delay = 0.3
                    for section in self._sections:
                        if section['title'] != '':
                            ba.textwidget(
                                parent=cnt2,
                                position=(60, v - title_spacing * 0.8),
                                size=(0, 0),
                                scale=1.0,
                                transition_delay=delay,
                                color=(0.7, 0.9, 0.7, 1),
                                h_align='left',
                                v_align='center',
                                text=ba.Lstr(resource=section['title']),
                                maxwidth=self._width * 0.7)
                            v -= title_spacing
                        delay = max(0.100, delay - 0.100)
                        v -= button_border
                        b_width, b_height = section['button_size']
                        b_count = len(section['items'])
                        b_column_count = int(
                            math.floor((self._width - boffs_h - 20) /
                                       (b_width + button_spacing)))
                        col = 0
                        item: Dict[str, Any]
                        assert self._store_window.button_infos is not None
                        for i, item_name in enumerate(section['items']):
                            item = self._store_window.button_infos[
                                item_name] = {}
                            item['call'] = ba.WeakCall(self._store_window.buy,
                                                       item_name)
                            if 'x_offs' in section:
                                boffs_h2 = section['x_offs']
                            else:
                                boffs_h2 = 0

                            if 'y_offs' in section:
                                boffs_v2 = section['y_offs']
                            else:
                                boffs_v2 = 0
                            b_pos = (boffs_h + boffs_h2 +
                                     (b_width + button_spacing) * col,
                                     v - b_height + boffs_v2)
                            storeitemui.instantiate_store_item_display(
                                item_name,
                                item,
                                parent_widget=cnt2,
                                b_pos=b_pos,
                                boffs_h=boffs_h,
                                b_width=b_width,
                                b_height=b_height,
                                boffs_h2=boffs_h2,
                                boffs_v2=boffs_v2,
                                delay=delay)
                            btn = item['button']
                            delay = max(0.1, delay - 0.1)
                            this_row_buttons.append(btn)

                            # Wire this button to the equivalent in the
                            # previous row.
                            if prev_row_buttons is not None:
                                # pylint: disable=unsubscriptable-object
                                if len(prev_row_buttons) > col:
                                    ba.widget(edit=btn,
                                              up_widget=prev_row_buttons[col])
                                    ba.widget(edit=prev_row_buttons[col],
                                              down_widget=btn)

                                    # If we're the last button in our row,
                                    # wire any in the previous row past
                                    # our position to go to us if down is
                                    # pressed.
                                    if (col + 1 == b_column_count
                                            or i == b_count - 1):
                                        for b_prev in prev_row_buttons[col +
                                                                       1:]:
                                            ba.widget(edit=b_prev,
                                                      down_widget=btn)
                                else:
                                    ba.widget(edit=btn,
                                              up_widget=prev_row_buttons[-1])
                            else:
                                ba.widget(edit=btn, up_widget=tab_button)

                            col += 1
                            if col == b_column_count or i == b_count - 1:
                                prev_row_buttons = this_row_buttons
                                this_row_buttons = []
                                col = 0
                                v -= b_height
                                if i < b_count - 1:
                                    v -= section['v_spacing']

                        v -= button_border

                    # Set a timer to update these buttons periodically as long
                    # as we're alive (so if we buy one it will grey out, etc).
                    self._store_window.update_buttons_timer = ba.Timer(
                        0.5,
                        ba.WeakCall(self._store_window.update_buttons),
                        repeat=True,
                        timetype=ba.TimeType.REAL)

                    # Also update them immediately.
                    self._store_window.update_buttons()
Ejemplo n.º 3
0
    def __init__(self,
                 items: List[str],
                 transition: str = 'in_right',
                 header_text: ba.Lstr = None):
        from ba.internal import get_store_item_display_size
        from bastd.ui.store import item as storeitemui
        if header_text is None:
            header_text = ba.Lstr(resource='unlockThisText',
                                  fallback_resource='unlockThisInTheStoreText')
        if len(items) != 1:
            raise ValueError('expected exactly 1 item')
        self._items = list(items)
        self._width = 580
        self._height = 520
        uiscale = ba.app.ui.uiscale
        super().__init__(root_widget=ba.containerwidget(
            size=(self._width, self._height),
            transition=transition,
            toolbar_visibility='menu_currency',
            scale=(1.2 if uiscale is ba.UIScale.SMALL else
                   1.1 if uiscale is ba.UIScale.MEDIUM else 1.0),
            stack_offset=(0, -15) if uiscale is ba.UIScale.SMALL else (0, 0)))
        self._is_double = False
        self._title_text = ba.textwidget(parent=self._root_widget,
                                         position=(self._width * 0.5,
                                                   self._height - 30),
                                         size=(0, 0),
                                         text=header_text,
                                         h_align='center',
                                         v_align='center',
                                         maxwidth=self._width * 0.9 - 120,
                                         scale=1.2,
                                         color=(1, 0.8, 0.3, 1))
        size = get_store_item_display_size(items[0])
        display: Dict[str, Any] = {}
        storeitemui.instantiate_store_item_display(
            items[0],
            display,
            parent_widget=self._root_widget,
            b_pos=(self._width * 0.5 - size[0] * 0.5 + 10 -
                   ((size[0] * 0.5 + 30) if self._is_double else 0),
                   self._height * 0.5 - size[1] * 0.5 + 30 +
                   (20 if self._is_double else 0)),
            b_width=size[0],
            b_height=size[1],
            button=False)

        # Wire up the parts we need.
        if self._is_double:
            pass  # not working
        else:
            if self._items == ['pro']:
                price_str = _ba.get_price(self._items[0])
                pyoffs = -15
            else:
                pyoffs = 0
                price = self._price = _ba.get_account_misc_read_val(
                    'price.' + str(items[0]), -1)
                price_str = ba.charstr(ba.SpecialChar.TICKET) + str(price)
            self._price_text = ba.textwidget(parent=self._root_widget,
                                             position=(self._width * 0.5,
                                                       150 + pyoffs),
                                             size=(0, 0),
                                             text=price_str,
                                             h_align='center',
                                             v_align='center',
                                             maxwidth=self._width * 0.9,
                                             scale=1.4,
                                             color=(0.2, 1, 0.2))

        self._update_timer = ba.Timer(1.0,
                                      ba.WeakCall(self._update),
                                      timetype=ba.TimeType.REAL,
                                      repeat=True)

        self._cancel_button = ba.buttonwidget(
            parent=self._root_widget,
            position=(50, 40),
            size=(150, 60),
            scale=1.0,
            on_activate_call=self._cancel,
            autoselect=True,
            label=ba.Lstr(resource='cancelText'))
        self._purchase_button = ba.buttonwidget(
            parent=self._root_widget,
            position=(self._width - 200, 40),
            size=(150, 60),
            scale=1.0,
            on_activate_call=self._purchase,
            autoselect=True,
            label=ba.Lstr(resource='store.purchaseText'))

        ba.containerwidget(edit=self._root_widget,
                           cancel_button=self._cancel_button,
                           start_button=self._purchase_button,
                           selected_child=self._purchase_button)