bauiv1lib.kiosk

UI functionality for running the game in kiosk mode.

  1# Released under the MIT License. See LICENSE for details.
  2#
  3"""UI functionality for running the game in kiosk mode."""
  4
  5from __future__ import annotations
  6
  7from typing import override
  8
  9import bascenev1 as bs
 10import bauiv1 as bui
 11
 12
 13class KioskWindow(bui.MainWindow):
 14    """Kiosk mode window."""
 15
 16    def __init__(
 17        self,
 18        transition: str | None = 'in_right',
 19        origin_widget: bui.Widget | None = None,
 20    ):
 21        # pylint: disable=too-many-locals, too-many-statements
 22        from bauiv1lib.confirm import QuitWindow
 23
 24        assert bui.app.classic is not None
 25
 26        self._width = 720.0
 27        self._height = 340.0
 28
 29        def _do_cancel() -> None:
 30            QuitWindow(swish=True, quit_type=bui.QuitType.BACK)
 31
 32        super().__init__(
 33            root_widget=bui.containerwidget(
 34                size=(self._width, self._height),
 35                # transition=transition,
 36                on_cancel_call=_do_cancel,
 37                background=False,
 38                stack_offset=(0, -130),
 39            ),
 40            transition=transition,
 41            origin_widget=origin_widget,
 42        )
 43
 44        self._r = 'kioskWindow'
 45
 46        self._show_multiplayer = False
 47
 48        # Let's reset all random player names every time we hit the main menu.
 49        bs.reset_random_player_names()
 50
 51        # Reset achievements too (at least locally).
 52        bui.app.config['Achievements'] = {}
 53
 54        t_delay_base = 0.0
 55        t_delay_scale = 0.0
 56        if not bui.app.classic.did_menu_intro:
 57            t_delay_base = 1.0
 58            t_delay_scale = 1.0
 59
 60        mesh_opaque = bui.getmesh('level_select_button_opaque')
 61        mesh_transparent = bui.getmesh('level_select_button_transparent')
 62        mask_tex = bui.gettexture('mapPreviewMask')
 63
 64        y_extra = 130.0 + (0.0 if self._show_multiplayer else -130.0)
 65        b_width = 250.0
 66        b_height = 200.0
 67        b_space = 280.0
 68        b_v = 80.0 + y_extra
 69        label_height = 130.0 + y_extra
 70        img_width = 180.0
 71        img_v = 158.0 + y_extra
 72
 73        if self._show_multiplayer:
 74            tdelay = t_delay_base + t_delay_scale * 1.3
 75            bui.textwidget(
 76                parent=self._root_widget,
 77                size=(0, 0),
 78                position=(self._width * 0.5, self._height + y_extra - 44),
 79                transition_delay=tdelay,
 80                text=bui.Lstr(resource=f'{self._r}.singlePlayerExamplesText'),
 81                flatness=1.0,
 82                scale=1.2,
 83                h_align='center',
 84                v_align='center',
 85                shadow=1.0,
 86            )
 87        else:
 88            tdelay = t_delay_base + t_delay_scale * 0.7
 89            bui.textwidget(
 90                parent=self._root_widget,
 91                size=(0, 0),
 92                position=(self._width * 0.5, self._height + y_extra - 34),
 93                transition_delay=tdelay,
 94                text=(
 95                    bui.Lstr(
 96                        resource='demoText',
 97                        fallback_resource='mainMenu.demoMenuText',
 98                    )
 99                    if bui.app.env.demo
100                    else 'ARCADE'
101                ),
102                flatness=1.0,
103                scale=1.2,
104                h_align='center',
105                v_align='center',
106                shadow=1.0,
107            )
108        h = self._width * 0.5 - b_space
109        tdelay = t_delay_base + t_delay_scale * 0.7
110        self._b1 = btn = bui.buttonwidget(
111            parent=self._root_widget,
112            autoselect=True,
113            size=(b_width, b_height),
114            on_activate_call=bui.Call(self._do_game, 'easy'),
115            transition_delay=tdelay,
116            position=(h - b_width * 0.5, b_v),
117            label='',
118            button_type='square',
119        )
120        bui.textwidget(
121            parent=self._root_widget,
122            draw_controller=btn,
123            transition_delay=tdelay,
124            size=(0, 0),
125            position=(h, label_height),
126            maxwidth=b_width * 0.7,
127            text=bui.Lstr(resource=f'{self._r}.easyText'),
128            scale=1.3,
129            h_align='center',
130            v_align='center',
131        )
132        bui.imagewidget(
133            parent=self._root_widget,
134            draw_controller=btn,
135            size=(img_width, 0.5 * img_width),
136            transition_delay=tdelay,
137            position=(h - img_width * 0.5, img_v),
138            texture=bui.gettexture('doomShroomPreview'),
139            mesh_opaque=mesh_opaque,
140            mesh_transparent=mesh_transparent,
141            mask_texture=mask_tex,
142        )
143        h = self._width * 0.5
144        tdelay = t_delay_base + t_delay_scale * 0.65
145        self._b2 = btn = bui.buttonwidget(
146            parent=self._root_widget,
147            autoselect=True,
148            size=(b_width, b_height),
149            on_activate_call=bui.Call(self._do_game, 'medium'),
150            position=(h - b_width * 0.5, b_v),
151            label='',
152            button_type='square',
153            transition_delay=tdelay,
154        )
155        bui.textwidget(
156            parent=self._root_widget,
157            draw_controller=btn,
158            transition_delay=tdelay,
159            size=(0, 0),
160            position=(h, label_height),
161            maxwidth=b_width * 0.7,
162            text=bui.Lstr(resource=f'{self._r}.mediumText'),
163            scale=1.3,
164            h_align='center',
165            v_align='center',
166        )
167        bui.imagewidget(
168            parent=self._root_widget,
169            draw_controller=btn,
170            size=(img_width, 0.5 * img_width),
171            transition_delay=tdelay,
172            position=(h - img_width * 0.5, img_v),
173            texture=bui.gettexture('footballStadiumPreview'),
174            mesh_opaque=mesh_opaque,
175            mesh_transparent=mesh_transparent,
176            mask_texture=mask_tex,
177        )
178        h = self._width * 0.5 + b_space
179        tdelay = t_delay_base + t_delay_scale * 0.6
180        self._b3 = btn = bui.buttonwidget(
181            parent=self._root_widget,
182            autoselect=True,
183            size=(b_width, b_height),
184            on_activate_call=bui.Call(self._do_game, 'hard'),
185            transition_delay=tdelay,
186            position=(h - b_width * 0.5, b_v),
187            label='',
188            button_type='square',
189        )
190        bui.textwidget(
191            parent=self._root_widget,
192            draw_controller=btn,
193            transition_delay=tdelay,
194            size=(0, 0),
195            position=(h, label_height),
196            maxwidth=b_width * 0.7,
197            text='Hard',
198            scale=1.3,
199            h_align='center',
200            v_align='center',
201        )
202        bui.imagewidget(
203            parent=self._root_widget,
204            draw_controller=btn,
205            transition_delay=tdelay,
206            size=(img_width, 0.5 * img_width),
207            position=(h - img_width * 0.5, img_v),
208            texture=bui.gettexture('courtyardPreview'),
209            mesh_opaque=mesh_opaque,
210            mesh_transparent=mesh_transparent,
211            mask_texture=mask_tex,
212        )
213        if not bui.app.classic.did_menu_intro:
214            bui.app.classic.did_menu_intro = True
215
216        self._b4: bui.Widget | None
217        self._b5: bui.Widget | None
218        self._b6: bui.Widget | None
219
220        if bool(False):
221            bui.textwidget(
222                parent=self._root_widget,
223                size=(0, 0),
224                position=(self._width * 0.5, self._height + y_extra - 44),
225                transition_delay=tdelay,
226                text=bui.Lstr(resource=f'{self._r}.versusExamplesText'),
227                flatness=1.0,
228                scale=1.2,
229                h_align='center',
230                v_align='center',
231                shadow=1.0,
232            )
233            h = self._width * 0.5 - b_space
234            tdelay = t_delay_base + t_delay_scale * 0.7
235            self._b4 = btn = bui.buttonwidget(
236                parent=self._root_widget,
237                autoselect=True,
238                size=(b_width, b_height),
239                on_activate_call=bui.Call(self._do_game, 'ctf'),
240                transition_delay=tdelay,
241                position=(h - b_width * 0.5, b_v),
242                label='',
243                button_type='square',
244            )
245            bui.textwidget(
246                parent=self._root_widget,
247                draw_controller=btn,
248                transition_delay=tdelay,
249                size=(0, 0),
250                position=(h, label_height),
251                maxwidth=b_width * 0.7,
252                text=bui.Lstr(translate=('gameNames', 'Capture the Flag')),
253                scale=1.3,
254                h_align='center',
255                v_align='center',
256            )
257            bui.imagewidget(
258                parent=self._root_widget,
259                draw_controller=btn,
260                size=(img_width, 0.5 * img_width),
261                transition_delay=tdelay,
262                position=(h - img_width * 0.5, img_v),
263                texture=bui.gettexture('bridgitPreview'),
264                mesh_opaque=mesh_opaque,
265                mesh_transparent=mesh_transparent,
266                mask_texture=mask_tex,
267            )
268
269            h = self._width * 0.5
270            tdelay = t_delay_base + t_delay_scale * 0.65
271            self._b5 = btn = bui.buttonwidget(
272                parent=self._root_widget,
273                autoselect=True,
274                size=(b_width, b_height),
275                on_activate_call=bui.Call(self._do_game, 'hockey'),
276                position=(h - b_width * 0.5, b_v),
277                label='',
278                button_type='square',
279                transition_delay=tdelay,
280            )
281            bui.textwidget(
282                parent=self._root_widget,
283                draw_controller=btn,
284                transition_delay=tdelay,
285                size=(0, 0),
286                position=(h, label_height),
287                maxwidth=b_width * 0.7,
288                text=bui.Lstr(translate=('gameNames', 'Hockey')),
289                scale=1.3,
290                h_align='center',
291                v_align='center',
292            )
293            bui.imagewidget(
294                parent=self._root_widget,
295                draw_controller=btn,
296                size=(img_width, 0.5 * img_width),
297                transition_delay=tdelay,
298                position=(h - img_width * 0.5, img_v),
299                texture=bui.gettexture('hockeyStadiumPreview'),
300                mesh_opaque=mesh_opaque,
301                mesh_transparent=mesh_transparent,
302                mask_texture=mask_tex,
303            )
304            h = self._width * 0.5 + b_space
305            tdelay = t_delay_base + t_delay_scale * 0.6
306            self._b6 = btn = bui.buttonwidget(
307                parent=self._root_widget,
308                autoselect=True,
309                size=(b_width, b_height),
310                on_activate_call=bui.Call(self._do_game, 'epic'),
311                transition_delay=tdelay,
312                position=(h - b_width * 0.5, b_v),
313                label='',
314                button_type='square',
315            )
316            bui.textwidget(
317                parent=self._root_widget,
318                draw_controller=btn,
319                transition_delay=tdelay,
320                size=(0, 0),
321                position=(h, label_height),
322                maxwidth=b_width * 0.7,
323                text=bui.Lstr(resource=f'{self._r}.epicModeText'),
324                scale=1.3,
325                h_align='center',
326                v_align='center',
327            )
328            bui.imagewidget(
329                parent=self._root_widget,
330                draw_controller=btn,
331                transition_delay=tdelay,
332                size=(img_width, 0.5 * img_width),
333                position=(h - img_width * 0.5, img_v),
334                texture=bui.gettexture('tipTopPreview'),
335                mesh_opaque=mesh_opaque,
336                mesh_transparent=mesh_transparent,
337                mask_texture=mask_tex,
338            )
339        else:
340            self._b4 = self._b5 = self._b6 = None
341
342        self._b7: bui.Widget | None
343        if bui.app.env.arcade:
344            self._b7 = bui.buttonwidget(
345                parent=self._root_widget,
346                autoselect=True,
347                size=(b_width, 50),
348                color=(0.45, 0.55, 0.45),
349                textcolor=(0.7, 0.8, 0.7),
350                scale=0.5,
351                position=(self._width * 0.5 - 60.0, b_v - 70.0),
352                transition_delay=tdelay,
353                label=bui.Lstr(resource=f'{self._r}.fullMenuText'),
354                on_activate_call=self._do_full_menu,
355            )
356        else:
357            self._b7 = None
358        self._restore_state()
359        self._update()
360        self._update_timer = bui.AppTimer(
361            1.0, bui.WeakCall(self._update), repeat=True
362        )
363
364    @override
365    def get_main_window_state(self) -> bui.MainWindowState:
366        # Support recreating our window for back/refresh purposes.
367        cls = type(self)
368        return bui.BasicMainWindowState(
369            create_call=lambda transition, origin_widget: cls(
370                transition=transition, origin_widget=origin_widget
371            )
372        )
373
374    @override
375    def on_main_window_close(self) -> None:
376        self._save_state()
377
378    def _restore_state(self) -> None:
379        assert bui.app.classic is not None
380        sel_name = bui.app.ui_v1.window_states.get(type(self))
381        sel: bui.Widget | None
382        if sel_name == 'b1':
383            sel = self._b1
384        elif sel_name == 'b2':
385            sel = self._b2
386        elif sel_name == 'b3':
387            sel = self._b3
388        elif sel_name == 'b4':
389            sel = self._b4
390        elif sel_name == 'b5':
391            sel = self._b5
392        elif sel_name == 'b6':
393            sel = self._b6
394        elif sel_name == 'b7':
395            sel = self._b7
396        else:
397            sel = self._b1
398        if sel:
399            bui.containerwidget(edit=self._root_widget, selected_child=sel)
400
401    def _save_state(self) -> None:
402        sel = self._root_widget.get_selected_child()
403        if sel == self._b1:
404            sel_name = 'b1'
405        elif sel == self._b2:
406            sel_name = 'b2'
407        elif sel == self._b3:
408            sel_name = 'b3'
409        elif sel == self._b4:
410            sel_name = 'b4'
411        elif sel == self._b5:
412            sel_name = 'b5'
413        elif sel == self._b6:
414            sel_name = 'b6'
415        elif sel == self._b7:
416            sel_name = 'b7'
417        else:
418            sel_name = 'b1'
419        assert bui.app.classic is not None
420        bui.app.ui_v1.window_states[type(self)] = sel_name
421
422    def _update(self) -> None:
423        plus = bui.app.plus
424        assert plus is not None
425
426        # Kiosk-mode is designed to be used signed-out... try for force
427        # the issue.
428        if plus.get_v1_account_state() == 'signed_in':
429            # _bs.sign_out()
430            # FIXME: Try to delete player profiles here too.
431            pass
432        else:
433            # Also make sure there's no player profiles.
434            appconfig = bui.app.config
435            appconfig['Player Profiles'] = {}
436
437    def _do_game(self, mode: str) -> None:
438        assert bui.app.classic is not None
439        self._save_state()
440        if mode in ['epic', 'ctf', 'hockey']:
441            appconfig = bui.app.config
442            if 'Team Tournament Playlists' not in appconfig:
443                appconfig['Team Tournament Playlists'] = {}
444            if 'Free-for-All Playlists' not in appconfig:
445                appconfig['Free-for-All Playlists'] = {}
446            appconfig['Show Tutorial'] = False
447            if mode == 'epic':
448                appconfig['Free-for-All Playlists']['Just Epic Elim'] = [
449                    {
450                        'settings': {
451                            'Epic Mode': 1,
452                            'Lives Per Player': 1,
453                            'Respawn Times': 1.0,
454                            'Time Limit': 0,
455                            'map': 'Tip Top',
456                        },
457                        'type': 'bs_elimination.EliminationGame',
458                    }
459                ]
460                appconfig['Free-for-All Playlist Selection'] = 'Just Epic Elim'
461                bui.fade_screen(
462                    False,
463                    endcall=bui.Call(
464                        bui.pushcall,
465                        bui.Call(bs.new_host_session, bs.FreeForAllSession),
466                    ),
467                )
468            else:
469                if mode == 'ctf':
470                    appconfig['Team Tournament Playlists']['Just CTF'] = [
471                        {
472                            'settings': {
473                                'Epic Mode': False,
474                                'Flag Idle Return Time': 30,
475                                'Flag Touch Return Time': 0,
476                                'Respawn Times': 1.0,
477                                'Score to Win': 3,
478                                'Time Limit': 0,
479                                'map': 'Bridgit',
480                            },
481                            'type': 'bs_capture_the_flag.CTFGame',
482                        }
483                    ]
484                    appconfig['Team Tournament Playlist Selection'] = 'Just CTF'
485                else:
486                    appconfig['Team Tournament Playlists']['Just Hockey'] = [
487                        {
488                            'settings': {
489                                'Respawn Times': 1.0,
490                                'Score to Win': 1,
491                                'Time Limit': 0,
492                                'map': 'Hockey Stadium',
493                            },
494                            'type': 'bs_hockey.HockeyGame',
495                        }
496                    ]
497                    appconfig['Team Tournament Playlist Selection'] = (
498                        'Just Hockey'
499                    )
500                bui.fade_screen(
501                    False,
502                    endcall=bui.Call(
503                        bui.pushcall,
504                        bui.Call(bs.new_host_session, bs.DualTeamSession),
505                    ),
506                )
507            bui.containerwidget(edit=self._root_widget, transition='out_left')
508            return
509
510        game = (
511            'Easy:Onslaught Training'
512            if mode == 'easy'
513            else (
514                'Easy:Rookie Football'
515                if mode == 'medium'
516                else 'Easy:Uber Onslaught'
517            )
518        )
519        cfg = bui.app.config
520        cfg['Selected Coop Game'] = game
521        cfg.commit()
522        if bui.app.classic.launch_coop_game(game, force=True):
523            bui.containerwidget(edit=self._root_widget, transition='out_left')
524
525    def _do_full_menu(self) -> None:
526        # pylint: disable=cyclic-import
527        from bauiv1lib.mainmenu import MainMenuWindow
528
529        # no-op if we're not in control.
530        if not self.main_window_has_control():
531            return
532
533        assert bui.app.classic is not None
534
535        self._save_state()
536        bui.app.classic.did_menu_intro = True  # prevent delayed transition-in
537
538        self.main_window_replace(MainMenuWindow())
class KioskWindow(bauiv1._uitypes.MainWindow):
 14class KioskWindow(bui.MainWindow):
 15    """Kiosk mode window."""
 16
 17    def __init__(
 18        self,
 19        transition: str | None = 'in_right',
 20        origin_widget: bui.Widget | None = None,
 21    ):
 22        # pylint: disable=too-many-locals, too-many-statements
 23        from bauiv1lib.confirm import QuitWindow
 24
 25        assert bui.app.classic is not None
 26
 27        self._width = 720.0
 28        self._height = 340.0
 29
 30        def _do_cancel() -> None:
 31            QuitWindow(swish=True, quit_type=bui.QuitType.BACK)
 32
 33        super().__init__(
 34            root_widget=bui.containerwidget(
 35                size=(self._width, self._height),
 36                # transition=transition,
 37                on_cancel_call=_do_cancel,
 38                background=False,
 39                stack_offset=(0, -130),
 40            ),
 41            transition=transition,
 42            origin_widget=origin_widget,
 43        )
 44
 45        self._r = 'kioskWindow'
 46
 47        self._show_multiplayer = False
 48
 49        # Let's reset all random player names every time we hit the main menu.
 50        bs.reset_random_player_names()
 51
 52        # Reset achievements too (at least locally).
 53        bui.app.config['Achievements'] = {}
 54
 55        t_delay_base = 0.0
 56        t_delay_scale = 0.0
 57        if not bui.app.classic.did_menu_intro:
 58            t_delay_base = 1.0
 59            t_delay_scale = 1.0
 60
 61        mesh_opaque = bui.getmesh('level_select_button_opaque')
 62        mesh_transparent = bui.getmesh('level_select_button_transparent')
 63        mask_tex = bui.gettexture('mapPreviewMask')
 64
 65        y_extra = 130.0 + (0.0 if self._show_multiplayer else -130.0)
 66        b_width = 250.0
 67        b_height = 200.0
 68        b_space = 280.0
 69        b_v = 80.0 + y_extra
 70        label_height = 130.0 + y_extra
 71        img_width = 180.0
 72        img_v = 158.0 + y_extra
 73
 74        if self._show_multiplayer:
 75            tdelay = t_delay_base + t_delay_scale * 1.3
 76            bui.textwidget(
 77                parent=self._root_widget,
 78                size=(0, 0),
 79                position=(self._width * 0.5, self._height + y_extra - 44),
 80                transition_delay=tdelay,
 81                text=bui.Lstr(resource=f'{self._r}.singlePlayerExamplesText'),
 82                flatness=1.0,
 83                scale=1.2,
 84                h_align='center',
 85                v_align='center',
 86                shadow=1.0,
 87            )
 88        else:
 89            tdelay = t_delay_base + t_delay_scale * 0.7
 90            bui.textwidget(
 91                parent=self._root_widget,
 92                size=(0, 0),
 93                position=(self._width * 0.5, self._height + y_extra - 34),
 94                transition_delay=tdelay,
 95                text=(
 96                    bui.Lstr(
 97                        resource='demoText',
 98                        fallback_resource='mainMenu.demoMenuText',
 99                    )
100                    if bui.app.env.demo
101                    else 'ARCADE'
102                ),
103                flatness=1.0,
104                scale=1.2,
105                h_align='center',
106                v_align='center',
107                shadow=1.0,
108            )
109        h = self._width * 0.5 - b_space
110        tdelay = t_delay_base + t_delay_scale * 0.7
111        self._b1 = btn = bui.buttonwidget(
112            parent=self._root_widget,
113            autoselect=True,
114            size=(b_width, b_height),
115            on_activate_call=bui.Call(self._do_game, 'easy'),
116            transition_delay=tdelay,
117            position=(h - b_width * 0.5, b_v),
118            label='',
119            button_type='square',
120        )
121        bui.textwidget(
122            parent=self._root_widget,
123            draw_controller=btn,
124            transition_delay=tdelay,
125            size=(0, 0),
126            position=(h, label_height),
127            maxwidth=b_width * 0.7,
128            text=bui.Lstr(resource=f'{self._r}.easyText'),
129            scale=1.3,
130            h_align='center',
131            v_align='center',
132        )
133        bui.imagewidget(
134            parent=self._root_widget,
135            draw_controller=btn,
136            size=(img_width, 0.5 * img_width),
137            transition_delay=tdelay,
138            position=(h - img_width * 0.5, img_v),
139            texture=bui.gettexture('doomShroomPreview'),
140            mesh_opaque=mesh_opaque,
141            mesh_transparent=mesh_transparent,
142            mask_texture=mask_tex,
143        )
144        h = self._width * 0.5
145        tdelay = t_delay_base + t_delay_scale * 0.65
146        self._b2 = btn = bui.buttonwidget(
147            parent=self._root_widget,
148            autoselect=True,
149            size=(b_width, b_height),
150            on_activate_call=bui.Call(self._do_game, 'medium'),
151            position=(h - b_width * 0.5, b_v),
152            label='',
153            button_type='square',
154            transition_delay=tdelay,
155        )
156        bui.textwidget(
157            parent=self._root_widget,
158            draw_controller=btn,
159            transition_delay=tdelay,
160            size=(0, 0),
161            position=(h, label_height),
162            maxwidth=b_width * 0.7,
163            text=bui.Lstr(resource=f'{self._r}.mediumText'),
164            scale=1.3,
165            h_align='center',
166            v_align='center',
167        )
168        bui.imagewidget(
169            parent=self._root_widget,
170            draw_controller=btn,
171            size=(img_width, 0.5 * img_width),
172            transition_delay=tdelay,
173            position=(h - img_width * 0.5, img_v),
174            texture=bui.gettexture('footballStadiumPreview'),
175            mesh_opaque=mesh_opaque,
176            mesh_transparent=mesh_transparent,
177            mask_texture=mask_tex,
178        )
179        h = self._width * 0.5 + b_space
180        tdelay = t_delay_base + t_delay_scale * 0.6
181        self._b3 = btn = bui.buttonwidget(
182            parent=self._root_widget,
183            autoselect=True,
184            size=(b_width, b_height),
185            on_activate_call=bui.Call(self._do_game, 'hard'),
186            transition_delay=tdelay,
187            position=(h - b_width * 0.5, b_v),
188            label='',
189            button_type='square',
190        )
191        bui.textwidget(
192            parent=self._root_widget,
193            draw_controller=btn,
194            transition_delay=tdelay,
195            size=(0, 0),
196            position=(h, label_height),
197            maxwidth=b_width * 0.7,
198            text='Hard',
199            scale=1.3,
200            h_align='center',
201            v_align='center',
202        )
203        bui.imagewidget(
204            parent=self._root_widget,
205            draw_controller=btn,
206            transition_delay=tdelay,
207            size=(img_width, 0.5 * img_width),
208            position=(h - img_width * 0.5, img_v),
209            texture=bui.gettexture('courtyardPreview'),
210            mesh_opaque=mesh_opaque,
211            mesh_transparent=mesh_transparent,
212            mask_texture=mask_tex,
213        )
214        if not bui.app.classic.did_menu_intro:
215            bui.app.classic.did_menu_intro = True
216
217        self._b4: bui.Widget | None
218        self._b5: bui.Widget | None
219        self._b6: bui.Widget | None
220
221        if bool(False):
222            bui.textwidget(
223                parent=self._root_widget,
224                size=(0, 0),
225                position=(self._width * 0.5, self._height + y_extra - 44),
226                transition_delay=tdelay,
227                text=bui.Lstr(resource=f'{self._r}.versusExamplesText'),
228                flatness=1.0,
229                scale=1.2,
230                h_align='center',
231                v_align='center',
232                shadow=1.0,
233            )
234            h = self._width * 0.5 - b_space
235            tdelay = t_delay_base + t_delay_scale * 0.7
236            self._b4 = btn = bui.buttonwidget(
237                parent=self._root_widget,
238                autoselect=True,
239                size=(b_width, b_height),
240                on_activate_call=bui.Call(self._do_game, 'ctf'),
241                transition_delay=tdelay,
242                position=(h - b_width * 0.5, b_v),
243                label='',
244                button_type='square',
245            )
246            bui.textwidget(
247                parent=self._root_widget,
248                draw_controller=btn,
249                transition_delay=tdelay,
250                size=(0, 0),
251                position=(h, label_height),
252                maxwidth=b_width * 0.7,
253                text=bui.Lstr(translate=('gameNames', 'Capture the Flag')),
254                scale=1.3,
255                h_align='center',
256                v_align='center',
257            )
258            bui.imagewidget(
259                parent=self._root_widget,
260                draw_controller=btn,
261                size=(img_width, 0.5 * img_width),
262                transition_delay=tdelay,
263                position=(h - img_width * 0.5, img_v),
264                texture=bui.gettexture('bridgitPreview'),
265                mesh_opaque=mesh_opaque,
266                mesh_transparent=mesh_transparent,
267                mask_texture=mask_tex,
268            )
269
270            h = self._width * 0.5
271            tdelay = t_delay_base + t_delay_scale * 0.65
272            self._b5 = btn = bui.buttonwidget(
273                parent=self._root_widget,
274                autoselect=True,
275                size=(b_width, b_height),
276                on_activate_call=bui.Call(self._do_game, 'hockey'),
277                position=(h - b_width * 0.5, b_v),
278                label='',
279                button_type='square',
280                transition_delay=tdelay,
281            )
282            bui.textwidget(
283                parent=self._root_widget,
284                draw_controller=btn,
285                transition_delay=tdelay,
286                size=(0, 0),
287                position=(h, label_height),
288                maxwidth=b_width * 0.7,
289                text=bui.Lstr(translate=('gameNames', 'Hockey')),
290                scale=1.3,
291                h_align='center',
292                v_align='center',
293            )
294            bui.imagewidget(
295                parent=self._root_widget,
296                draw_controller=btn,
297                size=(img_width, 0.5 * img_width),
298                transition_delay=tdelay,
299                position=(h - img_width * 0.5, img_v),
300                texture=bui.gettexture('hockeyStadiumPreview'),
301                mesh_opaque=mesh_opaque,
302                mesh_transparent=mesh_transparent,
303                mask_texture=mask_tex,
304            )
305            h = self._width * 0.5 + b_space
306            tdelay = t_delay_base + t_delay_scale * 0.6
307            self._b6 = btn = bui.buttonwidget(
308                parent=self._root_widget,
309                autoselect=True,
310                size=(b_width, b_height),
311                on_activate_call=bui.Call(self._do_game, 'epic'),
312                transition_delay=tdelay,
313                position=(h - b_width * 0.5, b_v),
314                label='',
315                button_type='square',
316            )
317            bui.textwidget(
318                parent=self._root_widget,
319                draw_controller=btn,
320                transition_delay=tdelay,
321                size=(0, 0),
322                position=(h, label_height),
323                maxwidth=b_width * 0.7,
324                text=bui.Lstr(resource=f'{self._r}.epicModeText'),
325                scale=1.3,
326                h_align='center',
327                v_align='center',
328            )
329            bui.imagewidget(
330                parent=self._root_widget,
331                draw_controller=btn,
332                transition_delay=tdelay,
333                size=(img_width, 0.5 * img_width),
334                position=(h - img_width * 0.5, img_v),
335                texture=bui.gettexture('tipTopPreview'),
336                mesh_opaque=mesh_opaque,
337                mesh_transparent=mesh_transparent,
338                mask_texture=mask_tex,
339            )
340        else:
341            self._b4 = self._b5 = self._b6 = None
342
343        self._b7: bui.Widget | None
344        if bui.app.env.arcade:
345            self._b7 = bui.buttonwidget(
346                parent=self._root_widget,
347                autoselect=True,
348                size=(b_width, 50),
349                color=(0.45, 0.55, 0.45),
350                textcolor=(0.7, 0.8, 0.7),
351                scale=0.5,
352                position=(self._width * 0.5 - 60.0, b_v - 70.0),
353                transition_delay=tdelay,
354                label=bui.Lstr(resource=f'{self._r}.fullMenuText'),
355                on_activate_call=self._do_full_menu,
356            )
357        else:
358            self._b7 = None
359        self._restore_state()
360        self._update()
361        self._update_timer = bui.AppTimer(
362            1.0, bui.WeakCall(self._update), repeat=True
363        )
364
365    @override
366    def get_main_window_state(self) -> bui.MainWindowState:
367        # Support recreating our window for back/refresh purposes.
368        cls = type(self)
369        return bui.BasicMainWindowState(
370            create_call=lambda transition, origin_widget: cls(
371                transition=transition, origin_widget=origin_widget
372            )
373        )
374
375    @override
376    def on_main_window_close(self) -> None:
377        self._save_state()
378
379    def _restore_state(self) -> None:
380        assert bui.app.classic is not None
381        sel_name = bui.app.ui_v1.window_states.get(type(self))
382        sel: bui.Widget | None
383        if sel_name == 'b1':
384            sel = self._b1
385        elif sel_name == 'b2':
386            sel = self._b2
387        elif sel_name == 'b3':
388            sel = self._b3
389        elif sel_name == 'b4':
390            sel = self._b4
391        elif sel_name == 'b5':
392            sel = self._b5
393        elif sel_name == 'b6':
394            sel = self._b6
395        elif sel_name == 'b7':
396            sel = self._b7
397        else:
398            sel = self._b1
399        if sel:
400            bui.containerwidget(edit=self._root_widget, selected_child=sel)
401
402    def _save_state(self) -> None:
403        sel = self._root_widget.get_selected_child()
404        if sel == self._b1:
405            sel_name = 'b1'
406        elif sel == self._b2:
407            sel_name = 'b2'
408        elif sel == self._b3:
409            sel_name = 'b3'
410        elif sel == self._b4:
411            sel_name = 'b4'
412        elif sel == self._b5:
413            sel_name = 'b5'
414        elif sel == self._b6:
415            sel_name = 'b6'
416        elif sel == self._b7:
417            sel_name = 'b7'
418        else:
419            sel_name = 'b1'
420        assert bui.app.classic is not None
421        bui.app.ui_v1.window_states[type(self)] = sel_name
422
423    def _update(self) -> None:
424        plus = bui.app.plus
425        assert plus is not None
426
427        # Kiosk-mode is designed to be used signed-out... try for force
428        # the issue.
429        if plus.get_v1_account_state() == 'signed_in':
430            # _bs.sign_out()
431            # FIXME: Try to delete player profiles here too.
432            pass
433        else:
434            # Also make sure there's no player profiles.
435            appconfig = bui.app.config
436            appconfig['Player Profiles'] = {}
437
438    def _do_game(self, mode: str) -> None:
439        assert bui.app.classic is not None
440        self._save_state()
441        if mode in ['epic', 'ctf', 'hockey']:
442            appconfig = bui.app.config
443            if 'Team Tournament Playlists' not in appconfig:
444                appconfig['Team Tournament Playlists'] = {}
445            if 'Free-for-All Playlists' not in appconfig:
446                appconfig['Free-for-All Playlists'] = {}
447            appconfig['Show Tutorial'] = False
448            if mode == 'epic':
449                appconfig['Free-for-All Playlists']['Just Epic Elim'] = [
450                    {
451                        'settings': {
452                            'Epic Mode': 1,
453                            'Lives Per Player': 1,
454                            'Respawn Times': 1.0,
455                            'Time Limit': 0,
456                            'map': 'Tip Top',
457                        },
458                        'type': 'bs_elimination.EliminationGame',
459                    }
460                ]
461                appconfig['Free-for-All Playlist Selection'] = 'Just Epic Elim'
462                bui.fade_screen(
463                    False,
464                    endcall=bui.Call(
465                        bui.pushcall,
466                        bui.Call(bs.new_host_session, bs.FreeForAllSession),
467                    ),
468                )
469            else:
470                if mode == 'ctf':
471                    appconfig['Team Tournament Playlists']['Just CTF'] = [
472                        {
473                            'settings': {
474                                'Epic Mode': False,
475                                'Flag Idle Return Time': 30,
476                                'Flag Touch Return Time': 0,
477                                'Respawn Times': 1.0,
478                                'Score to Win': 3,
479                                'Time Limit': 0,
480                                'map': 'Bridgit',
481                            },
482                            'type': 'bs_capture_the_flag.CTFGame',
483                        }
484                    ]
485                    appconfig['Team Tournament Playlist Selection'] = 'Just CTF'
486                else:
487                    appconfig['Team Tournament Playlists']['Just Hockey'] = [
488                        {
489                            'settings': {
490                                'Respawn Times': 1.0,
491                                'Score to Win': 1,
492                                'Time Limit': 0,
493                                'map': 'Hockey Stadium',
494                            },
495                            'type': 'bs_hockey.HockeyGame',
496                        }
497                    ]
498                    appconfig['Team Tournament Playlist Selection'] = (
499                        'Just Hockey'
500                    )
501                bui.fade_screen(
502                    False,
503                    endcall=bui.Call(
504                        bui.pushcall,
505                        bui.Call(bs.new_host_session, bs.DualTeamSession),
506                    ),
507                )
508            bui.containerwidget(edit=self._root_widget, transition='out_left')
509            return
510
511        game = (
512            'Easy:Onslaught Training'
513            if mode == 'easy'
514            else (
515                'Easy:Rookie Football'
516                if mode == 'medium'
517                else 'Easy:Uber Onslaught'
518            )
519        )
520        cfg = bui.app.config
521        cfg['Selected Coop Game'] = game
522        cfg.commit()
523        if bui.app.classic.launch_coop_game(game, force=True):
524            bui.containerwidget(edit=self._root_widget, transition='out_left')
525
526    def _do_full_menu(self) -> None:
527        # pylint: disable=cyclic-import
528        from bauiv1lib.mainmenu import MainMenuWindow
529
530        # no-op if we're not in control.
531        if not self.main_window_has_control():
532            return
533
534        assert bui.app.classic is not None
535
536        self._save_state()
537        bui.app.classic.did_menu_intro = True  # prevent delayed transition-in
538
539        self.main_window_replace(MainMenuWindow())

