示例#1
0
 def register_player(self, player: ba.Player) -> None:
     """Register a player with this score-set."""
     name = player.get_name()
     name_full = player.get_name(full=True)
     try:
         # If the player already exists, update his character and such as
         # it may have changed.
         self._player_records[name].associate_with_player(player)
     except Exception:
         # FIXME: Shouldn't use top level Exception catch for logic.
         #  Should only have this as a fallback and always log it.
         self._player_records[name] = PlayerRecord(name, name_full, player,
                                                   self)
示例#2
0
    def on_player_leave(self, player: ba.Player) -> None:
        ba.TeamGameActivity.on_player_leave(self, player)

        # A player leaving disqualifies the team if 'Entire Team Must Finish'
        # is on (otherwise in teams mode everyone could just leave except the
        # leading player to win).
        if (isinstance(self.session, ba.DualTeamSession)
                and self.settings.get('Entire Team Must Finish')):
            ba.screenmessage(ba.Lstr(
                translate=('statements',
                           '${TEAM} is disqualified because ${PLAYER} left'),
                subs=[('${TEAM}', player.team.name),
                      ('${PLAYER}', player.get_name(full=True))]),
                             color=(1, 1, 0))
            player.team.gamedata['finished'] = True
            player.team.gamedata['time'] = None
            player.team.gamedata['lap'] = 0
            ba.playsound(ba.getsound('boo'))
            for otherplayer in player.team.players:
                otherplayer.gamedata['lap'] = 0
                otherplayer.gamedata['finished'] = True
                try:
                    if otherplayer.actor is not None:
                        otherplayer.actor.handlemessage(ba.DieMessage())
                except Exception:
                    ba.print_exception('Error sending diemessages')

        # Defer so team/player lists will be updated.
        ba.pushcall(self._check_end_game)
示例#3
0
 def player_was_killed(self,
                       player: ba.Player,
                       killed: bool = False,
                       killer: ba.Player = None) -> None:
     """Should be called when a player is killed."""
     from ba._lang import Lstr
     name = player.get_name()
     prec = self._player_records[name]
     prec.streak = 0
     if killed:
         prec.accum_killed_count += 1
         prec.killed_count += 1
     try:
         if killed and _ba.getactivity().announce_player_deaths:
             if killer == player:
                 _ba.screenmessage(Lstr(resource='nameSuicideText',
                                        subs=[('${NAME}', name)]),
                                   top=True,
                                   color=player.color,
                                   image=player.get_icon())
             elif killer is not None:
                 if killer.team == player.team:
                     _ba.screenmessage(Lstr(resource='nameBetrayedText',
                                            subs=[('${NAME}',
                                                   killer.get_name()),
                                                  ('${VICTIM}', name)]),
                                       top=True,
                                       color=killer.color,
                                       image=killer.get_icon())
                 else:
                     _ba.screenmessage(Lstr(resource='nameKilledText',
                                            subs=[('${NAME}',
                                                   killer.get_name()),
                                                  ('${VICTIM}', name)]),
                                       top=True,
                                       color=killer.color,
                                       image=killer.get_icon())
             else:
                 _ba.screenmessage(Lstr(resource='nameDiedText',
                                        subs=[('${NAME}', name)]),
                                   top=True,
                                   color=player.color,
                                   image=player.get_icon())
     except Exception:
         from ba import _error
         _error.print_exception('error announcing kill')
示例#4
0
 def on_player_join(self, player: ba.Player) -> None:
     # Don't allow joining after we start
     # (would enable leave/rejoin tomfoolery).
     if self.has_begun():
         ba.screenmessage(ba.Lstr(resource='playerDelayedJoinText',
                                  subs=[('${PLAYER}',
                                         player.get_name(full=True))]),
                          color=(0, 1, 0))
         # For score purposes, mark them as having died right as the
         # game started.
         assert self._timer is not None
         PlayerData.get(player).death_time = self._timer.getstarttime()
         return
     self.spawn_player(player)
示例#5
0
    def on_player_join(self, player: ba.Player) -> None:

        # No longer allowing mid-game joiners here; too easy to exploit.
        if self.has_begun():
            player.gamedata['lives'] = 0
            player.gamedata['icons'] = []

            # Make sure our team has survival seconds set if they're all dead
            # (otherwise blocked new ffa players would be considered 'still
            # alive' in score tallying).
            if self._get_total_team_lives(
                    player.team
            ) == 0 and player.team.gamedata['survival_seconds'] is None:
                player.team.gamedata['survival_seconds'] = 0
            ba.screenmessage(ba.Lstr(resource='playerDelayedJoinText',
                                     subs=[('${PLAYER}',
                                            player.get_name(full=True))]),
                             color=(0, 1, 0))
            return

        player.gamedata['lives'] = self.settings['Lives Per Player']

        if self._solo_mode:
            player.gamedata['icons'] = []
            player.team.gamedata['spawn_order'].append(player)
            self._update_solo_mode()
        else:
            # Create our icon and spawn.
            player.gamedata['icons'] = [
                Icon(player, position=(0, 50), scale=0.8)
            ]
            if player.gamedata['lives'] > 0:
                self.spawn_player(player)

        # Don't waste time doing this until begin.
        if self.has_begun():
            self._update_icons()
示例#6
0
    def on_player_leave(self, player: ba.Player) -> None:
        """Called when a previously-accepted ba.Player leaves the session."""
        # pylint: disable=too-many-statements
        # pylint: disable=too-many-branches
        # pylint: disable=cyclic-import
        from ba._freeforallsession import FreeForAllSession
        from ba._lang import Lstr
        from ba import _error

        # Remove them from the game rosters.
        if player in self.players:

            _ba.playsound(_ba.getsound('playerLeft'))

            team: Optional[ba.Team]

            # The player will have no team if they are still in the lobby.
            try:
                team = player.team
            except _error.TeamNotFoundError:
                team = None

            activity = self._activity_weak()

            # If he had no team, he's in the lobby.
            # If we have a current activity with a lobby, ask them to
            # remove him.
            if team is None:
                with _ba.Context(self):
                    try:
                        self.lobby.remove_chooser(player)
                    except Exception:
                        _error.print_exception(
                            'Error in Lobby.remove_chooser()')

            # *If* they were actually in the game, announce their departure.
            if team is not None:
                _ba.screenmessage(
                    Lstr(resource='playerLeftText',
                         subs=[('${PLAYER}', player.get_name(full=True))]))

            # Remove him from his team and session lists.
            # (he may not be on the team list since player are re-added to
            # team lists every activity)
            if team is not None and player in team.players:

                # Testing; can remove this eventually.
                if isinstance(self, FreeForAllSession):
                    if len(team.players) != 1:
                        _error.print_error('expected 1 player in FFA team')
                team.players.remove(player)

            # Remove player from any current activity.
            if activity is not None and player in activity.players:
                activity.players.remove(player)

                # Run the activity callback unless its been expired.
                if not activity.is_expired():
                    try:
                        with _ba.Context(activity):
                            activity.on_player_leave(player)
                    except Exception:
                        _error.print_exception(
                            'exception in on_player_leave for activity',
                            activity)
                else:
                    _error.print_error('expired activity in on_player_leave;'
                                       " shouldn't happen")

                player.set_activity(None)
                player.set_node(None)

                # Reset the player; this will remove its actor-ref and clear
                # its calls/etc
                try:
                    with _ba.Context(activity):
                        player.reset()
                except Exception:
                    _error.print_exception(
                        'exception in player.reset in'
                        ' on_player_leave for player', player)

            # If we're a non-team session, remove the player's team completely.
            if not self._use_teams and team is not None:

                # If the team's in an activity, call its on_team_leave
                # callback.
                if activity is not None and team in activity.teams:
                    activity.teams.remove(team)

                    if not activity.is_expired():
                        try:
                            with _ba.Context(activity):
                                activity.on_team_leave(team)
                        except Exception:
                            _error.print_exception(
                                'exception in on_team_leave for activity',
                                activity)
                    else:
                        _error.print_error(
                            'expired activity in on_player_leave p2'
                            "; shouldn't happen")

                    # Clear the team's game-data (so dying stuff will
                    # have proper context).
                    try:
                        with _ba.Context(activity):
                            team.reset_gamedata()
                    except Exception:
                        _error.print_exception(
                            'exception clearing gamedata for team:', team,
                            'for player:', player, 'in activity:', activity)

                # Remove the team from the session.
                self.teams.remove(team)
                try:
                    with _ba.Context(self):
                        self.on_team_leave(team)
                except Exception:
                    _error.print_exception(
                        'exception in on_team_leave for session', self)

                # Clear the team's session-data (so dying stuff will
                # have proper context).
                try:
                    with _ba.Context(self):
                        team.reset_sessiondata()
                except Exception:
                    _error.print_exception(
                        'exception clearing sessiondata for team:', team,
                        'in session:', self)

            # Now remove them from the session list.
            self.players.remove(player)

        else:
            print('ERROR: Session.on_player_leave called'
                  ' for player not in our list.')
    def do_hit_at_position(self, pos: Sequence[float],
                           player: ba.Player) -> bool:
        """Handle a bomb hit at the given position."""
        # pylint: disable=too-many-statements
        from bastd.actor import popuptext
        activity = self.activity

        # Ignore hits if the game is over or if we've already been hit
        if activity.has_ended() or self._hit or not self._nodes:
            return False

        diff = (ba.Vec3(pos) - self._position)

        # Disregard Y difference. Our target point probably isn't exactly
        # on the ground anyway.
        diff[1] = 0.0
        dist = diff.length()

        bullseye = False
        if dist <= self._r3 + self._rfudge:
            # Inform our activity that we were hit
            self._hit = True
            activity.handlemessage(self.TargetHitMessage())
            keys: Dict[float, Sequence[float]] = {
                0.0: (1.0, 0.0, 0.0),
                0.049: (1.0, 0.0, 0.0),
                0.05: (1.0, 1.0, 1.0),
                0.1: (0.0, 1.0, 0.0)
            }
            cdull = (0.3, 0.3, 0.3)
            popupcolor: Sequence[float]
            if dist <= self._r1 + self._rfudge:
                bullseye = True
                self._nodes[1].color = cdull
                self._nodes[2].color = cdull
                ba.animate_array(self._nodes[0], 'color', 3, keys, loop=True)
                popupscale = 1.8
                popupcolor = (1, 1, 0, 1)
                streak = player.gamedata['streak']
                points = 10 + min(20, streak * 2)
                ba.playsound(ba.getsound('bellHigh'))
                if streak > 0:
                    ba.playsound(
                        ba.getsound(
                            'orchestraHit4' if streak > 3 else
                            'orchestraHit3' if streak > 2 else
                            'orchestraHit2' if streak > 1 else 'orchestraHit'))
            elif dist <= self._r2 + self._rfudge:
                self._nodes[0].color = cdull
                self._nodes[2].color = cdull
                ba.animate_array(self._nodes[1], 'color', 3, keys, loop=True)
                popupscale = 1.25
                popupcolor = (1, 0.5, 0.2, 1)
                points = 4
                ba.playsound(ba.getsound('bellMed'))
            else:
                self._nodes[0].color = cdull
                self._nodes[1].color = cdull
                ba.animate_array(self._nodes[2], 'color', 3, keys, loop=True)
                popupscale = 1.0
                popupcolor = (0.8, 0.3, 0.3, 1)
                points = 2
                ba.playsound(ba.getsound('bellLow'))

            # Award points/etc.. (technically should probably leave this up
            # to the activity).
            popupstr = '+' + str(points)

            # If there's more than 1 player in the game, include their
            # names and colors so they know who got the hit.
            if len(activity.players) > 1:
                popupcolor = ba.safecolor(player.color, target_intensity=0.75)
                popupstr += ' ' + player.get_name()
            popuptext.PopupText(popupstr,
                                position=self._position,
                                color=popupcolor,
                                scale=popupscale).autoretain()

            # Give this player's team points and update the score-board.
            player.team.gamedata['score'] += points
            assert isinstance(activity, TargetPracticeGame)
            activity.update_scoreboard()

            # Also give this individual player points
            # (only applies in teams mode).
            assert activity.stats is not None
            activity.stats.player_scored(player,
                                         points,
                                         showpoints=False,
                                         screenmessage=False)

            ba.animate_array(self._nodes[0], 'size', 1, {
                0.8: self._nodes[0].size,
                1.0: [0.0]
            })
            ba.animate_array(self._nodes[1], 'size', 1, {
                0.85: self._nodes[1].size,
                1.05: [0.0]
            })
            ba.animate_array(self._nodes[2], 'size', 1, {
                0.9: self._nodes[2].size,
                1.1: [0.0]
            })
            ba.timer(1.1, ba.Call(self.handlemessage, ba.DieMessage()))

        return bullseye
