better tourney results inbox messages

This commit is contained in:
Eric Froemling 2025-01-14 19:44:48 -08:00
parent 206e3d7f4b
commit 9de17896d1
No known key found for this signature in database
31 changed files with 739 additions and 219 deletions

94
.efrocachemap generated
View File

@ -432,43 +432,43 @@
"build/assets/ba_data/audio/zoeOw.ogg": "b2d705c31c9dcc1efdc71394764c3beb",
"build/assets/ba_data/audio/zoePickup01.ogg": "e9366dc2d2b8ab8b0c4e2c14c02d0789",
"build/assets/ba_data/audio/zoeScream01.ogg": "903e0e45ee9b3373e9d9ce20c814374e",
"build/assets/ba_data/data/langdata.json": "dbbd8f26d2f85c0b649d461e991b80cb",
"build/assets/ba_data/data/languages/arabic.json": "3c22e7b6d7b09a812a2e28b35c9e9241",
"build/assets/ba_data/data/langdata.json": "4c8242df36a1b589035beb4711cbf772",
"build/assets/ba_data/data/languages/arabic.json": "397dfd469ef7c744cbb472cd19b73f1f",
"build/assets/ba_data/data/languages/belarussian.json": "0b60a9d4496d1213c2d0b647d346ce30",
"build/assets/ba_data/data/languages/chinese.json": "fc45d2838b834889c06920ae7c2102fa",
"build/assets/ba_data/data/languages/chinesetraditional.json": "904b35b656c53f9830e406565edd5120",
"build/assets/ba_data/data/languages/chinese.json": "168b529f2d55d714886be57f162f6842",
"build/assets/ba_data/data/languages/chinesetraditional.json": "32f53581b80ce723edbe8aa7956e6727",
"build/assets/ba_data/data/languages/croatian.json": "e131a87cf5783e0fbb3d211a927efe1a",
"build/assets/ba_data/data/languages/czech.json": "d18b7d1c6bf51fc81af4084ef0e69e3e",
"build/assets/ba_data/data/languages/czech.json": "3418bee44e69be13b7f72996abe96921",
"build/assets/ba_data/data/languages/danish.json": "8e57db30c5250df2abff14a822f83ea7",
"build/assets/ba_data/data/languages/dutch.json": "4085dec5af362cf068b494524ced3872",
"build/assets/ba_data/data/languages/dutch.json": "4ba5bbcc0fecddd0aac6ee2c165d1e40",
"build/assets/ba_data/data/languages/english.json": "527d106870b0690cc39a80b88e60ab7a",
"build/assets/ba_data/data/languages/esperanto.json": "0e397cfa5f3fb8cef5f4a64f21cda880",
"build/assets/ba_data/data/languages/filipino.json": "3d9269a90a2fee164d0a7513c4f130a3",
"build/assets/ba_data/data/languages/filipino.json": "1894fc331dcad7ce9cf4c180843f548f",
"build/assets/ba_data/data/languages/french.json": "6d20655730b1017ef187fd828b91d43c",
"build/assets/ba_data/data/languages/german.json": "b92ec951b5a0ce4f73677051ca59a06b",
"build/assets/ba_data/data/languages/german.json": "bc656f1ada467161c23546f48d0dacc5",
"build/assets/ba_data/data/languages/gibberish.json": "2569fe1b2f686670f825e2faaa8c5dc3",
"build/assets/ba_data/data/languages/greek.json": "d28d1092fbb00ed857cbd53124c0dc78",
"build/assets/ba_data/data/languages/hindi.json": "567e6976b3c72f891431ad7fcc62ab16",
"build/assets/ba_data/data/languages/hungarian.json": "9d88004a98f0fbe2ea72edd5e0b3002e",
"build/assets/ba_data/data/languages/hungarian.json": "af801baffb2c06460635dfb04c34bb3e",
"build/assets/ba_data/data/languages/indonesian.json": "607ba358179185f032096ea1978e4448",
"build/assets/ba_data/data/languages/italian.json": "eabad2faba952c426876bc07e1490d09",
"build/assets/ba_data/data/languages/korean.json": "4e3524327a0174250aff5e1ef4c0c597",
"build/assets/ba_data/data/languages/italian.json": "254d4d3962fda17fe127636fa6221851",
"build/assets/ba_data/data/languages/korean.json": "360760d72832863e1a3451b0a514cb08",
"build/assets/ba_data/data/languages/malay.json": "0212e18e54efa202c17505376e5b82fb",
"build/assets/ba_data/data/languages/persian.json": "859a60de6226fdf9fc24b68b7f6782b6",
"build/assets/ba_data/data/languages/piratespeak.json": "7c7e3b72b87c1bcd5b04c9f64d912f0c",
"build/assets/ba_data/data/languages/polish.json": "941eb816c7db9e04d6a3b8f28a64e2e8",
"build/assets/ba_data/data/languages/portuguese.json": "b4463a05d65515f6812e1177c60ac666",
"build/assets/ba_data/data/languages/romanian.json": "5ae206fe0b71c4015b02b86da8931c8f",
"build/assets/ba_data/data/languages/russian.json": "fc64ed6b6356ea11385ee5c20748425a",
"build/assets/ba_data/data/languages/persian.json": "517217e679c768fff4ffec7f8000ab77",
"build/assets/ba_data/data/languages/piratespeak.json": "be23decfaf220b3aa3de3cf35e59b420",
"build/assets/ba_data/data/languages/polish.json": "993b612c5854fc42a78726ed09c65251",
"build/assets/ba_data/data/languages/portuguese.json": "f034d8099298b56792d4e0e41c5c34a0",
"build/assets/ba_data/data/languages/romanian.json": "b04345d8c7631d657a69c73eb7be755a",
"build/assets/ba_data/data/languages/russian.json": "eca8fe1ef8343aee559e49c49805b850",
"build/assets/ba_data/data/languages/serbian.json": "623fa4129a1154c2f32ed7867e56ff6a",
"build/assets/ba_data/data/languages/slovak.json": "c11c29708b3742cdc2a92b4fa0d6d29f",
"build/assets/ba_data/data/languages/spanish.json": "f8ab976d219e579546bb98b6d7fd12ce",
"build/assets/ba_data/data/languages/swedish.json": "3b179e7333183c70adb0811246b09959",
"build/assets/ba_data/data/languages/tamil.json": "ead39b864228696a9b0d19344bc4b5ec",
"build/assets/ba_data/data/languages/thai.json": "383540a1e9c7c131ac579f51afc87471",
"build/assets/ba_data/data/languages/turkish.json": "ccec3224e41bee03f798d9c1a7d23342",
"build/assets/ba_data/data/languages/turkish.json": "1415bdb746551f0a24f0e675304dfe07",
"build/assets/ba_data/data/languages/ukrainian.json": "6063d27c9d6ed013b2b64ff452433621",
"build/assets/ba_data/data/languages/venetian.json": "abebcc38ca2655578e65428cc0dd3c45",
"build/assets/ba_data/data/languages/venetian.json": "e0666c6a1db1792d895fcb250e59861b",
"build/assets/ba_data/data/languages/vietnamese.json": "59f6686890ceac2b0ac92597751a18ca",
"build/assets/ba_data/data/maps/big_g.json": "1dd301d490643088a435ce75df971054",
"build/assets/ba_data/data/maps/bridgit.json": "6aea74805f4880cc11237c5734a24422",
@ -1333,10 +1333,10 @@
"build/assets/ba_data/textures/buttonSquare.dds": "48e117e4a57a1d99ebf0e38dd414a95b",
"build/assets/ba_data/textures/buttonSquare.ktx": "936f04a43f99f692071c7e9567cc2a3d",
"build/assets/ba_data/textures/buttonSquare.pvr": "b53fe9f1725e0d28bc974f6ef1623fb0",
"build/assets/ba_data/textures/buttonSquareWide.dds": "5ab9ff791eae959d49f7ac1018f395a9",
"build/assets/ba_data/textures/buttonSquareWide.ktx": "043bbadbe5adff0236d2841fcaeadb87",
"build/assets/ba_data/textures/buttonSquareWide.pvr": "ca3773bfeacc8dcf42387a6da6ef0807",
"build/assets/ba_data/textures/buttonSquareWide_preview.png": "a5a02ed8a9a0b4d0043fcf21fc0f5f8a",
"build/assets/ba_data/textures/buttonSquareWide.dds": "879ecc705072338a427f25fdbf759792",
"build/assets/ba_data/textures/buttonSquareWide.ktx": "98e3b5d628823ffac2add60aa63c0274",
"build/assets/ba_data/textures/buttonSquareWide.pvr": "f5ea38658215153ef97bba12ce113923",
"build/assets/ba_data/textures/buttonSquareWide_preview.png": "543a1f28b538ada0f63c998f2c75e518",
"build/assets/ba_data/textures/buttonSquare_preview.png": "a9abe7bbf392caf8141adf81fc63f4f8",
"build/assets/ba_data/textures/chTitleChar1.dds": "d8c615a51d900da15b8aba5ce35296bf",
"build/assets/ba_data/textures/chTitleChar1.ktx": "44384ea28c9fe01440deb1fc80c7224a",
@ -4126,22 +4126,22 @@
"build/assets/windows/Win32/ucrtbased.dll": "bfd1180c269d3950b76f35a63655e9e1",
"build/assets/windows/Win32/vc_redist.x86.exe": "15a5f1f876503885adbdf5b3989b3718",
"build/assets/windows/Win32/vcruntime140d.dll": "865b2af4d1e26a1a8073c89acb06e599",
"build/prefab/full/linux_arm64_gui/debug/ballisticakit": "d0d676371cecc9fff81f2b23fa6e7275",
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "1a8c30616d66c2ab31879d44c355a436",
"build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "b7e5a81d1170d4e932d06c48e4500fe6",
"build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "e28fcdb70262585d0b3ae62f75d8e487",
"build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "714db737373c55db0126046e3a5998cf",
"build/prefab/full/linux_x86_64_gui/release/ballisticakit": "eb601b6b70a673fa8b5f67e766bf2a5e",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "618d617773fce1dc2cc0a7dcc48722e2",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "3a95038a0d6ec21e53585613d5736f64",
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "5b3e194acd4d65030362258c19c5c378",
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "5d8bcc750c247f14fe44bd0a8c432e36",
"build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "c31c365f0353760437328dc05badf43a",
"build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "9dbcec2283230c23e21e7a607153d322",
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "25b1790251a2190bbb8b036cf764b11f",
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "4d08c824bff1dccf9b9fe5f5af011023",
"build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "2b93477aa54b233ce0e65805eac1c160",
"build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "321b48eeb777a3955b3afd250e99c4c5",
"build/prefab/full/linux_arm64_gui/debug/ballisticakit": "1ffc1f5dcda65363be124baa30006ccd",
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "4cc5de1993654275f1d5156af47581a1",
"build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "15fd8eb2457aa3a925f723ef657f384c",
"build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "1c4bb442f9a71ee616a85ff404ee4a1d",
"build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "6c50ad81231ef51d23c09033babfef16",
"build/prefab/full/linux_x86_64_gui/release/ballisticakit": "abbd6b5cb2708d1b45e748d4383794d4",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "35a616d3ef72a4867a36da69d0b997c7",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "c261940da039b1e32eb0137a6e2bdfa8",
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "951b3624f3ea80423f00cae72be2854c",
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "5079d46536257fb5d751461fde6eac50",
"build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "17113f9727ada85f564042267a861f4b",
"build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "822261f0c05dc5de3c68f9d76f8fd969",
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "e63a924dde0304441f96a8304855754b",
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "2bf917a51df7eb8f322d7d7cb5523458",
"build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "95943c9107f7d3756bcf88a03a1673d1",
"build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "194d951ddd839028b2c6efabdf53019f",
"build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "1c375e8003442dd3d059bc0baa260e61",
"build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "40daac4bbc8990d5140f97e792bc4fb1",
"build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "1c375e8003442dd3d059bc0baa260e61",
@ -4154,14 +4154,14 @@
"build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "36bb6f32ab12e2a46b82155a93b2e527",
"build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "51884d81e2d7bdeb6b59a72f0247c8e1",
"build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "36bb6f32ab12e2a46b82155a93b2e527",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "b73abcca68eebe3067b6fe13db2d6545",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "ef9b111ceb3bb8566ae0c0d6579c06a3",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "426c27616a8c706e27ea94b308cc4f98",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "a1c5a7fa4b717ec9edf2cd030ff189ef",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "9a4debdbef814405208292bdeb7bee18",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "dc38927a539aa85ce720e085ee28490d",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "1256eedc2b351c1264e55a90f37efca9",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "ae14008d451581c06702334f94b50d4d",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "0cd1427bb99690616b493a59e7d567d9",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "8b472a65a2407291dde5c54bb1c24df1",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "c97c08a508bd57b325cc8817d12af73f",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "082957f5ee4f2e5d50d62384bda60895",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "4d8bf5c5937ed8c6e4f5e775366590cb",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "11909c454caaa9d50f3ed70869964743",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "4a9492a2cfa1d944ea5618eb2ac9cb7b",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "e1aeaf29546c0a357214c68fb2f0399c",
"src/assets/ba_data/python/babase/_mgen/__init__.py": "f885fed7f2ed98ff2ba271f9dbe3391c",
"src/assets/ba_data/python/babase/_mgen/enums.py": "794d258d59fd17a61752843a9a0551ad",
"src/ballistica/base/mgen/pyembed/binding_base.inc": "06042d31df0ff9af96b99477162e2a91",

