Ejemplo n.º 1
0
def have_pro() -> bool:
    """Return whether pro is currently unlocked."""

    # Check our tickets-based pro upgrade and our two real-IAP based upgrades.
    return bool(
        _ba.get_purchased('upgrades.pro') or _ba.get_purchased('static.pro')
        or _ba.get_purchased('static.pro_sale'))
Ejemplo n.º 2
0
def have_pro() -> bool:
    """Return whether pro is currently unlocked."""

    # Check our tickets-based pro upgrade and our two real-IAP based upgrades.
    # Also unlock this stuff in ballistica-core builds.
    return bool(
        _ba.get_purchased('upgrades.pro') or _ba.get_purchased('static.pro')
        or _ba.get_purchased('static.pro_sale')
        or 'ballistica' + 'core' == 'ballisticacore')
Ejemplo n.º 3
0
    def _on_ad_complete(self, actually_showed: bool) -> None:

        # Make sure any transactions the ad added got locally applied
        # (rewards added, etc.).
        _ba.run_transactions()

        # If we're already entering the tourney, ignore.
        if self._entering:
            return

        if not actually_showed:
            return

        # This should have awarded us the tournament_entry_ad purchase;
        # make sure that's present.
        # (otherwise the server will ignore our tournament entry anyway)
        if not _ba.get_purchased('tournament_entry_ad'):
            print('no tournament_entry_ad purchase present in _on_ad_complete')
            ba.screenmessage(ba.Lstr(resource='errorText'), color=(1, 0, 0))
            ba.playsound(ba.getsound('error'))
            return

        self._entering = True
        _ba.add_transaction({
            'type': 'ENTER_TOURNAMENT',
            'fee': 'ad',
            'tournamentID': self._tournament_id
        })
        _ba.run_transactions()
        self._launch()
Ejemplo n.º 4
0
def _calc_count_for_tab(tabval: List[Dict[str, Any]], our_tickets: int,
                        count: int) -> int:
    for section in tabval:
        for item in section['items']:
            ticket_cost = _ba.get_account_misc_read_val('price.' + item, None)
            if ticket_cost is not None:
                if (our_tickets >= ticket_cost
                        and not _ba.get_purchased(item)):
                    count += 1
    return count
Ejemplo n.º 5
0
    def _on_cancel(self) -> None:

        # Don't allow canceling for several seconds after poking an enter
        # button if it looks like we're waiting on a purchase or entering
        # the tournament.
        if ((ba.time(ba.TimeType.REAL, ba.TimeFormat.MILLISECONDS) -
             self._last_ticket_press_time < 6000) and
            (_ba.have_outstanding_transactions()
             or _ba.get_purchased(self._purchase_name) or self._entering)):
            ba.playsound(ba.getsound('error'))
            return
        self._transition_out()
Ejemplo n.º 6
0
def get_purchased_icons() -> List[str]:
    """(internal)"""
    # pylint: disable=cyclic-import
    from ba import _store
    if _ba.get_account_state() != 'signed_in':
        return []
    icons = []
    store_items = _store.get_store_items()
    for item_name, item in list(store_items.items()):
        if item_name.startswith('icons.') and _ba.get_purchased(item_name):
            icons.append(item['icon'])
    return icons
Ejemplo n.º 7
0
    def _update(self) -> None:
        can_die = False

        # We go away if we see that our target item is owned.
        if self._items == ['pro']:
            if ba.app.accounts.have_pro():
                can_die = True
        else:
            if _ba.get_purchased(self._items[0]):
                can_die = True

        if can_die:
            ba.containerwidget(edit=self._root_widget, transition='out_left')
Ejemplo n.º 8
0
def get_unowned_maps() -> List[str]:
    """Return the list of local maps not owned by the current account.

    Category: Asset Functions
    """
    from ba import _store
    unowned_maps: Set[str] = set()
    if not _ba.app.headless_build:
        for map_section in _store.get_store_layout()['maps']:
            for mapitem in map_section['items']:
                if not _ba.get_purchased(mapitem):
                    m_info = _store.get_store_item(mapitem)
                    unowned_maps.add(m_info['map_type'].name)
    return sorted(unowned_maps)
Ejemplo n.º 9
0
def get_unowned_game_types() -> Set[Type[ba.GameActivity]]:
    """Return present game types not owned by the current account."""
    try:
        from ba import _store
        unowned_games: Set[Type[ba.GameActivity]] = set()
        if not _ba.app.headless_build:
            for section in _store.get_store_layout()['minigames']:
                for mname in section['items']:
                    if not _ba.get_purchased(mname):
                        m_info = _store.get_store_item(mname)
                        unowned_games.add(m_info['gametype'])
        return unowned_games
    except Exception:
        from ba import _error
        _error.print_exception('error calcing un-owned games')
        return set()
