def set_score_text(self, text: str) -> None:
     """Show a message over the flag; handy for scores."""
     if not self.node:
         return
     if not self._score_text:
         start_scale = 0.0
         math = ba.newnode('math',
                           owner=self.node,
                           attrs={
                               'input1': (0, 1.4, 0),
                               'operation': 'add'
                           })
         self.node.connectattr('position', math, 'input2')
         self._score_text = ba.newnode('text',
                                       owner=self.node,
                                       attrs={
                                           'text': text,
                                           'in_world': True,
                                           'scale': 0.02,
                                           'shadow': 0.5,
                                           'flatness': 1.0,
                                           'h_align': 'center'
                                       })
         math.connectattr('output', self._score_text, 'position')
     else:
         assert isinstance(self._score_text.scale, float)
         start_scale = self._score_text.scale
         self._score_text.text = text
     self._score_text.color = ba.safecolor(self.node.color)
     ba.animate(self._score_text, 'scale', {0: start_scale, 0.2: 0.02})
     self._score_text_hide_timer = ba.Timer(
         1.0, ba.WeakCall(self._hide_score_text))
Exemple #2
0
    def on_begin(self) -> None:
        # pylint: disable=too-many-branches
        # pylint: disable=too-many-locals
        # pylint: disable=too-many-statements
        from bastd.actor.text import Text
        from bastd.actor.image import Image
        from ba.deprecated import get_resource
        ba.set_analytics_screen('FreeForAll Series Victory Screen' if self.
                                _is_ffa else 'Teams Series Victory Screen')
        if ba.app.uiscale is ba.UIScale.LARGE:
            sval = ba.Lstr(resource='pressAnyKeyButtonPlayAgainText')
        else:
            sval = ba.Lstr(resource='pressAnyButtonPlayAgainText')
        self._show_up_next = False
        self._custom_continue_message = sval
        super().on_begin()
        winning_sessionteam = self.settings_raw['winner']

        # Pause a moment before playing victory music.
        ba.timer(0.6, ba.WeakCall(self._play_victory_music))
        ba.timer(4.4,
                 ba.WeakCall(self._show_winner, self.settings_raw['winner']))
        ba.timer(4.6, ba.Call(ba.playsound, self._score_display_sound))

        # Score / Name / Player-record.
        player_entries: List[Tuple[int, str, ba.PlayerRecord]] = []

        # Note: for ffa, exclude players who haven't entered the game yet.
        if self._is_ffa:
            for _pkey, prec in self.stats.get_records().items():
                if prec.player.in_game:
                    player_entries.append(
                        (prec.player.sessionteam.customdata['score'],
                         prec.getname(full=True), prec))
            player_entries.sort(reverse=True, key=lambda x: x[0])
        else:
            for _pkey, prec in self.stats.get_records().items():
                player_entries.append((prec.score, prec.name_full, prec))
            player_entries.sort(reverse=True, key=lambda x: x[0])

        ts_height = 300.0
        ts_h_offs = -390.0
        tval = 6.4
        t_incr = 0.12

        always_use_first_to = get_resource('bestOfUseFirstToInstead')

        session = self.session
        if self._is_ffa:
            assert isinstance(session, ba.FreeForAllSession)
            txt = ba.Lstr(
                value='${A}:',
                subs=[('${A}',
                       ba.Lstr(resource='firstToFinalText',
                               subs=[('${COUNT}',
                                      str(session.get_ffa_series_length()))]))
                      ])
        else:
            assert isinstance(session, ba.MultiTeamSession)

            # Some languages may prefer to always show 'first to X' instead of
            # 'best of X'.
            # FIXME: This will affect all clients connected to us even if
            #  they're not using this language. Should try to come up
            #  with a wording that works everywhere.
            if always_use_first_to:
                txt = ba.Lstr(
                    value='${A}:',
                    subs=[
                        ('${A}',
                         ba.Lstr(resource='firstToFinalText',
                                 subs=[
                                     ('${COUNT}',
                                      str(session.get_series_length() / 2 + 1))
                                 ]))
                    ])
            else:
                txt = ba.Lstr(
                    value='${A}:',
                    subs=[('${A}',
                           ba.Lstr(resource='bestOfFinalText',
                                   subs=[('${COUNT}',
                                          str(session.get_series_length()))]))
                          ])

        Text(txt,
             v_align=Text.VAlign.CENTER,
             maxwidth=300,
             color=(0.5, 0.5, 0.5, 1.0),
             position=(0, 220),
             scale=1.2,
             transition=Text.Transition.IN_TOP_SLOW,
             h_align=Text.HAlign.CENTER,
             transition_delay=t_incr * 4).autoretain()

        win_score = (session.get_series_length() - 1) // 2 + 1
        lose_score = 0
        for team in self.teams:
            if team.sessionteam.customdata['score'] != win_score:
                lose_score = team.sessionteam.customdata['score']

        if not self._is_ffa:
            Text(ba.Lstr(resource='gamesToText',
                         subs=[('${WINCOUNT}', str(win_score)),
                               ('${LOSECOUNT}', str(lose_score))]),
                 color=(0.5, 0.5, 0.5, 1.0),
                 maxwidth=160,
                 v_align=Text.VAlign.CENTER,
                 position=(0, -215),
                 scale=1.8,
                 transition=Text.Transition.IN_LEFT,
                 h_align=Text.HAlign.CENTER,
                 transition_delay=4.8 + t_incr * 4).autoretain()

        if self._is_ffa:
            v_extra = 120
        else:
            v_extra = 0

        mvp: Optional[ba.PlayerRecord] = None
        mvp_name: Optional[str] = None

        # Show game MVP.
        if not self._is_ffa:
            mvp, mvp_name = None, None
            for entry in player_entries:
                if entry[2].team == winning_sessionteam:
                    mvp = entry[2]
                    mvp_name = entry[1]
                    break
            if mvp is not None:
                Text(ba.Lstr(resource='mostValuablePlayerText'),
                     color=(0.5, 0.5, 0.5, 1.0),
                     v_align=Text.VAlign.CENTER,
                     maxwidth=300,
                     position=(180, ts_height / 2 + 15),
                     transition=Text.Transition.IN_LEFT,
                     h_align=Text.HAlign.LEFT,
                     transition_delay=tval).autoretain()
                tval += 4 * t_incr

                Image(mvp.get_icon(),
                      position=(230, ts_height / 2 - 55 + 14 - 5),
                      scale=(70, 70),
                      transition=Image.Transition.IN_LEFT,
                      transition_delay=tval).autoretain()
                assert mvp_name is not None
                Text(ba.Lstr(value=mvp_name),
                     position=(280, ts_height / 2 - 55 + 15 - 5),
                     h_align=Text.HAlign.LEFT,
                     v_align=Text.VAlign.CENTER,
                     maxwidth=170,
                     scale=1.3,
                     color=ba.safecolor(mvp.team.color + (1, )),
                     transition=Text.Transition.IN_LEFT,
                     transition_delay=tval).autoretain()
                tval += 4 * t_incr

        # Most violent.
        most_kills = 0
        for entry in player_entries:
            if entry[2].kill_count >= most_kills:
                mvp = entry[2]
                mvp_name = entry[1]
                most_kills = entry[2].kill_count
        if mvp is not None:
            Text(ba.Lstr(resource='mostViolentPlayerText'),
                 color=(0.5, 0.5, 0.5, 1.0),
                 v_align=Text.VAlign.CENTER,
                 maxwidth=300,
                 position=(180, ts_height / 2 - 150 + v_extra + 15),
                 transition=Text.Transition.IN_LEFT,
                 h_align=Text.HAlign.LEFT,
                 transition_delay=tval).autoretain()
            Text(ba.Lstr(value='(${A})',
                         subs=[('${A}',
                                ba.Lstr(resource='killsTallyText',
                                        subs=[('${COUNT}', str(most_kills))]))
                               ]),
                 position=(260, ts_height / 2 - 150 - 15 + v_extra),
                 color=(0.3, 0.3, 0.3, 1.0),
                 scale=0.6,
                 h_align=Text.HAlign.LEFT,
                 transition=Text.Transition.IN_LEFT,
                 transition_delay=tval).autoretain()
            tval += 4 * t_incr

            Image(mvp.get_icon(),
                  position=(233, ts_height / 2 - 150 - 30 - 46 + 25 + v_extra),
                  scale=(50, 50),
                  transition=Image.Transition.IN_LEFT,
                  transition_delay=tval).autoretain()
            assert mvp_name is not None
            Text(ba.Lstr(value=mvp_name),
                 position=(270, ts_height / 2 - 150 - 30 - 36 + v_extra + 15),
                 h_align=Text.HAlign.LEFT,
                 v_align=Text.VAlign.CENTER,
                 maxwidth=180,
                 color=ba.safecolor(mvp.team.color + (1, )),
                 transition=Text.Transition.IN_LEFT,
                 transition_delay=tval).autoretain()
            tval += 4 * t_incr

        # Most killed.
        most_killed = 0
        mkp, mkp_name = None, None
        for entry in player_entries:
            if entry[2].killed_count >= most_killed:
                mkp = entry[2]
                mkp_name = entry[1]
                most_killed = entry[2].killed_count
        if mkp is not None:
            Text(ba.Lstr(resource='mostViolatedPlayerText'),
                 color=(0.5, 0.5, 0.5, 1.0),
                 v_align=Text.VAlign.CENTER,
                 maxwidth=300,
                 position=(180, ts_height / 2 - 300 + v_extra + 15),
                 transition=Text.Transition.IN_LEFT,
                 h_align=Text.HAlign.LEFT,
                 transition_delay=tval).autoretain()
            Text(ba.Lstr(value='(${A})',
                         subs=[('${A}',
                                ba.Lstr(resource='deathsTallyText',
                                        subs=[('${COUNT}', str(most_killed))]))
                               ]),
                 position=(260, ts_height / 2 - 300 - 15 + v_extra),
                 h_align=Text.HAlign.LEFT,
                 scale=0.6,
                 color=(0.3, 0.3, 0.3, 1.0),
                 transition=Text.Transition.IN_LEFT,
                 transition_delay=tval).autoretain()
            tval += 4 * t_incr
            Image(mkp.get_icon(),
                  position=(233, ts_height / 2 - 300 - 30 - 46 + 25 + v_extra),
                  scale=(50, 50),
                  transition=Image.Transition.IN_LEFT,
                  transition_delay=tval).autoretain()
            assert mkp_name is not None
            Text(ba.Lstr(value=mkp_name),
                 position=(270, ts_height / 2 - 300 - 30 - 36 + v_extra + 15),
                 h_align=Text.HAlign.LEFT,
                 v_align=Text.VAlign.CENTER,
                 color=ba.safecolor(mkp.team.color + (1, )),
                 maxwidth=180,
                 transition=Text.Transition.IN_LEFT,
                 transition_delay=tval).autoretain()
            tval += 4 * t_incr

        # Now show individual scores.
        tdelay = tval
        Text(ba.Lstr(resource='finalScoresText'),
             color=(0.5, 0.5, 0.5, 1.0),
             position=(ts_h_offs, ts_height / 2),
             transition=Text.Transition.IN_RIGHT,
             transition_delay=tdelay).autoretain()
        tdelay += 4 * t_incr

        v_offs = 0.0
        tdelay += len(player_entries) * 8 * t_incr
        for _score, name, prec in player_entries:
            tdelay -= 4 * t_incr
            v_offs -= 40
            Text(str(prec.team.customdata['score'])
                 if self._is_ffa else str(prec.score),
                 color=(0.5, 0.5, 0.5, 1.0),
                 position=(ts_h_offs + 230, ts_height / 2 + v_offs),
                 h_align=Text.HAlign.RIGHT,
                 transition=Text.Transition.IN_RIGHT,
                 transition_delay=tdelay).autoretain()
            tdelay -= 4 * t_incr

            Image(prec.get_icon(),
                  position=(ts_h_offs - 72, ts_height / 2 + v_offs + 15),
                  scale=(30, 30),
                  transition=Image.Transition.IN_LEFT,
                  transition_delay=tdelay).autoretain()
            Text(ba.Lstr(value=name),
                 position=(ts_h_offs - 50, ts_height / 2 + v_offs + 15),
                 h_align=Text.HAlign.LEFT,
                 v_align=Text.VAlign.CENTER,
                 maxwidth=180,
                 color=ba.safecolor(prec.team.color + (1, )),
                 transition=Text.Transition.IN_RIGHT,
                 transition_delay=tdelay).autoretain()

        ba.timer(15.0, ba.WeakCall(self._show_tips))
