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

Button showing a tournament in coop window.

TournamentButton( parent: _ba.Widget, x: float, y: float, select: bool, on_pressed: Callable[[bastd.ui.coop.tournamentbutton.TournamentButton], NoneType])
 21    def __init__(
 22        self,
 23        parent: ba.Widget,
 24        x: float,
 25        y: float,
 26        select: bool,
 27        on_pressed: Callable[[TournamentButton], None],
 28    ) -> None:
 29        self._r = 'coopSelectWindow'
 30        sclx = 300
 31        scly = 195.0
 32        self.on_pressed = on_pressed
 33        self.lsbt = ba.getmodel('level_select_button_transparent')
 34        self.lsbo = ba.getmodel('level_select_button_opaque')
 35        self.allow_ads = False
 36        self.tournament_id: str | None = None
 37        self.time_remaining: int = 0
 38        self.has_time_remaining: bool = False
 39        self.leader: Any = None
 40        self.required_league: str | None = None
 41        self.button = btn = ba.buttonwidget(
 42            parent=parent,
 43            position=(x + 23, y + 4),
 44            size=(sclx, scly),
 45            label='',
 46            button_type='square',
 47            autoselect=True,
 48            # on_activate_call=lambda: self.run(None, tournament_button=data)
 49            on_activate_call=ba.WeakCall(self._pressed),
 50        )
 51        ba.widget(
 52            edit=btn,
 53            show_buffer_bottom=50,
 54            show_buffer_top=50,
 55            show_buffer_left=400,
 56            show_buffer_right=200,
 57        )
 58        if select:
 59            ba.containerwidget(
 60                edit=parent, selected_child=btn, visible_child=btn
 61            )
 62        image_width = sclx * 0.85 * 0.75
 63
 64        self.image = ba.imagewidget(
 65            parent=parent,
 66            draw_controller=btn,
 67            position=(x + 21 + sclx * 0.5 - image_width * 0.5, y + scly - 150),
 68            size=(image_width, image_width * 0.5),
 69            model_transparent=self.lsbt,
 70            model_opaque=self.lsbo,
 71            texture=ba.gettexture('black'),
 72            opacity=0.2,
 73            mask_texture=ba.gettexture('mapPreviewMask'),
 74        )
 75
 76        self.lock_image = ba.imagewidget(
 77            parent=parent,
 78            draw_controller=btn,
 79            position=(x + 21 + sclx * 0.5 - image_width * 0.25, y + scly - 150),
 80            size=(image_width * 0.5, image_width * 0.5),
 81            texture=ba.gettexture('lock'),
 82            opacity=0.0,
 83        )
 84
 85        self.button_text = ba.textwidget(
 86            parent=parent,
 87            draw_controller=btn,
 88            position=(x + 20 + sclx * 0.5, y + scly - 35),
 89            size=(0, 0),
 90            h_align='center',
 91            text='-',
 92            v_align='center',
 93            maxwidth=sclx * 0.76,
 94            scale=0.85,
 95            color=(0.8, 1.0, 0.8, 1.0),
 96        )
 97
 98        header_color = (0.43, 0.4, 0.5, 1)
 99        value_color = (0.6, 0.6, 0.6, 1)
