Source code for bascenev1lib.activity.multiteamscore

# Released under the MIT License. See LICENSE for details.
#
"""Functionality related to teams mode score screen."""
from __future__ import annotations

from typing import override

import bascenev1 as bs

from bascenev1lib.actor.text import Text
from bascenev1lib.actor.image import Image


[docs] class MultiTeamScoreScreenActivity(bs.ScoreScreenActivity): """Base class for score screens.""" def __init__(self, settings: dict): super().__init__(settings=settings) self._score_display_sound = bs.getsound('scoreHit01') self._score_display_sound_small = bs.getsound('scoreHit02') self._show_up_next: bool = True
[docs] @override def on_begin(self) -> None: super().on_begin() session = self.session if self._show_up_next and isinstance(session, bs.MultiTeamSession): txt = bs.Lstr( value='${A} ${B}', subs=[ ( '${A}', bs.Lstr( resource='upNextText', subs=[ ('${COUNT}', str(session.get_game_number() + 1)) ], ), ), ('${B}', session.get_next_game_description()), ], ) Text( txt, maxwidth=900, h_attach=Text.HAttach.CENTER, v_attach=Text.VAttach.BOTTOM, h_align=Text.HAlign.CENTER, v_align=Text.VAlign.CENTER, position=(0, 53), flash=False, color=(0.3, 0.3, 0.35, 1.0), transition=Text.Transition.FADE_IN, transition_delay=2.0, ).autoretain()
[docs] def show_player_scores( self, *, delay: float = 2.5, results: bs.GameResults | None = 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, bs.FreeForAllSession) def _get_prec_score(p_rec: bs.PlayerRecord) -> int | None: if is_free_for_all and results is not None: assert isinstance(results, bs.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: bs.PlayerRecord) -> str | bs.Lstr: if is_free_for_all and results is not None: assert isinstance(results, bs.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, bs.GameResults) player_records = [] valid_players = list(self.stats.get_records().items()) # noinspection PyUnresolvedReferences def _get_player_score_set_entry( player: bs.SessionPlayer, ) -> bs.PlayerRecord | None: 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) player_records = [p[2] for p in player_records_scores] voffs = -140.0 + spacing * len(player_records) * 0.5 def _txt( xoffs: float, yoffs: float, text: bs.Lstr, *, h_align: Text.HAlign = Text.HAlign.RIGHT, extrascale: float = 1.0, maxwidth: float | None = 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, bs.MultiTeamSession) tval = bs.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, bs.Lstr(resource='playerText'), h_align=Text.HAlign.LEFT) _txt(180, 4, bs.Lstr(resource='killsText')) _txt(280, 4, bs.Lstr(resource='deathsText'), maxwidth=100) score_label = 'Score' if results is None else results.score_label translated = bs.Lstr(translate=('scoreNames', score_label)) _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: str | bs.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 + (voffs + 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 voffs -= spacing Image( playerrec.get_icon(), position=( ts_h_offs - 12 * scale, ts_v_offset + (voffs + 15.0) * scale, ), scale=(30.0 * scale, 30.0 * scale), transition=Image.Transition.IN_LEFT, transition_delay=tdelay, ).autoretain() Text( bs.Lstr(value=playerrec.getname(full=True)), maxwidth=160, scale=0.75 * scale, position=( ts_h_offs + 10.0 * scale, ts_v_offset + (voffs + 15) * scale, ), h_align=Text.HAlign.LEFT, v_align=Text.VAlign.CENTER, color=bs.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, )
# Docs-generation hack; import some stuff that we likely only forward-declared # in our actual source code so that docs tools can find it. from typing import (Coroutine, Any, Literal, Callable, Generator, Awaitable, Sequence, Self) import asyncio from concurrent.futures import Future from pathlib import Path from enum import Enum