View File

@ -1,4 +1,4 @@
### 1.7.37 (build 22184, api 9, 2025-01-13)
### 1.7.37 (build 22189, api 9, 2025-01-14)
- Bumping api version to 9. As you'll see below, there's some UI changes that
will require a bit of work for any UI mods to adapt to. If your mods don't
touch UI stuff at all you can simply bump your api version and call it a day.

View File

@ -79,6 +79,7 @@
"ba_data/python/baclassic/__pycache__/_benchmark.cpython-312.opt-1.pyc",
"ba_data/python/baclassic/__pycache__/_chest.cpython-312.opt-1.pyc",
"ba_data/python/baclassic/__pycache__/_clienteffect.cpython-312.opt-1.pyc",
"ba_data/python/baclassic/__pycache__/_displayitem.cpython-312.opt-1.pyc",
"ba_data/python/baclassic/__pycache__/_input.cpython-312.opt-1.pyc",
"ba_data/python/baclassic/__pycache__/_music.cpython-312.opt-1.pyc",
"ba_data/python/baclassic/__pycache__/_net.cpython-312.opt-1.pyc",
@ -97,6 +98,7 @@
"ba_data/python/baclassic/_benchmark.py",
"ba_data/python/baclassic/_chest.py",
"ba_data/python/baclassic/_clienteffect.py",
"ba_data/python/baclassic/_displayitem.py",
"ba_data/python/baclassic/_input.py",
"ba_data/python/baclassic/_music.py",
"ba_data/python/baclassic/_net.py",

View File

