bauiv1lib.play

Provides the top level play window.

  1# Released under the MIT License. See LICENSE for details.
  2#
  3"""Provides the top level play window."""
  4
  5from __future__ import annotations
  6
  7import logging
  8from typing import override, TYPE_CHECKING
  9
 10import bascenev1 as bs
 11import bauiv1 as bui
 12
 13if TYPE_CHECKING:
 14    from bauiv1 import MainWindowState
 15
 16
 17class PlaylistSelectContext:
 18    """For using PlayWindow to select a playlist instead of running game."""
 19
 20    back_state: MainWindowState | None = None
 21
 22
 23class PlayWindow(bui.MainWindow):
 24    """Window for selecting overall play type."""
 25
 26    def __init__(
 27        self,
 28        transition: str | None = 'in_right',
 29        origin_widget: bui.Widget | None = None,
 30        playlist_select_context: PlaylistSelectContext | None = None,
 31    ):
 32        # pylint: disable=too-many-statements
 33        # pylint: disable=too-many-locals
 34
 35        # Preload some modules we use in a background thread so we won't
 36        # have a visual hitch when the user taps them.
 37        bui.app.threadpool_submit_no_wait(self._preload_modules)
 38
 39        classic = bui.app.classic
 40        assert classic is not None
 41
 42        self._playlist_select_context = playlist_select_context
 43
 44        uiscale = bui.app.ui_v1.uiscale
 45        width = 1100 if uiscale is bui.UIScale.SMALL else 800
 46        x_offs = 150 if uiscale is bui.UIScale.SMALL else 0
 47        y_offs = -60 if uiscale is bui.UIScale.SMALL else 0
 48        height = 650 if uiscale is bui.UIScale.SMALL else 550
 49        button_width = 400
 50
 51        if origin_widget is not None:
 52
 53            # Need to store this ourself since we can function as a
 54            # non-main window.
 55            self._transition_out = 'out_scale'
 56        else:
 57            self._transition_out = 'out_right'
 58
 59        self._r = 'playWindow'
 60
 61        super().__init__(
 62            root_widget=bui.containerwidget(
 63                size=(width, height),
 64                toolbar_visibility=(
 65                    'menu_full'
 66                    if playlist_select_context is None
 67                    else 'menu_minimal'
 68                ),
 69                scale=(
 70                    1.35
 71                    if uiscale is bui.UIScale.SMALL
 72                    else 0.9 if uiscale is bui.UIScale.MEDIUM else 0.8
 73                ),
 74                stack_offset=(
 75                    (0, 20) if uiscale is bui.UIScale.SMALL else (0, 0)
 76                ),
 77            ),
 78            transition=transition,
 79            origin_widget=origin_widget,
 80        )
 81
 82        self._back_button: bui.Widget | None
 83        if uiscale is bui.UIScale.SMALL:
 84            self._back_button = None
 85            bui.containerwidget(
 86                edit=self._root_widget,
 87                on_cancel_call=self.main_window_back,
 88            )
 89        else:
 90            self._back_button = bui.buttonwidget(
 91                parent=self._root_widget,
 92                position=(55 + x_offs, height - 132 + y_offs),
 93                size=(60, 60),
 94                scale=1.1,
 95                text_res_scale=1.5,
 96                text_scale=1.2,
 97                autoselect=True,
 98                label=bui.charstr(bui.SpecialChar.BACK),
 99                button_type='backSmall',
100                on_activate_call=self.main_window_back,
101            )
102            bui.containerwidget(
103                edit=self._root_widget, cancel_button=self._back_button
104            )
105
106        txt = bui.textwidget(
107            parent=self._root_widget,
108            position=(width * 0.5, height - 101 + y_offs),
109            # position=(width * 0.5, height -
110            #           (101 if main_menu else 61)),
111            size=(0, 0),
112            text=bui.Lstr(
113                resource=(
114                    (f'{self._r}.titleText')
115                    if self._playlist_select_context is None
116                    else 'playlistsText'
117                )
118            ),
119            scale=1.7,
120            res_scale=2.0,
121            maxwidth=400,
122            color=bui.app.ui_v1.heading_color,
123            h_align='center',
124            v_align='center',
125        )
126
127        if uiscale is bui.UIScale.SMALL:
128            bui.textwidget(edit=txt, text='')
129
130        v = (
131            height
132            - (110 if self._playlist_select_context is None else 90)
133            + y_offs
134        )
135        v -= 100
136        clr = (0.6, 0.7, 0.6, 1.0)
137        v -= 280 if self._playlist_select_context is None else 180
138        v += 30 if uiscale is bui.UIScale.SMALL else 0
139        hoffs = (
140            x_offs + 80
141            if self._playlist_select_context is None
142            else x_offs - 100
143        )
144        scl = 1.13 if self._playlist_select_context is None else 0.68
145
146        self._lineup_tex = bui.gettexture('playerLineup')
147        angry_computer_transparent_mesh = bui.getmesh(
148            'angryComputerTransparent'
149        )
150        self._lineup_1_transparent_mesh = bui.getmesh(
151            'playerLineup1Transparent'
152        )
153        self._lineup_2_transparent_mesh = bui.getmesh(
154            'playerLineup2Transparent'
155        )
156        self._lineup_3_transparent_mesh = bui.getmesh(
157            'playerLineup3Transparent'
158        )
159        self._lineup_4_transparent_mesh = bui.getmesh(
160            'playerLineup4Transparent'
161        )
162        self._eyes_mesh = bui.getmesh('plasticEyesTransparent')
163
164        self._coop_button: bui.Widget | None = None
165
166        # Only show coop button in regular variant.
167        if self._playlist_select_context is None:
168            self._coop_button = btn = bui.buttonwidget(
169                parent=self._root_widget,
170                position=(hoffs, v + (scl * 15)),
171                size=(
172                    scl * button_width,
173                    scl * 300,
174                ),
175                extra_touch_border_scale=0.1,
176                autoselect=True,
177                label='',
178                button_type='square',
179                text_scale=1.13,
180                on_activate_call=self._coop,
181            )
182
183            if uiscale is bui.UIScale.SMALL:
184                bui.widget(
185                    edit=btn,
186                    left_widget=bui.get_special_widget('back_button'),
187                )
188                bui.widget(
189                    edit=btn,
190                    up_widget=bui.get_special_widget('account_button'),
191                )
192                bui.widget(
193                    edit=btn,
194                    down_widget=bui.get_special_widget('settings_button'),
195                )
196
197            self._draw_dude(
198                0,
199                btn,
200                hoffs,
201                v,
202                scl,
203                position=(140, 30),
204                color=(0.72, 0.4, 1.0),
205            )
206            self._draw_dude(
207                1,
208                btn,
209                hoffs,
210                v,
211                scl,
212                position=(185, 53),
213                color=(0.71, 0.5, 1.0),
214            )
215            self._draw_dude(
216                2,
217                btn,
218                hoffs,
219                v,
220                scl,
221                position=(220, 27),
222                color=(0.67, 0.44, 1.0),
223            )
224            self._draw_dude(
225                3, btn, hoffs, v, scl, position=(255, 57), color=(0.7, 0.3, 1.0)
226            )
227            bui.imagewidget(
228                parent=self._root_widget,
229                draw_controller=btn,
230                position=(hoffs + scl * 230, v + scl * 153),
231                size=(scl * 115, scl * 115),
232                texture=self._lineup_tex,
233                mesh_transparent=angry_computer_transparent_mesh,
234            )
235
236            bui.textwidget(
237                parent=self._root_widget,
238                draw_controller=btn,
239                position=(hoffs + scl * (-10), v + scl * 95),
240                size=(scl * button_width, scl * 50),
241                text=bui.Lstr(
242                    resource='playModes.singlePlayerCoopText',
243                    fallback_resource='playModes.coopText',
244                ),
245                maxwidth=scl * button_width * 0.7,
246                res_scale=1.5,
247                h_align='center',
248                v_align='center',
249                color=(0.7, 0.9, 0.7, 1.0),
250                scale=scl * 2.3,
251            )
252
253            bui.textwidget(
254                parent=self._root_widget,
255                draw_controller=btn,
256                position=(hoffs + scl * (-10), v + (scl * 54)),
257                size=(scl * button_width, scl * 30),
258                text=bui.Lstr(resource=f'{self._r}.oneToFourPlayersText'),
259                h_align='center',
260                v_align='center',
261                scale=0.83 * scl,
262                flatness=1.0,
263                maxwidth=scl * button_width * 0.7,
264                color=clr,
265            )
266
267        scl = 0.5 if self._playlist_select_context is None else 0.68
268        hoffs += 440 if self._playlist_select_context is None else 216
269        v += 180 if self._playlist_select_context is None else -68
270
271        self._teams_button = btn = bui.buttonwidget(
272            parent=self._root_widget,
273            position=(
274                hoffs,
275                v + (scl * 15 if self._playlist_select_context is None else 0),
276            ),
277            size=(
278                scl * button_width,
279                scl * (300 if self._playlist_select_context is None else 360),
280            ),
281            extra_touch_border_scale=0.1,
282            autoselect=True,
283            label='',
284            button_type='square',
285            text_scale=1.13,
286            on_activate_call=self._team_tourney,
287        )
288
289        bui.widget(
290            edit=btn,
291            up_widget=bui.get_special_widget('get_tokens_button'),
292            right_widget=bui.get_special_widget('squad_button'),
293        )
294
295        xxx = -14
296        self._draw_dude(
297            2,
298            btn,
299            hoffs,
300            v,
301            scl,
302            position=(xxx + 148, 30),
303            color=(0.2, 0.4, 1.0),
304        )
305        self._draw_dude(
306            3,
307            btn,
308            hoffs,
309            v,
310            scl,
311            position=(xxx + 181, 53),
312            color=(0.3, 0.4, 1.0),
313        )
314        self._draw_dude(
315            1,
316            btn,
317            hoffs,
318            v,
319            scl,
320            position=(xxx + 216, 33),
321            color=(0.3, 0.5, 1.0),
322        )
323        self._draw_dude(
324            0,
325            btn,
326            hoffs,
327            v,
328            scl,
329            position=(xxx + 245, 57),
330            color=(0.3, 0.5, 1.0),
331        )
332
333        xxx = 155
334        self._draw_dude(
335            0,
336            btn,
337            hoffs,
338            v,
339            scl,
340            position=(xxx + 151, 30),
341            color=(1.0, 0.5, 0.4),
342        )
343        self._draw_dude(
344            1,
345            btn,
346            hoffs,
347            v,
348            scl,
349            position=(xxx + 189, 53),
350            color=(1.0, 0.58, 0.58),
351        )
352        self._draw_dude(
353            3,
354            btn,
355            hoffs,
356            v,
357            scl,
358            position=(xxx + 223, 27),
359            color=(1.0, 0.5, 0.5),
360        )
361        self._draw_dude(
362            2,
363            btn,
364            hoffs,
365            v,
366            scl,
367            position=(xxx + 257, 57),
368            color=(1.0, 0.5, 0.5),
369        )
370
371        bui.textwidget(
372            parent=self._root_widget,
373            draw_controller=btn,
374            position=(hoffs + scl * (-10), v + scl * 95),
375            size=(scl * button_width, scl * 50),
376            text=bui.Lstr(
377                resource='playModes.teamsText', fallback_resource='teamsText'
378            ),
379            res_scale=1.5,
380            maxwidth=scl * button_width * 0.7,
381            h_align='center',
382            v_align='center',
383            color=(0.7, 0.9, 0.7, 1.0),
384            scale=scl * 2.3,
385        )
386        bui.textwidget(
387            parent=self._root_widget,
388            draw_controller=btn,
389            position=(hoffs + scl * (-10), v + (scl * 54)),
390            size=(scl * button_width, scl * 30),
391            text=bui.Lstr(resource=f'{self._r}.twoToEightPlayersText'),
392            h_align='center',
393            v_align='center',
394            res_scale=1.5,
395            scale=0.9 * scl,
396            flatness=1.0,
397            maxwidth=scl * button_width * 0.7,
398            color=clr,
399        )
400
401        hoffs += 0 if self._playlist_select_context is None else 300
402        v -= 155 if self._playlist_select_context is None else 0
403        self._free_for_all_button = btn = bui.buttonwidget(
404            parent=self._root_widget,
405            position=(
406                hoffs,
407                v + (scl * 15 if self._playlist_select_context is None else 0),
408            ),
409            size=(
410                scl * button_width,
411                scl * (300 if self._playlist_select_context is None else 360),
412            ),
413            extra_touch_border_scale=0.1,
414            autoselect=True,
415            label='',
416            button_type='square',
417            text_scale=1.13,
418            on_activate_call=self._free_for_all,
419        )
420
421        xxx = -5
422        self._draw_dude(
423            0,
424            btn,
425            hoffs,
426            v,
427            scl,
428            position=(xxx + 140, 30),
429            color=(0.4, 1.0, 0.4),
430        )
431        self._draw_dude(
432            3,
433            btn,
434            hoffs,
435            v,
436            scl,
437            position=(xxx + 185, 53),
438            color=(1.0, 0.4, 0.5),
439        )
440        self._draw_dude(
441            1,
442            btn,
443            hoffs,
444            v,
445            scl,
446            position=(xxx + 220, 27),
447            color=(0.4, 0.5, 1.0),
448        )
449        self._draw_dude(
450            2,
451            btn,
452            hoffs,
453            v,
454            scl,
455            position=(xxx + 255, 57),
456            color=(0.5, 1.0, 0.4),
457        )
458        xxx = 140
459        self._draw_dude(
460            2,
461            btn,
462            hoffs,
463            v,
464            scl,
465            position=(xxx + 148, 30),
466            color=(1.0, 0.9, 0.4),
467        )
468        self._draw_dude(
469            0,
470            btn,
471            hoffs,
472            v,
473            scl,
474            position=(xxx + 182, 53),
475            color=(0.7, 1.0, 0.5),
476        )
477        self._draw_dude(
478            3,
479            btn,
480            hoffs,
481            v,
482            scl,
483            position=(xxx + 233, 27),
484            color=(0.7, 0.5, 0.9),
485        )
486        self._draw_dude(
487            1,
488            btn,
489            hoffs,
490            v,
491            scl,
492            position=(xxx + 266, 53),
493            color=(0.4, 0.5, 0.8),
494        )
495        bui.textwidget(
496            parent=self._root_widget,
497            draw_controller=btn,
498            position=(hoffs + scl * (-10), v + scl * 95),
499            size=(scl * button_width, scl * 50),
500            text=bui.Lstr(
501                resource='playModes.freeForAllText',
502                fallback_resource='freeForAllText',
503            ),
504            maxwidth=scl * button_width * 0.7,
505            h_align='center',
506            v_align='center',
507            color=(0.7, 0.9, 0.7, 1.0),
508            scale=scl * 1.9,
509        )
510        bui.textwidget(
511            parent=self._root_widget,
512            draw_controller=btn,
513            position=(hoffs + scl * (-10), v + (scl * 54)),
514            size=(scl * button_width, scl * 30),
515            text=bui.Lstr(resource=f'{self._r}.twoToEightPlayersText'),
516            h_align='center',
517            v_align='center',
518            scale=0.9 * scl,
519            flatness=1.0,
520            maxwidth=scl * button_width * 0.7,
521            color=clr,
522        )
523
524        if uiscale is bui.UIScale.SMALL:
525            bui.containerwidget(
526                edit=self._root_widget,
527                selected_child=(
528                    self._coop_button
529                    if self._playlist_select_context is None
530                    else self._teams_button
531                ),
532            )
533        else:
534            bui.containerwidget(
535                edit=self._root_widget,
536                selected_child=(
537                    self._coop_button
538                    if self._playlist_select_context is None
539                    else self._teams_button
540                ),
541            )
542
543        self._restore_state()
544
545    @override
546    def get_main_window_state(self) -> bui.MainWindowState:
547        # Support recreating our window for back/refresh purposes.
548        cls = type(self)
549
550        # Pull any values out of self here; if we do it in the lambda
551        # we'll keep our window alive inadvertantly.
552        playlist_select_context = self._playlist_select_context
553        return bui.BasicMainWindowState(
554            create_call=lambda transition, origin_widget: cls(
555                transition=transition,
556                origin_widget=origin_widget,
557                playlist_select_context=playlist_select_context,
558            )
559        )
560
561    @override
562    def on_main_window_close(self) -> None:
563        self._save_state()
564
565    @staticmethod
566    def _preload_modules() -> None:
567        """Preload modules we use; avoids hitches (called in bg thread)."""
568        import bauiv1lib.mainmenu as _unused1
569        import bauiv1lib.account as _unused2
570        import bauiv1lib.coop.browser as _unused3
571        import bauiv1lib.playlist.browser as _unused4
572
573    def _coop(self) -> None:
574        # pylint: disable=cyclic-import
575        from bauiv1lib.account import show_sign_in_prompt
576        from bauiv1lib.coop.browser import CoopBrowserWindow
577
578        # no-op if we're not currently in control.
579        if not self.main_window_has_control():
580            return
581
582        plus = bui.app.plus
583        assert plus is not None
584
585        if plus.get_v1_account_state() != 'signed_in':
586            show_sign_in_prompt()
587            return
588
589        self.main_window_replace(
590            CoopBrowserWindow(origin_widget=self._coop_button)
591        )
592
593    def _team_tourney(self) -> None:
594        # pylint: disable=cyclic-import
595        from bauiv1lib.playlist.browser import PlaylistBrowserWindow
596
597        # no-op if we're not currently in control.
598        if not self.main_window_has_control():
599            return
600
601        self.main_window_replace(
602            PlaylistBrowserWindow(
603                origin_widget=self._teams_button,
604                sessiontype=bs.DualTeamSession,
605                playlist_select_context=self._playlist_select_context,
606            )
607        )
608
609    def _free_for_all(self) -> None:
610        # pylint: disable=cyclic-import
611        from bauiv1lib.playlist.browser import PlaylistBrowserWindow
612
613        # no-op if we're not currently in control.
614        if not self.main_window_has_control():
615            return
616
617        self.main_window_replace(
618            PlaylistBrowserWindow(
619                origin_widget=self._free_for_all_button,
620                sessiontype=bs.FreeForAllSession,
621                playlist_select_context=self._playlist_select_context,
622            )
623        )
624
625    def _draw_dude(
626        self,
627        i: int,
628        btn: bui.Widget,
629        hoffs: float,
630        v: float,
631        scl: float,
632        position: tuple[float, float],
633        color: tuple[float, float, float],
634    ) -> None:
635        # pylint: disable=too-many-positional-arguments
636        h_extra = -100
637        v_extra = 130
638        eye_color = (
639            0.7 * 1.0 + 0.3 * color[0],
640            0.7 * 1.0 + 0.3 * color[1],
641            0.7 * 1.0 + 0.3 * color[2],
642        )
643        if i == 0:
644            bui.imagewidget(
645                parent=self._root_widget,
646                draw_controller=btn,
647                position=(
648                    hoffs + scl * (h_extra + position[0]),
649                    v + scl * (v_extra + position[1]),
650                ),
651                size=(scl * 60, scl * 80),
652                color=color,
653                texture=self._lineup_tex,
654                mesh_transparent=self._lineup_1_transparent_mesh,
655            )
656            bui.imagewidget(
657                parent=self._root_widget,
658                draw_controller=btn,
659                position=(
660                    hoffs + scl * (h_extra + position[0] + 12),
661                    v + scl * (v_extra + position[1] + 53),
662                ),
663                size=(scl * 36, scl * 18),
664                texture=self._lineup_tex,
665                color=eye_color,
666                mesh_transparent=self._eyes_mesh,
667            )
668        elif i == 1:
669            bui.imagewidget(
670                parent=self._root_widget,
671                draw_controller=btn,
672                position=(
673                    hoffs + scl * (h_extra + position[0]),
674                    v + scl * (v_extra + position[1]),
675                ),
676                size=(scl * 45, scl * 90),
677                color=color,
678                texture=self._lineup_tex,
679                mesh_transparent=self._lineup_2_transparent_mesh,
680            )
681            bui.imagewidget(
682                parent=self._root_widget,
683                draw_controller=btn,
684                position=(
685                    hoffs + scl * (h_extra + position[0] + 5),
686                    v + scl * (v_extra + position[1] + 67),
687                ),
688                size=(scl * 32, scl * 16),
689                texture=self._lineup_tex,
690                color=eye_color,
691                mesh_transparent=self._eyes_mesh,
692            )
693        elif i == 2:
694            bui.imagewidget(
695                parent=self._root_widget,
696                draw_controller=btn,
697                position=(
698                    hoffs + scl * (h_extra + position[0]),
699                    v + scl * (v_extra + position[1]),
700                ),
701                size=(scl * 45, scl * 90),
702                color=color,
703                texture=self._lineup_tex,
704                mesh_transparent=self._lineup_3_transparent_mesh,
705            )
706            bui.imagewidget(
707                parent=self._root_widget,
708                draw_controller=btn,
709                position=(
710                    hoffs + scl * (h_extra + position[0] + 5),
711                    v + scl * (v_extra + position[1] + 59),
712                ),
713                size=(scl * 34, scl * 17),
714                texture=self._lineup_tex,
715                color=eye_color,
716                mesh_transparent=self._eyes_mesh,
717            )
718        elif i == 3:
719            bui.imagewidget(
720                parent=self._root_widget,
721                draw_controller=btn,
722                position=(
723                    hoffs + scl * (h_extra + position[0]),
724                    v + scl * (v_extra + position[1]),
725                ),
726                size=(scl * 48, scl * 96),
727                color=color,
728                texture=self._lineup_tex,
729                mesh_transparent=self._lineup_4_transparent_mesh,
730            )
731            bui.imagewidget(
732                parent=self._root_widget,
733                draw_controller=btn,
734                position=(
735                    hoffs + scl * (h_extra + position[0] + 2),
736                    v + scl * (v_extra + position[1] + 62),
737                ),
738                size=(scl * 38, scl * 19),
739                texture=self._lineup_tex,
740                color=eye_color,
741                mesh_transparent=self._eyes_mesh,
742            )
743
744    def _save_state(self) -> None:
745        try:
746            sel = self._root_widget.get_selected_child()
747            if sel == self._teams_button:
748                sel_name = 'Team Games'
749            elif self._coop_button is not None and sel == self._coop_button:
750                sel_name = 'Co-op Games'
751            elif sel == self._free_for_all_button:
752                sel_name = 'Free-for-All Games'
753            elif sel == self._back_button:
754                sel_name = 'Back'
755            else:
756                raise ValueError(f'unrecognized selection {sel}')
757            assert bui.app.classic is not None
758            bui.app.ui_v1.window_states[type(self)] = sel_name
759        except Exception:
760            logging.exception('Error saving state for %s.', self)
761
762    def _restore_state(self) -> None:
763        try:
764            assert bui.app.classic is not None
765            sel_name = bui.app.ui_v1.window_states.get(type(self))
766            if sel_name == 'Team Games':
767                sel = self._teams_button
768            elif sel_name == 'Co-op Games' and self._coop_button is not None:
769                sel = self._coop_button
770            elif sel_name == 'Free-for-All Games':
771                sel = self._free_for_all_button
772            elif sel_name == 'Back' and self._back_button is not None:
773                sel = self._back_button
774            else:
775                sel = (
776                    self._coop_button
777                    if self._coop_button is not None
778                    else self._teams_button
779                )
780            bui.containerwidget(edit=self._root_widget, selected_child=sel)
781        except Exception:
782            logging.exception('Error restoring state for %s.', self)
class PlaylistSelectContext:
18class PlaylistSelectContext:
19    """For using PlayWindow to select a playlist instead of running game."""
20
21    back_state: MainWindowState | None = None

