Cleaned up player and team lifecycle logic

This commit is contained in:
Eric Froemling 2020-05-31 18:15:29 -07:00
parent 1c0e4896fa
commit a2bb5b9751
32 changed files with 869 additions and 546 deletions

View File

@ -4132,16 +4132,16 @@
"assets/build/windows/x64/python.exe": "https://files.ballistica.net/cache/ba1/25/a7/dc87c1be41605eb6fefd0145144c", "assets/build/windows/x64/python.exe": "https://files.ballistica.net/cache/ba1/25/a7/dc87c1be41605eb6fefd0145144c",
"assets/build/windows/x64/python37.dll": "https://files.ballistica.net/cache/ba1/b9/e4/d912f56e42e9991bcbb4c804cfcb", "assets/build/windows/x64/python37.dll": "https://files.ballistica.net/cache/ba1/b9/e4/d912f56e42e9991bcbb4c804cfcb",
"assets/build/windows/x64/pythonw.exe": "https://files.ballistica.net/cache/ba1/6c/bb/b6f52c306aa4e88061510e96cefe", "assets/build/windows/x64/pythonw.exe": "https://files.ballistica.net/cache/ba1/6c/bb/b6f52c306aa4e88061510e96cefe",
"build/prefab/linux-server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/36/5a/025457e87759c13a5067617816cd", "build/prefab/linux-server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/cd/cc/837483543f1d6b184f64b5e6a950",
"build/prefab/linux-server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/66/49/fb5663472ab1f4bd32f5748ba4d7", "build/prefab/linux-server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/38/e6/d372324c7c08b5b300490fa5594e",
"build/prefab/linux/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/44/3e/6eb652f11d24c22f87ae8aab399b", "build/prefab/linux/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/0e/11/3dda0974b64f51be4961628f572c",
"build/prefab/linux/release/ballisticacore": "https://files.ballistica.net/cache/ba1/f4/57/7e48692454b644a7111902f12b58", "build/prefab/linux/release/ballisticacore": "https://files.ballistica.net/cache/ba1/cc/76/3f6356dd599091f5955ce349593b",
"build/prefab/mac-server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/18/55/246021de09b021993e8bdbdb19a6", "build/prefab/mac-server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/e1/23/3fe78cdef456a99140837ede7e49",
"build/prefab/mac-server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/3f/ea/079aa2649359d2024b7c20dd19d0", "build/prefab/mac-server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/1c/bd/4c73637ee172630ee00145032ce7",
"build/prefab/mac/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/05/5d/2608f732d75799cfe09c6947736a", "build/prefab/mac/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/7a/a7/342bea2a6ec2f94d5de7bb52a59f",
"build/prefab/mac/release/ballisticacore": "https://files.ballistica.net/cache/ba1/ef/20/4f2abb279d24dced1f11f83fbe78", "build/prefab/mac/release/ballisticacore": "https://files.ballistica.net/cache/ba1/aa/24/5426cb7e6ca01b9e5d67ad3217b0",
"build/prefab/windows-server/debug/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/f9/36/28081d9962d3f91c286663357e80", "build/prefab/windows-server/debug/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/cb/73/469fe3eb016f1e9c7f5c7811b182",
"build/prefab/windows-server/release/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/51/55/83ceb6ffb806c75815e5f9e88008", "build/prefab/windows-server/release/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/7c/fb/7e0880c1ab90b0484cdedc41c8ae",
"build/prefab/windows/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/46/ea/461f76e113d3a02da3c61ea04bdb", "build/prefab/windows/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/f3/f2/097e861cf6ca981b18191e4c43b5",
"build/prefab/windows/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/83/af/a8ce32760e4dc300ac861f745615" "build/prefab/windows/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/6b/7c/8407f4a7326b8b19f3e187d3ffcb"
} }

View File

@ -105,6 +105,7 @@
<w>assetpath</w> <w>assetpath</w>
<w>assettype</w> <w>assettype</w>
<w>assettypestr</w> <w>assettypestr</w>
<w>assigninput</w>
<w>astc</w> <w>astc</w>
<w>astcenc</w> <w>astcenc</w>
<w>astroid</w> <w>astroid</w>
@ -765,6 +766,7 @@
<w>getlog</w> <w>getlog</w>
<w>getmaps</w> <w>getmaps</w>
<w>getmodel</w> <w>getmodel</w>
<w>getname</w>
<w>getnodes</w> <w>getnodes</w>
<w>getnodetype</w> <w>getnodetype</w>
<w>getopt</w> <w>getopt</w>
@ -1382,6 +1384,7 @@
<w>posixpath</w> <w>posixpath</w>
<w>posixsubprocess</w> <w>posixsubprocess</w>
<w>postinit</w> <w>postinit</w>
<w>postinited</w>
<w>poststr</w> <w>poststr</w>
<w>powerdown</w> <w>powerdown</w>
<w>powersgiven</w> <w>powersgiven</w>
@ -1539,6 +1542,7 @@
<w>reqtype</w> <w>reqtype</w>
<w>reqtypes</w> <w>reqtypes</w>
<w>resample</w> <w>resample</w>
<w>resetinput</w>
<w>resourcetypeinfo</w> <w>resourcetypeinfo</w>
<w>respawn</w> <w>respawn</w>
<w>respawnable</w> <w>respawnable</w>
@ -1557,6 +1561,7 @@
<w>rfudge</w> <w>rfudge</w>
<w>rgba</w> <w>rgba</w>
<w>rlcompleter</w> <w>rlcompleter</w>
<w>rlock</w>
<w>rmats</w> <w>rmats</w>
<w>rmine</w> <w>rmine</w>
<w>robotparser</w> <w>robotparser</w>
@ -1580,6 +1585,7 @@
<w>rval</w> <w>rval</w>
<w>safecolor</w> <w>safecolor</w>
<w>safesetattr</w> <w>safesetattr</w>
<w>safesetcolor</w>
<w>saitek</w> <w>saitek</w>
<w>samsung</w> <w>samsung</w>
<w>sandboxing</w> <w>sandboxing</w>
@ -1641,12 +1647,18 @@
<w>sessionglobalsnode</w> <w>sessionglobalsnode</w>
<w>sessionname</w> <w>sessionname</w>
<w>sessionplayer</w> <w>sessionplayer</w>
<w>sessionplayers</w>
<w>sessionteam</w> <w>sessionteam</w>
<w>sessionteams</w>
<w>sessiontype</w> <w>sessiontype</w>
<w>setactivity</w>
<w>setalpha</w> <w>setalpha</w>
<w>setbuild</w> <w>setbuild</w>
<w>setdata</w>
<w>setlanguage</w> <w>setlanguage</w>
<w>setmusic</w> <w>setmusic</w>
<w>setname</w>
<w>setnode</w>
<w>setsticky</w> <w>setsticky</w>
<w>settingname</w> <w>settingname</w>
<w>setversion</w> <w>setversion</w>
@ -2053,6 +2065,7 @@
<w>webpages</w> <w>webpages</w>
<w>whatevs</w> <w>whatevs</w>
<w>wheee</w> <w>wheee</w>
<w>whos</w>
<w>widgetdeathtime</w> <w>widgetdeathtime</w>
<w>wiimote</w> <w>wiimote</w>
<w>wiimotes</w> <w>wiimotes</w>

View File

@ -34,7 +34,7 @@ NOTE: This file was autogenerated by gendummymodule; do not edit by hand.
""" """
# (hash we can use to see if this file is out of date) # (hash we can use to see if this file is out of date)
# SOURCES_HASH=33373537027192610824913965080968605548 # SOURCES_HASH=172645630847706592745162067676099526148
# I'm sorry Pylint. I know this file saddens you. Be strong. # I'm sorry Pylint. I know this file saddens you. Be strong.
# pylint: disable=useless-suppression # pylint: disable=useless-suppression
@ -76,13 +76,6 @@ def _uninferrable() -> Any:
class ActivityData: class ActivityData:
"""(internal)""" """(internal)"""
def destroy(self) -> None:
"""destroy() -> None
Destroys the internal data for the activity
"""
return None
def exists(self) -> bool: def exists(self) -> bool:
"""exists() -> bool """exists() -> bool
@ -91,6 +84,13 @@ class ActivityData:
""" """
return bool() return bool()
def expire(self) -> None:
"""expire() -> None
Expires the internal data for the activity
"""
return None
def make_foreground(self) -> None: def make_foreground(self) -> None:
"""make_foreground() -> None """make_foreground() -> None
@ -832,11 +832,6 @@ class SessionPlayer:
storing data associated with this player. storing data associated with this player.
This persists for the duration of the session. This persists for the duration of the session.
gamedata: Dict
A dict for use by the current ba.Activity for
storing data associated with this Player.
This gets cleared for each new ba.Activity.
inputdevice: ba.InputDevice inputdevice: ba.InputDevice
The input device associated with the player. The input device associated with the player.
@ -860,16 +855,15 @@ class SessionPlayer:
in_game: bool in_game: bool
team: ba.SessionTeam team: ba.SessionTeam
sessiondata: Dict sessiondata: Dict
gamedata: Dict
inputdevice: ba.InputDevice inputdevice: ba.InputDevice
color: Sequence[float] color: Sequence[float]
highlight: Sequence[float] highlight: Sequence[float]
character: str character: str
gameplayer: Optional[ba.Player] gameplayer: Optional[ba.Player]
def assign_input_call(self, type: Union[str, Tuple[str, ...]], def assigninput(self, type: Union[str, Tuple[str, ...]],
call: Callable) -> None: call: Callable) -> None:
"""assign_input_call(type: Union[str, Tuple[str, ...]], """assigninput(type: Union[str, Tuple[str, ...]],
call: Callable) -> None call: Callable) -> None
Set the python callable to be run for one or more types of input. Set the python callable to be run for one or more types of input.
@ -930,36 +924,13 @@ class SessionPlayer:
""" """
return None return None
def reset(self) -> None: def resetinput(self) -> None:
"""reset() -> None """resetinput() -> None
(internal)
"""
return None
def reset_input(self) -> None:
"""reset_input() -> None
Clears out the player's assigned input actions. Clears out the player's assigned input actions.
""" """
return None return None
def set_activity(self, activity: Optional[ba.Activity]) -> None:
"""set_activity(activity: Optional[ba.Activity]) -> None
(internal)
"""
return None
def set_data(self, team: ba.SessionTeam, character: str,
color: Sequence[float], highlight: Sequence[float]) -> None:
"""set_data(team: ba.SessionTeam, character: str,
color: Sequence[float], highlight: Sequence[float]) -> None
(internal)
"""
return None
def set_icon_info(self, texture: str, tint_texture: str, def set_icon_info(self, texture: str, tint_texture: str,
tint_color: Sequence[float], tint_color: Sequence[float],
tint2_color: Sequence[float]) -> None: tint2_color: Sequence[float]) -> None:
@ -970,11 +941,27 @@ class SessionPlayer:
""" """
return None return None
def set_name(self, def setactivity(self, activity: Optional[ba.Activity]) -> None:
name: str, """setactivity(activity: Optional[ba.Activity]) -> None
full_name: str = None,
real: bool = True) -> None: (internal)
"""set_name(name: str, full_name: str = None, real: bool = True) """
return None
def setdata(self, team: ba.SessionTeam, character: str,
color: Sequence[float], highlight: Sequence[float]) -> None:
"""setdata(team: ba.SessionTeam, character: str,
color: Sequence[float], highlight: Sequence[float]) -> None
(internal)
"""
return None
def setname(self,
name: str,
full_name: str = None,
real: bool = True) -> None:
"""setname(name: str, full_name: str = None, real: bool = True)
-> None -> None
Set the player's name to the provided string. Set the player's name to the provided string.
@ -983,8 +970,8 @@ class SessionPlayer:
""" """
return None return None
def set_node(self, node: Optional[Node]) -> None: def setnode(self, node: Optional[Node]) -> None:
"""set_node(node: Optional[Node]) -> None """setnode(node: Optional[Node]) -> None
(internal) (internal)
""" """

View File

@ -27,10 +27,10 @@ from typing import TYPE_CHECKING, Generic, TypeVar
from ba._team import Team from ba._team import Team
from ba._player import Player from ba._player import Player
from ba._error import (print_exception, SessionTeamNotFoundError, from ba._error import (print_exception, SessionTeamNotFoundError,
NodeNotFoundError) SessionPlayerNotFoundError, NodeNotFoundError)
from ba._dependency import DependencyComponent from ba._dependency import DependencyComponent
from ba._general import Call, verify_object_death from ba._general import Call, verify_object_death
from ba._messages import UNHANDLED, DieMessage, DeathType from ba._messages import UNHANDLED
import _ba import _ba
if TYPE_CHECKING: if TYPE_CHECKING:
@ -143,7 +143,7 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
def __init__(self, settings: Dict[str, Any]): def __init__(self, settings: Dict[str, Any]):
"""Creates an Activity in the current ba.Session. """Creates an Activity in the current ba.Session.
The activity will not be actually run until ba.Session.set_activity() The activity will not be actually run until ba.Session.setactivity()
is called. 'settings' should be a dict of key/value pairs specific is called. 'settings' should be a dict of key/value pairs specific
to the activity. to the activity.
@ -189,6 +189,10 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
self._has_ended = False self._has_ended = False
self._activity_death_check_timer: Optional[ba.Timer] = None self._activity_death_check_timer: Optional[ba.Timer] = None
self._expired = False self._expired = False
self._delay_delete_players: List[PlayerType] = []
self._delay_delete_teams: List[TeamType] = []
self._players_that_left: List[ReferenceType[PlayerType]] = []
self._teams_that_left: List[ReferenceType[TeamType]] = []
# This gets set once another activity has begun transitioning in but # This gets set once another activity has begun transitioning in but
# before this one is killed. The on_transition_out() method is also # before this one is killed. The on_transition_out() method is also
@ -208,8 +212,10 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
# This stuff gets filled in just before on_begin() is called. # This stuff gets filled in just before on_begin() is called.
self.teams = [] self.teams = []
self.players = [] self.players = []
self.lobby = None self.lobby = None
self._stats: Optional[ba.Stats] = None self._stats: Optional[ba.Stats] = None
self._gamedata: Optional[dict] = {}
def __del__(self) -> None: def __del__(self) -> None:
@ -258,6 +264,17 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
can begin. can begin.
""" """
@property
def gamedata(self) -> dict:
"""Entities needing to store simple data with an activity can put it
here. This dict will be deleted when the activity expires, so contained
objects generally do not need to worry about handling expired
activities.
"""
assert not self._expired
assert isinstance(self._gamedata, dict)
return self._gamedata
@property @property
def expired(self) -> bool: def expired(self) -> bool:
"""Whether the activity is expired. """Whether the activity is expired.
@ -282,7 +299,7 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
"""(internal)""" """(internal)"""
self._has_ended = val self._has_ended = val
def destroy(self) -> None: def expire(self) -> None:
"""Begin the process of tearing down the activity. """Begin the process of tearing down the activity.
(internal) (internal)
@ -424,7 +441,6 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
(internal) (internal)
""" """
from ba._general import WeakCall
assert not self._has_transitioned_in assert not self._has_transitioned_in
self._has_transitioned_in = True self._has_transitioned_in = True
@ -457,10 +473,13 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
glb.vignette_outer = prev_globals.vignette_outer glb.vignette_outer = prev_globals.vignette_outer
glb.vignette_inner = prev_globals.vignette_inner glb.vignette_inner = prev_globals.vignette_inner
# Start pruning our transient actors periodically. # Start pruning our various things periodically.
self._prune_dead_actors_timer = _ba.Timer(
5.17, WeakCall(self._prune_dead_actors), repeat=True)
self._prune_dead_actors() self._prune_dead_actors()
self._prune_dead_actors_timer = _ba.Timer(5.17,
self._prune_dead_actors,
repeat=True)
_ba.timer(13.3, self._prune_delay_deletes, repeat=True)
# Also start our low-level scene running. # Also start our low-level scene running.
self._activity_data.start() self._activity_data.start()
@ -556,12 +575,12 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
def add_player(self, sessionplayer: ba.SessionPlayer) -> None: def add_player(self, sessionplayer: ba.SessionPlayer) -> None:
"""(internal)""" """(internal)"""
assert sessionplayer.team is not None assert sessionplayer.team is not None
sessionplayer.reset_input() sessionplayer.resetinput()
sessionteam = sessionplayer.team sessionteam = sessionplayer.team
assert sessionplayer in sessionteam.players assert sessionplayer in sessionteam.players
team = sessionteam.gameteam team = sessionteam.gameteam
assert team is not None assert team is not None
sessionplayer.set_activity(self) sessionplayer.setactivity(self)
with _ba.Context(self): with _ba.Context(self):
sessionplayer.gameplayer = player = self.create_player( sessionplayer.gameplayer = player = self.create_player(
sessionplayer) sessionplayer)
@ -581,10 +600,10 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
print_exception('Error in on_player_join for', self) print_exception('Error in on_player_join for', self)
def remove_player(self, sessionplayer: ba.SessionPlayer) -> None: def remove_player(self, sessionplayer: ba.SessionPlayer) -> None:
"""(internal)""" """Remove a player from the Activity while it is running.
# This should only be called on unexpired activities (internal)
# the player has been added to. """
assert not self.expired assert not self.expired
player: Any = sessionplayer.gameplayer player: Any = sessionplayer.gameplayer
@ -605,30 +624,29 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
# verify_object_death(player) # verify_object_death(player)
with _ba.Context(self): with _ba.Context(self):
# Make a decent attempt to persevere if user code breaks.
try: try:
self.on_player_leave(player) self.on_player_leave(player)
except Exception: except Exception:
print_exception(f'Error in on_player_leave for {self}') print_exception(f'Error in on_player_leave for {self}')
try: try:
# If they have an actor, kill it. player.leave()
if player.actor:
player.actor.handlemessage(
DieMessage(how=DeathType.LEFT_GAME))
player.actor = None
except Exception: except Exception:
print_exception( print_exception(f'Error on leave for {player} in {self}')
f'Error killing player actor on leave for {self}')
try: self._reset_session_player_for_no_activity(sessionplayer)
player.reset()
sessionplayer.reset() # Add the player to a list to keep it around for a while. This is
sessionplayer.set_node(None) # to discourage logic from firing on player object death, which
sessionplayer.set_activity(None) # may not happen until activity end if something is holding refs
except Exception: # to it.
print_exception(f'Error resetting player for {self}') self._delay_delete_players.append(player)
self._players_that_left.append(weakref.ref(player))
def add_team(self, sessionteam: ba.SessionTeam) -> None: def add_team(self, sessionteam: ba.SessionTeam) -> None:
"""(internal)""" """Add a team to the Activity
(internal)
"""
assert not self.expired assert not self.expired
with _ba.Context(self): with _ba.Context(self):
@ -641,25 +659,20 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
print_exception(f'Error in on_team_join for {self}') print_exception(f'Error in on_team_join for {self}')
def remove_team(self, sessionteam: ba.SessionTeam) -> None: def remove_team(self, sessionteam: ba.SessionTeam) -> None:
"""(internal)""" """Remove a team from a Running Activity
# This should only be called on unexpired activities the team has (internal)
# been added to. """
assert not self.expired assert not self.expired
assert sessionteam.gameteam is not None assert sessionteam.gameteam is not None
assert sessionteam.gameteam in self.teams
team = sessionteam.gameteam team: Any = sessionteam.gameteam
assert isinstance(team, self._teamtype) assert isinstance(team, self._teamtype)
assert team in self.teams assert team in self.teams
self.teams.remove(team) self.teams.remove(team)
assert team not in self.teams assert team not in self.teams
# This should allow our ba.Team instance to die. Complain
# if that doesn't happen.
# verify_object_death(team)
with _ba.Context(self): with _ba.Context(self):
# Make a decent attempt to persevere if user code breaks. # Make a decent attempt to persevere if user code breaks.
try: try:
@ -667,12 +680,42 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
except Exception: except Exception:
print_exception(f'Error in on_team_leave for {self}') print_exception(f'Error in on_team_leave for {self}')
try: try:
sessionteam.reset_gamedata() team.leave()
except Exception: except Exception:
print_exception(f'Error in reset_gamedata for {self}') print_exception(f'Error on leave for {team} in {self}')
sessionteam.gameteam = None sessionteam.gameteam = None
# Add the team to a list to keep it around for a while. This is
# to discourage logic from firing on team object death, which
# may not happen until activity end if something is holding refs
# to it.
self._delay_delete_teams.append(team)
self._teams_that_left.append(weakref.ref(team))
def _reset_session_player_for_no_activity(
self, sessionplayer: ba.SessionPlayer) -> None:
# Let's be extra-defensive here: killing a node/input-call/etc
# could trigger user-code resulting in errors, but we would still
# like to complete the reset if possible.
try:
sessionplayer.setnode(None)
except Exception:
print_exception(
f'Error resetting SessionPlayer node on {sessionplayer}'
f' for {self}')
try:
sessionplayer.resetinput()
except Exception:
print_exception(
f'Error resetting SessionPlayer input on {sessionplayer}'
f' for {self}')
# These should never fail I think...
sessionplayer.setactivity(None)
sessionplayer.gameplayer = None
def _setup_player_and_team_types(self) -> None: def _setup_player_and_team_types(self) -> None:
"""Pull player and team types from our typing.Generic params.""" """Pull player and team types from our typing.Generic params."""
@ -682,7 +725,7 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
# to no generic params being passed) we automatically use the # to no generic params being passed) we automatically use the
# base class types, but also warn the user since this will mean # base class types, but also warn the user since this will mean
# less type safety for that class. (its better to pass the base # less type safety for that class. (its better to pass the base
# types explicitly vs. having them be Any) # player/team types explicitly vs. having them be Any)
if not TYPE_CHECKING: if not TYPE_CHECKING:
self._playertype = type(self).__orig_bases__[-1].__args__[0] self._playertype = type(self).__orig_bases__[-1].__args__[0]
if not isinstance(self._playertype, type): if not isinstance(self._playertype, type):
@ -749,9 +792,31 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
try: try:
self.on_expire() self.on_expire()
except Exception: except Exception:
print_exception(f'Error in Activity on_expire() for {self}') print_exception(f'Error in Activity on_expire() for {self}.')
# Send expire notices to all remaining actors. try:
self._gamedata = None
except Exception:
print_exception(f'Error clearing gamedata for {self}.')
# Don't want to be holding any delay-delete refs at this point.
self._prune_delay_deletes()
self._expire_actors()
self._expire_players()
self._expire_teams()
# This will kill all low level stuff: Timers, Nodes, etc., which
# should clear up any remaining refs to our Activity and allow us
# to die peacefully.
try:
self._activity_data.expire()
except Exception:
print_exception(
'Error during ba.Activity._expire() destroying data:')
def _expire_actors(self) -> None:
# Expire all Actors.
for actor_ref in self._actor_weak_refs: for actor_ref in self._actor_weak_refs:
actor = actor_ref() actor = actor_ref()
if actor is not None: if actor is not None:
@ -760,35 +825,58 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
actor.on_expire() actor.on_expire()
except Exception: except Exception:
print_exception(f'Error in Actor.on_expire()' print_exception(f'Error in Actor.on_expire()'
f' for {actor_ref()}') f' for {actor_ref()}.')
def _expire_players(self) -> None:
# Issue warnings for any players that left the game but don't
# get freed soon.
for ex_player in (p() for p in self._players_that_left):
if ex_player is not None:
verify_object_death(ex_player)
# Reset all Players.
# (releases any attached actors, clears game-data, etc)
for player in self.players: for player in self.players:
# This should allow our ba.Player instance to be freed.
# Complain if that doesn't happen.
verify_object_death(player)
try: try:
# This should allow our ba.Player instance to die. player.expire()
# Complain if that doesn't happen.
# verify_object_death(player)
sessionplayer = player.sessionplayer
player.reset()
sessionplayer.set_node(None)
sessionplayer.set_activity(None)
sessionplayer.gameplayer = None
sessionplayer.reset()
except Exception: except Exception:
print_exception(f'Error resetting Player {player}') print_exception(f'Error expiring {player}')
# Reset the SessionPlayer to a not-in-an-activity state.
try:
sessionplayer = player.sessionplayer
self._reset_session_player_for_no_activity(sessionplayer)
except SessionPlayerNotFoundError:
# Conceivably, someone could have held on to a Player object
# until now whos underlying SessionPlayer left long ago...
pass
except Exception:
print_exception(f'Error expiring {player}')
def _expire_teams(self) -> None:
# Issue warnings for any teams that left the game but don't
# get freed soon.
for ex_team in (p() for p in self._teams_that_left):
if ex_team is not None:
verify_object_death(ex_team)
# Ditto with Teams.
for team in self.teams: for team in self.teams:
# This should allow our ba.Team instance to die.
# Complain if that doesn't happen.
verify_object_death(team)
try:
team.expire()
except Exception:
print_exception(f'Error expiring {team}')
try: try:
sessionteam = team.sessionteam sessionteam = team.sessionteam
# This should allow our ba.Team instance to die.
# Complain if that doesn't happen.
# verify_object_death(sessionteam.gameteam)
sessionteam.gameteam = None sessionteam.gameteam = None
sessionteam.reset_gamedata()
except SessionTeamNotFoundError: except SessionTeamNotFoundError:
# It is expected that Team objects may last longer than # It is expected that Team objects may last longer than
# the SessionTeam they came from (game objects may hold # the SessionTeam they came from (game objects may hold
@ -796,17 +884,19 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
# player/team has left the game) # player/team has left the game)
pass pass
except Exception: except Exception:
print_exception(f'Error resetting Team {team}') print_exception(f'Error expiring Team {team}')
# Regardless of what happened here, we want to destroy our data, as def _prune_delay_deletes(self) -> None:
# our activity might not go down if we don't. This will kill all self._delay_delete_players.clear()
# Timers, Nodes, etc, which should clear up any remaining refs to our self._delay_delete_teams.clear()
# Actors and Activity and allow us to die peacefully.
try: # Clear out any dead weak-refs.
self._activity_data.destroy() self._teams_that_left = [
except Exception: t for t in self._teams_that_left if t() is not None
print_exception( ]
'Error during ba.Activity._expire() destroying data:') self._players_that_left = [
p for p in self._players_that_left if p() is not None
]
def _prune_dead_actors(self) -> None: def _prune_dead_actors(self) -> None:
self._last_prune_dead_actors_time = _ba.time() self._last_prune_dead_actors_time = _ba.time()

View File

@ -229,6 +229,6 @@ class ScoreScreenActivity(Activity[Player, Team]):
# Just to be extra careful, don't assign if we're transitioning out. # Just to be extra careful, don't assign if we're transitioning out.
# (though theoretically that would be ok). # (though theoretically that would be ok).
if not self.is_transitioning_out() and player: if not self.is_transitioning_out() and player:
player.assign_input_call( player.assigninput(
('jumpPress', 'punchPress', 'bombPress', 'pickUpPress'), ('jumpPress', 'punchPress', 'bombPress', 'pickUpPress'),
self._player_press) self._player_press)

View File

@ -53,7 +53,7 @@ def run_cpu_benchmark() -> None:
cfg['Graphics Quality'] = 'Low' cfg['Graphics Quality'] = 'Low'
cfg.apply() cfg.apply()
self.benchmark_type = 'cpu' self.benchmark_type = 'cpu'
self.set_activity(_ba.new_activity(tutorial.TutorialActivity)) self.setactivity(_ba.new_activity(tutorial.TutorialActivity))
def __del__(self) -> None: def __del__(self) -> None:
@ -91,7 +91,7 @@ def run_stress_test(playlist_type: str = 'Random',
Call(_ba.screenmessage, Call(_ba.screenmessage,
('stats will be written to ' + ('stats will be written to ' +
_modutils.get_human_readable_user_scripts_path() + _modutils.get_human_readable_user_scripts_path() +
'/stressTestStats.csv')), '/stress_test_stats.csv')),
timetype=TimeType.REAL) timetype=TimeType.REAL)

