Wired up private hosting playlist selection

This commit is contained in:
Eric Froemling 2021-01-26 17:49:30 -08:00
parent 49f8e4ddba
commit 398158f5f5
12 changed files with 426 additions and 275 deletions

View File

@ -3936,22 +3936,22 @@
"build/prefab/full/linux_arm64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/d2/41/e8362cfe9f6fd6dc882858021874",
"build/prefab/full/linux_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/83/ff/29cf07587f212d5278bb5ed92e75",
"build/prefab/full/linux_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/90/e7/c4834a3b41d8f9d837071dc0800a",
"build/prefab/full/linux_x86_64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/ce/3d/0febcd4db42fe5dedb701c60bddf",
"build/prefab/full/linux_x86_64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/c5/d8/3d7ca6668af72200a6eea00c6d84",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/79/f2/3381ea14e11854a9e8804953bb06",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/80/00/a02fdfe67796bc04a78dbd6ed353",
"build/prefab/full/mac_arm64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/19/36/ebf5f0f1c7a2923f7e3c4ee292ad",
"build/prefab/full/linux_x86_64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/7b/a8/9890f10c94576c95314f8af13691",
"build/prefab/full/linux_x86_64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/c1/5b/6f127d8dab6ee7926970fc6a608a",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/48/23/07790e69616cb6b4cc363d6387f3",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/97/45/3b791f71346eaa3336ce6d272710",
"build/prefab/full/mac_arm64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/a2/a0/0f9815da065672d14b75b078a2d7",
"build/prefab/full/mac_arm64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/fb/4f/fbddddae2d5963a1ec6f97234db3",
"build/prefab/full/mac_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/0d/32/8755d570c2e74316497bdd290f0d",
"build/prefab/full/mac_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/ff/3f/73f4d04c55fdc6ed0c94bfee3acc",
"build/prefab/full/mac_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/24/30/863f0c9948669219f66b117502bc",
"build/prefab/full/mac_x86_64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/3c/dc/317d57caa8b27d5b685193cf3de9",
"build/prefab/full/mac_x86_64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/b5/27/9acf2ec153e1ce69bc695065014e",
"build/prefab/full/mac_x86_64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/cd/8a/3fc0cca1383b6af2a4f8a5ae2cfe",
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/20/16/c0cf342b1971dcbc757abd29bdc5",
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/4b/24/a951fe7baf708257f00d992624f4",
"build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/1b/06/f81485b10ac37b58a012bc158952",
"build/prefab/full/windows_x86/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/85/44/92172591b72302dc4909b0e91108",
"build/prefab/full/windows_x86/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/d4/e6/8d5a9ffcf32588f8b770a88a80d5",
"build/prefab/full/windows_x86_server/debug/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/c7/9c/e55d58caf88ad6bb88f1a5ebfdbf",
"build/prefab/full/windows_x86_server/release/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/f2/6e/981553869590e8367854b5df2802",
"build/prefab/full/windows_x86/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/e9/27/5e1dae33935559c07c35af56e201",
"build/prefab/full/windows_x86/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/f8/af/4304308263b7fa98aaa5d430e753",
"build/prefab/full/windows_x86_server/debug/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/74/fd/099a83595d8843537e945373fe99",
"build/prefab/full/windows_x86_server/release/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/a2/cb/1590daac51edd95af2e1e243f435",
"build/prefab/lib/linux_arm64/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/34/21/016123d9ec8293ce92ba910dd00a",
"build/prefab/lib/linux_arm64/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/6f/f8/cf46e7c33a0a237e2c6f19ef4f92",
"build/prefab/lib/linux_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/10/67/5a3d6131d1b1fdf9f629301b68db",
@ -3960,12 +3960,12 @@
"build/prefab/lib/linux_x86_64/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/35/7c/b5f26ca01907df6ea80ff3ae5861",
"build/prefab/lib/linux_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/8c/76/9ea770a68773fd4a08fb78855fbb",
"build/prefab/lib/linux_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/1d/b3/329956f15f5cb76a63bd4fd3e0cc",
"build/prefab/lib/mac_arm64/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/30/04/7b7c4f847fd992b23719f755981a",
"build/prefab/lib/mac_arm64/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/bc/20/e13686c62052e3e388431d4859aa",
"build/prefab/lib/mac_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/a3/a1/1bb8a1926628e34054aeca5a0178",
"build/prefab/lib/mac_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/5a/33/c232d2c633bf70915e1a8eecdf95",
"build/prefab/lib/mac_x86_64/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/84/8c/2930553d642210c81b6342bffb9f",
"build/prefab/lib/mac_x86_64/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/68/22/cc580cef75b9e452de66095ba62b",
"build/prefab/lib/mac_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/93/2d/b6482a7ce6957156fe050f4cc9dc",
"build/prefab/lib/mac_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/d1/a2/1d592f5d21dd39d7b16da8f1c8c4"
"build/prefab/lib/mac_arm64/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/c6/90/79c30dd05db52cd4d47c69a89f24",
"build/prefab/lib/mac_arm64/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/3f/1e/21c32a8bbcd86b1005c5125bfbf6",
"build/prefab/lib/mac_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/9d/68/36828f4dbd36530a5c45d5b3cdaa",
"build/prefab/lib/mac_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/68/24/667e0f09f4e5fc7a7498f571dc38",
"build/prefab/lib/mac_x86_64/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/47/8d/f3bfac44ac9cae60574949c2770a",
"build/prefab/lib/mac_x86_64/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/31/19/d117f00a867c7725148b6e1d3932",
"build/prefab/lib/mac_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/8c/8e/c7bf8dfe338e75eb0026ec81691c",
"build/prefab/lib/mac_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/fc/ba/555e3d9c823d4cb19541169a514a"
}