For using PlayWindow to select a playlist instead of running game.

back_state: bauiv1.MainWindowState | None = None
class PlayWindow(bauiv1._uitypes.MainWindow):
 24class PlayWindow(bui.MainWindow):
 25    """Window for selecting overall play type."""
 26
 27    def __init__(
 28        self,
 29        transition: str | None = 'in_right',
 30        origin_widget: bui.Widget | None = None,
 31        playlist_select_context: PlaylistSelectContext | None = None,
 32    ):
 33        # pylint: disable=too-many-statements
 34        # pylint: disable=too-many-locals
 35
 36        # Preload some modules we use in a background thread so we won't
 37        # have a visual hitch when the user taps them.
 38        bui.app.threadpool_submit_no_wait(self._preload_modules)
 39
 40        classic = bui.app.classic
 41        assert classic is not None
 42
 43        self._playlist_select_context = playlist_select_context
 44
 45        uiscale = bui.app.ui_v1.uiscale
 46        width = 1100 if uiscale is bui.UIScale.SMALL else 800
 47        x_offs = 150 if uiscale is bui.UIScale.SMALL else 0
 48        y_offs = -60 if uiscale is bui.UIScale.SMALL else 0
 49        height = 650 if uiscale is bui.UIScale.SMALL else 550
 50        button_width = 400
 51
 52        if origin_widget is not None:
 53
 54            # Need to store this ourself since we can function as a
 55            # non-main window.
 56            self._transition_out = 'out_scale'
 57        else:
 58            self._transition_out = 'out_right'
 59
 60        self._r = 'playWindow'
 61
 62        super().__init__(
 63            root_widget=bui.containerwidget(
 64                size=(width, height),
 65                toolbar_visibility=(
 66                    'menu_full'
 67                    if playlist_select_context is None
 68                    else 'menu_minimal'
 69                ),
 70                scale=(
 71                    1.35
 72                    if uiscale is bui.UIScale.SMALL
 73                    else 0.9 if uiscale is bui.UIScale.MEDIUM else 0.8
 74                ),
 75                stack_offset=(
 76                    (0, 20) if uiscale is bui.UIScale.SMALL else (0, 0)
 77                ),
 78            ),
 79            transition=transition,
 80            origin_widget=origin_widget,
 81        )
 82
 83        self._back_button: bui.Widget | None
 84        if uiscale is bui.UIScale.SMALL:
 85            self._back_button = None
 86            bui.containerwidget(
 87                edit=self._root_widget,
 88                on_cancel_call=self.main_window_back,
 89            )
 90        else:
 91            self._back_button = bui.buttonwidget(
 92                parent=self._root_widget,
 93                position=(55 + x_offs, height - 132 + y_offs),
 94                size=(60, 60),
 95                scale=1.1,
 96                text_res_scale=1.5,
 97                text_scale=1.2,
 98                autoselect=True,
 99                label=bui.charstr(bui.SpecialChar.BACK),
100                button_type='backSmall',
101                on_activate_call=self.main_window_back,
102            )
103            bui.containerwidget(
104                edit=self._root_widget, cancel_button=self._back_button
105            )
106
107        txt = bui.textwidget(
108            parent=self._root_widget,
109            position=(width * 0.5, height - 101 + y_offs),
110            # position=(width * 0.5, height -
111            #           (101 if main_menu else 61)),
112            size=(0, 0),
113            text=bui.Lstr(
114                resource=(
115                    (f'{self._r}.titleText')
116                    if self._playlist_select_context is None
117                    else 'playlistsText'
118                )
119            ),
120            scale=1.7,
121            res_scale=2.0,
122            maxwidth=400,
123            color=bui.app.ui_v1.heading_color,
124            h_align='center',
125            v_align='center',
126        )
127
128        if uiscale is bui.UIScale.SMALL:
129            bui.textwidget(edit=txt, text='')
130
131        v = (
132            height
133            - (110 if self._playlist_select_context is None else 90)
134            + y_offs
135        )
136        v -= 100
137        clr = (0.6, 0.7, 0.6, 1.0)
138        v -= 280 if self._playlist_select_context is None else 180
139        v += 30 if uiscale is bui.UIScale.SMALL else 0
140        hoffs = (
141            x_offs + 80
142            if self._playlist_select_context is None
143            else x_offs - 100
144        )
145        scl = 1.13 if self._playlist_select_context is None else 0.68
146
147        self._lineup_tex = bui.gettexture('playerLineup')
148        angry_computer_transparent_mesh = bui.getmesh(
149            'angryComputerTransparent'
150        )
151        self._lineup_1_transparent_mesh = bui.getmesh(
152            'playerLineup1Transparent'
153        )
154        self._lineup_2_transparent_mesh = bui.getmesh(
155            'playerLineup2Transparent'
156        )
157        self._lineup_3_transparent_mesh = bui.getmesh(
158            'playerLineup3Transparent'
159        )
160        self._lineup_4_transparent_mesh = bui.getmesh(
161            'playerLineup4Transparent'
162        )
163        self._eyes_mesh = bui.getmesh('plasticEyesTransparent')
164
165        self._coop_button: bui.Widget | None = None
166
167        # Only show coop button in regular variant.
168        if self._playlist_select_context is None:
169            self._coop_button = btn = bui.buttonwidget(
170                parent=self._root_widget,
171                position=(hoffs, v + (scl * 15)),
172                size=(
173                    scl * button_width,
174                    scl * 300,
175                ),
176                extra_touch_border_scale=0.1,
177                autoselect=True,
178                label='',
179                button_type='square',
180                text_scale=1.13,
181                on_activate_call=self._coop,
182            )
183
184            if uiscale is bui.UIScale.SMALL:
185                bui.widget(
186                    edit=btn,
187                    left_widget=bui.get_special_widget('back_button'),
188                )
189                bui.widget(
190                    edit=btn,
191                    up_widget=bui.get_special_widget('account_button'),
192                )
193                bui.widget(
194                    edit=btn,
195                    down_widget=bui.get_special_widget('settings_button'),
196                )
197
198            self._draw_dude(
199                0,
200                btn,
201                hoffs,
202                v,
203                scl,
204                position=(140, 30),
205                color=(0.72, 0.4, 1.0),
206            )
207            self._draw_dude(
208                1,
209                btn,
210                hoffs,
211                v,
212                scl,
213                position=(185, 53),
214                color=(0.71, 0.5, 1.0),
215            )
216            self._draw_dude(
217                2,
218                btn,
219                hoffs,
220                v,
221                scl,
222                position=(220, 27),
223                color=(0.67, 0.44, 1.0),
224            )
225            self._draw_dude(
226                3, btn, hoffs, v, scl, position=(255, 57), color=(0.7, 0.3, 1.0)
227            )
228            bui.imagewidget(
229                parent=self._root_widget,
230                draw_controller=btn,
231                position=(hoffs + scl * 230, v + scl * 153),
232                size=(scl * 115, scl * 115),
233                texture=self._lineup_tex,
234                mesh_transparent=angry_computer_transparent_mesh,
235            )
236
237            bui.textwidget(
238                parent=self._root_widget,
239                draw_controller=btn,
240                position=(hoffs + scl * (-10), v + scl * 95),
241                size=(scl * button_width, scl * 50),
242                text=bui.Lstr(
243                    resource='playModes.singlePlayerCoopText',
244                    fallback_resource='playModes.coopText',
245                ),
246                maxwidth=scl * button_width * 0.7,
247                res_scale=1.5,
248                h_align='center',
249                v_align='center',
250                color=(0.7, 0.9, 0.7, 1.0),
251                scale=scl * 2.3,
252            )
253
254            bui.textwidget(
255                parent=self._root_widget,
256                draw_controller=btn,
257                position=(hoffs + scl * (-10), v + (scl * 54)),
258                size=(scl * button_width, scl * 30),
259                text=bui.Lstr(resource=f'{self._r}.oneToFourPlayersText'),
260                h_align='center',
261                v_align='center',
262                scale=0.83 * scl,
263                flatness=1.0,
264                maxwidth=scl * button_width * 0.7,
265                color=clr,
266            )
267
268        scl = 0.5 if self._playlist_select_context is None else 0.68
269        hoffs += 440 if self._playlist_select_context is None else 216
270        v += 180 if self._playlist_select_context is None else -68
271
272        self._teams_button = btn = bui.buttonwidget(
273            parent=self._root_widget,
274            position=(
275                hoffs,
276                v + (scl * 15 if self._playlist_select_context is None else 0),
277            ),
278            size=(
279                scl * button_width,
280                scl * (300 if self._playlist_select_context is None else 360),
281            ),
282            extra_touch_border_scale=0.1,
283            autoselect=True,
284            label='',
285            button_type='square',
286            text_scale=1.13,
287            on_activate_call=self._team_tourney,
288        )
289
290        bui.widget(
291            edit=btn,
292            up_widget=bui.get_special_widget('get_tokens_button'),
293            right_widget=bui.get_special_widget('squad_button'),
294        )
295
296        xxx = -14
297        self._draw_dude(
298            2,
299            btn,
300            hoffs,
301            v,
302            scl,
303            position=(xxx + 148, 30),
304            color=(0.2, 0.4, 1.0),
305        )
306        self._draw_dude(
307            3,
308            btn,
309            hoffs,
310            v,
311            scl,
312            position=(xxx + 181, 53),
313            color=(0.3, 0.4, 1.0),
314        )
315        self._draw_dude(
316            1,
317            btn,
318            hoffs,
319            v,
320            scl,
321            position=(xxx + 216, 33),
322            color=(0.3, 0.5, 1.0),
323        )
324        self._draw_dude(
325            0,
326            btn,
327            hoffs,
328            v,
329            scl,
330            position=(xxx + 245, 57),
331            color=(0.3, 0.5, 1.0),
332        )
333
334        xxx = 155
335        self._draw_dude(
336            0,
337            btn,
338            hoffs,
339            v,
340            scl,
341            position=(xxx + 151, 30),
342            color=(1.0, 0.5, 0.4),
343        )
344        self._draw_dude(
345            1,
346            btn,
347            hoffs,
348            v,
349            scl,
350            position=(xxx + 189, 53),
351            color=(1.0, 0.58, 0.58),
352        )
353        self._draw_dude(
354            3,
355            btn,
356            hoffs,
357            v,
358            scl,
359            position=(xxx + 223, 27),
360            color=(1.0, 0.5, 0.5),
361        )
362        self._draw_dude(
363            2,
364            btn,
365            hoffs,
366            v,
367            scl,
368            position=(xxx + 257, 57),
369            color=(1.0, 0.5, 0.5),
370        )
371
372        bui.textwidget(
373            parent=self._root_widget,
374            draw_controller=btn,
375            position=(hoffs + scl * (-10), v + scl * 95),
376            size=(scl * button_width, scl * 50),
377            text=bui.Lstr(
378                resource='playModes.teamsText', fallback_resource='teamsText'
379            ),
380            res_scale=1.5,
381            maxwidth=scl * button_width * 0.7,
382            h_align='center',
383            v_align='center',
384            color=(0.7, 0.9, 0.7, 1.0),
385            scale=scl * 2.3,
386        )
387        bui.textwidget(
388            parent=self._root_widget,
389            draw_controller=btn,
390            position=(hoffs + scl * (-10), v + (scl * 54)),
391            size=(scl * button_width, scl * 30),
392            text=bui.Lstr(resource=f'{self._r}.twoToEightPlayersText'),
393            h_align='center',
394            v_align='center',
395            res_scale=1.5,
396            scale=0.9 * scl,
397            flatness=1.0,
398            maxwidth=scl * button_width * 0.7,
399            color=clr,
400        )
401
402        hoffs += 0 if self._playlist_select_context is None else 300
403        v -= 155 if self._playlist_select_context is None else 0
404        self._free_for_all_button = btn = bui.buttonwidget(
405            parent=self._root_widget,
406            position=(
407                hoffs,
408                v + (scl * 15 if self._playlist_select_context is None else 0),
409            ),
410            size=(
411                scl * button_width,
412                scl * (300 if self._playlist_select_context is None else 360),
413            ),
414            extra_touch_border_scale=0.1,
415            autoselect=True,
416            label='',
417            button_type='square',
418            text_scale=1.13,
419            on_activate_call=self._free_for_all,
420        )
421
422        xxx = -5
423        self._draw_dude(
424            0,
425            btn,
426            hoffs,
427            v,
428            scl,
429            position=(xxx + 140, 30),
430            color=(0.4, 1.0, 0.4),
431        )
432        self._draw_dude(
433            3,
434            btn,
435            hoffs,
436            v,
437            scl,
438            position=(xxx + 185, 53),
439            color=(1.0, 0.4, 0.5),
440        )
441        self._draw_dude(
442            1,
443            btn,
444            hoffs,
445            v,
446            scl,
447            position=(xxx + 220, 27),
448            color=(0.4, 0.5, 1.0),
449        )
450        self._draw_dude(
451            2,
452            btn,
453            hoffs,
454            v,
455            scl,
456            position=(xxx + 255, 57),
457            color=(0.5, 1.0, 0.4),
458        )
459        xxx = 140
460        self._draw_dude(
461            2,
462            btn,
463            hoffs,
464            v,
465            scl,
466            position=(xxx + 148, 30),
467            color=(1.0, 0.9, 0.4),
468        )
469        self._draw_dude(
470            0,
471            btn,
472            hoffs,
473            v,
474            scl,
475            position=(xxx + 182, 53),
476            color=(0.7, 1.0, 0.5),
477        )
478        self._draw_dude(
479            3,
480            btn,
481            hoffs,
482            v,
483            scl,
484            position=(xxx + 233, 27),
485            color=(0.7, 0.5, 0.9),
486        )
487        self._draw_dude(
488            1,
489            btn,
490            hoffs,
491            v,
492            scl,
493            position=(xxx + 266, 53),
494            color=(0.4, 0.5, 0.8),
495        )
496        bui.textwidget(
497            parent=self._root_widget,
498            draw_controller=btn,
499            position=(hoffs + scl * (-10), v + scl * 95),
500            size=(scl * button_width, scl * 50),
501            text=bui.Lstr(
502                resource='playModes.freeForAllText',
503                fallback_resource='freeForAllText',
504            ),
505            maxwidth=scl * button_width * 0.7,
506            h_align='center',
507            v_align='center',
508            color=(0.7, 0.9, 0.7, 1.0),
509            scale=scl * 1.9,
510        )
511        bui.textwidget(
512            parent=self._root_widget,
513            draw_controller=btn,
514            position=(hoffs + scl * (-10), v + (scl * 54)),
515            size=(scl * button_width, scl * 30),
516            text=bui.Lstr(resource=f'{self._r}.twoToEightPlayersText'),
517            h_align='center',
518            v_align='center',
519            scale=0.9 * scl,
520            flatness=1.0,
521            maxwidth=scl * button_width * 0.7,
522            color=clr,
523        )
524
525        if uiscale is bui.UIScale.SMALL:
526            bui.containerwidget(
527                edit=self._root_widget,
528                selected_child=(
529                    self._coop_button
530                    if self._playlist_select_context is None
531                    else self._teams_button
532                ),
533            )
534        else:
535            bui.containerwidget(
536                edit=self._root_widget,
537                selected_child=(
538                    self._coop_button
539                    if self._playlist_select_context is None
540                    else self._teams_button
541                ),
542            )
543
544        self._restore_state()
545
546    @override
547    def get_main_window_state(self) -> bui.MainWindowState:
548        # Support recreating our window for back/refresh purposes.
549        cls = type(self)
550
551        # Pull any values out of self here; if we do it in the lambda
552        # we'll keep our window alive inadvertantly.
553        playlist_select_context = self._playlist_select_context
554        return bui.BasicMainWindowState(
555            create_call=lambda transition, origin_widget: cls(
556                transition=transition,
557                origin_widget=origin_widget,
558                playlist_select_context=playlist_select_context,
559            )
560        )
561
562    @override
563    def on_main_window_close(self) -> None:
564        self._save_state()
565
566    @staticmethod
567    def _preload_modules() -> None:
568        """Preload modules we use; avoids hitches (called in bg thread)."""
569        import bauiv1lib.mainmenu as _unused1
570        import bauiv1lib.account as _unused2
571        import bauiv1lib.coop.browser as _unused3
572        import bauiv1lib.playlist.browser as _unused4
573
574    def _coop(self) -> None:
575        # pylint: disable=cyclic-import
576        from bauiv1lib.account import show_sign_in_prompt
577        from bauiv1lib.coop.browser import CoopBrowserWindow
578
579        # no-op if we're not currently in control.
580        if not self.main_window_has_control():
581            return
582
583        plus = bui.app.plus
584        assert plus is not None
585
586        if plus.get_v1_account_state() != 'signed_in':
587            show_sign_in_prompt()
588            return
589
590        self.main_window_replace(
591            CoopBrowserWindow(origin_widget=self._coop_button)
592        )
593
594    def _team_tourney(self) -> None:
595        # pylint: disable=cyclic-import
596        from bauiv1lib.playlist.browser import PlaylistBrowserWindow
597
598        # no-op if we're not currently in control.
599        if not self.main_window_has_control():
600            return
601
602        self.main_window_replace(
603            PlaylistBrowserWindow(
604                origin_widget=self._teams_button,
605                sessiontype=bs.DualTeamSession,
606                playlist_select_context=self._playlist_select_context,
607            )
608        )
609
610    def _free_for_all(self) -> None:
611        # pylint: disable=cyclic-import
612        from bauiv1lib.playlist.browser import PlaylistBrowserWindow
613
614        # no-op if we're not currently in control.
615        if not self.main_window_has_control():
616            return
617
618        self.main_window_replace(
619            PlaylistBrowserWindow(
620                origin_widget=self._free_for_all_button,
621                sessiontype=bs.FreeForAllSession,
622                playlist_select_context=self._playlist_select_context,
623            )
624        )
625
626    def _draw_dude(
627        self,
628        i: int,
629        btn: bui.Widget,
630        hoffs: float,
631        v: float,
632        scl: float,
633        position: tuple[float, float],
634        color: tuple[float, float, float],
635    ) -> None:
636        # pylint: disable=too-many-positional-arguments
637        h_extra = -100
638        v_extra = 130
639        eye_color = (
640            0.7 * 1.0 + 0.3 * color[0],
641            0.7 * 1.0 + 0.3 * color[1],
642            0.7 * 1.0 + 0.3 * color[2],
643        )
644        if i == 0:
645            bui.imagewidget(
646                parent=self._root_widget,
647                draw_controller=btn,
648                position=(
649                    hoffs + scl * (h_extra + position[0]),
650                    v + scl * (v_extra + position[1]),
651                ),
652                size=(scl * 60, scl * 80),
653                color=color,
654                texture=self._lineup_tex,
655                mesh_transparent=self._lineup_1_transparent_mesh,
656            )
657            bui.imagewidget(
658                parent=self._root_widget,
659                draw_controller=btn,
660                position=(
661                    hoffs + scl * (h_extra + position[0] + 12),
662                    v + scl * (v_extra + position[1] + 53),
663                ),
664                size=(scl * 36, scl * 18),
665                texture=self._lineup_tex,
666                color=eye_color,
667                mesh_transparent=self._eyes_mesh,
668            )
669        elif i == 1:
670            bui.imagewidget(
671                parent=self._root_widget,
672                draw_controller=btn,
673                position=(
674                    hoffs + scl * (h_extra + position[0]),
675                    v + scl * (v_extra + position[1]),
676                ),
677                size=(scl * 45, scl * 90),
678                color=color,
679                texture=self._lineup_tex,
680                mesh_transparent=self._lineup_2_transparent_mesh,
681            )
682            bui.imagewidget(
683                parent=self._root_widget,
684                draw_controller=btn,
685                position=(
686                    hoffs + scl * (h_extra + position[0] + 5),
687                    v + scl * (v_extra + position[1] + 67),
688                ),
689                size=(scl * 32, scl * 16),
690                texture=self._lineup_tex,
691                color=eye_color,
692                mesh_transparent=self._eyes_mesh,
693            )
694        elif i == 2:
695            bui.imagewidget(
696                parent=self._root_widget,
697                draw_controller=btn,
698                position=(
699                    hoffs + scl * (h_extra + position[0]),
700                    v + scl * (v_extra + position[1]),
701                ),
702                size=(scl * 45, scl * 90),
703                color=color,
704                texture=self._lineup_tex,
705                mesh_transparent=self._lineup_3_transparent_mesh,
706            )
707            bui.imagewidget(
708                parent=self._root_widget,
709                draw_controller=btn,
710                position=(
711                    hoffs + scl * (h_extra + position[0] + 5),
712                    v + scl * (v_extra + position[1] + 59),
713                ),
714                size=(scl * 34, scl * 17),
715                texture=self._lineup_tex,
716                color=eye_color,
717                mesh_transparent=self._eyes_mesh,
718            )
719        elif i == 3:
720            bui.imagewidget(
721                parent=self._root_widget,
722                draw_controller=btn,
723                position=(
724                    hoffs + scl * (h_extra + position[0]),
725                    v + scl * (v_extra + position[1]),
726                ),
727                size=(scl * 48, scl * 96),
728                color=color,
729                texture=self._lineup_tex,
730                mesh_transparent=self._lineup_4_transparent_mesh,
731            )
732            bui.imagewidget(
733                parent=self._root_widget,
734                draw_controller=btn,
735                position=(
736                    hoffs + scl * (h_extra + position[0] + 2),
737                    v + scl * (v_extra + position[1] + 62),
738                ),
739                size=(scl * 38, scl * 19),
740                texture=self._lineup_tex,
741                color=eye_color,
742                mesh_transparent=self._eyes_mesh,
743            )
744
745    def _save_state(self) -> None:
746        try:
747            sel = self._root_widget.get_selected_child()
748            if sel == self._teams_button:
749                sel_name = 'Team Games'
750            elif self._coop_button is not None and sel == self._coop_button:
751                sel_name = 'Co-op Games'
752            elif sel == self._free_for_all_button:
753                sel_name = 'Free-for-All Games'
754            elif sel == self._back_button:
755                sel_name = 'Back'
756            else:
757                raise ValueError(f'unrecognized selection {sel}')
758            assert bui.app.classic is not None
759            bui.app.ui_v1.window_states[type(self)] = sel_name
760        except Exception:
761            logging.exception('Error saving state for %s.', self)
762
763    def _restore_state(self) -> None:
764        try:
765            assert bui.app.classic is not None
766            sel_name = bui.app.ui_v1.window_states.get(type(self))
767            if sel_name == 'Team Games':
768                sel = self._teams_button
769            elif sel_name == 'Co-op Games' and self._coop_button is not None:
770                sel = self._coop_button
771            elif sel_name == 'Free-for-All Games':
772                sel = self._free_for_all_button
773            elif sel_name == 'Back' and self._back_button is not None:
774                sel = self._back_button
775            else:
776                sel = (
777                    self._coop_button
778                    if self._coop_button is not None
779                    else self._teams_button
780                )
781            bui.containerwidget(edit=self._root_widget, selected_child=sel)
782        except Exception:
783            logging.exception('Error restoring state for %s.', self)