100
101        x_offs = 0
102        ba.textwidget(
103            parent=parent,
104            draw_controller=btn,
105            position=(x + 360, y + scly - 20),
106            size=(0, 0),
107            h_align='center',
108            text=ba.Lstr(resource=self._r + '.entryFeeText'),
109            v_align='center',
110            maxwidth=100,
111            scale=0.9,
112            color=header_color,
113            flatness=1.0,
114        )
115
116        self.entry_fee_text_top = ba.textwidget(
117            parent=parent,
118            draw_controller=btn,
119            position=(x + 360, y + scly - 60),
120            size=(0, 0),
121            h_align='center',
122            text='-',
123            v_align='center',
124            maxwidth=60,
125            scale=1.3,
126            color=value_color,
127            flatness=1.0,
128        )
129        self.entry_fee_text_or = ba.textwidget(
130            parent=parent,
131            draw_controller=btn,
132            position=(x + 360, y + scly - 90),
133            size=(0, 0),
134            h_align='center',
135            text='',
136            v_align='center',
137            maxwidth=60,
138            scale=0.5,
139            color=value_color,
140            flatness=1.0,
141        )
142        self.entry_fee_text_remaining = ba.textwidget(
143            parent=parent,
144            draw_controller=btn,
145            position=(x + 360, y + scly - 90),
146            size=(0, 0),
147            h_align='center',
148            text='',
149            v_align='center',
150            maxwidth=60,
151            scale=0.5,
152            color=value_color,
153            flatness=1.0,
154        )
155
156        self.entry_fee_ad_image = ba.imagewidget(
157            parent=parent,
158            size=(40, 40),
159            draw_controller=btn,
160            position=(x + 360 - 20, y + scly - 140),
161            opacity=0.0,
162            texture=ba.gettexture('tv'),
163        )
164
165        x_offs += 50
166
167        ba.textwidget(
168            parent=parent,
169            draw_controller=btn,
170            position=(x + 447 + x_offs, y + scly - 20),
171            size=(0, 0),
172            h_align='center',
173            text=ba.Lstr(resource=self._r + '.prizesText'),
174            v_align='center',
175            maxwidth=130,
176            scale=0.9,
177            color=header_color,
178            flatness=1.0,
179        )
180
181        self.button_x = x
182        self.button_y = y
183        self.button_scale_y = scly
184
185        xo2 = 0
186        prize_value_scale = 1.5
187
188        self.prize_range_1_text = ba.textwidget(
189            parent=parent,
190            draw_controller=btn,
191            position=(x + 355 + xo2 + x_offs, y + scly - 93),
192            size=(0, 0),
193            h_align='right',
194            v_align='center',
195            maxwidth=50,
196            text='-',
197            scale=0.8,
198            color=header_color,
199            flatness=1.0,
200        )
201        self.prize_value_1_text = ba.textwidget(
202            parent=parent,
203            draw_controller=btn,
204            position=(x + 380 + xo2 + x_offs, y + scly - 93),
205            size=(0, 0),
206            h_align='left',
207            text='-',
208            v_align='center',
209            maxwidth=100,
210            scale=prize_value_scale,
211            color=value_color,
212            flatness=1.0,
213        )
214
215        self.prize_range_2_text = ba.textwidget(
216            parent=parent,
217            draw_controller=btn,
218            position=(x + 355 + xo2 + x_offs, y + scly - 93),
219            size=(0, 0),
220            h_align='right',
221            v_align='center',
222            maxwidth=50,
223            scale=0.8,
224            color=header_color,
225            flatness=1.0,
226        )
227        self.prize_value_2_text = ba.textwidget(
228            parent=parent,
229            draw_controller=btn,
230            position=(x + 380 + xo2 + x_offs, y + scly - 93),
231            size=(0, 0),
232            h_align='left',
233            text='',
234            v_align='center',
235            maxwidth=100,
236            scale=prize_value_scale,
237            color=value_color,
238            flatness=1.0,
239        )
240
241        self.prize_range_3_text = ba.textwidget(
242            parent=parent,
243            draw_controller=btn,
244            position=(x + 355 + xo2 + x_offs, y + scly - 93),
245            size=(0, 0),
246            h_align='right',
247            v_align='center',
248            maxwidth=50,
249            scale=0.8,
250            color=header_color,
251            flatness=1.0,
252        )
253        self.prize_value_3_text = ba.textwidget(
254            parent=parent,
255            draw_controller=btn,
256            position=(x + 380 + xo2 + x_offs, y + scly - 93),
257            size=(0, 0),
258            h_align='left',
259            text='',
260            v_align='center',
261            maxwidth=100,
262            scale=prize_value_scale,
263            color=value_color,
264            flatness=1.0,
265        )
266
267        ba.textwidget(
268            parent=parent,
269            draw_controller=btn,
270            position=(x + 620 + x_offs, y + scly - 20),
271            size=(0, 0),
272            h_align='center',
273            text=ba.Lstr(resource=self._r + '.currentBestText'),
274            v_align='center',
275            maxwidth=180,
276            scale=0.9,
277            color=header_color,
278            flatness=1.0,
279        )
280        self.current_leader_name_text = ba.textwidget(
281            parent=parent,
282            draw_controller=btn,
283            position=(
284                x + 620 + x_offs - (170 / 1.4) * 0.5,
285                y + scly - 60 - 40 * 0.5,
286            ),
287            selectable=True,
288            click_activate=True,
289            autoselect=True,
290            on_activate_call=ba.WeakCall(self._show_leader),
291            size=(170 / 1.4, 40),
292            h_align='center',
293            text='-',
294            v_align='center',
295            maxwidth=170,
296            scale=1.4,
297            color=value_color,
298            flatness=1.0,
299        )
300        self.current_leader_score_text = ba.textwidget(
301            parent=parent,
302            draw_controller=btn,
303            position=(x + 620 + x_offs, y + scly - 113 + 10),
304            size=(0, 0),
305            h_align='center',
306            text='-',
307            v_align='center',
308            maxwidth=170,
309            scale=1.8,
310            color=value_color,
311            flatness=1.0,
312        )
313
314        self.more_scores_button = ba.buttonwidget(
315            parent=parent,
316            position=(x + 620 + x_offs - 60, y + scly - 50 - 125),
317            color=(0.5, 0.5, 0.6),
318            textcolor=(0.7, 0.7, 0.8),
319            label='-',
320            size=(120, 40),
321            autoselect=True,
322            up_widget=self.current_leader_name_text,
323            text_scale=0.6,
324            on_activate_call=ba.WeakCall(self._show_scores),
325        )
326        ba.widget(
327            edit=self.current_leader_name_text,
328            down_widget=self.more_scores_button,
329        )
330
331        ba.textwidget(
332            parent=parent,
333            draw_controller=btn,
334            position=(x + 820 + x_offs, y + scly - 20),
335            size=(0, 0),
336            h_align='center',
337            text=ba.Lstr(resource=self._r + '.timeRemainingText'),
338            v_align='center',
339            maxwidth=180,
340            scale=0.9,
341            color=header_color,
342            flatness=1.0,
343        )
344        self.time_remaining_value_text = ba.textwidget(
345            parent=parent,
346            draw_controller=btn,
347            position=(x + 820 + x_offs, y + scly - 68),
348            size=(0, 0),
349            h_align='center',
350            text='-',
351            v_align='center',
352            maxwidth=180,
353            scale=2.0,
354            color=value_color,
355            flatness=1.0,
356        )
357        self.time_remaining_out_of_text = ba.textwidget(
358            parent=parent,
359            draw_controller=btn,
360            position=(x + 820 + x_offs, y + scly - 110),
361            size=(0, 0),
362            h_align='center',
363            text='-',
364            v_align='center',
365            maxwidth=120,
366            scale=0.72,
367            color=(0.4, 0.4, 0.5),
368            flatness=1.0,
369        )
def update_for_data(self, entry: dict[str, typing.Any]) -> None:
410    def update_for_data(self, entry: dict[str, Any]) -> None:
411        """Update for new incoming data."""
412        # pylint: disable=too-many-statements
413        # pylint: disable=too-many-locals
414        # pylint: disable=too-many-branches
415        from ba.internal import getcampaign, get_tournament_prize_strings
416
417        prize_y_offs = (
418            34
419            if 'prizeRange3' in entry
420            else 20
421            if 'prizeRange2' in entry
422            else 12
423        )
424        x_offs = 90
425
426        # This seems to be a false alarm.
427        # pylint: disable=unbalanced-tuple-unpacking
428        pr1, pv1, pr2, pv2, pr3, pv3 = get_tournament_prize_strings(entry)
429        # pylint: enable=unbalanced-tuple-unpacking
430        enabled = 'requiredLeague' not in entry
431        ba.buttonwidget(
432            edit=self.button,
433            color=(0.5, 0.7, 0.2) if enabled else (0.5, 0.5, 0.5),
434        )
435        ba.imagewidget(edit=self.lock_image, opacity=0.0 if enabled else 1.0)
436        ba.textwidget(
437            edit=self.prize_range_1_text,
438            text='-' if pr1 == '' else pr1,
439            position=(
440                self.button_x + 365 + x_offs,
441                self.button_y + self.button_scale_y - 93 + prize_y_offs,
442            ),
443        )
444
445        # We want to draw values containing tickets a bit smaller
446        # (scratch that; we now draw medals a bit bigger).
447        ticket_char = ba.charstr(ba.SpecialChar.TICKET_BACKING)
448        prize_value_scale_large = 1.0
449        prize_value_scale_small = 1.0
450
451        ba.textwidget(
452            edit=self.prize_value_1_text,
453            text='-' if pv1 == '' else pv1,
454            scale=prize_value_scale_large
455            if ticket_char not in pv1
456            else prize_value_scale_small,
457            position=(
458                self.button_x + 380 + x_offs,
459                self.button_y + self.button_scale_y - 93 + prize_y_offs,
460            ),
461        )
462
463        ba.textwidget(
464            edit=self.prize_range_2_text,
465            text=pr2,
466            position=(
467                self.button_x + 365 + x_offs,
468                self.button_y + self.button_scale_y - 93 - 45 + prize_y_offs,
469            ),
470        )
471        ba.textwidget(
472            edit=self.prize_value_2_text,
473            text=pv2,
474            scale=prize_value_scale_large
475            if ticket_char not in pv2
476            else prize_value_scale_small,
477            position=(
478                self.button_x + 380 + x_offs,
479                self.button_y + self.button_scale_y - 93 - 45 + prize_y_offs,
480            ),
481        )
482
483        ba.textwidget(
484            edit=self.prize_range_3_text,
485            text=pr3,
486            position=(
487                self.button_x + 365 + x_offs,
488                self.button_y + self.button_scale_y - 93 - 90 + prize_y_offs,
489            ),
490        )
491        ba.textwidget(
492            edit=self.prize_value_3_text,
493            text=pv3,
494            scale=prize_value_scale_large
495            if ticket_char not in pv3
496            else prize_value_scale_small,
497            position=(
498                self.button_x + 380 + x_offs,
499                self.button_y + self.button_scale_y - 93 - 90 + prize_y_offs,
500            ),
501        )
502
503        leader_name = '-'
504        leader_score: str | ba.Lstr = '-'
505        if entry['scores']:
506            score = self.leader = copy.deepcopy(entry['scores'][0])
507            leader_name = score[1]
508            leader_score = (
509                ba.timestring(
510                    score[0] * 10,
511                    centi=True,
512                    timeformat=ba.TimeFormat.MILLISECONDS,
513                    suppress_format_warning=True,
514                )
515                if entry['scoreType'] == 'time'
516                else str(score[0])
517            )
518        else:
519            self.leader = None
520
521        ba.textwidget(
522            edit=self.current_leader_name_text, text=ba.Lstr(value=leader_name)
523        )
524        ba.textwidget(edit=self.current_leader_score_text, text=leader_score)
525        ba.buttonwidget(
526            edit=self.more_scores_button,
527            label=ba.Lstr(resource=self._r + '.seeMoreText'),
528        )
529        out_of_time_text: str | ba.Lstr = (
530            '-'
531            if 'totalTime' not in entry
532            else ba.Lstr(
533                resource=self._r + '.ofTotalTimeText',
534                subs=[
535                    (
536                        '${TOTAL}',
537                        ba.timestring(
538                            entry['totalTime'],
539                            centi=False,
540                            suppress_format_warning=True,
541                        ),
542                    )
543                ],
544            )
545        )
546        ba.textwidget(
547            edit=self.time_remaining_out_of_text, text=out_of_time_text
548        )
549
550        self.time_remaining = entry['timeRemaining']
551        self.has_time_remaining = entry is not None
552        self.tournament_id = entry['tournamentID']
553        self.required_league = (
554            None if 'requiredLeague' not in entry else entry['requiredLeague']
555        )
556
557        game = ba.app.accounts_v1.tournament_info[self.tournament_id]['game']
558
559        if game is None:
560            ba.textwidget(edit=self.button_text, text='-')
561            ba.imagewidget(
562                edit=self.image, texture=ba.gettexture('black'), opacity=0.2
563            )
564        else:
565            campaignname, levelname = game.split(':')
566            campaign = getcampaign(campaignname)
567            max_players = ba.app.accounts_v1.tournament_info[
568                self.tournament_id
569            ]['maxPlayers']
570            txt = ba.Lstr(
571                value='${A} ${B}',
572                subs=[
573                    ('${A}', campaign.getlevel(levelname).displayname),
574                    (
575                        '${B}',
576                        ba.Lstr(
577                            resource='playerCountAbbreviatedText',
578                            subs=[('${COUNT}', str(max_players))],
579                        ),
580                    ),
581                ],
582            )
583            ba.textwidget(edit=self.button_text, text=txt)
584            ba.imagewidget(
585                edit=self.image,
586                texture=campaign.getlevel(levelname).get_preview_texture(),
587                opacity=1.0 if enabled else 0.5,
588            )
589
590        fee = entry['fee']
591
592        if fee is None:
593            fee_var = None
594        elif fee == 4:
595            fee_var = 'price.tournament_entry_4'
596        elif fee == 3:
597            fee_var = 'price.tournament_entry_3'
598        elif fee == 2:
599            fee_var = 'price.tournament_entry_2'
600        elif fee == 1:
601            fee_var = 'price.tournament_entry_1'
602        else:
603            if fee != 0:
604                print('Unknown fee value:', fee)
605            fee_var = 'price.tournament_entry_0'
606
607        self.allow_ads = allow_ads = entry['allowAds']
608
609        final_fee: int | None = (
610            None
611            if fee_var is None
612            else ba.internal.get_v1_account_misc_read_val(fee_var, '?')
613        )
614
615        final_fee_str: str | ba.Lstr
616        if fee_var is None:
617            final_fee_str = ''
618        else:
619            if final_fee == 0:
620                final_fee_str = ba.Lstr(resource='getTicketsWindow.freeText')
621            else:
622                final_fee_str = ba.charstr(ba.SpecialChar.TICKET_BACKING) + str(
623                    final_fee
624                )
625
626        ad_tries_remaining = ba.app.accounts_v1.tournament_info[
627            self.tournament_id
628        ]['adTriesRemaining']
629        free_tries_remaining = ba.app.accounts_v1.tournament_info[
630            self.tournament_id
631        ]['freeTriesRemaining']
632
633        # Now, if this fee allows ads and we support video ads, show
634        # the 'or ad' version.
635        if allow_ads and ba.internal.has_video_ads():
636            ads_enabled = ba.internal.have_incentivized_ad()
637            ba.imagewidget(
638                edit=self.entry_fee_ad_image,
639                opacity=1.0 if ads_enabled else 0.25,
640            )
641            or_text = (
642                ba.Lstr(resource='orText', subs=[('${A}', ''), ('${B}', '')])
643                .evaluate()
644                .strip()
645            )
646            ba.textwidget(edit=self.entry_fee_text_or, text=or_text)
647            ba.textwidget(
648                edit=self.entry_fee_text_top,
649                position=(
650                    self.button_x + 360,
651                    self.button_y + self.button_scale_y - 60,
652                ),
653                scale=1.3,
654                text=final_fee_str,
655            )
656
657            # Possibly show number of ad-plays remaining.
658            ba.textwidget(
659                edit=self.entry_fee_text_remaining,
660                position=(
661                    self.button_x + 360,
662                    self.button_y + self.button_scale_y - 146,
663                ),
664                text=''
665                if ad_tries_remaining in [None, 0]
666                else ('' + str(ad_tries_remaining)),
667                color=(0.6, 0.6, 0.6, 1 if ads_enabled else 0.2),
668            )
669        else:
670            ba.imagewidget(edit=self.entry_fee_ad_image, opacity=0.0)
671            ba.textwidget(edit=self.entry_fee_text_or, text='')
672            ba.textwidget(
673                edit=self.entry_fee_text_top,
674                position=(
675                    self.button_x + 360,
676                    self.button_y + self.button_scale_y - 80,
677                ),
678                scale=1.3,
679                text=final_fee_str,
680            )
681
682            # Possibly show number of free-plays remaining.
683            ba.textwidget(
684                edit=self.entry_fee_text_remaining,
685                position=(
686                    self.button_x + 360,
687                    self.button_y + self.button_scale_y - 100,
688                ),
689                text=(
690                    ''
691                    if (free_tries_remaining in [None, 0] or final_fee != 0)
692                    else ('' + str(free_tries_remaining))
693                ),
694                color=(0.6, 0.6, 0.6, 1),
695            )

Update for new incoming data.