def on_begin(self) -> None: super().on_begin() self.setup_standard_time_limit(self.settings['Time Limit']) self.setup_standard_powerup_drops() self._flag_spawn_pos = (self.map.get_flag_position(None)) self._spawn_flag() defs = self.map.defs self._score_regions.append( ba.NodeActor( ba.newnode('region', attrs={ 'position': defs.boxes['goal1'][0:3], 'scale': defs.boxes['goal1'][6:9], 'type': 'box', 'materials': (self.score_region_material, ) }))) self._score_regions.append( ba.NodeActor( ba.newnode('region', attrs={ 'position': defs.boxes['goal2'][0:3], 'scale': defs.boxes['goal2'][6:9], 'type': 'box', 'materials': (self.score_region_material, ) }))) self._update_scoreboard() ba.playsound(self._chant_sound)
def on_begin(self) -> None: super().on_begin() self.setup_standard_time_limit(self._time_limit) self.setup_standard_powerup_drops() self._puck_spawn_pos = self.map.get_flag_position(None) self._spawn_puck() # Set up the two score regions. defs = self.map.defs self._score_regions = [] self._score_regions.append( ba.NodeActor( ba.newnode('region', attrs={ 'position': defs.boxes['goal1'][0:3], 'scale': defs.boxes['goal1'][6:9], 'type': 'box', 'materials': [self._score_region_material] }))) self._score_regions.append( ba.NodeActor( ba.newnode('region', attrs={ 'position': defs.boxes['goal2'][0:3], 'scale': defs.boxes['goal2'][6:9], 'type': 'box', 'materials': [self._score_region_material] }))) self._update_scoreboard() ba.playsound(self._chant_sound)
def on_transition_in(self) -> None: super().on_transition_in() self._scoreboard = Scoreboard() self._flag_spawn_pos = self.map.get_flag_position(None) self._spawn_flag() # Set up the two score regions. defs = self.map.defs self._score_regions.append( ba.NodeActor( ba.newnode('region', attrs={ 'position': defs.boxes['goal1'][0:3], 'scale': defs.boxes['goal1'][6:9], 'type': 'box', 'materials': [self._score_region_material] }))) self._score_regions.append( ba.NodeActor( ba.newnode('region', attrs={ 'position': defs.boxes['goal2'][0:3], 'scale': defs.boxes['goal2'][6:9], 'type': 'box', 'materials': [self._score_region_material] }))) ba.playsound(self._chant_sound)
def _touch_return_update(self, team: ba.Team) -> None: # Count down only while its away from base and not being held. if (team.gamedata['home_flag_at_base'] or team.gamedata['flag'].held_count > 0): team.gamedata['touch_return_timer_ticking'] = None return # No need to return when its at home. if team.gamedata['touch_return_timer_ticking'] is None: team.gamedata['touch_return_timer_ticking'] = ba.NodeActor( ba.newnode('sound', attrs={ 'sound': self._ticking_sound, 'positional': False, 'loop': True })) flag = team.gamedata['flag'] flag.touch_return_time -= 0.1 if flag.counter: flag.counter.text = '%.1f' % flag.touch_return_time flag.counter.color = (1, 1, 0, 1) flag.counter.scale = 0.02 if flag.touch_return_time <= 0.0: self._award_players_touching_own_flag(team) flag.handlemessage(ba.DieMessage())
def _touch_return_update(self, team: Team) -> None: # Count down only while its away from base and not being held. assert team.flag is not None if team.home_flag_at_base or team.flag.held_count > 0: team.touch_return_timer_ticking = None return # No need to return when its at home. if team.touch_return_timer_ticking is None: team.touch_return_timer_ticking = ba.NodeActor( ba.newnode('sound', attrs={ 'sound': self._ticking_sound, 'positional': False, 'loop': True })) flag = team.flag if flag.touch_return_time is not None: flag.touch_return_time -= 0.1 if flag.counter: flag.counter.text = f'{flag.touch_return_time:.1f}' flag.counter.color = (1, 1, 0, 1) flag.counter.scale = 0.02 if flag.touch_return_time <= 0.0: self._award_players_touching_own_flag(team) flag.handlemessage(ba.DieMessage())
def blast(x: int, y: int, z: int) -> None: # add sound ba.NodeActor(node=ba.newnode('scorch', attrs={ 'position': (x, z, y), 'size': 0.2, 'big': False, })).autoretain()
def handlemessage(self, msg: Any) -> Any: """ handle high-level game messages """ if isinstance(msg, playerspaz.PlayerSpazDeathMessage): from bastd.actor import respawnicon # Respawn dead players. player = msg.spaz.player self.stats.player_was_killed(player) assert self.initial_player_info is not None respawn_time = 2.0 + len(self.initial_player_info) * 1.0 # Respawn them shortly. player.gamedata['respawn_timer'] = ba.Timer( respawn_time, ba.Call(self.spawn_player_if_exists, player)) player.gamedata['respawn_icon'] = respawnicon.RespawnIcon( player, respawn_time) # Augment standard behavior. super().handlemessage(msg) elif isinstance(msg, spazbot.SpazBotDeathMessage): # Every time a bad guy dies, spawn a new one. ba.timer(3.0, ba.Call(self._spawn_bot, (type(msg.badguy)))) elif isinstance(msg, spazbot.SpazBotPunchedMessage): if self._preset in ['rookie', 'rookie_easy']: if msg.damage >= 500: self._award_achievement('Super Punch') elif self._preset in ['pro', 'pro_easy']: if msg.damage >= 1000: self._award_achievement('Super Mega Punch') # Respawn dead flags. elif isinstance(msg, stdflag.FlagDeathMessage): assert isinstance(msg.flag, FootballFlag) msg.flag.respawn_timer = ba.Timer(3.0, self._spawn_flag) self._flag_respawn_light = ba.NodeActor( ba.newnode('light', attrs={ 'position': self._flag_spawn_pos, 'height_attenuated': False, 'radius': 0.15, 'color': (1.0, 1.0, 0.3) })) assert self._flag_respawn_light.node ba.animate(self._flag_respawn_light.node, 'intensity', { 0: 0, 0.25: 0.15, 0.5: 0 }, loop=True) ba.timer(3.0, self._flag_respawn_light.node.delete) else: super().handlemessage(msg)
def on_transition_in(self) -> None: super().on_transition_in() self._scoreboard = Scoreboard(label=ba.Lstr(resource='scoreText'), score_split=0.5) self._score_region = ba.NodeActor( ba.newnode('region', attrs={ 'position': self.map.defs.boxes['score_region'][0:3], 'scale': self.map.defs.boxes['score_region'][6:9], 'type': 'box', 'materials': [self._score_region_material] }))
def handlemessage(self, msg: Any) -> Any: """ handle high-level game messages """ if isinstance(msg, ba.PlayerDiedMessage): # Augment standard behavior. super().handlemessage(msg) # Respawn them shortly. player = msg.getplayer(Player) assert self.initialplayerinfos is not None respawn_time = 2.0 + len(self.initialplayerinfos) * 1.0 player.respawn_timer = ba.Timer( respawn_time, ba.Call(self.spawn_player_if_exists, player)) player.respawn_icon = RespawnIcon(player, respawn_time) elif isinstance(msg, SpazBotDiedMessage): # Every time a bad guy dies, spawn a new one. ba.timer(3.0, ba.Call(self._spawn_bot, (type(msg.spazbot)))) elif isinstance(msg, SpazBotPunchedMessage): if self._preset in ['rookie', 'rookie_easy']: if msg.damage >= 500: self._award_achievement('Super Punch') elif self._preset in ['pro', 'pro_easy']: if msg.damage >= 1000: self._award_achievement('Super Mega Punch') # Respawn dead flags. elif isinstance(msg, FlagDiedMessage): assert isinstance(msg.flag, FootballFlag) msg.flag.respawn_timer = ba.Timer(3.0, self._spawn_flag) self._flag_respawn_light = ba.NodeActor( ba.newnode('light', attrs={ 'position': self._flag_spawn_pos, 'height_attenuated': False, 'radius': 0.15, 'color': (1.0, 1.0, 0.3) })) assert self._flag_respawn_light.node ba.animate(self._flag_respawn_light.node, 'intensity', { 0: 0, 0.25: 0.15, 0.5: 0 }, loop=True) ba.timer(3.0, self._flag_respawn_light.node.delete) else: return super().handlemessage(msg) return None
def on_begin(self) -> None: super().on_begin() self._pow = None self.setup_standard_powerup_drops(enable_tnt=False) self._tnt_drop_timer = ba.timer(30, ba.WeakCall(self._dropPowBox), repeat=True) self._start_time = ba.time() self.setup_standard_time_limit(self._time_limit) if self._solo_mode: self._vs_text = ba.NodeActor( ba.newnode('text', attrs={ 'position': (0, 105), 'h_attach': 'center', 'h_align': 'center', 'maxwidth': 200, 'shadow': 0.5, 'vr_depth': 390, 'scale': 0.6, 'v_attach': 'bottom', 'color': (0.8, 0.8, 0.3, 1.0), 'text': ba.Lstr(resource='vsText') })) # If balance-team-lives is on, add lives to the smaller team until # total lives match. if (isinstance(self.session, ba.DualTeamSession) and self._balance_total_lives and self.teams[0].players and self.teams[1].players): if self._get_total_team_lives( self.teams[0]) < self._get_total_team_lives(self.teams[1]): lesser_team = self.teams[0] greater_team = self.teams[1] else: lesser_team = self.teams[1] greater_team = self.teams[0] add_index = 0 while (self._get_total_team_lives(lesser_team) < self._get_total_team_lives(greater_team)): lesser_team.players[add_index].lives += 1 add_index = (add_index + 1) % len(lesser_team.players) self._update_icons() # We could check game-over conditions at explicit trigger points, # but lets just do the simple thing and poll it. ba.timer(1.0, self._update, repeat=True)
def handlemessage(self, msg: Any) -> Any: if isinstance(msg, stdflag.FlagPickedUpMessage): assert isinstance(msg.flag, FootballFlag) try: player = msg.node.getdelegate().getplayer() if player: msg.flag.last_holding_player = player msg.flag.held_count += 1 except Exception: ba.print_exception('exception in Football FlagPickedUpMessage;' " this shouldn't happen") elif isinstance(msg, stdflag.FlagDroppedMessage): assert isinstance(msg.flag, FootballFlag) msg.flag.held_count -= 1 # Respawn dead players if they're still in the game. elif isinstance(msg, playerspaz.PlayerSpazDeathMessage): # Augment standard behavior. super().handlemessage(msg) self.respawn_player(msg.spaz.player) # Respawn dead flags. elif isinstance(msg, stdflag.FlagDeathMessage): if not self.has_ended(): self._flag_respawn_timer = ba.Timer(3.0, self._spawn_flag) self._flag_respawn_light = ba.NodeActor( ba.newnode('light', attrs={ 'position': self._flag_spawn_pos, 'height_attenuated': False, 'radius': 0.15, 'color': (1.0, 1.0, 0.3) })) assert self._flag_respawn_light.node ba.animate(self._flag_respawn_light.node, 'intensity', { 0.0: 0, 0.25: 0.15, 0.5: 0 }, loop=True) ba.timer(3.0, self._flag_respawn_light.node.delete) else: # Augment standard behavior. super().handlemessage(msg)
def handlemessage(self, msg: Any) -> Any: if isinstance(msg, FlagPickedUpMessage): assert isinstance(msg.flag, FootballFlag) try: msg.flag.last_holding_player = msg.node.getdelegate( PlayerSpaz, True).getplayer(Player, True) except ba.NotFoundError: pass msg.flag.held_count += 1 elif isinstance(msg, FlagDroppedMessage): assert isinstance(msg.flag, FootballFlag) msg.flag.held_count -= 1 # Respawn dead players if they're still in the game. elif isinstance(msg, ba.PlayerDiedMessage): # Augment standard behavior. super().handlemessage(msg) self.respawn_player(msg.getplayer(Player)) # Respawn dead flags. elif isinstance(msg, FlagDiedMessage): if not self.has_ended(): self._flag_respawn_timer = ba.Timer(3.0, self._spawn_flag) self._flag_respawn_light = ba.NodeActor( ba.newnode('light', attrs={ 'position': self._flag_spawn_pos, 'height_attenuated': False, 'radius': 0.15, 'color': (1.0, 1.0, 0.3) })) assert self._flag_respawn_light.node ba.animate(self._flag_respawn_light.node, 'intensity', { 0.0: 0, 0.25: 0.15, 0.5: 0 }, loop=True) ba.timer(3.0, self._flag_respawn_light.node.delete) else: # Augment standard behavior. super().handlemessage(msg)
def on_begin(self) -> None: super().on_begin() self.setup_standard_time_limit(self.settings['Time Limit']) self.setup_standard_powerup_drops() if self._solo_mode: self._vs_text = ba.NodeActor( ba.newnode("text", attrs={ 'position': (0, 105), 'h_attach': "center", 'h_align': 'center', 'maxwidth': 200, 'shadow': 0.5, 'vr_depth': 390, 'scale': 0.6, 'v_attach': "bottom", 'color': (0.8, 0.8, 0.3, 1.0), 'text': ba.Lstr(resource='vsText') })) # If balance-team-lives is on, add lives to the smaller team until # total lives match. if (isinstance(self.session, ba.TeamsSession) and self.settings['Balance Total Lives'] and self.teams[0].players and self.teams[1].players): if self._get_total_team_lives( self.teams[0]) < self._get_total_team_lives(self.teams[1]): lesser_team = self.teams[0] greater_team = self.teams[1] else: lesser_team = self.teams[1] greater_team = self.teams[0] add_index = 0 while self._get_total_team_lives( lesser_team) < self._get_total_team_lives(greater_team): lesser_team.players[add_index].gamedata['lives'] += 1 add_index = (add_index + 1) % len(lesser_team.players) self._update_icons() # We could check game-over conditions at explicit trigger points, # but lets just do the simple thing and poll it. ba.timer(1.0, self._update, repeat=True)
def _got_news(self, news: str) -> None: # Run this stuff in the context of our activity since we # need to make nodes and stuff.. should fix the serverget # call so it. activity = self._activity() if activity is None or activity.expired: return with ba.Context(activity): self._phrases: List[str] = [] # Show upcoming achievements in non-vr versions # (currently too hard to read in vr). self._used_phrases = ( ['__ACH__'] if not ba.app.vr_mode else []) + [s for s in news.split('<br>\n') if s != ''] self._phrase_change_timer = ba.Timer( (self._message_duration + self._message_spacing), ba.WeakCall(self._change_phrase), repeat=True) scl = 1.2 if (ba.app.ui.uiscale is ba.UIScale.SMALL or ba.app.vr_mode) else 0.8 color2 = ((1, 1, 1, 1) if ba.app.vr_mode else (0.7, 0.65, 0.75, 1.0)) shadow = (1.0 if ba.app.vr_mode else 0.4) self._text = ba.NodeActor( ba.newnode('text', attrs={ 'v_attach': 'top', 'h_attach': 'center', 'h_align': 'center', 'vr_depth': -20, 'shadow': shadow, 'flatness': 0.8, 'v_align': 'top', 'color': color2, 'scale': scl, 'maxwidth': 900.0 / scl, 'position': (0, -10) })) self._change_phrase()
def on_begin(self) -> None: # FIXME: Split this up a bit. # pylint: disable=too-many-statements from bastd.actor import controlsguide super().on_begin() # Show controls help in kiosk mode. if ba.app.demo_mode or ba.app.arcade_mode: controlsguide.ControlsGuide(delay=3.0, lifespan=10.0, bright=True).autoretain() assert self.initialplayerinfos is not None abot: Type[SpazBot] bbot: Type[SpazBot] cbot: Type[SpazBot] if self._preset in ['rookie', 'rookie_easy']: self._exclude_powerups = ['curse'] self._have_tnt = False abot = (BrawlerBotLite if self._preset == 'rookie_easy' else BrawlerBot) self._bot_types_initial = [abot] * len(self.initialplayerinfos) bbot = (BomberBotLite if self._preset == 'rookie_easy' else BomberBot) self._bot_types_7 = ( [bbot] * (1 if len(self.initialplayerinfos) < 3 else 2)) cbot = (BomberBot if self._preset == 'rookie_easy' else TriggerBot) self._bot_types_14 = ( [cbot] * (1 if len(self.initialplayerinfos) < 3 else 2)) elif self._preset == 'tournament': self._exclude_powerups = [] self._have_tnt = True self._bot_types_initial = ( [BrawlerBot] * (1 if len(self.initialplayerinfos) < 2 else 2)) self._bot_types_7 = ( [TriggerBot] * (1 if len(self.initialplayerinfos) < 3 else 2)) self._bot_types_14 = ( [ChargerBot] * (1 if len(self.initialplayerinfos) < 4 else 2)) elif self._preset in ['pro', 'pro_easy', 'tournament_pro']: self._exclude_powerups = ['curse'] self._have_tnt = True self._bot_types_initial = [ChargerBot] * len( self.initialplayerinfos) abot = (BrawlerBot if self._preset == 'pro' else BrawlerBotLite) typed_bot_list: List[Type[SpazBot]] = [] self._bot_types_7 = ( typed_bot_list + [abot] + [BomberBot] * (1 if len(self.initialplayerinfos) < 3 else 2)) bbot = (TriggerBotPro if self._preset == 'pro' else TriggerBot) self._bot_types_14 = ( [bbot] * (1 if len(self.initialplayerinfos) < 3 else 2)) elif self._preset in ['uber', 'uber_easy']: self._exclude_powerups = [] self._have_tnt = True abot = (BrawlerBotPro if self._preset == 'uber' else BrawlerBot) bbot = (TriggerBotPro if self._preset == 'uber' else TriggerBot) typed_bot_list_2: List[Type[SpazBot]] = [] self._bot_types_initial = (typed_bot_list_2 + [StickyBot] + [abot] * len(self.initialplayerinfos)) self._bot_types_7 = ( [bbot] * (1 if len(self.initialplayerinfos) < 3 else 2)) self._bot_types_14 = ( [ExplodeyBot] * (1 if len(self.initialplayerinfos) < 3 else 2)) else: raise Exception() self.setup_low_life_warning_sound() self._drop_powerups(standard_points=True) ba.timer(4.0, self._start_powerup_drops) # Make a bogus team for our bots. bad_team_name = self.get_team_display_string('Bad Guys') self._bot_team = Team() self._bot_team.manual_init(team_id=1, name=bad_team_name, color=(0.5, 0.4, 0.4)) for team in [self.teams[0], self._bot_team]: team.score = 0 self.update_scores() # Time display. starttime_ms = ba.time(timeformat=ba.TimeFormat.MILLISECONDS) assert isinstance(starttime_ms, int) self._starttime_ms = starttime_ms self._time_text = ba.NodeActor( ba.newnode('text', attrs={ 'v_attach': 'top', 'h_attach': 'center', 'h_align': 'center', 'color': (1, 1, 0.5, 1), 'flatness': 0.5, 'shadow': 0.5, 'position': (0, -50), 'scale': 1.3, 'text': '' })) self._time_text_input = ba.NodeActor( ba.newnode('timedisplay', attrs={'showsubseconds': True})) self.globalsnode.connectattr('time', self._time_text_input.node, 'time2') assert self._time_text_input.node assert self._time_text.node self._time_text_input.node.connectattr('output', self._time_text.node, 'text') # Our TNT spawner (if applicable). if self._have_tnt: self._tntspawner = TNTSpawner(position=(0, 1, -1)) self._bots = SpazBotSet() self._bot_spawn_timer = ba.Timer(1.0, self._update_bots, repeat=True) for bottype in self._bot_types_initial: self._spawn_bot(bottype)
def on_begin(self) -> None: from bastd.actor.onscreentimer import OnScreenTimer super().on_begin() self.setup_standard_time_limit(self.settings['Time Limit']) self.setup_standard_powerup_drops() self._team_finish_pts = 100 # Throw a timer up on-screen. self._time_text = ba.NodeActor( ba.newnode('text', attrs={ 'v_attach': 'top', 'h_attach': 'center', 'h_align': 'center', 'color': (1, 1, 0.5, 1), 'flatness': 0.5, 'shadow': 0.5, 'position': (0, -50), 'scale': 1.4, 'text': '' })) self._timer = OnScreenTimer() if self.settings['Mine Spawning'] != 0: self._race_mines = [ RaceMine(point=p, mine=None) for p in self.map.get_def_points('race_mine') ] if self._race_mines: self._race_mine_timer = ba.Timer( 0.001 * self.settings['Mine Spawning'], self._update_race_mine, repeat=True) self._scoreboard_timer = ba.Timer(0.25, self._update_scoreboard, repeat=True) self._player_order_update_timer = ba.Timer(0.25, self._update_player_order, repeat=True) if self.slow_motion: t_scale = 0.4 light_y = 50 else: t_scale = 1.0 light_y = 150 lstart = 7.1 * t_scale inc = 1.25 * t_scale ba.timer(lstart, self._do_light_1) ba.timer(lstart + inc, self._do_light_2) ba.timer(lstart + 2 * inc, self._do_light_3) ba.timer(lstart + 3 * inc, self._start_race) self._start_lights = [] for i in range(4): lnub = ba.newnode('image', attrs={ 'texture': ba.gettexture('nub'), 'opacity': 1.0, 'absolute_scale': True, 'position': (-75 + i * 50, light_y), 'scale': (50, 50), 'attach': 'center' }) ba.animate( lnub, 'opacity', { 4.0 * t_scale: 0, 5.0 * t_scale: 1.0, 12.0 * t_scale: 1.0, 12.5 * t_scale: 0.0 }) ba.timer(13.0 * t_scale, lnub.delete) self._start_lights.append(lnub) self._start_lights[0].color = (0.2, 0, 0) self._start_lights[1].color = (0.2, 0, 0) self._start_lights[2].color = (0.2, 0.05, 0) self._start_lights[3].color = (0.0, 0.3, 0)
def _make_logo(self, x: float, y: float, scale: float, delay: float, custom_texture: str = None, jitter_scale: float = 1.0, rotate: float = 0.0, vr_depth_offset: float = 0.0) -> None: # Temp easter goodness. if custom_texture is None: custom_texture = self._get_custom_logo_tex_name() self._custom_logo_tex_name = custom_texture ltex = ba.gettexture( custom_texture if custom_texture is not None else 'logo') mopaque = (None if custom_texture is not None else ba.getmodel('logo')) mtrans = (None if custom_texture is not None else ba.getmodel('logoTransparent')) logo = ba.NodeActor( ba.newnode('image', attrs={ 'texture': ltex, 'model_opaque': mopaque, 'model_transparent': mtrans, 'vr_depth': -10 + vr_depth_offset, 'rotate': rotate, 'attach': 'center', 'tilt_translate': 0.21, 'absolute_scale': True })) self._logo_node = logo.node self._word_actors.append(logo) # Add a bit of stop-motion-y jitter to the logo # (unless we're in VR mode in which case its best to # leave things still). assert logo.node if not ba.app.vr_mode: cmb = ba.newnode('combine', owner=logo.node, attrs={'size': 2}) cmb.connectattr('output', logo.node, 'position') keys = {} time_v = 0.0 # Gen some random keys for that stop-motion-y look for _i in range(10): keys[time_v] = x + (random.random() - 0.5) * 0.7 * jitter_scale time_v += random.random() * 0.1 ba.animate(cmb, 'input0', keys, loop=True) keys = {} time_v = 0.0 for _i in range(10): keys[time_v * self._ts] = y + (random.random() - 0.5) * 0.7 * jitter_scale time_v += random.random() * 0.1 ba.animate(cmb, 'input1', keys, loop=True) else: logo.node.position = (x, y) cmb = ba.newnode('combine', owner=logo.node, attrs={'size': 2}) keys = { delay: 0.0, delay + 0.1: 700.0 * scale, delay + 0.2: 600.0 * scale } ba.animate(cmb, 'input0', keys) ba.animate(cmb, 'input1', keys) cmb.connectattr('output', logo.node, 'scale')
def _make_word(self, word: str, x: float, y: float, scale: float = 1.0, delay: float = 0.0, vr_depth_offset: float = 0.0, shadow: bool = False) -> None: if shadow: word_obj = ba.NodeActor( ba.newnode('text', attrs={ 'position': (x, y), 'big': True, 'color': (0.0, 0.0, 0.2, 0.08), 'tilt_translate': 0.09, 'opacity_scales_shadow': False, 'shadow': 0.2, 'vr_depth': -130, 'v_align': 'center', 'project_scale': 0.97 * scale, 'scale': 1.0, 'text': word })) self._word_actors.append(word_obj) else: word_obj = ba.NodeActor( ba.newnode('text', attrs={ 'position': (x, y), 'big': True, 'color': (1.2, 1.15, 1.15, 1.0), 'tilt_translate': 0.11, 'shadow': 0.2, 'vr_depth': -40 + vr_depth_offset, 'v_align': 'center', 'project_scale': scale, 'scale': 1.0, 'text': word })) self._word_actors.append(word_obj) # Add a bit of stop-motion-y jitter to the logo # (unless we're in VR mode in which case its best to # leave things still). if not ba.app.vr_mode: cmb: Optional[ba.Node] cmb2: Optional[ba.Node] if not shadow: cmb = ba.newnode('combine', owner=word_obj.node, attrs={'size': 2}) else: cmb = None if shadow: cmb2 = ba.newnode('combine', owner=word_obj.node, attrs={'size': 2}) else: cmb2 = None if not shadow: assert cmb and word_obj.node cmb.connectattr('output', word_obj.node, 'position') if shadow: assert cmb2 and word_obj.node cmb2.connectattr('output', word_obj.node, 'position') keys = {} keys2 = {} time_v = 0.0 for _i in range(10): val = x + (random.random() - 0.5) * 0.8 val2 = x + (random.random() - 0.5) * 0.8 keys[time_v * self._ts] = val keys2[time_v * self._ts] = val2 + 5 time_v += random.random() * 0.1 if cmb is not None: ba.animate(cmb, 'input0', keys, loop=True) if cmb2 is not None: ba.animate(cmb2, 'input0', keys2, loop=True) keys = {} keys2 = {} time_v = 0 for _i in range(10): val = y + (random.random() - 0.5) * 0.8 val2 = y + (random.random() - 0.5) * 0.8 keys[time_v * self._ts] = val keys2[time_v * self._ts] = val2 - 9 time_v += random.random() * 0.1 if cmb is not None: ba.animate(cmb, 'input1', keys, loop=True) if cmb2 is not None: ba.animate(cmb2, 'input1', keys2, loop=True) if not shadow: assert word_obj.node ba.animate(word_obj.node, 'project_scale', { delay: 0.0, delay + 0.1: scale * 1.1, delay + 0.2: scale }) else: assert word_obj.node ba.animate(word_obj.node, 'project_scale', { delay: 0.0, delay + 0.1: scale * 1.1, delay + 0.2: scale })
def on_transition_in(self) -> None: super().on_transition_in() random.seed(123) self._logo_node: Optional[ba.Node] = None self._custom_logo_tex_name: Optional[str] = None self._word_actors: List[ba.Actor] = [] app = ba.app # FIXME: We shouldn't be doing things conditionally based on whether # the host is VR mode or not (clients may differ in that regard). # Any differences need to happen at the engine level so everyone # sees things in their own optimal way. vr_mode = ba.app.vr_mode if not ba.app.toolbar_test: color = ((1.0, 1.0, 1.0, 1.0) if vr_mode else (0.5, 0.6, 0.5, 0.6)) # FIXME: Need a node attr for vr-specific-scale. scale = (0.9 if (app.ui.uiscale is ba.UIScale.SMALL or vr_mode) else 0.7) self.my_name = ba.NodeActor( ba.newnode('text', attrs={ 'v_attach': 'bottom', 'h_align': 'center', 'color': color, 'flatness': 1.0, 'shadow': 1.0 if vr_mode else 0.5, 'scale': scale, 'position': (0, 10), 'vr_depth': -10, 'text': '\xa9 2011-2020 Eric Froemling' })) # Throw up some text that only clients can see so they know that the # host is navigating menus while they're just staring at an # empty-ish screen. tval = ba.Lstr(resource='hostIsNavigatingMenusText', subs=[('${HOST}', _ba.get_account_display_string())]) self._host_is_navigating_text = ba.NodeActor( ba.newnode('text', attrs={ 'text': tval, 'client_only': True, 'position': (0, -200), 'flatness': 1.0, 'h_align': 'center' })) if not ba.app.main_menu_did_initial_transition and hasattr( self, 'my_name'): assert self.my_name.node ba.animate(self.my_name.node, 'opacity', {2.3: 0, 3.0: 1.0}) # FIXME: We shouldn't be doing things conditionally based on whether # the host is vr mode or not (clients may not be or vice versa). # Any differences need to happen at the engine level so everyone sees # things in their own optimal way. vr_mode = app.vr_mode uiscale = app.ui.uiscale # In cases where we're doing lots of dev work lets always show the # build number. force_show_build_number = False if not ba.app.toolbar_test: if app.debug_build or app.test_build or force_show_build_number: if app.debug_build: text = ba.Lstr(value='${V} (${B}) (${D})', subs=[ ('${V}', app.version), ('${B}', str(app.build_number)), ('${D}', ba.Lstr(resource='debugText')), ]) else: text = ba.Lstr(value='${V} (${B})', subs=[ ('${V}', app.version), ('${B}', str(app.build_number)), ]) else: text = ba.Lstr(value='${V}', subs=[('${V}', app.version)]) scale = 0.9 if (uiscale is ba.UIScale.SMALL or vr_mode) else 0.7 color = (1, 1, 1, 1) if vr_mode else (0.5, 0.6, 0.5, 0.7) self.version = ba.NodeActor( ba.newnode( 'text', attrs={ 'v_attach': 'bottom', 'h_attach': 'right', 'h_align': 'right', 'flatness': 1.0, 'vr_depth': -10, 'shadow': 1.0 if vr_mode else 0.5, 'color': color, 'scale': scale, 'position': (-260, 10) if vr_mode else (-10, 10), 'text': text })) if not ba.app.main_menu_did_initial_transition: assert self.version.node ba.animate(self.version.node, 'opacity', {2.3: 0, 3.0: 1.0}) # Show the iircade logo on our iircade build. if app.iircade_mode: img = ba.NodeActor( ba.newnode('image', attrs={ 'texture': ba.gettexture('iircadeLogo'), 'attach': 'center', 'scale': (250, 250), 'position': (0, 0), 'tilt_translate': 0.21, 'absolute_scale': True })).autoretain() imgdelay = 0.0 if app.main_menu_did_initial_transition else 1.0 ba.animate(img.node, 'opacity', { imgdelay + 1.5: 0.0, imgdelay + 2.5: 1.0 }) # Throw in test build info. self.beta_info = self.beta_info_2 = None if app.test_build and not (app.demo_mode or app.arcade_mode): pos = ((230, 125) if (app.demo_mode or app.arcade_mode) else (230, 35)) self.beta_info = ba.NodeActor( ba.newnode('text', attrs={ 'v_attach': 'center', 'h_align': 'center', 'color': (1, 1, 1, 1), 'shadow': 0.5, 'flatness': 0.5, 'scale': 1, 'vr_depth': -60, 'position': pos, 'text': ba.Lstr(resource='testBuildText') })) if not ba.app.main_menu_did_initial_transition: assert self.beta_info.node ba.animate(self.beta_info.node, 'opacity', {1.3: 0, 1.8: 1.0}) model = ba.getmodel('thePadLevel') trees_model = ba.getmodel('trees') bottom_model = ba.getmodel('thePadLevelBottom') color_texture = ba.gettexture('thePadLevelColor') trees_texture = ba.gettexture('treesColor') bgtex = ba.gettexture('menuBG') bgmodel = ba.getmodel('thePadBG') # Load these last since most platforms don't use them. vr_bottom_fill_model = ba.getmodel('thePadVRFillBottom') vr_top_fill_model = ba.getmodel('thePadVRFillTop') gnode = self.globalsnode gnode.camera_mode = 'rotate' tint = (1.14, 1.1, 1.0) gnode.tint = tint gnode.ambient_color = (1.06, 1.04, 1.03) gnode.vignette_outer = (0.45, 0.55, 0.54) gnode.vignette_inner = (0.99, 0.98, 0.98) self.bottom = ba.NodeActor( ba.newnode('terrain', attrs={ 'model': bottom_model, 'lighting': False, 'reflection': 'soft', 'reflection_scale': [0.45], 'color_texture': color_texture })) self.vr_bottom_fill = ba.NodeActor( ba.newnode('terrain', attrs={ 'model': vr_bottom_fill_model, 'lighting': False, 'vr_only': True, 'color_texture': color_texture })) self.vr_top_fill = ba.NodeActor( ba.newnode('terrain', attrs={ 'model': vr_top_fill_model, 'vr_only': True, 'lighting': False, 'color_texture': bgtex })) self.terrain = ba.NodeActor( ba.newnode('terrain', attrs={ 'model': model, 'color_texture': color_texture, 'reflection': 'soft', 'reflection_scale': [0.3] })) self.trees = ba.NodeActor( ba.newnode('terrain', attrs={ 'model': trees_model, 'lighting': False, 'reflection': 'char', 'reflection_scale': [0.1], 'color_texture': trees_texture })) self.bgterrain = ba.NodeActor( ba.newnode('terrain', attrs={ 'model': bgmodel, 'color': (0.92, 0.91, 0.9), 'lighting': False, 'background': True, 'color_texture': bgtex })) self._ts = 0.86 self._language: Optional[str] = None self._update_timer = ba.Timer(1.0, self._update, repeat=True) self._update() # Hopefully this won't hitch but lets space these out anyway. _ba.add_clean_frame_callback(ba.WeakCall(self._start_preloads)) random.seed() # On the main menu, also show our news. class News: """Wrangles news display.""" def __init__(self, activity: ba.Activity): self._valid = True self._message_duration = 10.0 self._message_spacing = 2.0 self._text: Optional[ba.NodeActor] = None self._activity = weakref.ref(activity) # If we're signed in, fetch news immediately. # Otherwise wait until we are signed in. self._fetch_timer: Optional[ba.Timer] = ba.Timer( 1.0, ba.WeakCall(self._try_fetching_news), repeat=True) self._try_fetching_news() # We now want to wait until we're signed in before fetching news. def _try_fetching_news(self) -> None: if _ba.get_account_state() == 'signed_in': self._fetch_news() self._fetch_timer = None def _fetch_news(self) -> None: ba.app.main_menu_last_news_fetch_time = time.time() # UPDATE - We now just pull news from MRVs. news = _ba.get_account_misc_read_val('n', None) if news is not None: self._got_news(news) def _change_phrase(self) -> None: from bastd.actor.text import Text # If our news is way out of date, lets re-request it; # otherwise, rotate our phrase. assert ba.app.main_menu_last_news_fetch_time is not None if time.time() - ba.app.main_menu_last_news_fetch_time > 600.0: self._fetch_news() self._text = None else: if self._text is not None: if not self._phrases: for phr in self._used_phrases: self._phrases.insert(0, phr) val = self._phrases.pop() if val == '__ACH__': vrmode = app.vr_mode Text(ba.Lstr(resource='nextAchievementsText'), color=((1, 1, 1, 1) if vrmode else (0.95, 0.9, 1, 0.4)), host_only=True, maxwidth=200, position=(-300, -35), h_align=Text.HAlign.RIGHT, transition=Text.Transition.FADE_IN, scale=0.9 if vrmode else 0.7, flatness=1.0 if vrmode else 0.6, shadow=1.0 if vrmode else 0.5, h_attach=Text.HAttach.CENTER, v_attach=Text.VAttach.TOP, transition_delay=1.0, transition_out_delay=self._message_duration ).autoretain() achs = [ a for a in app.achievements if not a.complete ] if achs: ach = achs.pop( random.randrange(min(4, len(achs)))) ach.create_display( -180, -35, 1.0, outdelay=self._message_duration, style='news') if achs: ach = achs.pop( random.randrange(min(8, len(achs)))) ach.create_display( 180, -35, 1.25, outdelay=self._message_duration, style='news') else: spc = self._message_spacing keys = { spc: 0.0, spc + 1.0: 1.0, spc + self._message_duration - 1.0: 1.0, spc + self._message_duration: 0.0 } assert self._text.node ba.animate(self._text.node, 'opacity', keys) # {k: v # for k, v in list(keys.items())}) self._text.node.text = val def _got_news(self, news: str) -> None: # Run this stuff in the context of our activity since we # need to make nodes and stuff.. should fix the serverget # call so it. activity = self._activity() if activity is None or activity.expired: return with ba.Context(activity): self._phrases: List[str] = [] # Show upcoming achievements in non-vr versions # (currently too hard to read in vr). self._used_phrases = ( ['__ACH__'] if not ba.app.vr_mode else []) + [s for s in news.split('<br>\n') if s != ''] self._phrase_change_timer = ba.Timer( (self._message_duration + self._message_spacing), ba.WeakCall(self._change_phrase), repeat=True) scl = 1.2 if (ba.app.ui.uiscale is ba.UIScale.SMALL or ba.app.vr_mode) else 0.8 color2 = ((1, 1, 1, 1) if ba.app.vr_mode else (0.7, 0.65, 0.75, 1.0)) shadow = (1.0 if ba.app.vr_mode else 0.4) self._text = ba.NodeActor( ba.newnode('text', attrs={ 'v_attach': 'top', 'h_attach': 'center', 'h_align': 'center', 'vr_depth': -20, 'shadow': shadow, 'flatness': 0.8, 'v_align': 'top', 'color': color2, 'scale': scl, 'maxwidth': 900.0 / scl, 'position': (0, -10) })) self._change_phrase() if not (app.demo_mode or app.arcade_mode) and not app.toolbar_test: self._news = News(self) # Bring up the last place we were, or start at the main menu otherwise. with ba.Context('ui'): from bastd.ui import specialoffer if bool(False): uicontroller = ba.app.ui.controller assert uicontroller is not None uicontroller.show_main_menu() else: main_menu_location = ba.app.ui.get_main_menu_location() # When coming back from a kiosk-mode game, jump to # the kiosk start screen. if ba.app.demo_mode or ba.app.arcade_mode: # pylint: disable=cyclic-import from bastd.ui.kiosk import KioskWindow ba.app.ui.set_main_menu_window( KioskWindow().get_root_widget()) # ..or in normal cases go back to the main menu else: if main_menu_location == 'Gather': # pylint: disable=cyclic-import from bastd.ui.gather import GatherWindow ba.app.ui.set_main_menu_window( GatherWindow(transition=None).get_root_widget()) elif main_menu_location == 'Watch': # pylint: disable=cyclic-import from bastd.ui.watch import WatchWindow ba.app.ui.set_main_menu_window( WatchWindow(transition=None).get_root_widget()) elif main_menu_location == 'Team Game Select': # pylint: disable=cyclic-import from bastd.ui.playlist.browser import ( PlaylistBrowserWindow) ba.app.ui.set_main_menu_window( PlaylistBrowserWindow( sessiontype=ba.DualTeamSession, transition=None).get_root_widget()) elif main_menu_location == 'Free-for-All Game Select': # pylint: disable=cyclic-import from bastd.ui.playlist.browser import ( PlaylistBrowserWindow) ba.app.ui.set_main_menu_window( PlaylistBrowserWindow( sessiontype=ba.FreeForAllSession, transition=None).get_root_widget()) elif main_menu_location == 'Coop Select': # pylint: disable=cyclic-import from bastd.ui.coop.browser import CoopBrowserWindow ba.app.ui.set_main_menu_window( CoopBrowserWindow( transition=None).get_root_widget()) else: # pylint: disable=cyclic-import from bastd.ui.mainmenu import MainMenuWindow ba.app.ui.set_main_menu_window( MainMenuWindow(transition=None).get_root_widget()) # attempt to show any pending offers immediately. # If that doesn't work, try again in a few seconds # (we may not have heard back from the server) # ..if that doesn't work they'll just have to wait # until the next opportunity. if not specialoffer.show_offer(): def try_again() -> None: if not specialoffer.show_offer(): # Try one last time.. ba.timer(2.0, specialoffer.show_offer, timetype=ba.TimeType.REAL) ba.timer(2.0, try_again, timetype=ba.TimeType.REAL) ba.app.main_menu_did_initial_transition = True
def __init__(self, scoreboard: Scoreboard, team: ba.Team, do_cover: bool, scale: float, label: Optional[ba.Lstr], flash_length: float): # pylint: disable=too-many-statements self._scoreboard = weakref.ref(scoreboard) self._do_cover = do_cover self._scale = scale self._flash_length = flash_length self._width = 140.0 * self._scale self._height = 32.0 * self._scale self._bar_width = 2.0 * self._scale self._bar_height = 32.0 * self._scale self._bar_tex = self._backing_tex = ba.gettexture('bar') self._cover_tex = ba.gettexture('uiAtlas') self._model = ba.getmodel('meterTransparent') self._pos: Optional[Sequence[float]] = None self._flash_timer: Optional[ba.Timer] = None self._flash_counter: Optional[int] = None self._flash_colors: Optional[bool] = None self._score: Optional[float] = None safe_team_color = ba.safecolor(team.color, target_intensity=1.0) # FIXME: Should not do things conditionally for vr-mode, as there may # be non-vr clients connected which will also get these value. vrmode = ba.app.vr_mode if self._do_cover: if vrmode: self._backing_color = [0.1 + c * 0.1 for c in safe_team_color] else: self._backing_color = [ 0.05 + c * 0.17 for c in safe_team_color ] else: self._backing_color = [0.05 + c * 0.1 for c in safe_team_color] opacity = (0.8 if vrmode else 0.8) if self._do_cover else 0.5 self._backing = ba.NodeActor( ba.newnode('image', attrs={ 'scale': (self._width, self._height), 'opacity': opacity, 'color': self._backing_color, 'vr_depth': -3, 'attach': 'topLeft', 'texture': self._backing_tex })) self._barcolor = safe_team_color self._bar = ba.NodeActor( ba.newnode('image', attrs={ 'opacity': 0.7, 'color': self._barcolor, 'attach': 'topLeft', 'texture': self._bar_tex })) self._bar_scale = ba.newnode('combine', owner=self._bar.node, attrs={ 'size': 2, 'input0': self._bar_width, 'input1': self._bar_height }) assert self._bar.node self._bar_scale.connectattr('output', self._bar.node, 'scale') self._bar_position = ba.newnode('combine', owner=self._bar.node, attrs={ 'size': 2, 'input0': 0, 'input1': 0 }) self._bar_position.connectattr('output', self._bar.node, 'position') self._cover_color = safe_team_color if self._do_cover: self._cover = ba.NodeActor( ba.newnode('image', attrs={ 'scale': (self._width * 1.15, self._height * 1.6), 'opacity': 1.0, 'color': self._cover_color, 'vr_depth': 2, 'attach': 'topLeft', 'texture': self._cover_tex, 'model_transparent': self._model })) clr = safe_team_color maxwidth = 130.0 * (1.0 - scoreboard.score_split) flatness = ((1.0 if vrmode else 0.5) if self._do_cover else 1.0) self._score_text = ba.NodeActor( ba.newnode('text', attrs={ 'h_attach': 'left', 'v_attach': 'top', 'h_align': 'right', 'v_align': 'center', 'maxwidth': maxwidth, 'vr_depth': 2, 'scale': self._scale * 0.9, 'text': '', 'shadow': 1.0 if vrmode else 0.5, 'flatness': flatness, 'color': clr })) clr = safe_team_color team_name_label: Union[str, ba.Lstr] if label is not None: team_name_label = label else: team_name_label = team.name # We do our own clipping here; should probably try to tap into some # existing functionality. if isinstance(team_name_label, ba.Lstr): # Hmmm; if the team-name is a non-translatable value lets go # ahead and clip it otherwise we leave it as-is so # translation can occur.. if team_name_label.is_flat_value(): val = team_name_label.evaluate() if len(val) > 10: team_name_label = ba.Lstr(value=val[:10] + '...') else: if len(team_name_label) > 10: team_name_label = team_name_label[:10] + '...' team_name_label = ba.Lstr(value=team_name_label) flatness = ((1.0 if vrmode else 0.5) if self._do_cover else 1.0) self._name_text = ba.NodeActor( ba.newnode('text', attrs={ 'h_attach': 'left', 'v_attach': 'top', 'h_align': 'left', 'v_align': 'center', 'vr_depth': 2, 'scale': self._scale * 0.9, 'shadow': 1.0 if vrmode else 0.5, 'flatness': flatness, 'maxwidth': 130 * scoreboard.score_split, 'text': team_name_label, 'color': clr + (1.0, ) }))
def _start_next_wave(self) -> None: # FIXME: Need to split this up. # pylint: disable=too-many-locals # pylint: disable=too-many-branches # pylint: disable=too-many-statements self.show_zoom_message(ba.Lstr(value='${A} ${B}', subs=[('${A}', ba.Lstr(resource='waveText')), ('${B}', str(self._wavenum))]), scale=1.0, duration=1.0, trail=True) ba.timer(0.4, ba.Call(ba.playsound, self._new_wave_sound)) t_sec = 0.0 base_delay = 0.5 delay = 0.0 bot_types: List[Union[Spawn, Spacing, None]] = [] if self._preset in {Preset.ENDLESS, Preset.ENDLESS_TOURNAMENT}: level = self._wavenum target_points = (level + 1) * 8.0 group_count = random.randint(1, 3) entries: List[Union[Spawn, Spacing, None]] = [] spaz_types: List[Tuple[Type[SpazBot], float]] = [] if level < 6: spaz_types += [(BomberBot, 5.0)] if level < 10: spaz_types += [(BrawlerBot, 5.0)] if level < 15: spaz_types += [(TriggerBot, 6.0)] if level > 5: spaz_types += [(TriggerBotPro, 7.5)] * (1 + (level - 5) // 7) if level > 2: spaz_types += [(BomberBotProShielded, 8.0) ] * (1 + (level - 2) // 6) if level > 6: spaz_types += [(TriggerBotProShielded, 12.0) ] * (1 + (level - 6) // 5) if level > 1: spaz_types += ([(ChargerBot, 10.0)] * (1 + (level - 1) // 4)) if level > 7: spaz_types += [(ChargerBotProShielded, 15.0) ] * (1 + (level - 7) // 3) # Bot type, their effect on target points. defender_types: List[Tuple[Type[SpazBot], float]] = [ (BomberBot, 0.9), (BrawlerBot, 0.9), (TriggerBot, 0.85), ] if level > 2: defender_types += [(ChargerBot, 0.75)] if level > 4: defender_types += ([(StickyBot, 0.7)] * (1 + (level - 5) // 6)) if level > 6: defender_types += ([(ExplodeyBot, 0.7)] * (1 + (level - 5) // 5)) if level > 8: defender_types += ([(BrawlerBotProShielded, 0.65)] * (1 + (level - 5) // 4)) if level > 10: defender_types += ([(TriggerBotProShielded, 0.6)] * (1 + (level - 6) // 3)) for group in range(group_count): this_target_point_s = target_points / group_count # Adding spacing makes things slightly harder. rval = random.random() if rval < 0.07: spacing = 1.5 this_target_point_s *= 0.85 elif rval < 0.15: spacing = 1.0 this_target_point_s *= 0.9 else: spacing = 0.0 path = random.randint(1, 3) # Don't allow hard paths on early levels. if level < 3: if path == 1: path = 3 # Easy path. if path == 3: pass # Harder path. elif path == 2: this_target_point_s *= 0.8 # Even harder path. elif path == 1: this_target_point_s *= 0.7 # Looping forward. elif path == 4: this_target_point_s *= 0.7 # Looping backward. elif path == 5: this_target_point_s *= 0.7 # Random. elif path == 6: this_target_point_s *= 0.7 def _add_defender(defender_type: Tuple[Type[SpazBot], float], pnt: Point) -> Tuple[float, Spawn]: # This is ok because we call it immediately. # pylint: disable=cell-var-from-loop return this_target_point_s * defender_type[1], Spawn( defender_type[0], point=pnt) # Add defenders. defender_type1 = defender_types[random.randrange( len(defender_types))] defender_type2 = defender_types[random.randrange( len(defender_types))] defender1 = defender2 = None if ((group == 0) or (group == 1 and level > 3) or (group == 2 and level > 5)): if random.random() < min(0.75, (level - 1) * 0.11): this_target_point_s, defender1 = _add_defender( defender_type1, Point.BOTTOM_LEFT) if random.random() < min(0.75, (level - 1) * 0.04): this_target_point_s, defender2 = _add_defender( defender_type2, Point.BOTTOM_RIGHT) spaz_type = spaz_types[random.randrange(len(spaz_types))] member_count = max( 1, int(round(this_target_point_s / spaz_type[1]))) for i, _member in enumerate(range(member_count)): if path == 4: this_path = i % 3 # Looping forward. elif path == 5: this_path = 3 - (i % 3) # Looping backward. elif path == 6: this_path = random.randint(1, 3) # Random. else: this_path = path entries.append(Spawn(spaz_type[0], path=this_path)) if spacing != 0.0: entries.append(Spacing(duration=spacing)) if defender1 is not None: entries.append(defender1) if defender2 is not None: entries.append(defender2) # Some spacing between groups. rval = random.random() if rval < 0.1: spacing = 5.0 elif rval < 0.5: spacing = 1.0 else: spacing = 1.0 entries.append(Spacing(duration=spacing)) wave = Wave(entries=entries) else: assert self._waves is not None wave = self._waves[self._wavenum - 1] bot_types += wave.entries self._time_bonus_mult = 1.0 this_flawless_bonus = 0 non_runner_spawn_time = 1.0 for info in bot_types: if info is None: continue if isinstance(info, Spacing): t_sec += info.duration continue bot_type = info.type path = info.path self._time_bonus_mult += bot_type.points_mult * 0.02 this_flawless_bonus += bot_type.points_mult * 5 # If its got a position, use that. if info.point is not None: point = info.point else: point = Point.START # Space our our slower bots. delay = base_delay delay /= self._get_bot_speed(bot_type) t_sec += delay * 0.5 tcall = ba.Call( self.add_bot_at_point, point, bot_type, path, 0.1 if point is Point.START else non_runner_spawn_time) ba.timer(t_sec, tcall) t_sec += delay * 0.5 # We can end the wave after all the spawning happens. ba.timer(t_sec - delay * 0.5 + non_runner_spawn_time + 0.01, self._set_can_end_wave) # Reset our time bonus. # In this game we use a constant time bonus so it erodes away in # roughly the same time (since the time limit a wave can take is # relatively constant) ..we then post-multiply a modifier to adjust # points. self._time_bonus = 150 self._flawless_bonus = this_flawless_bonus assert self._time_bonus_mult is not None txtval = ba.Lstr( value='${A}: ${B}', subs=[('${A}', ba.Lstr(resource='timeBonusText')), ('${B}', str(int(self._time_bonus * self._time_bonus_mult))) ]) self._time_bonus_text = ba.NodeActor( ba.newnode('text', attrs={ 'v_attach': 'top', 'h_attach': 'center', 'h_align': 'center', 'color': (1, 1, 0.0, 1), 'shadow': 1.0, 'vr_depth': -30, 'flatness': 1.0, 'position': (0, -60), 'scale': 0.8, 'text': txtval })) ba.timer(t_sec, self._start_time_bonus_timer) # Keep track of when this wave finishes emerging. We wanna stop # dropping land-mines powerups at some point (otherwise a crafty # player could fill the whole map with them) self._last_wave_end_time = ba.time() + t_sec totalwaves = str(len(self._waves)) if self._waves is not None else '??' txtval = ba.Lstr(value='${A} ${B}', subs=[('${A}', ba.Lstr(resource='waveText')), ('${B}', str(self._wavenum) + ('' if self._preset in { Preset.ENDLESS, Preset.ENDLESS_TOURNAMENT } else f'/{totalwaves}'))]) self._wave_text = ba.NodeActor( ba.newnode('text', attrs={ 'v_attach': 'top', 'h_attach': 'center', 'h_align': 'center', 'vr_depth': -10, 'color': (1, 1, 1, 1), 'shadow': 1.0, 'flatness': 1.0, 'position': (0, -40), 'scale': 1.3, 'text': txtval }))
def __init__(self, player: ba.Player, respawn_time: float): """Instantiate with a ba.Player and respawn_time (in seconds).""" self._visible = True on_right, offs_extra, respawn_icons = self._get_context(player) # Cache our mask tex on the team for easy access. mask_tex = player.team.gamedata.get('_spaz_respawn_icons_mask_tex') if mask_tex is None: mask_tex = ba.gettexture('characterIconMask') player.team.gamedata['_spaz_respawn_icons_mask_tex'] = mask_tex assert isinstance(mask_tex, ba.Texture) # Now find the first unused slot and use that. index = 0 while (index in respawn_icons and respawn_icons[index]() is not None and respawn_icons[index]().visible): index += 1 respawn_icons[index] = weakref.ref(self) offs = offs_extra + index * -53 icon = player.get_icon() texture = icon['texture'] h_offs = -10 ipos = (-40 - h_offs if on_right else 40 + h_offs, -180 + offs) self._image: Optional[ba.NodeActor] = ba.NodeActor( ba.newnode('image', attrs={ 'texture': texture, 'tint_texture': icon['tint_texture'], 'tint_color': icon['tint_color'], 'tint2_color': icon['tint2_color'], 'mask_texture': mask_tex, 'position': ipos, 'scale': (32, 32), 'opacity': 1.0, 'absolute_scale': True, 'attach': 'topRight' if on_right else 'topLeft' })) assert self._image.node ba.animate(self._image.node, 'opacity', {0.0: 0, 0.2: 0.7}) npos = (-40 - h_offs if on_right else 40 + h_offs, -205 + 49 + offs) self._name: Optional[ba.NodeActor] = ba.NodeActor( ba.newnode('text', attrs={ 'v_attach': 'top', 'h_attach': 'right' if on_right else 'left', 'text': ba.Lstr(value=player.getname()), 'maxwidth': 100, 'h_align': 'center', 'v_align': 'center', 'shadow': 1.0, 'flatness': 1.0, 'color': ba.safecolor(icon['tint_color']), 'scale': 0.5, 'position': npos })) assert self._name.node ba.animate(self._name.node, 'scale', {0: 0, 0.1: 0.5}) tpos = (-60 - h_offs if on_right else 60 + h_offs, -192 + offs) self._text: Optional[ba.NodeActor] = ba.NodeActor( ba.newnode('text', attrs={ 'position': tpos, 'h_attach': 'right' if on_right else 'left', 'h_align': 'right' if on_right else 'left', 'scale': 0.9, 'shadow': 0.5, 'flatness': 0.5, 'v_attach': 'top', 'color': ba.safecolor(icon['tint_color']), 'text': '' })) assert self._text.node ba.animate(self._text.node, 'scale', {0: 0, 0.1: 0.9}) self._respawn_time = ba.time() + respawn_time self._update() self._timer: Optional[ba.Timer] = ba.Timer(1.0, ba.WeakCall(self._update), repeat=True)
def _set_chosen_one_player(self, player: Optional[ba.Player]) -> None: try: for p_other in self.players: p_other.gamedata['chosen_light'] = None ba.playsound(self._swipsound) if not player: assert self._flag_spawn_pos is not None self._flag = flag.Flag(color=(1, 0.9, 0.2), position=self._flag_spawn_pos, touchable=False) self._chosen_one_player = None # Create a light to highlight the flag; # this will go away when the flag dies. ba.newnode('light', owner=self._flag.node, attrs={ 'position': self._flag_spawn_pos, 'intensity': 0.6, 'height_attenuated': False, 'volume_intensity_scale': 0.1, 'radius': 0.1, 'color': (1.2, 1.2, 0.4) }) # Also an extra momentary flash. self._flash_flag_spawn() else: if player.actor is not None: self._flag = None self._chosen_one_player = player if player.actor: if self.settings['Chosen One Gets Shield']: player.actor.handlemessage( ba.PowerupMessage('shield')) if self.settings['Chosen One Gets Gloves']: player.actor.handlemessage( ba.PowerupMessage('punch')) # Use a color that's partway between their team color # and white. color = [ 0.3 + c * 0.7 for c in ba.normalized_color(player.team.color) ] light = player.gamedata['chosen_light'] = ba.NodeActor( ba.newnode('light', attrs={ "intensity": 0.6, "height_attenuated": False, "volume_intensity_scale": 0.1, "radius": 0.13, "color": color })) assert light.node ba.animate(light.node, 'intensity', { 0: 1.0, 0.2: 0.4, 0.4: 1.0 }, loop=True) assert isinstance(player.actor, playerspaz.PlayerSpaz) player.actor.node.connectattr('position', light.node, 'position') except Exception: ba.print_exception('EXC in _set_chosen_one_player')
def on_begin(self) -> None: super().on_begin() player_count = len(self.players) hard = self._preset not in {Preset.PRO_EASY, Preset.UBER_EASY} if self._preset in {Preset.PRO, Preset.PRO_EASY, Preset.TOURNAMENT}: self._exclude_powerups = ['curse'] self._have_tnt = True self._waves = [ Wave(entries=[ Spawn(BomberBot, path=3 if hard else 2), Spawn(BomberBot, path=2), Spawn(BomberBot, path=2) if hard else None, Spawn(BomberBot, path=2) if player_count > 1 else None, Spawn(BomberBot, path=1) if hard else None, Spawn(BomberBot, path=1) if player_count > 2 else None, Spawn(BomberBot, path=1) if player_count > 3 else None, ]), Wave(entries=[ Spawn(BomberBot, path=1) if hard else None, Spawn(BomberBot, path=2) if hard else None, Spawn(BomberBot, path=2), Spawn(BomberBot, path=2), Spawn(BomberBot, path=2) if player_count > 3 else None, Spawn(BrawlerBot, path=3), Spawn(BrawlerBot, path=3), Spawn(BrawlerBot, path=3) if hard else None, Spawn(BrawlerBot, path=3) if player_count > 1 else None, Spawn(BrawlerBot, path=3) if player_count > 2 else None, ]), Wave(entries=[ Spawn(ChargerBot, path=2) if hard else None, Spawn(ChargerBot, path=2) if player_count > 2 else None, Spawn(TriggerBot, path=2), Spawn(TriggerBot, path=2) if player_count > 1 else None, Spacing(duration=3.0), Spawn(BomberBot, path=2) if hard else None, Spawn(BomberBot, path=2) if hard else None, Spawn(BomberBot, path=2), Spawn(BomberBot, path=3) if hard else None, Spawn(BomberBot, path=3), Spawn(BomberBot, path=3), Spawn(BomberBot, path=3) if player_count > 3 else None, ]), Wave(entries=[ Spawn(TriggerBot, path=1) if hard else None, Spacing(duration=1.0) if hard else None, Spawn(TriggerBot, path=2), Spacing(duration=1.0), Spawn(TriggerBot, path=3), Spacing(duration=1.0), Spawn(TriggerBot, path=1) if hard else None, Spacing(duration=1.0) if hard else None, Spawn(TriggerBot, path=2), Spacing(duration=1.0), Spawn(TriggerBot, path=3), Spacing(duration=1.0), Spawn(TriggerBot, path=1) if ( player_count > 1 and hard) else None, Spacing(duration=1.0), Spawn(TriggerBot, path=2) if player_count > 2 else None, Spacing(duration=1.0), Spawn(TriggerBot, path=3) if player_count > 3 else None, Spacing(duration=1.0), ]), Wave(entries=[ Spawn(ChargerBotProShielded if hard else ChargerBot, path=1), Spawn(BrawlerBot, path=2) if hard else None, Spawn(BrawlerBot, path=2), Spawn(BrawlerBot, path=2), Spawn(BrawlerBot, path=3) if hard else None, Spawn(BrawlerBot, path=3), Spawn(BrawlerBot, path=3), Spawn(BrawlerBot, path=3) if player_count > 1 else None, Spawn(BrawlerBot, path=3) if player_count > 2 else None, Spawn(BrawlerBot, path=3) if player_count > 3 else None, ]), Wave(entries=[ Spawn(BomberBotProShielded, path=3), Spacing(duration=1.5), Spawn(BomberBotProShielded, path=2), Spacing(duration=1.5), Spawn(BomberBotProShielded, path=1) if hard else None, Spacing(duration=1.0) if hard else None, Spawn(BomberBotProShielded, path=3), Spacing(duration=1.5), Spawn(BomberBotProShielded, path=2), Spacing(duration=1.5), Spawn(BomberBotProShielded, path=1) if hard else None, Spacing(duration=1.5) if hard else None, Spawn(BomberBotProShielded, path=3 ) if player_count > 1 else None, Spacing(duration=1.5), Spawn(BomberBotProShielded, path=2 ) if player_count > 2 else None, Spacing(duration=1.5), Spawn(BomberBotProShielded, path=1 ) if player_count > 3 else None, ]), ] elif self._preset in { Preset.UBER_EASY, Preset.UBER, Preset.TOURNAMENT_UBER }: self._exclude_powerups = [] self._have_tnt = True self._waves = [ Wave(entries=[ Spawn(TriggerBot, path=1) if hard else None, Spawn(TriggerBot, path=2), Spawn(TriggerBot, path=2), Spawn(TriggerBot, path=3), Spawn(BrawlerBotPro if hard else BrawlerBot, point=Point.BOTTOM_LEFT), Spawn(BrawlerBotPro, point=Point.BOTTOM_RIGHT ) if player_count > 2 else None, ]), Wave(entries=[ Spawn(ChargerBot, path=2), Spawn(ChargerBot, path=3), Spawn(ChargerBot, path=1) if hard else None, Spawn(ChargerBot, path=2), Spawn(ChargerBot, path=3), Spawn(ChargerBot, path=1) if player_count > 2 else None, ]), Wave(entries=[ Spawn(BomberBotProShielded, path=1) if hard else None, Spawn(BomberBotProShielded, path=2), Spawn(BomberBotProShielded, path=2), Spawn(BomberBotProShielded, path=3), Spawn(BomberBotProShielded, path=3), Spawn(ChargerBot, point=Point.BOTTOM_RIGHT), Spawn(ChargerBot, point=Point.BOTTOM_LEFT ) if player_count > 2 else None, ]), Wave(entries=[ Spawn(TriggerBotPro, path=1) if hard else None, Spawn(TriggerBotPro, path=1 if hard else 2), Spawn(TriggerBotPro, path=1 if hard else 2), Spawn(TriggerBotPro, path=1 if hard else 2), Spawn(TriggerBotPro, path=1 if hard else 2), Spawn(TriggerBotPro, path=1 if hard else 2), Spawn(TriggerBotPro, path=1 if hard else 2 ) if player_count > 1 else None, Spawn(TriggerBotPro, path=1 if hard else 2 ) if player_count > 3 else None, ]), Wave(entries=[ Spawn(TriggerBotProShielded if hard else TriggerBotPro, point=Point.BOTTOM_LEFT), Spawn(TriggerBotProShielded, point=Point.BOTTOM_RIGHT ) if hard else None, Spawn(TriggerBotProShielded, point=Point.BOTTOM_RIGHT ) if player_count > 2 else None, Spawn(BomberBot, path=3), Spawn(BomberBot, path=3), Spacing(duration=5.0), Spawn(BrawlerBot, path=2), Spawn(BrawlerBot, path=2), Spacing(duration=5.0), Spawn(TriggerBot, path=1) if hard else None, Spawn(TriggerBot, path=1) if hard else None, ]), Wave(entries=[ Spawn(BomberBotProShielded, path=2), Spawn(BomberBotProShielded, path=2) if hard else None, Spawn(StickyBot, point=Point.BOTTOM_RIGHT), Spawn(BomberBotProShielded, path=2), Spawn(BomberBotProShielded, path=2), Spawn(StickyBot, point=Point.BOTTOM_RIGHT ) if player_count > 2 else None, Spawn(BomberBotProShielded, path=2), Spawn(ExplodeyBot, point=Point.BOTTOM_LEFT), Spawn(BomberBotProShielded, path=2), Spawn(BomberBotProShielded, path=2 ) if player_count > 1 else None, Spacing(duration=5.0), Spawn(StickyBot, point=Point.BOTTOM_LEFT), Spacing(duration=2.0), Spawn(ExplodeyBot, point=Point.BOTTOM_RIGHT), ]), ] elif self._preset in {Preset.ENDLESS, Preset.ENDLESS_TOURNAMENT}: self._exclude_powerups = [] self._have_tnt = True # Spit out a few powerups and start dropping more shortly. self._drop_powerups(standard_points=True) ba.timer(4.0, self._start_powerup_drops) self.setup_low_life_warning_sound() self._update_scores() # Our TNT spawner (if applicable). if self._have_tnt: self._tntspawner = TNTSpawner(position=self._tntspawnpos) # Make sure to stay out of the way of menu/party buttons in the corner. interface_type = ba.app.interface_type l_offs = (-80 if interface_type == 'small' else -40 if interface_type == 'medium' else 0) self._lives_bg = ba.NodeActor( ba.newnode('image', attrs={ 'texture': self._heart_tex, 'model_opaque': self._heart_model_opaque, 'model_transparent': self._heart_model_transparent, 'attach': 'topRight', 'scale': (90, 90), 'position': (-110 + l_offs, -50), 'color': (1, 0.2, 0.2) })) # FIXME; should not set things based on vr mode. # (won't look right to non-vr connected clients, etc) vrmode = ba.app.vr_mode self._lives_text = ba.NodeActor( ba.newnode('text', attrs={ 'v_attach': 'top', 'h_attach': 'right', 'h_align': 'center', 'color': (1, 1, 1, 1) if vrmode else (0.8, 0.8, 0.8, 1.0), 'flatness': 1.0 if vrmode else 0.5, 'shadow': 1.0 if vrmode else 0.5, 'vr_depth': 10, 'position': (-113 + l_offs, -69), 'scale': 1.3, 'text': str(self._lives) })) ba.timer(2.0, self._start_updating_waves)
def _set_chosen_one_player(self, player: Optional[Player]) -> None: existing = self._get_chosen_one_player() if existing: existing.chosen_light = None ba.playsound(self._swipsound) if not player: assert self._flag_spawn_pos is not None self._flag = Flag(color=(1, 0.9, 0.2), position=self._flag_spawn_pos, touchable=False) self._chosen_one_player = None # Create a light to highlight the flag; # this will go away when the flag dies. ba.newnode('light', owner=self._flag.node, attrs={ 'position': self._flag_spawn_pos, 'intensity': 0.6, 'height_attenuated': False, 'volume_intensity_scale': 0.1, 'radius': 0.1, 'color': (1.2, 1.2, 0.4) }) # Also an extra momentary flash. self._flash_flag_spawn() else: if player.actor: self._flag = None self._chosen_one_player = player if self._chosen_one_gets_shield: player.actor.handlemessage(ba.PowerupMessage('shield')) if self._chosen_one_gets_gloves: player.actor.handlemessage(ba.PowerupMessage('punch')) # Use a color that's partway between their team color # and white. color = [ 0.3 + c * 0.7 for c in ba.normalized_color(player.team.color) ] light = player.chosen_light = ba.NodeActor( ba.newnode('light', attrs={ 'intensity': 0.6, 'height_attenuated': False, 'volume_intensity_scale': 0.1, 'radius': 0.13, 'color': color })) assert light.node ba.animate(light.node, 'intensity', { 0: 1.0, 0.2: 0.4, 0.4: 1.0 }, loop=True) assert isinstance(player.actor, PlayerSpaz) player.actor.node.connectattr('position', light.node, 'position')
def on_begin(self) -> None: super().on_begin() player_count = len(self.players) hard = self._preset not in ['pro_easy', 'uber_easy'] if self._preset in ['pro', 'pro_easy', 'tournament']: self._exclude_powerups = ['curse'] self._have_tnt = True self._waves = [ {'entries': [ {'type': BomberBot, 'path': 3 if hard else 2}, {'type': BomberBot, 'path': 2}, {'type': BomberBot, 'path': 2} if hard else None, {'type': BomberBot, 'path': 2} if player_count > 1 else None, {'type': BomberBot, 'path': 1} if hard else None, {'type': BomberBot, 'path': 1} if player_count > 2 else None, {'type': BomberBot, 'path': 1} if player_count > 3 else None, ]}, {'entries': [ {'type': BomberBot, 'path': 1} if hard else None, {'type': BomberBot, 'path': 2} if hard else None, {'type': BomberBot, 'path': 2}, {'type': BomberBot, 'path': 2}, {'type': BomberBot, 'path': 2} if player_count > 3 else None, {'type': BrawlerBot, 'path': 3}, {'type': BrawlerBot, 'path': 3}, {'type': BrawlerBot, 'path': 3} if hard else None, {'type': BrawlerBot, 'path': 3} if player_count > 1 else None, {'type': BrawlerBot, 'path': 3} if player_count > 2 else None, ]}, {'entries': [ {'type': ChargerBot, 'path': 2} if hard else None, {'type': ChargerBot, 'path': 2} if player_count > 2 else None, {'type': TriggerBot, 'path': 2}, {'type': TriggerBot, 'path': 2} if player_count > 1 else None, {'type': 'spacing', 'duration': 3.0}, {'type': BomberBot, 'path': 2} if hard else None, {'type': BomberBot, 'path': 2} if hard else None, {'type': BomberBot, 'path': 2}, {'type': BomberBot, 'path': 3} if hard else None, {'type': BomberBot, 'path': 3}, {'type': BomberBot, 'path': 3}, {'type': BomberBot, 'path': 3} if player_count > 3 else None, ]}, {'entries': [ {'type': TriggerBot, 'path': 1} if hard else None, {'type': 'spacing', 'duration': 1.0} if hard else None, {'type': TriggerBot, 'path': 2}, {'type': 'spacing', 'duration': 1.0}, {'type': TriggerBot, 'path': 3}, {'type': 'spacing', 'duration': 1.0}, {'type': TriggerBot, 'path': 1} if hard else None, {'type': 'spacing', 'duration': 1.0} if hard else None, {'type': TriggerBot, 'path': 2}, {'type': 'spacing', 'duration': 1.0}, {'type': TriggerBot, 'path': 3}, {'type': 'spacing', 'duration': 1.0}, {'type': TriggerBot, 'path': 1} if (player_count > 1 and hard) else None, {'type': 'spacing', 'duration': 1.0}, {'type': TriggerBot, 'path': 2} if player_count > 2 else None, {'type': 'spacing', 'duration': 1.0}, {'type': TriggerBot, 'path': 3} if player_count > 3 else None, {'type': 'spacing', 'duration': 1.0}, ]}, {'entries': [ {'type': ChargerBotProShielded if hard else ChargerBot, 'path': 1}, {'type': BrawlerBot, 'path': 2} if hard else None, {'type': BrawlerBot, 'path': 2}, {'type': BrawlerBot, 'path': 2}, {'type': BrawlerBot, 'path': 3} if hard else None, {'type': BrawlerBot, 'path': 3}, {'type': BrawlerBot, 'path': 3}, {'type': BrawlerBot, 'path': 3} if player_count > 1 else None, {'type': BrawlerBot, 'path': 3} if player_count > 2 else None, {'type': BrawlerBot, 'path': 3} if player_count > 3 else None, ]}, {'entries': [ {'type': BomberBotProShielded, 'path': 3}, {'type': 'spacing', 'duration': 1.5}, {'type': BomberBotProShielded, 'path': 2}, {'type': 'spacing', 'duration': 1.5}, {'type': BomberBotProShielded, 'path': 1} if hard else None, {'type': 'spacing', 'duration': 1.0} if hard else None, {'type': BomberBotProShielded, 'path': 3}, {'type': 'spacing', 'duration': 1.5}, {'type': BomberBotProShielded, 'path': 2}, {'type': 'spacing', 'duration': 1.5}, {'type': BomberBotProShielded, 'path': 1} if hard else None, {'type': 'spacing', 'duration': 1.5} if hard else None, {'type': BomberBotProShielded, 'path': 3} if player_count > 1 else None, {'type': 'spacing', 'duration': 1.5}, {'type': BomberBotProShielded, 'path': 2} if player_count > 2 else None, {'type': 'spacing', 'duration': 1.5}, {'type': BomberBotProShielded, 'path': 1} if player_count > 3 else None, ]}, ] # yapf: disable elif self._preset in ['uber_easy', 'uber', 'tournament_uber']: self._exclude_powerups = [] self._have_tnt = True self._waves = [ {'entries': [ {'type': TriggerBot, 'path': 1} if hard else None, {'type': TriggerBot, 'path': 2}, {'type': TriggerBot, 'path': 2}, {'type': TriggerBot, 'path': 3}, {'type': BrawlerBotPro if hard else BrawlerBot, 'point': 'bottom_left'}, {'type': BrawlerBotPro, 'point': 'bottom_right'} if player_count > 2 else None, ]}, {'entries': [ {'type': ChargerBot, 'path': 2}, {'type': ChargerBot, 'path': 3}, {'type': ChargerBot, 'path': 1} if hard else None, {'type': ChargerBot, 'path': 2}, {'type': ChargerBot, 'path': 3}, {'type': ChargerBot, 'path': 1} if player_count > 2 else None, ]}, {'entries': [ {'type': BomberBotProShielded, 'path': 1} if hard else None, {'type': BomberBotProShielded, 'path': 2}, {'type': BomberBotProShielded, 'path': 2}, {'type': BomberBotProShielded, 'path': 3}, {'type': BomberBotProShielded, 'path': 3}, {'type': ChargerBot, 'point': 'bottom_right'}, {'type': ChargerBot, 'point': 'bottom_left'} if player_count > 2 else None, ]}, {'entries': [ {'type': TriggerBotPro, 'path': 1} if hard else None, {'type': TriggerBotPro, 'path': 1 if hard else 2}, {'type': TriggerBotPro, 'path': 1 if hard else 2}, {'type': TriggerBotPro, 'path': 1 if hard else 2}, {'type': TriggerBotPro, 'path': 1 if hard else 2}, {'type': TriggerBotPro, 'path': 1 if hard else 2}, {'type': TriggerBotPro, 'path': 1 if hard else 2} if player_count > 1 else None, {'type': TriggerBotPro, 'path': 1 if hard else 2} if player_count > 3 else None, ]}, {'entries': [ {'type': TriggerBotProShielded if hard else TriggerBotPro, 'point': 'bottom_left'}, {'type': TriggerBotProShielded, 'point': 'bottom_right'} if hard else None, {'type': TriggerBotProShielded, 'point': 'bottom_right'} if player_count > 2 else None, {'type': BomberBot, 'path': 3}, {'type': BomberBot, 'path': 3}, {'type': 'spacing', 'duration': 5.0}, {'type': BrawlerBot, 'path': 2}, {'type': BrawlerBot, 'path': 2}, {'type': 'spacing', 'duration': 5.0}, {'type': TriggerBot, 'path': 1} if hard else None, {'type': TriggerBot, 'path': 1} if hard else None, ]}, {'entries': [ {'type': BomberBotProShielded, 'path': 2}, {'type': BomberBotProShielded, 'path': 2} if hard else None, {'type': StickyBot, 'point': 'bottom_right'}, {'type': BomberBotProShielded, 'path': 2}, {'type': BomberBotProShielded, 'path': 2}, {'type': StickyBot, 'point': 'bottom_right'} if player_count > 2 else None, {'type': BomberBotProShielded, 'path': 2}, {'type': ExplodeyBot, 'point': 'bottom_left'}, {'type': BomberBotProShielded, 'path': 2}, {'type': BomberBotProShielded, 'path': 2} if player_count > 1 else None, {'type': 'spacing', 'duration': 5.0}, {'type': StickyBot, 'point': 'bottom_left'}, {'type': 'spacing', 'duration': 2.0}, {'type': ExplodeyBot, 'point': 'bottom_right'}, ]}, ] # yapf: disable elif self._preset in ['endless', 'endless_tournament']: self._exclude_powerups = [] self._have_tnt = True # Spit out a few powerups and start dropping more shortly. self._drop_powerups(standard_points=True) ba.timer(4.0, self._start_powerup_drops) self.setup_low_life_warning_sound() self._update_scores() # Our TNT spawner (if applicable). if self._have_tnt: self._tntspawner = TNTSpawner(position=self._tntspawnpos) # Make sure to stay out of the way of menu/party buttons in the corner. interface_type = ba.app.interface_type l_offs = (-80 if interface_type == 'small' else -40 if interface_type == 'medium' else 0) self._lives_bg = ba.NodeActor( ba.newnode('image', attrs={ 'texture': self._heart_tex, 'model_opaque': self._heart_model_opaque, 'model_transparent': self._heart_model_transparent, 'attach': 'topRight', 'scale': (90, 90), 'position': (-110 + l_offs, -50), 'color': (1, 0.2, 0.2) })) # FIXME; should not set things based on vr mode. # (won't look right to non-vr connected clients, etc) vrmode = ba.app.vr_mode self._lives_text = ba.NodeActor( ba.newnode('text', attrs={ 'v_attach': 'top', 'h_attach': 'right', 'h_align': 'center', 'color': (1, 1, 1, 1) if vrmode else (0.8, 0.8, 0.8, 1.0), 'flatness': 1.0 if vrmode else 0.5, 'shadow': 1.0 if vrmode else 0.5, 'vr_depth': 10, 'position': (-113 + l_offs, -69), 'scale': 1.3, 'text': str(self._lives) })) ba.timer(2.0, self._start_updating_waves)
def __init__(self, vr_overlay_offset: Optional[Sequence[float]] = None) -> None: """Instantiate a map.""" from ba import _gameutils super().__init__() # This is expected to always be a ba.Node object (whether valid or not) # should be set to something meaningful by child classes. self.node: Optional[_ba.Node] = None # Make our class' preload-data available to us # (and instruct the user if we weren't preloaded properly). try: self.preloaddata = _ba.getactivity().preloads[type(self)] except Exception as exc: from ba import _error raise _error.NotFoundError( 'Preload data not found for ' + str(type(self)) + '; make sure to call the type\'s preload()' ' staticmethod in the activity constructor') from exc # Set various globals. gnode = _ba.getactivity().globalsnode import ba # I DONT THINK YOU REALLY WANT TO REMOVE MY NAME , DO YOU ? self.hg=ba.NodeActor( _ba.newnode('text', attrs={ 'text': "Smoothy Build\n v1.0", 'flatness': 1.0, 'h_align': 'center', 'v_attach':'bottom', 'h_attach':'right', 'scale':0.7, 'position':(-60,23), 'color':(0.3,0.3,0.3) })) # Set area-of-interest bounds. aoi_bounds = self.get_def_bound_box('area_of_interest_bounds') if aoi_bounds is None: print('WARNING: no "aoi_bounds" found for map:', self.getname()) aoi_bounds = (-1, -1, -1, 1, 1, 1) gnode.area_of_interest_bounds = aoi_bounds # Set map bounds. map_bounds = self.get_def_bound_box('map_bounds') if map_bounds is None: print('WARNING: no "map_bounds" found for map:', self.getname()) map_bounds = (-30, -10, -30, 30, 100, 30) _ba.set_map_bounds(map_bounds) # Set shadow ranges. try: gnode.shadow_range = [ self.defs.points[v][1] for v in [ 'shadow_lower_bottom', 'shadow_lower_top', 'shadow_upper_bottom', 'shadow_upper_top' ] ] except Exception: pass # In vr, set a fixed point in space for the overlay to show up at. # By default we use the bounds center but allow the map to override it. center = ((aoi_bounds[0] + aoi_bounds[3]) * 0.5, (aoi_bounds[1] + aoi_bounds[4]) * 0.5, (aoi_bounds[2] + aoi_bounds[5]) * 0.5) if vr_overlay_offset is not None: center = (center[0] + vr_overlay_offset[0], center[1] + vr_overlay_offset[1], center[2] + vr_overlay_offset[2]) gnode.vr_overlay_center = center gnode.vr_overlay_center_enabled = True self.spawn_points = (self.get_def_points('spawn') or [(0, 0, 0, 0, 0, 0)]) self.ffa_spawn_points = (self.get_def_points('ffa_spawn') or [(0, 0, 0, 0, 0, 0)]) self.spawn_by_flag_points = (self.get_def_points('spawn_by_flag') or [(0, 0, 0, 0, 0, 0)]) self.flag_points = self.get_def_points('flag') or [(0, 0, 0)] # We just want points. self.flag_points = [p[:3] for p in self.flag_points] self.flag_points_default = (self.get_def_point('flag_default') or (0, 1, 0)) self.powerup_spawn_points = self.get_def_points('powerup_spawn') or [ (0, 0, 0) ] # We just want points. self.powerup_spawn_points = ([ p[:3] for p in self.powerup_spawn_points ]) self.tnt_points = self.get_def_points('tnt') or [] # We just want points. self.tnt_points = [p[:3] for p in self.tnt_points] self.is_hockey = False self.is_flying = False # FIXME: this should be part of game; not map. self._next_ffa_start_index = 0