Window for selecting overall play type.

PlayWindow( transition: str | None = 'in_right', origin_widget: _bauiv1.Widget | None = None, playlist_select_context: PlaylistSelectContext | None = None)
 27    def __init__(
 28        self,
 29        transition: str | None = 'in_right',
 30        origin_widget: bui.Widget | None = None,
 31        playlist_select_context: PlaylistSelectContext | None = None,
 32    ):
 33        # pylint: disable=too-many-statements
 34        # pylint: disable=too-many-locals
 35
 36        # Preload some modules we use in a background thread so we won't
 37        # have a visual hitch when the user taps them.
 38        bui.app.threadpool_submit_no_wait(self._preload_modules)
 39
 40        classic = bui.app.classic
 41        assert classic is not None
 42
 43        self._playlist_select_context = playlist_select_context
 44
 45        uiscale = bui.app.ui_v1.uiscale
 46        width = 1100 if uiscale is bui.UIScale.SMALL else 800
 47        x_offs = 150 if uiscale is bui.UIScale.SMALL else 0
 48        y_offs = -60 if uiscale is bui.UIScale.SMALL else 0
 49        height = 650 if uiscale is bui.UIScale.SMALL else 550
 50        button_width = 400
 51
 52        if origin_widget is not None:
 53
 54            # Need to store this ourself since we can function as a
 55            # non-main window.
 56            self._transition_out = 'out_scale'
 57        else:
 58            self._transition_out = 'out_right'
 59
 60        self._r = 'playWindow'
 61
 62        super().__init__(
 63            root_widget=bui.containerwidget(
 64                size=(width, height),
 65                toolbar_visibility=(
 66                    'menu_full'
 67                    if playlist_select_context is None
 68                    else 'menu_minimal'
 69                ),
 70                scale=(
 71                    1.35
 72                    if uiscale is bui.UIScale.SMALL
 73                    else 0.9 if uiscale is bui.UIScale.MEDIUM else 0.8
 74                ),
 75                stack_offset=(
 76                    (0, 20) if uiscale is bui.UIScale.SMALL else (0, 0)
 77                ),
 78            ),
 79            transition=transition,
 80            origin_widget=origin_widget,
 81        )
 82
 83        self._back_button: bui.Widget | None
 84        if uiscale is bui.UIScale.SMALL:
 85            self._back_button = None
 86            bui.containerwidget(
 87                edit=self._root_widget,
 88                on_cancel_call=self.main_window_back,
 89            )
 90        else:
 91            self._back_button = bui.buttonwidget(
 92                parent=self._root_widget,
 93                position=(55 + x_offs, height - 132 + y_offs),
 94                size=(60, 60),
 95                scale=1.1,
 96                text_res_scale=1.5,
 97                text_scale=1.2,
 98                autoselect=True,
 99                label=bui.charstr(bui.SpecialChar.BACK),
100                button_type='backSmall',
101                on_activate_call=self.main_window_back,
102            )
103            bui.containerwidget(
104                edit=self._root_widget, cancel_button=self._back_button
105            )
106
107        txt = bui.textwidget(
108            parent=self._root_widget,
109            position=(width * 0.5, height - 101 + y_offs),
110            # position=(width * 0.5, height -
111            #           (101 if main_menu else 61)),
112            size=(0, 0),
113            text=bui.Lstr(
114                resource=(
115                    (f'{self._r}.titleText')
116                    if self._playlist_select_context is None
117                    else 'playlistsText'
118                )
119            ),
120            scale=1.7,
121            res_scale=2.0,
122            maxwidth=400,
123            color=bui.app.ui_v1.heading_color,
124            h_align='center',
125            v_align='center',
126        )
127
128        if uiscale is bui.UIScale.SMALL:
129            bui.textwidget(edit=txt, text='')
130
131        v = (
132            height
133            - (110 if self._playlist_select_context is None else 90)
134            + y_offs
135        )
136        v -= 100
137        clr = (0.6, 0.7, 0.6, 1.0)
138        v -= 280 if self._playlist_select_context is None else 180
139        v += 30 if uiscale is bui.UIScale.SMALL else 0
140        hoffs = (
141            x_offs + 80
142            if self._playlist_select_context is None
143            else x_offs - 100
144        )
145        scl = 1.13 if self._playlist_select_context is None else 0.68
146
147        self._lineup_tex = bui.gettexture('playerLineup')
148        angry_computer_transparent_mesh = bui.getmesh(
149            'angryComputerTransparent'
150        )
151        self._lineup_1_transparent_mesh = bui.getmesh(
152            'playerLineup1Transparent'
153        )
154        self._lineup_2_transparent_mesh = bui.getmesh(
155            'playerLineup2Transparent'
156        )
157        self._lineup_3_transparent_mesh = bui.getmesh(
158            'playerLineup3Transparent'
159        )
160        self._lineup_4_transparent_mesh = bui.getmesh(
161            'playerLineup4Transparent'
162        )
163        self._eyes_mesh = bui.getmesh('plasticEyesTransparent')
164
165        self._coop_button: bui.Widget | None = None
166
167        # Only show coop button in regular variant.
168        if self._playlist_select_context is None:
169            self._coop_button = btn = bui.buttonwidget(
170                parent=self._root_widget,
171                position=(hoffs, v + (scl * 15)),
172                size=(
173                    scl * button_width,
174                    scl * 300,
175                ),
176                extra_touch_border_scale=0.1,
177                autoselect=True,
178                label='',
179                button_type='square',
180                text_scale=1.13,
181                on_activate_call=self._coop,
182            )
183
184            if uiscale is bui.UIScale.SMALL:
185                bui.widget(
186                    edit=btn,
187                    left_widget=bui.get_special_widget('back_button'),
188                )
189                bui.widget(
190                    edit=btn,
191                    up_widget=bui.get_special_widget('account_button'),
192                )
193                bui.widget(
194                    edit=btn,
195                    down_widget=bui.get_special_widget('settings_button'),
196                )
197
198            self._draw_dude(
199                0,
200                btn,
201                hoffs,
202                v,
203                scl,
204                position=(140, 30),
205                color=(0.72, 0.4, 1.0),
206            )
207            self._draw_dude(
208                1,
209                btn,
210                hoffs,
211                v,
212                scl,
213                position=(185, 53),
214                color=(0.71, 0.5, 1.0),
215            )
216            self._draw_dude(
217                2,
218                btn,
219                hoffs,
220                v,
221                scl,
222                position=(220, 27),
223                color=(0.67, 0.44, 1.0),
224            )
225            self._draw_dude(
226                3, btn, hoffs, v, scl, position=(255, 57), color=(0.7, 0.3, 1.0)
227            )
228            bui.imagewidget(
229                parent=self._root_widget,
230                draw_controller=btn,
231                position=(hoffs + scl * 230, v + scl * 153),
232                size=(scl * 115, scl * 115),
233                texture=self._lineup_tex,
234                mesh_transparent=angry_computer_transparent_mesh,
235            )
236
237            bui.textwidget(
238                parent=self._root_widget,
239                draw_controller=btn,
240                position=(hoffs + scl * (-10), v + scl * 95),
241                size=(scl * button_width, scl * 50),
242                text=bui.Lstr(
243                    resource='playModes.singlePlayerCoopText',
244                    fallback_resource='playModes.coopText',
245                ),
246                maxwidth=scl * button_width * 0.7,
247                res_scale=1.5,
248                h_align='center',
249                v_align='center',
250                color=(0.7, 0.9, 0.7, 1.0),
251                scale=scl * 2.3,
252            )
253
254            bui.textwidget(
255                parent=self._root_widget,
256                draw_controller=btn,
257                position=(hoffs + scl * (-10), v + (scl * 54)),
258                size=(scl * button_width, scl * 30),
259                text=bui.Lstr(resource=f'{self._r}.oneToFourPlayersText'),
260                h_align='center',
261                v_align='center',
262                scale=0.83 * scl,
263                flatness=1.0,
264                maxwidth=scl * button_width * 0.7,
265                color=clr,
266            )
267
268        scl = 0.5 if self._playlist_select_context is None else 0.68
269        hoffs += 440 if self._playlist_select_context is None else 216
270        v += 180 if self._playlist_select_context is None else -68
271
272        self._teams_button = btn = bui.buttonwidget(
273            parent=self._root_widget,
274            position=(
275                hoffs,
276                v + (scl * 15 if self._playlist_select_context is None else 0),
277            ),
278            size=(
279                scl * button_width,
280                scl * (300 if self._playlist_select_context is None else 360),
281            ),
282            extra_touch_border_scale=0.1,
283            autoselect=True,
284            label='',
285            button_type='square',
286            text_scale=1.13,
287            on_activate_call=self._team_tourney,
288        )
289
290        bui.widget(
291            edit=btn,
292            up_widget=bui.get_special_widget('get_tokens_button'),
293            right_widget=bui.get_special_widget('squad_button'),
294        )
295
296        xxx = -14
297        self._draw_dude(
298            2,
299            btn,
300            hoffs,
301            v,
302            scl,
303            position=(xxx + 148, 30),
304            color=(0.2, 0.4, 1.0),
305        )
306        self._draw_dude(
307            3,
308            btn,
309            hoffs,
310            v,
311            scl,
312            position=(xxx + 181, 53),
313            color=(0.3, 0.4, 1.0),
314        )
315        self._draw_dude(
316            1,
317            btn,
318            hoffs,
319            v,
320            scl,
321            position=(xxx + 216, 33),
322            color=(0.3, 0.5, 1.0),
323        )
324        self._draw_dude(
325            0,
326            btn,
327            hoffs,
328            v,
329            scl,
330            position=(xxx + 245, 57),
331            color=(0.3, 0.5, 1.0),
332        )
333
334        xxx = 155
335        self._draw_dude(
336            0,
337            btn,
338            hoffs,
339            v,
340            scl,
341            position=(xxx + 151, 30),
342            color=(1.0, 0.5, 0.4),
343        )
344        self._draw_dude(
345            1,
346            btn,
347            hoffs,
348            v,
349            scl,
350            position=(xxx + 189, 53),
351            color=(1.0, 0.58, 0.58),
352        )
353        self._draw_dude(
354            3,
355            btn,
356            hoffs,
357            v,
358            scl,
359            position=(xxx + 223, 27),
360            color=(1.0, 0.5, 0.5),
361        )
362        self._draw_dude(
363            2,
364            btn,
365            hoffs,
366            v,
367            scl,
368            position=(xxx + 257, 57),
369            color=(1.0, 0.5, 0.5),
370        )
371
372        bui.textwidget(
373            parent=self._root_widget,
374            draw_controller=btn,
375            position=(hoffs + scl * (-10), v + scl * 95),
376            size=(scl * button_width, scl * 50),
377            text=bui.Lstr(
378                resource='playModes.teamsText', fallback_resource='teamsText'
379            ),
380            res_scale=1.5,
381            maxwidth=scl * button_width * 0.7,
382            h_align='center',
383            v_align='center',
384            color=(0.7, 0.9, 0.7, 1.0),
385            scale=scl * 2.3,
386        )
387        bui.textwidget(
388            parent=self._root_widget,
389            draw_controller=btn,
390            position=(hoffs + scl * (-10), v + (scl * 54)),
391            size=(scl * button_width, scl * 30),
392            text=bui.Lstr(resource=f'{self._r}.twoToEightPlayersText'),
393            h_align='center',
394            v_align='center',
395            res_scale=1.5,
396            scale=0.9 * scl,
397            flatness=1.0,
398            maxwidth=scl * button_width * 0.7,
399            color=clr,
400        )
401
402        hoffs += 0 if self._playlist_select_context is None else 300
403        v -= 155 if self._playlist_select_context is None else 0
404        self._free_for_all_button = btn = bui.buttonwidget(
405            parent=self._root_widget,
406            position=(
407                hoffs,
408                v + (scl * 15 if self._playlist_select_context is None else 0),
409            ),
410            size=(
411                scl * button_width,
412                scl * (300 if self._playlist_select_context is None else 360),
413            ),
414            extra_touch_border_scale=0.1,
415            autoselect=True,
416            label='',
417            button_type='square',
418            text_scale=1.13,
419            on_activate_call=self._free_for_all,
420        )
421
422        xxx = -5
423        self._draw_dude(
424            0,
425            btn,
426            hoffs,
427            v,
428            scl,
429            position=(xxx + 140, 30),
430            color=(0.4, 1.0, 0.4),
431        )
432        self._draw_dude(
433            3,
434            btn,
435            hoffs,
436            v,
437            scl,
438            position=(xxx + 185, 53),
439            color=(1.0, 0.4, 0.5),
440        )
441        self._draw_dude(
442            1,
443            btn,
444            hoffs,
445            v,
446            scl,
447            position=(xxx + 220, 27),
448            color=(0.4, 0.5, 1.0),
449        )
450        self._draw_dude(
451            2,
452            btn,
453            hoffs,
454            v,
455            scl,
456            position=(xxx + 255, 57),
457            color=(0.5, 1.0, 0.4),
458        )
459        xxx = 140
460        self._draw_dude(
461            2,
462            btn,
463            hoffs,
464            v,
465            scl,
466            position=(xxx + 148, 30),
467            color=(1.0, 0.9, 0.4),
468        )
469        self._draw_dude(
470            0,
471            btn,
472            hoffs,
473            v,
474            scl,
475            position=(xxx + 182, 53),
476            color=(0.7, 1.0, 0.5),
477        )
478        self._draw_dude(
479            3,
480            btn,
481            hoffs,
482            v,
483            scl,
484            position=(xxx + 233, 27),
485            color=(0.7, 0.5, 0.9),
486        )
487        self._draw_dude(
488            1,
489            btn,
490            hoffs,
491            v,
492            scl,
493            position=(xxx + 266, 53),
494            color=(0.4, 0.5, 0.8),
495        )
496        bui.textwidget(
497            parent=self._root_widget,
498            draw_controller=btn,
499            position=(hoffs + scl * (-10), v + scl * 95),
500            size=(scl * button_width, scl * 50),
501            text=bui.Lstr(
502                resource='playModes.freeForAllText',
503                fallback_resource='freeForAllText',
504            ),
505            maxwidth=scl * button_width * 0.7,
506            h_align='center',
507            v_align='center',
508            color=(0.7, 0.9, 0.7, 1.0),
509            scale=scl * 1.9,
510        )
511        bui.textwidget(
512            parent=self._root_widget,
513            draw_controller=btn,
514            position=(hoffs + scl * (-10), v + (scl * 54)),
515            size=(scl * button_width, scl * 30),
516            text=bui.Lstr(resource=f'{self._r}.twoToEightPlayersText'),
517            h_align='center',
518            v_align='center',
519            scale=0.9 * scl,
520            flatness=1.0,
521            maxwidth=scl * button_width * 0.7,
522            color=clr,
523        )
524
525        if uiscale is bui.UIScale.SMALL:
526            bui.containerwidget(
527                edit=self._root_widget,
528                selected_child=(
529                    self._coop_button
530                    if self._playlist_select_context is None
531                    else self._teams_button
532                ),
533            )
534        else:
535            bui.containerwidget(
536                edit=self._root_widget,
537                selected_child=(
538                    self._coop_button
539                    if self._playlist_select_context is None
540                    else self._teams_button
541                ),
542            )
543
544        self._restore_state()

Create a MainWindow given a root widget and transition info.

Automatically handles in and out transitions on the provided widget, so there is no need to set transitions when creating it.

@override
def get_main_window_state(self) -> bauiv1.MainWindowState:
546    @override
547    def get_main_window_state(self) -> bui.MainWindowState:
548        # Support recreating our window for back/refresh purposes.
549        cls = type(self)
550
551        # Pull any values out of self here; if we do it in the lambda
552        # we'll keep our window alive inadvertantly.
553        playlist_select_context = self._playlist_select_context
554        return bui.BasicMainWindowState(
555            create_call=lambda transition, origin_widget: cls(
556                transition=transition,
557                origin_widget=origin_widget,
558                playlist_select_context=playlist_select_context,
559            )
560        )

Return a WindowState to recreate this window, if supported.

@override
def on_main_window_close(self) -> None:
562    @override
563    def on_main_window_close(self) -> None:
564        self._save_state()

Called before transitioning out a main window.

A good opportunity to save window state/etc.