Ejemplo n.º 10
0
    def _update(self) -> None:

        # If we've got seconds left on our countdown, update it.
        if self._cancel_delay > 0:
            self._cancel_delay = max(0, self._cancel_delay - 1)
            self._update_cancel_button_graphics()

        can_die = False

        # We go away if we see that our target item is owned.
        if self._offer_item == 'pro':
            if _ba.app.accounts.have_pro():
                can_die = True
        else:
            if _ba.get_purchased(self._offer_item):
                can_die = True

        if can_die:
            self._transition_out('out_left')
Ejemplo n.º 11
0
    def update_buttons(self) -> None:
        """Update our buttons."""
        # pylint: disable=too-many-statements
        # pylint: disable=too-many-branches
        # pylint: disable=too-many-locals
        from ba.internal import have_pro, get_available_sale_time
        from ba import SpecialChar
        if not self._root_widget:
            return
        import datetime
        sales_raw = _ba.get_account_misc_read_val('sales', {})
        sales = {}
        try:
            # Look at the current set of sales; filter any with time remaining.
            for sale_item, sale_info in list(sales_raw.items()):
                to_end = (datetime.datetime.utcfromtimestamp(sale_info['e']) -
                          datetime.datetime.utcnow()).total_seconds()
                if to_end > 0:
                    sales[sale_item] = {
                        'to_end': to_end,
                        'original_price': sale_info['op']
                    }
        except Exception:
            ba.print_exception('Error parsing sales.')

        assert self.button_infos is not None
        for b_type, b_info in self.button_infos.items():

            if b_type in ['upgrades.pro', 'pro']:
                purchased = have_pro()
            else:
                purchased = _ba.get_purchased(b_type)

            sale_opacity = 0.0
            sale_title_text: Union[str, ba.Lstr] = ''
            sale_time_text: Union[str, ba.Lstr] = ''

            if purchased:
                title_color = (0.8, 0.7, 0.9, 1.0)
                color = (0.63, 0.55, 0.78)
                extra_image_opacity = 0.5
                call = ba.WeakCall(self._print_already_own, b_info['name'])
                price_text = ''
                price_text_left = ''
                price_text_right = ''
                show_purchase_check = True
                description_color: Sequence[float] = (0.4, 1.0, 0.4, 0.4)
                description_color2: Sequence[float] = (0.0, 0.0, 0.0, 0.0)
                price_color = (0.5, 1, 0.5, 0.3)
            else:
                title_color = (0.7, 0.9, 0.7, 1.0)
                color = (0.4, 0.8, 0.1)
                extra_image_opacity = 1.0
                call = b_info['call'] if 'call' in b_info else None
                if b_type in ['upgrades.pro', 'pro']:
                    sale_time = get_available_sale_time('extras')
                    if sale_time is not None:
                        priceraw = _ba.get_price('pro')
                        price_text_left = (priceraw
                                           if priceraw is not None else '?')
                        priceraw = _ba.get_price('pro_sale')
                        price_text_right = (priceraw
                                            if priceraw is not None else '?')
                        sale_opacity = 1.0
                        price_text = ''
                        sale_title_text = ba.Lstr(resource='store.saleText')
                        sale_time_text = ba.timestring(
                            sale_time,
                            centi=False,
                            timeformat=ba.TimeFormat.MILLISECONDS)
                    else:
                        priceraw = _ba.get_price('pro')
                        price_text = priceraw if priceraw is not None else '?'
                        price_text_left = ''
                        price_text_right = ''
                else:
                    price = _ba.get_account_misc_read_val('price.' + b_type, 0)

                    # Color the button differently if we cant afford this.
                    if _ba.get_account_state() == 'signed_in':
                        if _ba.get_account_ticket_count() < price:
                            color = (0.6, 0.61, 0.6)
                    price_text = ba.charstr(ba.SpecialChar.TICKET) + str(
                        _ba.get_account_misc_read_val('price.' + b_type, '?'))
                    price_text_left = ''
                    price_text_right = ''

                    # TESTING:
                    if b_type in sales:
                        sale_opacity = 1.0
                        price_text_left = ba.charstr(SpecialChar.TICKET) + str(
                            sales[b_type]['original_price'])
                        price_text_right = price_text
                        price_text = ''
                        sale_title_text = ba.Lstr(resource='store.saleText')
                        sale_time_text = ba.timestring(
                            int(sales[b_type]['to_end'] * 1000),
                            centi=False,
                            timeformat=ba.TimeFormat.MILLISECONDS)

                description_color = (0.5, 1.0, 0.5)
                description_color2 = (0.3, 1.0, 1.0)
                price_color = (0.2, 1, 0.2, 1.0)
                show_purchase_check = False

            if 'title_text' in b_info:
                ba.textwidget(edit=b_info['title_text'], color=title_color)
            if 'purchase_check' in b_info:
                ba.imagewidget(edit=b_info['purchase_check'],
                               opacity=1.0 if show_purchase_check else 0.0)
            if 'price_widget' in b_info:
                ba.textwidget(edit=b_info['price_widget'],
                              text=price_text,
                              color=price_color)
            if 'price_widget_left' in b_info:
                ba.textwidget(edit=b_info['price_widget_left'],
                              text=price_text_left)
            if 'price_widget_right' in b_info:
                ba.textwidget(edit=b_info['price_widget_right'],
                              text=price_text_right)
            if 'price_slash_widget' in b_info:
                ba.imagewidget(edit=b_info['price_slash_widget'],
                               opacity=sale_opacity)
            if 'sale_bg_widget' in b_info:
                ba.imagewidget(edit=b_info['sale_bg_widget'],
                               opacity=sale_opacity)
            if 'sale_title_widget' in b_info:
                ba.textwidget(edit=b_info['sale_title_widget'],
                              text=sale_title_text)
            if 'sale_time_widget' in b_info:
                ba.textwidget(edit=b_info['sale_time_widget'],
                              text=sale_time_text)
            if 'button' in b_info:
                ba.buttonwidget(edit=b_info['button'],
                                color=color,
                                on_activate_call=call)
            if 'extra_backings' in b_info:
                for bck in b_info['extra_backings']:
                    ba.imagewidget(edit=bck,
                                   color=color,
                                   opacity=extra_image_opacity)
            if 'extra_images' in b_info:
                for img in b_info['extra_images']:
                    ba.imagewidget(edit=img, opacity=extra_image_opacity)
            if 'extra_texts' in b_info:
                for etxt in b_info['extra_texts']:
                    ba.textwidget(edit=etxt, color=description_color)
            if 'extra_texts_2' in b_info:
                for etxt in b_info['extra_texts_2']:
                    ba.textwidget(edit=etxt, color=description_color2)
            if 'descriptionText' in b_info:
                ba.textwidget(edit=b_info['descriptionText'],
                              color=description_color)
