bastd.game.keepaway

Defines a keep-away game type.

  1# Released under the MIT License. See LICENSE for details.
  2#
  3"""Defines a keep-away game type."""
  4
  5# ba_meta require api 7
  6# (see https://ballistica.net/wiki/meta-tag-system)
  7
  8from __future__ import annotations
  9
 10from enum import Enum
 11from typing import TYPE_CHECKING
 12
 13import ba
 14from bastd.actor.playerspaz import PlayerSpaz
 15from bastd.actor.scoreboard import Scoreboard
 16from bastd.actor.flag import (Flag, FlagDroppedMessage, FlagDiedMessage,
 17                              FlagPickedUpMessage)
 18
 19if TYPE_CHECKING:
 20    from typing import Any, Sequence
 21
 22
 23class FlagState(Enum):
 24    """States our single flag can be in."""
 25    NEW = 0
 26    UNCONTESTED = 1
 27    CONTESTED = 2
 28    HELD = 3
 29
 30
 31class Player(ba.Player['Team']):
 32    """Our player type for this game."""
 33
 34
 35class Team(ba.Team[Player]):
 36    """Our team type for this game."""
 37
 38    def __init__(self, timeremaining: int) -> None:
 39        self.timeremaining = timeremaining
 40        self.holdingflag = False
 41
 42
 43# ba_meta export game
 44class KeepAwayGame(ba.TeamGameActivity[Player, Team]):
 45    """Game where you try to keep the flag away from your enemies."""
 46
 47    name = 'Keep Away'
 48    description = 'Carry the flag for a set length of time.'
 49    available_settings = [
 50        ba.IntSetting(
 51            'Hold Time',
 52            min_value=10,
 53            default=30,
 54            increment=10,
 55        ),
 56        ba.IntChoiceSetting(
 57            'Time Limit',
 58            choices=[
 59                ('None', 0),
 60                ('1 Minute', 60),
 61                ('2 Minutes', 120),
 62                ('5 Minutes', 300),
 63                ('10 Minutes', 600),
 64                ('20 Minutes', 1200),
 65            ],
 66            default=0,
 67        ),
 68        ba.FloatChoiceSetting(
 69            'Respawn Times',
 70            choices=[
 71                ('Shorter', 0.25),
 72                ('Short', 0.5),
 73                ('Normal', 1.0),
 74                ('Long', 2.0),
 75                ('Longer', 4.0),
 76            ],
 77            default=1.0,
 78        ),
 79    ]
 80    scoreconfig = ba.ScoreConfig(label='Time Held')
 81    default_music = ba.MusicType.KEEP_AWAY
 82
 83    @classmethod
 84    def supports_session_type(cls, sessiontype: type[ba.Session]) -> bool:
 85        return (issubclass(sessiontype, ba.DualTeamSession)
 86                or issubclass(sessiontype, ba.FreeForAllSession))
 87
 88    @classmethod
 89    def get_supported_maps(cls, sessiontype: type[ba.Session]) -> list[str]:
 90        return ba.getmaps('keep_away')
 91
 92    def __init__(self, settings: dict):
 93        super().__init__(settings)
 94        self._scoreboard = Scoreboard()
 95        self._swipsound = ba.getsound('swip')
 96        self._tick_sound = ba.getsound('tick')
 97        self._countdownsounds = {
 98            10: ba.getsound('announceTen'),
 99            9: ba.getsound('announceNine'),
100            8: ba.getsound('announceEight'),
101            7: ba.getsound('announceSeven'),
102            6: ba.getsound('announceSix'),
103            5: ba.getsound('announceFive'),
104            4: ba.getsound('announceFour'),
105            3: ba.getsound('announceThree'),
106            2: ba.getsound('announceTwo'),
107            1: ba.getsound('announceOne')
108        }
109        self._flag_spawn_pos: Sequence[float] | None = None
110        self._update_timer: ba.Timer | None = None
111        self._holding_players: list[Player] = []
112        self._flag_state: FlagState | None = None
113        self._flag_light: ba.Node | None = None
114        self._scoring_team: Team | None = None
115        self._flag: Flag | None = None
116        self._hold_time = int(settings['Hold Time'])
117        self._time_limit = float(settings['Time Limit'])
118
119    def get_instance_description(self) -> str | Sequence:
120        return 'Carry the flag for ${ARG1} seconds.', self._hold_time
121
122    def get_instance_description_short(self) -> str | Sequence:
123        return 'carry the flag for ${ARG1} seconds', self._hold_time
124
125    def create_team(self, sessionteam: ba.SessionTeam) -> Team:
126        return Team(timeremaining=self._hold_time)
127
128    def on_team_join(self, team: Team) -> None:
129        self._update_scoreboard()
130
131    def on_begin(self) -> None:
132        super().on_begin()
133        self.setup_standard_time_limit(self._time_limit)
134        self.setup_standard_powerup_drops()
135        self._flag_spawn_pos = self.map.get_flag_position(None)
136        self._spawn_flag()
137        self._update_timer = ba.Timer(1.0, call=self._tick, repeat=True)
138        self._update_flag_state()
139        Flag.project_stand(self._flag_spawn_pos)
140
141    def _tick(self) -> None:
142        self._update_flag_state()
143
144        # Award points to all living players holding the flag.
145        for player in self._holding_players:
146            if player:
147                assert self.stats
148                self.stats.player_scored(player,
149                                         3,
150                                         screenmessage=False,
151                                         display=False)
152
153        scoreteam = self._scoring_team
154
155        if scoreteam is not None:
156
157            if scoreteam.timeremaining > 0:
158                ba.playsound(self._tick_sound)
159
160            scoreteam.timeremaining = max(0, scoreteam.timeremaining - 1)
161            self._update_scoreboard()
162            if scoreteam.timeremaining > 0:
163                assert self._flag is not None
164                self._flag.set_score_text(str(scoreteam.timeremaining))
165
166            # Announce numbers we have sounds for.
167            if scoreteam.timeremaining in self._countdownsounds:
168                ba.playsound(self._countdownsounds[scoreteam.timeremaining])
169
170            # Winner.
171            if scoreteam.timeremaining <= 0:
172                self.end_game()
173
174    def end_game(self) -> None:
175        results = ba.GameResults()
176        for team in self.teams:
177            results.set_team_score(team, self._hold_time - team.timeremaining)
178        self.end(results=results, announce_delay=0)
179
180    def _update_flag_state(self) -> None:
181        for team in self.teams:
182            team.holdingflag = False
183        self._holding_players = []
184        for player in self.players:
185            holdingflag = False
186            try:
187                assert isinstance(player.actor, (PlayerSpaz, type(None)))
188                if (player.actor and player.actor.node
189                        and player.actor.node.hold_node):
190                    holdingflag = (
191                        player.actor.node.hold_node.getnodetype() == 'flag')
192            except Exception:
193                ba.print_exception('Error checking hold flag.')
194            if holdingflag:
195                self._holding_players.append(player)
196                player.team.holdingflag = True
197
198        holdingteams = set(t for t in self.teams if t.holdingflag)
199        prevstate = self._flag_state
200        assert self._flag is not None
201        assert self._flag_light
202        assert self._flag.node
203        if len(holdingteams) > 1:
204            self._flag_state = FlagState.CONTESTED
205            self._scoring_team = None
206            self._flag_light.color = (0.6, 0.6, 0.1)
207            self._flag.node.color = (1.0, 1.0, 0.4)
208        elif len(holdingteams) == 1:
209            holdingteam = list(holdingteams)[0]
210            self._flag_state = FlagState.HELD
211            self._scoring_team = holdingteam
212            self._flag_light.color = ba.normalized_color(holdingteam.color)
213            self._flag.node.color = holdingteam.color
214        else:
215            self._flag_state = FlagState.UNCONTESTED
216            self._scoring_team = None
217            self._flag_light.color = (0.2, 0.2, 0.2)
218            self._flag.node.color = (1, 1, 1)
219
220        if self._flag_state != prevstate:
221            ba.playsound(self._swipsound)
222
223    def _spawn_flag(self) -> None:
224        ba.playsound(self._swipsound)
225        self._flash_flag_spawn()
226        assert self._flag_spawn_pos is not None
227        self._flag = Flag(dropped_timeout=20, position=self._flag_spawn_pos)
228        self._flag_state = FlagState.NEW
229        self._flag_light = ba.newnode('light',
230                                      owner=self._flag.node,
231                                      attrs={
232                                          'intensity': 0.2,
233                                          'radius': 0.3,
234                                          'color': (0.2, 0.2, 0.2)
235                                      })
236        assert self._flag.node
237        self._flag.node.connectattr('position', self._flag_light, 'position')
238        self._update_flag_state()
239
240    def _flash_flag_spawn(self) -> None:
241        light = ba.newnode('light',
242                           attrs={
243                               'position': self._flag_spawn_pos,
244                               'color': (1, 1, 1),
245                               'radius': 0.3,
246                               'height_attenuated': False
247                           })
248        ba.animate(light, 'intensity', {0.0: 0, 0.25: 0.5, 0.5: 0}, loop=True)
249        ba.timer(1.0, light.delete)
250
251    def _update_scoreboard(self) -> None:
252        for team in self.teams:
253            self._scoreboard.set_team_value(team,
254                                            team.timeremaining,
255                                            self._hold_time,
256                                            countdown=True)
257
258    def handlemessage(self, msg: Any) -> Any:
259        if isinstance(msg, ba.PlayerDiedMessage):
260            # Augment standard behavior.
261            super().handlemessage(msg)
262            self.respawn_player(msg.getplayer(Player))
263        elif isinstance(msg, FlagDiedMessage):
264            self._spawn_flag()
265        elif isinstance(msg, (FlagDroppedMessage, FlagPickedUpMessage)):
266            self._update_flag_state()
267        else:
268            super().handlemessage(msg)
class FlagState(enum.Enum):
24class FlagState(Enum):
25    """States our single flag can be in."""
26    NEW = 0
27    UNCONTESTED = 1
28    CONTESTED = 2
29    HELD = 3

