bastd.game.ninjafight
Provides Ninja Fight mini-game.
1# Released under the MIT License. See LICENSE for details. 2# 3"""Provides Ninja Fight mini-game.""" 4 5# ba_meta require api 7 6# (see https://ballistica.net/wiki/meta-tag-system) 7 8from __future__ import annotations 9 10import random 11from typing import TYPE_CHECKING 12 13import ba 14from bastd.actor.spazbot import SpazBotSet, ChargerBot, SpazBotDiedMessage 15from bastd.actor.onscreentimer import OnScreenTimer 16 17if TYPE_CHECKING: 18 from typing import Any 19 20 21class Player(ba.Player['Team']): 22 """Our player type for this game.""" 23 24 25class Team(ba.Team[Player]): 26 """Our team type for this game.""" 27 28 29# ba_meta export game 30class NinjaFightGame(ba.TeamGameActivity[Player, Team]): 31 """ 32 A co-op game where you try to defeat a group 33 of Ninjas as fast as possible 34 """ 35 36 name = 'Ninja Fight' 37 description = 'How fast can you defeat the ninjas?' 38 scoreconfig = ba.ScoreConfig(label='Time', 39 scoretype=ba.ScoreType.MILLISECONDS, 40 lower_is_better=True) 41 default_music = ba.MusicType.TO_THE_DEATH 42 43 @classmethod 44 def get_supported_maps(cls, sessiontype: type[ba.Session]) -> list[str]: 45 # For now we're hard-coding spawn positions and whatnot 46 # so we need to be sure to specify that we only support 47 # a specific map. 48 return ['Courtyard'] 49 50 @classmethod 51 def supports_session_type(cls, sessiontype: type[ba.Session]) -> bool: 52 # We currently support Co-Op only. 53 return issubclass(sessiontype, ba.CoopSession) 54 55 # In the constructor we should load any media we need/etc. 56 # ...but not actually create anything yet. 57 def __init__(self, settings: dict): 58 super().__init__(settings) 59 self._winsound = ba.getsound('score') 60 self._won = False 61 self._timer: OnScreenTimer | None = None 62 self._bots = SpazBotSet() 63 self._preset = str(settings['preset']) 64 65 # Called when our game actually begins. 66 def on_begin(self) -> None: 67 super().on_begin() 68 is_pro = self._preset == 'pro' 69 70 # In pro mode there's no powerups. 71 if not is_pro: 72 self.setup_standard_powerup_drops() 73 74 # Make our on-screen timer and start it roughly when our bots appear. 75 self._timer = OnScreenTimer() 76 ba.timer(4.0, self._timer.start) 77 78 # Spawn some baddies. 79 ba.timer( 80 1.0, lambda: self._bots.spawn_bot( 81 ChargerBot, pos=(3, 3, -2), spawn_time=3.0)) 82 ba.timer( 83 2.0, lambda: self._bots.spawn_bot( 84 ChargerBot, pos=(-3, 3, -2), spawn_time=3.0)) 85 ba.timer( 86 3.0, lambda: self._bots.spawn_bot( 87 ChargerBot, pos=(5, 3, -2), spawn_time=3.0)) 88 ba.timer( 89 4.0, lambda: self._bots.spawn_bot( 90 ChargerBot, pos=(-5, 3, -2), spawn_time=3.0)) 91 92 # Add some extras for multiplayer or pro mode. 93 assert self.initialplayerinfos is not None 94 if len(self.initialplayerinfos) > 2 or is_pro: 95 ba.timer( 96 5.0, lambda: self._bots.spawn_bot( 97 ChargerBot, pos=(0, 3, -5), spawn_time=3.0)) 98 if len(self.initialplayerinfos) > 3 or is_pro: 99 ba.timer( 100 6.0, lambda: self._bots.spawn_bot( 101 ChargerBot, pos=(0, 3, 1), spawn_time=3.0)) 102 103 # Called for each spawning player. 104 def spawn_player(self, player: Player) -> ba.Actor: 105 106 # Let's spawn close to the center. 107 spawn_center = (0, 3, -2) 108 pos = (spawn_center[0] + random.uniform(-1.5, 1.5), spawn_center[1], 109 spawn_center[2] + random.uniform(-1.5, 1.5)) 110 return self.spawn_player_spaz(player, position=pos) 111 112 def _check_if_won(self) -> None: 113 # Simply end the game if there's no living bots. 114 # FIXME: Should also make sure all bots have been spawned; 115 # if spawning is spread out enough that we're able to kill 116 # all living bots before the next spawns, it would incorrectly 117 # count as a win. 118 if not self._bots.have_living_bots(): 119 self._won = True 120 self.end_game() 121 122 # Called for miscellaneous messages. 123 def handlemessage(self, msg: Any) -> Any: 124 125 # A player has died. 126 if isinstance(msg, ba.PlayerDiedMessage): 127 super().handlemessage(msg) # Augment standard behavior. 128 self.respawn_player(msg.getplayer(Player)) 129 130 # A spaz-bot has died. 131 elif isinstance(msg, SpazBotDiedMessage): 132 # Unfortunately the bot-set will always tell us there are living 133 # bots if we ask here (the currently-dying bot isn't officially 134 # marked dead yet) ..so lets push a call into the event loop to 135 # check once this guy has finished dying. 136 ba.pushcall(self._check_if_won) 137 138 # Let the base class handle anything we don't. 139 else: 140 return super().handlemessage(msg) 141 return None 142 143 # When this is called, we should fill out results and end the game 144 # *regardless* of whether is has been won. (this may be called due 145 # to a tournament ending or other external reason). 146 def end_game(self) -> None: 147 148 # Stop our on-screen timer so players can see what they got. 149 assert self._timer is not None 150 self._timer.stop() 151 152 results = ba.GameResults() 153 154 # If we won, set our score to the elapsed time in milliseconds. 155 # (there should just be 1 team here since this is co-op). 156 # ..if we didn't win, leave scores as default (None) which means 157 # we lost. 158 if self._won: 159 elapsed_time_ms = int((ba.time() - self._timer.starttime) * 1000.0) 160 ba.cameraflash() 161 ba.playsound(self._winsound) 162 for team in self.teams: 163 for player in team.players: 164 if player.actor: 165 player.actor.handlemessage(ba.CelebrateMessage()) 166 results.set_team_score(team, elapsed_time_ms) 167 168 # Ends the activity. 169 self.end(results)
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
31class NinjaFightGame(ba.TeamGameActivity[Player, Team]): 32 """ 33 A co-op game where you try to defeat a group 34 of Ninjas as fast as possible 35 """ 36 37 name = 'Ninja Fight' 38 description = 'How fast can you defeat the ninjas?' 39 scoreconfig = ba.ScoreConfig(label='Time', 40 scoretype=ba.ScoreType.MILLISECONDS, 41 lower_is_better=True) 42 default_music = ba.MusicType.TO_THE_DEATH 43 44 @classmethod 45 def get_supported_maps(cls, sessiontype: type[ba.Session]) -> list[str]: 46 # For now we're hard-coding spawn positions and whatnot 47 # so we need to be sure to specify that we only support 48 # a specific map. 49 return ['Courtyard'] 50 51 @classmethod 52 def supports_session_type(cls, sessiontype: type[ba.Session]) -> bool: 53 # We currently support Co-Op only. 54 return issubclass(sessiontype, ba.CoopSession) 55 56 # In the constructor we should load any media we need/etc. 57 # ...but not actually create anything yet. 58 def __init__(self, settings: dict): 59 super().__init__(settings) 60 self._winsound = ba.getsound('score') 61 self._won = False 62 self._timer: OnScreenTimer | None = None 63 self._bots = SpazBotSet() 64 self._preset = str(settings['preset']) 65 66 # Called when our game actually begins. 67 def on_begin(self) -> None: 68 super().on_begin() 69 is_pro = self._preset == 'pro' 70 71 # In pro mode there's no powerups. 72 if not is_pro: 73 self.setup_standard_powerup_drops() 74 75 # Make our on-screen timer and start it roughly when our bots appear. 76 self._timer = OnScreenTimer() 77 ba.timer(4.0, self._timer.start) 78 79 # Spawn some baddies. 80 ba.timer( 81 1.0, lambda: self._bots.spawn_bot( 82 ChargerBot, pos=(3, 3, -2), spawn_time=3.0)) 83 ba.timer( 84 2.0, lambda: self._bots.spawn_bot( 85 ChargerBot, pos=(-3, 3, -2), spawn_time=3.0)) 86 ba.timer( 87 3.0, lambda: self._bots.spawn_bot( 88 ChargerBot, pos=(5, 3, -2), spawn_time=3.0)) 89 ba.timer( 90 4.0, lambda: self._bots.spawn_bot( 91 ChargerBot, pos=(-5, 3, -2), spawn_time=3.0)) 92 93 # Add some extras for multiplayer or pro mode. 94 assert self.initialplayerinfos is not None 95 if len(self.initialplayerinfos) > 2 or is_pro: 96 ba.timer( 97 5.0, lambda: self._bots.spawn_bot( 98 ChargerBot, pos=(0, 3, -5), spawn_time=3.0)) 99 if len(self.initialplayerinfos) > 3 or is_pro: 100 ba.timer( 101 6.0, lambda: self._bots.spawn_bot( 102 ChargerBot, pos=(0, 3, 1), spawn_time=3.0)) 103 104 # Called for each spawning player. 105 def spawn_player(self, player: Player) -> ba.Actor: 106 107 # Let's spawn close to the center. 108 spawn_center = (0, 3, -2) 109 pos = (spawn_center[0] + random.uniform(-1.5, 1.5), spawn_center[1], 110 spawn_center[2] + random.uniform(-1.5, 1.5)) 111 return self.spawn_player_spaz(player, position=pos) 112 113 def _check_if_won(self) -> None: 114 # Simply end the game if there's no living bots. 115 # FIXME: Should also make sure all bots have been spawned; 116 # if spawning is spread out enough that we're able to kill 117 # all living bots before the next spawns, it would incorrectly 118 # count as a win. 119 if not self._bots.have_living_bots(): 120 self._won = True 121 self.end_game() 122 123 # Called for miscellaneous messages. 124 def handlemessage(self, msg: Any) -> Any: 125 126 # A player has died. 127 if isinstance(msg, ba.PlayerDiedMessage): 128 super().handlemessage(msg) # Augment standard behavior. 129 self.respawn_player(msg.getplayer(Player)) 130 131 # A spaz-bot has died. 132 elif isinstance(msg, SpazBotDiedMessage): 133 # Unfortunately the bot-set will always tell us there are living 134 # bots if we ask here (the currently-dying bot isn't officially 135 # marked dead yet) ..so lets push a call into the event loop to 136 # check once this guy has finished dying. 137 ba.pushcall(self._check_if_won) 138 139 # Let the base class handle anything we don't. 140 else: 141 return super().handlemessage(msg) 142 return None 143 144 # When this is called, we should fill out results and end the game 145 # *regardless* of whether is has been won. (this may be called due 146 # to a tournament ending or other external reason). 147 def end_game(self) -> None: 148 149 # Stop our on-screen timer so players can see what they got. 150 assert self._timer is not None 151 self._timer.stop() 152 153 results = ba.GameResults() 154 155 # If we won, set our score to the elapsed time in milliseconds. 156 # (there should just be 1 team here since this is co-op). 157 # ..if we didn't win, leave scores as default (None) which means 158 # we lost. 159 if self._won: 160 elapsed_time_ms = int((ba.time() - self._timer.starttime) * 1000.0) 161 ba.cameraflash() 162 ba.playsound(self._winsound) 163 for team in self.teams: 164 for player in team.players: 165 if player.actor: 166 player.actor.handlemessage(ba.CelebrateMessage()) 167 results.set_team_score(team, elapsed_time_ms) 168 169 # Ends the activity. 170 self.end(results)
A co-op game where you try to defeat a group of Ninjas as fast as possible
58 def __init__(self, settings: dict): 59 super().__init__(settings) 60 self._winsound = ba.getsound('score') 61 self._won = False 62 self._timer: OnScreenTimer | None = None 63 self._bots = SpazBotSet() 64 self._preset = str(settings['preset'])
Instantiate the Activity.
44 @classmethod 45 def get_supported_maps(cls, sessiontype: type[ba.Session]) -> list[str]: 46 # For now we're hard-coding spawn positions and whatnot 47 # so we need to be sure to specify that we only support 48 # a specific map. 49 return ['Courtyard']
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.
51 @classmethod 52 def supports_session_type(cls, sessiontype: type[ba.Session]) -> bool: 53 # We currently support Co-Op only. 54 return issubclass(sessiontype, ba.CoopSession)
Class method override; returns True for ba.DualTeamSessions and ba.FreeForAllSessions; False otherwise.
67 def on_begin(self) -> None: 68 super().on_begin() 69 is_pro = self._preset == 'pro' 70 71 # In pro mode there's no powerups. 72 if not is_pro: 73 self.setup_standard_powerup_drops() 74 75 # Make our on-screen timer and start it roughly when our bots appear. 76 self._timer = OnScreenTimer() 77 ba.timer(4.0, self._timer.start) 78 79 # Spawn some baddies. 80 ba.timer( 81 1.0, lambda: self._bots.spawn_bot( 82 ChargerBot, pos=(3, 3, -2), spawn_time=3.0)) 83 ba.timer( 84 2.0, lambda: self._bots.spawn_bot( 85 ChargerBot, pos=(-3, 3, -2), spawn_time=3.0)) 86 ba.timer( 87 3.0, lambda: self._bots.spawn_bot( 88 ChargerBot, pos=(5, 3, -2), spawn_time=3.0)) 89 ba.timer( 90 4.0, lambda: self._bots.spawn_bot( 91 ChargerBot, pos=(-5, 3, -2), spawn_time=3.0)) 92 93 # Add some extras for multiplayer or pro mode. 94 assert self.initialplayerinfos is not None 95 if len(self.initialplayerinfos) > 2 or is_pro: 96 ba.timer( 97 5.0, lambda: self._bots.spawn_bot( 98 ChargerBot, pos=(0, 3, -5), spawn_time=3.0)) 99 if len(self.initialplayerinfos) > 3 or is_pro: 100 ba.timer( 101 6.0, lambda: self._bots.spawn_bot( 102 ChargerBot, pos=(0, 3, 1), spawn_time=3.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.
105 def spawn_player(self, player: Player) -> ba.Actor: 106 107 # Let's spawn close to the center. 108 spawn_center = (0, 3, -2) 109 pos = (spawn_center[0] + random.uniform(-1.5, 1.5), spawn_center[1], 110 spawn_center[2] + random.uniform(-1.5, 1.5)) 111 return self.spawn_player_spaz(player, position=pos)
Spawn something for the provided ba.Player.
The default implementation simply calls spawn_player_spaz().
124 def handlemessage(self, msg: Any) -> Any: 125 126 # A player has died. 127 if isinstance(msg, ba.PlayerDiedMessage): 128 super().handlemessage(msg) # Augment standard behavior. 129 self.respawn_player(msg.getplayer(Player)) 130 131 # A spaz-bot has died. 132 elif isinstance(msg, SpazBotDiedMessage): 133 # Unfortunately the bot-set will always tell us there are living 134 # bots if we ask here (the currently-dying bot isn't officially 135 # marked dead yet) ..so lets push a call into the event loop to 136 # check once this guy has finished dying. 137 ba.pushcall(self._check_if_won) 138 139 # Let the base class handle anything we don't. 140 else: 141 return super().handlemessage(msg) 142 return None
General message handling; can be passed any message object.
147 def end_game(self) -> None: 148 149 # Stop our on-screen timer so players can see what they got. 150 assert self._timer is not None 151 self._timer.stop() 152 153 results = ba.GameResults() 154 155 # If we won, set our score to the elapsed time in milliseconds. 156 # (there should just be 1 team here since this is co-op). 157 # ..if we didn't win, leave scores as default (None) which means 158 # we lost. 159 if self._won: 160 elapsed_time_ms = int((ba.time() - self._timer.starttime) * 1000.0) 161 ba.cameraflash() 162 ba.playsound(self._winsound) 163 for team in self.teams: 164 for player in team.players: 165 if player.actor: 166 player.actor.handlemessage(ba.CelebrateMessage()) 167 results.set_team_score(team, elapsed_time_ms) 168 169 # Ends the activity. 170 self.end(results)
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._teamgame.TeamGameActivity
- on_transition_in
- spawn_player_spaz
- end
- ba._gameactivity.GameActivity
- tips
- available_settings
- 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
- get_instance_description_short
- on_continue
- is_waiting_for_continue
- continue_or_end_game
- on_player_join
- respawn_player
- spawn_player_if_exists
- setup_standard_powerup_drops
- setup_standard_time_limit
- show_zoom_message
- ba._activity.Activity
- settings_raw
- teams
- players
- announce_player_deaths
- is_joining_activity
- use_fixed_vr_overlay
- slow_motion
- inherits_slow_motion
- inherits_music
- inherits_vr_camera_offset
- inherits_vr_overlay_center
- inherits_tint
- allow_mid_activity_joins
- transition_time
- can_show_ad_on_death
- globalsnode
- stats
- on_expire
- customdata
- expired
- playertype
- teamtype
- retain_actor
- add_actor_weak_ref
- session
- on_player_leave
- on_team_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