示例#8
0
    def __init__(self, player: ba.Player, respawn_time: float):
        """
        Instantiate with a given ba.Player and respawn_time (in seconds)
        """
        self._visible = True

        on_right, offs_extra, respawn_icons = self._get_context(player)

        try:
            mask_tex = (player.team.gamedata['_spaz_respawn_icons_mask_tex'])
        except Exception:
            mask_tex = player.team.gamedata['_spaz_respawn_icons_mask_tex'] = (
                ba.gettexture('characterIconMask'))

        # 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.get_name()),
                           '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)
示例#9
0
    def player_scored(self,
                      player: ba.Player,
                      base_points: int = 1,
                      target: Sequence[float] = None,
                      kill: bool = False,
                      victim_player: ba.Player = None,
                      scale: float = 1.0,
                      color: Sequence[float] = None,
                      title: Union[str, ba.Lstr] = None,
                      screenmessage: bool = True,
                      display: bool = True,
                      importance: int = 1,
                      showpoints: bool = True,
                      big_message: bool = False) -> int:
        """Register a score for the player.

        Return value is actual score with multipliers and such factored in.
        """
        # FIXME: Tidy this up.
        # pylint: disable=cyclic-import
        # pylint: disable=too-many-branches
        # pylint: disable=too-many-locals
        # pylint: disable=too-many-statements
        from bastd.actor.popuptext import PopupText
        from ba import _math
        from ba._gameactivity import GameActivity
        from ba._lang import Lstr
        del victim_player  # Currently unused.
        name = player.get_name()
        s_player = self._player_records[name]

        if kill:
            s_player.submit_kill(showpoints=showpoints)

        display_color: Sequence[float] = (1.0, 1.0, 1.0, 1.0)

        if color is not None:
            display_color = color
        elif importance != 1:
            display_color = (1.0, 1.0, 0.4, 1.0)
        points = base_points

        # If they want a big announcement, throw a zoom-text up there.
        if display and big_message:
            try:
                assert self._activity is not None
                activity = self._activity()
                if isinstance(activity, GameActivity):
                    name_full = player.get_name(full=True, icon=False)
                    activity.show_zoom_message(
                        Lstr(resource='nameScoresText',
                             subs=[('${NAME}', name_full)]),
                        color=_math.normalized_color(player.team.color))
            except Exception:
                print_exception('error showing big_message')

        # If we currently have a actor, pop up a score over it.
        if display and showpoints:
            our_pos = player.node.position if player.node else None
            if our_pos is not None:
                if target is None:
                    target = our_pos

                # If display-pos is *way* lower than us, raise it up
                # (so we can still see scores from dudes that fell off cliffs).
                display_pos = (target[0], max(target[1], our_pos[1] - 2.0),
                               min(target[2], our_pos[2] + 2.0))
                activity = self.getactivity()
                if activity is not None:
                    if title is not None:
                        sval = Lstr(value='+${A} ${B}',
                                    subs=[('${A}', str(points)),
                                          ('${B}', title)])
                    else:
                        sval = Lstr(value='+${A}',
                                    subs=[('${A}', str(points))])
                    PopupText(sval,
                              color=display_color,
                              scale=1.2 * scale,
                              position=display_pos).autoretain()

        # Tally kills.
        if kill:
            s_player.accum_kill_count += 1
            s_player.kill_count += 1

        # Report non-kill scorings.
        try:
            if screenmessage and not kill:
                _ba.screenmessage(Lstr(resource='nameScoresText',
                                       subs=[('${NAME}', name)]),
                                  top=True,
                                  color=player.color,
                                  image=player.get_icon())
        except Exception:
            print_exception('error announcing score')

        s_player.score += points
        s_player.accumscore += points

        # Inform a running game of the score.
        if points != 0:
            activity = self._activity() if self._activity is not None else None
            if activity is not None:
                activity.handlemessage(PlayerScoredMessage(score=points))

        return points