States our single flag can be in.

NEW = <FlagState.NEW: 0>
UNCONTESTED = <FlagState.UNCONTESTED: 1>
CONTESTED = <FlagState.CONTESTED: 2>
HELD = <FlagState.HELD: 3>
Inherited Members
enum.Enum
name
value
class Player(ba._player.Player[ForwardRef('Team')]):
32class Player(ba.Player['Team']):
33    """Our player type for this game."""

Our player type for this game.

Player()
class Team(ba._team.Team[bastd.game.keepaway.Player]):
36class Team(ba.Team[Player]):
37    """Our team type for this game."""
38
39    def __init__(self, timeremaining: int) -> None:
40        self.timeremaining = timeremaining
41        self.holdingflag = False

Our team type for this game.

Team(timeremaining: int)
39    def __init__(self, timeremaining: int) -> None:
40        self.timeremaining = timeremaining
41        self.holdingflag = False
Inherited Members
ba._team.Team
manual_init
customdata
on_expire
sessionteam
class KeepAwayGame(ba._teamgame.TeamGameActivity[bastd.game.keepaway.Player, bastd.game.keepaway.Team]):
 45class KeepAwayGame(ba.TeamGameActivity[Player, Team]):
 46    """Game where you try to keep the flag away from your enemies."""
 47
 48    name = 'Keep Away'
 49    description = 'Carry the flag for a set length of time.'
 50    available_settings = [
 51        ba.IntSetting(
 52            'Hold Time',
 53            min_value=10,
 54            default=30,
 55            increment=10,
 56        ),
 57        ba.IntChoiceSetting(
 58            'Time Limit',
 59            choices=[
 60                ('None', 0),
 61                ('1 Minute', 60),
 62                ('2 Minutes', 120),
 63                ('5 Minutes', 300),
 64                ('10 Minutes', 600),
 65                ('20 Minutes', 1200),
 66            ],
 67            default=0,
 68        ),
 69        ba.FloatChoiceSetting(
 70            'Respawn Times',
 71            choices=[
 72                ('Shorter', 0.25),
 73                ('Short', 0.5),
 74                ('Normal', 1.0),
 75                ('Long', 2.0),
 76                ('Longer', 4.0),
 77            ],
 78            default=1.0,
 79        ),
 80    ]
 81    scoreconfig = ba.ScoreConfig(label='Time Held')
 82    default_music = ba.MusicType.KEEP_AWAY
 83
 84    @classmethod
 85    def supports_session_type(cls, sessiontype: type[ba.Session]) -> bool:
 86        return (issubclass(sessiontype, ba.DualTeamSession)
 87                or issubclass(sessiontype, ba.FreeForAllSession))
 88
 89    @classmethod
 90    def get_supported_maps(cls, sessiontype: type[ba.Session]) -> list[str]:
 91        return ba.getmaps('keep_away')
 92
 93    def __init__(self, settings: dict):
 94        super().__init__(settings)
 95        self._scoreboard = Scoreboard()
 96        self._swipsound = ba.getsound('swip')
 97        self._tick_sound = ba.getsound('tick')
 98        self._countdownsounds = {
 99            10: ba.getsound('announceTen'),
100            9: ba.getsound('announceNine'),
101            8: ba.getsound('announceEight'),
102            7: ba.getsound('announceSeven'),
103            6: ba.getsound('announceSix'),
104            5: ba.getsound('announceFive'),
105            4: ba.getsound('announceFour'),
106            3: ba.getsound('announceThree'),
107            2: ba.getsound('announceTwo'),
108            1: ba.getsound('announceOne')
109        }
110        self._flag_spawn_pos: Sequence[float] | None = None
111        self._update_timer: ba.Timer | None = None
112        self._holding_players: list[Player] = []
113        self._flag_state: FlagState | None = None
114        self._flag_light: ba.Node | None = None
115        self._scoring_team: Team | None = None
116        self._flag: Flag | None = None
117        self._hold_time = int(settings['Hold Time'])
118        self._time_limit = float(settings['Time Limit'])
119
120    def get_instance_description(self) -> str | Sequence:
121        return 'Carry the flag for ${ARG1} seconds.', self._hold_time
122
123    def get_instance_description_short(self) -> str | Sequence:
124        return 'carry the flag for ${ARG1} seconds', self._hold_time
125
126    def create_team(self, sessionteam: ba.SessionTeam) -> Team:
127        return Team(timeremaining=self._hold_time)
128
129    def on_team_join(self, team: Team) -> None:
130        self._update_scoreboard()
131
132    def on_begin(self) -> None:
133        super().on_begin()
134        self.setup_standard_time_limit(self._time_limit)
135        self.setup_standard_powerup_drops()
136        self._flag_spawn_pos = self.map.get_flag_position(None)
137        self._spawn_flag()
138        self._update_timer = ba.Timer(1.0, call=self._tick, repeat=True)
139        self._update_flag_state()
140        Flag.project_stand(self._flag_spawn_pos)
141
142    def _tick(self) -> None:
143        self._update_flag_state()
144
145        # Award points to all living players holding the flag.
146        for player in self._holding_players:
147            if player:
148                assert self.stats
149                self.stats.player_scored(player,
150                                         3,
151                                         screenmessage=False,
152                                         display=False)
153
154        scoreteam = self._scoring_team
155
156        if scoreteam is not None:
157
158            if scoreteam.timeremaining > 0:
159                ba.playsound(self._tick_sound)
160
161            scoreteam.timeremaining = max(0, scoreteam.timeremaining - 1)
162            self._update_scoreboard()
163            if scoreteam.timeremaining > 0:
164                assert self._flag is not None
165                self._flag.set_score_text(str(scoreteam.timeremaining))
166
167            # Announce numbers we have sounds for.
168            if scoreteam.timeremaining in self._countdownsounds:
169                ba.playsound(self._countdownsounds[scoreteam.timeremaining])
170
171            # Winner.
172            if scoreteam.timeremaining <= 0:
173                self.end_game()
174
175    def end_game(self) -> None:
176        results = ba.GameResults()
177        for team in self.teams:
178            results.set_team_score(team, self._hold_time - team.timeremaining)
179        self.end(results=results, announce_delay=0)
180
181    def _update_flag_state(self) -> None:
182        for team in self.teams:
183            team.holdingflag = False
184        self._holding_players = []
185        for player in self.players:
186            holdingflag = False
187            try:
188                assert isinstance(player.actor, (PlayerSpaz, type(None)))
189                if (player.actor and player.actor.node
190                        and player.actor.node.hold_node):
191                    holdingflag = (
192                        player.actor.node.hold_node.getnodetype() == 'flag')
193            except Exception:
194                ba.print_exception('Error checking hold flag.')
195            if holdingflag:
196                self._holding_players.append(player)
197                player.team.holdingflag = True
198
199        holdingteams = set(t for t in self.teams if t.holdingflag)
200        prevstate = self._flag_state
201        assert self._flag is not None
202        assert self._flag_light
203        assert self._flag.node
204        if len(holdingteams) > 1:
205            self._flag_state = FlagState.CONTESTED
206            self._scoring_team = None
207            self._flag_light.color = (0.6, 0.6, 0.1)
208            self._flag.node.color = (1.0, 1.0, 0.4)
209        elif len(holdingteams) == 1:
210            holdingteam = list(holdingteams)[0]
211            self._flag_state = FlagState.HELD
212            self._scoring_team = holdingteam
213            self._flag_light.color = ba.normalized_color(holdingteam.color)
214            self._flag.node.color = holdingteam.color
215        else:
216            self._flag_state = FlagState.UNCONTESTED
217            self._scoring_team = None
218            self._flag_light.color = (0.2, 0.2, 0.2)
219            self._flag.node.color = (1, 1, 1)
220
221        if self._flag_state != prevstate:
222            ba.playsound(self._swipsound)
223
224    def _spawn_flag(self) -> None:
225        ba.playsound(self._swipsound)
226        self._flash_flag_spawn()
227        assert self._flag_spawn_pos is not None
228        self._flag = Flag(dropped_timeout=20, position=self._flag_spawn_pos)
229        self._flag_state = FlagState.NEW
230        self._flag_light = ba.newnode('light',
231                                      owner=self._flag.node,
232                                      attrs={
233                                          'intensity': 0.2,
234                                          'radius': 0.3,
235                                          'color': (0.2, 0.2, 0.2)
236                                      })
237        assert self._flag.node
238        self._flag.node.connectattr('position', self._flag_light, 'position')
239        self._update_flag_state()
240
241    def _flash_flag_spawn(self) -> None:
242        light = ba.newnode('light',
243                           attrs={
244                               'position': self._flag_spawn_pos,
245                               'color': (1, 1, 1),
246                               'radius': 0.3,
247                               'height_attenuated': False
248                           })
249        ba.animate(light, 'intensity', {0.0: 0, 0.25: 0.5, 0.5: 0}, loop=True)
250        ba.timer(1.0, light.delete)
251
252    def _update_scoreboard(self) -> None:
253        for team in self.teams:
254            self._scoreboard.set_team_value(team,
255                                            team.timeremaining,
256                                            self._hold_time,
257                                            countdown=True)
258
259    def handlemessage(self, msg: Any) -> Any:
260        if isinstance(msg, ba.PlayerDiedMessage):
261            # Augment standard behavior.
262            super().handlemessage(msg)
263            self.respawn_player(msg.getplayer(Player))
264        elif isinstance(msg, FlagDiedMessage):
265            self._spawn_flag()
266        elif isinstance(msg, (FlagDroppedMessage, FlagPickedUpMessage)):
267            self._update_flag_state()
268        else:
269            super().handlemessage(msg)

