bastd.game.kingofthehill
Defines the King of the Hill game.
1# Released under the MIT License. See LICENSE for details. 2# 3"""Defines the King of the Hill game.""" 4 5# ba_meta require api 7 6# (see https://ballistica.net/wiki/meta-tag-system) 7 8from __future__ import annotations 9 10import weakref 11from enum import Enum 12from typing import TYPE_CHECKING 13 14import ba 15from bastd.actor.flag import Flag 16from bastd.actor.playerspaz import PlayerSpaz 17from bastd.actor.scoreboard import Scoreboard 18from bastd.gameutils import SharedObjects 19 20if TYPE_CHECKING: 21 from typing import Any, Sequence 22 23 24class FlagState(Enum): 25 """States our single flag can be in.""" 26 27 NEW = 0 28 UNCONTESTED = 1 29 CONTESTED = 2 30 HELD = 3 31 32 33class Player(ba.Player['Team']): 34 """Our player type for this game.""" 35 36 def __init__(self) -> None: 37 self.time_at_flag = 0 38 39 40class Team(ba.Team[Player]): 41 """Our team type for this game.""" 42 43 def __init__(self, time_remaining: int) -> None: 44 self.time_remaining = time_remaining 45 46 47# ba_meta export game 48class KingOfTheHillGame(ba.TeamGameActivity[Player, Team]): 49 """Game where a team wins by holding a 'hill' for a set amount of time.""" 50 51 name = 'King of the Hill' 52 description = 'Secure the flag for a set length of time.' 53 available_settings = [ 54 ba.IntSetting( 55 'Hold Time', 56 min_value=10, 57 default=30, 58 increment=10, 59 ), 60 ba.IntChoiceSetting( 61 'Time Limit', 62 choices=[ 63 ('None', 0), 64 ('1 Minute', 60), 65 ('2 Minutes', 120), 66 ('5 Minutes', 300), 67 ('10 Minutes', 600), 68 ('20 Minutes', 1200), 69 ], 70 default=0, 71 ), 72 ba.FloatChoiceSetting( 73 'Respawn Times', 74 choices=[ 75 ('Shorter', 0.25), 76 ('Short', 0.5), 77 ('Normal', 1.0), 78 ('Long', 2.0), 79 ('Longer', 4.0), 80 ], 81 default=1.0, 82 ), 83 ba.BoolSetting('Epic Mode', default=False), 84 ] 85 scoreconfig = ba.ScoreConfig(label='Time Held') 86 87 @classmethod 88 def supports_session_type(cls, sessiontype: type[ba.Session]) -> bool: 89 return issubclass(sessiontype, ba.MultiTeamSession) 90 91 @classmethod 92 def get_supported_maps(cls, sessiontype: type[ba.Session]) -> list[str]: 93 return ba.getmaps('king_of_the_hill') 94 95 def __init__(self, settings: dict): 96 super().__init__(settings) 97 shared = SharedObjects.get() 98 self._scoreboard = Scoreboard() 99 self._swipsound = ba.getsound('swip') 100 self._tick_sound = ba.getsound('tick') 101 self._countdownsounds = { 102 10: ba.getsound('announceTen'), 103 9: ba.getsound('announceNine'), 104 8: ba.getsound('announceEight'), 105 7: ba.getsound('announceSeven'), 106 6: ba.getsound('announceSix'), 107 5: ba.getsound('announceFive'), 108 4: ba.getsound('announceFour'), 109 3: ba.getsound('announceThree'), 110 2: ba.getsound('announceTwo'), 111 1: ba.getsound('announceOne'), 112 } 113 self._flag_pos: Sequence[float] | None = None 114 self._flag_state: FlagState | None = None 115 self._flag: Flag | None = None 116 self._flag_light: ba.Node | None = None 117 self._scoring_team: weakref.ref[Team] | None = None 118 self._hold_time = int(settings['Hold Time']) 119 self._time_limit = float(settings['Time Limit']) 120 self._epic_mode = bool(settings['Epic Mode']) 121 self._flag_region_material = ba.Material() 122 self._flag_region_material.add_actions( 123 conditions=('they_have_material', shared.player_material), 124 actions=( 125 ('modify_part_collision', 'collide', True), 126 ('modify_part_collision', 'physical', False), 127 ( 128 'call', 129 'at_connect', 130 ba.Call(self._handle_player_flag_region_collide, True), 131 ), 132 ( 133 'call', 134 'at_disconnect', 135 ba.Call(self._handle_player_flag_region_collide, False), 136 ), 137 ), 138 ) 139 140 # Base class overrides. 141 self.slow_motion = self._epic_mode 142 self.default_music = ( 143 ba.MusicType.EPIC if self._epic_mode else ba.MusicType.SCARY 144 ) 145 146 def get_instance_description(self) -> str | Sequence: 147 return 'Secure the flag for ${ARG1} seconds.', self._hold_time 148 149 def get_instance_description_short(self) -> str | Sequence: 150 return 'secure the flag for ${ARG1} seconds', self._hold_time 151 152 def create_team(self, sessionteam: ba.SessionTeam) -> Team: 153 return Team(time_remaining=self._hold_time) 154 155 def on_begin(self) -> None: 156 super().on_begin() 157 shared = SharedObjects.get() 158 self.setup_standard_time_limit(self._time_limit) 159 self.setup_standard_powerup_drops() 160 self._flag_pos = self.map.get_flag_position(None) 161 ba.timer(1.0, self._tick, repeat=True) 162 self._flag_state = FlagState.NEW 163 Flag.project_stand(self._flag_pos) 164 self._flag = Flag( 165 position=self._flag_pos, touchable=False, color=(1, 1, 1) 166 ) 167 self._flag_light = ba.newnode( 168 'light', 169 attrs={ 170 'position': self._flag_pos, 171 'intensity': 0.2, 172 'height_attenuated': False, 173 'radius': 0.4, 174 'color': (0.2, 0.2, 0.2), 175 }, 176 ) 177 # Flag region. 178 flagmats = [self._flag_region_material, shared.region_material] 179 ba.newnode( 180 'region', 181 attrs={ 182 'position': self._flag_pos, 183 'scale': (1.8, 1.8, 1.8), 184 'type': 'sphere', 185 'materials': flagmats, 186 }, 187 ) 188 self._update_flag_state() 189 190 def _tick(self) -> None: 191 self._update_flag_state() 192 193 # Give holding players points. 194 for player in self.players: 195 if player.time_at_flag > 0: 196 self.stats.player_scored( 197 player, 3, screenmessage=False, display=False 198 ) 199 if self._scoring_team is None: 200 scoring_team = None 201 else: 202 scoring_team = self._scoring_team() 203 if scoring_team: 204 205 if scoring_team.time_remaining > 0: 206 ba.playsound(self._tick_sound) 207 208 scoring_team.time_remaining = max( 209 0, scoring_team.time_remaining - 1 210 ) 211 self._update_scoreboard() 212 if scoring_team.time_remaining > 0: 213 assert self._flag is not None 214 self._flag.set_score_text(str(scoring_team.time_remaining)) 215 216 # Announce numbers we have sounds for. 217 numsound = self._countdownsounds.get(scoring_team.time_remaining) 218 if numsound is not None: 219 ba.playsound(numsound) 220 221 # winner 222 if scoring_team.time_remaining <= 0: 223 self.end_game() 224 225 def end_game(self) -> None: 226 results = ba.GameResults() 227 for team in self.teams: 228 results.set_team_score(team, self._hold_time - team.time_remaining) 229 self.end(results=results, announce_delay=0) 230 231 def _update_flag_state(self) -> None: 232 holding_teams = set( 233 player.team for player in self.players if player.time_at_flag 234 ) 235 prev_state = self._flag_state 236 assert self._flag_light 237 assert self._flag is not None 238 assert self._flag.node 239 if len(holding_teams) > 1: 240 self._flag_state = FlagState.CONTESTED 241 self._scoring_team = None 242 self._flag_light.color = (0.6, 0.6, 0.1) 243 self._flag.node.color = (1.0, 1.0, 0.4) 244 elif len(holding_teams) == 1: 245 holding_team = list(holding_teams)[0] 246 self._flag_state = FlagState.HELD 247 self._scoring_team = weakref.ref(holding_team) 248 self._flag_light.color = ba.normalized_color(holding_team.color) 249 self._flag.node.color = holding_team.color 250 else: 251 self._flag_state = FlagState.UNCONTESTED 252 self._scoring_team = None 253 self._flag_light.color = (0.2, 0.2, 0.2) 254 self._flag.node.color = (1, 1, 1) 255 if self._flag_state != prev_state: 256 ba.playsound(self._swipsound) 257 258 def _handle_player_flag_region_collide(self, colliding: bool) -> None: 259 try: 260 spaz = ba.getcollision().opposingnode.getdelegate(PlayerSpaz, True) 261 except ba.NotFoundError: 262 return 263 264 if not spaz.is_alive(): 265 return 266 267 player = spaz.getplayer(Player, True) 268 269 # Different parts of us can collide so a single value isn't enough 270 # also don't count it if we're dead (flying heads shouldn't be able to 271 # win the game :-) 272 if colliding and player.is_alive(): 273 player.time_at_flag += 1 274 else: 275 player.time_at_flag = max(0, player.time_at_flag - 1) 276 277 self._update_flag_state() 278 279 def _update_scoreboard(self) -> None: 280 for team in self.teams: 281 self._scoreboard.set_team_value( 282 team, team.time_remaining, self._hold_time, countdown=True 283 ) 284 285 def handlemessage(self, msg: Any) -> Any: 286 if isinstance(msg, ba.PlayerDiedMessage): 287 super().handlemessage(msg) # Augment default. 288 289 # No longer can count as time_at_flag once dead. 290 player = msg.getplayer(Player) 291 player.time_at_flag = 0 292 self._update_flag_state() 293 self.respawn_player(player)
25class FlagState(Enum): 26 """States our single flag can be in.""" 27 28 NEW = 0 29 UNCONTESTED = 1 30 CONTESTED = 2 31 HELD = 3
States our single flag can be in.
Inherited Members
- enum.Enum
- name
- value
34class Player(ba.Player['Team']): 35 """Our player type for this game.""" 36 37 def __init__(self) -> None: 38 self.time_at_flag = 0
Our player type for this game.
Inherited Members
- ba._player.Player
- actor
- on_expire
- team
- customdata
- sessionplayer
- node
- position
- exists
- getname
- is_alive
- get_icon
- assigninput
- resetinput
41class Team(ba.Team[Player]): 42 """Our team type for this game.""" 43 44 def __init__(self, time_remaining: int) -> None: 45 self.time_remaining = time_remaining
Our team type for this game.
Inherited Members
- ba._team.Team
- manual_init
- customdata
- on_expire
- sessionteam
49class KingOfTheHillGame(ba.TeamGameActivity[Player, Team]): 50 """Game where a team wins by holding a 'hill' for a set amount of time.""" 51 52 name = 'King of the Hill' 53 description = 'Secure the flag for a set length of time.' 54 available_settings = [ 55 ba.IntSetting( 56 'Hold Time', 57 min_value=10, 58 default=30, 59 increment=10, 60 ), 61 ba.IntChoiceSetting( 62 'Time Limit', 63 choices=[ 64 ('None', 0), 65 ('1 Minute', 60), 66 ('2 Minutes', 120), 67 ('5 Minutes', 300), 68 ('10 Minutes', 600), 69 ('20 Minutes', 1200), 70 ], 71 default=0, 72 ), 73 ba.FloatChoiceSetting( 74 'Respawn Times', 75 choices=[ 76 ('Shorter', 0.25), 77 ('Short', 0.5), 78 ('Normal', 1.0), 79 ('Long', 2.0), 80 ('Longer', 4.0), 81 ], 82 default=1.0, 83 ), 84 ba.BoolSetting('Epic Mode', default=False), 85 ] 86 scoreconfig = ba.ScoreConfig(label='Time Held') 87 88 @classmethod 89 def supports_session_type(cls, sessiontype: type[ba.Session]) -> bool: 90 return issubclass(sessiontype, ba.MultiTeamSession) 91 92 @classmethod 93 def get_supported_maps(cls, sessiontype: type[ba.Session]) -> list[str]: 94 return ba.getmaps('king_of_the_hill') 95 96 def __init__(self, settings: dict): 97 super().__init__(settings) 98 shared = SharedObjects.get() 99 self._scoreboard = Scoreboard() 100 self._swipsound = ba.getsound('swip') 101 self._tick_sound = ba.getsound('tick') 102 self._countdownsounds = { 103 10: ba.getsound('announceTen'), 104 9: ba.getsound('announceNine'), 105 8: ba.getsound('announceEight'), 106 7: ba.getsound('announceSeven'), 107 6: ba.getsound('announceSix'), 108 5: ba.getsound('announceFive'), 109 4: ba.getsound('announceFour'), 110 3: ba.getsound('announceThree'), 111 2: ba.getsound('announceTwo'), 112 1: ba.getsound('announceOne'), 113 } 114 self._flag_pos: Sequence[float] | None = None 115 self._flag_state: FlagState | None = None 116 self._flag: Flag | None = None 117 self._flag_light: ba.Node | None = None 118 self._scoring_team: weakref.ref[Team] | None = None 119 self._hold_time = int(settings['Hold Time']) 120 self._time_limit = float(settings['Time Limit']) 121 self._epic_mode = bool(settings['Epic Mode']) 122 self._flag_region_material = ba.Material() 123 self._flag_region_material.add_actions( 124 conditions=('they_have_material', shared.player_material), 125 actions=( 126 ('modify_part_collision', 'collide', True), 127 ('modify_part_collision', 'physical', False), 128 ( 129 'call', 130 'at_connect', 131 ba.Call(self._handle_player_flag_region_collide, True), 132 ), 133 ( 134 'call', 135 'at_disconnect', 136 ba.Call(self._handle_player_flag_region_collide, False), 137 ), 138 ), 139 ) 140 141 # Base class overrides. 142 self.slow_motion = self._epic_mode 143 self.default_music = ( 144 ba.MusicType.EPIC if self._epic_mode else ba.MusicType.SCARY 145 ) 146 147 def get_instance_description(self) -> str | Sequence: 148 return 'Secure the flag for ${ARG1} seconds.', self._hold_time 149 150 def get_instance_description_short(self) -> str | Sequence: 151 return 'secure the flag for ${ARG1} seconds', self._hold_time 152 153 def create_team(self, sessionteam: ba.SessionTeam) -> Team: 154 return Team(time_remaining=self._hold_time) 155 156 def on_begin(self) -> None: 157 super().on_begin() 158 shared = SharedObjects.get() 159 self.setup_standard_time_limit(self._time_limit) 160 self.setup_standard_powerup_drops() 161 self._flag_pos = self.map.get_flag_position(None) 162 ba.timer(1.0, self._tick, repeat=True) 163 self._flag_state = FlagState.NEW 164 Flag.project_stand(self._flag_pos) 165 self._flag = Flag( 166 position=self._flag_pos, touchable=False, color=(1, 1, 1) 167 ) 168 self._flag_light = ba.newnode( 169 'light', 170 attrs={ 171 'position': self._flag_pos, 172 'intensity': 0.2, 173 'height_attenuated': False, 174 'radius': 0.4, 175 'color': (0.2, 0.2, 0.2), 176 }, 177 ) 178 # Flag region. 179 flagmats = [self._flag_region_material, shared.region_material] 180 ba.newnode( 181 'region', 182 attrs={ 183 'position': self._flag_pos, 184 'scale': (1.8, 1.8, 1.8), 185 'type': 'sphere', 186 'materials': flagmats, 187 }, 188 ) 189 self._update_flag_state() 190 191 def _tick(self) -> None: 192 self._update_flag_state() 193 194 # Give holding players points. 195 for player in self.players: 196 if player.time_at_flag > 0: 197 self.stats.player_scored( 198 player, 3, screenmessage=False, display=False 199 ) 200 if self._scoring_team is None: 201 scoring_team = None 202 else: 203 scoring_team = self._scoring_team() 204 if scoring_team: 205 206 if scoring_team.time_remaining > 0: 207 ba.playsound(self._tick_sound) 208 209 scoring_team.time_remaining = max( 210 0, scoring_team.time_remaining - 1 211 ) 212 self._update_scoreboard() 213 if scoring_team.time_remaining > 0: 214 assert self._flag is not None 215 self._flag.set_score_text(str(scoring_team.time_remaining)) 216 217 # Announce numbers we have sounds for. 218 numsound = self._countdownsounds.get(scoring_team.time_remaining) 219 if numsound is not None: 220 ba.playsound(numsound) 221 222 # winner 223 if scoring_team.time_remaining <= 0: 224 self.end_game() 225 226 def end_game(self) -> None: 227 results = ba.GameResults() 228 for team in self.teams: 229 results.set_team_score(team, self._hold_time - team.time_remaining) 230 self.end(results=results, announce_delay=0) 231 232 def _update_flag_state(self) -> None: 233 holding_teams = set( 234 player.team for player in self.players if player.time_at_flag 235 ) 236 prev_state = self._flag_state 237 assert self._flag_light 238 assert self._flag is not None 239 assert self._flag.node 240 if len(holding_teams) > 1: 241 self._flag_state = FlagState.CONTESTED 242 self._scoring_team = None 243 self._flag_light.color = (0.6, 0.6, 0.1) 244 self._flag.node.color = (1.0, 1.0, 0.4) 245 elif len(holding_teams) == 1: 246 holding_team = list(holding_teams)[0] 247 self._flag_state = FlagState.HELD 248 self._scoring_team = weakref.ref(holding_team) 249 self._flag_light.color = ba.normalized_color(holding_team.color) 250 self._flag.node.color = holding_team.color 251 else: 252 self._flag_state = FlagState.UNCONTESTED 253 self._scoring_team = None 254 self._flag_light.color = (0.2, 0.2, 0.2) 255 self._flag.node.color = (1, 1, 1) 256 if self._flag_state != prev_state: 257 ba.playsound(self._swipsound) 258 259 def _handle_player_flag_region_collide(self, colliding: bool) -> None: 260 try: 261 spaz = ba.getcollision().opposingnode.getdelegate(PlayerSpaz, True) 262 except ba.NotFoundError: 263 return 264 265 if not spaz.is_alive(): 266 return 267 268 player = spaz.getplayer(Player, True) 269 270 # Different parts of us can collide so a single value isn't enough 271 # also don't count it if we're dead (flying heads shouldn't be able to 272 # win the game :-) 273 if colliding and player.is_alive(): 274 player.time_at_flag += 1 275 else: 276 player.time_at_flag = max(0, player.time_at_flag - 1) 277 278 self._update_flag_state() 279 280 def _update_scoreboard(self) -> None: 281 for team in self.teams: 282 self._scoreboard.set_team_value( 283 team, team.time_remaining, self._hold_time, countdown=True 284 ) 285 286 def handlemessage(self, msg: Any) -> Any: 287 if isinstance(msg, ba.PlayerDiedMessage): 288 super().handlemessage(msg) # Augment default. 289 290 # No longer can count as time_at_flag once dead. 291 player = msg.getplayer(Player) 292 player.time_at_flag = 0 293 self._update_flag_state() 294 self.respawn_player(player)
Game where a team wins by holding a 'hill' for a set amount of time.
96 def __init__(self, settings: dict): 97 super().__init__(settings) 98 shared = SharedObjects.get() 99 self._scoreboard = Scoreboard() 100 self._swipsound = ba.getsound('swip') 101 self._tick_sound = ba.getsound('tick') 102 self._countdownsounds = { 103 10: ba.getsound('announceTen'), 104 9: ba.getsound('announceNine'), 105 8: ba.getsound('announceEight'), 106 7: ba.getsound('announceSeven'), 107 6: ba.getsound('announceSix'), 108 5: ba.getsound('announceFive'), 109 4: ba.getsound('announceFour'), 110 3: ba.getsound('announceThree'), 111 2: ba.getsound('announceTwo'), 112 1: ba.getsound('announceOne'), 113 } 114 self._flag_pos: Sequence[float] | None = None 115 self._flag_state: FlagState | None = None 116 self._flag: Flag | None = None 117 self._flag_light: ba.Node | None = None 118 self._scoring_team: weakref.ref[Team] | None = None 119 self._hold_time = int(settings['Hold Time']) 120 self._time_limit = float(settings['Time Limit']) 121 self._epic_mode = bool(settings['Epic Mode']) 122 self._flag_region_material = ba.Material() 123 self._flag_region_material.add_actions( 124 conditions=('they_have_material', shared.player_material), 125 actions=( 126 ('modify_part_collision', 'collide', True), 127 ('modify_part_collision', 'physical', False), 128 ( 129 'call', 130 'at_connect', 131 ba.Call(self._handle_player_flag_region_collide, True), 132 ), 133 ( 134 'call', 135 'at_disconnect', 136 ba.Call(self._handle_player_flag_region_collide, False), 137 ), 138 ), 139 ) 140 141 # Base class overrides. 142 self.slow_motion = self._epic_mode 143 self.default_music = ( 144 ba.MusicType.EPIC if self._epic_mode else ba.MusicType.SCARY 145 )
Instantiate the Activity.
88 @classmethod 89 def supports_session_type(cls, sessiontype: type[ba.Session]) -> bool: 90 return issubclass(sessiontype, ba.MultiTeamSession)
Class method override; returns True for ba.DualTeamSessions and ba.FreeForAllSessions; False otherwise.
92 @classmethod 93 def get_supported_maps(cls, sessiontype: type[ba.Session]) -> list[str]: 94 return ba.getmaps('king_of_the_hill')
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.
147 def get_instance_description(self) -> str | Sequence: 148 return 'Secure 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.
150 def get_instance_description_short(self) -> str | Sequence: 151 return 'secure 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.
153 def create_team(self, sessionteam: ba.SessionTeam) -> Team: 154 return Team(time_remaining=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.
156 def on_begin(self) -> None: 157 super().on_begin() 158 shared = SharedObjects.get() 159 self.setup_standard_time_limit(self._time_limit) 160 self.setup_standard_powerup_drops() 161 self._flag_pos = self.map.get_flag_position(None) 162 ba.timer(1.0, self._tick, repeat=True) 163 self._flag_state = FlagState.NEW 164 Flag.project_stand(self._flag_pos) 165 self._flag = Flag( 166 position=self._flag_pos, touchable=False, color=(1, 1, 1) 167 ) 168 self._flag_light = ba.newnode( 169 'light', 170 attrs={ 171 'position': self._flag_pos, 172 'intensity': 0.2, 173 'height_attenuated': False, 174 'radius': 0.4, 175 'color': (0.2, 0.2, 0.2), 176 }, 177 ) 178 # Flag region. 179 flagmats = [self._flag_region_material, shared.region_material] 180 ba.newnode( 181 'region', 182 attrs={ 183 'position': self._flag_pos, 184 'scale': (1.8, 1.8, 1.8), 185 'type': 'sphere', 186 'materials': flagmats, 187 }, 188 ) 189 self._update_flag_state()
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.
226 def end_game(self) -> None: 227 results = ba.GameResults() 228 for team in self.teams: 229 results.set_team_score(team, self._hold_time - team.time_remaining) 230 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.
286 def handlemessage(self, msg: Any) -> Any: 287 if isinstance(msg, ba.PlayerDiedMessage): 288 super().handlemessage(msg) # Augment default. 289 290 # No longer can count as time_at_flag once dead. 291 player = msg.getplayer(Player) 292 player.time_at_flag = 0 293 self._update_flag_state() 294 self.respawn_player(player)
General message handling; can be passed any message object.
Inherited Members
- ba._teamgame.TeamGameActivity
- on_transition_in
- spawn_player_spaz
- end
- ba._gameactivity.GameActivity
- allow_pausing
- allow_kick_idle_players
- 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
- 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_join
- 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