# Released under the MIT License. See LICENSE for details.
#
"""UI functionality for running the game in kiosk mode."""
from __future__ import annotations
from typing import override
import bascenev1 as bs
import bauiv1 as bui
[docs]
class KioskWindow(bui.MainWindow):
"""Kiosk mode window."""
def __init__(
self,
transition: str | None = 'in_right',
origin_widget: bui.Widget | None = None,
):
# pylint: disable=too-many-locals, too-many-statements
from bauiv1lib.confirm import QuitWindow
assert bui.app.classic is not None
self._width = 720.0
self._height = 340.0
def _do_cancel() -> None:
QuitWindow(swish=True, quit_type=bui.QuitType.BACK)
super().__init__(
root_widget=bui.containerwidget(
size=(self._width, self._height),
# transition=transition,
on_cancel_call=_do_cancel,
background=False,
stack_offset=(0, -130),
),
transition=transition,
origin_widget=origin_widget,
)
self._r = 'kioskWindow'
self._show_multiplayer = False
# Let's reset all random player names every time we hit the main menu.
bs.reset_random_player_names()
# Reset achievements too (at least locally).
bui.app.config['Achievements'] = {}
t_delay_base = 0.0
t_delay_scale = 0.0
if not bui.app.classic.did_menu_intro:
t_delay_base = 1.0
t_delay_scale = 1.0
mesh_opaque = bui.getmesh('level_select_button_opaque')
mesh_transparent = bui.getmesh('level_select_button_transparent')
mask_tex = bui.gettexture('mapPreviewMask')
y_extra = 130.0 + (0.0 if self._show_multiplayer else -130.0)
b_width = 250.0
b_height = 200.0
b_space = 280.0
b_v = 80.0 + y_extra
label_height = 130.0 + y_extra
img_width = 180.0
img_v = 158.0 + y_extra
if self._show_multiplayer:
tdelay = t_delay_base + t_delay_scale * 1.3
bui.textwidget(
parent=self._root_widget,
size=(0, 0),
position=(self._width * 0.5, self._height + y_extra - 44),
transition_delay=tdelay,
text=bui.Lstr(resource=f'{self._r}.singlePlayerExamplesText'),
flatness=1.0,
scale=1.2,
h_align='center',
v_align='center',
shadow=1.0,
)
else:
tdelay = t_delay_base + t_delay_scale * 0.7
bui.textwidget(
parent=self._root_widget,
size=(0, 0),
position=(self._width * 0.5, self._height + y_extra - 34),
transition_delay=tdelay,
text=(
bui.Lstr(
resource='demoText',
fallback_resource='mainMenu.demoMenuText',
)
if bui.app.env.demo
else 'ARCADE'
),
flatness=1.0,
scale=1.2,
h_align='center',
v_align='center',
shadow=1.0,
)
h = self._width * 0.5 - b_space
tdelay = t_delay_base + t_delay_scale * 0.7
self._b1 = btn = bui.buttonwidget(
parent=self._root_widget,
autoselect=True,
size=(b_width, b_height),
on_activate_call=bui.Call(self._do_game, 'easy'),
transition_delay=tdelay,
position=(h - b_width * 0.5, b_v),
label='',
button_type='square',
)
bui.textwidget(
parent=self._root_widget,
draw_controller=btn,
transition_delay=tdelay,
size=(0, 0),
position=(h, label_height),
maxwidth=b_width * 0.7,
text=bui.Lstr(resource=f'{self._r}.easyText'),
scale=1.3,
h_align='center',
v_align='center',
)
bui.imagewidget(
parent=self._root_widget,
draw_controller=btn,
size=(img_width, 0.5 * img_width),
transition_delay=tdelay,
position=(h - img_width * 0.5, img_v),
texture=bui.gettexture('doomShroomPreview'),
mesh_opaque=mesh_opaque,
mesh_transparent=mesh_transparent,
mask_texture=mask_tex,
)
h = self._width * 0.5
tdelay = t_delay_base + t_delay_scale * 0.65
self._b2 = btn = bui.buttonwidget(
parent=self._root_widget,
autoselect=True,
size=(b_width, b_height),
on_activate_call=bui.Call(self._do_game, 'medium'),
position=(h - b_width * 0.5, b_v),
label='',
button_type='square',
transition_delay=tdelay,
)
bui.textwidget(
parent=self._root_widget,
draw_controller=btn,
transition_delay=tdelay,
size=(0, 0),
position=(h, label_height),
maxwidth=b_width * 0.7,
text=bui.Lstr(resource=f'{self._r}.mediumText'),
scale=1.3,
h_align='center',
v_align='center',
)
bui.imagewidget(
parent=self._root_widget,
draw_controller=btn,
size=(img_width, 0.5 * img_width),
transition_delay=tdelay,
position=(h - img_width * 0.5, img_v),
texture=bui.gettexture('footballStadiumPreview'),
mesh_opaque=mesh_opaque,
mesh_transparent=mesh_transparent,
mask_texture=mask_tex,
)
h = self._width * 0.5 + b_space
tdelay = t_delay_base + t_delay_scale * 0.6
self._b3 = btn = bui.buttonwidget(
parent=self._root_widget,
autoselect=True,
size=(b_width, b_height),
on_activate_call=bui.Call(self._do_game, 'hard'),
transition_delay=tdelay,
position=(h - b_width * 0.5, b_v),
label='',
button_type='square',
)
bui.textwidget(
parent=self._root_widget,
draw_controller=btn,
transition_delay=tdelay,
size=(0, 0),
position=(h, label_height),
maxwidth=b_width * 0.7,
text='Hard',
scale=1.3,
h_align='center',
v_align='center',
)
bui.imagewidget(
parent=self._root_widget,
draw_controller=btn,
transition_delay=tdelay,
size=(img_width, 0.5 * img_width),
position=(h - img_width * 0.5, img_v),
texture=bui.gettexture('courtyardPreview'),
mesh_opaque=mesh_opaque,
mesh_transparent=mesh_transparent,
mask_texture=mask_tex,
)
if not bui.app.classic.did_menu_intro:
bui.app.classic.did_menu_intro = True
self._b4: bui.Widget | None
self._b5: bui.Widget | None
self._b6: bui.Widget | None
if bool(False):
bui.textwidget(
parent=self._root_widget,
size=(0, 0),
position=(self._width * 0.5, self._height + y_extra - 44),
transition_delay=tdelay,
text=bui.Lstr(resource=f'{self._r}.versusExamplesText'),
flatness=1.0,
scale=1.2,
h_align='center',
v_align='center',
shadow=1.0,
)
h = self._width * 0.5 - b_space
tdelay = t_delay_base + t_delay_scale * 0.7
self._b4 = btn = bui.buttonwidget(
parent=self._root_widget,
autoselect=True,
size=(b_width, b_height),
on_activate_call=bui.Call(self._do_game, 'ctf'),
transition_delay=tdelay,
position=(h - b_width * 0.5, b_v),
label='',
button_type='square',
)
bui.textwidget(
parent=self._root_widget,
draw_controller=btn,
transition_delay=tdelay,
size=(0, 0),
position=(h, label_height),
maxwidth=b_width * 0.7,
text=bui.Lstr(translate=('gameNames', 'Capture the Flag')),
scale=1.3,
h_align='center',
v_align='center',
)
bui.imagewidget(
parent=self._root_widget,
draw_controller=btn,
size=(img_width, 0.5 * img_width),
transition_delay=tdelay,
position=(h - img_width * 0.5, img_v),
texture=bui.gettexture('bridgitPreview'),
mesh_opaque=mesh_opaque,
mesh_transparent=mesh_transparent,
mask_texture=mask_tex,
)
h = self._width * 0.5
tdelay = t_delay_base + t_delay_scale * 0.65
self._b5 = btn = bui.buttonwidget(
parent=self._root_widget,
autoselect=True,
size=(b_width, b_height),
on_activate_call=bui.Call(self._do_game, 'hockey'),
position=(h - b_width * 0.5, b_v),
label='',
button_type='square',
transition_delay=tdelay,
)
bui.textwidget(
parent=self._root_widget,
draw_controller=btn,
transition_delay=tdelay,
size=(0, 0),
position=(h, label_height),
maxwidth=b_width * 0.7,
text=bui.Lstr(translate=('gameNames', 'Hockey')),
scale=1.3,
h_align='center',
v_align='center',
)
bui.imagewidget(
parent=self._root_widget,
draw_controller=btn,
size=(img_width, 0.5 * img_width),
transition_delay=tdelay,
position=(h - img_width * 0.5, img_v),
texture=bui.gettexture('hockeyStadiumPreview'),
mesh_opaque=mesh_opaque,
mesh_transparent=mesh_transparent,
mask_texture=mask_tex,
)
h = self._width * 0.5 + b_space
tdelay = t_delay_base + t_delay_scale * 0.6
self._b6 = btn = bui.buttonwidget(
parent=self._root_widget,
autoselect=True,
size=(b_width, b_height),
on_activate_call=bui.Call(self._do_game, 'epic'),
transition_delay=tdelay,
position=(h - b_width * 0.5, b_v),
label='',
button_type='square',
)
bui.textwidget(
parent=self._root_widget,
draw_controller=btn,
transition_delay=tdelay,
size=(0, 0),
position=(h, label_height),
maxwidth=b_width * 0.7,
text=bui.Lstr(resource=f'{self._r}.epicModeText'),
scale=1.3,
h_align='center',
v_align='center',
)
bui.imagewidget(
parent=self._root_widget,
draw_controller=btn,
transition_delay=tdelay,
size=(img_width, 0.5 * img_width),
position=(h - img_width * 0.5, img_v),
texture=bui.gettexture('tipTopPreview'),
mesh_opaque=mesh_opaque,
mesh_transparent=mesh_transparent,
mask_texture=mask_tex,
)
else:
self._b4 = self._b5 = self._b6 = None
self._b7: bui.Widget | None
if bui.app.env.arcade:
self._b7 = bui.buttonwidget(
parent=self._root_widget,
autoselect=True,
size=(b_width, 50),
color=(0.45, 0.55, 0.45),
textcolor=(0.7, 0.8, 0.7),
scale=0.5,
position=(self._width * 0.5 - 60.0, b_v - 70.0),
transition_delay=tdelay,
label=bui.Lstr(resource=f'{self._r}.fullMenuText'),
on_activate_call=self._do_full_menu,
)
else:
self._b7 = None
self._restore_state()
self._update()
self._update_timer = bui.AppTimer(
1.0, bui.WeakCall(self._update), repeat=True
)
[docs]
@override
def get_main_window_state(self) -> bui.MainWindowState:
# Support recreating our window for back/refresh purposes.
cls = type(self)
return bui.BasicMainWindowState(
create_call=lambda transition, origin_widget: cls(
transition=transition, origin_widget=origin_widget
)
)
[docs]
@override
def on_main_window_close(self) -> None:
self._save_state()
def _restore_state(self) -> None:
assert bui.app.classic is not None
sel_name = bui.app.ui_v1.window_states.get(type(self))
sel: bui.Widget | None
if sel_name == 'b1':
sel = self._b1
elif sel_name == 'b2':
sel = self._b2
elif sel_name == 'b3':
sel = self._b3
elif sel_name == 'b4':
sel = self._b4
elif sel_name == 'b5':
sel = self._b5
elif sel_name == 'b6':
sel = self._b6
elif sel_name == 'b7':
sel = self._b7
else:
sel = self._b1
if sel:
bui.containerwidget(edit=self._root_widget, selected_child=sel)
def _save_state(self) -> None:
sel = self._root_widget.get_selected_child()
if sel == self._b1:
sel_name = 'b1'
elif sel == self._b2:
sel_name = 'b2'
elif sel == self._b3:
sel_name = 'b3'
elif sel == self._b4:
sel_name = 'b4'
elif sel == self._b5:
sel_name = 'b5'
elif sel == self._b6:
sel_name = 'b6'
elif sel == self._b7:
sel_name = 'b7'
else:
sel_name = 'b1'
assert bui.app.classic is not None
bui.app.ui_v1.window_states[type(self)] = sel_name
def _update(self) -> None:
plus = bui.app.plus
assert plus is not None
# Kiosk-mode is designed to be used signed-out... try for force
# the issue.
if plus.get_v1_account_state() == 'signed_in':
# _bs.sign_out()
# FIXME: Try to delete player profiles here too.
pass
else:
# Also make sure there's no player profiles.
appconfig = bui.app.config
appconfig['Player Profiles'] = {}
def _do_game(self, mode: str) -> None:
assert bui.app.classic is not None
self._save_state()
if mode in ['epic', 'ctf', 'hockey']:
appconfig = bui.app.config
if 'Team Tournament Playlists' not in appconfig:
appconfig['Team Tournament Playlists'] = {}
if 'Free-for-All Playlists' not in appconfig:
appconfig['Free-for-All Playlists'] = {}
appconfig['Show Tutorial'] = False
if mode == 'epic':
appconfig['Free-for-All Playlists']['Just Epic Elim'] = [
{
'settings': {
'Epic Mode': 1,
'Lives Per Player': 1,
'Respawn Times': 1.0,
'Time Limit': 0,
'map': 'Tip Top',
},
'type': 'bs_elimination.EliminationGame',
}
]
appconfig['Free-for-All Playlist Selection'] = 'Just Epic Elim'
bui.fade_screen(
False,
endcall=bui.Call(
bui.pushcall,
bui.Call(bs.new_host_session, bs.FreeForAllSession),
),
)
else:
if mode == 'ctf':
appconfig['Team Tournament Playlists']['Just CTF'] = [
{
'settings': {
'Epic Mode': False,
'Flag Idle Return Time': 30,
'Flag Touch Return Time': 0,
'Respawn Times': 1.0,
'Score to Win': 3,
'Time Limit': 0,
'map': 'Bridgit',
},
'type': 'bs_capture_the_flag.CTFGame',
}
]
appconfig['Team Tournament Playlist Selection'] = 'Just CTF'
else:
appconfig['Team Tournament Playlists']['Just Hockey'] = [
{
'settings': {
'Respawn Times': 1.0,
'Score to Win': 1,
'Time Limit': 0,
'map': 'Hockey Stadium',
},
'type': 'bs_hockey.HockeyGame',
}
]
appconfig['Team Tournament Playlist Selection'] = (
'Just Hockey'
)
bui.fade_screen(
False,
endcall=bui.Call(
bui.pushcall,
bui.Call(bs.new_host_session, bs.DualTeamSession),
),
)
bui.containerwidget(edit=self._root_widget, transition='out_left')
return
game = (
'Easy:Onslaught Training'
if mode == 'easy'
else (
'Easy:Rookie Football'
if mode == 'medium'
else 'Easy:Uber Onslaught'
)
)
cfg = bui.app.config
cfg['Selected Coop Game'] = game
cfg.commit()
if bui.app.classic.launch_coop_game(game, force=True):
bui.containerwidget(edit=self._root_widget, transition='out_left')
def _do_full_menu(self) -> None:
# pylint: disable=cyclic-import
from bauiv1lib.mainmenu import MainMenuWindow
# no-op if we're not in control.
if not self.main_window_has_control():
return
assert bui.app.classic is not None
self._save_state()
bui.app.classic.did_menu_intro = True # prevent delayed transition-in
self.main_window_replace(MainMenuWindow())
# Docs-generation hack; import some stuff that we likely only forward-declared
# in our actual source code so that docs tools can find it.
from typing import (Coroutine, Any, Literal, Callable,
Generator, Awaitable, Sequence, Self)
import asyncio
from concurrent.futures import Future
from pathlib import Path
from enum import Enum