Game where you try to keep the flag away from your enemies.

KeepAwayGame(settings: dict)
 93    def __init__(self, settings: dict):
 94        super().__init__(settings)
 95        self._scoreboard = Scoreboard()
 96        self._swipsound = ba.getsound('swip')
 97        self._tick_sound = ba.getsound('tick')
 98        self._countdownsounds = {
 99            10: ba.getsound('announceTen'),
100            9: ba.getsound('announceNine'),
101            8: ba.getsound('announceEight'),
102            7: ba.getsound('announceSeven'),
103            6: ba.getsound('announceSix'),
104            5: ba.getsound('announceFive'),
105            4: ba.getsound('announceFour'),
106            3: ba.getsound('announceThree'),
107            2: ba.getsound('announceTwo'),
108            1: ba.getsound('announceOne')
109        }
110        self._flag_spawn_pos: Sequence[float] | None = None
111        self._update_timer: ba.Timer | None = None
112        self._holding_players: list[Player] = []
113        self._flag_state: FlagState | None = None
114        self._flag_light: ba.Node | None = None
115        self._scoring_team: Team | None = None
116        self._flag: Flag | None = None
117        self._hold_time = int(settings['Hold Time'])
118        self._time_limit = float(settings['Time Limit'])

Instantiate the Activity.

name: str | None = 'Keep Away'
description: str | None = 'Carry the flag for a set length of time.'
available_settings: list[ba._settings.Setting] | None = [IntSetting(name='Hold Time', default=30, min_value=10, max_value=9999, increment=10), IntChoiceSetting(name='Time Limit', default=0, choices=[('None', 0), ('1 Minute', 60), ('2 Minutes', 120), ('5 Minutes', 300), ('10 Minutes', 600), ('20 Minutes', 1200)]), FloatChoiceSetting(name='Respawn Times', default=1.0, choices=[('Shorter', 0.25), ('Short', 0.5), ('Normal', 1.0), ('Long', 2.0), ('Longer', 4.0)])]
scoreconfig: ba._score.ScoreConfig | None = ScoreConfig(label='Time Held', scoretype=<ScoreType.POINTS: 'p'>, lower_is_better=False, none_is_winner=False, version='')
default_music: ba._music.MusicType | None = <MusicType.KEEP_AWAY: 'Keep Away'>
@classmethod
def supports_session_type(cls, sessiontype: type[ba._session.Session]) -> bool:
84    @classmethod
85    def supports_session_type(cls, sessiontype: type[ba.Session]) -> bool:
86        return (issubclass(sessiontype, ba.DualTeamSession)
87                or issubclass(sessiontype, ba.FreeForAllSession))