Ejemplo n.º 12
0
    def _update(self) -> None:
        # pylint: disable=too-many-branches
        from ba import SpecialChar, TimeFormat
        from ba.internal import (get_available_sale_time,
                                 get_available_purchase_count)
        if not self._button:
            return  # Our instance may outlive our UI objects.

        if self._ticket_text is not None:
            if _ba.get_account_state() == 'signed_in':
                sval = ba.charstr(SpecialChar.TICKET) + str(
                    _ba.get_account_ticket_count())
            else:
                sval = '-'
            ba.textwidget(edit=self._ticket_text, text=sval)
        available_purchases = get_available_purchase_count()

        # Old pro sale stuff..
        sale_time = get_available_sale_time('extras')

        # ..also look for new style sales.
        if sale_time is None:
            import datetime
            sales_raw = _ba.get_account_misc_read_val('sales', {})
            sale_times = []
            try:
                # Look at the current set of sales; filter any with time
                # remaining that we don't own.
                for sale_item, sale_info in list(sales_raw.items()):
                    if not _ba.get_purchased(sale_item):
                        to_end = (datetime.datetime.utcfromtimestamp(
                            sale_info['e']) -
                                  datetime.datetime.utcnow()).total_seconds()
                        if to_end > 0:
                            sale_times.append(to_end)
            except Exception:
                ba.print_exception('Error parsing sales')
            if sale_times:
                sale_time = int(min(sale_times) * 1000)

        if sale_time is not None:
            ba.textwidget(edit=self._sale_title_text,
                          text=ba.Lstr(resource='store.saleText'))
            ba.textwidget(edit=self._sale_time_text,
                          text=ba.timestring(
                              sale_time,
                              centi=False,
                              timeformat=TimeFormat.MILLISECONDS))
            ba.imagewidget(edit=self._sale_backing, opacity=1.0)
            ba.imagewidget(edit=self._available_purchase_backing, opacity=1.0)
            ba.textwidget(edit=self._available_purchase_text, text='')
            ba.imagewidget(edit=self._available_purchase_backing, opacity=0.0)
        else:
            ba.imagewidget(edit=self._sale_backing, opacity=0.0)
            ba.textwidget(edit=self._sale_time_text, text='')
            ba.textwidget(edit=self._sale_title_text, text='')
            if available_purchases > 0:
                ba.textwidget(edit=self._available_purchase_text,
                              text=str(available_purchases))
                ba.imagewidget(edit=self._available_purchase_backing,
                               opacity=1.0)
            else:
                ba.textwidget(edit=self._available_purchase_text, text='')
                ba.imagewidget(edit=self._available_purchase_backing,
                               opacity=0.0)
