mirror of
https://github.com/imayushsaini/Bombsquad-Ballistica-Modded-Server.git
synced 2025-10-20 00:00:39 +00:00
updating ba, bastd
This commit is contained in:
parent
c2ddced5cf
commit
3c43b5661b
169 changed files with 13214 additions and 4232 deletions
64
dist/ba_data/python/ba/__init__.py
vendored
64
dist/ba_data/python/ba/__init__.py
vendored
|
|
@ -6,7 +6,6 @@ This top level module is a collection of most commonly used functionality.
|
|||
For many modding purposes, the bits exposed here are all you'll need.
|
||||
In some specific cases you may need to pull in individual submodules instead.
|
||||
"""
|
||||
# pylint: disable=unused-import
|
||||
# pylint: disable=redefined-builtin
|
||||
|
||||
from _ba import (
|
||||
|
|
@ -18,7 +17,7 @@ from _ba import (
|
|||
newnode, playsound, printnodes, printobjects, pushcall, quit, rowwidget,
|
||||
safecolor, screenmessage, scrollwidget, set_analytics_screen, charstr,
|
||||
textwidget, time, timer, open_url, widget, clipboard_is_supported,
|
||||
clipboard_has_text, clipboard_get_text, clipboard_set_text)
|
||||
clipboard_has_text, clipboard_get_text, clipboard_set_text, getdata)
|
||||
from ba._activity import Activity
|
||||
from ba._plugin import PotentialPlugin, Plugin, PluginSubsystem
|
||||
from ba._actor import Actor
|
||||
|
|
@ -81,13 +80,64 @@ from ba._collision import Collision, getcollision
|
|||
|
||||
app: App
|
||||
|
||||
__all__ = [
|
||||
'Achievement', 'AchievementSubsystem', 'Activity', 'ActivityNotFoundError',
|
||||
'Actor', 'ActorNotFoundError', 'animate', 'animate_array', 'app', 'App',
|
||||
'AppConfig', 'AppDelegate', 'AssetPackage', 'BoolSetting', 'buttonwidget',
|
||||
'Call', 'cameraflash', 'camerashake', 'Campaign', 'CelebrateMessage',
|
||||
'charstr', 'checkboxwidget', 'ChoiceSetting', 'Chooser',
|
||||
'clipboard_get_text', 'clipboard_has_text', 'clipboard_is_supported',
|
||||
'clipboard_set_text', 'CollideModel', 'Collision', 'columnwidget',
|
||||
'containerwidget', 'Context', 'ContextCall', 'ContextError',
|
||||
'CoopGameActivity', 'CoopSession', 'Data', 'DeathType',
|
||||
'DelegateNotFoundError', 'Dependency', 'DependencyComponent',
|
||||
'DependencyError', 'DependencySet', 'DieMessage', 'do_once', 'DropMessage',
|
||||
'DroppedMessage', 'DualTeamSession', 'emitfx', 'EmptyPlayer', 'EmptyTeam',
|
||||
'Existable', 'existing', 'FloatChoiceSetting', 'FloatSetting',
|
||||
'FreeForAllSession', 'FreezeMessage', 'GameActivity', 'GameResults',
|
||||
'GameTip', 'garbage_collect', 'getactivity', 'getclass', 'getcollidemodel',
|
||||
'getcollision', 'getdata', 'getmaps', 'getmodel', 'getnodes', 'getsession',
|
||||
'getsound', 'gettexture', 'HitMessage', 'hscrollwidget', 'imagewidget',
|
||||
'ImpactDamageMessage', 'InputDevice', 'InputDeviceNotFoundError',
|
||||
'InputType', 'IntChoiceSetting', 'IntSetting',
|
||||
'is_browser_likely_available', 'is_point_in_box', 'Keyboard',
|
||||
'LanguageSubsystem', 'Level', 'Lobby', 'log', 'Lstr', 'Map', 'Material',
|
||||
'MetadataSubsystem', 'Model', 'MultiTeamSession', 'MusicPlayer',
|
||||
'MusicPlayMode', 'MusicSubsystem', 'MusicType', 'newactivity', 'newnode',
|
||||
'Node', 'NodeActor', 'NodeNotFoundError', 'normalized_color',
|
||||
'NotFoundError', 'open_url', 'OutOfBoundsMessage', 'Permission',
|
||||
'PickedUpMessage', 'PickUpMessage', 'Player', 'PlayerDiedMessage',
|
||||
'PlayerInfo', 'PlayerNotFoundError', 'PlayerRecord', 'PlayerScoredMessage',
|
||||
'playsound', 'Plugin', 'PluginSubsystem', 'PotentialPlugin',
|
||||
'PowerupAcceptMessage', 'PowerupMessage', 'print_error', 'print_exception',
|
||||
'printnodes', 'printobjects', 'pushcall', 'quit', 'rowwidget', 'safecolor',
|
||||
'ScoreConfig', 'ScoreType', 'screenmessage', 'scrollwidget',
|
||||
'ServerController', 'Session', 'SessionNotFoundError', 'SessionPlayer',
|
||||
'SessionPlayerNotFoundError', 'SessionTeam', 'SessionTeamNotFoundError',
|
||||
'set_analytics_screen', 'setmusic', 'Setting', 'ShouldShatterMessage',
|
||||
'show_damage_count', 'Sound', 'SpecialChar', 'StandLocation',
|
||||
'StandMessage', 'Stats', 'storagename', 'Team', 'TeamGameActivity',
|
||||
'TeamNotFoundError', 'Texture', 'textwidget', 'ThawMessage', 'time',
|
||||
'TimeFormat', 'Timer', 'timer', 'timestring', 'TimeType', 'uicleanupcheck',
|
||||
'UIController', 'UIScale', 'UISubsystem', 'UNHANDLED', 'Vec3',
|
||||
'vec3validate', 'verify_object_death', 'WeakCall', 'Widget', 'widget',
|
||||
'WidgetNotFoundError', 'Window'
|
||||
]
|
||||
|
||||
# Change everything's listed module to simply 'ba' (instead of 'ba.foo.bar').
|
||||
|
||||
# Have these things present themselves cleanly as 'ba.Foo'
|
||||
# instead of 'ba._submodule.Foo'
|
||||
def _simplify_module_names() -> None:
|
||||
for attr, obj in globals().items():
|
||||
if not attr.startswith('_'):
|
||||
if getattr(obj, '__module__', None) not in [None, 'ba']:
|
||||
obj.__module__ = 'ba'
|
||||
import os
|
||||
|
||||
# Though pdoc gets confused when we override __module__,
|
||||
# so let's make an exception for it.
|
||||
if os.environ.get('BA_DOCS_GENERATION', '0') != '1':
|
||||
from efro.util import set_canonical_module
|
||||
globs = globals()
|
||||
set_canonical_module(
|
||||
module_globals=globs,
|
||||
names=[n for n in globs.keys() if not n.startswith('_')])
|
||||
|
||||
|
||||
_simplify_module_names()
|
||||
|
|
|
|||
267
dist/ba_data/python/ba/_accountv1.py
vendored
Normal file
267
dist/ba_data/python/ba/_accountv1.py
vendored
Normal file
|
|
@ -0,0 +1,267 @@
|
|||
# Released under the MIT License. See LICENSE for details.
|
||||
#
|
||||
"""Account related functionality."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import copy
|
||||
import time
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import _ba
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Any, Optional
|
||||
|
||||
|
||||
class AccountV1Subsystem:
|
||||
"""Subsystem for legacy account handling in the app.
|
||||
|
||||
Category: **App Classes**
|
||||
|
||||
Access the single shared instance of this class at 'ba.app.accounts_v1'.
|
||||
"""
|
||||
|
||||
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."""
|
||||
|
||||
# Auto-sign-in to a local account in a moment if we're set to.
|
||||
def do_auto_sign_in() -> None:
|
||||
if _ba.app.headless_mode or _ba.app.config.get(
|
||||
'Auto Account State') == 'Local':
|
||||
_ba.sign_in_v1('Local')
|
||||
|
||||
_ba.pushcall(do_auto_sign_in)
|
||||
|
||||
def on_app_resume(self) -> None:
|
||||
"""Should be called when the app is resumed."""
|
||||
|
||||
# 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
|
||||
|
||||
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(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_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
|
||||
|
||||
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 data['p']:
|
||||
pro_mult = 1.0 + float(
|
||||
_ba.get_v1_account_misc_read_val('proPowerRankingBoost',
|
||||
0.0)) * 0.01
|
||||
else:
|
||||
pro_mult = 1.0
|
||||
|
||||
# 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)
|
||||
|
||||
def cache_tournament_info(self, info: Any) -> None:
|
||||
"""(internal)"""
|
||||
from ba._generated.enums import TimeType, TimeFormat
|
||||
for entry in info:
|
||||
cache_entry = self.tournament_info[entry['tournamentID']] = (
|
||||
copy.deepcopy(entry))
|
||||
|
||||
# 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_v1_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 ensure_have_account_player_profile(self) -> None:
|
||||
"""
|
||||
Ensure the standard account-named player profile exists;
|
||||
creating if needed.
|
||||
|
||||
(internal)
|
||||
"""
|
||||
# This only applies when we're signed in.
|
||||
if _ba.get_v1_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_v1_account_display_string(full=False)):
|
||||
return
|
||||
|
||||
config = _ba.app.config
|
||||
if ('Player Profiles' not in config
|
||||
or '__account__' not in config['Player Profiles']):
|
||||
|
||||
# Create a spaz with a nice default purply color.
|
||||
_ba.add_transaction({
|
||||
'type': 'ADD_PLAYER_PROFILE',
|
||||
'name': '__account__',
|
||||
'profile': {
|
||||
'character': 'Spaz',
|
||||
'color': [0.5, 0.25, 1.0],
|
||||
'highlight': [0.5, 0.25, 1.0]
|
||||
}
|
||||
})
|
||||
_ba.run_transactions()
|
||||
|
||||
def have_pro(self) -> bool:
|
||||
"""Return whether pro is currently unlocked."""
|
||||
|
||||
# Check our tickets-based pro upgrade and our two real-IAP based
|
||||
# upgrades. Also always 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(self) -> bool:
|
||||
"""Return whether pro-options are present.
|
||||
|
||||
This is True for owners of Pro or for old installs
|
||||
before Pro was a requirement for these options.
|
||||
"""
|
||||
|
||||
# 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_v1_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._generated.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_v1_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._generated.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_v1_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()
|
||||
51
dist/ba_data/python/ba/_accountv2.py
vendored
Normal file
51
dist/ba_data/python/ba/_accountv2.py
vendored
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
# Released under the MIT License. See LICENSE for details.
|
||||
#
|
||||
"""Account related functionality."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Optional
|
||||
|
||||
|
||||
class AccountV2Subsystem:
|
||||
"""Subsystem for modern account handling in the app.
|
||||
|
||||
Category: **App Classes**
|
||||
|
||||
Access the single shared instance of this class at 'ba.app.accounts_v2'.
|
||||
"""
|
||||
|
||||
def on_app_launch(self) -> None:
|
||||
"""Should be called at standard on_app_launch time."""
|
||||
|
||||
def set_primary_credentials(self, credentials: Optional[str]) -> None:
|
||||
"""Set credentials for the primary app account."""
|
||||
raise RuntimeError('This should be overridden.')
|
||||
|
||||
def have_primary_credentials(self) -> bool:
|
||||
"""Are credentials currently set for the primary app account?
|
||||
|
||||
Note that this does not mean these credentials are currently valid;
|
||||
only that they exist. If/when credentials are validated, the 'primary'
|
||||
account handle will be set.
|
||||
"""
|
||||
raise RuntimeError('This should be overridden.')
|
||||
|
||||
@property
|
||||
def primary(self) -> Optional[AccountV2Handle]:
|
||||
"""The primary account for the app, or None if not logged in."""
|
||||
return None
|
||||
|
||||
def get_primary(self) -> Optional[AccountV2Handle]:
|
||||
"""Internal - should be overridden by subclass."""
|
||||
return None
|
||||
|
||||
|
||||
class AccountV2Handle:
|
||||
"""Handle for interacting with a v2 account."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.tag = '?'
|
||||
19
dist/ba_data/python/ba/_achievement.py
vendored
19
dist/ba_data/python/ba/_achievement.py
vendored
|
|
@ -65,7 +65,7 @@ ACH_LEVEL_NAMES = {
|
|||
class AchievementSubsystem:
|
||||
"""Subsystem for achievement handling.
|
||||
|
||||
Category: App Classes
|
||||
Category: **App Classes**
|
||||
|
||||
Access the single shared instance of this class at 'ba.app.ach'.
|
||||
"""
|
||||
|
|
@ -409,9 +409,9 @@ def _get_ach_mult(include_pro_bonus: bool = False) -> int:
|
|||
|
||||
(just for display; changing this here won't affect actual rewards)
|
||||
"""
|
||||
val: int = _ba.get_account_misc_read_val('achAwardMult', 5)
|
||||
val: int = _ba.get_v1_account_misc_read_val('achAwardMult', 5)
|
||||
assert isinstance(val, int)
|
||||
if include_pro_bonus and _ba.app.accounts.have_pro():
|
||||
if include_pro_bonus and _ba.app.accounts_v1.have_pro():
|
||||
val *= 2
|
||||
return val
|
||||
|
||||
|
|
@ -436,7 +436,7 @@ def _display_next_achievement() -> None:
|
|||
class Achievement:
|
||||
"""Represents attributes and state for an individual achievement.
|
||||
|
||||
Category: App Classes
|
||||
Category: **App Classes**
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
|
|
@ -496,7 +496,7 @@ class Achievement:
|
|||
# signed in, lets not show them (otherwise we tend to get
|
||||
# confusing 'controller connected' achievements popping up while
|
||||
# waiting to log in which can be confusing).
|
||||
if _ba.get_account_state() != 'signed_in':
|
||||
if _ba.get_v1_account_state() != 'signed_in':
|
||||
return
|
||||
|
||||
# If we're being freshly complete, display/report it and whatnot.
|
||||
|
|
@ -592,8 +592,8 @@ class Achievement:
|
|||
|
||||
def get_award_ticket_value(self, include_pro_bonus: bool = False) -> int:
|
||||
"""Get the ticket award value for this achievement."""
|
||||
val: int = (_ba.get_account_misc_read_val('achAward.' + self._name,
|
||||
self._award) *
|
||||
val: int = (_ba.get_v1_account_misc_read_val('achAward.' + self._name,
|
||||
self._award) *
|
||||
_get_ach_mult(include_pro_bonus))
|
||||
assert isinstance(val, int)
|
||||
return val
|
||||
|
|
@ -601,7 +601,7 @@ class Achievement:
|
|||
@property
|
||||
def power_ranking_value(self) -> int:
|
||||
"""Get the power-ranking award value for this achievement."""
|
||||
val: int = _ba.get_account_misc_read_val(
|
||||
val: int = _ba.get_v1_account_misc_read_val(
|
||||
'achLeaguePoints.' + self._name, self._award)
|
||||
assert isinstance(val, int)
|
||||
return val
|
||||
|
|
@ -916,7 +916,6 @@ class Achievement:
|
|||
|
||||
def show_completion_banner(self, sound: bool = True) -> None:
|
||||
"""Create the banner/sound for an acquired achievement announcement."""
|
||||
from ba import _account
|
||||
from ba import _gameutils
|
||||
from bastd.actor.text import Text
|
||||
from bastd.actor.image import Image
|
||||
|
|
@ -1177,7 +1176,7 @@ class Achievement:
|
|||
objt.node.host_only = True
|
||||
|
||||
# Add the 'x 2' if we've got pro.
|
||||
if app.accounts.have_pro():
|
||||
if app.accounts_v1.have_pro():
|
||||
objt = Text('x 2',
|
||||
position=(-120 - 180 + 45, 80 + y_offs - 50),
|
||||
v_attach=Text.VAttach.BOTTOM,
|
||||
|
|
|
|||
130
dist/ba_data/python/ba/_activity.py
vendored
130
dist/ba_data/python/ba/_activity.py
vendored
|
|
@ -18,10 +18,11 @@ from ba._messages import UNHANDLED
|
|||
if TYPE_CHECKING:
|
||||
from typing import Optional, Any
|
||||
import ba
|
||||
from bastd.actor.respawnicon import RespawnIcon
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
PlayerType = TypeVar('PlayerType', bound=Player)
|
||||
TeamType = TypeVar('TeamType', bound=Team)
|
||||
# pylint: enable=invalid-name
|
||||
|
||||
|
||||
class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
|
||||
|
|
@ -32,104 +33,97 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
|
|||
Examples of Activities include games, score-screens, cutscenes, etc.
|
||||
A ba.Session has one 'current' Activity at any time, though their existence
|
||||
can overlap during transitions.
|
||||
|
||||
Attributes:
|
||||
|
||||
settings_raw
|
||||
The settings dict passed in when the activity was made.
|
||||
This attribute is deprecated and should be avoided when possible;
|
||||
activities should pull all values they need from the 'settings' arg
|
||||
passed to the Activity __init__ call.
|
||||
|
||||
teams
|
||||
The list of ba.Teams in the Activity. This gets populated just before
|
||||
before on_begin() is called and is updated automatically as players
|
||||
join or leave the game. (at least in free-for-all mode where every
|
||||
player gets their own team; in teams mode there are always 2 teams
|
||||
regardless of the player count).
|
||||
|
||||
players
|
||||
The list of ba.Players in the Activity. This gets populated just
|
||||
before on_begin() is called and is updated automatically as players
|
||||
join or leave the game.
|
||||
"""
|
||||
|
||||
# pylint: disable=too-many-public-methods
|
||||
|
||||
# Annotating attr types at the class level lets us introspect at runtime.
|
||||
settings_raw: dict[str, Any]
|
||||
"""The settings dict passed in when the activity was made.
|
||||
This attribute is deprecated and should be avoided when possible;
|
||||
activities should pull all values they need from the 'settings' arg
|
||||
passed to the Activity __init__ call."""
|
||||
|
||||
teams: list[TeamType]
|
||||
"""The list of ba.Team-s in the Activity. This gets populated just
|
||||
before on_begin() is called and is updated automatically as players
|
||||
join or leave the game. (at least in free-for-all mode where every
|
||||
player gets their own team; in teams mode there are always 2 teams
|
||||
regardless of the player count)."""
|
||||
|
||||
players: list[PlayerType]
|
||||
"""The list of ba.Player-s in the Activity. This gets populated just
|
||||
before on_begin() is called and is updated automatically as players
|
||||
join or leave the game."""
|
||||
|
||||
# Whether to print every time a player dies. This can be pertinent
|
||||
# in games such as Death-Match but can be annoying in games where it
|
||||
# doesn't matter.
|
||||
announce_player_deaths = False
|
||||
"""Whether to print every time a player dies. This can be pertinent
|
||||
in games such as Death-Match but can be annoying in games where it
|
||||
doesn't matter."""
|
||||
|
||||
# Joining activities are for waiting for initial player joins.
|
||||
# They are treated slightly differently than regular activities,
|
||||
# mainly in that all players are passed to the activity at once
|
||||
# instead of as each joins.
|
||||
is_joining_activity = False
|
||||
"""Joining activities are for waiting for initial player joins.
|
||||
They are treated slightly differently than regular activities,
|
||||
mainly in that all players are passed to the activity at once
|
||||
instead of as each joins."""
|
||||
|
||||
# Whether game-time should still progress when in menus/etc.
|
||||
allow_pausing = False
|
||||
"""Whether game-time should still progress when in menus/etc."""
|
||||
|
||||
# Whether idle players can potentially be kicked (should not happen in
|
||||
# menus/etc).
|
||||
allow_kick_idle_players = True
|
||||
"""Whether idle players can potentially be kicked (should not happen in
|
||||
menus/etc)."""
|
||||
|
||||
# In vr mode, this determines whether overlay nodes (text, images, etc)
|
||||
# are created at a fixed position in space or one that moves based on
|
||||
# the current map. Generally this should be on for games and off for
|
||||
# transitions/score-screens/etc. that persist between maps.
|
||||
use_fixed_vr_overlay = False
|
||||
"""In vr mode, this determines whether overlay nodes (text, images, etc)
|
||||
are created at a fixed position in space or one that moves based on
|
||||
the current map. Generally this should be on for games and off for
|
||||
transitions/score-screens/etc. that persist between maps."""
|
||||
|
||||
# If True, runs in slow motion and turns down sound pitch.
|
||||
slow_motion = False
|
||||
"""If True, runs in slow motion and turns down sound pitch."""
|
||||
|
||||
# Set this to True to inherit slow motion setting from previous
|
||||
# activity (useful for transitions to avoid hitches).
|
||||
inherits_slow_motion = False
|
||||
"""Set this to True to inherit slow motion setting from previous
|
||||
activity (useful for transitions to avoid hitches)."""
|
||||
|
||||
# Set this to True to keep playing the music from the previous activity
|
||||
# (without even restarting it).
|
||||
inherits_music = False
|
||||
"""Set this to True to keep playing the music from the previous activity
|
||||
(without even restarting it)."""
|
||||
|
||||
# Set this to true to inherit VR camera offsets from the previous
|
||||
# activity (useful for preventing sporadic camera movement
|
||||
# during transitions).
|
||||
inherits_vr_camera_offset = False
|
||||
"""Set this to true to inherit VR camera offsets from the previous
|
||||
activity (useful for preventing sporadic camera movement
|
||||
during transitions)."""
|
||||
|
||||
# Set this to true to inherit (non-fixed) VR overlay positioning from
|
||||
# the previous activity (useful for prevent sporadic overlay jostling
|
||||
# during transitions).
|
||||
inherits_vr_overlay_center = False
|
||||
"""Set this to true to inherit (non-fixed) VR overlay positioning from
|
||||
the previous activity (useful for prevent sporadic overlay jostling
|
||||
during transitions)."""
|
||||
|
||||
# Set this to true to inherit screen tint/vignette colors from the
|
||||
# previous activity (useful to prevent sudden color changes during
|
||||
# transitions).
|
||||
inherits_tint = False
|
||||
"""Set this to true to inherit screen tint/vignette colors from the
|
||||
previous activity (useful to prevent sudden color changes during
|
||||
transitions)."""
|
||||
|
||||
# Whether players should be allowed to join in the middle of this
|
||||
# activity. Note that Sessions may not allow mid-activity-joins even
|
||||
# if the activity says its ok.
|
||||
allow_mid_activity_joins: bool = True
|
||||
"""Whether players should be allowed to join in the middle of this
|
||||
activity. Note that Sessions may not allow mid-activity-joins even
|
||||
if the activity says its ok."""
|
||||
|
||||
# If the activity fades or transitions in, it should set the length of
|
||||
# time here so that previous activities will be kept alive for that
|
||||
# long (avoiding 'holes' in the screen)
|
||||
# This value is given in real-time seconds.
|
||||
transition_time = 0.0
|
||||
"""If the activity fades or transitions in, it should set the length of
|
||||
time here so that previous activities will be kept alive for that
|
||||
long (avoiding 'holes' in the screen)
|
||||
This value is given in real-time seconds."""
|
||||
|
||||
# Is it ok to show an ad after this activity ends before showing
|
||||
# the next activity?
|
||||
can_show_ad_on_death = False
|
||||
"""Is it ok to show an ad after this activity ends before showing
|
||||
the next activity?"""
|
||||
|
||||
def __init__(self, settings: dict):
|
||||
"""Creates an Activity in the current ba.Session.
|
||||
|
||||
The activity will not be actually run until ba.Session.setactivity()
|
||||
The activity will not be actually run until ba.Session.setactivity
|
||||
is called. 'settings' should be a dict of key/value pairs specific
|
||||
to the activity.
|
||||
|
||||
|
|
@ -369,8 +363,8 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
|
|||
def on_transition_out(self) -> None:
|
||||
"""Called when your activity begins transitioning out.
|
||||
|
||||
Note that this may happen at any time even if end() has not been
|
||||
called.
|
||||
Note that this may happen at any time even if ba.Activity.end() has
|
||||
not been called.
|
||||
"""
|
||||
|
||||
def on_begin(self) -> None:
|
||||
|
|
@ -386,11 +380,12 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
|
|||
return UNHANDLED
|
||||
|
||||
def has_transitioned_in(self) -> bool:
|
||||
"""Return whether on_transition_in() has been called."""
|
||||
"""Return whether ba.Activity.on_transition_in()
|
||||
has been called."""
|
||||
return self._has_transitioned_in
|
||||
|
||||
def has_begun(self) -> bool:
|
||||
"""Return whether on_begin() has been called."""
|
||||
"""Return whether ba.Activity.on_begin() has been called."""
|
||||
return self._has_begun
|
||||
|
||||
def has_ended(self) -> bool:
|
||||
|
|
@ -398,7 +393,7 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
|
|||
return self._has_ended
|
||||
|
||||
def is_transitioning_out(self) -> bool:
|
||||
"""Return whether on_transition_out() has been called."""
|
||||
"""Return whether ba.Activity.on_transition_out() has been called."""
|
||||
return self._transitioning_out
|
||||
|
||||
def transition_in(self, prev_globals: Optional[ba.Node]) -> None:
|
||||
|
|
@ -517,8 +512,8 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
|
|||
Subclasses can override this if the activity's player class
|
||||
requires a custom constructor; otherwise it will be called with
|
||||
no args. Note that the player object should not be used at this
|
||||
point as it is not yet fully wired up; wait for on_player_join()
|
||||
for that.
|
||||
point as it is not yet fully wired up; wait for
|
||||
ba.Activity.on_player_join() for that.
|
||||
"""
|
||||
del sessionplayer # Unused.
|
||||
player = self._playertype()
|
||||
|
|
@ -681,6 +676,7 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
|
|||
sessionplayer.setactivity(None)
|
||||
sessionplayer.activityplayer = None
|
||||
|
||||
# noinspection PyUnresolvedReferences
|
||||
def _setup_player_and_team_types(self) -> None:
|
||||
"""Pull player and team types from our typing.Generic params."""
|
||||
|
||||
|
|
|
|||
43
dist/ba_data/python/ba/_actor.py
vendored
43
dist/ba_data/python/ba/_actor.py
vendored
|
|
@ -21,7 +21,7 @@ TA = TypeVar('TA', bound='Actor')
|
|||
class Actor:
|
||||
"""High level logical entities in a ba.Activity.
|
||||
|
||||
Category: Gameplay Classes
|
||||
Category: **Gameplay Classes**
|
||||
|
||||
Actors act as controllers, combining some number of ba.Nodes,
|
||||
ba.Textures, ba.Sounds, etc. into a high-level cohesive unit.
|
||||
|
|
@ -33,15 +33,16 @@ class Actor:
|
|||
(killing off or transitioning out their nodes) when the last Python
|
||||
reference to them disappears, so you can use logic such as:
|
||||
|
||||
# Create a flag Actor in our game activity:
|
||||
from bastd.actor.flag import Flag
|
||||
self.flag = Flag(position=(0, 10, 0))
|
||||
|
||||
# Later, destroy the flag.
|
||||
# (provided nothing else is holding a reference to it)
|
||||
# We could also just assign a new flag to this value.
|
||||
# Either way, the old flag disappears.
|
||||
self.flag = None
|
||||
##### Example
|
||||
>>> # Create a flag Actor in our game activity:
|
||||
... from bastd.actor.flag import Flag
|
||||
... self.flag = Flag(position=(0, 10, 0))
|
||||
...
|
||||
... # Later, destroy the flag.
|
||||
... # (provided nothing else is holding a reference to it)
|
||||
... # We could also just assign a new flag to this value.
|
||||
... # Either way, the old flag disappears.
|
||||
... self.flag = None
|
||||
|
||||
This is in contrast to the behavior of the more low level ba.Nodes,
|
||||
which are always explicitly created and destroyed and don't care
|
||||
|
|
@ -51,18 +52,18 @@ class Actor:
|
|||
if you want an Actor to stick around until explicitly killed
|
||||
regardless of references.
|
||||
|
||||
Another key feature of ba.Actor is its handlemessage() method, which
|
||||
takes a single arbitrary object as an argument. This provides a safe way
|
||||
to communicate between ba.Actor, ba.Activity, ba.Session, and any other
|
||||
class providing a handlemessage() method. The most universally handled
|
||||
Another key feature of ba.Actor is its ba.Actor.handlemessage() method,
|
||||
which takes a single arbitrary object as an argument. This provides a safe
|
||||
way to communicate between ba.Actor, ba.Activity, ba.Session, and any other
|
||||
class providing a handlemessage() method. The most universally handled
|
||||
message type for Actors is the ba.DieMessage.
|
||||
|
||||
# Another way to kill the flag from the example above:
|
||||
# We can safely call this on any type with a 'handlemessage' method
|
||||
# (though its not guaranteed to always have a meaningful effect).
|
||||
# In this case the Actor instance will still be around, but its exists()
|
||||
# and is_alive() methods will both return False.
|
||||
self.flag.handlemessage(ba.DieMessage())
|
||||
Another way to kill the flag from the example above:
|
||||
We can safely call this on any type with a 'handlemessage' method
|
||||
(though its not guaranteed to always have a meaningful effect).
|
||||
In this case the Actor instance will still be around, but its
|
||||
ba.Actor.exists() and ba.Actor.is_alive() methods will both return False.
|
||||
>>> self.flag.handlemessage(ba.DieMessage())
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
|
|
@ -112,7 +113,7 @@ class Actor:
|
|||
return self
|
||||
|
||||
def on_expire(self) -> None:
|
||||
"""Called for remaining ba.Actors when their ba.Activity shuts down.
|
||||
"""Called for remaining `ba.Actor`s when their ba.Activity shuts down.
|
||||
|
||||
Actors can use this opportunity to clear callbacks or other
|
||||
references which have the potential of keeping the ba.Activity
|
||||
|
|
|
|||
22
dist/ba_data/python/ba/_ads.py
vendored
22
dist/ba_data/python/ba/_ads.py
vendored
|
|
@ -15,7 +15,7 @@ if TYPE_CHECKING:
|
|||
class AdsSubsystem:
|
||||
"""Subsystem for ads functionality in the app.
|
||||
|
||||
Category: App Classes
|
||||
Category: **App Classes**
|
||||
|
||||
Access the single shared instance of this class at 'ba.app.ads'.
|
||||
"""
|
||||
|
|
@ -77,7 +77,7 @@ class AdsSubsystem:
|
|||
# No ads without net-connections, etc.
|
||||
if not _ba.can_show_ad():
|
||||
show = False
|
||||
if app.accounts.have_pro():
|
||||
if app.accounts_v1.have_pro():
|
||||
show = False # Pro disables interstitials.
|
||||
try:
|
||||
session = _ba.get_foreground_host_session()
|
||||
|
|
@ -93,15 +93,15 @@ class AdsSubsystem:
|
|||
launch_count = app.config.get('launchCount', 0)
|
||||
|
||||
# If we're seeing short ads we may want to space them differently.
|
||||
interval_mult = (_ba.get_account_misc_read_val(
|
||||
interval_mult = (_ba.get_v1_account_misc_read_val(
|
||||
'ads.shortIntervalMult', 1.0)
|
||||
if self.last_ad_was_short else 1.0)
|
||||
if self.ad_amt is None:
|
||||
if launch_count <= 1:
|
||||
self.ad_amt = _ba.get_account_misc_read_val(
|
||||
self.ad_amt = _ba.get_v1_account_misc_read_val(
|
||||
'ads.startVal1', 0.99)
|
||||
else:
|
||||
self.ad_amt = _ba.get_account_misc_read_val(
|
||||
self.ad_amt = _ba.get_v1_account_misc_read_val(
|
||||
'ads.startVal2', 1.0)
|
||||
interval = None
|
||||
else:
|
||||
|
|
@ -110,15 +110,15 @@ class AdsSubsystem:
|
|||
# (we reach our threshold faster the longer we've been
|
||||
# playing).
|
||||
base = 'ads' if _ba.has_video_ads() else 'ads2'
|
||||
min_lc = _ba.get_account_misc_read_val(base + '.minLC', 0.0)
|
||||
max_lc = _ba.get_account_misc_read_val(base + '.maxLC', 5.0)
|
||||
min_lc_scale = (_ba.get_account_misc_read_val(
|
||||
min_lc = _ba.get_v1_account_misc_read_val(base + '.minLC', 0.0)
|
||||
max_lc = _ba.get_v1_account_misc_read_val(base + '.maxLC', 5.0)
|
||||
min_lc_scale = (_ba.get_v1_account_misc_read_val(
|
||||
base + '.minLCScale', 0.25))
|
||||
max_lc_scale = (_ba.get_account_misc_read_val(
|
||||
max_lc_scale = (_ba.get_v1_account_misc_read_val(
|
||||
base + '.maxLCScale', 0.34))
|
||||
min_lc_interval = (_ba.get_account_misc_read_val(
|
||||
min_lc_interval = (_ba.get_v1_account_misc_read_val(
|
||||
base + '.minLCInterval', 360))
|
||||
max_lc_interval = (_ba.get_account_misc_read_val(
|
||||
max_lc_interval = (_ba.get_v1_account_misc_read_val(
|
||||
base + '.maxLCInterval', 300))
|
||||
if launch_count < min_lc:
|
||||
lc_amt = 0.0
|
||||
|
|
|
|||
54
dist/ba_data/python/ba/_app.py
vendored
54
dist/ba_data/python/ba/_app.py
vendored
|
|
@ -7,6 +7,7 @@ import random
|
|||
import logging
|
||||
from enum import Enum
|
||||
from typing import TYPE_CHECKING
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
|
||||
import _ba
|
||||
from ba._music import MusicSubsystem
|
||||
|
|
@ -14,21 +15,25 @@ from ba._language import LanguageSubsystem
|
|||
from ba._ui import UISubsystem
|
||||
from ba._achievement import AchievementSubsystem
|
||||
from ba._plugin import PluginSubsystem
|
||||
from ba._account import AccountSubsystem
|
||||
from ba._accountv1 import AccountV1Subsystem
|
||||
from ba._meta import MetadataSubsystem
|
||||
from ba._ads import AdsSubsystem
|
||||
from ba._net import NetworkSubsystem
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import ba
|
||||
from bastd.actor import spazappearance
|
||||
import asyncio
|
||||
from typing import Optional, Any, Callable
|
||||
|
||||
import ba
|
||||
from ba.cloud import CloudSubsystem
|
||||
from bastd.actor import spazappearance
|
||||
from ba._accountv2 import AccountV2Subsystem
|
||||
|
||||
|
||||
class App:
|
||||
"""A class for high level app functionality and state.
|
||||
|
||||
Category: App Classes
|
||||
Category: **App Classes**
|
||||
|
||||
Use ba.app to access the single shared instance of this class.
|
||||
|
||||
|
|
@ -38,6 +43,10 @@ class App:
|
|||
|
||||
# pylint: disable=too-many-public-methods
|
||||
|
||||
# Implementations for these will be filled in by internal libs.
|
||||
accounts_v2: AccountV2Subsystem
|
||||
cloud: CloudSubsystem
|
||||
|
||||
class State(Enum):
|
||||
"""High level state the app can be in."""
|
||||
LAUNCHING = 0
|
||||
|
|
@ -45,6 +54,20 @@ class App:
|
|||
PAUSED = 2
|
||||
SHUTTING_DOWN = 3
|
||||
|
||||
@property
|
||||
def aioloop(self) -> asyncio.AbstractEventLoop:
|
||||
"""The Logic Thread's Asyncio Event Loop.
|
||||
|
||||
This allow async tasks to be run in the logic thread.
|
||||
Note that, at this time, the asyncio loop is encapsulated
|
||||
and explicitly stepped by the engine's logic thread loop and
|
||||
thus things like asyncio.get_running_loop() will not return this
|
||||
loop from most places in the logic thread; only from within a
|
||||
task explicitly created in this loop.
|
||||
"""
|
||||
assert self._aioloop is not None
|
||||
return self._aioloop
|
||||
|
||||
@property
|
||||
def build_number(self) -> int:
|
||||
"""Integer build number.
|
||||
|
|
@ -196,6 +219,8 @@ class App:
|
|||
# refreshed/etc.
|
||||
self.fg_state = 0
|
||||
|
||||
self._aioloop: Optional[asyncio.AbstractEventLoop] = None
|
||||
|
||||
self._env = _ba.env()
|
||||
self.protocol_version: int = self._env['protocol_version']
|
||||
assert isinstance(self.protocol_version, int)
|
||||
|
|
@ -211,6 +236,11 @@ class App:
|
|||
assert isinstance(self.iircade_mode, bool)
|
||||
self.allow_ticket_purchases: bool = not self.iircade_mode
|
||||
|
||||
# Default executor which can be used for misc background processing.
|
||||
# It should also be passed to any asyncio loops we create so that
|
||||
# everything shares the same single set of threads.
|
||||
self.threadpool = ThreadPoolExecutor(thread_name_prefix='baworker')
|
||||
|
||||
# Misc.
|
||||
self.tips: list[str] = []
|
||||
self.stress_test_reset_timer: Optional[ba.Timer] = None
|
||||
|
|
@ -235,7 +265,7 @@ class App:
|
|||
self.server: Optional[ba.ServerController] = None
|
||||
|
||||
self.meta = MetadataSubsystem()
|
||||
self.accounts = AccountSubsystem()
|
||||
self.accounts_v1 = AccountV1Subsystem()
|
||||
self.plugins = PluginSubsystem()
|
||||
self.music = MusicSubsystem()
|
||||
self.lang = LanguageSubsystem()
|
||||
|
|
@ -285,11 +315,11 @@ class App:
|
|||
"""Runs after the app finishes bootstrapping.
|
||||
|
||||
(internal)"""
|
||||
# pylint: disable=too-many-locals
|
||||
# pylint: disable=cyclic-import
|
||||
# pylint: disable=too-many-locals
|
||||
from ba import _asyncio
|
||||
from ba import _apputils
|
||||
from ba import _appconfig
|
||||
from ba import _achievement
|
||||
from ba import _map
|
||||
from ba import _campaign
|
||||
from bastd import appdelegate
|
||||
|
|
@ -299,6 +329,8 @@ class App:
|
|||
import custom_hooks
|
||||
custom_hooks.on_app_launch()
|
||||
|
||||
self._aioloop = _asyncio.setup_asyncio()
|
||||
|
||||
cfg = self.config
|
||||
|
||||
self.delegate = appdelegate.AppDelegate()
|
||||
|
|
@ -369,7 +401,8 @@ class App:
|
|||
_ba.timer(3.0, check_special_offer, timetype=TimeType.REAL)
|
||||
|
||||
self.meta.on_app_launch()
|
||||
self.accounts.on_app_launch()
|
||||
self.accounts_v2.on_app_launch()
|
||||
self.accounts_v1.on_app_launch()
|
||||
self.plugins.on_app_launch()
|
||||
|
||||
# See note below in on_app_pause.
|
||||
|
|
@ -407,7 +440,7 @@ class App:
|
|||
self._app_paused = False
|
||||
self._update_state()
|
||||
self.fg_state += 1
|
||||
self.accounts.on_app_resume()
|
||||
self.accounts_v1.on_app_resume()
|
||||
self.music.on_app_resume()
|
||||
self.plugins.on_app_resume()
|
||||
|
||||
|
|
@ -431,7 +464,6 @@ class App:
|
|||
activity: Optional[ba.Activity] = _ba.get_foreground_host_activity()
|
||||
if (activity is not None and activity.allow_pausing
|
||||
and not _ba.have_connected_clients()):
|
||||
from ba import _gameutils
|
||||
from ba._language import Lstr
|
||||
from ba._nodeactor import NodeActor
|
||||
|
||||
|
|
@ -574,7 +606,7 @@ class App:
|
|||
appname = _ba.appname()
|
||||
if url.startswith(f'{appname}://code/'):
|
||||
code = url.replace(f'{appname}://code/', '')
|
||||
self.accounts.add_pending_promo_code(code)
|
||||
self.accounts_v1.add_pending_promo_code(code)
|
||||
else:
|
||||
_ba.screenmessage(Lstr(resource='errorText'), color=(1, 0, 0))
|
||||
_ba.playsound(_ba.getsound('error'))
|
||||
|
|
|
|||
16
dist/ba_data/python/ba/_appconfig.py
vendored
16
dist/ba_data/python/ba/_appconfig.py
vendored
|
|
@ -14,7 +14,7 @@ if TYPE_CHECKING:
|
|||
class AppConfig(dict):
|
||||
"""A special dict that holds the game's persistent configuration values.
|
||||
|
||||
Category: App Classes
|
||||
Category: **App Classes**
|
||||
|
||||
It also provides methods for fetching values with app-defined fallback
|
||||
defaults, applying contained values to the game, and committing the
|
||||
|
|
@ -126,14 +126,14 @@ def read_config() -> tuple[AppConfig, bool]:
|
|||
try:
|
||||
import shutil
|
||||
shutil.copyfile(config_file_path, config_file_path + '.broken')
|
||||
except Exception as exc:
|
||||
print('EXC copying broken config:', exc)
|
||||
except Exception as exc2:
|
||||
print('EXC copying broken config:', exc2)
|
||||
try:
|
||||
_ba.log('broken config contents:\n' +
|
||||
config_contents.replace('\000', '<NULL_BYTE>'),
|
||||
to_stdout=False)
|
||||
except Exception as exc:
|
||||
print('EXC logging broken config contents:', exc)
|
||||
except Exception as exc2:
|
||||
print('EXC logging broken config contents:', exc2)
|
||||
config = AppConfig()
|
||||
|
||||
# Now attempt to read one of our 'prev' backup copies.
|
||||
|
|
@ -147,15 +147,15 @@ def read_config() -> tuple[AppConfig, bool]:
|
|||
config = AppConfig()
|
||||
config_file_healthy = True
|
||||
print('successfully read backup config.')
|
||||
except Exception as exc:
|
||||
print('EXC reading prev backup config:', exc)
|
||||
except Exception as exc2:
|
||||
print('EXC reading prev backup config:', exc2)
|
||||
return config, config_file_healthy
|
||||
|
||||
|
||||
def commit_app_config(force: bool = False) -> None:
|
||||
"""Commit the config to persistent storage.
|
||||
|
||||
Category: General Utility Functions
|
||||
Category: **General Utility Functions**
|
||||
|
||||
(internal)
|
||||
"""
|
||||
|
|
|
|||
8
dist/ba_data/python/ba/_asyncio.py
vendored
8
dist/ba_data/python/ba/_asyncio.py
vendored
|
|
@ -21,11 +21,12 @@ _asyncio_timer: Optional[ba.Timer] = None
|
|||
_asyncio_event_loop: Optional[asyncio.AbstractEventLoop] = None
|
||||
|
||||
|
||||
def setup_asyncio() -> None:
|
||||
"""Setup asyncio functionality for our game thread."""
|
||||
def setup_asyncio() -> asyncio.AbstractEventLoop:
|
||||
"""Setup asyncio functionality for the logic thread."""
|
||||
# pylint: disable=global-statement
|
||||
|
||||
import _ba
|
||||
import ba
|
||||
from ba._generated.enums import TimeType
|
||||
|
||||
assert _ba.in_game_thread()
|
||||
|
|
@ -40,6 +41,7 @@ def setup_asyncio() -> None:
|
|||
|
||||
global _asyncio_event_loop # pylint: disable=invalid-name
|
||||
_asyncio_event_loop = asyncio.new_event_loop()
|
||||
_asyncio_event_loop.set_default_executor(ba.app.threadpool)
|
||||
|
||||
# Ideally we should integrate asyncio into our C++ Thread class's
|
||||
# low level event loop so that asyncio timers/sockets/etc. could
|
||||
|
|
@ -70,3 +72,5 @@ def setup_asyncio() -> None:
|
|||
print('TEST AIO TASK ENDING')
|
||||
|
||||
_asyncio_event_loop.create_task(aio_test())
|
||||
|
||||
return _asyncio_event_loop
|
||||
|
|
|
|||
6
dist/ba_data/python/ba/_campaign.py
vendored
6
dist/ba_data/python/ba/_campaign.py
vendored
|
|
@ -23,9 +23,9 @@ def getcampaign(name: str) -> ba.Campaign:
|
|||
|
||||
|
||||
class Campaign:
|
||||
"""Represents a unique set or series of ba.Levels.
|
||||
"""Represents a unique set or series of ba.Level-s.
|
||||
|
||||
Category: App Classes
|
||||
Category: **App Classes**
|
||||
"""
|
||||
|
||||
def __init__(self, name: str, sequential: bool = True):
|
||||
|
|
@ -52,7 +52,7 @@ class Campaign:
|
|||
|
||||
@property
|
||||
def levels(self) -> list[ba.Level]:
|
||||
"""The list of ba.Levels in the Campaign."""
|
||||
"""The list of ba.Level-s in the Campaign."""
|
||||
return self._levels
|
||||
|
||||
def getlevel(self, name: str) -> ba.Level:
|
||||
|
|
|
|||
4
dist/ba_data/python/ba/_collision.py
vendored
4
dist/ba_data/python/ba/_collision.py
vendored
|
|
@ -16,7 +16,7 @@ if TYPE_CHECKING:
|
|||
class Collision:
|
||||
"""A class providing info about occurring collisions.
|
||||
|
||||
Category: Gameplay Classes
|
||||
Category: **Gameplay Classes**
|
||||
"""
|
||||
|
||||
@property
|
||||
|
|
@ -67,6 +67,6 @@ _collision = Collision()
|
|||
def getcollision() -> Collision:
|
||||
"""Return the in-progress collision.
|
||||
|
||||
Category: Gameplay Functions
|
||||
Category: **Gameplay Functions**
|
||||
"""
|
||||
return _collision
|
||||
|
|
|
|||
4
dist/ba_data/python/ba/_coopgame.py
vendored
4
dist/ba_data/python/ba/_coopgame.py
vendored
|
|
@ -14,14 +14,16 @@ if TYPE_CHECKING:
|
|||
from bastd.actor.playerspaz import PlayerSpaz
|
||||
import ba
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
PlayerType = TypeVar('PlayerType', bound='ba.Player')
|
||||
TeamType = TypeVar('TeamType', bound='ba.Team')
|
||||
# pylint: enable=invalid-name
|
||||
|
||||
|
||||
class CoopGameActivity(GameActivity[PlayerType, TeamType]):
|
||||
"""Base class for cooperative-mode games.
|
||||
|
||||
Category: Gameplay Classes
|
||||
Category: **Gameplay Classes**
|
||||
"""
|
||||
|
||||
# We can assume our session is a CoopSession.
|
||||
|
|
|
|||
8
dist/ba_data/python/ba/_coopsession.py
vendored
8
dist/ba_data/python/ba/_coopsession.py
vendored
|
|
@ -132,7 +132,7 @@ class CoopSession(Session):
|
|||
# else:
|
||||
# nextlevel = None
|
||||
nextlevel=levels[(level.index+1)%len(levels)]
|
||||
|
||||
|
||||
if nextlevel:
|
||||
gametype = nextlevel.gametype
|
||||
settings = nextlevel.get_settings()
|
||||
|
|
@ -284,12 +284,12 @@ class CoopSession(Session):
|
|||
|
||||
if outcome=="victory" or outcome=="restart" or outcome=="defeat":
|
||||
outcome = 'next_level'
|
||||
|
||||
|
||||
|
||||
|
||||
if (isinstance(activity,
|
||||
(JoinActivity, CoopScoreScreen, TransitionActivity))) or True:
|
||||
|
||||
|
||||
from features import team_balancer
|
||||
team_balancer.checkToExitCoop()
|
||||
|
||||
|
|
@ -355,7 +355,7 @@ class CoopSession(Session):
|
|||
else:
|
||||
pass
|
||||
if False:
|
||||
|
||||
|
||||
|
||||
playerinfos: list[ba.PlayerInfo]
|
||||
|
||||
|
|
|
|||
10
dist/ba_data/python/ba/_dependency.py
vendored
10
dist/ba_data/python/ba/_dependency.py
vendored
|
|
@ -19,7 +19,7 @@ T = TypeVar('T', bound='DependencyComponent')
|
|||
class Dependency(Generic[T]):
|
||||
"""A dependency on a DependencyComponent (with an optional config).
|
||||
|
||||
Category: Dependency Classes
|
||||
Category: **Dependency Classes**
|
||||
|
||||
This class is used to request and access functionality provided
|
||||
by other DependencyComponent classes from a DependencyComponent class.
|
||||
|
|
@ -87,7 +87,7 @@ class Dependency(Generic[T]):
|
|||
class DependencyComponent:
|
||||
"""Base class for all classes that can act as or use dependencies.
|
||||
|
||||
category: Dependency Classes
|
||||
Category: **Dependency Classes**
|
||||
"""
|
||||
|
||||
_dep_entry: weakref.ref[DependencyEntry]
|
||||
|
|
@ -146,7 +146,7 @@ class DependencyEntry:
|
|||
# This allows us to inject its data properly before __init__().
|
||||
print('creating', self.cls)
|
||||
instance = self.cls.__new__(self.cls)
|
||||
# pylint: disable=protected-access
|
||||
# pylint: disable=protected-access, unnecessary-dunder-call
|
||||
instance._dep_entry = weakref.ref(self)
|
||||
instance.__init__() # type: ignore
|
||||
|
||||
|
|
@ -165,7 +165,7 @@ class DependencyEntry:
|
|||
class DependencySet(Generic[T]):
|
||||
"""Set of resolved dependencies and their associated data.
|
||||
|
||||
Category: Dependency Classes
|
||||
Category: **Dependency Classes**
|
||||
|
||||
To use DependencyComponents, a set must be created, resolved, and then
|
||||
loaded. The DependencyComponents are only valid while the set remains
|
||||
|
|
@ -291,7 +291,7 @@ class DependencySet(Generic[T]):
|
|||
class AssetPackage(DependencyComponent):
|
||||
"""ba.DependencyComponent representing a bundled package of game assets.
|
||||
|
||||
Category: Asset Classes
|
||||
Category: **Asset Classes**
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
|
|
|
|||
2
dist/ba_data/python/ba/_dualteamsession.py
vendored
2
dist/ba_data/python/ba/_dualteamsession.py
vendored
|
|
@ -15,7 +15,7 @@ if TYPE_CHECKING:
|
|||
class DualTeamSession(MultiTeamSession):
|
||||
"""ba.Session type for teams mode games.
|
||||
|
||||
Category: Gameplay Classes
|
||||
Category: **Gameplay Classes**
|
||||
"""
|
||||
|
||||
# Base class overrides:
|
||||
|
|
|
|||
32
dist/ba_data/python/ba/_error.py
vendored
32
dist/ba_data/python/ba/_error.py
vendored
|
|
@ -16,7 +16,7 @@ if TYPE_CHECKING:
|
|||
class DependencyError(Exception):
|
||||
"""Exception raised when one or more ba.Dependency items are missing.
|
||||
|
||||
category: Exception Classes
|
||||
Category: **Exception Classes**
|
||||
|
||||
(this will generally be missing assets).
|
||||
"""
|
||||
|
|
@ -34,7 +34,7 @@ class DependencyError(Exception):
|
|||
class ContextError(Exception):
|
||||
"""Exception raised when a call is made in an invalid context.
|
||||
|
||||
category: Exception Classes
|
||||
Category: **Exception Classes**
|
||||
|
||||
Examples of this include calling UI functions within an Activity context
|
||||
or calling scene manipulation functions outside of a game context.
|
||||
|
|
@ -44,91 +44,91 @@ class ContextError(Exception):
|
|||
class NotFoundError(Exception):
|
||||
"""Exception raised when a referenced object does not exist.
|
||||
|
||||
category: Exception Classes
|
||||
Category: **Exception Classes**
|
||||
"""
|
||||
|
||||
|
||||
class PlayerNotFoundError(NotFoundError):
|
||||
"""Exception raised when an expected ba.Player does not exist.
|
||||
|
||||
category: Exception Classes
|
||||
Category: **Exception Classes**
|
||||
"""
|
||||
|
||||
|
||||
class SessionPlayerNotFoundError(NotFoundError):
|
||||
"""Exception raised when an expected ba.SessionPlayer does not exist.
|
||||
|
||||
category: Exception Classes
|
||||
Category: **Exception Classes**
|
||||
"""
|
||||
|
||||
|
||||
class TeamNotFoundError(NotFoundError):
|
||||
"""Exception raised when an expected ba.Team does not exist.
|
||||
|
||||
category: Exception Classes
|
||||
Category: **Exception Classes**
|
||||
"""
|
||||
|
||||
|
||||
class DelegateNotFoundError(NotFoundError):
|
||||
"""Exception raised when an expected delegate object does not exist.
|
||||
|
||||
category: Exception Classes
|
||||
Category: **Exception Classes**
|
||||
"""
|
||||
|
||||
|
||||
class SessionTeamNotFoundError(NotFoundError):
|
||||
"""Exception raised when an expected ba.SessionTeam does not exist.
|
||||
|
||||
category: Exception Classes
|
||||
Category: **Exception Classes**
|
||||
"""
|
||||
|
||||
|
||||
class NodeNotFoundError(NotFoundError):
|
||||
"""Exception raised when an expected ba.Node does not exist.
|
||||
|
||||
category: Exception Classes
|
||||
Category: **Exception Classes**
|
||||
"""
|
||||
|
||||
|
||||
class ActorNotFoundError(NotFoundError):
|
||||
"""Exception raised when an expected ba.Actor does not exist.
|
||||
|
||||
category: Exception Classes
|
||||
Category: **Exception Classes**
|
||||
"""
|
||||
|
||||
|
||||
class ActivityNotFoundError(NotFoundError):
|
||||
"""Exception raised when an expected ba.Activity does not exist.
|
||||
|
||||
category: Exception Classes
|
||||
Category: **Exception Classes**
|
||||
"""
|
||||
|
||||
|
||||
class SessionNotFoundError(NotFoundError):
|
||||
"""Exception raised when an expected ba.Session does not exist.
|
||||
|
||||
category: Exception Classes
|
||||
Category: **Exception Classes**
|
||||
"""
|
||||
|
||||
|
||||
class InputDeviceNotFoundError(NotFoundError):
|
||||
"""Exception raised when an expected ba.InputDevice does not exist.
|
||||
|
||||
category: Exception Classes
|
||||
Category: **Exception Classes**
|
||||
"""
|
||||
|
||||
|
||||
class WidgetNotFoundError(NotFoundError):
|
||||
"""Exception raised when an expected ba.Widget does not exist.
|
||||
|
||||
category: Exception Classes
|
||||
Category: **Exception Classes**
|
||||
"""
|
||||
|
||||
|
||||
def print_exception(*args: Any, **keywds: Any) -> None:
|
||||
"""Print info about an exception along with pertinent context state.
|
||||
|
||||
category: General Utility Functions
|
||||
Category: **General Utility Functions**
|
||||
|
||||
Prints all arguments provided along with various info about the
|
||||
current context and the outstanding exception.
|
||||
|
|
@ -168,7 +168,7 @@ def print_exception(*args: Any, **keywds: Any) -> None:
|
|||
def print_error(err_str: str, once: bool = False) -> None:
|
||||
"""Print info about an error along with pertinent context state.
|
||||
|
||||
category: General Utility Functions
|
||||
Category: **General Utility Functions**
|
||||
|
||||
Prints all positional arguments provided along with various info about the
|
||||
current context.
|
||||
|
|
|
|||
2
dist/ba_data/python/ba/_freeforallsession.py
vendored
2
dist/ba_data/python/ba/_freeforallsession.py
vendored
|
|
@ -16,7 +16,7 @@ if TYPE_CHECKING:
|
|||
class FreeForAllSession(MultiTeamSession):
|
||||
"""ba.Session type for free-for-all mode games.
|
||||
|
||||
Category: Gameplay Classes
|
||||
Category: **Gameplay Classes**
|
||||
"""
|
||||
use_teams = False
|
||||
use_team_colors = False
|
||||
|
|
|
|||
15
dist/ba_data/python/ba/_gameactivity.py
vendored
15
dist/ba_data/python/ba/_gameactivity.py
vendored
|
|
@ -24,14 +24,16 @@ if TYPE_CHECKING:
|
|||
from bastd.actor.bomb import TNTSpawner
|
||||
import ba
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
PlayerType = TypeVar('PlayerType', bound='ba.Player')
|
||||
TeamType = TypeVar('TeamType', bound='ba.Team')
|
||||
# pylint: enable=invalid-name
|
||||
|
||||
|
||||
class GameActivity(Activity[PlayerType, TeamType]):
|
||||
"""Common base class for all game ba.Activities.
|
||||
|
||||
category: Gameplay Classes
|
||||
Category: **Gameplay Classes**
|
||||
"""
|
||||
# pylint: disable=too-many-public-methods
|
||||
|
||||
|
|
@ -237,11 +239,11 @@ class GameActivity(Activity[PlayerType, TeamType]):
|
|||
self._zoom_message_times: dict[int, float] = {}
|
||||
self._is_waiting_for_continue = False
|
||||
|
||||
self._continue_cost = _ba.get_account_misc_read_val(
|
||||
self._continue_cost = _ba.get_v1_account_misc_read_val(
|
||||
'continueStartCost', 25)
|
||||
self._continue_cost_mult = _ba.get_account_misc_read_val(
|
||||
self._continue_cost_mult = _ba.get_v1_account_misc_read_val(
|
||||
'continuesMult', 2)
|
||||
self._continue_cost_offset = _ba.get_account_misc_read_val(
|
||||
self._continue_cost_offset = _ba.get_v1_account_misc_read_val(
|
||||
'continuesOffset', 0)
|
||||
|
||||
@property
|
||||
|
|
@ -258,6 +260,7 @@ class GameActivity(Activity[PlayerType, TeamType]):
|
|||
"""Return a name for this particular game instance."""
|
||||
return self.get_display_string(self.settings_raw)
|
||||
|
||||
# noinspection PyUnresolvedReferences
|
||||
def get_instance_scoreboard_display_string(self) -> ba.Lstr:
|
||||
"""Return a name for this particular game instance.
|
||||
|
||||
|
|
@ -387,7 +390,7 @@ class GameActivity(Activity[PlayerType, TeamType]):
|
|||
from ba._generated.enums import TimeType
|
||||
|
||||
try:
|
||||
if _ba.get_account_misc_read_val('enableContinues', False):
|
||||
if _ba.get_v1_account_misc_read_val('enableContinues', False):
|
||||
session = self.session
|
||||
|
||||
# We only support continuing in non-tournament games.
|
||||
|
|
@ -464,7 +467,7 @@ class GameActivity(Activity[PlayerType, TeamType]):
|
|||
data_t = data['t'] # This used to be the whole payload.
|
||||
|
||||
# Keep our cached tourney info up to date
|
||||
_ba.app.accounts.cache_tournament_info(data_t)
|
||||
_ba.app.accounts_v1.cache_tournament_info(data_t)
|
||||
self._setup_tournament_time_limit(
|
||||
max(5, data_t[0]['timeRemaining']))
|
||||
|
||||
|
|
|
|||
4
dist/ba_data/python/ba/_gameresults.py
vendored
4
dist/ba_data/python/ba/_gameresults.py
vendored
|
|
@ -27,10 +27,10 @@ class GameResults:
|
|||
"""
|
||||
Results for a completed game.
|
||||
|
||||
Category: Gameplay Classes
|
||||
Category: **Gameplay Classes**
|
||||
|
||||
Upon completion, a game should fill one of these out and pass it to its
|
||||
ba.Activity.end() call.
|
||||
ba.Activity.end call.
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
|
|
|
|||
17
dist/ba_data/python/ba/_gameutils.py
vendored
17
dist/ba_data/python/ba/_gameutils.py
vendored
|
|
@ -29,7 +29,7 @@ TROPHY_CHARS = {
|
|||
class GameTip:
|
||||
"""Defines a tip presentable to the user at the start of a game.
|
||||
|
||||
Category: Gameplay Classes
|
||||
Category: **Gameplay Classes**
|
||||
"""
|
||||
text: str
|
||||
icon: Optional[ba.Texture] = None
|
||||
|
|
@ -53,7 +53,7 @@ def animate(node: ba.Node,
|
|||
suppress_format_warning: bool = False) -> ba.Node:
|
||||
"""Animate values on a target ba.Node.
|
||||
|
||||
Category: Gameplay Functions
|
||||
Category: **Gameplay Functions**
|
||||
|
||||
Creates an 'animcurve' node with the provided values and time as an input,
|
||||
connect it to the provided attribute, and set it to die with the target.
|
||||
|
|
@ -98,6 +98,7 @@ def animate(node: ba.Node,
|
|||
# FIXME: Even if we are looping we should have a way to die once we
|
||||
# get disconnected.
|
||||
if not loop:
|
||||
# noinspection PyUnresolvedReferences
|
||||
_ba.timer(int(mult * items[-1][0]) + 1000,
|
||||
curve.delete,
|
||||
timeformat=TimeFormat.MILLISECONDS)
|
||||
|
|
@ -127,9 +128,9 @@ def animate_array(node: ba.Node,
|
|||
suppress_format_warning: bool = False) -> None:
|
||||
"""Animate an array of values on a target ba.Node.
|
||||
|
||||
Category: Gameplay Functions
|
||||
Category: **Gameplay Functions**
|
||||
|
||||
Like ba.animate(), but operates on array attributes.
|
||||
Like ba.animate, but operates on array attributes.
|
||||
"""
|
||||
# pylint: disable=too-many-locals
|
||||
combine = _ba.newnode('combine', owner=node, attrs={'size': size})
|
||||
|
|
@ -178,6 +179,7 @@ def animate_array(node: ba.Node,
|
|||
# curve after its done its job.
|
||||
if not loop:
|
||||
# (PyCharm seems to think item is a float, not a tuple)
|
||||
# noinspection PyUnresolvedReferences
|
||||
_ba.timer(int(mult * items[-1][0]) + 1000,
|
||||
curve.delete,
|
||||
timeformat=TimeFormat.MILLISECONDS)
|
||||
|
|
@ -189,6 +191,7 @@ def animate_array(node: ba.Node,
|
|||
# once we get disconnected.
|
||||
if not loop:
|
||||
# (PyCharm seems to think item is a float, not a tuple)
|
||||
# noinspection PyUnresolvedReferences
|
||||
_ba.timer(int(mult * items[-1][0]) + 1000,
|
||||
combine.delete,
|
||||
timeformat=TimeFormat.MILLISECONDS)
|
||||
|
|
@ -198,7 +201,7 @@ def show_damage_count(damage: str, position: Sequence[float],
|
|||
direction: Sequence[float]) -> None:
|
||||
"""Pop up a damage count at a position in space.
|
||||
|
||||
Category: Gameplay Functions
|
||||
Category: **Gameplay Functions**
|
||||
"""
|
||||
lifespan = 1.0
|
||||
app = _ba.app
|
||||
|
|
@ -253,7 +256,7 @@ def timestring(timeval: float,
|
|||
suppress_format_warning: bool = False) -> ba.Lstr:
|
||||
"""Generate a ba.Lstr for displaying a time value.
|
||||
|
||||
Category: General Utility Functions
|
||||
Category: **General Utility Functions**
|
||||
|
||||
Given a time value, returns a ba.Lstr with:
|
||||
(hours if > 0 ) : minutes : seconds : (centiseconds if centi=True).
|
||||
|
|
@ -321,7 +324,7 @@ def timestring(timeval: float,
|
|||
def cameraflash(duration: float = 999.0) -> None:
|
||||
"""Create a strobing camera flash effect.
|
||||
|
||||
Category: Gameplay Functions
|
||||
Category: **Gameplay Functions**
|
||||
|
||||
(as seen when a team wins a game)
|
||||
Duration is in seconds.
|
||||
|
|
|
|||
100
dist/ba_data/python/ba/_general.py
vendored
100
dist/ba_data/python/ba/_general.py
vendored
|
|
@ -24,22 +24,23 @@ if TYPE_CHECKING:
|
|||
class Existable(Protocol):
|
||||
"""A Protocol for objects supporting an exists() method.
|
||||
|
||||
Category: Protocols
|
||||
Category: **Protocols**
|
||||
"""
|
||||
|
||||
def exists(self) -> bool:
|
||||
"""Whether this object exists."""
|
||||
...
|
||||
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
ExistableType = TypeVar('ExistableType', bound=Existable)
|
||||
# pylint: enable=invalid-name
|
||||
T = TypeVar('T')
|
||||
|
||||
|
||||
def existing(obj: Optional[ExistableType]) -> Optional[ExistableType]:
|
||||
"""Convert invalid references to None for any ba.Existable object.
|
||||
|
||||
Category: Gameplay Functions
|
||||
Category: **Gameplay Functions**
|
||||
|
||||
To best support type checking, it is important that invalid references
|
||||
not be passed around and instead get converted to values of None.
|
||||
|
|
@ -59,7 +60,7 @@ def existing(obj: Optional[ExistableType]) -> Optional[ExistableType]:
|
|||
def getclass(name: str, subclassof: type[T]) -> type[T]:
|
||||
"""Given a full class name such as foo.bar.MyClass, return the class.
|
||||
|
||||
Category: General Utility Functions
|
||||
Category: **General Utility Functions**
|
||||
|
||||
The class will be checked to make sure it is a subclass of the provided
|
||||
'subclassof' class, and a TypeError will be raised if not.
|
||||
|
|
@ -140,7 +141,7 @@ def get_type_name(cls: type) -> str:
|
|||
class _WeakCall:
|
||||
"""Wrap a callable and arguments into a single callable object.
|
||||
|
||||
Category: General Utility Classes
|
||||
Category: **General Utility Classes**
|
||||
|
||||
When passed a bound method as the callable, the instance portion
|
||||
of it is weak-referenced, meaning the underlying instance is
|
||||
|
|
@ -150,20 +151,30 @@ class _WeakCall:
|
|||
Think of this as a handy way to tell an object to do something
|
||||
at some point in the future if it happens to still exist.
|
||||
|
||||
# EXAMPLE A: this code will create a FooClass instance and call its
|
||||
# bar() method 5 seconds later; it will be kept alive even though
|
||||
# we overwrite its variable with None because the bound method
|
||||
# we pass as a timer callback (foo.bar) strong-references it
|
||||
foo = FooClass()
|
||||
ba.timer(5.0, foo.bar)
|
||||
foo = None
|
||||
##### Examples
|
||||
**EXAMPLE A:** this code will create a FooClass instance and call its
|
||||
bar() method 5 seconds later; it will be kept alive even though
|
||||
we overwrite its variable with None because the bound method
|
||||
we pass as a timer callback (foo.bar) strong-references it
|
||||
>>> foo = FooClass()
|
||||
... ba.timer(5.0, foo.bar)
|
||||
... foo = None
|
||||
|
||||
# EXAMPLE B: this code will *not* keep our object alive; it will die
|
||||
# when we overwrite it with None and the timer will be a no-op when it
|
||||
# fires
|
||||
foo = FooClass()
|
||||
ba.timer(5.0, ba.WeakCall(foo.bar))
|
||||
foo = None
|
||||
**EXAMPLE B:** This code will *not* keep our object alive; it will die
|
||||
when we overwrite it with None and the timer will be a no-op when it
|
||||
fires
|
||||
>>> foo = FooClass()
|
||||
... ba.timer(5.0, ba.WeakCall(foo.bar))
|
||||
... foo = None
|
||||
|
||||
**EXAMPLE C:** Wrap a method call with some positional and keyword args:
|
||||
>>> myweakcall = ba.WeakCall(self.dostuff, argval1,
|
||||
... namedarg=argval2)
|
||||
... # Now we have a single callable to run that whole mess.
|
||||
... # The same as calling myobj.dostuff(argval1, namedarg=argval2)
|
||||
... # (provided my_obj still exists; this will do nothing
|
||||
... # otherwise).
|
||||
... myweakcall()
|
||||
|
||||
Note: additional args and keywords you provide to the WeakCall()
|
||||
constructor are stored as regular strong-references; you'll need
|
||||
|
|
@ -175,15 +186,6 @@ class _WeakCall:
|
|||
|
||||
Pass a callable as the first arg, followed by any number of
|
||||
arguments or keywords.
|
||||
|
||||
# Example: wrap a method call with some positional and
|
||||
# keyword args:
|
||||
myweakcall = ba.WeakCall(myobj.dostuff, argval1, namedarg=argval2)
|
||||
|
||||
# Now we have a single callable to run that whole mess.
|
||||
# The same as calling myobj.dostuff(argval1, namedarg=argval2)
|
||||
# (provided my_obj still exists; this will do nothing otherwise)
|
||||
myweakcall()
|
||||
"""
|
||||
if hasattr(args[0], '__func__'):
|
||||
self._call = WeakMethod(args[0])
|
||||
|
|
@ -212,13 +214,13 @@ class _WeakCall:
|
|||
class _Call:
|
||||
"""Wraps a callable and arguments into a single callable object.
|
||||
|
||||
Category: General Utility Classes
|
||||
Category: **General Utility Classes**
|
||||
|
||||
The callable is strong-referenced so it won't die until this
|
||||
object does.
|
||||
|
||||
Note that a bound method (ex: myobj.dosomething) contains a reference
|
||||
to 'self' (myobj in that case), so you will be keeping that object
|
||||
Note that a bound method (ex: ``myobj.dosomething``) contains a reference
|
||||
to ``self`` (``myobj`` in that case), so you will be keeping that object
|
||||
alive too. Use ba.WeakCall if you want to pass a method to callback
|
||||
without keeping its object alive.
|
||||
"""
|
||||
|
|
@ -229,12 +231,12 @@ class _Call:
|
|||
Pass a callable as the first arg, followed by any number of
|
||||
arguments or keywords.
|
||||
|
||||
# Example: wrap a method call with 1 positional and 1 keyword arg:
|
||||
mycall = ba.Call(myobj.dostuff, argval1, namedarg=argval2)
|
||||
|
||||
# Now we have a single callable to run that whole mess.
|
||||
# ..the same as calling myobj.dostuff(argval1, namedarg=argval2)
|
||||
mycall()
|
||||
##### Example
|
||||
Wrap a method call with 1 positional and 1 keyword arg:
|
||||
>>> mycall = ba.Call(myobj.dostuff, argval, namedarg=argval2)
|
||||
... # Now we have a single callable to run that whole mess.
|
||||
... # ..the same as calling myobj.dostuff(argval, namedarg=argval2)
|
||||
... mycall()
|
||||
"""
|
||||
self._call = args[0]
|
||||
self._args = args[1:]
|
||||
|
|
@ -283,7 +285,7 @@ class WeakMethod:
|
|||
def verify_object_death(obj: object) -> None:
|
||||
"""Warn if an object does not get freed within a short period.
|
||||
|
||||
Category: General Utility Functions
|
||||
Category: **General Utility Functions**
|
||||
|
||||
This can be handy to detect and prevent memory/resource leaks.
|
||||
"""
|
||||
|
|
@ -304,7 +306,7 @@ def verify_object_death(obj: object) -> None:
|
|||
def print_active_refs(obj: Any) -> None:
|
||||
"""Print info about things referencing a given object.
|
||||
|
||||
Category: General Utility Functions
|
||||
Category: **General Utility Functions**
|
||||
|
||||
Useful for tracking down cyclical references and causes for zombie objects.
|
||||
"""
|
||||
|
|
@ -361,7 +363,7 @@ def _verify_object_death(wref: weakref.ref) -> None:
|
|||
def storagename(suffix: str = None) -> str:
|
||||
"""Generate a unique name for storing class data in shared places.
|
||||
|
||||
Category: General Utility Functions
|
||||
Category: **General Utility Functions**
|
||||
|
||||
This consists of a leading underscore, the module path at the
|
||||
call site with dots replaced by underscores, the containing class's
|
||||
|
|
@ -371,15 +373,17 @@ def storagename(suffix: str = None) -> str:
|
|||
|
||||
Note that this will function even if called in the class definition.
|
||||
|
||||
# Example: generate a unique name for storage purposes:
|
||||
class MyThingie:
|
||||
|
||||
# This will give something like '_mymodule_submodule_mythingie_data'.
|
||||
_STORENAME = ba.storagename('data')
|
||||
|
||||
# Use that name to store some data in the Activity we were passed.
|
||||
def __init__(self, activity):
|
||||
activity.customdata[self._STORENAME] = {}
|
||||
##### Examples
|
||||
Generate a unique name for storage purposes:
|
||||
>>> class MyThingie:
|
||||
... # This will give something like
|
||||
... # '_mymodule_submodule_mythingie_data'.
|
||||
... _STORENAME = ba.storagename('data')
|
||||
...
|
||||
... # Use that name to store some data in the Activity we were
|
||||
... # passed.
|
||||
... def __init__(self, activity):
|
||||
... activity.customdata[self._STORENAME] = {}
|
||||
"""
|
||||
frame = inspect.currentframe()
|
||||
if frame is None:
|
||||
|
|
|
|||
8
dist/ba_data/python/ba/_hooks.py
vendored
8
dist/ba_data/python/ba/_hooks.py
vendored
|
|
@ -24,12 +24,11 @@ if TYPE_CHECKING:
|
|||
|
||||
def finish_bootstrapping() -> None:
|
||||
"""Do final bootstrapping related bits."""
|
||||
from ba._asyncio import setup_asyncio
|
||||
assert _ba.in_game_thread()
|
||||
|
||||
# Kick off our asyncio event handling, allowing us to use coroutines
|
||||
# in our game thread alongside our internal event handling.
|
||||
setup_asyncio()
|
||||
# setup_asyncio()
|
||||
|
||||
# Ok, bootstrapping is done; time to get the show started.
|
||||
_ba.app.on_app_launch()
|
||||
|
|
@ -385,3 +384,8 @@ def hash_strings(inputs: list[str]) -> str:
|
|||
sha.update(inp.encode())
|
||||
|
||||
return sha.hexdigest()
|
||||
|
||||
|
||||
def have_account_v2_credentials() -> bool:
|
||||
"""Do we have primary account-v2 credentials set?"""
|
||||
return _ba.app.accounts_v2.have_primary_credentials()
|
||||
|
|
|
|||
2
dist/ba_data/python/ba/_input.py
vendored
2
dist/ba_data/python/ba/_input.py
vendored
|
|
@ -639,5 +639,5 @@ def get_last_player_name_from_input_device(device: ba.InputDevice) -> str:
|
|||
if profilename == '_random':
|
||||
profilename = device.get_default_player_name()
|
||||
if profilename == '__account__':
|
||||
profilename = _ba.get_account_display_string()
|
||||
profilename = _ba.get_v1_account_display_string()
|
||||
return profilename
|
||||
|
|
|
|||
18
dist/ba_data/python/ba/_keyboard.py
vendored
18
dist/ba_data/python/ba/_keyboard.py
vendored
|
|
@ -13,23 +13,21 @@ if TYPE_CHECKING:
|
|||
class Keyboard:
|
||||
"""Chars definitions for on-screen keyboard.
|
||||
|
||||
Category: App Classes
|
||||
Category: **App Classes**
|
||||
|
||||
Keyboards are discoverable by the meta-tag system
|
||||
and the user can select which one they want to use.
|
||||
On-screen keyboard uses chars from active ba.Keyboard.
|
||||
Attributes:
|
||||
name
|
||||
Displays when user selecting this keyboard.
|
||||
chars
|
||||
Used for row/column lengths.
|
||||
pages
|
||||
Extra chars like emojis.
|
||||
nums
|
||||
The 'num' page.
|
||||
"""
|
||||
|
||||
name: str
|
||||
"""Displays when user selecting this keyboard."""
|
||||
|
||||
chars: list[tuple[str, ...]]
|
||||
"""Used for row/column lengths."""
|
||||
|
||||
pages: dict[str, tuple[str, ...]]
|
||||
"""Extra chars like emojis."""
|
||||
|
||||
nums: tuple[str, ...]
|
||||
"""The 'num' page."""
|
||||
|
|
|
|||
41
dist/ba_data/python/ba/_language.py
vendored
41
dist/ba_data/python/ba/_language.py
vendored
|
|
@ -17,7 +17,7 @@ if TYPE_CHECKING:
|
|||
class LanguageSubsystem:
|
||||
"""Wraps up language related app functionality.
|
||||
|
||||
Category: App Classes
|
||||
Category: **App Classes**
|
||||
|
||||
To use this class, access the single instance of it at 'ba.app.lang'.
|
||||
"""
|
||||
|
|
@ -367,7 +367,7 @@ class LanguageSubsystem:
|
|||
class Lstr:
|
||||
"""Used to define strings in a language-independent way.
|
||||
|
||||
category: General Utility Classes
|
||||
Category: **General Utility Classes**
|
||||
|
||||
These should be used whenever possible in place of hard-coded strings
|
||||
so that in-game or UI elements show up correctly on all clients in their
|
||||
|
|
@ -376,24 +376,28 @@ class Lstr:
|
|||
To see available resource keys, look at any of the bs_language_*.py files
|
||||
in the game or the translations pages at legacy.ballistica.net/translate.
|
||||
|
||||
# EXAMPLE 1: specify a string from a resource path
|
||||
mynode.text = ba.Lstr(resource='audioSettingsWindow.titleText')
|
||||
##### Examples
|
||||
EXAMPLE 1: specify a string from a resource path
|
||||
>>> mynode.text = ba.Lstr(resource='audioSettingsWindow.titleText')
|
||||
|
||||
# EXAMPLE 2: specify a translated string via a category and english value;
|
||||
# if a translated value is available, it will be used; otherwise the
|
||||
# english value will be. To see available translation categories, look
|
||||
# under the 'translations' resource section.
|
||||
mynode.text = ba.Lstr(translate=('gameDescriptions', 'Defeat all enemies'))
|
||||
EXAMPLE 2: specify a translated string via a category and english
|
||||
value; if a translated value is available, it will be used; otherwise
|
||||
the english value will be. To see available translation categories,
|
||||
look under the 'translations' resource section.
|
||||
>>> mynode.text = ba.Lstr(translate=('gameDescriptions',
|
||||
... 'Defeat all enemies'))
|
||||
|
||||
# EXAMPLE 3: specify a raw value and some substitutions. Substitutions can
|
||||
# be used with resource and translate modes as well.
|
||||
mynode.text = ba.Lstr(value='${A} / ${B}',
|
||||
subs=[('${A}', str(score)), ('${B}', str(total))])
|
||||
EXAMPLE 3: specify a raw value and some substitutions. Substitutions
|
||||
can be used with resource and translate modes as well.
|
||||
>>> mynode.text = ba.Lstr(value='${A} / ${B}',
|
||||
... subs=[('${A}', str(score)), ('${B}', str(total))])
|
||||
|
||||
# EXAMPLE 4: Lstrs can be nested. This example would display the resource
|
||||
# at res_a but replace ${NAME} with the value of the resource at res_b
|
||||
mytextnode.text = ba.Lstr(resource='res_a',
|
||||
subs=[('${NAME}', ba.Lstr(resource='res_b'))])
|
||||
EXAMPLE 4: ba.Lstr's can be nested. This example would display the
|
||||
resource at res_a but replace ${NAME} with the value of the
|
||||
resource at res_b
|
||||
>>> mytextnode.text = ba.Lstr(
|
||||
... resource='res_a',
|
||||
... subs=[('${NAME}', ba.Lstr(resource='res_b'))])
|
||||
"""
|
||||
|
||||
# pylint: disable=dangerous-default-value
|
||||
|
|
@ -406,7 +410,6 @@ class Lstr:
|
|||
fallback_value: str = '',
|
||||
subs: Sequence[tuple[str, Union[str, Lstr]]] = []) -> None:
|
||||
"""Create an Lstr from a string resource."""
|
||||
...
|
||||
|
||||
# noinspection PyShadowingNames,PyDefaultArgument
|
||||
@overload
|
||||
|
|
@ -415,7 +418,6 @@ class Lstr:
|
|||
translate: tuple[str, str],
|
||||
subs: Sequence[tuple[str, Union[str, Lstr]]] = []) -> None:
|
||||
"""Create an Lstr by translating a string in a category."""
|
||||
...
|
||||
|
||||
# noinspection PyDefaultArgument
|
||||
@overload
|
||||
|
|
@ -424,7 +426,6 @@ class Lstr:
|
|||
value: str,
|
||||
subs: Sequence[tuple[str, Union[str, Lstr]]] = []) -> None:
|
||||
"""Create an Lstr from a raw string value."""
|
||||
...
|
||||
|
||||
# pylint: enable=redefined-outer-name, dangerous-default-value
|
||||
|
||||
|
|
|
|||
2
dist/ba_data/python/ba/_level.py
vendored
2
dist/ba_data/python/ba/_level.py
vendored
|
|
@ -17,7 +17,7 @@ if TYPE_CHECKING:
|
|||
class Level:
|
||||
"""An entry in a ba.Campaign consisting of a name, game type, and settings.
|
||||
|
||||
category: Gameplay Classes
|
||||
Category: **Gameplay Classes**
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
|
|
|
|||
6
dist/ba_data/python/ba/_lobby.py
vendored
6
dist/ba_data/python/ba/_lobby.py
vendored
|
|
@ -410,6 +410,7 @@ class Chooser:
|
|||
self._profileindex = self._profilenames.index(self._profilename)
|
||||
else:
|
||||
self._profileindex = 0
|
||||
# noinspection PyUnresolvedReferences
|
||||
self._profilename = self._profilenames[self._profileindex]
|
||||
|
||||
def update_position(self) -> None:
|
||||
|
|
@ -451,7 +452,8 @@ class Chooser:
|
|||
clamp = not full
|
||||
elif name == '__account__':
|
||||
try:
|
||||
name = self._sessionplayer.inputdevice.get_account_name(full)
|
||||
name = self._sessionplayer.inputdevice.get_v1_account_name(
|
||||
full)
|
||||
except Exception:
|
||||
print_exception('Error getting account name for chooser.')
|
||||
name = 'Invalid'
|
||||
|
|
@ -893,7 +895,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.
|
||||
_ba.app.accounts.ensure_have_account_player_profile()
|
||||
_ba.app.accounts_v1.ensure_have_account_player_profile()
|
||||
for chooser in self.choosers:
|
||||
try:
|
||||
chooser.reload_profiles()
|
||||
|
|
|
|||
17
dist/ba_data/python/ba/_map.py
vendored
17
dist/ba_data/python/ba/_map.py
vendored
|
|
@ -18,7 +18,7 @@ if TYPE_CHECKING:
|
|||
def preload_map_preview_media() -> None:
|
||||
"""Preload media needed for map preview UIs.
|
||||
|
||||
Category: Asset Functions
|
||||
Category: **Asset Functions**
|
||||
"""
|
||||
_ba.getmodel('level_select_button_opaque')
|
||||
_ba.getmodel('level_select_button_transparent')
|
||||
|
|
@ -31,7 +31,7 @@ def preload_map_preview_media() -> None:
|
|||
def get_filtered_map_name(name: str) -> str:
|
||||
"""Filter a map name to account for name changes, etc.
|
||||
|
||||
Category: Asset Functions
|
||||
Category: **Asset Functions**
|
||||
|
||||
This can be used to support old playlists, etc.
|
||||
"""
|
||||
|
|
@ -46,7 +46,7 @@ def get_filtered_map_name(name: str) -> str:
|
|||
def get_map_display_string(name: str) -> ba.Lstr:
|
||||
"""Return a ba.Lstr for displaying a given map\'s name.
|
||||
|
||||
Category: Asset Functions
|
||||
Category: **Asset Functions**
|
||||
"""
|
||||
from ba import _language
|
||||
return _language.Lstr(translate=('mapsNames', name))
|
||||
|
|
@ -55,7 +55,7 @@ def get_map_display_string(name: str) -> ba.Lstr:
|
|||
def getmaps(playtype: str) -> list[str]:
|
||||
"""Return a list of ba.Map types supporting a playtype str.
|
||||
|
||||
Category: Asset Functions
|
||||
Category: **Asset Functions**
|
||||
|
||||
Maps supporting a given playtype must provide a particular set of
|
||||
features and lend themselves to a certain style of play.
|
||||
|
|
@ -104,7 +104,7 @@ def getmaps(playtype: str) -> list[str]:
|
|||
def get_unowned_maps() -> list[str]:
|
||||
"""Return the list of local maps not owned by the current account.
|
||||
|
||||
Category: Asset Functions
|
||||
Category: **Asset Functions**
|
||||
"""
|
||||
from ba import _store
|
||||
unowned_maps: set[str] = set()
|
||||
|
|
@ -120,7 +120,7 @@ def get_unowned_maps() -> list[str]:
|
|||
def get_map_class(name: str) -> type[ba.Map]:
|
||||
"""Return a map type given a name.
|
||||
|
||||
Category: Asset Functions
|
||||
Category: **Asset Functions**
|
||||
"""
|
||||
name = get_filtered_map_name(name)
|
||||
try:
|
||||
|
|
@ -133,7 +133,7 @@ def get_map_class(name: str) -> type[ba.Map]:
|
|||
class Map(Actor):
|
||||
"""A game map.
|
||||
|
||||
Category: Gameplay Classes
|
||||
Category: **Gameplay Classes**
|
||||
|
||||
Consists of a collection of terrain nodes, metadata, and other
|
||||
functionality comprising a game map.
|
||||
|
|
@ -189,7 +189,6 @@ class Map(Actor):
|
|||
def __init__(self,
|
||||
vr_overlay_offset: Optional[Sequence[float]] = None) -> None:
|
||||
"""Instantiate a map."""
|
||||
from ba import _gameutils
|
||||
super().__init__()
|
||||
|
||||
# This is expected to always be a ba.Node object (whether valid or not)
|
||||
|
|
@ -348,7 +347,7 @@ class Map(Actor):
|
|||
self, players: Sequence[ba.Player]) -> Sequence[float]:
|
||||
"""Return a random starting position in one of the FFA spawn areas.
|
||||
|
||||
If a list of ba.Players is provided; the returned points will be
|
||||
If a list of ba.Player-s is provided; the returned points will be
|
||||
as far from these players as possible.
|
||||
"""
|
||||
|
||||
|
|
|
|||
110
dist/ba_data/python/ba/_messages.py
vendored
110
dist/ba_data/python/ba/_messages.py
vendored
|
|
@ -50,45 +50,38 @@ class DeathType(Enum):
|
|||
class DieMessage:
|
||||
"""A message telling an object to die.
|
||||
|
||||
Category: Message Classes
|
||||
|
||||
Most ba.Actors respond to this.
|
||||
|
||||
Attributes:
|
||||
|
||||
immediate
|
||||
If this is set to True, the actor should disappear immediately.
|
||||
This is for 'removing' stuff from the game more so than 'killing'
|
||||
it. If False, the actor should die a 'normal' death and can take
|
||||
its time with lingering corpses, sound effects, etc.
|
||||
|
||||
how
|
||||
The particular reason for death.
|
||||
Category: **Message Classes**
|
||||
|
||||
Most ba.Actor-s respond to this.
|
||||
"""
|
||||
|
||||
immediate: bool = False
|
||||
"""If this is set to True, the actor should disappear immediately.
|
||||
This is for 'removing' stuff from the game more so than 'killing'
|
||||
it. If False, the actor should die a 'normal' death and can take
|
||||
its time with lingering corpses, sound effects, etc."""
|
||||
|
||||
how: DeathType = DeathType.GENERIC
|
||||
"""The particular reason for death."""
|
||||
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
PlayerType = TypeVar('PlayerType', bound='ba.Player')
|
||||
# pylint: enable=invalid-name
|
||||
|
||||
|
||||
class PlayerDiedMessage:
|
||||
"""A message saying a ba.Player has died.
|
||||
|
||||
category: Message Classes
|
||||
|
||||
Attributes:
|
||||
|
||||
killed
|
||||
If True, the player was killed;
|
||||
If False, they left the game or the round ended.
|
||||
|
||||
how
|
||||
The particular type of death.
|
||||
Category: **Message Classes**
|
||||
"""
|
||||
|
||||
killed: bool
|
||||
"""If True, the player was killed;
|
||||
If False, they left the game or the round ended."""
|
||||
|
||||
how: ba.DeathType
|
||||
"""The particular type of death."""
|
||||
|
||||
def __init__(self, player: ba.Player, was_killed: bool,
|
||||
killerplayer: Optional[ba.Player], how: ba.DeathType):
|
||||
|
|
@ -132,41 +125,34 @@ class PlayerDiedMessage:
|
|||
class StandMessage:
|
||||
"""A message telling an object to move to a position in space.
|
||||
|
||||
Category: Message Classes
|
||||
Category: **Message Classes**
|
||||
|
||||
Used when teleporting players to home base, etc.
|
||||
|
||||
Attributes:
|
||||
|
||||
position
|
||||
Where to move to.
|
||||
|
||||
angle
|
||||
The angle to face (in degrees)
|
||||
"""
|
||||
|
||||
position: Sequence[float] = (0.0, 0.0, 0.0)
|
||||
"""Where to move to."""
|
||||
|
||||
angle: float = 0.0
|
||||
"""The angle to face (in degrees)"""
|
||||
|
||||
|
||||
@dataclass
|
||||
class PickUpMessage:
|
||||
"""Tells an object that it has picked something up.
|
||||
|
||||
Category: Message Classes
|
||||
|
||||
Attributes:
|
||||
|
||||
node
|
||||
The ba.Node that is getting picked up.
|
||||
Category: **Message Classes**
|
||||
"""
|
||||
|
||||
node: ba.Node
|
||||
"""The ba.Node that is getting picked up."""
|
||||
|
||||
|
||||
@dataclass
|
||||
class DropMessage:
|
||||
"""Tells an object that it has dropped what it was holding.
|
||||
|
||||
Category: Message Classes
|
||||
Category: **Message Classes**
|
||||
"""
|
||||
|
||||
|
||||
|
|
@ -174,35 +160,29 @@ class DropMessage:
|
|||
class PickedUpMessage:
|
||||
"""Tells an object that it has been picked up by something.
|
||||
|
||||
Category: Message Classes
|
||||
|
||||
Attributes:
|
||||
|
||||
node
|
||||
The ba.Node doing the picking up.
|
||||
Category: **Message Classes**
|
||||
"""
|
||||
|
||||
node: ba.Node
|
||||
"""The ba.Node doing the picking up."""
|
||||
|
||||
|
||||
@dataclass
|
||||
class DroppedMessage:
|
||||
"""Tells an object that it has been dropped.
|
||||
|
||||
Category: Message Classes
|
||||
|
||||
Attributes:
|
||||
|
||||
node
|
||||
The ba.Node doing the dropping.
|
||||
Category: **Message Classes**
|
||||
"""
|
||||
|
||||
node: ba.Node
|
||||
"""The ba.Node doing the dropping."""
|
||||
|
||||
|
||||
@dataclass
|
||||
class ShouldShatterMessage:
|
||||
"""Tells an object that it should shatter.
|
||||
|
||||
Category: Message Classes
|
||||
Category: **Message Classes**
|
||||
"""
|
||||
|
||||
|
||||
|
|
@ -210,21 +190,18 @@ class ShouldShatterMessage:
|
|||
class ImpactDamageMessage:
|
||||
"""Tells an object that it has been jarred violently.
|
||||
|
||||
Category: Message Classes
|
||||
|
||||
Attributes:
|
||||
|
||||
intensity
|
||||
The intensity of the impact.
|
||||
Category: **Message Classes**
|
||||
"""
|
||||
|
||||
intensity: float
|
||||
"""The intensity of the impact."""
|
||||
|
||||
|
||||
@dataclass
|
||||
class FreezeMessage:
|
||||
"""Tells an object to become frozen.
|
||||
|
||||
Category: Message Classes
|
||||
Category: **Message Classes**
|
||||
|
||||
As seen in the effects of an ice ba.Bomb.
|
||||
"""
|
||||
|
|
@ -234,7 +211,7 @@ class FreezeMessage:
|
|||
class ThawMessage:
|
||||
"""Tells an object to stop being frozen.
|
||||
|
||||
Category: Message Classes
|
||||
Category: **Message Classes**
|
||||
"""
|
||||
|
||||
|
||||
|
|
@ -242,20 +219,17 @@ class ThawMessage:
|
|||
class CelebrateMessage:
|
||||
"""Tells an object to celebrate.
|
||||
|
||||
Category: Message Classes
|
||||
|
||||
Attributes:
|
||||
|
||||
duration
|
||||
Amount of time to celebrate in seconds.
|
||||
Category: **Message Classes**
|
||||
"""
|
||||
|
||||
duration: float = 10.0
|
||||
"""Amount of time to celebrate in seconds."""
|
||||
|
||||
|
||||
class HitMessage:
|
||||
"""Tells an object it has been hit in some way.
|
||||
|
||||
Category: Message Classes
|
||||
Category: **Message Classes**
|
||||
|
||||
This is used by punches, explosions, etc to convey
|
||||
their effect to a target.
|
||||
|
|
|
|||
2
dist/ba_data/python/ba/_meta.py
vendored
2
dist/ba_data/python/ba/_meta.py
vendored
|
|
@ -37,7 +37,7 @@ class ScanResults:
|
|||
class MetadataSubsystem:
|
||||
"""Subsystem for working with script metadata in the app.
|
||||
|
||||
Category: App Classes
|
||||
Category: **App Classes**
|
||||
|
||||
Access the single shared instance of this class at 'ba.app.meta'.
|
||||
"""
|
||||
|
|
|
|||
4
dist/ba_data/python/ba/_multiteamsession.py
vendored
4
dist/ba_data/python/ba/_multiteamsession.py
vendored
|
|
@ -22,7 +22,7 @@ DEFAULT_TEAM_NAMES = ('Blue', 'Red')
|
|||
class MultiTeamSession(Session):
|
||||
"""Common base class for ba.DualTeamSession and ba.FreeForAllSession.
|
||||
|
||||
Category: Gameplay Classes
|
||||
Category: **Gameplay Classes**
|
||||
|
||||
Free-for-all-mode is essentially just teams-mode with each ba.Player having
|
||||
their own ba.Team, so there is much overlap in functionality.
|
||||
|
|
@ -141,7 +141,7 @@ class MultiTeamSession(Session):
|
|||
team.customdata['previous_score'] = team.customdata['score'] = 0
|
||||
|
||||
def get_max_players(self) -> int:
|
||||
"""Return max number of ba.Players allowed to join the game at once."""
|
||||
"""Return max number of ba.Player-s allowed to join the game at once"""
|
||||
if self.use_teams:
|
||||
return _ba.app.config.get('Team Game Max Players', 8)
|
||||
return _ba.app.config.get('Free-for-All Max Players', 8)
|
||||
|
|
|
|||
13
dist/ba_data/python/ba/_music.py
vendored
13
dist/ba_data/python/ba/_music.py
vendored
|
|
@ -18,7 +18,7 @@ if TYPE_CHECKING:
|
|||
class MusicType(Enum):
|
||||
"""Types of music available to play in-game.
|
||||
|
||||
Category: Enums
|
||||
Category: **Enums**
|
||||
|
||||
These do not correspond to specific pieces of music, but rather to
|
||||
'situations'. The actual music played for each type can be overridden
|
||||
|
|
@ -51,7 +51,7 @@ class MusicType(Enum):
|
|||
class MusicPlayMode(Enum):
|
||||
"""Influences behavior when playing music.
|
||||
|
||||
Category: Enums
|
||||
Category: **Enums**
|
||||
"""
|
||||
REGULAR = 'regular'
|
||||
TEST = 'test'
|
||||
|
|
@ -61,7 +61,7 @@ class MusicPlayMode(Enum):
|
|||
class AssetSoundtrackEntry:
|
||||
"""A music entry using an internal asset.
|
||||
|
||||
Category: App Classes
|
||||
Category: **App Classes**
|
||||
"""
|
||||
assetname: str
|
||||
volume: float = 1.0
|
||||
|
|
@ -120,7 +120,7 @@ ASSET_SOUNDTRACK_ENTRIES: dict[MusicType, AssetSoundtrackEntry] = {
|
|||
class MusicSubsystem:
|
||||
"""Subsystem for music playback in the app.
|
||||
|
||||
Category: App Classes
|
||||
Category: **App Classes**
|
||||
|
||||
Access the single shared instance of this class at 'ba.app.music'.
|
||||
"""
|
||||
|
|
@ -385,7 +385,7 @@ class MusicSubsystem:
|
|||
class MusicPlayer:
|
||||
"""Wrangles soundtrack music playback.
|
||||
|
||||
Category: App Classes
|
||||
Category: **App Classes**
|
||||
|
||||
Music can be played either through the game itself
|
||||
or via a platform-specific external player.
|
||||
|
|
@ -474,7 +474,7 @@ def setmusic(musictype: Optional[ba.MusicType],
|
|||
continuous: bool = False) -> None:
|
||||
"""Set the app to play (or stop playing) a certain type of music.
|
||||
|
||||
category: Gameplay Functions
|
||||
category: **Gameplay Functions**
|
||||
|
||||
This function will handle loading and playing sound assets as necessary,
|
||||
and also supports custom user soundtracks on specific platforms so the
|
||||
|
|
@ -485,7 +485,6 @@ def setmusic(musictype: Optional[ba.MusicType],
|
|||
if 'continuous' is True and musictype is the same as what is already
|
||||
playing, the playing track will not be restarted.
|
||||
"""
|
||||
from ba import _gameutils
|
||||
|
||||
# All we do here now is set a few music attrs on the current globals
|
||||
# node. The foreground globals' current playing music then gets fed to
|
||||
|
|
|
|||
22
dist/ba_data/python/ba/_net.py
vendored
22
dist/ba_data/python/ba/_net.py
vendored
|
|
@ -14,7 +14,6 @@ import _ba
|
|||
if TYPE_CHECKING:
|
||||
from typing import Any, Union, Callable, Optional
|
||||
import socket
|
||||
import ba
|
||||
MasterServerCallback = Callable[[Union[None, dict[str, Any]]], None]
|
||||
|
||||
# Timeout for standard functions talking to the master-server/etc.
|
||||
|
|
@ -26,9 +25,14 @@ class NetworkSubsystem:
|
|||
|
||||
def __init__(self) -> None:
|
||||
|
||||
# Anyone accessing/modifying region_pings should hold this lock.
|
||||
self.region_pings_lock = threading.Lock()
|
||||
self.region_pings: dict[str, float] = {}
|
||||
# Anyone accessing/modifying zone_pings should hold this lock,
|
||||
# as it is updated by a background thread.
|
||||
self.zone_pings_lock = threading.Lock()
|
||||
|
||||
# Region IDs mapped to average pings. This will remain empty
|
||||
# until enough pings have been run to be reasonably certain
|
||||
# that a nearby server has been pinged.
|
||||
self.zone_pings: dict[str, float] = {}
|
||||
|
||||
# For debugging.
|
||||
self.v1_test_log: str = ''
|
||||
|
|
@ -106,7 +110,7 @@ class MasterServerCallThread(threading.Thread):
|
|||
def run(self) -> None:
|
||||
# pylint: disable=too-many-branches, consider-using-with
|
||||
import urllib.request
|
||||
import urllib.error
|
||||
import urllib.parse
|
||||
import json
|
||||
|
||||
from efro.error import is_urllib_network_error
|
||||
|
|
@ -114,19 +118,19 @@ class MasterServerCallThread(threading.Thread):
|
|||
try:
|
||||
self._data = _general.utf8_all(self._data)
|
||||
_ba.set_thread_name('BA_ServerCallThread')
|
||||
parse = urllib.parse
|
||||
if self._request_type == 'get':
|
||||
response = urllib.request.urlopen(
|
||||
urllib.request.Request(
|
||||
(_ba.get_master_server_address() + '/' +
|
||||
self._request + '?' + parse.urlencode(self._data)),
|
||||
None, {'User-Agent': _ba.app.user_agent_string}),
|
||||
self._request + '?' +
|
||||
urllib.parse.urlencode(self._data)), None,
|
||||
{'User-Agent': _ba.app.user_agent_string}),
|
||||
timeout=DEFAULT_REQUEST_TIMEOUT_SECONDS)
|
||||
elif self._request_type == 'post':
|
||||
response = urllib.request.urlopen(
|
||||
urllib.request.Request(
|
||||
_ba.get_master_server_address() + '/' + self._request,
|
||||
parse.urlencode(self._data).encode(),
|
||||
urllib.parse.urlencode(self._data).encode(),
|
||||
{'User-Agent': _ba.app.user_agent_string}),
|
||||
timeout=DEFAULT_REQUEST_TIMEOUT_SECONDS)
|
||||
else:
|
||||
|
|
|
|||
2
dist/ba_data/python/ba/_nodeactor.py
vendored
2
dist/ba_data/python/ba/_nodeactor.py
vendored
|
|
@ -17,7 +17,7 @@ if TYPE_CHECKING:
|
|||
class NodeActor(Actor):
|
||||
"""A simple ba.Actor type that wraps a single ba.Node.
|
||||
|
||||
Category: Gameplay Classes
|
||||
Category: **Gameplay Classes**
|
||||
|
||||
This Actor will delete its Node when told to die, and it's
|
||||
exists() call will return whether the Node still exists or not.
|
||||
|
|
|
|||
27
dist/ba_data/python/ba/_player.py
vendored
27
dist/ba_data/python/ba/_player.py
vendored
|
|
@ -16,8 +16,10 @@ if TYPE_CHECKING:
|
|||
from typing import Optional, Sequence, Any, Union, Callable
|
||||
import ba
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
PlayerType = TypeVar('PlayerType', bound='ba.Player')
|
||||
TeamType = TypeVar('TeamType', bound='ba.Team')
|
||||
# pylint: enable=invalid-name
|
||||
|
||||
|
||||
@dataclass
|
||||
|
|
@ -48,18 +50,15 @@ class Player(Generic[TeamType]):
|
|||
These correspond to ba.SessionPlayer objects, but are associated with a
|
||||
single ba.Activity instance. This allows activities to specify their
|
||||
own custom ba.Player types.
|
||||
|
||||
Attributes:
|
||||
|
||||
actor
|
||||
The ba.Actor associated with the player.
|
||||
|
||||
"""
|
||||
|
||||
# These are instance attrs but we define them at the type level so
|
||||
# their type annotations are introspectable (for docs generation).
|
||||
character: str
|
||||
|
||||
actor: Optional[ba.Actor]
|
||||
"""The ba.Actor associated with the player."""
|
||||
|
||||
color: Sequence[float]
|
||||
highlight: Sequence[float]
|
||||
|
||||
|
|
@ -225,8 +224,7 @@ class Player(Generic[TeamType]):
|
|||
return self._sessionplayer.exists() and not self._expired
|
||||
|
||||
def getname(self, full: bool = False, icon: bool = True) -> str:
|
||||
"""getname(full: bool = False, icon: bool = True) -> str
|
||||
|
||||
"""
|
||||
Returns the player's name. If icon is True, the long version of the
|
||||
name may include an icon.
|
||||
"""
|
||||
|
|
@ -235,8 +233,7 @@ class Player(Generic[TeamType]):
|
|||
return self._sessionplayer.getname(full=full, icon=icon)
|
||||
|
||||
def is_alive(self) -> bool:
|
||||
"""is_alive() -> bool
|
||||
|
||||
"""
|
||||
Returns True if the player has a ba.Actor assigned and its
|
||||
is_alive() method return True. False is returned otherwise.
|
||||
"""
|
||||
|
|
@ -245,8 +242,7 @@ class Player(Generic[TeamType]):
|
|||
return self.actor is not None and self.actor.is_alive()
|
||||
|
||||
def get_icon(self) -> dict[str, Any]:
|
||||
"""get_icon() -> dict[str, Any]
|
||||
|
||||
"""
|
||||
Returns the character's icon (images, colors, etc contained in a dict)
|
||||
"""
|
||||
assert self._postinited
|
||||
|
|
@ -256,9 +252,7 @@ class Player(Generic[TeamType]):
|
|||
def assigninput(self, inputtype: Union[ba.InputType, tuple[ba.InputType,
|
||||
...]],
|
||||
call: Callable) -> None:
|
||||
"""assigninput(type: Union[ba.InputType, Tuple[ba.InputType, ...]],
|
||||
call: Callable) -> None
|
||||
|
||||
"""
|
||||
Set the python callable to be run for one or more types of input.
|
||||
"""
|
||||
assert self._postinited
|
||||
|
|
@ -266,8 +260,7 @@ class Player(Generic[TeamType]):
|
|||
return self._sessionplayer.assigninput(type=inputtype, call=call)
|
||||
|
||||
def resetinput(self) -> None:
|
||||
"""resetinput() -> None
|
||||
|
||||
"""
|
||||
Clears out the player's assigned input actions.
|
||||
"""
|
||||
assert self._postinited
|
||||
|
|
|
|||
8
dist/ba_data/python/ba/_plugin.py
vendored
8
dist/ba_data/python/ba/_plugin.py
vendored
|
|
@ -16,9 +16,9 @@ if TYPE_CHECKING:
|
|||
class PluginSubsystem:
|
||||
"""Subsystem for plugin handling in the app.
|
||||
|
||||
Category: App Classes
|
||||
Category: **App Classes**
|
||||
|
||||
Access the single shared instance of this class at 'ba.app.plugins'.
|
||||
Access the single shared instance of this class at `ba.app.plugins`.
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
|
|
@ -96,7 +96,7 @@ class PluginSubsystem:
|
|||
class PotentialPlugin:
|
||||
"""Represents a ba.Plugin which can potentially be loaded.
|
||||
|
||||
Category: App Classes
|
||||
Category: **App Classes**
|
||||
|
||||
These generally represent plugins which were detected by the
|
||||
meta-tag scan. However they may also represent plugins which
|
||||
|
|
@ -111,7 +111,7 @@ class PotentialPlugin:
|
|||
class Plugin:
|
||||
"""A plugin to alter app behavior in some way.
|
||||
|
||||
Category: App Classes
|
||||
Category: **App Classes**
|
||||
|
||||
Plugins are discoverable by the meta-tag system
|
||||
and the user can select which ones they want to activate.
|
||||
|
|
|
|||
24
dist/ba_data/python/ba/_powerup.py
vendored
24
dist/ba_data/python/ba/_powerup.py
vendored
|
|
@ -16,31 +16,27 @@ if TYPE_CHECKING:
|
|||
class PowerupMessage:
|
||||
"""A message telling an object to accept a powerup.
|
||||
|
||||
Category: Message Classes
|
||||
Category: **Message Classes**
|
||||
|
||||
This message is normally received by touching a ba.PowerupBox.
|
||||
|
||||
Attributes:
|
||||
|
||||
poweruptype
|
||||
The type of powerup to be granted (a string).
|
||||
See ba.Powerup.poweruptype for available type values.
|
||||
|
||||
sourcenode
|
||||
The node the powerup game from, or None otherwise.
|
||||
If a powerup is accepted, a ba.PowerupAcceptMessage should be sent
|
||||
back to the sourcenode to inform it of the fact. This will generally
|
||||
cause the powerup box to make a sound and disappear or whatnot.
|
||||
"""
|
||||
|
||||
poweruptype: str
|
||||
"""The type of powerup to be granted (a string).
|
||||
See ba.Powerup.poweruptype for available type values."""
|
||||
|
||||
sourcenode: Optional[ba.Node] = None
|
||||
"""The node the powerup game from, or None otherwise.
|
||||
If a powerup is accepted, a ba.PowerupAcceptMessage should be sent
|
||||
back to the sourcenode to inform it of the fact. This will generally
|
||||
cause the powerup box to make a sound and disappear or whatnot."""
|
||||
|
||||
|
||||
@dataclass
|
||||
class PowerupAcceptMessage:
|
||||
"""A message informing a ba.Powerup that it was accepted.
|
||||
|
||||
Category: Message Classes
|
||||
Category: **Message Classes**
|
||||
|
||||
This is generally sent in response to a ba.PowerupMessage
|
||||
to inform the box (or whoever granted it) that it can go away.
|
||||
|
|
|
|||
8
dist/ba_data/python/ba/_profile.py
vendored
8
dist/ba_data/python/ba/_profile.py
vendored
|
|
@ -73,7 +73,7 @@ def get_player_profile_colors(
|
|||
color = PLAYER_COLORS[random.randrange(6)]
|
||||
else:
|
||||
# First 6 are bright-ish.
|
||||
color = PLAYER_COLORS[sum([ord(c) for c in profilename]) % 6]
|
||||
color = PLAYER_COLORS[sum(ord(c) for c in profilename) % 6]
|
||||
|
||||
try:
|
||||
assert profilename is not None
|
||||
|
|
@ -86,8 +86,8 @@ def get_player_profile_colors(
|
|||
highlight = PLAYER_COLORS[random.randrange(
|
||||
len(PLAYER_COLORS) - 2)]
|
||||
else:
|
||||
highlight = PLAYER_COLORS[sum(
|
||||
[ord(c) + 1
|
||||
for c in profilename]) % (len(PLAYER_COLORS) - 2)]
|
||||
highlight = PLAYER_COLORS[sum(ord(c) + 1
|
||||
for c in profilename) %
|
||||
(len(PLAYER_COLORS) - 2)]
|
||||
|
||||
return color, highlight
|
||||
|
|
|
|||
36
dist/ba_data/python/ba/_score.py
vendored
36
dist/ba_data/python/ba/_score.py
vendored
|
|
@ -16,7 +16,7 @@ if TYPE_CHECKING:
|
|||
class ScoreType(Enum):
|
||||
"""Type of scores.
|
||||
|
||||
Category: Enums
|
||||
Category: **Enums**
|
||||
"""
|
||||
SECONDS = 's'
|
||||
MILLISECONDS = 'ms'
|
||||
|
|
@ -27,30 +27,22 @@ class ScoreType(Enum):
|
|||
class ScoreConfig:
|
||||
"""Settings for how a game handles scores.
|
||||
|
||||
Category: Gameplay Classes
|
||||
|
||||
Attributes:
|
||||
|
||||
label
|
||||
A label show to the user for scores; 'Score', 'Time Survived', etc.
|
||||
|
||||
scoretype
|
||||
How the score value should be displayed.
|
||||
|
||||
lower_is_better
|
||||
Whether lower scores are preferable. Higher scores are by default.
|
||||
|
||||
none_is_winner
|
||||
Whether a value of None is considered better than other scores.
|
||||
By default it is not.
|
||||
|
||||
version
|
||||
To change high-score lists used by a game without renaming the game,
|
||||
change this. Defaults to an empty string.
|
||||
|
||||
Category: **Gameplay Classes**
|
||||
"""
|
||||
|
||||
label: str = 'Score'
|
||||
"""A label show to the user for scores; 'Score', 'Time Survived', etc."""
|
||||
|
||||
scoretype: ba.ScoreType = ScoreType.POINTS
|
||||
"""How the score value should be displayed."""
|
||||
|
||||
lower_is_better: bool = False
|
||||
"""Whether lower scores are preferable. Higher scores are by default."""
|
||||
|
||||
none_is_winner: bool = False
|
||||
"""Whether a value of None is considered better than other scores.
|
||||
By default it is not."""
|
||||
|
||||
version: str = ''
|
||||
"""To change high-score lists used by a game without renaming the game,
|
||||
change this. Defaults to an empty string."""
|
||||
|
|
|
|||
6
dist/ba_data/python/ba/_servermode.py
vendored
6
dist/ba_data/python/ba/_servermode.py
vendored
|
|
@ -77,7 +77,7 @@ def _cmd(command_data: bytes) -> None:
|
|||
class ServerController:
|
||||
"""Overall controller for the app in server mode.
|
||||
|
||||
Category: App Classes
|
||||
Category: **App Classes**
|
||||
"""
|
||||
|
||||
def __init__(self, config: ServerConfig) -> None:
|
||||
|
|
@ -227,7 +227,7 @@ class ServerController:
|
|||
|
||||
def _prepare_to_serve(self) -> None:
|
||||
"""Run in a timer to do prep before beginning to serve."""
|
||||
signed_in = _ba.get_account_state() == 'signed_in'
|
||||
signed_in = _ba.get_v1_account_state() == 'signed_in'
|
||||
if not signed_in:
|
||||
|
||||
# Signing in to the local server account should not take long;
|
||||
|
|
@ -302,7 +302,7 @@ class ServerController:
|
|||
appcfg = app.config
|
||||
sessiontype = self._get_session_type()
|
||||
|
||||
if _ba.get_account_state() != 'signed_in':
|
||||
if _ba.get_v1_account_state() != 'signed_in':
|
||||
print('WARNING: launch_server_session() expects to run '
|
||||
'with a signed in server account')
|
||||
|
||||
|
|
|
|||
82
dist/ba_data/python/ba/_session.py
vendored
82
dist/ba_data/python/ba/_session.py
vendored
|
|
@ -17,9 +17,9 @@ if TYPE_CHECKING:
|
|||
|
||||
|
||||
class Session:
|
||||
"""Defines a high level series of ba.Activities with a common purpose.
|
||||
"""Defines a high level series of ba.Activity-es with a common purpose.
|
||||
|
||||
category: Gameplay Classes
|
||||
Category: **Gameplay Classes**
|
||||
|
||||
Examples of sessions are ba.FreeForAllSession, ba.DualTeamSession, and
|
||||
ba.CoopSession.
|
||||
|
|
@ -27,58 +27,48 @@ class Session:
|
|||
A Session is responsible for wrangling and transitioning between various
|
||||
ba.Activity instances such as mini-games and score-screens, and for
|
||||
maintaining state between them (players, teams, score tallies, etc).
|
||||
|
||||
Attributes:
|
||||
|
||||
sessionteams
|
||||
All the ba.SessionTeams in the Session. Most things should use the
|
||||
list of ba.Teams in ba.Activity; not this.
|
||||
|
||||
sessionplayers
|
||||
All ba.SessionPlayers in the Session. Most things should use the
|
||||
list of ba.Players in ba.Activity; not this. Some players, such as
|
||||
those who have not yet selected a character, will only be
|
||||
found on this list.
|
||||
|
||||
min_players
|
||||
The minimum number of players who must be present for the Session
|
||||
to proceed past the initial joining screen.
|
||||
|
||||
max_players
|
||||
The maximum number of players allowed in the Session.
|
||||
|
||||
lobby
|
||||
The ba.Lobby instance where new ba.Players go to select a
|
||||
Profile/Team/etc. before being added to games.
|
||||
Be aware this value may be None if a Session does not allow
|
||||
any such selection.
|
||||
|
||||
use_teams
|
||||
Whether this session groups players into an explicit set of
|
||||
teams. If this is off, a unique team is generated for each
|
||||
player that joins.
|
||||
|
||||
use_team_colors
|
||||
Whether players on a team should all adopt the colors of that
|
||||
team instead of their own profile colors. This only applies if
|
||||
use_teams is enabled.
|
||||
|
||||
customdata
|
||||
A shared dictionary for objects to use as storage on this session.
|
||||
Ensure that keys here are unique to avoid collisions.
|
||||
|
||||
"""
|
||||
use_teams: bool = False
|
||||
use_team_colors: bool = True
|
||||
|
||||
# Note: even though these are instance vars, we annotate them at the
|
||||
# class level so that docs generation can access their types.
|
||||
use_teams: bool = False
|
||||
"""Whether this session groups players into an explicit set of
|
||||
teams. If this is off, a unique team is generated for each
|
||||
player that joins."""
|
||||
|
||||
use_team_colors: bool = True
|
||||
"""Whether players on a team should all adopt the colors of that
|
||||
team instead of their own profile colors. This only applies if
|
||||
use_teams is enabled."""
|
||||
|
||||
# Note: even though these are instance vars, we annotate and document them
|
||||
# at the class level so that looks better and nobody get lost while
|
||||
# reading large __init__
|
||||
|
||||
lobby: ba.Lobby
|
||||
"""The ba.Lobby instance where new ba.Player-s go to select a
|
||||
Profile/Team/etc. before being added to games.
|
||||
Be aware this value may be None if a Session does not allow
|
||||
any such selection."""
|
||||
|
||||
max_players: int
|
||||
"""The maximum number of players allowed in the Session."""
|
||||
|
||||
min_players: int
|
||||
"""The minimum number of players who must be present for the Session
|
||||
to proceed past the initial joining screen"""
|
||||
|
||||
sessionplayers: list[ba.SessionPlayer]
|
||||
"""All ba.SessionPlayers in the Session. Most things should use the
|
||||
list of ba.Player-s in ba.Activity; not this. Some players, such as
|
||||
those who have not yet selected a character, will only be
|
||||
found on this list."""
|
||||
|
||||
customdata: dict
|
||||
"""A shared dictionary for objects to use as storage on this session.
|
||||
Ensure that keys here are unique to avoid collisions."""
|
||||
|
||||
sessionteams: list[ba.SessionTeam]
|
||||
"""All the ba.SessionTeams in the Session. Most things should use the
|
||||
list of ba.Team-s in ba.Activity; not this."""
|
||||
|
||||
def __init__(self,
|
||||
depsets: Sequence[ba.DependencySet],
|
||||
|
|
|
|||
13
dist/ba_data/python/ba/_stats.py
vendored
13
dist/ba_data/python/ba/_stats.py
vendored
|
|
@ -21,20 +21,17 @@ if TYPE_CHECKING:
|
|||
class PlayerScoredMessage:
|
||||
"""Informs something that a ba.Player scored.
|
||||
|
||||
Category: Message Classes
|
||||
|
||||
Attributes:
|
||||
|
||||
score
|
||||
The score value.
|
||||
Category: **Message Classes**
|
||||
"""
|
||||
|
||||
score: int
|
||||
"""The score value."""
|
||||
|
||||
|
||||
class PlayerRecord:
|
||||
"""Stats for an individual player in a ba.Stats object.
|
||||
|
||||
Category: Gameplay Classes
|
||||
Category: **Gameplay Classes**
|
||||
|
||||
This does not necessarily correspond to a ba.Player that is
|
||||
still present (stats may be retained for players that leave
|
||||
|
|
@ -232,7 +229,7 @@ class PlayerRecord:
|
|||
class Stats:
|
||||
"""Manages scores and statistics for a ba.Session.
|
||||
|
||||
category: Gameplay Classes
|
||||
Category: **Gameplay Classes**
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
|
|
|
|||
17
dist/ba_data/python/ba/_store.py
vendored
17
dist/ba_data/python/ba/_store.py
vendored
|
|
@ -366,11 +366,11 @@ def get_store_layout() -> dict[str, list[dict[str, Any]]]:
|
|||
'games.ninja_fight', 'games.meteor_shower', 'games.target_practice'
|
||||
]
|
||||
}]
|
||||
if _ba.get_account_misc_read_val('xmas', False):
|
||||
if _ba.get_v1_account_misc_read_val('xmas', False):
|
||||
store_layout['characters'][0]['items'].append('characters.santa')
|
||||
store_layout['characters'][0]['items'].append('characters.wizard')
|
||||
store_layout['characters'][0]['items'].append('characters.cyborg')
|
||||
if _ba.get_account_misc_read_val('easter', False):
|
||||
if _ba.get_v1_account_misc_read_val('easter', False):
|
||||
store_layout['characters'].append({
|
||||
'title': 'store.holidaySpecialText',
|
||||
'items': ['characters.bunny']
|
||||
|
|
@ -401,10 +401,10 @@ def get_clean_price(price_string: str) -> str:
|
|||
def get_available_purchase_count(tab: str = None) -> int:
|
||||
"""(internal)"""
|
||||
try:
|
||||
if _ba.get_account_state() != 'signed_in':
|
||||
if _ba.get_v1_account_state() != 'signed_in':
|
||||
return 0
|
||||
count = 0
|
||||
our_tickets = _ba.get_account_ticket_count()
|
||||
our_tickets = _ba.get_v1_account_ticket_count()
|
||||
store_data = get_store_layout()
|
||||
if tab is not None:
|
||||
tabs = [(tab, store_data[tab])]
|
||||
|
|
@ -425,7 +425,8 @@ 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)
|
||||
ticket_cost = _ba.get_v1_account_misc_read_val(
|
||||
'price.' + item, None)
|
||||
if ticket_cost is not None:
|
||||
if (our_tickets >= ticket_cost
|
||||
and not _ba.get_purchased(item)):
|
||||
|
|
@ -447,7 +448,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 app.accounts.have_pro():
|
||||
if app.accounts_v1.have_pro():
|
||||
return None
|
||||
|
||||
# If we haven't calced/loaded start times yet.
|
||||
|
|
@ -462,7 +463,7 @@ def get_available_sale_time(tab: str) -> Optional[int]:
|
|||
|
||||
# We start the timer once we get the duration from
|
||||
# the server.
|
||||
start_duration = _ba.get_account_misc_read_val(
|
||||
start_duration = _ba.get_v1_account_misc_read_val(
|
||||
'proSaleDurationMinutes', None)
|
||||
if start_duration is not None:
|
||||
app.pro_sale_start_time = int(
|
||||
|
|
@ -488,7 +489,7 @@ def get_available_sale_time(tab: str) -> Optional[int]:
|
|||
sale_times.append(val)
|
||||
|
||||
# Now look for sales in this tab.
|
||||
sales_raw = _ba.get_account_misc_read_val('sales', {})
|
||||
sales_raw = _ba.get_v1_account_misc_read_val('sales', {})
|
||||
store_layout = get_store_layout()
|
||||
for section in store_layout[tab]:
|
||||
for item in section['items']:
|
||||
|
|
|
|||
41
dist/ba_data/python/ba/_team.py
vendored
41
dist/ba_data/python/ba/_team.py
vendored
|
|
@ -17,39 +17,32 @@ if TYPE_CHECKING:
|
|||
class SessionTeam:
|
||||
"""A team of one or more ba.SessionPlayers.
|
||||
|
||||
Category: Gameplay Classes
|
||||
Category: **Gameplay Classes**
|
||||
|
||||
Note that a SessionPlayer *always* has a SessionTeam;
|
||||
in some cases, such as free-for-all ba.Sessions,
|
||||
each SessionTeam consists of just one SessionPlayer.
|
||||
|
||||
Attributes:
|
||||
|
||||
name
|
||||
The team's name.
|
||||
|
||||
id
|
||||
The unique numeric id of the team.
|
||||
|
||||
color
|
||||
The team's color.
|
||||
|
||||
players
|
||||
The list of ba.SessionPlayers on the team.
|
||||
|
||||
customdata
|
||||
A dict for use by the current ba.Session for
|
||||
storing data associated with this team.
|
||||
Unlike customdata, this persists for the duration
|
||||
of the session.
|
||||
"""
|
||||
|
||||
# Annotate our attr types at the class level so they're introspectable.
|
||||
|
||||
name: Union[ba.Lstr, str]
|
||||
"""The team's name."""
|
||||
|
||||
color: tuple[float, ...] # FIXME: can't we make this fixed len?
|
||||
"""The team's color."""
|
||||
|
||||
players: list[ba.SessionPlayer]
|
||||
"""The list of ba.SessionPlayer-s on the team."""
|
||||
|
||||
customdata: dict
|
||||
"""A dict for use by the current ba.Session for
|
||||
storing data associated with this team.
|
||||
Unlike customdata, this persists for the duration
|
||||
of the session."""
|
||||
|
||||
id: int
|
||||
"""The unique numeric id of the team."""
|
||||
|
||||
def __init__(self,
|
||||
team_id: int = 0,
|
||||
|
|
@ -73,13 +66,15 @@ class SessionTeam:
|
|||
self.customdata = {}
|
||||
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
PlayerType = TypeVar('PlayerType', bound='ba.Player')
|
||||
# pylint: enable=invalid-name
|
||||
|
||||
|
||||
class Team(Generic[PlayerType]):
|
||||
"""A team in a specific ba.Activity.
|
||||
|
||||
Category: Gameplay Classes
|
||||
Category: **Gameplay Classes**
|
||||
|
||||
These correspond to ba.SessionTeam objects, but are created per activity
|
||||
so that the activity can use its own custom team subclass.
|
||||
|
|
@ -197,7 +192,7 @@ class Team(Generic[PlayerType]):
|
|||
class EmptyTeam(Team['ba.EmptyPlayer']):
|
||||
"""An empty player for use by Activities that don't need to define one.
|
||||
|
||||
Category: Gameplay Classes
|
||||
Category: **Gameplay Classes**
|
||||
|
||||
ba.Player and ba.Team are 'Generic' types, and so passing those top level
|
||||
classes as type arguments when defining a ba.Activity reduces type safety.
|
||||
|
|
|
|||
6
dist/ba_data/python/ba/_teamgame.py
vendored
6
dist/ba_data/python/ba/_teamgame.py
vendored
|
|
@ -17,14 +17,16 @@ if TYPE_CHECKING:
|
|||
from bastd.actor.playerspaz import PlayerSpaz
|
||||
import ba
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
PlayerType = TypeVar('PlayerType', bound='ba.Player')
|
||||
TeamType = TypeVar('TeamType', bound='ba.Team')
|
||||
# pylint: enable=invalid-name
|
||||
|
||||
|
||||
class TeamGameActivity(GameActivity[PlayerType, TeamType]):
|
||||
"""Base class for teams and free-for-all mode games.
|
||||
|
||||
Category: Gameplay Classes
|
||||
Category: **Gameplay Classes**
|
||||
|
||||
(Free-for-all is essentially just a special case where every
|
||||
ba.Player has their own ba.Team)
|
||||
|
|
@ -120,7 +122,7 @@ class TeamGameActivity(GameActivity[PlayerType, TeamType]):
|
|||
unless 'announce_winning_team' is False.
|
||||
(for results without a single most-important winner).
|
||||
"""
|
||||
# pylint: disable=arguments-differ
|
||||
# pylint: disable=arguments-renamed
|
||||
from ba._coopsession import CoopSession
|
||||
from ba._multiteamsession import MultiTeamSession
|
||||
from ba._general import Call
|
||||
|
|
|
|||
2
dist/ba_data/python/ba/_ui.py
vendored
2
dist/ba_data/python/ba/_ui.py
vendored
|
|
@ -18,7 +18,7 @@ if TYPE_CHECKING:
|
|||
class UISubsystem:
|
||||
"""Consolidated UI functionality for the app.
|
||||
|
||||
Category: App Classes
|
||||
Category: **App Classes**
|
||||
|
||||
To use this class, access the single instance of it at 'ba.app.ui'.
|
||||
"""
|
||||
|
|
|
|||
93
dist/ba_data/python/ba/cloud.py
vendored
Normal file
93
dist/ba_data/python/ba/cloud.py
vendored
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
# Released under the MIT License. See LICENSE for details.
|
||||
#
|
||||
"""Functionality related to the cloud."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, overload
|
||||
|
||||
import _ba
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Union, Callable, Any
|
||||
|
||||
from efro.message import Message
|
||||
import bacommon.cloud
|
||||
|
||||
# TODO: Should make it possible to define a protocol in bacommon.cloud and
|
||||
# autogenerate this. That would give us type safety between this and
|
||||
# internal protocols.
|
||||
|
||||
|
||||
class CloudSubsystem:
|
||||
"""Used for communicating with the cloud."""
|
||||
|
||||
def is_connected(self) -> bool:
|
||||
"""Return whether a connection to the cloud is present.
|
||||
|
||||
This is a good indicator (though not for certain) that sending
|
||||
messages will succeed.
|
||||
"""
|
||||
return False # Needs to be overridden
|
||||
|
||||
@overload
|
||||
def send_message(
|
||||
self,
|
||||
msg: bacommon.cloud.LoginProxyRequestMessage,
|
||||
on_response: Callable[
|
||||
[Union[bacommon.cloud.LoginProxyRequestResponse,
|
||||
Exception]], None],
|
||||
) -> None:
|
||||
...
|
||||
|
||||
@overload
|
||||
def send_message(
|
||||
self,
|
||||
msg: bacommon.cloud.LoginProxyStateQueryMessage,
|
||||
on_response: Callable[
|
||||
[Union[bacommon.cloud.LoginProxyStateQueryResponse,
|
||||
Exception]], None],
|
||||
) -> None:
|
||||
...
|
||||
|
||||
@overload
|
||||
def send_message(
|
||||
self,
|
||||
msg: bacommon.cloud.LoginProxyCompleteMessage,
|
||||
on_response: Callable[[Union[None, Exception]], None],
|
||||
) -> None:
|
||||
...
|
||||
|
||||
@overload
|
||||
def send_message(
|
||||
self,
|
||||
msg: bacommon.cloud.CredentialsCheckMessage,
|
||||
on_response: Callable[
|
||||
[Union[bacommon.cloud.CredentialsCheckResponse, Exception]], None],
|
||||
) -> None:
|
||||
...
|
||||
|
||||
@overload
|
||||
def send_message(
|
||||
self,
|
||||
msg: bacommon.cloud.AccountSessionReleaseMessage,
|
||||
on_response: Callable[[Union[None, Exception]], None],
|
||||
) -> None:
|
||||
...
|
||||
|
||||
def send_message(
|
||||
self,
|
||||
msg: Message,
|
||||
on_response: Callable[[Any], None],
|
||||
) -> None:
|
||||
"""Asynchronously send a message to the cloud from the game thread.
|
||||
|
||||
The provided on_response call will be run in the logic thread
|
||||
and passed either the response or the error that occurred.
|
||||
"""
|
||||
from ba._general import Call
|
||||
del msg # Unused.
|
||||
|
||||
_ba.pushcall(
|
||||
Call(on_response,
|
||||
RuntimeError('Cloud functionality is not available.')))
|
||||
23
dist/ba_data/python/ba/internal.py
vendored
23
dist/ba_data/python/ba/internal.py
vendored
|
|
@ -7,8 +7,6 @@ or disappear without warning, so should be avoided (or used sparingly and
|
|||
defensively) in mods.
|
||||
"""
|
||||
|
||||
# pylint: disable=unused-import
|
||||
|
||||
from ba._map import (get_unowned_maps, get_map_class, register_map,
|
||||
preload_map_preview_media, get_map_display_string,
|
||||
get_filtered_map_name)
|
||||
|
|
@ -39,3 +37,24 @@ from ba._store import (get_available_sale_time, get_available_purchase_count,
|
|||
get_store_item, get_clean_price)
|
||||
from ba._tournament import get_tournament_prize_strings
|
||||
from ba._gameutils import get_trophy_string
|
||||
|
||||
__all__ = [
|
||||
'get_unowned_maps', 'get_map_class', 'register_map',
|
||||
'preload_map_preview_media', 'get_map_display_string',
|
||||
'get_filtered_map_name', 'commit_app_config', 'get_device_value',
|
||||
'get_input_map_hash', 'get_input_device_config', 'getclass', 'json_prep',
|
||||
'get_type_name', 'JoinActivity', 'ScoreScreenActivity',
|
||||
'is_browser_likely_available', 'get_remote_app_name',
|
||||
'should_submit_debug_info', 'run_gpu_benchmark', 'run_cpu_benchmark',
|
||||
'run_media_reload_benchmark', 'run_stress_test', 'getcampaign',
|
||||
'PlayerProfilesChangedMessage', 'DEFAULT_TEAM_COLORS',
|
||||
'DEFAULT_TEAM_NAMES', 'do_play_music', 'master_server_get',
|
||||
'master_server_post', 'get_ip_address_type',
|
||||
'DEFAULT_REQUEST_TIMEOUT_SECONDS', 'get_default_powerup_distribution',
|
||||
'get_player_profile_colors', 'get_player_profile_icon',
|
||||
'get_player_colors', 'get_next_tip', 'get_default_free_for_all_playlist',
|
||||
'get_default_teams_playlist', 'filter_playlist', 'get_available_sale_time',
|
||||
'get_available_purchase_count', 'get_store_item_name_translated',
|
||||
'get_store_item_display_size', 'get_store_layout', 'get_store_item',
|
||||
'get_clean_price', 'get_tournament_prize_strings', 'get_trophy_string'
|
||||
]
|
||||
|
|
|
|||
71
dist/ba_data/python/bacommon/bacloud.py
vendored
Normal file
71
dist/ba_data/python/bacommon/bacloud.py
vendored
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
# Released under the MIT License. See LICENSE for details.
|
||||
#
|
||||
"""Functionality related to the bacloud tool."""
|
||||
|
||||
from __future__ import annotations
|
||||
from dataclasses import dataclass
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from efro.dataclassio import ioprepped
|
||||
|
||||
if TYPE_CHECKING:
|
||||
pass
|
||||
|
||||
|
||||
@ioprepped
|
||||
@dataclass
|
||||
class Response:
|
||||
# noinspection PyUnresolvedReferences
|
||||
"""Response sent from the bacloud server to the client.
|
||||
|
||||
Attributes:
|
||||
message: If present, client should print this message before any other
|
||||
response processing (including error handling) occurs.
|
||||
message_end: end arg for message print() call.
|
||||
error: If present, client should abort with this error message.
|
||||
delay_seconds: How long to wait before proceeding with remaining
|
||||
response (can be useful when waiting for server progress in a loop).
|
||||
login: If present, a token that should be stored client-side and passed
|
||||
with subsequent commands.
|
||||
logout: If True, any existing client-side token should be discarded.
|
||||
dir_manifest: If present, client should generate a manifest of this dir.
|
||||
It should be added to end_command args as 'manifest'.
|
||||
uploads: If present, client should upload the requested files (arg1)
|
||||
individually to a server command (arg2) with provided args (arg3).
|
||||
uploads_inline: If present, a list of pathnames that should be base64
|
||||
gzipped and uploaded to an 'uploads_inline' dict in end_command args.
|
||||
This should be limited to relatively small files.
|
||||
downloads_inline: If present, pathnames mapped to base64 gzipped data to
|
||||
be written to the client. This should only be used for relatively
|
||||
small files as they are all included inline as part of the response.
|
||||
deletes: If present, file paths that should be deleted on the client.
|
||||
dir_prune_empty: If present, all empty dirs under this one should be
|
||||
removed.
|
||||
open_url: If present, url to display to the user.
|
||||
input_prompt: If present, a line of input is read and placed into
|
||||
end_command args as 'input'. The first value is the prompt printed
|
||||
before reading and the second is whether it should be read as a
|
||||
password (without echoing to the terminal).
|
||||
end_message: If present, a message that should be printed after all other
|
||||
response processing is done.
|
||||
end_message_end: end arg for end_message print() call.
|
||||
end_command: If present, this command is run with these args at the end
|
||||
of response processing.
|
||||
"""
|
||||
message: Optional[str] = None
|
||||
message_end: str = '\n'
|
||||
error: Optional[str] = None
|
||||
delay_seconds: float = 0.0
|
||||
login: Optional[str] = None
|
||||
logout: bool = False
|
||||
dir_manifest: Optional[str] = None
|
||||
uploads: Optional[tuple[list[str], str, dict]] = None
|
||||
uploads_inline: Optional[list[str]] = None
|
||||
downloads_inline: Optional[dict[str, str]] = None
|
||||
deletes: Optional[list[str]] = None
|
||||
dir_prune_empty: Optional[str] = None
|
||||
open_url: Optional[str] = None
|
||||
input_prompt: Optional[tuple[str, bool]] = None
|
||||
end_message: Optional[str] = None
|
||||
end_message_end: str = '\n'
|
||||
end_command: Optional[tuple[str, dict]] = None
|
||||
103
dist/ba_data/python/bacommon/cloud.py
vendored
Normal file
103
dist/ba_data/python/bacommon/cloud.py
vendored
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
# Released under the MIT License. See LICENSE for details.
|
||||
#
|
||||
"""Functionality related to cloud functionality."""
|
||||
|
||||
from __future__ import annotations
|
||||
from dataclasses import dataclass
|
||||
from typing import TYPE_CHECKING, Annotated, Optional
|
||||
from enum import Enum
|
||||
|
||||
from efro.message import Message, Response
|
||||
from efro.dataclassio import ioprepped, IOAttrs
|
||||
|
||||
if TYPE_CHECKING:
|
||||
pass
|
||||
|
||||
|
||||
@ioprepped
|
||||
@dataclass
|
||||
class LoginProxyRequestMessage(Message):
|
||||
"""Request send to the cloud to ask for a login-proxy."""
|
||||
|
||||
@classmethod
|
||||
def get_response_types(cls) -> list[type[Response]]:
|
||||
return [LoginProxyRequestResponse]
|
||||
|
||||
|
||||
@ioprepped
|
||||
@dataclass
|
||||
class LoginProxyRequestResponse(Response):
|
||||
"""Response to a request for a login proxy."""
|
||||
|
||||
# URL to direct the user to for login.
|
||||
url: Annotated[str, IOAttrs('u')]
|
||||
|
||||
# Proxy-Login id for querying results.
|
||||
proxyid: Annotated[str, IOAttrs('p')]
|
||||
|
||||
# Proxy-Login key for querying results.
|
||||
proxykey: Annotated[str, IOAttrs('k')]
|
||||
|
||||
|
||||
@ioprepped
|
||||
@dataclass
|
||||
class LoginProxyStateQueryMessage(Message):
|
||||
"""Soo.. how is that login proxy going?"""
|
||||
proxyid: Annotated[str, IOAttrs('p')]
|
||||
proxykey: Annotated[str, IOAttrs('k')]
|
||||
|
||||
@classmethod
|
||||
def get_response_types(cls) -> list[type[Response]]:
|
||||
return [LoginProxyStateQueryResponse]
|
||||
|
||||
|
||||
@ioprepped
|
||||
@dataclass
|
||||
class LoginProxyStateQueryResponse(Response):
|
||||
"""Here's the info on that login-proxy you asked about, boss."""
|
||||
|
||||
class State(Enum):
|
||||
"""States a login-proxy can be in."""
|
||||
WAITING = 'waiting'
|
||||
SUCCESS = 'success'
|
||||
FAIL = 'fail'
|
||||
|
||||
state: Annotated[State, IOAttrs('s')]
|
||||
|
||||
# On success, these will be filled out.
|
||||
credentials: Annotated[Optional[str], IOAttrs('tk')]
|
||||
|
||||
|
||||
@ioprepped
|
||||
@dataclass
|
||||
class LoginProxyCompleteMessage(Message):
|
||||
"""Just so you know, we're done with this proxy."""
|
||||
proxyid: Annotated[str, IOAttrs('p')]
|
||||
|
||||
|
||||
@ioprepped
|
||||
@dataclass
|
||||
class AccountSessionReleaseMessage(Message):
|
||||
"""We're done using this particular session."""
|
||||
token: Annotated[str, IOAttrs('tk')]
|
||||
|
||||
|
||||
@ioprepped
|
||||
@dataclass
|
||||
class CredentialsCheckMessage(Message):
|
||||
"""Are our current credentials valid?"""
|
||||
|
||||
@classmethod
|
||||
def get_response_types(cls) -> list[type[Response]]:
|
||||
return [CredentialsCheckResponse]
|
||||
|
||||
|
||||
@ioprepped
|
||||
@dataclass
|
||||
class CredentialsCheckResponse(Response):
|
||||
"""Info returned when checking credentials."""
|
||||
|
||||
verified: Annotated[bool, IOAttrs('v')]
|
||||
|
||||
# Current account tag (good time to check if it has changed).
|
||||
tag: Annotated[str, IOAttrs('t')]
|
||||
2
dist/ba_data/python/bacommon/net.py
vendored
2
dist/ba_data/python/bacommon/net.py
vendored
|
|
@ -17,7 +17,7 @@ if TYPE_CHECKING:
|
|||
@dataclass
|
||||
class ServerNodeEntry:
|
||||
"""Information about a specific server."""
|
||||
region: Annotated[str, IOAttrs('r')]
|
||||
zone: Annotated[str, IOAttrs('r')]
|
||||
address: Annotated[str, IOAttrs('a')]
|
||||
port: Annotated[int, IOAttrs('p')]
|
||||
|
||||
|
|
|
|||
14
dist/ba_data/python/bastd/activity/coopscore.py
vendored
14
dist/ba_data/python/bastd/activity/coopscore.py
vendored
|
|
@ -52,8 +52,9 @@ class CoopScoreScreen(ba.Activity[ba.Player, ba.Team]):
|
|||
ba.app.ach.achievements_for_coop_level(self._campaign.name + ':' +
|
||||
settings['level']))
|
||||
|
||||
self._account_type = (_ba.get_account_type() if
|
||||
_ba.get_account_state() == 'signed_in' else None)
|
||||
self._account_type = (_ba.get_v1_account_type()
|
||||
if _ba.get_v1_account_state() == 'signed_in' else
|
||||
None)
|
||||
|
||||
self._game_service_icon_color: Optional[Sequence[float]]
|
||||
self._game_service_achievements_texture: Optional[ba.Texture]
|
||||
|
|
@ -631,7 +632,7 @@ class CoopScoreScreen(ba.Activity[ba.Player, ba.Team]):
|
|||
if ba.app.server is None:
|
||||
# If we're running in normal non-headless build, show this text
|
||||
# because only host can continue the game.
|
||||
adisp = _ba.get_account_display_string()
|
||||
adisp = _ba.get_v1_account_display_string()
|
||||
txt = Text(ba.Lstr(resource='waitingForHostText',
|
||||
subs=[('${HOST}', adisp)]),
|
||||
maxwidth=300,
|
||||
|
|
@ -732,7 +733,7 @@ class CoopScoreScreen(ba.Activity[ba.Player, ba.Team]):
|
|||
'scoreVersion': sver,
|
||||
'scores': our_high_scores_all
|
||||
})
|
||||
if _ba.get_account_state() != 'signed_in':
|
||||
if _ba.get_v1_account_state() != 'signed_in':
|
||||
# We expect this only in kiosk mode; complain otherwise.
|
||||
if not (ba.app.demo_mode or ba.app.arcade_mode):
|
||||
print('got not-signed-in at score-submit; unexpected')
|
||||
|
|
@ -841,6 +842,7 @@ class CoopScoreScreen(ba.Activity[ba.Player, ba.Team]):
|
|||
if display_scores[i][1] is None:
|
||||
name_str = '-'
|
||||
else:
|
||||
# noinspection PyUnresolvedReferences
|
||||
name_str = ', '.join([
|
||||
p['name'] for p in display_scores[i][1]['players']
|
||||
])
|
||||
|
|
@ -1259,8 +1261,8 @@ 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.accounts.tournament_info:
|
||||
tourney_info = ba.app.accounts.tournament_info[
|
||||
if tournament_id in ba.app.accounts_v1.tournament_info:
|
||||
tourney_info = ba.app.accounts_v1.tournament_info[
|
||||
tournament_id]
|
||||
# pylint: disable=unbalanced-tuple-unpacking
|
||||
pr1, pv1, pr2, pv2, pr3, pv3 = (
|
||||
|
|
|
|||
|
|
@ -94,6 +94,7 @@ class MultiTeamScoreScreenActivity(ScoreScreenActivity):
|
|||
assert self.stats
|
||||
valid_players = list(self.stats.get_records().items())
|
||||
|
||||
# noinspection PyUnresolvedReferences
|
||||
def _get_player_score_set_entry(
|
||||
player: ba.SessionPlayer) -> Optional[ba.PlayerRecord]:
|
||||
for p_rec in valid_players:
|
||||
|
|
|
|||
202
dist/ba_data/python/bastd/actor/bomb.py
vendored
202
dist/ba_data/python/bastd/actor/bomb.py
vendored
|
|
@ -16,118 +16,118 @@ from bastd.gameutils import SharedObjects
|
|||
if TYPE_CHECKING:
|
||||
from typing import Any, Sequence, Optional, Callable
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
PlayerType = TypeVar('PlayerType', bound='ba.Player')
|
||||
# pylint: enable=invalid-name
|
||||
|
||||
|
||||
class BombFactory:
|
||||
"""Wraps up media and other resources used by ba.Bombs.
|
||||
|
||||
category: Gameplay Classes
|
||||
Category: **Gameplay Classes**
|
||||
|
||||
A single instance of this is shared between all bombs
|
||||
and can be retrieved via bastd.actor.bomb.get_factory().
|
||||
|
||||
Attributes:
|
||||
|
||||
bomb_model
|
||||
The ba.Model of a standard or ice bomb.
|
||||
|
||||
sticky_bomb_model
|
||||
The ba.Model of a sticky-bomb.
|
||||
|
||||
impact_bomb_model
|
||||
The ba.Model of an impact-bomb.
|
||||
|
||||
land_mine_model
|
||||
The ba.Model of a land-mine.
|
||||
|
||||
tnt_model
|
||||
The ba.Model of a tnt box.
|
||||
|
||||
regular_tex
|
||||
The ba.Texture for regular bombs.
|
||||
|
||||
ice_tex
|
||||
The ba.Texture for ice bombs.
|
||||
|
||||
sticky_tex
|
||||
The ba.Texture for sticky bombs.
|
||||
|
||||
impact_tex
|
||||
The ba.Texture for impact bombs.
|
||||
|
||||
impact_lit_tex
|
||||
The ba.Texture for impact bombs with lights lit.
|
||||
|
||||
land_mine_tex
|
||||
The ba.Texture for land-mines.
|
||||
|
||||
land_mine_lit_tex
|
||||
The ba.Texture for land-mines with the light lit.
|
||||
|
||||
tnt_tex
|
||||
The ba.Texture for tnt boxes.
|
||||
|
||||
hiss_sound
|
||||
The ba.Sound for the hiss sound an ice bomb makes.
|
||||
|
||||
debris_fall_sound
|
||||
The ba.Sound for random falling debris after an explosion.
|
||||
|
||||
wood_debris_fall_sound
|
||||
A ba.Sound for random wood debris falling after an explosion.
|
||||
|
||||
explode_sounds
|
||||
A tuple of ba.Sounds for explosions.
|
||||
|
||||
freeze_sound
|
||||
A ba.Sound of an ice bomb freezing something.
|
||||
|
||||
fuse_sound
|
||||
A ba.Sound of a burning fuse.
|
||||
|
||||
activate_sound
|
||||
A ba.Sound for an activating impact bomb.
|
||||
|
||||
warn_sound
|
||||
A ba.Sound for an impact bomb about to explode due to time-out.
|
||||
|
||||
bomb_material
|
||||
A ba.Material applied to all bombs.
|
||||
|
||||
normal_sound_material
|
||||
A ba.Material that generates standard bomb noises on impacts, etc.
|
||||
|
||||
sticky_material
|
||||
A ba.Material that makes 'splat' sounds and makes collisions softer.
|
||||
|
||||
land_mine_no_explode_material
|
||||
A ba.Material that keeps land-mines from blowing up.
|
||||
Applied to land-mines when they are created to allow land-mines to
|
||||
touch without exploding.
|
||||
|
||||
land_mine_blast_material
|
||||
A ba.Material applied to activated land-mines that causes them to
|
||||
explode on impact.
|
||||
|
||||
impact_blast_material
|
||||
A ba.Material applied to activated impact-bombs that causes them to
|
||||
explode on impact.
|
||||
|
||||
blast_material
|
||||
A ba.Material applied to bomb blast geometry which triggers impact
|
||||
events with what it touches.
|
||||
|
||||
dink_sounds
|
||||
A tuple of ba.Sounds for when bombs hit the ground.
|
||||
|
||||
sticky_impact_sound
|
||||
The ba.Sound for a squish made by a sticky bomb hitting something.
|
||||
|
||||
roll_sound
|
||||
ba.Sound for a rolling bomb.
|
||||
"""
|
||||
|
||||
bomb_model: ba.Model
|
||||
"""The ba.Model of a standard or ice bomb."""
|
||||
|
||||
sticky_bomb_model: ba.Model
|
||||
"""The ba.Model of a sticky-bomb."""
|
||||
|
||||
impact_bomb_model: ba.Model
|
||||
"""The ba.Model of an impact-bomb."""
|
||||
|
||||
land_mine_model: ba.Model
|
||||
"""The ba.Model of a land-mine."""
|
||||
|
||||
tnt_model: ba.Model
|
||||
"""The ba.Model of a tnt box."""
|
||||
|
||||
regular_tex: ba.Texture
|
||||
"""The ba.Texture for regular bombs."""
|
||||
|
||||
ice_tex: ba.Texture
|
||||
"""The ba.Texture for ice bombs."""
|
||||
|
||||
sticky_tex: ba.Texture
|
||||
"""The ba.Texture for sticky bombs."""
|
||||
|
||||
impact_tex: ba.Texture
|
||||
"""The ba.Texture for impact bombs."""
|
||||
|
||||
impact_lit_tex: ba.Texture
|
||||
"""The ba.Texture for impact bombs with lights lit."""
|
||||
|
||||
land_mine_tex: ba.Texture
|
||||
"""The ba.Texture for land-mines."""
|
||||
|
||||
land_mine_lit_tex: ba.Texture
|
||||
"""The ba.Texture for land-mines with the light lit."""
|
||||
|
||||
tnt_tex: ba.Texture
|
||||
"""The ba.Texture for tnt boxes."""
|
||||
|
||||
hiss_sound: ba.Sound
|
||||
"""The ba.Sound for the hiss sound an ice bomb makes."""
|
||||
|
||||
debris_fall_sound: ba.Sound
|
||||
"""The ba.Sound for random falling debris after an explosion."""
|
||||
|
||||
wood_debris_fall_sound: ba.Sound
|
||||
"""A ba.Sound for random wood debris falling after an explosion."""
|
||||
|
||||
explode_sounds: Sequence[ba.Sound]
|
||||
"""A tuple of ba.Sound-s for explosions."""
|
||||
|
||||
freeze_sound: ba.Sound
|
||||
"""A ba.Sound of an ice bomb freezing something."""
|
||||
|
||||
fuse_sound: ba.Sound
|
||||
"""A ba.Sound of a burning fuse."""
|
||||
|
||||
activate_sound: ba.Sound
|
||||
"""A ba.Sound for an activating impact bomb."""
|
||||
|
||||
warn_sound: ba.Sound
|
||||
"""A ba.Sound for an impact bomb about to explode due to time-out."""
|
||||
|
||||
bomb_material: ba.Material
|
||||
"""A ba.Material applied to all bombs."""
|
||||
|
||||
normal_sound_material: ba.Material
|
||||
"""A ba.Material that generates standard bomb noises on impacts, etc."""
|
||||
|
||||
sticky_material: ba.Material
|
||||
"""A ba.Material that makes 'splat' sounds and makes collisions softer."""
|
||||
|
||||
land_mine_no_explode_material: ba.Material
|
||||
"""A ba.Material that keeps land-mines from blowing up.
|
||||
Applied to land-mines when they are created to allow land-mines to
|
||||
touch without exploding."""
|
||||
|
||||
land_mine_blast_material: ba.Material
|
||||
"""A ba.Material applied to activated land-mines that causes them to
|
||||
explode on impact."""
|
||||
|
||||
impact_blast_material: ba.Material
|
||||
"""A ba.Material applied to activated impact-bombs that causes them to
|
||||
explode on impact."""
|
||||
|
||||
blast_material: ba.Material
|
||||
"""A ba.Material applied to bomb blast geometry which triggers impact
|
||||
events with what it touches."""
|
||||
|
||||
dink_sounds: Sequence[ba.Sound]
|
||||
"""A tuple of ba.Sound-s for when bombs hit the ground."""
|
||||
|
||||
sticky_impact_sound: ba.Sound
|
||||
"""The ba.Sound for a squish made by a sticky bomb hitting something."""
|
||||
|
||||
roll_sound: ba.Sound
|
||||
"""ba.Sound for a rolling bomb."""
|
||||
|
||||
_STORENAME = ba.storagename()
|
||||
|
||||
@classmethod
|
||||
|
|
|
|||
93
dist/ba_data/python/bastd/actor/flag.py
vendored
93
dist/ba_data/python/bastd/actor/flag.py
vendored
|
|
@ -15,38 +15,36 @@ if TYPE_CHECKING:
|
|||
|
||||
|
||||
class FlagFactory:
|
||||
"""Wraps up media and other resources used by ba.Flags.
|
||||
"""Wraps up media and other resources used by `Flag`s.
|
||||
|
||||
category: Gameplay Classes
|
||||
Category: **Gameplay Classes**
|
||||
|
||||
A single instance of this is shared between all flags
|
||||
and can be retrieved via bastd.actor.flag.get_factory().
|
||||
|
||||
Attributes:
|
||||
|
||||
flagmaterial
|
||||
The ba.Material applied to all ba.Flags.
|
||||
|
||||
impact_sound
|
||||
The ba.Sound used when a ba.Flag hits the ground.
|
||||
|
||||
skid_sound
|
||||
The ba.Sound used when a ba.Flag skids along the ground.
|
||||
|
||||
no_hit_material
|
||||
A ba.Material that prevents contact with most objects;
|
||||
applied to 'non-touchable' flags.
|
||||
|
||||
flag_texture
|
||||
The ba.Texture for flags.
|
||||
and can be retrieved via FlagFactory.get().
|
||||
"""
|
||||
|
||||
flagmaterial: ba.Material
|
||||
"""The ba.Material applied to all `Flag`s."""
|
||||
|
||||
impact_sound: ba.Sound
|
||||
"""The ba.Sound used when a `Flag` hits the ground."""
|
||||
|
||||
skid_sound: ba.Sound
|
||||
"""The ba.Sound used when a `Flag` skids along the ground."""
|
||||
|
||||
no_hit_material: ba.Material
|
||||
"""A ba.Material that prevents contact with most objects;
|
||||
applied to 'non-touchable' flags."""
|
||||
|
||||
flag_texture: ba.Texture
|
||||
"""The ba.Texture for flags."""
|
||||
|
||||
_STORENAME = ba.storagename()
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""Instantiate a FlagFactory.
|
||||
"""Instantiate a `FlagFactory`.
|
||||
|
||||
You shouldn't need to do this; call bastd.actor.flag.get_factory() to
|
||||
You shouldn't need to do this; call FlagFactory.get() to
|
||||
get a shared instance.
|
||||
"""
|
||||
shared = SharedObjects.get()
|
||||
|
|
@ -109,7 +107,7 @@ class FlagFactory:
|
|||
|
||||
@classmethod
|
||||
def get(cls) -> FlagFactory:
|
||||
"""Get/create a shared FlagFactory instance."""
|
||||
"""Get/create a shared `FlagFactory` instance."""
|
||||
activity = ba.getactivity()
|
||||
factory = activity.customdata.get(cls._STORENAME)
|
||||
if factory is None:
|
||||
|
|
@ -121,58 +119,47 @@ class FlagFactory:
|
|||
|
||||
@dataclass
|
||||
class FlagPickedUpMessage:
|
||||
"""A message saying a ba.Flag has been picked up.
|
||||
"""A message saying a `Flag` has been picked up.
|
||||
|
||||
category: Message Classes
|
||||
Category: **Message Classes**
|
||||
"""
|
||||
|
||||
Attributes:
|
||||
|
||||
flag
|
||||
The ba.Flag that has been picked up.
|
||||
|
||||
node
|
||||
The ba.Node doing the picking up.
|
||||
"""
|
||||
flag: Flag
|
||||
"""The `Flag` that has been picked up."""
|
||||
|
||||
node: ba.Node
|
||||
"""The ba.Node doing the picking up."""
|
||||
|
||||
|
||||
@dataclass
|
||||
class FlagDiedMessage:
|
||||
"""A message saying a ba.Flag has died.
|
||||
"""A message saying a `Flag` has died.
|
||||
|
||||
category: Message Classes
|
||||
|
||||
Attributes:
|
||||
|
||||
flag
|
||||
The ba.Flag that died.
|
||||
Category: **Message Classes**
|
||||
"""
|
||||
|
||||
flag: Flag
|
||||
"""The `Flag` that died."""
|
||||
|
||||
|
||||
@dataclass
|
||||
class FlagDroppedMessage:
|
||||
"""A message saying a ba.Flag has been dropped.
|
||||
"""A message saying a `Flag` has been dropped.
|
||||
|
||||
category: Message Classes
|
||||
|
||||
Attributes:
|
||||
|
||||
flag
|
||||
The ba.Flag that was dropped.
|
||||
|
||||
node
|
||||
The ba.Node that was holding it.
|
||||
Category: **Message Classes**
|
||||
"""
|
||||
|
||||
flag: Flag
|
||||
"""The `Flag` that was dropped."""
|
||||
|
||||
node: ba.Node
|
||||
"""The ba.Node that was holding it."""
|
||||
|
||||
|
||||
class Flag(ba.Actor):
|
||||
"""A flag; used in games such as capture-the-flag or king-of-the-hill.
|
||||
|
||||
category: Gameplay Classes
|
||||
Category: **Gameplay Classes**
|
||||
|
||||
Can be stationary or carry-able by players.
|
||||
"""
|
||||
|
|
@ -189,7 +176,7 @@ class Flag(ba.Actor):
|
|||
useful for things like king-of-the-hill where players should
|
||||
not be moving the flag around.
|
||||
|
||||
'materials can be a list of extra ba.Materials to apply to the flag.
|
||||
'materials can be a list of extra `ba.Material`s to apply to the flag.
|
||||
|
||||
If 'dropped_timeout' is provided (in seconds), the flag will die
|
||||
after remaining untouched for that long once it has been moved
|
||||
|
|
|
|||
23
dist/ba_data/python/bastd/actor/playerspaz.py
vendored
23
dist/ba_data/python/bastd/actor/playerspaz.py
vendored
|
|
@ -12,36 +12,36 @@ from spazmod import modifyspaz
|
|||
if TYPE_CHECKING:
|
||||
from typing import Any, Sequence, Optional, Literal
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
PlayerType = TypeVar('PlayerType', bound=ba.Player)
|
||||
TeamType = TypeVar('TeamType', bound=ba.Team)
|
||||
# pylint: enable=invalid-name
|
||||
|
||||
|
||||
class PlayerSpazHurtMessage:
|
||||
"""A message saying a ba.PlayerSpaz was hurt.
|
||||
"""A message saying a PlayerSpaz was hurt.
|
||||
|
||||
category: Message Classes
|
||||
|
||||
Attributes:
|
||||
|
||||
spaz
|
||||
The ba.PlayerSpaz that was hurt
|
||||
Category: **Message Classes**
|
||||
"""
|
||||
|
||||
spaz: PlayerSpaz
|
||||
"""The PlayerSpaz that was hurt"""
|
||||
|
||||
def __init__(self, spaz: PlayerSpaz):
|
||||
"""Instantiate with the given ba.Spaz value."""
|
||||
self.spaz = spaz
|
||||
|
||||
|
||||
class PlayerSpaz(Spaz):
|
||||
"""A ba.Spaz subclass meant to be controlled by a ba.Player.
|
||||
"""A Spaz subclass meant to be controlled by a ba.Player.
|
||||
|
||||
category: Gameplay Classes
|
||||
Category: **Gameplay Classes**
|
||||
|
||||
When a PlayerSpaz dies, it delivers a ba.PlayerDiedMessage
|
||||
to the current ba.Activity. (unless the death was the result of the
|
||||
player leaving the game, in which case no message is sent)
|
||||
|
||||
When a PlayerSpaz is hurt, it delivers a ba.PlayerSpazHurtMessage
|
||||
When a PlayerSpaz is hurt, it delivers a PlayerSpazHurtMessage
|
||||
to the current ba.Activity.
|
||||
"""
|
||||
|
||||
|
|
@ -72,8 +72,7 @@ class PlayerSpaz(Spaz):
|
|||
self._player = player
|
||||
self._drive_player_position()
|
||||
|
||||
import custom_hooks
|
||||
custom_hooks.playerspaz_init(self, self.node, self._player)
|
||||
modifyspaz.main(self, self.node, self._player)
|
||||
|
||||
# Overloads to tell the type system our return type based on doraise val.
|
||||
|
||||
|
|
|
|||
122
dist/ba_data/python/bastd/actor/powerupbox.py
vendored
122
dist/ba_data/python/bastd/actor/powerupbox.py
vendored
|
|
@ -23,69 +23,67 @@ class _TouchedMessage:
|
|||
class PowerupBoxFactory:
|
||||
"""A collection of media and other resources used by ba.Powerups.
|
||||
|
||||
category: Gameplay Classes
|
||||
Category: **Gameplay Classes**
|
||||
|
||||
A single instance of this is shared between all powerups
|
||||
and can be retrieved via ba.Powerup.get_factory().
|
||||
|
||||
Attributes:
|
||||
|
||||
model
|
||||
The ba.Model of the powerup box.
|
||||
|
||||
model_simple
|
||||
A simpler ba.Model of the powerup box, for use in shadows, etc.
|
||||
|
||||
tex_bomb
|
||||
Triple-bomb powerup ba.Texture.
|
||||
|
||||
tex_punch
|
||||
Punch powerup ba.Texture.
|
||||
|
||||
tex_ice_bombs
|
||||
Ice bomb powerup ba.Texture.
|
||||
|
||||
tex_sticky_bombs
|
||||
Sticky bomb powerup ba.Texture.
|
||||
|
||||
tex_shield
|
||||
Shield powerup ba.Texture.
|
||||
|
||||
tex_impact_bombs
|
||||
Impact-bomb powerup ba.Texture.
|
||||
|
||||
tex_health
|
||||
Health powerup ba.Texture.
|
||||
|
||||
tex_land_mines
|
||||
Land-mine powerup ba.Texture.
|
||||
|
||||
tex_curse
|
||||
Curse powerup ba.Texture.
|
||||
|
||||
health_powerup_sound
|
||||
ba.Sound played when a health powerup is accepted.
|
||||
|
||||
powerup_sound
|
||||
ba.Sound played when a powerup is accepted.
|
||||
|
||||
powerdown_sound
|
||||
ba.Sound that can be used when powerups wear off.
|
||||
|
||||
powerup_material
|
||||
ba.Material applied to powerup boxes.
|
||||
|
||||
powerup_accept_material
|
||||
Powerups will send a ba.PowerupMessage to anything they touch
|
||||
that has this ba.Material applied.
|
||||
"""
|
||||
|
||||
model: ba.Model
|
||||
"""The ba.Model of the powerup box."""
|
||||
|
||||
model_simple: ba.Model
|
||||
"""A simpler ba.Model of the powerup box, for use in shadows, etc."""
|
||||
|
||||
tex_bomb: ba.Texture
|
||||
"""Triple-bomb powerup ba.Texture."""
|
||||
|
||||
tex_punch: ba.Texture
|
||||
"""Punch powerup ba.Texture."""
|
||||
|
||||
tex_ice_bombs: ba.Texture
|
||||
"""Ice bomb powerup ba.Texture."""
|
||||
|
||||
tex_sticky_bombs: ba.Texture
|
||||
"""Sticky bomb powerup ba.Texture."""
|
||||
|
||||
tex_shield: ba.Texture
|
||||
"""Shield powerup ba.Texture."""
|
||||
|
||||
tex_impact_bombs: ba.Texture
|
||||
"""Impact-bomb powerup ba.Texture."""
|
||||
|
||||
tex_health: ba.Texture
|
||||
"""Health powerup ba.Texture."""
|
||||
|
||||
tex_land_mines: ba.Texture
|
||||
"""Land-mine powerup ba.Texture."""
|
||||
|
||||
tex_curse: ba.Texture
|
||||
"""Curse powerup ba.Texture."""
|
||||
|
||||
health_powerup_sound: ba.Sound
|
||||
"""ba.Sound played when a health powerup is accepted."""
|
||||
|
||||
powerup_sound: ba.Sound
|
||||
"""ba.Sound played when a powerup is accepted."""
|
||||
|
||||
powerdown_sound: ba.Sound
|
||||
"""ba.Sound that can be used when powerups wear off."""
|
||||
|
||||
powerup_material: ba.Material
|
||||
"""ba.Material applied to powerup boxes."""
|
||||
|
||||
powerup_accept_material: ba.Material
|
||||
"""Powerups will send a ba.PowerupMessage to anything they touch
|
||||
that has this ba.Material applied."""
|
||||
|
||||
_STORENAME = ba.storagename()
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""Instantiate a PowerupBoxFactory.
|
||||
|
||||
You shouldn't need to do this; call ba.Powerup.get_factory()
|
||||
You shouldn't need to do this; call Powerup.get_factory()
|
||||
to get a shared instance.
|
||||
"""
|
||||
from ba.internal import get_default_powerup_distribution
|
||||
|
|
@ -191,18 +189,16 @@ class PowerupBox(ba.Actor):
|
|||
|
||||
This will deliver a ba.PowerupMessage to anything that touches it
|
||||
which has the ba.PowerupBoxFactory.powerup_accept_material applied.
|
||||
|
||||
Attributes:
|
||||
|
||||
poweruptype
|
||||
The string powerup type. This can be 'triple_bombs', 'punch',
|
||||
'ice_bombs', 'impact_bombs', 'land_mines', 'sticky_bombs', 'shield',
|
||||
'health', or 'curse'.
|
||||
|
||||
node
|
||||
The 'prop' ba.Node representing this box.
|
||||
"""
|
||||
|
||||
poweruptype: str
|
||||
"""The string powerup type. This can be 'triple_bombs', 'punch',
|
||||
'ice_bombs', 'impact_bombs', 'land_mines', 'sticky_bombs', 'shield',
|
||||
'health', or 'curse'."""
|
||||
|
||||
node: ba.Node
|
||||
"""The 'prop' ba.Node representing this box."""
|
||||
|
||||
def __init__(self,
|
||||
position: Sequence[float] = (0.0, 1.0, 0.0),
|
||||
poweruptype: str = 'triple_bombs',
|
||||
|
|
|
|||
28
dist/ba_data/python/bastd/actor/spawner.py
vendored
28
dist/ba_data/python/bastd/actor/spawner.py
vendored
|
|
@ -16,29 +16,27 @@ if TYPE_CHECKING:
|
|||
class Spawner:
|
||||
"""Utility for delayed spawning of objects.
|
||||
|
||||
category: Gameplay Classes
|
||||
Category: **Gameplay Classes**
|
||||
|
||||
Creates a light flash and sends a ba.Spawner.SpawnMessage
|
||||
Creates a light flash and sends a Spawner.SpawnMessage
|
||||
to the current activity after a delay.
|
||||
"""
|
||||
|
||||
class SpawnMessage:
|
||||
"""Spawn message sent by a ba.Spawner after its delay has passed.
|
||||
"""Spawn message sent by a Spawner after its delay has passed.
|
||||
|
||||
category: Message Classes
|
||||
|
||||
Attributes:
|
||||
|
||||
spawner
|
||||
The ba.Spawner we came from.
|
||||
|
||||
data
|
||||
The data object passed by the user.
|
||||
|
||||
pt
|
||||
The spawn position.
|
||||
Category: **Message Classes**
|
||||
"""
|
||||
|
||||
spawner: Spawner
|
||||
"""The ba.Spawner we came from."""
|
||||
|
||||
data: Any
|
||||
"""The data object passed by the user."""
|
||||
|
||||
pt: Sequence[float]
|
||||
"""The spawn position."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
spawner: Spawner,
|
||||
|
|
|
|||
11
dist/ba_data/python/bastd/actor/spaz.py
vendored
11
dist/ba_data/python/bastd/actor/spaz.py
vendored
|
|
@ -16,7 +16,6 @@ from bastd.gameutils import SharedObjects
|
|||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Any, Sequence, Optional, Union, Callable
|
||||
from bastd.actor.spazfactory import SpazFactory
|
||||
|
||||
POWERUP_WEAR_OFF_TIME = 20000
|
||||
BASE_PUNCH_COOLDOWN = 400
|
||||
|
|
@ -42,23 +41,21 @@ class Spaz(ba.Actor):
|
|||
"""
|
||||
Base class for various Spazzes.
|
||||
|
||||
category: Gameplay Classes
|
||||
Category: **Gameplay Classes**
|
||||
|
||||
A Spaz is the standard little humanoid character in the game.
|
||||
It can be controlled by a player or by AI, and can have
|
||||
various different appearances. The name 'Spaz' is not to be
|
||||
confused with the 'Spaz' character in the game, which is just
|
||||
one of the skins available for instances of this class.
|
||||
|
||||
Attributes:
|
||||
|
||||
node
|
||||
The 'spaz' ba.Node.
|
||||
"""
|
||||
|
||||
# pylint: disable=too-many-public-methods
|
||||
# pylint: disable=too-many-locals
|
||||
|
||||
node: ba.Node
|
||||
"""The 'spaz' ba.Node."""
|
||||
|
||||
points_mult = 1
|
||||
curse_time: Optional[float] = 5.0
|
||||
default_bomb_count = 1
|
||||
|
|
|
|||
40
dist/ba_data/python/bastd/actor/spazbot.py
vendored
40
dist/ba_data/python/bastd/actor/spazbot.py
vendored
|
|
@ -27,17 +27,15 @@ PRO_BOT_HIGHLIGHT = (0.6, 0.1, 0.05)
|
|||
class SpazBotPunchedMessage:
|
||||
"""A message saying a ba.SpazBot got punched.
|
||||
|
||||
category: Message Classes
|
||||
|
||||
Attributes:
|
||||
|
||||
spazbot
|
||||
The ba.SpazBot that got punched.
|
||||
|
||||
damage
|
||||
How much damage was done to the ba.SpazBot.
|
||||
Category: **Message Classes**
|
||||
"""
|
||||
|
||||
spazbot: SpazBot
|
||||
"""The ba.SpazBot that got punched."""
|
||||
|
||||
damage: int
|
||||
"""How much damage was done to the SpazBot."""
|
||||
|
||||
def __init__(self, spazbot: SpazBot, damage: int):
|
||||
"""Instantiate a message with the given values."""
|
||||
self.spazbot = spazbot
|
||||
|
|
@ -47,20 +45,18 @@ class SpazBotPunchedMessage:
|
|||
class SpazBotDiedMessage:
|
||||
"""A message saying a ba.SpazBot has died.
|
||||
|
||||
category: Message Classes
|
||||
|
||||
Attributes:
|
||||
|
||||
spazbot
|
||||
The ba.SpazBot that was killed.
|
||||
|
||||
killerplayer
|
||||
The ba.Player that killed it (or None).
|
||||
|
||||
how
|
||||
The particular type of death.
|
||||
Category: **Message Classes**
|
||||
"""
|
||||
|
||||
spazbot: SpazBot
|
||||
"""The SpazBot that was killed."""
|
||||
|
||||
killerplayer: Optional[ba.Player]
|
||||
"""The ba.Player that killed it (or None)."""
|
||||
|
||||
how: ba.DeathType
|
||||
"""The particular type of death."""
|
||||
|
||||
def __init__(self, spazbot: SpazBot, killerplayer: Optional[ba.Player],
|
||||
how: ba.DeathType):
|
||||
"""Instantiate with given values."""
|
||||
|
|
@ -72,7 +68,7 @@ class SpazBotDiedMessage:
|
|||
class SpazBot(Spaz):
|
||||
"""A really dumb AI version of ba.Spaz.
|
||||
|
||||
category: Bot Classes
|
||||
Category: **Bot Classes**
|
||||
|
||||
Add these to a ba.BotSet to use them.
|
||||
|
||||
|
|
|
|||
122
dist/ba_data/python/bastd/actor/spazfactory.py
vendored
122
dist/ba_data/python/bastd/actor/spazfactory.py
vendored
|
|
@ -11,72 +11,70 @@ from bastd.gameutils import SharedObjects
|
|||
import _ba
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Any
|
||||
from typing import Any, Sequence
|
||||
|
||||
|
||||
class SpazFactory:
|
||||
"""Wraps up media and other resources used by ba.Spaz instances.
|
||||
|
||||
Category: Gameplay Classes
|
||||
Category: **Gameplay Classes**
|
||||
|
||||
Generally one of these is created per ba.Activity and shared
|
||||
between all spaz instances. Use ba.Spaz.get_factory() to return
|
||||
between all spaz instances. Use ba.Spaz.get_factory() to return
|
||||
the shared factory for the current activity.
|
||||
|
||||
Attributes:
|
||||
|
||||
impact_sounds_medium
|
||||
A tuple of ba.Sounds for when a ba.Spaz hits something kinda hard.
|
||||
|
||||
impact_sounds_hard
|
||||
A tuple of ba.Sounds for when a ba.Spaz hits something really hard.
|
||||
|
||||
impact_sounds_harder
|
||||
A tuple of ba.Sounds for when a ba.Spaz hits something really
|
||||
really hard.
|
||||
|
||||
single_player_death_sound
|
||||
The sound that plays for an 'important' spaz death such as in
|
||||
co-op games.
|
||||
|
||||
punch_sound
|
||||
A standard punch ba.Sound.
|
||||
|
||||
punch_sound_strong
|
||||
A tuple of stronger sounding punch ba.Sounds.
|
||||
|
||||
punch_sound_stronger
|
||||
A really really strong sounding punch ba.Sound.
|
||||
|
||||
swish_sound
|
||||
A punch swish ba.Sound.
|
||||
|
||||
block_sound
|
||||
A ba.Sound for when an attack is blocked by invincibility.
|
||||
|
||||
shatter_sound
|
||||
A ba.Sound for when a frozen ba.Spaz shatters.
|
||||
|
||||
splatter_sound
|
||||
A ba.Sound for when a ba.Spaz blows up via curse.
|
||||
|
||||
spaz_material
|
||||
A ba.Material applied to all of parts of a ba.Spaz.
|
||||
|
||||
roller_material
|
||||
A ba.Material applied to the invisible roller ball body that
|
||||
a ba.Spaz uses for locomotion.
|
||||
|
||||
punch_material
|
||||
A ba.Material applied to the 'fist' of a ba.Spaz.
|
||||
|
||||
pickup_material
|
||||
A ba.Material applied to the 'grabber' body of a ba.Spaz.
|
||||
|
||||
curse_material
|
||||
A ba.Material applied to a cursed ba.Spaz that triggers an explosion.
|
||||
"""
|
||||
|
||||
impact_sounds_medium: Sequence[ba.Sound]
|
||||
"""A tuple of ba.Sound-s for when a ba.Spaz hits something kinda hard."""
|
||||
|
||||
impact_sounds_hard: Sequence[ba.Sound]
|
||||
"""A tuple of ba.Sound-s for when a ba.Spaz hits something really hard."""
|
||||
|
||||
impact_sounds_harder: Sequence[ba.Sound]
|
||||
"""A tuple of ba.Sound-s for when a ba.Spaz hits something really
|
||||
really hard."""
|
||||
|
||||
single_player_death_sound: ba.Sound
|
||||
"""The sound that plays for an 'important' spaz death such as in
|
||||
co-op games."""
|
||||
|
||||
punch_sound: ba.Sound
|
||||
"""A standard punch ba.Sound."""
|
||||
|
||||
punch_sound_strong: Sequence[ba.Sound]
|
||||
"""A tuple of stronger sounding punch ba.Sounds."""
|
||||
|
||||
punch_sound_stronger: ba.Sound
|
||||
"""A really really strong sounding punch ba.Sound."""
|
||||
|
||||
swish_sound: ba.Sound
|
||||
"""A punch swish ba.Sound."""
|
||||
|
||||
block_sound: ba.Sound
|
||||
"""A ba.Sound for when an attack is blocked by invincibility."""
|
||||
|
||||
shatter_sound: ba.Sound
|
||||
"""A ba.Sound for when a frozen ba.Spaz shatters."""
|
||||
|
||||
splatter_sound: ba.Sound
|
||||
"""A ba.Sound for when a ba.Spaz blows up via curse."""
|
||||
|
||||
spaz_material: ba.Material
|
||||
"""A ba.Material applied to all of parts of a ba.Spaz."""
|
||||
|
||||
roller_material: ba.Material
|
||||
"""A ba.Material applied to the invisible roller ball body that
|
||||
a ba.Spaz uses for locomotion."""
|
||||
|
||||
punch_material: ba.Material
|
||||
"""A ba.Material applied to the 'fist' of a ba.Spaz."""
|
||||
|
||||
pickup_material: ba.Material
|
||||
"""A ba.Material applied to the 'grabber' body of a ba.Spaz."""
|
||||
|
||||
curse_material: ba.Material
|
||||
"""A ba.Material applied to a cursed ba.Spaz that triggers an explosion."""
|
||||
|
||||
_STORENAME = ba.storagename()
|
||||
|
||||
def _preload(self, character: str) -> None:
|
||||
|
|
@ -210,14 +208,14 @@ class SpazFactory:
|
|||
|
||||
# Lets load some basic rules.
|
||||
# (allows them to be tweaked from the master server)
|
||||
self.shield_decay_rate = _ba.get_account_misc_read_val('rsdr', 10.0)
|
||||
self.punch_cooldown = _ba.get_account_misc_read_val('rpc', 400)
|
||||
self.punch_cooldown_gloves = (_ba.get_account_misc_read_val(
|
||||
self.shield_decay_rate = _ba.get_v1_account_misc_read_val('rsdr', 10.0)
|
||||
self.punch_cooldown = _ba.get_v1_account_misc_read_val('rpc', 400)
|
||||
self.punch_cooldown_gloves = (_ba.get_v1_account_misc_read_val(
|
||||
'rpcg', 300))
|
||||
self.punch_power_scale = _ba.get_account_misc_read_val('rpp', 1.2)
|
||||
self.punch_power_scale_gloves = (_ba.get_account_misc_read_val(
|
||||
self.punch_power_scale = _ba.get_v1_account_misc_read_val('rpp', 1.2)
|
||||
self.punch_power_scale_gloves = (_ba.get_v1_account_misc_read_val(
|
||||
'rppg', 1.4))
|
||||
self.max_shield_spillover_damage = (_ba.get_account_misc_read_val(
|
||||
self.max_shield_spillover_damage = (_ba.get_v1_account_misc_read_val(
|
||||
'rsms', 500))
|
||||
|
||||
def get_style(self, character: str) -> str:
|
||||
|
|
|
|||
10
dist/ba_data/python/bastd/game/assault.py
vendored
10
dist/ba_data/python/bastd/game/assault.py
vendored
|
|
@ -175,12 +175,16 @@ class AssaultGame(ba.TeamGameActivity[Player, Team]):
|
|||
|
||||
def _handle_base_collide(self, team: Team) -> None:
|
||||
try:
|
||||
player = ba.getcollision().opposingnode.getdelegate(
|
||||
PlayerSpaz, True).getplayer(Player, True)
|
||||
spaz = ba.getcollision().opposingnode.getdelegate(PlayerSpaz, True)
|
||||
except ba.NotFoundError:
|
||||
return
|
||||
|
||||
if not player.is_alive():
|
||||
if not spaz.is_alive():
|
||||
return
|
||||
|
||||
try:
|
||||
player = spaz.getplayer(Player, True)
|
||||
except ba.NotFoundError:
|
||||
return
|
||||
|
||||
# If its another team's player, they scored.
|
||||
|
|
|
|||
16
dist/ba_data/python/bastd/game/capturetheflag.py
vendored
16
dist/ba_data/python/bastd/game/capturetheflag.py
vendored
|
|
@ -274,6 +274,11 @@ class CaptureTheFlagGame(ba.TeamGameActivity[Player, Team]):
|
|||
|
||||
# If the enemy flag is already here, score!
|
||||
if team.enemy_flag_at_base:
|
||||
# And show team name which scored (but actually we could
|
||||
# show here player who returned enemy flag).
|
||||
self.show_zoom_message(ba.Lstr(resource='nameScoresText',
|
||||
subs=[('${NAME}', team.name)]),
|
||||
color=team.color)
|
||||
self._score(team)
|
||||
else:
|
||||
team.enemy_flag_at_base = True
|
||||
|
|
@ -435,11 +440,14 @@ class CaptureTheFlagGame(ba.TeamGameActivity[Player, Team]):
|
|||
"""
|
||||
player: Optional[Player]
|
||||
try:
|
||||
player = ba.getcollision().sourcenode.getdelegate(
|
||||
PlayerSpaz, True).getplayer(Player, True)
|
||||
spaz = ba.getcollision().sourcenode.getdelegate(PlayerSpaz, True)
|
||||
except ba.NotFoundError:
|
||||
# This can happen if the player leaves but his corpse touches/etc.
|
||||
player = None
|
||||
return
|
||||
|
||||
if not spaz.is_alive():
|
||||
return
|
||||
|
||||
player = spaz.getplayer(Player, True)
|
||||
|
||||
if player:
|
||||
player.touching_own_flag += (1 if connecting else -1)
|
||||
|
|
|
|||
|
|
@ -239,11 +239,15 @@ class KingOfTheHillGame(ba.TeamGameActivity[Player, Team]):
|
|||
|
||||
def _handle_player_flag_region_collide(self, colliding: bool) -> None:
|
||||
try:
|
||||
player = ba.getcollision().opposingnode.getdelegate(
|
||||
PlayerSpaz, True).getplayer(Player, True)
|
||||
spaz = ba.getcollision().sourcenode.getdelegate(PlayerSpaz, True)
|
||||
except ba.NotFoundError:
|
||||
return
|
||||
|
||||
if not spaz.is_alive():
|
||||
return
|
||||
|
||||
player = spaz.getplayer(Player, True)
|
||||
|
||||
# Different parts of us can collide so a single value isn't enough
|
||||
# also don't count it if we're dead (flying heads shouldn't be able to
|
||||
# win the game :-)
|
||||
|
|
|
|||
16
dist/ba_data/python/bastd/game/race.py
vendored
16
dist/ba_data/python/bastd/game/race.py
vendored
|
|
@ -224,9 +224,15 @@ class RaceGame(ba.TeamGameActivity[Player, Team]):
|
|||
collision = ba.getcollision()
|
||||
try:
|
||||
region = collision.sourcenode.getdelegate(RaceRegion, True)
|
||||
player = collision.opposingnode.getdelegate(PlayerSpaz,
|
||||
True).getplayer(
|
||||
Player, True)
|
||||
spaz = collision.opposingnode.getdelegate(PlayerSpaz, True)
|
||||
except ba.NotFoundError:
|
||||
return
|
||||
|
||||
if not spaz.is_alive():
|
||||
return
|
||||
|
||||
try:
|
||||
player = spaz.getplayer(Player, True)
|
||||
except ba.NotFoundError:
|
||||
return
|
||||
|
||||
|
|
@ -263,9 +269,9 @@ class RaceGame(ba.TeamGameActivity[Player, Team]):
|
|||
# Otherwise its the max.
|
||||
if isinstance(self.session, ba.DualTeamSession
|
||||
) and self._entire_team_must_finish:
|
||||
team.lap = min([p.lap for p in team.players])
|
||||
team.lap = min(p.lap for p in team.players)
|
||||
else:
|
||||
team.lap = max([p.lap for p in team.players])
|
||||
team.lap = max(p.lap for p in team.players)
|
||||
|
||||
# A player is finishing.
|
||||
if player.lap == self._laps:
|
||||
|
|
|
|||
1
dist/ba_data/python/bastd/game/runaround.py
vendored
1
dist/ba_data/python/bastd/game/runaround.py
vendored
|
|
@ -51,6 +51,7 @@ class Point(Enum):
|
|||
@dataclass
|
||||
class Spawn:
|
||||
"""Defines a bot spawn event."""
|
||||
# noinspection PyUnresolvedReferences
|
||||
type: type[SpazBot]
|
||||
path: int = 0
|
||||
point: Optional[Point] = None
|
||||
|
|
|
|||
8
dist/ba_data/python/bastd/mainmenu.py
vendored
8
dist/ba_data/python/bastd/mainmenu.py
vendored
|
|
@ -67,7 +67,7 @@ class MainMenuActivity(ba.Activity[ba.Player, ba.Team]):
|
|||
# host is navigating menus while they're just staring at an
|
||||
# empty-ish screen.
|
||||
tval = ba.Lstr(resource='hostIsNavigatingMenusText',
|
||||
subs=[('${HOST}', _ba.get_account_display_string())])
|
||||
subs=[('${HOST}', _ba.get_v1_account_display_string())])
|
||||
self._host_is_navigating_text = ba.NodeActor(
|
||||
ba.newnode('text',
|
||||
attrs={
|
||||
|
|
@ -274,7 +274,7 @@ class MainMenuActivity(ba.Activity[ba.Player, ba.Team]):
|
|||
|
||||
# We now want to wait until we're signed in before fetching news.
|
||||
def _try_fetching_news(self) -> None:
|
||||
if _ba.get_account_state() == 'signed_in':
|
||||
if _ba.get_v1_account_state() == 'signed_in':
|
||||
self._fetch_news()
|
||||
self._fetch_timer = None
|
||||
|
||||
|
|
@ -282,7 +282,7 @@ class MainMenuActivity(ba.Activity[ba.Player, ba.Team]):
|
|||
ba.app.main_menu_last_news_fetch_time = time.time()
|
||||
|
||||
# UPDATE - We now just pull news from MRVs.
|
||||
news = _ba.get_account_misc_read_val('n', None)
|
||||
news = _ba.get_v1_account_misc_read_val('n', None)
|
||||
if news is not None:
|
||||
self._got_news(news)
|
||||
|
||||
|
|
@ -757,7 +757,7 @@ class MainMenuActivity(ba.Activity[ba.Player, ba.Team]):
|
|||
})
|
||||
|
||||
def _get_custom_logo_tex_name(self) -> Optional[str]:
|
||||
if _ba.get_account_misc_read_val('easter', False):
|
||||
if _ba.get_v1_account_misc_read_val('easter', False):
|
||||
return 'logoEaster'
|
||||
return None
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ def show_sign_in_prompt(account_type: str = None) -> None:
|
|||
if account_type == 'Google Play':
|
||||
ConfirmWindow(
|
||||
ba.Lstr(resource='notSignedInGooglePlayErrorText'),
|
||||
lambda: _ba.sign_in('Google Play'),
|
||||
lambda: _ba.sign_in_v1('Google Play'),
|
||||
ok_text=ba.Lstr(resource='accountSettingsWindow.signInText'),
|
||||
width=460,
|
||||
height=130)
|
||||
|
|
|
|||
4
dist/ba_data/python/bastd/ui/account/link.py
vendored
4
dist/ba_data/python/bastd/ui/account/link.py
vendored
|
|
@ -50,7 +50,7 @@ class AccountLinkWindow(ba.Window):
|
|||
autoselect=True,
|
||||
icon=ba.gettexture('crossOut'),
|
||||
iconscale=1.2)
|
||||
maxlinks = _ba.get_account_misc_read_val('maxLinkAccounts', 5)
|
||||
maxlinks = _ba.get_v1_account_misc_read_val('maxLinkAccounts', 5)
|
||||
ba.textwidget(
|
||||
parent=self._root_widget,
|
||||
position=(self._width * 0.5, self._height * 0.56),
|
||||
|
|
@ -84,7 +84,7 @@ class AccountLinkWindow(ba.Window):
|
|||
|
||||
def _generate_press(self) -> None:
|
||||
from bastd.ui import account
|
||||
if _ba.get_account_state() != 'signed_in':
|
||||
if _ba.get_v1_account_state() != 'signed_in':
|
||||
account.show_sign_in_prompt()
|
||||
return
|
||||
ba.screenmessage(
|
||||
|
|
|
|||
97
dist/ba_data/python/bastd/ui/account/settings.py
vendored
97
dist/ba_data/python/bastd/ui/account/settings.py
vendored
|
|
@ -45,10 +45,11 @@ class AccountSettingsWindow(ba.Window):
|
|||
self._r = 'accountSettingsWindow'
|
||||
self._modal = modal
|
||||
self._needs_refresh = False
|
||||
self._signed_in = (_ba.get_account_state() == 'signed_in')
|
||||
self._account_state_num = _ba.get_account_state_num()
|
||||
self._show_linked = (self._signed_in and _ba.get_account_misc_read_val(
|
||||
'allowAccountLinking2', False))
|
||||
self._signed_in = (_ba.get_v1_account_state() == 'signed_in')
|
||||
self._account_state_num = _ba.get_v1_account_state_num()
|
||||
self._show_linked = (self._signed_in
|
||||
and _ba.get_v1_account_misc_read_val(
|
||||
'allowAccountLinking2', False))
|
||||
self._check_sign_in_timer = ba.Timer(1.0,
|
||||
ba.WeakCall(self._update),
|
||||
timetype=ba.TimeType.REAL,
|
||||
|
|
@ -57,7 +58,7 @@ class AccountSettingsWindow(ba.Window):
|
|||
# Currently we can only reset achievements on game-center.
|
||||
account_type: Optional[str]
|
||||
if self._signed_in:
|
||||
account_type = _ba.get_account_type()
|
||||
account_type = _ba.get_v1_account_type()
|
||||
else:
|
||||
account_type = None
|
||||
self._can_reset_achievements = (account_type == 'Game Center')
|
||||
|
|
@ -91,7 +92,7 @@ class AccountSettingsWindow(ba.Window):
|
|||
self._show_sign_in_buttons.append('Local')
|
||||
|
||||
# Ditto with shiny new V2 ones.
|
||||
if bool(False):
|
||||
if bool(True):
|
||||
self._show_sign_in_buttons.append('V2')
|
||||
|
||||
top_extra = 15 if uiscale is ba.UIScale.SMALL else 0
|
||||
|
|
@ -158,10 +159,10 @@ class AccountSettingsWindow(ba.Window):
|
|||
# Hmm should update this to use get_account_state_num.
|
||||
# Theoretically if we switch from one signed-in account to another
|
||||
# in the background this would break.
|
||||
account_state_num = _ba.get_account_state_num()
|
||||
account_state = _ba.get_account_state()
|
||||
account_state_num = _ba.get_v1_account_state_num()
|
||||
account_state = _ba.get_v1_account_state()
|
||||
|
||||
show_linked = (self._signed_in and _ba.get_account_misc_read_val(
|
||||
show_linked = (self._signed_in and _ba.get_v1_account_misc_read_val(
|
||||
'allowAccountLinking2', False))
|
||||
|
||||
if (account_state_num != self._account_state_num
|
||||
|
|
@ -190,8 +191,8 @@ class AccountSettingsWindow(ba.Window):
|
|||
# pylint: disable=cyclic-import
|
||||
from bastd.ui import confirm
|
||||
|
||||
account_state = _ba.get_account_state()
|
||||
account_type = (_ba.get_account_type()
|
||||
account_state = _ba.get_v1_account_state()
|
||||
account_type = (_ba.get_v1_account_type()
|
||||
if account_state == 'signed_in' else 'unknown')
|
||||
|
||||
is_google = account_type == 'Google Play'
|
||||
|
|
@ -225,7 +226,7 @@ class AccountSettingsWindow(ba.Window):
|
|||
game_service_button_space = 60.0
|
||||
|
||||
show_linked_accounts_text = (self._signed_in
|
||||
and _ba.get_account_misc_read_val(
|
||||
and _ba.get_v1_account_misc_read_val(
|
||||
'allowAccountLinking2', False))
|
||||
linked_accounts_text_space = 60.0
|
||||
|
||||
|
|
@ -254,17 +255,22 @@ class AccountSettingsWindow(ba.Window):
|
|||
player_profiles_button_space = 100.0
|
||||
|
||||
show_link_accounts_button = (self._signed_in
|
||||
and _ba.get_account_misc_read_val(
|
||||
and _ba.get_v1_account_misc_read_val(
|
||||
'allowAccountLinking2', False))
|
||||
link_accounts_button_space = 70.0
|
||||
|
||||
show_unlink_accounts_button = show_link_accounts_button
|
||||
unlink_accounts_button_space = 90.0
|
||||
|
||||
show_sign_out_button = (self._signed_in
|
||||
and account_type in ['Local', 'Google Play'])
|
||||
show_sign_out_button = (self._signed_in and account_type
|
||||
in ['Local', 'Google Play', 'V2'])
|
||||
sign_out_button_space = 70.0
|
||||
|
||||
show_cancel_v2_sign_in_button = (
|
||||
account_state == 'signing_in'
|
||||
and ba.app.accounts_v2.have_primary_credentials())
|
||||
cancel_v2_sign_in_button_space = 70.0
|
||||
|
||||
if self._subcontainer is not None:
|
||||
self._subcontainer.delete()
|
||||
self._sub_height = 60.0
|
||||
|
|
@ -308,6 +314,8 @@ class AccountSettingsWindow(ba.Window):
|
|||
self._sub_height += unlink_accounts_button_space
|
||||
if show_sign_out_button:
|
||||
self._sub_height += sign_out_button_space
|
||||
if show_cancel_v2_sign_in_button:
|
||||
self._sub_height += cancel_v2_sign_in_button_space
|
||||
self._subcontainer = ba.containerwidget(parent=self._scrollwidget,
|
||||
size=(self._sub_width,
|
||||
self._sub_height),
|
||||
|
|
@ -327,7 +335,7 @@ class AccountSettingsWindow(ba.Window):
|
|||
size=(0, 0),
|
||||
text=ba.Lstr(
|
||||
resource='accountSettingsWindow.deviceSpecificAccountText',
|
||||
subs=[('${NAME}', _ba.get_account_display_string())]),
|
||||
subs=[('${NAME}', _ba.get_v1_account_display_string())]),
|
||||
scale=0.7,
|
||||
color=(0.5, 0.5, 0.6),
|
||||
maxwidth=self._sub_width * 0.9,
|
||||
|
|
@ -581,7 +589,7 @@ class AccountSettingsWindow(ba.Window):
|
|||
if show_game_service_button:
|
||||
button_width = 300
|
||||
v -= game_service_button_space * 0.85
|
||||
account_type = _ba.get_account_type()
|
||||
account_type = _ba.get_v1_account_type()
|
||||
if account_type == 'Game Center':
|
||||
account_type_name = ba.Lstr(resource='gameCenterText')
|
||||
elif account_type == 'Game Circle':
|
||||
|
|
@ -849,6 +857,24 @@ class AccountSettingsWindow(ba.Window):
|
|||
right_widget=_ba.get_special_widget('party_button'))
|
||||
ba.widget(edit=btn, left_widget=bbtn, show_buffer_bottom=15)
|
||||
|
||||
if show_cancel_v2_sign_in_button:
|
||||
v -= cancel_v2_sign_in_button_space
|
||||
self._cancel_v2_sign_in_button = btn = ba.buttonwidget(
|
||||
parent=self._subcontainer,
|
||||
position=((self._sub_width - button_width) * 0.5, v),
|
||||
size=(button_width, 60),
|
||||
label=ba.Lstr(resource='cancelText'),
|
||||
color=(0.55, 0.5, 0.6),
|
||||
textcolor=(0.75, 0.7, 0.8),
|
||||
autoselect=True,
|
||||
on_activate_call=self._cancel_v2_sign_in_press)
|
||||
if first_selectable is None:
|
||||
first_selectable = btn
|
||||
if ba.app.ui.use_toolbars:
|
||||
ba.widget(edit=btn,
|
||||
right_widget=_ba.get_special_widget('party_button'))
|
||||
ba.widget(edit=btn, left_widget=bbtn, show_buffer_bottom=15)
|
||||
|
||||
# Whatever the topmost selectable thing is, we want it to scroll all
|
||||
# the way up when we select it.
|
||||
if first_selectable is not None:
|
||||
|
|
@ -863,8 +889,8 @@ class AccountSettingsWindow(ba.Window):
|
|||
def _on_achievements_press(self) -> None:
|
||||
# pylint: disable=cyclic-import
|
||||
from bastd.ui import achievements
|
||||
account_state = _ba.get_account_state()
|
||||
account_type = (_ba.get_account_type()
|
||||
account_state = _ba.get_v1_account_state()
|
||||
account_type = (_ba.get_v1_account_type()
|
||||
if account_state == 'signed_in' else 'unknown')
|
||||
# for google play we use the built-in UI; otherwise pop up our own
|
||||
if account_type == 'Google Play':
|
||||
|
|
@ -889,7 +915,7 @@ class AccountSettingsWindow(ba.Window):
|
|||
# let's not proceed..
|
||||
if _ba.get_public_login_id() is None:
|
||||
return False
|
||||
accounts = _ba.get_account_misc_read_val_2('linkedAccounts', [])
|
||||
accounts = _ba.get_v1_account_misc_read_val_2('linkedAccounts', [])
|
||||
return len(accounts) > 1
|
||||
|
||||
def _update_unlink_accounts_button(self) -> None:
|
||||
|
|
@ -911,8 +937,8 @@ class AccountSettingsWindow(ba.Window):
|
|||
num = int(time.time()) % 4
|
||||
accounts_str = num * '.' + (4 - num) * ' '
|
||||
else:
|
||||
accounts = _ba.get_account_misc_read_val_2('linkedAccounts', [])
|
||||
# our_account = _bs.get_account_display_string()
|
||||
accounts = _ba.get_v1_account_misc_read_val_2('linkedAccounts', [])
|
||||
# our_account = _bs.get_v1_account_display_string()
|
||||
# accounts = [a for a in accounts if a != our_account]
|
||||
# accounts_str = u', '.join(accounts) if accounts else
|
||||
# ba.Lstr(translate=('settingNames', 'None'))
|
||||
|
|
@ -951,7 +977,7 @@ class AccountSettingsWindow(ba.Window):
|
|||
if self._tickets_text is None:
|
||||
return
|
||||
try:
|
||||
tc_str = str(_ba.get_account_ticket_count())
|
||||
tc_str = str(_ba.get_v1_account_ticket_count())
|
||||
except Exception:
|
||||
ba.print_exception()
|
||||
tc_str = '-'
|
||||
|
|
@ -963,7 +989,7 @@ class AccountSettingsWindow(ba.Window):
|
|||
if self._account_name_text is None:
|
||||
return
|
||||
try:
|
||||
name_str = _ba.get_account_display_string()
|
||||
name_str = _ba.get_v1_account_display_string()
|
||||
except Exception:
|
||||
ba.print_exception()
|
||||
name_str = '??'
|
||||
|
|
@ -1005,22 +1031,37 @@ class AccountSettingsWindow(ba.Window):
|
|||
pbrowser.ProfileBrowserWindow(
|
||||
origin_widget=self._player_profiles_button)
|
||||
|
||||
def _cancel_v2_sign_in_press(self) -> None:
|
||||
# Just say we don't wanna be signed in anymore.
|
||||
ba.app.accounts_v2.set_primary_credentials(None)
|
||||
|
||||
# Speed UI updates along.
|
||||
ba.timer(0.1, ba.WeakCall(self._update), timetype=ba.TimeType.REAL)
|
||||
|
||||
def _sign_out_press(self) -> None:
|
||||
_ba.sign_out()
|
||||
|
||||
if ba.app.accounts_v2.have_primary_credentials():
|
||||
ba.app.accounts_v2.set_primary_credentials(None)
|
||||
else:
|
||||
_ba.sign_out_v1()
|
||||
|
||||
cfg = ba.app.config
|
||||
|
||||
# Take note that its our *explicit* intention to not be signed in at
|
||||
# this point.
|
||||
# Also take note that its our *explicit* intention to not be
|
||||
# signed in at this point (affects v1 accounts).
|
||||
cfg['Auto Account State'] = 'signed_out'
|
||||
cfg.commit()
|
||||
ba.buttonwidget(edit=self._sign_out_button,
|
||||
label=ba.Lstr(resource=self._r + '.signingOutText'))
|
||||
|
||||
# Speed UI updates along.
|
||||
ba.timer(0.1, ba.WeakCall(self._update), timetype=ba.TimeType.REAL)
|
||||
|
||||
def _sign_in_press(self,
|
||||
account_type: str,
|
||||
show_test_warning: bool = True) -> None:
|
||||
del show_test_warning # unused
|
||||
_ba.sign_in(account_type)
|
||||
_ba.sign_in_v1(account_type)
|
||||
|
||||
# Make note of the type account we're *wanting* to be signed in with.
|
||||
cfg = ba.app.config
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ class AccountUnlinkWindow(ba.Window):
|
|||
if our_login_id is None:
|
||||
entries = []
|
||||
else:
|
||||
account_infos = _ba.get_account_misc_read_val_2(
|
||||
account_infos = _ba.get_v1_account_misc_read_val_2(
|
||||
'linkedAccounts2', [])
|
||||
entries = [{
|
||||
'name': ai['d'],
|
||||
|
|
|
|||
159
dist/ba_data/python/bastd/ui/account/v2.py
vendored
159
dist/ba_data/python/bastd/ui/account/v2.py
vendored
|
|
@ -4,36 +4,90 @@
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import ba
|
||||
import _ba
|
||||
|
||||
from efro.error import CommunicationError
|
||||
import bacommon.cloud
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Any, Optional
|
||||
from typing import Union, Optional
|
||||
|
||||
STATUS_CHECK_INTERVAL_SECONDS = 2.0
|
||||
|
||||
|
||||
class V2SignInWindow(ba.Window):
|
||||
"""A window allowing signing in to a v2 account."""
|
||||
|
||||
def __init__(self, origin_widget: ba.Widget):
|
||||
from ba.internal import is_browser_likely_available
|
||||
logincode = '1412345'
|
||||
address = (
|
||||
f'{_ba.get_master_server_address(version=2)}?login={logincode}')
|
||||
self._width = 600
|
||||
self._height = 500
|
||||
self._height = 550
|
||||
self._proxyid: Optional[str] = None
|
||||
self._proxykey: Optional[str] = None
|
||||
|
||||
uiscale = ba.app.ui.uiscale
|
||||
super().__init__(root_widget=ba.containerwidget(
|
||||
size=(self._width, self._height),
|
||||
transition='in_scale',
|
||||
scale_origin_stack_offset=origin_widget.get_screen_space_center(),
|
||||
scale=(1.25 if uiscale is ba.UIScale.SMALL else
|
||||
1.0 if uiscale is ba.UIScale.MEDIUM else 0.85)))
|
||||
1.05 if uiscale is ba.UIScale.MEDIUM else 0.9)))
|
||||
|
||||
self._loading_text = ba.textwidget(
|
||||
parent=self._root_widget,
|
||||
position=(self._width * 0.5, self._height * 0.5),
|
||||
h_align='center',
|
||||
v_align='center',
|
||||
size=(0, 0),
|
||||
maxwidth=0.9 * self._width,
|
||||
text=ba.Lstr(value='${A}...',
|
||||
subs=[('${A}', ba.Lstr(resource='loadingText'))]),
|
||||
)
|
||||
|
||||
self._cancel_button = ba.buttonwidget(
|
||||
parent=self._root_widget,
|
||||
position=(30, self._height - 65),
|
||||
size=(130, 50),
|
||||
scale=0.8,
|
||||
label=ba.Lstr(resource='cancelText'),
|
||||
on_activate_call=self._done,
|
||||
autoselect=True,
|
||||
textcolor=(0.75, 0.7, 0.8),
|
||||
)
|
||||
ba.containerwidget(edit=self._root_widget,
|
||||
cancel_button=self._cancel_button)
|
||||
|
||||
self._update_timer: Optional[ba.Timer] = None
|
||||
|
||||
# Ask the cloud for a proxy login id.
|
||||
ba.app.cloud.send_message(bacommon.cloud.LoginProxyRequestMessage(),
|
||||
on_response=ba.WeakCall(
|
||||
self._on_proxy_request_response))
|
||||
|
||||
def _on_proxy_request_response(
|
||||
self, response: Union[bacommon.cloud.LoginProxyRequestResponse,
|
||||
Exception]
|
||||
) -> None:
|
||||
from ba.internal import is_browser_likely_available
|
||||
|
||||
# Something went wrong. Show an error message and that's it.
|
||||
if isinstance(response, Exception):
|
||||
ba.textwidget(
|
||||
edit=self._loading_text,
|
||||
text=ba.Lstr(resource='internal.unavailableNoConnectionText'),
|
||||
color=(1, 0, 0))
|
||||
return
|
||||
|
||||
# Show link(s) the user can use to log in.
|
||||
address = _ba.get_master_server_address(version=2) + response.url
|
||||
address_pretty = address.removeprefix('https://')
|
||||
|
||||
ba.textwidget(
|
||||
parent=self._root_widget,
|
||||
position=(self._width * 0.5, self._height - 85),
|
||||
position=(self._width * 0.5, self._height - 95),
|
||||
size=(0, 0),
|
||||
text=ba.Lstr(
|
||||
resource='accountSettingsWindow.v2LinkInstructionsText'),
|
||||
|
|
@ -45,19 +99,19 @@ class V2SignInWindow(ba.Window):
|
|||
if is_browser_likely_available():
|
||||
ba.buttonwidget(parent=self._root_widget,
|
||||
position=((self._width * 0.5 - button_width * 0.5),
|
||||
self._height - 175),
|
||||
self._height - 185),
|
||||
autoselect=True,
|
||||
size=(button_width, 60),
|
||||
label=ba.Lstr(value=address),
|
||||
label=ba.Lstr(value=address_pretty),
|
||||
color=(0.55, 0.5, 0.6),
|
||||
textcolor=(0.75, 0.7, 0.8),
|
||||
on_activate_call=lambda: ba.open_url(address))
|
||||
qroffs = 0.0
|
||||
else:
|
||||
ba.textwidget(parent=self._root_widget,
|
||||
position=(self._width * 0.5, self._height - 135),
|
||||
position=(self._width * 0.5, self._height - 145),
|
||||
size=(0, 0),
|
||||
text=ba.Lstr(value=address),
|
||||
text=ba.Lstr(value=address_pretty),
|
||||
flatness=1.0,
|
||||
maxwidth=self._width,
|
||||
scale=0.75,
|
||||
|
|
@ -65,28 +119,75 @@ class V2SignInWindow(ba.Window):
|
|||
v_align='center')
|
||||
qroffs = 20.0
|
||||
|
||||
self._cancel_button = ba.buttonwidget(
|
||||
parent=self._root_widget,
|
||||
position=(30, self._height - 55),
|
||||
size=(130, 50),
|
||||
scale=0.8,
|
||||
label=ba.Lstr(resource='cancelText'),
|
||||
# color=(0.6, 0.5, 0.6),
|
||||
on_activate_call=self._done,
|
||||
autoselect=True,
|
||||
textcolor=(0.75, 0.7, 0.8),
|
||||
# icon=ba.gettexture('crossOut'),
|
||||
# iconscale=1.2
|
||||
)
|
||||
ba.containerwidget(edit=self._root_widget,
|
||||
cancel_button=self._cancel_button)
|
||||
|
||||
qr_size = 270
|
||||
ba.imagewidget(parent=self._root_widget,
|
||||
position=(self._width * 0.5 - qr_size * 0.5,
|
||||
self._height * 0.34 + qroffs - qr_size * 0.5),
|
||||
self._height * 0.36 + qroffs - qr_size * 0.5),
|
||||
size=(qr_size, qr_size),
|
||||
texture=_ba.get_qrcode_texture(address))
|
||||
|
||||
# Start querying for results.
|
||||
self._proxyid = response.proxyid
|
||||
self._proxykey = response.proxykey
|
||||
ba.timer(STATUS_CHECK_INTERVAL_SECONDS,
|
||||
ba.WeakCall(self._ask_for_status))
|
||||
|
||||
def _ask_for_status(self) -> None:
|
||||
assert self._proxyid is not None
|
||||
assert self._proxykey is not None
|
||||
ba.app.cloud.send_message(bacommon.cloud.LoginProxyStateQueryMessage(
|
||||
proxyid=self._proxyid, proxykey=self._proxykey),
|
||||
on_response=ba.WeakCall(self._got_status))
|
||||
|
||||
def _got_status(
|
||||
self, response: Union[bacommon.cloud.LoginProxyStateQueryResponse,
|
||||
Exception]
|
||||
) -> None:
|
||||
|
||||
# For now, if anything goes wrong on the server-side, just abort
|
||||
# with a vague error message. Can be more verbose later if need be.
|
||||
if (isinstance(response, bacommon.cloud.LoginProxyStateQueryResponse)
|
||||
and response.state is response.State.FAIL):
|
||||
ba.playsound(ba.getsound('error'))
|
||||
ba.screenmessage(ba.Lstr(resource='errorText'), color=(1, 0, 0))
|
||||
self._done()
|
||||
return
|
||||
|
||||
# If we got a token, set ourself as signed in. Hooray!
|
||||
if (isinstance(response, bacommon.cloud.LoginProxyStateQueryResponse)
|
||||
and response.state is response.State.SUCCESS):
|
||||
assert response.credentials is not None
|
||||
ba.app.accounts_v2.set_primary_credentials(response.credentials)
|
||||
|
||||
# As a courtesy, tell the server we're done with this proxy
|
||||
# so it can clean up (not a huge deal if this fails)
|
||||
assert self._proxyid is not None
|
||||
try:
|
||||
ba.app.cloud.send_message(
|
||||
bacommon.cloud.LoginProxyCompleteMessage(
|
||||
proxyid=self._proxyid),
|
||||
on_response=ba.WeakCall(self._proxy_complete_response))
|
||||
except CommunicationError:
|
||||
pass
|
||||
except Exception:
|
||||
logging.warning(
|
||||
'Unexpected error sending login-proxy-complete message',
|
||||
exc_info=True)
|
||||
|
||||
self._done()
|
||||
return
|
||||
|
||||
# If we're still waiting, ask again soon.
|
||||
if (isinstance(response, Exception)
|
||||
or response.state is response.State.WAITING):
|
||||
ba.timer(STATUS_CHECK_INTERVAL_SECONDS,
|
||||
ba.WeakCall(self._ask_for_status))
|
||||
|
||||
def _proxy_complete_response(self, response: Union[None,
|
||||
Exception]) -> None:
|
||||
del response # Not used.
|
||||
# We could do something smart like retry on exceptions here, but
|
||||
# this isn't critical so we'll just let anything slide.
|
||||
|
||||
def _done(self) -> None:
|
||||
ba.containerwidget(edit=self._root_widget, transition='out_scale')
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ class AccountViewerWindow(popup.PopupWindow):
|
|||
|
||||
# In cases where the user most likely has a browser/email, lets
|
||||
# offer a 'report this user' button.
|
||||
if (is_browser_likely_available() and _ba.get_account_misc_read_val(
|
||||
if (is_browser_likely_available() and _ba.get_v1_account_misc_read_val(
|
||||
'showAccountExtrasMenu', False)):
|
||||
|
||||
self._extras_menu_button = ba.buttonwidget(
|
||||
|
|
|
|||
36
dist/ba_data/python/bastd/ui/appinvite.py
vendored
36
dist/ba_data/python/bastd/ui/appinvite.py
vendored
|
|
@ -60,15 +60,14 @@ class AppInviteWindow(ba.Window):
|
|||
resource='gatherWindow.earnTicketsForRecommendingAmountText',
|
||||
fallback_resource=(
|
||||
'gatherWindow.earnTicketsForRecommendingText'),
|
||||
subs=[
|
||||
('${COUNT}',
|
||||
str(_ba.get_account_misc_read_val('friendTryTickets',
|
||||
300))),
|
||||
('${YOU_COUNT}',
|
||||
str(
|
||||
_ba.get_account_misc_read_val('friendTryAwardTickets',
|
||||
100)))
|
||||
]))
|
||||
subs=[('${COUNT}',
|
||||
str(
|
||||
_ba.get_v1_account_misc_read_val(
|
||||
'friendTryTickets', 300))),
|
||||
('${YOU_COUNT}',
|
||||
str(
|
||||
_ba.get_v1_account_misc_read_val(
|
||||
'friendTryAwardTickets', 100)))]))
|
||||
|
||||
or_text = ba.Lstr(resource='orText',
|
||||
subs=[('${A}', ''),
|
||||
|
|
@ -129,17 +128,18 @@ class AppInviteWindow(ba.Window):
|
|||
ba.playsound(ba.getsound('error'))
|
||||
return
|
||||
|
||||
if _ba.get_account_state() == 'signed_in':
|
||||
if _ba.get_v1_account_state() == 'signed_in':
|
||||
ba.set_analytics_screen('App Invite UI')
|
||||
_ba.show_app_invite(
|
||||
ba.Lstr(resource='gatherWindow.appInviteTitleText',
|
||||
subs=[('${APP_NAME}', ba.Lstr(resource='titleText'))
|
||||
]).evaluate(),
|
||||
ba.Lstr(resource='gatherWindow.appInviteMessageText',
|
||||
subs=[('${COUNT}', str(self._data['tickets'])),
|
||||
('${NAME}', _ba.get_account_name().split()[0]),
|
||||
('${APP_NAME}', ba.Lstr(resource='titleText'))
|
||||
]).evaluate(), self._data['code'])
|
||||
subs=[
|
||||
('${COUNT}', str(self._data['tickets'])),
|
||||
('${NAME}', _ba.get_v1_account_name().split()[0]),
|
||||
('${APP_NAME}', ba.Lstr(resource='titleText'))
|
||||
]).evaluate(), self._data['code'])
|
||||
else:
|
||||
ba.playsound(ba.getsound('error'))
|
||||
|
||||
|
|
@ -256,7 +256,7 @@ class ShowFriendCodeWindow(ba.Window):
|
|||
]).evaluate(),
|
||||
ba.Lstr(resource='gatherWindow.appInviteMessageText',
|
||||
subs=[('${COUNT}', str(self._data['tickets'])),
|
||||
('${NAME}', _ba.get_account_name().split()[0]),
|
||||
('${NAME}', _ba.get_v1_account_name().split()[0]),
|
||||
('${APP_NAME}', ba.Lstr(resource='titleText'))
|
||||
]).evaluate(), self._data['code'])
|
||||
|
||||
|
|
@ -264,7 +264,7 @@ class ShowFriendCodeWindow(ba.Window):
|
|||
import urllib.parse
|
||||
|
||||
# If somehow we got signed out.
|
||||
if _ba.get_account_state() != 'signed_in':
|
||||
if _ba.get_v1_account_state() != 'signed_in':
|
||||
ba.screenmessage(ba.Lstr(resource='notSignedInText'),
|
||||
color=(1, 0, 0))
|
||||
ba.playsound(ba.getsound('error'))
|
||||
|
|
@ -273,7 +273,7 @@ class ShowFriendCodeWindow(ba.Window):
|
|||
ba.set_analytics_screen('Email Friend Code')
|
||||
subject = (ba.Lstr(resource='gatherWindow.friendHasSentPromoCodeText').
|
||||
evaluate().replace(
|
||||
'${NAME}', _ba.get_account_name()).replace(
|
||||
'${NAME}', _ba.get_v1_account_name()).replace(
|
||||
'${APP_NAME}',
|
||||
ba.Lstr(resource='titleText').evaluate()).replace(
|
||||
'${COUNT}', str(self._data['tickets'])))
|
||||
|
|
@ -304,7 +304,7 @@ def handle_app_invites_press(force_code: bool = False) -> None:
|
|||
"""(internal)"""
|
||||
app = ba.app
|
||||
do_app_invites = (app.platform == 'android' and app.subplatform == 'google'
|
||||
and _ba.get_account_misc_read_val(
|
||||
and _ba.get_v1_account_misc_read_val(
|
||||
'enableAppInvites', False) and not app.on_tv)
|
||||
if force_code:
|
||||
do_app_invites = False
|
||||
|
|
|
|||
|
|
@ -156,7 +156,7 @@ class CharacterPicker(popup.PopupWindow):
|
|||
def _on_store_press(self) -> None:
|
||||
from bastd.ui.account import show_sign_in_prompt
|
||||
from bastd.ui.store.browser import StoreBrowserWindow
|
||||
if _ba.get_account_state() != 'signed_in':
|
||||
if _ba.get_v1_account_state() != 'signed_in':
|
||||
show_sign_in_prompt()
|
||||
return
|
||||
self._transition_out()
|
||||
|
|
|
|||
5
dist/ba_data/python/bastd/ui/colorpicker.py
vendored
5
dist/ba_data/python/bastd/ui/colorpicker.py
vendored
|
|
@ -94,7 +94,7 @@ class ColorPicker(PopupWindow):
|
|||
on_activate_call=ba.WeakCall(self._select_other))
|
||||
|
||||
# Custom colors are limited to pro currently.
|
||||
if not ba.app.accounts.have_pro():
|
||||
if not ba.app.accounts_v1.have_pro():
|
||||
ba.imagewidget(parent=self.root_widget,
|
||||
position=(50, 12),
|
||||
size=(30, 30),
|
||||
|
|
@ -118,7 +118,7 @@ class ColorPicker(PopupWindow):
|
|||
from bastd.ui import purchase
|
||||
|
||||
# Requires pro.
|
||||
if not ba.app.accounts.have_pro():
|
||||
if not ba.app.accounts_v1.have_pro():
|
||||
purchase.PurchaseWindow(items=['pro'])
|
||||
self._transition_out()
|
||||
return
|
||||
|
|
@ -249,6 +249,7 @@ class ColorPickerExact(PopupWindow):
|
|||
# color to the delegate, so start doing that.
|
||||
self._update_for_color()
|
||||
|
||||
# noinspection PyUnresolvedReferences
|
||||
def _update_for_color(self) -> None:
|
||||
if not self.root_widget:
|
||||
return
|
||||
|
|
|
|||
34
dist/ba_data/python/bastd/ui/config.py
vendored
34
dist/ba_data/python/bastd/ui/config.py
vendored
|
|
@ -17,13 +17,11 @@ class ConfigCheckBox:
|
|||
|
||||
It will automatically save and apply the config when its
|
||||
value changes.
|
||||
|
||||
Attributes:
|
||||
|
||||
widget
|
||||
The underlying ba.Widget instance.
|
||||
"""
|
||||
|
||||
widget: ba.Widget
|
||||
"""The underlying ba.Widget instance."""
|
||||
|
||||
def __init__(self,
|
||||
parent: ba.Widget,
|
||||
configkey: str,
|
||||
|
|
@ -65,22 +63,20 @@ class ConfigNumberEdit:
|
|||
|
||||
It will automatically save and apply the config when its
|
||||
value changes.
|
||||
|
||||
Attributes:
|
||||
|
||||
nametext
|
||||
The text widget displaying the name.
|
||||
|
||||
valuetext
|
||||
The text widget displaying the current value.
|
||||
|
||||
minusbutton
|
||||
The button widget used to reduce the value.
|
||||
|
||||
plusbutton
|
||||
The button widget used to increase the value.
|
||||
"""
|
||||
|
||||
nametext: ba.Widget
|
||||
"""The text widget displaying the name."""
|
||||
|
||||
valuetext: ba.Widget
|
||||
"""The text widget displaying the current value."""
|
||||
|
||||
minusbutton: ba.Widget
|
||||
"""The button widget used to reduce the value."""
|
||||
|
||||
plusbutton: ba.Widget
|
||||
"""The button widget used to increase the value."""
|
||||
|
||||
def __init__(self,
|
||||
parent: ba.Widget,
|
||||
configkey: str,
|
||||
|
|
|
|||
8
dist/ba_data/python/bastd/ui/continues.py
vendored
8
dist/ba_data/python/bastd/ui/continues.py
vendored
|
|
@ -142,9 +142,9 @@ class ContinuesWindow(ba.Window):
|
|||
self._on_cancel()
|
||||
return
|
||||
|
||||
if _ba.get_account_state() == 'signed_in':
|
||||
if _ba.get_v1_account_state() == 'signed_in':
|
||||
sval = (ba.charstr(ba.SpecialChar.TICKET) +
|
||||
str(_ba.get_account_ticket_count()))
|
||||
str(_ba.get_v1_account_ticket_count()))
|
||||
else:
|
||||
sval = '?'
|
||||
if self._tickets_text is not None:
|
||||
|
|
@ -176,14 +176,14 @@ class ContinuesWindow(ba.Window):
|
|||
ba.playsound(ba.getsound('error'))
|
||||
else:
|
||||
# If somehow we got signed out...
|
||||
if _ba.get_account_state() != 'signed_in':
|
||||
if _ba.get_v1_account_state() != 'signed_in':
|
||||
ba.screenmessage(ba.Lstr(resource='notSignedInText'),
|
||||
color=(1, 0, 0))
|
||||
ba.playsound(ba.getsound('error'))
|
||||
return
|
||||
|
||||
# If it appears we don't have enough tickets, offer to buy more.
|
||||
tickets = _ba.get_account_ticket_count()
|
||||
tickets = _ba.get_v1_account_ticket_count()
|
||||
if tickets < self._cost:
|
||||
# FIXME: Should we start the timer back up again after?
|
||||
self._counting_down = False
|
||||
|
|
|
|||
64
dist/ba_data/python/bastd/ui/coop/browser.py
vendored
64
dist/ba_data/python/bastd/ui/coop/browser.py
vendored
|
|
@ -92,7 +92,7 @@ class CoopBrowserWindow(ba.Window):
|
|||
|
||||
self._tourney_data_up_to_date = False
|
||||
|
||||
self._campaign_difficulty = _ba.get_account_misc_val(
|
||||
self._campaign_difficulty = _ba.get_v1_account_misc_val(
|
||||
'campaignDifficulty', 'easy')
|
||||
|
||||
super().__init__(root_widget=ba.containerwidget(
|
||||
|
|
@ -235,7 +235,7 @@ class CoopBrowserWindow(ba.Window):
|
|||
self._subcontainer: Optional[ba.Widget] = None
|
||||
|
||||
# Take note of our account state; we'll refresh later if this changes.
|
||||
self._account_state_num = _ba.get_account_state_num()
|
||||
self._account_state_num = _ba.get_v1_account_state_num()
|
||||
|
||||
# Same for fg/bg state.
|
||||
self._fg_state = app.fg_state
|
||||
|
|
@ -251,14 +251,14 @@ class CoopBrowserWindow(ba.Window):
|
|||
# If we've got a cached tournament list for our account and info for
|
||||
# each one of those tournaments, go ahead and display it as a
|
||||
# starting point.
|
||||
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.accounts.tournament_info
|
||||
for t_id in app.accounts.account_tournament_list[1])):
|
||||
if (app.accounts_v1.account_tournament_list is not None
|
||||
and app.accounts_v1.account_tournament_list[0]
|
||||
== _ba.get_v1_account_state_num() and all(
|
||||
t_id in app.accounts_v1.tournament_info
|
||||
for t_id in app.accounts_v1.account_tournament_list[1])):
|
||||
tourney_data = [
|
||||
app.accounts.tournament_info[t_id]
|
||||
for t_id in app.accounts.account_tournament_list[1]
|
||||
app.accounts_v1.tournament_info[t_id]
|
||||
for t_id in app.accounts_v1.account_tournament_list[1]
|
||||
]
|
||||
self._update_for_data(tourney_data)
|
||||
|
||||
|
|
@ -269,6 +269,7 @@ class CoopBrowserWindow(ba.Window):
|
|||
repeat=True)
|
||||
self._update()
|
||||
|
||||
# noinspection PyUnresolvedReferences
|
||||
@staticmethod
|
||||
def _preload_modules() -> None:
|
||||
"""Preload modules we use (called in bg thread)."""
|
||||
|
|
@ -299,7 +300,7 @@ class CoopBrowserWindow(ba.Window):
|
|||
self._tourney_data_up_to_date = False
|
||||
|
||||
# If our account state has changed, do a full request.
|
||||
account_state_num = _ba.get_account_state_num()
|
||||
account_state_num = _ba.get_v1_account_state_num()
|
||||
if account_state_num != self._account_state_num:
|
||||
self._account_state_num = account_state_num
|
||||
self._save_state()
|
||||
|
|
@ -357,7 +358,7 @@ class CoopBrowserWindow(ba.Window):
|
|||
try:
|
||||
ba.imagewidget(
|
||||
edit=self._hard_button_lock_image,
|
||||
opacity=0.0 if ba.app.accounts.have_pro_options() else 1.0)
|
||||
opacity=0.0 if ba.app.accounts_v1.have_pro_options() else 1.0)
|
||||
except Exception:
|
||||
ba.print_exception('Error updating campaign lock.')
|
||||
|
||||
|
|
@ -479,7 +480,7 @@ class CoopBrowserWindow(ba.Window):
|
|||
tbtn['required_league'] = (None if 'requiredLeague' not in entry
|
||||
else entry['requiredLeague'])
|
||||
|
||||
game = ba.app.accounts.tournament_info[
|
||||
game = ba.app.accounts_v1.tournament_info[
|
||||
tbtn['tournament_id']]['game']
|
||||
|
||||
if game is None:
|
||||
|
|
@ -490,7 +491,7 @@ class CoopBrowserWindow(ba.Window):
|
|||
else:
|
||||
campaignname, levelname = game.split(':')
|
||||
campaign = getcampaign(campaignname)
|
||||
max_players = ba.app.accounts.tournament_info[
|
||||
max_players = ba.app.accounts_v1.tournament_info[
|
||||
tbtn['tournament_id']]['maxPlayers']
|
||||
txt = ba.Lstr(
|
||||
value='${A} ${B}',
|
||||
|
|
@ -524,7 +525,7 @@ class CoopBrowserWindow(ba.Window):
|
|||
tbtn['allow_ads'] = allow_ads = entry['allowAds']
|
||||
|
||||
final_fee: Optional[int] = (None if fee_var is None else
|
||||
_ba.get_account_misc_read_val(
|
||||
_ba.get_v1_account_misc_read_val(
|
||||
fee_var, '?'))
|
||||
|
||||
final_fee_str: Union[str, ba.Lstr]
|
||||
|
|
@ -539,9 +540,9 @@ class CoopBrowserWindow(ba.Window):
|
|||
ba.charstr(ba.SpecialChar.TICKET_BACKING) +
|
||||
str(final_fee))
|
||||
|
||||
ad_tries_remaining = ba.app.accounts.tournament_info[
|
||||
ad_tries_remaining = ba.app.accounts_v1.tournament_info[
|
||||
tbtn['tournament_id']]['adTriesRemaining']
|
||||
free_tries_remaining = ba.app.accounts.tournament_info[
|
||||
free_tries_remaining = ba.app.accounts_v1.tournament_info[
|
||||
tbtn['tournament_id']]['freeTriesRemaining']
|
||||
|
||||
# Now, if this fee allows ads and we support video ads, show
|
||||
|
|
@ -591,7 +592,7 @@ class CoopBrowserWindow(ba.Window):
|
|||
|
||||
def _on_tournament_query_response(self, data: Optional[dict[str,
|
||||
Any]]) -> None:
|
||||
accounts = ba.app.accounts
|
||||
accounts = ba.app.accounts_v1
|
||||
if data is not None:
|
||||
tournament_data = data['t'] # This used to be the whole payload.
|
||||
self._last_tournament_query_response_time = ba.time(
|
||||
|
|
@ -605,9 +606,11 @@ class CoopBrowserWindow(ba.Window):
|
|||
accounts.cache_tournament_info(tournament_data)
|
||||
|
||||
# Also cache the current tourney list/order for this account.
|
||||
accounts.account_tournament_list = (_ba.get_account_state_num(), [
|
||||
e['tournamentID'] for e in tournament_data
|
||||
])
|
||||
accounts.account_tournament_list = (_ba.get_v1_account_state_num(),
|
||||
[
|
||||
e['tournamentID']
|
||||
for e in tournament_data
|
||||
])
|
||||
|
||||
self._doing_tournament_query = False
|
||||
self._update_for_data(tournament_data)
|
||||
|
|
@ -616,7 +619,8 @@ class CoopBrowserWindow(ba.Window):
|
|||
# pylint: disable=cyclic-import
|
||||
from bastd.ui.purchase import PurchaseWindow
|
||||
if difficulty != self._campaign_difficulty:
|
||||
if difficulty == 'hard' and not ba.app.accounts.have_pro_options():
|
||||
if (difficulty == 'hard'
|
||||
and not ba.app.accounts_v1.have_pro_options()):
|
||||
PurchaseWindow(items=['pro'])
|
||||
return
|
||||
ba.playsound(ba.getsound('gunCocking'))
|
||||
|
|
@ -871,7 +875,7 @@ class CoopBrowserWindow(ba.Window):
|
|||
# no tournaments).
|
||||
if self._tournament_button_count == 0:
|
||||
unavailable_text = ba.Lstr(resource='unavailableText')
|
||||
if _ba.get_account_state() != 'signed_in':
|
||||
if _ba.get_v1_account_state() != 'signed_in':
|
||||
unavailable_text = ba.Lstr(
|
||||
value='${A} (${B})',
|
||||
subs=[('${A}', unavailable_text),
|
||||
|
|
@ -942,7 +946,7 @@ class CoopBrowserWindow(ba.Window):
|
|||
]
|
||||
|
||||
# Show easter-egg-hunt either if its easter or we own it.
|
||||
if _ba.get_account_misc_read_val(
|
||||
if _ba.get_v1_account_misc_read_val(
|
||||
'easter', False) or _ba.get_purchased('games.easter_egg_hunt'):
|
||||
items = [
|
||||
'Challenges:Easter Egg Hunt', 'Challenges:Pro Easter Egg Hunt'
|
||||
|
|
@ -1345,7 +1349,7 @@ class CoopBrowserWindow(ba.Window):
|
|||
# pylint: disable=cyclic-import
|
||||
from bastd.ui.account import show_sign_in_prompt
|
||||
from bastd.ui.league.rankwindow import LeagueRankWindow
|
||||
if _ba.get_account_state() != 'signed_in':
|
||||
if _ba.get_v1_account_state() != 'signed_in':
|
||||
show_sign_in_prompt()
|
||||
return
|
||||
self._save_state()
|
||||
|
|
@ -1362,7 +1366,7 @@ class CoopBrowserWindow(ba.Window):
|
|||
) -> None:
|
||||
# pylint: disable=cyclic-import
|
||||
from bastd.ui.account import show_sign_in_prompt
|
||||
if _ba.get_account_state() != 'signed_in':
|
||||
if _ba.get_v1_account_state() != 'signed_in':
|
||||
show_sign_in_prompt()
|
||||
return
|
||||
self._save_state()
|
||||
|
|
@ -1426,7 +1430,7 @@ class CoopBrowserWindow(ba.Window):
|
|||
# Do a bit of pre-flight for tournament options.
|
||||
if tournament_button is not None:
|
||||
|
||||
if _ba.get_account_state() != 'signed_in':
|
||||
if _ba.get_v1_account_state() != 'signed_in':
|
||||
show_sign_in_prompt()
|
||||
return
|
||||
|
||||
|
|
@ -1464,7 +1468,7 @@ class CoopBrowserWindow(ba.Window):
|
|||
return
|
||||
|
||||
# Game is whatever the tournament tells us it is.
|
||||
game = ba.app.accounts.tournament_info[
|
||||
game = ba.app.accounts_v1.tournament_info[
|
||||
tournament_button['tournament_id']]['game']
|
||||
|
||||
if tournament_button is None and game == 'Easy:The Last Stand':
|
||||
|
|
@ -1480,8 +1484,8 @@ class CoopBrowserWindow(ba.Window):
|
|||
if tournament_button is None and game in (
|
||||
'Challenges:Infinite Runaround',
|
||||
'Challenges:Infinite Onslaught'
|
||||
) and not ba.app.accounts.have_pro():
|
||||
if _ba.get_account_state() != 'signed_in':
|
||||
) and not ba.app.accounts_v1.have_pro():
|
||||
if _ba.get_v1_account_state() != 'signed_in':
|
||||
show_sign_in_prompt()
|
||||
else:
|
||||
PurchaseWindow(items=['pro'])
|
||||
|
|
@ -1507,7 +1511,7 @@ class CoopBrowserWindow(ba.Window):
|
|||
|
||||
if (tournament_button is None and required_purchase is not None
|
||||
and not _ba.get_purchased(required_purchase)):
|
||||
if _ba.get_account_state() != 'signed_in':
|
||||
if _ba.get_v1_account_state() != 'signed_in':
|
||||
show_sign_in_prompt()
|
||||
else:
|
||||
PurchaseWindow(items=[required_purchase])
|
||||
|
|
|
|||
|
|
@ -199,7 +199,7 @@ class GameButton:
|
|||
# Hard-code games we haven't unlocked.
|
||||
if ((game in ('Challenges:Infinite Runaround',
|
||||
'Challenges:Infinite Onslaught')
|
||||
and not ba.app.accounts.have_pro())
|
||||
and not ba.app.accounts_v1.have_pro())
|
||||
or (game in ('Challenges:Meteor Shower', )
|
||||
and not _ba.get_purchased('games.meteor_shower'))
|
||||
or (game in ('Challenges:Target Practice',
|
||||
|
|
|
|||
|
|
@ -151,7 +151,7 @@ class GatherWindow(ba.Window):
|
|||
tabdefs: list[tuple[GatherWindow.TabID, ba.Lstr]] = [
|
||||
(self.TabID.ABOUT, ba.Lstr(resource=self._r + '.aboutText'))
|
||||
]
|
||||
if _ba.get_account_misc_read_val('enablePublicParties', True):
|
||||
if _ba.get_v1_account_misc_read_val('enablePublicParties', True):
|
||||
tabdefs.append((self.TabID.INTERNET,
|
||||
ba.Lstr(resource=self._r + '.publicText')))
|
||||
tabdefs.append(
|
||||
|
|
|
|||
|
|
@ -52,7 +52,8 @@ class AboutGatherTab(GatherTab):
|
|||
include_invite = True
|
||||
msc_scale = 1.1
|
||||
c_height_2 = min(region_height, string_height * msc_scale + 100)
|
||||
try_tickets = _ba.get_account_misc_read_val('friendTryTickets', None)
|
||||
try_tickets = _ba.get_v1_account_misc_read_val('friendTryTickets',
|
||||
None)
|
||||
if try_tickets is None:
|
||||
include_invite = False
|
||||
self._container = ba.containerwidget(
|
||||
|
|
@ -106,7 +107,7 @@ class AboutGatherTab(GatherTab):
|
|||
def _invite_to_try_press(self) -> None:
|
||||
from bastd.ui.account import show_sign_in_prompt
|
||||
from bastd.ui.appinvite import handle_app_invites_press
|
||||
if _ba.get_account_state() != 'signed_in':
|
||||
if _ba.get_v1_account_state() != 'signed_in':
|
||||
show_sign_in_prompt()
|
||||
return
|
||||
handle_app_invites_press()
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ import ba
|
|||
if TYPE_CHECKING:
|
||||
from typing import Any, Optional, Union, Callable
|
||||
from bastd.ui.gather import GatherWindow
|
||||
from bastd.ui.confirm import ConfirmWindow
|
||||
|
||||
|
||||
def _safe_set_text(txt: Optional[ba.Widget],
|
||||
|
|
|
|||
|
|
@ -166,7 +166,7 @@ class PrivateGatherTab(GatherTab):
|
|||
elif hcfg.session_type == 'teams':
|
||||
sessiontype = ba.DualTeamSession
|
||||
else:
|
||||
raise RuntimeError('fInvalid sessiontype: {hcfg.session_type}')
|
||||
raise RuntimeError(f'Invalid sessiontype: {hcfg.session_type}')
|
||||
pvars = PlaylistTypeVars(sessiontype)
|
||||
|
||||
playlist_name = ba.app.config.get(
|
||||
|
|
@ -225,7 +225,7 @@ class PrivateGatherTab(GatherTab):
|
|||
def _update_currency_ui(self) -> None:
|
||||
# Keep currency count up to date if applicable.
|
||||
try:
|
||||
t_str = str(_ba.get_account_ticket_count())
|
||||
t_str = str(_ba.get_v1_account_ticket_count())
|
||||
except Exception:
|
||||
t_str = '?'
|
||||
if self._get_tickets_button:
|
||||
|
|
@ -245,7 +245,7 @@ class PrivateGatherTab(GatherTab):
|
|||
if self._state.sub_tab is SubTabType.HOST:
|
||||
|
||||
# If we're not signed in, just refresh to show that.
|
||||
if (_ba.get_account_state() != 'signed_in'
|
||||
if (_ba.get_v1_account_state() != 'signed_in'
|
||||
and self._showing_not_signed_in_screen):
|
||||
self._refresh_sub_tab()
|
||||
else:
|
||||
|
|
@ -254,7 +254,7 @@ class PrivateGatherTab(GatherTab):
|
|||
if (self._last_hosting_state_query_time is None
|
||||
or now - self._last_hosting_state_query_time > 15.0):
|
||||
self._debug_server_comm('querying private party state')
|
||||
if _ba.get_account_state() == 'signed_in':
|
||||
if _ba.get_v1_account_state() == 'signed_in':
|
||||
_ba.add_transaction(
|
||||
{
|
||||
'type': 'PRIVATE_PARTY_QUERY',
|
||||
|
|
@ -437,7 +437,7 @@ class PrivateGatherTab(GatherTab):
|
|||
# pylint: disable=too-many-branches
|
||||
# pylint: disable=too-many-statements
|
||||
|
||||
if _ba.get_account_state() != 'signed_in':
|
||||
if _ba.get_v1_account_state() != 'signed_in':
|
||||
ba.textwidget(parent=self._container,
|
||||
size=(0, 0),
|
||||
h_align='center',
|
||||
|
|
@ -776,7 +776,7 @@ class PrivateGatherTab(GatherTab):
|
|||
or self._waiting_for_initial_state):
|
||||
return
|
||||
|
||||
if _ba.get_account_state() != 'signed_in':
|
||||
if _ba.get_v1_account_state() != 'signed_in':
|
||||
ba.screenmessage(ba.Lstr(resource='notSignedInErrorText'))
|
||||
ba.playsound(ba.getsound('error'))
|
||||
self._refresh_sub_tab()
|
||||
|
|
@ -795,7 +795,7 @@ class PrivateGatherTab(GatherTab):
|
|||
if self._hostingstate.tickets_to_host_now > 0:
|
||||
ticket_count: Optional[int]
|
||||
try:
|
||||
ticket_count = _ba.get_account_ticket_count()
|
||||
ticket_count = _ba.get_v1_account_ticket_count()
|
||||
except Exception:
|
||||
# FIXME: should add a ba.NotSignedInError we can use here.
|
||||
ticket_count = None
|
||||
|
|
@ -809,7 +809,7 @@ class PrivateGatherTab(GatherTab):
|
|||
{
|
||||
'type': 'PRIVATE_PARTY_START',
|
||||
'config': dataclass_to_dict(self._hostingconfig),
|
||||
'region_pings': ba.app.net.region_pings,
|
||||
'region_pings': ba.app.net.zone_pings,
|
||||
'expire_time': time.time() + 20,
|
||||
},
|
||||
callback=ba.WeakCall(self._hosting_state_response))
|
||||
|
|
|
|||
18
dist/ba_data/python/bastd/ui/gather/publictab.py
vendored
18
dist/ba_data/python/bastd/ui/gather/publictab.py
vendored
|
|
@ -88,8 +88,8 @@ class UIRow:
|
|||
if party.clean_display_index == index:
|
||||
return
|
||||
|
||||
ping_good = _ba.get_account_misc_read_val('pingGood', 100)
|
||||
ping_med = _ba.get_account_misc_read_val('pingMed', 500)
|
||||
ping_good = _ba.get_v1_account_misc_read_val('pingGood', 100)
|
||||
ping_med = _ba.get_v1_account_misc_read_val('pingMed', 500)
|
||||
|
||||
self._clear()
|
||||
hpos = 20
|
||||
|
|
@ -122,8 +122,8 @@ class UIRow:
|
|||
if party.stats_addr:
|
||||
url = party.stats_addr.replace(
|
||||
'${ACCOUNT}',
|
||||
_ba.get_account_misc_read_val_2('resolvedAccountID',
|
||||
'UNKNOWN'))
|
||||
_ba.get_v1_account_misc_read_val_2('resolvedAccountID',
|
||||
'UNKNOWN'))
|
||||
self._stats_button = ba.buttonwidget(
|
||||
color=(0.3, 0.6, 0.94),
|
||||
textcolor=(1.0, 1.0, 1.0),
|
||||
|
|
@ -793,7 +793,7 @@ class PublicGatherTab(GatherTab):
|
|||
self._process_pending_party_infos()
|
||||
|
||||
# Anytime we sign in/out, make sure we refresh our list.
|
||||
signed_in = _ba.get_account_state() == 'signed_in'
|
||||
signed_in = _ba.get_v1_account_state() == 'signed_in'
|
||||
if self._signed_in != signed_in:
|
||||
self._signed_in = signed_in
|
||||
self._party_lists_dirty = True
|
||||
|
|
@ -986,7 +986,7 @@ class PublicGatherTab(GatherTab):
|
|||
p[1].index))
|
||||
|
||||
# If signed out or errored, show no parties.
|
||||
if (_ba.get_account_state() != 'signed_in'
|
||||
if (_ba.get_v1_account_state() != 'signed_in'
|
||||
or not self._have_valid_server_list):
|
||||
self._parties_displayed = {}
|
||||
else:
|
||||
|
|
@ -1023,11 +1023,11 @@ class PublicGatherTab(GatherTab):
|
|||
# Fire off a new public-party query periodically.
|
||||
if (self._last_server_list_query_time is None
|
||||
or now - self._last_server_list_query_time > 0.001 *
|
||||
_ba.get_account_misc_read_val('pubPartyRefreshMS', 10000)):
|
||||
_ba.get_v1_account_misc_read_val('pubPartyRefreshMS', 10000)):
|
||||
self._last_server_list_query_time = now
|
||||
if DEBUG_SERVER_COMMUNICATION:
|
||||
print('REQUESTING SERVER LIST')
|
||||
if _ba.get_account_state() == 'signed_in':
|
||||
if _ba.get_v1_account_state() == 'signed_in':
|
||||
_ba.add_transaction(
|
||||
{
|
||||
'type': 'PUBLIC_PARTY_QUERY',
|
||||
|
|
@ -1156,7 +1156,7 @@ class PublicGatherTab(GatherTab):
|
|||
|
||||
def _on_start_advertizing_press(self) -> None:
|
||||
from bastd.ui.account import show_sign_in_prompt
|
||||
if _ba.get_account_state() != 'signed_in':
|
||||
if _ba.get_v1_account_state() != 'signed_in':
|
||||
show_sign_in_prompt()
|
||||
return
|
||||
|
||||
|
|
|
|||
53
dist/ba_data/python/bastd/ui/getcurrency.py
vendored
53
dist/ba_data/python/bastd/ui/getcurrency.py
vendored
|
|
@ -179,22 +179,27 @@ class GetCurrencyWindow(ba.Window):
|
|||
c2txt = ba.Lstr(
|
||||
resource=rsrc,
|
||||
subs=[('${COUNT}',
|
||||
str(_ba.get_account_misc_read_val('tickets2Amount', 500)))])
|
||||
str(_ba.get_v1_account_misc_read_val('tickets2Amount',
|
||||
500)))])
|
||||
c3txt = ba.Lstr(
|
||||
resource=rsrc,
|
||||
subs=[('${COUNT}',
|
||||
str(_ba.get_account_misc_read_val('tickets3Amount',
|
||||
1500)))])
|
||||
subs=[
|
||||
('${COUNT}',
|
||||
str(_ba.get_v1_account_misc_read_val('tickets3Amount', 1500)))
|
||||
])
|
||||
c4txt = ba.Lstr(
|
||||
resource=rsrc,
|
||||
subs=[('${COUNT}',
|
||||
str(_ba.get_account_misc_read_val('tickets4Amount',
|
||||
5000)))])
|
||||
subs=[
|
||||
('${COUNT}',
|
||||
str(_ba.get_v1_account_misc_read_val('tickets4Amount', 5000)))
|
||||
])
|
||||
c5txt = ba.Lstr(
|
||||
resource=rsrc,
|
||||
subs=[('${COUNT}',
|
||||
str(_ba.get_account_misc_read_val('tickets5Amount',
|
||||
15000)))])
|
||||
subs=[
|
||||
('${COUNT}',
|
||||
str(_ba.get_v1_account_misc_read_val('tickets5Amount',
|
||||
15000)))
|
||||
])
|
||||
|
||||
h = 110.0
|
||||
|
||||
|
|
@ -261,7 +266,7 @@ class GetCurrencyWindow(ba.Window):
|
|||
label=ba.Lstr(resource=self._r + '.ticketsFromASponsorText',
|
||||
subs=[('${COUNT}',
|
||||
str(
|
||||
_ba.get_account_misc_read_val(
|
||||
_ba.get_v1_account_misc_read_val(
|
||||
'sponsorTickets', 5)))]),
|
||||
tex_name='ticketsMore',
|
||||
enabled=self._enable_ad_button,
|
||||
|
|
@ -301,11 +306,10 @@ class GetCurrencyWindow(ba.Window):
|
|||
size=b_size_3,
|
||||
label=ba.Lstr(
|
||||
resource='gatherWindow.earnTicketsForRecommendingText',
|
||||
subs=[
|
||||
('${COUNT}',
|
||||
str(_ba.get_account_misc_read_val(
|
||||
'sponsorTickets', 5)))
|
||||
]),
|
||||
subs=[('${COUNT}',
|
||||
str(
|
||||
_ba.get_v1_account_misc_read_val(
|
||||
'sponsorTickets', 5)))]),
|
||||
tex_name='ticketsMore',
|
||||
enabled=True,
|
||||
tex_opacity=0.6,
|
||||
|
|
@ -336,7 +340,7 @@ class GetCurrencyWindow(ba.Window):
|
|||
'.youHaveText').evaluate().partition('${COUNT}')[0].strip())
|
||||
txt2 = (ba.Lstr(
|
||||
resource=self._r +
|
||||
'.youHaveText').evaluate().rpartition('${COUNT}')[0].strip())
|
||||
'.youHaveText').evaluate().rpartition('${COUNT}')[-1].strip())
|
||||
|
||||
ba.textwidget(parent=self._root_widget,
|
||||
text=txt1,
|
||||
|
|
@ -427,16 +431,16 @@ class GetCurrencyWindow(ba.Window):
|
|||
import datetime
|
||||
|
||||
# if we somehow get signed out, just die..
|
||||
if _ba.get_account_state() != 'signed_in':
|
||||
if _ba.get_v1_account_state() != 'signed_in':
|
||||
self._back()
|
||||
return
|
||||
|
||||
self._ticket_count = _ba.get_account_ticket_count()
|
||||
self._ticket_count = _ba.get_v1_account_ticket_count()
|
||||
|
||||
# update our incentivized ad button depending on whether ads are
|
||||
# available
|
||||
if self._ad_button is not None:
|
||||
next_reward_ad_time = _ba.get_account_misc_read_val_2(
|
||||
next_reward_ad_time = _ba.get_v1_account_misc_read_val_2(
|
||||
'nextRewardAdTime', None)
|
||||
if next_reward_ad_time is not None:
|
||||
next_reward_ad_time = datetime.datetime.utcfromtimestamp(
|
||||
|
|
@ -494,8 +498,9 @@ class GetCurrencyWindow(ba.Window):
|
|||
app = ba.app
|
||||
if ((app.test_build or
|
||||
(app.platform == 'android'
|
||||
and app.subplatform in ['oculus', 'cardboard'])) and
|
||||
_ba.get_account_misc_read_val('allowAccountLinking2', False)):
|
||||
and app.subplatform in ['oculus', 'cardboard']))
|
||||
and _ba.get_v1_account_misc_read_val('allowAccountLinking2',
|
||||
False)):
|
||||
ba.screenmessage(ba.Lstr(resource=self._r +
|
||||
'.unavailableLinkAccountText'),
|
||||
color=(1, 0.5, 0))
|
||||
|
|
@ -509,7 +514,7 @@ class GetCurrencyWindow(ba.Window):
|
|||
from bastd.ui import appinvite
|
||||
from ba.internal import master_server_get
|
||||
if item == 'app_invite':
|
||||
if _ba.get_account_state() != 'signed_in':
|
||||
if _ba.get_v1_account_state() != 'signed_in':
|
||||
account.show_sign_in_prompt()
|
||||
return
|
||||
appinvite.handle_app_invites_press()
|
||||
|
|
@ -554,7 +559,7 @@ class GetCurrencyWindow(ba.Window):
|
|||
if item == 'ad':
|
||||
import datetime
|
||||
# if ads are disabled until some time, error..
|
||||
next_reward_ad_time = _ba.get_account_misc_read_val_2(
|
||||
next_reward_ad_time = _ba.get_v1_account_misc_read_val_2(
|
||||
'nextRewardAdTime', None)
|
||||
if next_reward_ad_time is not None:
|
||||
next_reward_ad_time = datetime.datetime.utcfromtimestamp(
|
||||
|
|
|
|||
4
dist/ba_data/python/bastd/ui/iconpicker.py
vendored
4
dist/ba_data/python/bastd/ui/iconpicker.py
vendored
|
|
@ -40,7 +40,7 @@ class IconPicker(popup.PopupWindow):
|
|||
self._transitioning_out = False
|
||||
|
||||
self._icons = [ba.charstr(ba.SpecialChar.LOGO)
|
||||
] + ba.app.accounts.get_purchased_icons()
|
||||
] + ba.app.accounts_v1.get_purchased_icons()
|
||||
count = len(self._icons)
|
||||
columns = 4
|
||||
rows = int(math.ceil(float(count) / columns))
|
||||
|
|
@ -137,7 +137,7 @@ class IconPicker(popup.PopupWindow):
|
|||
def _on_store_press(self) -> None:
|
||||
from bastd.ui.account import show_sign_in_prompt
|
||||
from bastd.ui.store.browser import StoreBrowserWindow
|
||||
if _ba.get_account_state() != 'signed_in':
|
||||
if _ba.get_v1_account_state() != 'signed_in':
|
||||
show_sign_in_prompt()
|
||||
return
|
||||
self._transition_out()
|
||||
|
|
|
|||
2
dist/ba_data/python/bastd/ui/kiosk.py
vendored
2
dist/ba_data/python/bastd/ui/kiosk.py
vendored
|
|
@ -360,7 +360,7 @@ class KioskWindow(ba.Window):
|
|||
def _update(self) -> None:
|
||||
# Kiosk-mode is designed to be used signed-out... try for force
|
||||
# the issue.
|
||||
if _ba.get_account_state() == 'signed_in':
|
||||
if _ba.get_v1_account_state() == 'signed_in':
|
||||
# _bs.sign_out()
|
||||
# FIXME: Try to delete player profiles here too.
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ class LeagueRankButton:
|
|||
self._smooth_update_timer: Optional[ba.Timer] = None
|
||||
|
||||
# Take note of our account state; we'll refresh later if this changes.
|
||||
self._account_state_num = _ba.get_account_state_num()
|
||||
self._account_state_num = _ba.get_v1_account_state_num()
|
||||
self._last_power_ranking_query_time: Optional[float] = None
|
||||
self._doing_power_ranking_query = False
|
||||
self.set_position(position)
|
||||
|
|
@ -106,7 +106,7 @@ class LeagueRankButton:
|
|||
self._update()
|
||||
|
||||
# If we've got cached power-ranking data already, apply it.
|
||||
data = ba.app.accounts.get_cached_league_rank_data()
|
||||
data = ba.app.accounts_v1.get_cached_league_rank_data()
|
||||
if data is not None:
|
||||
self._update_for_league_rank_data(data)
|
||||
|
||||
|
|
@ -224,7 +224,7 @@ class LeagueRankButton:
|
|||
|
||||
in_top = data is not None and data['rank'] is not None
|
||||
do_percent = False
|
||||
if data is None or _ba.get_account_state() != 'signed_in':
|
||||
if data is None or _ba.get_v1_account_state() != 'signed_in':
|
||||
self._percent = self._rank = None
|
||||
status_text = '-'
|
||||
elif in_top:
|
||||
|
|
@ -248,7 +248,8 @@ class LeagueRankButton:
|
|||
self._percent = self._rank = None
|
||||
status_text = '-'
|
||||
else:
|
||||
our_points = ba.app.accounts.get_league_rank_points(data)
|
||||
our_points = ba.app.accounts_v1.get_league_rank_points(
|
||||
data)
|
||||
progress = float(our_points) / data['scores'][-1][1]
|
||||
self._percent = int(progress * 100.0)
|
||||
self._rank = None
|
||||
|
|
@ -327,14 +328,14 @@ class LeagueRankButton:
|
|||
def _on_power_ranking_query_response(
|
||||
self, data: Optional[dict[str, Any]]) -> None:
|
||||
self._doing_power_ranking_query = False
|
||||
ba.app.accounts.cache_league_rank_data(data)
|
||||
ba.app.accounts_v1.cache_league_rank_data(data)
|
||||
self._update_for_league_rank_data(data)
|
||||
|
||||
def _update(self) -> None:
|
||||
cur_time = ba.time(ba.TimeType.REAL)
|
||||
|
||||
# If our account state has changed, refresh our UI.
|
||||
account_state_num = _ba.get_account_state_num()
|
||||
account_state_num = _ba.get_v1_account_state_num()
|
||||
if account_state_num != self._account_state_num:
|
||||
self._account_state_num = account_state_num
|
||||
|
||||
|
|
|
|||
|
|
@ -118,13 +118,13 @@ class LeagueRankWindow(ba.Window):
|
|||
self._season: Optional[str] = None
|
||||
|
||||
# take note of our account state; we'll refresh later if this changes
|
||||
self._account_state = _ba.get_account_state()
|
||||
self._account_state = _ba.get_v1_account_state()
|
||||
|
||||
self._refresh()
|
||||
self._restore_state()
|
||||
|
||||
# if we've got cached power-ranking data already, display it
|
||||
info = ba.app.accounts.get_cached_league_rank_data()
|
||||
info = ba.app.accounts_v1.get_cached_league_rank_data()
|
||||
if info is not None:
|
||||
self._update_for_league_rank_data(info)
|
||||
|
||||
|
|
@ -155,7 +155,8 @@ class LeagueRankWindow(ba.Window):
|
|||
resource='coopSelectWindow.activenessAllTimeInfoText'
|
||||
if self._season == 'a' else 'coopSelectWindow.activenessInfoText',
|
||||
subs=[('${MAX}',
|
||||
str(_ba.get_account_misc_read_val('activenessMax', 1.0)))])
|
||||
str(_ba.get_v1_account_misc_read_val('activenessMax',
|
||||
1.0)))])
|
||||
confirm.ConfirmWindow(txt,
|
||||
cancel_button=False,
|
||||
width=460,
|
||||
|
|
@ -164,17 +165,15 @@ class LeagueRankWindow(ba.Window):
|
|||
|
||||
def _on_pro_mult_press(self) -> None:
|
||||
from bastd.ui import confirm
|
||||
txt = ba.Lstr(
|
||||
resource='coopSelectWindow.proMultInfoText',
|
||||
subs=[
|
||||
('${PERCENT}',
|
||||
str(_ba.get_account_misc_read_val('proPowerRankingBoost',
|
||||
10))),
|
||||
('${PRO}',
|
||||
ba.Lstr(resource='store.bombSquadProNameText',
|
||||
subs=[('${APP_NAME}', ba.Lstr(resource='titleText'))
|
||||
]))
|
||||
])
|
||||
txt = ba.Lstr(resource='coopSelectWindow.proMultInfoText',
|
||||
subs=[('${PERCENT}',
|
||||
str(
|
||||
_ba.get_v1_account_misc_read_val(
|
||||
'proPowerRankingBoost', 10))),
|
||||
('${PRO}',
|
||||
ba.Lstr(resource='store.bombSquadProNameText',
|
||||
subs=[('${APP_NAME}',
|
||||
ba.Lstr(resource='titleText'))]))])
|
||||
confirm.ConfirmWindow(txt,
|
||||
cancel_button=False,
|
||||
width=460,
|
||||
|
|
@ -196,7 +195,7 @@ class LeagueRankWindow(ba.Window):
|
|||
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:
|
||||
ba.app.accounts.cache_league_rank_data(data)
|
||||
ba.app.accounts_v1.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)
|
||||
|
|
@ -209,7 +208,7 @@ class LeagueRankWindow(ba.Window):
|
|||
cur_time = ba.time(ba.TimeType.REAL)
|
||||
|
||||
# if our account state has changed, refresh our UI
|
||||
account_state = _ba.get_account_state()
|
||||
account_state = _ba.get_v1_account_state()
|
||||
if account_state != self._account_state:
|
||||
self._account_state = account_state
|
||||
self._save_state()
|
||||
|
|
@ -353,7 +352,7 @@ class LeagueRankWindow(ba.Window):
|
|||
maxwidth=200)
|
||||
|
||||
self._activity_mult_button: Optional[ba.Widget]
|
||||
if _ba.get_account_misc_read_val('act', False):
|
||||
if _ba.get_v1_account_misc_read_val('act', False):
|
||||
self._activity_mult_button = ba.buttonwidget(
|
||||
parent=w_parent,
|
||||
position=(h2 - 60, v2 + 10),
|
||||
|
|
@ -594,7 +593,7 @@ class LeagueRankWindow(ba.Window):
|
|||
# pylint: disable=too-many-locals
|
||||
if not self._root_widget:
|
||||
return
|
||||
accounts = ba.app.accounts
|
||||
accounts = ba.app.accounts_v1
|
||||
in_top = (data is not None and data['rank'] is not None)
|
||||
eq_text = self._rdict.powerRankingPointsEqualsText
|
||||
pts_txt = self._rdict.powerRankingPointsText
|
||||
|
|
@ -603,7 +602,7 @@ class LeagueRankWindow(ba.Window):
|
|||
finished_season_unranked = False
|
||||
self._can_do_more_button = True
|
||||
extra_text = ''
|
||||
if _ba.get_account_state() != 'signed_in':
|
||||
if _ba.get_v1_account_state() != 'signed_in':
|
||||
status_text = '(' + ba.Lstr(
|
||||
resource='notSignedInText').evaluate() + ')'
|
||||
elif in_top:
|
||||
|
|
@ -790,7 +789,8 @@ class LeagueRankWindow(ba.Window):
|
|||
|
||||
have_pro = False if data is None else data['p']
|
||||
pro_mult = 1.0 + float(
|
||||
_ba.get_account_misc_read_val('proPowerRankingBoost', 0.0)) * 0.01
|
||||
_ba.get_v1_account_misc_read_val('proPowerRankingBoost',
|
||||
0.0)) * 0.01
|
||||
# pylint: disable=consider-using-f-string
|
||||
ba.textwidget(edit=self._pro_mult_text,
|
||||
text=' -' if
|
||||
|
|
|
|||
37
dist/ba_data/python/bastd/ui/mainmenu.py
vendored
37
dist/ba_data/python/bastd/ui/mainmenu.py
vendored
|
|
@ -67,15 +67,16 @@ class MainMenuWindow(ba.Window):
|
|||
self._restore_state()
|
||||
|
||||
# Keep an eye on a few things and refresh if they change.
|
||||
self._account_state = _ba.get_account_state()
|
||||
self._account_state_num = _ba.get_account_state_num()
|
||||
self._account_type = (_ba.get_account_type()
|
||||
self._account_state = _ba.get_v1_account_state()
|
||||
self._account_state_num = _ba.get_v1_account_state_num()
|
||||
self._account_type = (_ba.get_v1_account_type()
|
||||
if self._account_state == 'signed_in' else None)
|
||||
self._refresh_timer = ba.Timer(1.0,
|
||||
ba.WeakCall(self._check_refresh),
|
||||
repeat=True,
|
||||
timetype=ba.TimeType.REAL)
|
||||
|
||||
# noinspection PyUnresolvedReferences
|
||||
@staticmethod
|
||||
def _preload_modules() -> None:
|
||||
"""Preload modules we use (called in bg thread)."""
|
||||
|
|
@ -121,9 +122,9 @@ class MainMenuWindow(ba.Window):
|
|||
ba.print_exception('Error showing get-remote-app info')
|
||||
|
||||
def _get_store_char_tex(self) -> str:
|
||||
return ('storeCharacterXmas' if _ba.get_account_misc_read_val(
|
||||
return ('storeCharacterXmas' if _ba.get_v1_account_misc_read_val(
|
||||
'xmas', False) else
|
||||
'storeCharacterEaster' if _ba.get_account_misc_read_val(
|
||||
'storeCharacterEaster' if _ba.get_v1_account_misc_read_val(
|
||||
'easter', False) else 'storeCharacter')
|
||||
|
||||
def _check_refresh(self) -> None:
|
||||
|
|
@ -137,13 +138,13 @@ class MainMenuWindow(ba.Window):
|
|||
return
|
||||
|
||||
store_char_tex = self._get_store_char_tex()
|
||||
account_state_num = _ba.get_account_state_num()
|
||||
account_state_num = _ba.get_v1_account_state_num()
|
||||
if (account_state_num != self._account_state_num
|
||||
or store_char_tex != self._store_char_tex):
|
||||
self._store_char_tex = store_char_tex
|
||||
self._account_state_num = account_state_num
|
||||
account_state = self._account_state = (_ba.get_account_state())
|
||||
self._account_type = (_ba.get_account_type()
|
||||
account_state = self._account_state = (_ba.get_v1_account_state())
|
||||
self._account_type = (_ba.get_v1_account_type()
|
||||
if account_state == 'signed_in' else None)
|
||||
self._save_state()
|
||||
self._refresh()
|
||||
|
|
@ -212,8 +213,8 @@ class MainMenuWindow(ba.Window):
|
|||
on_activate_call=self._settings)
|
||||
|
||||
# Scattered eggs on easter.
|
||||
if _ba.get_account_misc_read_val('easter',
|
||||
False) and not self._in_game:
|
||||
if _ba.get_v1_account_misc_read_val('easter',
|
||||
False) and not self._in_game:
|
||||
icon_size = 34
|
||||
ba.imagewidget(parent=self._root_widget,
|
||||
position=(h - icon_size * 0.5 - 15,
|
||||
|
|
@ -309,7 +310,7 @@ class MainMenuWindow(ba.Window):
|
|||
transition_delay=self._tdelay)
|
||||
|
||||
# Scattered eggs on easter.
|
||||
if _ba.get_account_misc_read_val('easter', False):
|
||||
if _ba.get_v1_account_misc_read_val('easter', False):
|
||||
icon_size = 30
|
||||
ba.imagewidget(parent=self._root_widget,
|
||||
position=(h - icon_size * 0.5 + 25,
|
||||
|
|
@ -426,8 +427,8 @@ class MainMenuWindow(ba.Window):
|
|||
self._height = 200.0
|
||||
enable_account_button = True
|
||||
account_type_name: Union[str, ba.Lstr]
|
||||
if _ba.get_account_state() == 'signed_in':
|
||||
account_type_name = _ba.get_account_display_string()
|
||||
if _ba.get_v1_account_state() == 'signed_in':
|
||||
account_type_name = _ba.get_v1_account_display_string()
|
||||
account_type_icon = None
|
||||
account_textcolor = (1.0, 1.0, 1.0)
|
||||
else:
|
||||
|
|
@ -617,8 +618,8 @@ class MainMenuWindow(ba.Window):
|
|||
enable_sound=account_type_enable_button_sound)
|
||||
|
||||
# Scattered eggs on easter.
|
||||
if _ba.get_account_misc_read_val('easter',
|
||||
False) and not self._in_game:
|
||||
if _ba.get_v1_account_misc_read_val('easter',
|
||||
False) and not self._in_game:
|
||||
icon_size = 32
|
||||
ba.imagewidget(parent=self._root_widget,
|
||||
position=(h - icon_size * 0.5 + 35,
|
||||
|
|
@ -647,8 +648,8 @@ class MainMenuWindow(ba.Window):
|
|||
self._how_to_play_button = btn
|
||||
|
||||
# Scattered eggs on easter.
|
||||
if _ba.get_account_misc_read_val('easter',
|
||||
False) and not self._in_game:
|
||||
if _ba.get_v1_account_misc_read_val('easter',
|
||||
False) and not self._in_game:
|
||||
icon_size = 28
|
||||
ba.imagewidget(parent=self._root_widget,
|
||||
position=(h - icon_size * 0.5 + 30,
|
||||
|
|
@ -850,7 +851,7 @@ class MainMenuWindow(ba.Window):
|
|||
# pylint: disable=cyclic-import
|
||||
from bastd.ui.store.browser import StoreBrowserWindow
|
||||
from bastd.ui.account import show_sign_in_prompt
|
||||
if _ba.get_account_state() != 'signed_in':
|
||||
if _ba.get_v1_account_state() != 'signed_in':
|
||||
show_sign_in_prompt()
|
||||
return
|
||||
self._save_state()
|
||||
|
|
|
|||
19
dist/ba_data/python/bastd/ui/partyqueue.py
vendored
19
dist/ba_data/python/bastd/ui/partyqueue.py
vendored
|
|
@ -320,8 +320,8 @@ class PartyQueueWindow(ba.Window):
|
|||
if -1 not in self._dudes_by_id:
|
||||
dude = self.Dude(
|
||||
self, response['d'], self._initial_offset, True,
|
||||
_ba.get_account_misc_read_val_2('resolvedAccountID', None),
|
||||
_ba.get_account_display_string())
|
||||
_ba.get_v1_account_misc_read_val_2('resolvedAccountID', None),
|
||||
_ba.get_v1_account_display_string())
|
||||
self._dudes_by_id[-1] = dude
|
||||
self._dudes.append(dude)
|
||||
else:
|
||||
|
|
@ -341,6 +341,7 @@ class PartyQueueWindow(ba.Window):
|
|||
self._dudes_by_id[enemy_id].claimed = True
|
||||
|
||||
# remove unclaimed dudes from both of our lists
|
||||
# noinspection PyUnresolvedReferences
|
||||
self._dudes_by_id = dict([
|
||||
item for item in list(self._dudes_by_id.items()) if item[1].claimed
|
||||
])
|
||||
|
|
@ -456,11 +457,11 @@ class PartyQueueWindow(ba.Window):
|
|||
"""Boost was pressed."""
|
||||
from bastd.ui import account
|
||||
from bastd.ui import getcurrency
|
||||
if _ba.get_account_state() != 'signed_in':
|
||||
if _ba.get_v1_account_state() != 'signed_in':
|
||||
account.show_sign_in_prompt()
|
||||
return
|
||||
|
||||
if _ba.get_account_ticket_count() < self._boost_tickets:
|
||||
if _ba.get_v1_account_ticket_count() < self._boost_tickets:
|
||||
ba.playsound(ba.getsound('error'))
|
||||
getcurrency.show_get_tickets_prompt()
|
||||
return
|
||||
|
|
@ -497,17 +498,17 @@ class PartyQueueWindow(ba.Window):
|
|||
# Update boost button color based on if we have enough moola.
|
||||
if self._boost_button is not None:
|
||||
can_boost = (
|
||||
(_ba.get_account_state() == 'signed_in'
|
||||
and _ba.get_account_ticket_count() >= self._boost_tickets))
|
||||
(_ba.get_v1_account_state() == 'signed_in'
|
||||
and _ba.get_v1_account_ticket_count() >= self._boost_tickets))
|
||||
ba.buttonwidget(edit=self._boost_button,
|
||||
color=(0, 1, 0) if can_boost else (0.7, 0.7, 0.7))
|
||||
|
||||
# Update ticket-count.
|
||||
if self._tickets_text is not None:
|
||||
if self._boost_button is not None:
|
||||
if _ba.get_account_state() == 'signed_in':
|
||||
if _ba.get_v1_account_state() == 'signed_in':
|
||||
val = ba.charstr(ba.SpecialChar.TICKET) + str(
|
||||
_ba.get_account_ticket_count())
|
||||
_ba.get_v1_account_ticket_count())
|
||||
else:
|
||||
val = ba.charstr(ba.SpecialChar.TICKET) + '???'
|
||||
ba.textwidget(edit=self._tickets_text, text=val)
|
||||
|
|
@ -517,7 +518,7 @@ class PartyQueueWindow(ba.Window):
|
|||
current_time = ba.time(ba.TimeType.REAL)
|
||||
if (self._last_transaction_time is None
|
||||
or current_time - self._last_transaction_time >
|
||||
0.001 * _ba.get_account_misc_read_val('pqInt', 5000)):
|
||||
0.001 * _ba.get_v1_account_misc_read_val('pqInt', 5000)):
|
||||
self._last_transaction_time = current_time
|
||||
_ba.add_transaction(
|
||||
{
|
||||
|
|
|
|||
3
dist/ba_data/python/bastd/ui/play.py
vendored
3
dist/ba_data/python/bastd/ui/play.py
vendored
|
|
@ -417,6 +417,7 @@ class PlayWindow(ba.Window):
|
|||
|
||||
self._restore_state()
|
||||
|
||||
# noinspection PyUnresolvedReferences
|
||||
@staticmethod
|
||||
def _preload_modules() -> None:
|
||||
"""Preload modules we use (called in bg thread)."""
|
||||
|
|
@ -446,7 +447,7 @@ class PlayWindow(ba.Window):
|
|||
# pylint: disable=cyclic-import
|
||||
from bastd.ui.account import show_sign_in_prompt
|
||||
from bastd.ui.coop.browser import CoopBrowserWindow
|
||||
if _ba.get_account_state() != 'signed_in':
|
||||
if _ba.get_v1_account_state() != 'signed_in':
|
||||
show_sign_in_prompt()
|
||||
return
|
||||
self._save_state()
|
||||
|
|
|
|||
|
|
@ -176,7 +176,7 @@ class PlaylistAddGameWindow(ba.Window):
|
|||
def _on_get_more_games_press(self) -> None:
|
||||
from bastd.ui.account import show_sign_in_prompt
|
||||
from bastd.ui.store.browser import StoreBrowserWindow
|
||||
if _ba.get_account_state() != 'signed_in':
|
||||
if _ba.get_v1_account_state() != 'signed_in':
|
||||
show_sign_in_prompt()
|
||||
return
|
||||
StoreBrowserWindow(modal=True,
|
||||
|
|
|
|||
|
|
@ -140,7 +140,7 @@ class PlaylistBrowserWindow(ba.Window):
|
|||
def _ensure_standard_playlists_exist(self) -> None:
|
||||
# On new installations, go ahead and create a few playlists
|
||||
# besides the hard-coded default one:
|
||||
if not _ba.get_account_misc_val('madeStandardPlaylists', False):
|
||||
if not _ba.get_v1_account_misc_val('madeStandardPlaylists', False):
|
||||
_ba.add_transaction({
|
||||
'type':
|
||||
'ADD_PLAYLIST',
|
||||
|
|
|
|||
|
|
@ -253,7 +253,7 @@ class PlaylistCustomizeBrowserWindow(ba.Window):
|
|||
self._update()
|
||||
|
||||
def _update(self) -> None:
|
||||
have = ba.app.accounts.have_pro_options()
|
||||
have = ba.app.accounts_v1.have_pro_options()
|
||||
for lock in self._lock_images:
|
||||
ba.imagewidget(edit=lock, opacity=0.0 if have else 1.0)
|
||||
|
||||
|
|
@ -383,7 +383,7 @@ class PlaylistCustomizeBrowserWindow(ba.Window):
|
|||
# pylint: disable=cyclic-import
|
||||
from bastd.ui.playlist.editcontroller import PlaylistEditController
|
||||
from bastd.ui.purchase import PurchaseWindow
|
||||
if not ba.app.accounts.have_pro_options():
|
||||
if not ba.app.accounts_v1.have_pro_options():
|
||||
PurchaseWindow(items=['pro'])
|
||||
return
|
||||
|
||||
|
|
@ -407,7 +407,7 @@ class PlaylistCustomizeBrowserWindow(ba.Window):
|
|||
# pylint: disable=cyclic-import
|
||||
from bastd.ui.playlist.editcontroller import PlaylistEditController
|
||||
from bastd.ui.purchase import PurchaseWindow
|
||||
if not ba.app.accounts.have_pro_options():
|
||||
if not ba.app.accounts_v1.have_pro_options():
|
||||
PurchaseWindow(items=['pro'])
|
||||
return
|
||||
if self._selected_playlist_name is None:
|
||||
|
|
@ -445,7 +445,7 @@ class PlaylistCustomizeBrowserWindow(ba.Window):
|
|||
from bastd.ui.playlist import share
|
||||
|
||||
# Gotta be signed in for this to work.
|
||||
if _ba.get_account_state() != 'signed_in':
|
||||
if _ba.get_v1_account_state() != 'signed_in':
|
||||
ba.screenmessage(ba.Lstr(resource='notSignedInErrorText'),
|
||||
color=(1, 0, 0))
|
||||
ba.playsound(ba.getsound('error'))
|
||||
|
|
@ -472,12 +472,12 @@ class PlaylistCustomizeBrowserWindow(ba.Window):
|
|||
def _share_playlist(self) -> None:
|
||||
# pylint: disable=cyclic-import
|
||||
from bastd.ui.purchase import PurchaseWindow
|
||||
if not ba.app.accounts.have_pro_options():
|
||||
if not ba.app.accounts_v1.have_pro_options():
|
||||
PurchaseWindow(items=['pro'])
|
||||
return
|
||||
|
||||
# Gotta be signed in for this to work.
|
||||
if _ba.get_account_state() != 'signed_in':
|
||||
if _ba.get_v1_account_state() != 'signed_in':
|
||||
ba.screenmessage(ba.Lstr(resource='notSignedInErrorText'),
|
||||
color=(1, 0, 0))
|
||||
ba.playsound(ba.getsound('error'))
|
||||
|
|
@ -508,7 +508,7 @@ class PlaylistCustomizeBrowserWindow(ba.Window):
|
|||
# pylint: disable=cyclic-import
|
||||
from bastd.ui.purchase import PurchaseWindow
|
||||
from bastd.ui.confirm import ConfirmWindow
|
||||
if not ba.app.accounts.have_pro_options():
|
||||
if not ba.app.accounts_v1.have_pro_options():
|
||||
PurchaseWindow(items=['pro'])
|
||||
return
|
||||
|
||||
|
|
@ -534,7 +534,7 @@ class PlaylistCustomizeBrowserWindow(ba.Window):
|
|||
# pylint: disable=too-many-branches
|
||||
# pylint: disable=cyclic-import
|
||||
from bastd.ui.purchase import PurchaseWindow
|
||||
if not ba.app.accounts.have_pro_options():
|
||||
if not ba.app.accounts_v1.have_pro_options():
|
||||
PurchaseWindow(items=['pro'])
|
||||
return
|
||||
if self._selected_playlist_name is None:
|
||||
|
|
|
|||
|
|
@ -210,7 +210,7 @@ class PlaylistMapSelectWindow(ba.Window):
|
|||
def _on_store_press(self) -> None:
|
||||
from bastd.ui import account
|
||||
from bastd.ui.store.browser import StoreBrowserWindow
|
||||
if _ba.get_account_state() != 'signed_in':
|
||||
if _ba.get_v1_account_state() != 'signed_in':
|
||||
account.show_sign_in_prompt()
|
||||
return
|
||||
StoreBrowserWindow(modal=True,
|
||||
|
|
|
|||
6
dist/ba_data/python/bastd/ui/playoptions.py
vendored
6
dist/ba_data/python/bastd/ui/playoptions.py
vendored
|
|
@ -250,7 +250,7 @@ class PlayOptionsWindow(popup.PopupWindow):
|
|||
autoselect=True,
|
||||
textcolor=(0.8, 0.8, 0.8),
|
||||
label=ba.Lstr(resource='teamNamesColorText'))
|
||||
if not ba.app.accounts.have_pro():
|
||||
if not ba.app.accounts_v1.have_pro():
|
||||
ba.imagewidget(
|
||||
parent=self.root_widget,
|
||||
size=(30, 30),
|
||||
|
|
@ -348,8 +348,8 @@ class PlayOptionsWindow(popup.PopupWindow):
|
|||
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':
|
||||
if not ba.app.accounts_v1.have_pro():
|
||||
if _ba.get_v1_account_state() != 'signed_in':
|
||||
show_sign_in_prompt()
|
||||
else:
|
||||
PurchaseWindow(items=['pro'])
|
||||
|
|
|
|||
4
dist/ba_data/python/bastd/ui/popup.py
vendored
4
dist/ba_data/python/bastd/ui/popup.py
vendored
|
|
@ -15,7 +15,9 @@ if TYPE_CHECKING:
|
|||
|
||||
|
||||
class PopupWindow:
|
||||
"""A transient window that positions and scales itself for visibility."""
|
||||
"""A transient window that positions and scales itself for visibility.
|
||||
|
||||
Category: UI Classes"""
|
||||
|
||||
def __init__(self,
|
||||
position: tuple[float, float],
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue