def _handle_score(self) -> None: """A point has been scored.""" assert self._puck is not None assert self._score_regions is not None # Our puck might stick around for a second or two # we don't want it to be able to score again. if self._puck.scored: return region = ba.get_collision_info("source_node") index = 0 for index in range(len(self._score_regions)): if region == self._score_regions[index].node: break for team in self.teams: if team.get_id() == index: scoring_team = team team.gamedata['score'] += 1 # Tell all players to celebrate. for player in team.players: if player.actor: player.actor.handlemessage(ba.CelebrateMessage(2.0)) # If we've got the player from the scoring team that last # touched us, give them points. if (scoring_team.get_id() in self._puck.last_players_to_touch and self._puck.last_players_to_touch[ scoring_team.get_id()]): self.stats.player_scored(self._puck.last_players_to_touch[ scoring_team.get_id()], 100, big_message=True) # End game if we won. if team.gamedata['score'] >= self.settings['Score to Win']: self.end_game() ba.playsound(self._foghorn_sound) ba.playsound(self._cheer_sound) self._puck.scored = True # Kill the puck (it'll respawn itself shortly). ba.timer(1.0, self._kill_puck) light = ba.newnode('light', attrs={ 'position': ba.get_collision_info('position'), 'height_attenuated': False, 'color': (1, 0, 0) }) ba.animate(light, 'intensity', {0: 0, 0.5: 1, 1.0: 0}, loop=True) ba.timer(1.0, light.delete) ba.cameraflash(duration=10.0) self._update_scoreboard()
def _handle_score(self) -> None: """A point has been scored.""" # Our flag might stick around for a second or two # make sure it doesn't score again. assert self._flag is not None if self._flag.scored: return region = ba.get_collision_info('source_node') i = None for i in range(len(self._score_regions)): if region == self._score_regions[i].node: break for team in self.teams: if team.get_id() == i: team.gamedata['score'] += 7 # Tell all players to celebrate. for player in team.players: if player.actor: player.actor.handlemessage(ba.CelebrateMessage(2.0)) # If someone on this team was last to touch it, # give them points. assert self._flag is not None if (self._flag.last_holding_player and team == self._flag.last_holding_player.team): self.stats.player_scored(self._flag.last_holding_player, 50, big_message=True) # end game if we won if team.gamedata['score'] >= self.settings['Score to Win']: self.end_game() ba.playsound(self._score_sound) ba.playsound(self._cheer_sound) assert self._flag self._flag.scored = True # Kill the flag (it'll respawn shortly). ba.timer(1.0, self._kill_flag) light = ba.newnode('light', attrs={ 'position': ba.get_collision_info('position'), 'height_attenuated': False, 'color': (1, 0, 0) }) ba.animate(light, 'intensity', {0.0: 0, 0.5: 1, 1.0: 0}, loop=True) ba.timer(1.0, light.delete) ba.cameraflash(duration=10.0) self._update_scoreboard()
def _handle_flag_player_collide(self) -> None: flagnode, playernode = ba.get_collision_info('source_node', 'opposing_node') try: player = playernode.getdelegate().getplayer() flag = flagnode.getdelegate() except Exception: return # Player may have left and his body hit the flag. assert isinstance(player, ba.Player) assert isinstance(flag, ConquestFlag) assert flag.light if flag.team is not player.team: flag.team = player.team flag.light.color = player.team.color flag.node.color = player.team.color self.stats.player_scored(player, 10, screenmessage=False) ba.playsound(self._swipsound) self._flash_flag(flag) self._update_scores() # Respawn any players on this team that were in limbo due to the # lack of a flag for their team. for otherplayer in self.players: if (otherplayer.team is flag.team and otherplayer.actor is not None and not otherplayer.is_alive() and otherplayer.gamedata['respawn_timer'] is None): self.spawn_player(otherplayer)
def _handle_splat(self) -> None: node = ba.get_collision_info('opposing_node') if (node is not self.owner and ba.time() - self._last_sticky_sound_time > 1.0): self._last_sticky_sound_time = ba.time() assert self.node ba.playsound(get_factory().sticky_impact_sound, 2.0, position=self.node.position)
def _handle_reset_collide(self) -> None: # If we have a chosen one, ignore these. if self._get_chosen_one_player() is not None: return delegate = ba.get_collision_info('opposing_node').getdelegate() if isinstance(delegate, PlayerSpaz): player = ba.playercast_o(Player, delegate.getplayer()) if player is not None and player.is_alive(): self._set_chosen_one_player(player)
def _handle_puck_player_collide(self) -> None: try: pucknode, playernode = ba.get_collision_info( 'source_node', 'opposing_node') puck = pucknode.getdelegate() player = playernode.getdelegate().getplayer() except Exception: player = puck = None if player and puck: puck.last_players_to_touch[player.team.get_id()] = player
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 _handle_flag_entered_base(self, team: ba.Team) -> None: node = ba.get_collision_info('opposing_node') assert isinstance(node, (ba.Node, type(None))) flag = CTFFlag.from_node(node) if not flag: print('Unable to get flag in _handle_flag_entered_base') return if flag.team is team: team.gamedata['home_flag_at_base'] = True # If the enemy flag is already here, score! if team.gamedata['enemy_flag_at_base']: self._score(team) else: team.gamedata['enemy_flag_at_base'] = True if team.gamedata['home_flag_at_base']: # Award points to whoever was carrying the enemy flag. player = flag.last_player_to_hold if player and player.team is team: assert self.stats self.stats.player_scored(player, 50, big_message=True) # Update score and reset flags. self._score(team) # If the home-team flag isn't here, print a message to that effect. else: # Don't want slo-mo affecting this curtime = ba.time(ba.TimeType.BASE) if curtime - self._last_home_flag_notice_print_time > 5.0: self._last_home_flag_notice_print_time = curtime bpos = team.gamedata['base_pos'] tval = ba.Lstr(resource='ownFlagAtYourBaseWarning') tnode = ba.newnode('text', attrs={ 'text': tval, 'in_world': True, 'scale': 0.013, 'color': (1, 1, 0, 1), 'h_align': 'center', 'position': (bpos[0], bpos[1] + 3.2, bpos[2]) }) ba.timer(5.1, tnode.delete) ba.animate(tnode, 'scale', { 0.0: 0, 0.2: 0.013, 4.8: 0.013, 5.0: 0 })
def _handle_reset_collide(self) -> None: # If we have a chosen one, ignore these. if self._get_chosen_one_player() is not None: return try: player = (ba.get_collision_info( 'opposing_node').getdelegate().getplayer()) except Exception: return if player is not None and player.is_alive(): self._set_chosen_one_player(player)
def _handle_puck_player_collide(self) -> None: try: pucknode, playernode = ba.get_collision_info( 'source_node', 'opposing_node') puck = pucknode.getdelegate() player = playernode.getdelegate().getplayer() except Exception: player = puck = None assert isinstance(player, Player) assert isinstance(puck, Puck) if player and puck: puck.last_players_to_touch[player.team.id] = player
def _handle_player_flag_region_collide(self, colliding: bool) -> None: playernode = ba.get_collision_info('opposing_node') try: player = playernode.getdelegate().getplayer() except Exception: return # Different parts of us can collide so a single value isn't enough # also don't count it if we're dead (flying heads shouldn't be able to # win the game :-) if colliding and player.is_alive(): player.gamedata['at_flag'] += 1 else: player.gamedata['at_flag'] = max(0, player.gamedata['at_flag'] - 1) self._update_flag_state()
def _handle_impact(self) -> None: node = ba.get_collision_info('opposing_node') # if we're an impact bomb and we came from this node, don't explode... # alternately if we're hitting another impact-bomb from the same # source, don't explode... try: node_delegate = node.getdelegate() except Exception: node_delegate = None if node: if (self.bomb_type == 'impact' and (node is self.owner or (isinstance(node_delegate, Bomb) and node_delegate.bomb_type == 'impact' and node_delegate.owner is self.owner))): return self.handlemessage(ExplodeMessage())
def _handle_player_flag_region_collide(self, colliding: bool) -> None: delegate = ba.get_collision_info('opposing_node').getdelegate() if not isinstance(delegate, PlayerSpaz): return player = ba.playercast_o(Player, delegate.getplayer()) if not player: return # Different parts of us can collide so a single value isn't enough # also don't count it if we're dead (flying heads shouldn't be able to # win the game :-) if colliding and player.is_alive(): player.time_at_flag += 1 else: player.time_at_flag = max(0, player.time_at_flag - 1) self._update_flag_state()
def _on_egg_player_collide(self) -> None: if not self.has_ended(): egg_node, playernode = ba.get_collision_info( 'source_node', 'opposing_node') if egg_node is not None and playernode is not None: egg = egg_node.getdelegate() assert isinstance(egg, Egg) spaz = playernode.getdelegate() assert isinstance(spaz, playerspaz.PlayerSpaz) player = (spaz.getplayer() if hasattr(spaz, 'getplayer') else None) if player and egg: player.team.gamedata['score'] += 1 # Displays a +1 (and adds to individual player score in # teams mode). self.stats.player_scored(player, 1, screenmessage=False) if self._max_eggs < 5: self._max_eggs += 1.0 elif self._max_eggs < 10: self._max_eggs += 0.5 elif self._max_eggs < 30: self._max_eggs += 0.3 self._update_scoreboard() ba.playsound(self._collect_sound, 0.5, position=egg.node.position) # Create a flash. light = ba.newnode('light', attrs={ 'position': egg_node.position, 'height_attenuated': False, 'radius': 0.1, 'color': (1, 1, 0) }) ba.animate(light, 'intensity', { 0: 0, 0.1: 1.0, 0.2: 0 }, loop=False) ba.timer(0.200, light.delete) egg.handlemessage(ba.DieMessage())
def _handle_flag_left_base(self, team: Team) -> None: cur_time = ba.time() op_node = ba.get_collision_info('opposing_node') assert isinstance(op_node, (ba.Node, type(None))) flag = CTFFlag.from_node(op_node) if not flag: return if flag.team is team: # Check times here to prevent too much flashing. if (team.last_flag_leave_time is None or cur_time - team.last_flag_leave_time > 3.0): ba.playsound(self._alarmsound, position=team.base_pos) self._flash_base(team) team.last_flag_leave_time = cur_time team.home_flag_at_base = False else: team.enemy_flag_at_base = False
def _handle_flag_left_base(self, team: ba.Team) -> None: cur_time = ba.time() op_node = ba.get_collision_info("opposing_node") assert isinstance(op_node, (ba.Node, type(None))) flag = CTFFlag.from_node(op_node) if not flag: return if flag.team is team: # Check times here to prevent too much flashing. if ('last_flag_leave_time' not in team.gamedata or cur_time - team.gamedata['last_flag_leave_time'] > 3.0): ba.playsound(self._alarmsound, position=team.gamedata['base_pos']) self._flash_base(team) team.gamedata['last_flag_leave_time'] = cur_time team.gamedata['home_flag_at_base'] = False else: team.gamedata['enemy_flag_at_base'] = False
def handlemessage(self, msg: Any) -> Any: # pylint: disable=too-many-branches if __debug__: self._handlemessage_sanity_check() if isinstance(msg, ba.PowerupAcceptMessage): factory = get_factory() assert self.node if self.poweruptype == 'health': ba.playsound(factory.health_powerup_sound, 3, position=self.node.position) ba.playsound(factory.powerup_sound, 3, position=self.node.position) self._powersgiven = True self.handlemessage(ba.DieMessage()) elif isinstance(msg, _TouchedMessage): if not self._powersgiven: node = ba.get_collision_info('opposing_node') if node: node.handlemessage( ba.PowerupMessage(self.poweruptype, source_node=self.node)) elif isinstance(msg, ba.DieMessage): if self.node: if msg.immediate: self.node.delete() else: ba.animate(self.node, 'model_scale', {0: 1, 0.1: 0}) ba.timer(0.1, self.node.delete) elif isinstance(msg, ba.OutOfBoundsMessage): self.handlemessage(ba.DieMessage()) elif isinstance(msg, ba.HitMessage): # Don't die on punches (that's annoying). if msg.hit_type != 'punch': self.handlemessage(ba.DieMessage()) else: super().handlemessage(msg)
def handlemessage(self, msg: Any) -> Any: if __debug__: self._handlemessage_sanity_check() if isinstance(msg, ba.DieMessage): if self.node: self.node.delete() elif isinstance(msg, ExplodeHitMessage): node = ba.get_collision_info('opposing_node') if node: assert self.node nodepos = self.node.position # new mag = 2000.0 if self.blast_type == 'ice': mag *= 0.5 elif self.blast_type == 'land_mine': mag *= 2.5 elif self.blast_type == 'tnt': mag *= 2.0 node.handlemessage( ba.HitMessage(pos=nodepos, velocity=(0, 0, 0), magnitude=mag, hit_type=self.hit_type, hit_subtype=self.hit_subtype, radius=self.radius, source_player=ba.existing( self.source_player))) if self.blast_type == 'ice': ba.playsound(get_factory().freeze_sound, 10, position=nodepos) node.handlemessage(ba.FreezeMessage()) else: super().handlemessage(msg)
def _handle_score(self) -> None: """ a point has been scored """ # FIXME tidy this up # pylint: disable=too-many-branches # Our flag might stick around for a second or two; # we don't want it to be able to score again. assert self._flag is not None if self._flag.scored: return # See which score region it was. region = ba.get_collision_info('source_node') i = None for i in range(len(self.score_regions)): if region == self.score_regions[i].node: break for team in [self.teams[0], self._bot_team]: assert team is not None if team.get_id() == i: team.gamedata['score'] += 7 # Tell all players (or bots) to celebrate. if i == 0: for player in team.players: if player.actor: player.actor.handlemessage( ba.CelebrateMessage(2.0)) else: self._bots.celebrate(2.0) # If the good guys scored, add more enemies. if i == 0: if self.teams[0].gamedata['score'] == 7: assert self._bot_types_7 is not None for bottype in self._bot_types_7: self._spawn_bot(bottype) elif self.teams[0].gamedata['score'] == 14: assert self._bot_types_14 is not None for bottype in self._bot_types_14: self._spawn_bot(bottype) ba.playsound(self._score_sound) if i == 0: ba.playsound(self._cheer_sound) else: ba.playsound(self._boo_sound) # Kill the flag (it'll respawn shortly). self._flag.scored = True ba.timer(0.2, self._kill_flag) self.update_scores() light = ba.newnode('light', attrs={ 'position': ba.get_collision_info('position'), 'height_attenuated': False, 'color': (1, 0, 0) }) ba.animate(light, 'intensity', {0: 0, 0.5: 1, 1.0: 0}, loop=True) ba.timer(1.0, light.delete) if i == 0: ba.cameraflash(duration=10.0)
def _handle_race_point_collide(self) -> None: # FIXME: Tidy this up. # pylint: disable=too-many-statements # pylint: disable=too-many-branches # pylint: disable=too-many-nested-blocks region_node, playernode = ba.get_collision_info( 'source_node', 'opposing_node') try: player = playernode.getdelegate().getplayer() except Exception: player = None region = region_node.getdelegate() if not player or not region: return assert isinstance(player, ba.Player) assert isinstance(region, RaceRegion) last_region = player.gamedata['last_region'] this_region = region.index if last_region != this_region: # If a player tries to skip regions, smite them. # Allow a one region leeway though (its plausible players can get # blown over a region, etc). if this_region > last_region + 2: if player.is_alive(): assert player.actor player.actor.handlemessage(ba.DieMessage()) ba.screenmessage(ba.Lstr( translate=('statements', 'Killing ${NAME} for' ' skipping part of the track!'), subs=[('${NAME}', player.get_name(full=True))]), color=(1, 0, 0)) else: # If this player is in first, note that this is the # front-most race-point. if player.gamedata['rank'] == 0: self._front_race_region = this_region player.gamedata['last_region'] = this_region if last_region >= len(self._regions) - 2 and this_region == 0: team = player.team player.gamedata['lap'] = min(self.settings['Laps'], player.gamedata['lap'] + 1) # In teams mode with all-must-finish on, the team lap # value is the min of all team players. # Otherwise its the max. if isinstance(self.session, ba.DualTeamSession) and self.settings.get( 'Entire Team Must Finish'): team.gamedata['lap'] = min( [p.gamedata['lap'] for p in team.players]) else: team.gamedata['lap'] = max( [p.gamedata['lap'] for p in team.players]) # A player is finishing. if player.gamedata['lap'] == self.settings['Laps']: # In teams mode, hand out points based on the order # players come in. if isinstance(self.session, ba.DualTeamSession): assert self._team_finish_pts is not None if self._team_finish_pts > 0: self.stats.player_scored(player, self._team_finish_pts, screenmessage=False) self._team_finish_pts -= 25 # Flash where the player is. self._flash_player(player, 1.0) player.gamedata['finished'] = True assert player.actor player.actor.handlemessage( ba.DieMessage(immediate=True)) # Makes sure noone behind them passes them in rank # while finishing. player.gamedata['distance'] = 9999.0 # If the whole team has finished the race. if team.gamedata['lap'] == self.settings['Laps']: ba.playsound(self._score_sound) player.team.gamedata['finished'] = True assert self._timer is not None cur_time = ba.time( timeformat=ba.TimeFormat.MILLISECONDS) start_time = self._timer.getstarttime( timeformat=ba.TimeFormat.MILLISECONDS) self._last_team_time = ( player.team.gamedata['time']) = (cur_time - start_time) self._check_end_game() # Team has yet to finish. else: ba.playsound(self._swipsound) # They've just finished a lap but not the race. else: ba.playsound(self._swipsound) self._flash_player(player, 0.3) # Print their lap number over their head. try: assert isinstance(player.actor, PlayerSpaz) mathnode = ba.newnode('math', owner=player.actor.node, attrs={ 'input1': (0, 1.9, 0), 'operation': 'add' }) player.actor.node.connectattr( 'torso_position', mathnode, 'input2') tstr = ba.Lstr(resource='lapNumberText', subs=[('${CURRENT}', str(player.gamedata['lap'] + 1)), ('${TOTAL}', str(self.settings['Laps']))]) txtnode = ba.newnode('text', owner=mathnode, attrs={ 'text': tstr, 'in_world': True, 'color': (1, 1, 0, 1), 'scale': 0.015, 'h_align': 'center' }) mathnode.connectattr('output', txtnode, 'position') ba.animate(txtnode, 'scale', { 0.0: 0, 0.2: 0.019, 2.0: 0.019, 2.2: 0 }) ba.timer(2.3, mathnode.delete) except Exception as exc: print('Exception printing lap:', exc)
def _handle_base_collide(self, team: ba.Team) -> None: # Attempt to pull a living ba.Player from what we hit. cnode = ba.get_collision_info('opposing_node') assert isinstance(cnode, ba.Node) actor = cnode.getdelegate() if not isinstance(actor, playerspaz.PlayerSpaz): return player = actor.getplayer() if not player or not player.is_alive(): return # If its another team's player, they scored. player_team = player.team if player_team is not team: # Prevent multiple simultaneous scores. if ba.time() != self._last_score_time: self._last_score_time = ba.time() self.stats.player_scored(player, 50, big_message=True) ba.playsound(self._score_sound) self._flash_base(team) # Move all players on the scoring team back to their start # and add flashes of light so its noticeable. for player in player_team.players: if player.is_alive(): if player.node: pos = player.node.position light = ba.newnode('light', attrs={ 'position': pos, 'color': player_team.color, 'height_attenuated': False, 'radius': 0.4 }) ba.timer(0.5, light.delete) ba.animate(light, 'intensity', { 0: 0, 0.1: 1.0, 0.5: 0 }) new_pos = (self.map.get_start_position( player_team.get_id())) light = ba.newnode('light', attrs={ 'position': new_pos, 'color': player_team.color, 'radius': 0.4, 'height_attenuated': False }) ba.timer(0.5, light.delete) ba.animate(light, 'intensity', { 0: 0, 0.1: 1.0, 0.5: 0 }) if player.actor: player.actor.handlemessage( ba.StandMessage(new_pos, random.uniform(0, 360))) # Have teammates celebrate. for player in player_team.players: if player.actor: player.actor.handlemessage(ba.CelebrateMessage(2.0)) player_team.gamedata['score'] += 1 self._update_scoreboard() if (player_team.gamedata['score'] >= self.settings_raw['Score to Win']): self.end_game()