Exemple #3
0
    def _on_query_response(self, data: Optional[Dict[str, Any]]) -> None:
        # FIXME: Tidy this up.
        # pylint: disable=too-many-locals
        # pylint: disable=too-many-branches
        # pylint: disable=too-many-statements
        # pylint: disable=too-many-nested-blocks
        if data is None:
            ba.textwidget(
                edit=self._loading_text,
                text=ba.Lstr(resource='internal.unavailableNoConnectionText'))
        else:
            try:
                self._loading_text.delete()
                trophystr = ''
                try:
                    trophystr = data['trophies']
                    num = 10
                    chunks = [
                        trophystr[i:i + num]
                        for i in range(0, len(trophystr), num)
                    ]
                    trophystr = ('\n\n'.join(chunks))
                    if trophystr == '':
                        trophystr = '-'
                except Exception:
                    ba.print_exception('Error displaying trophies.')
                account_name_spacing = 15
                tscale = 0.65
                ts_height = _ba.get_string_height(trophystr,
                                                  suppress_warning=True)
                sub_width = self._width - 80
                sub_height = 200 + ts_height * tscale + \
                    account_name_spacing * len(data['accountDisplayStrings'])
                self._subcontainer = ba.containerwidget(
                    parent=self._scrollwidget,
                    size=(sub_width, sub_height),
                    background=False)
                v = sub_height - 20

                title_scale = 0.37
                center = 0.3
                maxwidth_scale = 0.45
                showing_character = False
                if data['profileDisplayString'] is not None:
                    tint_color = (1, 1, 1)
                    try:
                        if data['profile'] is not None:
                            profile = data['profile']
                            character = ba.app.spaz_appearances.get(
                                profile['character'], None)
                            if character is not None:
                                tint_color = (profile['color'] if 'color'
                                              in profile else (1, 1, 1))
                                tint2_color = (profile['highlight']
                                               if 'highlight' in profile else
                                               (1, 1, 1))
                                icon_tex = character.icon_texture
                                tint_tex = character.icon_mask_texture
                                mask_texture = ba.gettexture(
                                    'characterIconMask')
                                ba.imagewidget(
                                    parent=self._subcontainer,
                                    position=(sub_width * center - 40, v - 80),
                                    size=(80, 80),
                                    color=(1, 1, 1),
                                    mask_texture=mask_texture,
                                    texture=ba.gettexture(icon_tex),
                                    tint_texture=ba.gettexture(tint_tex),
                                    tint_color=tint_color,
                                    tint2_color=tint2_color)
                                v -= 95
                    except Exception:
                        ba.print_exception('Error displaying character.')
                    ba.textwidget(
                        parent=self._subcontainer,
                        size=(0, 0),
                        position=(sub_width * center, v),
                        h_align='center',
                        v_align='center',
                        scale=0.9,
                        color=ba.safecolor(tint_color, 0.7),
                        shadow=1.0,
                        text=ba.Lstr(value=data['profileDisplayString']),
                        maxwidth=sub_width * maxwidth_scale * 0.75)
                    showing_character = True
                    v -= 33

                center = 0.75 if showing_character else 0.5
                maxwidth_scale = 0.45 if showing_character else 0.9

                v = sub_height - 20
                if len(data['accountDisplayStrings']) <= 1:
                    account_title = ba.Lstr(
                        resource='settingsWindow.accountText')
                else:
                    account_title = ba.Lstr(
                        resource='accountSettingsWindow.accountsText',
                        fallback_resource='settingsWindow.accountText')
                ba.textwidget(parent=self._subcontainer,
                              size=(0, 0),
                              position=(sub_width * center, v),
                              flatness=1.0,
                              h_align='center',
                              v_align='center',
                              scale=title_scale,
                              color=ba.app.infotextcolor,
                              text=account_title,
                              maxwidth=sub_width * maxwidth_scale)
                draw_small = (showing_character
                              or len(data['accountDisplayStrings']) > 1)
                v -= 14 if draw_small else 20
                for account_string in data['accountDisplayStrings']:
                    ba.textwidget(parent=self._subcontainer,
                                  size=(0, 0),
                                  position=(sub_width * center, v),
                                  h_align='center',
                                  v_align='center',
                                  scale=0.55 if draw_small else 0.8,
                                  text=account_string,
                                  maxwidth=sub_width * maxwidth_scale)
                    v -= account_name_spacing

                v += account_name_spacing
                v -= 25 if showing_character else 29

                ba.textwidget(parent=self._subcontainer,
                              size=(0, 0),
                              position=(sub_width * center, v),
                              flatness=1.0,
                              h_align='center',
                              v_align='center',
                              scale=title_scale,
                              color=ba.app.infotextcolor,
                              text=ba.Lstr(resource='rankText'),
                              maxwidth=sub_width * maxwidth_scale)
                v -= 14
                if data['rank'] is None:
                    rank_str = '-'
                    suffix_offset = None
                else:
                    str_raw = ba.Lstr(
                        resource='league.rankInLeagueText').evaluate()
                    # FIXME: Would be nice to not have to eval this.
                    rank_str = ba.Lstr(
                        resource='league.rankInLeagueText',
                        subs=[('${RANK}', str(data['rank'][2])),
                              ('${NAME}',
                               ba.Lstr(translate=('leagueNames',
                                                  data['rank'][0]))),
                              ('${SUFFIX}', '')]).evaluate()
                    rank_str_width = min(
                        sub_width * maxwidth_scale,
                        _ba.get_string_width(rank_str, suppress_warning=True) *
                        0.55)

                    # Only tack our suffix on if its at the end and only for
                    # non-diamond leagues.
                    if (str_raw.endswith('${SUFFIX}')
                            and data['rank'][0] != 'Diamond'):
                        suffix_offset = rank_str_width * 0.5 + 2
                    else:
                        suffix_offset = None

                ba.textwidget(parent=self._subcontainer,
                              size=(0, 0),
                              position=(sub_width * center, v),
                              h_align='center',
                              v_align='center',
                              scale=0.55,
                              text=rank_str,
                              maxwidth=sub_width * maxwidth_scale)
                if suffix_offset is not None:
                    assert data['rank'] is not None
                    ba.textwidget(parent=self._subcontainer,
                                  size=(0, 0),
                                  position=(sub_width * center + suffix_offset,
                                            v + 3),
                                  h_align='left',
                                  v_align='center',
                                  scale=0.29,
                                  flatness=1.0,
                                  text='[' + str(data['rank'][1]) + ']')
                v -= 14

                str_raw = ba.Lstr(
                    resource='league.rankInLeagueText').evaluate()
                old_offs = -50
                prev_ranks_shown = 0
                for prev_rank in data['prevRanks']:
                    rank_str = ba.Lstr(
                        value='${S}:    ${I}',
                        subs=[
                            ('${S}',
                             ba.Lstr(resource='league.seasonText',
                                     subs=[('${NUMBER}', str(prev_rank[0]))])),
                            ('${I}',
                             ba.Lstr(resource='league.rankInLeagueText',
                                     subs=[('${RANK}', str(prev_rank[3])),
                                           ('${NAME}',
                                            ba.Lstr(translate=('leagueNames',
                                                               prev_rank[1]))),
                                           ('${SUFFIX}', '')]))
                        ]).evaluate()
                    rank_str_width = min(
                        sub_width * maxwidth_scale,
                        _ba.get_string_width(rank_str, suppress_warning=True) *
                        0.3)

                    # Only tack our suffix on if its at the end and only for
                    # non-diamond leagues.
                    if (str_raw.endswith('${SUFFIX}')
                            and prev_rank[1] != 'Diamond'):
                        suffix_offset = rank_str_width + 2
                    else:
                        suffix_offset = None
                    ba.textwidget(parent=self._subcontainer,
                                  size=(0, 0),
                                  position=(sub_width * center + old_offs, v),
                                  h_align='left',
                                  v_align='center',
                                  scale=0.3,
                                  text=rank_str,
                                  flatness=1.0,
                                  maxwidth=sub_width * maxwidth_scale)
                    if suffix_offset is not None:
                        ba.textwidget(parent=self._subcontainer,
                                      size=(0, 0),
                                      position=(sub_width * center + old_offs +
                                                suffix_offset, v + 1),
                                      h_align='left',
                                      v_align='center',
                                      scale=0.20,
                                      flatness=1.0,
                                      text='[' + str(prev_rank[2]) + ']')
                    prev_ranks_shown += 1
                    v -= 10

                v -= 13

                ba.textwidget(parent=self._subcontainer,
                              size=(0, 0),
                              position=(sub_width * center, v),
                              flatness=1.0,
                              h_align='center',
                              v_align='center',
                              scale=title_scale,
                              color=ba.app.infotextcolor,
                              text=ba.Lstr(resource='achievementsText'),
                              maxwidth=sub_width * maxwidth_scale)
                v -= 14
                ba.textwidget(parent=self._subcontainer,
                              size=(0, 0),
                              position=(sub_width * center, v),
                              h_align='center',
                              v_align='center',
                              scale=0.55,
                              text=str(data['achievementsCompleted']) + ' / ' +
                              str(len(ba.app.achievements)),
                              maxwidth=sub_width * maxwidth_scale)
                v -= 25

                if prev_ranks_shown == 0 and showing_character:
                    v -= 20
                elif prev_ranks_shown == 1 and showing_character:
                    v -= 10

                center = 0.5
                maxwidth_scale = 0.9

                ba.textwidget(parent=self._subcontainer,
                              size=(0, 0),
                              position=(sub_width * center, v),
                              h_align='center',
                              v_align='center',
                              scale=title_scale,
                              color=ba.app.infotextcolor,
                              flatness=1.0,
                              text=ba.Lstr(resource='trophiesThisSeasonText',
                                           fallback_resource='trophiesText'),
                              maxwidth=sub_width * maxwidth_scale)
                v -= 19
                ba.textwidget(parent=self._subcontainer,
                              size=(0, ts_height),
                              position=(sub_width * 0.5,
                                        v - ts_height * tscale),
                              h_align='center',
                              v_align='top',
                              corner_scale=tscale,
                              text=trophystr)

            except Exception:
                ba.print_exception('Error displaying account info.')
    def show_player_scores(self,
                           delay: float = 2.5,
                           results: Optional[ba.TeamGameResults] = None,
                           scale: float = 1.0,
                           x_offset: float = 0.0,
                           y_offset: float = 0.0) -> None:
        """Show scores for individual players."""
        # pylint: disable=too-many-locals
        # pylint: disable=too-many-statements
        from bastd.actor.text import Text
        from bastd.actor.image import Image

        ts_v_offset = 150.0 + y_offset
        ts_h_offs = 80.0 + x_offset
        tdelay = delay
        spacing = 40

        is_free_for_all = isinstance(self.session, ba.FreeForAllSession)

        def _get_prec_score(p_rec: ba.PlayerRecord) -> Optional[int]:
            if is_free_for_all and results is not None:
                assert isinstance(results, ba.TeamGameResults)
                val = results.get_team_score(p_rec.team)
                return val
            return p_rec.accumscore

        def _get_prec_score_str(p_rec: ba.PlayerRecord) -> Union[str, ba.Lstr]:
            if is_free_for_all and results is not None:
                assert isinstance(results, ba.TeamGameResults)
                val = results.get_team_score_str(p_rec.team)
                assert val is not None
                return val
            return str(p_rec.accumscore)

        # stats.get_records() can return players that are no longer in
        # the game.. if we're using results we have to filter those out
        # (since they're not in results and that's where we pull their
        # scores from)
        if results is not None:
            assert isinstance(results, ba.TeamGameResults)
            player_records = []
            assert self.stats
            valid_players = list(self.stats.get_records().items())

            def _get_player_score_set_entry(
                    player: ba.Player) -> Optional[ba.PlayerRecord]:
                for p_rec in valid_players:
                    # PyCharm incorrectly thinks valid_players is a List[str]
                    # noinspection PyUnresolvedReferences
                    if p_rec[1].player is player:
                        return p_rec[1]
                return None

            # Results is already sorted; just convert it into a list of
            # score-set-entries.
            for winner in results.get_winners():
                for team in winner.teams:
                    if len(team.players) == 1:
                        player_entry = _get_player_score_set_entry(
                            team.players[0])
                        if player_entry is not None:
                            player_records.append(player_entry)
        else:
            player_records = []
            player_records_scores = [
                (_get_prec_score(p), name, p)
                for name, p in list(self.stats.get_records().items())
            ]
            player_records_scores.sort(reverse=True)

            # Just want living player entries.
            player_records = [p[2] for p in player_records_scores if p[2]]

        v_offs = -140.0 + spacing * len(player_records) * 0.5

        def _txt(x_offs: float,
                 y_offs: float,
                 text: ba.Lstr,
                 h_align: Text.HAlign = Text.HAlign.RIGHT,
                 extrascale: float = 1.0,
                 maxwidth: Optional[float] = 120.0) -> None:
            Text(text,
                 color=(0.5, 0.5, 0.6, 0.5),
                 position=(ts_h_offs + x_offs * scale,
                           ts_v_offset + (v_offs + y_offs + 4.0) * scale),
                 h_align=h_align,
                 v_align=Text.VAlign.CENTER,
                 scale=0.8 * scale * extrascale,
                 maxwidth=maxwidth,
                 transition=Text.Transition.IN_LEFT,
                 transition_delay=tdelay).autoretain()

        session = self.session
        assert isinstance(session, ba.MultiTeamSession)
        tval = ba.Lstr(resource='gameLeadersText',
                       subs=[('${COUNT}', str(session.get_game_number()))])
        _txt(180,
             43,
             tval,
             h_align=Text.HAlign.CENTER,
             extrascale=1.4,
             maxwidth=None)
        _txt(-15, 4, ba.Lstr(resource='playerText'), h_align=Text.HAlign.LEFT)
        _txt(180, 4, ba.Lstr(resource='killsText'))
        _txt(280, 4, ba.Lstr(resource='deathsText'), maxwidth=100)

        score_name = 'Score' if results is None else results.get_score_name()
        translated = ba.Lstr(translate=('scoreNames', score_name))

        _txt(390, 0, translated)

        topkillcount = 0
        topkilledcount = 99999
        top_score = 0 if not player_records else _get_prec_score(
            player_records[0])

        for prec in player_records:
            topkillcount = max(topkillcount, prec.accum_kill_count)
            topkilledcount = min(topkilledcount, prec.accum_killed_count)

        def _scoretxt(text: Union[str, ba.Lstr],
                      x_offs: float,
                      highlight: bool,
                      delay2: float,
                      maxwidth: float = 70.0) -> None:
            Text(text,
                 position=(ts_h_offs + x_offs * scale,
                           ts_v_offset + (v_offs + 15) * scale),
                 scale=scale,
                 color=(1.0, 0.9, 0.5, 1.0) if highlight else
                 (0.5, 0.5, 0.6, 0.5),
                 h_align=Text.HAlign.RIGHT,
                 v_align=Text.VAlign.CENTER,
                 maxwidth=maxwidth,
                 transition=Text.Transition.IN_LEFT,
                 transition_delay=tdelay + delay2).autoretain()

        for playerrec in player_records:
            tdelay += 0.05
            v_offs -= spacing
            Image(playerrec.get_icon(),
                  position=(ts_h_offs - 12 * scale,
                            ts_v_offset + (v_offs + 15.0) * scale),
                  scale=(30.0 * scale, 30.0 * scale),
                  transition=Image.Transition.IN_LEFT,
                  transition_delay=tdelay).autoretain()
            Text(ba.Lstr(value=playerrec.get_name(full=True)),
                 maxwidth=160,
                 scale=0.75 * scale,
                 position=(ts_h_offs + 10.0 * scale,
                           ts_v_offset + (v_offs + 15) * scale),
                 h_align=Text.HAlign.LEFT,
                 v_align=Text.VAlign.CENTER,
                 color=ba.safecolor(playerrec.team.color + (1, )),
                 transition=Text.Transition.IN_LEFT,
                 transition_delay=tdelay).autoretain()
            _scoretxt(str(playerrec.accum_kill_count), 180,
                      playerrec.accum_kill_count == topkillcount, 0.1)
            _scoretxt(str(playerrec.accum_killed_count), 280,
                      playerrec.accum_killed_count == topkilledcount, 0.1)
            _scoretxt(_get_prec_score_str(playerrec), 390,
                      _get_prec_score(playerrec) == top_score, 0.2)