Class method override; returns True for ba.DualTeamSessions and ba.FreeForAllSessions; False otherwise.

@classmethod
def get_supported_maps(cls, sessiontype: type[ba._session.Session]) -> list[str]:
89    @classmethod
90    def get_supported_maps(cls, sessiontype: type[ba.Session]) -> list[str]:
91        return ba.getmaps('keep_away')

Called by the default ba.GameActivity.create_settings_ui() implementation; should return a list of map names valid for this game-type for the given ba.Session type.

def get_instance_description(self) -> Union[str, Sequence]:
120    def get_instance_description(self) -> str | Sequence:
121        return 'Carry the flag for ${ARG1} seconds.', self._hold_time

Return a description for this game instance, in English.

This is shown in the center of the screen below the game name at the start of a game. It should start with a capital letter and end with a period, and can be a bit more verbose than the version returned by get_instance_description_short().

Note that translation is applied by looking up the specific returned value as a key, so the number of returned variations should be limited; ideally just one or two. To include arbitrary values in the description, you can return a sequence of values in the following form instead of just a string:

This will give us something like 'Score 3 goals.' in English

and can properly translate to 'Anota 3 goles.' in Spanish.

If we just returned the string 'Score 3 Goals' here, there would

have to be a translation entry for each specific number. ew.

