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