View File

@ -974,6 +974,7 @@
<w>homebrew</w>
<w>hometest</w>
<w>hostconfig</w>
<w>hostingconfig</w>
<w>hostingstate</w>
<w>hostuser</w>
<w>hout</w>
@ -1458,6 +1459,7 @@
<w>oghashes</w>
<w>ogval</w>
<w>oival</w>
<w>okbtn</w>
<w>oldlady</w>
<w>onln</w>
<w>onscreencountdown</w>
@ -1893,6 +1895,7 @@
<w>sessionteam's</w>
<w>sessionteams</w>
<w>sessiontype</w>
<w>sessiontypestr</w>
<w>setactivity</w>
<w>setalpha</w>
<w>setbuild</w>

View File

@ -57,6 +57,11 @@ class UISubsystem:
self.heading_color = (0.72, 0.7, 0.75)
self.infotextcolor = (0.7, 0.9, 0.7)
# Switch our overall game selection UI flow between Play and
# Private-party playlist selection modes; should do this in
# a more elegant way once we revamp high level UI stuff a bit.
self.selecting_private_party_playlist: bool = False
@property
def uiscale(self) -> ba.UIScale:
"""Current ui scale for the app."""

View File

@ -217,6 +217,15 @@ class GatherWindow(ba.Window):
def __del__(self) -> None:
_ba.set_party_icon_always_visible(False)
def playlist_select(self, origin_widget: ba.Widget) -> None:
"""Called by the private-hosting tab to select a playlist."""
from bastd.ui.play import PlayWindow
self._save_state()
ba.containerwidget(edit=self._root_widget, transition='out_left')
ba.app.ui.selecting_private_party_playlist = True
ba.app.ui.set_main_menu_window(
PlayWindow(origin_widget=origin_widget).get_root_widget())
def _set_tab(self, tab_id: TabID) -> None:
if self._current_tab is tab_id:
return

View File

