def handlemessage(self, msg: Any) -> Any: if isinstance(msg, ba.PlayerDiedMessage): # Augment standard behavior. super().handlemessage(msg) player: Player = msg.getplayer(Player) player.lives -= 1 if player.lives < 0: ba.print_error( "Got lives < 0 in Elim; this shouldn't happen. duo: True") player.lives = 0 # If we have any icons, update their state. for icon in player.icons: icon.handle_player_died() # Play big death sound on our last death # or for every one in solo mode. if player.lives == 0: ba.playsound(SpazFactory.get().single_player_death_sound) # If we hit zero lives, we're dead (and our team might be too). if player.lives == 0: # If the whole team is now dead, mark their survival time. if self._get_total_team_lives(player.team) == 0: assert self._start_time is not None player.team.survival_seconds = int(ba.time() - self._start_time) # In solo, put ourself at the back of the spawn order. if self._duo_mode: player.team.spawn_order.remove(player) player.team.spawn_order.append(player)
def equip_shields(self, decay: bool = False) -> None: """ Give this spaz a nice energy shield. """ if not self.node: ba.print_error('Can\'t equip shields; no node.') return factory = SpazFactory.get() if self.shield is None: self.shield = ba.newnode('shield', owner=self.node, attrs={ 'color': ((0 + random.random() * 6.5), (0 + random.random() * 6.5), (0 + random.random() * 6.5)), 'radius': 1.3 }) self.node.connectattr('position_center', self.shield, 'position') #ba.animate_array(self.shield, 'color', 3,{0:(1,0,0),0.2:(1,0.5,0),0.4:(1,1,0),0.6:(0,1,0),0.8:(0,1,1),1.0:(0,0,1),1.2:(1,0,0)},True) self.shield_hitpoints = self.shield_hitpoints_max = 650 self.shield_decay_rate = factory.shield_decay_rate if decay else 0 self.shield.hurt = 0 ba.playsound(factory.shield_up_sound, 1.0, position=self.node.position) if self.shield_decay_rate > 0: self.shield_decay_timer = ba.Timer(0.5, ba.WeakCall(self.shield_decay), repeat=True) # So user can see the decay. self.shield.always_show_health_bar = True
def handlemessage(self, msg: Any) -> Any: if isinstance(msg, PlayerSpazDeathMessage): # Augment default behavior. super().handlemessage(msg) player = msg.spaz.getplayer() if not player: ba.print_error('got no player in PlayerSpazDeathMessage') return if not player.gamedata['finished']: self.respawn_player(player, respawn_time=1) else: super().handlemessage(msg)
def _refresh(self) -> None: chars: Optional[list[str]] = None if self._mode in ['normal', 'caps']: chars = list(self._chars) if self._mode == 'caps': chars = [c.upper() for c in chars] ba.buttonwidget(edit=self._shift_button, color=self._key_color_lit if self._mode == 'caps' else self._key_color_dark, label=charstr(SpCh.SHIFT), on_activate_call=self._shift) ba.buttonwidget(edit=self._num_mode_button, label='123#&*', on_activate_call=self._num_mode) ba.buttonwidget(edit=self._emoji_button, color=self._key_color_dark, label=charstr(SpCh.LOGO_FLAT), on_activate_call=self._next_mode) else: if self._mode == 'num': chars = list(self._keyboard.nums) else: chars = list(self._keyboard.pages[self._mode]) ba.buttonwidget(edit=self._shift_button, color=self._key_color_dark, label='', on_activate_call=self._null_press) ba.buttonwidget(edit=self._num_mode_button, label='abc', on_activate_call=self._abc_mode) ba.buttonwidget(edit=self._emoji_button, color=self._key_color_dark, label=charstr(SpCh.LOGO_FLAT), on_activate_call=self._next_mode) for i, btn in enumerate(self._char_keys): assert chars is not None have_char = True if i >= len(chars): # No such char. have_char = False pagename = self._mode ba.print_error( f'Size of page "{pagename}" of keyboard' f' "{self._keyboard.name}" is incorrect:' f' {len(chars)} != {len(self._chars)}' f' (size of default "normal" page)', once=True) ba.buttonwidget(edit=btn, label=chars[i] if have_char else ' ', on_activate_call=ba.Call( self._type_char, chars[i] if have_char else ' '))
def _tick(self) -> None: # Give the chosen one points. player = self._get_chosen_one_player() if player is not None: # This shouldn't happen, but just in case. if not player.is_alive(): ba.print_error('got dead player as chosen one in _tick') self._set_chosen_one_player(None) else: scoring_team = player.team assert self.stats self.stats.player_scored(player, 3, screenmessage=False, display=False) scoring_team.gamedata['time_remaining'] = max( 0, scoring_team.gamedata['time_remaining'] - 1) # show the count over their head try: if scoring_team.gamedata['time_remaining'] > 0: if isinstance(player.actor, spaz.Spaz): player.actor.set_score_text( str(scoring_team.gamedata['time_remaining'])) except Exception: pass self._update_scoreboard() # announce numbers we have sounds for try: ba.playsound(self._countdownsounds[ scoring_team.gamedata['time_remaining']]) except Exception: pass # Winner! if scoring_team.gamedata['time_remaining'] <= 0: self.end_game() else: # (player is None) # This shouldn't happen, but just in case. # (Chosen-one player ceasing to exist should # trigger on_player_leave which resets chosen-one) if self._chosen_one_player is not None: ba.print_error('got nonexistent player as chosen one in _tick') self._set_chosen_one_player(None)
def handlemessage(self, msg: Any) -> Any: if isinstance(msg, ba.PlayerDiedMessage): #== losingPlayer = msg.getplayer(Player) if len(self.myPlayers) > 1: if self.playerFromName(self.myPlayers[self.count - 2]) == losingPlayer: winningPlayer = self.playerFromName( self.myPlayers[self.count - 1]) else: winningPlayer = self.playerFromName( self.myPlayers[self.count - 2]) if str(losingPlayer.getname()) in self.myPlayers: self.myPlayers.remove(str(losingPlayer.getname())) if winningPlayer.team.survival_seconds <= losingPlayer.team.survival_seconds: winningPlayer.team.survival_seconds = losingPlayer.team.survival_seconds + 1 else: winningPlayer.team.survival_seconds += 1 winningPlayer.wins += 1 if winningPlayer.is_alive() and not len(self.myPlayers) == 1: winningPlayer.actor.handlemessage( ba.DieMessage(immediate=True)) self.count -= 1 self.spawnPlayer() #== # Augment standard behavior. super().handlemessage(msg) player: Player = msg.getplayer(Player) player.lives -= 1 if player.lives < 0: ba.print_error("Got lives < 0 in Elim; this shouldn't happen.") player.lives = 0 # If we have any icons, update their state. # Play big death sound on our last death # or for every one in solo mode. if player.lives == 0: ba.playsound(SpazFactory.get().single_player_death_sound) # If we hit zero lives, we're dead (and our team might be too). if player.lives == 0: pass else: self.respawn_player(player)
def handlemessage(self, msg: Any) -> Any: if isinstance(msg, playerspaz.PlayerSpazDeathMessage): player = msg.spaz.getplayer() if player is None: ba.print_error('FIXME: getplayer() should no longer ' 'ever be returning None.') return if not player: return self.stats.player_was_killed(player) ba.timer(0.1, self._checkroundover) elif isinstance(msg, ba.PlayerScoredMessage): self._score += msg.score self._update_scores() elif isinstance(msg, spazbot.SpazBotDeathMessage): pts, importance = msg.badguy.get_death_points(msg.how) target: Optional[Sequence[float]] if msg.killerplayer: try: assert msg.badguy.node target = msg.badguy.node.position except Exception: ba.print_exception() target = None try: self.stats.player_scored(msg.killerplayer, pts, target=target, kill=True, screenmessage=False, importance=importance) ba.playsound(self._dingsound if importance == 1 else self._dingsoundhigh, volume=0.6) except Exception as exc: print('EXC on last-stand SpazBotDeathMessage', exc) # Normally we pull scores from the score-set, but if there's no # player lets be explicit. else: self._score += pts self._update_scores() else: super().handlemessage(msg)
def _handle_touching_own_flag(self, team: Team, connecting: bool) -> None: """Called when a player touches or stops touching their own team flag. We keep track of when each player is touching their own flag so we can award points when returned. """ player: Optional[Player] try: player = ba.getcollision().sourcenode.getdelegate( PlayerSpaz, True).getplayer(Player, True) except ba.NotFoundError: # This can happen if the player leaves but his corpse touches/etc. player = None if player: player.touching_own_flag += (1 if connecting else -1) # If return-time is zero, just kill it immediately.. otherwise keep # track of touches and count down. if float(self.flag_touch_return_time) <= 0.0: assert team.flag is not None if (connecting and not team.home_flag_at_base and team.flag.held_count == 0): self._award_players_touching_own_flag(team) ba.getcollision().opposingnode.handlemessage(ba.DieMessage()) # Takes a non-zero amount of time to return. else: if connecting: team.flag_return_touches += 1 if team.flag_return_touches == 1: team.touch_return_timer = ba.Timer( 0.1, call=ba.Call(self._touch_return_update, team), repeat=True) team.touch_return_timer_ticking = None else: team.flag_return_touches -= 1 if team.flag_return_touches == 0: team.touch_return_timer = None team.touch_return_timer_ticking = None if team.flag_return_touches < 0: ba.print_error('CTF flag_return_touches < 0')
def _handle_hit_own_flag(self, team: ba.Team, val: int) -> None: """ keep track of when each player is touching their own flag so we can award points when returned """ srcnode = ba.get_collision_info('source_node') assert isinstance(srcnode, (ba.Node, type(None))) player = self._player_from_node(srcnode) if player: player.gamedata['touching_own_flag'] += (1 if val else -1) # If return-time is zero, just kill it immediately.. otherwise keep # track of touches and count down. if float(self.settings_raw['Flag Touch Return Time']) <= 0.0: if (not team.gamedata['home_flag_at_base'] and team.gamedata['flag'].held_count == 0): # Use a node message to kill the flag instead of just killing # our team's. (avoids redundantly killing new flags if # multiple body parts generate callbacks in one step). node = ba.get_collision_info('opposing_node') if node: self._award_players_touching_own_flag(team) node.handlemessage(ba.DieMessage()) # Takes a non-zero amount of time to return. else: if val: team.gamedata['flag_return_touches'] += 1 if team.gamedata['flag_return_touches'] == 1: team.gamedata['touch_return_timer'] = ba.Timer( 0.1, call=ba.Call(self._touch_return_update, team), repeat=True) team.gamedata['touch_return_timer_ticking'] = None else: team.gamedata['flag_return_touches'] -= 1 if team.gamedata['flag_return_touches'] == 0: team.gamedata['touch_return_timer'] = None team.gamedata['touch_return_timer_ticking'] = None if team.gamedata['flag_return_touches'] < 0: ba.print_error( "CTF: flag_return_touches < 0; this shouldn't happen.")
def handlemessage(self, msg: Any) -> Any: if isinstance(msg, playerspaz.PlayerSpazDeathMessage): # Augment standard behavior. super().handlemessage(msg) player = msg.spaz.player player.gamedata['lives'] -= 1 if player.gamedata['lives'] < 0: ba.print_error( "Got lives < 0 in Elim; this shouldn't happen. solo:" + str(self._solo_mode)) player.gamedata['lives'] = 0 # If we have any icons, update their state. for icon in player.gamedata['icons']: icon.handle_player_died() # Play big death sound on our last death # or for every one in solo mode. if self._solo_mode or player.gamedata['lives'] == 0: ba.playsound(spaz.get_factory().single_player_death_sound) # If we hit zero lives, we're dead (and our team might be too). if player.gamedata['lives'] == 0: # If the whole team is now dead, mark their survival time. if self._get_total_team_lives(player.team) == 0: assert self._start_time is not None player.team.gamedata['survival_seconds'] = int( ba.time() - self._start_time) else: # Otherwise, in regular mode, respawn. if not self._solo_mode: self.respawn_player(player) # In solo, put ourself at the back of the spawn order. if self._solo_mode: player.team.gamedata['spawn_order'].remove(player) player.team.gamedata['spawn_order'].append(player)
def _die(self, immediate: bool = False) -> None: session = self._session() if session is None and self.node: # If session is gone, our node should be too, # since it was part of the session's scene. # Let's make sure that's the case. # (since otherwise we have no way to kill it) ba.print_error('got None session on Background _die' ' (and node still exists!)') elif session is not None: with ba.Context(session): if not self._dying and self.node: self._dying = True if immediate: self.node.delete() else: ba.animate(self.node, 'opacity', { 0.0: 1.0, self.fade_time: 0.0 }, loop=False) ba.timer(self.fade_time + 0.1, self.node.delete)
def __init__(self, text: Union[str, ba.Lstr], position: Tuple[float, float] = (0.0, 0.0), shiftposition: Tuple[float, float] = None, shiftdelay: float = None, lifespan: float = None, flash: bool = True, trail: bool = True, h_align: str = 'center', color: Sequence[float] = (0.9, 0.4, 0.0), jitter: float = 0.0, trailcolor: Sequence[float] = (1.0, 0.35, 0.1, 0.0), scale: float = 1.0, project_scale: float = 1.0, tilt_translate: float = 0.0, maxwidth: float = None): # pylint: disable=too-many-locals super().__init__() self._dying = False positionadjusted = (position[0], position[1] - 100) if shiftdelay is None: shiftdelay = 2.500 if shiftdelay < 0.0: ba.print_error('got shiftdelay < 0') shiftdelay = 0.0 self._project_scale = project_scale self.node = ba.newnode('text', delegate=self, attrs={ 'position': positionadjusted, 'big': True, 'text': text, 'trail': trail, 'vr_depth': 0, 'shadow': 0.0 if trail else 0.3, 'scale': scale, 'maxwidth': maxwidth if maxwidth is not None else 0.0, 'tilt_translate': tilt_translate, 'h_align': h_align, 'v_align': 'center' }) # we never jitter in vr mode.. if ba.app.vr_mode: jitter = 0.0 # if they want jitter, animate its position slightly... if jitter > 0.0: self._jitter(positionadjusted, jitter * scale) # if they want shifting, move to the shift position and # then resume jittering if shiftposition is not None: positionadjusted2 = (shiftposition[0], shiftposition[1] - 100) ba.timer( shiftdelay, ba.WeakCall(self._shift, positionadjusted, positionadjusted2)) if jitter > 0.0: ba.timer( shiftdelay + 0.25, ba.WeakCall(self._jitter, positionadjusted2, jitter * scale)) color_combine = ba.newnode('combine', owner=self.node, attrs={ 'input2': color[2], 'input3': 1.0, 'size': 4 }) if trail: trailcolor_n = ba.newnode('combine', owner=self.node, attrs={ 'size': 3, 'input0': trailcolor[0], 'input1': trailcolor[1], 'input2': trailcolor[2] }) trailcolor_n.connectattr('output', self.node, 'trailcolor') basemult = 0.85 ba.animate( self.node, 'trail_project_scale', { 0: 0 * project_scale, basemult * 0.201: 0.6 * project_scale, basemult * 0.347: 0.8 * project_scale, basemult * 0.478: 0.9 * project_scale, basemult * 0.595: 0.93 * project_scale, basemult * 0.748: 0.95 * project_scale, basemult * 0.941: 0.95 * project_scale }) if flash: mult = 2.0 tm1 = 0.15 tm2 = 0.3 ba.animate(color_combine, 'input0', { 0: color[0] * mult, tm1: color[0], tm2: color[0] * mult }, loop=True) ba.animate(color_combine, 'input1', { 0: color[1] * mult, tm1: color[1], tm2: color[1] * mult }, loop=True) ba.animate(color_combine, 'input2', { 0: color[2] * mult, tm1: color[2], tm2: color[2] * mult }, loop=True) else: color_combine.input0 = color[0] color_combine.input1 = color[1] color_combine.connectattr('output', self.node, 'color') ba.animate(self.node, 'project_scale', { 0: 0, 0.27: 1.05 * project_scale, 0.3: 1 * project_scale }) # if they give us a lifespan, kill ourself down the line if lifespan is not None: ba.timer(lifespan, ba.WeakCall(self.handlemessage, ba.DieMessage()))