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 = ('Be the chosen one for a length of time to win.\n' 46 'Kill the chosen one to become it.') 47 available_settings = [ 48 ba.IntSetting( 49 'Chosen One Time', 50 min_value=10, 51 default=30, 52 increment=10, 53 ), 54 ba.BoolSetting('Chosen One Gets Gloves', default=True), 55 ba.BoolSetting('Chosen One Gets Shield', default=False), 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 ba.BoolSetting('Epic Mode', default=False), 80 ] 81 scoreconfig = ba.ScoreConfig(label='Time Held') 82 83 @classmethod 84 def get_supported_maps(cls, sessiontype: type[ba.Session]) -> list[str]: 85 return ba.getmaps('keep_away') 86 87 def __init__(self, settings: dict): 88 super().__init__(settings) 89 self._scoreboard = Scoreboard() 90 self._chosen_one_player: Player | None = None 91 self._swipsound = ba.getsound('swip') 92 self._countdownsounds: dict[int, ba.Sound] = { 93 10: ba.getsound('announceTen'), 94 9: ba.getsound('announceNine'), 95 8: ba.getsound('announceEight'), 96 7: ba.getsound('announceSeven'), 97 6: ba.getsound('announceSix'), 98 5: ba.getsound('announceFive'), 99 4: ba.getsound('announceFour'), 100 3: ba.getsound('announceThree'), 101 2: ba.getsound('announceTwo'), 102 1: ba.getsound('announceOne') 103 } 104 self._flag_spawn_pos: Sequence[float] | None = None 105 self._reset_region_material: ba.Material | None = None 106 self._flag: Flag | None = None 107 self._reset_region: ba.Node | None = None 108 self._epic_mode = bool(settings['Epic Mode']) 109 self._chosen_one_time = int(settings['Chosen One Time']) 110 self._time_limit = float(settings['Time Limit']) 111 self._chosen_one_gets_shield = bool(settings['Chosen One Gets Shield']) 112 self._chosen_one_gets_gloves = bool(settings['Chosen One Gets Gloves']) 113 114 # Base class overrides 115 self.slow_motion = self._epic_mode 116 self.default_music = (ba.MusicType.EPIC 117 if self._epic_mode else ba.MusicType.CHOSEN_ONE) 118 119 def get_instance_description(self) -> str | Sequence: 120 return 'There can be only one.' 121 122 def create_team(self, sessionteam: ba.SessionTeam) -> Team: 123 return Team(time_remaining=self._chosen_one_time) 124 125 def on_team_join(self, team: Team) -> None: 126 self._update_scoreboard() 127 128 def on_player_leave(self, player: Player) -> None: 129 super().on_player_leave(player) 130 if self._get_chosen_one_player() is player: 131 self._set_chosen_one_player(None) 132 133 def on_begin(self) -> None: 134 super().on_begin() 135 shared = SharedObjects.get() 136 self.setup_standard_time_limit(self._time_limit) 137 self.setup_standard_powerup_drops() 138 self._flag_spawn_pos = self.map.get_flag_position(None) 139 Flag.project_stand(self._flag_spawn_pos) 140 ba.timer(1.0, call=self._tick, repeat=True) 141 142 mat = self._reset_region_material = ba.Material() 143 mat.add_actions( 144 conditions=( 145 'they_have_material', 146 shared.player_material, 147 ), 148 actions=( 149 ('modify_part_collision', 'collide', True), 150 ('modify_part_collision', 'physical', False), 151 ('call', 'at_connect', 152 ba.WeakCall(self._handle_reset_collide)), 153 ), 154 ) 155 156 self._set_chosen_one_player(None) 157 158 def _create_reset_region(self) -> None: 159 assert self._reset_region_material is not None 160 assert self._flag_spawn_pos is not None 161 pos = self._flag_spawn_pos 162 self._reset_region = ba.newnode( 163 'region', 164 attrs={ 165 'position': (pos[0], pos[1] + 0.75, pos[2]), 166 'scale': (0.5, 0.5, 0.5), 167 'type': 'sphere', 168 'materials': [self._reset_region_material] 169 }) 170 171 def _get_chosen_one_player(self) -> Player | None: 172 # Should never return invalid references; return None in that case. 173 if self._chosen_one_player: 174 return self._chosen_one_player 175 return None 176 177 def _handle_reset_collide(self) -> None: 178 # If we have a chosen one, ignore these. 179 if self._get_chosen_one_player() is not None: 180 return 181 182 # Attempt to get a Actor that we hit. 183 try: 184 spaz = ba.getcollision().opposingnode.getdelegate(PlayerSpaz, True) 185 player = spaz.getplayer(Player, True) 186 except ba.NotFoundError: 187 return 188 189 if spaz.is_alive(): 190 self._set_chosen_one_player(player) 191 192 def _flash_flag_spawn(self) -> None: 193 light = ba.newnode('light', 194 attrs={ 195 'position': self._flag_spawn_pos, 196 'color': (1, 1, 1), 197 'radius': 0.3, 198 'height_attenuated': False 199 }) 200 ba.animate(light, 'intensity', {0: 0, 0.25: 0.5, 0.5: 0}, loop=True) 201 ba.timer(1.0, light.delete) 202 203 def _tick(self) -> None: 204 205 # Give the chosen one points. 206 player = self._get_chosen_one_player() 207 if player is not None: 208 209 # This shouldn't happen, but just in case. 210 if not player.is_alive(): 211 ba.print_error('got dead player as chosen one in _tick') 212 self._set_chosen_one_player(None) 213 else: 214 scoring_team = player.team 215 assert self.stats 216 self.stats.player_scored(player, 217 3, 218 screenmessage=False, 219 display=False) 220 221 scoring_team.time_remaining = max( 222 0, scoring_team.time_remaining - 1) 223 224 # Show the count over their head 225 if scoring_team.time_remaining > 0: 226 if isinstance(player.actor, PlayerSpaz) and player.actor: 227 player.actor.set_score_text( 228 str(scoring_team.time_remaining)) 229 230 self._update_scoreboard() 231 232 # announce numbers we have sounds for 233 if scoring_team.time_remaining in self._countdownsounds: 234 ba.playsound( 235 self._countdownsounds[scoring_team.time_remaining]) 236 237 # Winner! 238 if scoring_team.time_remaining <= 0: 239 self.end_game() 240 241 else: 242 # (player is None) 243 # This shouldn't happen, but just in case. 244 # (Chosen-one player ceasing to exist should 245 # trigger on_player_leave which resets chosen-one) 246 if self._chosen_one_player is not None: 247 ba.print_error('got nonexistent player as chosen one in _tick') 248 self._set_chosen_one_player(None) 249 250 def end_game(self) -> None: 251 results = ba.GameResults() 252 for team in self.teams: 253 results.set_team_score(team, 254 self._chosen_one_time - team.time_remaining) 255 self.end(results=results, announce_delay=0) 256 257 def _set_chosen_one_player(self, player: Player | None) -> None: 258 existing = self._get_chosen_one_player() 259 if existing: 260 existing.chosen_light = None 261 ba.playsound(self._swipsound) 262 if not player: 263 assert self._flag_spawn_pos is not None 264 self._flag = Flag(color=(1, 0.9, 0.2), 265 position=self._flag_spawn_pos, 266 touchable=False) 267 self._chosen_one_player = None 268 269 # Create a light to highlight the flag; 270 # this will go away when the flag dies. 271 ba.newnode('light', 272 owner=self._flag.node, 273 attrs={ 274 'position': self._flag_spawn_pos, 275 'intensity': 0.6, 276 'height_attenuated': False, 277 'volume_intensity_scale': 0.1, 278 'radius': 0.1, 279 'color': (1.2, 1.2, 0.4) 280 }) 281 282 # Also an extra momentary flash. 283 self._flash_flag_spawn() 284 285 # Re-create our flag region in case if someone is waiting for 286 # flag right there: 287 self._create_reset_region() 288 else: 289 if player.actor: 290 self._flag = None 291 self._chosen_one_player = player 292 293 if self._chosen_one_gets_shield: 294 player.actor.handlemessage(ba.PowerupMessage('shield')) 295 if self._chosen_one_gets_gloves: 296 player.actor.handlemessage(ba.PowerupMessage('punch')) 297 298 # Use a color that's partway between their team color 299 # and white. 300 color = [ 301 0.3 + c * 0.7 302 for c in ba.normalized_color(player.team.color) 303 ] 304 light = player.chosen_light = ba.NodeActor( 305 ba.newnode('light', 306 attrs={ 307 'intensity': 0.6, 308 'height_attenuated': False, 309 'volume_intensity_scale': 0.1, 310 'radius': 0.13, 311 'color': color 312 })) 313 314 assert light.node 315 ba.animate(light.node, 316 'intensity', { 317 0: 1.0, 318 0.2: 0.4, 319 0.4: 1.0 320 }, 321 loop=True) 322 assert isinstance(player.actor, PlayerSpaz) 323 player.actor.node.connectattr('position', light.node, 324 'position') 325 326 def handlemessage(self, msg: Any) -> Any: 327 if isinstance(msg, ba.PlayerDiedMessage): 328 # Augment standard behavior. 329 super().handlemessage(msg) 330 player = msg.getplayer(Player) 331 if player is self._get_chosen_one_player(): 332 killerplayer = msg.getkillerplayer(Player) 333 self._set_chosen_one_player(None if ( 334 killerplayer is None or killerplayer is player 335 or not killerplayer.is_alive()) else killerplayer) 336 self.respawn_player(player) 337 else: 338 super().handlemessage(msg) 339 340 def _update_scoreboard(self) -> None: 341 for team in self.teams: 342 self._scoreboard.set_team_value(team, 343 team.time_remaining, 344 self._chosen_one_time, 345 countdown=True)
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
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 = ('Be the chosen one for a length of time to win.\n' 47 'Kill the chosen one to become it.') 48 available_settings = [ 49 ba.IntSetting( 50 'Chosen One Time', 51 min_value=10, 52 default=30, 53 increment=10, 54 ), 55 ba.BoolSetting('Chosen One Gets Gloves', default=True), 56 ba.BoolSetting('Chosen One Gets Shield', default=False), 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 ba.BoolSetting('Epic Mode', default=False), 81 ] 82 scoreconfig = ba.ScoreConfig(label='Time Held') 83 84 @classmethod 85 def get_supported_maps(cls, sessiontype: type[ba.Session]) -> list[str]: 86 return ba.getmaps('keep_away') 87 88 def __init__(self, settings: dict): 89 super().__init__(settings) 90 self._scoreboard = Scoreboard() 91 self._chosen_one_player: Player | None = None 92 self._swipsound = ba.getsound('swip') 93 self._countdownsounds: dict[int, ba.Sound] = { 94 10: ba.getsound('announceTen'), 95 9: ba.getsound('announceNine'), 96 8: ba.getsound('announceEight'), 97 7: ba.getsound('announceSeven'), 98 6: ba.getsound('announceSix'), 99 5: ba.getsound('announceFive'), 100 4: ba.getsound('announceFour'), 101 3: ba.getsound('announceThree'), 102 2: ba.getsound('announceTwo'), 103 1: ba.getsound('announceOne') 104 } 105 self._flag_spawn_pos: Sequence[float] | None = None 106 self._reset_region_material: ba.Material | None = None 107 self._flag: Flag | None = None 108 self._reset_region: ba.Node | None = None 109 self._epic_mode = bool(settings['Epic Mode']) 110 self._chosen_one_time = int(settings['Chosen One Time']) 111 self._time_limit = float(settings['Time Limit']) 112 self._chosen_one_gets_shield = bool(settings['Chosen One Gets Shield']) 113 self._chosen_one_gets_gloves = bool(settings['Chosen One Gets Gloves']) 114 115 # Base class overrides 116 self.slow_motion = self._epic_mode 117 self.default_music = (ba.MusicType.EPIC 118 if self._epic_mode else ba.MusicType.CHOSEN_ONE) 119 120 def get_instance_description(self) -> str | Sequence: 121 return 'There can be only one.' 122 123 def create_team(self, sessionteam: ba.SessionTeam) -> Team: 124 return Team(time_remaining=self._chosen_one_time) 125 126 def on_team_join(self, team: Team) -> None: 127 self._update_scoreboard() 128 129 def on_player_leave(self, player: Player) -> None: 130 super().on_player_leave(player) 131 if self._get_chosen_one_player() is player: 132 self._set_chosen_one_player(None) 133 134 def on_begin(self) -> None: 135 super().on_begin() 136 shared = SharedObjects.get() 137 self.setup_standard_time_limit(self._time_limit) 138 self.setup_standard_powerup_drops() 139 self._flag_spawn_pos = self.map.get_flag_position(None) 140 Flag.project_stand(self._flag_spawn_pos) 141 ba.timer(1.0, call=self._tick, repeat=True) 142 143 mat = self._reset_region_material = ba.Material() 144 mat.add_actions( 145 conditions=( 146 'they_have_material', 147 shared.player_material, 148 ), 149 actions=( 150 ('modify_part_collision', 'collide', True), 151 ('modify_part_collision', 'physical', False), 152 ('call', 'at_connect', 153 ba.WeakCall(self._handle_reset_collide)), 154 ), 155 ) 156 157 self._set_chosen_one_player(None) 158 159 def _create_reset_region(self) -> None: 160 assert self._reset_region_material is not None 161 assert self._flag_spawn_pos is not None 162 pos = self._flag_spawn_pos 163 self._reset_region = ba.newnode( 164 'region', 165 attrs={ 166 'position': (pos[0], pos[1] + 0.75, pos[2]), 167 'scale': (0.5, 0.5, 0.5), 168 'type': 'sphere', 169 'materials': [self._reset_region_material] 170 }) 171 172 def _get_chosen_one_player(self) -> Player | None: 173 # Should never return invalid references; return None in that case. 174 if self._chosen_one_player: 175 return self._chosen_one_player 176 return None 177 178 def _handle_reset_collide(self) -> None: 179 # If we have a chosen one, ignore these. 180 if self._get_chosen_one_player() is not None: 181 return 182 183 # Attempt to get a Actor that we hit. 184 try: 185 spaz = ba.getcollision().opposingnode.getdelegate(PlayerSpaz, True) 186 player = spaz.getplayer(Player, True) 187 except ba.NotFoundError: 188 return 189 190 if spaz.is_alive(): 191 self._set_chosen_one_player(player) 192 193 def _flash_flag_spawn(self) -> None: 194 light = ba.newnode('light', 195 attrs={ 196 'position': self._flag_spawn_pos, 197 'color': (1, 1, 1), 198 'radius': 0.3, 199 'height_attenuated': False 200 }) 201 ba.animate(light, 'intensity', {0: 0, 0.25: 0.5, 0.5: 0}, loop=True) 202 ba.timer(1.0, light.delete) 203 204 def _tick(self) -> None: 205 206 # Give the chosen one points. 207 player = self._get_chosen_one_player() 208 if player is not None: 209 210 # This shouldn't happen, but just in case. 211 if not player.is_alive(): 212 ba.print_error('got dead player as chosen one in _tick') 213 self._set_chosen_one_player(None) 214 else: 215 scoring_team = player.team 216 assert self.stats 217 self.stats.player_scored(player, 218 3, 219 screenmessage=False, 220 display=False) 221 222 scoring_team.time_remaining = max( 223 0, scoring_team.time_remaining - 1) 224 225 # Show the count over their head 226 if scoring_team.time_remaining > 0: 227 if isinstance(player.actor, PlayerSpaz) and player.actor: 228 player.actor.set_score_text( 229 str(scoring_team.time_remaining)) 230 231 self._update_scoreboard() 232 233 # announce numbers we have sounds for 234 if scoring_team.time_remaining in self._countdownsounds: 235 ba.playsound( 236 self._countdownsounds[scoring_team.time_remaining]) 237 238 # Winner! 239 if scoring_team.time_remaining <= 0: 240 self.end_game() 241 242 else: 243 # (player is None) 244 # This shouldn't happen, but just in case. 245 # (Chosen-one player ceasing to exist should 246 # trigger on_player_leave which resets chosen-one) 247 if self._chosen_one_player is not None: 248 ba.print_error('got nonexistent player as chosen one in _tick') 249 self._set_chosen_one_player(None) 250 251 def end_game(self) -> None: 252 results = ba.GameResults() 253 for team in self.teams: 254 results.set_team_score(team, 255 self._chosen_one_time - team.time_remaining) 256 self.end(results=results, announce_delay=0) 257 258 def _set_chosen_one_player(self, player: Player | None) -> None: 259 existing = self._get_chosen_one_player() 260 if existing: 261 existing.chosen_light = None 262 ba.playsound(self._swipsound) 263 if not player: 264 assert self._flag_spawn_pos is not None 265 self._flag = Flag(color=(1, 0.9, 0.2), 266 position=self._flag_spawn_pos, 267 touchable=False) 268 self._chosen_one_player = None 269 270 # Create a light to highlight the flag; 271 # this will go away when the flag dies. 272 ba.newnode('light', 273 owner=self._flag.node, 274 attrs={ 275 'position': self._flag_spawn_pos, 276 'intensity': 0.6, 277 'height_attenuated': False, 278 'volume_intensity_scale': 0.1, 279 'radius': 0.1, 280 'color': (1.2, 1.2, 0.4) 281 }) 282 283 # Also an extra momentary flash. 284 self._flash_flag_spawn() 285 286 # Re-create our flag region in case if someone is waiting for 287 # flag right there: 288 self._create_reset_region() 289 else: 290 if player.actor: 291 self._flag = None 292 self._chosen_one_player = player 293 294 if self._chosen_one_gets_shield: 295 player.actor.handlemessage(ba.PowerupMessage('shield')) 296 if self._chosen_one_gets_gloves: 297 player.actor.handlemessage(ba.PowerupMessage('punch')) 298 299 # Use a color that's partway between their team color 300 # and white. 301 color = [ 302 0.3 + c * 0.7 303 for c in ba.normalized_color(player.team.color) 304 ] 305 light = player.chosen_light = ba.NodeActor( 306 ba.newnode('light', 307 attrs={ 308 'intensity': 0.6, 309 'height_attenuated': False, 310 'volume_intensity_scale': 0.1, 311 'radius': 0.13, 312 'color': color 313 })) 314 315 assert light.node 316 ba.animate(light.node, 317 'intensity', { 318 0: 1.0, 319 0.2: 0.4, 320 0.4: 1.0 321 }, 322 loop=True) 323 assert isinstance(player.actor, PlayerSpaz) 324 player.actor.node.connectattr('position', light.node, 325 'position') 326 327 def handlemessage(self, msg: Any) -> Any: 328 if isinstance(msg, ba.PlayerDiedMessage): 329 # Augment standard behavior. 330 super().handlemessage(msg) 331 player = msg.getplayer(Player) 332 if player is self._get_chosen_one_player(): 333 killerplayer = msg.getkillerplayer(Player) 334 self._set_chosen_one_player(None if ( 335 killerplayer is None or killerplayer is player 336 or not killerplayer.is_alive()) else killerplayer) 337 self.respawn_player(player) 338 else: 339 super().handlemessage(msg) 340 341 def _update_scoreboard(self) -> None: 342 for team in self.teams: 343 self._scoreboard.set_team_value(team, 344 team.time_remaining, 345 self._chosen_one_time, 346 countdown=True)
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.
88 def __init__(self, settings: dict): 89 super().__init__(settings) 90 self._scoreboard = Scoreboard() 91 self._chosen_one_player: Player | None = None 92 self._swipsound = ba.getsound('swip') 93 self._countdownsounds: dict[int, ba.Sound] = { 94 10: ba.getsound('announceTen'), 95 9: ba.getsound('announceNine'), 96 8: ba.getsound('announceEight'), 97 7: ba.getsound('announceSeven'), 98 6: ba.getsound('announceSix'), 99 5: ba.getsound('announceFive'), 100 4: ba.getsound('announceFour'), 101 3: ba.getsound('announceThree'), 102 2: ba.getsound('announceTwo'), 103 1: ba.getsound('announceOne') 104 } 105 self._flag_spawn_pos: Sequence[float] | None = None 106 self._reset_region_material: ba.Material | None = None 107 self._flag: Flag | None = None 108 self._reset_region: ba.Node | None = None 109 self._epic_mode = bool(settings['Epic Mode']) 110 self._chosen_one_time = int(settings['Chosen One Time']) 111 self._time_limit = float(settings['Time Limit']) 112 self._chosen_one_gets_shield = bool(settings['Chosen One Gets Shield']) 113 self._chosen_one_gets_gloves = bool(settings['Chosen One Gets Gloves']) 114 115 # Base class overrides 116 self.slow_motion = self._epic_mode 117 self.default_music = (ba.MusicType.EPIC 118 if self._epic_mode else ba.MusicType.CHOSEN_ONE)
Instantiate the Activity.
84 @classmethod 85 def get_supported_maps(cls, sessiontype: type[ba.Session]) -> list[str]: 86 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.
123 def create_team(self, sessionteam: ba.SessionTeam) -> Team: 124 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)
129 def on_player_leave(self, player: Player) -> None: 130 super().on_player_leave(player) 131 if self._get_chosen_one_player() is player: 132 self._set_chosen_one_player(None)
Called when a ba.Player is leaving the Activity.
134 def on_begin(self) -> None: 135 super().on_begin() 136 shared = SharedObjects.get() 137 self.setup_standard_time_limit(self._time_limit) 138 self.setup_standard_powerup_drops() 139 self._flag_spawn_pos = self.map.get_flag_position(None) 140 Flag.project_stand(self._flag_spawn_pos) 141 ba.timer(1.0, call=self._tick, repeat=True) 142 143 mat = self._reset_region_material = ba.Material() 144 mat.add_actions( 145 conditions=( 146 'they_have_material', 147 shared.player_material, 148 ), 149 actions=( 150 ('modify_part_collision', 'collide', True), 151 ('modify_part_collision', 'physical', False), 152 ('call', 'at_connect', 153 ba.WeakCall(self._handle_reset_collide)), 154 ), 155 ) 156 157 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.
251 def end_game(self) -> None: 252 results = ba.GameResults() 253 for team in self.teams: 254 results.set_team_score(team, 255 self._chosen_one_time - team.time_remaining) 256 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.
327 def handlemessage(self, msg: Any) -> Any: 328 if isinstance(msg, ba.PlayerDiedMessage): 329 # Augment standard behavior. 330 super().handlemessage(msg) 331 player = msg.getplayer(Player) 332 if player is self._get_chosen_one_player(): 333 killerplayer = msg.getkillerplayer(Player) 334 self._set_chosen_one_player(None if ( 335 killerplayer is None or killerplayer is player 336 or not killerplayer.is_alive()) else killerplayer) 337 self.respawn_player(player) 338 else: 339 super().handlemessage(msg)
General message handling; can be passed any message object.
Inherited Members
- ba._activity.Activity
- slow_motion
- 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._gameactivity.GameActivity
- default_music
- 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
- 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._teamgame.TeamGameActivity
- supports_session_type
- on_transition_in
- spawn_player_spaz
- end
- ba._dependency.DependencyComponent
- dep_is_present
- get_dynamic_deps