# Released under the MIT License. See LICENSE for details.
#
"""UI related to league rank."""
# pylint: disable=too-many-lines
from __future__ import annotations
import copy
import logging
from typing import TYPE_CHECKING, override
from bauiv1lib.popup import PopupMenu
import bauiv1 as bui
if TYPE_CHECKING:
from typing import Any
[docs]
class LeagueRankWindow(bui.MainWindow):
"""Window for showing league rank."""
def __init__(
self,
transition: str | None = 'in_right',
origin_widget: bui.Widget | None = None,
):
# pylint: disable=too-many-statements
plus = bui.app.plus
assert plus is not None
bui.set_analytics_screen('League Rank Window')
self._league_rank_data: dict[str, Any] | None = None
self._power_ranking_achievements_button: bui.Widget | None = None
self._up_to_date_bonus_button: bui.Widget | None = None
self._power_ranking_trophies_button: bui.Widget | None = None
self._league_title_text: bui.Widget | None = None
self._league_text: bui.Widget | None = None
self._league_number_text: bui.Widget | None = None
self._your_power_ranking_text: bui.Widget | None = None
self._loading_spinner: bui.Widget | None = None
self._season_ends_text: bui.Widget | None = None
self._power_ranking_rank_text: bui.Widget | None = None
self._to_ranked_text: bui.Widget | None = None
self._trophy_counts_reset_text: bui.Widget | None = None
assert bui.app.classic is not None
uiscale = bui.app.ui_v1.uiscale
self._width = 1500 if uiscale is bui.UIScale.SMALL else 1120
x_inset = 100 if uiscale is bui.UIScale.SMALL else 0
self._height = (
1000
if uiscale is bui.UIScale.SMALL
else 710 if uiscale is bui.UIScale.MEDIUM else 800
)
self._r = 'coopSelectWindow'
self._rdict = bui.app.lang.get_resource(self._r)
# top_extra = 20 if uiscale is bui.UIScale.SMALL else 0
# self._xoffs = 80.0 if uiscale is bui.UIScale.SMALL else 0
self._xoffs = 40
self._league_url_arg = ''
self._is_current_season = False
self._can_do_more_button = True
# Do some fancy math to fill all available screen area up to the
# size of our backing container. This lets us fit to the exact
# screen shape at small ui scale.
screensize = bui.get_virtual_screen_size()
scale = (
1.3
if uiscale is bui.UIScale.SMALL
else 0.93 if uiscale is bui.UIScale.MEDIUM else 0.8
)
# Calc screen size in our local container space and clamp to a
# bit smaller than our container size.
target_width = min(self._width - 130, screensize[0] / scale)
target_height = min(self._height - 130, screensize[1] / scale)
# To get top/left coords, go to the center of our window and
# offset by half the width/height of our target area.
yoffs = 0.5 * self._height + 0.5 * target_height + 30.0
self._scroll_width = target_width
self._scroll_height = target_height - 35
scroll_bottom = yoffs - 80 - self._scroll_height
super().__init__(
root_widget=bui.containerwidget(
size=(self._width, self._height),
stack_offset=(
(0, 0)
if uiscale is bui.UIScale.SMALL
else (0, 10) if uiscale is bui.UIScale.MEDIUM else (0, 0)
),
scale=scale,
toolbar_visibility=(
'menu_minimal'
if uiscale is bui.UIScale.SMALL
else 'menu_full'
),
),
transition=transition,
origin_widget=origin_widget,
# We're affected by screen size only at small ui-scale.
refresh_on_screen_size_changes=uiscale is bui.UIScale.SMALL,
)
if uiscale is bui.UIScale.SMALL:
self._back_button = bui.get_special_widget('back_button')
bui.containerwidget(
edit=self._root_widget, on_cancel_call=self.main_window_back
)
else:
self._back_button = bui.buttonwidget(
parent=self._root_widget,
position=(75 + x_inset, yoffs - 60),
size=(60, 55),
scale=1.2,
autoselect=True,
label=bui.charstr(bui.SpecialChar.BACK),
button_type='backSmall',
on_activate_call=self.main_window_back,
)
bui.containerwidget(
edit=self._root_widget,
cancel_button=self._back_button,
selected_child=self._back_button,
)
self._title_text = bui.textwidget(
parent=self._root_widget,
position=(
self._width * 0.5,
yoffs - (55 if uiscale is bui.UIScale.SMALL else 30),
),
size=(0, 0),
text=bui.Lstr(
resource='league.leagueRankText',
fallback_resource='coopSelectWindow.powerRankingText',
),
h_align='center',
color=bui.app.ui_v1.title_color,
scale=1.2 if uiscale is bui.UIScale.SMALL else 1.3,
maxwidth=600,
v_align='center',
)
self._scrollwidget = bui.scrollwidget(
parent=self._root_widget,
highlight=False,
size=(self._scroll_width, self._scroll_height),
position=(
self._width * 0.5 - self._scroll_width * 0.5,
scroll_bottom,
),
center_small_content=True,
center_small_content_horizontally=True,
border_opacity=0.4,
)
bui.widget(edit=self._scrollwidget, autoselect=True)
bui.containerwidget(edit=self._scrollwidget, claims_left_right=True)
self._last_power_ranking_query_time: float | None = None
self._doing_power_ranking_query = False
self._subcontainer: bui.Widget | None = None
self._subcontainerwidth = 1024
self._subcontainerheight = 483
self._power_ranking_score_widgets: list[bui.Widget] = []
self._season_popup_menu: PopupMenu | None = None
self._requested_season: str | None = None
self._season: str | None = None
# Take note of our account state; we'll refresh later if this
# changes.
self._account_state = plus.get_v1_account_state()
self._refresh()
self._restore_state()
# If we've got cached power-ranking data already, display it.
assert bui.app.classic is not None
info = bui.app.classic.accounts.get_cached_league_rank_data()
if info is not None:
self._update_for_league_rank_data(info)
self._update_timer = bui.AppTimer(
1.0, bui.WeakCall(self._update), repeat=True
)
self._update(show=info is None)
[docs]
@override
def get_main_window_state(self) -> bui.MainWindowState:
# Support recreating our window for back/refresh purposes.
cls = type(self)
return bui.BasicMainWindowState(
create_call=lambda transition, origin_widget: cls(
transition=transition, origin_widget=origin_widget
)
)
[docs]
@override
def on_main_window_close(self) -> None:
self._save_state()
def _on_achievements_press(self) -> None:
from bauiv1lib.achievements import AchievementsWindow
# Only allow this for all-time or the current season (we
# currently don't keep specific achievement data for old
# seasons).
if self._season == 'a' or self._is_current_season:
prab = self._power_ranking_achievements_button
assert prab is not None
self.main_window_replace(AchievementsWindow(origin_widget=prab))
else:
bui.screenmessage(
bui.Lstr(
resource='achievementsUnavailableForOldSeasonsText',
fallback_resource='unavailableText',
),
color=(1, 0, 0),
)
bui.getsound('error').play()
def _on_activity_mult_press(self) -> None:
from bauiv1lib import confirm
plus = bui.app.plus
assert plus is not None
txt = bui.Lstr(
resource=(
'coopSelectWindow.activenessAllTimeInfoText'
if self._season == 'a'
else 'coopSelectWindow.activenessInfoText'
),
subs=[
(
'${MAX}',
str(
plus.get_v1_account_misc_read_val('activenessMax', 1.0)
),
)
],
)
confirm.ConfirmWindow(
txt,
cancel_button=False,
width=460,
height=150,
origin_widget=self._activity_mult_button,
)
def _on_up_to_date_bonus_press(self) -> None:
from bauiv1lib import confirm
plus = bui.app.plus
assert plus is not None
txt = bui.Lstr(
resource='league.upToDateBonusDescriptionText',
subs=[
(
'${PERCENT}',
str(
plus.get_v1_account_misc_read_val(
'proPowerRankingBoost', 10
)
),
),
],
)
confirm.ConfirmWindow(
txt,
cancel_button=False,
width=460,
height=130,
origin_widget=self._up_to_date_bonus_button,
)
def _on_trophies_press(self) -> None:
from bauiv1lib.trophies import TrophiesWindow
info = self._league_rank_data
if info is not None:
prtb = self._power_ranking_trophies_button
assert prtb is not None
TrophiesWindow(
position=prtb.get_screen_space_center(),
data=info,
)
else:
bui.getsound('error').play()
def _on_power_ranking_query_response(
self, data: dict[str, Any] | None
) -> None:
self._doing_power_ranking_query = False
# Important: *only* cache this if we requested the current
# season.
if data is not None and data.get('s', None) is None:
assert bui.app.classic is not None
bui.app.classic.accounts.cache_league_rank_data(data)
# Always store a copy locally though (even for other seasons).
self._league_rank_data = copy.deepcopy(data)
self._update_for_league_rank_data(data)
def _restore_state(self) -> None:
pass
def _update(self, show: bool = False) -> None:
plus = bui.app.plus
assert plus is not None
cur_time = bui.apptime()
# If our account state has changed, refresh our UI.
account_state = plus.get_v1_account_state()
if account_state != self._account_state:
self._account_state = account_state
self._save_state()
self._refresh()
# And power ranking too.
if not self._doing_power_ranking_query:
self._last_power_ranking_query_time = None
# Send off a new power-ranking query if its been long enough or
# our requested season has changed or whatnot.
if not self._doing_power_ranking_query and (
self._last_power_ranking_query_time is None
or cur_time - self._last_power_ranking_query_time > 30.0
):
try:
if show:
bui.textwidget(edit=self._league_title_text, text='')
bui.textwidget(edit=self._league_text, text='')
bui.textwidget(edit=self._league_number_text, text='')
bui.textwidget(edit=self._your_power_ranking_text, text='')
bui.spinnerwidget(edit=self._loading_spinner, visible=True)
bui.textwidget(edit=self._to_ranked_text, text='')
bui.textwidget(edit=self._power_ranking_rank_text, text='')
bui.textwidget(edit=self._season_ends_text, text='')
bui.textwidget(edit=self._trophy_counts_reset_text, text='')
except Exception:
logging.exception('Error showing updated rank info.')
self._last_power_ranking_query_time = cur_time
self._doing_power_ranking_query = True
plus.power_ranking_query(
season=self._requested_season,
callback=bui.WeakCall(self._on_power_ranking_query_response),
)
def _refresh(self) -> None:
# pylint: disable=too-many-statements
plus = bui.app.plus
assert plus is not None
# (Re)create the sub-container if need be.
if self._subcontainer is not None:
self._subcontainer.delete()
self._subcontainer = bui.containerwidget(
parent=self._scrollwidget,
size=(self._subcontainerwidth, self._subcontainerheight),
background=False,
)
w_parent = self._subcontainer
v = self._subcontainerheight - 20
v -= 0
h2 = 80
v2 = v - 60
worth_color = (0.6, 0.6, 0.65)
tally_color = (0.5, 0.6, 0.8)
spc = 43
h_offs_tally = 150
tally_maxwidth = 120
v2 -= 70
bui.textwidget(
parent=w_parent,
position=(self._xoffs + h2 - 60, v2 + 106),
size=(0, 0),
flatness=1.0,
shadow=0.0,
text=bui.Lstr(resource='coopSelectWindow.pointsText'),
h_align='left',
v_align='center',
scale=0.8,
color=(1, 1, 1, 0.3),
maxwidth=200,
)
self._power_ranking_achievements_button = bui.buttonwidget(
parent=w_parent,
position=(self._xoffs + h2 - 60, v2 + 10),
size=(200, 80),
icon=bui.gettexture('achievementsIcon'),
autoselect=True,
on_activate_call=bui.WeakCall(self._on_achievements_press),
up_widget=self._back_button,
left_widget=self._back_button,
color=(0.5, 0.5, 0.6),
textcolor=(0.7, 0.7, 0.8),
label='',
)
self._power_ranking_achievement_total_text = bui.textwidget(
parent=w_parent,
position=(self._xoffs + h2 + h_offs_tally, v2 + 45),
size=(0, 0),
flatness=1.0,
shadow=0.0,
text='-',
h_align='left',
v_align='center',
scale=0.8,
color=tally_color,
maxwidth=tally_maxwidth,
)
v2 -= 80
self._power_ranking_trophies_button = bui.buttonwidget(
parent=w_parent,
position=(self._xoffs + h2 - 60, v2 + 10),
size=(200, 80),
icon=bui.gettexture('medalSilver'),
autoselect=True,
on_activate_call=bui.WeakCall(self._on_trophies_press),
left_widget=self._back_button,
color=(0.5, 0.5, 0.6),
textcolor=(0.7, 0.7, 0.8),
label='',
)
self._power_ranking_trophies_total_text = bui.textwidget(
parent=w_parent,
position=(self._xoffs + h2 + h_offs_tally, v2 + 45),
size=(0, 0),
flatness=1.0,
shadow=0.0,
text='-',
h_align='left',
v_align='center',
scale=0.8,
color=tally_color,
maxwidth=tally_maxwidth,
)
v2 -= 100
bui.textwidget(
parent=w_parent,
position=(self._xoffs + h2 - 60, v2 + 86),
size=(0, 0),
flatness=1.0,
shadow=0.0,
text=bui.Lstr(resource='coopSelectWindow.multipliersText'),
h_align='left',
v_align='center',
scale=0.8,
color=(1, 1, 1, 0.3),
maxwidth=200,
)
self._activity_mult_button: bui.Widget | None
if plus.get_v1_account_misc_read_val('act', False):
self._activity_mult_button = bui.buttonwidget(
parent=w_parent,
position=(self._xoffs + h2 - 60, v2 + 10),
size=(200, 60),
icon=bui.gettexture('heart'),
icon_color=(0.5, 0, 0.5),
label=bui.Lstr(resource='coopSelectWindow.activityText'),
autoselect=True,
on_activate_call=bui.WeakCall(self._on_activity_mult_press),
left_widget=self._back_button,
color=(0.5, 0.5, 0.6),
textcolor=(0.7, 0.7, 0.8),
)
self._activity_mult_text = bui.textwidget(
parent=w_parent,
position=(self._xoffs + h2 + h_offs_tally, v2 + 40),
size=(0, 0),
flatness=1.0,
shadow=0.0,
text='-',
h_align='left',
v_align='center',
scale=0.8,
color=tally_color,
maxwidth=tally_maxwidth,
)
v2 -= 65
else:
self._activity_mult_button = None
self._up_to_date_bonus_button = bui.buttonwidget(
parent=w_parent,
position=(self._xoffs + h2 - 60, v2 + 10),
size=(200, 60),
icon=bui.gettexture('logo'),
icon_color=(0.3, 0, 0.3),
label=bui.Lstr(resource='league.upToDateBonusText'),
autoselect=True,
on_activate_call=bui.WeakCall(self._on_up_to_date_bonus_press),
left_widget=self._back_button,
color=(0.5, 0.5, 0.6),
textcolor=(0.7, 0.7, 0.8),
)
self._up_to_date_bonus_text = bui.textwidget(
parent=w_parent,
position=(self._xoffs + h2 + h_offs_tally, v2 + 40),
size=(0, 0),
flatness=1.0,
shadow=0.0,
text='-',
h_align='left',
v_align='center',
scale=0.8,
color=tally_color,
maxwidth=tally_maxwidth,
)
v2 -= 30
v2 -= spc
bui.textwidget(
parent=w_parent,
position=(self._xoffs + h2 + h_offs_tally - 10 - 40, v2 + 35),
size=(0, 0),
flatness=1.0,
shadow=0.0,
text=bui.Lstr(resource='finalScoreText'),
h_align='right',
v_align='center',
scale=0.9,
color=worth_color,
maxwidth=150,
)
self._power_ranking_total_text = bui.textwidget(
parent=w_parent,
position=(self._xoffs + h2 + h_offs_tally - 40, v2 + 35),
size=(0, 0),
flatness=1.0,
shadow=0.0,
text='-',
h_align='left',
v_align='center',
scale=0.9,
color=tally_color,
maxwidth=tally_maxwidth,
)
self._season_show_text = bui.textwidget(
parent=w_parent,
position=(self._xoffs + 390 - 15, v - 20),
size=(0, 0),
color=(0.6, 0.6, 0.7),
maxwidth=200,
text=bui.Lstr(resource='showText'),
h_align='right',
v_align='center',
scale=0.8,
shadow=0,
flatness=1.0,
)
self._league_title_text = bui.textwidget(
parent=w_parent,
position=(self._xoffs + 470, v - 97),
size=(0, 0),
color=(0.6, 0.6, 0.7),
maxwidth=230,
text='',
h_align='center',
v_align='center',
scale=0.9,
shadow=0,
flatness=1.0,
)
self._league_text_scale = 1.8
self._league_text_maxwidth = 210
self._league_text = bui.textwidget(
parent=w_parent,
position=(self._xoffs + 470, v - 140),
size=(0, 0),
color=(1, 1, 1),
maxwidth=self._league_text_maxwidth,
text='-',
h_align='center',
v_align='center',
scale=self._league_text_scale,
shadow=1.0,
flatness=1.0,
)
self._league_number_base_pos = (470, v - 140)
self._league_number_text = bui.textwidget(
parent=w_parent,
position=(self._xoffs + 470, v - 140),
size=(0, 0),
color=(1, 1, 1),
maxwidth=100,
text='',
h_align='left',
v_align='center',
scale=0.8,
shadow=1.0,
flatness=1.0,
)
self._loading_spinner = bui.spinnerwidget(
parent=w_parent,
position=(
self._subcontainerwidth * 0.5,
self._subcontainerheight * 0.5,
),
style='bomb',
size=64,
)
self._your_power_ranking_text = bui.textwidget(
parent=w_parent,
position=(self._xoffs + 470, v - 142 - 70),
size=(0, 0),
color=(0.6, 0.6, 0.7),
maxwidth=230,
text='',
h_align='center',
v_align='center',
scale=0.9,
shadow=0,
flatness=1.0,
)
self._to_ranked_text = bui.textwidget(
parent=w_parent,
position=(self._xoffs + 470, v - 250 - 70),
size=(0, 0),
color=(0.6, 0.6, 0.7),
maxwidth=230,
text='',
h_align='center',
v_align='center',
scale=0.8,
shadow=0,
flatness=1.0,
)
self._power_ranking_rank_text = bui.textwidget(
parent=w_parent,
position=(self._xoffs + 473, v - 210 - 70),
size=(0, 0),
big=False,
text='-',
h_align='center',
v_align='center',
scale=1.0,
)
self._season_ends_text = bui.textwidget(
parent=w_parent,
position=(self._xoffs + 470, v - 380),
size=(0, 0),
color=(0.6, 0.6, 0.6),
maxwidth=230,
text='',
h_align='center',
v_align='center',
scale=0.9,
shadow=0,
flatness=1.0,
)
self._trophy_counts_reset_text = bui.textwidget(
parent=w_parent,
position=(self._xoffs + 470, v - 410),
size=(0, 0),
color=(0.5, 0.5, 0.5),
maxwidth=230,
text='Trophy counts will reset next season.',
h_align='center',
v_align='center',
scale=0.8,
shadow=0,
flatness=1.0,
)
self._power_ranking_score_widgets = []
self._power_ranking_score_v = v - 56
h = 707
v -= 451
self._see_more_button = bui.buttonwidget(
parent=w_parent,
label=self._rdict.seeMoreText,
position=(self._xoffs + h, v),
color=(0.5, 0.5, 0.6),
textcolor=(0.7, 0.7, 0.8),
size=(230, 60),
autoselect=True,
on_activate_call=bui.WeakCall(self._on_more_press),
)
def _on_more_press(self) -> None:
plus = bui.app.plus
assert plus is not None
our_login_id = plus.get_v1_account_public_login_id()
if not self._can_do_more_button or our_login_id is None:
bui.getsound('error').play()
bui.screenmessage(
bui.Lstr(resource='unavailableText'), color=(1, 0, 0)
)
return
if self._season is None:
season_str = ''
else:
season_str = '&season=' + (
'all_time' if self._season == 'a' else self._season
)
if self._league_url_arg != '':
league_str = '&league=' + self._league_url_arg
else:
league_str = ''
bui.open_url(
plus.get_master_server_address()
+ '/highscores?list=powerRankings&v=2'
+ league_str
+ season_str
+ '&player='
+ our_login_id
)
def _update_for_league_rank_data(self, data: dict[str, Any] | None) -> None:
# pylint: disable=too-many-statements
# pylint: disable=too-many-branches
# pylint: disable=too-many-locals
if not self._root_widget:
return
plus = bui.app.plus
uiscale = bui.app.ui_v1.uiscale
assert plus is not None
assert bui.app.classic is not None
accounts = bui.app.classic.accounts
in_top = data is not None and data['rank'] is not None
eq_text = self._rdict.powerRankingPointsEqualsText
pts_txt = self._rdict.powerRankingPointsText
num_text = bui.Lstr(resource='numberText').evaluate()
do_percent = False
finished_season_unranked = False
self._can_do_more_button = True
extra_text = ''
if plus.get_v1_account_state() != 'signed_in':
status_text = (
'(' + bui.Lstr(resource='notSignedInText').evaluate() + ')'
)
elif in_top:
assert data is not None
status_text = num_text.replace('${NUMBER}', str(data['rank']))
elif data is not None:
try:
# Handle old seasons where we didn't wind up ranked at
# the end.
if not data['scores']:
status_text = (
self._rdict.powerRankingFinishedSeasonUnrankedText
)
extra_text = ''
finished_season_unranked = True
self._can_do_more_button = False
else:
our_points = accounts.get_league_rank_points(data)
progress = float(our_points) / max(1, data['scores'][-1][1])
status_text = str(int(progress * 100.0)) + '%'
extra_text = (
'\n'
+ self._rdict.powerRankingPointsToRankedText.replace(
'${CURRENT}', str(our_points)
).replace('${REMAINING}', str(data['scores'][-1][1]))
)
do_percent = True
except Exception:
logging.exception('Error updating power ranking.')
status_text = self._rdict.powerRankingNotInTopText.replace(
'${NUMBER}', str(data['listSize'])
)
extra_text = ''
else:
status_text = '-'
self._season = data['s'] if data is not None else None
v = self._subcontainerheight - 20
popup_was_selected = False
if self._season_popup_menu is not None:
btn = self._season_popup_menu.get_button()
assert self._subcontainer
if self._subcontainer.get_selected_child() == btn:
popup_was_selected = True
btn.delete()
season_choices = []
season_choices_display = []
did_first = False
self._is_current_season = False
if data is not None:
# Build our list of seasons we have available.
for ssn in data['sl']:
season_choices.append(ssn)
if ssn != 'a' and not did_first:
season_choices_display.append(
bui.Lstr(
resource='league.currentSeasonText',
subs=[('${NUMBER}', ssn)],
)
)
did_first = True
# If we either did not specify a season or specified
# the first, we're looking at the current.
if self._season in [ssn, None]:
self._is_current_season = True
elif ssn == 'a':
season_choices_display.append(
bui.Lstr(resource='league.allTimeText')
)
else:
season_choices_display.append(
bui.Lstr(
resource='league.seasonText',
subs=[('${NUMBER}', ssn)],
)
)
assert self._subcontainer
self._season_popup_menu = PopupMenu(
parent=self._subcontainer,
position=(self._xoffs + 390, v - 45),
width=150,
button_size=(200, 50),
choices=season_choices,
on_value_change_call=bui.WeakCall(self._on_season_change),
choices_display=season_choices_display,
current_choice=self._season,
)
if popup_was_selected:
bui.containerwidget(
edit=self._subcontainer,
selected_child=self._season_popup_menu.get_button(),
)
bui.widget(edit=self._see_more_button, show_buffer_bottom=100)
bui.widget(
edit=self._season_popup_menu.get_button(),
up_widget=self._back_button,
)
if uiscale is not bui.UIScale.SMALL:
bui.widget(
edit=self._back_button,
down_widget=self._power_ranking_achievements_button,
right_widget=self._season_popup_menu.get_button(),
)
bui.textwidget(
edit=self._league_title_text,
text=(
''
if self._season == 'a'
else bui.Lstr(resource='league.leagueText')
),
)
if data is None:
lname = ''
lnum = ''
lcolor = (1, 1, 1)
self._league_url_arg = ''
elif self._season == 'a':
lname = bui.Lstr(resource='league.allTimeText').evaluate()
lnum = ''
lcolor = (1, 1, 1)
self._league_url_arg = ''
else:
lnum = ('[' + str(data['l']['i']) + ']') if data['l']['i2'] else ''
lname = bui.Lstr(
translate=('leagueNames', data['l']['n'])
).evaluate()
lcolor = data['l']['c']
self._league_url_arg = (
data['l']['n'] + '_' + str(data['l']['i'])
).lower()
to_end_string: bui.Lstr | str
if data is None or self._season == 'a' or data['se'] is None:
to_end_string = ''
show_season_end = False
else:
show_season_end = True
days_to_end = data['se'][0]
minutes_to_end = data['se'][1]
if days_to_end > 0:
to_end_string = bui.Lstr(
resource='league.seasonEndsDaysText',
subs=[('${NUMBER}', str(days_to_end))],
)
elif days_to_end == 0 and minutes_to_end >= 60:
to_end_string = bui.Lstr(
resource='league.seasonEndsHoursText',
subs=[('${NUMBER}', str(minutes_to_end // 60))],
)
elif days_to_end == 0 and minutes_to_end >= 0:
to_end_string = bui.Lstr(
resource='league.seasonEndsMinutesText',
subs=[('${NUMBER}', str(minutes_to_end))],
)
else:
to_end_string = bui.Lstr(
resource='league.seasonEndedDaysAgoText',
subs=[('${NUMBER}', str(-(days_to_end + 1)))],
)
bui.textwidget(edit=self._season_ends_text, text=to_end_string)
bui.textwidget(
edit=self._trophy_counts_reset_text,
text=(
bui.Lstr(resource='league.trophyCountsResetText')
if self._is_current_season and show_season_end
else ''
),
)
bui.textwidget(edit=self._league_text, text=lname, color=lcolor)
l_text_width = min(
self._league_text_maxwidth,
bui.get_string_width(lname, suppress_warning=True)
* self._league_text_scale,
)
bui.textwidget(
edit=self._league_number_text,
text=lnum,
color=lcolor,
position=(
self._xoffs
+ self._league_number_base_pos[0]
+ l_text_width * 0.5
+ 8,
self._league_number_base_pos[1] + 10,
),
)
bui.textwidget(
edit=self._to_ranked_text,
text=(
bui.Lstr(resource='coopSelectWindow.toRankedText').evaluate()
+ ''
+ extra_text
if do_percent
else ''
),
)
bui.textwidget(
edit=self._your_power_ranking_text,
text=(
bui.Lstr(
resource='rankText',
fallback_resource='coopSelectWindow.yourPowerRankingText',
)
if (not do_percent)
else ''
),
)
bui.spinnerwidget(edit=self._loading_spinner, visible=False)
bui.textwidget(
edit=self._power_ranking_rank_text,
position=(self._xoffs + 473, v - 70 - (170 if do_percent else 220)),
text=status_text,
big=(in_top or do_percent),
scale=(
3.0
if (in_top or do_percent)
else 0.7 if finished_season_unranked else 1.0
),
)
if self._activity_mult_button is not None:
if data is None or data['act'] is None:
bui.buttonwidget(
edit=self._activity_mult_button,
textcolor=(0.7, 0.7, 0.8, 0.5),
icon_color=(0.5, 0, 0.5, 0.3),
)
bui.textwidget(edit=self._activity_mult_text, text=' -')
else:
bui.buttonwidget(
edit=self._activity_mult_button,
textcolor=(0.7, 0.7, 0.8, 1.0),
icon_color=(0.5, 0, 0.5, 1.0),
)
bui.textwidget(
edit=self._activity_mult_text,
text=f'x {data['act']:.2f}',
)
# This used to be a bonus for 'BombSquad Pro' holders, but since
# we're transitioning away from that it is now a bonus for
# everyone running a recent-ish version of the game.
have_up_to_date_bonus = data is not None
pro_mult = (
1.0
+ float(
plus.get_v1_account_misc_read_val('proPowerRankingBoost', 0.0)
)
* 0.01
)
bui.textwidget(
edit=self._up_to_date_bonus_text,
text=(
' -'
if (data is None or not have_up_to_date_bonus)
else f'x {pro_mult:.2f}'
),
)
bui.buttonwidget(
edit=self._up_to_date_bonus_button,
textcolor=(0.7, 0.7, 0.8, (1.0 if have_up_to_date_bonus else 0.5)),
icon_color=(
(0.5, 0, 0.5) if have_up_to_date_bonus else (0.5, 0, 0.5, 0.2)
),
)
bui.buttonwidget(
edit=self._power_ranking_achievements_button,
label=('' if data is None else str(data['a']) + ' ')
+ bui.Lstr(resource='achievementsText').evaluate(),
)
# For the achievement value, use the number they gave us for
# non-current seasons; otherwise calc our own.
total_ach_value = 0
for ach in bui.app.classic.ach.achievements:
if ach.complete:
total_ach_value += ach.power_ranking_value
if self._season != 'a' and not self._is_current_season:
if data is not None and 'at' in data:
total_ach_value = data['at']
bui.textwidget(
edit=self._power_ranking_achievement_total_text,
text=(
'-'
if data is None
else ('+ ' + pts_txt.replace('${NUMBER}', str(total_ach_value)))
),
)
total_trophies_count = accounts.get_league_rank_points(
data, 'trophyCount'
)
total_trophies_value = accounts.get_league_rank_points(data, 'trophies')
bui.buttonwidget(
edit=self._power_ranking_trophies_button,
label=('' if data is None else str(total_trophies_count) + ' ')
+ bui.Lstr(resource='trophiesText').evaluate(),
)
bui.textwidget(
edit=self._power_ranking_trophies_total_text,
text=(
'-'
if data is None
else (
'+ '
+ pts_txt.replace('${NUMBER}', str(total_trophies_value))
)
),
)
bui.textwidget(
edit=self._power_ranking_total_text,
text=(
'-'
if data is None
else eq_text.replace(
'${NUMBER}', str(accounts.get_league_rank_points(data))
)
),
)
for widget in self._power_ranking_score_widgets:
widget.delete()
self._power_ranking_score_widgets = []
scores = data['scores'] if data is not None else []
tally_color = (0.5, 0.6, 0.8)
w_parent = self._subcontainer
v2 = self._power_ranking_score_v
for score in scores:
h2 = 680
is_us = score[3]
self._power_ranking_score_widgets.append(
bui.textwidget(
parent=w_parent,
position=(self._xoffs + h2 - 20, v2),
size=(0, 0),
color=(1, 1, 1) if is_us else (0.6, 0.6, 0.7),
maxwidth=40,
flatness=1.0,
shadow=0.0,
text=num_text.replace('${NUMBER}', str(score[0])),
h_align='right',
v_align='center',
scale=0.5,
)
)
self._power_ranking_score_widgets.append(
bui.textwidget(
parent=w_parent,
position=(self._xoffs + h2 + 20, v2),
size=(0, 0),
color=(1, 1, 1) if is_us else tally_color,
maxwidth=60,
text=str(score[1]),
flatness=1.0,
shadow=0.0,
h_align='center',
v_align='center',
scale=0.7,
)
)
txt = bui.textwidget(
parent=w_parent,
position=(self._xoffs + h2 + 60, v2 - (28 * 0.5) / 0.9),
size=(210 / 0.9, 28),
color=(1, 1, 1) if is_us else (0.6, 0.6, 0.6),
maxwidth=210,
flatness=1.0,
shadow=0.0,
autoselect=True,
selectable=True,
click_activate=True,
text=score[2],
h_align='left',
v_align='center',
scale=0.9,
)
self._power_ranking_score_widgets.append(txt)
bui.textwidget(
edit=txt,
on_activate_call=bui.Call(
self._show_account_info, score[4], txt
),
)
assert self._season_popup_menu is not None
bui.widget(
edit=txt, left_widget=self._season_popup_menu.get_button()
)
v2 -= 28
def _show_account_info(
self, account_id: str, textwidget: bui.Widget
) -> None:
from bauiv1lib.account.viewer import AccountViewerWindow
bui.getsound('swish').play()
AccountViewerWindow(
account_id=account_id, position=textwidget.get_screen_space_center()
)
def _on_season_change(self, value: str) -> None:
self._requested_season = value
self._last_power_ranking_query_time = None # Update asap.
self._update(show=True)
def _save_state(self) -> None:
pass
# 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