Exemple #5
0
    def _refresh(self) -> None:
        # pylint: disable=too-many-locals
        from ba.internal import (PlayerProfilesChangedMessage,
                                 get_player_profile_colors,
                                 get_player_profile_icon)
        old_selection = self._selected_profile

        # Delete old.
        while self._profile_widgets:
            self._profile_widgets.pop().delete()
        try:
            self._profiles = ba.app.config['Player Profiles']
        except Exception:
            self._profiles = {}
        assert self._profiles is not None
        items = list(self._profiles.items())
        items.sort(key=lambda x: x[0].lower())
        index = 0
        account_name: Optional[str]
        if _ba.get_account_state() == 'signed_in':
            account_name = _ba.get_account_display_string()
        else:
            account_name = None
        widget_to_select = None
        for p_name, _ in items:
            if p_name == '__account__' and account_name is None:
                continue
            color, _highlight = get_player_profile_colors(p_name)
            scl = 1.1
            txtw = ba.textwidget(
                parent=self._columnwidget,
                position=(0, 32),
                size=((self._width - 40) / scl, 28),
                text=ba.Lstr(
                    value=account_name if p_name ==
                    '__account__' else get_player_profile_icon(p_name) +
                    p_name),
                h_align='left',
                v_align='center',
                on_select_call=ba.WeakCall(self._select, p_name, index),
                maxwidth=self._scroll_width * 0.92,
                corner_scale=scl,
                color=ba.safecolor(color, 0.4),
                always_highlight=True,
                on_activate_call=ba.Call(self._edit_button.activate),
                selectable=True)
            if index == 0:
                ba.widget(edit=txtw, up_widget=self._back_button)
            ba.widget(edit=txtw, show_buffer_top=40, show_buffer_bottom=40)
            self._profile_widgets.append(txtw)

            # Select/show this one if it was previously selected
            # (but defer till after this loop since our height is
            # still changing).
            if p_name == old_selection:
                widget_to_select = txtw

            index += 1

        if widget_to_select is not None:
            ba.columnwidget(edit=self._columnwidget,
                            selected_child=widget_to_select,
                            visible_child=widget_to_select)

        # If there's a team-chooser in existence, tell it the profile-list
        # has probably changed.
        session = _ba.get_foreground_host_session()
        if session is not None:
            session.handlemessage(PlayerProfilesChangedMessage())