View File

@ -98,7 +98,7 @@ class CoopSession(Session):
self._custom_menu_ui: List[Dict[str, Any]] = [] self._custom_menu_ui: List[Dict[str, Any]] = []
# Start our joining screen. # Start our joining screen.
self.set_activity(_ba.new_activity(CoopJoinActivity)) self.setactivity(_ba.new_activity(CoopJoinActivity))
self._next_game_instance: Optional[ba.GameActivity] = None self._next_game_instance: Optional[ba.GameActivity] = None
self._next_game_level_name: Optional[str] = None self._next_game_level_name: Optional[str] = None
@ -298,7 +298,7 @@ class CoopSession(Session):
and not app.kiosk_mode): and not app.kiosk_mode):
if self._tutorial_activity is None: if self._tutorial_activity is None:
raise RuntimeError('Tutorial not preloaded properly.') raise RuntimeError('Tutorial not preloaded properly.')
self.set_activity(self._tutorial_activity) self.setactivity(self._tutorial_activity)
self._tutorial_activity = None self._tutorial_activity = None
self._ran_tutorial_activity = True self._ran_tutorial_activity = True
self._custom_menu_ui = [] self._custom_menu_ui = []
@ -313,10 +313,10 @@ class CoopSession(Session):
# Skip players that are still choosing a team. # Skip players that are still choosing a team.
if player.in_game: if player.in_game:
self.stats.register_player(player) self.stats.register_player(player)
self.stats.set_activity(next_game) self.stats.setactivity(next_game)
# Now flip the current activity. # Now flip the current activity.
self.set_activity(next_game) self.setactivity(next_game)
if not app.kiosk_mode: if not app.kiosk_mode:
if self.tournament_id is not None: if self.tournament_id is not None:
@ -338,7 +338,7 @@ class CoopSession(Session):
# If we were in a tutorial, just pop a transition to get to the # If we were in a tutorial, just pop a transition to get to the
# actual round. # actual round.
elif isinstance(activity, TutorialActivity): elif isinstance(activity, TutorialActivity):
self.set_activity(_ba.new_activity(TransitionActivity)) self.setactivity(_ba.new_activity(TransitionActivity))
else: else:
player_info: List[ba.PlayerInfo] player_info: List[ba.PlayerInfo]
@ -392,9 +392,9 @@ class CoopSession(Session):
if outcome == 'restart': if outcome == 'restart':
# This will pop up back in the same round. # This will pop up back in the same round.
self.set_activity(_ba.new_activity(TransitionActivity)) self.setactivity(_ba.new_activity(TransitionActivity))
else: else:
self.set_activity( self.setactivity(
_ba.new_activity( _ba.new_activity(
CoopScoreScreen, { CoopScoreScreen, {
'player_info': player_info, 'player_info': player_info,

View File

@ -56,7 +56,7 @@ class DualTeamSession(MultiTeamSession):
# If everyone has the same score, call it a draw. # If everyone has the same score, call it a draw.
if len(winners) < 2: if len(winners) < 2:
self.set_activity(_ba.new_activity(DrawScoreScreenActivity)) self.setactivity(_ba.new_activity(DrawScoreScreenActivity))
else: else:
winner = winners[0].teams[0] winner = winners[0].teams[0]
winner.sessiondata['score'] += 1 winner.sessiondata['score'] += 1
@ -64,10 +64,10 @@ class DualTeamSession(MultiTeamSession):
# If a team has won, show final victory screen. # If a team has won, show final victory screen.
if winner.sessiondata['score'] >= (self._series_length - if winner.sessiondata['score'] >= (self._series_length -
1) / 2 + 1: 1) / 2 + 1:
self.set_activity( self.setactivity(
_ba.new_activity(TeamSeriesVictoryScoreScreenActivity, _ba.new_activity(TeamSeriesVictoryScoreScreenActivity,
{'winner': winner})) {'winner': winner}))
else: else:
self.set_activity( self.setactivity(
_ba.new_activity(TeamVictoryScoreScreenActivity, _ba.new_activity(TeamVictoryScoreScreenActivity,
{'winner': winner})) {'winner': winner}))

View File

@ -81,7 +81,7 @@ class FreeForAllSession(MultiTeamSession):
# If there's multiple players and everyone has the same score, # If there's multiple players and everyone has the same score,
# call it a draw. # call it a draw.
if len(self.players) > 1 and len(winners) < 2: if len(self.players) > 1 and len(winners) < 2:
self.set_activity( self.setactivity(
_ba.new_activity(DrawScoreScreenActivity, _ba.new_activity(DrawScoreScreenActivity,
{'results': results})) {'results': results}))
else: else:
@ -105,10 +105,10 @@ class FreeForAllSession(MultiTeamSession):
or (len(series_winners) > 1 or (len(series_winners) > 1
and series_winners[0].sessiondata['score'] != and series_winners[0].sessiondata['score'] !=
series_winners[1].sessiondata['score'])): series_winners[1].sessiondata['score'])):
self.set_activity( self.setactivity(
_ba.new_activity(TeamSeriesVictoryScoreScreenActivity, _ba.new_activity(TeamSeriesVictoryScoreScreenActivity,
{'winner': series_winners[0]})) {'winner': series_winners[0]}))
else: else:
self.set_activity( self.setactivity(
_ba.new_activity(FreeForAllVictoryScoreScreenActivity, _ba.new_activity(FreeForAllVictoryScoreScreenActivity,
{'results': results})) {'results': results}))

View File

@ -55,7 +55,7 @@ class JoinInfo:
self._press_to_bomb: Union[str, ba.Lstr] = _ba.charstr( self._press_to_bomb: Union[str, ba.Lstr] = _ba.charstr(
SpecialChar.RIGHT_BUTTON) SpecialChar.RIGHT_BUTTON)
self._joinmsg = Lstr(resource='pressAnyButtonToJoinText') self._joinmsg = Lstr(resource='pressAnyButtonToJoinText')
can_switch_teams = (len(lobby.teams) > 1) can_switch_teams = (len(lobby.sessionteams) > 1)
# If we have a keyboard, grab keys for punch and pickup. # If we have a keyboard, grab keys for punch and pickup.
# FIXME: This of course is only correct on the local device; # FIXME: This of course is only correct on the local device;
@ -151,7 +151,7 @@ class Chooser:
if self._text_node: if self._text_node:
self._text_node.delete() self._text_node.delete()
def __init__(self, vpos: float, player: _ba.SessionPlayer, def __init__(self, vpos: float, sessionplayer: _ba.SessionPlayer,
lobby: 'Lobby') -> None: lobby: 'Lobby') -> None:
self._deek_sound = _ba.getsound('deek') self._deek_sound = _ba.getsound('deek')
self._click_sound = _ba.getsound('click01') self._click_sound = _ba.getsound('click01')
@ -161,7 +161,7 @@ class Chooser:
self._mask_texture = _ba.gettexture('characterIconMask') self._mask_texture = _ba.gettexture('characterIconMask')
self._vpos = vpos self._vpos = vpos
self._lobby = weakref.ref(lobby) self._lobby = weakref.ref(lobby)
self._player = player self._sessionplayer = sessionplayer
self._inited = False self._inited = False
self._dead = False self._dead = False
self._text_node: Optional[ba.Node] = None self._text_node: Optional[ba.Node] = None
@ -174,13 +174,8 @@ class Chooser:
app = _ba.app app = _ba.app
# try: # Load available player profiles either from the local config or
# print(player.inputdevice) # from the remote device.
# except Exception as exc:
# print('GOT EXC', type(exc))
# Load available profiles either from the local config or from the
# remote device.
self.reload_profiles() self.reload_profiles()
# Note: this is just our local index out of available teams; *not* # Note: this is just our local index out of available teams; *not*
@ -199,7 +194,7 @@ class Chooser:
# list. # list.
char_index_offset = app.lobby_random_char_index_offset char_index_offset = app.lobby_random_char_index_offset
self._random_character_index = ( self._random_character_index = (
(player.inputdevice.id + char_index_offset) % (sessionplayer.inputdevice.id + char_index_offset) %
len(self._character_names)) len(self._character_names))
# Attempt to set an initial profile based on what was used previously # Attempt to set an initial profile based on what was used previously
@ -231,8 +226,8 @@ class Chooser:
animate_array(self.icon, 'scale', 2, {0: (0, 0), 0.1: (45, 45)}) animate_array(self.icon, 'scale', 2, {0: (0, 0), 0.1: (45, 45)})
# Set our initial name to '<choosing player>' in case anyone asks. # Set our initial name to '<choosing player>' in case anyone asks.
self._player.set_name(Lstr(resource='choosingPlayerText').evaluate(), self._sessionplayer.setname(
real=False) Lstr(resource='choosingPlayerText').evaluate(), real=False)
# Init these to our rando but they should get switched to the # Init these to our rando but they should get switched to the
# selected profile (if any) right after. # selected profile (if any) right after.
@ -249,7 +244,7 @@ class Chooser:
def _select_initial_profile(self) -> int: def _select_initial_profile(self) -> int:
app = _ba.app app = _ba.app
profilenames = self._profilenames profilenames = self._profilenames
inputdevice = self._player.inputdevice inputdevice = self._sessionplayer.inputdevice
# If we've got a set profile name for this device, work backwards # If we've got a set profile name for this device, work backwards
# from that to get our index. # from that to get our index.
@ -307,9 +302,9 @@ class Chooser:
return profilenames.index('_random') return profilenames.index('_random')
@property @property
def player(self) -> ba.SessionPlayer: def sessionplayer(self) -> ba.SessionPlayer:
"""The ba.Player associated with this chooser.""" """The ba.SessionPlayer associated with this chooser."""
return self._player return self._sessionplayer
@property @property
def ready(self) -> bool: def ready(self) -> bool:
@ -324,9 +319,10 @@ class Chooser:
"""(internal)""" """(internal)"""
self._dead = val self._dead = val
def get_team(self) -> ba.SessionTeam: @property
"""Return this chooser's selected ba.Team.""" def sessionteam(self) -> ba.SessionTeam:
return self.lobby.teams[self._selected_team_index] """Return this chooser's currently selected ba.SessionTeam."""
return self.lobby.sessionteams[self._selected_team_index]
@property @property
def lobby(self) -> ba.Lobby: def lobby(self) -> ba.Lobby:
@ -376,7 +372,7 @@ class Chooser:
# Re-construct our profile index and other stuff since the profile # Re-construct our profile index and other stuff since the profile
# list might have changed. # list might have changed.
input_device = self._player.inputdevice input_device = self._sessionplayer.inputdevice
is_remote = input_device.is_remote_client is_remote = input_device.is_remote_client
is_test_input = input_device.name.startswith('TestInput') is_test_input = input_device.name.startswith('TestInput')
@ -434,10 +430,10 @@ class Chooser:
assert self._text_node assert self._text_node
spacing = 350 spacing = 350
teams = self.lobby.teams sessionteams = self.lobby.sessionteams
offs = (spacing * -0.5 * len(teams) + offs = (spacing * -0.5 * len(sessionteams) +
spacing * self._selected_team_index + 250) spacing * self._selected_team_index + 250)
if len(teams) > 1: if len(sessionteams) > 1:
offs -= 35 offs -= 35
animate_array(self._text_node, 'position', 2, { animate_array(self._text_node, 'position', 2, {
0: self._text_node.position, 0: self._text_node.position,
@ -460,14 +456,15 @@ class Chooser:
clamp = False clamp = False
if name == '_random': if name == '_random':
try: try:
name = (self._player.inputdevice.get_default_player_name()) name = (
self._sessionplayer.inputdevice.get_default_player_name())
except Exception: except Exception:
print_exception('Error getting _random chooser name.') print_exception('Error getting _random chooser name.')
name = 'Invalid' name = 'Invalid'
clamp = not full clamp = not full
elif name == '__account__': elif name == '__account__':
try: try:
name = self._player.inputdevice.get_account_name(full) name = self._sessionplayer.inputdevice.get_account_name(full)
except Exception: except Exception:
print_exception('Error getting account name for chooser.') print_exception('Error getting account name for chooser.')
name = 'Invalid' name = 'Invalid'
@ -513,41 +510,41 @@ class Chooser:
# Give their input-device UI ownership too # Give their input-device UI ownership too
# (prevent someone else from snatching it in crowded games) # (prevent someone else from snatching it in crowded games)
_ba.set_ui_input_device(self._player.inputdevice) _ba.set_ui_input_device(self._sessionplayer.inputdevice)
return return
if not ready: if not ready:
self._player.assign_input_call( self._sessionplayer.assigninput(
'leftPress', Call(self.handlemessage, 'leftPress', Call(self.handlemessage,
ChangeMessage('team', -1))) ChangeMessage('team', -1)))
self._player.assign_input_call( self._sessionplayer.assigninput(
'rightPress', Call(self.handlemessage, 'rightPress', Call(self.handlemessage,
ChangeMessage('team', 1))) ChangeMessage('team', 1)))
self._player.assign_input_call( self._sessionplayer.assigninput(
'bombPress', 'bombPress',
Call(self.handlemessage, ChangeMessage('character', 1))) Call(self.handlemessage, ChangeMessage('character', 1)))
self._player.assign_input_call( self._sessionplayer.assigninput(
'upPress', 'upPress',
Call(self.handlemessage, ChangeMessage('profileindex', -1))) Call(self.handlemessage, ChangeMessage('profileindex', -1)))
self._player.assign_input_call( self._sessionplayer.assigninput(
'downPress', 'downPress',
Call(self.handlemessage, ChangeMessage('profileindex', 1))) Call(self.handlemessage, ChangeMessage('profileindex', 1)))
self._player.assign_input_call( self._sessionplayer.assigninput(
('jumpPress', 'pickUpPress', 'punchPress'), ('jumpPress', 'pickUpPress', 'punchPress'),
Call(self.handlemessage, ChangeMessage('ready', 1))) Call(self.handlemessage, ChangeMessage('ready', 1)))
self._ready = False self._ready = False
self._update_text() self._update_text()
self._player.set_name('untitled', real=False) self._sessionplayer.setname('untitled', real=False)
else: else:
self._player.assign_input_call( self._sessionplayer.assigninput(
('leftPress', 'rightPress', 'upPress', 'downPress', ('leftPress', 'rightPress', 'upPress', 'downPress',
'jumpPress', 'bombPress', 'pickUpPress'), self._do_nothing) 'jumpPress', 'bombPress', 'pickUpPress'), self._do_nothing)
self._player.assign_input_call( self._sessionplayer.assigninput(
('jumpPress', 'bombPress', 'pickUpPress', 'punchPress'), ('jumpPress', 'bombPress', 'pickUpPress', 'punchPress'),
Call(self.handlemessage, ChangeMessage('ready', 0))) Call(self.handlemessage, ChangeMessage('ready', 0)))
# Store the last profile picked by this input for reuse. # Store the last profile picked by this input for reuse.
input_device = self._player.inputdevice input_device = self._sessionplayer.inputdevice
name = input_device.name name = input_device.name
unique_id = input_device.unique_identifier unique_id = input_device.unique_identifier
device_profiles = _ba.app.config.setdefault( device_profiles = _ba.app.config.setdefault(
@ -569,9 +566,9 @@ class Chooser:
_ba.app.config.commit() _ba.app.config.commit()
# Set this player's short and full name. # Set this player's short and full name.
self._player.set_name(self._getname(), self._sessionplayer.setname(self._getname(),
self._getname(full=True), self._getname(full=True),
real=True) real=True)
self._ready = True self._ready = True
self._update_text() self._update_text()
@ -586,25 +583,26 @@ class Chooser:
if not self._ready: if not self._ready:
if _ba.app.config.get('Auto Balance Teams', False): if _ba.app.config.get('Auto Balance Teams', False):
lobby = self.lobby lobby = self.lobby
teams = lobby.teams sessionteams = lobby.sessionteams
if len(teams) > 1: if len(sessionteams) > 1:
# First, calc how many players are on each team # First, calc how many players are on each team
# ..we need to count both active players and # ..we need to count both active players and
# choosers that have been marked as ready. # choosers that have been marked as ready.
team_player_counts = {} team_player_counts = {}
for team in teams: for sessionteam in sessionteams:
team_player_counts[team.id] = len(team.players) team_player_counts[sessionteam.id] = len(
sessionteam.players)
for chooser in lobby.choosers: for chooser in lobby.choosers:
if chooser.ready: if chooser.ready:
team_player_counts[chooser.get_team().id] += 1 team_player_counts[chooser.sessionteam.id] += 1
largest_team_size = max(team_player_counts.values()) largest_team_size = max(team_player_counts.values())
smallest_team_size = (min(team_player_counts.values())) smallest_team_size = (min(team_player_counts.values()))
# Force switch if we're on the biggest team # Force switch if we're on the biggest sessionteam
# and there's a smaller one available. # and there's a smaller one available.
if (largest_team_size != smallest_team_size if (largest_team_size != smallest_team_size
and team_player_counts[self.get_team().id] >= and team_player_counts[self.sessionteam.id] >=
largest_team_size): largest_team_size):
force_team_switch = True force_team_switch = True
@ -623,7 +621,8 @@ class Chooser:
if now - self._last_change[0] < QUICK_CHANGE_INTERVAL: if now - self._last_change[0] < QUICK_CHANGE_INTERVAL:
count += 1 count += 1
if count > MAX_QUICK_CHANGE_COUNT: if count > MAX_QUICK_CHANGE_COUNT:
_ba.disconnect_client(self._player.inputdevice.client_id) _ba.disconnect_client(
self._sessionplayer.inputdevice.client_id)
elif now - self._last_change[0] > QUICK_CHANGE_RESET_INTERVAL: elif now - self._last_change[0] > QUICK_CHANGE_RESET_INTERVAL:
count = 0 count = 0
self._last_change = (now, count) self._last_change = (now, count)
@ -644,11 +643,12 @@ class Chooser:
return return
if msg.what == 'team': if msg.what == 'team':
teams = self.lobby.teams sessionteams = self.lobby.sessionteams
if len(teams) > 1: if len(sessionteams) > 1:
_ba.playsound(self._swish_sound) _ba.playsound(self._swish_sound)
self._selected_team_index = ( self._selected_team_index = (
(self._selected_team_index + msg.value) % len(teams)) (self._selected_team_index + msg.value) %
len(sessionteams))
self._update_text() self._update_text()
self.update_position() self.update_position()
self._update_icon() self._update_icon()
@ -685,14 +685,14 @@ class Chooser:
# Once we're ready, we've saved the name, so lets ask the system # Once we're ready, we've saved the name, so lets ask the system
# for it so we get appended numbers and stuff. # for it so we get appended numbers and stuff.
text = Lstr(value=self._player.getname(full=True)) text = Lstr(value=self._sessionplayer.getname(full=True))
text = Lstr(value='${A} (${B})', text = Lstr(value='${A} (${B})',
subs=[('${A}', text), subs=[('${A}', text),
('${B}', Lstr(resource='readyText'))]) ('${B}', Lstr(resource='readyText'))])
else: else:
text = Lstr(value=self._getname(full=True)) text = Lstr(value=self._getname(full=True))
can_switch_teams = len(self.lobby.teams) > 1 can_switch_teams = len(self.lobby.sessionteams) > 1
# Flash as we're coming in. # Flash as we're coming in.
fin_color = _ba.safecolor(self.get_color()) + (1, ) fin_color = _ba.safecolor(self.get_color()) + (1, )
@ -719,7 +719,7 @@ class Chooser:
"""Return the currently selected color.""" """Return the currently selected color."""
val: Sequence[float] val: Sequence[float]
if self.lobby.use_team_colors: if self.lobby.use_team_colors:
val = self.lobby.teams[self._selected_team_index].color val = self.lobby.sessionteams[self._selected_team_index].color
else: else:
val = self._color val = self._color
if len(val) != 3: if len(val) != 3:
@ -736,17 +736,17 @@ class Chooser:
# isn't too close to any other team's color. # isn't too close to any other team's color.
highlight = list(self._highlight) highlight = list(self._highlight)
if self.lobby.use_team_colors: if self.lobby.use_team_colors:
for i, team in enumerate(self.lobby.teams): for i, sessionteam in enumerate(self.lobby.sessionteams):
if i != self._selected_team_index: if i != self._selected_team_index:
# Find the dominant component of this team's color # Find the dominant component of this sessionteam's color
# and adjust ours so that the component is # and adjust ours so that the component is
# not super-dominant. # not super-dominant.
max_val = 0.0 max_val = 0.0
max_index = 0 max_index = 0
for j in range(3): for j in range(3):
if team.color[j] > max_val: if sessionteam.color[j] > max_val:
max_val = team.color[j] max_val = sessionteam.color[j]
max_index = j max_index = j
that_color_for_us = highlight[max_index] that_color_for_us = highlight[max_index]
our_second_biggest = max(highlight[(max_index + 1) % 3], our_second_biggest = max(highlight[(max_index + 1) % 3],
@ -760,7 +760,7 @@ class Chooser:
def getplayer(self) -> ba.SessionPlayer: def getplayer(self) -> ba.SessionPlayer:
"""Return the player associated with this chooser.""" """Return the player associated with this chooser."""
return self._player return self._sessionplayer
def _update_icon(self) -> None: def _update_icon(self) -> None:
if self._profilenames[self._profileindex] == '_edit': if self._profilenames[self._profileindex] == '_edit':
@ -791,7 +791,7 @@ class Chooser:
clr = self.get_color() clr = self.get_color()
clr2 = self.get_highlight() clr2 = self.get_highlight()
can_switch_teams = len(self.lobby.teams) > 1 can_switch_teams = len(self.lobby.sessionteams) > 1
# If we're initing, flash. # If we're initing, flash.
if not self._inited: if not self._inited:
@ -812,7 +812,7 @@ class Chooser:
self.icon.tint2_color = clr2 self.icon.tint2_color = clr2
# Store the icon info the the player. # Store the icon info the the player.
self._player.set_icon_info(tex_name, tint_tex_name, clr, clr2) self._sessionplayer.set_icon_info(tex_name, tint_tex_name, clr, clr2)
class Lobby: class Lobby:
@ -823,13 +823,13 @@ class Lobby:
def __del__(self) -> None: def __del__(self) -> None:
# Reset any players that still have a chooser in us # Reset any players that still have a chooser in us.
# (should allow the choosers to die). # (should allow the choosers to die).
players = [ sessionplayers = [
chooser.player for chooser in self.choosers if chooser.player c.sessionplayer for c in self.choosers if c.sessionplayer
] ]
for player in players: for sessionplayer in sessionplayers:
player.reset() sessionplayer.resetinput()
def __init__(self) -> None: def __init__(self) -> None:
from ba._team import SessionTeam from ba._team import SessionTeam
@ -837,10 +837,10 @@ class Lobby:
session = _ba.getsession() session = _ba.getsession()
self._use_team_colors = session.use_team_colors self._use_team_colors = session.use_team_colors
if session.use_teams: if session.use_teams:
self._teams = [weakref.ref(team) for team in session.teams] self._sessionteams = [weakref.ref(team) for team in session.teams]
else: else:
self._dummy_teams = SessionTeam() self._dummy_teams = SessionTeam()
self._teams = [weakref.ref(self._dummy_teams)] self._sessionteams = [weakref.ref(self._dummy_teams)]
v_offset = (-150 if isinstance(session, CoopSession) else -50) v_offset = (-150 if isinstance(session, CoopSession) else -50)
self.choosers: List[Chooser] = [] self.choosers: List[Chooser] = []
self.base_v_offset = v_offset self.base_v_offset = v_offset
@ -868,10 +868,10 @@ class Lobby:
return self._use_team_colors return self._use_team_colors
@property @property
def teams(self) -> List[ba.SessionTeam]: def sessionteams(self) -> List[ba.SessionTeam]:
"""Teams available in this lobby.""" """ba.SessionTeams available in this lobby."""
allteams = [] allteams = []
for tref in self._teams: for tref in self._sessionteams:
team = tref() team = tref()
assert team is not None assert team is not None
allteams.append(team) allteams.append(team)
@ -921,11 +921,12 @@ class Lobby:
"""Return whether all choosers are marked ready.""" """Return whether all choosers are marked ready."""
return all(chooser.ready for chooser in self.choosers) return all(chooser.ready for chooser in self.choosers)
def add_chooser(self, player: ba.SessionPlayer) -> None: def add_chooser(self, sessionplayer: ba.SessionPlayer) -> None:
"""Add a chooser to the lobby for the provided player.""" """Add a chooser to the lobby for the provided player."""
self.choosers.append( self.choosers.append(
Chooser(vpos=self._vpos, player=player, lobby=self)) Chooser(vpos=self._vpos, sessionplayer=sessionplayer, lobby=self))
self._next_add_team = (self._next_add_team + 1) % len(self._teams) self._next_add_team = (self._next_add_team + 1) % len(
self._sessionteams)
self._vpos -= 48 self._vpos -= 48
def remove_chooser(self, player: ba.SessionPlayer) -> None: def remove_chooser(self, player: ba.SessionPlayer) -> None:
@ -964,6 +965,6 @@ class Lobby:
# Copy the list; it can change under us otherwise. # Copy the list; it can change under us otherwise.
for chooser in list(self.choosers): for chooser in list(self.choosers):
if chooser.player: if chooser.sessionplayer:
chooser.player.remove_from_game() chooser.sessionplayer.remove_from_game()
self.remove_all_choosers() self.remove_all_choosers()

View File

@ -133,7 +133,7 @@ class MultiTeamSession(Session):
self._instantiate_next_game() self._instantiate_next_game()
# Start in our custom join screen. # Start in our custom join screen.
self.set_activity(_ba.new_activity(MultiTeamJoinActivity)) self.setactivity(_ba.new_activity(MultiTeamJoinActivity))
def get_ffa_series_length(self) -> int: def get_ffa_series_length(self) -> int:
"""Return free-for-all series length.""" """Return free-for-all series length."""
@ -179,14 +179,14 @@ class MultiTeamSession(Session):
# If we have a tutorial to show, that's the first thing we do no # If we have a tutorial to show, that's the first thing we do no
# matter what. # matter what.
if self._tutorial_activity_instance is not None: if self._tutorial_activity_instance is not None:
self.set_activity(self._tutorial_activity_instance) self.setactivity(self._tutorial_activity_instance)
self._tutorial_activity_instance = None self._tutorial_activity_instance = None
# If we're leaving the tutorial activity, pop a transition activity # If we're leaving the tutorial activity, pop a transition activity
# to transition us into a round gracefully (otherwise we'd snap from # to transition us into a round gracefully (otherwise we'd snap from
# one terrain to another instantly). # one terrain to another instantly).
elif isinstance(activity, TutorialActivity): elif isinstance(activity, TutorialActivity):
self.set_activity( self.setactivity(
_ba.new_activity(_activitytypes.TransitionActivity)) _ba.new_activity(_activitytypes.TransitionActivity))
# If we're in a between-round activity or a restart-activity, hop # If we're in a between-round activity or a restart-activity, hop
@ -226,10 +226,10 @@ class MultiTeamSession(Session):
has_team = False has_team = False
if has_team: if has_team:
self.stats.register_player(player) self.stats.register_player(player)
self.stats.set_activity(next_game) self.stats.setactivity(next_game)
# Now flip the current activity. # Now flip the current activity.
self.set_activity(next_game) self.setactivity(next_game)
# If we're leaving a round, go to the score screen. # If we're leaving a round, go to the score screen.
else: else:

View File

@ -26,6 +26,8 @@ from dataclasses import dataclass
from typing import TYPE_CHECKING, TypeVar, Generic from typing import TYPE_CHECKING, TypeVar, Generic
import _ba import _ba
from ba._error import SessionPlayerNotFoundError, print_exception
from ba._messages import DeathType, DieMessage
if TYPE_CHECKING: if TYPE_CHECKING:
from typing import (Type, Optional, Sequence, Dict, Any, Union, Tuple, from typing import (Type, Optional, Sequence, Dict, Any, Union, Tuple,
@ -52,7 +54,7 @@ class StandLocation:
Category: Gameplay Classes Category: Gameplay Classes
""" """
position: _ba.Vec3 position: ba.Vec3
angle: Optional[float] = None angle: Optional[float] = None
@ -61,27 +63,35 @@ class Player(Generic[TeamType]):
Category: Gameplay Classes Category: Gameplay Classes
These correspond to ba.SessionPlayer objects, but are created per activity These correspond to ba.SessionPlayer objects, but are associated with a
so that the activity can use its own custom player subclass. single ba.Activity instance. This allows activities to specify their
own custom ba.Player types.
Attributes:
actor
The ba.Actor associated with the player.
""" """
# Defining these types at the class level instead of in __init__ so # These are instance attrs but we define them at the type level so
# that types are introspectable (these are still instance attrs). # their type annotations are introspectable (for docs generation).
team: TeamType
character: str character: str
actor: Optional[ba.Actor] actor: Optional[ba.Actor]
color: Sequence[float] color: Sequence[float]
highlight: Sequence[float] highlight: Sequence[float]
_team: TeamType
_sessionplayer: ba.SessionPlayer _sessionplayer: ba.SessionPlayer
_nodeactor: Optional[ba.NodeActor] _nodeactor: Optional[ba.NodeActor]
_expired: bool
# Should aim to kill this eventually (at least gamedata). _postinited: bool
# Game-specific data can be tacked on to the per-game player class. sessiondata: dict
sessiondata: Dict _gamedata: dict
gamedata: Dict
# NOTE: avoiding having any __init__() here since it seems to not # NOTE: avoiding having any __init__() here since it seems to not
# get called by default if a dataclass inherits from us. # get called by default if a dataclass inherits from us.
# This also lets us keep trivial player classes cleaner by skipping
# the super().__init__() line.
def postinit(self, sessionplayer: ba.SessionPlayer) -> None: def postinit(self, sessionplayer: ba.SessionPlayer) -> None:
"""Wire up a newly created player. """Wire up a newly created player.
@ -108,23 +118,82 @@ class Player(Generic[TeamType]):
self.character = sessionplayer.character self.character = sessionplayer.character
self.color = sessionplayer.color self.color = sessionplayer.color
self.highlight = sessionplayer.highlight self.highlight = sessionplayer.highlight
self.team = sessionplayer.team.gameteam # type: ignore self._team = sessionplayer.team.gameteam # type: ignore
assert self.team is not None assert self._team is not None
self.sessiondata = sessionplayer.sessiondata self.sessiondata = sessionplayer.sessiondata
self.gamedata = sessionplayer.gamedata self._gamedata = {}
self._expired = False
# Create our player node in the current activity. self._postinited = True
# Note: do we want to save a few cycles here by managing our player
# node manually instead of wrapping it in a NodeActor?
node = _ba.newnode('player', attrs={'playerID': sessionplayer.id}) node = _ba.newnode('player', attrs={'playerID': sessionplayer.id})
self._nodeactor = NodeActor(node) self._nodeactor = NodeActor(node)
sessionplayer.set_node(node) sessionplayer.setnode(node)
def leave(self) -> None:
"""Called when the Player leaves a running game.
(internal)
"""
assert self._postinited
assert not self._expired
try:
# If they still have an actor, kill it.
if self.actor:
self.actor.handlemessage(DieMessage(how=DeathType.LEFT_GAME))
self.actor = None
except Exception:
print_exception(f'Error killing actor on leave for {self}')
self._nodeactor = None
del self._team
del self._gamedata
def expire(self) -> None:
"""Called when the Player is expiring (when its Activity does so).
(internal)
"""
assert self._postinited
assert not self._expired
self._expired = True
try:
self.on_expire()
except Exception:
print_exception(f'Error in on_expire for {self}.')
def reset(self) -> None:
"""(internal)"""
self._nodeactor = None self._nodeactor = None
self.actor = None self.actor = None
self.team = None # type: ignore del self._team
del self._gamedata
def on_expire(self) -> None:
"""Can be overridden to handle player expiration.
The player expires when the Activity it is a part of expires.
Expired players should no longer run any game logic (which will
likely error). They should, however, remove any references to
players/teams/games/etc. which could prevent them from being freed.
"""
@property
def team(self) -> TeamType:
"""The ba.Team for this player."""
assert self._postinited
assert not self._expired
return self._team
@property
def gamedata(self) -> dict:
"""Arbitrary values associated with the player.
Though it is encouraged that most player values be properly defined
on the ba.Player subclass, it may be useful for player-agnostic
objects to store values here. This dict is cleared when the player
leaves or expires so objects stored here will be disposed of at
the expected time, unlike the Player instance itself which may
continue to be referenced after it is no longer part of the game.
"""
assert self._postinited
assert not self._expired
return self._gamedata
@property @property
def sessionplayer(self) -> ba.SessionPlayer: def sessionplayer(self) -> ba.SessionPlayer:
@ -132,10 +201,10 @@ class Player(Generic[TeamType]):
Throws a ba.SessionPlayerNotFoundError if it does not exist. Throws a ba.SessionPlayerNotFoundError if it does not exist.
""" """
assert self._postinited
if bool(self._sessionplayer): if bool(self._sessionplayer):
return self._sessionplayer return self._sessionplayer
from ba import _error raise SessionPlayerNotFoundError()
raise _error.SessionPlayerNotFoundError()
@property @property
def node(self) -> ba.Node: def node(self) -> ba.Node:
@ -143,29 +212,35 @@ class Player(Generic[TeamType]):
This node can be used to get a generic player position/etc. This node can be used to get a generic player position/etc.
""" """
if not self._nodeactor: assert self._postinited
from ba import _error assert not self._expired
raise _error.NodeNotFoundError assert self._nodeactor
return self._nodeactor.node return self._nodeactor.node
@property @property
def position(self) -> ba.Vec3: def position(self) -> ba.Vec3:
"""The position of the player, as defined by its current Actor. """The position of the player, as defined by its current ba.Actor.
This value should not be used when the player has no Actor, as This value is undefined when the player has no Actor.
it is undefined in that case.
""" """
assert self._postinited
assert not self._expired
assert self.actor is not None
return _ba.Vec3(self.node.position) return _ba.Vec3(self.node.position)
def exists(self) -> bool: def exists(self) -> bool:
"""Whether the underlying player still exists. """Whether the underlying player still exists.
This will return False if the underlying ba.SessionPlayer has
left the game or if the ba.Activity this player was associated
with has ended.
Most functionality will fail on a nonexistent player. Most functionality will fail on a nonexistent player.
Note that you can also use the boolean operator for this same Note that you can also use the boolean operator for this same
functionality, so a statement such as "if player" will do functionality, so a statement such as "if player" will do
the right thing both for Player objects and values of None. the right thing both for Player objects and values of None.
""" """
return self._sessionplayer.exists() assert self._postinited
return self._sessionplayer.exists() and not self._expired
def getname(self, full: bool = False, icon: bool = True) -> str: def getname(self, full: bool = False, icon: bool = True) -> str:
"""getname(full: bool = False, icon: bool = True) -> str """getname(full: bool = False, icon: bool = True) -> str
@ -173,6 +248,8 @@ class Player(Generic[TeamType]):
Returns the player's name. If icon is True, the long version of the Returns the player's name. If icon is True, the long version of the
name may include an icon. name may include an icon.
""" """
assert self._postinited
assert not self._expired
return self._sessionplayer.getname(full=full, icon=icon) return self._sessionplayer.getname(full=full, icon=icon)
def is_alive(self) -> bool: def is_alive(self) -> bool:
@ -181,6 +258,8 @@ class Player(Generic[TeamType]):
Returns True if the player has a ba.Actor assigned and its Returns True if the player has a ba.Actor assigned and its
is_alive() method return True. False is returned otherwise. is_alive() method return True. False is returned otherwise.
""" """
assert self._postinited
assert not self._expired
return self.actor is not None and self.actor.is_alive() return self.actor is not None and self.actor.is_alive()
def get_icon(self) -> Dict[str, Any]: def get_icon(self) -> Dict[str, Any]:
@ -188,11 +267,13 @@ class Player(Generic[TeamType]):
Returns the character's icon (images, colors, etc contained in a dict) Returns the character's icon (images, colors, etc contained in a dict)
""" """
assert self._postinited
assert not self._expired
return self._sessionplayer.get_icon() return self._sessionplayer.get_icon()
def assign_input_call(self, inputtype: Union[str, Tuple[str, ...]], def assigninput(self, inputtype: Union[str, Tuple[str, ...]],
call: Callable) -> None: call: Callable) -> None:
"""assign_input_call(type: Union[str, Tuple[str, ...]], """assigninput(type: Union[str, Tuple[str, ...]],
call: Callable) -> None call: Callable) -> None
Set the python callable to be run for one or more types of input. Set the python callable to be run for one or more types of input.
@ -203,14 +284,18 @@ class Player(Generic[TeamType]):
'rightRelease', 'run', 'flyPress', 'flyRelease', 'startPress', 'rightRelease', 'run', 'flyPress', 'flyRelease', 'startPress',
'startRelease' 'startRelease'
""" """
return self._sessionplayer.assign_input_call(type=inputtype, call=call) assert self._postinited
assert not self._expired
return self._sessionplayer.assigninput(type=inputtype, call=call)
def reset_input(self) -> None: def resetinput(self) -> None:
"""reset_input() -> None """resetinput() -> None
Clears out the player's assigned input actions. Clears out the player's assigned input actions.
""" """
self._sessionplayer.reset_input() assert self._postinited
assert not self._expired
self._sessionplayer.resetinput()
def __bool__(self) -> bool: def __bool__(self) -> bool:
return self.exists() return self.exists()

View File

@ -348,7 +348,7 @@ class Session:
'_launch_end_session_activity called twice (since_last=' + '_launch_end_session_activity called twice (since_last=' +
str(since_last) + ')') str(since_last) + ')')
self._launch_end_session_activity_time = curtime self._launch_end_session_activity_time = curtime
self.set_activity(_ba.new_activity(EndSessionActivity)) self.setactivity(_ba.new_activity(EndSessionActivity))
self._wants_to_end = False self._wants_to_end = False
self._ending = True # Prevent further actions. self._ending = True # Prevent further actions.
@ -398,42 +398,44 @@ class Session:
"""General message handling; can be passed any message object.""" """General message handling; can be passed any message object."""
from ba._lobby import PlayerReadyMessage from ba._lobby import PlayerReadyMessage
from ba._messages import PlayerProfilesChangedMessage, UNHANDLED from ba._messages import PlayerProfilesChangedMessage, UNHANDLED
if isinstance(msg, PlayerReadyMessage): if isinstance(msg, PlayerReadyMessage):
self._on_player_ready(msg.chooser) self._on_player_ready(msg.chooser)
return None
if isinstance(msg, PlayerProfilesChangedMessage): elif isinstance(msg, PlayerProfilesChangedMessage):
# If we have a current activity with a lobby, ask it to reload # If we have a current activity with a lobby, ask it to reload
# profiles. # profiles.
with _ba.Context(self): with _ba.Context(self):
self.lobby.reload_profiles() self.lobby.reload_profiles()
return None return None
return UNHANDLED else:
return UNHANDLED
return None
class _SetActivityLock: class _SetActivityScopedLock:
def __init__(self, session: ba.Session) -> None: def __init__(self, session: ba.Session) -> None:
self._session = session self._session = session
if session._in_set_activity:
raise RuntimeError('Session.setactivity() called recursively.')
self._session._in_set_activity = True self._session._in_set_activity = True
def __del__(self) -> None: def __del__(self) -> None:
self._session._in_set_activity = False self._session._in_set_activity = False
def set_activity(self, activity: ba.Activity) -> None: def setactivity(self, activity: ba.Activity) -> None:
"""Assign a new current ba.Activity for the session. """Assign a new current ba.Activity for the session.
Note that this will not change the current context to the new Note that this will not change the current context to the new
Activity's. Code must be run in the new activity's methods Activity's. Code must be run in the new activity's methods
(on_transition_in, etc) to get it. (so you can't do (on_transition_in, etc) to get it. (so you can't do
session.set_activity(foo) and then ba.newnode() to add a node to foo) session.setactivity(foo) and then ba.newnode() to add a node to foo)
""" """
from ba._enums import TimeType from ba._enums import TimeType
# Sanity test: make sure this doesn't get called recursively. # Make sure we don't get called recursively.
if self._in_set_activity: _rlock = self._SetActivityScopedLock(self)
raise RuntimeError('Session.set_activity() called recursively.')
self._in_set_activity = True
if activity.session is not _ba.getsession(): if activity.session is not _ba.getsession():
raise RuntimeError("Provided Activity's Session is not current.") raise RuntimeError("Provided Activity's Session is not current.")
@ -482,7 +484,7 @@ class Session:
if prev_activity is not None: if prev_activity is not None:
with _ba.Context('ui'): with _ba.Context('ui'):
_ba.timer(max(0.0, activity.transition_time), _ba.timer(max(0.0, activity.transition_time),
prev_activity.destroy, prev_activity.expire,
timetype=TimeType.REAL) timetype=TimeType.REAL)
self._in_set_activity = False self._in_set_activity = False
@ -507,17 +509,17 @@ class Session:
with _ba.Context(self): with _ba.Context(self):
self.on_activity_end(activity, results) self.on_activity_end(activity, results)
except Exception: except Exception:
print_exception('exception in on_activity_end() for session', self, print_exception(f'Error in on_activity_end() for session {self}'
'activity', activity, 'with results', results) f' activity {activity} with results {results}')
def _request_player(self, sessionplayer: ba.SessionPlayer) -> bool: def _request_player(self, sessionplayer: ba.SessionPlayer) -> bool:
"""Called by the C++ layer when players want to join.""" """Called by the native layer when a player wants to join."""
# If we're ending, allow no new players. # If we're ending, allow no new players.
if self._ending: if self._ending:
return False return False
# Ask the session subclass to approve/deny this request. # Ask the ba.Session subclass to approve/deny this request.
try: try:
with _ba.Context(self): with _ba.Context(self):
result = self.on_player_request(sessionplayer) result = self.on_player_request(sessionplayer)
@ -525,14 +527,14 @@ class Session:
print_exception(f'Error in on_player_request for {self}') print_exception(f'Error in on_player_request for {self}')
result = False result = False
# If the user said yes, add the player to the session list. # If they said yes, add the player to the lobby.
if result: if result:
self.players.append(sessionplayer) self.players.append(sessionplayer)
with _ba.Context(self): with _ba.Context(self):
try: try:
self.lobby.add_chooser(sessionplayer) self.lobby.add_chooser(sessionplayer)
except Exception: except Exception:
print_exception('exception in lobby.add_chooser()') print_exception('Error in lobby.add_chooser().')
return result return result
@ -548,39 +550,42 @@ class Session:
This means we're ready to begin the next one This means we're ready to begin the next one
""" """
if self._next_activity is not None: if self._next_activity is None:
# Should this ever happen?
print_error('begin_next_activity() called with no _next_activity')
return
# We store both a weak and a strong ref to the new activity; # We store both a weak and a strong ref to the new activity;
# the strong is to keep it alive and the weak is so we can access # the strong is to keep it alive and the weak is so we can access
# it even after we've released the strong-ref to allow it to die. # it even after we've released the strong-ref to allow it to die.
self._activity_retained = self._next_activity self._activity_retained = self._next_activity
self._activity_weak = weakref.ref(self._next_activity) self._activity_weak = weakref.ref(self._next_activity)
self._next_activity = None self._next_activity = None
self._activity_should_end_immediately = False self._activity_should_end_immediately = False
# Kick out anyone loitering in the lobby. # Kick out anyone loitering in the lobby.
self.lobby.remove_all_choosers_and_kick_players() self.lobby.remove_all_choosers_and_kick_players()
# Kick off the activity. # Kick off the activity.
self._activity_retained.begin(self) self._activity_retained.begin(self)
# If we want to go down, we're now free to kick off that process. # If we want to completely end the session, we can now kick that off.
if self._wants_to_end: if self._wants_to_end:
self._launch_end_session_activity() self._launch_end_session_activity()
else: else:
# Otherwise, if the activity has already been told to end, # Otherwise, if the activity has already been told to end,
# do so now. # do so now.
if self._activity_should_end_immediately: if self._activity_should_end_immediately:
self._activity_retained.end( self._activity_retained.end(
self._activity_should_end_immediately_results, self._activity_should_end_immediately_results,
self._activity_should_end_immediately_delay) self._activity_should_end_immediately_delay)
def _on_player_ready(self, chooser: ba.Chooser) -> None: def _on_player_ready(self, chooser: ba.Chooser) -> None:
"""Called when a ba.Player has checked themself ready.""" """Called when a ba.Player has checked themself ready."""
lobby = chooser.lobby lobby = chooser.lobby
activity = self._activity_weak() activity = self._activity_weak()
# In joining activities, we wait till all choosers are ready # In joining-activities, we wait till all choosers are ready
# and then create all players at once. # and then create all players at once.
if activity is not None and activity.is_joining_activity: if activity is not None and activity.is_joining_activity:
if lobby.check_all_ready(): if lobby.check_all_ready():
@ -613,8 +618,10 @@ class Session:
"""(internal)""" """(internal)"""
from ba._apputils import garbage_collect, call_after_ad from ba._apputils import garbage_collect, call_after_ad
# Since we're mostly between activities at this point, lets run a cycle # Since things should be generally still right now, it's a good time
# of garbage collection; hopefully it won't cause hitches here. # to run garbage collection to clear out any circular dependency
# loops. We keep this disabled normally to avoid non-deterministic
# hitches.
garbage_collect(session_end=False) garbage_collect(session_end=False)
with _ba.Context(self): with _ba.Context(self):
@ -635,7 +642,7 @@ class Session:
# Reset the player's input here, as it is probably # Reset the player's input here, as it is probably
# referencing the chooser which could inadvertently keep it alive. # referencing the chooser which could inadvertently keep it alive.
sessionplayer.reset_input() sessionplayer.resetinput()
# We can pass it to the current activity if it has already begun # We can pass it to the current activity if it has already begun
# (otherwise it'll get passed once begin is called). # (otherwise it'll get passed once begin is called).
@ -658,7 +665,7 @@ class Session:
# If we're a non-team session, each player gets their own team. # If we're a non-team session, each player gets their own team.
# (keeps mini-game coding simpler if we can always deal with teams). # (keeps mini-game coding simpler if we can always deal with teams).
if self.use_teams: if self.use_teams:
sessionteam = chooser.get_team() sessionteam = chooser.sessionteam
else: else:
our_team_id = self._next_team_id our_team_id = self._next_team_id
self._next_team_id += 1 self._next_team_id += 1
@ -670,11 +677,12 @@ class Session:
# Add player's team to the Session. # Add player's team to the Session.
self.teams.append(sessionteam) self.teams.append(sessionteam)
try:
with _ba.Context(self): with _ba.Context(self):
try:
self.on_team_join(sessionteam) self.on_team_join(sessionteam)
except Exception: except Exception:
print_exception(f'exception in on_team_join for {self}') print_exception(f'Error in on_team_join for {self}.')
# Add player's team to the Activity. # Add player's team to the Activity.
if pass_to_activity: if pass_to_activity:
@ -682,10 +690,10 @@ class Session:
assert sessionplayer not in sessionteam.players assert sessionplayer not in sessionteam.players
sessionteam.players.append(sessionplayer) sessionteam.players.append(sessionplayer)
sessionplayer.set_data(team=sessionteam, sessionplayer.setdata(team=sessionteam,
character=chooser.get_character_name(), character=chooser.get_character_name(),
color=chooser.get_color(), color=chooser.get_color(),
highlight=chooser.get_highlight()) highlight=chooser.get_highlight())
self.stats.register_player(sessionplayer) self.stats.register_player(sessionplayer)
if pass_to_activity: if pass_to_activity:

View File

@ -259,7 +259,7 @@ class Stats:
self.orchestrahitsound3: Optional[ba.Sound] = None self.orchestrahitsound3: Optional[ba.Sound] = None
self.orchestrahitsound4: Optional[ba.Sound] = None self.orchestrahitsound4: Optional[ba.Sound] = None
def set_activity(self, activity: Optional[ba.Activity]) -> None: def setactivity(self, activity: Optional[ba.Activity]) -> None:
"""Set the current activity for this instance.""" """Set the current activity for this instance."""
self._activity = None if activity is None else weakref.ref(activity) self._activity = None if activity is None else weakref.ref(activity)

View File

@ -25,6 +25,8 @@ from __future__ import annotations
import weakref import weakref
from typing import TYPE_CHECKING, TypeVar, Generic from typing import TYPE_CHECKING, TypeVar, Generic
from ba._error import print_exception
if TYPE_CHECKING: if TYPE_CHECKING:
from weakref import ReferenceType from weakref import ReferenceType
from typing import Dict, List, Sequence, Tuple, Union, Optional from typing import Dict, List, Sequence, Tuple, Union, Optional
@ -88,14 +90,9 @@ class SessionTeam:
self.name = name self.name = name
self.color = tuple(color) self.color = tuple(color)
self.players = [] self.players = []
self.gamedata = {}
self.sessiondata = {} self.sessiondata = {}
self.gameteam: Optional[Team] = None self.gameteam: Optional[Team] = None
def reset_gamedata(self) -> None:
"""(internal)"""
self.gamedata = {}
def reset_sessiondata(self) -> None: def reset_sessiondata(self) -> None:
"""(internal)""" """(internal)"""
self.sessiondata = {} self.sessiondata = {}
@ -118,12 +115,14 @@ class Team(Generic[PlayerType]):
players: List[PlayerType] players: List[PlayerType]
id: int id: int
name: Union[ba.Lstr, str] name: Union[ba.Lstr, str]
color: Tuple[float, ...] # FIXME: can't we make this fixed len? color: Tuple[float, ...] # FIXME: can't we make this fixed length?
_sessionteam: ReferenceType[SessionTeam] _sessionteam: ReferenceType[SessionTeam]
_expired: bool
_postinited: bool
_gamedata: dict
# TODO: kill these. # TODO: kill these.
gamedata: Dict sessiondata: dict
sessiondata: Dict
# NOTE: avoiding having any __init__() here since it seems to not # NOTE: avoiding having any __init__() here since it seems to not
# get called by default if a dataclass inherits from us. # get called by default if a dataclass inherits from us.
@ -150,8 +149,10 @@ class Team(Generic[PlayerType]):
self.id = sessionteam.id self.id = sessionteam.id
self.name = sessionteam.name self.name = sessionteam.name
self.color = sessionteam.color self.color = sessionteam.color
self.gamedata = sessionteam.gamedata
self.sessiondata = sessionteam.sessiondata self.sessiondata = sessionteam.sessiondata
self._gamedata = {}
self._expired = False
self._postinited = True
def manual_init(self, team_id: int, name: Union[ba.Lstr, str], def manual_init(self, team_id: int, name: Union[ba.Lstr, str],
color: Tuple[float, ...]) -> None: color: Tuple[float, ...]) -> None:
@ -159,8 +160,54 @@ class Team(Generic[PlayerType]):
self.id = team_id self.id = team_id
self.name = name self.name = name
self.color = color self.color = color
self.gamedata = {} self._gamedata = {}
self.sessiondata = {} self.sessiondata = {}
self._expired = False
self._postinited = True
@property
def gamedata(self) -> dict:
"""Arbitrary values associated with the team.
Though it is encouraged that most player values be properly defined
on the ba.Team subclass, it may be useful for player-agnostic
objects to store values here. This dict is cleared when the team
leaves or expires so objects stored here will be disposed of at
the expected time, unlike the Team instance itself which may
continue to be referenced after it is no longer part of the game.
"""
assert self._postinited
assert not self._expired
return self._gamedata
def leave(self) -> None:
"""Called when the Team leaves a running game.
(internal)
"""
assert self._postinited
assert not self._expired
del self._gamedata
del self.players
def expire(self) -> None:
"""Called when the Team is expiring (due to the Activity expiring).
(internal)
"""
assert self._postinited
assert not self._expired
self._expired = True
try:
self.on_expire()
except Exception:
print_exception(f'Error in on_expire for {self}.')
del self._gamedata
del self.players
def on_expire(self) -> None:
"""Can be overridden to handle team expiration."""
@property @property
def sessionteam(self) -> SessionTeam: def sessionteam(self) -> SessionTeam:
@ -168,6 +215,7 @@ class Team(Generic[PlayerType]):
Throws a ba.SessionTeamNotFoundError if there is none. Throws a ba.SessionTeamNotFoundError if there is none.
""" """
assert self._postinited
if self._sessionteam is not None: if self._sessionteam is not None:
sessionteam = self._sessionteam() sessionteam = self._sessionteam()
if sessionteam is not None: if sessionteam is not None:

View File

@ -138,34 +138,33 @@ class PlayerSpaz(Spaz):
# wiring up. # wiring up.
if self._connected_to_player: if self._connected_to_player:
if player != self._connected_to_player: if player != self._connected_to_player:
player.reset_input() player.resetinput()
self.disconnect_controls_from_player() self.disconnect_controls_from_player()
else: else:
player.reset_input() player.resetinput()
player.assign_input_call('upDown', self.on_move_up_down) player.assigninput('upDown', self.on_move_up_down)
player.assign_input_call('leftRight', self.on_move_left_right) player.assigninput('leftRight', self.on_move_left_right)
player.assign_input_call('holdPositionPress', player.assigninput('holdPositionPress', self.on_hold_position_press)
self.on_hold_position_press) player.assigninput('holdPositionRelease',
player.assign_input_call('holdPositionRelease', self.on_hold_position_release)
self.on_hold_position_release)
if enable_jump: if enable_jump:
player.assign_input_call('jumpPress', self.on_jump_press) player.assigninput('jumpPress', self.on_jump_press)
player.assign_input_call('jumpRelease', self.on_jump_release) player.assigninput('jumpRelease', self.on_jump_release)
if enable_pickup: if enable_pickup:
player.assign_input_call('pickUpPress', self.on_pickup_press) player.assigninput('pickUpPress', self.on_pickup_press)
player.assign_input_call('pickUpRelease', self.on_pickup_release) player.assigninput('pickUpRelease', self.on_pickup_release)
if enable_punch: if enable_punch:
player.assign_input_call('punchPress', self.on_punch_press) player.assigninput('punchPress', self.on_punch_press)
player.assign_input_call('punchRelease', self.on_punch_release) player.assigninput('punchRelease', self.on_punch_release)
if enable_bomb: if enable_bomb:
player.assign_input_call('bombPress', self.on_bomb_press) player.assigninput('bombPress', self.on_bomb_press)
player.assign_input_call('bombRelease', self.on_bomb_release) player.assigninput('bombRelease', self.on_bomb_release)
if enable_run: if enable_run:
player.assign_input_call('run', self.on_run) player.assigninput('run', self.on_run)
if enable_fly: if enable_fly:
player.assign_input_call('flyPress', self.on_fly_press) player.assigninput('flyPress', self.on_fly_press)
player.assign_input_call('flyRelease', self.on_fly_release) player.assigninput('flyRelease', self.on_fly_release)
self._connected_to_player = player self._connected_to_player = player
@ -175,7 +174,7 @@ class PlayerSpaz(Spaz):
ba.Player from control of this spaz. ba.Player from control of this spaz.
""" """
if self._connected_to_player: if self._connected_to_player:
self._connected_to_player.reset_input() self._connected_to_player.resetinput()
self._connected_to_player = None self._connected_to_player = None
# Send releases for anything in case its held. # Send releases for anything in case its held.
@ -239,7 +238,7 @@ class PlayerSpaz(Spaz):
activity = self._activity() activity = self._activity()
player = self.getplayer(ba.Player, doraise=False) player = self.getplayer(ba.Player, False)
if not killed: if not killed:
killerplayer = None killerplayer = None
else: else:

View File

@ -46,10 +46,10 @@ class RespawnIcon:
on_right, offs_extra, respawn_icons = self._get_context(player) on_right, offs_extra, respawn_icons = self._get_context(player)
# Cache our mask tex on the team for easy access. # Cache our mask tex on the team for easy access.
mask_tex = getattr(player.team, '_spaz_respawn_icons_mask_tex', None) mask_tex = player.team.gamedata.get('_spaz_respawn_icons_mask_tex')
if mask_tex is None: if mask_tex is None:
mask_tex = ba.gettexture('characterIconMask') mask_tex = ba.gettexture('characterIconMask')
setattr(player.team, '_spaz_respawn_icons_mask_tex', mask_tex) player.team.gamedata['_spaz_respawn_icons_mask_tex'] = mask_tex
assert isinstance(mask_tex, ba.Texture) assert isinstance(mask_tex, ba.Texture)
# Now find the first unused slot and use that. # Now find the first unused slot and use that.
@ -134,34 +134,31 @@ class RespawnIcon:
def _get_context(self, player: ba.Player) -> Tuple[bool, float, Dict]: def _get_context(self, player: ba.Player) -> Tuple[bool, float, Dict]:
"""Return info on where we should be shown and stored.""" """Return info on where we should be shown and stored."""
activity = ba.getactivity() activity = ba.getactivity()
if isinstance(ba.getsession(), ba.DualTeamSession): if isinstance(ba.getsession(), ba.DualTeamSession):
on_right = player.team.id % 2 == 1 on_right = player.team.id % 2 == 1
# Store a list of icons in the team. # Store a list of icons in the team.
respawn_icons = getattr(player.team, '_spaz_respawn_icons_right', icons = player.team.gamedata.get('_spaz_respawn_icons')
None) if icons is None:
if respawn_icons is None: player.team.gamedata['_spaz_respawn_icons'] = icons = {}
respawn_icons = {} assert isinstance(icons, dict)
setattr(player.team, '_spaz_respawn_icons_right',
respawn_icons)
assert isinstance(respawn_icons, dict)
offs_extra = -20 offs_extra = -20
else: else:
on_right = False on_right = False
# Store a list of icons in the activity. # Store a list of icons in the activity.
# FIXME: Need an elegant way to store our shared stuff with icons = activity.gamedata.get('_spaz_respawn_icons')
# the activity. if icons is None:
try: activity.gamedata['_spaz_respawn_icons'] = icons = {}
respawn_icons = activity.spaz_respawn_icons_right assert isinstance(icons, dict)
except Exception:
respawn_icons = activity.spaz_respawn_icons_right = {}
if isinstance(activity.session, ba.FreeForAllSession): if isinstance(activity.session, ba.FreeForAllSession):
offs_extra = -150 offs_extra = -150
else: else:
offs_extra = -20 offs_extra = -20
return on_right, offs_extra, respawn_icons return on_right, offs_extra, icons
def _update(self) -> None: def _update(self) -> None:
remaining = int(round(self._respawn_time - ba.time())) remaining = int(round(self._respawn_time - ba.time()))

View File

@ -30,14 +30,12 @@ import ba
if TYPE_CHECKING: if TYPE_CHECKING:
from typing import Any, Optional, Sequence, Dict, Union from typing import Any, Optional, Sequence, Dict, Union
# This could use some tidying up when I get a chance..
# pylint: disable=too-many-statements
class _Entry: class _Entry:
def __init__(self, scoreboard: Scoreboard, team: ba.Team, do_cover: bool, def __init__(self, scoreboard: Scoreboard, team: ba.Team, do_cover: bool,
scale: float, label: Optional[ba.Lstr], flash_length: float): scale: float, label: Optional[ba.Lstr], flash_length: float):
# pylint: disable=too-many-statements
self._scoreboard = weakref.ref(scoreboard) self._scoreboard = weakref.ref(scoreboard)
self._do_cover = do_cover self._do_cover = do_cover
self._scale = scale self._scale = scale
@ -58,7 +56,7 @@ class _Entry:
safe_team_color = ba.safecolor(team.color, target_intensity=1.0) safe_team_color = ba.safecolor(team.color, target_intensity=1.0)
# FIXME: Should not do things conditionally for vr-mode, as there may # FIXME: Should not do things conditionally for vr-mode, as there may
# be non-vr clients connected. # be non-vr clients connected which will also get these value.
vrmode = ba.app.vr_mode vrmode = ba.app.vr_mode
if self._do_cover: if self._do_cover:
@ -152,11 +150,11 @@ class _Entry:
else: else:
team_name_label = team.name team_name_label = team.name
# we do our own clipping here; should probably try to tap into some # We do our own clipping here; should probably try to tap into some
# existing functionality # existing functionality.
if isinstance(team_name_label, ba.Lstr): if isinstance(team_name_label, ba.Lstr):
# hmmm; if the team-name is a non-translatable value lets go # Hmmm; if the team-name is a non-translatable value lets go
# ahead and clip it otherwise we leave it as-is so # ahead and clip it otherwise we leave it as-is so
# translation can occur.. # translation can occur..
if team_name_label.is_flat_value(): if team_name_label.is_flat_value():
@ -204,6 +202,7 @@ class _Entry:
# Abort if we've been killed # Abort if we've been killed
if not self._backing.node: if not self._backing.node:
return return
self._pos = tuple(position) self._pos = tuple(position)
self._backing.node.position = (position[0] + self._width / 2, self._backing.node.position = (position[0] + self._width / 2,
position[1] - self._height / 2) position[1] - self._height / 2)
@ -226,29 +225,29 @@ class _Entry:
def _set_flash_colors(self, flash: bool) -> None: def _set_flash_colors(self, flash: bool) -> None:
self._flash_colors = flash self._flash_colors = flash
def _safesetattr(node: Optional[ba.Node], attr: str, val: Any) -> None: def _safesetcolor(node: Optional[ba.Node], val: Any) -> None:
if node: if node:
setattr(node, attr, val) node.color = val
if flash: if flash:
scale = 2.0 scale = 2.0
_safesetattr( _safesetcolor(
self._backing.node, 'color', self._backing.node,
(self._backing_color[0] * scale, self._backing_color[1] * (self._backing_color[0] * scale, self._backing_color[1] *
scale, self._backing_color[2] * scale)) scale, self._backing_color[2] * scale))
_safesetattr(self._bar.node, 'color', _safesetcolor(self._bar.node,
(self._barcolor[0] * scale, self._barcolor[1] * scale, (self._barcolor[0] * scale, self._barcolor[1] *
self._barcolor[2] * scale)) scale, self._barcolor[2] * scale))
if self._do_cover: if self._do_cover:
_safesetattr( _safesetcolor(
self._cover.node, 'color', self._cover.node,
(self._cover_color[0] * scale, self._cover_color[1] * (self._cover_color[0] * scale, self._cover_color[1] *
scale, self._cover_color[2] * scale)) scale, self._cover_color[2] * scale))
else: else:
_safesetattr(self._backing.node, 'color', self._backing_color) _safesetcolor(self._backing.node, self._backing_color)
_safesetattr(self._bar.node, 'color', self._barcolor) _safesetcolor(self._bar.node, self._barcolor)
if self._do_cover: if self._do_cover:
_safesetattr(self._cover.node, 'color', self._cover_color) _safesetcolor(self._cover.node, self._cover_color)
def _do_flash(self) -> None: def _do_flash(self) -> None:
assert self._flash_counter is not None assert self._flash_counter is not None
@ -266,8 +265,8 @@ class _Entry:
show_value: bool = True) -> None: show_value: bool = True) -> None:
"""Set the value for the scoreboard entry.""" """Set the value for the scoreboard entry."""
# if we have no score yet, just set it.. otherwise compare # If we have no score yet, just set it.. otherwise compare
# and see if we should flash # and see if we should flash.
if self._score is None: if self._score is None:
self._score = score self._score = score
else: else:
@ -327,8 +326,15 @@ class _EntryProxy:
# Remove our team from the scoreboard if its still around. # Remove our team from the scoreboard if its still around.
# (but deferred, in case we die in a sim step or something where # (but deferred, in case we die in a sim step or something where
# its illegal to modify nodes) # its illegal to modify nodes)
if scoreboard is not None: if scoreboard is None:
return
try:
ba.pushcall(ba.Call(scoreboard.remove_team, self._team_id)) ba.pushcall(ba.Call(scoreboard.remove_team, self._team_id))
except ba.ContextError:
# This happens if we fire after the activity expires.
# In that case we don't need to do anything.
pass
class Scoreboard: class Scoreboard:
@ -338,7 +344,7 @@ class Scoreboard:
""" """
def __init__(self, label: ba.Lstr = None, score_split: float = 0.7): def __init__(self, label: ba.Lstr = None, score_split: float = 0.7):
"""Instantiate a score-board. """Instantiate a scoreboard.
Label can be something like 'points' and will Label can be something like 'points' and will
show up on boards if provided. show up on boards if provided.
@ -376,8 +382,8 @@ class Scoreboard:
# Create a proxy in the team which will kill # Create a proxy in the team which will kill
# our entry when it dies (for convenience) # our entry when it dies (for convenience)
assert not hasattr(team, '_scoreboard_entry') assert '_scoreboard_entry' not in team.gamedata
setattr(team, '_scoreboard_entry', _EntryProxy(self, team)) team.gamedata['_scoreboard_entry'] = _EntryProxy(self, team)
# Now set the entry. # Now set the entry.
self._entries[team.id].set_value(score=score, self._entries[team.id].set_value(score=score,

View File

@ -181,9 +181,13 @@ class AssaultGame(ba.TeamGameActivity[Player, Team]):
ba.timer(length, light.delete) ba.timer(length, light.delete)
def _handle_base_collide(self, team: Team) -> None: def _handle_base_collide(self, team: Team) -> None:
player = ba.getcollision().opposingnode.getdelegate( try:
PlayerSpaz, True).getplayer(Player) player = ba.getcollision().opposingnode.getdelegate(
if not player or not player.is_alive(): PlayerSpaz, True).getplayer(Player, True)
except ba.NotFoundError:
return
if not player.is_alive():
return return
# If its another team's player, they scored. # If its another team's player, they scored.

View File

@ -231,9 +231,9 @@ class CaptureTheFlagGame(ba.TeamGameActivity[Player, Team]):
actions=( actions=(
('modify_part_collision', 'physical', False), ('modify_part_collision', 'physical', False),
('call', 'at_connect', ('call', 'at_connect',
lambda: self._handle_hit_own_flag(team, 1)), lambda: self._handle_touching_own_flag(team, True)),
('call', 'at_disconnect', ('call', 'at_disconnect',
lambda: self._handle_hit_own_flag(team, 0)), lambda: self._handle_touching_own_flag(team, False)),
)) ))
# Other parts of our spazzes don't collide with our flags at all. # Other parts of our spazzes don't collide with our flags at all.
@ -448,32 +448,28 @@ class CaptureTheFlagGame(ba.TeamGameActivity[Player, Team]):
delegate = node.getdelegate(PlayerSpaz) delegate = node.getdelegate(PlayerSpaz)
return None if delegate is None else delegate.getplayer(Player) return None if delegate is None else delegate.getplayer(Player)
def _handle_hit_own_flag(self, team: Team, val: int) -> None: def _handle_touching_own_flag(self, team: Team, connecting: bool) -> None:
"""Called when a player touches their own team flag. """Called when a player touches or stops touching their own team flag.
We keep track of when each player is touching their We keep track of when each player is touching their own flag so we
own flag so we can award points when returned. can award points when returned.
""" """
player = self._player_from_node(ba.getcollision().sourcenode) player = self._player_from_node(ba.getcollision().sourcenode)
if player: if player:
player.touching_own_flag += (1 if val else -1) player.touching_own_flag += (1 if connecting else -1)
# If return-time is zero, just kill it immediately.. otherwise keep # If return-time is zero, just kill it immediately.. otherwise keep
# track of touches and count down. # track of touches and count down.
if float(self.flag_touch_return_time) <= 0.0: if float(self.flag_touch_return_time) <= 0.0:
assert team.flag is not None assert team.flag is not None
if not team.home_flag_at_base and team.flag.held_count == 0: if (connecting and not team.home_flag_at_base
and team.flag.held_count == 0):
# Use a node message to kill the flag instead of just killing
# our team's. (avoids redundantly killing new flags if
# multiple body parts generate callbacks in one step).
node = ba.getcollision().opposingnode
self._award_players_touching_own_flag(team) self._award_players_touching_own_flag(team)
node.handlemessage(ba.DieMessage()) ba.getcollision().opposingnode.handlemessage(ba.DieMessage())
# Takes a non-zero amount of time to return. # Takes a non-zero amount of time to return.
else: else:
if val: if connecting:
team.flag_return_touches += 1 team.flag_return_touches += 1
if team.flag_return_touches == 1: if team.flag_return_touches == 1:
team.touch_return_timer = ba.Timer( team.touch_return_timer = ba.Timer(
@ -506,7 +502,7 @@ class CaptureTheFlagGame(ba.TeamGameActivity[Player, Team]):
angle: float = None) -> PlayerSpaz: angle: float = None) -> PlayerSpaz:
"""Intercept new spazzes and add our team material for them.""" """Intercept new spazzes and add our team material for them."""
spaz = super().spawn_player_spaz(player, position, angle) spaz = super().spawn_player_spaz(player, position, angle)
player = spaz.getplayer(Player, doraise=True) player = spaz.getplayer(Player, True)
team: Team = player.team team: Team = player.team
player.touching_own_flag = 0 player.touching_own_flag = 0
no_physical_mats: List[ba.Material] = [ no_physical_mats: List[ba.Material] = [
@ -548,10 +544,15 @@ class CaptureTheFlagGame(ba.TeamGameActivity[Player, Team]):
ba.timer(0.1, ba.Call(self._spawn_flag_for_team, msg.flag.team)) ba.timer(0.1, ba.Call(self._spawn_flag_for_team, msg.flag.team))
elif isinstance(msg, FlagPickedUpMessage): elif isinstance(msg, FlagPickedUpMessage):
# Store the last player to hold the flag for scoring purposes. # Store the last player to hold the flag for scoring purposes.
assert isinstance(msg.flag, CTFFlag) assert isinstance(msg.flag, CTFFlag)
msg.flag.last_player_to_hold = msg.node.getdelegate( try:
PlayerSpaz, True).getplayer(Player) msg.flag.last_player_to_hold = msg.node.getdelegate(
PlayerSpaz, True).getplayer(Player, True)
except ba.NotFoundError:
pass
msg.flag.held_count += 1 msg.flag.held_count += 1
msg.flag.reset_return_times() msg.flag.reset_return_times()

View File

@ -236,10 +236,12 @@ class ConquestGame(ba.TeamGameActivity[Player, Team]):
def _handle_flag_player_collide(self) -> None: def _handle_flag_player_collide(self) -> None:
collision = ba.getcollision() collision = ba.getcollision()
flag = collision.sourcenode.getdelegate(ConquestFlag) try:
player = collision.opposingnode.getdelegate(PlayerSpaz, flag = collision.sourcenode.getdelegate(ConquestFlag, True)
True).getplayer(Player) player = collision.opposingnode.getdelegate(PlayerSpaz,
if not flag or not player: True).getplayer(
Player, True)
except ba.NotFoundError:
return return
assert flag.light assert flag.light

View File

@ -139,41 +139,39 @@ class EasterEggHuntGame(ba.TeamGameActivity[Player, Team]):
if self.has_ended(): if self.has_ended():
return return
collision = ba.getcollision() collision = ba.getcollision()
egg = collision.sourcenode.getdelegate(Egg) try:
player = collision.opposingnode.getdelegate(PlayerSpaz, egg = collision.sourcenode.getdelegate(Egg, True)
True).getplayer(Player) player = collision.opposingnode.getdelegate(PlayerSpaz,
if player and egg: True).getplayer(
player.team.score += 1 Player, True)
except Exception:
return
# Displays a +1 (and adds to individual player score in player.team.score += 1
# teams mode).
self.stats.player_scored(player, 1, screenmessage=False)
if self._max_eggs < 5:
self._max_eggs += 1.0
elif self._max_eggs < 10:
self._max_eggs += 0.5
elif self._max_eggs < 30:
self._max_eggs += 0.3
self._update_scoreboard()
ba.playsound(self._collect_sound, 0.5, position=egg.node.position)
# Create a flash. # Displays a +1 (and adds to individual player score in
light = ba.newnode('light', # teams mode).
attrs={ self.stats.player_scored(player, 1, screenmessage=False)
'position': egg.node.position, if self._max_eggs < 5:
'height_attenuated': False, self._max_eggs += 1.0
'radius': 0.1, elif self._max_eggs < 10:
'color': (1, 1, 0) self._max_eggs += 0.5
}) elif self._max_eggs < 30:
ba.animate(light, self._max_eggs += 0.3
'intensity', { self._update_scoreboard()
0: 0, ba.playsound(self._collect_sound, 0.5, position=egg.node.position)
0.1: 1.0,
0.2: 0 # Create a flash.
}, light = ba.newnode('light',
loop=False) attrs={
ba.timer(0.200, light.delete) 'position': egg.node.position,
egg.handlemessage(ba.DieMessage()) 'height_attenuated': False,
'radius': 0.1,
'color': (1, 1, 0)
})
ba.animate(light, 'intensity', {0: 0, 0.1: 1.0, 0.2: 0}, loop=False)
ba.timer(0.200, light.delete)
egg.handlemessage(ba.DieMessage())
def _update(self) -> None: def _update(self) -> None:
# Misc. periodic updating. # Misc. periodic updating.

View File

@ -267,9 +267,11 @@ class FootballTeamGame(ba.TeamGameActivity[Player, Team]):
def handlemessage(self, msg: Any) -> Any: def handlemessage(self, msg: Any) -> Any:
if isinstance(msg, FlagPickedUpMessage): if isinstance(msg, FlagPickedUpMessage):
assert isinstance(msg.flag, FootballFlag) assert isinstance(msg.flag, FootballFlag)
player = msg.node.getdelegate(PlayerSpaz, True).getplayer(Player) try:
if player: msg.flag.last_holding_player = msg.node.getdelegate(
msg.flag.last_holding_player = player PlayerSpaz, True).getplayer(Player, True)
except ba.NotFoundError:
pass
msg.flag.held_count += 1 msg.flag.held_count += 1
elif isinstance(msg, FlagDroppedMessage): elif isinstance(msg, FlagDroppedMessage):

View File

@ -256,11 +256,15 @@ class HockeyGame(ba.TeamGameActivity[Player, Team]):
def _handle_puck_player_collide(self) -> None: def _handle_puck_player_collide(self) -> None:
collision = ba.getcollision() collision = ba.getcollision()
puck = collision.sourcenode.getdelegate(Puck) try:
player = collision.opposingnode.getdelegate(PlayerSpaz, puck = collision.sourcenode.getdelegate(Puck, True)
True).getplayer(Player) player = collision.opposingnode.getdelegate(PlayerSpaz,
if player and puck: True).getplayer(
puck.last_players_to_touch[player.team.id] = player Player, True)
except ba.NotFoundError:
return
puck.last_players_to_touch[player.team.id] = player
def _kill_puck(self) -> None: def _kill_puck(self) -> None:
self._puck = None self._puck = None

View File

@ -247,9 +247,10 @@ class KingOfTheHillGame(ba.TeamGameActivity[Player, Team]):
ba.playsound(self._swipsound) ba.playsound(self._swipsound)
def _handle_player_flag_region_collide(self, colliding: bool) -> None: def _handle_player_flag_region_collide(self, colliding: bool) -> None:
player = ba.getcollision().opposingnode.getdelegate( try:
PlayerSpaz, True).getplayer(Player) player = ba.getcollision().opposingnode.getdelegate(
if not player: PlayerSpaz, True).getplayer(Player, True)
except ba.NotFoundError:
return return
# Different parts of us can collide so a single value isn't enough # Different parts of us can collide so a single value isn't enough

View File

@ -1196,8 +1196,7 @@ class OnslaughtGame(ba.CoopGameActivity[Player, Team]):
def handlemessage(self, msg: Any) -> Any: def handlemessage(self, msg: Any) -> Any:
if isinstance(msg, PlayerSpazHurtMessage): if isinstance(msg, PlayerSpazHurtMessage):
player = msg.spaz.getplayer(Player, doraise=True) msg.spaz.getplayer(Player, True).has_been_hurt = True
player.has_been_hurt = True
self._a_player_has_been_hurt = True self._a_player_has_been_hurt = True
elif isinstance(msg, ba.PlayerScoredMessage): elif isinstance(msg, ba.PlayerScoredMessage):

View File

@ -231,11 +231,12 @@ class RaceGame(ba.TeamGameActivity[Player, Team]):
# pylint: disable=too-many-branches # pylint: disable=too-many-branches
# pylint: disable=too-many-nested-blocks # pylint: disable=too-many-nested-blocks
collision = ba.getcollision() collision = ba.getcollision()
region = collision.sourcenode.getdelegate(RaceRegion) try:
playerspaz = collision.opposingnode.getdelegate(PlayerSpaz, region = collision.sourcenode.getdelegate(RaceRegion, True)
doraise=False) player = collision.opposingnode.getdelegate(PlayerSpaz,
player = playerspaz.getplayer(Player) if playerspaz else None True).getplayer(
if not player or not region: Player, True)
except ba.NotFoundError:
return return
last_region = player.last_region last_region = player.last_region

View File

@ -920,14 +920,14 @@ class MainMenuSession(ba.Session):
super().__init__([self._activity_deps]) super().__init__([self._activity_deps])
self._locked = False self._locked = False
self.set_activity(ba.new_activity(MainMenuActivity)) self.setactivity(ba.new_activity(MainMenuActivity))
def on_activity_end(self, activity: ba.Activity, results: Any) -> None: def on_activity_end(self, activity: ba.Activity, results: Any) -> None:
if self._locked: if self._locked:
_ba.unlock_all_input() _ba.unlock_all_input()
# Any ending activity leads us into the main menu one. # Any ending activity leads us into the main menu one.
self.set_activity(ba.new_activity(MainMenuActivity)) self.setactivity(ba.new_activity(MainMenuActivity))
def on_player_request(self, player: ba.SessionPlayer) -> bool: def on_player_request(self, player: ba.SessionPlayer) -> bool:
# Reject all player requests. # Reject all player requests.

View File

@ -2412,7 +2412,7 @@ class TutorialActivity(ba.Activity[Player, Team]):
super().on_player_join(player) super().on_player_join(player)
# We just wanna know if this player presses anything. # We just wanna know if this player presses anything.
player.assign_input_call( player.assigninput(
('jumpPress', 'punchPress', 'bombPress', 'pickUpPress'), ('jumpPress', 'punchPress', 'bombPress', 'pickUpPress'),
ba.Call(self._player_pressed_button, player)) ba.Call(self._player_pressed_button, player))

View File

@ -254,13 +254,13 @@ class PlaylistEditWindow(ba.Window):
def _add(self) -> None: def _add(self) -> None:
# Store list name then tell the session to perform an add. # Store list name then tell the session to perform an add.
self._editcontroller.set_name( self._editcontroller.setname(
cast(str, ba.textwidget(query=self._text_field))) cast(str, ba.textwidget(query=self._text_field)))
self._editcontroller.add_game_pressed() self._editcontroller.add_game_pressed()
def _edit(self) -> None: def _edit(self) -> None:
# Store list name then tell the session to perform an add. # Store list name then tell the session to perform an add.
self._editcontroller.set_name( self._editcontroller.setname(
cast(str, ba.textwidget(query=self._text_field))) cast(str, ba.textwidget(query=self._text_field)))
self._editcontroller.edit_game_pressed() self._editcontroller.edit_game_pressed()

View File

@ -119,7 +119,7 @@ class PlaylistEditController:
"""(internal)""" """(internal)"""
return self._name return self._name
def set_name(self, name: str) -> None: def setname(self, name: str) -> None:
"""(internal)""" """(internal)"""
self._name = name self._name = name

View File

@ -1,5 +1,5 @@
<!-- THIS FILE IS AUTO GENERATED; DO NOT EDIT BY HAND --> <!-- THIS FILE IS AUTO GENERATED; DO NOT EDIT BY HAND -->
<h4><em>last updated on 2020-05-30 for Ballistica version 1.5.0 build 20036</em></h4> <h4><em>last updated on 2020-05-31 for Ballistica version 1.5.0 build 20036</em></h4>
<p>This page documents the Python classes and functions in the 'ba' module, <p>This page documents the Python classes and functions in the 'ba' module,
which are the ones most relevant to modding in Ballistica. If you come across something you feel should be included here or could be better explained, please <a href="mailto:support@froemling.net">let me know</a>. Happy modding!</p> which are the ones most relevant to modding in Ballistica. If you come across something you feel should be included here or could be better explained, please <a href="mailto:support@froemling.net">let me know</a>. Happy modding!</p>
<hr> <hr>
@ -343,7 +343,7 @@ actually award achievements.</p>
can overlap during transitions.</p> can overlap during transitions.</p>
<h3>Attributes:</h3> <h3>Attributes:</h3>
<h5><a href="#attr_ba_Activity__expired">expired</a>, <a href="#attr_ba_Activity__globalsnode">globalsnode</a>, <a href="#attr_ba_Activity__players">players</a>, <a href="#attr_ba_Activity__playertype">playertype</a>, <a href="#attr_ba_Activity__session">session</a>, <a href="#attr_ba_Activity__settings_raw">settings_raw</a>, <a href="#attr_ba_Activity__stats">stats</a>, <a href="#attr_ba_Activity__teams">teams</a>, <a href="#attr_ba_Activity__teamtype">teamtype</a></h5> <h5><a href="#attr_ba_Activity__expired">expired</a>, <a href="#attr_ba_Activity__gamedata">gamedata</a>, <a href="#attr_ba_Activity__globalsnode">globalsnode</a>, <a href="#attr_ba_Activity__players">players</a>, <a href="#attr_ba_Activity__playertype">playertype</a>, <a href="#attr_ba_Activity__session">session</a>, <a href="#attr_ba_Activity__settings_raw">settings_raw</a>, <a href="#attr_ba_Activity__stats">stats</a>, <a href="#attr_ba_Activity__teams">teams</a>, <a href="#attr_ba_Activity__teamtype">teamtype</a></h5>
<dl> <dl>
<dt><h4><a name="attr_ba_Activity__expired">expired</a></h4></dt><dd> <dt><h4><a name="attr_ba_Activity__expired">expired</a></h4></dt><dd>
<p><span>bool</span></p> <p><span>bool</span></p>
@ -353,6 +353,14 @@ actually award achievements.</p>
At this point no new nodes, timers, etc should be made, At this point no new nodes, timers, etc should be made,
run, etc, and the activity should be considered a 'zombie'.</p> run, etc, and the activity should be considered a 'zombie'.</p>
</dd>
<dt><h4><a name="attr_ba_Activity__gamedata">gamedata</a></h4></dt><dd>
<p><span>dict</span></p>
<p>Entities needing to store simple data with an activity can put it
here. This dict will be deleted when the activity expires, so contained
objects generally do not need to worry about handling expired
activities.</p>
</dd> </dd>
<dt><h4><a name="attr_ba_Activity__globalsnode">globalsnode</a></h4></dt><dd> <dt><h4><a name="attr_ba_Activity__globalsnode">globalsnode</a></h4></dt><dd>
<p><span><a href="#class_ba_Node">ba.Node</a></span></p> <p><span><a href="#class_ba_Node">ba.Node</a></span></p>
@ -419,7 +427,7 @@ regardless of the player count).</p>
<p>Creates an Activity in the current <a href="#class_ba_Session">ba.Session</a>.</p> <p>Creates an Activity in the current <a href="#class_ba_Session">ba.Session</a>.</p>
<p>The activity will not be actually run until <a href="#method_ba_Session__set_activity">ba.Session.set_activity</a>() <p>The activity will not be actually run until <a href="#method_ba_Session__setactivity">ba.Session.setactivity</a>()
is called. 'settings' should be a dict of key/value pairs specific is called. 'settings' should be a dict of key/value pairs specific
to the activity.</p> to the activity.</p>
@ -1288,29 +1296,34 @@ mycall()</pre>
</p> </p>
<h3>Attributes:</h3> <h3>Attributes:</h3>
<h5><a href="#attr_ba_Chooser__lobby">lobby</a>, <a href="#attr_ba_Chooser__player">player</a>, <a href="#attr_ba_Chooser__ready">ready</a></h5> <h5><a href="#attr_ba_Chooser__lobby">lobby</a>, <a href="#attr_ba_Chooser__ready">ready</a>, <a href="#attr_ba_Chooser__sessionplayer">sessionplayer</a>, <a href="#attr_ba_Chooser__sessionteam">sessionteam</a></h5>
<dl> <dl>
<dt><h4><a name="attr_ba_Chooser__lobby">lobby</a></h4></dt><dd> <dt><h4><a name="attr_ba_Chooser__lobby">lobby</a></h4></dt><dd>
<p><span><a href="#class_ba_Lobby">ba.Lobby</a></span></p> <p><span><a href="#class_ba_Lobby">ba.Lobby</a></span></p>
<p>The chooser's <a href="#class_ba_Lobby">ba.Lobby</a>.</p> <p>The chooser's <a href="#class_ba_Lobby">ba.Lobby</a>.</p>
</dd>
<dt><h4><a name="attr_ba_Chooser__player">player</a></h4></dt><dd>
<p><span><a href="#class_ba_SessionPlayer">ba.SessionPlayer</a></span></p>
<p>The <a href="#class_ba_Player">ba.Player</a> associated with this chooser.</p>
</dd> </dd>
<dt><h4><a name="attr_ba_Chooser__ready">ready</a></h4></dt><dd> <dt><h4><a name="attr_ba_Chooser__ready">ready</a></h4></dt><dd>
<p><span>bool</span></p> <p><span>bool</span></p>
<p>Whether this chooser is checked in as ready.</p> <p>Whether this chooser is checked in as ready.</p>
</dd>
<dt><h4><a name="attr_ba_Chooser__sessionplayer">sessionplayer</a></h4></dt><dd>
<p><span><a href="#class_ba_SessionPlayer">ba.SessionPlayer</a></span></p>
<p>The <a href="#class_ba_SessionPlayer">ba.SessionPlayer</a> associated with this chooser.</p>
</dd>
<dt><h4><a name="attr_ba_Chooser__sessionteam">sessionteam</a></h4></dt><dd>
<p><span><a href="#class_ba_SessionTeam">ba.SessionTeam</a></span></p>
<p>Return this chooser's currently selected <a href="#class_ba_SessionTeam">ba.SessionTeam</a>.</p>
</dd> </dd>
</dl> </dl>
<h3>Methods:</h3> <h3>Methods:</h3>
<h5><a href="#method_ba_Chooser____init__">&lt;constructor&gt;</a>, <a href="#method_ba_Chooser__get_character_name">get_character_name()</a>, <a href="#method_ba_Chooser__get_color">get_color()</a>, <a href="#method_ba_Chooser__get_highlight">get_highlight()</a>, <a href="#method_ba_Chooser__get_lobby">get_lobby()</a>, <a href="#method_ba_Chooser__get_team">get_team()</a>, <a href="#method_ba_Chooser__getplayer">getplayer()</a>, <a href="#method_ba_Chooser__handlemessage">handlemessage()</a>, <a href="#method_ba_Chooser__reload_profiles">reload_profiles()</a>, <a href="#method_ba_Chooser__update_from_profile">update_from_profile()</a>, <a href="#method_ba_Chooser__update_position">update_position()</a></h5> <h5><a href="#method_ba_Chooser____init__">&lt;constructor&gt;</a>, <a href="#method_ba_Chooser__get_character_name">get_character_name()</a>, <a href="#method_ba_Chooser__get_color">get_color()</a>, <a href="#method_ba_Chooser__get_highlight">get_highlight()</a>, <a href="#method_ba_Chooser__get_lobby">get_lobby()</a>, <a href="#method_ba_Chooser__getplayer">getplayer()</a>, <a href="#method_ba_Chooser__handlemessage">handlemessage()</a>, <a href="#method_ba_Chooser__reload_profiles">reload_profiles()</a>, <a href="#method_ba_Chooser__update_from_profile">update_from_profile()</a>, <a href="#method_ba_Chooser__update_position">update_position()</a></h5>
<dl> <dl>
<dt><h4><a name="method_ba_Chooser____init__">&lt;constructor&gt;</a></dt></h4><dd> <dt><h4><a name="method_ba_Chooser____init__">&lt;constructor&gt;</a></dt></h4><dd>
<p><span>ba.Chooser(vpos: float, player: _<a href="#class_ba_SessionPlayer">ba.SessionPlayer</a>, lobby: "Lobby")</span></p> <p><span>ba.Chooser(vpos: float, sessionplayer: _<a href="#class_ba_SessionPlayer">ba.SessionPlayer</a>, lobby: "Lobby")</span></p>
</dd> </dd>
<dt><h4><a name="method_ba_Chooser__get_character_name">get_character_name()</a></dt></h4><dd> <dt><h4><a name="method_ba_Chooser__get_character_name">get_character_name()</a></dt></h4><dd>
@ -1336,12 +1349,6 @@ mycall()</pre>
<p>Return this chooser's lobby if it still exists; otherwise None.</p> <p>Return this chooser's lobby if it still exists; otherwise None.</p>
</dd>
<dt><h4><a name="method_ba_Chooser__get_team">get_team()</a></dt></h4><dd>
<p><span>get_team(self) -&gt; <a href="#class_ba_SessionTeam">ba.SessionTeam</a></span></p>
<p>Return this chooser's selected <a href="#class_ba_Team">ba.Team</a>.</p>
</dd> </dd>
<dt><h4><a name="method_ba_Chooser__getplayer">getplayer()</a></dt></h4><dd> <dt><h4><a name="method_ba_Chooser__getplayer">getplayer()</a></dt></h4><dd>
<p><span>getplayer(self) -&gt; <a href="#class_ba_SessionPlayer">ba.SessionPlayer</a></span></p> <p><span>getplayer(self) -&gt; <a href="#class_ba_SessionPlayer">ba.SessionPlayer</a></span></p>
@ -1545,7 +1552,7 @@ start_long_action(callback_when_done=<a href="#class_ba_ContextCall">ba.ContextC
<h3>Attributes Inherited:</h3> <h3>Attributes Inherited:</h3>
<h5><a href="#attr_ba_Activity__players">players</a>, <a href="#attr_ba_Activity__settings_raw">settings_raw</a>, <a href="#attr_ba_Activity__teams">teams</a></h5> <h5><a href="#attr_ba_Activity__players">players</a>, <a href="#attr_ba_Activity__settings_raw">settings_raw</a>, <a href="#attr_ba_Activity__teams">teams</a></h5>
<h3>Attributes Defined Here:</h3> <h3>Attributes Defined Here:</h3>
<h5><a href="#attr_ba_CoopGameActivity__expired">expired</a>, <a href="#attr_ba_CoopGameActivity__globalsnode">globalsnode</a>, <a href="#attr_ba_CoopGameActivity__map">map</a>, <a href="#attr_ba_CoopGameActivity__playertype">playertype</a>, <a href="#attr_ba_CoopGameActivity__session">session</a>, <a href="#attr_ba_CoopGameActivity__stats">stats</a>, <a href="#attr_ba_CoopGameActivity__teamtype">teamtype</a></h5> <h5><a href="#attr_ba_CoopGameActivity__expired">expired</a>, <a href="#attr_ba_CoopGameActivity__gamedata">gamedata</a>, <a href="#attr_ba_CoopGameActivity__globalsnode">globalsnode</a>, <a href="#attr_ba_CoopGameActivity__map">map</a>, <a href="#attr_ba_CoopGameActivity__playertype">playertype</a>, <a href="#attr_ba_CoopGameActivity__session">session</a>, <a href="#attr_ba_CoopGameActivity__stats">stats</a>, <a href="#attr_ba_CoopGameActivity__teamtype">teamtype</a></h5>
<dl> <dl>
<dt><h4><a name="attr_ba_CoopGameActivity__expired">expired</a></h4></dt><dd> <dt><h4><a name="attr_ba_CoopGameActivity__expired">expired</a></h4></dt><dd>
<p><span>bool</span></p> <p><span>bool</span></p>
@ -1555,6 +1562,14 @@ start_long_action(callback_when_done=<a href="#class_ba_ContextCall">ba.ContextC
At this point no new nodes, timers, etc should be made, At this point no new nodes, timers, etc should be made,
run, etc, and the activity should be considered a 'zombie'.</p> run, etc, and the activity should be considered a 'zombie'.</p>
</dd>
<dt><h4><a name="attr_ba_CoopGameActivity__gamedata">gamedata</a></h4></dt><dd>
<p><span>dict</span></p>
<p>Entities needing to store simple data with an activity can put it
here. This dict will be deleted when the activity expires, so contained
objects generally do not need to worry about handling expired
activities.</p>
</dd> </dd>
<dt><h4><a name="attr_ba_CoopGameActivity__globalsnode">globalsnode</a></h4></dt><dd> <dt><h4><a name="attr_ba_CoopGameActivity__globalsnode">globalsnode</a></h4></dt><dd>
<p><span><a href="#class_ba_Node">ba.Node</a></span></p> <p><span><a href="#class_ba_Node">ba.Node</a></span></p>
@ -1595,7 +1610,7 @@ start_long_action(callback_when_done=<a href="#class_ba_ContextCall">ba.ContextC
</dd> </dd>
</dl> </dl>
<h3>Methods Inherited:</h3> <h3>Methods Inherited:</h3>
<h5><a href="#method_ba_GameActivity__add_actor_weak_ref">add_actor_weak_ref()</a>, <a href="#method_ba_GameActivity__add_player">add_player()</a>, <a href="#method_ba_GameActivity__add_team">add_team()</a>, <a href="#method_ba_GameActivity__begin">begin()</a>, <a href="#method_ba_GameActivity__continue_or_end_game">continue_or_end_game()</a>, <a href="#method_ba_GameActivity__create_player">create_player()</a>, <a href="#method_ba_GameActivity__create_settings_ui">create_settings_ui()</a>, <a href="#method_ba_GameActivity__create_team">create_team()</a>, <a href="#method_ba_GameActivity__dep_is_present">dep_is_present()</a>, <a href="#method_ba_GameActivity__destroy">destroy()</a>, <a href="#method_ba_GameActivity__end">end()</a>, <a href="#method_ba_GameActivity__end_game">end_game()</a>, <a href="#method_ba_GameActivity__get_description">get_description()</a>, <a href="#method_ba_GameActivity__get_description_display_string">get_description_display_string()</a>, <a href="#method_ba_GameActivity__get_display_string">get_display_string()</a>, <a href="#method_ba_GameActivity__get_dynamic_deps">get_dynamic_deps()</a>, <a href="#method_ba_GameActivity__get_game_settings">get_game_settings()</a>, <a href="#method_ba_GameActivity__get_instance_description">get_instance_description()</a>, <a href="#method_ba_GameActivity__get_instance_description_short">get_instance_description_short()</a>, <a href="#method_ba_GameActivity__get_instance_display_string">get_instance_display_string()</a>, <a href="#method_ba_GameActivity__get_instance_scoreboard_display_string">get_instance_scoreboard_display_string()</a>, <a href="#method_ba_GameActivity__get_score_info">get_score_info()</a>, <a href="#method_ba_GameActivity__get_settings_display_string">get_settings_display_string()</a>, <a href="#method_ba_GameActivity__get_supported_maps">get_supported_maps()</a>, <a href="#method_ba_GameActivity__get_team_display_string">get_team_display_string()</a>, <a href="#method_ba_GameActivity__getname">getname()</a>, <a href="#method_ba_GameActivity__handlemessage">handlemessage()</a>, <a href="#method_ba_GameActivity__has_begun">has_begun()</a>, <a href="#method_ba_GameActivity__has_ended">has_ended()</a>, <a href="#method_ba_GameActivity__has_transitioned_in">has_transitioned_in()</a>, <a href="#method_ba_GameActivity__is_transitioning_out">is_transitioning_out()</a>, <a href="#method_ba_GameActivity__is_waiting_for_continue">is_waiting_for_continue()</a>, <a href="#method_ba_GameActivity__on_continue">on_continue()</a>, <a href="#method_ba_GameActivity__on_expire">on_expire()</a>, <a href="#method_ba_GameActivity__on_player_join">on_player_join()</a>, <a href="#method_ba_GameActivity__on_player_leave">on_player_leave()</a>, <a href="#method_ba_GameActivity__on_team_join">on_team_join()</a>, <a href="#method_ba_GameActivity__on_team_leave">on_team_leave()</a>, <a href="#method_ba_GameActivity__on_transition_in">on_transition_in()</a>, <a href="#method_ba_GameActivity__on_transition_out">on_transition_out()</a>, <a href="#method_ba_GameActivity__remove_player">remove_player()</a>, <a href="#method_ba_GameActivity__remove_team">remove_team()</a>, <a href="#method_ba_GameActivity__respawn_player">respawn_player()</a>, <a href="#method_ba_GameActivity__retain_actor">retain_actor()</a>, <a href="#method_ba_GameActivity__set_has_ended">set_has_ended()</a>, <a href="#method_ba_GameActivity__setup_standard_powerup_drops">setup_standard_powerup_drops()</a>, <a href="#method_ba_GameActivity__setup_standard_time_limit">setup_standard_time_limit()</a>, <a href="#method_ba_GameActivity__show_zoom_message">show_zoom_message()</a>, <a href="#method_ba_GameActivity__spawn_player">spawn_player()</a>, <a href="#method_ba_GameActivity__spawn_player_if_exists">spawn_player_if_exists()</a>, <a href="#method_ba_GameActivity__transition_in">transition_in()</a>, <a href="#method_ba_GameActivity__transition_out">transition_out()</a></h5> <h5><a href="#method_ba_GameActivity__add_actor_weak_ref">add_actor_weak_ref()</a>, <a href="#method_ba_GameActivity__add_player">add_player()</a>, <a href="#method_ba_GameActivity__add_team">add_team()</a>, <a href="#method_ba_GameActivity__begin">begin()</a>, <a href="#method_ba_GameActivity__continue_or_end_game">continue_or_end_game()</a>, <a href="#method_ba_GameActivity__create_player">create_player()</a>, <a href="#method_ba_GameActivity__create_settings_ui">create_settings_ui()</a>, <a href="#method_ba_GameActivity__create_team">create_team()</a>, <a href="#method_ba_GameActivity__dep_is_present">dep_is_present()</a>, <a href="#method_ba_GameActivity__end">end()</a>, <a href="#method_ba_GameActivity__end_game">end_game()</a>, <a href="#method_ba_GameActivity__expire">expire()</a>, <a href="#method_ba_GameActivity__get_description">get_description()</a>, <a href="#method_ba_GameActivity__get_description_display_string">get_description_display_string()</a>, <a href="#method_ba_GameActivity__get_display_string">get_display_string()</a>, <a href="#method_ba_GameActivity__get_dynamic_deps">get_dynamic_deps()</a>, <a href="#method_ba_GameActivity__get_game_settings">get_game_settings()</a>, <a href="#method_ba_GameActivity__get_instance_description">get_instance_description()</a>, <a href="#method_ba_GameActivity__get_instance_description_short">get_instance_description_short()</a>, <a href="#method_ba_GameActivity__get_instance_display_string">get_instance_display_string()</a>, <a href="#method_ba_GameActivity__get_instance_scoreboard_display_string">get_instance_scoreboard_display_string()</a>, <a href="#method_ba_GameActivity__get_score_info">get_score_info()</a>, <a href="#method_ba_GameActivity__get_settings_display_string">get_settings_display_string()</a>, <a href="#method_ba_GameActivity__get_supported_maps">get_supported_maps()</a>, <a href="#method_ba_GameActivity__get_team_display_string">get_team_display_string()</a>, <a href="#method_ba_GameActivity__getname">getname()</a>, <a href="#method_ba_GameActivity__handlemessage">handlemessage()</a>, <a href="#method_ba_GameActivity__has_begun">has_begun()</a>, <a href="#method_ba_GameActivity__has_ended">has_ended()</a>, <a href="#method_ba_GameActivity__has_transitioned_in">has_transitioned_in()</a>, <a href="#method_ba_GameActivity__is_transitioning_out">is_transitioning_out()</a>, <a href="#method_ba_GameActivity__is_waiting_for_continue">is_waiting_for_continue()</a>, <a href="#method_ba_GameActivity__on_continue">on_continue()</a>, <a href="#method_ba_GameActivity__on_expire">on_expire()</a>, <a href="#method_ba_GameActivity__on_player_join">on_player_join()</a>, <a href="#method_ba_GameActivity__on_player_leave">on_player_leave()</a>, <a href="#method_ba_GameActivity__on_team_join">on_team_join()</a>, <a href="#method_ba_GameActivity__on_team_leave">on_team_leave()</a>, <a href="#method_ba_GameActivity__on_transition_in">on_transition_in()</a>, <a href="#method_ba_GameActivity__on_transition_out">on_transition_out()</a>, <a href="#method_ba_GameActivity__remove_player">remove_player()</a>, <a href="#method_ba_GameActivity__remove_team">remove_team()</a>, <a href="#method_ba_GameActivity__respawn_player">respawn_player()</a>, <a href="#method_ba_GameActivity__retain_actor">retain_actor()</a>, <a href="#method_ba_GameActivity__set_has_ended">set_has_ended()</a>, <a href="#method_ba_GameActivity__setup_standard_powerup_drops">setup_standard_powerup_drops()</a>, <a href="#method_ba_GameActivity__setup_standard_time_limit">setup_standard_time_limit()</a>, <a href="#method_ba_GameActivity__show_zoom_message">show_zoom_message()</a>, <a href="#method_ba_GameActivity__spawn_player">spawn_player()</a>, <a href="#method_ba_GameActivity__spawn_player_if_exists">spawn_player_if_exists()</a>, <a href="#method_ba_GameActivity__transition_in">transition_in()</a>, <a href="#method_ba_GameActivity__transition_out">transition_out()</a></h5>
<h3>Methods Defined or Overridden:</h3> <h3>Methods Defined or Overridden:</h3>
<h5><a href="#method_ba_CoopGameActivity____init__">&lt;constructor&gt;</a>, <a href="#method_ba_CoopGameActivity__celebrate">celebrate()</a>, <a href="#method_ba_CoopGameActivity__fade_to_red">fade_to_red()</a>, <a href="#method_ba_CoopGameActivity__get_score_type">get_score_type()</a>, <a href="#method_ba_CoopGameActivity__on_begin">on_begin()</a>, <a href="#method_ba_CoopGameActivity__setup_low_life_warning_sound">setup_low_life_warning_sound()</a>, <a href="#method_ba_CoopGameActivity__spawn_player_spaz">spawn_player_spaz()</a>, <a href="#method_ba_CoopGameActivity__supports_session_type">supports_session_type()</a></h5> <h5><a href="#method_ba_CoopGameActivity____init__">&lt;constructor&gt;</a>, <a href="#method_ba_CoopGameActivity__celebrate">celebrate()</a>, <a href="#method_ba_CoopGameActivity__fade_to_red">fade_to_red()</a>, <a href="#method_ba_CoopGameActivity__get_score_type">get_score_type()</a>, <a href="#method_ba_CoopGameActivity__on_begin">on_begin()</a>, <a href="#method_ba_CoopGameActivity__setup_low_life_warning_sound">setup_low_life_warning_sound()</a>, <a href="#method_ba_CoopGameActivity__spawn_player_spaz">spawn_player_spaz()</a>, <a href="#method_ba_CoopGameActivity__supports_session_type">supports_session_type()</a></h5>
<dl> <dl>
@ -1685,7 +1700,7 @@ there is no associated Campaign.</p>
</dd> </dd>
</dl> </dl>
<h3>Methods Inherited:</h3> <h3>Methods Inherited:</h3>
<h5><a href="#method_ba_Session__begin_next_activity">begin_next_activity()</a>, <a href="#method_ba_Session__end">end()</a>, <a href="#method_ba_Session__end_activity">end_activity()</a>, <a href="#method_ba_Session__getactivity">getactivity()</a>, <a href="#method_ba_Session__handlemessage">handlemessage()</a>, <a href="#method_ba_Session__on_player_request">on_player_request()</a>, <a href="#method_ba_Session__on_team_join">on_team_join()</a>, <a href="#method_ba_Session__on_team_leave">on_team_leave()</a>, <a href="#method_ba_Session__set_activity">set_activity()</a>, <a href="#method_ba_Session__transitioning_out_activity_was_freed">transitioning_out_activity_was_freed()</a></h5> <h5><a href="#method_ba_Session__begin_next_activity">begin_next_activity()</a>, <a href="#method_ba_Session__end">end()</a>, <a href="#method_ba_Session__end_activity">end_activity()</a>, <a href="#method_ba_Session__getactivity">getactivity()</a>, <a href="#method_ba_Session__handlemessage">handlemessage()</a>, <a href="#method_ba_Session__on_player_request">on_player_request()</a>, <a href="#method_ba_Session__on_team_join">on_team_join()</a>, <a href="#method_ba_Session__on_team_leave">on_team_leave()</a>, <a href="#method_ba_Session__setactivity">setactivity()</a>, <a href="#method_ba_Session__transitioning_out_activity_was_freed">transitioning_out_activity_was_freed()</a></h5>
<h3>Methods Defined or Overridden:</h3> <h3>Methods Defined or Overridden:</h3>
<h5><a href="#method_ba_CoopSession____init__">&lt;constructor&gt;</a>, <a href="#method_ba_CoopSession__get_current_game_instance">get_current_game_instance()</a>, <a href="#method_ba_CoopSession__get_custom_menu_entries">get_custom_menu_entries()</a>, <a href="#method_ba_CoopSession__on_activity_end">on_activity_end()</a>, <a href="#method_ba_CoopSession__on_player_leave">on_player_leave()</a>, <a href="#method_ba_CoopSession__restart">restart()</a></h5> <h5><a href="#method_ba_CoopSession____init__">&lt;constructor&gt;</a>, <a href="#method_ba_CoopSession__get_current_game_instance">get_current_game_instance()</a>, <a href="#method_ba_CoopSession__get_custom_menu_entries">get_custom_menu_entries()</a>, <a href="#method_ba_CoopSession__on_activity_end">on_activity_end()</a>, <a href="#method_ba_CoopSession__on_player_leave">on_player_leave()</a>, <a href="#method_ba_CoopSession__restart">restart()</a></h5>
<dl> <dl>
@ -2036,7 +2051,7 @@ its time with lingering corpses, sound effects, etc.</p>
</dd> </dd>
</dl> </dl>
<h3>Methods Inherited:</h3> <h3>Methods Inherited:</h3>
<h5><a href="#method_ba_MultiTeamSession__announce_game_results">announce_game_results()</a>, <a href="#method_ba_MultiTeamSession__begin_next_activity">begin_next_activity()</a>, <a href="#method_ba_MultiTeamSession__end">end()</a>, <a href="#method_ba_MultiTeamSession__end_activity">end_activity()</a>, <a href="#method_ba_MultiTeamSession__get_custom_menu_entries">get_custom_menu_entries()</a>, <a href="#method_ba_MultiTeamSession__get_ffa_series_length">get_ffa_series_length()</a>, <a href="#method_ba_MultiTeamSession__get_game_number">get_game_number()</a>, <a href="#method_ba_MultiTeamSession__get_max_players">get_max_players()</a>, <a href="#method_ba_MultiTeamSession__get_next_game_description">get_next_game_description()</a>, <a href="#method_ba_MultiTeamSession__get_series_length">get_series_length()</a>, <a href="#method_ba_MultiTeamSession__getactivity">getactivity()</a>, <a href="#method_ba_MultiTeamSession__handlemessage">handlemessage()</a>, <a href="#method_ba_MultiTeamSession__on_activity_end">on_activity_end()</a>, <a href="#method_ba_MultiTeamSession__on_player_leave">on_player_leave()</a>, <a href="#method_ba_MultiTeamSession__on_player_request">on_player_request()</a>, <a href="#method_ba_MultiTeamSession__on_team_join">on_team_join()</a>, <a href="#method_ba_MultiTeamSession__on_team_leave">on_team_leave()</a>, <a href="#method_ba_MultiTeamSession__set_activity">set_activity()</a>, <a href="#method_ba_MultiTeamSession__transitioning_out_activity_was_freed">transitioning_out_activity_was_freed()</a></h5> <h5><a href="#method_ba_MultiTeamSession__announce_game_results">announce_game_results()</a>, <a href="#method_ba_MultiTeamSession__begin_next_activity">begin_next_activity()</a>, <a href="#method_ba_MultiTeamSession__end">end()</a>, <a href="#method_ba_MultiTeamSession__end_activity">end_activity()</a>, <a href="#method_ba_MultiTeamSession__get_custom_menu_entries">get_custom_menu_entries()</a>, <a href="#method_ba_MultiTeamSession__get_ffa_series_length">get_ffa_series_length()</a>, <a href="#method_ba_MultiTeamSession__get_game_number">get_game_number()</a>, <a href="#method_ba_MultiTeamSession__get_max_players">get_max_players()</a>, <a href="#method_ba_MultiTeamSession__get_next_game_description">get_next_game_description()</a>, <a href="#method_ba_MultiTeamSession__get_series_length">get_series_length()</a>, <a href="#method_ba_MultiTeamSession__getactivity">getactivity()</a>, <a href="#method_ba_MultiTeamSession__handlemessage">handlemessage()</a>, <a href="#method_ba_MultiTeamSession__on_activity_end">on_activity_end()</a>, <a href="#method_ba_MultiTeamSession__on_player_leave">on_player_leave()</a>, <a href="#method_ba_MultiTeamSession__on_player_request">on_player_request()</a>, <a href="#method_ba_MultiTeamSession__on_team_join">on_team_join()</a>, <a href="#method_ba_MultiTeamSession__on_team_leave">on_team_leave()</a>, <a href="#method_ba_MultiTeamSession__setactivity">setactivity()</a>, <a href="#method_ba_MultiTeamSession__transitioning_out_activity_was_freed">transitioning_out_activity_was_freed()</a></h5>
<h3>Methods Defined or Overridden:</h3> <h3>Methods Defined or Overridden:</h3>
<dl> <dl>
<dt><h4><a name="method_ba_DualTeamSession____init__">&lt;constructor&gt;</a></dt></h4><dd> <dt><h4><a name="method_ba_DualTeamSession____init__">&lt;constructor&gt;</a></dt></h4><dd>
@ -2082,7 +2097,7 @@ its time with lingering corpses, sound effects, etc.</p>
</dd> </dd>
</dl> </dl>
<h3>Methods Inherited:</h3> <h3>Methods Inherited:</h3>
<h5><a href="#method_ba_MultiTeamSession__announce_game_results">announce_game_results()</a>, <a href="#method_ba_MultiTeamSession__begin_next_activity">begin_next_activity()</a>, <a href="#method_ba_MultiTeamSession__end">end()</a>, <a href="#method_ba_MultiTeamSession__end_activity">end_activity()</a>, <a href="#method_ba_MultiTeamSession__get_custom_menu_entries">get_custom_menu_entries()</a>, <a href="#method_ba_MultiTeamSession__get_ffa_series_length">get_ffa_series_length()</a>, <a href="#method_ba_MultiTeamSession__get_game_number">get_game_number()</a>, <a href="#method_ba_MultiTeamSession__get_max_players">get_max_players()</a>, <a href="#method_ba_MultiTeamSession__get_next_game_description">get_next_game_description()</a>, <a href="#method_ba_MultiTeamSession__get_series_length">get_series_length()</a>, <a href="#method_ba_MultiTeamSession__getactivity">getactivity()</a>, <a href="#method_ba_MultiTeamSession__handlemessage">handlemessage()</a>, <a href="#method_ba_MultiTeamSession__on_activity_end">on_activity_end()</a>, <a href="#method_ba_MultiTeamSession__on_player_leave">on_player_leave()</a>, <a href="#method_ba_MultiTeamSession__on_player_request">on_player_request()</a>, <a href="#method_ba_MultiTeamSession__on_team_join">on_team_join()</a>, <a href="#method_ba_MultiTeamSession__on_team_leave">on_team_leave()</a>, <a href="#method_ba_MultiTeamSession__set_activity">set_activity()</a>, <a href="#method_ba_MultiTeamSession__transitioning_out_activity_was_freed">transitioning_out_activity_was_freed()</a></h5> <h5><a href="#method_ba_MultiTeamSession__announce_game_results">announce_game_results()</a>, <a href="#method_ba_MultiTeamSession__begin_next_activity">begin_next_activity()</a>, <a href="#method_ba_MultiTeamSession__end">end()</a>, <a href="#method_ba_MultiTeamSession__end_activity">end_activity()</a>, <a href="#method_ba_MultiTeamSession__get_custom_menu_entries">get_custom_menu_entries()</a>, <a href="#method_ba_MultiTeamSession__get_ffa_series_length">get_ffa_series_length()</a>, <a href="#method_ba_MultiTeamSession__get_game_number">get_game_number()</a>, <a href="#method_ba_MultiTeamSession__get_max_players">get_max_players()</a>, <a href="#method_ba_MultiTeamSession__get_next_game_description">get_next_game_description()</a>, <a href="#method_ba_MultiTeamSession__get_series_length">get_series_length()</a>, <a href="#method_ba_MultiTeamSession__getactivity">getactivity()</a>, <a href="#method_ba_MultiTeamSession__handlemessage">handlemessage()</a>, <a href="#method_ba_MultiTeamSession__on_activity_end">on_activity_end()</a>, <a href="#method_ba_MultiTeamSession__on_player_leave">on_player_leave()</a>, <a href="#method_ba_MultiTeamSession__on_player_request">on_player_request()</a>, <a href="#method_ba_MultiTeamSession__on_team_join">on_team_join()</a>, <a href="#method_ba_MultiTeamSession__on_team_leave">on_team_leave()</a>, <a href="#method_ba_MultiTeamSession__setactivity">setactivity()</a>, <a href="#method_ba_MultiTeamSession__transitioning_out_activity_was_freed">transitioning_out_activity_was_freed()</a></h5>
<h3>Methods Defined or Overridden:</h3> <h3>Methods Defined or Overridden:</h3>
<h5><a href="#method_ba_FreeForAllSession____init__">&lt;constructor&gt;</a>, <a href="#method_ba_FreeForAllSession__get_ffa_point_awards">get_ffa_point_awards()</a></h5> <h5><a href="#method_ba_FreeForAllSession____init__">&lt;constructor&gt;</a>, <a href="#method_ba_FreeForAllSession__get_ffa_point_awards">get_ffa_point_awards()</a></h5>
<dl> <dl>
@ -2130,7 +2145,7 @@ its time with lingering corpses, sound effects, etc.</p>
<h3>Attributes Inherited:</h3> <h3>Attributes Inherited:</h3>
<h5><a href="#attr_ba_Activity__players">players</a>, <a href="#attr_ba_Activity__settings_raw">settings_raw</a>, <a href="#attr_ba_Activity__teams">teams</a></h5> <h5><a href="#attr_ba_Activity__players">players</a>, <a href="#attr_ba_Activity__settings_raw">settings_raw</a>, <a href="#attr_ba_Activity__teams">teams</a></h5>
<h3>Attributes Defined Here:</h3> <h3>Attributes Defined Here:</h3>
<h5><a href="#attr_ba_GameActivity__expired">expired</a>, <a href="#attr_ba_GameActivity__globalsnode">globalsnode</a>, <a href="#attr_ba_GameActivity__map">map</a>, <a href="#attr_ba_GameActivity__playertype">playertype</a>, <a href="#attr_ba_GameActivity__session">session</a>, <a href="#attr_ba_GameActivity__stats">stats</a>, <a href="#attr_ba_GameActivity__teamtype">teamtype</a></h5> <h5><a href="#attr_ba_GameActivity__expired">expired</a>, <a href="#attr_ba_GameActivity__gamedata">gamedata</a>, <a href="#attr_ba_GameActivity__globalsnode">globalsnode</a>, <a href="#attr_ba_GameActivity__map">map</a>, <a href="#attr_ba_GameActivity__playertype">playertype</a>, <a href="#attr_ba_GameActivity__session">session</a>, <a href="#attr_ba_GameActivity__stats">stats</a>, <a href="#attr_ba_GameActivity__teamtype">teamtype</a></h5>
<dl> <dl>
<dt><h4><a name="attr_ba_GameActivity__expired">expired</a></h4></dt><dd> <dt><h4><a name="attr_ba_GameActivity__expired">expired</a></h4></dt><dd>
<p><span>bool</span></p> <p><span>bool</span></p>
@ -2140,6 +2155,14 @@ its time with lingering corpses, sound effects, etc.</p>
At this point no new nodes, timers, etc should be made, At this point no new nodes, timers, etc should be made,
run, etc, and the activity should be considered a 'zombie'.</p> run, etc, and the activity should be considered a 'zombie'.</p>
</dd>
<dt><h4><a name="attr_ba_GameActivity__gamedata">gamedata</a></h4></dt><dd>
<p><span>dict</span></p>
<p>Entities needing to store simple data with an activity can put it
here. This dict will be deleted when the activity expires, so contained
objects generally do not need to worry about handling expired
activities.</p>
</dd> </dd>
<dt><h4><a name="attr_ba_GameActivity__globalsnode">globalsnode</a></h4></dt><dd> <dt><h4><a name="attr_ba_GameActivity__globalsnode">globalsnode</a></h4></dt><dd>
<p><span><a href="#class_ba_Node">ba.Node</a></span></p> <p><span><a href="#class_ba_Node">ba.Node</a></span></p>
@ -2180,7 +2203,7 @@ its time with lingering corpses, sound effects, etc.</p>
</dd> </dd>
</dl> </dl>
<h3>Methods Inherited:</h3> <h3>Methods Inherited:</h3>
<h5><a href="#method_ba_Activity__add_actor_weak_ref">add_actor_weak_ref()</a>, <a href="#method_ba_Activity__add_player">add_player()</a>, <a href="#method_ba_Activity__add_team">add_team()</a>, <a href="#method_ba_Activity__begin">begin()</a>, <a href="#method_ba_Activity__create_player">create_player()</a>, <a href="#method_ba_Activity__create_team">create_team()</a>, <a href="#method_ba_Activity__dep_is_present">dep_is_present()</a>, <a href="#method_ba_Activity__destroy">destroy()</a>, <a href="#method_ba_Activity__get_dynamic_deps">get_dynamic_deps()</a>, <a href="#method_ba_Activity__has_begun">has_begun()</a>, <a href="#method_ba_Activity__has_ended">has_ended()</a>, <a href="#method_ba_Activity__has_transitioned_in">has_transitioned_in()</a>, <a href="#method_ba_Activity__is_transitioning_out">is_transitioning_out()</a>, <a href="#method_ba_Activity__on_expire">on_expire()</a>, <a href="#method_ba_Activity__on_player_leave">on_player_leave()</a>, <a href="#method_ba_Activity__on_team_join">on_team_join()</a>, <a href="#method_ba_Activity__on_team_leave">on_team_leave()</a>, <a href="#method_ba_Activity__on_transition_out">on_transition_out()</a>, <a href="#method_ba_Activity__remove_player">remove_player()</a>, <a href="#method_ba_Activity__remove_team">remove_team()</a>, <a href="#method_ba_Activity__retain_actor">retain_actor()</a>, <a href="#method_ba_Activity__set_has_ended">set_has_ended()</a>, <a href="#method_ba_Activity__transition_in">transition_in()</a>, <a href="#method_ba_Activity__transition_out">transition_out()</a></h5> <h5><a href="#method_ba_Activity__add_actor_weak_ref">add_actor_weak_ref()</a>, <a href="#method_ba_Activity__add_player">add_player()</a>, <a href="#method_ba_Activity__add_team">add_team()</a>, <a href="#method_ba_Activity__begin">begin()</a>, <a href="#method_ba_Activity__create_player">create_player()</a>, <a href="#method_ba_Activity__create_team">create_team()</a>, <a href="#method_ba_Activity__dep_is_present">dep_is_present()</a>, <a href="#method_ba_Activity__expire">expire()</a>, <a href="#method_ba_Activity__get_dynamic_deps">get_dynamic_deps()</a>, <a href="#method_ba_Activity__has_begun">has_begun()</a>, <a href="#method_ba_Activity__has_ended">has_ended()</a>, <a href="#method_ba_Activity__has_transitioned_in">has_transitioned_in()</a>, <a href="#method_ba_Activity__is_transitioning_out">is_transitioning_out()</a>, <a href="#method_ba_Activity__on_expire">on_expire()</a>, <a href="#method_ba_Activity__on_player_leave">on_player_leave()</a>, <a href="#method_ba_Activity__on_team_join">on_team_join()</a>, <a href="#method_ba_Activity__on_team_leave">on_team_leave()</a>, <a href="#method_ba_Activity__on_transition_out">on_transition_out()</a>, <a href="#method_ba_Activity__remove_player">remove_player()</a>, <a href="#method_ba_Activity__remove_team">remove_team()</a>, <a href="#method_ba_Activity__retain_actor">retain_actor()</a>, <a href="#method_ba_Activity__set_has_ended">set_has_ended()</a>, <a href="#method_ba_Activity__transition_in">transition_in()</a>, <a href="#method_ba_Activity__transition_out">transition_out()</a></h5>
<h3>Methods Defined or Overridden:</h3> <h3>Methods Defined or Overridden:</h3>
<h5><a href="#method_ba_GameActivity____init__">&lt;constructor&gt;</a>, <a href="#method_ba_GameActivity__continue_or_end_game">continue_or_end_game()</a>, <a href="#method_ba_GameActivity__create_settings_ui">create_settings_ui()</a>, <a href="#method_ba_GameActivity__end">end()</a>, <a href="#method_ba_GameActivity__end_game">end_game()</a>, <a href="#method_ba_GameActivity__get_description">get_description()</a>, <a href="#method_ba_GameActivity__get_description_display_string">get_description_display_string()</a>, <a href="#method_ba_GameActivity__get_display_string">get_display_string()</a>, <a href="#method_ba_GameActivity__get_game_settings">get_game_settings()</a>, <a href="#method_ba_GameActivity__get_instance_description">get_instance_description()</a>, <a href="#method_ba_GameActivity__get_instance_description_short">get_instance_description_short()</a>, <a href="#method_ba_GameActivity__get_instance_display_string">get_instance_display_string()</a>, <a href="#method_ba_GameActivity__get_instance_scoreboard_display_string">get_instance_scoreboard_display_string()</a>, <a href="#method_ba_GameActivity__get_score_info">get_score_info()</a>, <a href="#method_ba_GameActivity__get_settings_display_string">get_settings_display_string()</a>, <a href="#method_ba_GameActivity__get_supported_maps">get_supported_maps()</a>, <a href="#method_ba_GameActivity__get_team_display_string">get_team_display_string()</a>, <a href="#method_ba_GameActivity__getname">getname()</a>, <a href="#method_ba_GameActivity__handlemessage">handlemessage()</a>, <a href="#method_ba_GameActivity__is_waiting_for_continue">is_waiting_for_continue()</a>, <a href="#method_ba_GameActivity__on_begin">on_begin()</a>, <a href="#method_ba_GameActivity__on_continue">on_continue()</a>, <a href="#method_ba_GameActivity__on_player_join">on_player_join()</a>, <a href="#method_ba_GameActivity__on_transition_in">on_transition_in()</a>, <a href="#method_ba_GameActivity__respawn_player">respawn_player()</a>, <a href="#method_ba_GameActivity__setup_standard_powerup_drops">setup_standard_powerup_drops()</a>, <a href="#method_ba_GameActivity__setup_standard_time_limit">setup_standard_time_limit()</a>, <a href="#method_ba_GameActivity__show_zoom_message">show_zoom_message()</a>, <a href="#method_ba_GameActivity__spawn_player">spawn_player()</a>, <a href="#method_ba_GameActivity__spawn_player_if_exists">spawn_player_if_exists()</a>, <a href="#method_ba_GameActivity__spawn_player_spaz">spawn_player_spaz()</a>, <a href="#method_ba_GameActivity__supports_session_type">supports_session_type()</a></h5> <h5><a href="#method_ba_GameActivity____init__">&lt;constructor&gt;</a>, <a href="#method_ba_GameActivity__continue_or_end_game">continue_or_end_game()</a>, <a href="#method_ba_GameActivity__create_settings_ui">create_settings_ui()</a>, <a href="#method_ba_GameActivity__end">end()</a>, <a href="#method_ba_GameActivity__end_game">end_game()</a>, <a href="#method_ba_GameActivity__get_description">get_description()</a>, <a href="#method_ba_GameActivity__get_description_display_string">get_description_display_string()</a>, <a href="#method_ba_GameActivity__get_display_string">get_display_string()</a>, <a href="#method_ba_GameActivity__get_game_settings">get_game_settings()</a>, <a href="#method_ba_GameActivity__get_instance_description">get_instance_description()</a>, <a href="#method_ba_GameActivity__get_instance_description_short">get_instance_description_short()</a>, <a href="#method_ba_GameActivity__get_instance_display_string">get_instance_display_string()</a>, <a href="#method_ba_GameActivity__get_instance_scoreboard_display_string">get_instance_scoreboard_display_string()</a>, <a href="#method_ba_GameActivity__get_score_info">get_score_info()</a>, <a href="#method_ba_GameActivity__get_settings_display_string">get_settings_display_string()</a>, <a href="#method_ba_GameActivity__get_supported_maps">get_supported_maps()</a>, <a href="#method_ba_GameActivity__get_team_display_string">get_team_display_string()</a>, <a href="#method_ba_GameActivity__getname">getname()</a>, <a href="#method_ba_GameActivity__handlemessage">handlemessage()</a>, <a href="#method_ba_GameActivity__is_waiting_for_continue">is_waiting_for_continue()</a>, <a href="#method_ba_GameActivity__on_begin">on_begin()</a>, <a href="#method_ba_GameActivity__on_continue">on_continue()</a>, <a href="#method_ba_GameActivity__on_player_join">on_player_join()</a>, <a href="#method_ba_GameActivity__on_transition_in">on_transition_in()</a>, <a href="#method_ba_GameActivity__respawn_player">respawn_player()</a>, <a href="#method_ba_GameActivity__setup_standard_powerup_drops">setup_standard_powerup_drops()</a>, <a href="#method_ba_GameActivity__setup_standard_time_limit">setup_standard_time_limit()</a>, <a href="#method_ba_GameActivity__show_zoom_message">show_zoom_message()</a>, <a href="#method_ba_GameActivity__spawn_player">spawn_player()</a>, <a href="#method_ba_GameActivity__spawn_player_if_exists">spawn_player_if_exists()</a>, <a href="#method_ba_GameActivity__spawn_player_spaz">spawn_player_spaz()</a>, <a href="#method_ba_GameActivity__supports_session_type">supports_session_type()</a></h5>
<dl> <dl>
@ -2828,11 +2851,11 @@ can be changed to separate its new high score lists/etc. from the old.</p>
</p> </p>
<h3>Attributes:</h3> <h3>Attributes:</h3>
<h5><a href="#attr_ba_Lobby__teams">teams</a>, <a href="#attr_ba_Lobby__use_team_colors">use_team_colors</a></h5> <h5><a href="#attr_ba_Lobby__sessionteams">sessionteams</a>, <a href="#attr_ba_Lobby__use_team_colors">use_team_colors</a></h5>
<dl> <dl>
<dt><h4><a name="attr_ba_Lobby__teams">teams</a></h4></dt><dd> <dt><h4><a name="attr_ba_Lobby__sessionteams">sessionteams</a></h4></dt><dd>
<p><span>List[<a href="#class_ba_SessionTeam">ba.SessionTeam</a>]</span></p> <p><span>List[<a href="#class_ba_SessionTeam">ba.SessionTeam</a>]</span></p>
<p>Teams available in this lobby.</p> <p><a href="#class_ba_SessionTeam">ba.SessionTeams</a> available in this lobby.</p>
</dd> </dd>
<dt><h4><a name="attr_ba_Lobby__use_team_colors">use_team_colors</a></h4></dt><dd> <dt><h4><a name="attr_ba_Lobby__use_team_colors">use_team_colors</a></h4></dt><dd>
@ -2851,7 +2874,7 @@ can be changed to separate its new high score lists/etc. from the old.</p>
</dd> </dd>
<dt><h4><a name="method_ba_Lobby__add_chooser">add_chooser()</a></dt></h4><dd> <dt><h4><a name="method_ba_Lobby__add_chooser">add_chooser()</a></dt></h4><dd>
<p><span>add_chooser(self, player: <a href="#class_ba_SessionPlayer">ba.SessionPlayer</a>) -&gt; None</span></p> <p><span>add_chooser(self, sessionplayer: <a href="#class_ba_SessionPlayer">ba.SessionPlayer</a>) -&gt; None</span></p>
<p>Add a chooser to the lobby for the provided player.</p> <p>Add a chooser to the lobby for the provided player.</p>
@ -3367,7 +3390,7 @@ Use <a href="#function_ba_getmodel">ba.getmodel</a>() to instantiate one.</p>
</dd> </dd>
</dl> </dl>
<h3>Methods Inherited:</h3> <h3>Methods Inherited:</h3>
<h5><a href="#method_ba_Session__begin_next_activity">begin_next_activity()</a>, <a href="#method_ba_Session__end">end()</a>, <a href="#method_ba_Session__end_activity">end_activity()</a>, <a href="#method_ba_Session__get_custom_menu_entries">get_custom_menu_entries()</a>, <a href="#method_ba_Session__getactivity">getactivity()</a>, <a href="#method_ba_Session__handlemessage">handlemessage()</a>, <a href="#method_ba_Session__on_player_leave">on_player_leave()</a>, <a href="#method_ba_Session__on_player_request">on_player_request()</a>, <a href="#method_ba_Session__on_team_leave">on_team_leave()</a>, <a href="#method_ba_Session__set_activity">set_activity()</a>, <a href="#method_ba_Session__transitioning_out_activity_was_freed">transitioning_out_activity_was_freed()</a></h5> <h5><a href="#method_ba_Session__begin_next_activity">begin_next_activity()</a>, <a href="#method_ba_Session__end">end()</a>, <a href="#method_ba_Session__end_activity">end_activity()</a>, <a href="#method_ba_Session__get_custom_menu_entries">get_custom_menu_entries()</a>, <a href="#method_ba_Session__getactivity">getactivity()</a>, <a href="#method_ba_Session__handlemessage">handlemessage()</a>, <a href="#method_ba_Session__on_player_leave">on_player_leave()</a>, <a href="#method_ba_Session__on_player_request">on_player_request()</a>, <a href="#method_ba_Session__on_team_leave">on_team_leave()</a>, <a href="#method_ba_Session__setactivity">setactivity()</a>, <a href="#method_ba_Session__transitioning_out_activity_was_freed">transitioning_out_activity_was_freed()</a></h5>
<h3>Methods Defined or Overridden:</h3> <h3>Methods Defined or Overridden:</h3>
<h5><a href="#method_ba_MultiTeamSession____init__">&lt;constructor&gt;</a>, <a href="#method_ba_MultiTeamSession__announce_game_results">announce_game_results()</a>, <a href="#method_ba_MultiTeamSession__get_ffa_series_length">get_ffa_series_length()</a>, <a href="#method_ba_MultiTeamSession__get_game_number">get_game_number()</a>, <a href="#method_ba_MultiTeamSession__get_max_players">get_max_players()</a>, <a href="#method_ba_MultiTeamSession__get_next_game_description">get_next_game_description()</a>, <a href="#method_ba_MultiTeamSession__get_series_length">get_series_length()</a>, <a href="#method_ba_MultiTeamSession__on_activity_end">on_activity_end()</a>, <a href="#method_ba_MultiTeamSession__on_team_join">on_team_join()</a></h5> <h5><a href="#method_ba_MultiTeamSession____init__">&lt;constructor&gt;</a>, <a href="#method_ba_MultiTeamSession__announce_game_results">announce_game_results()</a>, <a href="#method_ba_MultiTeamSession__get_ffa_series_length">get_ffa_series_length()</a>, <a href="#method_ba_MultiTeamSession__get_game_number">get_game_number()</a>, <a href="#method_ba_MultiTeamSession__get_max_players">get_max_players()</a>, <a href="#method_ba_MultiTeamSession__get_next_game_description">get_next_game_description()</a>, <a href="#method_ba_MultiTeamSession__get_series_length">get_series_length()</a>, <a href="#method_ba_MultiTeamSession__on_activity_end">on_activity_end()</a>, <a href="#method_ba_MultiTeamSession__on_team_join">on_team_join()</a></h5>
<dl> <dl>
@ -3843,13 +3866,29 @@ even if myactor is set to None.</p>
<p>Category: <a href="#class_category_Gameplay_Classes">Gameplay Classes</a></p> <p>Category: <a href="#class_category_Gameplay_Classes">Gameplay Classes</a></p>
<p> These correspond to <a href="#class_ba_SessionPlayer">ba.SessionPlayer</a> objects, but are created per activity <p> These correspond to <a href="#class_ba_SessionPlayer">ba.SessionPlayer</a> objects, but are associated with a
so that the activity can use its own custom player subclass. single <a href="#class_ba_Activity">ba.Activity</a> instance. This allows activities to specify their
</p> own custom <a href="#class_ba_Player">ba.Player</a> types.</p>
<h3>Attributes:</h3> <h3>Attributes:</h3>
<h5><a href="#attr_ba_Player__node">node</a>, <a href="#attr_ba_Player__position">position</a>, <a href="#attr_ba_Player__sessionplayer">sessionplayer</a></h5> <h5><a href="#attr_ba_Player__actor">actor</a>, <a href="#attr_ba_Player__gamedata">gamedata</a>, <a href="#attr_ba_Player__node">node</a>, <a href="#attr_ba_Player__position">position</a>, <a href="#attr_ba_Player__sessionplayer">sessionplayer</a>, <a href="#attr_ba_Player__team">team</a></h5>
<dl> <dl>
<dt><h4><a name="attr_ba_Player__actor">actor</a></h4></dt><dd>
<p><span>Optional[<a href="#class_ba_Actor">ba.Actor</a>]</span></p>
<p>The <a href="#class_ba_Actor">ba.Actor</a> associated with the player.</p>
</dd>
<dt><h4><a name="attr_ba_Player__gamedata">gamedata</a></h4></dt><dd>
<p><span>dict</span></p>
<p>Arbitrary values associated with the player.
Though it is encouraged that most player values be properly defined
on the <a href="#class_ba_Player">ba.Player</a> subclass, it may be useful for player-agnostic
objects to store values here. This dict is cleared when the player
leaves or expires so objects stored here will be disposed of at
the expected time, unlike the Player instance itself which may
continue to be referenced after it is no longer part of the game.</p>
</dd>
<dt><h4><a name="attr_ba_Player__node">node</a></h4></dt><dd> <dt><h4><a name="attr_ba_Player__node">node</a></h4></dt><dd>
<p><span><a href="#class_ba_Node">ba.Node</a></span></p> <p><span><a href="#class_ba_Node">ba.Node</a></span></p>
<p>A <a href="#class_ba_Node">ba.Node</a> of type 'player' associated with this Player.</p> <p>A <a href="#class_ba_Node">ba.Node</a> of type 'player' associated with this Player.</p>
@ -3859,10 +3898,9 @@ even if myactor is set to None.</p>
</dd> </dd>
<dt><h4><a name="attr_ba_Player__position">position</a></h4></dt><dd> <dt><h4><a name="attr_ba_Player__position">position</a></h4></dt><dd>
<p><span><a href="#class_ba_Vec3">ba.Vec3</a></span></p> <p><span><a href="#class_ba_Vec3">ba.Vec3</a></span></p>
<p>The position of the player, as defined by its current Actor.</p> <p>The position of the player, as defined by its current <a href="#class_ba_Actor">ba.Actor</a>.</p>
<p> This value should not be used when the player has no Actor, as <p> This value is undefined when the player has no Actor.</p>
it is undefined in that case.</p>
</dd> </dd>
<dt><h4><a name="attr_ba_Player__sessionplayer">sessionplayer</a></h4></dt><dd> <dt><h4><a name="attr_ba_Player__sessionplayer">sessionplayer</a></h4></dt><dd>
@ -3871,15 +3909,20 @@ even if myactor is set to None.</p>
<p> Throws a <a href="#class_ba_SessionPlayerNotFoundError">ba.SessionPlayerNotFoundError</a> if it does not exist.</p> <p> Throws a <a href="#class_ba_SessionPlayerNotFoundError">ba.SessionPlayerNotFoundError</a> if it does not exist.</p>
</dd>
<dt><h4><a name="attr_ba_Player__team">team</a></h4></dt><dd>
<p><span>TeamType</span></p>
<p>The <a href="#class_ba_Team">ba.Team</a> for this player.</p>
</dd> </dd>
</dl> </dl>
<h3>Methods:</h3> <h3>Methods:</h3>
<h5><a href="#method_ba_Player__assign_input_call">assign_input_call()</a>, <a href="#method_ba_Player__exists">exists()</a>, <a href="#method_ba_Player__get_icon">get_icon()</a>, <a href="#method_ba_Player__getname">getname()</a>, <a href="#method_ba_Player__is_alive">is_alive()</a>, <a href="#method_ba_Player__reset_input">reset_input()</a></h5> <h5><a href="#method_ba_Player__assigninput">assigninput()</a>, <a href="#method_ba_Player__exists">exists()</a>, <a href="#method_ba_Player__get_icon">get_icon()</a>, <a href="#method_ba_Player__getname">getname()</a>, <a href="#method_ba_Player__is_alive">is_alive()</a>, <a href="#method_ba_Player__on_expire">on_expire()</a>, <a href="#method_ba_Player__resetinput">resetinput()</a></h5>
<dl> <dl>
<dt><h4><a name="method_ba_Player__assign_input_call">assign_input_call()</a></dt></h4><dd> <dt><h4><a name="method_ba_Player__assigninput">assigninput()</a></dt></h4><dd>
<p><span>assign_input_call(self, inputtype: Union[str, Tuple[str, ...]], call: Callable) -&gt; None</span></p> <p><span>assigninput(self, inputtype: Union[str, Tuple[str, ...]], call: Callable) -&gt; None</span></p>
<p>assign_input_call(type: Union[str, Tuple[str, ...]], <p>assigninput(type: Union[str, Tuple[str, ...]],
call: Callable) -&gt; None</p> call: Callable) -&gt; None</p>
<p>Set the python callable to be run for one or more types of input. <p>Set the python callable to be run for one or more types of input.
@ -3896,7 +3939,10 @@ Valid type values are: 'jumpPress', 'jumpRelease', 'punchPress',
<p>Whether the underlying player still exists.</p> <p>Whether the underlying player still exists.</p>
<p>Most functionality will fail on a nonexistent player. <p>This will return False if the underlying <a href="#class_ba_SessionPlayer">ba.SessionPlayer</a> has
left the game or if the <a href="#class_ba_Activity">ba.Activity</a> this player was associated
with has ended.
Most functionality will fail on a nonexistent player.
Note that you can also use the boolean operator for this same Note that you can also use the boolean operator for this same
functionality, so a statement such as "if player" will do functionality, so a statement such as "if player" will do
the right thing both for Player objects and values of None.</p> the right thing both for Player objects and values of None.</p>
@ -3928,10 +3974,21 @@ name may include an icon.</p>
is_alive() method return True. False is returned otherwise.</p> is_alive() method return True. False is returned otherwise.</p>
</dd> </dd>
<dt><h4><a name="method_ba_Player__reset_input">reset_input()</a></dt></h4><dd> <dt><h4><a name="method_ba_Player__on_expire">on_expire()</a></dt></h4><dd>
<p><span>reset_input(self) -&gt; None</span></p> <p><span>on_expire(self) -&gt; None</span></p>
<p>reset_input() -&gt; None</p> <p>Can be overridden to handle player expiration.</p>
<p>The player expires when the Activity it is a part of expires.
Expired players should no longer run any game logic (which will
likely error). They should, however, remove any references to
players/teams/games/etc. which could prevent them from being freed.</p>
</dd>
<dt><h4><a name="method_ba_Player__resetinput">resetinput()</a></dt></h4><dd>
<p><span>resetinput(self) -&gt; None</span></p>
<p>resetinput() -&gt; None</p>
<p>Clears out the player's assigned input actions.</p> <p>Clears out the player's assigned input actions.</p>
@ -4362,7 +4419,7 @@ player that joins.</p>
</dd> </dd>
</dl> </dl>
<h3>Methods:</h3> <h3>Methods:</h3>
<h5><a href="#method_ba_Session____init__">&lt;constructor&gt;</a>, <a href="#method_ba_Session__begin_next_activity">begin_next_activity()</a>, <a href="#method_ba_Session__end">end()</a>, <a href="#method_ba_Session__end_activity">end_activity()</a>, <a href="#method_ba_Session__get_custom_menu_entries">get_custom_menu_entries()</a>, <a href="#method_ba_Session__getactivity">getactivity()</a>, <a href="#method_ba_Session__handlemessage">handlemessage()</a>, <a href="#method_ba_Session__on_activity_end">on_activity_end()</a>, <a href="#method_ba_Session__on_player_leave">on_player_leave()</a>, <a href="#method_ba_Session__on_player_request">on_player_request()</a>, <a href="#method_ba_Session__on_team_join">on_team_join()</a>, <a href="#method_ba_Session__on_team_leave">on_team_leave()</a>, <a href="#method_ba_Session__set_activity">set_activity()</a></h5> <h5><a href="#method_ba_Session____init__">&lt;constructor&gt;</a>, <a href="#method_ba_Session__begin_next_activity">begin_next_activity()</a>, <a href="#method_ba_Session__end">end()</a>, <a href="#method_ba_Session__end_activity">end_activity()</a>, <a href="#method_ba_Session__get_custom_menu_entries">get_custom_menu_entries()</a>, <a href="#method_ba_Session__getactivity">getactivity()</a>, <a href="#method_ba_Session__handlemessage">handlemessage()</a>, <a href="#method_ba_Session__on_activity_end">on_activity_end()</a>, <a href="#method_ba_Session__on_player_leave">on_player_leave()</a>, <a href="#method_ba_Session__on_player_request">on_player_request()</a>, <a href="#method_ba_Session__on_team_join">on_team_join()</a>, <a href="#method_ba_Session__on_team_leave">on_team_leave()</a>, <a href="#method_ba_Session__setactivity">setactivity()</a></h5>
<dl> <dl>
<dt><h4><a name="method_ba_Session____init__">&lt;constructor&gt;</a></dt></h4><dd> <dt><h4><a name="method_ba_Session____init__">&lt;constructor&gt;</a></dt></h4><dd>
<p><span>ba.Session(depsets: Sequence[<a href="#class_ba_DependencySet">ba.DependencySet</a>], team_names: Sequence[str] = None, team_colors: Sequence[Sequence[float]] = None, min_players: int = 1, max_players: int = 8)</span></p> <p><span>ba.Session(depsets: Sequence[<a href="#class_ba_DependencySet">ba.DependencySet</a>], team_names: Sequence[str] = None, team_colors: Sequence[Sequence[float]] = None, min_players: int = 1, max_players: int = 8)</span></p>
@ -4459,15 +4516,15 @@ another <a href="#class_ba_Activity">ba.Activity</a>.</p>
<p>Called when a <a href="#class_ba_Team">ba.Team</a> is leaving the session.</p> <p>Called when a <a href="#class_ba_Team">ba.Team</a> is leaving the session.</p>
</dd> </dd>
<dt><h4><a name="method_ba_Session__set_activity">set_activity()</a></dt></h4><dd> <dt><h4><a name="method_ba_Session__setactivity">setactivity()</a></dt></h4><dd>
<p><span>set_activity(self, activity: <a href="#class_ba_Activity">ba.Activity</a>) -&gt; None</span></p> <p><span>setactivity(self, activity: <a href="#class_ba_Activity">ba.Activity</a>) -&gt; None</span></p>
<p>Assign a new current <a href="#class_ba_Activity">ba.Activity</a> for the session.</p> <p>Assign a new current <a href="#class_ba_Activity">ba.Activity</a> for the session.</p>
<p>Note that this will not change the current context to the new <p>Note that this will not change the current context to the new
Activity's. Code must be run in the new activity's methods Activity's. Code must be run in the new activity's methods
(on_transition_in, etc) to get it. (so you can't do (on_transition_in, etc) to get it. (so you can't do
session.set_activity(foo) and then <a href="#function_ba_newnode">ba.newnode</a>() to add a node to foo)</p> session.setactivity(foo) and then <a href="#function_ba_newnode">ba.newnode</a>() to add a node to foo)</p>
</dd> </dd>
</dl> </dl>
@ -4499,7 +4556,7 @@ that a SessionPlayer is still present if retaining references to one
for any length of time.</p> for any length of time.</p>
<h3>Attributes:</h3> <h3>Attributes:</h3>
<h5><a href="#attr_ba_SessionPlayer__character">character</a>, <a href="#attr_ba_SessionPlayer__color">color</a>, <a href="#attr_ba_SessionPlayer__gamedata">gamedata</a>, <a href="#attr_ba_SessionPlayer__gameplayer">gameplayer</a>, <a href="#attr_ba_SessionPlayer__highlight">highlight</a>, <a href="#attr_ba_SessionPlayer__id">id</a>, <a href="#attr_ba_SessionPlayer__in_game">in_game</a>, <a href="#attr_ba_SessionPlayer__inputdevice">inputdevice</a>, <a href="#attr_ba_SessionPlayer__sessiondata">sessiondata</a>, <a href="#attr_ba_SessionPlayer__team">team</a></h5> <h5><a href="#attr_ba_SessionPlayer__character">character</a>, <a href="#attr_ba_SessionPlayer__color">color</a>, <a href="#attr_ba_SessionPlayer__gameplayer">gameplayer</a>, <a href="#attr_ba_SessionPlayer__highlight">highlight</a>, <a href="#attr_ba_SessionPlayer__id">id</a>, <a href="#attr_ba_SessionPlayer__in_game">in_game</a>, <a href="#attr_ba_SessionPlayer__inputdevice">inputdevice</a>, <a href="#attr_ba_SessionPlayer__sessiondata">sessiondata</a>, <a href="#attr_ba_SessionPlayer__team">team</a></h5>
<dl> <dl>
<dt><h4><a name="attr_ba_SessionPlayer__character">character</a></h4></dt><dd> <dt><h4><a name="attr_ba_SessionPlayer__character">character</a></h4></dt><dd>
<p><span> str</span></p> <p><span> str</span></p>
@ -4511,13 +4568,6 @@ for any length of time.</p>
<p>The base color for this Player. <p>The base color for this Player.
In team games this will match the <a href="#class_ba_SessionTeam">ba.SessionTeam</a>'s color.</p> In team games this will match the <a href="#class_ba_SessionTeam">ba.SessionTeam</a>'s color.</p>
</dd>
<dt><h4><a name="attr_ba_SessionPlayer__gamedata">gamedata</a></h4></dt><dd>
<p><span> Dict</span></p>
<p>A dict for use by the current <a href="#class_ba_Activity">ba.Activity</a> for
storing data associated with this Player.
This gets cleared for each new <a href="#class_ba_Activity">ba.Activity</a>.</p>
</dd> </dd>
<dt><h4><a name="attr_ba_SessionPlayer__gameplayer">gameplayer</a></h4></dt><dd> <dt><h4><a name="attr_ba_SessionPlayer__gameplayer">gameplayer</a></h4></dt><dd>
<p><span> Optional[<a href="#class_ba_Player">ba.Player</a>]</span></p> <p><span> Optional[<a href="#class_ba_Player">ba.Player</a>]</span></p>
@ -4568,10 +4618,10 @@ is still in its lobby selecting a team/etc. then a
</dd> </dd>
</dl> </dl>
<h3>Methods:</h3> <h3>Methods:</h3>
<h5><a href="#method_ba_SessionPlayer__assign_input_call">assign_input_call()</a>, <a href="#method_ba_SessionPlayer__exists">exists()</a>, <a href="#method_ba_SessionPlayer__get_account_id">get_account_id()</a>, <a href="#method_ba_SessionPlayer__get_icon">get_icon()</a>, <a href="#method_ba_SessionPlayer__getname">getname()</a>, <a href="#method_ba_SessionPlayer__remove_from_game">remove_from_game()</a>, <a href="#method_ba_SessionPlayer__reset_input">reset_input()</a>, <a href="#method_ba_SessionPlayer__set_name">set_name()</a></h5> <h5><a href="#method_ba_SessionPlayer__assigninput">assigninput()</a>, <a href="#method_ba_SessionPlayer__exists">exists()</a>, <a href="#method_ba_SessionPlayer__get_account_id">get_account_id()</a>, <a href="#method_ba_SessionPlayer__get_icon">get_icon()</a>, <a href="#method_ba_SessionPlayer__getname">getname()</a>, <a href="#method_ba_SessionPlayer__remove_from_game">remove_from_game()</a>, <a href="#method_ba_SessionPlayer__resetinput">resetinput()</a>, <a href="#method_ba_SessionPlayer__setname">setname()</a></h5>
<dl> <dl>
<dt><h4><a name="method_ba_SessionPlayer__assign_input_call">assign_input_call()</a></dt></h4><dd> <dt><h4><a name="method_ba_SessionPlayer__assigninput">assigninput()</a></dt></h4><dd>
<p><span>assign_input_call(type: Union[str, Tuple[str, ...]], <p><span>assigninput(type: Union[str, Tuple[str, ...]],
call: Callable) -&gt; None</span></p> call: Callable) -&gt; None</span></p>
<p>Set the python callable to be run for one or more types of input. <p>Set the python callable to be run for one or more types of input.
@ -4619,14 +4669,14 @@ name may include an icon.</p>
<p>Removes the player from the game.</p> <p>Removes the player from the game.</p>
</dd> </dd>
<dt><h4><a name="method_ba_SessionPlayer__reset_input">reset_input()</a></dt></h4><dd> <dt><h4><a name="method_ba_SessionPlayer__resetinput">resetinput()</a></dt></h4><dd>
<p><span>reset_input() -&gt; None</span></p> <p><span>resetinput() -&gt; None</span></p>
<p>Clears out the player's assigned input actions.</p> <p>Clears out the player's assigned input actions.</p>
</dd> </dd>
<dt><h4><a name="method_ba_SessionPlayer__set_name">set_name()</a></dt></h4><dd> <dt><h4><a name="method_ba_SessionPlayer__setname">setname()</a></dt></h4><dd>
<p><span>set_name(name: str, full_name: str = None, real: bool = True) <p><span>setname(name: str, full_name: str = None, real: bool = True)
-&gt; None</span></p> -&gt; None</span></p>
<p>Set the player's name to the provided string. <p>Set the player's name to the provided string.
@ -4857,7 +4907,7 @@ of the session.</p>
<h3>Methods:</h3> <h3>Methods:</h3>
<dl> <dl>
<dt><h4><a name="method_ba_StandLocation____init__">&lt;constructor&gt;</a></dt></h4><dd> <dt><h4><a name="method_ba_StandLocation____init__">&lt;constructor&gt;</a></dt></h4><dd>
<p><span>ba.StandLocation(position: _<a href="#class_ba_Vec3">ba.Vec3</a>, angle: Optional[float] = None)</span></p> <p><span>ba.StandLocation(position: <a href="#class_ba_Vec3">ba.Vec3</a>, angle: Optional[float] = None)</span></p>
</dd> </dd>
</dl> </dl>
@ -4902,7 +4952,7 @@ of the session.</p>
</p> </p>
<h3>Methods:</h3> <h3>Methods:</h3>
<h5><a href="#method_ba_Stats____init__">&lt;constructor&gt;</a>, <a href="#method_ba_Stats__get_records">get_records()</a>, <a href="#method_ba_Stats__getactivity">getactivity()</a>, <a href="#method_ba_Stats__player_got_hit">player_got_hit()</a>, <a href="#method_ba_Stats__player_scored">player_scored()</a>, <a href="#method_ba_Stats__player_was_killed">player_was_killed()</a>, <a href="#method_ba_Stats__register_player">register_player()</a>, <a href="#method_ba_Stats__reset">reset()</a>, <a href="#method_ba_Stats__reset_accum">reset_accum()</a>, <a href="#method_ba_Stats__set_activity">set_activity()</a></h5> <h5><a href="#method_ba_Stats____init__">&lt;constructor&gt;</a>, <a href="#method_ba_Stats__get_records">get_records()</a>, <a href="#method_ba_Stats__getactivity">getactivity()</a>, <a href="#method_ba_Stats__player_got_hit">player_got_hit()</a>, <a href="#method_ba_Stats__player_scored">player_scored()</a>, <a href="#method_ba_Stats__player_was_killed">player_was_killed()</a>, <a href="#method_ba_Stats__register_player">register_player()</a>, <a href="#method_ba_Stats__reset">reset()</a>, <a href="#method_ba_Stats__reset_accum">reset_accum()</a>, <a href="#method_ba_Stats__setactivity">setactivity()</a></h5>
<dl> <dl>
<dt><h4><a name="method_ba_Stats____init__">&lt;constructor&gt;</a></dt></h4><dd> <dt><h4><a name="method_ba_Stats____init__">&lt;constructor&gt;</a></dt></h4><dd>
<p><span>ba.Stats()</span></p> <p><span>ba.Stats()</span></p>
@ -4960,8 +5010,8 @@ of the session.</p>
<p>Reset per-sound sub-scores.</p> <p>Reset per-sound sub-scores.</p>
</dd> </dd>
<dt><h4><a name="method_ba_Stats__set_activity">set_activity()</a></dt></h4><dd> <dt><h4><a name="method_ba_Stats__setactivity">setactivity()</a></dt></h4><dd>
<p><span>set_activity(self, activity: Optional[<a href="#class_ba_Activity">ba.Activity</a>]) -&gt; None</span></p> <p><span>setactivity(self, activity: Optional[<a href="#class_ba_Activity">ba.Activity</a>]) -&gt; None</span></p>
<p>Set the current activity for this instance.</p> <p>Set the current activity for this instance.</p>
@ -4979,7 +5029,19 @@ of the session.</p>
</p> </p>
<h3>Attributes:</h3> <h3>Attributes:</h3>
<h5><a href="#attr_ba_Team__gamedata">gamedata</a>, <a href="#attr_ba_Team__sessionteam">sessionteam</a></h5>
<dl> <dl>
<dt><h4><a name="attr_ba_Team__gamedata">gamedata</a></h4></dt><dd>
<p><span>dict</span></p>
<p>Arbitrary values associated with the team.
Though it is encouraged that most player values be properly defined
on the <a href="#class_ba_Team">ba.Team</a> subclass, it may be useful for player-agnostic
objects to store values here. This dict is cleared when the team
leaves or expires so objects stored here will be disposed of at
the expected time, unlike the Team instance itself which may
continue to be referenced after it is no longer part of the game.</p>
</dd>
<dt><h4><a name="attr_ba_Team__sessionteam">sessionteam</a></h4></dt><dd> <dt><h4><a name="attr_ba_Team__sessionteam">sessionteam</a></h4></dt><dd>
<p><span>SessionTeam</span></p> <p><span>SessionTeam</span></p>
<p>Return the <a href="#class_ba_SessionTeam">ba.SessionTeam</a> corresponding to this Team.</p> <p>Return the <a href="#class_ba_SessionTeam">ba.SessionTeam</a> corresponding to this Team.</p>
@ -4989,12 +5051,19 @@ of the session.</p>
</dd> </dd>
</dl> </dl>
<h3>Methods:</h3> <h3>Methods:</h3>
<h5><a href="#method_ba_Team__manual_init">manual_init()</a>, <a href="#method_ba_Team__on_expire">on_expire()</a></h5>
<dl> <dl>
<dt><h4><a name="method_ba_Team__manual_init">manual_init()</a></dt></h4><dd> <dt><h4><a name="method_ba_Team__manual_init">manual_init()</a></dt></h4><dd>
<p><span>manual_init(self, team_id: int, name: Union[<a href="#class_ba_Lstr">ba.Lstr</a>, str], color: Tuple[float, ...]) -&gt; None</span></p> <p><span>manual_init(self, team_id: int, name: Union[<a href="#class_ba_Lstr">ba.Lstr</a>, str], color: Tuple[float, ...]) -&gt; None</span></p>
<p>Manually init a team for uses such as bots.</p> <p>Manually init a team for uses such as bots.</p>
</dd>
<dt><h4><a name="method_ba_Team__on_expire">on_expire()</a></dt></h4><dd>
<p><span>on_expire(self) -&gt; None</span></p>
<p>Can be overridden to handle team expiration.</p>
</dd> </dd>
</dl> </dl>
<hr> <hr>
@ -5011,7 +5080,7 @@ of the session.</p>
<h3>Attributes Inherited:</h3> <h3>Attributes Inherited:</h3>
<h5><a href="#attr_ba_Activity__players">players</a>, <a href="#attr_ba_Activity__settings_raw">settings_raw</a>, <a href="#attr_ba_Activity__teams">teams</a></h5> <h5><a href="#attr_ba_Activity__players">players</a>, <a href="#attr_ba_Activity__settings_raw">settings_raw</a>, <a href="#attr_ba_Activity__teams">teams</a></h5>
<h3>Attributes Defined Here:</h3> <h3>Attributes Defined Here:</h3>
<h5><a href="#attr_ba_TeamGameActivity__expired">expired</a>, <a href="#attr_ba_TeamGameActivity__globalsnode">globalsnode</a>, <a href="#attr_ba_TeamGameActivity__map">map</a>, <a href="#attr_ba_TeamGameActivity__playertype">playertype</a>, <a href="#attr_ba_TeamGameActivity__session">session</a>, <a href="#attr_ba_TeamGameActivity__stats">stats</a>, <a href="#attr_ba_TeamGameActivity__teamtype">teamtype</a></h5> <h5><a href="#attr_ba_TeamGameActivity__expired">expired</a>, <a href="#attr_ba_TeamGameActivity__gamedata">gamedata</a>, <a href="#attr_ba_TeamGameActivity__globalsnode">globalsnode</a>, <a href="#attr_ba_TeamGameActivity__map">map</a>, <a href="#attr_ba_TeamGameActivity__playertype">playertype</a>, <a href="#attr_ba_TeamGameActivity__session">session</a>, <a href="#attr_ba_TeamGameActivity__stats">stats</a>, <a href="#attr_ba_TeamGameActivity__teamtype">teamtype</a></h5>
<dl> <dl>
<dt><h4><a name="attr_ba_TeamGameActivity__expired">expired</a></h4></dt><dd> <dt><h4><a name="attr_ba_TeamGameActivity__expired">expired</a></h4></dt><dd>
<p><span>bool</span></p> <p><span>bool</span></p>
@ -5021,6 +5090,14 @@ of the session.</p>
At this point no new nodes, timers, etc should be made, At this point no new nodes, timers, etc should be made,
run, etc, and the activity should be considered a 'zombie'.</p> run, etc, and the activity should be considered a 'zombie'.</p>
</dd>
<dt><h4><a name="attr_ba_TeamGameActivity__gamedata">gamedata</a></h4></dt><dd>
<p><span>dict</span></p>
<p>Entities needing to store simple data with an activity can put it
here. This dict will be deleted when the activity expires, so contained
objects generally do not need to worry about handling expired
activities.</p>
</dd> </dd>
<dt><h4><a name="attr_ba_TeamGameActivity__globalsnode">globalsnode</a></h4></dt><dd> <dt><h4><a name="attr_ba_TeamGameActivity__globalsnode">globalsnode</a></h4></dt><dd>
<p><span><a href="#class_ba_Node">ba.Node</a></span></p> <p><span><a href="#class_ba_Node">ba.Node</a></span></p>
@ -5061,7 +5138,7 @@ of the session.</p>
</dd> </dd>
</dl> </dl>
<h3>Methods Inherited:</h3> <h3>Methods Inherited:</h3>
<h5><a href="#method_ba_GameActivity__add_actor_weak_ref">add_actor_weak_ref()</a>, <a href="#method_ba_GameActivity__add_player">add_player()</a>, <a href="#method_ba_GameActivity__add_team">add_team()</a>, <a href="#method_ba_GameActivity__begin">begin()</a>, <a href="#method_ba_GameActivity__continue_or_end_game">continue_or_end_game()</a>, <a href="#method_ba_GameActivity__create_player">create_player()</a>, <a href="#method_ba_GameActivity__create_settings_ui">create_settings_ui()</a>, <a href="#method_ba_GameActivity__create_team">create_team()</a>, <a href="#method_ba_GameActivity__dep_is_present">dep_is_present()</a>, <a href="#method_ba_GameActivity__destroy">destroy()</a>, <a href="#method_ba_GameActivity__end_game">end_game()</a>, <a href="#method_ba_GameActivity__get_description">get_description()</a>, <a href="#method_ba_GameActivity__get_description_display_string">get_description_display_string()</a>, <a href="#method_ba_GameActivity__get_display_string">get_display_string()</a>, <a href="#method_ba_GameActivity__get_dynamic_deps">get_dynamic_deps()</a>, <a href="#method_ba_GameActivity__get_game_settings">get_game_settings()</a>, <a href="#method_ba_GameActivity__get_instance_description">get_instance_description()</a>, <a href="#method_ba_GameActivity__get_instance_description_short">get_instance_description_short()</a>, <a href="#method_ba_GameActivity__get_instance_display_string">get_instance_display_string()</a>, <a href="#method_ba_GameActivity__get_instance_scoreboard_display_string">get_instance_scoreboard_display_string()</a>, <a href="#method_ba_GameActivity__get_score_info">get_score_info()</a>, <a href="#method_ba_GameActivity__get_settings_display_string">get_settings_display_string()</a>, <a href="#method_ba_GameActivity__get_supported_maps">get_supported_maps()</a>, <a href="#method_ba_GameActivity__get_team_display_string">get_team_display_string()</a>, <a href="#method_ba_GameActivity__getname">getname()</a>, <a href="#method_ba_GameActivity__handlemessage">handlemessage()</a>, <a href="#method_ba_GameActivity__has_begun">has_begun()</a>, <a href="#method_ba_GameActivity__has_ended">has_ended()</a>, <a href="#method_ba_GameActivity__has_transitioned_in">has_transitioned_in()</a>, <a href="#method_ba_GameActivity__is_transitioning_out">is_transitioning_out()</a>, <a href="#method_ba_GameActivity__is_waiting_for_continue">is_waiting_for_continue()</a>, <a href="#method_ba_GameActivity__on_continue">on_continue()</a>, <a href="#method_ba_GameActivity__on_expire">on_expire()</a>, <a href="#method_ba_GameActivity__on_player_join">on_player_join()</a>, <a href="#method_ba_GameActivity__on_player_leave">on_player_leave()</a>, <a href="#method_ba_GameActivity__on_team_join">on_team_join()</a>, <a href="#method_ba_GameActivity__on_team_leave">on_team_leave()</a>, <a href="#method_ba_GameActivity__on_transition_out">on_transition_out()</a>, <a href="#method_ba_GameActivity__remove_player">remove_player()</a>, <a href="#method_ba_GameActivity__remove_team">remove_team()</a>, <a href="#method_ba_GameActivity__respawn_player">respawn_player()</a>, <a href="#method_ba_GameActivity__retain_actor">retain_actor()</a>, <a href="#method_ba_GameActivity__set_has_ended">set_has_ended()</a>, <a href="#method_ba_GameActivity__setup_standard_powerup_drops">setup_standard_powerup_drops()</a>, <a href="#method_ba_GameActivity__setup_standard_time_limit">setup_standard_time_limit()</a>, <a href="#method_ba_GameActivity__show_zoom_message">show_zoom_message()</a>, <a href="#method_ba_GameActivity__spawn_player">spawn_player()</a>, <a href="#method_ba_GameActivity__spawn_player_if_exists">spawn_player_if_exists()</a>, <a href="#method_ba_GameActivity__transition_in">transition_in()</a>, <a href="#method_ba_GameActivity__transition_out">transition_out()</a></h5> <h5><a href="#method_ba_GameActivity__add_actor_weak_ref">add_actor_weak_ref()</a>, <a href="#method_ba_GameActivity__add_player">add_player()</a>, <a href="#method_ba_GameActivity__add_team">add_team()</a>, <a href="#method_ba_GameActivity__begin">begin()</a>, <a href="#method_ba_GameActivity__continue_or_end_game">continue_or_end_game()</a>, <a href="#method_ba_GameActivity__create_player">create_player()</a>, <a href="#method_ba_GameActivity__create_settings_ui">create_settings_ui()</a>, <a href="#method_ba_GameActivity__create_team">create_team()</a>, <a href="#method_ba_GameActivity__dep_is_present">dep_is_present()</a>, <a href="#method_ba_GameActivity__end_game">end_game()</a>, <a href="#method_ba_GameActivity__expire">expire()</a>, <a href="#method_ba_GameActivity__get_description">get_description()</a>, <a href="#method_ba_GameActivity__get_description_display_string">get_description_display_string()</a>, <a href="#method_ba_GameActivity__get_display_string">get_display_string()</a>, <a href="#method_ba_GameActivity__get_dynamic_deps">get_dynamic_deps()</a>, <a href="#method_ba_GameActivity__get_game_settings">get_game_settings()</a>, <a href="#method_ba_GameActivity__get_instance_description">get_instance_description()</a>, <a href="#method_ba_GameActivity__get_instance_description_short">get_instance_description_short()</a>, <a href="#method_ba_GameActivity__get_instance_display_string">get_instance_display_string()</a>, <a href="#method_ba_GameActivity__get_instance_scoreboard_display_string">get_instance_scoreboard_display_string()</a>, <a href="#method_ba_GameActivity__get_score_info">get_score_info()</a>, <a href="#method_ba_GameActivity__get_settings_display_string">get_settings_display_string()</a>, <a href="#method_ba_GameActivity__get_supported_maps">get_supported_maps()</a>, <a href="#method_ba_GameActivity__get_team_display_string">get_team_display_string()</a>, <a href="#method_ba_GameActivity__getname">getname()</a>, <a href="#method_ba_GameActivity__handlemessage">handlemessage()</a>, <a href="#method_ba_GameActivity__has_begun">has_begun()</a>, <a href="#method_ba_GameActivity__has_ended">has_ended()</a>, <a href="#method_ba_GameActivity__has_transitioned_in">has_transitioned_in()</a>, <a href="#method_ba_GameActivity__is_transitioning_out">is_transitioning_out()</a>, <a href="#method_ba_GameActivity__is_waiting_for_continue">is_waiting_for_continue()</a>, <a href="#method_ba_GameActivity__on_continue">on_continue()</a>, <a href="#method_ba_GameActivity__on_expire">on_expire()</a>, <a href="#method_ba_GameActivity__on_player_join">on_player_join()</a>, <a href="#method_ba_GameActivity__on_player_leave">on_player_leave()</a>, <a href="#method_ba_GameActivity__on_team_join">on_team_join()</a>, <a href="#method_ba_GameActivity__on_team_leave">on_team_leave()</a>, <a href="#method_ba_GameActivity__on_transition_out">on_transition_out()</a>, <a href="#method_ba_GameActivity__remove_player">remove_player()</a>, <a href="#method_ba_GameActivity__remove_team">remove_team()</a>, <a href="#method_ba_GameActivity__respawn_player">respawn_player()</a>, <a href="#method_ba_GameActivity__retain_actor">retain_actor()</a>, <a href="#method_ba_GameActivity__set_has_ended">set_has_ended()</a>, <a href="#method_ba_GameActivity__setup_standard_powerup_drops">setup_standard_powerup_drops()</a>, <a href="#method_ba_GameActivity__setup_standard_time_limit">setup_standard_time_limit()</a>, <a href="#method_ba_GameActivity__show_zoom_message">show_zoom_message()</a>, <a href="#method_ba_GameActivity__spawn_player">spawn_player()</a>, <a href="#method_ba_GameActivity__spawn_player_if_exists">spawn_player_if_exists()</a>, <a href="#method_ba_GameActivity__transition_in">transition_in()</a>, <a href="#method_ba_GameActivity__transition_out">transition_out()</a></h5>
<h3>Methods Defined or Overridden:</h3> <h3>Methods Defined or Overridden:</h3>
<h5><a href="#method_ba_TeamGameActivity____init__">&lt;constructor&gt;</a>, <a href="#method_ba_TeamGameActivity__end">end()</a>, <a href="#method_ba_TeamGameActivity__on_begin">on_begin()</a>, <a href="#method_ba_TeamGameActivity__on_transition_in">on_transition_in()</a>, <a href="#method_ba_TeamGameActivity__spawn_player_spaz">spawn_player_spaz()</a>, <a href="#method_ba_TeamGameActivity__supports_session_type">supports_session_type()</a></h5> <h5><a href="#method_ba_TeamGameActivity____init__">&lt;constructor&gt;</a>, <a href="#method_ba_TeamGameActivity__end">end()</a>, <a href="#method_ba_TeamGameActivity__on_begin">on_begin()</a>, <a href="#method_ba_TeamGameActivity__on_transition_in">on_transition_in()</a>, <a href="#method_ba_TeamGameActivity__spawn_player_spaz">spawn_player_spaz()</a>, <a href="#method_ba_TeamGameActivity__supports_session_type">supports_session_type()</a></h5>
<dl> <dl>