bauiv1lib.coop.tournamentbutton

Defines button for co-op games.

  1# Released under the MIT License. See LICENSE for details.
  2#
  3"""Defines button for co-op games."""
  4
  5from __future__ import annotations
  6
  7from typing import TYPE_CHECKING
  8import copy
  9
 10import bauiv1 as bui
 11
 12if TYPE_CHECKING:
 13    from typing import Any, Callable
 14
 15# As of 1.7.37, no longer charging entry fees for tourneys (tourneys now
 16# reward chests and the game now makes its money from tokens/ads used to
 17# speed up chest openings).
 18USE_ENTRY_FEES = False
 19
 20
 21class TournamentButton:
 22    """Button showing a tournament in coop window."""
 23
 24    def __init__(
 25        self,
 26        parent: bui.Widget,
 27        x: float,
 28        y: float,
 29        select: bool,
 30        on_pressed: Callable[[TournamentButton], None],
 31    ) -> None:
 32        # pylint: disable=too-many-positional-arguments
 33        # pylint: disable=too-many-statements
 34        self._r = 'coopSelectWindow'
 35        sclx = 300
 36        scly = 195.0
 37        self.on_pressed = on_pressed
 38        self.lsbt = bui.getmesh('level_select_button_transparent')
 39        self.lsbo = bui.getmesh('level_select_button_opaque')
 40        self.allow_ads = False
 41        self.tournament_id: str | None = None
 42        self.game: str | None = None
 43        self.time_remaining: int = 0
 44        self.has_time_remaining: bool = False
 45        self.leader: Any = None
 46        self.required_league: str | None = None
 47        self._base_x_offs = 0 if USE_ENTRY_FEES else -45.0
 48        self.button = btn = bui.buttonwidget(
 49            parent=parent,
 50            position=(x + 23, y + 4),
 51            size=(sclx, scly),
 52            label='',
 53            button_type='square',
 54            autoselect=True,
 55            on_activate_call=bui.WeakCall(self._pressed),
 56        )
 57        bui.widget(
 58            edit=btn,
 59            show_buffer_bottom=50,
 60            show_buffer_top=50,
 61            show_buffer_left=400,
 62            show_buffer_right=200,
 63        )
 64        if select:
 65            bui.containerwidget(
 66                edit=parent, selected_child=btn, visible_child=btn
 67            )
 68        image_width = sclx * 0.85 * 0.75
 69
 70        self.image = bui.imagewidget(
 71            parent=parent,
 72            draw_controller=btn,
 73            position=(x + 21 + sclx * 0.5 - image_width * 0.5, y + scly - 150),
 74            size=(image_width, image_width * 0.5),
 75            mesh_transparent=self.lsbt,
 76            mesh_opaque=self.lsbo,
 77            texture=bui.gettexture('black'),
 78            opacity=0.2,
 79            mask_texture=bui.gettexture('mapPreviewMask'),
 80        )
 81
 82        self.lock_image = bui.imagewidget(
 83            parent=parent,
 84            draw_controller=btn,
 85            position=(x + 21 + sclx * 0.5 - image_width * 0.15, y + scly - 130),
 86            size=(image_width * 0.3, image_width * 0.3),
 87            texture=bui.gettexture('lock'),
 88            opacity=0.0,
 89        )
 90
 91        self.button_text = bui.textwidget(
 92            parent=parent,
 93            draw_controller=btn,
 94            position=(x + 20 + sclx * 0.5, y + scly - 35),
 95            size=(0, 0),
 96            h_align='center',
 97            text='-',
 98            v_align='center',
 99            maxwidth=sclx * 0.76,
100            scale=0.85,
101            color=(0.8, 1.0, 0.8, 1.0),
102        )
103
104        header_color = (0.43, 0.4, 0.5, 1)
105        value_color = (0.6, 0.6, 0.6, 1)
106
107        x_offs = self._base_x_offs
108
109        # No longer using entry fees.
110        if USE_ENTRY_FEES:
111            bui.textwidget(
112                parent=parent,
113                draw_controller=btn,
114                position=(x + 360, y + scly - 20),
115                size=(0, 0),
116                h_align='center',
117                text=bui.Lstr(resource=f'{self._r}.entryFeeText'),
118                v_align='center',
119                maxwidth=100,
120                scale=0.9,
121                color=header_color,
122                flatness=1.0,
123            )
124
125            self.entry_fee_text_top = bui.textwidget(
126                parent=parent,
127                draw_controller=btn,
128                position=(x + 360, y + scly - 60),
129                size=(0, 0),
130                h_align='center',
131                text='-',
132                v_align='center',
133                maxwidth=60,
134                scale=1.3,
135                color=value_color,
136                flatness=1.0,
137            )
138            self.entry_fee_text_or = bui.textwidget(
139                parent=parent,
140                draw_controller=btn,
141                position=(x + 360, y + scly - 90),
142                size=(0, 0),
143                h_align='center',
144                text='',
145                v_align='center',
146                maxwidth=60,
147                scale=0.5,
148                color=value_color,
149                flatness=1.0,
150            )
151            self.entry_fee_text_remaining = bui.textwidget(
152                parent=parent,
153                draw_controller=btn,
154                position=(x + 360, y + scly - 90),
155                size=(0, 0),
156                h_align='center',
157                text='',
158                v_align='center',
159                maxwidth=60,
160                scale=0.5,
161                color=value_color,
162                flatness=1.0,
163            )
164
165            self.entry_fee_ad_image = bui.imagewidget(
166                parent=parent,
167                size=(40, 40),
168                draw_controller=btn,
169                position=(x + 360 - 20, y + scly - 140),
170                opacity=0.0,
171                texture=bui.gettexture('tv'),
172            )
173
174        x_offs += 50
175
176        bui.textwidget(
177            parent=parent,
178            draw_controller=btn,
179            position=(x + 447 + x_offs, y + scly - 20),
180            size=(0, 0),
181            h_align='center',
182            text=bui.Lstr(resource=f'{self._r}.prizesText'),
183            v_align='center',
184            maxwidth=130,
185            scale=0.9,
186            color=header_color,
187            flatness=1.0,
188        )
189
190        self.button_x = x
191        self.button_y = y
192        self.button_scale_y = scly
193
194        # Offset for prize range/values.
195        xo2 = 0.0
196
197        self.prize_range_1_text = bui.textwidget(
198            parent=parent,
199            draw_controller=btn,
200            position=(x + 355 + xo2 + x_offs, y + scly - 93),
201            size=(0, 0),
202            h_align='right',
203            v_align='center',
204            maxwidth=50,
205            text='',
206            scale=0.8,
207            color=header_color,
208            flatness=1.0,
209        )
210        self.prize_value_1_text = bui.textwidget(
211            parent=parent,
212            draw_controller=btn,
213            position=(x + 380 + xo2 + x_offs, y + scly - 93),
214            size=(0, 0),
215            h_align='left',
216            text='',
217            v_align='center',
218            maxwidth=100,
219            color=value_color,
220            flatness=1.0,
221        )
222        self._chestsz = 50
223        self.prize_chest_1_image = bui.imagewidget(
224            parent=parent,
225            draw_controller=btn,
226            texture=bui.gettexture('white'),
227            position=(x + 380 + xo2 + x_offs, y + scly - 93),
228            size=(self._chestsz, self._chestsz),
229            opacity=0.0,
230        )
231
232        self.prize_range_2_text = bui.textwidget(
233            parent=parent,
234            draw_controller=btn,
235            position=(x + 355 + xo2 + x_offs, y + scly - 93),
236            size=(0, 0),
237            h_align='right',
238            text='',
239            v_align='center',
240            maxwidth=50,
241            scale=0.8,
242            color=header_color,
243            flatness=1.0,
244        )
245        self.prize_value_2_text = bui.textwidget(
246            parent=parent,
247            draw_controller=btn,
248            position=(x + 380 + xo2 + x_offs, y + scly - 93),
249            size=(0, 0),
250            h_align='left',
251            text='',
252            v_align='center',
253            maxwidth=100,
254            color=value_color,
255            flatness=1.0,
256        )
257        self.prize_chest_2_image = bui.imagewidget(
258            parent=parent,
259            draw_controller=btn,
260            texture=bui.gettexture('white'),
261            position=(x + 380 + xo2 + x_offs, y + scly - 93),
262            size=(self._chestsz, self._chestsz),
263            opacity=0.0,
264        )
265
266        self.prize_range_3_text = bui.textwidget(
267            parent=parent,
268            draw_controller=btn,
269            position=(x + 355 + xo2 + x_offs, y + scly - 93),
270            size=(0, 0),
271            h_align='right',
272            text='',
273            v_align='center',
274            maxwidth=50,
275            scale=0.8,
276            color=header_color,
277            flatness=1.0,
278        )
279        self.prize_value_3_text = bui.textwidget(
280            parent=parent,
281            draw_controller=btn,
282            position=(x + 380 + xo2 + x_offs, y + scly - 93),
283            size=(0, 0),
284            h_align='left',
285            text='',
286            v_align='center',
287            maxwidth=100,
288            color=value_color,
289            flatness=1.0,
290        )
291        self.prize_chest_3_image = bui.imagewidget(
292            parent=parent,
293            draw_controller=btn,
294            texture=bui.gettexture('white'),
295            position=(x + 380 + xo2 + x_offs, y + scly - 93),
296            size=(self._chestsz, self._chestsz),
297            opacity=0.0,
298        )
299
300        bui.textwidget(
301            parent=parent,
302            draw_controller=btn,
303            position=(x + 625 + x_offs, y + scly - 20),
304            size=(0, 0),
305            h_align='center',
306            text=bui.Lstr(resource=f'{self._r}.currentBestText'),
307            v_align='center',
308            maxwidth=180,
309            scale=0.9,
310            color=header_color,
311            flatness=1.0,
312        )
313        self.current_leader_name_text = bui.textwidget(
314            parent=parent,
315            draw_controller=btn,
316            position=(
317                x + 625 + x_offs - (170 / 1.4) * 0.5,
318                y + scly - 60 - 40 * 0.5,
319            ),
320            selectable=True,
321            click_activate=True,
322            autoselect=True,
323            on_activate_call=bui.WeakCall(self._show_leader),
324            size=(170 / 1.4, 40),
325            h_align='center',
326            text='-',
327            v_align='center',
328            maxwidth=170,
329            glow_type='uniform',
330            scale=1.4,
331            color=value_color,
332            flatness=1.0,
333        )
334        self.current_leader_score_text = bui.textwidget(
335            parent=parent,
336            draw_controller=btn,
337            position=(x + 625 + x_offs, y + scly - 113 + 10),
338            size=(0, 0),
339            h_align='center',
340            text='-',
341            v_align='center',
342            maxwidth=170,
343            scale=1.8,
344            color=value_color,
345            flatness=1.0,
346        )
347
348        self.more_scores_button = bui.buttonwidget(
349            parent=parent,
350            position=(x + 625 + x_offs - 60, y + scly - 50 - 125),
351            color=(0.5, 0.5, 0.6),
352            textcolor=(0.7, 0.7, 0.8),
353            label='-',
354            size=(120, 40),
355            autoselect=True,
356            up_widget=self.current_leader_name_text,
357            text_scale=0.6,
358            on_activate_call=bui.WeakCall(self._show_scores),
359        )
360        bui.widget(
361            edit=self.current_leader_name_text,
362            down_widget=self.more_scores_button,
363        )
364
365        bui.textwidget(
366            parent=parent,
367            draw_controller=btn,
368            position=(x + 840 + x_offs, y + scly - 20),
369            size=(0, 0),
370            h_align='center',
371            text=bui.Lstr(resource=f'{self._r}.timeRemainingText'),
372            v_align='center',
373            maxwidth=180,
374            scale=0.9,
375            color=header_color,
376            flatness=1.0,
377        )
378        self.time_remaining_value_text = bui.textwidget(
379            parent=parent,
380            draw_controller=btn,
381            position=(x + 840 + x_offs, y + scly - 68),
382            size=(0, 0),
383            h_align='center',
384            text='-',
385            v_align='center',
386            maxwidth=180,
387            scale=2.0,
388            color=value_color,
389            flatness=1.0,
390        )
391        self.time_remaining_out_of_text = bui.textwidget(
392            parent=parent,
393            draw_controller=btn,
394            position=(x + 840 + x_offs, y + scly - 110),
395            size=(0, 0),
396            h_align='center',
397            text='-',
398            v_align='center',
399            maxwidth=120,
400            scale=0.72,
401            color=(0.4, 0.4, 0.5),
402            flatness=1.0,
403        )
404        self._lock_update_timer = bui.AppTimer(
405            1.03, bui.WeakCall(self._update_lock_state), repeat=True
406        )
407
408    def _pressed(self) -> None:
409        self.on_pressed(self)
410
411    def _show_leader(self) -> None:
412        # pylint: disable=cyclic-import
413        from bauiv1lib.account.viewer import AccountViewerWindow
414
415        tournament_id = self.tournament_id
416
417        # FIXME: This assumes a single player entry in leader; should expand
418        #  this to work with multiple.
419        if (
420            tournament_id is None
421            or self.leader is None
422            or len(self.leader[2]) != 1
423        ):
424            bui.getsound('error').play()
425            return
426        bui.getsound('swish').play()
427        AccountViewerWindow(
428            account_id=self.leader[2][0].get('a', None),
429            profile_id=self.leader[2][0].get('p', None),
430            position=self.current_leader_name_text.get_screen_space_center(),
431        )
432
433    def _show_scores(self) -> None:
434        # pylint: disable=cyclic-import
435        from bauiv1lib.tournamentscores import TournamentScoresWindow
436
437        tournament_id = self.tournament_id
438        if tournament_id is None:
439            bui.getsound('error').play()
440            return
441
442        TournamentScoresWindow(
443            tournament_id=tournament_id,
444            position=self.more_scores_button.get_screen_space_center(),
445        )
446
447    def _update_lock_state(self) -> None:
448
449        if self.game is None:
450            return
451
452        assert bui.app.classic is not None
453
454        campaignname, levelname = self.game.split(':')
455        campaign = bui.app.classic.getcampaign(campaignname)
456
457        enabled = (
458            self.required_league is None
459            and bui.app.classic.is_game_unlocked(self.game)
460        )
461        bui.buttonwidget(
462            edit=self.button,
463            color=(0.5, 0.7, 0.2) if enabled else (0.5, 0.5, 0.5),
464        )
465        bui.imagewidget(edit=self.lock_image, opacity=0.0 if enabled else 1.0)
466        bui.imagewidget(
467            edit=self.image,
468            texture=bui.gettexture(
469                campaign.getlevel(levelname).preview_texture_name
470            ),
471            opacity=1.0 if enabled else 0.5,
472        )
473
474    def update_for_data(self, entry: dict[str, Any]) -> None:
475        """Update for new incoming data."""
476        # pylint: disable=too-many-statements
477        # pylint: disable=too-many-locals
478        # pylint: disable=too-many-branches
479
480        plus = bui.app.plus
481        assert plus is not None
482
483        classic = bui.app.classic
484        assert classic is not None
485
486        prize_y_offs = (
487            34
488            if 'prizeRange3' in entry
489            else 20 if 'prizeRange2' in entry else 12
490        )
491        x_offs = self._base_x_offs + 90
492
493        # Special offset for prize ranges/vals.
494        x_offs2 = x_offs - 20.0
495
496        # Special offset for prize chests.
497        x_offs2c = x_offs2 + 50
498
499        # Fetch prize range and trophy strings.
500        (pr1, pv1, pr2, pv2, pr3, pv3) = classic.get_tournament_prize_strings(
501            entry, include_tickets=False
502        )
503
504        self.time_remaining = entry['timeRemaining']
505        self.has_time_remaining = entry is not None
506        self.tournament_id = entry['tournamentID']
507        self.required_league = entry.get('requiredLeague')
508
509        assert bui.app.classic is not None
510        self.game = bui.app.classic.accounts.tournament_info[
511            self.tournament_id
512        ]['game']
513        assert isinstance(self.game, str)
514
515        campaignname, levelname = self.game.split(':')
516        campaign = bui.app.classic.getcampaign(campaignname)
517
518        self._update_lock_state()
519
520        bui.textwidget(
521            edit=self.prize_range_1_text,
522            text='-' if pr1 == '' else pr1,
523            position=(
524                self.button_x + 365 + x_offs2,
525                self.button_y + self.button_scale_y - 93 + prize_y_offs,
526            ),
527        )
528
529        bui.textwidget(
530            edit=self.prize_value_1_text,
531            text='-' if pv1 == '' else pv1,
532            position=(
533                self.button_x + 380 + x_offs2,
534                self.button_y + self.button_scale_y - 93 + prize_y_offs,
535            ),
536        )
537        bui.imagewidget(
538            edit=self.prize_chest_1_image,
539            position=(
540                self.button_x + 380 + x_offs2c,
541                self.button_y
542                + self.button_scale_y
543                - 93
544                + prize_y_offs
545                - 0.5 * self._chestsz,
546            ),
547        )
548        classic.set_tournament_prize_image(entry, 0, self.prize_chest_1_image)
549
550        bui.textwidget(
551            edit=self.prize_range_2_text,
552            text=pr2,
553            position=(
554                self.button_x + 365 + x_offs2,
555                self.button_y + self.button_scale_y - 93 - 45 + prize_y_offs,
556            ),
557        )
558        bui.textwidget(
559            edit=self.prize_value_2_text,
560            text=pv2,
561            position=(
562                self.button_x + 380 + x_offs2,
563                self.button_y + self.button_scale_y - 93 - 45 + prize_y_offs,
564            ),
565        )
566        bui.imagewidget(
567            edit=self.prize_chest_2_image,
568            position=(
569                self.button_x + 380 + x_offs2c,
570                self.button_y
571                + self.button_scale_y
572                - 93
573                - 45
574                + prize_y_offs
575                - 0.5 * self._chestsz,
576            ),
577        )
578        classic.set_tournament_prize_image(entry, 1, self.prize_chest_2_image)
579
580        bui.textwidget(
581            edit=self.prize_range_3_text,
582            text=pr3,
583            position=(
584                self.button_x + 365 + x_offs2,
585                self.button_y + self.button_scale_y - 93 - 90 + prize_y_offs,
586            ),
587        )
588        bui.textwidget(
589            edit=self.prize_value_3_text,
590            text=pv3,
591            position=(
592                self.button_x + 380 + x_offs2,
593                self.button_y + self.button_scale_y - 93 - 90 + prize_y_offs,
594            ),
595        )
596        bui.imagewidget(
597            edit=self.prize_chest_3_image,
598            position=(
599                self.button_x + 380 + x_offs2c,
600                self.button_y
601                + self.button_scale_y
602                - 93
603                - 90
604                + prize_y_offs
605                - 0.5 * self._chestsz,
606            ),
607        )
608        classic.set_tournament_prize_image(entry, 2, self.prize_chest_3_image)
609
610        leader_name = '-'
611        leader_score: str | bui.Lstr = '-'
612        if entry['scores']:
613            score = self.leader = copy.deepcopy(entry['scores'][0])
614            leader_name = score[1]
615            leader_score = (
616                bui.timestring((score[0] * 10) / 1000.0, centi=True)
617                if entry['scoreType'] == 'time'
618                else str(score[0])
619            )
620        else:
621            self.leader = None
622
623        bui.textwidget(
624            edit=self.current_leader_name_text, text=bui.Lstr(value=leader_name)
625        )
626        bui.textwidget(edit=self.current_leader_score_text, text=leader_score)
627        bui.buttonwidget(
628            edit=self.more_scores_button,
629            label=bui.Lstr(resource=f'{self._r}.seeMoreText'),
630        )
631        out_of_time_text: str | bui.Lstr = (
632            '-'
633            if 'totalTime' not in entry
634            else bui.Lstr(
635                resource=f'{self._r}.ofTotalTimeText',
636                subs=[
637                    (
638                        '${TOTAL}',
639                        bui.timestring(entry['totalTime'], centi=False),
640                    )
641                ],
642            )
643        )
644        bui.textwidget(
645            edit=self.time_remaining_out_of_text, text=out_of_time_text
646        )
647
648        # if self.game is None:
649        #     bui.textwidget(edit=self.button_text, text='-')
650        #     bui.imagewidget(
651        #         edit=self.image, texture=bui.gettexture('black'), opacity=0.2
652        #     )
653        # else:
654        max_players = bui.app.classic.accounts.tournament_info[
655            self.tournament_id
656        ]['maxPlayers']
657
658        txt = bui.Lstr(
659            value='${A} ${B}',
660            subs=[
661                ('${A}', campaign.getlevel(levelname).displayname),
662                (
663                    '${B}',
664                    bui.Lstr(
665                        resource='playerCountAbbreviatedText',
666                        subs=[('${COUNT}', str(max_players))],
667                    ),
668                ),
669            ],
670        )
671        bui.textwidget(edit=self.button_text, text=txt)
672
673        fee = entry['fee']
674        assert isinstance(fee, int | None)
675
676        if fee is None:
677            fee_var = None
678        elif fee == 4:
679            fee_var = 'price.tournament_entry_4'
680        elif fee == 3:
681            fee_var = 'price.tournament_entry_3'
682        elif fee == 2:
683            fee_var = 'price.tournament_entry_2'
684        elif fee == 1:
685            fee_var = 'price.tournament_entry_1'
686        elif fee == -1:
687            fee_var = None
688        else:
689            if fee != 0:
690                print('Unknown fee value:', fee)
691            fee_var = 'price.tournament_entry_0'
692
693        self.allow_ads = allow_ads = (
694            entry['allowAds'] if USE_ENTRY_FEES else False
695        )
696
697        final_fee = (
698            None
699            if fee_var is None
700            else plus.get_v1_account_misc_read_val(fee_var, '?')
701        )
702        assert isinstance(final_fee, int | None)
703
704        final_fee_str: str | bui.Lstr
705        if fee_var is None:
706            final_fee_str = ''
707        else:
708            if final_fee == 0:
709                final_fee_str = bui.Lstr(resource='getTicketsWindow.freeText')
710            else:
711                final_fee_str = bui.charstr(
712                    bui.SpecialChar.TICKET_BACKING
713                ) + str(final_fee)
714
715        assert bui.app.classic is not None
716        ad_tries_remaining = bui.app.classic.accounts.tournament_info[
717            self.tournament_id
718        ]['adTriesRemaining']
719        assert isinstance(ad_tries_remaining, int | None)
720        free_tries_remaining = bui.app.classic.accounts.tournament_info[
721            self.tournament_id
722        ]['freeTriesRemaining']
723        assert isinstance(free_tries_remaining, int | None)
724
725        # Now, if this fee allows ads and we support video ads, show the
726        # 'or ad' version.
727        if USE_ENTRY_FEES:
728            if allow_ads and plus.has_video_ads():
729                ads_enabled = plus.have_incentivized_ad()
730                bui.imagewidget(
731                    edit=self.entry_fee_ad_image,
732                    opacity=1.0 if ads_enabled else 0.25,
733                )
734                or_text = (
735                    bui.Lstr(
736                        resource='orText', subs=[('${A}', ''), ('${B}', '')]
737                    )
738                    .evaluate()
739                    .strip()
740                )
741                bui.textwidget(edit=self.entry_fee_text_or, text=or_text)
742                bui.textwidget(
743                    edit=self.entry_fee_text_top,
744                    position=(
745                        self.button_x + 360,
746                        self.button_y + self.button_scale_y - 60,
747                    ),
748                    scale=1.3,
749                    text=final_fee_str,
750                )
751
752                # Possibly show number of ad-plays remaining.
753                bui.textwidget(
754                    edit=self.entry_fee_text_remaining,
755                    position=(
756                        self.button_x + 360,
757                        self.button_y + self.button_scale_y - 146,
758                    ),
759                    text=(
760                        ''
761                        if ad_tries_remaining in [None, 0]
762                        else ('' + str(ad_tries_remaining))
763                    ),
764                    color=(0.6, 0.6, 0.6, 1 if ads_enabled else 0.2),
765                )
766            else:
767                bui.imagewidget(edit=self.entry_fee_ad_image, opacity=0.0)
768                bui.textwidget(edit=self.entry_fee_text_or, text='')
769                bui.textwidget(
770                    edit=self.entry_fee_text_top,
771                    position=(
772                        self.button_x + 360,
773                        self.button_y + self.button_scale_y - 80,
774                    ),
775                    scale=1.3,
776                    text=final_fee_str,
777                )
778
779                # Possibly show number of free-plays remaining.
780                bui.textwidget(
781                    edit=self.entry_fee_text_remaining,
782                    position=(
783                        self.button_x + 360,
784                        self.button_y + self.button_scale_y - 100,
785                    ),
786                    text=(
787                        ''
788                        if (free_tries_remaining in [None, 0] or final_fee != 0)
789                        else ('' + str(free_tries_remaining))
790                    ),
791                    color=(0.6, 0.6, 0.6, 1),
792                )
USE_ENTRY_FEES = False
class TournamentButton:
 22class TournamentButton:
 23    """Button showing a tournament in coop window."""
 24
 25    def __init__(
 26        self,
 27        parent: bui.Widget,
 28        x: float,
 29        y: float,
 30        select: bool,
 31        on_pressed: Callable[[TournamentButton], None],
 32    ) -> None:
 33        # pylint: disable=too-many-positional-arguments
 34        # pylint: disable=too-many-statements
 35        self._r = 'coopSelectWindow'
 36        sclx = 300
 37        scly = 195.0
 38        self.on_pressed = on_pressed
 39        self.lsbt = bui.getmesh('level_select_button_transparent')
 40        self.lsbo = bui.getmesh('level_select_button_opaque')
 41        self.allow_ads = False
 42        self.tournament_id: str | None = None
 43        self.game: str | None = None
 44        self.time_remaining: int = 0
 45        self.has_time_remaining: bool = False
 46        self.leader: Any = None
 47        self.required_league: str | None = None
 48        self._base_x_offs = 0 if USE_ENTRY_FEES else -45.0
 49        self.button = btn = bui.buttonwidget(
 50            parent=parent,
 51            position=(x + 23, y + 4),
 52            size=(sclx, scly),
 53            label='',
 54            button_type='square',
 55            autoselect=True,
 56            on_activate_call=bui.WeakCall(self._pressed),
 57        )
 58        bui.widget(
 59            edit=btn,
 60            show_buffer_bottom=50,
 61            show_buffer_top=50,
 62            show_buffer_left=400,
 63            show_buffer_right=200,
 64        )
 65        if select:
 66            bui.containerwidget(
 67                edit=parent, selected_child=btn, visible_child=btn
 68            )
 69        image_width = sclx * 0.85 * 0.75
 70
 71        self.image = bui.imagewidget(
 72            parent=parent,
 73            draw_controller=btn,
 74            position=(x + 21 + sclx * 0.5 - image_width * 0.5, y + scly - 150),
 75            size=(image_width, image_width * 0.5),
 76            mesh_transparent=self.lsbt,
 77            mesh_opaque=self.lsbo,
 78            texture=bui.gettexture('black'),
 79            opacity=0.2,
 80            mask_texture=bui.gettexture('mapPreviewMask'),
 81        )
 82
 83        self.lock_image = bui.imagewidget(
 84            parent=parent,
 85            draw_controller=btn,
 86            position=(x + 21 + sclx * 0.5 - image_width * 0.15, y + scly - 130),
 87            size=(image_width * 0.3, image_width * 0.3),
 88            texture=bui.gettexture('lock'),
 89            opacity=0.0,
 90        )
 91
 92        self.button_text = bui.textwidget(
 93            parent=parent,
 94            draw_controller=btn,
 95            position=(x + 20 + sclx * 0.5, y + scly - 35),
 96            size=(0, 0),
 97            h_align='center',
 98            text='-',
 99            v_align='center',
100            maxwidth=sclx * 0.76,
101            scale=0.85,
102            color=(0.8, 1.0, 0.8, 1.0),
103        )
104
105        header_color = (0.43, 0.4, 0.5, 1)
106        value_color = (0.6, 0.6, 0.6, 1)
107
108        x_offs = self._base_x_offs
109
110        # No longer using entry fees.
111        if USE_ENTRY_FEES:
112            bui.textwidget(
113                parent=parent,
114                draw_controller=btn,
115                position=(x + 360, y + scly - 20),
116                size=(0, 0),
117                h_align='center',
118                text=bui.Lstr(resource=f'{self._r}.entryFeeText'),
119                v_align='center',
120                maxwidth=100,
121                scale=0.9,
122                color=header_color,
123                flatness=1.0,
124            )
125
126            self.entry_fee_text_top = bui.textwidget(
127                parent=parent,
128                draw_controller=btn,
129                position=(x + 360, y + scly - 60),
130                size=(0, 0),
131                h_align='center',
132                text='-',
133                v_align='center',
134                maxwidth=60,
135                scale=1.3,
136                color=value_color,
137                flatness=1.0,
138            )
139            self.entry_fee_text_or = bui.textwidget(
140                parent=parent,
141                draw_controller=btn,
142                position=(x + 360, y + scly - 90),
143                size=(0, 0),
144                h_align='center',
145                text='',
146                v_align='center',
147                maxwidth=60,
148                scale=0.5,
149                color=value_color,
150                flatness=1.0,
151            )
152            self.entry_fee_text_remaining = bui.textwidget(
153                parent=parent,
154                draw_controller=btn,
155                position=(x + 360, y + scly - 90),
156                size=(0, 0),
157                h_align='center',
158                text='',
159                v_align='center',
160                maxwidth=60,
161                scale=0.5,
162                color=value_color,
163                flatness=1.0,
164            )
165
166            self.entry_fee_ad_image = bui.imagewidget(
167                parent=parent,
168                size=(40, 40),
169                draw_controller=btn,
170                position=(x + 360 - 20, y + scly - 140),
171                opacity=0.0,
172                texture=bui.gettexture('tv'),
173            )
174
175        x_offs += 50
176
177        bui.textwidget(
178            parent=parent,
179            draw_controller=btn,
180            position=(x + 447 + x_offs, y + scly - 20),
181            size=(0, 0),
182            h_align='center',
183            text=bui.Lstr(resource=f'{self._r}.prizesText'),
184            v_align='center',
185            maxwidth=130,
186            scale=0.9,
187            color=header_color,
188            flatness=1.0,
189        )
190
191        self.button_x = x
192        self.button_y = y
193        self.button_scale_y = scly
194
195        # Offset for prize range/values.
196        xo2 = 0.0
197
198        self.prize_range_1_text = bui.textwidget(
199            parent=parent,
200            draw_controller=btn,
201            position=(x + 355 + xo2 + x_offs, y + scly - 93),
202            size=(0, 0),
203            h_align='right',
204            v_align='center',
205            maxwidth=50,
206            text='',
207            scale=0.8,
208            color=header_color,
209            flatness=1.0,
210        )
211        self.prize_value_1_text = bui.textwidget(
212            parent=parent,
213            draw_controller=btn,
214            position=(x + 380 + xo2 + x_offs, y + scly - 93),
215            size=(0, 0),
216            h_align='left',
217            text='',
218            v_align='center',
219            maxwidth=100,
220            color=value_color,
221            flatness=1.0,
222        )
223        self._chestsz = 50
224        self.prize_chest_1_image = bui.imagewidget(
225            parent=parent,
226            draw_controller=btn,
227            texture=bui.gettexture('white'),
228            position=(x + 380 + xo2 + x_offs, y + scly - 93),
229            size=(self._chestsz, self._chestsz),
230            opacity=0.0,
231        )
232
233        self.prize_range_2_text = bui.textwidget(
234            parent=parent,
235            draw_controller=btn,
236            position=(x + 355 + xo2 + x_offs, y + scly - 93),
237            size=(0, 0),
238            h_align='right',
239            text='',
240            v_align='center',
241            maxwidth=50,
242            scale=0.8,
243            color=header_color,
244            flatness=1.0,
245        )
246        self.prize_value_2_text = bui.textwidget(
247            parent=parent,
248            draw_controller=btn,
249            position=(x + 380 + xo2 + x_offs, y + scly - 93),
250            size=(0, 0),
251            h_align='left',
252            text='',
253            v_align='center',
254            maxwidth=100,
255            color=value_color,
256            flatness=1.0,
257        )
258        self.prize_chest_2_image = bui.imagewidget(
259            parent=parent,
260            draw_controller=btn,
261            texture=bui.gettexture('white'),
262            position=(x + 380 + xo2 + x_offs, y + scly - 93),
263            size=(self._chestsz, self._chestsz),
264            opacity=0.0,
265        )
266
267        self.prize_range_3_text = bui.textwidget(
268            parent=parent,
269            draw_controller=btn,
270            position=(x + 355 + xo2 + x_offs, y + scly - 93),
271            size=(0, 0),
272            h_align='right',
273            text='',
274            v_align='center',
275            maxwidth=50,
276            scale=0.8,
277            color=header_color,
278            flatness=1.0,
279        )
280        self.prize_value_3_text = bui.textwidget(
281            parent=parent,
282            draw_controller=btn,
283            position=(x + 380 + xo2 + x_offs, y + scly - 93),
284            size=(0, 0),
285            h_align='left',
286            text='',
287            v_align='center',
288            maxwidth=100,
289            color=value_color,
290            flatness=1.0,
291        )
292        self.prize_chest_3_image = bui.imagewidget(
293            parent=parent,
294            draw_controller=btn,
295            texture=bui.gettexture('white'),
296            position=(x + 380 + xo2 + x_offs, y + scly - 93),
297            size=(self._chestsz, self._chestsz),
298            opacity=0.0,
299        )
300
301        bui.textwidget(
302            parent=parent,
303            draw_controller=btn,
304            position=(x + 625 + x_offs, y + scly - 20),
305            size=(0, 0),
306            h_align='center',
307            text=bui.Lstr(resource=f'{self._r}.currentBestText'),
308            v_align='center',
309            maxwidth=180,
310            scale=0.9,
311            color=header_color,
312            flatness=1.0,
313        )
314        self.current_leader_name_text = bui.textwidget(
315            parent=parent,
316            draw_controller=btn,
317            position=(
318                x + 625 + x_offs - (170 / 1.4) * 0.5,
319                y + scly - 60 - 40 * 0.5,
320            ),
321            selectable=True,
322            click_activate=True,
323            autoselect=True,
324            on_activate_call=bui.WeakCall(self._show_leader),
325            size=(170 / 1.4, 40),
326            h_align='center',
327            text='-',
328            v_align='center',
329            maxwidth=170,
330            glow_type='uniform',
331            scale=1.4,
332            color=value_color,
333            flatness=1.0,
334        )
335        self.current_leader_score_text = bui.textwidget(
336            parent=parent,
337            draw_controller=btn,
338            position=(x + 625 + x_offs, y + scly - 113 + 10),
339            size=(0, 0),
340            h_align='center',
341            text='-',
342            v_align='center',
343            maxwidth=170,
344            scale=1.8,
345            color=value_color,
346            flatness=1.0,
347        )
348
349        self.more_scores_button = bui.buttonwidget(
350            parent=parent,
351            position=(x + 625 + x_offs - 60, y + scly - 50 - 125),
352            color=(0.5, 0.5, 0.6),
353            textcolor=(0.7, 0.7, 0.8),
354            label='-',
355            size=(120, 40),
356            autoselect=True,
357            up_widget=self.current_leader_name_text,
358            text_scale=0.6,
359            on_activate_call=bui.WeakCall(self._show_scores),
360        )
361        bui.widget(
362            edit=self.current_leader_name_text,
363            down_widget=self.more_scores_button,
364        )
365
366        bui.textwidget(
367            parent=parent,
368            draw_controller=btn,
369            position=(x + 840 + x_offs, y + scly - 20),
370            size=(0, 0),
371            h_align='center',
372            text=bui.Lstr(resource=f'{self._r}.timeRemainingText'),
373            v_align='center',
374            maxwidth=180,
375            scale=0.9,
376            color=header_color,
377            flatness=1.0,
378        )
379        self.time_remaining_value_text = bui.textwidget(
380            parent=parent,
381            draw_controller=btn,
382            position=(x + 840 + x_offs, y + scly - 68),
383            size=(0, 0),
384            h_align='center',
385            text='-',
386            v_align='center',
387            maxwidth=180,
388            scale=2.0,
389            color=value_color,
390            flatness=1.0,
391        )
392        self.time_remaining_out_of_text = bui.textwidget(
393            parent=parent,
394            draw_controller=btn,
395            position=(x + 840 + x_offs, y + scly - 110),
396            size=(0, 0),
397            h_align='center',
398            text='-',
399            v_align='center',
400            maxwidth=120,
401            scale=0.72,
402            color=(0.4, 0.4, 0.5),
403            flatness=1.0,
404        )
405        self._lock_update_timer = bui.AppTimer(
406            1.03, bui.WeakCall(self._update_lock_state), repeat=True
407        )
408
409    def _pressed(self) -> None:
410        self.on_pressed(self)
411
412    def _show_leader(self) -> None:
413        # pylint: disable=cyclic-import
414        from bauiv1lib.account.viewer import AccountViewerWindow
415
416        tournament_id = self.tournament_id
417
418        # FIXME: This assumes a single player entry in leader; should expand
419        #  this to work with multiple.
420        if (
421            tournament_id is None
422            or self.leader is None
423            or len(self.leader[2]) != 1
424        ):
425            bui.getsound('error').play()
426            return
427        bui.getsound('swish').play()
428        AccountViewerWindow(
429            account_id=self.leader[2][0].get('a', None),
430            profile_id=self.leader[2][0].get('p', None),
431            position=self.current_leader_name_text.get_screen_space_center(),
432        )
433
434    def _show_scores(self) -> None:
435        # pylint: disable=cyclic-import
436        from bauiv1lib.tournamentscores import TournamentScoresWindow
437
438        tournament_id = self.tournament_id
439        if tournament_id is None:
440            bui.getsound('error').play()
441            return
442
443        TournamentScoresWindow(
444            tournament_id=tournament_id,
445            position=self.more_scores_button.get_screen_space_center(),
446        )
447
448    def _update_lock_state(self) -> None:
449
450        if self.game is None:
451            return
452
453        assert bui.app.classic is not None
454
455        campaignname, levelname = self.game.split(':')
456        campaign = bui.app.classic.getcampaign(campaignname)
457
458        enabled = (
459            self.required_league is None
460            and bui.app.classic.is_game_unlocked(self.game)
461        )
462        bui.buttonwidget(
463            edit=self.button,
464            color=(0.5, 0.7, 0.2) if enabled else (0.5, 0.5, 0.5),
465        )
466        bui.imagewidget(edit=self.lock_image, opacity=0.0 if enabled else 1.0)
467        bui.imagewidget(
468            edit=self.image,
469            texture=bui.gettexture(
470                campaign.getlevel(levelname).preview_texture_name
471            ),
472            opacity=1.0 if enabled else 0.5,
473        )
474
475    def update_for_data(self, entry: dict[str, Any]) -> None:
476        """Update for new incoming data."""
477        # pylint: disable=too-many-statements
478        # pylint: disable=too-many-locals
479        # pylint: disable=too-many-branches
480
481        plus = bui.app.plus
482        assert plus is not None
483
484        classic = bui.app.classic
485        assert classic is not None
486
487        prize_y_offs = (
488            34
489            if 'prizeRange3' in entry
490            else 20 if 'prizeRange2' in entry else 12
491        )
492        x_offs = self._base_x_offs + 90
493
494        # Special offset for prize ranges/vals.
495        x_offs2 = x_offs - 20.0
496
497        # Special offset for prize chests.
498        x_offs2c = x_offs2 + 50
499
500        # Fetch prize range and trophy strings.
501        (pr1, pv1, pr2, pv2, pr3, pv3) = classic.get_tournament_prize_strings(
502            entry, include_tickets=False
503        )
504
505        self.time_remaining = entry['timeRemaining']
506        self.has_time_remaining = entry is not None
507        self.tournament_id = entry['tournamentID']
508        self.required_league = entry.get('requiredLeague')
509
510        assert bui.app.classic is not None
511        self.game = bui.app.classic.accounts.tournament_info[
512            self.tournament_id
513        ]['game']
514        assert isinstance(self.game, str)
515
516        campaignname, levelname = self.game.split(':')
517        campaign = bui.app.classic.getcampaign(campaignname)
518
519        self._update_lock_state()
520
521        bui.textwidget(
522            edit=self.prize_range_1_text,
523            text='-' if pr1 == '' else pr1,
524            position=(
525                self.button_x + 365 + x_offs2,
526                self.button_y + self.button_scale_y - 93 + prize_y_offs,
527            ),
528        )
529
530        bui.textwidget(
531            edit=self.prize_value_1_text,
532            text='-' if pv1 == '' else pv1,
533            position=(
534                self.button_x + 380 + x_offs2,
535                self.button_y + self.button_scale_y - 93 + prize_y_offs,
536            ),
537        )
538        bui.imagewidget(
539            edit=self.prize_chest_1_image,
540            position=(
541                self.button_x + 380 + x_offs2c,
542                self.button_y
543                + self.button_scale_y
544                - 93
545                + prize_y_offs
546                - 0.5 * self._chestsz,
547            ),
548        )
549        classic.set_tournament_prize_image(entry, 0, self.prize_chest_1_image)
550
551        bui.textwidget(
552            edit=self.prize_range_2_text,
553            text=pr2,
554            position=(
555                self.button_x + 365 + x_offs2,
556                self.button_y + self.button_scale_y - 93 - 45 + prize_y_offs,
557            ),
558        )
559        bui.textwidget(
560            edit=self.prize_value_2_text,
561            text=pv2,
562            position=(
563                self.button_x + 380 + x_offs2,
564                self.button_y + self.button_scale_y - 93 - 45 + prize_y_offs,
565            ),
566        )
567        bui.imagewidget(
568            edit=self.prize_chest_2_image,
569            position=(
570                self.button_x + 380 + x_offs2c,
571                self.button_y
572                + self.button_scale_y
573                - 93
574                - 45
575                + prize_y_offs
576                - 0.5 * self._chestsz,
577            ),
578        )
579        classic.set_tournament_prize_image(entry, 1, self.prize_chest_2_image)
580
581        bui.textwidget(
582            edit=self.prize_range_3_text,
583            text=pr3,
584            position=(
585                self.button_x + 365 + x_offs2,
586                self.button_y + self.button_scale_y - 93 - 90 + prize_y_offs,
587            ),
588        )
589        bui.textwidget(
590            edit=self.prize_value_3_text,
591            text=pv3,
592            position=(
593                self.button_x + 380 + x_offs2,
594                self.button_y + self.button_scale_y - 93 - 90 + prize_y_offs,
595            ),
596        )
597        bui.imagewidget(
598            edit=self.prize_chest_3_image,
599            position=(
600                self.button_x + 380 + x_offs2c,
601                self.button_y
602                + self.button_scale_y
603                - 93
604                - 90
605                + prize_y_offs
606                - 0.5 * self._chestsz,
607            ),
608        )
609        classic.set_tournament_prize_image(entry, 2, self.prize_chest_3_image)
610
611        leader_name = '-'
612        leader_score: str | bui.Lstr = '-'
613        if entry['scores']:
614            score = self.leader = copy.deepcopy(entry['scores'][0])
615            leader_name = score[1]
616            leader_score = (
617                bui.timestring((score[0] * 10) / 1000.0, centi=True)
618                if entry['scoreType'] == 'time'
619                else str(score[0])
620            )
621        else:
622            self.leader = None
623
624        bui.textwidget(
625            edit=self.current_leader_name_text, text=bui.Lstr(value=leader_name)
626        )
627        bui.textwidget(edit=self.current_leader_score_text, text=leader_score)
628        bui.buttonwidget(
629            edit=self.more_scores_button,
630            label=bui.Lstr(resource=f'{self._r}.seeMoreText'),
631        )
632        out_of_time_text: str | bui.Lstr = (
633            '-'
634            if 'totalTime' not in entry
635            else bui.Lstr(
636                resource=f'{self._r}.ofTotalTimeText',
637                subs=[
638                    (
639                        '${TOTAL}',
640                        bui.timestring(entry['totalTime'], centi=False),
641                    )
642                ],
643            )
644        )
645        bui.textwidget(
646            edit=self.time_remaining_out_of_text, text=out_of_time_text
647        )
648
649        # if self.game is None:
650        #     bui.textwidget(edit=self.button_text, text='-')
651        #     bui.imagewidget(
652        #         edit=self.image, texture=bui.gettexture('black'), opacity=0.2
653        #     )
654        # else:
655        max_players = bui.app.classic.accounts.tournament_info[
656            self.tournament_id
657        ]['maxPlayers']
658
659        txt = bui.Lstr(
660            value='${A} ${B}',
661            subs=[
662                ('${A}', campaign.getlevel(levelname).displayname),
663                (
664                    '${B}',
665                    bui.Lstr(
666                        resource='playerCountAbbreviatedText',
667                        subs=[('${COUNT}', str(max_players))],
668                    ),
669                ),
670            ],
671        )
672        bui.textwidget(edit=self.button_text, text=txt)
673
674        fee = entry['fee']
675        assert isinstance(fee, int | None)
676
677        if fee is None:
678            fee_var = None
679        elif fee == 4:
680            fee_var = 'price.tournament_entry_4'
681        elif fee == 3:
682            fee_var = 'price.tournament_entry_3'
683        elif fee == 2:
684            fee_var = 'price.tournament_entry_2'
685        elif fee == 1:
686            fee_var = 'price.tournament_entry_1'
687        elif fee == -1:
688            fee_var = None
689        else:
690            if fee != 0:
691                print('Unknown fee value:', fee)
692            fee_var = 'price.tournament_entry_0'
693
694        self.allow_ads = allow_ads = (
695            entry['allowAds'] if USE_ENTRY_FEES else False
696        )
697
698        final_fee = (
699            None
700            if fee_var is None
701            else plus.get_v1_account_misc_read_val(fee_var, '?')
702        )
703        assert isinstance(final_fee, int | None)
704
705        final_fee_str: str | bui.Lstr
706        if fee_var is None:
707            final_fee_str = ''
708        else:
709            if final_fee == 0:
710                final_fee_str = bui.Lstr(resource='getTicketsWindow.freeText')
711            else:
712                final_fee_str = bui.charstr(
713                    bui.SpecialChar.TICKET_BACKING
714                ) + str(final_fee)
715
716        assert bui.app.classic is not None
717        ad_tries_remaining = bui.app.classic.accounts.tournament_info[
718            self.tournament_id
719        ]['adTriesRemaining']
720        assert isinstance(ad_tries_remaining, int | None)
721        free_tries_remaining = bui.app.classic.accounts.tournament_info[
722            self.tournament_id
723        ]['freeTriesRemaining']
724        assert isinstance(free_tries_remaining, int | None)
725
726        # Now, if this fee allows ads and we support video ads, show the
727        # 'or ad' version.
728        if USE_ENTRY_FEES:
729            if allow_ads and plus.has_video_ads():
730                ads_enabled = plus.have_incentivized_ad()
731                bui.imagewidget(
732                    edit=self.entry_fee_ad_image,
733                    opacity=1.0 if ads_enabled else 0.25,
734                )
735                or_text = (
736                    bui.Lstr(
737                        resource='orText', subs=[('${A}', ''), ('${B}', '')]
738                    )
739                    .evaluate()
740                    .strip()
741                )
742                bui.textwidget(edit=self.entry_fee_text_or, text=or_text)
743                bui.textwidget(
744                    edit=self.entry_fee_text_top,
745                    position=(
746                        self.button_x + 360,
747                        self.button_y + self.button_scale_y - 60,
748                    ),
749                    scale=1.3,
750                    text=final_fee_str,
751                )
752
753                # Possibly show number of ad-plays remaining.
754                bui.textwidget(
755                    edit=self.entry_fee_text_remaining,
756                    position=(
757                        self.button_x + 360,
758                        self.button_y + self.button_scale_y - 146,
759                    ),
760                    text=(
761                        ''
762                        if ad_tries_remaining in [None, 0]
763                        else ('' + str(ad_tries_remaining))
764                    ),
765                    color=(0.6, 0.6, 0.6, 1 if ads_enabled else 0.2),
766                )
767            else:
768                bui.imagewidget(edit=self.entry_fee_ad_image, opacity=0.0)
769                bui.textwidget(edit=self.entry_fee_text_or, text='')
770                bui.textwidget(
771                    edit=self.entry_fee_text_top,
772                    position=(
773                        self.button_x + 360,
774                        self.button_y + self.button_scale_y - 80,
775                    ),
776                    scale=1.3,
777                    text=final_fee_str,
778                )
779
780                # Possibly show number of free-plays remaining.
781                bui.textwidget(
782                    edit=self.entry_fee_text_remaining,
783                    position=(
784                        self.button_x + 360,
785                        self.button_y + self.button_scale_y - 100,
786                    ),
787                    text=(
788                        ''
789                        if (free_tries_remaining in [None, 0] or final_fee != 0)
790                        else ('' + str(free_tries_remaining))
791                    ),
792                    color=(0.6, 0.6, 0.6, 1),
793                )

