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))
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))
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)
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())
def __init__(self, player: ba.Player, respawn_time: float): """Instantiate with a ba.Player and respawn_time (in seconds).""" self._visible = True on_right, offs_extra, respawn_icons = self._get_context(player) # Cache our mask tex on the team for easy access. mask_tex = player.team.gamedata.get('_spaz_respawn_icons_mask_tex') if mask_tex is None: mask_tex = ba.gettexture('characterIconMask') player.team.gamedata['_spaz_respawn_icons_mask_tex'] = mask_tex assert isinstance(mask_tex, ba.Texture) # Now find the first unused slot and use that. index = 0 while (index in respawn_icons and respawn_icons[index]() is not None and respawn_icons[index]().visible): index += 1 respawn_icons[index] = weakref.ref(self) offs = offs_extra + index * -53 icon = player.get_icon() texture = icon['texture'] h_offs = -10 ipos = (-40 - h_offs if on_right else 40 + h_offs, -180 + offs) self._image: Optional[ba.NodeActor] = ba.NodeActor( ba.newnode('image', attrs={ 'texture': texture, 'tint_texture': icon['tint_texture'], 'tint_color': icon['tint_color'], 'tint2_color': icon['tint2_color'], 'mask_texture': mask_tex, 'position': ipos, 'scale': (32, 32), 'opacity': 1.0, 'absolute_scale': True, 'attach': 'topRight' if on_right else 'topLeft' })) assert self._image.node ba.animate(self._image.node, 'opacity', {0.0: 0, 0.2: 0.7}) npos = (-40 - h_offs if on_right else 40 + h_offs, -205 + 49 + offs) self._name: Optional[ba.NodeActor] = ba.NodeActor( ba.newnode('text', attrs={ 'v_attach': 'top', 'h_attach': 'right' if on_right else 'left', 'text': ba.Lstr(value=player.getname()), 'maxwidth': 100, 'h_align': 'center', 'v_align': 'center', 'shadow': 1.0, 'flatness': 1.0, 'color': ba.safecolor(icon['tint_color']), 'scale': 0.5, 'position': npos })) assert self._name.node ba.animate(self._name.node, 'scale', {0: 0, 0.1: 0.5}) tpos = (-60 - h_offs if on_right else 60 + h_offs, -192 + offs) self._text: Optional[ba.NodeActor] = ba.NodeActor( ba.newnode('text', attrs={ 'position': tpos, 'h_attach': 'right' if on_right else 'left', 'h_align': 'right' if on_right else 'left', 'scale': 0.9, 'shadow': 0.5, 'flatness': 0.5, 'v_attach': 'top', 'color': ba.safecolor(icon['tint_color']), 'text': '' })) assert self._text.node ba.animate(self._text.node, 'scale', {0: 0, 0.1: 0.9}) self._respawn_time = ba.time() + respawn_time self._update() self._timer: Optional[ba.Timer] = ba.Timer(1.0, ba.WeakCall(self._update), repeat=True)
def __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())