bastd.game.chosenone
Provides the chosen-one mini-game.
1# Released under the MIT License. See LICENSE for details. 2# 3"""Provides the chosen-one mini-game.""" 4 5# ba_meta require api 7 6# (see https://ballistica.net/wiki/meta-tag-system) 7 8from __future__ import annotations 9 10from typing import TYPE_CHECKING 11 12import ba 13from bastd.actor.flag import Flag 14from bastd.actor.playerspaz import PlayerSpaz 15from bastd.actor.scoreboard import Scoreboard 16from bastd.gameutils import SharedObjects 17 18if TYPE_CHECKING: 19 from typing import Any, Sequence 20 21 22class Player(ba.Player['Team']): 23 """Our player type for this game.""" 24 25 def __init__(self) -> None: 26 self.chosen_light: ba.NodeActor | None = None 27 28 29class Team(ba.Team[Player]): 30 """Our team type for this game.""" 31 32 def __init__(self, time_remaining: int) -> None: 33 self.time_remaining = time_remaining 34 35 36# ba_meta export game 37class ChosenOneGame(ba.TeamGameActivity[Player, Team]): 38 """ 39 Game involving trying to remain the one 'chosen one' 40 for a set length of time while everyone else tries to 41 kill you and become the chosen one themselves. 42 """ 43 44 name = 'Chosen One' 45 description = ( 46 'Be the chosen one for a length of time to win.\n' 47 'Kill the chosen one to become it.' 48 ) 49 available_settings = [ 50 ba.IntSetting( 51 'Chosen One Time', 52 min_value=10, 53 default=30, 54 increment=10, 55 ), 56 ba.BoolSetting('Chosen One Gets Gloves', default=True), 57 ba.BoolSetting('Chosen One Gets Shield', default=False), 58 ba.IntChoiceSetting( 59 'Time Limit', 60 choices=[ 61 ('None', 0), 62 ('1 Minute', 60), 63 ('2 Minutes', 120), 64 ('5 Minutes', 300), 65 ('10 Minutes', 600), 66 ('20 Minutes', 1200), 67 ], 68 default=0, 69 ), 70 ba.FloatChoiceSetting( 71 'Respawn Times', 72 choices=[ 73 ('Shorter', 0.25), 74 ('Short', 0.5), 75 ('Normal', 1.0), 76 ('Long', 2.0), 77 ('Longer', 4.0), 78 ], 79 default=1.0, 80 ), 81 ba.BoolSetting('Epic Mode', default=False), 82 ] 83 scoreconfig = ba.ScoreConfig(label='Time Held') 84 85 @classmethod 86 def get_supported_maps(cls, sessiontype: type[ba.Session]) -> list[str]: 87 return ba.getmaps('keep_away') 88 89 def __init__(self, settings: dict): 90 super().__init__(settings) 91 self._scoreboard = Scoreboard() 92 self._chosen_one_player: Player | None = None 93 self._swipsound = ba.getsound('swip') 94 self._countdownsounds: dict[int, ba.Sound] = { 95 10: ba.getsound('announceTen'), 96 9: ba.getsound('announceNine'), 97 8: ba.getsound('announceEight'), 98 7: ba.getsound('announceSeven'), 99 6: ba.getsound('announceSix'), 100 5: ba.getsound('announceFive'), 101 4: ba.getsound('announceFour'), 102 3: ba.getsound('announceThree'), 103 2: ba.getsound('announceTwo'), 104 1: ba.getsound('announceOne'), 105 } 106 self._flag_spawn_pos: Sequence[float] | None = None 107 self._reset_region_material: ba.Material | None = None 108 self._flag: Flag | None = None 109 self._reset_region: ba.Node | None = None 110 self._epic_mode = bool(settings['Epic Mode']) 111 self._chosen_one_time = int(settings['Chosen One Time']) 112 self._time_limit = float(settings['Time Limit']) 113 self._chosen_one_gets_shield = bool(settings['Chosen One Gets Shield']) 114 self._chosen_one_gets_gloves = bool(settings['Chosen One Gets Gloves']) 115 116 # Base class overrides 117 self.slow_motion = self._epic_mode 118 self.default_music = ( 119 ba.MusicType.EPIC if self._epic_mode else ba.MusicType.CHOSEN_ONE 120 ) 121 122 def get_instance_description(self) -> str | Sequence: 123 return 'There can be only one.' 124 125 def create_team(self, sessionteam: ba.SessionTeam) -> Team: 126 return Team(time_remaining=self._chosen_one_time) 127 128 def on_team_join(self, team: Team) -> None: 129 self._update_scoreboard() 130 131 def on_player_leave(self, player: Player) -> None: 132 super().on_player_leave(player) 133 if self._get_chosen_one_player() is player: 134 self._set_chosen_one_player(None) 135 136 def on_begin(self) -> None: 137 super().on_begin() 138 shared = SharedObjects.get() 139 self.setup_standard_time_limit(self._time_limit) 140 self.setup_standard_powerup_drops() 141 self._flag_spawn_pos = self.map.get_flag_position(None) 142 Flag.project_stand(self._flag_spawn_pos) 143 ba.timer(1.0, call=self._tick, repeat=True) 144 145 mat = self._reset_region_material = ba.Material() 146 mat.add_actions( 147 conditions=( 148 'they_have_material', 149 shared.player_material, 150 ), 151 actions=( 152 ('modify_part_collision', 'collide', True), 153 ('modify_part_collision', 'physical', False), 154 ('call', 'at_connect', ba.WeakCall(self._handle_reset_collide)), 155 ), 156 ) 157 158 self._set_chosen_one_player(None) 159 160 def _create_reset_region(self) -> None: 161 assert self._reset_region_material is not None 162 assert self._flag_spawn_pos is not None 163 pos = self._flag_spawn_pos 164 self._reset_region = ba.newnode( 165 'region', 166 attrs={ 167 'position': (pos[0], pos[1] + 0.75, pos[2]), 168 'scale': (0.5, 0.5, 0.5), 169 'type': 'sphere', 170 'materials': [self._reset_region_material], 171 }, 172 ) 173 174 def _get_chosen_one_player(self) -> Player | None: 175 # Should never return invalid references; return None in that case. 176 if self._chosen_one_player: 177 return self._chosen_one_player 178 return None 179 180 def _handle_reset_collide(self) -> None: 181 # If we have a chosen one, ignore these. 182 if self._get_chosen_one_player() is not None: 183 return 184 185 # Attempt to get a Actor that we hit. 186 try: 187 spaz = ba.getcollision().opposingnode.getdelegate(PlayerSpaz, True) 188 player = spaz.getplayer(Player, True) 189 except ba.NotFoundError: 190 return 191 192 if spaz.is_alive(): 193 self._set_chosen_one_player(player) 194 195 def _flash_flag_spawn(self) -> None: 196 light = ba.newnode( 197 'light', 198 attrs={ 199 'position': self._flag_spawn_pos, 200 'color': (1, 1, 1), 201 'radius': 0.3, 202 'height_attenuated': False, 203 }, 204 ) 205 ba.animate(light, 'intensity', {0: 0, 0.25: 0.5, 0.5: 0}, loop=True) 206 ba.timer(1.0, light.delete) 207 208 def _tick(self) -> None: 209 210 # Give the chosen one points. 211 player = self._get_chosen_one_player() 212 if player is not None: 213 214 # This shouldn't happen, but just in case. 215 if not player.is_alive(): 216 ba.print_error('got dead player as chosen one in _tick') 217 self._set_chosen_one_player(None) 218 else: 219 scoring_team = player.team 220 assert self.stats 221 self.stats.player_scored( 222 player, 3, screenmessage=False, display=False 223 ) 224 225 scoring_team.time_remaining = max( 226 0, scoring_team.time_remaining - 1 227 ) 228 229 # Show the count over their head 230 if scoring_team.time_remaining > 0: 231 if isinstance(player.actor, PlayerSpaz) and player.actor: 232 player.actor.set_score_text( 233 str(scoring_team.time_remaining) 234 ) 235 236 self._update_scoreboard() 237 238 # announce numbers we have sounds for 239 if scoring_team.time_remaining in self._countdownsounds: 240 ba.playsound( 241 self._countdownsounds[scoring_team.time_remaining] 242 ) 243 244 # Winner! 245 if scoring_team.time_remaining <= 0: 246 self.end_game() 247 248 else: 249 # (player is None) 250 # This shouldn't happen, but just in case. 251 # (Chosen-one player ceasing to exist should 252 # trigger on_player_leave which resets chosen-one) 253 if self._chosen_one_player is not None: 254 ba.print_error('got nonexistent player as chosen one in _tick') 255 self._set_chosen_one_player(None) 256 257 def end_game(self) -> None: 258 results = ba.GameResults() 259 for team in self.teams: 260 results.set_team_score( 261 team, self._chosen_one_time - team.time_remaining 262 ) 263 self.end(results=results, announce_delay=0) 264 265 def _set_chosen_one_player(self, player: Player | None) -> None: 266 existing = self._get_chosen_one_player() 267 if existing: 268 existing.chosen_light = None 269 ba.playsound(self._swipsound) 270 if not player: 271 assert self._flag_spawn_pos is not None 272 self._flag = Flag( 273 color=(1, 0.9, 0.2), 274 position=self._flag_spawn_pos, 275 touchable=False, 276 ) 277 self._chosen_one_player = None 278 279 # Create a light to highlight the flag; 280 # this will go away when the flag dies. 281 ba.newnode( 282 'light', 283 owner=self._flag.node, 284 attrs={ 285 'position': self._flag_spawn_pos, 286 'intensity': 0.6, 287 'height_attenuated': False, 288 'volume_intensity_scale': 0.1, 289 'radius': 0.1, 290 'color': (1.2, 1.2, 0.4), 291 }, 292 ) 293 294 # Also an extra momentary flash. 295 self._flash_flag_spawn() 296 297 # Re-create our flag region in case if someone is waiting for 298 # flag right there: 299 self._create_reset_region() 300 else: 301 if player.actor: 302 self._flag = None 303 self._chosen_one_player = player 304 305 if self._chosen_one_gets_shield: 306 player.actor.handlemessage(ba.PowerupMessage('shield')) 307 if self._chosen_one_gets_gloves: 308 player.actor.handlemessage(ba.PowerupMessage('punch')) 309 310 # Use a color that's partway between their team color 311 # and white. 312 color = [ 313 0.3 + c * 0.7 314 for c in ba.normalized_color(player.team.color) 315 ] 316 light = player.chosen_light = ba.NodeActor( 317 ba.newnode( 318 'light', 319 attrs={ 320 'intensity': 0.6, 321 'height_attenuated': False, 322 'volume_intensity_scale': 0.1, 323 'radius': 0.13, 324 'color': color, 325 }, 326 ) 327 ) 328 329 assert light.node 330 ba.animate( 331 light.node, 332 'intensity', 333 {0: 1.0, 0.2: 0.4, 0.4: 1.0}, 334 loop=True, 335 ) 336 assert isinstance(player.actor, PlayerSpaz) 337 player.actor.node.connectattr( 338 'position', light.node, 'position' 339 ) 340 341 def handlemessage(self, msg: Any) -> Any: 342 if isinstance(msg, ba.PlayerDiedMessage): 343 # Augment standard behavior. 344 super().handlemessage(msg) 345 player = msg.getplayer(Player) 346 if player is self._get_chosen_one_player(): 347 killerplayer = msg.getkillerplayer(Player) 348 self._set_chosen_one_player( 349 None 350 if ( 351 killerplayer is None 352 or killerplayer is player 353 or not killerplayer.is_alive() 354 ) 355 else killerplayer 356 ) 357 self.respawn_player(player) 358 else: 359 super().handlemessage(msg) 360 361 def _update_scoreboard(self) -> None: 362 for team in self.teams: 363 self._scoreboard.set_team_value( 364 team, team.time_remaining, self._chosen_one_time, countdown=True 365 )
23class Player(ba.Player['Team']): 24 """Our player type for this game.""" 25 26 def __init__(self) -> None: 27 self.chosen_light: ba.NodeActor | None = None
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
30class Team(ba.Team[Player]): 31 """Our team type for this game.""" 32 33 def __init__(self, time_remaining: int) -> None: 34 self.time_remaining = time_remaining
Our team type for this game.
Inherited Members
- ba._team.Team
- manual_init
- customdata
- on_expire
- sessionteam
38class ChosenOneGame(ba.TeamGameActivity[Player, Team]): 39 """ 40 Game involving trying to remain the one 'chosen one' 41 for a set length of time while everyone else tries to 42 kill you and become the chosen one themselves. 43 """ 44 45 name = 'Chosen One' 46 description = ( 47 'Be the chosen one for a length of time to win.\n' 48 'Kill the chosen one to become it.' 49 ) 50 available_settings = [ 51 ba.IntSetting( 52 'Chosen One Time', 53 min_value=10, 54 default=30, 55 increment=10, 56 ), 57 ba.BoolSetting('Chosen One Gets Gloves', default=True), 58 ba.BoolSetting('Chosen One Gets Shield', default=False), 59 ba.IntChoiceSetting( 60 'Time Limit', 61 choices=[ 62 ('None', 0), 63 ('1 Minute', 60), 64 ('2 Minutes', 120), 65 ('5 Minutes', 300), 66 ('10 Minutes', 600), 67 ('20 Minutes', 1200), 68 ], 69 default=0, 70 ), 71 ba.FloatChoiceSetting( 72 'Respawn Times', 73 choices=[ 74 ('Shorter', 0.25), 75 ('Short', 0.5), 76 ('Normal', 1.0), 77 ('Long', 2.0), 78 ('Longer', 4.0), 79 ], 80 default=1.0, 81 ), 82 ba.BoolSetting('Epic Mode', default=False), 83 ] 84 scoreconfig = ba.ScoreConfig(label='Time Held') 85 86 @classmethod 87 def get_supported_maps(cls, sessiontype: type[ba.Session]) -> list[str]: 88 return ba.getmaps('keep_away') 89 90 def __init__(self, settings: dict): 91 super().__init__(settings) 92 self._scoreboard = Scoreboard() 93 self._chosen_one_player: Player | None = None 94 self._swipsound = ba.getsound('swip') 95 self._countdownsounds: dict[int, ba.Sound] = { 96 10: ba.getsound('announceTen'), 97 9: ba.getsound('announceNine'), 98 8: ba.getsound('announceEight'), 99 7: ba.getsound('announceSeven'), 100 6: ba.getsound('announceSix'), 101 5: ba.getsound('announceFive'), 102 4: ba.getsound('announceFour'), 103 3: ba.getsound('announceThree'), 104 2: ba.getsound('announceTwo'), 105 1: ba.getsound('announceOne'), 106 } 107 self._flag_spawn_pos: Sequence[float] | None = None 108 self._reset_region_material: ba.Material | None = None 109 self._flag: Flag | None = None 110 self._reset_region: ba.Node | None = None 111 self._epic_mode = bool(settings['Epic Mode']) 112 self._chosen_one_time = int(settings['Chosen One Time']) 113 self._time_limit = float(settings['Time Limit']) 114 self._chosen_one_gets_shield = bool(settings['Chosen One Gets Shield']) 115 self._chosen_one_gets_gloves = bool(settings['Chosen One Gets Gloves']) 116 117 # Base class overrides 118 self.slow_motion = self._epic_mode 119 self.default_music = ( 120 ba.MusicType.EPIC if self._epic_mode else ba.MusicType.CHOSEN_ONE 121 ) 122 123 def get_instance_description(self) -> str | Sequence: 124 return 'There can be only one.' 125 126 def create_team(self, sessionteam: ba.SessionTeam) -> Team: 127 return Team(time_remaining=self._chosen_one_time) 128 129 def on_team_join(self, team: Team) -> None: 130 self._update_scoreboard() 131 132 def on_player_leave(self, player: Player) -> None: 133 super().on_player_leave(player) 134 if self._get_chosen_one_player() is player: 135 self._set_chosen_one_player(None) 136 137 def on_begin(self) -> None: 138 super().on_begin() 139 shared = SharedObjects.get() 140 self.setup_standard_time_limit(self._time_limit) 141 self.setup_standard_powerup_drops() 142 self._flag_spawn_pos = self.map.get_flag_position(None) 143 Flag.project_stand(self._flag_spawn_pos) 144 ba.timer(1.0, call=self._tick, repeat=True) 145 146 mat = self._reset_region_material = ba.Material() 147 mat.add_actions( 148 conditions=( 149 'they_have_material', 150 shared.player_material, 151 ), 152 actions=( 153 ('modify_part_collision', 'collide', True), 154 ('modify_part_collision', 'physical', False), 155 ('call', 'at_connect', ba.WeakCall(self._handle_reset_collide)), 156 ), 157 ) 158 159 self._set_chosen_one_player(None) 160 161 def _create_reset_region(self) -> None: 162 assert self._reset_region_material is not None 163 assert self._flag_spawn_pos is not None 164 pos = self._flag_spawn_pos 165 self._reset_region = ba.newnode( 166 'region', 167 attrs={ 168 'position': (pos[0], pos[1] + 0.75, pos[2]), 169 'scale': (0.5, 0.5, 0.5), 170 'type': 'sphere', 171 'materials': [self._reset_region_material], 172 }, 173 ) 174 175 def _get_chosen_one_player(self) -> Player | None: 176 # Should never return invalid references; return None in that case. 177 if self._chosen_one_player: 178 return self._chosen_one_player 179 return None 180 181 def _handle_reset_collide(self) -> None: 182 # If we have a chosen one, ignore these. 183 if self._get_chosen_one_player() is not None: 184 return 185 186 # Attempt to get a Actor that we hit. 187 try: 188 spaz = ba.getcollision().opposingnode.getdelegate(PlayerSpaz, True) 189 player = spaz.getplayer(Player, True) 190 except ba.NotFoundError: 191 return 192 193 if spaz.is_alive(): 194 self._set_chosen_one_player(player) 195 196 def _flash_flag_spawn(self) -> None: 197 light = ba.newnode( 198 'light', 199 attrs={ 200 'position': self._flag_spawn_pos, 201 'color': (1, 1, 1), 202 'radius': 0.3, 203 'height_attenuated': False, 204 }, 205 ) 206 ba.animate(light, 'intensity', {0: 0, 0.25: 0.5, 0.5: 0}, loop=True) 207 ba.timer(1.0, light.delete) 208 209 def _tick(self) -> None: 210 211 # Give the chosen one points. 212 player = self._get_chosen_one_player() 213 if player is not None: 214 215 # This shouldn't happen, but just in case. 216 if not player.is_alive(): 217 ba.print_error('got dead player as chosen one in _tick') 218 self._set_chosen_one_player(None) 219 else: 220 scoring_team = player.team 221 assert self.stats 222 self.stats.player_scored( 223 player, 3, screenmessage=False, display=False 224 ) 225 226 scoring_team.time_remaining = max( 227 0, scoring_team.time_remaining - 1 228 ) 229 230 # Show the count over their head 231 if scoring_team.time_remaining > 0: 232 if isinstance(player.actor, PlayerSpaz) and player.actor: 233 player.actor.set_score_text( 234 str(scoring_team.time_remaining) 235 ) 236 237 self._update_scoreboard() 238 239 # announce numbers we have sounds for 240 if scoring_team.time_remaining in self._countdownsounds: 241 ba.playsound( 242 self._countdownsounds[scoring_team.time_remaining] 243 ) 244 245 # Winner! 246 if scoring_team.time_remaining <= 0: 247 self.end_game() 248 249 else: 250 # (player is None) 251 # This shouldn't happen, but just in case. 252 # (Chosen-one player ceasing to exist should 253 # trigger on_player_leave which resets chosen-one) 254 if self._chosen_one_player is not None: 255 ba.print_error('got nonexistent player as chosen one in _tick') 256 self._set_chosen_one_player(None) 257 258 def end_game(self) -> None: 259 results = ba.GameResults() 260 for team in self.teams: 261 results.set_team_score( 262 team, self._chosen_one_time - team.time_remaining 263 ) 264 self.end(results=results, announce_delay=0) 265 266 def _set_chosen_one_player(self, player: Player | None) -> None: 267 existing = self._get_chosen_one_player() 268 if existing: 269 existing.chosen_light = None 270 ba.playsound(self._swipsound) 271 if not player: 272 assert self._flag_spawn_pos is not None 273 self._flag = Flag( 274 color=(1, 0.9, 0.2), 275 position=self._flag_spawn_pos, 276 touchable=False, 277 ) 278 self._chosen_one_player = None 279 280 # Create a light to highlight the flag; 281 # this will go away when the flag dies. 282 ba.newnode( 283 'light', 284 owner=self._flag.node, 285 attrs={ 286 'position': self._flag_spawn_pos, 287 'intensity': 0.6, 288 'height_attenuated': False, 289 'volume_intensity_scale': 0.1, 290 'radius': 0.1, 291 'color': (1.2, 1.2, 0.4), 292 }, 293 ) 294 295 # Also an extra momentary flash. 296 self._flash_flag_spawn() 297 298 # Re-create our flag region in case if someone is waiting for 299 # flag right there: 300 self._create_reset_region() 301 else: 302 if player.actor: 303 self._flag = None 304 self._chosen_one_player = player 305 306 if self._chosen_one_gets_shield: 307 player.actor.handlemessage(ba.PowerupMessage('shield')) 308 if self._chosen_one_gets_gloves: 309 player.actor.handlemessage(ba.PowerupMessage('punch')) 310 311 # Use a color that's partway between their team color 312 # and white. 313 color = [ 314 0.3 + c * 0.7 315 for c in ba.normalized_color(player.team.color) 316 ] 317 light = player.chosen_light = ba.NodeActor( 318 ba.newnode( 319 'light', 320 attrs={ 321 'intensity': 0.6, 322 'height_attenuated': False, 323 'volume_intensity_scale': 0.1, 324 'radius': 0.13, 325 'color': color, 326 }, 327 ) 328 ) 329 330 assert light.node 331 ba.animate( 332 light.node, 333 'intensity', 334 {0: 1.0, 0.2: 0.4, 0.4: 1.0}, 335 loop=True, 336 ) 337 assert isinstance(player.actor, PlayerSpaz) 338 player.actor.node.connectattr( 339 'position', light.node, 'position' 340 ) 341 342 def handlemessage(self, msg: Any) -> Any: 343 if isinstance(msg, ba.PlayerDiedMessage): 344 # Augment standard behavior. 345 super().handlemessage(msg) 346 player = msg.getplayer(Player) 347 if player is self._get_chosen_one_player(): 348 killerplayer = msg.getkillerplayer(Player) 349 self._set_chosen_one_player( 350 None 351 if ( 352 killerplayer is None 353 or killerplayer is player 354 or not killerplayer.is_alive() 355 ) 356 else killerplayer 357 ) 358 self.respawn_player(player) 359 else: 360 super().handlemessage(msg) 361 362 def _update_scoreboard(self) -> None: 363 for team in self.teams: 364 self._scoreboard.set_team_value( 365 team, team.time_remaining, self._chosen_one_time, countdown=True 366 )
Game involving trying to remain the one 'chosen one' for a set length of time while everyone else tries to kill you and become the chosen one themselves.
90 def __init__(self, settings: dict): 91 super().__init__(settings) 92 self._scoreboard = Scoreboard() 93 self._chosen_one_player: Player | None = None 94 self._swipsound = ba.getsound('swip') 95 self._countdownsounds: dict[int, ba.Sound] = { 96 10: ba.getsound('announceTen'), 97 9: ba.getsound('announceNine'), 98 8: ba.getsound('announceEight'), 99 7: ba.getsound('announceSeven'), 100 6: ba.getsound('announceSix'), 101 5: ba.getsound('announceFive'), 102 4: ba.getsound('announceFour'), 103 3: ba.getsound('announceThree'), 104 2: ba.getsound('announceTwo'), 105 1: ba.getsound('announceOne'), 106 } 107 self._flag_spawn_pos: Sequence[float] | None = None 108 self._reset_region_material: ba.Material | None = None 109 self._flag: Flag | None = None 110 self._reset_region: ba.Node | None = None 111 self._epic_mode = bool(settings['Epic Mode']) 112 self._chosen_one_time = int(settings['Chosen One Time']) 113 self._time_limit = float(settings['Time Limit']) 114 self._chosen_one_gets_shield = bool(settings['Chosen One Gets Shield']) 115 self._chosen_one_gets_gloves = bool(settings['Chosen One Gets Gloves']) 116 117 # Base class overrides 118 self.slow_motion = self._epic_mode 119 self.default_music = ( 120 ba.MusicType.EPIC if self._epic_mode else ba.MusicType.CHOSEN_ONE 121 )
Instantiate the Activity.
86 @classmethod 87 def get_supported_maps(cls, sessiontype: type[ba.Session]) -> list[str]: 88 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.
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.
126 def create_team(self, sessionteam: ba.SessionTeam) -> Team: 127 return Team(time_remaining=self._chosen_one_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.
Called when a new ba.Team joins the Activity.
(including the initial set of Teams)
132 def on_player_leave(self, player: Player) -> None: 133 super().on_player_leave(player) 134 if self._get_chosen_one_player() is player: 135 self._set_chosen_one_player(None)
Called when a ba.Player is leaving the Activity.
137 def on_begin(self) -> None: 138 super().on_begin() 139 shared = SharedObjects.get() 140 self.setup_standard_time_limit(self._time_limit) 141 self.setup_standard_powerup_drops() 142 self._flag_spawn_pos = self.map.get_flag_position(None) 143 Flag.project_stand(self._flag_spawn_pos) 144 ba.timer(1.0, call=self._tick, repeat=True) 145 146 mat = self._reset_region_material = ba.Material() 147 mat.add_actions( 148 conditions=( 149 'they_have_material', 150 shared.player_material, 151 ), 152 actions=( 153 ('modify_part_collision', 'collide', True), 154 ('modify_part_collision', 'physical', False), 155 ('call', 'at_connect', ba.WeakCall(self._handle_reset_collide)), 156 ), 157 ) 158 159 self._set_chosen_one_player(None)
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.
258 def end_game(self) -> None: 259 results = ba.GameResults() 260 for team in self.teams: 261 results.set_team_score( 262 team, self._chosen_one_time - team.time_remaining 263 ) 264 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.
342 def handlemessage(self, msg: Any) -> Any: 343 if isinstance(msg, ba.PlayerDiedMessage): 344 # Augment standard behavior. 345 super().handlemessage(msg) 346 player = msg.getplayer(Player) 347 if player is self._get_chosen_one_player(): 348 killerplayer = msg.getkillerplayer(Player) 349 self._set_chosen_one_player( 350 None 351 if ( 352 killerplayer is None 353 or killerplayer is player 354 or not killerplayer.is_alive() 355 ) 356 else killerplayer 357 ) 358 self.respawn_player(player) 359 else: 360 super().handlemessage(msg)
General message handling; can be passed any message object.
Inherited Members
- ba._teamgame.TeamGameActivity
- supports_session_type
- 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
- get_instance_description_short
- 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_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