return ['Score ${ARG1} goals.', self.settings_raw['Score to Win']]

This way the first string can be consistently translated, with any arg values then substituted into the result. ${ARG1} will be replaced with the first value, ${ARG2} with the second, etc.

def get_instance_description_short(self) -> Union[str, Sequence]:
123    def get_instance_description_short(self) -> str | Sequence:
124        return 'carry the flag for ${ARG1} seconds', self._hold_time

Return a short description for this game instance in English.

This description is used above the game scoreboard in the corner of the screen, so it should be as concise as possible. It should be lowercase and should not contain periods or other punctuation.

Note that translation is applied by looking up the specific returned value as a key, so the number of returned variations should be limited; ideally just one or two. To include arbitrary values in the description, you can return a sequence of values in the following form instead of just a string:

This will give us something like 'score 3 goals' in English

and can properly translate to 'anota 3 goles' in Spanish.

If we just returned the string 'score 3 goals' here, there would

have to be a translation entry for each specific number. ew.

return ['score ${ARG1} goals', self.settings_raw['Score to Win']]

This way the first string can be consistently translated, with any arg values then substituted into the result. ${ARG1} will be replaced with the first value, ${ARG2} with the second, etc.

def create_team(self, sessionteam: ba._team.SessionTeam) -> bastd.game.keepaway.Team:
126    def create_team(self, sessionteam: ba.SessionTeam) -> Team:
127        return Team(timeremaining=self._hold_time)