Ejemplo n.º 13
0
def get_available_sale_time(tab: str) -> Optional[int]:
    """(internal)"""
    # pylint: disable=too-many-branches
    # pylint: disable=too-many-nested-blocks
    # pylint: disable=too-many-locals
    try:
        import datetime
        from ba._generated.enums import TimeType, TimeFormat
        app = _ba.app
        sale_times: List[Optional[int]] = []

        # Calc time for our pro sale (old special case).
        if tab == 'extras':
            config = app.config
            if app.accounts.have_pro():
                return None

            # If we haven't calced/loaded start times yet.
            if app.pro_sale_start_time is None:

                # If we've got a time-remaining in our config, start there.
                if 'PSTR' in config:
                    app.pro_sale_start_time = int(
                        _ba.time(TimeType.REAL, TimeFormat.MILLISECONDS))
                    app.pro_sale_start_val = config['PSTR']
                else:

                    # We start the timer once we get the duration from
                    # the server.
                    start_duration = _ba.get_account_misc_read_val(
                        'proSaleDurationMinutes', None)
                    if start_duration is not None:
                        app.pro_sale_start_time = int(
                            _ba.time(TimeType.REAL, TimeFormat.MILLISECONDS))
                        app.pro_sale_start_val = (60000 * start_duration)

                    # If we haven't heard from the server yet, no sale..
                    else:
                        return None

            assert app.pro_sale_start_val is not None
            val: Optional[int] = max(
                0, app.pro_sale_start_val -
                (_ba.time(TimeType.REAL, TimeFormat.MILLISECONDS) -
                 app.pro_sale_start_time))

            # Keep the value in the config up to date. I suppose we should
            # write the config occasionally but it should happen often enough
            # for other reasons.
            config['PSTR'] = val
            if val == 0:
                val = None
            sale_times.append(val)

        # Now look for sales in this tab.
        sales_raw = _ba.get_account_misc_read_val('sales', {})
        store_layout = get_store_layout()
        for section in store_layout[tab]:
            for item in section['items']:
                if item in sales_raw:
                    if not _ba.get_purchased(item):
                        to_end = ((datetime.datetime.utcfromtimestamp(
                            sales_raw[item]['e']) -
                                   datetime.datetime.utcnow()).total_seconds())
                        if to_end > 0:
                            sale_times.append(int(to_end * 1000))

        # Return the smallest time I guess?
        sale_times_int = [t for t in sale_times if isinstance(t, int)]
        return min(sale_times_int) if sale_times_int else None

    except Exception:
        from ba import _error
        _error.print_exception('error calcing sale time')
        return None
Ejemplo n.º 14
0
def get_appearances(include_locked: bool = False) -> List[str]:
    """Get the list of available spaz appearances."""
    # pylint: disable=too-many-statements
    # pylint: disable=too-many-branches
    disallowed = []
    if not include_locked:
        # hmm yeah this'll be tough to hack...
        if not _ba.get_purchased('characters.santa'):
            disallowed.append('Santa Claus')
        if not _ba.get_purchased('characters.frosty'):
            disallowed.append('Frosty')
        if not _ba.get_purchased('characters.bones'):
            disallowed.append('Bones')
        if not _ba.get_purchased('characters.bernard'):
            disallowed.append('Bernard')
        if not _ba.get_purchased('characters.pixie'):
            disallowed.append('Pixel')
        if not _ba.get_purchased('characters.pascal'):
            disallowed.append('Pascal')
        if not _ba.get_purchased('characters.actionhero'):
            disallowed.append('Todd McBurton')
        if not _ba.get_purchased('characters.taobaomascot'):
            disallowed.append('Taobao Mascot')
        if not _ba.get_purchased('characters.agent'):
            disallowed.append('Agent Johnson')
        if not _ba.get_purchased('characters.jumpsuit'):
            disallowed.append('Lee')
        if not _ba.get_purchased('characters.assassin'):
            disallowed.append('Zola')
        if not _ba.get_purchased('characters.wizard'):
            disallowed.append('Grumbledorf')
        if not _ba.get_purchased('characters.cowboy'):
            disallowed.append('Butch')
        if not _ba.get_purchased('characters.witch'):
            disallowed.append('Witch')
        if not _ba.get_purchased('characters.warrior'):
            disallowed.append('Warrior')
        if not _ba.get_purchased('characters.superhero'):
            disallowed.append('Middle-Man')
        if not _ba.get_purchased('characters.alien'):
            disallowed.append('Alien')
        if not _ba.get_purchased('characters.oldlady'):
            disallowed.append('OldLady')
        if not _ba.get_purchased('characters.gladiator'):
            disallowed.append('Gladiator')
        if not _ba.get_purchased('characters.wrestler'):
            disallowed.append('Wrestler')
        if not _ba.get_purchased('characters.operasinger'):
            disallowed.append('Gretel')
        if not _ba.get_purchased('characters.robot'):
            disallowed.append('Robot')
        if not _ba.get_purchased('characters.cyborg'):
            disallowed.append('B-9000')
        if not _ba.get_purchased('characters.bunny'):
            disallowed.append('Easter Bunny')
        if not _ba.get_purchased('characters.kronk'):
            disallowed.append('Kronk')
        if not _ba.get_purchased('characters.zoe'):
            disallowed.append('Zoe')
        if not _ba.get_purchased('characters.jackmorgan'):
            disallowed.append('Jack Morgan')
        if not _ba.get_purchased('characters.mel'):
            disallowed.append('Mel')
        if not _ba.get_purchased('characters.snakeshadow'):
            disallowed.append('Snake Shadow')
    return [
        s for s in list(ba.app.spaz_appearances.keys()) if s not in disallowed
    ]