示例#10
0
 def player_got_hit(self, player: ba.Player) -> None:
     """Call this when a player got hit."""
     s_player = self._player_records[player.get_name()]
     s_player.streak = 0
示例#11
0
    def __init__(self,
                 player: ba.Player,
                 position: Tuple[float, float],
                 scale: float,
                 show_lives: bool = True,
                 show_death: bool = True,
                 name_scale: float = 1.0,
                 name_maxwidth: float = 115.0,
                 flatness: float = 1.0,
                 shadow: float = 1.0):
        super().__init__()

        self._player = player
        self._show_lives = show_lives
        self._show_death = show_death
        self._name_scale = name_scale
        self._outline_tex = ba.gettexture('characterIconMask')

        icon = player.get_icon()
        self.node = ba.newnode('image',
                               delegate=self,
                               attrs={
                                   'texture': icon['texture'],
                                   'tint_texture': icon['tint_texture'],
                                   'tint_color': icon['tint_color'],
                                   'vr_depth': 400,
                                   'tint2_color': icon['tint2_color'],
                                   'mask_texture': self._outline_tex,
                                   'opacity': 1.0,
                                   'absolute_scale': True,
                                   'attach': 'bottomCenter'
                               })
        self._name_text = ba.newnode(
            'text',
            owner=self.node,
            attrs={
                'text': ba.Lstr(value=player.get_name()),
                'color': ba.safecolor(player.team.color),
                'h_align': 'center',
                'v_align': 'center',
                'vr_depth': 410,
                'maxwidth': name_maxwidth,
                'shadow': shadow,
                'flatness': flatness,
                'h_attach': 'center',
                'v_attach': 'bottom'
            })
        if self._show_lives:
            self._lives_text = ba.newnode('text',
                                          owner=self.node,
                                          attrs={
                                              'text': 'x0',
                                              'color': (1, 1, 0.5),
                                              'h_align': 'left',
                                              'vr_depth': 430,
                                              'shadow': 1.0,
                                              'flatness': 1.0,
                                              'h_attach': 'center',
                                              'v_attach': 'bottom'
                                          })
        self.set_position_and_scale(position, scale)