@ -8,7 +8,7 @@ import os
import copy
import time
from enum import Enum
from dataclasses import dataclass
from dataclasses import dataclass, asdict
from typing import TYPE_CHECKING, cast
import ba
@ -18,7 +18,7 @@ from bastd.ui.gather import GatherTab
from bastd.ui import getcurrency
if TYPE_CHECKING:
from typing import Optional, Dict, Any, List
from typing import Optional, Dict, Any, List, Type
from bastd.ui.gather import GatherWindow
# Print a bit of info about queries, etc.
@ -56,6 +56,18 @@ class HostingState:
free_host_minutes_remaining: Optional[float] = None
@dataclass
class HostingConfig:
"""Config we provide when hosting."""
session_type: str = 'ffa'
playlist_name: str = 'Unknown'
randomize: bool = False
tutorial: bool = False
custom_team_names: Optional[List[str]] = None
custom_team_colors: Optional[List[List[float]]] = None
playlist: Optional[List[Dict[str, Any]]] = None
class PrivateGatherTab(GatherTab):
"""The private tab in the gather UI"""
@ -82,6 +94,12 @@ class PrivateGatherTab(GatherTab):
self._showing_not_signed_in_screen = False
self._create_time = time.time()
self._last_action_send_time: Optional[float] = None
self._connect_press_time: Optional[float] = None
try:
self._hostingconfig = self._build_hosting_config()
except Exception:
ba.print_exception('Error building hosting config')
self._hostingconfig = HostingConfig()
def on_activate(
self,
@ -159,6 +177,55 @@ class PrivateGatherTab(GatherTab):
return self._container
def _build_hosting_config(self) -> HostingConfig:
from bastd.ui.playlist import PlaylistTypeVars
from ba.internal import filter_playlist
hcfg = HostingConfig()
cfg = ba.app.config
sessiontypestr = cfg.get('Private Party Host Session Type')
if not isinstance(sessiontypestr, str):
raise RuntimeError(f'Invalid sessiontype {sessiontypestr}')
hcfg.session_type = sessiontypestr
sessiontype: Type[ba.Session]
if hcfg.session_type == 'ffa':
sessiontype = ba.FreeForAllSession
elif hcfg.session_type == 'teams':
sessiontype = ba.DualTeamSession
else:
raise RuntimeError('fInvalid sessiontype: {hcfg.session_type}')
pvars = PlaylistTypeVars(sessiontype)
playlist_name = ba.app.config.get(
f'{pvars.config_name} Playlist Selection')
if not isinstance(playlist_name, str):
playlist_name = '__default__'
hcfg.playlist_name = (pvars.default_list_name.evaluate()
if playlist_name == '__default__' else
playlist_name)
if playlist_name == '__default__':
playlist = pvars.get_default_list_call()
else:
playlist = cfg[f'{pvars.config_name} Playlists'][playlist_name]
hcfg.playlist = filter_playlist(playlist, sessiontype)
randomize = cfg.get(f'{pvars.config_name} Playlist Randomize')
if not isinstance(randomize, bool):
randomize = False
hcfg.randomize = randomize
tutorial = cfg.get('Show Tutorial')
if not isinstance(tutorial, bool):
tutorial = False
hcfg.tutorial = tutorial
if hcfg.session_type == 'teams':
hcfg.custom_team_names = copy.copy(cfg.get('Custom Team Names'))
hcfg.custom_team_colors = copy.copy(cfg.get('Custom Team Colors'))
return hcfg
def on_deactivate(self) -> None:
self._update_timer = None
@ -344,7 +411,7 @@ class PrivateGatherTab(GatherTab):
'manualConnectText'),
position=(self._c_width * 0.5 - 150,
self._c_height - 350),
on_activate_call=self._connect_press,
on_activate_call=self._join_connect_press,
autoselect=True)
ba.textwidget(edit=self._join_party_code_text,
on_return_press_call=btn.activate)
@ -358,7 +425,6 @@ class PrivateGatherTab(GatherTab):
# overlay layer so we'd show up above it).
getcurrency.GetCurrencyWindow(modal=True,
origin_widget=self._get_tickets_button)
# self._transition_out()
def _build_host_tab(self) -> None:
# pylint: disable=too-many-branches
@ -457,12 +523,18 @@ class PrivateGatherTab(GatherTab):
size=(400, 70),
color=(0.6, 0.5, 0.6),
textcolor=(0.8, 0.75, 0.8),
label='Default Free-For-All Playlist',
on_activate_call=lambda: ba.screenmessage(
'TODO: WIRE UP PLAYLIST SELECTION'),
label=self._hostingconfig.playlist_name,
on_activate_call=self._playlist_press,
position=(self._c_width * 0.5 - 200, v - 35),
up_widget=self._host_sub_tab_text,
autoselect=True)
# If it appears we're coming back from playlist selection,
# re-select our playlist button.
if ba.app.ui.selecting_private_party_playlist:
ba.containerwidget(edit=self._container,
selected_child=self._host_playlist_button)
ba.app.ui.selecting_private_party_playlist = False
else:
# We've got a current party; show its info.
ba.textwidget(
@ -641,6 +713,10 @@ class PrivateGatherTab(GatherTab):
on_activate_call=self._host_button_press,
autoselect=True)
def _playlist_press(self) -> None:
assert self._host_playlist_button is not None
self.window.playlist_select(origin_widget=self._host_playlist_button)
def _host_copy_press(self) -> None:
assert self._hostingstate.party_code is not None
ba.clipboard_set_text(self._hostingstate.party_code)
@ -656,6 +732,17 @@ class PrivateGatherTab(GatherTab):
f'{time.time()-self._create_time:.2f}')
def _connect_to_party_code(self, code: str) -> None:
# Ignore attempted followup sends for a few seconds.
# (this will reset if we get a response)
now = time.time()
if (self._connect_press_time is not None
and now - self._connect_press_time < 5.0):
self._debug_server_comm(
'not sending private party connect (too soon)')
return
self._connect_press_time = now
self._debug_server_comm('sending private party connect')
_ba.add_transaction(
{
@ -697,9 +784,12 @@ class PrivateGatherTab(GatherTab):
ba.playsound(ba.getsound('error'))
return
self._last_action_send_time = time.time()
_ba.add_transaction({'type': 'PRIVATE_PARTY_START'},
callback=ba.WeakCall(
self._hosting_state_response))
_ba.add_transaction(
{
'type': 'PRIVATE_PARTY_START',
'config': asdict(self._hostingconfig)
},
callback=ba.WeakCall(self._hosting_state_response))
_ba.run_transactions()
else:
@ -713,7 +803,9 @@ class PrivateGatherTab(GatherTab):
self._waiting_for_hosting_state = True
self._refresh_sub_tab()
def _connect_press(self) -> None:
def _join_connect_press(self) -> None:
# Error immediately if its an empty code.
code: Optional[str] = None
if self._join_party_code_text:
code = cast(str, ba.textwidget(query=self._join_party_code_text))
@ -723,10 +815,12 @@ class PrivateGatherTab(GatherTab):
color=(1, 0, 0))
ba.playsound(ba.getsound('error'))
return
self._connect_to_party_code(code)
def _connect_response(self, result: Optional[Dict[str, Any]]) -> None:
try:
self._connect_press_time = None
if result is None:
raise RuntimeError()
cresult = dataclass_from_dict(ConnectResult, result)

View File

@ -1027,6 +1027,8 @@ class MainMenuWindow(ba.Window):
from bastd.ui.play import PlayWindow
self._save_state()
ba.containerwidget(edit=self._root_widget, transition='out_left')
ba.app.ui.selecting_private_party_playlist = False
ba.app.ui.set_main_menu_window(
PlayWindow(origin_widget=self._start_button).get_root_widget())

View File

@ -27,11 +27,14 @@ class PlayWindow(ba.Window):
# have a visual hitch when the user taps them.
threading.Thread(target=self._preload_modules).start()
new_style = True
# We can currently be used either for main menu duty or for selecting
# playlists (should make this more elegant/general).
self._is_main_menu = not ba.app.ui.selecting_private_party_playlist
uiscale = ba.app.ui.uiscale
width = 1000 if uiscale is ba.UIScale.SMALL else 800
x_offs = 100 if uiscale is ba.UIScale.SMALL else 0
height = 550 if new_style else 400
height = 550
button_width = 400
scale_origin: Optional[Tuple[float, float]]
@ -50,14 +53,12 @@ class PlayWindow(ba.Window):
transition=transition,
toolbar_visibility='menu_full',
scale_origin_stack_offset=scale_origin,
scale=((1.6 if new_style else 1.52) if uiscale is ba.UIScale.SMALL
else 0.9 if uiscale is ba.UIScale.MEDIUM else 0.8),
stack_offset=((0, 0) if new_style else (
10, 7)) if uiscale is ba.UIScale.SMALL else (0, 0)))
scale=(1.6 if uiscale is ba.UIScale.SMALL else
0.9 if uiscale is ba.UIScale.MEDIUM else 0.8),
stack_offset=(0, 0) if uiscale is ba.UIScale.SMALL else (0, 0)))
self._back_button = back_button = btn = ba.buttonwidget(
parent=self._root_widget,
position=(55 + x_offs, height - 132) if new_style else
(55, height - 92),
position=(55 + x_offs, height - 132),
size=(120, 60),
scale=1.1,
text_res_scale=1.5,
@ -66,17 +67,21 @@ class PlayWindow(ba.Window):
label=ba.Lstr(resource='backText'),
button_type='back')
txt = ba.textwidget(parent=self._root_widget,
position=(width * 0.5,
height - (101 if new_style else 61)),
size=(0, 0),
text=ba.Lstr(resource=self._r + '.titleText'),
scale=1.7,
res_scale=2.0,
maxwidth=400,
color=ba.app.ui.heading_color,
h_align='center',
v_align='center')
txt = ba.textwidget(
parent=self._root_widget,
position=(width * 0.5, height - 101),
# position=(width * 0.5, height -
# (101 if main_menu else 61)),
size=(0, 0),
text=ba.Lstr(resource=(
self._r +
'.titleText') if self._is_main_menu else 'playlistsText'),
scale=1.7,
res_scale=2.0,
maxwidth=400,
color=ba.app.ui.heading_color,
h_align='center',
v_align='center')
ba.buttonwidget(edit=btn,
button_type='backSmall',
@ -85,14 +90,14 @@ class PlayWindow(ba.Window):
if ba.app.ui.use_toolbars and uiscale is ba.UIScale.SMALL:
ba.textwidget(edit=txt, text='')
v = height - (110 if new_style else 60)
v = height - (110 if self._is_main_menu else 60)
v -= 100
clr = (0.6, 0.7, 0.6, 1.0)
v -= 280 if new_style else 180
v -= 280 if self._is_main_menu else 180
v += (30
if ba.app.ui.use_toolbars and uiscale is ba.UIScale.SMALL else 0)
hoffs = x_offs + 80 if new_style else 0
scl = 1.13 if new_style else 0.68
hoffs = x_offs + 80 if self._is_main_menu else 0
scl = 1.13 if self._is_main_menu else 0.68
self._lineup_tex = ba.gettexture('playerLineup')
angry_computer_transparent_model = ba.getmodel(
@ -107,93 +112,101 @@ class PlayWindow(ba.Window):
'playerLineup4Transparent')
self._eyes_model = ba.getmodel('plasticEyesTransparent')
self._coop_button = btn = ba.buttonwidget(
parent=self._root_widget,
position=(hoffs, v + (scl * 15 if new_style else 0)),
size=(scl * button_width, scl * (300 if new_style else 360)),
extra_touch_border_scale=0.1,
autoselect=True,
label='',
button_type='square',
text_scale=1.13,
on_activate_call=self._coop)
self._coop_button: Optional[ba.Widget] = None
# Only show coop button in main-menu variant.
if self._is_main_menu:
self._coop_button = btn = ba.buttonwidget(
parent=self._root_widget,
position=(hoffs, v + (scl * 15 if self._is_main_menu else 0)),
size=(scl * button_width,
scl * (300 if self._is_main_menu else 360)),
extra_touch_border_scale=0.1,
autoselect=True,
label='',
button_type='square',
text_scale=1.13,
on_activate_call=self._coop)
if ba.app.ui.use_toolbars and uiscale is ba.UIScale.SMALL:
ba.widget(edit=btn,
left_widget=_ba.get_special_widget('back_button'))
ba.widget(edit=btn,
up_widget=_ba.get_special_widget('account_button'))
ba.widget(edit=btn,
down_widget=_ba.get_special_widget('settings_button'))
if ba.app.ui.use_toolbars and uiscale is ba.UIScale.SMALL:
ba.widget(edit=btn,
left_widget=_ba.get_special_widget('back_button'))
ba.widget(edit=btn,
up_widget=_ba.get_special_widget('account_button'))
ba.widget(
edit=btn,
down_widget=_ba.get_special_widget('settings_button'))
self._draw_dude(0,
btn,
hoffs,
v,
scl,
position=(140, 30),
color=(0.72, 0.4, 1.0))
self._draw_dude(1,
btn,
hoffs,
v,
scl,
position=(185, 53),
color=(0.71, 0.5, 1.0))
self._draw_dude(2,
btn,
hoffs,
v,
scl,
position=(220, 27),
color=(0.67, 0.44, 1.0))
self._draw_dude(3,
btn,
hoffs,
v,
scl,
position=(255, 57),
color=(0.7, 0.3, 1.0))
ba.imagewidget(parent=self._root_widget,
draw_controller=btn,
position=(hoffs + scl * 230, v + scl * 153),
size=(scl * 115, scl * 115),
texture=self._lineup_tex,
model_transparent=angry_computer_transparent_model)
self._draw_dude(0,
btn,
hoffs,
v,
scl,
position=(140, 30),
color=(0.72, 0.4, 1.0))
self._draw_dude(1,
btn,
hoffs,
v,
scl,
position=(185, 53),
color=(0.71, 0.5, 1.0))
self._draw_dude(2,
btn,
hoffs,
v,
scl,
position=(220, 27),
color=(0.67, 0.44, 1.0))
self._draw_dude(3,
btn,
hoffs,
v,
scl,
position=(255, 57),
color=(0.7, 0.3, 1.0))
ba.imagewidget(parent=self._root_widget,
draw_controller=btn,
position=(hoffs + scl * 230, v + scl * 153),
size=(scl * 115, scl * 115),
texture=self._lineup_tex,
model_transparent=angry_computer_transparent_model)
ba.textwidget(parent=self._root_widget,
draw_controller=btn,
position=(hoffs + scl * (-10), v + scl * 95),
size=(scl * button_width, scl * 50),
text=ba.Lstr(resource='playModes.singlePlayerCoopText',
fallback_resource='playModes.coopText'),
maxwidth=scl * button_width * 0.7,
res_scale=1.5,
h_align='center',
v_align='center',
color=(0.7, 0.9, 0.7, 1.0),
scale=scl * 2.3)
ba.textwidget(parent=self._root_widget,
draw_controller=btn,
position=(hoffs + scl * (-10), v + scl * 95),
size=(scl * button_width, scl * 50),
text=ba.Lstr(
resource='playModes.singlePlayerCoopText',
fallback_resource='playModes.coopText'),
maxwidth=scl * button_width * 0.7,
res_scale=1.5,
h_align='center',
v_align='center',
color=(0.7, 0.9, 0.7, 1.0),
scale=scl * 2.3)
ba.textwidget(parent=self._root_widget,
draw_controller=btn,
position=(hoffs + scl * (-10), v + (scl * 54)),
size=(scl * button_width, scl * 30),
text=ba.Lstr(resource=self._r + '.oneToFourPlayersText'),
h_align='center',
v_align='center',
scale=0.83 * scl,
flatness=1.0,
maxwidth=scl * button_width * 0.7,
color=clr)
ba.textwidget(parent=self._root_widget,
draw_controller=btn,
position=(hoffs + scl * (-10), v + (scl * 54)),
size=(scl * button_width, scl * 30),
text=ba.Lstr(resource=self._r +
'.oneToFourPlayersText'),
h_align='center',
v_align='center',
scale=0.83 * scl,
flatness=1.0,
maxwidth=scl * button_width * 0.7,
color=clr)
scl = 0.5 if new_style else 0.68
hoffs += 440 if new_style else 260
v += 180 if new_style else 0
scl = 0.5 if self._is_main_menu else 0.68
hoffs += 440 if self._is_main_menu else 216
v += 180 if self._is_main_menu else -68
self._teams_button = btn = ba.buttonwidget(
parent=self._root_widget,
position=(hoffs, v + (scl * 15 if new_style else 0)),
size=(scl * button_width, scl * (300 if new_style else 360)),
position=(hoffs, v + (scl * 15 if self._is_main_menu else 0)),
size=(scl * button_width,
scl * (300 if self._is_main_menu else 360)),
extra_touch_border_scale=0.1,
autoselect=True,
label='',
@ -292,12 +305,13 @@ class PlayWindow(ba.Window):
maxwidth=scl * button_width * 0.7,
color=clr)
hoffs += 0 if new_style else 260
v -= 155 if new_style else 0
hoffs += 0 if self._is_main_menu else 300
v -= 155 if self._is_main_menu else 0
self._free_for_all_button = btn = ba.buttonwidget(
parent=self._root_widget,
position=(hoffs, v + (scl * 15 if new_style else 0)),
size=(scl * button_width, scl * (300 if new_style else 360)),
position=(hoffs, v + (scl * 15 if self._is_main_menu else 0)),
size=(scl * button_width,
scl * (300 if self._is_main_menu else 360)),
extra_touch_border_scale=0.1,
autoselect=True,
label='',
@ -391,12 +405,14 @@ class PlayWindow(ba.Window):
back_button.delete()
ba.containerwidget(edit=self._root_widget,
on_cancel_call=self._back,
selected_child=self._coop_button)
selected_child=self._coop_button
if self._is_main_menu else self._teams_button)
else:
ba.buttonwidget(edit=back_button, on_activate_call=self._back)
ba.containerwidget(edit=self._root_widget,
cancel_button=back_button,
selected_child=self._coop_button)
selected_child=self._coop_button
if self._is_main_menu else self._teams_button)
self._restore_state()
@ -410,12 +426,20 @@ class PlayWindow(ba.Window):
def _back(self) -> None:
# pylint: disable=cyclic-import
from bastd.ui.mainmenu import MainMenuWindow
self._save_state()
ba.app.ui.set_main_menu_window(
MainMenuWindow(transition='in_left').get_root_widget())
ba.containerwidget(edit=self._root_widget,
transition=self._transition_out)
if self._is_main_menu:
from bastd.ui.mainmenu import MainMenuWindow
self._save_state()
ba.app.ui.set_main_menu_window(
MainMenuWindow(transition='in_left').get_root_widget())
ba.containerwidget(edit=self._root_widget,
transition=self._transition_out)
else:
from bastd.ui.gather import GatherWindow
self._save_state()
ba.app.ui.set_main_menu_window(
GatherWindow(transition='in_left').get_root_widget())
ba.containerwidget(edit=self._root_widget,
transition=self._transition_out)
def _coop(self) -> None:
# pylint: disable=cyclic-import
@ -532,7 +556,7 @@ class PlayWindow(ba.Window):
sel = self._root_widget.get_selected_child()
if sel == self._teams_button:
sel_name = 'Team Games'
elif sel == self._coop_button:
elif self._coop_button is not None and sel == self._coop_button:
sel_name = 'Co-op Games'
elif sel == self._free_for_all_button:
sel_name = 'Free-for-All Games'
@ -549,14 +573,15 @@ class PlayWindow(ba.Window):
sel_name = ba.app.ui.window_states.get(type(self))
if sel_name == 'Team Games':
sel = self._teams_button
elif sel_name == 'Co-op Games':
elif sel_name == 'Co-op Games' and self._coop_button is not None:
sel = self._coop_button
elif sel_name == 'Free-for-All Games':
sel = self._free_for_all_button
elif sel_name == 'Back':
sel = self._back_button
else:
sel = self._coop_button
sel = (self._coop_button if self._coop_button is not None else
self._teams_button)
ba.containerwidget(edit=self._root_widget, selected_child=sel)
except Exception:
ba.print_exception(f'Error restoring state for {self}.')