@ -208,6 +208,7 @@ SCRIPT_TARGETS_PY_PUBLIC = \
$(BUILD_DIR)/ba_data/python/baclassic/_benchmark.py \
$(BUILD_DIR)/ba_data/python/baclassic/_chest.py \
$(BUILD_DIR)/ba_data/python/baclassic/_clienteffect.py \
$(BUILD_DIR)/ba_data/python/baclassic/_displayitem.py \
$(BUILD_DIR)/ba_data/python/baclassic/_input.py \
$(BUILD_DIR)/ba_data/python/baclassic/_music.py \
$(BUILD_DIR)/ba_data/python/baclassic/_net.py \
@ -490,6 +491,7 @@ SCRIPT_TARGETS_PYC_PUBLIC = \
$(BUILD_DIR)/ba_data/python/baclassic/__pycache__/_benchmark.cpython-312.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/baclassic/__pycache__/_chest.cpython-312.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/baclassic/__pycache__/_clienteffect.cpython-312.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/baclassic/__pycache__/_displayitem.cpython-312.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/baclassic/__pycache__/_input.cpython-312.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/baclassic/__pycache__/_music.cpython-312.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/baclassic/__pycache__/_net.cpython-312.opt-1.pyc \

View File

@ -41,8 +41,8 @@ def is_browser_likely_available() -> bool:
category: General Utility Functions
If this returns False you may want to avoid calling babase.show_url()
with any lengthy addresses. (ba.show_url() will display an address
If this returns False you may want to avoid calling babase.open_url()
with any lengthy addresses. (babase.open_url() will display an address
as a string in a window if unable to bring up a browser, but that
is only useful for simple URLs.)
"""

View File

@ -176,6 +176,7 @@ class _WeakCall:
'Warning: callable passed to babase.WeakCall() is not'
' weak-referencable (%s); use functools.partial instead'
' to avoid this warning.',
args[0],
stack_info=True,
)
type(self)._did_invalid_call_warning = True

View File

@ -27,6 +27,7 @@ from baclassic._chest import (
CHEST_APPEARANCE_DISPLAY_INFO_DEFAULT,
CHEST_APPEARANCE_DISPLAY_INFOS,
)
from baclassic._displayitem import show_display_item
__all__ = [
'ChestAppearanceDisplayInfo',
@ -36,6 +37,7 @@ __all__ = [
'ClassicAppSubsystem',
'Achievement',
'AchievementSubsystem',
'show_display_item',
]
# We want stuff here to show up as packagename.Foo instead of

View File

@ -0,0 +1,109 @@
# Released under the MIT License. See LICENSE for details.
#
"""Display-item related functionality."""
from __future__ import annotations
from typing import TYPE_CHECKING
from efro.util import pairs_from_flat
import bacommon.bs
import bauiv1
if TYPE_CHECKING:
pass
def show_display_item(
itemwrapper: bacommon.bs.DisplayItemWrapper,
parent: bauiv1.Widget,
pos: tuple[float, float],
width: float,
) -> None:
"""Create ui to depict a display-item."""
height = width * 0.666
# Silent no-op if our parent ui is dead.
if not parent:
return
img: str | None = None
img_y_offs = 0.0
text_y_offs = 0.0
show_text = True
if isinstance(itemwrapper.item, bacommon.bs.TicketsDisplayItem):
img = 'tickets'
img_y_offs = width * 0.11
text_y_offs = width * -0.15
elif isinstance(itemwrapper.item, bacommon.bs.TokensDisplayItem):
img = 'coin'
img_y_offs = width * 0.11
text_y_offs = width * -0.15
elif isinstance(itemwrapper.item, bacommon.bs.ChestDisplayItem):
from baclassic._chest import (
CHEST_APPEARANCE_DISPLAY_INFOS,
CHEST_APPEARANCE_DISPLAY_INFO_DEFAULT,
)
img = None
show_text = False
c_info = CHEST_APPEARANCE_DISPLAY_INFOS.get(
itemwrapper.item.appearance, CHEST_APPEARANCE_DISPLAY_INFO_DEFAULT
)
c_size = width * 0.85
bauiv1.imagewidget(
parent=parent,
position=(pos[0] - c_size * 0.5, pos[1] - c_size * 0.5),
color=c_info.color,
size=(c_size, c_size),
texture=bauiv1.gettexture(c_info.texclosed),
tint_texture=bauiv1.gettexture(c_info.texclosedtint),
tint_color=c_info.tint,
tint2_color=c_info.tint2,
)
# Enable this for testing spacing.
if bool(False):
bauiv1.imagewidget(
parent=parent,
position=(
pos[0] - width * 0.5,
pos[1] - height * 0.5,
),
size=(width, height),
texture=bauiv1.gettexture('white'),
color=(0, 1, 0),
opacity=0.1,
)
imgsize = width * 0.33
if img is not None:
bauiv1.imagewidget(
parent=parent,
position=(
pos[0] - imgsize * 0.5,
pos[1] + img_y_offs - imgsize * 0.5,
),
size=(imgsize, imgsize),
texture=bauiv1.gettexture(img),
)
if show_text:
subs = itemwrapper.description_subs
if subs is None:
subs = []
bauiv1.textwidget(
parent=parent,
position=(pos[0], pos[1] + text_y_offs),
scale=width * 0.006,
size=(0, 0),
text=bauiv1.Lstr(
translate=('serverResponses', itemwrapper.description),
subs=pairs_from_flat(subs),
),
maxwidth=width * 0.9,
color=(0.0, 1.0, 0.0),
h_align='center',
v_align='center',
)

View File

@ -53,7 +53,7 @@ if TYPE_CHECKING:
# Build number and version of the ballistica binary we expect to be
# using.
TARGET_BALLISTICA_BUILD = 22184
TARGET_BALLISTICA_BUILD = 22189
TARGET_BALLISTICA_VERSION = '1.7.37'

View File

@ -88,7 +88,7 @@ class AccountViewerWindow(PopupWindow):
scale=0.6,
text=bui.Lstr(resource='playerInfoText'),
maxwidth=200,
color=(0.7, 0.7, 0.7, 0.7),
color=bui.app.ui_v1.title_color,
)
self._scrollwidget = bui.scrollwidget(
@ -97,6 +97,7 @@ class AccountViewerWindow(PopupWindow):
position=(30, 30),
capture_arrows=True,
simple_culling_v=10,
border_opacity=0.4,
)
bui.widget(edit=self._scrollwidget, autoselect=True)

View File

@ -20,7 +20,7 @@ class AchievementsWindow(bui.MainWindow):
# pylint: disable=too-many-locals
assert bui.app.classic is not None
uiscale = bui.app.ui_v1.uiscale
self._width = 600 if uiscale is bui.UIScale.SMALL else 450
self._width = 600 if uiscale is bui.UIScale.SMALL else 500
self._height = (
380
if uiscale is bui.UIScale.SMALL
@ -102,11 +102,11 @@ class AchievementsWindow(bui.MainWindow):
parent=self._root_widget,
size=(
self._width - 60,
self._height - (150 if uiscale is bui.UIScale.SMALL else 70),
self._height - (150 if uiscale is bui.UIScale.SMALL else 80),
),
position=(
30,
(110 if uiscale is bui.UIScale.SMALL else 30) + yoffs,
(110 if uiscale is bui.UIScale.SMALL else 35) + yoffs,
),
capture_arrows=True,
simple_culling_v=10,

View File

@ -9,6 +9,7 @@ import math
import random
from typing import override, TYPE_CHECKING
from efro.util import strict_partial
import bacommon.bs
import bauiv1 as bui
@ -29,8 +30,6 @@ class ChestWindow(bui.MainWindow):
transition: str | None = 'in_right',
origin_widget: bui.Widget | None = None,
):
# print('ChestWindow()')
self._index = index
assert bui.app.classic is not None
@ -514,7 +513,6 @@ class ChestWindow(bui.MainWindow):
)
self._show_odds(initial_highlighted_row=-1)
# bui.textwidget(edit=self._infotext, text='')
def _highlight_odds_row(self, row: int, extra: bool = False) -> None:
@ -702,8 +700,6 @@ class ChestWindow(bui.MainWindow):
# Convey that something is in progress.
if self._open_now_button:
# bui.buttonwidget(edit=self._open_now_button,
# color=(0.4, 1.0, 0.4))
bui.spinnerwidget(edit=self._open_now_spinner, visible=True)
for twidget in self._open_now_texts:
bui.textwidget(edit=twidget, color=(1, 1, 1, 0.2))
@ -788,7 +784,7 @@ class ChestWindow(bui.MainWindow):
self._reset()
msg = (
'This slot can hold a treasure chest.\n\n'
'Earn chests by beating campaing levels,\n'
'Earn chests by playing campaign levels,\n'
'placing in tournaments, and completing\n'
'achievements.'
)
@ -799,12 +795,22 @@ class ChestWindow(bui.MainWindow):
) -> None:
# pylint: disable=too-many-locals
from baclassic import show_display_item
# No-op if our ui is dead.
if not self._root_widget:
return
assert response.contents is not None
# Insert test items for testing.
if bool(False):
response.contents += [
bacommon.bs.DisplayItemWrapper.for_display_item(
bacommon.bs.TestDisplayItem()
)
]
tincr = 0.4
tendoffs = tincr * 4.0
toffs = 0.0
@ -861,7 +867,7 @@ class ChestWindow(bui.MainWindow):
),
)
xspacing = 150
xspacing = 100
xoffs = -0.5 * (len(response.contents) - 1) * xspacing
bui.apptimer(
toffs - 0.2, lambda: bui.getsound('corkPop2').play(volume=4.0)
@ -898,14 +904,25 @@ class ChestWindow(bui.MainWindow):
toffsopen = toffs
bui.apptimer(toffs, bui.WeakCall(self._show_chest_opening))
toffs += tincr * 1.0
width = xspacing * 0.75
for obj in response.contents:
width = xspacing * 0.95
for item in response.contents:
toffs += tincr
bui.apptimer(
toffs - 0.1, lambda: bui.getsound('cashRegister').play()
)
bui.apptimer(
toffs, bui.WeakCall(self._show_chest_item, obj, xoffs, width)
toffs,
strict_partial(
show_display_item,
item,
self._root_widget,
pos=(
self._width * 0.5 + xoffs,
self._height - 250.0 + self._yoffs,
),
width=width,
),
)
xoffs += xspacing
toffs += tincr
@ -1003,61 +1020,6 @@ class ChestWindow(bui.MainWindow):
initial_highlighted_extra=True,
)
def _show_chest_item(
self,
itemwrapper: bacommon.bs.DisplayItemWrapper,
xoffs: float,
width: float,
) -> None:
# No-op if our ui is dead.
if not self._root_widget:
return
img: str | None = None
if isinstance(itemwrapper.item, bacommon.bs.TicketsDisplayItem):
img = 'tickets'
elif isinstance(itemwrapper.item, bacommon.bs.TokensDisplayItem):
img = 'coin'
# Translate the wrapper description and apply any subs.
descfin = bui.Lstr(
translate=('serverResponses', itemwrapper.description)
).evaluate()
subs = (
[]
if itemwrapper.description_subs is None
else itemwrapper.description_subs
)
assert len(subs) % 2 == 0 # Should always be even.
for j in range(0, len(subs) - 1, 2):
descfin = descfin.replace(subs[j], subs[j + 1])
imgsize = 34
if img is not None:
bui.imagewidget(
parent=self._root_widget,
position=(
self._width * 0.5 + xoffs - imgsize * 0.5,
self._height - 252 + 14.0 + self._yoffs - imgsize * 0.5,
),
size=(imgsize, imgsize),
texture=bui.gettexture(img),
)
bui.textwidget(
parent=self._root_widget,
position=(
self._width * 0.5 + xoffs,
self._height - 252 - 14.0 + self._yoffs,
),
scale=0.65,
size=(0, 0),
text=f'+ {descfin}',
maxwidth=width,
color=(0.0, 1.0, 0.0),
h_align='center',
v_align='center',
)
def _show_done_button(self) -> None:
# No-op if our ui is dead.
if not self._root_widget:

View File

@ -275,7 +275,7 @@ class CoopBrowserWindow(bui.MainWindow):
simple_culling_v=10.0,
claims_left_right=True,
selection_loops_to_parent=True,
border_opacity=0.3 if uiscale is bui.UIScale.SMALL else 1.0,
border_opacity=0.4,
)
if uiscale is bui.UIScale.SMALL:

View File

@ -627,6 +627,10 @@ class TournamentButton:
max_players = bui.app.classic.accounts.tournament_info[
self.tournament_id
]['maxPlayers']
print('GOT GAME', game)
print('GOT ID', self.tournament_id)
print('GOT PLAYERS', max_players)
txt = bui.Lstr(
value='${A} ${B}',
subs=[

View File

@ -1,25 +1,35 @@
# Released under the MIT License. See LICENSE for details.
#
# pylint: disable=too-many-lines
"""Provides a popup window to view achievements."""
from __future__ import annotations
import weakref
from functools import partial
from dataclasses import dataclass
from typing import override, assert_never
from typing import override, assert_never, TYPE_CHECKING
from efro.util import strict_partial, pairs_from_flat
from efro.error import CommunicationError
import bacommon.bs
import bauiv1 as bui
if TYPE_CHECKING:
from typing import Callable
class _Section:
def get_height(self) -> float:
"""Return section height."""
raise NotImplementedError()
def draw(self, subcontainer: bui.Widget, y: float) -> None:
"""Draw the section."""
def get_button_row(self) -> list[bui.Widget]:
"""Return rows of selectable controls."""
return []
def emit(self, subcontainer: bui.Widget, y: float) -> None:
"""Emit the section."""
class _TextSection(_Section):
@ -27,9 +37,8 @@ class _TextSection(_Section):
def __init__(
self,
sub_width: float,
text: str,
text: bui.Lstr | str,
*,
subs: list[str],
spacing_top: float = 0.0,
spacing_bottom: float = 0.0,
scale: float = 0.6,
@ -40,23 +49,22 @@ class _TextSection(_Section):
self.spacing_bottom = spacing_bottom
self.color = color
self.textfin = bui.Lstr(translate=('serverResponses', text)).evaluate()
assert len(subs) % 2 == 0 # Should always be even.
for j in range(0, len(subs) - 1, 2):
self.textfin = self.textfin.replace(subs[j], subs[j + 1])
# We need to bake this down since we plug its final size into
# our math.
self.textbaked = text.evaluate() if isinstance(text, bui.Lstr) else text
# Calc scale to fit width and then see what height we need at
# that scale.
t_width = max(
10.0,
bui.get_string_width(self.textfin, suppress_warning=True) * scale,
bui.get_string_width(self.textbaked, suppress_warning=True) * scale,
)
self.text_scale = scale * min(1.0, (sub_width * 0.9) / t_width)
self.text_height = (
0.0
if not self.textfin
else bui.get_string_height(self.textfin, suppress_warning=True)
if not self.textbaked
else bui.get_string_height(self.textbaked, suppress_warning=True)
) * self.text_scale
self.full_height = self.text_height + spacing_top + spacing_bottom
@ -66,25 +74,137 @@ class _TextSection(_Section):
return self.full_height
@override
def draw(self, subcontainer: bui.Widget, y: float) -> None:
def emit(self, subcontainer: bui.Widget, y: float) -> None:
bui.textwidget(
parent=subcontainer,
position=(
self.sub_width * 0.5,
y - self.spacing_top - self.text_height * 0.5,
# y - self.height * 0.5 - 23.0,
),
color=self.color,
scale=self.text_scale,
flatness=1.0,
shadow=0.0,
text=self.textfin,
text=self.textbaked,
size=(0, 0),
h_align='center',
v_align='center',
)
class _ButtonSection(_Section):
def __init__(
self,
sub_width: float,
label: bui.Lstr | str,
*,
color: tuple[float, float, float],
label_color: tuple[float, float, float],
call: Callable[[_ButtonSection], None],
spacing_top: float = 0.0,
spacing_bottom: float = 0.0,
) -> None:
self.sub_width = sub_width
self.spacing_top = spacing_top
self.spacing_bottom = spacing_bottom
self.color = color
self.label_color = label_color
self.button: bui.Widget | None = None
self.call = call
self.labelfin = label
self.button_width = 130
self.button_height = 30
self.full_height = self.button_height + spacing_top + spacing_bottom
@override
def get_height(self) -> float:
return self.full_height
@staticmethod
def weak_call(section: weakref.ref[_ButtonSection]) -> None:
"""Call button section call if section still exists."""
section_strong = section()
if section_strong is None:
return
section_strong.call(section_strong)
@override
def emit(self, subcontainer: bui.Widget, y: float) -> None:
self.button = bui.buttonwidget(
parent=subcontainer,
position=(
self.sub_width * 0.5 - self.button_width * 0.5,
y - self.spacing_top - self.button_height,
),
autoselect=True,
label=self.labelfin,
textcolor=self.label_color,
text_scale=0.55,
size=(self.button_width, self.button_height),
color=self.color,
on_activate_call=strict_partial(self.weak_call, weakref.ref(self)),
)
bui.widget(edit=self.button, depth_range=(0.1, 1.0))
@override
def get_button_row(self) -> list[bui.Widget]:
"""Return rows of selectable controls."""
assert self.button is not None
return [self.button]
class _DisplayItemsSection(_Section):
def __init__(
self,
sub_width: float,
items: list[bacommon.bs.DisplayItemWrapper],
width: float = 100.0,
*,
spacing_top: float = 0.0,
spacing_bottom: float = 0.0,
) -> None:
self.display_item_width = width
# FIXME - ask for this somewhere in case it changes.
self.display_item_height = self.display_item_width * 0.666
self.items = items
self.sub_width = sub_width
self.spacing_top = spacing_top
self.spacing_bottom = spacing_bottom
self.full_height = (
self.display_item_height + spacing_top + spacing_bottom
)
@override
def get_height(self) -> float:
return self.full_height
@override
def emit(self, subcontainer: bui.Widget, y: float) -> None:
# pylint: disable=cyclic-import
from baclassic import show_display_item
xspacing = 1.1 * self.display_item_width
total_width = (
0 if not self.items else ((len(self.items) - 1) * xspacing)
)
x = -0.5 * total_width
for item in self.items:
show_display_item(
item,
subcontainer,
pos=(
self.sub_width * 0.5 + x,
y - self.spacing_top - self.display_item_height * 0.5,
),
width=self.display_item_width,
)
x += xspacing
@dataclass
class _EntryDisplay:
interaction_style: bacommon.bs.BasicClientUI.InteractionStyle
@ -99,7 +219,6 @@ class _EntryDisplay:
button_spinner_positive: bui.Widget | None = None
button_negative: bui.Widget | None = None
button_spinner_negative: bui.Widget | None = None
# message_text: bui.Widget | None = None
processing_complete: bool = False
@ -498,7 +617,7 @@ class InboxWindow(bui.MainWindow):
button_label_negative: bacommon.bs.BasicClientUI.ButtonLabel
sections: list[_Section] = []
total_height = 90.0
total_height = 80.0
# Display only entries where we recognize all style/label
# values and ui component types.
@ -514,14 +633,18 @@ class InboxWindow(bui.MainWindow):
idcls = bacommon.bs.BasicClientUIComponentTypeID
for component in wrapper.ui.components:
ctypeid = component.get_type_id()
section: _Section
if ctypeid is idcls.TEXT:
assert isinstance(
component, bacommon.bs.BasicClientUIComponentText
)
section = _TextSection(
sub_width=sub_width,
text=component.text,
subs=component.subs,
text=bui.Lstr(
translate=('serverResponses', component.text),
subs=pairs_from_flat(component.subs),
),
color=component.color,
scale=component.scale,
spacing_top=component.spacing_top,
@ -530,8 +653,209 @@ class InboxWindow(bui.MainWindow):
total_height += section.get_height()
sections.append(section)
elif ctypeid is idcls.LINK:
assert isinstance(
component, bacommon.bs.BasicClientUIComponentLink
)
def _do_open_url(url: str, sec: _ButtonSection) -> None:
del sec # Unused.
bui.open_url(url)
section = _ButtonSection(
sub_width=sub_width,
label=bui.Lstr(
translate=('serverResponses', component.label),
subs=pairs_from_flat(component.subs),
),
color=color,
call=partial(_do_open_url, component.url),
label_color=(0.5, 0.7, 0.6),
spacing_top=component.spacing_top,
spacing_bottom=component.spacing_bottom,
)
total_height += section.get_height()
sections.append(section)
elif ctypeid is idcls.DISPLAY_ITEMS:
assert isinstance(
component,
bacommon.bs.BasicClientUIDisplayItems,
)
section = _DisplayItemsSection(
sub_width=sub_width,
items=component.items,
width=component.width,
spacing_top=component.spacing_top,
spacing_bottom=component.spacing_bottom,
)
total_height += section.get_height()
sections.append(section)
elif ctypeid is idcls.BS_CLASSIC_TOURNEY_RESULT:
from bascenev1 import get_trophy_string
assert isinstance(
component,
bacommon.bs.BasicClientUIBsClassicTourneyResult,
)
campaignname, levelname = component.game.split(':')
assert bui.app.classic is not None
campaign = bui.app.classic.getcampaign(campaignname)
tourney_name = bui.Lstr(
value='${A} ${B}',
subs=[
(
'${A}',
campaign.getlevel(levelname).displayname,
),
(
'${B}',
bui.Lstr(
resource='playerCountAbbreviatedText',
subs=[
('${COUNT}', str(component.players))
],
),
),
],
)
if component.trophy is not None:
trophy_prefix = (
get_trophy_string(component.trophy) + ' '
)
else:
trophy_prefix = ''
section = _TextSection(
sub_width=sub_width,
# text=bui.Lstr(
# translate=(
# 'serverResponses',
# 'You placed #${RANK}' ' in a tournament!',
# # 'You placed in a tournament!',
# ),
# subs=[('${RANK}', str(component.rank))],
# ),
text=bui.Lstr(
value='${P}${V}',
subs=[
('${P}', trophy_prefix),
(
'${V}',
bui.Lstr(
translate=(
'serverResponses',
'You placed #${RANK}'
' in a tournament!',
# 'You placed in a tournament!',
),
subs=[
('${RANK}', str(component.rank))
],
),
),
],
),
color=(1.0, 1.0, 1.0, 1.0),
scale=0.6,
)
total_height += section.get_height()
sections.append(section)
section = _TextSection(
sub_width=sub_width,
# text=bui.Lstr(
# value='${P}${V}',
# subs=[
# ('${P}', trophy_prefix),
# ('${V}', tourney_name),
# ],
# ),
text=tourney_name,
spacing_top=5,
color=(0.7, 0.7, 1.0, 1.0),
scale=0.7,
)
total_height += section.get_height()
sections.append(section)
# rank_trophy_str = f'#{component.rank}'
# if component.trophy is not None:
# rank_trophy_str = get_trophy_string(
# component.trophy
# )
# section = _TextSection(
# sub_width=sub_width,
# text=rank_trophy_str,
# spacing_top=10,
# scale=1.0,
# )
# total_height += section.get_height()
# sections.append(section)
def _do_tourney_scores(
tournament_id: str, sec: _ButtonSection
) -> None:
from bauiv1lib.tournamentscores import (
TournamentScoresWindow,
)
assert sec.button is not None
_ = (
TournamentScoresWindow(
tournament_id=tournament_id,
position=(
sec.button
).get_screen_space_center(),
),
)
section = _ButtonSection(
sub_width=sub_width,
label=bui.Lstr(
translate=('serverResponses', 'Final Standings')
),
color=color,
call=partial(
_do_tourney_scores, component.tournament_id
),
label_color=(0.5, 0.7, 0.6),
spacing_top=7.0,
)
total_height += section.get_height()
sections.append(section)
section = _TextSection(
sub_width=sub_width,
text=bui.Lstr(
translate=(
'serverResponses',
'Your prize:',
)
),
spacing_top=6,
color=(1.0, 1.0, 1.0, 0.4),
scale=0.35,
)
total_height += section.get_height()
sections.append(section)
section = _DisplayItemsSection(
sub_width=sub_width,
items=component.prizes,
width=70.0,
spacing_top=0.0,
spacing_bottom=0.0,
)
total_height += section.get_height()
sections.append(section)
elif ctypeid is idcls.UNKNOWN:
raise RuntimeError('Should not get here.')
else:
# Make sure we handle all types.
assert_never(ctypeid)
@ -550,8 +874,9 @@ class InboxWindow(bui.MainWindow):
section = _TextSection(
sub_width=sub_width,
text='You must update the app to view this.',
subs=[],
text=bui.Lstr(
value='You must update the app to view this.'
),
)
total_height += section.get_height()
sections.append(section)
@ -611,7 +936,11 @@ class InboxWindow(bui.MainWindow):
# Section contents.
for sec in entry_display.sections:
sec.draw(subcontainer, ysection)
sec.emit(subcontainer, ysection)
# Wire up any widgets created by this section.
sec_button_row = sec.get_button_row()
if sec_button_row:
buttonrows.append(sec_button_row)
ysection -= sec.get_height()
buttonrow: list[bui.Widget] = []
@ -633,6 +962,7 @@ class InboxWindow(bui.MainWindow):
entry_display.button_positive = btn = bui.buttonwidget(
parent=subcontainer,
position=bpos,
autoselect=True,
size=(bwidth, bheight),
label=bui.app.classic.basic_client_ui_button_label_str(
entry_display.button_label_positive
@ -663,6 +993,7 @@ class InboxWindow(bui.MainWindow):
entry_display.button_negative = btn2 = bui.buttonwidget(
parent=subcontainer,
position=bpos,
autoselect=True,
size=(bwidth, bheight),
label=bui.app.classic.basic_client_ui_button_label_str(
entry_display.button_label_negative
@ -712,11 +1043,16 @@ class InboxWindow(bui.MainWindow):
bui.widget(
edit=button,
up_widget=above_widget,
down_widget=(
button if below_widget is None else below_widget
),
down_widget=below_widget,
# down_widget=(
# button if below_widget is None else below_widget
# ),
right_widget=buttons[max(j - 1, 0)],
left_widget=buttons[min(j + 1, len(buttons) - 1)],
)
above_widget = buttons[0]
def _get_bs_classic_tourney_results_sections() -> list[_Section]:
return []

View File

@ -126,6 +126,7 @@ class PlaylistAddGameWindow(bui.MainWindow):
position=(x_inset + 61, v - scroll_height),
size=(self._scroll_width, scroll_height),
highlight=False,
border_opacity=0.4,
)
bui.widget(
edit=self._scrollwidget,

View File

@ -142,9 +142,9 @@ class PlaylistBrowserWindow(bui.MainWindow):
size=(self._scroll_width, self._scroll_height),
position=(
(self._width - self._scroll_width) * 0.5,
65 + scroll_offs,
65 + scroll_offs + (0 if uiscale is bui.UIScale.SMALL else -5),
),
border_opacity=0.4 if uiscale is bui.UIScale.SMALL else 1.0,
border_opacity=0.4,
)
bui.containerwidget(edit=self._scrollwidget, claims_left_right=True)
self._subcontainer: bui.Widget | None = None

View File

@ -274,6 +274,7 @@ class PlaylistCustomizeBrowserWindow(bui.MainWindow):
position=(140 + x_inset, v - self._scroll_height),
size=(self._width - (180 + 2 * x_inset), self._scroll_height + 10),
highlight=False,
border_opacity=0.4,
)
if self._back_button is not None:
bui.widget(edit=self._back_button, right_widget=scrollwidget)

View File

@ -232,6 +232,7 @@ class PlaylistEditWindow(bui.MainWindow):
highlight=False,
on_select_call=bui.Call(self._set_ui_selection, 'gameList'),
size=(self._scroll_width, (scroll_height - 15)),
border_opacity=0.4,
)
bui.widget(
edit=scrollwidget,

View File

@ -206,6 +206,7 @@ class PlaylistEditGameWindow(bui.MainWindow):
highlight=False,
claims_left_right=True,
selection_loops_to_parent=True,
border_opacity=0.4,
)
self._subcontainer = bui.containerwidget(
parent=self._scrollwidget,

View File

@ -111,6 +111,7 @@ class PlaylistMapSelectWindow(bui.MainWindow):
parent=self._root_widget,
position=(40 + x_inset, v - self._scroll_height),
size=(self._scroll_width, self._scroll_height),
border_opacity=0.4,
)
bui.containerwidget(
edit=self._root_widget, selected_child=self._scrollwidget

View File

@ -30,7 +30,7 @@ class ResourceTypeInfoWindow(PopupWindow):
)
self._transitioning_out = False
self._width = 570
self._height = 350
self._height = 400
bg_color = (0.5, 0.4, 0.6)
super().__init__(
size=(self._width, self._height),
@ -56,6 +56,7 @@ class ResourceTypeInfoWindow(PopupWindow):
yoffs = self._height - 145
if resource_type == 'tickets':
yoffs -= 20
rdesc = (
'Tickets can be used to unlock characters,\n'
'maps, minigames, and more in the store.\n'
@ -70,8 +71,8 @@ class ResourceTypeInfoWindow(PopupWindow):
'and for other game and account features.\n'
'\n'
'You can win tokens in the game or buy them\n'
'in packs. Or buy a Gold Pass to get infinite\n'
'tokens forever and never hear of them again.'
'in packs. Or buy a Gold Pass for infinite\n'
'tokens and never hear about them again.'
)
texname = 'coin'
elif resource_type == 'trophies':

View File

@ -93,7 +93,7 @@ class AllSettingsWindow(bui.MainWindow):
all_buttons_width = 4.0 * bwidth + 3.0 * margin
x = width * 0.5 - all_buttons_width * 0.5
y = height + yoffs - 320.0
y = height + yoffs - 335.0
def _button(
position: tuple[float, float],

View File

@ -146,7 +146,8 @@ class TournamentEntryWindow(PopupWindow):
scale=0.6,
text=bui.Lstr(resource='tournamentEntryText'),
maxwidth=180,
color=(1, 1, 1, 0.4),
# color=(1, 1, 1, 0.4),
color=bui.app.ui_v1.title_color,
)
btn = self._pay_with_tickets_button = bui.buttonwidget(

View File

@ -22,33 +22,20 @@ class TournamentScoresWindow(PopupWindow):
self,
tournament_id: str,
*,
tournament_activity: bs.GameActivity | None = None,
position: tuple[float, float] = (0.0, 0.0),
scale: float | None = None,
offset: tuple[float, float] = (0.0, 0.0),
tint_color: Sequence[float] = (1.0, 1.0, 1.0),
tint2_color: Sequence[float] = (1.0, 1.0, 1.0),
selected_character: str | None = None,
on_close_call: Callable[[], Any] | None = None,
):
plus = bui.app.plus
assert plus is not None
del tournament_activity # unused arg
del tint_color # unused arg
del tint2_color # unused arg
del selected_character # unused arg
self._tournament_id = tournament_id
self._subcontainer: bui.Widget | None = None
self._on_close_call = on_close_call
assert bui.app.classic is not None
uiscale = bui.app.ui_v1.uiscale
if scale is None:
scale = (
2.3
if uiscale is bui.UIScale.SMALL
else 1.65 if uiscale is bui.UIScale.MEDIUM else 1.23
)
scale = (
2.3
if uiscale is bui.UIScale.SMALL
else 1.65 if uiscale is bui.UIScale.MEDIUM else 1.23
)
self._transitioning_out = False
self._width = 400
@ -60,13 +47,12 @@ class TournamentScoresWindow(PopupWindow):
bg_color = (0.5, 0.4, 0.6)
# creates our _root_widget
# Creates our _root_widget.
super().__init__(
position=position,
size=(self._width, self._height),
scale=scale,
bg_color=bg_color,
offset=offset,
)
self._cancel_button = bui.buttonwidget(
@ -91,7 +77,7 @@ class TournamentScoresWindow(PopupWindow):
scale=0.6,
text=bui.Lstr(resource='tournamentStandingsText'),
maxwidth=200,
color=(1, 1, 1, 0.4),
color=bui.app.ui_v1.title_color,
)
self._scrollwidget = bui.scrollwidget(
@ -100,16 +86,18 @@ class TournamentScoresWindow(PopupWindow):
position=(30, 30),
highlight=False,
simple_culling_v=10,
border_opacity=0.4,
)
bui.widget(edit=self._scrollwidget, autoselect=True)
self._loading_spinner = bui.spinnerwidget(
parent=self.root_widget,
position=(self._width * 0.5, self._height * 0.5),
)
self._loading_text = bui.textwidget(
parent=self._scrollwidget,
scale=0.5,
text=bui.Lstr(
value='${A}...',
subs=[('${A}', bui.Lstr(resource='loadingText'))],
),
text='',
size=(self._width - 60, 100),
h_align='center',
v_align='center',
@ -132,10 +120,12 @@ class TournamentScoresWindow(PopupWindow):
self, data: dict[str, Any] | None
) -> None:
if data is not None:
# this used to be the whole payload
# This used to be the whole payload.
data_t: list[dict[str, Any]] = data['t']
# kill our loading text if we've got scores.. otherwise just
# replace it with 'no scores yet'
# Kill our loading text if we've got scores; otherwise just
# replace it with 'no scores yet'.
bui.spinnerwidget(edit=self._loading_spinner, visible=False)
if data_t[0]['scores']:
self._loading_text.delete()
else:
@ -219,7 +209,8 @@ class TournamentScoresWindow(PopupWindow):
def _show_player_info(self, entry: Any, textwidget: bui.Widget) -> None:
from bauiv1lib.account.viewer import AccountViewerWindow
# for the moment we only work if a single player-info is present..
# For the moment we only work if a single player-info is
# present.
if len(entry[2]) != 1:
bui.getsound('error').play()
return
@ -238,8 +229,6 @@ class TournamentScoresWindow(PopupWindow):
if not self._transitioning_out:
self._transitioning_out = True
bui.containerwidget(edit=self.root_widget, transition='out_scale')
if self._on_close_call is not None:
self._on_close_call()
@override
def on_popup_cancel(self) -> None:

View File

@ -535,7 +535,7 @@ static auto PyGetStringHeight(PyObject* self, PyObject* args, PyObject* keywds)
#if BA_DEBUG_BUILD
if (g_base->assets->CompileResourceString(s) != s) {
BA_LOG_PYTHON_TRACE(
"resource-string passed to get_string_height; this should be avoided");
"Resource-string passed to get_string_height; this should be avoided.");
}
#endif
assert(g_base->graphics);

View File

@ -39,7 +39,7 @@ auto main(int argc, char** argv) -> int {
namespace ballistica {
// These are set automatically via script; don't modify them here.
const int kEngineBuildNumber = 22184;
const int kEngineBuildNumber = 22189;
const char* kEngineVersion = "1.7.37";
const int kEngineApiVersion = 9;

View File

@ -778,9 +778,10 @@ void HScrollWidget::Draw(base::RenderPass* pass, bool draw_transparent) {
if (draw_transparent && IsHierarchySelected()
&& g_base->ui->ShouldHighlightWidgets() && highlight_
&& border_opacity_ > 0.0f) {
float m = 0.8f
+ std::abs(sinf(static_cast<float>(current_time_ms) * 0.006467f))
* 0.2f * border_opacity_;
float m = (0.8f
+ std::abs(sinf(static_cast<float>(current_time_ms) * 0.006467f))
* 0.2f)
* border_opacity_;
if (glow_dirty_) {
float r2 = l + width();

View File

@ -836,9 +836,10 @@ void ScrollWidget::Draw(base::RenderPass* pass, bool draw_transparent) {
// If selected, do glow at depth 0.9 - 1.0.
if (draw_transparent && IsHierarchySelected()
&& g_base->ui->ShouldHighlightWidgets() && highlight_) {
float m = 0.8f
+ std::abs(sinf(static_cast<float>(current_time) * 0.006467f))
* 0.2f * border_opacity_;
float m =
(0.8f
+ std::abs(sinf(static_cast<float>(current_time) * 0.006467f)) * 0.2f)
* border_opacity_;
if (glow_dirty_) {
float r2 = l + width();
float l2 = l;

View File

@ -9,6 +9,7 @@ from enum import Enum
from dataclasses import dataclass, field
from typing import Annotated, override, assert_never
from efro.util import pairs_to_flat
from efro.dataclassio import ioprepped, IOAttrs, IOMultiType
from efro.message import Message, Response
@ -102,6 +103,8 @@ class DisplayItemTypeID(Enum):
UNKNOWN = 'u'
TICKETS = 't'
TOKENS = 'k'
TEST = 's'
CHEST = 'c'
class DisplayItem(IOMultiType[DisplayItemTypeID]):
@ -123,19 +126,21 @@ class DisplayItem(IOMultiType[DisplayItemTypeID]):
def get_type(cls, type_id: DisplayItemTypeID) -> type[DisplayItem]:
"""Return the subclass for each of our type-ids."""
# pylint: disable=cyclic-import
out: type[DisplayItem]
t = DisplayItemTypeID
if type_id is t.UNKNOWN:
out = UnknownDisplayItem
elif type_id is t.TICKETS:
out = TicketsDisplayItem
elif type_id is t.TOKENS:
out = TokensDisplayItem
else:
# Important to make sure we provide all types.
assert_never(type_id)
return out
return UnknownDisplayItem
if type_id is t.TICKETS:
return TicketsDisplayItem
if type_id is t.TOKENS:
return TokensDisplayItem
if type_id is t.TEST:
return TestDisplayItem
if type_id is t.CHEST:
return ChestDisplayItem
# Important to make sure we provide all types.
assert_never(type_id)
def get_description(self) -> tuple[str, list[tuple[str, str]]]:
"""Return a string description and subs for the item.
@ -211,6 +216,38 @@ class TokensDisplayItem(DisplayItem):
return '${C} Tokens', [('${C}', str(self.count))]
@ioprepped
@dataclass
class TestDisplayItem(DisplayItem):
"""Fills usable space for a display-item - good for calibration."""
@override
@classmethod
def get_type_id(cls) -> DisplayItemTypeID:
return DisplayItemTypeID.TEST
@override
def get_description(self) -> tuple[str, list[tuple[str, str]]]:
return 'Test Display Item Here', []
@ioprepped
@dataclass
class ChestDisplayItem(DisplayItem):
"""Display a chest."""
appearance: Annotated[ClassicChestAppearance, IOAttrs('a')]
@override
@classmethod
def get_type_id(cls) -> DisplayItemTypeID:
return DisplayItemTypeID.CHEST
@override
def get_description(self) -> tuple[str, list[tuple[str, str]]]:
return '${TYPE} Chest', [('${TYPE}', self.appearance.name.capitalize())]
@ioprepped
@dataclass
class DisplayItemWrapper:
@ -224,9 +261,7 @@ class DisplayItemWrapper:
def for_display_item(cls, item: DisplayItem) -> DisplayItemWrapper:
"""Convenience method to wrap a DisplayItem."""
desc, subs = item.get_description()
# Flatten subs to single list.
flat_subs = [item for pair in subs for item in pair]
return DisplayItemWrapper(item, desc, flat_subs)
return DisplayItemWrapper(item, desc, pairs_to_flat(subs))
@ioprepped
@ -263,10 +298,10 @@ class ChestInfoResponse(Response):
IOAttrs('a', enum_fallback=ClassicChestAppearance.UNKNOWN),
]
# How much to unlock *now*.
# How much it costs to unlock *now*.
unlock_tokens: Annotated[int, IOAttrs('tk')]
# When unlocks on its own.
# When it unlocks on its own.
unlock_time: Annotated[datetime.datetime, IOAttrs('t')]
# Possible prizes we contain.
@ -396,6 +431,9 @@ class BasicClientUIComponentTypeID(Enum):
UNKNOWN = 'u'
TEXT = 't'
LINK = 'l'
BS_CLASSIC_TOURNEY_RESULT = 'ct'
DISPLAY_ITEMS = 'di'
class BasicClientUIComponent(IOMultiType[BasicClientUIComponentTypeID]):
@ -422,8 +460,12 @@ class BasicClientUIComponent(IOMultiType[BasicClientUIComponentTypeID]):
return BasicClientUIComponentUnknown
if type_id is t.TEXT:
return BasicClientUIComponentText
# if type_id is t.SCREEN_MESSAGE:
# return BasicClientUIComponentScreenMessage
if type_id is t.LINK:
return BasicClientUIComponentLink
if type_id is t.BS_CLASSIC_TOURNEY_RESULT:
return BasicClientUIBsClassicTourneyResult
if type_id is t.DISPLAY_ITEMS:
return BasicClientUIDisplayItems
# Important to make sure we provide all types.
assert_never(type_id)
@ -464,12 +506,7 @@ class BasicClientUIComponentText(BasicClientUIComponent):
scale: Annotated[float, IOAttrs('sc', store_default=False)] = 1.0
color: Annotated[
tuple[float, float, float, float], IOAttrs('c', store_default=False)
] = (
1.0,
1.0,
1.0,
1.0,
)
] = (1.0, 1.0, 1.0, 1.0)
spacing_top: Annotated[float, IOAttrs('st', store_default=False)] = 0.0
spacing_bottom: Annotated[float, IOAttrs('sb', store_default=False)] = 0.0
@ -479,6 +516,59 @@ class BasicClientUIComponentText(BasicClientUIComponent):
return BasicClientUIComponentTypeID.TEXT
@ioprepped
@dataclass
class BasicClientUIComponentLink(BasicClientUIComponent):
"""Show a link in the inbox message."""
url: Annotated[str, IOAttrs('u')]
label: Annotated[str, IOAttrs('l')]
subs: Annotated[list[str], IOAttrs('s', store_default=False)] = field(
default_factory=list
)
spacing_top: Annotated[float, IOAttrs('st', store_default=False)] = 0.0
spacing_bottom: Annotated[float, IOAttrs('sb', store_default=False)] = 0.0
@override
@classmethod
def get_type_id(cls) -> BasicClientUIComponentTypeID:
return BasicClientUIComponentTypeID.LINK
@ioprepped
@dataclass
class BasicClientUIBsClassicTourneyResult(BasicClientUIComponent):
"""Show info about a classic tourney."""
tournament_id: Annotated[str, IOAttrs('t')]
game: Annotated[str, IOAttrs('g')]
players: Annotated[int, IOAttrs('p')]
rank: Annotated[int, IOAttrs('r')]
trophy: Annotated[str | None, IOAttrs('tr')]
prizes: Annotated[list[DisplayItemWrapper], IOAttrs('pr')]
@override
@classmethod
def get_type_id(cls) -> BasicClientUIComponentTypeID:
return BasicClientUIComponentTypeID.BS_CLASSIC_TOURNEY_RESULT
@ioprepped
@dataclass
class BasicClientUIDisplayItems(BasicClientUIComponent):
"""Show some display-items."""
items: Annotated[list[DisplayItemWrapper], IOAttrs('d')]
width: Annotated[float, IOAttrs('w')] = 100.0
spacing_top: Annotated[float, IOAttrs('st', store_default=False)] = 0.0
spacing_bottom: Annotated[float, IOAttrs('sb', store_default=False)] = 0.0
@override
@classmethod
def get_type_id(cls) -> BasicClientUIComponentTypeID:
return BasicClientUIComponentTypeID.DISPLAY_ITEMS
@ioprepped
@dataclass
class BasicClientUI(ClientUI):
@ -642,9 +732,6 @@ class ClientEffectScreenMessage(ClientEffect):
"""Display a screen-message."""
message: Annotated[str, IOAttrs('m')]
# Note: Firestore can't store arrays of arrays so we flatten it to a
# single dimension.
subs: Annotated[list[str], IOAttrs('s')]
color: Annotated[tuple[float, float, float], IOAttrs('c')] = (1.0, 1.0, 1.0)