Exemple #6
0
    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)
Exemple #7
0
    def __init__(self,
                 player: 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.getname()),
                                         '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)
def show_player_scores(self,
                       delay: float = 2.5,
                       results: Optional[ba.GameResults] = None,
                       scale: float = 1.0,
                       x_offset: float = 0.0,
                       y_offset: float = 0.0) -> None:
    """Show scores for individual players."""
    # pylint: disable=too-many-locals
    # pylint: disable=too-many-statements

    ts_v_offset = 150.0 + y_offset
    ts_h_offs = 80.0 + x_offset
    tdelay = delay
    spacing = 40

    is_free_for_all = isinstance(self.session, ba.FreeForAllSession)

    is_two_team = True if len(self.session.sessionteams) == 2 else False

    def _get_prec_score(p_rec: ba.PlayerRecord) -> Optional[int]:
        if is_free_for_all and results is not None:
            assert isinstance(results, ba.GameResults)
            assert p_rec.team.activityteam is not None
            val = results.get_sessionteam_score(p_rec.team)
            return val
        return p_rec.accumscore

    def _get_prec_score_str(p_rec: ba.PlayerRecord) -> Union[str, ba.Lstr]:
        if is_free_for_all and results is not None:
            assert isinstance(results, ba.GameResults)
            assert p_rec.team.activityteam is not None
            val = results.get_sessionteam_score_str(p_rec.team)
            assert val is not None
            return val
        return str(p_rec.accumscore)

    # stats.get_records() can return players that are no longer in
    # the game.. if we're using results we have to filter those out
    # (since they're not in results and that's where we pull their
    # scores from)
    if results is not None:
        assert isinstance(results, ba.GameResults)
        player_records = []
        assert self.stats
        valid_players = list(self.stats.get_records().items())

        def _get_player_score_set_entry(
                player: ba.SessionPlayer) -> Optional[ba.PlayerRecord]:
            for p_rec in valid_players:
                if p_rec[1].player is player:
                    return p_rec[1]
            return None

        # Results is already sorted; just convert it into a list of
        # score-set-entries.
        for winnergroup in results.winnergroups:
            for team in winnergroup.teams:
                if len(team.players) == 1:
                    player_entry = _get_player_score_set_entry(team.players[0])
                    if player_entry is not None:
                        player_records.append(player_entry)
    else:
        player_records = []
        player_records_scores = [
            (_get_prec_score(p), name, p)
            for name, p in list(self.stats.get_records().items())
        ]
        player_records_scores.sort(reverse=True)

        # Just want living player entries.
        player_records = [p[2] for p in player_records_scores if p[2]]

    voffs = -140.0 + spacing * 5 * 0.5

    voffs_team0 = voffs
    tdelay_team0 = tdelay

    def _txt(xoffs: float,
             yoffs: float,
             text: ba.Lstr,
             h_align: Text.HAlign = Text.HAlign.RIGHT,
             extrascale: float = 1.0,
             maxwidth: Optional[float] = 120.0) -> None:
        Text(text,
             color=(0.5, 0.5, 0.6, 0.5),
             position=(ts_h_offs + xoffs * scale,
                       ts_v_offset + (voffs + yoffs + 4.0) * scale),
             h_align=h_align,
             v_align=Text.VAlign.CENTER,
             scale=0.8 * scale * extrascale,
             maxwidth=maxwidth,
             transition=Text.Transition.IN_LEFT,
             transition_delay=tdelay).autoretain()

    session = self.session
    assert isinstance(session, ba.MultiTeamSession)
    if is_two_team:
        tval = "Game " + str(session.get_game_number()) + " Results"
        _txt(-75,
             160,
             tval,
             h_align=Text.HAlign.CENTER,
             extrascale=1.4,
             maxwidth=None)
    _txt(-15, 4, ba.Lstr(resource='playerText'), h_align=Text.HAlign.LEFT)
    _txt(180, 4, ba.Lstr(resource='killsText'))
    _txt(280, 4, ba.Lstr(resource='deathsText'), maxwidth=100)

    score_label = 'Score' if results is None else results.score_label
    translated = ba.Lstr(translate=('scoreNames', score_label))

    _txt(390, 0, translated)

    if is_two_team:
        _txt(-595, 4, ba.Lstr(resource='playerText'), h_align=Text.HAlign.LEFT)
        _txt(-400, 4, ba.Lstr(resource='killsText'))
        _txt(-300, 4, ba.Lstr(resource='deathsText'), maxwidth=100)
        _txt(-190, 0, translated)

    topkillcount = 0
    topkilledcount = 99999
    top_score = 0 if not player_records else _get_prec_score(player_records[0])

    for prec in player_records:
        topkillcount = max(topkillcount, prec.accum_kill_count)
        topkilledcount = min(topkilledcount, prec.accum_killed_count)

    def _scoretxt(text: Union[str, ba.Lstr],
                  x_offs: float,
                  highlight: bool,
                  delay2: float,
                  maxwidth: float = 70.0,
                  team_id=1) -> None:

        Text(text,
             position=(ts_h_offs + x_offs * scale,
                       ts_v_offset + (voffs + 15) * scale) if team_id == 1 else
             (ts_h_offs + x_offs * scale,
              ts_v_offset + (voffs_team0 + 15) * scale),
             scale=scale,
             color=(1.0, 0.9, 0.5, 1.0) if highlight else (0.5, 0.5, 0.6, 0.5),
             h_align=Text.HAlign.RIGHT,
             v_align=Text.VAlign.CENTER,
             maxwidth=maxwidth,
             transition=Text.Transition.IN_LEFT,
             transition_delay=(tdelay + delay2) if team_id == 1 else
             (tdelay_team0 + delay2)).autoretain()

    for playerrec in player_records:
        if is_two_team and playerrec.team.id == 0:
            tdelay_team0 += 0.05
            voffs_team0 -= spacing
            x_image = 617
            x_text = -595
            y = ts_v_offset + (voffs_team0 + 15.0) * scale

        else:
            tdelay += 0.05
            voffs -= spacing
            x_image = 12
            x_text = 10.0
            y = ts_v_offset + (voffs + 15.0) * scale

        Image(playerrec.get_icon(),
              position=(ts_h_offs - x_image * scale, y),
              scale=(30.0 * scale, 30.0 * scale),
              transition=Image.Transition.IN_LEFT,
              transition_delay=tdelay
              if playerrec.team.id == 1 else tdelay_team0).autoretain()
        Text(ba.Lstr(value=playerrec.getname(full=True)),
             maxwidth=160,
             scale=0.75 * scale,
             position=(ts_h_offs + x_text * scale, y),
             h_align=Text.HAlign.LEFT,
             v_align=Text.VAlign.CENTER,
             color=ba.safecolor(playerrec.team.color + (1, )),
             transition=Text.Transition.IN_LEFT,
             transition_delay=tdelay
             if playerrec.team.id == 1 else tdelay_team0).autoretain()

        if is_two_team and playerrec.team.id == 0:
            _scoretxt(str(playerrec.accum_kill_count),
                      -400,
                      playerrec.accum_kill_count == topkillcount,
                      0.1,
                      team_id=0)
            _scoretxt(str(playerrec.accum_killed_count),
                      -300,
                      playerrec.accum_killed_count == topkilledcount,
                      0.1,
                      team_id=0)
            _scoretxt(_get_prec_score_str(playerrec),
                      -190,
                      _get_prec_score(playerrec) == top_score,
                      0.2,
                      team_id=0)
        else:
            _scoretxt(str(playerrec.accum_kill_count), 180,
                      playerrec.accum_kill_count == topkillcount, 0.1)
            _scoretxt(str(playerrec.accum_killed_count), 280,
                      playerrec.accum_killed_count == topkilledcount, 0.1)
            _scoretxt(_get_prec_score_str(playerrec), 390,
                      _get_prec_score(playerrec) == top_score, 0.2)
    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
    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 on_begin(self) -> None:
        # pylint: disable=too-many-locals
        # pylint: disable=too-many-statements
        from bastd.actor.text import Text
        from bastd.actor.image import Image
        ba.set_analytics_screen('FreeForAll Score Screen')
        super().on_begin()

        y_base = 100.0
        ts_h_offs = -305.0
        tdelay = 1.0
        scale = 1.2
        spacing = 37.0

        # We include name and previous score in the sort to reduce the amount
        # of random jumping around the list we do in cases of ties.
        player_order_prev = list(self.players)
        player_order_prev.sort(
            reverse=True,
            key=lambda p: (
                p.team.sessionteam.customdata['previous_score'],
                p.getname(full=True),
            ))
        player_order = list(self.players)
        player_order.sort(reverse=True,
                          key=lambda p: (
                              p.team.sessionteam.customdata['score'],
                              p.team.sessionteam.customdata['score'],
                              p.getname(full=True),
                          ))

        v_offs = -74.0 + spacing * len(player_order_prev) * 0.5
        delay1 = 1.3 + 0.1
        delay2 = 2.9 + 0.1
        delay3 = 2.9 + 0.1
        order_change = player_order != player_order_prev

        if order_change:
            delay3 += 1.5

        ba.timer(0.3, ba.Call(ba.playsound, self._score_display_sound))
        results = self.settings_raw['results']
        assert isinstance(results, ba.GameResults)
        self.show_player_scores(delay=0.001,
                                results=results,
                                scale=1.2,
                                x_offset=-110.0)

        sound_times: Set[float] = set()

        def _scoretxt(text: str,
                      x_offs: float,
                      y_offs: float,
                      highlight: bool,
                      delay: float,
                      extrascale: float,
                      flash: bool = False) -> Text:
            return Text(text,
                        position=(ts_h_offs + x_offs * scale,
                                  y_base + (y_offs + v_offs + 2.0) * scale),
                        scale=scale * extrascale,
                        color=((1.0, 0.7, 0.3, 1.0) if highlight else
                               (0.7, 0.7, 0.7, 0.7)),
                        h_align=Text.HAlign.RIGHT,
                        transition=Text.Transition.IN_LEFT,
                        transition_delay=tdelay + delay,
                        flash=flash).autoretain()

        v_offs -= spacing
        slide_amt = 0.0
        transtime = 0.250
        transtime2 = 0.250

        session = self.session
        assert isinstance(session, ba.FreeForAllSession)
        title = Text(ba.Lstr(resource='firstToSeriesText',
                             subs=[('${COUNT}',
                                    str(session.get_ffa_series_length()))]),
                     scale=1.05 * scale,
                     position=(ts_h_offs - 0.0 * scale,
                               y_base + (v_offs + 50.0) * scale),
                     h_align=Text.HAlign.CENTER,
                     color=(0.5, 0.5, 0.5, 0.5),
                     transition=Text.Transition.IN_LEFT,
                     transition_delay=tdelay).autoretain()

        v_offs -= 25
        v_offs_start = v_offs

        ba.timer(
            tdelay + delay3,
            ba.WeakCall(
                self._safe_animate, title.position_combine, 'input0', {
                    0.0: ts_h_offs - 0.0 * scale,
                    transtime2: ts_h_offs - (0.0 + slide_amt) * scale
                }))

        for i, player in enumerate(player_order_prev):
            v_offs_2 = v_offs_start - spacing * (player_order.index(player))
            ba.timer(tdelay + 0.3,
                     ba.Call(ba.playsound, self._score_display_sound_small))
            if order_change:
                ba.timer(tdelay + delay2 + 0.1,
                         ba.Call(ba.playsound, self._cymbal_sound))
            img = Image(player.get_icon(),
                        position=(ts_h_offs - 72.0 * scale,
                                  y_base + (v_offs + 15.0) * scale),
                        scale=(30.0 * scale, 30.0 * scale),
                        transition=Image.Transition.IN_LEFT,
                        transition_delay=tdelay).autoretain()
            ba.timer(
                tdelay + delay2,
                ba.WeakCall(
                    self._safe_animate, img.position_combine, 'input1', {
                        0: y_base + (v_offs + 15.0) * scale,
                        transtime: y_base + (v_offs_2 + 15.0) * scale
                    }))
            ba.timer(
                tdelay + delay3,
                ba.WeakCall(
                    self._safe_animate, img.position_combine, 'input0', {
                        0: ts_h_offs - 72.0 * scale,
                        transtime2: ts_h_offs - (72.0 + slide_amt) * scale
                    }))
            txt = Text(ba.Lstr(value=player.getname(full=True)),
                       maxwidth=130.0,
                       scale=0.75 * scale,
                       position=(ts_h_offs - 50.0 * scale,
                                 y_base + (v_offs + 15.0) * scale),
                       h_align=Text.HAlign.LEFT,
                       v_align=Text.VAlign.CENTER,
                       color=ba.safecolor(player.team.color + (1, )),
                       transition=Text.Transition.IN_LEFT,
                       transition_delay=tdelay).autoretain()
            ba.timer(
                tdelay + delay2,
                ba.WeakCall(
                    self._safe_animate, txt.position_combine, 'input1', {
                        0: y_base + (v_offs + 15.0) * scale,
                        transtime: y_base + (v_offs_2 + 15.0) * scale
                    }))
            ba.timer(
                tdelay + delay3,
                ba.WeakCall(
                    self._safe_animate, txt.position_combine, 'input0', {
                        0: ts_h_offs - 50.0 * scale,
                        transtime2: ts_h_offs - (50.0 + slide_amt) * scale
                    }))

            txt_num = Text('#' + str(i + 1),
                           scale=0.55 * scale,
                           position=(ts_h_offs - 95.0 * scale,
                                     y_base + (v_offs + 8.0) * scale),
                           h_align=Text.HAlign.RIGHT,
                           color=(0.6, 0.6, 0.6, 0.6),
                           transition=Text.Transition.IN_LEFT,
                           transition_delay=tdelay).autoretain()
            ba.timer(
                tdelay + delay3,
                ba.WeakCall(
                    self._safe_animate, txt_num.position_combine, 'input0', {
                        0: ts_h_offs - 95.0 * scale,
                        transtime2: ts_h_offs - (95.0 + slide_amt) * scale
                    }))

            s_txt = _scoretxt(
                str(player.team.sessionteam.customdata['previous_score']), 80,
                0, False, 0, 1.0)
            ba.timer(
                tdelay + delay2,
                ba.WeakCall(
                    self._safe_animate, s_txt.position_combine, 'input1', {
                        0: y_base + (v_offs + 2.0) * scale,
                        transtime: y_base + (v_offs_2 + 2.0) * scale
                    }))
            ba.timer(
                tdelay + delay3,
                ba.WeakCall(
                    self._safe_animate, s_txt.position_combine, 'input0', {
                        0: ts_h_offs + 80.0 * scale,
                        transtime2: ts_h_offs + (80.0 - slide_amt) * scale
                    }))

            score_change = (
                player.team.sessionteam.customdata['score'] -
                player.team.sessionteam.customdata['previous_score'])
            if score_change > 0:
                xval = 113
                yval = 3.0
                s_txt_2 = _scoretxt('+' + str(score_change),
                                    xval,
                                    yval,
                                    True,
                                    0,
                                    0.7,
                                    flash=True)
                ba.timer(
                    tdelay + delay2,
                    ba.WeakCall(
                        self._safe_animate, s_txt_2.position_combine, 'input1',
                        {
                            0: y_base + (v_offs + yval + 2.0) * scale,
                            transtime: y_base + (v_offs_2 + yval + 2.0) * scale
                        }))
                ba.timer(
                    tdelay + delay3,
                    ba.WeakCall(
                        self._safe_animate, s_txt_2.position_combine, 'input0',
                        {
                            0: ts_h_offs + xval * scale,
                            transtime2: ts_h_offs + (xval - slide_amt) * scale
                        }))

                def _safesetattr(node: Optional[ba.Node], attr: str,
                                 value: Any) -> None:
                    if node:
                        setattr(node, attr, value)

                ba.timer(
                    tdelay + delay1,
                    ba.Call(_safesetattr, s_txt.node, 'color', (1, 1, 1, 1)))
                for j in range(score_change):
                    ba.timer((tdelay + delay1 + 0.15 * j),
                             ba.Call(
                                 _safesetattr, s_txt.node, 'text',
                                 str(player.team.sessionteam.
                                     customdata['previous_score'] + j + 1)))
                    tfin = tdelay + delay1 + 0.15 * j
                    if tfin not in sound_times:
                        sound_times.add(tfin)
                        ba.timer(
                            tfin,
                            ba.Call(ba.playsound,
                                    self._score_display_sound_small))
            v_offs -= spacing
 def neonLightSwitch(self,shine,Highlight,NameColor):
     spaz = self.spazRef()
     if spaz is not None and spaz.is_alive() and spaz.node.exists():
         color = (random.random(),random.random(),random.random())
         if NameColor: ba.animate_array(spaz.node,"nameColor",3,{0:spaz.node.nameColor,500:ba.safecolor(color)}, timetype=tt, timeformat=tf)
         if shine:color = tuple([min(10., 10 * x) for x in color])
         ba.animate_array(spaz.node,"color",3,{0:spaz.node.color,500:color}, timetype=tt, timeformat=tf)
         if Highlight:
             #print spaz.node.highlight
             color = (random.random(),random.random(),random.random())
             if shine:color = tuple([min(10., 10 * x) for x in color])
             ba.animate_array(spaz.node,"highlight",3,{0:spaz.node.highlight,500:color}, timetype=tt, timeformat=tf)
     else:
         self.neroLightTimer = None
         self.handlemessage(ba.DieMessage())