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/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",
"build/prefab/linux-server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/36/5a/025457e87759c13a5067617816cd",
"build/prefab/linux-server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/66/49/fb5663472ab1f4bd32f5748ba4d7",
"build/prefab/linux/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/44/3e/6eb652f11d24c22f87ae8aab399b",
"build/prefab/linux/release/ballisticacore": "https://files.ballistica.net/cache/ba1/f4/57/7e48692454b644a7111902f12b58",
"build/prefab/mac-server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/18/55/246021de09b021993e8bdbdb19a6",
"build/prefab/mac-server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/3f/ea/079aa2649359d2024b7c20dd19d0",
"build/prefab/mac/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/05/5d/2608f732d75799cfe09c6947736a",
"build/prefab/mac/release/ballisticacore": "https://files.ballistica.net/cache/ba1/ef/20/4f2abb279d24dced1f11f83fbe78",
"build/prefab/windows-server/debug/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/f9/36/28081d9962d3f91c286663357e80",
"build/prefab/windows-server/release/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/51/55/83ceb6ffb806c75815e5f9e88008",
"build/prefab/windows/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/46/ea/461f76e113d3a02da3c61ea04bdb",
"build/prefab/windows/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/83/af/a8ce32760e4dc300ac861f745615"
"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/38/e6/d372324c7c08b5b300490fa5594e",
"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/cc/76/3f6356dd599091f5955ce349593b",
"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/1c/bd/4c73637ee172630ee00145032ce7",
"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/aa/24/5426cb7e6ca01b9e5d67ad3217b0",
"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/7c/fb/7e0880c1ab90b0484cdedc41c8ae",
"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/6b/7c/8407f4a7326b8b19f3e187d3ffcb"
}

View File