View File

@ -1,5 +1,6 @@
# Released under the MIT License. See LICENSE for details.
#
# pylint: disable=too-many-lines
"""Small handy bits of functionality."""
from __future__ import annotations
@ -15,7 +16,7 @@ from typing import TYPE_CHECKING, cast, TypeVar, Generic, overload, ParamSpec
if TYPE_CHECKING:
import asyncio
from typing import Any, Callable, Literal
from typing import Any, Callable, Literal, Sequence
T = TypeVar('T')
ValT = TypeVar('ValT')
@ -983,6 +984,21 @@ def extract_arg(
return val
def pairs_to_flat(pairs: Sequence[tuple[T, T]]) -> list[T]:
"""Given a sequence of same-typed pairs, flattens to a list."""
return [item for pair in pairs for item in pair]
def pairs_from_flat(flat: Sequence[T]) -> list[tuple[T, T]]:
"""Given a flat even numbered sequence, returns pairs."""
if len(flat) % 2 != 0:
raise ValueError('Provided sequence has an odd number of elements.')
out: list[tuple[T, T]] = []
for i in range(0, len(flat) - 1, 2):
out.append((flat[i], flat[i + 1]))
return out
def weighted_choice(*args: tuple[T, float]) -> T:
"""Given object/weight pairs as args, returns a random object.