View File

@ -53,6 +53,91 @@ class PlaylistBrowserWindow(ba.Window):
self._sub_width: Optional[float] = None
self._sub_height: Optional[float] = None
self._ensure_standard_playlists_exist()
# Get the current selection (if any).
self._selected_playlist = ba.app.config.get(self._pvars.config_name +
' Playlist Selection')
uiscale = ba.app.ui.uiscale
self._width = 900 if uiscale is ba.UIScale.SMALL else 800
x_inset = 50 if uiscale is ba.UIScale.SMALL else 0
self._height = (480 if uiscale is ba.UIScale.SMALL else
510 if uiscale is ba.UIScale.MEDIUM else 580)
top_extra = 20 if uiscale is ba.UIScale.SMALL else 0
super().__init__(root_widget=ba.containerwidget(
size=(self._width, self._height + top_extra),
transition=transition,
toolbar_visibility='menu_full',
scale_origin_stack_offset=scale_origin,
scale=(1.69 if uiscale is ba.UIScale.SMALL else
1.05 if uiscale is ba.UIScale.MEDIUM else 0.9),
stack_offset=(0, -26) if uiscale is ba.UIScale.SMALL else (0, 0)))
self._back_button: Optional[ba.Widget] = ba.buttonwidget(
parent=self._root_widget,
position=(59 + x_inset, self._height - 70),
size=(120, 60),
scale=1.0,
on_activate_call=self._on_back_press,
autoselect=True,
label=ba.Lstr(resource='backText'),
button_type='back')
ba.containerwidget(edit=self._root_widget,
cancel_button=self._back_button)
txt = self._title_text = ba.textwidget(
parent=self._root_widget,
position=(self._width * 0.5, self._height - 41),
size=(0, 0),
text=self._pvars.window_title_name,
scale=1.3,
res_scale=1.5,
color=ba.app.ui.heading_color,
h_align='center',
v_align='center')
if uiscale is ba.UIScale.SMALL and ba.app.ui.use_toolbars:
ba.textwidget(edit=txt, text='')
ba.buttonwidget(edit=self._back_button,
button_type='backSmall',
size=(60, 54),
position=(59 + x_inset, self._height - 67),
label=ba.charstr(ba.SpecialChar.BACK))
if uiscale is ba.UIScale.SMALL and ba.app.ui.use_toolbars:
self._back_button.delete()
self._back_button = None
ba.containerwidget(edit=self._root_widget,
on_cancel_call=self._on_back_press)
scroll_offs = 33
else:
scroll_offs = 0
self._scroll_width = self._width - (100 + 2 * x_inset)
self._scroll_height = (self._height -
(146 if uiscale is ba.UIScale.SMALL
and ba.app.ui.use_toolbars else 136))
self._scrollwidget = ba.scrollwidget(
parent=self._root_widget,
highlight=False,
size=(self._scroll_width, self._scroll_height),
position=((self._width - self._scroll_width) * 0.5,
65 + scroll_offs))
ba.containerwidget(edit=self._scrollwidget, claims_left_right=True)
self._subcontainer: Optional[ba.Widget] = None
self._config_name_full = self._pvars.config_name + ' Playlists'
self._last_config = None
# Update now and once per second.
# (this should do our initial refresh)
self._update()
self._update_timer = ba.Timer(1.0,
ba.WeakCall(self._update),
timetype=ba.TimeType.REAL,
repeat=True)
def _ensure_standard_playlists_exist(self) -> None:
# On new installations, go ahead and create a few playlists
# besides the hard-coded default one:
if not _ba.get_account_misc_val('madeStandardPlaylists', False):
@ -196,88 +281,6 @@ class PlaylistBrowserWindow(ba.Window):
})
_ba.run_transactions()
# Get the current selection (if any).
self._selected_playlist = ba.app.config.get(self._pvars.config_name +
' Playlist Selection')
uiscale = ba.app.ui.uiscale
self._width = 900 if uiscale is ba.UIScale.SMALL else 800
x_inset = 50 if uiscale is ba.UIScale.SMALL else 0
self._height = (480 if uiscale is ba.UIScale.SMALL else
510 if uiscale is ba.UIScale.MEDIUM else 580)
top_extra = 20 if uiscale is ba.UIScale.SMALL else 0
super().__init__(root_widget=ba.containerwidget(
size=(self._width, self._height + top_extra),
transition=transition,
toolbar_visibility='menu_full',
scale_origin_stack_offset=scale_origin,
scale=(1.69 if uiscale is ba.UIScale.SMALL else
1.05 if uiscale is ba.UIScale.MEDIUM else 0.9),
stack_offset=(0, -26) if uiscale is ba.UIScale.SMALL else (0, 0)))
self._back_button: Optional[ba.Widget] = ba.buttonwidget(
parent=self._root_widget,
position=(59 + x_inset, self._height - 70),
size=(120, 60),
scale=1.0,
on_activate_call=self._on_back_press,
autoselect=True,
label=ba.Lstr(resource='backText'),
button_type='back')
ba.containerwidget(edit=self._root_widget,
cancel_button=self._back_button)
txt = self._title_text = ba.textwidget(
parent=self._root_widget,
position=(self._width * 0.5, self._height - 41),
size=(0, 0),
text=self._pvars.window_title_name,
scale=1.3,
res_scale=1.5,
color=ba.app.ui.heading_color,
h_align='center',
v_align='center')
if uiscale is ba.UIScale.SMALL and ba.app.ui.use_toolbars:
ba.textwidget(edit=txt, text='')
ba.buttonwidget(edit=self._back_button,
button_type='backSmall',
size=(60, 54),
position=(59 + x_inset, self._height - 67),
label=ba.charstr(ba.SpecialChar.BACK))
if uiscale is ba.UIScale.SMALL and ba.app.ui.use_toolbars:
self._back_button.delete()
self._back_button = None
ba.containerwidget(edit=self._root_widget,
on_cancel_call=self._on_back_press)
scroll_offs = 33
else:
scroll_offs = 0
self._scroll_width = self._width - (100 + 2 * x_inset)
self._scroll_height = (self._height -
(146 if uiscale is ba.UIScale.SMALL
and ba.app.ui.use_toolbars else 136))
self._scrollwidget = ba.scrollwidget(
parent=self._root_widget,
highlight=False,
size=(self._scroll_width, self._scroll_height),
position=((self._width - self._scroll_width) * 0.5,
65 + scroll_offs))
ba.containerwidget(edit=self._scrollwidget, claims_left_right=True)
self._subcontainer: Optional[ba.Widget] = None
self._config_name_full = self._pvars.config_name + ' Playlists'
self._last_config = None
# Update now and once per second.
# (this should do our initial refresh)
self._update()
self._update_timer = ba.Timer(1.0,
ba.WeakCall(self._update),
timetype=ba.TimeType.REAL,
repeat=True)
def _refresh(self) -> None:
# FIXME: Should tidy this up.
# pylint: disable=too-many-statements
@ -285,9 +288,7 @@ class PlaylistBrowserWindow(ba.Window):
# pylint: disable=too-many-locals
# pylint: disable=too-many-nested-blocks
from efro.util import asserttype
from ba.internal import (get_map_class,
get_default_free_for_all_playlist,
get_default_teams_playlist, filter_playlist)
from ba.internal import get_map_class, filter_playlist
if not self._root_widget:
return
if self._subcontainer is not None:
@ -415,13 +416,7 @@ class PlaylistBrowserWindow(ba.Window):
map_textures = []
map_texture_entries = []
if name == '__default__':
if self._sessiontype is ba.FreeForAllSession:
playlist = (get_default_free_for_all_playlist())
elif self._sessiontype is ba.DualTeamSession:
playlist = get_default_teams_playlist()
else:
raise Exception('unrecognized session-type: ' +
str(self._sessiontype))
playlist = self._pvars.get_default_list_call()
else:
if name not in appconfig[self._pvars.config_name +
' Playlists']:

