bascenev1lib.game.meteorshower
Defines a bomb-dodging mini-game.
1# Released under the MIT License. See LICENSE for details. 2# 3"""Defines a bomb-dodging mini-game.""" 4 5# ba_meta require api 8 6# (see https://ballistica.net/wiki/meta-tag-system) 7 8from __future__ import annotations 9 10import random 11from typing import TYPE_CHECKING 12 13from bascenev1lib.actor.bomb import Bomb 14from bascenev1lib.actor.onscreentimer import OnScreenTimer 15import bascenev1 as bs 16 17if TYPE_CHECKING: 18 from typing import Any, Sequence 19 20 21class Player(bs.Player['Team']): 22 """Our player type for this game.""" 23 24 def __init__(self) -> None: 25 super().__init__() 26 self.death_time: float | None = None 27 28 29class Team(bs.Team[Player]): 30 """Our team type for this game.""" 31 32 33# ba_meta export bascenev1.GameActivity 34class MeteorShowerGame(bs.TeamGameActivity[Player, Team]): 35 """Minigame involving dodging falling bombs.""" 36 37 name = 'Meteor Shower' 38 description = 'Dodge the falling bombs.' 39 available_settings = [bs.BoolSetting('Epic Mode', default=False)] 40 scoreconfig = bs.ScoreConfig( 41 label='Survived', scoretype=bs.ScoreType.MILLISECONDS, version='B' 42 ) 43 44 # Print messages when players die (since its meaningful in this game). 45 announce_player_deaths = True 46 47 # Don't allow joining after we start 48 # (would enable leave/rejoin tomfoolery). 49 allow_mid_activity_joins = False 50 51 # We're currently hard-coded for one map. 52 @classmethod 53 def get_supported_maps(cls, sessiontype: type[bs.Session]) -> list[str]: 54 return ['Rampage'] 55 56 # We support teams, free-for-all, and co-op sessions. 57 @classmethod 58 def supports_session_type(cls, sessiontype: type[bs.Session]) -> bool: 59 return ( 60 issubclass(sessiontype, bs.DualTeamSession) 61 or issubclass(sessiontype, bs.FreeForAllSession) 62 or issubclass(sessiontype, bs.CoopSession) 63 ) 64 65 def __init__(self, settings: dict): 66 super().__init__(settings) 67 68 self._epic_mode = settings.get('Epic Mode', False) 69 self._last_player_death_time: float | None = None 70 self._meteor_time = 2.0 71 self._timer: OnScreenTimer | None = None 72 73 # Some base class overrides: 74 self.default_music = ( 75 bs.MusicType.EPIC if self._epic_mode else bs.MusicType.SURVIVAL 76 ) 77 if self._epic_mode: 78 self.slow_motion = True 79 80 def on_begin(self) -> None: 81 super().on_begin() 82 83 # Drop a wave every few seconds.. and every so often drop the time 84 # between waves ..lets have things increase faster if we have fewer 85 # players. 86 delay = 5.0 if len(self.players) > 2 else 2.5 87 if self._epic_mode: 88 delay *= 0.25 89 bs.timer(delay, self._decrement_meteor_time, repeat=True) 90 91 # Kick off the first wave in a few seconds. 92 delay = 3.0 93 if self._epic_mode: 94 delay *= 0.25 95 bs.timer(delay, self._set_meteor_timer) 96 97 self._timer = OnScreenTimer() 98 self._timer.start() 99 100 # Check for immediate end (if we've only got 1 player, etc). 101 bs.timer(5.0, self._check_end_game) 102 103 def on_player_leave(self, player: Player) -> None: 104 # Augment default behavior. 105 super().on_player_leave(player) 106 107 # A departing player may trigger game-over. 108 self._check_end_game() 109 110 # overriding the default character spawning.. 111 def spawn_player(self, player: Player) -> bs.Actor: 112 spaz = self.spawn_player_spaz(player) 113 114 # Let's reconnect this player's controls to this 115 # spaz but *without* the ability to attack or pick stuff up. 116 spaz.connect_controls_to_player( 117 enable_punch=False, enable_bomb=False, enable_pickup=False 118 ) 119 120 # Also lets have them make some noise when they die. 121 spaz.play_big_death_sound = True 122 return spaz 123 124 # Various high-level game events come through this method. 125 def handlemessage(self, msg: Any) -> Any: 126 if isinstance(msg, bs.PlayerDiedMessage): 127 # Augment standard behavior. 128 super().handlemessage(msg) 129 130 curtime = bs.time() 131 132 # Record the player's moment of death. 133 # assert isinstance(msg.spaz.player 134 msg.getplayer(Player).death_time = curtime 135 136 # In co-op mode, end the game the instant everyone dies 137 # (more accurate looking). 138 # In teams/ffa, allow a one-second fudge-factor so we can 139 # get more draws if players die basically at the same time. 140 if isinstance(self.session, bs.CoopSession): 141 # Teams will still show up if we check now.. check in 142 # the next cycle. 143 bs.pushcall(self._check_end_game) 144 145 # Also record this for a final setting of the clock. 146 self._last_player_death_time = curtime 147 else: 148 bs.timer(1.0, self._check_end_game) 149 150 else: 151 # Default handler: 152 return super().handlemessage(msg) 153 return None 154 155 def _check_end_game(self) -> None: 156 living_team_count = 0 157 for team in self.teams: 158 for player in team.players: 159 if player.is_alive(): 160 living_team_count += 1 161 break 162 163 # In co-op, we go till everyone is dead.. otherwise we go 164 # until one team remains. 165 if isinstance(self.session, bs.CoopSession): 166 if living_team_count <= 0: 167 self.end_game() 168 else: 169 if living_team_count <= 1: 170 self.end_game() 171 172 def _set_meteor_timer(self) -> None: 173 bs.timer( 174 (1.0 + 0.2 * random.random()) * self._meteor_time, 175 self._drop_bomb_cluster, 176 ) 177 178 def _drop_bomb_cluster(self) -> None: 179 # Random note: code like this is a handy way to plot out extents 180 # and debug things. 181 loc_test = False 182 if loc_test: 183 bs.newnode('locator', attrs={'position': (8, 6, -5.5)}) 184 bs.newnode('locator', attrs={'position': (8, 6, -2.3)}) 185 bs.newnode('locator', attrs={'position': (-7.3, 6, -5.5)}) 186 bs.newnode('locator', attrs={'position': (-7.3, 6, -2.3)}) 187 188 # Drop several bombs in series. 189 delay = 0.0 190 for _i in range(random.randrange(1, 3)): 191 # Drop them somewhere within our bounds with velocity pointing 192 # toward the opposite side. 193 pos = ( 194 -7.3 + 15.3 * random.random(), 195 11, 196 -5.57 + 2.1 * random.random(), 197 ) 198 dropdir = -1.0 if pos[0] > 0 else 1.0 199 vel = ( 200 (-5.0 + random.random() * 30.0) * dropdir, 201 random.uniform(-3.066, -4.12), 202 0, 203 ) 204 bs.timer(delay, bs.Call(self._drop_bomb, pos, vel)) 205 delay += 0.1 206 self._set_meteor_timer() 207 208 def _drop_bomb( 209 self, position: Sequence[float], velocity: Sequence[float] 210 ) -> None: 211 Bomb(position=position, velocity=velocity).autoretain() 212 213 def _decrement_meteor_time(self) -> None: 214 self._meteor_time = max(0.01, self._meteor_time * 0.9) 215 216 def end_game(self) -> None: 217 cur_time = bs.time() 218 assert self._timer is not None 219 start_time = self._timer.getstarttime() 220 221 # Mark death-time as now for any still-living players 222 # and award players points for how long they lasted. 223 # (these per-player scores are only meaningful in team-games) 224 for team in self.teams: 225 for player in team.players: 226 survived = False 227 228 # Throw an extra fudge factor in so teams that 229 # didn't die come out ahead of teams that did. 230 if player.death_time is None: 231 survived = True 232 player.death_time = cur_time + 1 233 234 # Award a per-player score depending on how many seconds 235 # they lasted (per-player scores only affect teams mode; 236 # everywhere else just looks at the per-team score). 237 score = int(player.death_time - self._timer.getstarttime()) 238 if survived: 239 score += 50 # A bit extra for survivors. 240 self.stats.player_scored(player, score, screenmessage=False) 241 242 # Stop updating our time text, and set the final time to match 243 # exactly when our last guy died. 244 self._timer.stop(endtime=self._last_player_death_time) 245 246 # Ok now calc game results: set a score for each team and then tell 247 # the game to end. 248 results = bs.GameResults() 249 250 # Remember that 'free-for-all' mode is simply a special form 251 # of 'teams' mode where each player gets their own team, so we can 252 # just always deal in teams and have all cases covered. 253 for team in self.teams: 254 # Set the team score to the max time survived by any player on 255 # that team. 256 longest_life = 0.0 257 for player in team.players: 258 assert player.death_time is not None 259 longest_life = max(longest_life, player.death_time - start_time) 260 261 # Submit the score value in milliseconds. 262 results.set_team_score(team, int(1000.0 * longest_life)) 263 264 self.end(results=results)
22class Player(bs.Player['Team']): 23 """Our player type for this game.""" 24 25 def __init__(self) -> None: 26 super().__init__() 27 self.death_time: float | None = None
Our player type for this game.
Inherited Members
- bascenev1._player.Player
- character
- actor
- color
- highlight
- on_expire
- team
- customdata
- sessionplayer
- node
- position
- exists
- getname
- is_alive
- get_icon
- assigninput
- resetinput
Our team type for this game.
Inherited Members
- bascenev1._team.Team
- players
- id
- name
- color
- manual_init
- customdata
- on_expire
- sessionteam
35class MeteorShowerGame(bs.TeamGameActivity[Player, Team]): 36 """Minigame involving dodging falling bombs.""" 37 38 name = 'Meteor Shower' 39 description = 'Dodge the falling bombs.' 40 available_settings = [bs.BoolSetting('Epic Mode', default=False)] 41 scoreconfig = bs.ScoreConfig( 42 label='Survived', scoretype=bs.ScoreType.MILLISECONDS, version='B' 43 ) 44 45 # Print messages when players die (since its meaningful in this game). 46 announce_player_deaths = True 47 48 # Don't allow joining after we start 49 # (would enable leave/rejoin tomfoolery). 50 allow_mid_activity_joins = False 51 52 # We're currently hard-coded for one map. 53 @classmethod 54 def get_supported_maps(cls, sessiontype: type[bs.Session]) -> list[str]: 55 return ['Rampage'] 56 57 # We support teams, free-for-all, and co-op sessions. 58 @classmethod 59 def supports_session_type(cls, sessiontype: type[bs.Session]) -> bool: 60 return ( 61 issubclass(sessiontype, bs.DualTeamSession) 62 or issubclass(sessiontype, bs.FreeForAllSession) 63 or issubclass(sessiontype, bs.CoopSession) 64 ) 65 66 def __init__(self, settings: dict): 67 super().__init__(settings) 68 69 self._epic_mode = settings.get('Epic Mode', False) 70 self._last_player_death_time: float | None = None 71 self._meteor_time = 2.0 72 self._timer: OnScreenTimer | None = None 73 74 # Some base class overrides: 75 self.default_music = ( 76 bs.MusicType.EPIC if self._epic_mode else bs.MusicType.SURVIVAL 77 ) 78 if self._epic_mode: 79 self.slow_motion = True 80 81 def on_begin(self) -> None: 82 super().on_begin() 83 84 # Drop a wave every few seconds.. and every so often drop the time 85 # between waves ..lets have things increase faster if we have fewer 86 # players. 87 delay = 5.0 if len(self.players) > 2 else 2.5 88 if self._epic_mode: 89 delay *= 0.25 90 bs.timer(delay, self._decrement_meteor_time, repeat=True) 91 92 # Kick off the first wave in a few seconds. 93 delay = 3.0 94 if self._epic_mode: 95 delay *= 0.25 96 bs.timer(delay, self._set_meteor_timer) 97 98 self._timer = OnScreenTimer() 99 self._timer.start() 100 101 # Check for immediate end (if we've only got 1 player, etc). 102 bs.timer(5.0, self._check_end_game) 103 104 def on_player_leave(self, player: Player) -> None: 105 # Augment default behavior. 106 super().on_player_leave(player) 107 108 # A departing player may trigger game-over. 109 self._check_end_game() 110 111 # overriding the default character spawning.. 112 def spawn_player(self, player: Player) -> bs.Actor: 113 spaz = self.spawn_player_spaz(player) 114 115 # Let's reconnect this player's controls to this 116 # spaz but *without* the ability to attack or pick stuff up. 117 spaz.connect_controls_to_player( 118 enable_punch=False, enable_bomb=False, enable_pickup=False 119 ) 120 121 # Also lets have them make some noise when they die. 122 spaz.play_big_death_sound = True 123 return spaz 124 125 # Various high-level game events come through this method. 126 def handlemessage(self, msg: Any) -> Any: 127 if isinstance(msg, bs.PlayerDiedMessage): 128 # Augment standard behavior. 129 super().handlemessage(msg) 130 131 curtime = bs.time() 132 133 # Record the player's moment of death. 134 # assert isinstance(msg.spaz.player 135 msg.getplayer(Player).death_time = curtime 136 137 # In co-op mode, end the game the instant everyone dies 138 # (more accurate looking). 139 # In teams/ffa, allow a one-second fudge-factor so we can 140 # get more draws if players die basically at the same time. 141 if isinstance(self.session, bs.CoopSession): 142 # Teams will still show up if we check now.. check in 143 # the next cycle. 144 bs.pushcall(self._check_end_game) 145 146 # Also record this for a final setting of the clock. 147 self._last_player_death_time = curtime 148 else: 149 bs.timer(1.0, self._check_end_game) 150 151 else: 152 # Default handler: 153 return super().handlemessage(msg) 154 return None 155 156 def _check_end_game(self) -> None: 157 living_team_count = 0 158 for team in self.teams: 159 for player in team.players: 160 if player.is_alive(): 161 living_team_count += 1 162 break 163 164 # In co-op, we go till everyone is dead.. otherwise we go 165 # until one team remains. 166 if isinstance(self.session, bs.CoopSession): 167 if living_team_count <= 0: 168 self.end_game() 169 else: 170 if living_team_count <= 1: 171 self.end_game() 172 173 def _set_meteor_timer(self) -> None: 174 bs.timer( 175 (1.0 + 0.2 * random.random()) * self._meteor_time, 176 self._drop_bomb_cluster, 177 ) 178 179 def _drop_bomb_cluster(self) -> None: 180 # Random note: code like this is a handy way to plot out extents 181 # and debug things. 182 loc_test = False 183 if loc_test: 184 bs.newnode('locator', attrs={'position': (8, 6, -5.5)}) 185 bs.newnode('locator', attrs={'position': (8, 6, -2.3)}) 186 bs.newnode('locator', attrs={'position': (-7.3, 6, -5.5)}) 187 bs.newnode('locator', attrs={'position': (-7.3, 6, -2.3)}) 188 189 # Drop several bombs in series. 190 delay = 0.0 191 for _i in range(random.randrange(1, 3)): 192 # Drop them somewhere within our bounds with velocity pointing 193 # toward the opposite side. 194 pos = ( 195 -7.3 + 15.3 * random.random(), 196 11, 197 -5.57 + 2.1 * random.random(), 198 ) 199 dropdir = -1.0 if pos[0] > 0 else 1.0 200 vel = ( 201 (-5.0 + random.random() * 30.0) * dropdir, 202 random.uniform(-3.066, -4.12), 203 0, 204 ) 205 bs.timer(delay, bs.Call(self._drop_bomb, pos, vel)) 206 delay += 0.1 207 self._set_meteor_timer() 208 209 def _drop_bomb( 210 self, position: Sequence[float], velocity: Sequence[float] 211 ) -> None: 212 Bomb(position=position, velocity=velocity).autoretain() 213 214 def _decrement_meteor_time(self) -> None: 215 self._meteor_time = max(0.01, self._meteor_time * 0.9) 216 217 def end_game(self) -> None: 218 cur_time = bs.time() 219 assert self._timer is not None 220 start_time = self._timer.getstarttime() 221 222 # Mark death-time as now for any still-living players 223 # and award players points for how long they lasted. 224 # (these per-player scores are only meaningful in team-games) 225 for team in self.teams: 226 for player in team.players: 227 survived = False 228 229 # Throw an extra fudge factor in so teams that 230 # didn't die come out ahead of teams that did. 231 if player.death_time is None: 232 survived = True 233 player.death_time = cur_time + 1 234 235 # Award a per-player score depending on how many seconds 236 # they lasted (per-player scores only affect teams mode; 237 # everywhere else just looks at the per-team score). 238 score = int(player.death_time - self._timer.getstarttime()) 239 if survived: 240 score += 50 # A bit extra for survivors. 241 self.stats.player_scored(player, score, screenmessage=False) 242 243 # Stop updating our time text, and set the final time to match 244 # exactly when our last guy died. 245 self._timer.stop(endtime=self._last_player_death_time) 246 247 # Ok now calc game results: set a score for each team and then tell 248 # the game to end. 249 results = bs.GameResults() 250 251 # Remember that 'free-for-all' mode is simply a special form 252 # of 'teams' mode where each player gets their own team, so we can 253 # just always deal in teams and have all cases covered. 254 for team in self.teams: 255 # Set the team score to the max time survived by any player on 256 # that team. 257 longest_life = 0.0 258 for player in team.players: 259 assert player.death_time is not None 260 longest_life = max(longest_life, player.death_time - start_time) 261 262 # Submit the score value in milliseconds. 263 results.set_team_score(team, int(1000.0 * longest_life)) 264 265 self.end(results=results)
Minigame involving dodging falling bombs.
66 def __init__(self, settings: dict): 67 super().__init__(settings) 68 69 self._epic_mode = settings.get('Epic Mode', False) 70 self._last_player_death_time: float | None = None 71 self._meteor_time = 2.0 72 self._timer: OnScreenTimer | None = None 73 74 # Some base class overrides: 75 self.default_music = ( 76 bs.MusicType.EPIC if self._epic_mode else bs.MusicType.SURVIVAL 77 ) 78 if self._epic_mode: 79 self.slow_motion = True
Instantiate the Activity.
Whether to print every time a player dies. This can be pertinent in games such as Death-Match but can be annoying in games where it doesn't matter.
Whether players should be allowed to join in the middle of this activity. Note that Sessions may not allow mid-activity-joins even if the activity says its ok.
53 @classmethod 54 def get_supported_maps(cls, sessiontype: type[bs.Session]) -> list[str]: 55 return ['Rampage']
Called by the default bascenev1.GameActivity.create_settings_ui() implementation; should return a list of map names valid for this game-type for the given bascenev1.Session type.
58 @classmethod 59 def supports_session_type(cls, sessiontype: type[bs.Session]) -> bool: 60 return ( 61 issubclass(sessiontype, bs.DualTeamSession) 62 or issubclass(sessiontype, bs.FreeForAllSession) 63 or issubclass(sessiontype, bs.CoopSession) 64 )
Class method override; returns True for ba.DualTeamSessions and ba.FreeForAllSessions; False otherwise.
81 def on_begin(self) -> None: 82 super().on_begin() 83 84 # Drop a wave every few seconds.. and every so often drop the time 85 # between waves ..lets have things increase faster if we have fewer 86 # players. 87 delay = 5.0 if len(self.players) > 2 else 2.5 88 if self._epic_mode: 89 delay *= 0.25 90 bs.timer(delay, self._decrement_meteor_time, repeat=True) 91 92 # Kick off the first wave in a few seconds. 93 delay = 3.0 94 if self._epic_mode: 95 delay *= 0.25 96 bs.timer(delay, self._set_meteor_timer) 97 98 self._timer = OnScreenTimer() 99 self._timer.start() 100 101 # Check for immediate end (if we've only got 1 player, etc). 102 bs.timer(5.0, self._check_end_game)
Called once the previous Activity has finished transitioning out.
At this point the activity's initial players and teams are filled in and it should begin its actual game logic.
104 def on_player_leave(self, player: Player) -> None: 105 # Augment default behavior. 106 super().on_player_leave(player) 107 108 # A departing player may trigger game-over. 109 self._check_end_game()
Called when a bascenev1.Player is leaving the Activity.
112 def spawn_player(self, player: Player) -> bs.Actor: 113 spaz = self.spawn_player_spaz(player) 114 115 # Let's reconnect this player's controls to this 116 # spaz but *without* the ability to attack or pick stuff up. 117 spaz.connect_controls_to_player( 118 enable_punch=False, enable_bomb=False, enable_pickup=False 119 ) 120 121 # Also lets have them make some noise when they die. 122 spaz.play_big_death_sound = True 123 return spaz
Spawn something for the provided bascenev1.Player.
The default implementation simply calls spawn_player_spaz().
126 def handlemessage(self, msg: Any) -> Any: 127 if isinstance(msg, bs.PlayerDiedMessage): 128 # Augment standard behavior. 129 super().handlemessage(msg) 130 131 curtime = bs.time() 132 133 # Record the player's moment of death. 134 # assert isinstance(msg.spaz.player 135 msg.getplayer(Player).death_time = curtime 136 137 # In co-op mode, end the game the instant everyone dies 138 # (more accurate looking). 139 # In teams/ffa, allow a one-second fudge-factor so we can 140 # get more draws if players die basically at the same time. 141 if isinstance(self.session, bs.CoopSession): 142 # Teams will still show up if we check now.. check in 143 # the next cycle. 144 bs.pushcall(self._check_end_game) 145 146 # Also record this for a final setting of the clock. 147 self._last_player_death_time = curtime 148 else: 149 bs.timer(1.0, self._check_end_game) 150 151 else: 152 # Default handler: 153 return super().handlemessage(msg) 154 return None
General message handling; can be passed any message object.
217 def end_game(self) -> None: 218 cur_time = bs.time() 219 assert self._timer is not None 220 start_time = self._timer.getstarttime() 221 222 # Mark death-time as now for any still-living players 223 # and award players points for how long they lasted. 224 # (these per-player scores are only meaningful in team-games) 225 for team in self.teams: 226 for player in team.players: 227 survived = False 228 229 # Throw an extra fudge factor in so teams that 230 # didn't die come out ahead of teams that did. 231 if player.death_time is None: 232 survived = True 233 player.death_time = cur_time + 1 234 235 # Award a per-player score depending on how many seconds 236 # they lasted (per-player scores only affect teams mode; 237 # everywhere else just looks at the per-team score). 238 score = int(player.death_time - self._timer.getstarttime()) 239 if survived: 240 score += 50 # A bit extra for survivors. 241 self.stats.player_scored(player, score, screenmessage=False) 242 243 # Stop updating our time text, and set the final time to match 244 # exactly when our last guy died. 245 self._timer.stop(endtime=self._last_player_death_time) 246 247 # Ok now calc game results: set a score for each team and then tell 248 # the game to end. 249 results = bs.GameResults() 250 251 # Remember that 'free-for-all' mode is simply a special form 252 # of 'teams' mode where each player gets their own team, so we can 253 # just always deal in teams and have all cases covered. 254 for team in self.teams: 255 # Set the team score to the max time survived by any player on 256 # that team. 257 longest_life = 0.0 258 for player in team.players: 259 assert player.death_time is not None 260 longest_life = max(longest_life, player.death_time - start_time) 261 262 # Submit the score value in milliseconds. 263 results.set_team_score(team, int(1000.0 * longest_life)) 264 265 self.end(results=results)
Tell the game to wrap up and call bascenev1.Activity.end().
This method should be overridden by subclasses. A game should always be prepared to end and deliver results, even if there is no 'winner' yet; this way things like the standard time-limit (bascenev1.GameActivity.setup_standard_time_limit()) will work with the game.
Inherited Members
- bascenev1._teamgame.TeamGameActivity
- on_transition_in
- spawn_player_spaz
- end
- bascenev1._gameactivity.GameActivity
- tips
- allow_pausing
- allow_kick_idle_players
- show_kill_points
- create_settings_ui
- getscoreconfig
- getname
- get_display_string
- get_team_display_string
- get_description
- get_description_display_string
- get_available_settings
- get_settings_display_string
- initialplayerinfos
- map
- get_instance_display_string
- get_instance_scoreboard_display_string
- get_instance_description
- get_instance_description_short
- on_continue
- is_waiting_for_continue
- continue_or_end_game
- on_player_join
- respawn_player
- spawn_player_if_exists
- setup_standard_powerup_drops
- setup_standard_time_limit
- show_zoom_message
- bascenev1._activity.Activity
- settings_raw
- teams
- players
- is_joining_activity
- use_fixed_vr_overlay
- slow_motion
- inherits_slow_motion
- inherits_music
- inherits_vr_camera_offset
- inherits_vr_overlay_center
- inherits_tint
- transition_time
- can_show_ad_on_death
- paused_text
- preloads
- lobby
- context
- globalsnode
- stats
- on_expire
- customdata
- expired
- playertype
- teamtype
- retain_actor
- add_actor_weak_ref
- session
- on_team_join
- on_team_leave
- on_transition_out
- has_transitioned_in
- has_begun
- has_ended
- is_transitioning_out
- transition_out
- create_player
- create_team
- bascenev1._dependency.DependencyComponent
- dep_is_present
- get_dynamic_deps