Button showing a tournament in coop window.

TournamentButton( parent: _bauiv1.Widget, x: float, y: float, select: bool, on_pressed: Callable[[TournamentButton], NoneType])
 25    def __init__(
 26        self,
 27        parent: bui.Widget,
 28        x: float,
 29        y: float,
 30        select: bool,
 31        on_pressed: Callable[[TournamentButton], None],
 32    ) -> None:
 33        # pylint: disable=too-many-positional-arguments
 34        # pylint: disable=too-many-statements
 35        self._r = 'coopSelectWindow'
 36        sclx = 300
 37        scly = 195.0
 38        self.on_pressed = on_pressed
 39        self.lsbt = bui.getmesh('level_select_button_transparent')
 40        self.lsbo = bui.getmesh('level_select_button_opaque')
 41        self.allow_ads = False
 42        self.tournament_id: str | None = None
 43        self.game: str | None = None
 44        self.time_remaining: int = 0
 45        self.has_time_remaining: bool = False
 46        self.leader: Any = None
 47        self.required_league: str | None = None
 48        self._base_x_offs = 0 if USE_ENTRY_FEES else -45.0
 49        self.button = btn = bui.buttonwidget(
 50            parent=parent,
 51            position=(x + 23, y + 4),
 52            size=(sclx, scly),
 53            label='',
 54            button_type='square',
 55            autoselect=True,
 56            on_activate_call=bui.WeakCall(self._pressed),
 57        )
 58        bui.widget(
 59            edit=btn,
 60            show_buffer_bottom=50,
 61            show_buffer_top=50,
 62            show_buffer_left=400,
 63            show_buffer_right=200,
 64        )
 65        if select:
 66            bui.containerwidget(
 67                edit=parent, selected_child=btn, visible_child=btn
 68            )
 69        image_width = sclx * 0.85 * 0.75
 70
 71        self.image = bui.imagewidget(
 72            parent=parent,
 73            draw_controller=btn,
 74            position=(x + 21 + sclx * 0.5 - image_width * 0.5, y + scly - 150),
 75            size=(image_width, image_width * 0.5),
 76            mesh_transparent=self.lsbt,
 77            mesh_opaque=self.lsbo,
 78            texture=bui.gettexture('black'),
 79            opacity=0.2,
 80            mask_texture=bui.gettexture('mapPreviewMask'),
 81        )
 82
 83        self.lock_image = bui.imagewidget(
 84            parent=parent,
 85            draw_controller=btn,
 86            position=(x + 21 + sclx * 0.5 - image_width * 0.15, y + scly - 130),
 87            size=(image_width * 0.3, image_width * 0.3),
 88            texture=bui.gettexture('lock'),
 89            opacity=0.0,
 90        )
 91
 92        self.button_text = bui.textwidget(
 93            parent=parent,
 94            draw_controller=btn,
 95            position=(x + 20 + sclx * 0.5, y + scly - 35),
 96            size=(0, 0),
 97            h_align='center',
 98            text='-',
 99            v_align='center',
100            maxwidth=sclx * 0.76,
101            scale=0.85,
102            color=(0.8, 1.0, 0.8, 1.0),
103        )
104
105        header_color = (0.43, 0.4, 0.5, 1)
106        value_color = (0.6, 0.6, 0.6, 1)
107
108        x_offs = self._base_x_offs
109
110        # No longer using entry fees.
111        if USE_ENTRY_FEES:
112            bui.textwidget(
113                parent=parent,
114                draw_controller=btn,
115                position=(x + 360, y + scly - 20),
116                size=(0, 0),
117                h_align='center',
118                text=bui.Lstr(resource=f'{self._r}.entryFeeText'),
119                v_align='center',
120                maxwidth=100,
121                scale=0.9,
122                color=header_color,
123                flatness=1.0,
124            )
125
126            self.entry_fee_text_top = bui.textwidget(
127                parent=parent,
128                draw_controller=btn,
129                position=(x + 360, y + scly - 60),
130                size=(0, 0),
131                h_align='center',
132                text='-',
133                v_align='center',
134                maxwidth=60,
135                scale=1.3,
136                color=value_color,
137                flatness=1.0,
138            )
139            self.entry_fee_text_or = bui.textwidget(
140                parent=parent,
141                draw_controller=btn,
142                position=(x + 360, y + scly - 90),
143                size=(0, 0),
144                h_align='center',
145                text='',
146                v_align='center',
147                maxwidth=60,
148                scale=0.5,
149                color=value_color,
150                flatness=1.0,
151            )
152            self.entry_fee_text_remaining = bui.textwidget(
153                parent=parent,
154                draw_controller=btn,
155                position=(x + 360, y + scly - 90),
156                size=(0, 0),
157                h_align='center',
158                text='',
159                v_align='center',
160                maxwidth=60,
161                scale=0.5,
162                color=value_color,
163                flatness=1.0,
164            )
165
166            self.entry_fee_ad_image = bui.imagewidget(
167                parent=parent,
168                size=(40, 40),
169                draw_controller=btn,
170                position=(x + 360 - 20, y + scly - 140),
171                opacity=0.0,
172                texture=bui.gettexture('tv'),
173            )
174
175        x_offs += 50
176
177        bui.textwidget(
178            parent=parent,
179            draw_controller=btn,
180            position=(x + 447 + x_offs, y + scly - 20),
181            size=(0, 0),
182            h_align='center',
183            text=bui.Lstr(resource=f'{self._r}.prizesText'),
184            v_align='center',
185            maxwidth=130,
186            scale=0.9,
187            color=header_color,
188            flatness=1.0,
189        )
190
191        self.button_x = x
192        self.button_y = y
193        self.button_scale_y = scly
194
195        # Offset for prize range/values.
196        xo2 = 0.0
197
198        self.prize_range_1_text = bui.textwidget(
199            parent=parent,
200            draw_controller=btn,
201            position=(x + 355 + xo2 + x_offs, y + scly - 93),
202            size=(0, 0),
203            h_align='right',
204            v_align='center',
205            maxwidth=50,
206            text='',
207            scale=0.8,
208            color=header_color,
209            flatness=1.0,
210        )
211        self.prize_value_1_text = bui.textwidget(
212            parent=parent,
213            draw_controller=btn,
214            position=(x + 380 + xo2 + x_offs, y + scly - 93),
215            size=(0, 0),
216            h_align='left',
217            text='',
218            v_align='center',
219            maxwidth=100,
220            color=value_color,
221            flatness=1.0,
222        )
223        self._chestsz = 50
224        self.prize_chest_1_image = bui.imagewidget(
225            parent=parent,
226            draw_controller=btn,
227            texture=bui.gettexture('white'),
228            position=(x + 380 + xo2 + x_offs, y + scly - 93),
229            size=(self._chestsz, self._chestsz),
230            opacity=0.0,
231        )
232
233        self.prize_range_2_text = bui.textwidget(
234            parent=parent,
235            draw_controller=btn,
236            position=(x + 355 + xo2 + x_offs, y + scly - 93),
237            size=(0, 0),
238            h_align='right',
239            text='',
240            v_align='center',
241            maxwidth=50,
242            scale=0.8,
243            color=header_color,
244            flatness=1.0,
245        )
246        self.prize_value_2_text = bui.textwidget(
247            parent=parent,
248            draw_controller=btn,
249            position=(x + 380 + xo2 + x_offs, y + scly - 93),
250            size=(0, 0),
251            h_align='left',
252            text='',
253            v_align='center',
254            maxwidth=100,
255            color=value_color,
256            flatness=1.0,
257        )
258        self.prize_chest_2_image = bui.imagewidget(
259            parent=parent,
260            draw_controller=btn,
261            texture=bui.gettexture('white'),
262            position=(x + 380 + xo2 + x_offs, y + scly - 93),
263            size=(self._chestsz, self._chestsz),
264            opacity=0.0,
265        )
266
267        self.prize_range_3_text = bui.textwidget(
268            parent=parent,
269            draw_controller=btn,
270            position=(x + 355 + xo2 + x_offs, y + scly - 93),
271            size=(0, 0),
272            h_align='right',
273            text='',
274            v_align='center',
275            maxwidth=50,
276            scale=0.8,
277            color=header_color,
278            flatness=1.0,
279        )
280        self.prize_value_3_text = bui.textwidget(
281            parent=parent,
282            draw_controller=btn,
283            position=(x + 380 + xo2 + x_offs, y + scly - 93),
284            size=(0, 0),
285            h_align='left',
286            text='',
287            v_align='center',
288            maxwidth=100,
289            color=value_color,
290            flatness=1.0,
291        )
292        self.prize_chest_3_image = bui.imagewidget(
293            parent=parent,
294            draw_controller=btn,
295            texture=bui.gettexture('white'),
296            position=(x + 380 + xo2 + x_offs, y + scly - 93),
297            size=(self._chestsz, self._chestsz),
298            opacity=0.0,
299        )
300
301        bui.textwidget(
302            parent=parent,
303            draw_controller=btn,
304            position=(x + 625 + x_offs, y + scly - 20),
305            size=(0, 0),
306            h_align='center',
307            text=bui.Lstr(resource=f'{self._r}.currentBestText'),
308            v_align='center',
309            maxwidth=180,
310            scale=0.9,
311            color=header_color,
312            flatness=1.0,
313        )
314        self.current_leader_name_text = bui.textwidget(
315            parent=parent,
316            draw_controller=btn,
317            position=(
318                x + 625 + x_offs - (170 / 1.4) * 0.5,
319                y + scly - 60 - 40 * 0.5,
320            ),
321            selectable=True,
322            click_activate=True,
323            autoselect=True,
324            on_activate_call=bui.WeakCall(self._show_leader),
325            size=(170 / 1.4, 40),
326            h_align='center',
327            text='-',
328            v_align='center',
329            maxwidth=170,
330            glow_type='uniform',
331            scale=1.4,
332            color=value_color,
333            flatness=1.0,
334        )
335        self.current_leader_score_text = bui.textwidget(
336            parent=parent,
337            draw_controller=btn,
338            position=(x + 625 + x_offs, y + scly - 113 + 10),
339            size=(0, 0),
340            h_align='center',
341            text='-',
342            v_align='center',
343            maxwidth=170,
344            scale=1.8,
345            color=value_color,
346            flatness=1.0,
347        )
348
349        self.more_scores_button = bui.buttonwidget(
350            parent=parent,
351            position=(x + 625 + x_offs - 60, y + scly - 50 - 125),
352            color=(0.5, 0.5, 0.6),
353            textcolor=(0.7, 0.7, 0.8),
354            label='-',
355            size=(120, 40),
356            autoselect=True,
357            up_widget=self.current_leader_name_text,
358            text_scale=0.6,
359            on_activate_call=bui.WeakCall(self._show_scores),
360        )
361        bui.widget(
362            edit=self.current_leader_name_text,
363            down_widget=self.more_scores_button,
364        )
365
366        bui.textwidget(
367            parent=parent,
368            draw_controller=btn,
369            position=(x + 840 + x_offs, y + scly - 20),
370            size=(0, 0),
371            h_align='center',
372            text=bui.Lstr(resource=f'{self._r}.timeRemainingText'),
373            v_align='center',
374            maxwidth=180,
375            scale=0.9,
376            color=header_color,
377            flatness=1.0,
378        )
379        self.time_remaining_value_text = bui.textwidget(
380            parent=parent,
381            draw_controller=btn,
382            position=(x + 840 + x_offs, y + scly - 68),
383            size=(0, 0),
384            h_align='center',
385            text='-',
386            v_align='center',
387            maxwidth=180,
388            scale=2.0,
389            color=value_color,
390            flatness=1.0,
391        )
392        self.time_remaining_out_of_text = bui.textwidget(
393            parent=parent,
394            draw_controller=btn,
395            position=(x + 840 + x_offs, y + scly - 110),
396            size=(0, 0),
397            h_align='center',
398            text='-',
399            v_align='center',
400            maxwidth=120,
401            scale=0.72,
402            color=(0.4, 0.4, 0.5),
403            flatness=1.0,
404        )
405        self._lock_update_timer = bui.AppTimer(
406            1.03, bui.WeakCall(self._update_lock_state), repeat=True
407        )
on_pressed
lsbt
lsbo
allow_ads
tournament_id: str | None
game: str | None
time_remaining: int
has_time_remaining: bool
leader: Any
required_league: str | None
image
lock_image
button_text
button_x
button_y
button_scale_y
prize_range_1_text
prize_value_1_text
prize_chest_1_image
prize_range_2_text
prize_value_2_text
prize_chest_2_image
prize_range_3_text
prize_value_3_text
prize_chest_3_image
current_leader_name_text
current_leader_score_text
more_scores_button
time_remaining_value_text
time_remaining_out_of_text
def update_for_data(self, entry: dict[str, typing.Any]) -> None:
475    def update_for_data(self, entry: dict[str, Any]) -> None:
476        """Update for new incoming data."""
477        # pylint: disable=too-many-statements
478        # pylint: disable=too-many-locals
479        # pylint: disable=too-many-branches
480
481        plus = bui.app.plus
482        assert plus is not None
483
484        classic = bui.app.classic
485        assert classic is not None
486
487        prize_y_offs = (
488            34
489            if 'prizeRange3' in entry
490            else 20 if 'prizeRange2' in entry else 12
491        )
492        x_offs = self._base_x_offs + 90
493
494        # Special offset for prize ranges/vals.
495        x_offs2 = x_offs - 20.0
496
497        # Special offset for prize chests.
498        x_offs2c = x_offs2 + 50
499
500        # Fetch prize range and trophy strings.
501        (pr1, pv1, pr2, pv2, pr3, pv3) = classic.get_tournament_prize_strings(
502            entry, include_tickets=False
503        )
504
505        self.time_remaining = entry['timeRemaining']
506        self.has_time_remaining = entry is not None
507        self.tournament_id = entry['tournamentID']
508        self.required_league = entry.get('requiredLeague')
509
510        assert bui.app.classic is not None
511        self.game = bui.app.classic.accounts.tournament_info[
512            self.tournament_id
513        ]['game']
514        assert isinstance(self.game, str)
515
516        campaignname, levelname = self.game.split(':')
517        campaign = bui.app.classic.getcampaign(campaignname)
518
519        self._update_lock_state()
520
521        bui.textwidget(
522            edit=self.prize_range_1_text,
523            text='-' if pr1 == '' else pr1,
524            position=(
525                self.button_x + 365 + x_offs2,
526                self.button_y + self.button_scale_y - 93 + prize_y_offs,
527            ),
528        )
529
530        bui.textwidget(
531            edit=self.prize_value_1_text,
532            text='-' if pv1 == '' else pv1,
533            position=(
534                self.button_x + 380 + x_offs2,
535                self.button_y + self.button_scale_y - 93 + prize_y_offs,
536            ),
537        )
538        bui.imagewidget(
539            edit=self.prize_chest_1_image,
540            position=(
541                self.button_x + 380 + x_offs2c,
542                self.button_y
543                + self.button_scale_y
544                - 93
545                + prize_y_offs
546                - 0.5 * self._chestsz,
547            ),
548        )
549        classic.set_tournament_prize_image(entry, 0, self.prize_chest_1_image)
550
551        bui.textwidget(
552            edit=self.prize_range_2_text,
553            text=pr2,
554            position=(
555                self.button_x + 365 + x_offs2,
556                self.button_y + self.button_scale_y - 93 - 45 + prize_y_offs,
557            ),
558        )
559        bui.textwidget(
560            edit=self.prize_value_2_text,
561            text=pv2,
562            position=(
563                self.button_x + 380 + x_offs2,
564                self.button_y + self.button_scale_y - 93 - 45 + prize_y_offs,
565            ),
566        )
567        bui.imagewidget(
568            edit=self.prize_chest_2_image,
569            position=(
570                self.button_x + 380 + x_offs2c,
571                self.button_y
572                + self.button_scale_y
573                - 93
574                - 45
575                + prize_y_offs
576                - 0.5 * self._chestsz,
577            ),
578        )
579        classic.set_tournament_prize_image(entry, 1, self.prize_chest_2_image)
580
581        bui.textwidget(
582            edit=self.prize_range_3_text,
583            text=pr3,
584            position=(
585                self.button_x + 365 + x_offs2,
586                self.button_y + self.button_scale_y - 93 - 90 + prize_y_offs,
587            ),
588        )
589        bui.textwidget(
590            edit=self.prize_value_3_text,
591            text=pv3,
592            position=(
593                self.button_x + 380 + x_offs2,
594                self.button_y + self.button_scale_y - 93 - 90 + prize_y_offs,
595            ),
596        )
597        bui.imagewidget(
598            edit=self.prize_chest_3_image,
599            position=(
600                self.button_x + 380 + x_offs2c,
601                self.button_y
602                + self.button_scale_y
603                - 93
604                - 90
605                + prize_y_offs
606                - 0.5 * self._chestsz,
607            ),
608        )
609        classic.set_tournament_prize_image(entry, 2, self.prize_chest_3_image)
610
611        leader_name = '-'
612        leader_score: str | bui.Lstr = '-'
613        if entry['scores']:
614            score = self.leader = copy.deepcopy(entry['scores'][0])
615            leader_name = score[1]
616            leader_score = (
617                bui.timestring((score[0] * 10) / 1000.0, centi=True)
618                if entry['scoreType'] == 'time'
619                else str(score[0])
620            )
621        else:
622            self.leader = None
623
624        bui.textwidget(
625            edit=self.current_leader_name_text, text=bui.Lstr(value=leader_name)
626        )
627        bui.textwidget(edit=self.current_leader_score_text, text=leader_score)
628        bui.buttonwidget(
629            edit=self.more_scores_button,
630            label=bui.Lstr(resource=f'{self._r}.seeMoreText'),
631        )
632        out_of_time_text: str | bui.Lstr = (
633            '-'
634            if 'totalTime' not in entry
635            else bui.Lstr(
636                resource=f'{self._r}.ofTotalTimeText',
637                subs=[
638                    (
639                        '${TOTAL}',
640                        bui.timestring(entry['totalTime'], centi=False),
641                    )
642                ],
643            )
644        )
645        bui.textwidget(
646            edit=self.time_remaining_out_of_text, text=out_of_time_text
647        )
648
649        # if self.game is None:
650        #     bui.textwidget(edit=self.button_text, text='-')
651        #     bui.imagewidget(
652        #         edit=self.image, texture=bui.gettexture('black'), opacity=0.2
653        #     )
654        # else:
655        max_players = bui.app.classic.accounts.tournament_info[
656            self.tournament_id
657        ]['maxPlayers']
658
659        txt = bui.Lstr(
660            value='${A} ${B}',
661            subs=[
662                ('${A}', campaign.getlevel(levelname).displayname),
663                (
664                    '${B}',
665                    bui.Lstr(
666                        resource='playerCountAbbreviatedText',
667                        subs=[('${COUNT}', str(max_players))],
668                    ),
669                ),
670            ],
671        )
672        bui.textwidget(edit=self.button_text, text=txt)
673
674        fee = entry['fee']
675        assert isinstance(fee, int | None)
676
677        if fee is None:
678            fee_var = None
679        elif fee == 4:
680            fee_var = 'price.tournament_entry_4'
681        elif fee == 3:
682            fee_var = 'price.tournament_entry_3'
683        elif fee == 2:
684            fee_var = 'price.tournament_entry_2'
685        elif fee == 1:
686            fee_var = 'price.tournament_entry_1'
687        elif fee == -1:
688            fee_var = None
689        else:
690            if fee != 0:
691                print('Unknown fee value:', fee)
692            fee_var = 'price.tournament_entry_0'
693
694        self.allow_ads = allow_ads = (
695            entry['allowAds'] if USE_ENTRY_FEES else False
696        )
697
698        final_fee = (
699            None
700            if fee_var is None
701            else plus.get_v1_account_misc_read_val(fee_var, '?')
702        )
703        assert isinstance(final_fee, int | None)
704
705        final_fee_str: str | bui.Lstr
706        if fee_var is None:
707            final_fee_str = ''
708        else:
709            if final_fee == 0:
710                final_fee_str = bui.Lstr(resource='getTicketsWindow.freeText')
711            else:
712                final_fee_str = bui.charstr(
713                    bui.SpecialChar.TICKET_BACKING
714                ) + str(final_fee)
715
716        assert bui.app.classic is not None
717        ad_tries_remaining = bui.app.classic.accounts.tournament_info[
718            self.tournament_id
719        ]['adTriesRemaining']
720        assert isinstance(ad_tries_remaining, int | None)
721        free_tries_remaining = bui.app.classic.accounts.tournament_info[
722            self.tournament_id
723        ]['freeTriesRemaining']
724        assert isinstance(free_tries_remaining, int | None)
725
726        # Now, if this fee allows ads and we support video ads, show the
727        # 'or ad' version.
728        if USE_ENTRY_FEES:
729            if allow_ads and plus.has_video_ads():
730                ads_enabled = plus.have_incentivized_ad()
731                bui.imagewidget(
732                    edit=self.entry_fee_ad_image,
733                    opacity=1.0 if ads_enabled else 0.25,
734                )
735                or_text = (
736                    bui.Lstr(
737                        resource='orText', subs=[('${A}', ''), ('${B}', '')]
738                    )
739                    .evaluate()
740                    .strip()
741                )
742                bui.textwidget(edit=self.entry_fee_text_or, text=or_text)
743                bui.textwidget(
744                    edit=self.entry_fee_text_top,
745                    position=(
746                        self.button_x + 360,
747                        self.button_y + self.button_scale_y - 60,
748                    ),
749                    scale=1.3,
750                    text=final_fee_str,
751                )
752
753                # Possibly show number of ad-plays remaining.
754                bui.textwidget(
755                    edit=self.entry_fee_text_remaining,
756                    position=(
757                        self.button_x + 360,
758                        self.button_y + self.button_scale_y - 146,
759                    ),
760                    text=(
761                        ''
762                        if ad_tries_remaining in [None, 0]
763                        else ('' + str(ad_tries_remaining))
764                    ),
765                    color=(0.6, 0.6, 0.6, 1 if ads_enabled else 0.2),
766                )
767            else:
768                bui.imagewidget(edit=self.entry_fee_ad_image, opacity=0.0)
769                bui.textwidget(edit=self.entry_fee_text_or, text='')
770                bui.textwidget(
771                    edit=self.entry_fee_text_top,
772                    position=(
773                        self.button_x + 360,
774                        self.button_y + self.button_scale_y - 80,
775                    ),
776                    scale=1.3,
777                    text=final_fee_str,
778                )
779
780                # Possibly show number of free-plays remaining.
781                bui.textwidget(
782                    edit=self.entry_fee_text_remaining,
783                    position=(
784                        self.button_x + 360,
785                        self.button_y + self.button_scale_y - 100,
786                    ),
787                    text=(
788                        ''
789                        if (free_tries_remaining in [None, 0] or final_fee != 0)
790                        else ('' + str(free_tries_remaining))
791                    ),
792                    color=(0.6, 0.6, 0.6, 1),
793                )

Update for new incoming data.