bastd.game.thelaststand
Defines the last stand minigame.
1# Released under the MIT License. See LICENSE for details. 2# 3"""Defines the last stand minigame.""" 4 5from __future__ import annotations 6 7import random 8from dataclasses import dataclass 9from typing import TYPE_CHECKING 10 11import ba 12from bastd.actor.playerspaz import PlayerSpaz 13from bastd.actor.bomb import TNTSpawner 14from bastd.actor.scoreboard import Scoreboard 15from bastd.actor.powerupbox import PowerupBoxFactory, PowerupBox 16from bastd.actor.spazbot import (SpazBotSet, SpazBotDiedMessage, BomberBot, 17 BomberBotPro, BomberBotProShielded, 18 BrawlerBot, BrawlerBotPro, 19 BrawlerBotProShielded, TriggerBot, 20 TriggerBotPro, TriggerBotProShielded, 21 ChargerBot, StickyBot, ExplodeyBot) 22 23if TYPE_CHECKING: 24 from typing import Any, Sequence 25 from bastd.actor.spazbot import SpazBot 26 27 28@dataclass 29class SpawnInfo: 30 """Spawning info for a particular bot type.""" 31 spawnrate: float 32 increase: float 33 dincrease: float 34 35 36class Player(ba.Player['Team']): 37 """Our player type for this game.""" 38 39 40class Team(ba.Team[Player]): 41 """Our team type for this game.""" 42 43 44class TheLastStandGame(ba.CoopGameActivity[Player, Team]): 45 """Slow motion how-long-can-you-last game.""" 46 47 name = 'The Last Stand' 48 description = 'Final glorious epic slow motion battle to the death.' 49 tips = [ 50 'This level never ends, but a high score here\n' 51 'will earn you eternal respect throughout the world.' 52 ] 53 54 # Show messages when players die since it matters here. 55 announce_player_deaths = True 56 57 # And of course the most important part. 58 slow_motion = True 59 60 default_music = ba.MusicType.EPIC 61 62 def __init__(self, settings: dict): 63 settings['map'] = 'Rampage' 64 super().__init__(settings) 65 self._new_wave_sound = ba.getsound('scoreHit01') 66 self._winsound = ba.getsound('score') 67 self._cashregistersound = ba.getsound('cashRegister') 68 self._spawn_center = (0, 5.5, -4.14) 69 self._tntspawnpos = (0, 5.5, -6) 70 self._powerup_center = (0, 7, -4.14) 71 self._powerup_spread = (7, 2) 72 self._preset = str(settings.get('preset', 'default')) 73 self._excludepowerups: list[str] = [] 74 self._scoreboard: Scoreboard | None = None 75 self._score = 0 76 self._bots = SpazBotSet() 77 self._dingsound = ba.getsound('dingSmall') 78 self._dingsoundhigh = ba.getsound('dingSmallHigh') 79 self._tntspawner: TNTSpawner | None = None 80 self._bot_update_interval: float | None = None 81 self._bot_update_timer: ba.Timer | None = None 82 self._powerup_drop_timer = None 83 84 # For each bot type: [spawnrate, increase, d_increase] 85 self._bot_spawn_types = { 86 BomberBot: SpawnInfo(1.00, 0.00, 0.000), 87 BomberBotPro: SpawnInfo(0.00, 0.05, 0.001), 88 BomberBotProShielded: SpawnInfo(0.00, 0.02, 0.002), 89 BrawlerBot: SpawnInfo(1.00, 0.00, 0.000), 90 BrawlerBotPro: SpawnInfo(0.00, 0.05, 0.001), 91 BrawlerBotProShielded: SpawnInfo(0.00, 0.02, 0.002), 92 TriggerBot: SpawnInfo(0.30, 0.00, 0.000), 93 TriggerBotPro: SpawnInfo(0.00, 0.05, 0.001), 94 TriggerBotProShielded: SpawnInfo(0.00, 0.02, 0.002), 95 ChargerBot: SpawnInfo(0.30, 0.05, 0.000), 96 StickyBot: SpawnInfo(0.10, 0.03, 0.001), 97 ExplodeyBot: SpawnInfo(0.05, 0.02, 0.002) 98 } # yapf: disable 99 100 def on_transition_in(self) -> None: 101 super().on_transition_in() 102 ba.timer(1.3, ba.Call(ba.playsound, self._new_wave_sound)) 103 self._scoreboard = Scoreboard(label=ba.Lstr(resource='scoreText'), 104 score_split=0.5) 105 106 def on_begin(self) -> None: 107 super().on_begin() 108 109 # Spit out a few powerups and start dropping more shortly. 110 self._drop_powerups(standard_points=True) 111 ba.timer(2.0, ba.WeakCall(self._start_powerup_drops)) 112 ba.timer(0.001, ba.WeakCall(self._start_bot_updates)) 113 self.setup_low_life_warning_sound() 114 self._update_scores() 115 self._tntspawner = TNTSpawner(position=self._tntspawnpos, 116 respawn_time=10.0) 117 118 def spawn_player(self, player: Player) -> ba.Actor: 119 pos = (self._spawn_center[0] + random.uniform(-1.5, 1.5), 120 self._spawn_center[1], 121 self._spawn_center[2] + random.uniform(-1.5, 1.5)) 122 return self.spawn_player_spaz(player, position=pos) 123 124 def _start_bot_updates(self) -> None: 125 self._bot_update_interval = 3.3 - 0.3 * (len(self.players)) 126 self._update_bots() 127 self._update_bots() 128 if len(self.players) > 2: 129 self._update_bots() 130 if len(self.players) > 3: 131 self._update_bots() 132 self._bot_update_timer = ba.Timer(self._bot_update_interval, 133 ba.WeakCall(self._update_bots)) 134 135 def _drop_powerup(self, 136 index: int, 137 poweruptype: str | None = None) -> None: 138 if poweruptype is None: 139 poweruptype = (PowerupBoxFactory.get().get_random_powerup_type( 140 excludetypes=self._excludepowerups)) 141 PowerupBox(position=self.map.powerup_spawn_points[index], 142 poweruptype=poweruptype).autoretain() 143 144 def _start_powerup_drops(self) -> None: 145 self._powerup_drop_timer = ba.Timer(3.0, 146 ba.WeakCall(self._drop_powerups), 147 repeat=True) 148 149 def _drop_powerups(self, 150 standard_points: bool = False, 151 force_first: str | None = None) -> None: 152 """Generic powerup drop.""" 153 from bastd.actor import powerupbox 154 if standard_points: 155 pts = self.map.powerup_spawn_points 156 for i in range(len(pts)): 157 ba.timer( 158 1.0 + i * 0.5, 159 ba.WeakCall(self._drop_powerup, i, 160 force_first if i == 0 else None)) 161 else: 162 drop_pt = (self._powerup_center[0] + random.uniform( 163 -1.0 * self._powerup_spread[0], 1.0 * self._powerup_spread[0]), 164 self._powerup_center[1], 165 self._powerup_center[2] + random.uniform( 166 -self._powerup_spread[1], self._powerup_spread[1])) 167 168 # Drop one random one somewhere. 169 powerupbox.PowerupBox( 170 position=drop_pt, 171 poweruptype=PowerupBoxFactory.get().get_random_powerup_type( 172 excludetypes=self._excludepowerups)).autoretain() 173 174 def do_end(self, outcome: str) -> None: 175 """End the game.""" 176 if outcome == 'defeat': 177 self.fade_to_red() 178 self.end(delay=2.0, 179 results={ 180 'outcome': outcome, 181 'score': self._score, 182 'playerinfos': self.initialplayerinfos 183 }) 184 185 def _update_bots(self) -> None: 186 assert self._bot_update_interval is not None 187 self._bot_update_interval = max(0.5, self._bot_update_interval * 0.98) 188 self._bot_update_timer = ba.Timer(self._bot_update_interval, 189 ba.WeakCall(self._update_bots)) 190 botspawnpts: list[Sequence[float]] = [[-5.0, 5.5, -4.14], 191 [0.0, 5.5, -4.14], 192 [5.0, 5.5, -4.14]] 193 dists = [0.0, 0.0, 0.0] 194 playerpts: list[Sequence[float]] = [] 195 for player in self.players: 196 try: 197 if player.is_alive(): 198 assert isinstance(player.actor, PlayerSpaz) 199 assert player.actor.node 200 playerpts.append(player.actor.node.position) 201 except Exception: 202 ba.print_exception('Error updating bots.') 203 for i in range(3): 204 for playerpt in playerpts: 205 dists[i] += abs(playerpt[0] - botspawnpts[i][0]) 206 dists[i] += random.random() * 5.0 # Minor random variation. 207 if dists[0] > dists[1] and dists[0] > dists[2]: 208 spawnpt = botspawnpts[0] 209 elif dists[1] > dists[2]: 210 spawnpt = botspawnpts[1] 211 else: 212 spawnpt = botspawnpts[2] 213 214 spawnpt = (spawnpt[0] + 3.0 * (random.random() - 0.5), spawnpt[1], 215 2.0 * (random.random() - 0.5) + spawnpt[2]) 216 217 # Normalize our bot type total and find a random number within that. 218 total = 0.0 219 for spawninfo in self._bot_spawn_types.values(): 220 total += spawninfo.spawnrate 221 randval = random.random() * total 222 223 # Now go back through and see where this value falls. 224 total = 0 225 bottype: type[SpazBot] | None = None 226 for spawntype, spawninfo in self._bot_spawn_types.items(): 227 total += spawninfo.spawnrate 228 if randval <= total: 229 bottype = spawntype 230 break 231 spawn_time = 1.0 232 assert bottype is not None 233 self._bots.spawn_bot(bottype, pos=spawnpt, spawn_time=spawn_time) 234 235 # After every spawn we adjust our ratios slightly to get more 236 # difficult. 237 for spawninfo in self._bot_spawn_types.values(): 238 spawninfo.spawnrate += spawninfo.increase 239 spawninfo.increase += spawninfo.dincrease 240 241 def _update_scores(self) -> None: 242 score = self._score 243 244 # Achievements apply to the default preset only. 245 if self._preset == 'default': 246 if score >= 250: 247 self._award_achievement('Last Stand Master') 248 if score >= 500: 249 self._award_achievement('Last Stand Wizard') 250 if score >= 1000: 251 self._award_achievement('Last Stand God') 252 assert self._scoreboard is not None 253 self._scoreboard.set_team_value(self.teams[0], score, max_score=None) 254 255 def handlemessage(self, msg: Any) -> Any: 256 if isinstance(msg, ba.PlayerDiedMessage): 257 player = msg.getplayer(Player) 258 self.stats.player_was_killed(player) 259 ba.timer(0.1, self._checkroundover) 260 261 elif isinstance(msg, ba.PlayerScoredMessage): 262 self._score += msg.score 263 self._update_scores() 264 265 elif isinstance(msg, SpazBotDiedMessage): 266 pts, importance = msg.spazbot.get_death_points(msg.how) 267 target: Sequence[float] | None 268 if msg.killerplayer: 269 assert msg.spazbot.node 270 target = msg.spazbot.node.position 271 self.stats.player_scored(msg.killerplayer, 272 pts, 273 target=target, 274 kill=True, 275 screenmessage=False, 276 importance=importance) 277 ba.playsound(self._dingsound 278 if importance == 1 else self._dingsoundhigh, 279 volume=0.6) 280 281 # Normally we pull scores from the score-set, but if there's no 282 # player lets be explicit. 283 else: 284 self._score += pts 285 self._update_scores() 286 else: 287 super().handlemessage(msg) 288 289 def _on_got_scores_to_beat(self, scores: list[dict[str, Any]]) -> None: 290 self._show_standard_scores_to_beat_ui(scores) 291 292 def end_game(self) -> None: 293 # Tell our bots to celebrate just to rub it in. 294 self._bots.final_celebrate() 295 ba.setmusic(None) 296 ba.pushcall(ba.WeakCall(self.do_end, 'defeat')) 297 298 def _checkroundover(self) -> None: 299 """End the round if conditions are met.""" 300 if not any(player.is_alive() for player in self.teams[0].players): 301 self.end_game()
29@dataclass 30class SpawnInfo: 31 """Spawning info for a particular bot type.""" 32 spawnrate: float 33 increase: float 34 dincrease: float
Spawning info for a particular bot type.
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
Our team type for this game.
Inherited Members
45class TheLastStandGame(ba.CoopGameActivity[Player, Team]): 46 """Slow motion how-long-can-you-last game.""" 47 48 name = 'The Last Stand' 49 description = 'Final glorious epic slow motion battle to the death.' 50 tips = [ 51 'This level never ends, but a high score here\n' 52 'will earn you eternal respect throughout the world.' 53 ] 54 55 # Show messages when players die since it matters here. 56 announce_player_deaths = True 57 58 # And of course the most important part. 59 slow_motion = True 60 61 default_music = ba.MusicType.EPIC 62 63 def __init__(self, settings: dict): 64 settings['map'] = 'Rampage' 65 super().__init__(settings) 66 self._new_wave_sound = ba.getsound('scoreHit01') 67 self._winsound = ba.getsound('score') 68 self._cashregistersound = ba.getsound('cashRegister') 69 self._spawn_center = (0, 5.5, -4.14) 70 self._tntspawnpos = (0, 5.5, -6) 71 self._powerup_center = (0, 7, -4.14) 72 self._powerup_spread = (7, 2) 73 self._preset = str(settings.get('preset', 'default')) 74 self._excludepowerups: list[str] = [] 75 self._scoreboard: Scoreboard | None = None 76 self._score = 0 77 self._bots = SpazBotSet() 78 self._dingsound = ba.getsound('dingSmall') 79 self._dingsoundhigh = ba.getsound('dingSmallHigh') 80 self._tntspawner: TNTSpawner | None = None 81 self._bot_update_interval: float | None = None 82 self._bot_update_timer: ba.Timer | None = None 83 self._powerup_drop_timer = None 84 85 # For each bot type: [spawnrate, increase, d_increase] 86 self._bot_spawn_types = { 87 BomberBot: SpawnInfo(1.00, 0.00, 0.000), 88 BomberBotPro: SpawnInfo(0.00, 0.05, 0.001), 89 BomberBotProShielded: SpawnInfo(0.00, 0.02, 0.002), 90 BrawlerBot: SpawnInfo(1.00, 0.00, 0.000), 91 BrawlerBotPro: SpawnInfo(0.00, 0.05, 0.001), 92 BrawlerBotProShielded: SpawnInfo(0.00, 0.02, 0.002), 93 TriggerBot: SpawnInfo(0.30, 0.00, 0.000), 94 TriggerBotPro: SpawnInfo(0.00, 0.05, 0.001), 95 TriggerBotProShielded: SpawnInfo(0.00, 0.02, 0.002), 96 ChargerBot: SpawnInfo(0.30, 0.05, 0.000), 97 StickyBot: SpawnInfo(0.10, 0.03, 0.001), 98 ExplodeyBot: SpawnInfo(0.05, 0.02, 0.002) 99 } # yapf: disable 100 101 def on_transition_in(self) -> None: 102 super().on_transition_in() 103 ba.timer(1.3, ba.Call(ba.playsound, self._new_wave_sound)) 104 self._scoreboard = Scoreboard(label=ba.Lstr(resource='scoreText'), 105 score_split=0.5) 106 107 def on_begin(self) -> None: 108 super().on_begin() 109 110 # Spit out a few powerups and start dropping more shortly. 111 self._drop_powerups(standard_points=True) 112 ba.timer(2.0, ba.WeakCall(self._start_powerup_drops)) 113 ba.timer(0.001, ba.WeakCall(self._start_bot_updates)) 114 self.setup_low_life_warning_sound() 115 self._update_scores() 116 self._tntspawner = TNTSpawner(position=self._tntspawnpos, 117 respawn_time=10.0) 118 119 def spawn_player(self, player: Player) -> ba.Actor: 120 pos = (self._spawn_center[0] + random.uniform(-1.5, 1.5), 121 self._spawn_center[1], 122 self._spawn_center[2] + random.uniform(-1.5, 1.5)) 123 return self.spawn_player_spaz(player, position=pos) 124 125 def _start_bot_updates(self) -> None: 126 self._bot_update_interval = 3.3 - 0.3 * (len(self.players)) 127 self._update_bots() 128 self._update_bots() 129 if len(self.players) > 2: 130 self._update_bots() 131 if len(self.players) > 3: 132 self._update_bots() 133 self._bot_update_timer = ba.Timer(self._bot_update_interval, 134 ba.WeakCall(self._update_bots)) 135 136 def _drop_powerup(self, 137 index: int, 138 poweruptype: str | None = None) -> None: 139 if poweruptype is None: 140 poweruptype = (PowerupBoxFactory.get().get_random_powerup_type( 141 excludetypes=self._excludepowerups)) 142 PowerupBox(position=self.map.powerup_spawn_points[index], 143 poweruptype=poweruptype).autoretain() 144 145 def _start_powerup_drops(self) -> None: 146 self._powerup_drop_timer = ba.Timer(3.0, 147 ba.WeakCall(self._drop_powerups), 148 repeat=True) 149 150 def _drop_powerups(self, 151 standard_points: bool = False, 152 force_first: str | None = None) -> None: 153 """Generic powerup drop.""" 154 from bastd.actor import powerupbox 155 if standard_points: 156 pts = self.map.powerup_spawn_points 157 for i in range(len(pts)): 158 ba.timer( 159 1.0 + i * 0.5, 160 ba.WeakCall(self._drop_powerup, i, 161 force_first if i == 0 else None)) 162 else: 163 drop_pt = (self._powerup_center[0] + random.uniform( 164 -1.0 * self._powerup_spread[0], 1.0 * self._powerup_spread[0]), 165 self._powerup_center[1], 166 self._powerup_center[2] + random.uniform( 167 -self._powerup_spread[1], self._powerup_spread[1])) 168 169 # Drop one random one somewhere. 170 powerupbox.PowerupBox( 171 position=drop_pt, 172 poweruptype=PowerupBoxFactory.get().get_random_powerup_type( 173 excludetypes=self._excludepowerups)).autoretain() 174 175 def do_end(self, outcome: str) -> None: 176 """End the game.""" 177 if outcome == 'defeat': 178 self.fade_to_red() 179 self.end(delay=2.0, 180 results={ 181 'outcome': outcome, 182 'score': self._score, 183 'playerinfos': self.initialplayerinfos 184 }) 185 186 def _update_bots(self) -> None: 187 assert self._bot_update_interval is not None 188 self._bot_update_interval = max(0.5, self._bot_update_interval * 0.98) 189 self._bot_update_timer = ba.Timer(self._bot_update_interval, 190 ba.WeakCall(self._update_bots)) 191 botspawnpts: list[Sequence[float]] = [[-5.0, 5.5, -4.14], 192 [0.0, 5.5, -4.14], 193 [5.0, 5.5, -4.14]] 194 dists = [0.0, 0.0, 0.0] 195 playerpts: list[Sequence[float]] = [] 196 for player in self.players: 197 try: 198 if player.is_alive(): 199 assert isinstance(player.actor, PlayerSpaz) 200 assert player.actor.node 201 playerpts.append(player.actor.node.position) 202 except Exception: 203 ba.print_exception('Error updating bots.') 204 for i in range(3): 205 for playerpt in playerpts: 206 dists[i] += abs(playerpt[0] - botspawnpts[i][0]) 207 dists[i] += random.random() * 5.0 # Minor random variation. 208 if dists[0] > dists[1] and dists[0] > dists[2]: 209 spawnpt = botspawnpts[0] 210 elif dists[1] > dists[2]: 211 spawnpt = botspawnpts[1] 212 else: 213 spawnpt = botspawnpts[2] 214 215 spawnpt = (spawnpt[0] + 3.0 * (random.random() - 0.5), spawnpt[1], 216 2.0 * (random.random() - 0.5) + spawnpt[2]) 217 218 # Normalize our bot type total and find a random number within that. 219 total = 0.0 220 for spawninfo in self._bot_spawn_types.values(): 221 total += spawninfo.spawnrate 222 randval = random.random() * total 223 224 # Now go back through and see where this value falls. 225 total = 0 226 bottype: type[SpazBot] | None = None 227 for spawntype, spawninfo in self._bot_spawn_types.items(): 228 total += spawninfo.spawnrate 229 if randval <= total: 230 bottype = spawntype 231 break 232 spawn_time = 1.0 233 assert bottype is not None 234 self._bots.spawn_bot(bottype, pos=spawnpt, spawn_time=spawn_time) 235 236 # After every spawn we adjust our ratios slightly to get more 237 # difficult. 238 for spawninfo in self._bot_spawn_types.values(): 239 spawninfo.spawnrate += spawninfo.increase 240 spawninfo.increase += spawninfo.dincrease 241 242 def _update_scores(self) -> None: 243 score = self._score 244 245 # Achievements apply to the default preset only. 246 if self._preset == 'default': 247 if score >= 250: 248 self._award_achievement('Last Stand Master') 249 if score >= 500: 250 self._award_achievement('Last Stand Wizard') 251 if score >= 1000: 252 self._award_achievement('Last Stand God') 253 assert self._scoreboard is not None 254 self._scoreboard.set_team_value(self.teams[0], score, max_score=None) 255 256 def handlemessage(self, msg: Any) -> Any: 257 if isinstance(msg, ba.PlayerDiedMessage): 258 player = msg.getplayer(Player) 259 self.stats.player_was_killed(player) 260 ba.timer(0.1, self._checkroundover) 261 262 elif isinstance(msg, ba.PlayerScoredMessage): 263 self._score += msg.score 264 self._update_scores() 265 266 elif isinstance(msg, SpazBotDiedMessage): 267 pts, importance = msg.spazbot.get_death_points(msg.how) 268 target: Sequence[float] | None 269 if msg.killerplayer: 270 assert msg.spazbot.node 271 target = msg.spazbot.node.position 272 self.stats.player_scored(msg.killerplayer, 273 pts, 274 target=target, 275 kill=True, 276 screenmessage=False, 277 importance=importance) 278 ba.playsound(self._dingsound 279 if importance == 1 else self._dingsoundhigh, 280 volume=0.6) 281 282 # Normally we pull scores from the score-set, but if there's no 283 # player lets be explicit. 284 else: 285 self._score += pts 286 self._update_scores() 287 else: 288 super().handlemessage(msg) 289 290 def _on_got_scores_to_beat(self, scores: list[dict[str, Any]]) -> None: 291 self._show_standard_scores_to_beat_ui(scores) 292 293 def end_game(self) -> None: 294 # Tell our bots to celebrate just to rub it in. 295 self._bots.final_celebrate() 296 ba.setmusic(None) 297 ba.pushcall(ba.WeakCall(self.do_end, 'defeat')) 298 299 def _checkroundover(self) -> None: 300 """End the round if conditions are met.""" 301 if not any(player.is_alive() for player in self.teams[0].players): 302 self.end_game()
Slow motion how-long-can-you-last game.
63 def __init__(self, settings: dict): 64 settings['map'] = 'Rampage' 65 super().__init__(settings) 66 self._new_wave_sound = ba.getsound('scoreHit01') 67 self._winsound = ba.getsound('score') 68 self._cashregistersound = ba.getsound('cashRegister') 69 self._spawn_center = (0, 5.5, -4.14) 70 self._tntspawnpos = (0, 5.5, -6) 71 self._powerup_center = (0, 7, -4.14) 72 self._powerup_spread = (7, 2) 73 self._preset = str(settings.get('preset', 'default')) 74 self._excludepowerups: list[str] = [] 75 self._scoreboard: Scoreboard | None = None 76 self._score = 0 77 self._bots = SpazBotSet() 78 self._dingsound = ba.getsound('dingSmall') 79 self._dingsoundhigh = ba.getsound('dingSmallHigh') 80 self._tntspawner: TNTSpawner | None = None 81 self._bot_update_interval: float | None = None 82 self._bot_update_timer: ba.Timer | None = None 83 self._powerup_drop_timer = None 84 85 # For each bot type: [spawnrate, increase, d_increase] 86 self._bot_spawn_types = { 87 BomberBot: SpawnInfo(1.00, 0.00, 0.000), 88 BomberBotPro: SpawnInfo(0.00, 0.05, 0.001), 89 BomberBotProShielded: SpawnInfo(0.00, 0.02, 0.002), 90 BrawlerBot: SpawnInfo(1.00, 0.00, 0.000), 91 BrawlerBotPro: SpawnInfo(0.00, 0.05, 0.001), 92 BrawlerBotProShielded: SpawnInfo(0.00, 0.02, 0.002), 93 TriggerBot: SpawnInfo(0.30, 0.00, 0.000), 94 TriggerBotPro: SpawnInfo(0.00, 0.05, 0.001), 95 TriggerBotProShielded: SpawnInfo(0.00, 0.02, 0.002), 96 ChargerBot: SpawnInfo(0.30, 0.05, 0.000), 97 StickyBot: SpawnInfo(0.10, 0.03, 0.001), 98 ExplodeyBot: SpawnInfo(0.05, 0.02, 0.002) 99 } # yapf: disable
Instantiate the Activity.
Whether to print every time a player dies. This can be pertinent in games such as Death-Match but can be annoying in games where it doesn't matter.
101 def on_transition_in(self) -> None: 102 super().on_transition_in() 103 ba.timer(1.3, ba.Call(ba.playsound, self._new_wave_sound)) 104 self._scoreboard = Scoreboard(label=ba.Lstr(resource='scoreText'), 105 score_split=0.5)
Called when the Activity is first becoming visible.
Upon this call, the Activity should fade in backgrounds, start playing music, etc. It does not yet have access to players or teams, however. They remain owned by the previous Activity up until ba.Activity.on_begin() is called.
107 def on_begin(self) -> None: 108 super().on_begin() 109 110 # Spit out a few powerups and start dropping more shortly. 111 self._drop_powerups(standard_points=True) 112 ba.timer(2.0, ba.WeakCall(self._start_powerup_drops)) 113 ba.timer(0.001, ba.WeakCall(self._start_bot_updates)) 114 self.setup_low_life_warning_sound() 115 self._update_scores() 116 self._tntspawner = TNTSpawner(position=self._tntspawnpos, 117 respawn_time=10.0)
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.
119 def spawn_player(self, player: Player) -> ba.Actor: 120 pos = (self._spawn_center[0] + random.uniform(-1.5, 1.5), 121 self._spawn_center[1], 122 self._spawn_center[2] + random.uniform(-1.5, 1.5)) 123 return self.spawn_player_spaz(player, position=pos)
Spawn something for the provided ba.Player.
The default implementation simply calls spawn_player_spaz().
175 def do_end(self, outcome: str) -> None: 176 """End the game.""" 177 if outcome == 'defeat': 178 self.fade_to_red() 179 self.end(delay=2.0, 180 results={ 181 'outcome': outcome, 182 'score': self._score, 183 'playerinfos': self.initialplayerinfos 184 })
End the game.
256 def handlemessage(self, msg: Any) -> Any: 257 if isinstance(msg, ba.PlayerDiedMessage): 258 player = msg.getplayer(Player) 259 self.stats.player_was_killed(player) 260 ba.timer(0.1, self._checkroundover) 261 262 elif isinstance(msg, ba.PlayerScoredMessage): 263 self._score += msg.score 264 self._update_scores() 265 266 elif isinstance(msg, SpazBotDiedMessage): 267 pts, importance = msg.spazbot.get_death_points(msg.how) 268 target: Sequence[float] | None 269 if msg.killerplayer: 270 assert msg.spazbot.node 271 target = msg.spazbot.node.position 272 self.stats.player_scored(msg.killerplayer, 273 pts, 274 target=target, 275 kill=True, 276 screenmessage=False, 277 importance=importance) 278 ba.playsound(self._dingsound 279 if importance == 1 else self._dingsoundhigh, 280 volume=0.6) 281 282 # Normally we pull scores from the score-set, but if there's no 283 # player lets be explicit. 284 else: 285 self._score += pts 286 self._update_scores() 287 else: 288 super().handlemessage(msg)
General message handling; can be passed any message object.
293 def end_game(self) -> None: 294 # Tell our bots to celebrate just to rub it in. 295 self._bots.final_celebrate() 296 ba.setmusic(None) 297 ba.pushcall(ba.WeakCall(self.do_end, 'defeat'))
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.
Inherited Members
- ba._coopgame.CoopGameActivity
- session
- supports_session_type
- get_score_type
- celebrate
- spawn_player_spaz
- fade_to_red
- setup_low_life_warning_sound
- ba._gameactivity.GameActivity
- available_settings
- scoreconfig
- 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_supported_maps
- get_settings_display_string
- map
- get_instance_display_string
- get_instance_scoreboard_display_string
- get_instance_description
- get_instance_description_short
- on_continue
- is_waiting_for_continue
- continue_or_end_game
- on_player_join
- end
- respawn_player
- spawn_player_if_exists
- setup_standard_powerup_drops
- setup_standard_time_limit
- show_zoom_message
- ba._activity.Activity
- settings_raw
- teams
- players
- 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
- 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
- create_team
- ba._dependency.DependencyComponent
- dep_is_present
- get_dynamic_deps