Ejemplo n.º 15
0
    def _update(self) -> None:
        # pylint: disable=too-many-boolean-expressions
        from ba.internal import have_pro, getcampaign
        game = self._game
        campaignname, levelname = game.split(':')

        # Hack - The Last Stand doesn't actually exist in the
        # easy tourney; we just want it for display purposes. Map it to
        # the hard-mode version.
        if game == 'Easy:The Last Stand':
            campaignname = 'Default'

        campaign = getcampaign(campaignname)

        # If this campaign is sequential, make sure we've unlocked
        # everything up to here.
        unlocked = True
        if campaign.sequential:
            for level in campaign.levels:
                if level.name == levelname:
                    break
                if not level.complete:
                    unlocked = False
                    break

        # We never actually allow playing last-stand on easy mode.
        if game == 'Easy:The Last Stand':
            unlocked = False

        # Hard-code games we haven't unlocked.
        if ((game in ('Challenges:Infinite Runaround',
                      'Challenges:Infinite Onslaught') and not have_pro())
                or (game in ('Challenges:Meteor Shower', )
                    and not _ba.get_purchased('games.meteor_shower'))
                or (game in ('Challenges:Target Practice',
                             'Challenges:Target Practice B')
                    and not _ba.get_purchased('games.target_practice'))
                or (game in ('Challenges:Ninja Fight', )
                    and not _ba.get_purchased('games.ninja_fight'))
                or (game in ('Challenges:Pro Ninja Fight', )
                    and not _ba.get_purchased('games.ninja_fight'))
                or (game in ('Challenges:Easter Egg Hunt',
                             'Challenges:Pro Easter Egg Hunt')
                    and not _ba.get_purchased('games.easter_egg_hunt'))):
            unlocked = False

        # Let's tint levels a slightly different color when easy mode
        # is selected.
        unlocked_color = (0.85, 0.95,
                          0.5) if game.startswith('Easy:') else (0.5, 0.7, 0.2)

        ba.buttonwidget(edit=self._button,
                        color=unlocked_color if unlocked else (0.5, 0.5, 0.5))

        ba.imagewidget(edit=self._lock_widget,
                       opacity=0.0 if unlocked else 1.0)
        ba.imagewidget(edit=self._preview_widget,
                       opacity=1.0 if unlocked else 0.3)
        ba.textwidget(edit=self._name_widget,
                      color=(0.8, 1.0, 0.8, 1.0) if unlocked else
                      (0.7, 0.7, 0.7, 0.7))
        for widget in self._star_widgets:
            ba.imagewidget(edit=widget,
                           opacity=1.0 if unlocked else 0.3,
                           color=(2.2, 1.2, 0.3) if unlocked else (1, 1, 1))
        for i, ach in enumerate(self._achievements):
            a_complete = ach.complete
            ba.imagewidget(edit=self._achievement_widgets[i][0],
                           opacity=1.0 if (a_complete and unlocked) else 0.3)
            ba.imagewidget(edit=self._achievement_widgets[i][1],
                           opacity=(1.0 if (a_complete and unlocked) else
                                    0.2 if a_complete else 0.0))