Create the Team instance for this Activity.

Subclasses can override this if the activity's team class requires a custom constructor; otherwise it will be called with no args. Note that the team object should not be used at this point as it is not yet fully wired up; wait for on_team_join() for that.

def on_team_join(self, team: bastd.game.keepaway.Team) -> None:
129    def on_team_join(self, team: Team) -> None:
130        self._update_scoreboard()

Called when a new ba.Team joins the Activity.

(including the initial set of Teams)

def on_begin(self) -> None:
132    def on_begin(self) -> None:
133        super().on_begin()
134        self.setup_standard_time_limit(self._time_limit)
135        self.setup_standard_powerup_drops()
136        self._flag_spawn_pos = self.map.get_flag_position(None)
137        self._spawn_flag()
138        self._update_timer = ba.Timer(1.0, call=self._tick, repeat=True)
139        self._update_flag_state()
140        Flag.project_stand(self._flag_spawn_pos)

Called once the previous ba.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.

def end_game(self) -> None:
175    def end_game(self) -> None:
176        results = ba.GameResults()
177        for team in self.teams:
178            results.set_team_score(team, self._hold_time - team.timeremaining)
179        self.end(results=results, announce_delay=0)

Tell the game to wrap up and call ba.Activity.end() immediately.

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 (ba.GameActivity.setup_standard_time_limit()) will work with the game.

def handlemessage(self, msg: Any) -> Any:
259    def handlemessage(self, msg: Any) -> Any:
260        if isinstance(msg, ba.PlayerDiedMessage):
261            # Augment standard behavior.
262            super().handlemessage(msg)
263            self.respawn_player(msg.getplayer(Player))
264        elif isinstance(msg, FlagDiedMessage):
265            self._spawn_flag()
266        elif isinstance(msg, (FlagDroppedMessage, FlagPickedUpMessage)):
267            self._update_flag_state()
268        else:
269            super().handlemessage(msg)

General message handling; can be passed any message object.

Inherited Members
ba._teamgame.TeamGameActivity
on_transition_in
spawn_player_spaz
end
ba._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
map
get_instance_display_string
get_instance_scoreboard_display_string
on_continue
is_waiting_for_continue
continue_or_end_game
on_player_join
respawn_player
spawn_player_if_exists
spawn_player
setup_standard_powerup_drops
setup_standard_time_limit
show_zoom_message
ba._activity.Activity
settings_raw
teams
players
announce_player_deaths
is_joining_activity
use_fixed_vr_overlay
slow_motion
inherits_slow_motion
inherits_music
inherits_vr_camera_offset
inherits_vr_overlay_center
inherits_tint
allow_mid_activity_joins
transition_time
can_show_ad_on_death
globalsnode
stats
on_expire
customdata
expired
playertype
teamtype
retain_actor
add_actor_weak_ref
session
on_player_leave
on_team_leave
on_transition_out
has_transitioned_in
has_begun
has_ended
is_transitioning_out
transition_out
create_player
ba._dependency.DependencyComponent
dep_is_present
get_dynamic_deps