@ -105,6 +105,7 @@
<w>assetpath</w>
<w>assettype</w>
<w>assettypestr</w>
<w>assigninput</w>
<w>astc</w>
<w>astcenc</w>
<w>astroid</w>
@ -765,6 +766,7 @@
<w>getlog</w>
<w>getmaps</w>
<w>getmodel</w>
<w>getname</w>
<w>getnodes</w>
<w>getnodetype</w>
<w>getopt</w>
@ -1382,6 +1384,7 @@
<w>posixpath</w>
<w>posixsubprocess</w>
<w>postinit</w>
<w>postinited</w>
<w>poststr</w>
<w>powerdown</w>
<w>powersgiven</w>
@ -1539,6 +1542,7 @@
<w>reqtype</w>
<w>reqtypes</w>
<w>resample</w>
<w>resetinput</w>
<w>resourcetypeinfo</w>
<w>respawn</w>
<w>respawnable</w>
@ -1557,6 +1561,7 @@
<w>rfudge</w>
<w>rgba</w>
<w>rlcompleter</w>
<w>rlock</w>
<w>rmats</w>
<w>rmine</w>
<w>robotparser</w>
@ -1580,6 +1585,7 @@
<w>rval</w>
<w>safecolor</w>
<w>safesetattr</w>
<w>safesetcolor</w>
<w>saitek</w>
<w>samsung</w>
<w>sandboxing</w>
@ -1641,12 +1647,18 @@
<w>sessionglobalsnode</w>
<w>sessionname</w>
<w>sessionplayer</w>
<w>sessionplayers</w>
<w>sessionteam</w>
<w>sessionteams</w>
<w>sessiontype</w>
<w>setactivity</w>
<w>setalpha</w>
<w>setbuild</w>
<w>setdata</w>
<w>setlanguage</w>
<w>setmusic</w>
<w>setname</w>
<w>setnode</w>
<w>setsticky</w>
<w>settingname</w>
<w>setversion</w>
@ -2053,6 +2065,7 @@
<w>webpages</w>
<w>whatevs</w>
<w>wheee</w>
<w>whos</w>
<w>widgetdeathtime</w>
<w>wiimote</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)
# SOURCES_HASH=33373537027192610824913965080968605548
# SOURCES_HASH=172645630847706592745162067676099526148
# I'm sorry Pylint. I know this file saddens you. Be strong.
# pylint: disable=useless-suppression
@ -76,13 +76,6 @@ def _uninferrable() -> Any:
class ActivityData:
"""(internal)"""
def destroy(self) -> None:
"""destroy() -> None
Destroys the internal data for the activity
"""
return None
def exists(self) -> bool:
"""exists() -> bool
@ -91,6 +84,13 @@ class ActivityData:
"""
return bool()
def expire(self) -> None:
"""expire() -> None
Expires the internal data for the activity
"""
return None
def make_foreground(self) -> None:
"""make_foreground() -> None
@ -832,11 +832,6 @@ class SessionPlayer:
storing data associated with this player.
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
The input device associated with the player.
@ -860,16 +855,15 @@ class SessionPlayer:
in_game: bool
team: ba.SessionTeam
sessiondata: Dict
gamedata: Dict
inputdevice: ba.InputDevice
color: Sequence[float]
highlight: Sequence[float]
character: str
gameplayer: Optional[ba.Player]
def assign_input_call(self, type: Union[str, Tuple[str, ...]],
call: Callable) -> None:
"""assign_input_call(type: Union[str, Tuple[str, ...]],
def assigninput(self, type: Union[str, Tuple[str, ...]],
call: Callable) -> None:
"""assigninput(type: Union[str, Tuple[str, ...]],
call: Callable) -> None
Set the python callable to be run for one or more types of input.
@ -930,36 +924,13 @@ class SessionPlayer:
"""
return None
def reset(self) -> None:
"""reset() -> None
(internal)
"""
return None
def reset_input(self) -> None:
"""reset_input() -> None
def resetinput(self) -> None:
"""resetinput() -> None
Clears out the player's assigned input actions.
"""
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,
tint_color: Sequence[float],
tint2_color: Sequence[float]) -> None:
@ -970,11 +941,27 @@ class SessionPlayer:
"""
return None
def set_name(self,
name: str,
full_name: str = None,
real: bool = True) -> None:
"""set_name(name: str, full_name: str = None, real: bool = True)
def setactivity(self, activity: Optional[ba.Activity]) -> None:
"""setactivity(activity: Optional[ba.Activity]) -> None
(internal)
"""
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
Set the player's name to the provided string.
@ -983,8 +970,8 @@ class SessionPlayer:
"""
return None
def set_node(self, node: Optional[Node]) -> None:
"""set_node(node: Optional[Node]) -> None
def setnode(self, node: Optional[Node]) -> None:
"""setnode(node: Optional[Node]) -> None
(internal)
"""

View File

@ -27,10 +27,10 @@ from typing import TYPE_CHECKING, Generic, TypeVar
from ba._team import Team
from ba._player import Player
from ba._error import (print_exception, SessionTeamNotFoundError,
NodeNotFoundError)
SessionPlayerNotFoundError, NodeNotFoundError)
from ba._dependency import DependencyComponent
from ba._general import Call, verify_object_death
from ba._messages import UNHANDLED, DieMessage, DeathType
from ba._messages import UNHANDLED
import _ba
if TYPE_CHECKING:
@ -143,7 +143,7 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
def __init__(self, settings: Dict[str, Any]):
"""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
to the activity.
@ -189,6 +189,10 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
self._has_ended = False
self._activity_death_check_timer: Optional[ba.Timer] = None
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
# 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.
self.teams = []
self.players = []
self.lobby = None
self._stats: Optional[ba.Stats] = None
self._gamedata: Optional[dict] = {}
def __del__(self) -> None:
@ -258,6 +264,17 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
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
def expired(self) -> bool:
"""Whether the activity is expired.
@ -282,7 +299,7 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
"""(internal)"""
self._has_ended = val
def destroy(self) -> None:
def expire(self) -> None:
"""Begin the process of tearing down the activity.
(internal)
@ -424,7 +441,6 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
(internal)
"""
from ba._general import WeakCall
assert not self._has_transitioned_in
self._has_transitioned_in = True
@ -457,10 +473,13 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
glb.vignette_outer = prev_globals.vignette_outer
glb.vignette_inner = prev_globals.vignette_inner
# Start pruning our transient actors periodically.
self._prune_dead_actors_timer = _ba.Timer(
5.17, WeakCall(self._prune_dead_actors), repeat=True)
# Start pruning our various things periodically.
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.
self._activity_data.start()
@ -556,12 +575,12 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
def add_player(self, sessionplayer: ba.SessionPlayer) -> None:
"""(internal)"""
assert sessionplayer.team is not None
sessionplayer.reset_input()
sessionplayer.resetinput()
sessionteam = sessionplayer.team
assert sessionplayer in sessionteam.players
team = sessionteam.gameteam
assert team is not None
sessionplayer.set_activity(self)
sessionplayer.setactivity(self)
with _ba.Context(self):
sessionplayer.gameplayer = player = self.create_player(
sessionplayer)
@ -581,10 +600,10 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
print_exception('Error in on_player_join for', self)
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
# the player has been added to.
(internal)
"""
assert not self.expired
player: Any = sessionplayer.gameplayer
@ -605,30 +624,29 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
# verify_object_death(player)
with _ba.Context(self):
# Make a decent attempt to persevere if user code breaks.
try:
self.on_player_leave(player)
except Exception:
print_exception(f'Error in on_player_leave for {self}')
try:
# If they have an actor, kill it.
if player.actor:
player.actor.handlemessage(
DieMessage(how=DeathType.LEFT_GAME))
player.actor = None
player.leave()
except Exception:
print_exception(
f'Error killing player actor on leave for {self}')
try:
player.reset()
sessionplayer.reset()
sessionplayer.set_node(None)
sessionplayer.set_activity(None)
except Exception:
print_exception(f'Error resetting player for {self}')
print_exception(f'Error on leave for {player} in {self}')
self._reset_session_player_for_no_activity(sessionplayer)
# Add the player to a list to keep it around for a while. This is
# to discourage logic from firing on player object death, which
# may not happen until activity end if something is holding refs
# to it.
self._delay_delete_players.append(player)
self._players_that_left.append(weakref.ref(player))
def add_team(self, sessionteam: ba.SessionTeam) -> None:
"""(internal)"""
"""Add a team to the Activity
(internal)
"""
assert not self.expired
with _ba.Context(self):
@ -641,25 +659,20 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
print_exception(f'Error in on_team_join for {self}')
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
# been added to.
(internal)
"""
assert not self.expired
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 team in self.teams
self.teams.remove(team)
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):
# Make a decent attempt to persevere if user code breaks.
try:
@ -667,12 +680,42 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
except Exception:
print_exception(f'Error in on_team_leave for {self}')
try:
sessionteam.reset_gamedata()
team.leave()
except Exception:
print_exception(f'Error in reset_gamedata for {self}')
print_exception(f'Error on leave for {team} in {self}')
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:
"""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
# base class types, but also warn the user since this will mean
# 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:
self._playertype = type(self).__orig_bases__[-1].__args__[0]
if not isinstance(self._playertype, type):
@ -749,9 +792,31 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
try:
self.on_expire()
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:
actor = actor_ref()
if actor is not None:
@ -760,35 +825,58 @@ class Activity(DependencyComponent, Generic[PlayerType, TeamType]):
actor.on_expire()
except Exception:
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:
# This should allow our ba.Player instance to be freed.
# Complain if that doesn't happen.
verify_object_death(player)
try:
# This should allow our ba.Player instance to die.
# 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()
player.expire()
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:
# 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:
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.reset_gamedata()
except SessionTeamNotFoundError:
# It is expected that Team objects may last longer than
# 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)
pass
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
# our activity might not go down if we don't. This will kill all
# Timers, Nodes, etc, which should clear up any remaining refs to our
# Actors and Activity and allow us to die peacefully.
try:
self._activity_data.destroy()
except Exception:
print_exception(
'Error during ba.Activity._expire() destroying data:')
def _prune_delay_deletes(self) -> None:
self._delay_delete_players.clear()
self._delay_delete_teams.clear()
# Clear out any dead weak-refs.
self._teams_that_left = [
t for t in self._teams_that_left if t() is not None
]
self._players_that_left = [
p for p in self._players_that_left if p() is not None
]
def _prune_dead_actors(self) -> None:
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.
# (though theoretically that would be ok).
if not self.is_transitioning_out() and player:
player.assign_input_call(
player.assigninput(
('jumpPress', 'punchPress', 'bombPress', 'pickUpPress'),
self._player_press)

View File

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

View File

@ -98,7 +98,7 @@ class CoopSession(Session):
self._custom_menu_ui: List[Dict[str, Any]] = []
# 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_level_name: Optional[str] = None
@ -298,7 +298,7 @@ class CoopSession(Session):
and not app.kiosk_mode):
if self._tutorial_activity is None:
raise RuntimeError('Tutorial not preloaded properly.')
self.set_activity(self._tutorial_activity)
self.setactivity(self._tutorial_activity)
self._tutorial_activity = None
self._ran_tutorial_activity = True
self._custom_menu_ui = []
@ -313,10 +313,10 @@ class CoopSession(Session):
# Skip players that are still choosing a team.
if player.in_game:
self.stats.register_player(player)
self.stats.set_activity(next_game)
self.stats.setactivity(next_game)
# Now flip the current activity.
self.set_activity(next_game)
self.setactivity(next_game)
if not app.kiosk_mode:
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
# actual round.
elif isinstance(activity, TutorialActivity):
self.set_activity(_ba.new_activity(TransitionActivity))
self.setactivity(_ba.new_activity(TransitionActivity))
else:
player_info: List[ba.PlayerInfo]
@ -392,9 +392,9 @@ class CoopSession(Session):
if outcome == 'restart':
# This will pop up back in the same round.
self.set_activity(_ba.new_activity(TransitionActivity))
self.setactivity(_ba.new_activity(TransitionActivity))
else:
self.set_activity(
self.setactivity(
_ba.new_activity(
CoopScoreScreen, {
'player_info': player_info,

View File

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

View File

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

View File

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

View File

@ -133,7 +133,7 @@ class MultiTeamSession(Session):
self._instantiate_next_game()
# 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:
"""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
# matter what.
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
# If we're leaving the tutorial activity, pop a transition activity
# to transition us into a round gracefully (otherwise we'd snap from
# one terrain to another instantly).
elif isinstance(activity, TutorialActivity):
self.set_activity(
self.setactivity(
_ba.new_activity(_activitytypes.TransitionActivity))
# If we're in a between-round activity or a restart-activity, hop
@ -226,10 +226,10 @@ class MultiTeamSession(Session):
has_team = False
if has_team:
self.stats.register_player(player)
self.stats.set_activity(next_game)
self.stats.setactivity(next_game)
# 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.
else:

View File

@ -26,6 +26,8 @@ from dataclasses import dataclass
from typing import TYPE_CHECKING, TypeVar, Generic
import _ba
from ba._error import SessionPlayerNotFoundError, print_exception
from ba._messages import DeathType, DieMessage
if TYPE_CHECKING:
from typing import (Type, Optional, Sequence, Dict, Any, Union, Tuple,
@ -52,7 +54,7 @@ class StandLocation:
Category: Gameplay Classes
"""
position: _ba.Vec3
position: ba.Vec3
angle: Optional[float] = None
@ -61,27 +63,35 @@ class Player(Generic[TeamType]):
Category: Gameplay Classes
These correspond to ba.SessionPlayer objects, but are created per activity
so that the activity can use its own custom player subclass.
These correspond to ba.SessionPlayer objects, but are associated with a
single ba.Activity instance. This allows activities to specify their
own custom ba.Player types.
Attributes:
actor
The ba.Actor associated with the player.
"""
# Defining these types at the class level instead of in __init__ so
# that types are introspectable (these are still instance attrs).
team: TeamType
# These are instance attrs but we define them at the type level so
# their type annotations are introspectable (for docs generation).
character: str
actor: Optional[ba.Actor]
color: Sequence[float]
highlight: Sequence[float]
_team: TeamType
_sessionplayer: ba.SessionPlayer
_nodeactor: Optional[ba.NodeActor]
# Should aim to kill this eventually (at least gamedata).
# Game-specific data can be tacked on to the per-game player class.
sessiondata: Dict
gamedata: Dict
_expired: bool
_postinited: bool
sessiondata: dict
_gamedata: dict
# NOTE: avoiding having any __init__() here since it seems to not
# 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:
"""Wire up a newly created player.
@ -108,23 +118,82 @@ class Player(Generic[TeamType]):
self.character = sessionplayer.character
self.color = sessionplayer.color
self.highlight = sessionplayer.highlight
self.team = sessionplayer.team.gameteam # type: ignore
assert self.team is not None
self._team = sessionplayer.team.gameteam # type: ignore
assert self._team is not None
self.sessiondata = sessionplayer.sessiondata
self.gamedata = sessionplayer.gamedata
# Create our player node in the current activity.
# Note: do we want to save a few cycles here by managing our player
# node manually instead of wrapping it in a NodeActor?
self._gamedata = {}
self._expired = False
self._postinited = True
node = _ba.newnode('player', attrs={'playerID': sessionplayer.id})
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.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
def sessionplayer(self) -> ba.SessionPlayer:
@ -132,10 +201,10 @@ class Player(Generic[TeamType]):
Throws a ba.SessionPlayerNotFoundError if it does not exist.
"""
assert self._postinited
if bool(self._sessionplayer):
return self._sessionplayer
from ba import _error
raise _error.SessionPlayerNotFoundError()
raise SessionPlayerNotFoundError()
@property
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.
"""
if not self._nodeactor:
from ba import _error
raise _error.NodeNotFoundError
assert self._postinited
assert not self._expired
assert self._nodeactor
return self._nodeactor.node
@property
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
it is undefined in that case.
This value is undefined when the player has no Actor.
"""
assert self._postinited
assert not self._expired
assert self.actor is not None
return _ba.Vec3(self.node.position)
def exists(self) -> bool:
"""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.
Note that you can also use the boolean operator for this same
functionality, so a statement such as "if player" will do
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:
"""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
name may include an icon.
"""
assert self._postinited
assert not self._expired
return self._sessionplayer.getname(full=full, icon=icon)
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
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()
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)
"""
assert self._postinited
assert not self._expired
return self._sessionplayer.get_icon()
def assign_input_call(self, inputtype: Union[str, Tuple[str, ...]],
call: Callable) -> None:
"""assign_input_call(type: Union[str, Tuple[str, ...]],
def assigninput(self, inputtype: Union[str, Tuple[str, ...]],
call: Callable) -> None:
"""assigninput(type: Union[str, Tuple[str, ...]],
call: Callable) -> None
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',
'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:
"""reset_input() -> None
def resetinput(self) -> None:
"""resetinput() -> None
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:
return self.exists()

View File

@ -348,7 +348,7 @@ class Session:
'_launch_end_session_activity called twice (since_last=' +
str(since_last) + ')')
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._ending = True # Prevent further actions.
@ -398,42 +398,44 @@ class Session:
"""General message handling; can be passed any message object."""
from ba._lobby import PlayerReadyMessage
from ba._messages import PlayerProfilesChangedMessage, UNHANDLED
if isinstance(msg, PlayerReadyMessage):
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
# profiles.
with _ba.Context(self):
self.lobby.reload_profiles()
return None
return UNHANDLED
else:
return UNHANDLED
return None
class _SetActivityLock:
class _SetActivityScopedLock:
def __init__(self, session: ba.Session) -> None:
self._session = session
if session._in_set_activity:
raise RuntimeError('Session.setactivity() called recursively.')
self._session._in_set_activity = True
def __del__(self) -> None:
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.
Note that this will not change the current context to the new
Activity's. Code must be run in the new activity's methods
(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
# Sanity test: make sure this doesn't get called recursively.
if self._in_set_activity:
raise RuntimeError('Session.set_activity() called recursively.')
self._in_set_activity = True
# Make sure we don't get called recursively.
_rlock = self._SetActivityScopedLock(self)
if activity.session is not _ba.getsession():
raise RuntimeError("Provided Activity's Session is not current.")
@ -482,7 +484,7 @@ class Session:
if prev_activity is not None:
with _ba.Context('ui'):
_ba.timer(max(0.0, activity.transition_time),
prev_activity.destroy,
prev_activity.expire,
timetype=TimeType.REAL)
self._in_set_activity = False
@ -507,17 +509,17 @@ class Session:
with _ba.Context(self):
self.on_activity_end(activity, results)
except Exception:
print_exception('exception in on_activity_end() for session', self,
'activity', activity, 'with results', results)
print_exception(f'Error in on_activity_end() for session {self}'
f' activity {activity} with results {results}')
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 self._ending:
return False
# Ask the session subclass to approve/deny this request.
# Ask the ba.Session subclass to approve/deny this request.
try:
with _ba.Context(self):
result = self.on_player_request(sessionplayer)
@ -525,14 +527,14 @@ class Session:
print_exception(f'Error in on_player_request for {self}')
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:
self.players.append(sessionplayer)
with _ba.Context(self):
try:
self.lobby.add_chooser(sessionplayer)
except Exception:
print_exception('exception in lobby.add_chooser()')
print_exception('Error in lobby.add_chooser().')
return result
@ -548,39 +550,42 @@ class Session:
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;
# 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.
self._activity_retained = self._next_activity
self._activity_weak = weakref.ref(self._next_activity)
self._next_activity = None
self._activity_should_end_immediately = False
# 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
# it even after we've released the strong-ref to allow it to die.
self._activity_retained = self._next_activity
self._activity_weak = weakref.ref(self._next_activity)
self._next_activity = None
self._activity_should_end_immediately = False
# Kick out anyone loitering in the lobby.
self.lobby.remove_all_choosers_and_kick_players()
# Kick out anyone loitering in the lobby.
self.lobby.remove_all_choosers_and_kick_players()
# Kick off the activity.
self._activity_retained.begin(self)
# Kick off the activity.
self._activity_retained.begin(self)
# If we want to go down, we're now free to kick off that process.
if self._wants_to_end:
self._launch_end_session_activity()
else:
# Otherwise, if the activity has already been told to end,
# do so now.
if self._activity_should_end_immediately:
self._activity_retained.end(
self._activity_should_end_immediately_results,
self._activity_should_end_immediately_delay)
# If we want to completely end the session, we can now kick that off.
if self._wants_to_end:
self._launch_end_session_activity()
else:
# Otherwise, if the activity has already been told to end,
# do so now.
if self._activity_should_end_immediately:
self._activity_retained.end(
self._activity_should_end_immediately_results,
self._activity_should_end_immediately_delay)
def _on_player_ready(self, chooser: ba.Chooser) -> None:
"""Called when a ba.Player has checked themself ready."""
lobby = chooser.lobby
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.
if activity is not None and activity.is_joining_activity:
if lobby.check_all_ready():
@ -613,8 +618,10 @@ class Session:
"""(internal)"""
from ba._apputils import garbage_collect, call_after_ad
# Since we're mostly between activities at this point, lets run a cycle
# of garbage collection; hopefully it won't cause hitches here.
# Since things should be generally still right now, it's a good time
# 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)
with _ba.Context(self):
@ -635,7 +642,7 @@ class Session:
# Reset the player's input here, as it is probably
# 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
# (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.
# (keeps mini-game coding simpler if we can always deal with teams).
if self.use_teams:
sessionteam = chooser.get_team()
sessionteam = chooser.sessionteam
else:
our_team_id = self._next_team_id
self._next_team_id += 1
@ -670,11 +677,12 @@ class Session:
# Add player's team to the Session.
self.teams.append(sessionteam)
try:
with _ba.Context(self):
with _ba.Context(self):
try:
self.on_team_join(sessionteam)
except Exception:
print_exception(f'exception in on_team_join for {self}')
except Exception:
print_exception(f'Error in on_team_join for {self}.')
# Add player's team to the Activity.
if pass_to_activity:
@ -682,10 +690,10 @@ class Session:
assert sessionplayer not in sessionteam.players
sessionteam.players.append(sessionplayer)
sessionplayer.set_data(team=sessionteam,
character=chooser.get_character_name(),
color=chooser.get_color(),
highlight=chooser.get_highlight())
sessionplayer.setdata(team=sessionteam,
character=chooser.get_character_name(),
color=chooser.get_color(),
highlight=chooser.get_highlight())
self.stats.register_player(sessionplayer)
if pass_to_activity:

View File

@ -259,7 +259,7 @@ class Stats:
self.orchestrahitsound3: 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."""
self._activity = None if activity is None else weakref.ref(activity)

View File

@ -25,6 +25,8 @@ from __future__ import annotations
import weakref
from typing import TYPE_CHECKING, TypeVar, Generic
from ba._error import print_exception
if TYPE_CHECKING:
from weakref import ReferenceType
from typing import Dict, List, Sequence, Tuple, Union, Optional
@ -88,14 +90,9 @@ class SessionTeam:
self.name = name
self.color = tuple(color)
self.players = []
self.gamedata = {}
self.sessiondata = {}
self.gameteam: Optional[Team] = None
def reset_gamedata(self) -> None:
"""(internal)"""
self.gamedata = {}
def reset_sessiondata(self) -> None:
"""(internal)"""
self.sessiondata = {}
@ -118,12 +115,14 @@ class Team(Generic[PlayerType]):
players: List[PlayerType]
id: int
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]
_expired: bool
_postinited: bool
_gamedata: dict
# TODO: kill these.
gamedata: Dict
sessiondata: Dict
sessiondata: dict
# NOTE: avoiding having any __init__() here since it seems to not
# get called by default if a dataclass inherits from us.
@ -150,8 +149,10 @@ class Team(Generic[PlayerType]):
self.id = sessionteam.id
self.name = sessionteam.name
self.color = sessionteam.color
self.gamedata = sessionteam.gamedata
self.sessiondata = sessionteam.sessiondata
self._gamedata = {}
self._expired = False
self._postinited = True
def manual_init(self, team_id: int, name: Union[ba.Lstr, str],
color: Tuple[float, ...]) -> None:
@ -159,8 +160,54 @@ class Team(Generic[PlayerType]):
self.id = team_id
self.name = name
self.color = color
self.gamedata = {}
self._gamedata = {}
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
def sessionteam(self) -> SessionTeam:
@ -168,6 +215,7 @@ class Team(Generic[PlayerType]):
Throws a ba.SessionTeamNotFoundError if there is none.
"""
assert self._postinited
if self._sessionteam is not None:
sessionteam = self._sessionteam()
if sessionteam is not None:

View File

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

View File

@ -46,10 +46,10 @@ class RespawnIcon:
on_right, offs_extra, respawn_icons = self._get_context(player)
# 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:
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)
# 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]:
"""Return info on where we should be shown and stored."""
activity = ba.getactivity()
if isinstance(ba.getsession(), ba.DualTeamSession):
on_right = player.team.id % 2 == 1
# Store a list of icons in the team.
respawn_icons = getattr(player.team, '_spaz_respawn_icons_right',
None)
if respawn_icons is None:
respawn_icons = {}
setattr(player.team, '_spaz_respawn_icons_right',
respawn_icons)
assert isinstance(respawn_icons, dict)
icons = player.team.gamedata.get('_spaz_respawn_icons')
if icons is None:
player.team.gamedata['_spaz_respawn_icons'] = icons = {}
assert isinstance(icons, dict)
offs_extra = -20
else:
on_right = False
# Store a list of icons in the activity.
# FIXME: Need an elegant way to store our shared stuff with
# the activity.
try:
respawn_icons = activity.spaz_respawn_icons_right
except Exception:
respawn_icons = activity.spaz_respawn_icons_right = {}
icons = activity.gamedata.get('_spaz_respawn_icons')
if icons is None:
activity.gamedata['_spaz_respawn_icons'] = icons = {}
assert isinstance(icons, dict)
if isinstance(activity.session, ba.FreeForAllSession):
offs_extra = -150
else:
offs_extra = -20
return on_right, offs_extra, respawn_icons
return on_right, offs_extra, icons
def _update(self) -> None:
remaining = int(round(self._respawn_time - ba.time()))

View File

@ -30,14 +30,12 @@ import ba
if TYPE_CHECKING:
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:
def __init__(self, scoreboard: Scoreboard, team: ba.Team, do_cover: bool,
scale: float, label: Optional[ba.Lstr], flash_length: float):
# pylint: disable=too-many-statements
self._scoreboard = weakref.ref(scoreboard)
self._do_cover = do_cover
self._scale = scale
@ -58,7 +56,7 @@ class _Entry:
safe_team_color = ba.safecolor(team.color, target_intensity=1.0)
# 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
if self._do_cover:
@ -152,11 +150,11 @@ class _Entry:
else:
team_name_label = team.name
# we do our own clipping here; should probably try to tap into some
# existing functionality
# We do our own clipping here; should probably try to tap into some
# existing functionality.
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
# translation can occur..
if team_name_label.is_flat_value():
@ -204,6 +202,7 @@ class _Entry:
# Abort if we've been killed
if not self._backing.node:
return
self._pos = tuple(position)
self._backing.node.position = (position[0] + self._width / 2,
position[1] - self._height / 2)
@ -226,29 +225,29 @@ class _Entry:
def _set_flash_colors(self, flash: bool) -> None:
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:
setattr(node, attr, val)
node.color = val
if flash:
scale = 2.0
_safesetattr(
self._backing.node, 'color',
_safesetcolor(
self._backing.node,
(self._backing_color[0] * scale, self._backing_color[1] *
scale, self._backing_color[2] * scale))
_safesetattr(self._bar.node, 'color',
(self._barcolor[0] * scale, self._barcolor[1] * scale,
self._barcolor[2] * scale))
_safesetcolor(self._bar.node,
(self._barcolor[0] * scale, self._barcolor[1] *
scale, self._barcolor[2] * scale))
if self._do_cover:
_safesetattr(
self._cover.node, 'color',
_safesetcolor(
self._cover.node,
(self._cover_color[0] * scale, self._cover_color[1] *
scale, self._cover_color[2] * scale))
else:
_safesetattr(self._backing.node, 'color', self._backing_color)
_safesetattr(self._bar.node, 'color', self._barcolor)
_safesetcolor(self._backing.node, self._backing_color)
_safesetcolor(self._bar.node, self._barcolor)
if self._do_cover:
_safesetattr(self._cover.node, 'color', self._cover_color)
_safesetcolor(self._cover.node, self._cover_color)
def _do_flash(self) -> None:
assert self._flash_counter is not None
@ -266,8 +265,8 @@ class _Entry:
show_value: bool = True) -> None:
"""Set the value for the scoreboard entry."""
# if we have no score yet, just set it.. otherwise compare
# and see if we should flash
# If we have no score yet, just set it.. otherwise compare
# and see if we should flash.
if self._score is None:
self._score = score
else:
@ -327,8 +326,15 @@ class _EntryProxy:
# Remove our team from the scoreboard if its still around.
# (but deferred, in case we die in a sim step or something where
# 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))
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:
@ -338,7 +344,7 @@ class Scoreboard:
"""
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
show up on boards if provided.
@ -376,8 +382,8 @@ class Scoreboard:
# Create a proxy in the team which will kill
# our entry when it dies (for convenience)
assert not hasattr(team, '_scoreboard_entry')
setattr(team, '_scoreboard_entry', _EntryProxy(self, team))
assert '_scoreboard_entry' not in team.gamedata
team.gamedata['_scoreboard_entry'] = _EntryProxy(self, team)
# Now set the entry.
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)
def _handle_base_collide(self, team: Team) -> None:
player = ba.getcollision().opposingnode.getdelegate(
PlayerSpaz, True).getplayer(Player)
if not player or not player.is_alive():
try:
player = ba.getcollision().opposingnode.getdelegate(
PlayerSpaz, True).getplayer(Player, True)
except ba.NotFoundError:
return
if not player.is_alive():
return
# If its another team's player, they scored.

View File

@ -231,9 +231,9 @@ class CaptureTheFlagGame(ba.TeamGameActivity[Player, Team]):
actions=(
('modify_part_collision', 'physical', False),
('call', 'at_connect',
lambda: self._handle_hit_own_flag(team, 1)),
lambda: self._handle_touching_own_flag(team, True)),
('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.
@ -448,32 +448,28 @@ class CaptureTheFlagGame(ba.TeamGameActivity[Player, Team]):
delegate = node.getdelegate(PlayerSpaz)
return None if delegate is None else delegate.getplayer(Player)
def _handle_hit_own_flag(self, team: Team, val: int) -> None:
"""Called when a player touches their own team flag.
def _handle_touching_own_flag(self, team: Team, connecting: bool) -> None:
"""Called when a player touches or stops touching their own team flag.
We keep track of when each player is touching their
own flag so we can award points when returned.
We keep track of when each player is touching their own flag so we
can award points when returned.
"""
player = self._player_from_node(ba.getcollision().sourcenode)
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
# track of touches and count down.
if float(self.flag_touch_return_time) <= 0.0:
assert team.flag is not None
if 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
if (connecting and not team.home_flag_at_base
and team.flag.held_count == 0):
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.
else:
if val:
if connecting:
team.flag_return_touches += 1
if team.flag_return_touches == 1:
team.touch_return_timer = ba.Timer(
@ -506,7 +502,7 @@ class CaptureTheFlagGame(ba.TeamGameActivity[Player, Team]):
angle: float = None) -> PlayerSpaz:
"""Intercept new spazzes and add our team material for them."""
spaz = super().spawn_player_spaz(player, position, angle)
player = spaz.getplayer(Player, doraise=True)
player = spaz.getplayer(Player, True)
team: Team = player.team
player.touching_own_flag = 0
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))
elif isinstance(msg, FlagPickedUpMessage):
# Store the last player to hold the flag for scoring purposes.
assert isinstance(msg.flag, CTFFlag)
msg.flag.last_player_to_hold = msg.node.getdelegate(
PlayerSpaz, True).getplayer(Player)
try:
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.reset_return_times()

View File

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

View File

@ -139,41 +139,39 @@ class EasterEggHuntGame(ba.TeamGameActivity[Player, Team]):
if self.has_ended():
return
collision = ba.getcollision()
egg = collision.sourcenode.getdelegate(Egg)
player = collision.opposingnode.getdelegate(PlayerSpaz,
True).getplayer(Player)
if player and egg:
player.team.score += 1
try:
egg = collision.sourcenode.getdelegate(Egg, True)
player = collision.opposingnode.getdelegate(PlayerSpaz,
True).getplayer(
Player, True)
except Exception:
return
# Displays a +1 (and adds to individual player score in
# 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)
player.team.score += 1
# Create a flash.
light = ba.newnode('light',
attrs={
'position': egg.node.position,
'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())
# Displays a +1 (and adds to individual player score in
# 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.
light = ba.newnode('light',
attrs={
'position': egg.node.position,
'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:
# Misc. periodic updating.

View File

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

View File

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

View File

@ -247,9 +247,10 @@ class KingOfTheHillGame(ba.TeamGameActivity[Player, Team]):
ba.playsound(self._swipsound)
def _handle_player_flag_region_collide(self, colliding: bool) -> None:
player = ba.getcollision().opposingnode.getdelegate(
PlayerSpaz, True).getplayer(Player)
if not player:
try:
player = ba.getcollision().opposingnode.getdelegate(
PlayerSpaz, True).getplayer(Player, True)
except ba.NotFoundError:
return
# 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:
if isinstance(msg, PlayerSpazHurtMessage):
player = msg.spaz.getplayer(Player, doraise=True)
player.has_been_hurt = True
msg.spaz.getplayer(Player, True).has_been_hurt = True
self._a_player_has_been_hurt = True
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-nested-blocks
collision = ba.getcollision()
region = collision.sourcenode.getdelegate(RaceRegion)
playerspaz = collision.opposingnode.getdelegate(PlayerSpaz,
doraise=False)
player = playerspaz.getplayer(Player) if playerspaz else None
if not player or not region:
try:
region = collision.sourcenode.getdelegate(RaceRegion, True)
player = collision.opposingnode.getdelegate(PlayerSpaz,
True).getplayer(
Player, True)
except ba.NotFoundError:
return
last_region = player.last_region

View File

@ -920,14 +920,14 @@ class MainMenuSession(ba.Session):
super().__init__([self._activity_deps])
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:
if self._locked:
_ba.unlock_all_input()
# 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:
# Reject all player requests.

View File

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

View File

@ -254,13 +254,13 @@ class PlaylistEditWindow(ba.Window):
def _add(self) -> None:
# 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)))
self._editcontroller.add_game_pressed()
def _edit(self) -> None:
# 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)))
self._editcontroller.edit_game_pressed()

View File

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

View File

@ -1,5 +1,5 @@
<!-- 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,
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>
@ -343,7 +343,7 @@ actually award achievements.</p>
can overlap during transitions.</p>
<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>
<dt><h4><a name="attr_ba_Activity__expired">expired</a></h4></dt><dd>
<p><span>bool</span></p>
@ -353,6 +353,14 @@ actually award achievements.</p>
At this point no new nodes, timers, etc should be made,
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>
<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>
@ -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>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
to the activity.</p>
@ -1288,29 +1296,34 @@ mycall()</pre>
</p>
<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>
<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>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>
<dt><h4><a name="attr_ba_Chooser__ready">ready</a></h4></dt><dd>
<p><span>bool</span></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>
</dl>
<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>
<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>
<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>
</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>
<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>
@ -1545,7 +1552,7 @@ start_long_action(callback_when_done=<a href="#class_ba_ContextCall">ba.ContextC
<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>
<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>
<dt><h4><a name="attr_ba_CoopGameActivity__expired">expired</a></h4></dt><dd>
<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,
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>
<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>
@ -1595,7 +1610,7 @@ start_long_action(callback_when_done=<a href="#class_ba_ContextCall">ba.ContextC
</dd>
</dl>
<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>
<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>
@ -1685,7 +1700,7 @@ there is no associated Campaign.</p>
</dd>
</dl>
<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>
<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>
@ -2036,7 +2051,7 @@ its time with lingering corpses, sound effects, etc.</p>
</dd>
</dl>
<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>
<dl>
<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>
</dl>
<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>
<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>
@ -2130,7 +2145,7 @@ its time with lingering corpses, sound effects, etc.</p>
<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>
<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>
<dt><h4><a name="attr_ba_GameActivity__expired">expired</a></h4></dt><dd>
<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,
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>
<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>
@ -2180,7 +2203,7 @@ its time with lingering corpses, sound effects, etc.</p>
</dd>
</dl>
<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>
<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>
@ -2828,11 +2851,11 @@ can be changed to separate its new high score lists/etc. from the old.</p>
</p>
<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>
<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>Teams available in this lobby.</p>
<p><a href="#class_ba_SessionTeam">ba.SessionTeams</a> available in this lobby.</p>
</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>
<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>
@ -3367,7 +3390,7 @@ Use <a href="#function_ba_getmodel">ba.getmodel</a>() to instantiate one.</p>
</dd>
</dl>
<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>
<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>
@ -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> These correspond to <a href="#class_ba_SessionPlayer">ba.SessionPlayer</a> objects, but are created per activity
so that the activity can use its own custom player subclass.
</p>
<p> These correspond to <a href="#class_ba_SessionPlayer">ba.SessionPlayer</a> objects, but are associated with a
single <a href="#class_ba_Activity">ba.Activity</a> instance. This allows activities to specify their
own custom <a href="#class_ba_Player">ba.Player</a> types.</p>
<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>
<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>
<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>
@ -3859,10 +3898,9 @@ even if myactor is set to None.</p>
</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>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
it is undefined in that case.</p>
<p> This value is undefined when the player has no Actor.</p>
</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>
</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>
</dl>
<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>
<dt><h4><a name="method_ba_Player__assign_input_call">assign_input_call()</a></dt></h4><dd>
<p><span>assign_input_call(self, inputtype: Union[str, Tuple[str, ...]], call: Callable) -&gt; None</span></p>
<dt><h4><a name="method_ba_Player__assigninput">assigninput()</a></dt></h4><dd>
<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>
<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>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
functionality, so a statement such as "if player" will do
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>
</dd>
<dt><h4><a name="method_ba_Player__reset_input">reset_input()</a></dt></h4><dd>
<p><span>reset_input(self) -&gt; None</span></p>
<dt><h4><a name="method_ba_Player__on_expire">on_expire()</a></dt></h4><dd>
<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>
@ -4362,7 +4419,7 @@ player that joins.</p>
</dd>
</dl>
<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>
<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>
@ -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>
</dd>
<dt><h4><a name="method_ba_Session__set_activity">set_activity()</a></dt></h4><dd>
<p><span>set_activity(self, activity: <a href="#class_ba_Activity">ba.Activity</a>) -&gt; None</span></p>
<dt><h4><a name="method_ba_Session__setactivity">setactivity()</a></dt></h4><dd>
<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>Note that this will not change the current context to the new
Activity's. Code must be run in the new activity's methods
(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>
</dl>
@ -4499,7 +4556,7 @@ that a SessionPlayer is still present if retaining references to one
for any length of time.</p>
<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>
<dt><h4><a name="attr_ba_SessionPlayer__character">character</a></h4></dt><dd>
<p><span> str</span></p>
@ -4511,13 +4568,6 @@ for any length of time.</p>
<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>
</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>
<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>
@ -4568,10 +4618,10 @@ is still in its lobby selecting a team/etc. then a
</dd>
</dl>
<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>
<dt><h4><a name="method_ba_SessionPlayer__assign_input_call">assign_input_call()</a></dt></h4><dd>
<p><span>assign_input_call(type: Union[str, Tuple[str, ...]],
<dt><h4><a name="method_ba_SessionPlayer__assigninput">assigninput()</a></dt></h4><dd>
<p><span>assigninput(type: Union[str, Tuple[str, ...]],
call: Callable) -&gt; None</span></p>
<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>
</dd>
<dt><h4><a name="method_ba_SessionPlayer__reset_input">reset_input()</a></dt></h4><dd>
<p><span>reset_input() -&gt; None</span></p>
<dt><h4><a name="method_ba_SessionPlayer__resetinput">resetinput()</a></dt></h4><dd>
<p><span>resetinput() -&gt; None</span></p>
<p>Clears out the player's assigned input actions.</p>
</dd>
<dt><h4><a name="method_ba_SessionPlayer__set_name">set_name()</a></dt></h4><dd>
<p><span>set_name(name: str, full_name: str = None, real: bool = True)
<dt><h4><a name="method_ba_SessionPlayer__setname">setname()</a></dt></h4><dd>
<p><span>setname(name: str, full_name: str = None, real: bool = True)
-&gt; None</span></p>
<p>Set the player's name to the provided string.
@ -4857,7 +4907,7 @@ of the session.</p>
<h3>Methods:</h3>
<dl>
<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>
</dl>
@ -4902,7 +4952,7 @@ of the session.</p>
</p>
<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>
<dt><h4><a name="method_ba_Stats____init__">&lt;constructor&gt;</a></dt></h4><dd>
<p><span>ba.Stats()</span></p>
@ -4960,8 +5010,8 @@ of the session.</p>
<p>Reset per-sound sub-scores.</p>
</dd>
<dt><h4><a name="method_ba_Stats__set_activity">set_activity()</a></dt></h4><dd>
<p><span>set_activity(self, activity: Optional[<a href="#class_ba_Activity">ba.Activity</a>]) -&gt; None</span></p>
<dt><h4><a name="method_ba_Stats__setactivity">setactivity()</a></dt></h4><dd>
<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>
@ -4979,7 +5029,19 @@ of the session.</p>
</p>
<h3>Attributes:</h3>
<h5><a href="#attr_ba_Team__gamedata">gamedata</a>, <a href="#attr_ba_Team__sessionteam">sessionteam</a></h5>
<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>
<p><span>SessionTeam</span></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>
</dl>
<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>
<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>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>
</dl>
<hr>
@ -5011,7 +5080,7 @@ of the session.</p>
<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>
<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>
<dt><h4><a name="attr_ba_TeamGameActivity__expired">expired</a></h4></dt><dd>
<p><span>bool</span></p>
@ -5021,6 +5090,14 @@ of the session.</p>
At this point no new nodes, timers, etc should be made,
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>
<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>
@ -5061,7 +5138,7 @@ of the session.</p>
</dd>
</dl>
<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>
<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>