View File

@ -26,10 +26,7 @@ class PlayOptionsWindow(popup.PopupWindow):
# pylint: disable=too-many-branches
# pylint: disable=too-many-statements
# pylint: disable=too-many-locals
from ba.internal import (getclass, get_default_teams_playlist,
get_default_free_for_all_playlist,
filter_playlist)
from ba.internal import get_map_class
from ba.internal import get_map_class, getclass, filter_playlist
from bastd.ui.playlist import PlaylistTypeVars
self._r = 'gameListWindow'
@ -37,6 +34,10 @@ class PlayOptionsWindow(popup.PopupWindow):
self._pvars = PlaylistTypeVars(sessiontype)
self._transitioning_out = False
# We behave differently if we're being used for playlist selection
# vs starting a game directly (should make this more elegant).
self._selecting_mode = ba.app.ui.selecting_private_party_playlist
self._do_randomize_val = (ba.app.config.get(
self._pvars.config_name + ' Playlist Randomize', 0))
@ -69,13 +70,7 @@ class PlayOptionsWindow(popup.PopupWindow):
max_columns = 5
name = playlist
if name == '__default__':
if self._sessiontype is ba.FreeForAllSession:
plst = get_default_free_for_all_playlist()
elif self._sessiontype is ba.DualTeamSession:
plst = get_default_teams_playlist()
else:
raise TypeError('unrecognized session-type: ' +
str(self._sessiontype))
plst = self._pvars.get_default_list_call()
else:
try:
plst = ba.app.config[self._pvars.config_name +
@ -323,23 +318,24 @@ class PlayOptionsWindow(popup.PopupWindow):
ba.widget(edit=self._show_tutorial_check_box,
up_widget=self._custom_colors_names_button)
self._play_button = ba.buttonwidget(
self._ok_button = ba.buttonwidget(
parent=self.root_widget,
position=(70, 44),
size=(200, 45),
scale=1.8,
text_res_scale=1.5,
on_activate_call=self._on_play_press,
on_activate_call=self._on_ok_press,
autoselect=True,
label=ba.Lstr(resource='playText'))
label=ba.Lstr(
resource='okText' if self._selecting_mode else 'playText'))
ba.widget(edit=self._play_button,
ba.widget(edit=self._ok_button,
up_widget=self._show_tutorial_check_box)
ba.containerwidget(edit=self.root_widget,
start_button=self._play_button,
start_button=self._ok_button,
cancel_button=self._cancel_button,
selected_child=self._play_button)
selected_child=self._ok_button)
# Update now and once per second.
self._update_timer = ba.Timer(1.0,
@ -387,7 +383,7 @@ class PlayOptionsWindow(popup.PopupWindow):
def _on_cancel_press(self) -> None:
self._transition_out()
def _on_play_press(self) -> None:
def _on_ok_press(self) -> None:
# Disallow if our playlist has disappeared.
if not self._does_target_playlist_exist():
@ -402,12 +398,32 @@ class PlayOptionsWindow(popup.PopupWindow):
cfg = ba.app.config
cfg[self._pvars.config_name + ' Playlist Selection'] = self._playlist
# Head back to the gather window in playlist-select mode
# or start the game in regular mode.
if self._selecting_mode:
from bastd.ui.gather import GatherWindow
if self._sessiontype is ba.FreeForAllSession:
typename = 'ffa'
elif self._sessiontype is ba.DualTeamSession:
typename = 'teams'
else:
raise RuntimeError('Only teams and ffa currently supported')
cfg['Private Party Host Session Type'] = typename
ba.playsound(ba.getsound('gunCocking'))
ba.app.ui.set_main_menu_window(
GatherWindow(transition='in_right').get_root_widget())
self._transition_out(transition='out_left')
if self._delegate is not None:
self._delegate.on_play_options_window_run_game()
else:
_ba.fade_screen(False, endcall=self._run_selected_playlist)
_ba.lock_all_input()
self._transition_out(transition='out_left')
if self._delegate is not None:
self._delegate.on_play_options_window_run_game()
cfg.commit()
_ba.fade_screen(False, endcall=self._run_selected_playlist)
_ba.lock_all_input()
self._transition_out(transition='out_left')
if self._delegate is not None:
self._delegate.on_play_options_window_run_game()
def _run_selected_playlist(self) -> None:
_ba.unlock_all_input()

View File

@ -90,15 +90,15 @@ class TeamNamesColorsWindow(popup.PopupWindow):
on_activate_call=self._on_cancel_press,
size=(150, 50),
position=(self._width * 0.5 - 200, 20))
savebtn = ba.buttonwidget(parent=self.root_widget,
label=ba.Lstr(resource='saveText'),
autoselect=True,
on_activate_call=self._save,
size=(150, 50),
position=(self._width * 0.5 + 50, 20))
okbtn = ba.buttonwidget(parent=self.root_widget,
label=ba.Lstr(resource='okText'),
autoselect=True,
on_activate_call=self._ok,
size=(150, 50),
position=(self._width * 0.5 + 50, 20))
ba.containerwidget(edit=self.root_widget,
selected_child=self._color_buttons[0])
ba.widget(edit=savebtn, left_widget=cancelbtn)
ba.widget(edit=okbtn, left_widget=cancelbtn)
self._update()
def _color_click(self, i: int) -> None:
@ -136,7 +136,7 @@ class TeamNamesColorsWindow(popup.PopupWindow):
ba.textwidget(edit=self._color_text_fields[i],
color=self._colors[i])
def _save(self) -> None:
def _ok(self) -> None:
from ba.internal import DEFAULT_TEAM_COLORS, DEFAULT_TEAM_NAMES
cfg = ba.app.config
@ -171,11 +171,10 @@ class TeamNamesColorsWindow(popup.PopupWindow):
if key in cfg:
del cfg[key]
else:
cfg['Custom Team Names'] = tuple(new_names)
cfg['Custom Team Colors'] = tuple(self._colors)
cfg['Custom Team Names'] = list(new_names)
cfg['Custom Team Colors'] = list(self._colors)
cfg.commit()
ba.playsound(ba.getsound('gunCocking'))
self._transition_out()
def _transition_out(self, transition: str = 'out_scale') -> None:

View File

@ -419,6 +419,7 @@
<w>hostactivity</w>
<w>hostcmd</w>
<w>hostinfo</w>
<w>hostingconfig</w>
<w>hostingstate</w>
<w>hotkeys</w>
<w>hotplug</w>
@ -627,6 +628,7 @@
<w>offsx</w>
<w>offsy</w>
<w>oiffsss</w>
<w>okbtn</w>
<w>oldname</w>
<w>oooo</w>
<w>ooooooo</w>
@ -800,6 +802,7 @@
<w>sessionplayer</w>
<w>sessionteam</w>
<w>sessiontype</w>
<w>sessiontypestr</w>
<w>setactivity</w>
<w>setattro</w>
<w>setattrofunc</w>

View File

@ -1,5 +1,5 @@
<!-- THIS FILE IS AUTO GENERATED; DO NOT EDIT BY HAND -->
<h4><em>last updated on 2021-01-26 for Ballistica version 1.6.0 build 20278</em></h4>
<h4><em>last updated on 2021-01-26 for Ballistica version 1.6.0 build 20279</em></h4>
<p>This page documents the Python classes and functions in the 'ba' module,
which are the ones most relevant to modding in Ballistica. If you come across something you feel should be included here or could be better explained, please <a href="mailto:support@froemling.net">let me know</a>. Happy modding!</p>
<hr>