From 7535e0807bafd733e9e6e3597b59b76e3bcebaf4 Mon Sep 17 00:00:00 2001 From: Eric Froemling Date: Fri, 16 Oct 2020 14:26:30 -0700 Subject: [PATCH] Tidied up AccountSubsystem --- .efrocachemap | 40 +- assets/src/ba_data/python/ba/_account.py | 366 ++++++++++-------- assets/src/ba_data/python/ba/_achievement.py | 5 +- assets/src/ba_data/python/ba/_app.py | 44 +-- assets/src/ba_data/python/ba/_apputils.py | 3 +- assets/src/ba_data/python/ba/_gameactivity.py | 5 +- assets/src/ba_data/python/ba/_lobby.py | 3 +- assets/src/ba_data/python/ba/_store.py | 3 +- assets/src/ba_data/python/ba/internal.py | 6 - .../python/bastd/activity/coopscore.py | 5 +- .../ba_data/python/bastd/ui/colorpicker.py | 7 +- .../ba_data/python/bastd/ui/coop/browser.py | 39 +- .../python/bastd/ui/coop/gamebutton.py | 5 +- assets/src/ba_data/python/bastd/ui/gather.py | 3 +- .../src/ba_data/python/bastd/ui/iconpicker.py | 4 +- .../python/bastd/ui/league/rankbutton.py | 9 +- .../python/bastd/ui/league/rankwindow.py | 23 +- .../bastd/ui/playlist/customizebrowser.py | 50 ++- .../ba_data/python/bastd/ui/playoptions.py | 23 +- .../python/bastd/ui/profile/browser.py | 6 +- .../src/ba_data/python/bastd/ui/purchase.py | 3 +- .../python/bastd/ui/soundtrack/browser.py | 53 ++- .../ba_data/python/bastd/ui/specialoffer.py | 3 +- .../ba_data/python/bastd/ui/store/browser.py | 4 +- .../python/bastd/ui/tournamententry.py | 24 +- src/generated_src/ballistica/binding.py | 2 +- 26 files changed, 355 insertions(+), 383 deletions(-) diff --git a/.efrocachemap b/.efrocachemap index 686b8d08..dae4e975 100644 --- a/.efrocachemap +++ b/.efrocachemap @@ -3932,24 +3932,24 @@ "assets/build/windows/Win32/ucrtbased.dll": "https://files.ballistica.net/cache/ba1/b5/85/f8b6d0558ddb87267f34254b1450", "assets/build/windows/Win32/vc_redist.x86.exe": "https://files.ballistica.net/cache/ba1/1c/e1/4a1a2eddda2f4aebd5f8b64ab08e", "assets/build/windows/Win32/vcruntime140d.dll": "https://files.ballistica.net/cache/ba1/50/8d/bc2600ac9491f1b14d659709451f", - "build/prefab/full/linux_x86_64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/4c/74/73bd143107ea1bc1cc150cf1d9a0", - "build/prefab/full/linux_x86_64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/d8/1f/b71688ce17abe2f1750d3b98c1a3", - "build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/7b/f5/0fb038ef5e8c9cb22f8487213622", - "build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/a7/22/7582b2dc2eb28e8c9b05c7860c83", - "build/prefab/full/mac_x86_64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/01/6a/6787038c5a2fe07be6dd08d6df5f", - "build/prefab/full/mac_x86_64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/eb/c3/12a2c00732a90af63ae853b76a3b", - "build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/63/e7/795fd31307cda51524ea0cfc4055", - "build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/92/48/1bfcef97e05d641771a934e312f3", - "build/prefab/full/windows_x86/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/cf/25/4ef41f30fb1380505759a16a7302", - "build/prefab/full/windows_x86/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/78/eb/9ee7487ac9d633020c02bcdd475b", - "build/prefab/full/windows_x86_server/debug/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/4e/59/bd1e3f68c9ccf0180187dbf60ce1", - "build/prefab/full/windows_x86_server/release/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/3c/f2/e0eb7217dba038dd146421b0bf5e", - "build/prefab/lib/linux_x86_64/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/66/0f/34874cece602328e1a2b27efbe09", - "build/prefab/lib/linux_x86_64/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/ee/58/ccc75a3b2679f01a9e9b9f693ab0", - "build/prefab/lib/linux_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/2a/82/a34db5a370d695fb52891b0a7c54", - "build/prefab/lib/linux_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/1b/c4/0b7f73a301f24177650efb8cfa69", - "build/prefab/lib/mac_x86_64/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/e6/55/d4819d9a2e5dcd48c2b2641c57df", - "build/prefab/lib/mac_x86_64/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/21/3f/8c9d1590314cd5172e944f64e41c", - "build/prefab/lib/mac_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/91/72/b037555786dfb8a5ebc36467e60f", - "build/prefab/lib/mac_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/e3/e1/c9e61438f6458b9ce4ac4cfe66ed" + "build/prefab/full/linux_x86_64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/7e/ef/160b7c29aecf8594ce1aa3bb4d9f", + "build/prefab/full/linux_x86_64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/88/29/515b5c5def018fac0176b966bd93", + "build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/e2/4e/146ae464fad04c9a41ae0d7e01f3", + "build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/7a/14/febc99c12dabf2c09cf7969bd82f", + "build/prefab/full/mac_x86_64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/ed/78/7f2d157baabca2fb67b29d95c730", + "build/prefab/full/mac_x86_64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/e4/0e/05afd617a3333be2cebc6e7937f9", + "build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/47/49/9aef6c1fc8986c36740ae8ae5c83", + "build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/de/0c/e9bedd01292d103d92b8be7e9076", + "build/prefab/full/windows_x86/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/29/02/166905a0cfcb78acccd082a5d77e", + "build/prefab/full/windows_x86/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/7a/76/df87e67aef1847f9cc4f6d8bc43b", + "build/prefab/full/windows_x86_server/debug/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/d1/3d/431f9304a0c9eeb87d93af764bb6", + "build/prefab/full/windows_x86_server/release/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/da/02/41c7bbfefbae5518f2faa2631a01", + "build/prefab/lib/linux_x86_64/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/88/8a/aaeb70e76d31ce2686bee5cc4c14", + "build/prefab/lib/linux_x86_64/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/fd/cd/3397d744c7405740df4d4ae567f0", + "build/prefab/lib/linux_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/c2/5b/ad020af2e062e5caff25e7d0aec4", + "build/prefab/lib/linux_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/54/40/8e0cc564f49f8963803834ec7995", + "build/prefab/lib/mac_x86_64/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/bd/15/6ddf0c20288b9f72e5dd2c16a62a", + "build/prefab/lib/mac_x86_64/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/b1/d0/fe3646d8225126baafec16641a93", + "build/prefab/lib/mac_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/23/b6/ada9721f37bc2a090fde0d0a1d83", + "build/prefab/lib/mac_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/99/4c/d5dd9bc0b501dcb0d817f8166243" } \ No newline at end of file diff --git a/assets/src/ba_data/python/ba/_account.py b/assets/src/ba_data/python/ba/_account.py index 4d1208bb..4b8c9973 100644 --- a/assets/src/ba_data/python/ba/_account.py +++ b/assets/src/ba_data/python/ba/_account.py @@ -5,6 +5,7 @@ from __future__ import annotations import copy +import time from typing import TYPE_CHECKING import _ba @@ -25,6 +26,15 @@ class AccountSubsystem: def __init__(self) -> None: self.account_tournament_list: Optional[Tuple[int, List[str]]] = None + # FIXME: should abstract/structure these. + self.tournament_info: Dict = {} + self.league_rank_cache: Dict = {} + self.last_post_purchase_message_time: Optional[float] = None + + # If we try to run promo-codes due to launch-args/etc we might + # not be signed in yet; go ahead and queue them up in that case. + self.pending_promo_codes: List[str] = [] + def on_app_launch(self) -> None: """Called when the app is done bootstrapping.""" @@ -36,190 +46,222 @@ class AccountSubsystem: _ba.pushcall(do_auto_sign_in) + def on_app_resume(self) -> None: + """Should be called when the app is resumed.""" -def handle_account_gained_tickets(count: int) -> None: - """Called when the current account has been awarded tickets. + # Mark our cached tourneys as invalid so anyone using them knows + # they might be out of date. + for entry in list(self.tournament_info.values()): + entry['valid'] = False - (internal) - """ - from ba._language import Lstr - _ba.screenmessage(Lstr(resource='getTicketsWindow.receivedTicketsText', - subs=[('${COUNT}', str(count))]), - color=(0, 1, 0)) - _ba.playsound(_ba.getsound('cashRegister')) + def handle_account_gained_tickets(self, count: int) -> None: + """Called when the current account has been awarded tickets. + (internal) + """ + from ba._language import Lstr + _ba.screenmessage(Lstr(resource='getTicketsWindow.receivedTicketsText', + subs=[('${COUNT}', str(count))]), + color=(0, 1, 0)) + _ba.playsound(_ba.getsound('cashRegister')) -def cache_league_rank_data(data: Any) -> None: - """(internal)""" - _ba.app.league_rank_cache['info'] = copy.deepcopy(data) + def cache_league_rank_data(self, data: Any) -> None: + """(internal)""" + self.league_rank_cache['info'] = copy.deepcopy(data) + def get_cached_league_rank_data(self) -> Any: + """(internal)""" + return self.league_rank_cache.get('info', None) -def get_cached_league_rank_data() -> Any: - """(internal)""" - return _ba.app.league_rank_cache.get('info', None) + def get_league_rank_points(self, + data: Optional[Dict[str, Any]], + subset: str = None) -> int: + """(internal)""" + if data is None: + return 0 + # If the data contains an achievement total, use that. otherwise calc + # locally. + if data['at'] is not None: + total_ach_value = data['at'] + else: + total_ach_value = 0 + for ach in _ba.app.ach.achievements: + if ach.complete: + total_ach_value += ach.power_ranking_value -def get_league_rank_points(data: Optional[Dict[str, Any]], - subset: str = None) -> int: - """(internal)""" - if data is None: - return 0 + trophies_total: int = (data['t0a'] * data['t0am'] + + data['t0b'] * data['t0bm'] + + data['t1'] * data['t1m'] + + data['t2'] * data['t2m'] + + data['t3'] * data['t3m'] + + data['t4'] * data['t4m']) + if subset == 'trophyCount': + val: int = (data['t0a'] + data['t0b'] + data['t1'] + data['t2'] + + data['t3'] + data['t4']) + assert isinstance(val, int) + return val + if subset == 'trophies': + assert isinstance(trophies_total, int) + return trophies_total + if subset is not None: + raise ValueError('invalid subset value: ' + str(subset)) - # If the data contains an achievement total, use that. otherwise calc - # locally. - if data['at'] is not None: - total_ach_value = data['at'] - else: - total_ach_value = 0 - for ach in _ba.app.ach.achievements: - if ach.complete: - total_ach_value += ach.power_ranking_value + if data['p']: + pro_mult = 1.0 + float( + _ba.get_account_misc_read_val('proPowerRankingBoost', + 0.0)) * 0.01 + else: + pro_mult = 1.0 - trophies_total: int = (data['t0a'] * data['t0am'] + - data['t0b'] * data['t0bm'] + - data['t1'] * data['t1m'] + - data['t2'] * data['t2m'] + - data['t3'] * data['t3m'] + data['t4'] * data['t4m']) - if subset == 'trophyCount': - val: int = (data['t0a'] + data['t0b'] + data['t1'] + data['t2'] + - data['t3'] + data['t4']) - assert isinstance(val, int) - return val - if subset == 'trophies': - assert isinstance(trophies_total, int) - return trophies_total - if subset is not None: - raise ValueError('invalid subset value: ' + str(subset)) + # For final value, apply our pro mult and activeness-mult. + return int( + (total_ach_value + trophies_total) * + (data['act'] if data['act'] is not None else 1.0) * pro_mult) - if data['p']: - pro_mult = 1.0 + float( - _ba.get_account_misc_read_val('proPowerRankingBoost', 0.0)) * 0.01 - else: - pro_mult = 1.0 + def cache_tournament_info(self, info: Any) -> None: + """(internal)""" + from ba._enums import TimeType, TimeFormat + for entry in info: + cache_entry = self.tournament_info[entry['tournamentID']] = ( + copy.deepcopy(entry)) - # For final value, apply our pro mult and activeness-mult. - return int((total_ach_value + trophies_total) * - (data['act'] if data['act'] is not None else 1.0) * pro_mult) + # Also store the time we received this, so we can adjust + # time-remaining values/etc. + cache_entry['timeReceived'] = _ba.time(TimeType.REAL, + TimeFormat.MILLISECONDS) + cache_entry['valid'] = True + def get_purchased_icons(self) -> 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 -def cache_tournament_info(info: Any) -> None: - """(internal)""" - from ba._enums import TimeType, TimeFormat - for entry in info: - cache_entry = _ba.app.tournament_info[entry['tournamentID']] = ( - copy.deepcopy(entry)) + def ensure_have_account_player_profile(self) -> None: + """ + Ensure the standard account-named player profile exists; + creating if needed. - # Also store the time we received this, so we can adjust - # time-remaining values/etc. - cache_entry['timeReceived'] = _ba.time(TimeType.REAL, - TimeFormat.MILLISECONDS) - cache_entry['valid'] = True + (internal) + """ + # 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 -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 + 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() -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 + def have_pro(self) -> bool: + """Return whether pro is currently unlocked.""" - # 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 + # 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' == _ba.appname()) - config = _ba.app.config - if ('Player Profiles' not in config - or '__account__' not in config['Player Profiles']): + def have_pro_options(self) -> bool: + """Return whether pro-options are present. - # Create a spaz with a nice default purply color. + This is True for owners of Pro or old installs + before Pro was a requirement for these. + """ + + # We expose pro options if the server tells us to + # (which is generally just when we own pro), + # or also if we've been grandfathered in or are using ballistica-core + # builds. + return self.have_pro() or bool( + _ba.get_account_misc_read_val_2('proOptionsUnlocked', False) + or _ba.app.config.get('lc14292', 0) > 1) + + def show_post_purchase_message(self) -> None: + """(internal)""" + from ba._language import Lstr + from ba._enums import TimeType + cur_time = _ba.time(TimeType.REAL) + if (self.last_post_purchase_message_time is None + or cur_time - self.last_post_purchase_message_time > 3.0): + self.last_post_purchase_message_time = cur_time + with _ba.Context('ui'): + _ba.screenmessage(Lstr(resource='updatingAccountText', + fallback_resource='purchasingText'), + color=(0, 1, 0)) + _ba.playsound(_ba.getsound('click01')) + + def on_account_state_changed(self) -> None: + """(internal)""" + from ba._language import Lstr + + # Run any pending promo codes we had queued up while not signed in. + if _ba.get_account_state() == 'signed_in' and self.pending_promo_codes: + for code in self.pending_promo_codes: + _ba.screenmessage(Lstr(resource='submittingPromoCodeText'), + color=(0, 1, 0)) + _ba.add_transaction({ + 'type': 'PROMO_CODE', + 'expire_time': time.time() + 5, + 'code': code + }) + _ba.run_transactions() + self.pending_promo_codes = [] + + def add_pending_promo_code(self, code: str) -> None: + """(internal)""" + from ba._language import Lstr + from ba._enums import TimeType + + # If we're not signed in, queue up the code to run the next time we + # are and issue a warning if we haven't signed in within the next + # few seconds. + if _ba.get_account_state() != 'signed_in': + + def check_pending_codes() -> None: + """(internal)""" + + # If we're still not signed in and have pending codes, + # inform the user that they need to sign in to use them. + if self.pending_promo_codes: + _ba.screenmessage(Lstr(resource='signInForPromoCodeText'), + color=(1, 0, 0)) + _ba.playsound(_ba.getsound('error')) + + self.pending_promo_codes.append(code) + _ba.timer(6.0, check_pending_codes, timetype=TimeType.REAL) + return + _ba.screenmessage(Lstr(resource='submittingPromoCodeText'), + color=(0, 1, 0)) _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] - } + 'type': 'PROMO_CODE', + 'expire_time': time.time() + 5, + 'code': code }) _ba.run_transactions() - - -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' == _ba.appname()) - - -def have_pro_options() -> bool: - """Return whether pro-options are present. - - This is True for owners of Pro or old installs - before Pro was a requirement for these. - """ - - # We expose pro options if the server tells us to - # (which is generally just when we own pro), - # or also if we've been grandfathered in or are using ballistica-core - # builds. - return have_pro() or bool( - _ba.get_account_misc_read_val_2('proOptionsUnlocked', False) - or _ba.app.config.get('lc14292', 0) > 1) - - -def show_post_purchase_message() -> None: - """(internal)""" - from ba._language import Lstr - from ba._enums import TimeType - app = _ba.app - cur_time = _ba.time(TimeType.REAL) - if (app.last_post_purchase_message_time is None - or cur_time - app.last_post_purchase_message_time > 3.0): - app.last_post_purchase_message_time = cur_time - with _ba.Context('ui'): - _ba.screenmessage(Lstr(resource='updatingAccountText', - fallback_resource='purchasingText'), - color=(0, 1, 0)) - _ba.playsound(_ba.getsound('click01')) - - -def on_account_state_changed() -> None: - """(internal)""" - import time - from ba import _language - app = _ba.app - - # Run any pending promo codes we had queued up while not signed in. - if _ba.get_account_state() == 'signed_in' and app.pending_promo_codes: - for code in app.pending_promo_codes: - _ba.screenmessage( - _language.Lstr(resource='submittingPromoCodeText'), - color=(0, 1, 0)) - _ba.add_transaction({ - 'type': 'PROMO_CODE', - 'expire_time': time.time() + 5, - 'code': code - }) - _ba.run_transactions() - app.pending_promo_codes = [] diff --git a/assets/src/ba_data/python/ba/_achievement.py b/assets/src/ba_data/python/ba/_achievement.py index d8019e97..8775313e 100644 --- a/assets/src/ba_data/python/ba/_achievement.py +++ b/assets/src/ba_data/python/ba/_achievement.py @@ -409,10 +409,9 @@ def _get_ach_mult(include_pro_bonus: bool = False) -> int: (just for display; changing this here won't affect actual rewards) """ - from ba import _account val: int = _ba.get_account_misc_read_val('achAwardMult', 5) assert isinstance(val, int) - if include_pro_bonus and _account.have_pro(): + if include_pro_bonus and _ba.app.accounts.have_pro(): val *= 2 return val @@ -1178,7 +1177,7 @@ class Achievement: objt.node.host_only = True # Add the 'x 2' if we've got pro. - if _account.have_pro(): + if app.accounts.have_pro(): objt = Text('x 2', position=(-120 - 180 + 45, 80 + y_offs - 50), v_attach=Text.VAttach.BOTTOM, diff --git a/assets/src/ba_data/python/ba/_app.py b/assets/src/ba_data/python/ba/_app.py index 5963bf94..d2191d11 100644 --- a/assets/src/ba_data/python/ba/_app.py +++ b/assets/src/ba_data/python/ba/_app.py @@ -209,15 +209,11 @@ class App: self.did_weak_call_warning = False self.ran_on_app_launch = False - # If we try to run promo-codes due to launch-args/etc we might - # not be signed in yet; go ahead and queue them up in that case. - self.pending_promo_codes: List[str] = [] self.last_in_game_ad_remove_message_show_time: Optional[float] = None self.log_have_new = False self.log_upload_timer_started = False self._config: Optional[ba.AppConfig] = None self.printed_live_object_warning = False - self.last_post_purchase_message_time: Optional[float] = None # We include this extra hash with shared input-mapping names so # that we don't share mappings between differently-configured @@ -273,8 +269,6 @@ class App: self.main_menu_window_refresh_check_count = 0 # FIXME: Mv to mainmenu. self.main_menu_resume_callbacks: list = [] # Can probably go away. self.special_offer: Optional[Dict] = None - self.league_rank_cache: Dict = {} - self.tournament_info: Dict = {} self.ping_thread_count = 0 self.invite_confirm_windows: List[Any] = [] # FIXME: Don't use Any. self.store_layout: Optional[Dict[str, List[Dict[str, Any]]]] = None @@ -512,13 +506,9 @@ class App: def on_app_resume(self) -> None: """Run when the app resumes from a suspended state.""" - self.music.on_app_resume() self.fg_state += 1 - - # Mark our cached tourneys as invalid so anyone using them knows - # they might be out of date. - for entry in list(self.tournament_info.values()): - entry['valid'] = False + self.accounts.on_app_resume() + self.music.on_app_resume() def launch_coop_game(self, game: str, @@ -596,38 +586,10 @@ class App: def handle_deep_link(self, url: str) -> None: """Handle a deep link URL.""" from ba._language import Lstr - from ba._enums import TimeType appname = _ba.appname() if url.startswith(f'{appname}://code/'): code = url.replace(f'{appname}://code/', '') - - # If we're not signed in, queue up the code to run the next time we - # are and issue a warning if we haven't signed in within the next - # few seconds. - if _ba.get_account_state() != 'signed_in': - - def check_pending_codes() -> None: - """(internal)""" - - # If we're still not signed in and have pending codes, - # inform the user that they need to sign in to use them. - if self.pending_promo_codes: - _ba.screenmessage( - Lstr(resource='signInForPromoCodeText'), - color=(1, 0, 0)) - _ba.playsound(_ba.getsound('error')) - - self.pending_promo_codes.append(code) - _ba.timer(6.0, check_pending_codes, timetype=TimeType.REAL) - return - _ba.screenmessage(Lstr(resource='submittingPromoCodeText'), - color=(0, 1, 0)) - _ba.add_transaction({ - 'type': 'PROMO_CODE', - 'expire_time': time.time() + 5, - 'code': code - }) - _ba.run_transactions() + self.accounts.add_pending_promo_code(code) else: _ba.screenmessage(Lstr(resource='errorText'), color=(1, 0, 0)) _ba.playsound(_ba.getsound('error')) diff --git a/assets/src/ba_data/python/ba/_apputils.py b/assets/src/ba_data/python/ba/_apputils.py index 10f4ca15..0e16a444 100644 --- a/assets/src/ba_data/python/ba/_apputils.py +++ b/assets/src/ba_data/python/ba/_apputils.py @@ -246,7 +246,6 @@ def call_after_ad(call: Callable[[], Any]) -> None: # pylint: disable=too-many-statements # pylint: disable=too-many-branches # pylint: disable=too-many-locals - from ba._account import have_pro from ba._enums import TimeType import time app = _ba.app @@ -255,7 +254,7 @@ def call_after_ad(call: Callable[[], Any]) -> None: # No ads without net-connections, etc. if not _ba.can_show_ad(): show = False - if have_pro(): + if app.accounts.have_pro(): show = False # Pro disables interstitials. try: session = _ba.get_foreground_host_session() diff --git a/assets/src/ba_data/python/ba/_gameactivity.py b/assets/src/ba_data/python/ba/_gameactivity.py index 3ada842d..1c1e7fbe 100644 --- a/assets/src/ba_data/python/ba/_gameactivity.py +++ b/assets/src/ba_data/python/ba/_gameactivity.py @@ -8,6 +8,7 @@ from __future__ import annotations import random from typing import TYPE_CHECKING, TypeVar +import _ba from ba._activity import Activity from ba._score import ScoreConfig from ba._language import Lstr @@ -16,7 +17,6 @@ from ba._error import NotFoundError, print_error, print_exception from ba._general import Call, WeakCall from ba._player import PlayerInfo from ba import _map -import _ba if TYPE_CHECKING: from typing import (List, Optional, Dict, Type, Any, Callable, Sequence, @@ -461,12 +461,11 @@ class GameActivity(Activity[PlayerType, TeamType]): def _on_tournament_query_response(self, data: Optional[Dict[str, Any]]) -> None: - from ba._account import cache_tournament_info if data is not None: data_t = data['t'] # This used to be the whole payload. # Keep our cached tourney info up to date - cache_tournament_info(data_t) + _ba.app.accounts.cache_tournament_info(data_t) self._setup_tournament_time_limit( max(5, data_t[0]['timeRemaining'])) diff --git a/assets/src/ba_data/python/ba/_lobby.py b/assets/src/ba_data/python/ba/_lobby.py index 338d5121..577a0c42 100644 --- a/assets/src/ba_data/python/ba/_lobby.py +++ b/assets/src/ba_data/python/ba/_lobby.py @@ -881,7 +881,6 @@ class Lobby: def reload_profiles(self) -> None: """Reload available player profiles.""" # pylint: disable=cyclic-import - from ba._account import ensure_have_account_player_profile from bastd.actor.spazappearance import get_appearances # We may have gained or lost character names if the user @@ -890,7 +889,7 @@ class Lobby: self.character_names_local_unlocked.sort(key=lambda x: x.lower()) # Do any overall prep we need to such as creating account profile. - ensure_have_account_player_profile() + _ba.app.accounts.ensure_have_account_player_profile() for chooser in self.choosers: try: chooser.reload_profiles() diff --git a/assets/src/ba_data/python/ba/_store.py b/assets/src/ba_data/python/ba/_store.py index cd9f16ae..fd4e5bfc 100644 --- a/assets/src/ba_data/python/ba/_store.py +++ b/assets/src/ba_data/python/ba/_store.py @@ -440,7 +440,6 @@ def get_available_sale_time(tab: str) -> Optional[int]: # pylint: disable=too-many-locals try: import datetime - from ba._account import have_pro from ba._enums import TimeType, TimeFormat app = _ba.app sale_times: List[Optional[int]] = [] @@ -448,7 +447,7 @@ def get_available_sale_time(tab: str) -> Optional[int]: # Calc time for our pro sale (old special case). if tab == 'extras': config = app.config - if have_pro(): + if app.accounts.have_pro(): return None # If we haven't calced/loaded start times yet. diff --git a/assets/src/ba_data/python/ba/internal.py b/assets/src/ba_data/python/ba/internal.py index 9bf9568d..0b1092d5 100644 --- a/assets/src/ba_data/python/ba/internal.py +++ b/assets/src/ba_data/python/ba/internal.py @@ -16,12 +16,6 @@ from ba._appconfig import commit_app_config from ba._input import (get_device_value, get_input_map_hash, get_input_device_config) from ba._general import getclass, json_prep, get_type_name -from ba._account import (on_account_state_changed, - handle_account_gained_tickets, have_pro_options, - have_pro, cache_tournament_info, - ensure_have_account_player_profile, - get_purchased_icons, get_cached_league_rank_data, - get_league_rank_points, cache_league_rank_data) from ba._activitytypes import JoinActivity, ScoreScreenActivity from ba._apputils import (is_browser_likely_available, get_remote_app_name, should_submit_debug_info, show_ad, show_ad_2) diff --git a/assets/src/ba_data/python/bastd/activity/coopscore.py b/assets/src/ba_data/python/bastd/activity/coopscore.py index 1c9e8cfe..b8678cd0 100644 --- a/assets/src/ba_data/python/bastd/activity/coopscore.py +++ b/assets/src/ba_data/python/bastd/activity/coopscore.py @@ -1197,8 +1197,9 @@ class CoopScoreScreen(ba.Activity[ba.Player, ba.Team]): try: tournament_id = self.session.tournament_id if tournament_id is not None: - if tournament_id in ba.app.tournament_info: - tourney_info = ba.app.tournament_info[tournament_id] + if tournament_id in ba.app.accounts.tournament_info: + tourney_info = ba.app.accounts.tournament_info[ + tournament_id] # pylint: disable=unbalanced-tuple-unpacking pr1, pv1, pr2, pv2, pr3, pv3 = ( get_tournament_prize_strings(tourney_info)) diff --git a/assets/src/ba_data/python/bastd/ui/colorpicker.py b/assets/src/ba_data/python/bastd/ui/colorpicker.py index aac01dbe..b9630a25 100644 --- a/assets/src/ba_data/python/bastd/ui/colorpicker.py +++ b/assets/src/ba_data/python/bastd/ui/colorpicker.py @@ -28,7 +28,7 @@ class ColorPicker(PopupWindow): offset: Tuple[float, float] = (0.0, 0.0), tag: Any = ''): # pylint: disable=too-many-locals - from ba.internal import have_pro, get_player_colors + from ba.internal import get_player_colors c_raw = get_player_colors() assert len(c_raw) == 16 @@ -94,7 +94,7 @@ class ColorPicker(PopupWindow): on_activate_call=ba.WeakCall(self._select_other)) # Custom colors are limited to pro currently. - if not have_pro(): + if not ba.app.accounts.have_pro(): ba.imagewidget(parent=self.root_widget, position=(50, 12), size=(30, 30), @@ -116,10 +116,9 @@ class ColorPicker(PopupWindow): def _select_other(self) -> None: from bastd.ui import purchase - from ba.internal import have_pro # Requires pro. - if not have_pro(): + if not ba.app.accounts.have_pro(): purchase.PurchaseWindow(items=['pro']) self._transition_out() return diff --git a/assets/src/ba_data/python/bastd/ui/coop/browser.py b/assets/src/ba_data/python/bastd/ui/coop/browser.py index 868c42ba..20b369cb 100644 --- a/assets/src/ba_data/python/bastd/ui/coop/browser.py +++ b/assets/src/ba_data/python/bastd/ui/coop/browser.py @@ -253,11 +253,11 @@ class CoopBrowserWindow(ba.Window): if (app.accounts.account_tournament_list is not None and app.accounts.account_tournament_list[0] == _ba.get_account_state_num() and all([ - t_id in app.tournament_info + t_id in app.accounts.tournament_info for t_id in app.accounts.account_tournament_list[1] ])): tourney_data = [ - app.tournament_info[t_id] + app.accounts.tournament_info[t_id] for t_id in app.accounts.account_tournament_list[1] ] self._update_for_data(tourney_data) @@ -350,10 +350,10 @@ class CoopBrowserWindow(ba.Window): self._update_hard_mode_lock_image() def _update_hard_mode_lock_image(self) -> None: - from ba.internal import have_pro_options try: - ba.imagewidget(edit=self._hard_button_lock_image, - opacity=0.0 if have_pro_options() else 1.0) + ba.imagewidget( + edit=self._hard_button_lock_image, + opacity=0.0 if ba.app.accounts.have_pro_options() else 1.0) except Exception: ba.print_exception('Error updating campaign lock.') @@ -475,7 +475,8 @@ class CoopBrowserWindow(ba.Window): tbtn['required_league'] = (None if 'requiredLeague' not in entry else entry['requiredLeague']) - game = ba.app.tournament_info[tbtn['tournament_id']]['game'] + game = ba.app.accounts.tournament_info[ + tbtn['tournament_id']]['game'] if game is None: ba.textwidget(edit=tbtn['button_text'], text='-') @@ -485,7 +486,7 @@ class CoopBrowserWindow(ba.Window): else: campaignname, levelname = game.split(':') campaign = getcampaign(campaignname) - max_players = ba.app.tournament_info[ + max_players = ba.app.accounts.tournament_info[ tbtn['tournament_id']]['maxPlayers'] txt = ba.Lstr( value='${A} ${B}', @@ -534,9 +535,9 @@ class CoopBrowserWindow(ba.Window): ba.charstr(ba.SpecialChar.TICKET_BACKING) + str(final_fee)) - ad_tries_remaining = ba.app.tournament_info[ + ad_tries_remaining = ba.app.accounts.tournament_info[ tbtn['tournament_id']]['adTriesRemaining'] - free_tries_remaining = ba.app.tournament_info[ + free_tries_remaining = ba.app.accounts.tournament_info[ tbtn['tournament_id']]['freeTriesRemaining'] # Now, if this fee allows ads and we support video ads, show @@ -586,8 +587,7 @@ class CoopBrowserWindow(ba.Window): def _on_tournament_query_response(self, data: Optional[Dict[str, Any]]) -> None: - from ba.internal import cache_tournament_info - app = ba.app + accounts = ba.app.accounts if data is not None: tournament_data = data['t'] # This used to be the whole payload. self._last_tournament_query_response_time = ba.time( @@ -598,22 +598,21 @@ class CoopBrowserWindow(ba.Window): # Keep our cached tourney info up to date. if data is not None: self._tourney_data_up_to_date = True - cache_tournament_info(tournament_data) + accounts.cache_tournament_info(tournament_data) # Also cache the current tourney list/order for this account. - app.accounts.account_tournament_list = ( - _ba.get_account_state_num(), - [e['tournamentID'] for e in tournament_data]) + accounts.account_tournament_list = (_ba.get_account_state_num(), [ + e['tournamentID'] for e in tournament_data + ]) self._doing_tournament_query = False self._update_for_data(tournament_data) def _set_campaign_difficulty(self, difficulty: str) -> None: # pylint: disable=cyclic-import - from ba.internal import have_pro_options from bastd.ui.purchase import PurchaseWindow if difficulty != self._campaign_difficulty: - if difficulty == 'hard' and not have_pro_options(): + if difficulty == 'hard' and not ba.app.accounts.have_pro_options(): PurchaseWindow(items=['pro']) return ba.playsound(ba.getsound('gunCocking')) @@ -1412,7 +1411,6 @@ class CoopBrowserWindow(ba.Window): # pylint: disable=too-many-statements # pylint: disable=too-many-return-statements # pylint: disable=cyclic-import - from ba.internal import have_pro from bastd.ui.confirm import ConfirmWindow from bastd.ui.tournamententry import TournamentEntryWindow from bastd.ui.purchase import PurchaseWindow @@ -1460,7 +1458,7 @@ class CoopBrowserWindow(ba.Window): return # Game is whatever the tournament tells us it is. - game = ba.app.tournament_info[ + game = ba.app.accounts.tournament_info[ tournament_button['tournament_id']]['game'] if tournament_button is None and game == 'Easy:The Last Stand': @@ -1475,7 +1473,8 @@ class CoopBrowserWindow(ba.Window): # need be. if tournament_button is None and game in ( 'Challenges:Infinite Runaround', - 'Challenges:Infinite Onslaught') and not have_pro(): + 'Challenges:Infinite Onslaught' + ) and not ba.app.accounts.have_pro(): if _ba.get_account_state() != 'signed_in': show_sign_in_prompt() else: diff --git a/assets/src/ba_data/python/bastd/ui/coop/gamebutton.py b/assets/src/ba_data/python/bastd/ui/coop/gamebutton.py index 6c6e8f07..5f50af0f 100644 --- a/assets/src/ba_data/python/bastd/ui/coop/gamebutton.py +++ b/assets/src/ba_data/python/bastd/ui/coop/gamebutton.py @@ -164,7 +164,7 @@ class GameButton: def _update(self) -> None: # pylint: disable=too-many-boolean-expressions - from ba.internal import have_pro, getcampaign + from ba.internal import getcampaign game = self._game campaignname, levelname = game.split(':') @@ -193,7 +193,8 @@ class GameButton: # Hard-code games we haven't unlocked. if ((game in ('Challenges:Infinite Runaround', - 'Challenges:Infinite Onslaught') and not have_pro()) + 'Challenges:Infinite Onslaught') + and not ba.app.accounts.have_pro()) or (game in ('Challenges:Meteor Shower', ) and not _ba.get_purchased('games.meteor_shower')) or (game in ('Challenges:Target Practice', diff --git a/assets/src/ba_data/python/bastd/ui/gather.py b/assets/src/ba_data/python/bastd/ui/gather.py index f3ccad20..73d3f4dd 100644 --- a/assets/src/ba_data/python/bastd/ui/gather.py +++ b/assets/src/ba_data/python/bastd/ui/gather.py @@ -1256,9 +1256,8 @@ class GatherWindow(ba.Window): texture=ba.gettexture('lock')) def _is_internet_locked(self) -> bool: - from ba.internal import have_pro if _ba.get_account_misc_read_val('ilck', False): - return not have_pro() + return not ba.app.accounts.have_pro() return False def _on_max_public_party_size_minus_press(self) -> None: diff --git a/assets/src/ba_data/python/bastd/ui/iconpicker.py b/assets/src/ba_data/python/bastd/ui/iconpicker.py index fe6dfcc0..1cab6e56 100644 --- a/assets/src/ba_data/python/bastd/ui/iconpicker.py +++ b/assets/src/ba_data/python/bastd/ui/iconpicker.py @@ -28,7 +28,6 @@ class IconPicker(popup.PopupWindow): tint2_color: Sequence[float] = (1.0, 1.0, 1.0), selected_icon: str = None): # pylint: disable=too-many-locals - from ba.internal import get_purchased_icons del parent # unused here del tint_color # unused_here del tint2_color # unused here @@ -40,7 +39,8 @@ class IconPicker(popup.PopupWindow): self._delegate = delegate self._transitioning_out = False - self._icons = [ba.charstr(ba.SpecialChar.LOGO)] + get_purchased_icons() + self._icons = [ba.charstr(ba.SpecialChar.LOGO) + ] + ba.app.accounts.get_purchased_icons() count = len(self._icons) columns = 4 rows = int(math.ceil(float(count) / columns)) diff --git a/assets/src/ba_data/python/bastd/ui/league/rankbutton.py b/assets/src/ba_data/python/bastd/ui/league/rankbutton.py index 7f0e5cfd..f299f5ff 100644 --- a/assets/src/ba_data/python/bastd/ui/league/rankbutton.py +++ b/assets/src/ba_data/python/bastd/ui/league/rankbutton.py @@ -26,7 +26,6 @@ class LeagueRankButton: color: Tuple[float, float, float] = None, textcolor: Tuple[float, float, float] = None, smooth_update_delay: float = None): - from ba.internal import get_cached_league_rank_data if on_activate_call is None: on_activate_call = ba.WeakCall(self._default_on_activate_call) self._on_activate_call = on_activate_call @@ -107,7 +106,7 @@ class LeagueRankButton: self._update() # If we've got cached power-ranking data already, apply it. - data = get_cached_league_rank_data() + data = ba.app.accounts.get_cached_league_rank_data() if data is not None: self._update_for_league_rank_data(data) @@ -216,7 +215,6 @@ class LeagueRankButton: Any]]) -> None: # pylint: disable=too-many-branches # pylint: disable=too-many-statements - from ba.internal import get_league_rank_points # If our button has died, ignore. if not self._button: @@ -250,7 +248,7 @@ class LeagueRankButton: self._percent = self._rank = None status_text = '-' else: - our_points = get_league_rank_points(data) + our_points = ba.app.accounts.get_league_rank_points(data) progress = float(our_points) / data['scores'][-1][1] self._percent = int(progress * 100.0) self._rank = None @@ -328,9 +326,8 @@ class LeagueRankButton: def _on_power_ranking_query_response( self, data: Optional[Dict[str, Any]]) -> None: - from ba.internal import cache_league_rank_data self._doing_power_ranking_query = False - cache_league_rank_data(data) + ba.app.accounts.cache_league_rank_data(data) self._update_for_league_rank_data(data) def _update(self) -> None: diff --git a/assets/src/ba_data/python/bastd/ui/league/rankwindow.py b/assets/src/ba_data/python/bastd/ui/league/rankwindow.py index e0c7230b..0ee0c9db 100644 --- a/assets/src/ba_data/python/bastd/ui/league/rankwindow.py +++ b/assets/src/ba_data/python/bastd/ui/league/rankwindow.py @@ -22,7 +22,6 @@ class LeagueRankWindow(ba.Window): transition: str = 'in_right', modal: bool = False, origin_widget: ba.Widget = None): - from ba.internal import get_cached_league_rank_data ba.set_analytics_screen('League Rank Window') self._league_rank_data: Optional[Dict[str, Any]] = None @@ -125,7 +124,7 @@ class LeagueRankWindow(ba.Window): self._restore_state() # if we've got cached power-ranking data already, display it - info = get_cached_league_rank_data() + info = ba.app.accounts.get_cached_league_rank_data() if info is not None: self._update_for_league_rank_data(info) @@ -194,11 +193,10 @@ class LeagueRankWindow(ba.Window): def _on_power_ranking_query_response( self, data: Optional[Dict[str, Any]]) -> None: - from ba.internal import cache_league_rank_data self._doing_power_ranking_query = False # important: *only* cache this if we requested the current season.. if data is not None and data.get('s', None) is None: - cache_league_rank_data(data) + ba.app.accounts.cache_league_rank_data(data) # always store a copy locally though (even for other seasons) self._league_rank_data = copy.deepcopy(data) self._update_for_league_rank_data(data) @@ -594,9 +592,9 @@ class LeagueRankWindow(ba.Window): # pylint: disable=too-many-statements # pylint: disable=too-many-branches # pylint: disable=too-many-locals - from ba.internal import get_league_rank_points if not self._root_widget: return + accounts = ba.app.accounts in_top = (data is not None and data['rank'] is not None) eq_text = self._rdict.powerRankingPointsEqualsText pts_txt = self._rdict.powerRankingPointsText @@ -622,7 +620,7 @@ class LeagueRankWindow(ba.Window): finished_season_unranked = True self._can_do_more_button = False else: - our_points = get_league_rank_points(data) + our_points = accounts.get_league_rank_points(data) progress = float(our_points) / max(1, data['scores'][-1][1]) status_text = str(int(progress * 100.0)) + '%' @@ -820,8 +818,10 @@ class LeagueRankWindow(ba.Window): ('+ ' + pts_txt.replace('${NUMBER}', str(total_ach_value)))) - total_trophies_count = (get_league_rank_points(data, 'trophyCount')) - total_trophies_value = (get_league_rank_points(data, 'trophies')) + total_trophies_count = (accounts.get_league_rank_points( + data, 'trophyCount')) + total_trophies_value = (accounts.get_league_rank_points( + data, 'trophies')) ba.buttonwidget(edit=self._power_ranking_trophies_button, label=('' if data is None else (str(total_trophies_count) + ' ')) + @@ -831,9 +831,10 @@ class LeagueRankWindow(ba.Window): text='-' if data is None else ('+ ' + pts_txt.replace('${NUMBER}', str(total_trophies_value)))) - ba.textwidget(edit=self._power_ranking_total_text, - text='-' if data is None else eq_text.replace( - '${NUMBER}', str(get_league_rank_points(data)))) + ba.textwidget( + edit=self._power_ranking_total_text, + text='-' if data is None else eq_text.replace( + '${NUMBER}', str(accounts.get_league_rank_points(data)))) for widget in self._power_ranking_score_widgets: widget.delete() self._power_ranking_score_widgets = [] diff --git a/assets/src/ba_data/python/bastd/ui/playlist/customizebrowser.py b/assets/src/ba_data/python/bastd/ui/playlist/customizebrowser.py index 8387c12a..4ee5b297 100644 --- a/assets/src/ba_data/python/bastd/ui/playlist/customizebrowser.py +++ b/assets/src/ba_data/python/bastd/ui/playlist/customizebrowser.py @@ -253,8 +253,7 @@ class PlaylistCustomizeBrowserWindow(ba.Window): self._update() def _update(self) -> None: - from ba.internal import have_pro_options - have = have_pro_options() + have = ba.app.accounts.have_pro_options() for lock in self._lock_images: ba.imagewidget(edit=lock, opacity=0.0 if have else 1.0) @@ -382,11 +381,10 @@ class PlaylistCustomizeBrowserWindow(ba.Window): def _new_playlist(self) -> None: # pylint: disable=cyclic-import - from ba.internal import have_pro_options - from bastd.ui.playlist import editcontroller - from bastd.ui import purchase - if not have_pro_options(): - purchase.PurchaseWindow(items=['pro']) + from bastd.ui.playlist.editcontroller import PlaylistEditController + from bastd.ui.purchase import PurchaseWindow + if not ba.app.accounts.have_pro_options(): + PurchaseWindow(items=['pro']) return # Clamp at our max playlist number. @@ -402,16 +400,15 @@ class PlaylistCustomizeBrowserWindow(ba.Window): self._save_playlist_selection() # Kick off the edit UI. - editcontroller.PlaylistEditController(sessiontype=self._sessiontype) + PlaylistEditController(sessiontype=self._sessiontype) ba.containerwidget(edit=self._root_widget, transition='out_left') def _edit_playlist(self) -> None: # pylint: disable=cyclic-import - from ba.internal import have_pro_options - from bastd.ui.playlist import editcontroller - from bastd.ui import purchase - if not have_pro_options(): - purchase.PurchaseWindow(items=['pro']) + from bastd.ui.playlist.editcontroller import PlaylistEditController + from bastd.ui.purchase import PurchaseWindow + if not ba.app.accounts.have_pro_options(): + PurchaseWindow(items=['pro']) return if self._selected_playlist_name is None: return @@ -421,7 +418,7 @@ class PlaylistCustomizeBrowserWindow(ba.Window): '.cantEditDefaultText')) return self._save_playlist_selection() - editcontroller.PlaylistEditController( + PlaylistEditController( existing_playlist_name=self._selected_playlist_name, sessiontype=self._sessiontype) ba.containerwidget(edit=self._root_widget, transition='out_left') @@ -474,10 +471,9 @@ class PlaylistCustomizeBrowserWindow(ba.Window): def _share_playlist(self) -> None: # pylint: disable=cyclic-import - from ba.internal import have_pro_options - from bastd.ui import purchase - if not have_pro_options(): - purchase.PurchaseWindow(items=['pro']) + from bastd.ui.purchase import PurchaseWindow + if not ba.app.accounts.have_pro_options(): + PurchaseWindow(items=['pro']) return # Gotta be signed in for this to work. @@ -510,11 +506,10 @@ class PlaylistCustomizeBrowserWindow(ba.Window): def _delete_playlist(self) -> None: # pylint: disable=cyclic-import - from ba.internal import have_pro_options - from bastd.ui import purchase - from bastd.ui import confirm - if not have_pro_options(): - purchase.PurchaseWindow(items=['pro']) + from bastd.ui.purchase import PurchaseWindow + from bastd.ui.confirm import ConfirmWindow + if not ba.app.accounts.have_pro_options(): + PurchaseWindow(items=['pro']) return if self._selected_playlist_name is None: @@ -524,7 +519,7 @@ class PlaylistCustomizeBrowserWindow(ba.Window): ba.screenmessage( ba.Lstr(resource=self._r + '.cantDeleteDefaultText')) else: - confirm.ConfirmWindow( + ConfirmWindow( ba.Lstr(resource=self._r + '.deleteConfirmText', subs=[('${LIST}', self._selected_playlist_name)]), self._do_delete_playlist, 450, 150) @@ -538,10 +533,9 @@ class PlaylistCustomizeBrowserWindow(ba.Window): def _duplicate_playlist(self) -> None: # pylint: disable=too-many-branches # pylint: disable=cyclic-import - from ba.internal import have_pro_options - from bastd.ui import purchase - if not have_pro_options(): - purchase.PurchaseWindow(items=['pro']) + from bastd.ui.purchase import PurchaseWindow + if not ba.app.accounts.have_pro_options(): + PurchaseWindow(items=['pro']) return if self._selected_playlist_name is None: return diff --git a/assets/src/ba_data/python/bastd/ui/playoptions.py b/assets/src/ba_data/python/bastd/ui/playoptions.py index e3341496..ca0a0696 100644 --- a/assets/src/ba_data/python/bastd/ui/playoptions.py +++ b/assets/src/ba_data/python/bastd/ui/playoptions.py @@ -26,8 +26,7 @@ class PlayOptionsWindow(popup.PopupWindow): # pylint: disable=too-many-branches # pylint: disable=too-many-statements # pylint: disable=too-many-locals - from ba.internal import (getclass, have_pro, - get_default_teams_playlist, + from ba.internal import (getclass, get_default_teams_playlist, get_default_free_for_all_playlist, filter_playlist) from ba.internal import get_map_class @@ -256,7 +255,7 @@ class PlayOptionsWindow(popup.PopupWindow): autoselect=True, textcolor=(0.8, 0.8, 0.8), label=ba.Lstr(resource='teamNamesColorText')) - if not have_pro(): + if not ba.app.accounts.have_pro(): ba.imagewidget( parent=self.root_widget, size=(30, 30), @@ -350,21 +349,19 @@ class PlayOptionsWindow(popup.PopupWindow): self._update() def _custom_colors_names_press(self) -> None: - from ba.internal import have_pro - from bastd.ui import account as accountui - from bastd.ui import teamnamescolors - from bastd.ui import purchase - if not have_pro(): + from bastd.ui.account import show_sign_in_prompt + from bastd.ui.teamnamescolors import TeamNamesColorsWindow + from bastd.ui.purchase import PurchaseWindow + if not ba.app.accounts.have_pro(): if _ba.get_account_state() != 'signed_in': - accountui.show_sign_in_prompt() + show_sign_in_prompt() else: - purchase.PurchaseWindow(items=['pro']) + PurchaseWindow(items=['pro']) self._transition_out() return assert self._custom_colors_names_button - teamnamescolors.TeamNamesColorsWindow( - scale_origin=self._custom_colors_names_button. - get_screen_space_center()) + TeamNamesColorsWindow(scale_origin=self._custom_colors_names_button. + get_screen_space_center()) def _does_target_playlist_exist(self) -> bool: if self._playlist == '__default__': diff --git a/assets/src/ba_data/python/bastd/ui/profile/browser.py b/assets/src/ba_data/python/bastd/ui/profile/browser.py index 6521b815..833c546d 100644 --- a/assets/src/ba_data/python/bastd/ui/profile/browser.py +++ b/assets/src/ba_data/python/bastd/ui/profile/browser.py @@ -23,7 +23,6 @@ class ProfileBrowserWindow(ba.Window): origin_widget: ba.Widget = None): # pylint: disable=too-many-statements # pylint: disable=too-many-locals - from ba.internal import ensure_have_account_player_profile self._in_main_menu = in_main_menu if self._in_main_menu: back_label = ba.Lstr(resource='backText') @@ -52,7 +51,7 @@ class ProfileBrowserWindow(ba.Window): self._r = 'playerProfilesWindow' # Ensure we've got an account-profile in cases where we're signed in. - ensure_have_account_player_profile() + ba.app.accounts.ensure_have_account_player_profile() top_extra = 20 if uiscale is ba.UIScale.SMALL else 0 @@ -171,14 +170,13 @@ class ProfileBrowserWindow(ba.Window): def _new_profile(self) -> None: # pylint: disable=cyclic-import - from ba.internal import have_pro_options from bastd.ui.profile.edit import EditProfileWindow from bastd.ui.purchase import PurchaseWindow # Limit to a handful profiles if they don't have pro-options. max_non_pro_profiles = _ba.get_account_misc_read_val('mnpp', 5) assert self._profiles is not None - if (not have_pro_options() + if (not ba.app.accounts.have_pro_options() and len(self._profiles) >= max_non_pro_profiles): PurchaseWindow(items=['pro'], header_text=ba.Lstr( diff --git a/assets/src/ba_data/python/bastd/ui/purchase.py b/assets/src/ba_data/python/bastd/ui/purchase.py index 3450e780..519fb489 100644 --- a/assets/src/ba_data/python/bastd/ui/purchase.py +++ b/assets/src/ba_data/python/bastd/ui/purchase.py @@ -114,12 +114,11 @@ class PurchaseWindow(ba.Window): selected_child=self._purchase_button) def _update(self) -> None: - from ba.internal import have_pro can_die = False # We go away if we see that our target item is owned. if self._items == ['pro']: - if have_pro(): + if ba.app.accounts.have_pro(): can_die = True else: if _ba.get_purchased(self._items[0]): diff --git a/assets/src/ba_data/python/bastd/ui/soundtrack/browser.py b/assets/src/ba_data/python/bastd/ui/soundtrack/browser.py index ffb6898f..c1a719f0 100644 --- a/assets/src/ba_data/python/bastd/ui/soundtrack/browser.py +++ b/assets/src/ba_data/python/bastd/ui/soundtrack/browser.py @@ -211,8 +211,7 @@ class SoundtrackBrowserWindow(ba.Window): on_cancel_call=self._back) def _update(self) -> None: - from ba.internal import have_pro_options - have = have_pro_options() + have = ba.app.accounts.have_pro_options() for lock in self._lock_images: ba.imagewidget(edit=lock, opacity=0.0 if have else 1.0) @@ -231,11 +230,10 @@ class SoundtrackBrowserWindow(ba.Window): def _delete_soundtrack(self) -> None: # pylint: disable=cyclic-import - from ba.internal import have_pro_options - from bastd.ui import purchase - from bastd.ui import confirm - if not have_pro_options(): - purchase.PurchaseWindow(items=['pro']) + from bastd.ui.purchase import PurchaseWindow + from bastd.ui.confirm import ConfirmWindow + if not ba.app.accounts.have_pro_options(): + PurchaseWindow(items=['pro']) return if self._selected_soundtrack is None: return @@ -245,17 +243,16 @@ class SoundtrackBrowserWindow(ba.Window): '.cantDeleteDefaultText'), color=(1, 0, 0)) else: - confirm.ConfirmWindow( + ConfirmWindow( ba.Lstr(resource=self._r + '.deleteConfirmText', subs=[('${NAME}', self._selected_soundtrack)]), self._do_delete_soundtrack, 450, 150) def _duplicate_soundtrack(self) -> None: # pylint: disable=cyclic-import - from ba.internal import have_pro_options - from bastd.ui import purchase - if not have_pro_options(): - purchase.PurchaseWindow(items=['pro']) + from bastd.ui.purchase import PurchaseWindow + if not ba.app.accounts.have_pro_options(): + PurchaseWindow(items=['pro']) return cfg = ba.app.config cfg.setdefault('Soundtracks', {}) @@ -324,21 +321,19 @@ class SoundtrackBrowserWindow(ba.Window): def _edit_soundtrack_with_sound(self) -> None: # pylint: disable=cyclic-import - from ba.internal import have_pro_options - from bastd.ui import purchase - if not have_pro_options(): - purchase.PurchaseWindow(items=['pro']) + from bastd.ui.purchase import PurchaseWindow + if not ba.app.accounts.have_pro_options(): + PurchaseWindow(items=['pro']) return ba.playsound(ba.getsound('swish')) self._edit_soundtrack() def _edit_soundtrack(self) -> None: # pylint: disable=cyclic-import - from ba.internal import have_pro_options - from bastd.ui import purchase - from bastd.ui.soundtrack import edit as stedit - if not have_pro_options(): - purchase.PurchaseWindow(items=['pro']) + from bastd.ui.purchase import PurchaseWindow + from bastd.ui.soundtrack.edit import SoundtrackEditWindow + if not ba.app.accounts.have_pro_options(): + PurchaseWindow(items=['pro']) return if self._selected_soundtrack is None: return @@ -352,9 +347,8 @@ class SoundtrackBrowserWindow(ba.Window): self._save_state() ba.containerwidget(edit=self._root_widget, transition='out_left') ba.app.ui.set_main_menu_window( - stedit.SoundtrackEditWindow( - existing_soundtrack=self._selected_soundtrack).get_root_widget( - )) + SoundtrackEditWindow(existing_soundtrack=self._selected_soundtrack + ).get_root_widget()) def _get_soundtrack_display_name(self, soundtrack: str) -> ba.Lstr: if soundtrack == '__default__': @@ -438,15 +432,14 @@ class SoundtrackBrowserWindow(ba.Window): def _new_soundtrack(self) -> None: # pylint: disable=cyclic-import - from ba.internal import have_pro_options - from bastd.ui import purchase - from bastd.ui.soundtrack import edit as stedit - if not have_pro_options(): - purchase.PurchaseWindow(items=['pro']) + from bastd.ui.purchase import PurchaseWindow + from bastd.ui.soundtrack.edit import SoundtrackEditWindow + if not ba.app.accounts.have_pro_options(): + PurchaseWindow(items=['pro']) return self._save_state() ba.containerwidget(edit=self._root_widget, transition='out_left') - stedit.SoundtrackEditWindow(existing_soundtrack=None) + SoundtrackEditWindow(existing_soundtrack=None) def _create_done(self, new_soundtrack: str) -> None: if new_soundtrack is not None: diff --git a/assets/src/ba_data/python/bastd/ui/specialoffer.py b/assets/src/ba_data/python/bastd/ui/specialoffer.py index 35273224..cb11339c 100644 --- a/assets/src/ba_data/python/bastd/ui/specialoffer.py +++ b/assets/src/ba_data/python/bastd/ui/specialoffer.py @@ -331,7 +331,6 @@ class SpecialOfferWindow(ba.Window): text=str(self._cancel_delay) if self._cancel_delay > 0 else '') def _update(self) -> None: - from ba.internal import have_pro # If we've got seconds left on our countdown, update it. if self._cancel_delay > 0: @@ -342,7 +341,7 @@ class SpecialOfferWindow(ba.Window): # We go away if we see that our target item is owned. if self._offer_item == 'pro': - if have_pro(): + if _ba.app.accounts.have_pro(): can_die = True else: if _ba.get_purchased(self._offer_item): diff --git a/assets/src/ba_data/python/bastd/ui/store/browser.py b/assets/src/ba_data/python/bastd/ui/store/browser.py index e7fe57f1..5ea8ad30 100644 --- a/assets/src/ba_data/python/bastd/ui/store/browser.py +++ b/assets/src/ba_data/python/bastd/ui/store/browser.py @@ -515,7 +515,7 @@ class StoreBrowserWindow(ba.Window): # 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.internal import get_available_sale_time from ba import SpecialChar if not self._root_widget: return @@ -539,7 +539,7 @@ class StoreBrowserWindow(ba.Window): for b_type, b_info in self.button_infos.items(): if b_type in ['upgrades.pro', 'pro']: - purchased = have_pro() + purchased = _ba.app.accounts.have_pro() else: purchased = _ba.get_purchased(b_type) diff --git a/assets/src/ba_data/python/bastd/ui/tournamententry.py b/assets/src/ba_data/python/bastd/ui/tournamententry.py index 62a4080f..b4ee89c0 100644 --- a/assets/src/ba_data/python/bastd/ui/tournamententry.py +++ b/assets/src/ba_data/python/bastd/ui/tournamententry.py @@ -33,7 +33,8 @@ class TournamentEntryWindow(popup.PopupWindow): ba.set_analytics_screen('Tournament Entry Window') self._tournament_id = tournament_id - self._tournament_info = (ba.app.tournament_info[self._tournament_id]) + self._tournament_info = ( + ba.app.accounts.tournament_info[self._tournament_id]) # Set a few vars depending on the tourney fee. self._fee = self._tournament_info['fee'] @@ -264,13 +265,13 @@ class TournamentEntryWindow(popup.PopupWindow): # If there seems to be a relatively-recent valid cached info for this # tournament, use it. Otherwise we'll kick off a query ourselves. - if (self._tournament_id in ba.app.tournament_info - and ba.app.tournament_info[self._tournament_id]['valid'] and - (ba.time(ba.TimeType.REAL, ba.TimeFormat.MILLISECONDS) - - ba.app.tournament_info[self._tournament_id]['timeReceived'] < - 1000 * 60 * 5)): + if (self._tournament_id in ba.app.accounts.tournament_info and + ba.app.accounts.tournament_info[self._tournament_id]['valid'] + and (ba.time(ba.TimeType.REAL, ba.TimeFormat.MILLISECONDS) - + ba.app.accounts.tournament_info[self._tournament_id] + ['timeReceived'] < 1000 * 60 * 5)): try: - info = ba.app.tournament_info[self._tournament_id] + info = ba.app.accounts.tournament_info[self._tournament_id] self._seconds_remaining = max( 0, info['timeRemaining'] - int( (ba.time(ba.TimeType.REAL, ba.TimeFormat.MILLISECONDS) @@ -294,12 +295,12 @@ class TournamentEntryWindow(popup.PopupWindow): def _on_tournament_query_response(self, data: Optional[Dict[str, Any]]) -> None: - from ba.internal import cache_tournament_info + accounts = ba.app.accounts self._running_query = False if data is not None: data = data['t'] # This used to be the whole payload. - cache_tournament_info(data) - self._seconds_remaining = ba.app.tournament_info[ + accounts.cache_tournament_info(data) + self._seconds_remaining = accounts.tournament_info[ self._tournament_id]['timeRemaining'] self._have_valid_data = True @@ -348,7 +349,8 @@ class TournamentEntryWindow(popup.PopupWindow): self._running_query = True # Grab the latest info on our tourney. - self._tournament_info = ba.app.tournament_info[self._tournament_id] + self._tournament_info = ba.app.accounts.tournament_info[ + self._tournament_id] # If we don't have valid data always show a '-' for time. if not self._have_valid_data: diff --git a/src/generated_src/ballistica/binding.py b/src/generated_src/ballistica/binding.py index 17d2c7f6..c3742f4d 100644 --- a/src/generated_src/ballistica/binding.py +++ b/src/generated_src/ballistica/binding.py @@ -73,7 +73,7 @@ def get_binding_values() -> object: _hooks.do_quit, # kQuitCall _hooks.shutdown, # kShutdownCall _hooks.gc_disable, # kGCDisableCall - _account.show_post_purchase_message, # kShowPostPurchaseMessageCall + ba.app.accounts.show_post_purchase_message, # kShowPostPurchaseMessageCall _hooks.device_menu_press, # kDeviceMenuPressCall _hooks.show_url_window, # kShowURLWindowCall _hooks.party_invite_revoke, # kHandlePartyInviteRevokeCall