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

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.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.25, y + scly - 150),
 86            size=(image_width * 0.5, image_width * 0.5),
 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        )
on_pressed
lsbt
lsbo
allow_ads
tournament_id: 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:
444    def update_for_data(self, entry: dict[str, Any]) -> None:
445        """Update for new incoming data."""
446        # pylint: disable=too-many-statements
447        # pylint: disable=too-many-locals
448        # pylint: disable=too-many-branches
449
450        plus = bui.app.plus
451        assert plus is not None
452
453        classic = bui.app.classic
454        assert classic is not None
455
456        prize_y_offs = (
457            34
458            if 'prizeRange3' in entry
459            else 20 if 'prizeRange2' in entry else 12
460        )
461        x_offs = self._base_x_offs + 90
462
463        # Special offset for prize ranges/vals.
464        x_offs2 = x_offs - 20.0
465
466        # Special offset for prize chests.
467        x_offs2c = x_offs2 + 50
468
469        # Fetch prize range and trophy strings.
470        (pr1, pv1, pr2, pv2, pr3, pv3) = classic.get_tournament_prize_strings(
471            entry, include_tickets=False
472        )
473
474        enabled = 'requiredLeague' not in entry
475        bui.buttonwidget(
476            edit=self.button,
477            color=(0.5, 0.7, 0.2) if enabled else (0.5, 0.5, 0.5),
478        )
479        bui.imagewidget(edit=self.lock_image, opacity=0.0 if enabled else 1.0)
480        bui.textwidget(
481            edit=self.prize_range_1_text,
482            text='-' if pr1 == '' else pr1,
483            position=(
484                self.button_x + 365 + x_offs2,
485                self.button_y + self.button_scale_y - 93 + prize_y_offs,
486            ),
487        )
488
489        bui.textwidget(
490            edit=self.prize_value_1_text,
491            text='-' if pv1 == '' else pv1,
492            position=(
493                self.button_x + 380 + x_offs2,
494                self.button_y + self.button_scale_y - 93 + prize_y_offs,
495            ),
496        )
497        bui.imagewidget(
498            edit=self.prize_chest_1_image,
499            position=(
500                self.button_x + 380 + x_offs2c,
501                self.button_y
502                + self.button_scale_y
503                - 93
504                + prize_y_offs
505                - 0.5 * self._chestsz,
506            ),
507        )
508        classic.set_tournament_prize_image(entry, 0, self.prize_chest_1_image)
509
510        bui.textwidget(
511            edit=self.prize_range_2_text,
512            text=pr2,
513            position=(
514                self.button_x + 365 + x_offs2,
515                self.button_y + self.button_scale_y - 93 - 45 + prize_y_offs,
516            ),
517        )
518        bui.textwidget(
519            edit=self.prize_value_2_text,
520            text=pv2,
521            position=(
522                self.button_x + 380 + x_offs2,
523                self.button_y + self.button_scale_y - 93 - 45 + prize_y_offs,
524            ),
525        )
526        bui.imagewidget(
527            edit=self.prize_chest_2_image,
528            position=(
529                self.button_x + 380 + x_offs2c,
530                self.button_y
531                + self.button_scale_y
532                - 93
533                - 45
534                + prize_y_offs
535                - 0.5 * self._chestsz,
536            ),
537        )
538        classic.set_tournament_prize_image(entry, 1, self.prize_chest_2_image)
539
540        bui.textwidget(
541            edit=self.prize_range_3_text,
542            text=pr3,
543            position=(
544                self.button_x + 365 + x_offs2,
545                self.button_y + self.button_scale_y - 93 - 90 + prize_y_offs,
546            ),
547        )
548        bui.textwidget(
549            edit=self.prize_value_3_text,
550            text=pv3,
551            position=(
552                self.button_x + 380 + x_offs2,
553                self.button_y + self.button_scale_y - 93 - 90 + prize_y_offs,
554            ),
555        )
556        bui.imagewidget(
557            edit=self.prize_chest_3_image,
558            position=(
559                self.button_x + 380 + x_offs2c,
560                self.button_y
561                + self.button_scale_y
562                - 93
563                - 90
564                + prize_y_offs
565                - 0.5 * self._chestsz,
566            ),
567        )
568        classic.set_tournament_prize_image(entry, 2, self.prize_chest_3_image)
569
570        leader_name = '-'
571        leader_score: str | bui.Lstr = '-'
572        if entry['scores']:
573            score = self.leader = copy.deepcopy(entry['scores'][0])
574            leader_name = score[1]
575            leader_score = (
576                bui.timestring((score[0] * 10) / 1000.0, centi=True)
577                if entry['scoreType'] == 'time'
578                else str(score[0])
579            )
580        else:
581            self.leader = None
582
583        bui.textwidget(
584            edit=self.current_leader_name_text, text=bui.Lstr(value=leader_name)
585        )
586        bui.textwidget(edit=self.current_leader_score_text, text=leader_score)
587        bui.buttonwidget(
588            edit=self.more_scores_button,
589            label=bui.Lstr(resource=f'{self._r}.seeMoreText'),
590        )
591        out_of_time_text: str | bui.Lstr = (
592            '-'
593            if 'totalTime' not in entry
594            else bui.Lstr(
595                resource=f'{self._r}.ofTotalTimeText',
596                subs=[
597                    (
598                        '${TOTAL}',
599                        bui.timestring(entry['totalTime'], centi=False),
600                    )
601                ],
602            )
603        )
604        bui.textwidget(
605            edit=self.time_remaining_out_of_text, text=out_of_time_text
606        )
607
608        self.time_remaining = entry['timeRemaining']
609        self.has_time_remaining = entry is not None
610        self.tournament_id = entry['tournamentID']
611        self.required_league = (
612            None if 'requiredLeague' not in entry else entry['requiredLeague']
613        )
614
615        assert bui.app.classic is not None
616        game = bui.app.classic.accounts.tournament_info[self.tournament_id][
617            'game'
618        ]
619
620        if game is None:
621            bui.textwidget(edit=self.button_text, text='-')
622            bui.imagewidget(
623                edit=self.image, texture=bui.gettexture('black'), opacity=0.2
624            )
625        else:
626            campaignname, levelname = game.split(':')
627            campaign = bui.app.classic.getcampaign(campaignname)
628            max_players = bui.app.classic.accounts.tournament_info[
629                self.tournament_id
630            ]['maxPlayers']
631
632            txt = bui.Lstr(
633                value='${A} ${B}',
634                subs=[
635                    ('${A}', campaign.getlevel(levelname).displayname),
636                    (
637                        '${B}',
638                        bui.Lstr(
639                            resource='playerCountAbbreviatedText',
640                            subs=[('${COUNT}', str(max_players))],
641                        ),
642                    ),
643                ],
644            )
645            bui.textwidget(edit=self.button_text, text=txt)
646            bui.imagewidget(
647                edit=self.image,
648                texture=bui.gettexture(
649                    campaign.getlevel(levelname).preview_texture_name
650                ),
651                opacity=1.0 if enabled else 0.5,
652            )
653
654        fee = entry['fee']
655        assert isinstance(fee, int | None)
656
657        if fee is None:
658            fee_var = None
659        elif fee == 4:
660            fee_var = 'price.tournament_entry_4'
661        elif fee == 3:
662            fee_var = 'price.tournament_entry_3'
663        elif fee == 2:
664            fee_var = 'price.tournament_entry_2'
665        elif fee == 1:
666            fee_var = 'price.tournament_entry_1'
667        elif fee == -1:
668            fee_var = None
669        else:
670            if fee != 0:
671                print('Unknown fee value:', fee)
672            fee_var = 'price.tournament_entry_0'
673
674        self.allow_ads = allow_ads = (
675            entry['allowAds'] if USE_ENTRY_FEES else False
676        )
677
678        final_fee = (
679            None
680            if fee_var is None
681            else plus.get_v1_account_misc_read_val(fee_var, '?')
682        )
683        assert isinstance(final_fee, int | None)
684
685        final_fee_str: str | bui.Lstr
686        if fee_var is None:
687            final_fee_str = ''
688        else:
689            if final_fee == 0:
690                final_fee_str = bui.Lstr(resource='getTicketsWindow.freeText')
691            else:
692                final_fee_str = bui.charstr(
693                    bui.SpecialChar.TICKET_BACKING
694                ) + str(final_fee)
695
696        assert bui.app.classic is not None
697        ad_tries_remaining = bui.app.classic.accounts.tournament_info[
698            self.tournament_id
699        ]['adTriesRemaining']
700        assert isinstance(ad_tries_remaining, int | None)
701        free_tries_remaining = bui.app.classic.accounts.tournament_info[
702            self.tournament_id
703        ]['freeTriesRemaining']
704        assert isinstance(free_tries_remaining, int | None)
705
706        # Now, if this fee allows ads and we support video ads, show the
707        # 'or ad' version.
708        if USE_ENTRY_FEES:
709            if allow_ads and plus.has_video_ads():
710                ads_enabled = plus.have_incentivized_ad()
711                bui.imagewidget(
712                    edit=self.entry_fee_ad_image,
713                    opacity=1.0 if ads_enabled else 0.25,
714                )
715                or_text = (
716                    bui.Lstr(
717                        resource='orText', subs=[('${A}', ''), ('${B}', '')]
718                    )
719                    .evaluate()
720                    .strip()
721                )
722                bui.textwidget(edit=self.entry_fee_text_or, text=or_text)
723                bui.textwidget(
724                    edit=self.entry_fee_text_top,
725                    position=(
726                        self.button_x + 360,
727                        self.button_y + self.button_scale_y - 60,
728                    ),
729                    scale=1.3,
730                    text=final_fee_str,
731                )
732
733                # Possibly show number of ad-plays remaining.
734                bui.textwidget(
735                    edit=self.entry_fee_text_remaining,
736                    position=(
737                        self.button_x + 360,
738                        self.button_y + self.button_scale_y - 146,
739                    ),
740                    text=(
741                        ''
742                        if ad_tries_remaining in [None, 0]
743                        else ('' + str(ad_tries_remaining))
744                    ),
745                    color=(0.6, 0.6, 0.6, 1 if ads_enabled else 0.2),
746                )
747            else:
748                bui.imagewidget(edit=self.entry_fee_ad_image, opacity=0.0)
749                bui.textwidget(edit=self.entry_fee_text_or, text='')
750                bui.textwidget(
751                    edit=self.entry_fee_text_top,
752                    position=(
753                        self.button_x + 360,
754                        self.button_y + self.button_scale_y - 80,
755                    ),
756                    scale=1.3,
757                    text=final_fee_str,
758                )
759
760                # Possibly show number of free-plays remaining.
761                bui.textwidget(
762                    edit=self.entry_fee_text_remaining,
763                    position=(
764                        self.button_x + 360,
765                        self.button_y + self.button_scale_y - 100,
766                    ),
767                    text=(
768                        ''
769                        if (free_tries_remaining in [None, 0] or final_fee != 0)
770                        else ('' + str(free_tries_remaining))
771                    ),
772                    color=(0.6, 0.6, 0.6, 1),
773                )

Update for new incoming data.