bastd.ui.league.rankwindow

UI related to league rank.

   1# Released under the MIT License. See LICENSE for details.
   2#
   3"""UI related to league rank."""
   4# pylint: disable=too-many-lines
   5
   6from __future__ import annotations
   7
   8import copy
   9from typing import TYPE_CHECKING
  10
  11import ba
  12import ba.internal
  13from bastd.ui import popup as popup_ui
  14
  15if TYPE_CHECKING:
  16    from typing import Any
  17
  18
  19class LeagueRankWindow(ba.Window):
  20    """Window for showing league rank."""
  21
  22    def __init__(
  23        self,
  24        transition: str = 'in_right',
  25        modal: bool = False,
  26        origin_widget: ba.Widget | None = None,
  27    ):
  28        ba.set_analytics_screen('League Rank Window')
  29
  30        self._league_rank_data: dict[str, Any] | None = None
  31        self._modal = modal
  32
  33        # If they provided an origin-widget, scale up from that.
  34        scale_origin: tuple[float, float] | None
  35        if origin_widget is not None:
  36            self._transition_out = 'out_scale'
  37            scale_origin = origin_widget.get_screen_space_center()
  38            transition = 'in_scale'
  39        else:
  40            self._transition_out = 'out_right'
  41            scale_origin = None
  42
  43        uiscale = ba.app.ui.uiscale
  44        self._width = 1320 if uiscale is ba.UIScale.SMALL else 1120
  45        x_inset = 100 if uiscale is ba.UIScale.SMALL else 0
  46        self._height = (
  47            657
  48            if uiscale is ba.UIScale.SMALL
  49            else 710
  50            if uiscale is ba.UIScale.MEDIUM
  51            else 800
  52        )
  53        self._r = 'coopSelectWindow'
  54        self._rdict = ba.app.lang.get_resource(self._r)
  55        top_extra = 20 if uiscale is ba.UIScale.SMALL else 0
  56
  57        self._league_url_arg = ''
  58
  59        self._is_current_season = False
  60        self._can_do_more_button = True
  61
  62        super().__init__(
  63            root_widget=ba.containerwidget(
  64                size=(self._width, self._height + top_extra),
  65                stack_offset=(0, -15)
  66                if uiscale is ba.UIScale.SMALL
  67                else (0, 10)
  68                if uiscale is ba.UIScale.MEDIUM
  69                else (0, 0),
  70                transition=transition,
  71                scale_origin_stack_offset=scale_origin,
  72                scale=(
  73                    1.2
  74                    if uiscale is ba.UIScale.SMALL
  75                    else 0.93
  76                    if uiscale is ba.UIScale.MEDIUM
  77                    else 0.8
  78                ),
  79            )
  80        )
  81
  82        self._back_button = btn = ba.buttonwidget(
  83            parent=self._root_widget,
  84            position=(
  85                75 + x_inset,
  86                self._height - 87 - (4 if uiscale is ba.UIScale.SMALL else 0),
  87            ),
  88            size=(120, 60),
  89            scale=1.2,
  90            autoselect=True,
  91            label=ba.Lstr(resource='doneText' if self._modal else 'backText'),
  92            button_type=None if self._modal else 'back',
  93            on_activate_call=self._back,
  94        )
  95
  96        self._title_text = ba.textwidget(
  97            parent=self._root_widget,
  98            position=(self._width * 0.5, self._height - 56),
  99            size=(0, 0),
 100            text=ba.Lstr(
 101                resource='league.leagueRankText',
 102                fallback_resource='coopSelectWindow.powerRankingText',
 103            ),
 104            h_align='center',
 105            color=ba.app.ui.title_color,
 106            scale=1.4,
 107            maxwidth=600,
 108            v_align='center',
 109        )
 110
 111        ba.buttonwidget(
 112            edit=btn,
 113            button_type='backSmall',
 114            position=(
 115                75 + x_inset,
 116                self._height - 87 - (2 if uiscale is ba.UIScale.SMALL else 0),
 117            ),
 118            size=(60, 55),
 119            label=ba.charstr(ba.SpecialChar.BACK),
 120        )
 121
 122        self._scroll_width = self._width - (130 + 2 * x_inset)
 123        self._scroll_height = self._height - 160
 124        self._scrollwidget = ba.scrollwidget(
 125            parent=self._root_widget,
 126            highlight=False,
 127            position=(65 + x_inset, 70),
 128            size=(self._scroll_width, self._scroll_height),
 129            center_small_content=True,
 130        )
 131        ba.widget(edit=self._scrollwidget, autoselect=True)
 132        ba.containerwidget(edit=self._scrollwidget, claims_left_right=True)
 133        ba.containerwidget(
 134            edit=self._root_widget,
 135            cancel_button=self._back_button,
 136            selected_child=self._back_button,
 137        )
 138
 139        self._last_power_ranking_query_time: float | None = None
 140        self._doing_power_ranking_query = False
 141
 142        self._subcontainer: ba.Widget | None = None
 143        self._subcontainerwidth = 800
 144        self._subcontainerheight = 483
 145        self._power_ranking_score_widgets: list[ba.Widget] = []
 146
 147        self._season_popup_menu: popup_ui.PopupMenu | None = None
 148        self._requested_season: str | None = None
 149        self._season: str | None = None
 150
 151        # take note of our account state; we'll refresh later if this changes
 152        self._account_state = ba.internal.get_v1_account_state()
 153
 154        self._refresh()
 155        self._restore_state()
 156
 157        # if we've got cached power-ranking data already, display it
 158        info = ba.app.accounts_v1.get_cached_league_rank_data()
 159        if info is not None:
 160            self._update_for_league_rank_data(info)
 161
 162        self._update_timer = ba.Timer(
 163            1.0,
 164            ba.WeakCall(self._update),
 165            timetype=ba.TimeType.REAL,
 166            repeat=True,
 167        )
 168        self._update(show=(info is None))
 169
 170    def _on_achievements_press(self) -> None:
 171        from bastd.ui import achievements
 172
 173        # only allow this for all-time or the current season
 174        # (we currently don't keep specific achievement data for old seasons)
 175        if self._season == 'a' or self._is_current_season:
 176            prab = self._power_ranking_achievements_button
 177            achievements.AchievementsWindow(
 178                position=prab.get_screen_space_center()
 179            )
 180        else:
 181            ba.screenmessage(
 182                ba.Lstr(
 183                    resource='achievementsUnavailableForOldSeasonsText',
 184                    fallback_resource='unavailableText',
 185                ),
 186                color=(1, 0, 0),
 187            )
 188            ba.playsound(ba.getsound('error'))
 189
 190    def _on_activity_mult_press(self) -> None:
 191        from bastd.ui import confirm
 192
 193        txt = ba.Lstr(
 194            resource='coopSelectWindow.activenessAllTimeInfoText'
 195            if self._season == 'a'
 196            else 'coopSelectWindow.activenessInfoText',
 197            subs=[
 198                (
 199                    '${MAX}',
 200                    str(
 201                        ba.internal.get_v1_account_misc_read_val(
 202                            'activenessMax', 1.0
 203                        )
 204                    ),
 205                )
 206            ],
 207        )
 208        confirm.ConfirmWindow(
 209            txt,
 210            cancel_button=False,
 211            width=460,
 212            height=150,
 213            origin_widget=self._activity_mult_button,
 214        )
 215
 216    def _on_pro_mult_press(self) -> None:
 217        from bastd.ui import confirm
 218
 219        txt = ba.Lstr(
 220            resource='coopSelectWindow.proMultInfoText',
 221            subs=[
 222                (
 223                    '${PERCENT}',
 224                    str(
 225                        ba.internal.get_v1_account_misc_read_val(
 226                            'proPowerRankingBoost', 10
 227                        )
 228                    ),
 229                ),
 230                (
 231                    '${PRO}',
 232                    ba.Lstr(
 233                        resource='store.bombSquadProNameText',
 234                        subs=[('${APP_NAME}', ba.Lstr(resource='titleText'))],
 235                    ),
 236                ),
 237            ],
 238        )
 239        confirm.ConfirmWindow(
 240            txt,
 241            cancel_button=False,
 242            width=460,
 243            height=130,
 244            origin_widget=self._pro_mult_button,
 245        )
 246
 247    def _on_trophies_press(self) -> None:
 248        from bastd.ui.trophies import TrophiesWindow
 249
 250        info = self._league_rank_data
 251        if info is not None:
 252            prtb = self._power_ranking_trophies_button
 253            TrophiesWindow(
 254                position=prtb.get_screen_space_center(),
 255                data=info,
 256            )
 257        else:
 258            ba.playsound(ba.getsound('error'))
 259
 260    def _on_power_ranking_query_response(
 261        self, data: dict[str, Any] | None
 262    ) -> None:
 263        self._doing_power_ranking_query = False
 264        # important: *only* cache this if we requested the current season..
 265        if data is not None and data.get('s', None) is None:
 266            ba.app.accounts_v1.cache_league_rank_data(data)
 267        # always store a copy locally though (even for other seasons)
 268        self._league_rank_data = copy.deepcopy(data)
 269        self._update_for_league_rank_data(data)
 270
 271    def _restore_state(self) -> None:
 272        pass
 273
 274    def _update(self, show: bool = False) -> None:
 275
 276        cur_time = ba.time(ba.TimeType.REAL)
 277
 278        # if our account state has changed, refresh our UI
 279        account_state = ba.internal.get_v1_account_state()
 280        if account_state != self._account_state:
 281            self._account_state = account_state
 282            self._save_state()
 283            self._refresh()
 284
 285            # and power ranking too...
 286            if not self._doing_power_ranking_query:
 287                self._last_power_ranking_query_time = None
 288
 289        # send off a new power-ranking query if its been long enough or our
 290        # requested season has changed or whatnot..
 291        if not self._doing_power_ranking_query and (
 292            self._last_power_ranking_query_time is None
 293            or cur_time - self._last_power_ranking_query_time > 30.0
 294        ):
 295            try:
 296                if show:
 297                    ba.textwidget(edit=self._league_title_text, text='')
 298                    ba.textwidget(edit=self._league_text, text='')
 299                    ba.textwidget(edit=self._league_number_text, text='')
 300                    ba.textwidget(
 301                        edit=self._your_power_ranking_text,
 302                        text=ba.Lstr(
 303                            value='${A}...',
 304                            subs=[('${A}', ba.Lstr(resource='loadingText'))],
 305                        ),
 306                    )
 307                    ba.textwidget(edit=self._to_ranked_text, text='')
 308                    ba.textwidget(edit=self._power_ranking_rank_text, text='')
 309                    ba.textwidget(edit=self._season_ends_text, text='')
 310                    ba.textwidget(edit=self._trophy_counts_reset_text, text='')
 311            except Exception:
 312                ba.print_exception('Error showing updated rank info.')
 313
 314            self._last_power_ranking_query_time = cur_time
 315            self._doing_power_ranking_query = True
 316            ba.internal.power_ranking_query(
 317                season=self._requested_season,
 318                callback=ba.WeakCall(self._on_power_ranking_query_response),
 319            )
 320
 321    def _refresh(self) -> None:
 322        # pylint: disable=too-many-statements
 323
 324        # (re)create the sub-container if need be..
 325        if self._subcontainer is not None:
 326            self._subcontainer.delete()
 327        self._subcontainer = ba.containerwidget(
 328            parent=self._scrollwidget,
 329            size=(self._subcontainerwidth, self._subcontainerheight),
 330            background=False,
 331        )
 332
 333        w_parent = self._subcontainer
 334        v = self._subcontainerheight - 20
 335
 336        v -= 0
 337
 338        h2 = 80
 339        v2 = v - 60
 340        worth_color = (0.6, 0.6, 0.65)
 341        tally_color = (0.5, 0.6, 0.8)
 342        spc = 43
 343
 344        h_offs_tally = 150
 345        tally_maxwidth = 120
 346        v2 -= 70
 347
 348        ba.textwidget(
 349            parent=w_parent,
 350            position=(h2 - 60, v2 + 106),
 351            size=(0, 0),
 352            flatness=1.0,
 353            shadow=0.0,
 354            text=ba.Lstr(resource='coopSelectWindow.pointsText'),
 355            h_align='left',
 356            v_align='center',
 357            scale=0.8,
 358            color=(1, 1, 1, 0.3),
 359            maxwidth=200,
 360        )
 361
 362        self._power_ranking_achievements_button = ba.buttonwidget(
 363            parent=w_parent,
 364            position=(h2 - 60, v2 + 10),
 365            size=(200, 80),
 366            icon=ba.gettexture('achievementsIcon'),
 367            autoselect=True,
 368            on_activate_call=ba.WeakCall(self._on_achievements_press),
 369            up_widget=self._back_button,
 370            left_widget=self._back_button,
 371            color=(0.5, 0.5, 0.6),
 372            textcolor=(0.7, 0.7, 0.8),
 373            label='',
 374        )
 375
 376        self._power_ranking_achievement_total_text = ba.textwidget(
 377            parent=w_parent,
 378            position=(h2 + h_offs_tally, v2 + 45),
 379            size=(0, 0),
 380            flatness=1.0,
 381            shadow=0.0,
 382            text='-',
 383            h_align='left',
 384            v_align='center',
 385            scale=0.8,
 386            color=tally_color,
 387            maxwidth=tally_maxwidth,
 388        )
 389
 390        v2 -= 80
 391
 392        self._power_ranking_trophies_button = ba.buttonwidget(
 393            parent=w_parent,
 394            position=(h2 - 60, v2 + 10),
 395            size=(200, 80),
 396            icon=ba.gettexture('medalSilver'),
 397            autoselect=True,
 398            on_activate_call=ba.WeakCall(self._on_trophies_press),
 399            left_widget=self._back_button,
 400            color=(0.5, 0.5, 0.6),
 401            textcolor=(0.7, 0.7, 0.8),
 402            label='',
 403        )
 404        self._power_ranking_trophies_total_text = ba.textwidget(
 405            parent=w_parent,
 406            position=(h2 + h_offs_tally, v2 + 45),
 407            size=(0, 0),
 408            flatness=1.0,
 409            shadow=0.0,
 410            text='-',
 411            h_align='left',
 412            v_align='center',
 413            scale=0.8,
 414            color=tally_color,
 415            maxwidth=tally_maxwidth,
 416        )
 417
 418        v2 -= 100
 419
 420        ba.textwidget(
 421            parent=w_parent,
 422            position=(h2 - 60, v2 + 86),
 423            size=(0, 0),
 424            flatness=1.0,
 425            shadow=0.0,
 426            text=ba.Lstr(resource='coopSelectWindow.multipliersText'),
 427            h_align='left',
 428            v_align='center',
 429            scale=0.8,
 430            color=(1, 1, 1, 0.3),
 431            maxwidth=200,
 432        )
 433
 434        self._activity_mult_button: ba.Widget | None
 435        if ba.internal.get_v1_account_misc_read_val('act', False):
 436            self._activity_mult_button = ba.buttonwidget(
 437                parent=w_parent,
 438                position=(h2 - 60, v2 + 10),
 439                size=(200, 60),
 440                icon=ba.gettexture('heart'),
 441                icon_color=(0.5, 0, 0.5),
 442                label=ba.Lstr(resource='coopSelectWindow.activityText'),
 443                autoselect=True,
 444                on_activate_call=ba.WeakCall(self._on_activity_mult_press),
 445                left_widget=self._back_button,
 446                color=(0.5, 0.5, 0.6),
 447                textcolor=(0.7, 0.7, 0.8),
 448            )
 449
 450            self._activity_mult_text = ba.textwidget(
 451                parent=w_parent,
 452                position=(h2 + h_offs_tally, v2 + 40),
 453                size=(0, 0),
 454                flatness=1.0,
 455                shadow=0.0,
 456                text='-',
 457                h_align='left',
 458                v_align='center',
 459                scale=0.8,
 460                color=tally_color,
 461                maxwidth=tally_maxwidth,
 462            )
 463            v2 -= 65
 464        else:
 465            self._activity_mult_button = None
 466
 467        self._pro_mult_button = ba.buttonwidget(
 468            parent=w_parent,
 469            position=(h2 - 60, v2 + 10),
 470            size=(200, 60),
 471            icon=ba.gettexture('logo'),
 472            icon_color=(0.3, 0, 0.3),
 473            label=ba.Lstr(
 474                resource='store.bombSquadProNameText',
 475                subs=[('${APP_NAME}', ba.Lstr(resource='titleText'))],
 476            ),
 477            autoselect=True,
 478            on_activate_call=ba.WeakCall(self._on_pro_mult_press),
 479            left_widget=self._back_button,
 480            color=(0.5, 0.5, 0.6),
 481            textcolor=(0.7, 0.7, 0.8),
 482        )
 483
 484        self._pro_mult_text = ba.textwidget(
 485            parent=w_parent,
 486            position=(h2 + h_offs_tally, v2 + 40),
 487            size=(0, 0),
 488            flatness=1.0,
 489            shadow=0.0,
 490            text='-',
 491            h_align='left',
 492            v_align='center',
 493            scale=0.8,
 494            color=tally_color,
 495            maxwidth=tally_maxwidth,
 496        )
 497        v2 -= 30
 498
 499        v2 -= spc
 500        ba.textwidget(
 501            parent=w_parent,
 502            position=(h2 + h_offs_tally - 10 - 40, v2 + 35),
 503            size=(0, 0),
 504            flatness=1.0,
 505            shadow=0.0,
 506            text=ba.Lstr(resource='finalScoreText'),
 507            h_align='right',
 508            v_align='center',
 509            scale=0.9,
 510            color=worth_color,
 511            maxwidth=150,
 512        )
 513        self._power_ranking_total_text = ba.textwidget(
 514            parent=w_parent,
 515            position=(h2 + h_offs_tally - 40, v2 + 35),
 516            size=(0, 0),
 517            flatness=1.0,
 518            shadow=0.0,
 519            text='-',
 520            h_align='left',
 521            v_align='center',
 522            scale=0.9,
 523            color=tally_color,
 524            maxwidth=tally_maxwidth,
 525        )
 526
 527        self._season_show_text = ba.textwidget(
 528            parent=w_parent,
 529            position=(390 - 15, v - 20),
 530            size=(0, 0),
 531            color=(0.6, 0.6, 0.7),
 532            maxwidth=200,
 533            text=ba.Lstr(resource='showText'),
 534            h_align='right',
 535            v_align='center',
 536            scale=0.8,
 537            shadow=0,
 538            flatness=1.0,
 539        )
 540
 541        self._league_title_text = ba.textwidget(
 542            parent=w_parent,
 543            position=(470, v - 97),
 544            size=(0, 0),
 545            color=(0.6, 0.6, 0.7),
 546            maxwidth=230,
 547            text='',
 548            h_align='center',
 549            v_align='center',
 550            scale=0.9,
 551            shadow=0,
 552            flatness=1.0,
 553        )
 554
 555        self._league_text_scale = 1.8
 556        self._league_text_maxwidth = 210
 557        self._league_text = ba.textwidget(
 558            parent=w_parent,
 559            position=(470, v - 140),
 560            size=(0, 0),
 561            color=(1, 1, 1),
 562            maxwidth=self._league_text_maxwidth,
 563            text='-',
 564            h_align='center',
 565            v_align='center',
 566            scale=self._league_text_scale,
 567            shadow=1.0,
 568            flatness=1.0,
 569        )
 570        self._league_number_base_pos = (470, v - 140)
 571        self._league_number_text = ba.textwidget(
 572            parent=w_parent,
 573            position=(470, v - 140),
 574            size=(0, 0),
 575            color=(1, 1, 1),
 576            maxwidth=100,
 577            text='',
 578            h_align='left',
 579            v_align='center',
 580            scale=0.8,
 581            shadow=1.0,
 582            flatness=1.0,
 583        )
 584
 585        self._your_power_ranking_text = ba.textwidget(
 586            parent=w_parent,
 587            position=(470, v - 142 - 70),
 588            size=(0, 0),
 589            color=(0.6, 0.6, 0.7),
 590            maxwidth=230,
 591            text='',
 592            h_align='center',
 593            v_align='center',
 594            scale=0.9,
 595            shadow=0,
 596            flatness=1.0,
 597        )
 598
 599        self._to_ranked_text = ba.textwidget(
 600            parent=w_parent,
 601            position=(470, v - 250 - 70),
 602            size=(0, 0),
 603            color=(0.6, 0.6, 0.7),
 604            maxwidth=230,
 605            text='',
 606            h_align='center',
 607            v_align='center',
 608            scale=0.8,
 609            shadow=0,
 610            flatness=1.0,
 611        )
 612
 613        self._power_ranking_rank_text = ba.textwidget(
 614            parent=w_parent,
 615            position=(473, v - 210 - 70),
 616            size=(0, 0),
 617            big=False,
 618            text='-',
 619            h_align='center',
 620            v_align='center',
 621            scale=1.0,
 622        )
 623
 624        self._season_ends_text = ba.textwidget(
 625            parent=w_parent,
 626            position=(470, v - 380),
 627            size=(0, 0),
 628            color=(0.6, 0.6, 0.6),
 629            maxwidth=230,
 630            text='',
 631            h_align='center',
 632            v_align='center',
 633            scale=0.9,
 634            shadow=0,
 635            flatness=1.0,
 636        )
 637        self._trophy_counts_reset_text = ba.textwidget(
 638            parent=w_parent,
 639            position=(470, v - 410),
 640            size=(0, 0),
 641            color=(0.5, 0.5, 0.5),
 642            maxwidth=230,
 643            text='Trophy counts will reset next season.',
 644            h_align='center',
 645            v_align='center',
 646            scale=0.8,
 647            shadow=0,
 648            flatness=1.0,
 649        )
 650
 651        self._power_ranking_score_widgets = []
 652
 653        self._power_ranking_score_v = v - 56
 654
 655        h = 707
 656        v -= 451
 657
 658        self._see_more_button = ba.buttonwidget(
 659            parent=w_parent,
 660            label=self._rdict.seeMoreText,
 661            position=(h, v),
 662            color=(0.5, 0.5, 0.6),
 663            textcolor=(0.7, 0.7, 0.8),
 664            size=(230, 60),
 665            autoselect=True,
 666            on_activate_call=ba.WeakCall(self._on_more_press),
 667        )
 668
 669    def _on_more_press(self) -> None:
 670        our_login_id = ba.internal.get_public_login_id()
 671        # our_login_id = _bs.get_account_misc_read_val_2(
 672        #     'resolvedAccountID', None)
 673        if not self._can_do_more_button or our_login_id is None:
 674            ba.playsound(ba.getsound('error'))
 675            ba.screenmessage(
 676                ba.Lstr(resource='unavailableText'), color=(1, 0, 0)
 677            )
 678            return
 679        if self._season is None:
 680            season_str = ''
 681        else:
 682            season_str = '&season=' + (
 683                'all_time' if self._season == 'a' else self._season
 684            )
 685        if self._league_url_arg != '':
 686            league_str = '&league=' + self._league_url_arg
 687        else:
 688            league_str = ''
 689        ba.open_url(
 690            ba.internal.get_master_server_address()
 691            + '/highscores?list=powerRankings&v=2'
 692            + league_str
 693            + season_str
 694            + '&player='
 695            + our_login_id
 696        )
 697
 698    def _update_for_league_rank_data(self, data: dict[str, Any] | None) -> None:
 699        # pylint: disable=too-many-statements
 700        # pylint: disable=too-many-branches
 701        # pylint: disable=too-many-locals
 702        if not self._root_widget:
 703            return
 704        accounts = ba.app.accounts_v1
 705        in_top = data is not None and data['rank'] is not None
 706        eq_text = self._rdict.powerRankingPointsEqualsText
 707        pts_txt = self._rdict.powerRankingPointsText
 708        num_text = ba.Lstr(resource='numberText').evaluate()
 709        do_percent = False
 710        finished_season_unranked = False
 711        self._can_do_more_button = True
 712        extra_text = ''
 713        if ba.internal.get_v1_account_state() != 'signed_in':
 714            status_text = (
 715                '(' + ba.Lstr(resource='notSignedInText').evaluate() + ')'
 716            )
 717        elif in_top:
 718            assert data is not None
 719            status_text = num_text.replace('${NUMBER}', str(data['rank']))
 720        elif data is not None:
 721            try:
 722                # handle old seasons where we didn't wind up ranked
 723                # at the end..
 724                if not data['scores']:
 725                    status_text = (
 726                        self._rdict.powerRankingFinishedSeasonUnrankedText
 727                    )
 728                    extra_text = ''
 729                    finished_season_unranked = True
 730                    self._can_do_more_button = False
 731                else:
 732                    our_points = accounts.get_league_rank_points(data)
 733                    progress = float(our_points) / max(1, data['scores'][-1][1])
 734                    status_text = str(int(progress * 100.0)) + '%'
 735                    extra_text = (
 736                        '\n'
 737                        + self._rdict.powerRankingPointsToRankedText.replace(
 738                            '${CURRENT}', str(our_points)
 739                        ).replace('${REMAINING}', str(data['scores'][-1][1]))
 740                    )
 741                    do_percent = True
 742            except Exception:
 743                ba.print_exception('Error updating power ranking.')
 744                status_text = self._rdict.powerRankingNotInTopText.replace(
 745                    '${NUMBER}', str(data['listSize'])
 746                )
 747                extra_text = ''
 748        else:
 749            status_text = '-'
 750
 751        self._season = data['s'] if data is not None else None
 752
 753        v = self._subcontainerheight - 20
 754        popup_was_selected = False
 755        if self._season_popup_menu is not None:
 756            btn = self._season_popup_menu.get_button()
 757            assert self._subcontainer
 758            if self._subcontainer.get_selected_child() == btn:
 759                popup_was_selected = True
 760            btn.delete()
 761        season_choices = []
 762        season_choices_display = []
 763        did_first = False
 764        self._is_current_season = False
 765        if data is not None:
 766            # build our list of seasons we have available
 767            for ssn in data['sl']:
 768                season_choices.append(ssn)
 769                if ssn != 'a' and not did_first:
 770                    season_choices_display.append(
 771                        ba.Lstr(
 772                            resource='league.currentSeasonText',
 773                            subs=[('${NUMBER}', ssn)],
 774                        )
 775                    )
 776                    did_first = True
 777                    # if we either did not specify a season or specified the
 778                    # first, we're looking at the current..
 779                    if self._season in [ssn, None]:
 780                        self._is_current_season = True
 781                elif ssn == 'a':
 782                    season_choices_display.append(
 783                        ba.Lstr(resource='league.allTimeText')
 784                    )
 785                else:
 786                    season_choices_display.append(
 787                        ba.Lstr(
 788                            resource='league.seasonText',
 789                            subs=[('${NUMBER}', ssn)],
 790                        )
 791                    )
 792            assert self._subcontainer
 793            self._season_popup_menu = popup_ui.PopupMenu(
 794                parent=self._subcontainer,
 795                position=(390, v - 45),
 796                width=150,
 797                button_size=(200, 50),
 798                choices=season_choices,
 799                on_value_change_call=ba.WeakCall(self._on_season_change),
 800                choices_display=season_choices_display,
 801                current_choice=self._season,
 802            )
 803            if popup_was_selected:
 804                ba.containerwidget(
 805                    edit=self._subcontainer,
 806                    selected_child=self._season_popup_menu.get_button(),
 807                )
 808            ba.widget(edit=self._see_more_button, show_buffer_bottom=100)
 809            ba.widget(
 810                edit=self._season_popup_menu.get_button(),
 811                up_widget=self._back_button,
 812            )
 813            ba.widget(
 814                edit=self._back_button,
 815                down_widget=self._power_ranking_achievements_button,
 816                right_widget=self._season_popup_menu.get_button(),
 817            )
 818
 819        ba.textwidget(
 820            edit=self._league_title_text,
 821            text=''
 822            if self._season == 'a'
 823            else ba.Lstr(resource='league.leagueText'),
 824        )
 825
 826        if data is None:
 827            lname = ''
 828            lnum = ''
 829            lcolor = (1, 1, 1)
 830            self._league_url_arg = ''
 831        elif self._season == 'a':
 832            lname = ba.Lstr(resource='league.allTimeText').evaluate()
 833            lnum = ''
 834            lcolor = (1, 1, 1)
 835            self._league_url_arg = ''
 836        else:
 837            lnum = ('[' + str(data['l']['i']) + ']') if data['l']['i2'] else ''
 838            lname = ba.Lstr(
 839                translate=('leagueNames', data['l']['n'])
 840            ).evaluate()
 841            lcolor = data['l']['c']
 842            self._league_url_arg = (
 843                data['l']['n'] + '_' + str(data['l']['i'])
 844            ).lower()
 845
 846        to_end_string: ba.Lstr | str
 847        if data is None or self._season == 'a' or data['se'] is None:
 848            to_end_string = ''
 849            show_season_end = False
 850        else:
 851            show_season_end = True
 852            days_to_end = data['se'][0]
 853            minutes_to_end = data['se'][1]
 854            if days_to_end > 0:
 855                to_end_string = ba.Lstr(
 856                    resource='league.seasonEndsDaysText',
 857                    subs=[('${NUMBER}', str(days_to_end))],
 858                )
 859            elif days_to_end == 0 and minutes_to_end >= 60:
 860                to_end_string = ba.Lstr(
 861                    resource='league.seasonEndsHoursText',
 862                    subs=[('${NUMBER}', str(minutes_to_end // 60))],
 863                )
 864            elif days_to_end == 0 and minutes_to_end >= 0:
 865                to_end_string = ba.Lstr(
 866                    resource='league.seasonEndsMinutesText',
 867                    subs=[('${NUMBER}', str(minutes_to_end))],
 868                )
 869            else:
 870                to_end_string = ba.Lstr(
 871                    resource='league.seasonEndedDaysAgoText',
 872                    subs=[('${NUMBER}', str(-(days_to_end + 1)))],
 873                )
 874
 875        ba.textwidget(edit=self._season_ends_text, text=to_end_string)
 876        ba.textwidget(
 877            edit=self._trophy_counts_reset_text,
 878            text=ba.Lstr(resource='league.trophyCountsResetText')
 879            if self._is_current_season and show_season_end
 880            else '',
 881        )
 882
 883        ba.textwidget(edit=self._league_text, text=lname, color=lcolor)
 884        l_text_width = min(
 885            self._league_text_maxwidth,
 886            ba.internal.get_string_width(lname, suppress_warning=True)
 887            * self._league_text_scale,
 888        )
 889        ba.textwidget(
 890            edit=self._league_number_text,
 891            text=lnum,
 892            color=lcolor,
 893            position=(
 894                self._league_number_base_pos[0] + l_text_width * 0.5 + 8,
 895                self._league_number_base_pos[1] + 10,
 896            ),
 897        )
 898        ba.textwidget(
 899            edit=self._to_ranked_text,
 900            text=ba.Lstr(resource='coopSelectWindow.toRankedText').evaluate()
 901            + ''
 902            + extra_text
 903            if do_percent
 904            else '',
 905        )
 906
 907        ba.textwidget(
 908            edit=self._your_power_ranking_text,
 909            text=ba.Lstr(
 910                resource='rankText',
 911                fallback_resource='coopSelectWindow.yourPowerRankingText',
 912            )
 913            if (not do_percent)
 914            else '',
 915        )
 916
 917        ba.textwidget(
 918            edit=self._power_ranking_rank_text,
 919            position=(473, v - 70 - (170 if do_percent else 220)),
 920            text=status_text,
 921            big=(in_top or do_percent),
 922            scale=3.0
 923            if (in_top or do_percent)
 924            else 0.7
 925            if finished_season_unranked
 926            else 1.0,
 927        )
 928
 929        if self._activity_mult_button is not None:
 930            if data is None or data['act'] is None:
 931                ba.buttonwidget(
 932                    edit=self._activity_mult_button,
 933                    textcolor=(0.7, 0.7, 0.8, 0.5),
 934                    icon_color=(0.5, 0, 0.5, 0.3),
 935                )
 936                ba.textwidget(edit=self._activity_mult_text, text='     -')
 937            else:
 938                ba.buttonwidget(
 939                    edit=self._activity_mult_button,
 940                    textcolor=(0.7, 0.7, 0.8, 1.0),
 941                    icon_color=(0.5, 0, 0.5, 1.0),
 942                )
 943                # pylint: disable=consider-using-f-string
 944                ba.textwidget(
 945                    edit=self._activity_mult_text,
 946                    text='x ' + ('%.2f' % data['act']),
 947                )
 948
 949        have_pro = False if data is None else data['p']
 950        pro_mult = (
 951            1.0
 952            + float(
 953                ba.internal.get_v1_account_misc_read_val(
 954                    'proPowerRankingBoost', 0.0
 955                )
 956            )
 957            * 0.01
 958        )
 959        # pylint: disable=consider-using-f-string
 960        ba.textwidget(
 961            edit=self._pro_mult_text,
 962            text='     -'
 963            if (data is None or not have_pro)
 964            else 'x ' + ('%.2f' % pro_mult),
 965        )
 966        ba.buttonwidget(
 967            edit=self._pro_mult_button,
 968            textcolor=(0.7, 0.7, 0.8, (1.0 if have_pro else 0.5)),
 969            icon_color=(0.5, 0, 0.5) if have_pro else (0.5, 0, 0.5, 0.2),
 970        )
 971        ba.buttonwidget(
 972            edit=self._power_ranking_achievements_button,
 973            label=('' if data is None else (str(data['a']) + ' '))
 974            + ba.Lstr(resource='achievementsText').evaluate(),
 975        )
 976
 977        # for the achievement value, use the number they gave us for
 978        # non-current seasons; otherwise calc our own
 979        total_ach_value = 0
 980        for ach in ba.app.ach.achievements:
 981            if ach.complete:
 982                total_ach_value += ach.power_ranking_value
 983        if self._season != 'a' and not self._is_current_season:
 984            if data is not None and 'at' in data:
 985                total_ach_value = data['at']
 986
 987        ba.textwidget(
 988            edit=self._power_ranking_achievement_total_text,
 989            text='-'
 990            if data is None
 991            else ('+ ' + pts_txt.replace('${NUMBER}', str(total_ach_value))),
 992        )
 993
 994        total_trophies_count = accounts.get_league_rank_points(
 995            data, 'trophyCount'
 996        )
 997        total_trophies_value = accounts.get_league_rank_points(data, 'trophies')
 998        ba.buttonwidget(
 999            edit=self._power_ranking_trophies_button,
1000            label=('' if data is None else (str(total_trophies_count) + ' '))
1001            + ba.Lstr(resource='trophiesText').evaluate(),
1002        )
1003        ba.textwidget(
1004            edit=self._power_ranking_trophies_total_text,
1005            text='-'
1006            if data is None
1007            else (
1008                '+ ' + pts_txt.replace('${NUMBER}', str(total_trophies_value))
1009            ),
1010        )
1011
1012        ba.textwidget(
1013            edit=self._power_ranking_total_text,
1014            text='-'
1015            if data is None
1016            else eq_text.replace(
1017                '${NUMBER}', str(accounts.get_league_rank_points(data))
1018            ),
1019        )
1020        for widget in self._power_ranking_score_widgets:
1021            widget.delete()
1022        self._power_ranking_score_widgets = []
1023
1024        scores = data['scores'] if data is not None else []
1025        tally_color = (0.5, 0.6, 0.8)
1026        w_parent = self._subcontainer
1027        v2 = self._power_ranking_score_v
1028
1029        for score in scores:
1030            h2 = 680
1031            is_us = score[3]
1032            self._power_ranking_score_widgets.append(
1033                ba.textwidget(
1034                    parent=w_parent,
1035                    position=(h2 - 20, v2),
1036                    size=(0, 0),
1037                    color=(1, 1, 1) if is_us else (0.6, 0.6, 0.7),
1038                    maxwidth=40,
1039                    flatness=1.0,
1040                    shadow=0.0,
1041                    text=num_text.replace('${NUMBER}', str(score[0])),
1042                    h_align='right',
1043                    v_align='center',
1044                    scale=0.5,
1045                )
1046            )
1047            self._power_ranking_score_widgets.append(
1048                ba.textwidget(
1049                    parent=w_parent,
1050                    position=(h2 + 20, v2),
1051                    size=(0, 0),
1052                    color=(1, 1, 1) if is_us else tally_color,
1053                    maxwidth=60,
1054                    text=str(score[1]),
1055                    flatness=1.0,
1056                    shadow=0.0,
1057                    h_align='center',
1058                    v_align='center',
1059                    scale=0.7,
1060                )
1061            )
1062            txt = ba.textwidget(
1063                parent=w_parent,
1064                position=(h2 + 60, v2 - (28 * 0.5) / 0.9),
1065                size=(210 / 0.9, 28),
1066                color=(1, 1, 1) if is_us else (0.6, 0.6, 0.6),
1067                maxwidth=210,
1068                flatness=1.0,
1069                shadow=0.0,
1070                autoselect=True,
1071                selectable=True,
1072                click_activate=True,
1073                text=score[2],
1074                h_align='left',
1075                v_align='center',
1076                scale=0.9,
1077            )
1078            self._power_ranking_score_widgets.append(txt)
1079            ba.textwidget(
1080                edit=txt,
1081                on_activate_call=ba.Call(
1082                    self._show_account_info, score[4], txt
1083                ),
1084            )
1085            assert self._season_popup_menu is not None
1086            ba.widget(
1087                edit=txt, left_widget=self._season_popup_menu.get_button()
1088            )
1089            v2 -= 28
1090
1091    def _show_account_info(
1092        self, account_id: str, textwidget: ba.Widget
1093    ) -> None:
1094        from bastd.ui.account import viewer
1095
1096        ba.playsound(ba.getsound('swish'))
1097        viewer.AccountViewerWindow(
1098            account_id=account_id, position=textwidget.get_screen_space_center()
1099        )
1100
1101    def _on_season_change(self, value: str) -> None:
1102        self._requested_season = value
1103        self._last_power_ranking_query_time = None  # make sure we update asap
1104        self._update(show=True)
1105
1106    def _save_state(self) -> None:
1107        pass
1108
1109    def _back(self) -> None:
1110        from bastd.ui.coop.browser import CoopBrowserWindow
1111
1112        self._save_state()
1113        ba.containerwidget(
1114            edit=self._root_widget, transition=self._transition_out
1115        )
1116        if not self._modal:
1117            ba.app.ui.set_main_menu_window(
1118                CoopBrowserWindow(transition='in_left').get_root_widget()
1119            )
class LeagueRankWindow(ba.ui.Window):
  20class LeagueRankWindow(ba.Window):
  21    """Window for showing league rank."""
  22
  23    def __init__(
  24        self,
  25        transition: str = 'in_right',
  26        modal: bool = False,
  27        origin_widget: ba.Widget | None = None,
  28    ):
  29        ba.set_analytics_screen('League Rank Window')
  30
  31        self._league_rank_data: dict[str, Any] | None = None
  32        self._modal = modal
  33
  34        # If they provided an origin-widget, scale up from that.
  35        scale_origin: tuple[float, float] | None
  36        if origin_widget is not None:
  37            self._transition_out = 'out_scale'
  38            scale_origin = origin_widget.get_screen_space_center()
  39            transition = 'in_scale'
  40        else:
  41            self._transition_out = 'out_right'
  42            scale_origin = None
  43
  44        uiscale = ba.app.ui.uiscale
  45        self._width = 1320 if uiscale is ba.UIScale.SMALL else 1120
  46        x_inset = 100 if uiscale is ba.UIScale.SMALL else 0
  47        self._height = (
  48            657
  49            if uiscale is ba.UIScale.SMALL
  50            else 710
  51            if uiscale is ba.UIScale.MEDIUM
  52            else 800
  53        )
  54        self._r = 'coopSelectWindow'
  55        self._rdict = ba.app.lang.get_resource(self._r)
  56        top_extra = 20 if uiscale is ba.UIScale.SMALL else 0
  57
  58        self._league_url_arg = ''
  59
  60        self._is_current_season = False
  61        self._can_do_more_button = True
  62
  63        super().__init__(
  64            root_widget=ba.containerwidget(
  65                size=(self._width, self._height + top_extra),
  66                stack_offset=(0, -15)
  67                if uiscale is ba.UIScale.SMALL
  68                else (0, 10)
  69                if uiscale is ba.UIScale.MEDIUM
  70                else (0, 0),
  71                transition=transition,
  72                scale_origin_stack_offset=scale_origin,
  73                scale=(
  74                    1.2
  75                    if uiscale is ba.UIScale.SMALL
  76                    else 0.93
  77                    if uiscale is ba.UIScale.MEDIUM
  78                    else 0.8
  79                ),
  80            )
  81        )
  82
  83        self._back_button = btn = ba.buttonwidget(
  84            parent=self._root_widget,
  85            position=(
  86                75 + x_inset,
  87                self._height - 87 - (4 if uiscale is ba.UIScale.SMALL else 0),
  88            ),
  89            size=(120, 60),
  90            scale=1.2,
  91            autoselect=True,
  92            label=ba.Lstr(resource='doneText' if self._modal else 'backText'),
  93            button_type=None if self._modal else 'back',
  94            on_activate_call=self._back,
  95        )
  96
  97        self._title_text = ba.textwidget(
  98            parent=self._root_widget,
  99            position=(self._width * 0.5, self._height - 56),
 100            size=(0, 0),
 101            text=ba.Lstr(
 102                resource='league.leagueRankText',
 103                fallback_resource='coopSelectWindow.powerRankingText',
 104            ),
 105            h_align='center',
 106            color=ba.app.ui.title_color,
 107            scale=1.4,
 108            maxwidth=600,
 109            v_align='center',
 110        )
 111
 112        ba.buttonwidget(
 113            edit=btn,
 114            button_type='backSmall',
 115            position=(
 116                75 + x_inset,
 117                self._height - 87 - (2 if uiscale is ba.UIScale.SMALL else 0),
 118            ),
 119            size=(60, 55),
 120            label=ba.charstr(ba.SpecialChar.BACK),
 121        )
 122
 123        self._scroll_width = self._width - (130 + 2 * x_inset)
 124        self._scroll_height = self._height - 160
 125        self._scrollwidget = ba.scrollwidget(
 126            parent=self._root_widget,
 127            highlight=False,
 128            position=(65 + x_inset, 70),
 129            size=(self._scroll_width, self._scroll_height),
 130            center_small_content=True,
 131        )
 132        ba.widget(edit=self._scrollwidget, autoselect=True)
 133        ba.containerwidget(edit=self._scrollwidget, claims_left_right=True)
 134        ba.containerwidget(
 135            edit=self._root_widget,
 136            cancel_button=self._back_button,
 137            selected_child=self._back_button,
 138        )
 139
 140        self._last_power_ranking_query_time: float | None = None
 141        self._doing_power_ranking_query = False
 142
 143        self._subcontainer: ba.Widget | None = None
 144        self._subcontainerwidth = 800
 145        self._subcontainerheight = 483
 146        self._power_ranking_score_widgets: list[ba.Widget] = []
 147
 148        self._season_popup_menu: popup_ui.PopupMenu | None = None
 149        self._requested_season: str | None = None
 150        self._season: str | None = None
 151
 152        # take note of our account state; we'll refresh later if this changes
 153        self._account_state = ba.internal.get_v1_account_state()
 154
 155        self._refresh()
 156        self._restore_state()
 157
 158        # if we've got cached power-ranking data already, display it
 159        info = ba.app.accounts_v1.get_cached_league_rank_data()
 160        if info is not None:
 161            self._update_for_league_rank_data(info)
 162
 163        self._update_timer = ba.Timer(
 164            1.0,
 165            ba.WeakCall(self._update),
 166            timetype=ba.TimeType.REAL,
 167            repeat=True,
 168        )
 169        self._update(show=(info is None))
 170
 171    def _on_achievements_press(self) -> None:
 172        from bastd.ui import achievements
 173
 174        # only allow this for all-time or the current season
 175        # (we currently don't keep specific achievement data for old seasons)
 176        if self._season == 'a' or self._is_current_season:
 177            prab = self._power_ranking_achievements_button
 178            achievements.AchievementsWindow(
 179                position=prab.get_screen_space_center()
 180            )
 181        else:
 182            ba.screenmessage(
 183                ba.Lstr(
 184                    resource='achievementsUnavailableForOldSeasonsText',
 185                    fallback_resource='unavailableText',
 186                ),
 187                color=(1, 0, 0),
 188            )
 189            ba.playsound(ba.getsound('error'))
 190
 191    def _on_activity_mult_press(self) -> None:
 192        from bastd.ui import confirm
 193
 194        txt = ba.Lstr(
 195            resource='coopSelectWindow.activenessAllTimeInfoText'
 196            if self._season == 'a'
 197            else 'coopSelectWindow.activenessInfoText',
 198            subs=[
 199                (
 200                    '${MAX}',
 201                    str(
 202                        ba.internal.get_v1_account_misc_read_val(
 203                            'activenessMax', 1.0
 204                        )
 205                    ),
 206                )
 207            ],
 208        )
 209        confirm.ConfirmWindow(
 210            txt,
 211            cancel_button=False,
 212            width=460,
 213            height=150,
 214            origin_widget=self._activity_mult_button,
 215        )
 216
 217    def _on_pro_mult_press(self) -> None:
 218        from bastd.ui import confirm
 219
 220        txt = ba.Lstr(
 221            resource='coopSelectWindow.proMultInfoText',
 222            subs=[
 223                (
 224                    '${PERCENT}',
 225                    str(
 226                        ba.internal.get_v1_account_misc_read_val(
 227                            'proPowerRankingBoost', 10
 228                        )
 229                    ),
 230                ),
 231                (
 232                    '${PRO}',
 233                    ba.Lstr(
 234                        resource='store.bombSquadProNameText',
 235                        subs=[('${APP_NAME}', ba.Lstr(resource='titleText'))],
 236                    ),
 237                ),
 238            ],
 239        )
 240        confirm.ConfirmWindow(
 241            txt,
 242            cancel_button=False,
 243            width=460,
 244            height=130,
 245            origin_widget=self._pro_mult_button,
 246        )
 247
 248    def _on_trophies_press(self) -> None:
 249        from bastd.ui.trophies import TrophiesWindow
 250
 251        info = self._league_rank_data
 252        if info is not None:
 253            prtb = self._power_ranking_trophies_button
 254            TrophiesWindow(
 255                position=prtb.get_screen_space_center(),
 256                data=info,
 257            )
 258        else:
 259            ba.playsound(ba.getsound('error'))
 260
 261    def _on_power_ranking_query_response(
 262        self, data: dict[str, Any] | None
 263    ) -> None:
 264        self._doing_power_ranking_query = False
 265        # important: *only* cache this if we requested the current season..
 266        if data is not None and data.get('s', None) is None:
 267            ba.app.accounts_v1.cache_league_rank_data(data)
 268        # always store a copy locally though (even for other seasons)
 269        self._league_rank_data = copy.deepcopy(data)
 270        self._update_for_league_rank_data(data)
 271
 272    def _restore_state(self) -> None:
 273        pass
 274
 275    def _update(self, show: bool = False) -> None:
 276
 277        cur_time = ba.time(ba.TimeType.REAL)
 278
 279        # if our account state has changed, refresh our UI
 280        account_state = ba.internal.get_v1_account_state()
 281        if account_state != self._account_state:
 282            self._account_state = account_state
 283            self._save_state()
 284            self._refresh()
 285
 286            # and power ranking too...
 287            if not self._doing_power_ranking_query:
 288                self._last_power_ranking_query_time = None
 289
 290        # send off a new power-ranking query if its been long enough or our
 291        # requested season has changed or whatnot..
 292        if not self._doing_power_ranking_query and (
 293            self._last_power_ranking_query_time is None
 294            or cur_time - self._last_power_ranking_query_time > 30.0
 295        ):
 296            try:
 297                if show:
 298                    ba.textwidget(edit=self._league_title_text, text='')
 299                    ba.textwidget(edit=self._league_text, text='')
 300                    ba.textwidget(edit=self._league_number_text, text='')
 301                    ba.textwidget(
 302                        edit=self._your_power_ranking_text,
 303                        text=ba.Lstr(
 304                            value='${A}...',
 305                            subs=[('${A}', ba.Lstr(resource='loadingText'))],
 306                        ),
 307                    )
 308                    ba.textwidget(edit=self._to_ranked_text, text='')
 309                    ba.textwidget(edit=self._power_ranking_rank_text, text='')
 310                    ba.textwidget(edit=self._season_ends_text, text='')
 311                    ba.textwidget(edit=self._trophy_counts_reset_text, text='')
 312            except Exception:
 313                ba.print_exception('Error showing updated rank info.')
 314
 315            self._last_power_ranking_query_time = cur_time
 316            self._doing_power_ranking_query = True
 317            ba.internal.power_ranking_query(
 318                season=self._requested_season,
 319                callback=ba.WeakCall(self._on_power_ranking_query_response),
 320            )
 321
 322    def _refresh(self) -> None:
 323        # pylint: disable=too-many-statements
 324
 325        # (re)create the sub-container if need be..
 326        if self._subcontainer is not None:
 327            self._subcontainer.delete()
 328        self._subcontainer = ba.containerwidget(
 329            parent=self._scrollwidget,
 330            size=(self._subcontainerwidth, self._subcontainerheight),
 331            background=False,
 332        )
 333
 334        w_parent = self._subcontainer
 335        v = self._subcontainerheight - 20
 336
 337        v -= 0
 338
 339        h2 = 80
 340        v2 = v - 60
 341        worth_color = (0.6, 0.6, 0.65)
 342        tally_color = (0.5, 0.6, 0.8)
 343        spc = 43
 344
 345        h_offs_tally = 150
 346        tally_maxwidth = 120
 347        v2 -= 70
 348
 349        ba.textwidget(
 350            parent=w_parent,
 351            position=(h2 - 60, v2 + 106),
 352            size=(0, 0),
 353            flatness=1.0,
 354            shadow=0.0,
 355            text=ba.Lstr(resource='coopSelectWindow.pointsText'),
 356            h_align='left',
 357            v_align='center',
 358            scale=0.8,
 359            color=(1, 1, 1, 0.3),
 360            maxwidth=200,
 361        )
 362
 363        self._power_ranking_achievements_button = ba.buttonwidget(
 364            parent=w_parent,
 365            position=(h2 - 60, v2 + 10),
 366            size=(200, 80),
 367            icon=ba.gettexture('achievementsIcon'),
 368            autoselect=True,
 369            on_activate_call=ba.WeakCall(self._on_achievements_press),
 370            up_widget=self._back_button,
 371            left_widget=self._back_button,
 372            color=(0.5, 0.5, 0.6),
 373            textcolor=(0.7, 0.7, 0.8),
 374            label='',
 375        )
 376
 377        self._power_ranking_achievement_total_text = ba.textwidget(
 378            parent=w_parent,
 379            position=(h2 + h_offs_tally, v2 + 45),
 380            size=(0, 0),
 381            flatness=1.0,
 382            shadow=0.0,
 383            text='-',
 384            h_align='left',
 385            v_align='center',
 386            scale=0.8,
 387            color=tally_color,
 388            maxwidth=tally_maxwidth,
 389        )
 390
 391        v2 -= 80
 392
 393        self._power_ranking_trophies_button = ba.buttonwidget(
 394            parent=w_parent,
 395            position=(h2 - 60, v2 + 10),
 396            size=(200, 80),
 397            icon=ba.gettexture('medalSilver'),
 398            autoselect=True,
 399            on_activate_call=ba.WeakCall(self._on_trophies_press),
 400            left_widget=self._back_button,
 401            color=(0.5, 0.5, 0.6),
 402            textcolor=(0.7, 0.7, 0.8),
 403            label='',
 404        )
 405        self._power_ranking_trophies_total_text = ba.textwidget(
 406            parent=w_parent,
 407            position=(h2 + h_offs_tally, v2 + 45),
 408            size=(0, 0),
 409            flatness=1.0,
 410            shadow=0.0,
 411            text='-',
 412            h_align='left',
 413            v_align='center',
 414            scale=0.8,
 415            color=tally_color,
 416            maxwidth=tally_maxwidth,
 417        )
 418
 419        v2 -= 100
 420
 421        ba.textwidget(
 422            parent=w_parent,
 423            position=(h2 - 60, v2 + 86),
 424            size=(0, 0),
 425            flatness=1.0,
 426            shadow=0.0,
 427            text=ba.Lstr(resource='coopSelectWindow.multipliersText'),
 428            h_align='left',
 429            v_align='center',
 430            scale=0.8,
 431            color=(1, 1, 1, 0.3),
 432            maxwidth=200,
 433        )
 434
 435        self._activity_mult_button: ba.Widget | None
 436        if ba.internal.get_v1_account_misc_read_val('act', False):
 437            self._activity_mult_button = ba.buttonwidget(
 438                parent=w_parent,
 439                position=(h2 - 60, v2 + 10),
 440                size=(200, 60),
 441                icon=ba.gettexture('heart'),
 442                icon_color=(0.5, 0, 0.5),
 443                label=ba.Lstr(resource='coopSelectWindow.activityText'),
 444                autoselect=True,
 445                on_activate_call=ba.WeakCall(self._on_activity_mult_press),
 446                left_widget=self._back_button,
 447                color=(0.5, 0.5, 0.6),
 448                textcolor=(0.7, 0.7, 0.8),
 449            )
 450
 451            self._activity_mult_text = ba.textwidget(
 452                parent=w_parent,
 453                position=(h2 + h_offs_tally, v2 + 40),
 454                size=(0, 0),
 455                flatness=1.0,
 456                shadow=0.0,
 457                text='-',
 458                h_align='left',
 459                v_align='center',
 460                scale=0.8,
 461                color=tally_color,
 462                maxwidth=tally_maxwidth,
 463            )
 464            v2 -= 65
 465        else:
 466            self._activity_mult_button = None
 467
 468        self._pro_mult_button = ba.buttonwidget(
 469            parent=w_parent,
 470            position=(h2 - 60, v2 + 10),
 471            size=(200, 60),
 472            icon=ba.gettexture('logo'),
 473            icon_color=(0.3, 0, 0.3),
 474            label=ba.Lstr(
 475                resource='store.bombSquadProNameText',
 476                subs=[('${APP_NAME}', ba.Lstr(resource='titleText'))],
 477            ),
 478            autoselect=True,
 479            on_activate_call=ba.WeakCall(self._on_pro_mult_press),
 480            left_widget=self._back_button,
 481            color=(0.5, 0.5, 0.6),
 482            textcolor=(0.7, 0.7, 0.8),
 483        )
 484
 485        self._pro_mult_text = ba.textwidget(
 486            parent=w_parent,
 487            position=(h2 + h_offs_tally, v2 + 40),
 488            size=(0, 0),
 489            flatness=1.0,
 490            shadow=0.0,
 491            text='-',
 492            h_align='left',
 493            v_align='center',
 494            scale=0.8,
 495            color=tally_color,
 496            maxwidth=tally_maxwidth,
 497        )
 498        v2 -= 30
 499
 500        v2 -= spc
 501        ba.textwidget(
 502            parent=w_parent,
 503            position=(h2 + h_offs_tally - 10 - 40, v2 + 35),
 504            size=(0, 0),
 505            flatness=1.0,
 506            shadow=0.0,
 507            text=ba.Lstr(resource='finalScoreText'),
 508            h_align='right',
 509            v_align='center',
 510            scale=0.9,
 511            color=worth_color,
 512            maxwidth=150,
 513        )
 514        self._power_ranking_total_text = ba.textwidget(
 515            parent=w_parent,
 516            position=(h2 + h_offs_tally - 40, v2 + 35),
 517            size=(0, 0),
 518            flatness=1.0,
 519            shadow=0.0,
 520            text='-',
 521            h_align='left',
 522            v_align='center',
 523            scale=0.9,
 524            color=tally_color,
 525            maxwidth=tally_maxwidth,
 526        )
 527
 528        self._season_show_text = ba.textwidget(
 529            parent=w_parent,
 530            position=(390 - 15, v - 20),
 531            size=(0, 0),
 532            color=(0.6, 0.6, 0.7),
 533            maxwidth=200,
 534            text=ba.Lstr(resource='showText'),
 535            h_align='right',
 536            v_align='center',
 537            scale=0.8,
 538            shadow=0,
 539            flatness=1.0,
 540        )
 541
 542        self._league_title_text = ba.textwidget(
 543            parent=w_parent,
 544            position=(470, v - 97),
 545            size=(0, 0),
 546            color=(0.6, 0.6, 0.7),
 547            maxwidth=230,
 548            text='',
 549            h_align='center',
 550            v_align='center',
 551            scale=0.9,
 552            shadow=0,
 553            flatness=1.0,
 554        )
 555
 556        self._league_text_scale = 1.8
 557        self._league_text_maxwidth = 210
 558        self._league_text = ba.textwidget(
 559            parent=w_parent,
 560            position=(470, v - 140),
 561            size=(0, 0),
 562            color=(1, 1, 1),
 563            maxwidth=self._league_text_maxwidth,
 564            text='-',
 565            h_align='center',
 566            v_align='center',
 567            scale=self._league_text_scale,
 568            shadow=1.0,
 569            flatness=1.0,
 570        )
 571        self._league_number_base_pos = (470, v - 140)
 572        self._league_number_text = ba.textwidget(
 573            parent=w_parent,
 574            position=(470, v - 140),
 575            size=(0, 0),
 576            color=(1, 1, 1),
 577            maxwidth=100,
 578            text='',
 579            h_align='left',
 580            v_align='center',
 581            scale=0.8,
 582            shadow=1.0,
 583            flatness=1.0,
 584        )
 585
 586        self._your_power_ranking_text = ba.textwidget(
 587            parent=w_parent,
 588            position=(470, v - 142 - 70),
 589            size=(0, 0),
 590            color=(0.6, 0.6, 0.7),
 591            maxwidth=230,
 592            text='',
 593            h_align='center',
 594            v_align='center',
 595            scale=0.9,
 596            shadow=0,
 597            flatness=1.0,
 598        )
 599
 600        self._to_ranked_text = ba.textwidget(
 601            parent=w_parent,
 602            position=(470, v - 250 - 70),
 603            size=(0, 0),
 604            color=(0.6, 0.6, 0.7),
 605            maxwidth=230,
 606            text='',
 607            h_align='center',
 608            v_align='center',
 609            scale=0.8,
 610            shadow=0,
 611            flatness=1.0,
 612        )
 613
 614        self._power_ranking_rank_text = ba.textwidget(
 615            parent=w_parent,
 616            position=(473, v - 210 - 70),
 617            size=(0, 0),
 618            big=False,
 619            text='-',
 620            h_align='center',
 621            v_align='center',
 622            scale=1.0,
 623        )
 624
 625        self._season_ends_text = ba.textwidget(
 626            parent=w_parent,
 627            position=(470, v - 380),
 628            size=(0, 0),
 629            color=(0.6, 0.6, 0.6),
 630            maxwidth=230,
 631            text='',
 632            h_align='center',
 633            v_align='center',
 634            scale=0.9,
 635            shadow=0,
 636            flatness=1.0,
 637        )
 638        self._trophy_counts_reset_text = ba.textwidget(
 639            parent=w_parent,
 640            position=(470, v - 410),
 641            size=(0, 0),
 642            color=(0.5, 0.5, 0.5),
 643            maxwidth=230,
 644            text='Trophy counts will reset next season.',
 645            h_align='center',
 646            v_align='center',
 647            scale=0.8,
 648            shadow=0,
 649            flatness=1.0,
 650        )
 651
 652        self._power_ranking_score_widgets = []
 653
 654        self._power_ranking_score_v = v - 56
 655
 656        h = 707
 657        v -= 451
 658
 659        self._see_more_button = ba.buttonwidget(
 660            parent=w_parent,
 661            label=self._rdict.seeMoreText,
 662            position=(h, v),
 663            color=(0.5, 0.5, 0.6),
 664            textcolor=(0.7, 0.7, 0.8),
 665            size=(230, 60),
 666            autoselect=True,
 667            on_activate_call=ba.WeakCall(self._on_more_press),
 668        )
 669
 670    def _on_more_press(self) -> None:
 671        our_login_id = ba.internal.get_public_login_id()
 672        # our_login_id = _bs.get_account_misc_read_val_2(
 673        #     'resolvedAccountID', None)
 674        if not self._can_do_more_button or our_login_id is None:
 675            ba.playsound(ba.getsound('error'))
 676            ba.screenmessage(
 677                ba.Lstr(resource='unavailableText'), color=(1, 0, 0)
 678            )
 679            return
 680        if self._season is None:
 681            season_str = ''
 682        else:
 683            season_str = '&season=' + (
 684                'all_time' if self._season == 'a' else self._season
 685            )
 686        if self._league_url_arg != '':
 687            league_str = '&league=' + self._league_url_arg
 688        else:
 689            league_str = ''
 690        ba.open_url(
 691            ba.internal.get_master_server_address()
 692            + '/highscores?list=powerRankings&v=2'
 693            + league_str
 694            + season_str
 695            + '&player='
 696            + our_login_id
 697        )
 698
 699    def _update_for_league_rank_data(self, data: dict[str, Any] | None) -> None:
 700        # pylint: disable=too-many-statements
 701        # pylint: disable=too-many-branches
 702        # pylint: disable=too-many-locals
 703        if not self._root_widget:
 704            return
 705        accounts = ba.app.accounts_v1
 706        in_top = data is not None and data['rank'] is not None
 707        eq_text = self._rdict.powerRankingPointsEqualsText
 708        pts_txt = self._rdict.powerRankingPointsText
 709        num_text = ba.Lstr(resource='numberText').evaluate()
 710        do_percent = False
 711        finished_season_unranked = False
 712        self._can_do_more_button = True
 713        extra_text = ''
 714        if ba.internal.get_v1_account_state() != 'signed_in':
 715            status_text = (
 716                '(' + ba.Lstr(resource='notSignedInText').evaluate() + ')'
 717            )
 718        elif in_top:
 719            assert data is not None
 720            status_text = num_text.replace('${NUMBER}', str(data['rank']))
 721        elif data is not None:
 722            try:
 723                # handle old seasons where we didn't wind up ranked
 724                # at the end..
 725                if not data['scores']:
 726                    status_text = (
 727                        self._rdict.powerRankingFinishedSeasonUnrankedText
 728                    )
 729                    extra_text = ''
 730                    finished_season_unranked = True
 731                    self._can_do_more_button = False
 732                else:
 733                    our_points = accounts.get_league_rank_points(data)
 734                    progress = float(our_points) / max(1, data['scores'][-1][1])
 735                    status_text = str(int(progress * 100.0)) + '%'
 736                    extra_text = (
 737                        '\n'
 738                        + self._rdict.powerRankingPointsToRankedText.replace(
 739                            '${CURRENT}', str(our_points)
 740                        ).replace('${REMAINING}', str(data['scores'][-1][1]))
 741                    )
 742                    do_percent = True
 743            except Exception:
 744                ba.print_exception('Error updating power ranking.')
 745                status_text = self._rdict.powerRankingNotInTopText.replace(
 746                    '${NUMBER}', str(data['listSize'])
 747                )
 748                extra_text = ''
 749        else:
 750            status_text = '-'
 751
 752        self._season = data['s'] if data is not None else None
 753
 754        v = self._subcontainerheight - 20
 755        popup_was_selected = False
 756        if self._season_popup_menu is not None:
 757            btn = self._season_popup_menu.get_button()
 758            assert self._subcontainer
 759            if self._subcontainer.get_selected_child() == btn:
 760                popup_was_selected = True
 761            btn.delete()
 762        season_choices = []
 763        season_choices_display = []
 764        did_first = False
 765        self._is_current_season = False
 766        if data is not None:
 767            # build our list of seasons we have available
 768            for ssn in data['sl']:
 769                season_choices.append(ssn)
 770                if ssn != 'a' and not did_first:
 771                    season_choices_display.append(
 772                        ba.Lstr(
 773                            resource='league.currentSeasonText',
 774                            subs=[('${NUMBER}', ssn)],
 775                        )
 776                    )
 777                    did_first = True
 778                    # if we either did not specify a season or specified the
 779                    # first, we're looking at the current..
 780                    if self._season in [ssn, None]:
 781                        self._is_current_season = True
 782                elif ssn == 'a':
 783                    season_choices_display.append(
 784                        ba.Lstr(resource='league.allTimeText')
 785                    )
 786                else:
 787                    season_choices_display.append(
 788                        ba.Lstr(
 789                            resource='league.seasonText',
 790                            subs=[('${NUMBER}', ssn)],
 791                        )
 792                    )
 793            assert self._subcontainer
 794            self._season_popup_menu = popup_ui.PopupMenu(
 795                parent=self._subcontainer,
 796                position=(390, v - 45),
 797                width=150,
 798                button_size=(200, 50),
 799                choices=season_choices,
 800                on_value_change_call=ba.WeakCall(self._on_season_change),
 801                choices_display=season_choices_display,
 802                current_choice=self._season,
 803            )
 804            if popup_was_selected:
 805                ba.containerwidget(
 806                    edit=self._subcontainer,
 807                    selected_child=self._season_popup_menu.get_button(),
 808                )
 809            ba.widget(edit=self._see_more_button, show_buffer_bottom=100)
 810            ba.widget(
 811                edit=self._season_popup_menu.get_button(),
 812                up_widget=self._back_button,
 813            )
 814            ba.widget(
 815                edit=self._back_button,
 816                down_widget=self._power_ranking_achievements_button,
 817                right_widget=self._season_popup_menu.get_button(),
 818            )
 819
 820        ba.textwidget(
 821            edit=self._league_title_text,
 822            text=''
 823            if self._season == 'a'
 824            else ba.Lstr(resource='league.leagueText'),
 825        )
 826
 827        if data is None:
 828            lname = ''
 829            lnum = ''
 830            lcolor = (1, 1, 1)
 831            self._league_url_arg = ''
 832        elif self._season == 'a':
 833            lname = ba.Lstr(resource='league.allTimeText').evaluate()
 834            lnum = ''
 835            lcolor = (1, 1, 1)
 836            self._league_url_arg = ''
 837        else:
 838            lnum = ('[' + str(data['l']['i']) + ']') if data['l']['i2'] else ''
 839            lname = ba.Lstr(
 840                translate=('leagueNames', data['l']['n'])
 841            ).evaluate()
 842            lcolor = data['l']['c']
 843            self._league_url_arg = (
 844                data['l']['n'] + '_' + str(data['l']['i'])
 845            ).lower()
 846
 847        to_end_string: ba.Lstr | str
 848        if data is None or self._season == 'a' or data['se'] is None:
 849            to_end_string = ''
 850            show_season_end = False
 851        else:
 852            show_season_end = True
 853            days_to_end = data['se'][0]
 854            minutes_to_end = data['se'][1]
 855            if days_to_end > 0:
 856                to_end_string = ba.Lstr(
 857                    resource='league.seasonEndsDaysText',
 858                    subs=[('${NUMBER}', str(days_to_end))],
 859                )
 860            elif days_to_end == 0 and minutes_to_end >= 60:
 861                to_end_string = ba.Lstr(
 862                    resource='league.seasonEndsHoursText',
 863                    subs=[('${NUMBER}', str(minutes_to_end // 60))],
 864                )
 865            elif days_to_end == 0 and minutes_to_end >= 0:
 866                to_end_string = ba.Lstr(
 867                    resource='league.seasonEndsMinutesText',
 868                    subs=[('${NUMBER}', str(minutes_to_end))],
 869                )
 870            else:
 871                to_end_string = ba.Lstr(
 872                    resource='league.seasonEndedDaysAgoText',
 873                    subs=[('${NUMBER}', str(-(days_to_end + 1)))],
 874                )
 875
 876        ba.textwidget(edit=self._season_ends_text, text=to_end_string)
 877        ba.textwidget(
 878            edit=self._trophy_counts_reset_text,
 879            text=ba.Lstr(resource='league.trophyCountsResetText')
 880            if self._is_current_season and show_season_end
 881            else '',
 882        )
 883
 884        ba.textwidget(edit=self._league_text, text=lname, color=lcolor)
 885        l_text_width = min(
 886            self._league_text_maxwidth,
 887            ba.internal.get_string_width(lname, suppress_warning=True)
 888            * self._league_text_scale,
 889        )
 890        ba.textwidget(
 891            edit=self._league_number_text,
 892            text=lnum,
 893            color=lcolor,
 894            position=(
 895                self._league_number_base_pos[0] + l_text_width * 0.5 + 8,
 896                self._league_number_base_pos[1] + 10,
 897            ),
 898        )
 899        ba.textwidget(
 900            edit=self._to_ranked_text,
 901            text=ba.Lstr(resource='coopSelectWindow.toRankedText').evaluate()
 902            + ''
 903            + extra_text
 904            if do_percent
 905            else '',
 906        )
 907
 908        ba.textwidget(
 909            edit=self._your_power_ranking_text,
 910            text=ba.Lstr(
 911                resource='rankText',
 912                fallback_resource='coopSelectWindow.yourPowerRankingText',
 913            )
 914            if (not do_percent)
 915            else '',
 916        )
 917
 918        ba.textwidget(
 919            edit=self._power_ranking_rank_text,
 920            position=(473, v - 70 - (170 if do_percent else 220)),
 921            text=status_text,
 922            big=(in_top or do_percent),
 923            scale=3.0
 924            if (in_top or do_percent)
 925            else 0.7
 926            if finished_season_unranked
 927            else 1.0,
 928        )
 929
 930        if self._activity_mult_button is not None:
 931            if data is None or data['act'] is None:
 932                ba.buttonwidget(
 933                    edit=self._activity_mult_button,
 934                    textcolor=(0.7, 0.7, 0.8, 0.5),
 935                    icon_color=(0.5, 0, 0.5, 0.3),
 936                )
 937                ba.textwidget(edit=self._activity_mult_text, text='     -')
 938            else:
 939                ba.buttonwidget(
 940                    edit=self._activity_mult_button,
 941                    textcolor=(0.7, 0.7, 0.8, 1.0),
 942                    icon_color=(0.5, 0, 0.5, 1.0),
 943                )
 944                # pylint: disable=consider-using-f-string
 945                ba.textwidget(
 946                    edit=self._activity_mult_text,
 947                    text='x ' + ('%.2f' % data['act']),
 948                )
 949
 950        have_pro = False if data is None else data['p']
 951        pro_mult = (
 952            1.0
 953            + float(
 954                ba.internal.get_v1_account_misc_read_val(
 955                    'proPowerRankingBoost', 0.0
 956                )
 957            )
 958            * 0.01
 959        )
 960        # pylint: disable=consider-using-f-string
 961        ba.textwidget(
 962            edit=self._pro_mult_text,
 963            text='     -'
 964            if (data is None or not have_pro)
 965            else 'x ' + ('%.2f' % pro_mult),
 966        )
 967        ba.buttonwidget(
 968            edit=self._pro_mult_button,
 969            textcolor=(0.7, 0.7, 0.8, (1.0 if have_pro else 0.5)),
 970            icon_color=(0.5, 0, 0.5) if have_pro else (0.5, 0, 0.5, 0.2),
 971        )
 972        ba.buttonwidget(
 973            edit=self._power_ranking_achievements_button,
 974            label=('' if data is None else (str(data['a']) + ' '))
 975            + ba.Lstr(resource='achievementsText').evaluate(),
 976        )
 977
 978        # for the achievement value, use the number they gave us for
 979        # non-current seasons; otherwise calc our own
 980        total_ach_value = 0
 981        for ach in ba.app.ach.achievements:
 982            if ach.complete:
 983                total_ach_value += ach.power_ranking_value
 984        if self._season != 'a' and not self._is_current_season:
 985            if data is not None and 'at' in data:
 986                total_ach_value = data['at']
 987
 988        ba.textwidget(
 989            edit=self._power_ranking_achievement_total_text,
 990            text='-'
 991            if data is None
 992            else ('+ ' + pts_txt.replace('${NUMBER}', str(total_ach_value))),
 993        )
 994
 995        total_trophies_count = accounts.get_league_rank_points(
 996            data, 'trophyCount'
 997        )
 998        total_trophies_value = accounts.get_league_rank_points(data, 'trophies')
 999        ba.buttonwidget(
1000            edit=self._power_ranking_trophies_button,
1001            label=('' if data is None else (str(total_trophies_count) + ' '))
1002            + ba.Lstr(resource='trophiesText').evaluate(),
1003        )
1004        ba.textwidget(
1005            edit=self._power_ranking_trophies_total_text,
1006            text='-'
1007            if data is None
1008            else (
1009                '+ ' + pts_txt.replace('${NUMBER}', str(total_trophies_value))
1010            ),
1011        )
1012
1013        ba.textwidget(
1014            edit=self._power_ranking_total_text,
1015            text='-'
1016            if data is None
1017            else eq_text.replace(
1018                '${NUMBER}', str(accounts.get_league_rank_points(data))
1019            ),
1020        )
1021        for widget in self._power_ranking_score_widgets:
1022            widget.delete()
1023        self._power_ranking_score_widgets = []
1024
1025        scores = data['scores'] if data is not None else []
1026        tally_color = (0.5, 0.6, 0.8)
1027        w_parent = self._subcontainer
1028        v2 = self._power_ranking_score_v
1029
1030        for score in scores:
1031            h2 = 680
1032            is_us = score[3]
1033            self._power_ranking_score_widgets.append(
1034                ba.textwidget(
1035                    parent=w_parent,
1036                    position=(h2 - 20, v2),
1037                    size=(0, 0),
1038                    color=(1, 1, 1) if is_us else (0.6, 0.6, 0.7),
1039                    maxwidth=40,
1040                    flatness=1.0,
1041                    shadow=0.0,
1042                    text=num_text.replace('${NUMBER}', str(score[0])),
1043                    h_align='right',
1044                    v_align='center',
1045                    scale=0.5,
1046                )
1047            )
1048            self._power_ranking_score_widgets.append(
1049                ba.textwidget(
1050                    parent=w_parent,
1051                    position=(h2 + 20, v2),
1052                    size=(0, 0),
1053                    color=(1, 1, 1) if is_us else tally_color,
1054                    maxwidth=60,
1055                    text=str(score[1]),
1056                    flatness=1.0,
1057                    shadow=0.0,
1058                    h_align='center',
1059                    v_align='center',
1060                    scale=0.7,
1061                )
1062            )
1063            txt = ba.textwidget(
1064                parent=w_parent,
1065                position=(h2 + 60, v2 - (28 * 0.5) / 0.9),
1066                size=(210 / 0.9, 28),
1067                color=(1, 1, 1) if is_us else (0.6, 0.6, 0.6),
1068                maxwidth=210,
1069                flatness=1.0,
1070                shadow=0.0,
1071                autoselect=True,
1072                selectable=True,
1073                click_activate=True,
1074                text=score[2],
1075                h_align='left',
1076                v_align='center',
1077                scale=0.9,
1078            )
1079            self._power_ranking_score_widgets.append(txt)
1080            ba.textwidget(
1081                edit=txt,
1082                on_activate_call=ba.Call(
1083                    self._show_account_info, score[4], txt
1084                ),
1085            )
1086            assert self._season_popup_menu is not None
1087            ba.widget(
1088                edit=txt, left_widget=self._season_popup_menu.get_button()
1089            )
1090            v2 -= 28
1091
1092    def _show_account_info(
1093        self, account_id: str, textwidget: ba.Widget
1094    ) -> None:
1095        from bastd.ui.account import viewer
1096
1097        ba.playsound(ba.getsound('swish'))
1098        viewer.AccountViewerWindow(
1099            account_id=account_id, position=textwidget.get_screen_space_center()
1100        )
1101
1102    def _on_season_change(self, value: str) -> None:
1103        self._requested_season = value
1104        self._last_power_ranking_query_time = None  # make sure we update asap
1105        self._update(show=True)
1106
1107    def _save_state(self) -> None:
1108        pass
1109
1110    def _back(self) -> None:
1111        from bastd.ui.coop.browser import CoopBrowserWindow
1112
1113        self._save_state()
1114        ba.containerwidget(
1115            edit=self._root_widget, transition=self._transition_out
1116        )
1117        if not self._modal:
1118            ba.app.ui.set_main_menu_window(
1119                CoopBrowserWindow(transition='in_left').get_root_widget()
1120            )

Window for showing league rank.

LeagueRankWindow( transition: str = 'in_right', modal: bool = False, origin_widget: _ba.Widget | None = None)
 23    def __init__(
 24        self,
 25        transition: str = 'in_right',
 26        modal: bool = False,
 27        origin_widget: ba.Widget | None = None,
 28    ):
 29        ba.set_analytics_screen('League Rank Window')
 30
 31        self._league_rank_data: dict[str, Any] | None = None
 32        self._modal = modal
 33
 34        # If they provided an origin-widget, scale up from that.
 35        scale_origin: tuple[float, float] | None
 36        if origin_widget is not None:
 37            self._transition_out = 'out_scale'
 38            scale_origin = origin_widget.get_screen_space_center()
 39            transition = 'in_scale'
 40        else:
 41            self._transition_out = 'out_right'
 42            scale_origin = None
 43
 44        uiscale = ba.app.ui.uiscale
 45        self._width = 1320 if uiscale is ba.UIScale.SMALL else 1120
 46        x_inset = 100 if uiscale is ba.UIScale.SMALL else 0
 47        self._height = (
 48            657
 49            if uiscale is ba.UIScale.SMALL
 50            else 710
 51            if uiscale is ba.UIScale.MEDIUM
 52            else 800
 53        )
 54        self._r = 'coopSelectWindow'
 55        self._rdict = ba.app.lang.get_resource(self._r)
 56        top_extra = 20 if uiscale is ba.UIScale.SMALL else 0
 57
 58        self._league_url_arg = ''
 59
 60        self._is_current_season = False
 61        self._can_do_more_button = True
 62
 63        super().__init__(
 64            root_widget=ba.containerwidget(
 65                size=(self._width, self._height + top_extra),
 66                stack_offset=(0, -15)
 67                if uiscale is ba.UIScale.SMALL
 68                else (0, 10)
 69                if uiscale is ba.UIScale.MEDIUM
 70                else (0, 0),
 71                transition=transition,
 72                scale_origin_stack_offset=scale_origin,
 73                scale=(
 74                    1.2
 75                    if uiscale is ba.UIScale.SMALL
 76                    else 0.93
 77                    if uiscale is ba.UIScale.MEDIUM
 78                    else 0.8
 79                ),
 80            )
 81        )
 82
 83        self._back_button = btn = ba.buttonwidget(
 84            parent=self._root_widget,
 85            position=(
 86                75 + x_inset,
 87                self._height - 87 - (4 if uiscale is ba.UIScale.SMALL else 0),
 88            ),
 89            size=(120, 60),
 90            scale=1.2,
 91            autoselect=True,
 92            label=ba.Lstr(resource='doneText' if self._modal else 'backText'),
 93            button_type=None if self._modal else 'back',
 94            on_activate_call=self._back,
 95        )
 96
 97        self._title_text = ba.textwidget(
 98            parent=self._root_widget,
 99            position=(self._width * 0.5, self._height - 56),
100            size=(0, 0),
101            text=ba.Lstr(
102                resource='league.leagueRankText',
103                fallback_resource='coopSelectWindow.powerRankingText',
104            ),
105            h_align='center',
106            color=ba.app.ui.title_color,
107            scale=1.4,
108            maxwidth=600,
109            v_align='center',
110        )
111
112        ba.buttonwidget(
113            edit=btn,
114            button_type='backSmall',
115            position=(
116                75 + x_inset,
117                self._height - 87 - (2 if uiscale is ba.UIScale.SMALL else 0),
118            ),
119            size=(60, 55),
120            label=ba.charstr(ba.SpecialChar.BACK),
121        )
122
123        self._scroll_width = self._width - (130 + 2 * x_inset)
124        self._scroll_height = self._height - 160
125        self._scrollwidget = ba.scrollwidget(
126            parent=self._root_widget,
127            highlight=False,
128            position=(65 + x_inset, 70),
129            size=(self._scroll_width, self._scroll_height),
130            center_small_content=True,
131        )
132        ba.widget(edit=self._scrollwidget, autoselect=True)
133        ba.containerwidget(edit=self._scrollwidget, claims_left_right=True)
134        ba.containerwidget(
135            edit=self._root_widget,
136            cancel_button=self._back_button,
137            selected_child=self._back_button,
138        )
139
140        self._last_power_ranking_query_time: float | None = None
141        self._doing_power_ranking_query = False
142
143        self._subcontainer: ba.Widget | None = None
144        self._subcontainerwidth = 800
145        self._subcontainerheight = 483
146        self._power_ranking_score_widgets: list[ba.Widget] = []
147
148        self._season_popup_menu: popup_ui.PopupMenu | None = None
149        self._requested_season: str | None = None
150        self._season: str | None = None
151
152        # take note of our account state; we'll refresh later if this changes
153        self._account_state = ba.internal.get_v1_account_state()
154
155        self._refresh()
156        self._restore_state()
157
158        # if we've got cached power-ranking data already, display it
159        info = ba.app.accounts_v1.get_cached_league_rank_data()
160        if info is not None:
161            self._update_for_league_rank_data(info)
162
163        self._update_timer = ba.Timer(
164            1.0,
165            ba.WeakCall(self._update),
166            timetype=ba.TimeType.REAL,
167            repeat=True,
168        )
169        self._update(show=(info is None))
Inherited Members
ba.ui.Window
get_root_widget