Kiosk mode window.

KioskWindow( transition: str | None = 'in_right', origin_widget: _bauiv1.Widget | None = None)
 17    def __init__(
 18        self,
 19        transition: str | None = 'in_right',
 20        origin_widget: bui.Widget | None = None,
 21    ):
 22        # pylint: disable=too-many-locals, too-many-statements
 23        from bauiv1lib.confirm import QuitWindow
 24
 25        assert bui.app.classic is not None
 26
 27        self._width = 720.0
 28        self._height = 340.0
 29
 30        def _do_cancel() -> None:
 31            QuitWindow(swish=True, quit_type=bui.QuitType.BACK)
 32
 33        super().__init__(
 34            root_widget=bui.containerwidget(
 35                size=(self._width, self._height),
 36                # transition=transition,
 37                on_cancel_call=_do_cancel,
 38                background=False,
 39                stack_offset=(0, -130),
 40            ),
 41            transition=transition,
 42            origin_widget=origin_widget,
 43        )
 44
 45        self._r = 'kioskWindow'
 46
 47        self._show_multiplayer = False
 48
 49        # Let's reset all random player names every time we hit the main menu.
 50        bs.reset_random_player_names()
 51
 52        # Reset achievements too (at least locally).
 53        bui.app.config['Achievements'] = {}
 54
 55        t_delay_base = 0.0
 56        t_delay_scale = 0.0
 57        if not bui.app.classic.did_menu_intro:
 58            t_delay_base = 1.0
 59            t_delay_scale = 1.0
 60
 61        mesh_opaque = bui.getmesh('level_select_button_opaque')
 62        mesh_transparent = bui.getmesh('level_select_button_transparent')
 63        mask_tex = bui.gettexture('mapPreviewMask')
 64
 65        y_extra = 130.0 + (0.0 if self._show_multiplayer else -130.0)
 66        b_width = 250.0
 67        b_height = 200.0
 68        b_space = 280.0
 69        b_v = 80.0 + y_extra
 70        label_height = 130.0 + y_extra
 71        img_width = 180.0
 72        img_v = 158.0 + y_extra
 73
 74        if self._show_multiplayer:
 75            tdelay = t_delay_base + t_delay_scale * 1.3
 76            bui.textwidget(
 77                parent=self._root_widget,
 78                size=(0, 0),
 79                position=(self._width * 0.5, self._height + y_extra - 44),
 80                transition_delay=tdelay,
 81                text=bui.Lstr(resource=f'{self._r}.singlePlayerExamplesText'),
 82                flatness=1.0,
 83                scale=1.2,
 84                h_align='center',
 85                v_align='center',
 86                shadow=1.0,
 87            )
 88        else:
 89            tdelay = t_delay_base + t_delay_scale * 0.7
 90            bui.textwidget(
 91                parent=self._root_widget,
 92                size=(0, 0),
 93                position=(self._width * 0.5, self._height + y_extra - 34),
 94                transition_delay=tdelay,
 95                text=(
 96                    bui.Lstr(
 97                        resource='demoText',
 98                        fallback_resource='mainMenu.demoMenuText',
 99                    )
100                    if bui.app.env.demo
101                    else 'ARCADE'
102                ),
103                flatness=1.0,
104                scale=1.2,
105                h_align='center',
106                v_align='center',
107                shadow=1.0,
108            )
109        h = self._width * 0.5 - b_space
110        tdelay = t_delay_base + t_delay_scale * 0.7
111        self._b1 = btn = bui.buttonwidget(
112            parent=self._root_widget,
113            autoselect=True,
114            size=(b_width, b_height),
115            on_activate_call=bui.Call(self._do_game, 'easy'),
116            transition_delay=tdelay,
117            position=(h - b_width * 0.5, b_v),
118            label='',
119            button_type='square',
120        )
121        bui.textwidget(
122            parent=self._root_widget,
123            draw_controller=btn,
124            transition_delay=tdelay,
125            size=(0, 0),
126            position=(h, label_height),
127            maxwidth=b_width * 0.7,
128            text=bui.Lstr(resource=f'{self._r}.easyText'),
129            scale=1.3,
130            h_align='center',
131            v_align='center',
132        )
133        bui.imagewidget(
134            parent=self._root_widget,
135            draw_controller=btn,
136            size=(img_width, 0.5 * img_width),
137            transition_delay=tdelay,
138            position=(h - img_width * 0.5, img_v),
139            texture=bui.gettexture('doomShroomPreview'),
140            mesh_opaque=mesh_opaque,
141            mesh_transparent=mesh_transparent,
142            mask_texture=mask_tex,
143        )
144        h = self._width * 0.5
145        tdelay = t_delay_base + t_delay_scale * 0.65
146        self._b2 = btn = bui.buttonwidget(
147            parent=self._root_widget,
148            autoselect=True,
149            size=(b_width, b_height),
150            on_activate_call=bui.Call(self._do_game, 'medium'),
151            position=(h - b_width * 0.5, b_v),
152            label='',
153            button_type='square',
154            transition_delay=tdelay,
155        )
156        bui.textwidget(
157            parent=self._root_widget,
158            draw_controller=btn,
159            transition_delay=tdelay,
160            size=(0, 0),
161            position=(h, label_height),
162            maxwidth=b_width * 0.7,
163            text=bui.Lstr(resource=f'{self._r}.mediumText'),
164            scale=1.3,
165            h_align='center',
166            v_align='center',
167        )
168        bui.imagewidget(
169            parent=self._root_widget,
170            draw_controller=btn,
171            size=(img_width, 0.5 * img_width),
172            transition_delay=tdelay,
173            position=(h - img_width * 0.5, img_v),
174            texture=bui.gettexture('footballStadiumPreview'),
175            mesh_opaque=mesh_opaque,
176            mesh_transparent=mesh_transparent,
177            mask_texture=mask_tex,
178        )
179        h = self._width * 0.5 + b_space
180        tdelay = t_delay_base + t_delay_scale * 0.6
181        self._b3 = btn = bui.buttonwidget(
182            parent=self._root_widget,
183            autoselect=True,
184            size=(b_width, b_height),
185            on_activate_call=bui.Call(self._do_game, 'hard'),
186            transition_delay=tdelay,
187            position=(h - b_width * 0.5, b_v),
188            label='',
189            button_type='square',
190        )
191        bui.textwidget(
192            parent=self._root_widget,
193            draw_controller=btn,
194            transition_delay=tdelay,
195            size=(0, 0),
196            position=(h, label_height),
197            maxwidth=b_width * 0.7,
198            text='Hard',
199            scale=1.3,
200            h_align='center',
201            v_align='center',
202        )
203        bui.imagewidget(
204            parent=self._root_widget,
205            draw_controller=btn,
206            transition_delay=tdelay,
207            size=(img_width, 0.5 * img_width),
208            position=(h - img_width * 0.5, img_v),
209            texture=bui.gettexture('courtyardPreview'),
210            mesh_opaque=mesh_opaque,
211            mesh_transparent=mesh_transparent,
212            mask_texture=mask_tex,
213        )
214        if not bui.app.classic.did_menu_intro:
215            bui.app.classic.did_menu_intro = True
216
217        self._b4: bui.Widget | None
218        self._b5: bui.Widget | None
219        self._b6: bui.Widget | None
220
221        if bool(False):
222            bui.textwidget(
223                parent=self._root_widget,
224                size=(0, 0),
225                position=(self._width * 0.5, self._height + y_extra - 44),
226                transition_delay=tdelay,
227                text=bui.Lstr(resource=f'{self._r}.versusExamplesText'),
228                flatness=1.0,
229                scale=1.2,
230                h_align='center',
231                v_align='center',
232                shadow=1.0,
233            )
234            h = self._width * 0.5 - b_space
235            tdelay = t_delay_base + t_delay_scale * 0.7
236            self._b4 = btn = bui.buttonwidget(
237                parent=self._root_widget,
238                autoselect=True,
239                size=(b_width, b_height),
240                on_activate_call=bui.Call(self._do_game, 'ctf'),
241                transition_delay=tdelay,
242                position=(h - b_width * 0.5, b_v),
243                label='',
244                button_type='square',
245            )
246            bui.textwidget(
247                parent=self._root_widget,
248                draw_controller=btn,
249                transition_delay=tdelay,
250                size=(0, 0),
251                position=(h, label_height),
252                maxwidth=b_width * 0.7,
253                text=bui.Lstr(translate=('gameNames', 'Capture the Flag')),
254                scale=1.3,
255                h_align='center',
256                v_align='center',
257            )
258            bui.imagewidget(
259                parent=self._root_widget,
260                draw_controller=btn,
261                size=(img_width, 0.5 * img_width),
262                transition_delay=tdelay,
263                position=(h - img_width * 0.5, img_v),
264                texture=bui.gettexture('bridgitPreview'),
265                mesh_opaque=mesh_opaque,
266                mesh_transparent=mesh_transparent,
267                mask_texture=mask_tex,
268            )
269
270            h = self._width * 0.5
271            tdelay = t_delay_base + t_delay_scale * 0.65
272            self._b5 = btn = bui.buttonwidget(
273                parent=self._root_widget,
274                autoselect=True,
275                size=(b_width, b_height),
276                on_activate_call=bui.Call(self._do_game, 'hockey'),
277                position=(h - b_width * 0.5, b_v),
278                label='',
279                button_type='square',
280                transition_delay=tdelay,
281            )
282            bui.textwidget(
283                parent=self._root_widget,
284                draw_controller=btn,
285                transition_delay=tdelay,
286                size=(0, 0),
287                position=(h, label_height),
288                maxwidth=b_width * 0.7,
289                text=bui.Lstr(translate=('gameNames', 'Hockey')),
290                scale=1.3,
291                h_align='center',
292                v_align='center',
293            )
294            bui.imagewidget(
295                parent=self._root_widget,
296                draw_controller=btn,
297                size=(img_width, 0.5 * img_width),
298                transition_delay=tdelay,
299                position=(h - img_width * 0.5, img_v),
300                texture=bui.gettexture('hockeyStadiumPreview'),
301                mesh_opaque=mesh_opaque,
302                mesh_transparent=mesh_transparent,
303                mask_texture=mask_tex,
304            )
305            h = self._width * 0.5 + b_space
306            tdelay = t_delay_base + t_delay_scale * 0.6
307            self._b6 = btn = bui.buttonwidget(
308                parent=self._root_widget,
309                autoselect=True,
310                size=(b_width, b_height),
311                on_activate_call=bui.Call(self._do_game, 'epic'),
312                transition_delay=tdelay,
313                position=(h - b_width * 0.5, b_v),
314                label='',
315                button_type='square',
316            )
317            bui.textwidget(
318                parent=self._root_widget,
319                draw_controller=btn,
320                transition_delay=tdelay,
321                size=(0, 0),
322                position=(h, label_height),
323                maxwidth=b_width * 0.7,
324                text=bui.Lstr(resource=f'{self._r}.epicModeText'),
325                scale=1.3,
326                h_align='center',
327                v_align='center',
328            )
329            bui.imagewidget(
330                parent=self._root_widget,
331                draw_controller=btn,
332                transition_delay=tdelay,
333                size=(img_width, 0.5 * img_width),
334                position=(h - img_width * 0.5, img_v),
335                texture=bui.gettexture('tipTopPreview'),
336                mesh_opaque=mesh_opaque,
337                mesh_transparent=mesh_transparent,
338                mask_texture=mask_tex,
339            )
340        else:
341            self._b4 = self._b5 = self._b6 = None
342
343        self._b7: bui.Widget | None
344        if bui.app.env.arcade:
345            self._b7 = bui.buttonwidget(
346                parent=self._root_widget,
347                autoselect=True,
348                size=(b_width, 50),
349                color=(0.45, 0.55, 0.45),
350                textcolor=(0.7, 0.8, 0.7),
351                scale=0.5,
352                position=(self._width * 0.5 - 60.0, b_v - 70.0),
353                transition_delay=tdelay,
354                label=bui.Lstr(resource=f'{self._r}.fullMenuText'),
355                on_activate_call=self._do_full_menu,
356            )
357        else:
358            self._b7 = None
359        self._restore_state()
360        self._update()
361        self._update_timer = bui.AppTimer(
362            1.0, bui.WeakCall(self._update), repeat=True
363        )

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:
365    @override
366    def get_main_window_state(self) -> bui.MainWindowState:
367        # Support recreating our window for back/refresh purposes.
368        cls = type(self)
369        return bui.BasicMainWindowState(
370            create_call=lambda transition, origin_widget: cls(
371                transition=transition, origin_widget=origin_widget
372            )
373        )

Return a WindowState to recreate this window, if supported.

@override
def on_main_window_close(self) -> None:
375    @override
376    def on_main_window_close(self) -> None:
377        self._save_state()

Called before transitioning out a main window.

A good opportunity to save window state/etc.