mirror of
https://github.com/RYDE-WORK/ballistica.git
synced 2026-01-19 21:37:57 +08:00
Merge branch 'efroemling:main' into main
This commit is contained in:
commit
d2e5baea6a
80
.efrocachemap
generated
80
.efrocachemap
generated
@ -421,8 +421,8 @@
|
||||
"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": "7cdc9f897e458e98cd0131a13b87db24",
|
||||
"build/assets/ba_data/data/languages/arabic.json": "8f89f09ad168c251765efebde4c9069c",
|
||||
"build/assets/ba_data/data/langdata.json": "3775cd8b6f05c2205b7653302308acf9",
|
||||
"build/assets/ba_data/data/languages/arabic.json": "3c22e7b6d7b09a812a2e28b35c9e9241",
|
||||
"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",
|
||||
@ -430,7 +430,7 @@
|
||||
"build/assets/ba_data/data/languages/czech.json": "d18b7d1c6bf51fc81af4084ef0e69e3e",
|
||||
"build/assets/ba_data/data/languages/danish.json": "8e57db30c5250df2abff14a822f83ea7",
|
||||
"build/assets/ba_data/data/languages/dutch.json": "f4e1e8e9231cda9d1bcc7e87a7f8821e",
|
||||
"build/assets/ba_data/data/languages/english.json": "b5917c3b975155e35fedb655dbd7568c",
|
||||
"build/assets/ba_data/data/languages/english.json": "131508c56b563b9552bee5535f107b5a",
|
||||
"build/assets/ba_data/data/languages/esperanto.json": "0e397cfa5f3fb8cef5f4a64f21cda880",
|
||||
"build/assets/ba_data/data/languages/filipino.json": "3d9269a90a2fee164d0a7513c4f130a3",
|
||||
"build/assets/ba_data/data/languages/french.json": "6d20655730b1017ef187fd828b91d43c",
|
||||
@ -445,7 +445,7 @@
|
||||
"build/assets/ba_data/data/languages/malay.json": "0212e18e54efa202c17505376e5b82fb",
|
||||
"build/assets/ba_data/data/languages/persian.json": "2584895475fe62b3fe49a5ea5e69b4b1",
|
||||
"build/assets/ba_data/data/languages/piratespeak.json": "7c7e3b72b87c1bcd5b04c9f64d912f0c",
|
||||
"build/assets/ba_data/data/languages/polish.json": "d0822d5d3bdd72ddb04dc3c43a0b1395",
|
||||
"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",
|
||||
@ -4103,42 +4103,42 @@
|
||||
"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": "e22300fab655f96dae4021fa82cf5130",
|
||||
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "a585418907ebcd55788fc7522293055d",
|
||||
"build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "58e5c912b710cb61600f3ad82efd17e1",
|
||||
"build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "867fc8d0171facc2791323b4a2fc46fc",
|
||||
"build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "d2f9014578a6d5eca3c345b7c9470812",
|
||||
"build/prefab/full/linux_x86_64_gui/release/ballisticakit": "56d41454f04299cfa455aba882028cc1",
|
||||
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "21a9ef40c5cd663a29dc8c24c2ff0f95",
|
||||
"build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "29e757036b87bef6ce6d68203bda9efb",
|
||||
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "66dde4c6865cfc5b8de8369b92c4a103",
|
||||
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "55f347328de6f3e2c8f2dc9749b57436",
|
||||
"build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "0d1dbc0d04f264ceddb3ad6803105bd9",
|
||||
"build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "12e891f298f604be7592b7c7ed5b8479",
|
||||
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "30cb8ee5ebd69d98a3bd625774d19bf0",
|
||||
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "e788895a7c94af2cb7817c02e061c429",
|
||||
"build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "92a3afc186b9031c7ff91ce2a6800ce7",
|
||||
"build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "7e947dea1b10297b441ffc829943f437",
|
||||
"build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "c8c1f174ed35992eb71bf1ff22787f4e",
|
||||
"build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "67e1e5286f3fe64c9935e0e22d3fe443",
|
||||
"build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "c8c1f174ed35992eb71bf1ff22787f4e",
|
||||
"build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "67e1e5286f3fe64c9935e0e22d3fe443",
|
||||
"build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "c6f45e134e680f696df6d5f605fb8308",
|
||||
"build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "cfb87dbb40bfd0cdf4be9da5086265f8",
|
||||
"build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "c6f45e134e680f696df6d5f605fb8308",
|
||||
"build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "cfb87dbb40bfd0cdf4be9da5086265f8",
|
||||
"build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "b39713b9f3a9102fefff1f6d62a9fd82",
|
||||
"build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "1c2d42d75e8f975514b2e30f5602516d",
|
||||
"build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "b39713b9f3a9102fefff1f6d62a9fd82",
|
||||
"build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "1c2d42d75e8f975514b2e30f5602516d",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "20ffab975711f6865ea4627a1e5ed377",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "06124d291e394169ff2a50bef10f832d",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "8d9d7fe4ab396118ef3ef2721372c9cf",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "18890dfa2a926a787d46f00a974eae95",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "d597aae4c2924f031d5514386ec84a63",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "04ab6918264677f49f3add66cdc722e8",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "2a4f8529288c1e8e584f8196085b840b",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "02da45dd942ae7703304750f2f6c05c1",
|
||||
"build/prefab/full/linux_arm64_gui/debug/ballisticakit": "13233366609a24c56012f54ded7aefc6",
|
||||
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "9e3b238711c4c3589a307fdc4058ba7e",
|
||||
"build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "67c4c290e7b05f8b0bfb4f9be87dfa09",
|
||||
"build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "9fbba3a76f2fc50b9951e6f3f036a32b",
|
||||
"build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "c156541560f67ea45207d18561aa96a4",
|
||||
"build/prefab/full/linux_x86_64_gui/release/ballisticakit": "523946201fa68a8761abd8012888a1ee",
|
||||
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "1e164a6146131c57bdd80650c7cd01eb",
|
||||
"build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "547282ad972d2659c7adf3cdcc0fe38c",
|
||||
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "f8f206e99e6515d857d03a6756432916",
|
||||
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "1948fefc1a65e0c09229229058d7a398",
|
||||
"build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "397cdcd79c10c35f13bda51cbf0431b8",
|
||||
"build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "c1a395b445754161e96040c0a3432471",
|
||||
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "1c4e960c1b621d4376c3ca0f8e876aad",
|
||||
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "60eea6534bc325bef60f2de3de497ebb",
|
||||
"build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "67e01108249a1ae8ec6ae106c684febf",
|
||||
"build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "2dbf2871814d9ae25d38ad0ed8c44dc5",
|
||||
"build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "33a0ae6f1ea5a0b0c60055ce01478488",
|
||||
"build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "aad882eaf2230b89973e2cf4f13c9759",
|
||||
"build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "33a0ae6f1ea5a0b0c60055ce01478488",
|
||||
"build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "aad882eaf2230b89973e2cf4f13c9759",
|
||||
"build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "c20929c73caa78445525c5788b6963e0",
|
||||
"build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "0f21a43d99552df99e0d21c646e6e698",
|
||||
"build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "c20929c73caa78445525c5788b6963e0",
|
||||
"build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "0f21a43d99552df99e0d21c646e6e698",
|
||||
"build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "01dab862a43d9e7c4ee4e49212442d42",
|
||||
"build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "ae4e3f563892f6b9311c4b7284f28c11",
|
||||
"build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "01dab862a43d9e7c4ee4e49212442d42",
|
||||
"build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "ae4e3f563892f6b9311c4b7284f28c11",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "d24d48e0b6d35d91350b707b74dd95f5",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "c946a1405b62469889c1d596b2021753",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "92431ded412435040f3c0be770753060",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "72e4f88ff70048a2a49c907680c34121",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "0a68d461e96190865ce3457ec7c38e22",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "f8182ea0ad5537d4cf968a0b07594cff",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "996197f97e10bcea0bed1e88e8e75bdd",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "42461cf73d82008266892bf79f0634df",
|
||||
"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",
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
### 1.7.37 (build 22143, api 9, 2024-12-22)
|
||||
### 1.7.37 (build 22150, api 9, 2024-12-28)
|
||||
- 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.
|
||||
|
||||
@ -420,11 +420,13 @@
|
||||
"ba_data/python/bauiv1lib/account/__pycache__/__init__.cpython-312.opt-1.pyc",
|
||||
"ba_data/python/bauiv1lib/account/__pycache__/link.cpython-312.opt-1.pyc",
|
||||
"ba_data/python/bauiv1lib/account/__pycache__/settings.cpython-312.opt-1.pyc",
|
||||
"ba_data/python/bauiv1lib/account/__pycache__/signin.cpython-312.opt-1.pyc",
|
||||
"ba_data/python/bauiv1lib/account/__pycache__/unlink.cpython-312.opt-1.pyc",
|
||||
"ba_data/python/bauiv1lib/account/__pycache__/v2proxy.cpython-312.opt-1.pyc",
|
||||
"ba_data/python/bauiv1lib/account/__pycache__/viewer.cpython-312.opt-1.pyc",
|
||||
"ba_data/python/bauiv1lib/account/link.py",
|
||||
"ba_data/python/bauiv1lib/account/settings.py",
|
||||
"ba_data/python/bauiv1lib/account/signin.py",
|
||||
"ba_data/python/bauiv1lib/account/unlink.py",
|
||||
"ba_data/python/bauiv1lib/account/v2proxy.py",
|
||||
"ba_data/python/bauiv1lib/account/viewer.py",
|
||||
@ -476,9 +478,7 @@
|
||||
"ba_data/python/bauiv1lib/kiosk.py",
|
||||
"ba_data/python/bauiv1lib/league/__init__.py",
|
||||
"ba_data/python/bauiv1lib/league/__pycache__/__init__.cpython-312.opt-1.pyc",
|
||||
"ba_data/python/bauiv1lib/league/__pycache__/rankbutton.cpython-312.opt-1.pyc",
|
||||
"ba_data/python/bauiv1lib/league/__pycache__/rankwindow.cpython-312.opt-1.pyc",
|
||||
"ba_data/python/bauiv1lib/league/rankbutton.py",
|
||||
"ba_data/python/bauiv1lib/league/rankwindow.py",
|
||||
"ba_data/python/bauiv1lib/mainmenu.py",
|
||||
"ba_data/python/bauiv1lib/party.py",
|
||||
@ -570,10 +570,8 @@
|
||||
"ba_data/python/bauiv1lib/store/__init__.py",
|
||||
"ba_data/python/bauiv1lib/store/__pycache__/__init__.cpython-312.opt-1.pyc",
|
||||
"ba_data/python/bauiv1lib/store/__pycache__/browser.cpython-312.opt-1.pyc",
|
||||
"ba_data/python/bauiv1lib/store/__pycache__/button.cpython-312.opt-1.pyc",
|
||||
"ba_data/python/bauiv1lib/store/__pycache__/item.cpython-312.opt-1.pyc",
|
||||
"ba_data/python/bauiv1lib/store/browser.py",
|
||||
"ba_data/python/bauiv1lib/store/button.py",
|
||||
"ba_data/python/bauiv1lib/store/item.py",
|
||||
"ba_data/python/bauiv1lib/tabs.py",
|
||||
"ba_data/python/bauiv1lib/teamnamescolors.py",
|
||||
|
||||
@ -341,6 +341,7 @@ SCRIPT_TARGETS_PY_PUBLIC = \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/account/__init__.py \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/account/link.py \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/account/settings.py \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/account/signin.py \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/account/unlink.py \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/account/v2proxy.py \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/account/viewer.py \
|
||||
@ -378,7 +379,6 @@ SCRIPT_TARGETS_PY_PUBLIC = \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/keyboard/englishkeyboard.py \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/kiosk.py \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/league/__init__.py \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/league/rankbutton.py \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/league/rankwindow.py \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/mainmenu.py \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/party.py \
|
||||
@ -432,7 +432,6 @@ SCRIPT_TARGETS_PY_PUBLIC = \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/soundtrack/macmusicapp.py \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/store/__init__.py \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/store/browser.py \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/store/button.py \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/store/item.py \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/tabs.py \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/teamnamescolors.py \
|
||||
@ -622,6 +621,7 @@ SCRIPT_TARGETS_PYC_PUBLIC = \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/account/__pycache__/__init__.cpython-312.opt-1.pyc \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/account/__pycache__/link.cpython-312.opt-1.pyc \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/account/__pycache__/settings.cpython-312.opt-1.pyc \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/account/__pycache__/signin.cpython-312.opt-1.pyc \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/account/__pycache__/unlink.cpython-312.opt-1.pyc \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/account/__pycache__/v2proxy.cpython-312.opt-1.pyc \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/account/__pycache__/viewer.cpython-312.opt-1.pyc \
|
||||
@ -659,7 +659,6 @@ SCRIPT_TARGETS_PYC_PUBLIC = \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/keyboard/__pycache__/englishkeyboard.cpython-312.opt-1.pyc \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/__pycache__/kiosk.cpython-312.opt-1.pyc \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/league/__pycache__/__init__.cpython-312.opt-1.pyc \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/league/__pycache__/rankbutton.cpython-312.opt-1.pyc \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/league/__pycache__/rankwindow.cpython-312.opt-1.pyc \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/__pycache__/mainmenu.cpython-312.opt-1.pyc \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/__pycache__/party.cpython-312.opt-1.pyc \
|
||||
@ -713,7 +712,6 @@ SCRIPT_TARGETS_PYC_PUBLIC = \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/soundtrack/__pycache__/macmusicapp.cpython-312.opt-1.pyc \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/store/__pycache__/__init__.cpython-312.opt-1.pyc \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/store/__pycache__/browser.cpython-312.opt-1.pyc \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/store/__pycache__/button.cpython-312.opt-1.pyc \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/store/__pycache__/item.cpython-312.opt-1.pyc \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/__pycache__/tabs.cpython-312.opt-1.pyc \
|
||||
$(BUILD_DIR)/ba_data/python/bauiv1lib/__pycache__/teamnamescolors.cpython-312.opt-1.pyc \
|
||||
|
||||
@ -175,7 +175,7 @@ from babase._general import (
|
||||
get_type_name,
|
||||
)
|
||||
from babase._language import Lstr, LanguageSubsystem
|
||||
from babase._logging import balog, lifecyclelog
|
||||
from babase._logging import balog, applog, lifecyclelog
|
||||
from babase._login import LoginAdapter, LoginInfo
|
||||
|
||||
from babase._mgen.enums import (
|
||||
@ -214,6 +214,7 @@ __all__ = [
|
||||
'AppIntentExec',
|
||||
'AppMode',
|
||||
'app_instance_uuid',
|
||||
'applog',
|
||||
'appname',
|
||||
'appnameupper',
|
||||
'AppModeSelector',
|
||||
|
||||
@ -35,6 +35,16 @@ class AccountV2Subsystem:
|
||||
|
||||
from babase._login import LoginAdapterGPGS, LoginAdapterGameCenter
|
||||
|
||||
# Register to be informed when connectivity changes.
|
||||
plus = _babase.app.plus
|
||||
self._connectivity_changed_cb = (
|
||||
None
|
||||
if plus is None
|
||||
else plus.cloud.on_connectivity_changed_callbacks.register(
|
||||
self._on_cloud_connectivity_changed
|
||||
)
|
||||
)
|
||||
|
||||
# Whether or not everything related to an initial sign in (or
|
||||
# lack thereof) has completed. This includes things like
|
||||
# workspace syncing. Completion of this is what flips the app
|
||||
@ -265,7 +275,7 @@ class AccountV2Subsystem:
|
||||
# We may want to auto-sign-in based on this new state.
|
||||
self._update_auto_sign_in()
|
||||
|
||||
def on_cloud_connectivity_changed(self, connected: bool) -> None:
|
||||
def _on_cloud_connectivity_changed(self, connected: bool) -> None:
|
||||
"""Should be called with cloud connectivity changes."""
|
||||
del connected # Unused.
|
||||
assert _babase.in_logic_thread()
|
||||
|
||||
@ -25,7 +25,7 @@ from babase._appintent import AppIntentDefault, AppIntentExec
|
||||
from babase._stringedit import StringEditSubsystem
|
||||
from babase._devconsole import DevConsoleSubsystem
|
||||
from babase._appconfig import AppConfig
|
||||
from babase._logging import lifecyclelog
|
||||
from babase._logging import lifecyclelog, applog
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import asyncio
|
||||
@ -909,6 +909,7 @@ class App:
|
||||
# Entering shutdown state:
|
||||
if self.state is not self.State.SHUTTING_DOWN:
|
||||
self.state = self.State.SHUTTING_DOWN
|
||||
applog.info('Shutting down...')
|
||||
lifecyclelog.info('app-state is now %s', self.state.name)
|
||||
self._on_shutting_down()
|
||||
|
||||
|
||||
@ -27,20 +27,22 @@ class AppMode:
|
||||
"""Return whether this mode can handle the provided intent.
|
||||
|
||||
For this to return True, the AppMode must claim to support the
|
||||
provided intent (via its _supports_intent() method) AND the
|
||||
provided intent (via its _can_handle_intent() method) AND the
|
||||
AppExperience associated with the AppMode must be supported by
|
||||
the current app and runtime environment.
|
||||
"""
|
||||
# TODO: check AppExperience.
|
||||
return cls._supports_intent(intent)
|
||||
# TODO: check AppExperience against current environment.
|
||||
return cls._can_handle_intent(intent)
|
||||
|
||||
@classmethod
|
||||
def _supports_intent(cls, intent: AppIntent) -> bool:
|
||||
def _can_handle_intent(cls, intent: AppIntent) -> bool:
|
||||
"""Return whether our mode can handle the provided intent.
|
||||
|
||||
AppModes should override this to define what they can handle.
|
||||
Note that AppExperience does not have to be considered here; that
|
||||
is handled automatically by the can_handle_intent() call."""
|
||||
AppModes should override this to communicate what they can
|
||||
handle. Note that AppExperience does not have to be considered
|
||||
here; that is handled automatically by the can_handle_intent()
|
||||
call.
|
||||
"""
|
||||
raise NotImplementedError('AppMode subclasses must override this.')
|
||||
|
||||
def handle_intent(self, intent: AppIntent) -> None:
|
||||
|
||||
@ -26,7 +26,7 @@ class EmptyAppMode(AppMode):
|
||||
|
||||
@override
|
||||
@classmethod
|
||||
def _supports_intent(cls, intent: AppIntent) -> bool:
|
||||
def _can_handle_intent(cls, intent: AppIntent) -> bool:
|
||||
# We support default and exec intents currently.
|
||||
return isinstance(intent, AppIntentExec | AppIntentDefault)
|
||||
|
||||
|
||||
@ -8,4 +8,5 @@ import logging
|
||||
|
||||
# Our standard set of loggers.
|
||||
balog = logging.getLogger('ba')
|
||||
applog = logging.getLogger('ba.app')
|
||||
lifecyclelog = logging.getLogger('ba.lifecycle')
|
||||
|
||||
@ -208,6 +208,7 @@ class AccountV1Subsystem:
|
||||
'Player Profiles' not in config
|
||||
or '__account__' not in config['Player Profiles']
|
||||
):
|
||||
print('CREATING INITIAL')
|
||||
# Create a spaz with a nice default purply color.
|
||||
plus.add_v1_account_transaction(
|
||||
{
|
||||
|
||||
@ -11,6 +11,8 @@ from typing import TYPE_CHECKING, override
|
||||
from bacommon.app import AppExperience
|
||||
import babase
|
||||
import bauiv1
|
||||
from bauiv1lib.connectivity import wait_for_connectivity
|
||||
from bauiv1lib.account.signin import show_sign_in_prompt
|
||||
|
||||
import _baclassic
|
||||
|
||||
@ -30,9 +32,15 @@ class ClassicAppMode(babase.AppMode):
|
||||
self._on_primary_account_changed_callback: (
|
||||
CallbackRegistration | None
|
||||
) = None
|
||||
self._on_connectivity_changed_callback: CallbackRegistration | None = (
|
||||
None
|
||||
)
|
||||
self._test_sub: babase.CloudSubscription | None = None
|
||||
self._account_data_sub: babase.CloudSubscription | None = None
|
||||
|
||||
self._have_account_values = False
|
||||
self._have_connectivity = False
|
||||
|
||||
@override
|
||||
@classmethod
|
||||
def get_app_experience(cls) -> AppExperience:
|
||||
@ -40,7 +48,7 @@ class ClassicAppMode(babase.AppMode):
|
||||
|
||||
@override
|
||||
@classmethod
|
||||
def _supports_intent(cls, intent: babase.AppIntent) -> bool:
|
||||
def _can_handle_intent(cls, intent: babase.AppIntent) -> bool:
|
||||
# We support default and exec intents currently.
|
||||
return isinstance(
|
||||
intent, babase.AppIntentExec | babase.AppIntentDefault
|
||||
@ -118,14 +126,22 @@ class ClassicAppMode(babase.AppMode):
|
||||
self._root_ui_chest_slot_pressed, 3
|
||||
)
|
||||
|
||||
# We want to be informed when connectivity changes.
|
||||
self._on_connectivity_changed_callback = (
|
||||
plus.cloud.on_connectivity_changed_callbacks.register(
|
||||
self._update_for_connectivity_change
|
||||
)
|
||||
)
|
||||
# We want to be informed when primary account changes.
|
||||
self._on_primary_account_changed_callback = (
|
||||
plus.accounts.on_primary_account_changed_callbacks.register(
|
||||
self.update_for_primary_account
|
||||
self._update_for_primary_account
|
||||
)
|
||||
)
|
||||
# Establish subscriptions/etc. for any current primary account.
|
||||
self.update_for_primary_account(plus.accounts.primary)
|
||||
self._update_for_primary_account(plus.accounts.primary)
|
||||
self._have_connectivity = plus.cloud.is_connected()
|
||||
self._update_for_connectivity_change(self._have_connectivity)
|
||||
|
||||
@override
|
||||
def on_deactivate(self) -> None:
|
||||
@ -136,7 +152,7 @@ class ClassicAppMode(babase.AppMode):
|
||||
self._on_primary_account_changed_callback = None
|
||||
|
||||
# Remove anything following any current account.
|
||||
self.update_for_primary_account(None)
|
||||
self._update_for_primary_account(None)
|
||||
|
||||
# Save where we were in the UI so we return there next time.
|
||||
if classic is not None:
|
||||
@ -152,7 +168,7 @@ class ClassicAppMode(babase.AppMode):
|
||||
if not babase.app.active:
|
||||
babase.invoke_main_menu()
|
||||
|
||||
def update_for_primary_account(
|
||||
def _update_for_primary_account(
|
||||
self, account: babase.AccountV2Handle | None
|
||||
) -> None:
|
||||
"""Update subscriptions/etc. for a new primary account state."""
|
||||
@ -180,14 +196,14 @@ class ClassicAppMode(babase.AppMode):
|
||||
|
||||
if account is None:
|
||||
self._account_data_sub = None
|
||||
_baclassic.set_root_ui_values(
|
||||
tickets_text='-',
|
||||
tokens_text='-',
|
||||
league_rank_text='-',
|
||||
_baclassic.set_root_ui_account_values(
|
||||
tickets_text='',
|
||||
tokens_text='',
|
||||
league_rank_text='',
|
||||
league_type='',
|
||||
achievements_percent_text='-',
|
||||
level_text='-',
|
||||
xp_text='-',
|
||||
achievements_percent_text='',
|
||||
level_text='',
|
||||
xp_text='',
|
||||
inbox_count_text='',
|
||||
gold_pass=False,
|
||||
chest_0_appearance='',
|
||||
@ -195,6 +211,8 @@ class ClassicAppMode(babase.AppMode):
|
||||
chest_2_appearance='',
|
||||
chest_3_appearance='',
|
||||
)
|
||||
self._have_account_values = False
|
||||
self._update_ui_live_state()
|
||||
|
||||
else:
|
||||
with account:
|
||||
@ -204,6 +222,22 @@ class ClassicAppMode(babase.AppMode):
|
||||
)
|
||||
)
|
||||
|
||||
def _update_for_connectivity_change(self, connected: bool) -> None:
|
||||
"""Update when the app's connectivity state changes."""
|
||||
self._have_connectivity = connected
|
||||
self._update_ui_live_state()
|
||||
|
||||
def _update_ui_live_state(self) -> None:
|
||||
# We want to show ui elements faded if we don't have a live
|
||||
# connection to the master-server OR if we haven't received a
|
||||
# set of account values from them yet. If we just plug in raw
|
||||
# connectivity state here we get UI stuff un-fading a moment or
|
||||
# two before values appear (since the subscriptions have not
|
||||
# sent us any values yet) which looks odd.
|
||||
_baclassic.set_root_ui_have_live_values(
|
||||
self._have_connectivity and self._have_account_values
|
||||
)
|
||||
|
||||
def _on_sub_test_update(self, val: int | None) -> None:
|
||||
print(f'GOT SUB TEST UPDATE: {val}')
|
||||
|
||||
@ -221,7 +255,7 @@ class ClassicAppMode(babase.AppMode):
|
||||
chest2 = val.chests.get('2')
|
||||
chest3 = val.chests.get('3')
|
||||
|
||||
_baclassic.set_root_ui_values(
|
||||
_baclassic.set_root_ui_account_values(
|
||||
tickets_text=str(val.tickets),
|
||||
tokens_text=str(val.tokens),
|
||||
league_rank_text=(
|
||||
@ -249,6 +283,10 @@ class ClassicAppMode(babase.AppMode):
|
||||
),
|
||||
)
|
||||
|
||||
# Note that we have values and updated faded state accordingly.
|
||||
self._have_account_values = True
|
||||
self._update_ui_live_state()
|
||||
|
||||
def _root_ui_menu_press(self) -> None:
|
||||
from babase import push_back_press
|
||||
|
||||
@ -265,6 +303,7 @@ class ClassicAppMode(babase.AppMode):
|
||||
ui.clear_main_window()
|
||||
return
|
||||
|
||||
# Otherwise
|
||||
push_back_press()
|
||||
|
||||
def _root_ui_account_press(self) -> None:
|
||||
@ -393,17 +432,26 @@ class ClassicAppMode(babase.AppMode):
|
||||
def _root_ui_achievements_press(self) -> None:
|
||||
from bauiv1lib.achievements import AchievementsWindow
|
||||
|
||||
self._auxiliary_window_nav(
|
||||
win_type=AchievementsWindow,
|
||||
win_create_call=lambda: AchievementsWindow(
|
||||
origin_widget=bauiv1.get_special_widget('achievements_button')
|
||||
),
|
||||
if not self._ensure_signed_in_v1():
|
||||
return
|
||||
|
||||
wait_for_connectivity(
|
||||
on_connected=lambda: self._auxiliary_window_nav(
|
||||
win_type=AchievementsWindow,
|
||||
win_create_call=lambda: AchievementsWindow(
|
||||
origin_widget=bauiv1.get_special_widget(
|
||||
'achievements_button'
|
||||
)
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
def _root_ui_inbox_press(self) -> None:
|
||||
from bauiv1lib.connectivity import wait_for_connectivity
|
||||
from bauiv1lib.inbox import InboxWindow
|
||||
|
||||
if not self._ensure_signed_in():
|
||||
return
|
||||
|
||||
wait_for_connectivity(
|
||||
on_connected=lambda: self._auxiliary_window_nav(
|
||||
win_type=InboxWindow,
|
||||
@ -416,11 +464,16 @@ class ClassicAppMode(babase.AppMode):
|
||||
def _root_ui_store_press(self) -> None:
|
||||
from bauiv1lib.store.browser import StoreBrowserWindow
|
||||
|
||||
self._auxiliary_window_nav(
|
||||
win_type=StoreBrowserWindow,
|
||||
win_create_call=lambda: StoreBrowserWindow(
|
||||
origin_widget=bauiv1.get_special_widget('store_button')
|
||||
),
|
||||
if not self._ensure_signed_in_v1():
|
||||
return
|
||||
|
||||
wait_for_connectivity(
|
||||
on_connected=lambda: self._auxiliary_window_nav(
|
||||
win_type=StoreBrowserWindow,
|
||||
win_create_call=lambda: StoreBrowserWindow(
|
||||
origin_widget=bauiv1.get_special_widget('store_button')
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
def _root_ui_tickets_meter_press(self) -> None:
|
||||
@ -438,13 +491,9 @@ class ClassicAppMode(babase.AppMode):
|
||||
)
|
||||
|
||||
def _root_ui_trophy_meter_press(self) -> None:
|
||||
from bauiv1lib.account import show_sign_in_prompt
|
||||
from bauiv1lib.league.rankwindow import LeagueRankWindow
|
||||
|
||||
plus = bauiv1.app.plus
|
||||
assert plus is not None
|
||||
if plus.get_v1_account_state() != 'signed_in':
|
||||
show_sign_in_prompt()
|
||||
if not self._ensure_signed_in_v1():
|
||||
return
|
||||
|
||||
self._auxiliary_window_nav(
|
||||
@ -464,6 +513,9 @@ class ClassicAppMode(babase.AppMode):
|
||||
def _root_ui_inventory_press(self) -> None:
|
||||
from bauiv1lib.inventory import InventoryWindow
|
||||
|
||||
if not self._ensure_signed_in_v1():
|
||||
return
|
||||
|
||||
self._auxiliary_window_nav(
|
||||
win_type=InventoryWindow,
|
||||
win_create_call=lambda: InventoryWindow(
|
||||
@ -471,9 +523,36 @@ class ClassicAppMode(babase.AppMode):
|
||||
),
|
||||
)
|
||||
|
||||
def _ensure_signed_in(self) -> bool:
|
||||
"""Make sure we're signed in (requiring modern v2 accounts)."""
|
||||
plus = bauiv1.app.plus
|
||||
if plus is None:
|
||||
bauiv1.screenmessage('This requires plus.', color=(1, 0, 0))
|
||||
bauiv1.getsound('error').play()
|
||||
return False
|
||||
if plus.accounts.primary is None:
|
||||
show_sign_in_prompt()
|
||||
return False
|
||||
return True
|
||||
|
||||
def _ensure_signed_in_v1(self) -> bool:
|
||||
"""Make sure we're signed in (allowing legacy v1-only accounts)."""
|
||||
plus = bauiv1.app.plus
|
||||
if plus is None:
|
||||
bauiv1.screenmessage('This requires plus.', color=(1, 0, 0))
|
||||
bauiv1.getsound('error').play()
|
||||
return False
|
||||
if plus.get_v1_account_state() != 'signed_in':
|
||||
show_sign_in_prompt()
|
||||
return False
|
||||
return True
|
||||
|
||||
def _root_ui_get_tokens_press(self) -> None:
|
||||
from bauiv1lib.gettokens import GetTokensWindow
|
||||
|
||||
if not self._ensure_signed_in():
|
||||
return
|
||||
|
||||
self._auxiliary_window_nav(
|
||||
win_type=GetTokensWindow,
|
||||
win_create_call=lambda: GetTokensWindow(
|
||||
@ -488,7 +567,6 @@ class ClassicAppMode(babase.AppMode):
|
||||
ChestWindow2,
|
||||
ChestWindow3,
|
||||
)
|
||||
from bauiv1lib.connectivity import wait_for_connectivity
|
||||
|
||||
widgetid: Literal[
|
||||
'chest_0_button',
|
||||
|
||||
@ -53,7 +53,7 @@ if TYPE_CHECKING:
|
||||
|
||||
# Build number and version of the ballistica binary we expect to be
|
||||
# using.
|
||||
TARGET_BALLISTICA_BUILD = 22143
|
||||
TARGET_BALLISTICA_BUILD = 22150
|
||||
TARGET_BALLISTICA_VERSION = '1.7.37'
|
||||
|
||||
|
||||
|
||||
@ -7,6 +7,7 @@ from __future__ import annotations
|
||||
import logging
|
||||
from typing import TYPE_CHECKING, overload
|
||||
|
||||
from efro.call import CallbackSet
|
||||
import babase
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@ -24,6 +25,12 @@ if TYPE_CHECKING:
|
||||
class CloudSubsystem(babase.AppSubsystem):
|
||||
"""Manages communication with cloud components."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
self.on_connectivity_changed_callbacks: CallbackSet[
|
||||
Callable[[bool], None]
|
||||
] = CallbackSet()
|
||||
|
||||
@property
|
||||
def connected(self) -> bool:
|
||||
"""Property equivalent of CloudSubsystem.is_connected()."""
|
||||
@ -44,9 +51,12 @@ class CloudSubsystem(babase.AppSubsystem):
|
||||
plus = babase.app.plus
|
||||
assert plus is not None
|
||||
|
||||
# Inform things that use this.
|
||||
# (TODO: should generalize this into some sort of registration system)
|
||||
plus.accounts.on_cloud_connectivity_changed(connected)
|
||||
# Fire any registered callbacks for this.
|
||||
for call in self.on_connectivity_changed_callbacks.getcalls():
|
||||
try:
|
||||
call(connected)
|
||||
except Exception:
|
||||
logging.exception('Error in connectivity-changed callback.')
|
||||
|
||||
@overload
|
||||
def send_message_cb(
|
||||
|
||||
@ -19,9 +19,6 @@ from bascenev1lib.actor.zoomtext import ZoomText
|
||||
if TYPE_CHECKING:
|
||||
from typing import Any, Sequence
|
||||
|
||||
from bauiv1lib.store.button import StoreButton
|
||||
from bauiv1lib.league.rankbutton import LeagueRankButton
|
||||
|
||||
|
||||
class CoopScoreScreen(bs.Activity[bs.Player, bs.Team]):
|
||||
"""Score screen showing the results of a cooperative game."""
|
||||
@ -105,10 +102,7 @@ class CoopScoreScreen(bs.Activity[bs.Player, bs.Team]):
|
||||
|
||||
# Ui bits.
|
||||
self._corner_button_offs: tuple[float, float] | None = None
|
||||
self._league_rank_button: LeagueRankButton | None = None
|
||||
self._store_button_instance: StoreButton | None = None
|
||||
self._restart_button: bui.Widget | None = None
|
||||
self._update_corner_button_positions_timer: bui.AppTimer | None = None
|
||||
self._next_level_error: bs.Actor | None = None
|
||||
|
||||
# Score/gameplay bits.
|
||||
@ -207,11 +201,6 @@ class CoopScoreScreen(bs.Activity[bs.Player, bs.Team]):
|
||||
)
|
||||
|
||||
def _ui_menu(self) -> None:
|
||||
# from bauiv1lib import specialoffer
|
||||
|
||||
# if specialoffer.show_offer():
|
||||
# return
|
||||
|
||||
bui.containerwidget(edit=self._root_ui, transition='out_left')
|
||||
with self.context:
|
||||
bs.timer(0.1, bs.Call(bs.WeakCall(self.session.end)))
|
||||
@ -219,11 +208,6 @@ class CoopScoreScreen(bs.Activity[bs.Player, bs.Team]):
|
||||
def _ui_restart(self) -> None:
|
||||
from bauiv1lib.tournamententry import TournamentEntryWindow
|
||||
|
||||
# from bauiv1lib import specialoffer
|
||||
|
||||
# if specialoffer.show_offer():
|
||||
# return
|
||||
|
||||
# If we're in a tournament and it looks like there's no time left,
|
||||
# disallow.
|
||||
if self.session.tournament_id is not None:
|
||||
@ -270,10 +254,6 @@ class CoopScoreScreen(bs.Activity[bs.Player, bs.Team]):
|
||||
self.end({'outcome': 'restart'})
|
||||
|
||||
def _ui_next(self) -> None:
|
||||
# from bauiv1lib.specialoffer import show_offer
|
||||
|
||||
# if show_offer():
|
||||
# return
|
||||
|
||||
# If we didn't just complete this level but are choosing to play the
|
||||
# next one, set it as current (this won't happen otherwise).
|
||||
@ -333,6 +313,12 @@ class CoopScoreScreen(bs.Activity[bs.Player, bs.Team]):
|
||||
)
|
||||
|
||||
def _should_show_worlds_best_button(self) -> bool:
|
||||
|
||||
# Old high score lists webpage for tourneys seems broken
|
||||
# (looking at meteor shower at least).
|
||||
if self.session.tournament_id is not None:
|
||||
return False
|
||||
|
||||
# Link is too complicated to display with no browser.
|
||||
return bui.is_browser_likely_available()
|
||||
|
||||
@ -349,8 +335,8 @@ class CoopScoreScreen(bs.Activity[bs.Player, bs.Team]):
|
||||
def show_ui(self) -> None:
|
||||
"""Show the UI for restarting, playing the next Level, etc."""
|
||||
# pylint: disable=too-many-locals
|
||||
from bauiv1lib.store.button import StoreButton
|
||||
from bauiv1lib.league.rankbutton import LeagueRankButton
|
||||
# pylint: disable=too-many-statements
|
||||
# pylint: disable=too-many-branches
|
||||
|
||||
assert bui.app.classic is not None
|
||||
|
||||
@ -364,7 +350,9 @@ class CoopScoreScreen(bs.Activity[bs.Player, bs.Team]):
|
||||
return
|
||||
|
||||
rootc = self._root_ui = bui.containerwidget(
|
||||
size=(0, 0), transition='in_right'
|
||||
size=(0, 0),
|
||||
transition='in_right',
|
||||
toolbar_visibility='no_menu_minimal',
|
||||
)
|
||||
|
||||
h_offs = 7.0
|
||||
@ -420,38 +408,83 @@ class CoopScoreScreen(bs.Activity[bs.Player, bs.Team]):
|
||||
if not show_next_button:
|
||||
h_offs += 70
|
||||
|
||||
menu_button = bui.buttonwidget(
|
||||
parent=rootc,
|
||||
autoselect=True,
|
||||
position=(h_offs - 130 - 60, v_offs),
|
||||
size=(110, 85),
|
||||
label='',
|
||||
on_activate_call=bui.WeakCall(self._ui_menu),
|
||||
)
|
||||
bui.imagewidget(
|
||||
parent=rootc,
|
||||
draw_controller=menu_button,
|
||||
position=(h_offs - 130 - 60 + 22, v_offs + 14),
|
||||
size=(60, 60),
|
||||
texture=self._menu_icon_texture,
|
||||
opacity=0.8,
|
||||
)
|
||||
self._restart_button = restart_button = bui.buttonwidget(
|
||||
parent=rootc,
|
||||
autoselect=True,
|
||||
position=(h_offs - 60, v_offs),
|
||||
size=(110, 85),
|
||||
label='',
|
||||
on_activate_call=bui.WeakCall(self._ui_restart),
|
||||
)
|
||||
bui.imagewidget(
|
||||
parent=rootc,
|
||||
draw_controller=restart_button,
|
||||
position=(h_offs - 60 + 19, v_offs + 7),
|
||||
size=(70, 70),
|
||||
texture=self._replay_icon_texture,
|
||||
opacity=0.8,
|
||||
)
|
||||
# Due to virtual-bounds changes, have to squish buttons a bit to
|
||||
# avoid overlapping with tips at bottom. Could look nicer to
|
||||
# rework things in the middle to get more space, but would
|
||||
# rather not touch this old code more than necessary.
|
||||
small_buttons = True
|
||||
|
||||
if small_buttons:
|
||||
menu_button = bui.buttonwidget(
|
||||
parent=rootc,
|
||||
autoselect=True,
|
||||
position=(h_offs - 130 - 45, v_offs + 40),
|
||||
size=(100, 50),
|
||||
label='',
|
||||
button_type='square',
|
||||
on_activate_call=bui.WeakCall(self._ui_menu),
|
||||
)
|
||||
bui.imagewidget(
|
||||
parent=rootc,
|
||||
draw_controller=menu_button,
|
||||
position=(h_offs - 130 - 60 + 43, v_offs + 43),
|
||||
size=(45, 45),
|
||||
texture=self._menu_icon_texture,
|
||||
opacity=0.8,
|
||||
)
|
||||
else:
|
||||
menu_button = bui.buttonwidget(
|
||||
parent=rootc,
|
||||
autoselect=True,
|
||||
position=(h_offs - 130 - 60, v_offs),
|
||||
size=(110, 85),
|
||||
label='',
|
||||
on_activate_call=bui.WeakCall(self._ui_menu),
|
||||
)
|
||||
bui.imagewidget(
|
||||
parent=rootc,
|
||||
draw_controller=menu_button,
|
||||
position=(h_offs - 130 - 60 + 22, v_offs + 14),
|
||||
size=(60, 60),
|
||||
texture=self._menu_icon_texture,
|
||||
opacity=0.8,
|
||||
)
|
||||
|
||||
if small_buttons:
|
||||
self._restart_button = restart_button = bui.buttonwidget(
|
||||
parent=rootc,
|
||||
autoselect=True,
|
||||
position=(h_offs - 60, v_offs + 40),
|
||||
size=(100, 50),
|
||||
label='',
|
||||
button_type='square',
|
||||
on_activate_call=bui.WeakCall(self._ui_restart),
|
||||
)
|
||||
bui.imagewidget(
|
||||
parent=rootc,
|
||||
draw_controller=restart_button,
|
||||
position=(h_offs - 60 + 25, v_offs + 42),
|
||||
size=(47, 47),
|
||||
texture=self._replay_icon_texture,
|
||||
opacity=0.8,
|
||||
)
|
||||
else:
|
||||
self._restart_button = restart_button = bui.buttonwidget(
|
||||
parent=rootc,
|
||||
autoselect=True,
|
||||
position=(h_offs - 60, v_offs),
|
||||
size=(110, 85),
|
||||
label='',
|
||||
on_activate_call=bui.WeakCall(self._ui_restart),
|
||||
)
|
||||
bui.imagewidget(
|
||||
parent=rootc,
|
||||
draw_controller=restart_button,
|
||||
position=(h_offs - 60 + 19, v_offs + 7),
|
||||
size=(70, 70),
|
||||
texture=self._replay_icon_texture,
|
||||
opacity=0.8,
|
||||
)
|
||||
|
||||
next_button: bui.Widget | None = None
|
||||
|
||||
@ -468,24 +501,46 @@ class CoopScoreScreen(bs.Activity[bs.Player, bs.Team]):
|
||||
button_sound = False
|
||||
image_opacity = 0.2
|
||||
color = (0.3, 0.3, 0.3)
|
||||
next_button = bui.buttonwidget(
|
||||
parent=rootc,
|
||||
autoselect=True,
|
||||
position=(h_offs + 130 - 60, v_offs),
|
||||
size=(110, 85),
|
||||
label='',
|
||||
on_activate_call=call,
|
||||
color=color,
|
||||
enable_sound=button_sound,
|
||||
)
|
||||
bui.imagewidget(
|
||||
parent=rootc,
|
||||
draw_controller=next_button,
|
||||
position=(h_offs + 130 - 60 + 12, v_offs + 5),
|
||||
size=(80, 80),
|
||||
texture=self._next_level_icon_texture,
|
||||
opacity=image_opacity,
|
||||
)
|
||||
|
||||
if small_buttons:
|
||||
next_button = bui.buttonwidget(
|
||||
parent=rootc,
|
||||
autoselect=True,
|
||||
position=(h_offs + 130 - 75, v_offs + 40),
|
||||
size=(100, 50),
|
||||
label='',
|
||||
button_type='square',
|
||||
on_activate_call=call,
|
||||
color=color,
|
||||
enable_sound=button_sound,
|
||||
)
|
||||
bui.imagewidget(
|
||||
parent=rootc,
|
||||
draw_controller=next_button,
|
||||
position=(h_offs + 130 - 60 + 12, v_offs + 40),
|
||||
size=(50, 50),
|
||||
texture=self._next_level_icon_texture,
|
||||
opacity=image_opacity,
|
||||
)
|
||||
else:
|
||||
next_button = bui.buttonwidget(
|
||||
parent=rootc,
|
||||
autoselect=True,
|
||||
position=(h_offs + 130 - 60, v_offs),
|
||||
size=(110, 85),
|
||||
label='',
|
||||
on_activate_call=call,
|
||||
color=color,
|
||||
enable_sound=button_sound,
|
||||
)
|
||||
bui.imagewidget(
|
||||
parent=rootc,
|
||||
draw_controller=next_button,
|
||||
position=(h_offs + 130 - 60 + 12, v_offs + 5),
|
||||
size=(80, 80),
|
||||
texture=self._next_level_icon_texture,
|
||||
opacity=image_opacity,
|
||||
)
|
||||
|
||||
x_offs_extra = 0 if show_next_button else -100
|
||||
self._corner_button_offs = (
|
||||
@ -493,33 +548,6 @@ class CoopScoreScreen(bs.Activity[bs.Player, bs.Team]):
|
||||
v_offs + 519.0,
|
||||
)
|
||||
|
||||
if env.demo or env.arcade:
|
||||
self._league_rank_button = None
|
||||
self._store_button_instance = None
|
||||
else:
|
||||
self._league_rank_button = LeagueRankButton(
|
||||
parent=rootc,
|
||||
position=(h_offs + 300 + x_offs_extra, v_offs + 519),
|
||||
size=(100, 60),
|
||||
scale=0.9,
|
||||
color=(0.4, 0.4, 0.9),
|
||||
textcolor=(0.9, 0.9, 2.0),
|
||||
transition_delay=0.0,
|
||||
smooth_update_delay=5.0,
|
||||
)
|
||||
self._store_button_instance = StoreButton(
|
||||
parent=rootc,
|
||||
position=(h_offs + 400 + x_offs_extra, v_offs + 519),
|
||||
show_tickets=True,
|
||||
sale_scale=0.85,
|
||||
size=(100, 60),
|
||||
scale=0.9,
|
||||
button_type='square',
|
||||
color=(0.35, 0.25, 0.45),
|
||||
textcolor=(0.9, 0.7, 1.0),
|
||||
transition_delay=0.0,
|
||||
)
|
||||
|
||||
bui.containerwidget(
|
||||
edit=rootc,
|
||||
selected_child=(
|
||||
@ -530,25 +558,12 @@ class CoopScoreScreen(bs.Activity[bs.Player, bs.Team]):
|
||||
on_cancel_call=menu_button.activate,
|
||||
)
|
||||
|
||||
self._update_corner_button_positions()
|
||||
self._update_corner_button_positions_timer = bui.AppTimer(
|
||||
1.0, bui.WeakCall(self._update_corner_button_positions), repeat=True
|
||||
)
|
||||
|
||||
def _update_corner_button_positions(self) -> None:
|
||||
assert self._corner_button_offs is not None
|
||||
pos_x = self._corner_button_offs[0]
|
||||
pos_y = self._corner_button_offs[1]
|
||||
if self._league_rank_button is not None:
|
||||
self._league_rank_button.set_position((pos_x, pos_y))
|
||||
if self._store_button_instance is not None:
|
||||
self._store_button_instance.set_position((pos_x + 100, pos_y))
|
||||
|
||||
def _player_press(self) -> None:
|
||||
# (Only for headless builds).
|
||||
|
||||
# If this activity is a good 'end point', ask server-mode just once if
|
||||
# it wants to do anything special like switch sessions or kill the app.
|
||||
# If this activity is a good 'end point', ask server-mode just
|
||||
# once if it wants to do anything special like switch sessions
|
||||
# or kill the app.
|
||||
if (
|
||||
self._allow_server_transition
|
||||
and bs.app.classic is not None
|
||||
@ -715,7 +730,7 @@ class CoopScoreScreen(bs.Activity[bs.Player, bs.Team]):
|
||||
color=(0.5, 1, 0.5, 1),
|
||||
h_align='center',
|
||||
scale=0.4,
|
||||
position=(0, 255),
|
||||
position=(0, 260),
|
||||
jitter=1.0,
|
||||
).autoretain()
|
||||
Text(
|
||||
|
||||
@ -162,40 +162,6 @@ class UIV1AppSubsystem(babase.AppSubsystem):
|
||||
# checks.
|
||||
self.upkeeptimer = babase.AppTimer(2.6543, ui_upkeep, repeat=True)
|
||||
|
||||
def auto_set_back_window(self, from_window: MainWindow) -> None:
|
||||
"""Sets the main menu window automatically from a parent WindowState."""
|
||||
|
||||
main_window = self._main_window()
|
||||
|
||||
# This should never get called for top-level main-windows.
|
||||
assert (
|
||||
main_window is None or main_window.main_window_is_top_level is False
|
||||
)
|
||||
|
||||
back_state = (
|
||||
None if main_window is None else main_window.main_window_back_state
|
||||
)
|
||||
if back_state is None:
|
||||
raise RuntimeError(
|
||||
f'Main window {main_window} provides no back-state;'
|
||||
f' cannot use auto-back.'
|
||||
)
|
||||
|
||||
# Valid states should have values here.
|
||||
assert back_state.is_top_level is not None
|
||||
assert back_state.is_auxiliary is not None
|
||||
assert back_state.window_type is not None
|
||||
|
||||
backwin = back_state.create_window(transition='in_left')
|
||||
|
||||
self.set_main_window(
|
||||
backwin,
|
||||
from_window=from_window,
|
||||
is_back=True,
|
||||
back_state=back_state,
|
||||
suppress_warning=True,
|
||||
)
|
||||
|
||||
def get_main_window(self) -> bauiv1.MainWindow | None:
|
||||
"""Return main window, if any."""
|
||||
return self._main_window()
|
||||
@ -211,11 +177,14 @@ class UIV1AppSubsystem(babase.AppSubsystem):
|
||||
back_state: MainWindowState | None = None,
|
||||
suppress_warning: bool = False,
|
||||
) -> None:
|
||||
"""Set the current 'main' window, replacing any existing.
|
||||
"""Set the current 'main' window.
|
||||
|
||||
Generally this should not be called directly; The high level
|
||||
MainWindow methods main_window_replace() and main_window_back()
|
||||
should be used when possible for navigation.
|
||||
should be used whenever possible to implement navigation.
|
||||
|
||||
The caller is responsible for cleaning up any previous main
|
||||
window.
|
||||
"""
|
||||
# pylint: disable=too-many-locals
|
||||
# pylint: disable=too-many-branches
|
||||
|
||||
@ -46,7 +46,14 @@ class Window:
|
||||
|
||||
|
||||
class MainWindow(Window):
|
||||
"""A special window that can be used as a main window."""
|
||||
"""A special type of window that can be set as 'main'.
|
||||
|
||||
The UI system has at most one main window at any given time.
|
||||
MainWindows support high level functionality such as saving and
|
||||
restoring states, allowing them to be automatically recreated when
|
||||
navigating back from other locations or when something like ui-scale
|
||||
changes.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@ -146,11 +153,33 @@ class MainWindow(Window):
|
||||
if not self.main_window_has_control():
|
||||
return
|
||||
|
||||
uiv1 = babase.app.ui_v1
|
||||
|
||||
# Get the 'back' window coming in.
|
||||
if not self.main_window_is_top_level:
|
||||
|
||||
# Get the 'back' window coming in.
|
||||
babase.app.ui_v1.auto_set_back_window(self)
|
||||
back_state = self.main_window_back_state
|
||||
if back_state is None:
|
||||
raise RuntimeError(
|
||||
f'Main window {self} provides no back-state.'
|
||||
)
|
||||
|
||||
# Valid states should have values here.
|
||||
assert back_state.is_top_level is not None
|
||||
assert back_state.is_auxiliary is not None
|
||||
assert back_state.window_type is not None
|
||||
|
||||
backwin = back_state.create_window(transition='in_left')
|
||||
|
||||
uiv1.set_main_window(
|
||||
backwin,
|
||||
from_window=self,
|
||||
is_back=True,
|
||||
back_state=back_state,
|
||||
suppress_warning=True,
|
||||
)
|
||||
|
||||
# Transition ourself out.
|
||||
self.main_window_close()
|
||||
|
||||
def main_window_replace(
|
||||
@ -203,7 +232,7 @@ class MainWindow(Window):
|
||||
|
||||
|
||||
class MainWindowState:
|
||||
"""Persistent state for a specific main-window and its ancestors.
|
||||
"""Persistent state for a specific MainWindow.
|
||||
|
||||
This allows MainWindows to be automatically recreated for back-button
|
||||
purposes, when switching app-modes, etc.
|
||||
|
||||
@ -1,21 +1,3 @@
|
||||
# Released under the MIT License. See LICENSE for details.
|
||||
#
|
||||
"""UI functionality related to accounts."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import bauiv1 as bui
|
||||
|
||||
|
||||
def show_sign_in_prompt() -> None:
|
||||
"""Bring up a prompt telling the user they must sign in."""
|
||||
from bauiv1lib.confirm import ConfirmWindow
|
||||
from bauiv1lib.account.settings import AccountSettingsWindow
|
||||
|
||||
ConfirmWindow(
|
||||
bui.Lstr(resource='notSignedInErrorText'),
|
||||
lambda: AccountSettingsWindow(modal=True, close_once_signed_in=True),
|
||||
ok_text=bui.Lstr(resource='accountSettingsWindow.signInText'),
|
||||
width=460,
|
||||
height=130,
|
||||
)
|
||||
|
||||
@ -105,13 +105,13 @@ class AccountLinkWindow(bui.Window):
|
||||
)
|
||||
|
||||
def _generate_press(self) -> None:
|
||||
from bauiv1lib import account
|
||||
from bauiv1lib.account.signin import show_sign_in_prompt
|
||||
|
||||
plus = bui.app.plus
|
||||
assert plus is not None
|
||||
|
||||
if plus.get_v1_account_state() != 'signed_in':
|
||||
account.show_sign_in_prompt()
|
||||
show_sign_in_prompt()
|
||||
return
|
||||
bui.screenmessage(
|
||||
bui.Lstr(resource='gatherWindow.requestingAPromoCodeText'),
|
||||
|
||||
@ -28,7 +28,6 @@ class AccountSettingsWindow(bui.MainWindow):
|
||||
def __init__(
|
||||
self,
|
||||
transition: str | None = 'in_right',
|
||||
modal: bool = False,
|
||||
origin_widget: bui.Widget | None = None,
|
||||
close_once_signed_in: bool = False,
|
||||
):
|
||||
@ -49,7 +48,6 @@ class AccountSettingsWindow(bui.MainWindow):
|
||||
self._explicitly_signed_out_of_gpgs = False
|
||||
|
||||
self._r = 'accountSettingsWindow'
|
||||
self._modal = modal
|
||||
self._needs_refresh = False
|
||||
self._v1_signed_in = plus.get_v1_account_state() == 'signed_in'
|
||||
self._v1_account_state_num = plus.get_v1_account_state_num()
|
||||
@ -129,22 +127,17 @@ class AccountSettingsWindow(bui.MainWindow):
|
||||
scale=0.8,
|
||||
text_scale=1.2,
|
||||
autoselect=True,
|
||||
label=bui.Lstr(
|
||||
resource='cancelText' if self._modal else 'backText'
|
||||
),
|
||||
button_type='regular' if self._modal else 'back',
|
||||
on_activate_call=(
|
||||
self._modal_close if self._modal else self.main_window_back
|
||||
),
|
||||
label=bui.Lstr(resource='backText'),
|
||||
button_type='back',
|
||||
on_activate_call=self.main_window_back,
|
||||
)
|
||||
bui.containerwidget(edit=self._root_widget, cancel_button=btn)
|
||||
if not self._modal:
|
||||
bui.buttonwidget(
|
||||
edit=btn,
|
||||
button_type='backSmall',
|
||||
size=(60, 56),
|
||||
label=bui.charstr(bui.SpecialChar.BACK),
|
||||
)
|
||||
bui.buttonwidget(
|
||||
edit=btn,
|
||||
button_type='backSmall',
|
||||
size=(60, 56),
|
||||
label=bui.charstr(bui.SpecialChar.BACK),
|
||||
)
|
||||
|
||||
titleyoffs = -9 if uiscale is bui.UIScale.SMALL else 0
|
||||
titlescale = 0.7 if uiscale is bui.UIScale.SMALL else 1.0
|
||||
@ -176,18 +169,6 @@ class AccountSettingsWindow(bui.MainWindow):
|
||||
self._refresh()
|
||||
self._restore_state()
|
||||
|
||||
def _modal_close(self) -> None:
|
||||
assert self._modal
|
||||
|
||||
# no-op if our underlying widget is dead or on its way out.
|
||||
if not self._root_widget or self._root_widget.transitioning_out:
|
||||
return
|
||||
|
||||
bui.containerwidget(
|
||||
edit=self._root_widget,
|
||||
transition=('out_right'),
|
||||
)
|
||||
|
||||
@override
|
||||
def get_main_window_state(self) -> bui.MainWindowState:
|
||||
# Support recreating our window for back/refresh purposes.
|
||||
@ -369,6 +350,9 @@ class AccountSettingsWindow(bui.MainWindow):
|
||||
show_manage_account_button = primary_v2_account is not None
|
||||
manage_account_button_space = 70.0
|
||||
|
||||
show_create_account_button = show_v2_proxy_sign_in_button
|
||||
create_account_button_space = 70.0
|
||||
|
||||
# Apple asks us to make a delete-account button directly
|
||||
# available in the UI. Currently disabling this elsewhere
|
||||
# however as I feel that poking 'Manage Account' and scrolling
|
||||
@ -445,6 +429,8 @@ class AccountSettingsWindow(bui.MainWindow):
|
||||
self._sub_height += sign_in_benefits_space
|
||||
if show_manage_account_button:
|
||||
self._sub_height += manage_account_button_space
|
||||
if show_create_account_button:
|
||||
self._sub_height += create_account_button_space
|
||||
if show_link_accounts_button:
|
||||
self._sub_height += link_accounts_button_space
|
||||
if show_v1_obsolete_note:
|
||||
@ -856,6 +842,28 @@ class AccountSettingsWindow(bui.MainWindow):
|
||||
)
|
||||
bui.widget(edit=btn, left_widget=bbtn)
|
||||
|
||||
if show_create_account_button:
|
||||
button_width = 300
|
||||
v -= create_account_button_space
|
||||
self._create_button = btn = bui.buttonwidget(
|
||||
parent=self._subcontainer,
|
||||
position=((self._sub_width - button_width) * 0.5, v - 30),
|
||||
autoselect=True,
|
||||
size=(button_width, 60),
|
||||
# label=bui.Lstr(resource=f'{self._r}.createAccountText'),
|
||||
label='Create an Account',
|
||||
color=(0.55, 0.5, 0.6),
|
||||
# icon=bui.gettexture('settingsIcon'),
|
||||
textcolor=(0.75, 0.7, 0.8),
|
||||
on_activate_call=bui.WeakCall(self._on_create_account_press),
|
||||
)
|
||||
if first_selectable is None:
|
||||
first_selectable = btn
|
||||
bui.widget(
|
||||
edit=btn, right_widget=bui.get_special_widget('squad_button')
|
||||
)
|
||||
bui.widget(edit=btn, left_widget=bbtn)
|
||||
|
||||
# the button to go to OS-Specific leaderboards/high-score-lists/etc.
|
||||
if show_game_service_button:
|
||||
button_width = 300
|
||||
@ -1212,6 +1220,9 @@ class AccountSettingsWindow(bui.MainWindow):
|
||||
def _on_manage_account_press(self) -> None:
|
||||
self._do_manage_account_press(WebLocation.ACCOUNT_EDITOR)
|
||||
|
||||
def _on_create_account_press(self) -> None:
|
||||
bui.open_url('https://ballistica.net/createaccount')
|
||||
|
||||
def _on_delete_account_press(self) -> None:
|
||||
self._do_manage_account_press(WebLocation.ACCOUNT_DELETE_SECTION)
|
||||
|
||||
|
||||
51
src/assets/ba_data/python/bauiv1lib/account/signin.py
Normal file
51
src/assets/ba_data/python/bauiv1lib/account/signin.py
Normal file
@ -0,0 +1,51 @@
|
||||
# Released under the MIT License. See LICENSE for details.
|
||||
#
|
||||
"""UI functionality related to accounts."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import bauiv1 as bui
|
||||
|
||||
|
||||
def show_sign_in_prompt() -> None:
|
||||
"""Bring up a prompt telling the user they must sign in."""
|
||||
from bauiv1lib.confirm import ConfirmWindow
|
||||
|
||||
ConfirmWindow(
|
||||
bui.Lstr(resource='notSignedInErrorText'),
|
||||
_show_account_settings,
|
||||
ok_text=bui.Lstr(resource='accountSettingsWindow.signInText'),
|
||||
width=460,
|
||||
height=130,
|
||||
)
|
||||
|
||||
|
||||
def _show_account_settings() -> None:
|
||||
from bauiv1lib.account.settings import AccountSettingsWindow
|
||||
|
||||
# NOTE TO USERS: The code below is not the proper way to do things;
|
||||
# whenever possible one should use a MainWindow's
|
||||
# main_window_replace() or main_window_back() methods. We just need
|
||||
# to do things a bit more manually in this case.
|
||||
|
||||
prev_main_window = bui.app.ui_v1.get_main_window()
|
||||
|
||||
# Special-case: If it seems we're already in the account window, do
|
||||
# nothing.
|
||||
if isinstance(prev_main_window, AccountSettingsWindow):
|
||||
return
|
||||
|
||||
# Set our new main window.
|
||||
bui.app.ui_v1.set_main_window(
|
||||
AccountSettingsWindow(
|
||||
close_once_signed_in=True,
|
||||
origin_widget=bui.get_special_widget('account_button'),
|
||||
),
|
||||
from_window=False,
|
||||
is_auxiliary=True,
|
||||
suppress_warning=True,
|
||||
)
|
||||
|
||||
# Transition out any previous main window.
|
||||
if prev_main_window is not None:
|
||||
prev_main_window.main_window_close()
|
||||
@ -34,9 +34,9 @@ class V2ProxySignInWindow(bui.Window):
|
||||
origin_widget.get_screen_space_center()
|
||||
),
|
||||
scale=(
|
||||
1.25
|
||||
1.16
|
||||
if uiscale is bui.UIScale.SMALL
|
||||
else 1.05 if uiscale is bui.UIScale.MEDIUM else 0.9
|
||||
else 1.0 if uiscale is bui.UIScale.MEDIUM else 0.9
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
@ -193,7 +193,7 @@ class CharacterPicker(PopupWindow):
|
||||
bui.widget(edit=btn, show_buffer_top=30, show_buffer_bottom=30)
|
||||
|
||||
def _on_store_press(self) -> None:
|
||||
from bauiv1lib.account import show_sign_in_prompt
|
||||
from bauiv1lib.account.signin import show_sign_in_prompt
|
||||
|
||||
plus = bui.app.plus
|
||||
assert plus is not None
|
||||
|
||||
@ -1032,7 +1032,7 @@ class CoopBrowserWindow(bui.MainWindow):
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.confirm import ConfirmWindow
|
||||
from bauiv1lib.purchase import PurchaseWindow
|
||||
from bauiv1lib.account import show_sign_in_prompt
|
||||
from bauiv1lib.account.signin import show_sign_in_prompt
|
||||
|
||||
plus = bui.app.plus
|
||||
assert plus is not None
|
||||
@ -1106,7 +1106,7 @@ class CoopBrowserWindow(bui.MainWindow):
|
||||
|
||||
def run_tournament(self, tournament_button: TournamentButton) -> None:
|
||||
"""Run the provided tournament game."""
|
||||
from bauiv1lib.account import show_sign_in_prompt
|
||||
from bauiv1lib.account.signin import show_sign_in_prompt
|
||||
from bauiv1lib.tournamententry import TournamentEntryWindow
|
||||
|
||||
plus = bui.app.plus
|
||||
|
||||
@ -200,7 +200,7 @@ class AboutGatherTab(GatherTab):
|
||||
return scroll_widget
|
||||
|
||||
def _invite_to_try_press(self) -> None:
|
||||
from bauiv1lib.account import show_sign_in_prompt
|
||||
from bauiv1lib.account.signin import show_sign_in_prompt
|
||||
from bauiv1lib.appinvite import handle_app_invites_press
|
||||
|
||||
plus = bui.app.plus
|
||||
|
||||
@ -1375,7 +1375,7 @@ class PublicGatherTab(GatherTab):
|
||||
)
|
||||
|
||||
def _on_start_advertizing_press(self) -> None:
|
||||
from bauiv1lib.account import show_sign_in_prompt
|
||||
from bauiv1lib.account.signin import show_sign_in_prompt
|
||||
|
||||
plus = bui.app.plus
|
||||
assert plus is not None
|
||||
|
||||
@ -171,7 +171,7 @@ class IconPicker(PopupWindow):
|
||||
bui.widget(edit=btn, show_buffer_top=30, show_buffer_bottom=30)
|
||||
|
||||
def _on_store_press(self) -> None:
|
||||
from bauiv1lib.account import show_sign_in_prompt
|
||||
from bauiv1lib.account.signin import show_sign_in_prompt
|
||||
|
||||
plus = bui.app.plus
|
||||
assert plus is not None
|
||||
|
||||
@ -1,424 +0,0 @@
|
||||
# Released under the MIT License. See LICENSE for details.
|
||||
#
|
||||
"""Provides a button showing league rank."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import bauiv1 as bui
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Any, Callable
|
||||
|
||||
|
||||
class LeagueRankButton:
|
||||
"""Button showing league rank."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
parent: bui.Widget,
|
||||
position: tuple[float, float],
|
||||
size: tuple[float, float],
|
||||
scale: float,
|
||||
*,
|
||||
on_activate_call: Callable[[], Any] | None = None,
|
||||
transition_delay: float | None = None,
|
||||
color: tuple[float, float, float] | None = None,
|
||||
textcolor: tuple[float, float, float] | None = None,
|
||||
smooth_update_delay: float | None = None,
|
||||
):
|
||||
if on_activate_call is None:
|
||||
on_activate_call = bui.WeakCall(self._default_on_activate_call)
|
||||
self._on_activate_call = on_activate_call
|
||||
if smooth_update_delay is None:
|
||||
smooth_update_delay = 1.0
|
||||
self._smooth_update_delay = smooth_update_delay
|
||||
self._size = size
|
||||
self._scale = scale
|
||||
if color is None:
|
||||
color = (0.5, 0.6, 0.5)
|
||||
if textcolor is None:
|
||||
textcolor = (1, 1, 1)
|
||||
self._color = color
|
||||
self._textcolor = textcolor
|
||||
self._header_color = (0.8, 0.8, 2.0)
|
||||
self._parent = parent
|
||||
self._position: tuple[float, float] = (0.0, 0.0)
|
||||
|
||||
self._button = bui.buttonwidget(
|
||||
parent=parent,
|
||||
size=size,
|
||||
label='',
|
||||
button_type='square',
|
||||
scale=scale,
|
||||
autoselect=True,
|
||||
on_activate_call=self._on_activate,
|
||||
transition_delay=transition_delay,
|
||||
color=color,
|
||||
)
|
||||
|
||||
self._title_text = bui.textwidget(
|
||||
parent=parent,
|
||||
size=(0, 0),
|
||||
draw_controller=self._button,
|
||||
h_align='center',
|
||||
v_align='center',
|
||||
maxwidth=size[0] * scale * 0.85,
|
||||
text=bui.Lstr(
|
||||
resource='league.leagueRankText',
|
||||
fallback_resource='coopSelectWindow.powerRankingText',
|
||||
),
|
||||
color=self._header_color,
|
||||
flatness=1.0,
|
||||
shadow=1.0,
|
||||
scale=scale * 0.5,
|
||||
transition_delay=transition_delay,
|
||||
)
|
||||
|
||||
self._value_text = bui.textwidget(
|
||||
parent=parent,
|
||||
size=(0, 0),
|
||||
h_align='center',
|
||||
v_align='center',
|
||||
maxwidth=size[0] * scale * 0.85,
|
||||
text='-',
|
||||
draw_controller=self._button,
|
||||
big=True,
|
||||
scale=scale,
|
||||
transition_delay=transition_delay,
|
||||
color=textcolor,
|
||||
)
|
||||
|
||||
plus = bui.app.plus
|
||||
assert plus is not None
|
||||
|
||||
self._smooth_percent: float | None = None
|
||||
self._percent: int | None = None
|
||||
self._smooth_rank: float | None = None
|
||||
self._rank: int | None = None
|
||||
self._ticking_sound: bui.Sound | None = None
|
||||
self._smooth_increase_speed = 1.0
|
||||
self._league: str | None = None
|
||||
self._improvement_text: str | None = None
|
||||
|
||||
self._smooth_update_timer: bui.AppTimer | None = None
|
||||
|
||||
# Take note of our account state; we'll refresh later if this changes.
|
||||
self._account_state_num = plus.get_v1_account_state_num()
|
||||
self._last_power_ranking_query_time: float | None = None
|
||||
self._doing_power_ranking_query = False
|
||||
self.set_position(position)
|
||||
self._bg_flash = False
|
||||
self._update_timer = bui.AppTimer(
|
||||
1.0, bui.WeakCall(self._update), repeat=True
|
||||
)
|
||||
self._update()
|
||||
|
||||
# If we've got cached power-ranking data already, apply it.
|
||||
assert bui.app.classic is not None
|
||||
data = bui.app.classic.accounts.get_cached_league_rank_data()
|
||||
if data is not None:
|
||||
self._update_for_league_rank_data(data)
|
||||
|
||||
def _on_activate(self) -> None:
|
||||
bui.increment_analytics_count('League rank button press')
|
||||
self._on_activate_call()
|
||||
|
||||
def __del__(self) -> None:
|
||||
if self._ticking_sound is not None:
|
||||
self._ticking_sound.stop()
|
||||
self._ticking_sound = None
|
||||
|
||||
def _start_smooth_update(self) -> None:
|
||||
self._smooth_update_timer = bui.AppTimer(
|
||||
0.05, bui.WeakCall(self._smooth_update), repeat=True
|
||||
)
|
||||
|
||||
def _smooth_update(self) -> None:
|
||||
# pylint: disable=too-many-branches
|
||||
# pylint: disable=too-many-statements
|
||||
try:
|
||||
if not self._button:
|
||||
return
|
||||
if self._ticking_sound is None:
|
||||
self._ticking_sound = bui.getsound('scoreIncrease')
|
||||
self._ticking_sound.play()
|
||||
self._bg_flash = not self._bg_flash
|
||||
color_used = (
|
||||
(self._color[0] * 2, self._color[1] * 2, self._color[2] * 2)
|
||||
if self._bg_flash
|
||||
else self._color
|
||||
)
|
||||
textcolor_used = (1, 1, 1) if self._bg_flash else self._textcolor
|
||||
header_color_used = (
|
||||
(1, 1, 1) if self._bg_flash else self._header_color
|
||||
)
|
||||
|
||||
if self._rank is not None:
|
||||
assert self._smooth_rank is not None
|
||||
self._smooth_rank -= 1.0 * self._smooth_increase_speed
|
||||
finished = int(self._smooth_rank) <= self._rank
|
||||
elif self._smooth_percent is not None:
|
||||
self._smooth_percent += 1.0 * self._smooth_increase_speed
|
||||
assert self._percent is not None
|
||||
finished = int(self._smooth_percent) >= self._percent
|
||||
else:
|
||||
finished = True
|
||||
if finished:
|
||||
if self._rank is not None:
|
||||
self._smooth_rank = float(self._rank)
|
||||
elif self._percent is not None:
|
||||
self._smooth_percent = float(self._percent)
|
||||
color_used = self._color
|
||||
textcolor_used = self._textcolor
|
||||
self._smooth_update_timer = None
|
||||
if self._ticking_sound is not None:
|
||||
self._ticking_sound.stop()
|
||||
self._ticking_sound = None
|
||||
bui.getsound('cashRegister2').play()
|
||||
assert self._improvement_text is not None
|
||||
diff_text = bui.textwidget(
|
||||
parent=self._parent,
|
||||
size=(0, 0),
|
||||
h_align='center',
|
||||
v_align='center',
|
||||
text='+' + self._improvement_text + '!',
|
||||
position=(
|
||||
self._position[0] + self._size[0] * 0.5 * self._scale,
|
||||
self._position[1] + self._size[1] * -0.2 * self._scale,
|
||||
),
|
||||
color=(0, 1, 0),
|
||||
flatness=1.0,
|
||||
shadow=0.0,
|
||||
scale=self._scale * 0.7,
|
||||
)
|
||||
|
||||
def safe_delete(widget: bui.Widget) -> None:
|
||||
if widget:
|
||||
widget.delete()
|
||||
|
||||
bui.apptimer(2.0, bui.Call(safe_delete, diff_text))
|
||||
status_text: str | bui.Lstr
|
||||
if self._rank is not None:
|
||||
assert self._smooth_rank is not None
|
||||
status_text = bui.Lstr(
|
||||
resource='numberText',
|
||||
subs=[('${NUMBER}', str(int(self._smooth_rank)))],
|
||||
)
|
||||
elif self._smooth_percent is not None:
|
||||
status_text = str(int(self._smooth_percent)) + '%'
|
||||
else:
|
||||
status_text = '-'
|
||||
bui.textwidget(
|
||||
edit=self._value_text, text=status_text, color=textcolor_used
|
||||
)
|
||||
bui.textwidget(edit=self._title_text, color=header_color_used)
|
||||
bui.buttonwidget(edit=self._button, color=color_used)
|
||||
|
||||
except Exception:
|
||||
logging.exception('Error doing smooth update.')
|
||||
self._smooth_update_timer = None
|
||||
|
||||
def _update_for_league_rank_data(self, data: dict[str, Any] | None) -> None:
|
||||
# pylint: disable=too-many-branches
|
||||
# pylint: disable=too-many-statements
|
||||
|
||||
plus = bui.app.plus
|
||||
assert plus is not None
|
||||
|
||||
# If our button has died, ignore.
|
||||
if not self._button:
|
||||
return
|
||||
|
||||
status_text: str | bui.Lstr
|
||||
|
||||
in_top = data is not None and data['rank'] is not None
|
||||
do_percent = False
|
||||
if data is None or plus.get_v1_account_state() != 'signed_in':
|
||||
self._percent = self._rank = None
|
||||
status_text = '-'
|
||||
elif in_top:
|
||||
self._percent = None
|
||||
self._rank = data['rank']
|
||||
prev_league = self._league
|
||||
self._league = data['l']
|
||||
|
||||
# If this is the first set, league has changed, or rank has gotten
|
||||
# worse, snap the smooth value immediately.
|
||||
assert self._rank is not None
|
||||
if (
|
||||
self._smooth_rank is None
|
||||
or prev_league != self._league
|
||||
or self._rank > int(self._smooth_rank)
|
||||
):
|
||||
self._smooth_rank = float(self._rank)
|
||||
status_text = bui.Lstr(
|
||||
resource='numberText',
|
||||
subs=[('${NUMBER}', str(int(self._smooth_rank)))],
|
||||
)
|
||||
else:
|
||||
try:
|
||||
if not data['scores'] or data['scores'][-1][1] <= 0:
|
||||
self._percent = self._rank = None
|
||||
status_text = '-'
|
||||
else:
|
||||
assert bui.app.classic is not None
|
||||
our_points = (
|
||||
bui.app.classic.accounts.get_league_rank_points(data)
|
||||
)
|
||||
progress = float(our_points) / data['scores'][-1][1]
|
||||
self._percent = int(progress * 100.0)
|
||||
self._rank = None
|
||||
do_percent = True
|
||||
prev_league = self._league
|
||||
self._league = data['l']
|
||||
|
||||
# If this is the first set, league has changed, or percent
|
||||
# has decreased, snap the smooth value immediately.
|
||||
if (
|
||||
self._smooth_percent is None
|
||||
or prev_league != self._league
|
||||
or self._percent < int(self._smooth_percent)
|
||||
):
|
||||
self._smooth_percent = float(self._percent)
|
||||
status_text = str(int(self._smooth_percent)) + '%'
|
||||
|
||||
except Exception:
|
||||
logging.exception('Error updating power ranking.')
|
||||
self._percent = self._rank = None
|
||||
status_text = '-'
|
||||
|
||||
# If we're doing a smooth update, set a timer.
|
||||
if (
|
||||
self._rank is not None
|
||||
and self._smooth_rank is not None
|
||||
and int(self._smooth_rank) != self._rank
|
||||
):
|
||||
self._improvement_text = str(
|
||||
-(int(self._rank) - int(self._smooth_rank))
|
||||
)
|
||||
diff = abs(self._rank - self._smooth_rank)
|
||||
if diff > 100:
|
||||
self._smooth_increase_speed = diff / 80.0
|
||||
elif diff > 50:
|
||||
self._smooth_increase_speed = diff / 70.0
|
||||
elif diff > 25:
|
||||
self._smooth_increase_speed = diff / 55.0
|
||||
else:
|
||||
self._smooth_increase_speed = diff / 40.0
|
||||
self._smooth_increase_speed = max(0.4, self._smooth_increase_speed)
|
||||
bui.apptimer(
|
||||
self._smooth_update_delay,
|
||||
bui.WeakCall(self._start_smooth_update),
|
||||
)
|
||||
|
||||
if (
|
||||
self._percent is not None
|
||||
and self._smooth_percent is not None
|
||||
and int(self._smooth_percent) != self._percent
|
||||
):
|
||||
self._improvement_text = str(
|
||||
(int(self._percent) - int(self._smooth_percent))
|
||||
)
|
||||
self._smooth_increase_speed = 0.3
|
||||
bui.apptimer(
|
||||
self._smooth_update_delay,
|
||||
bui.WeakCall(self._start_smooth_update),
|
||||
)
|
||||
|
||||
if do_percent:
|
||||
bui.textwidget(
|
||||
edit=self._title_text,
|
||||
text=bui.Lstr(resource='coopSelectWindow.toRankedText'),
|
||||
)
|
||||
else:
|
||||
try:
|
||||
assert data is not None
|
||||
txt = bui.Lstr(
|
||||
resource='league.leagueFullText',
|
||||
subs=[
|
||||
(
|
||||
'${NAME}',
|
||||
bui.Lstr(translate=('leagueNames', data['l']['n'])),
|
||||
),
|
||||
],
|
||||
)
|
||||
t_color = data['l']['c']
|
||||
except Exception:
|
||||
txt = bui.Lstr(
|
||||
resource='league.leagueRankText',
|
||||
fallback_resource='coopSelectWindow.powerRankingText',
|
||||
)
|
||||
assert bui.app.classic is not None
|
||||
t_color = bui.app.ui_v1.title_color
|
||||
bui.textwidget(edit=self._title_text, text=txt, color=t_color)
|
||||
bui.textwidget(edit=self._value_text, text=status_text)
|
||||
|
||||
def _on_power_ranking_query_response(
|
||||
self, data: dict[str, Any] | None
|
||||
) -> None:
|
||||
self._doing_power_ranking_query = False
|
||||
assert bui.app.classic is not None
|
||||
bui.app.classic.accounts.cache_league_rank_data(data)
|
||||
self._update_for_league_rank_data(data)
|
||||
|
||||
def _update(self) -> None:
|
||||
cur_time = bui.apptime()
|
||||
|
||||
plus = bui.app.plus
|
||||
assert plus is not None
|
||||
|
||||
# If our account state has changed, refresh our UI.
|
||||
account_state_num = plus.get_v1_account_state_num()
|
||||
if account_state_num != self._account_state_num:
|
||||
self._account_state_num = account_state_num
|
||||
|
||||
# And power ranking too...
|
||||
if not self._doing_power_ranking_query:
|
||||
self._last_power_ranking_query_time = None
|
||||
|
||||
# Send off a new power-ranking query if its been
|
||||
# long enough or whatnot.
|
||||
if not self._doing_power_ranking_query and (
|
||||
self._last_power_ranking_query_time is None
|
||||
or cur_time - self._last_power_ranking_query_time > 30.0
|
||||
):
|
||||
self._last_power_ranking_query_time = cur_time
|
||||
self._doing_power_ranking_query = True
|
||||
plus.power_ranking_query(
|
||||
callback=bui.WeakCall(self._on_power_ranking_query_response)
|
||||
)
|
||||
|
||||
def _default_on_activate_call(self) -> None:
|
||||
# pylint: disable=cyclic-import
|
||||
# from bauiv1lib.league.rankwindow import LeagueRankWindow
|
||||
|
||||
raise RuntimeError()
|
||||
# LeagueRankWindow(modal=True, origin_widget=self._button)
|
||||
|
||||
def set_position(self, position: tuple[float, float]) -> None:
|
||||
"""Set the button's position."""
|
||||
self._position = position
|
||||
if not self._button:
|
||||
return
|
||||
bui.buttonwidget(edit=self._button, position=self._position)
|
||||
bui.textwidget(
|
||||
edit=self._title_text,
|
||||
position=(
|
||||
self._position[0] + self._size[0] * 0.5 * self._scale,
|
||||
self._position[1] + self._size[1] * 0.82 * self._scale,
|
||||
),
|
||||
)
|
||||
bui.textwidget(
|
||||
edit=self._value_text,
|
||||
position=(
|
||||
self._position[0] + self._size[0] * 0.5 * self._scale,
|
||||
self._position[1] + self._size[1] * 0.36 * self._scale,
|
||||
),
|
||||
)
|
||||
|
||||
def get_button(self) -> bui.Widget:
|
||||
"""Return the underlying button bui.Widget>"""
|
||||
return self._button
|
||||
@ -83,7 +83,6 @@ class MainMenuWindow(bui.MainWindow):
|
||||
# pylint: disable=cyclic-import
|
||||
import bauiv1lib.getremote as _unused
|
||||
import bauiv1lib.confirm as _unused2
|
||||
import bauiv1lib.store.button as _unused3
|
||||
import bauiv1lib.account.settings as _unused5
|
||||
import bauiv1lib.store.browser as _unused6
|
||||
import bauiv1lib.credits as _unused7
|
||||
@ -153,30 +152,31 @@ class MainMenuWindow(bui.MainWindow):
|
||||
uiscale = app.ui_v1.uiscale
|
||||
|
||||
# Temp note about UI changes.
|
||||
bui.textwidget(
|
||||
parent=self._root_widget,
|
||||
position=(
|
||||
(-400, 400)
|
||||
if uiscale is bui.UIScale.LARGE
|
||||
else (
|
||||
(-270, 320)
|
||||
if uiscale is bui.UIScale.MEDIUM
|
||||
else (-280, 280)
|
||||
)
|
||||
),
|
||||
size=(0, 0),
|
||||
scale=0.4,
|
||||
flatness=1.0,
|
||||
text=(
|
||||
'WARNING: This build contains a revamped UI\n'
|
||||
'which is still a work-in-progress. A number\n'
|
||||
'of features are not currently functional or\n'
|
||||
'contain bugs. To go back to the stable legacy UI,\n'
|
||||
'grab version 1.7.36 from ballistica.net'
|
||||
),
|
||||
h_align='left',
|
||||
v_align='top',
|
||||
)
|
||||
if bool(False):
|
||||
bui.textwidget(
|
||||
parent=self._root_widget,
|
||||
position=(
|
||||
(-400, 400)
|
||||
if uiscale is bui.UIScale.LARGE
|
||||
else (
|
||||
(-270, 320)
|
||||
if uiscale is bui.UIScale.MEDIUM
|
||||
else (-280, 280)
|
||||
)
|
||||
),
|
||||
size=(0, 0),
|
||||
scale=0.4,
|
||||
flatness=1.0,
|
||||
text=(
|
||||
'WARNING: This build contains a revamped UI\n'
|
||||
'which is still a work-in-progress. A number\n'
|
||||
'of features are not currently functional or\n'
|
||||
'contain bugs. To go back to the stable legacy UI,\n'
|
||||
'grab version 1.7.36 from ballistica.net'
|
||||
),
|
||||
h_align='left',
|
||||
v_align='top',
|
||||
)
|
||||
|
||||
self._have_quit_button = app.classic.platform in (
|
||||
'windows',
|
||||
|
||||
@ -566,7 +566,7 @@ class PartyQueueWindow(bui.Window):
|
||||
|
||||
def on_boost_press(self) -> None:
|
||||
"""Boost was pressed."""
|
||||
from bauiv1lib import account
|
||||
from bauiv1lib.account.signin import show_sign_in_prompt
|
||||
|
||||
# from bauiv1lib import gettickets
|
||||
|
||||
@ -574,7 +574,7 @@ class PartyQueueWindow(bui.Window):
|
||||
assert plus is not None
|
||||
|
||||
if plus.get_v1_account_state() != 'signed_in':
|
||||
account.show_sign_in_prompt()
|
||||
show_sign_in_prompt()
|
||||
return
|
||||
|
||||
if plus.get_v1_account_ticket_count() < self._boost_tickets:
|
||||
|
||||
@ -572,7 +572,7 @@ class PlayWindow(bui.MainWindow):
|
||||
|
||||
def _coop(self) -> None:
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.account import show_sign_in_prompt
|
||||
from bauiv1lib.account.signin import show_sign_in_prompt
|
||||
from bauiv1lib.coop.browser import CoopBrowserWindow
|
||||
|
||||
# no-op if we're not currently in control.
|
||||
|
||||
@ -255,7 +255,7 @@ class PlaylistAddGameWindow(bui.MainWindow):
|
||||
)
|
||||
|
||||
def _on_get_more_games_press(self) -> None:
|
||||
from bauiv1lib.account import show_sign_in_prompt
|
||||
from bauiv1lib.account.signin import show_sign_in_prompt
|
||||
from bauiv1lib.store.browser import StoreBrowserWindow
|
||||
|
||||
# No-op if we're not in control.
|
||||
@ -271,17 +271,12 @@ class PlaylistAddGameWindow(bui.MainWindow):
|
||||
|
||||
self.main_window_replace(
|
||||
StoreBrowserWindow(
|
||||
# modal=True,
|
||||
show_tab=StoreBrowserWindow.TabID.MINIGAMES,
|
||||
# on_close_call=self._on_store_close,
|
||||
origin_widget=self._get_more_games_button,
|
||||
minimal_toolbars=True,
|
||||
)
|
||||
)
|
||||
|
||||
# def _on_store_close(self) -> None:
|
||||
# self._refresh(select_get_more_games_button=True)
|
||||
|
||||
def _add(self) -> None:
|
||||
bui.lock_all_input() # Make sure no more commands happen.
|
||||
bui.apptimer(0.1, bui.unlock_all_input)
|
||||
|
||||
@ -283,7 +283,7 @@ class PlaylistMapSelectWindow(bui.MainWindow):
|
||||
)
|
||||
|
||||
def _on_store_press(self) -> None:
|
||||
from bauiv1lib import account
|
||||
from bauiv1lib.account.signin import show_sign_in_prompt
|
||||
from bauiv1lib.store.browser import StoreBrowserWindow
|
||||
|
||||
# No-op if we're not in control.
|
||||
@ -294,7 +294,7 @@ class PlaylistMapSelectWindow(bui.MainWindow):
|
||||
assert plus is not None
|
||||
|
||||
if plus.get_v1_account_state() != 'signed_in':
|
||||
account.show_sign_in_prompt()
|
||||
show_sign_in_prompt()
|
||||
return
|
||||
|
||||
self._selected_get_more_maps = True
|
||||
|
||||
@ -432,7 +432,7 @@ class PlayOptionsWindow(PopupWindow):
|
||||
self._update()
|
||||
|
||||
def _custom_colors_names_press(self) -> None:
|
||||
from bauiv1lib.account import show_sign_in_prompt
|
||||
from bauiv1lib.account.signin import show_sign_in_prompt
|
||||
from bauiv1lib.teamnamescolors import TeamNamesColorsWindow
|
||||
from bauiv1lib.purchase import PurchaseWindow
|
||||
|
||||
|
||||
@ -550,7 +550,7 @@ class EditProfileWindow(
|
||||
|
||||
def upgrade_profile(self) -> None:
|
||||
"""Attempt to upgrade the profile to global."""
|
||||
from bauiv1lib import account
|
||||
from bauiv1lib.account.signin import show_sign_in_prompt
|
||||
from bauiv1lib.profile import upgrade as pupgrade
|
||||
|
||||
new_name = self.getname().strip()
|
||||
@ -566,7 +566,7 @@ class EditProfileWindow(
|
||||
assert plus is not None
|
||||
|
||||
if plus.get_v1_account_state() != 'signed_in':
|
||||
account.show_sign_in_prompt()
|
||||
show_sign_in_prompt()
|
||||
return
|
||||
|
||||
pupgrade.ProfileUpgradeWindow(self)
|
||||
|
||||
@ -53,24 +53,52 @@ class ResourceTypeInfoWindow(PopupWindow):
|
||||
iconscale=1.2,
|
||||
)
|
||||
|
||||
yoffs = self._height - 150
|
||||
|
||||
if resource_type == 'tickets':
|
||||
rdesc = 'Will describe tickets.'
|
||||
rdesc = (
|
||||
'Use tickets to unlock characters, maps,\n'
|
||||
'minigames, and more in the store.\n'
|
||||
'\n'
|
||||
'Earn tickets by completing achievements or\n'
|
||||
'by opening chests won in the game.'
|
||||
)
|
||||
texname = 'tickets'
|
||||
elif resource_type == 'tokens':
|
||||
rdesc = 'Will describe tokens.'
|
||||
rdesc = (
|
||||
'Tokens can be used to speed up chest unlocks\n'
|
||||
'and skip other waits.\n'
|
||||
'\n'
|
||||
'You can buy packs of tokens or buy a Gold Pass\n'
|
||||
'to get infinite tokens forever.\n'
|
||||
)
|
||||
texname = 'coin'
|
||||
elif resource_type == 'trophies':
|
||||
rdesc = 'Will show trophies & league rankings.'
|
||||
rdesc = 'TODO: Will show trophies & league rankings.'
|
||||
texname = 'crossOut'
|
||||
elif resource_type == 'xp':
|
||||
rdesc = 'Will describe xp/levels.'
|
||||
rdesc = 'TODO: Will describe xp/levels.'
|
||||
texname = 'crossOut'
|
||||
else:
|
||||
assert_never(resource_type)
|
||||
|
||||
imgsize = 100.0
|
||||
bui.imagewidget(
|
||||
parent=self.root_widget,
|
||||
position=(self._width * 0.5 - imgsize * 0.5, yoffs + 5.0),
|
||||
size=(imgsize, imgsize),
|
||||
texture=bui.gettexture(texname),
|
||||
)
|
||||
|
||||
bui.textwidget(
|
||||
parent=self.root_widget,
|
||||
h_align='center',
|
||||
v_align='center',
|
||||
v_align='top',
|
||||
size=(0, 0),
|
||||
position=(self._width * 0.5, self._height * 0.5),
|
||||
text=(f'UNDER CONSTRUCTION.\n({rdesc})'),
|
||||
maxwidth=self._width * 0.8,
|
||||
position=(self._width * 0.5, yoffs - 5.0),
|
||||
text=rdesc,
|
||||
scale=0.8,
|
||||
)
|
||||
|
||||
def _on_cancel_press(self) -> None:
|
||||
|
||||
@ -787,13 +787,13 @@ class AdvancedSettingsWindow(bui.MainWindow):
|
||||
|
||||
def _on_friend_promo_code_press(self) -> None:
|
||||
from bauiv1lib import appinvite
|
||||
from bauiv1lib import account
|
||||
from bauiv1lib.account.signin import show_sign_in_prompt
|
||||
|
||||
plus = bui.app.plus
|
||||
assert plus is not None
|
||||
|
||||
if plus.get_v1_account_state() != 'signed_in':
|
||||
account.show_sign_in_prompt()
|
||||
show_sign_in_prompt()
|
||||
return
|
||||
appinvite.handle_app_invites_press()
|
||||
|
||||
|
||||
@ -281,12 +281,12 @@ class StoreBrowserWindow(bui.MainWindow):
|
||||
self._restore_state()
|
||||
|
||||
def _restore_purchases(self) -> None:
|
||||
from bauiv1lib import account
|
||||
from bauiv1lib.account.signin import show_sign_in_prompt
|
||||
|
||||
plus = bui.app.plus
|
||||
assert plus is not None
|
||||
if plus.accounts.primary is None:
|
||||
account.show_sign_in_prompt()
|
||||
show_sign_in_prompt()
|
||||
else:
|
||||
plus.restore_purchases()
|
||||
|
||||
@ -490,7 +490,7 @@ class StoreBrowserWindow(bui.MainWindow):
|
||||
|
||||
def buy(self, item: str) -> None:
|
||||
"""Attempt to purchase the provided item."""
|
||||
from bauiv1lib import account
|
||||
from bauiv1lib.account.signin import show_sign_in_prompt
|
||||
from bauiv1lib.confirm import ConfirmWindow
|
||||
|
||||
assert bui.app.classic is not None
|
||||
@ -509,7 +509,7 @@ class StoreBrowserWindow(bui.MainWindow):
|
||||
bui.getsound('error').play()
|
||||
else:
|
||||
if plus.get_v1_account_state() != 'signed_in':
|
||||
account.show_sign_in_prompt()
|
||||
show_sign_in_prompt()
|
||||
else:
|
||||
self._last_buy_time = curtime
|
||||
|
||||
|
||||
@ -1,329 +0,0 @@
|
||||
# Released under the MIT License. See LICENSE for details.
|
||||
#
|
||||
"""UI functionality for a button leading to the store."""
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from efro.util import utc_now
|
||||
|
||||
import bauiv1 as bui
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Any, Sequence, Callable
|
||||
|
||||
|
||||
class StoreButton:
|
||||
"""A button leading to the store."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
parent: bui.Widget,
|
||||
position: Sequence[float],
|
||||
size: Sequence[float],
|
||||
scale: float,
|
||||
*,
|
||||
on_activate_call: Callable[[], Any] | None = None,
|
||||
transition_delay: float | None = None,
|
||||
color: Sequence[float] | None = None,
|
||||
textcolor: Sequence[float] | None = None,
|
||||
show_tickets: bool = False,
|
||||
button_type: str | None = None,
|
||||
sale_scale: float = 1.0,
|
||||
):
|
||||
self._position = position
|
||||
self._size = size
|
||||
self._scale = scale
|
||||
|
||||
if on_activate_call is None:
|
||||
on_activate_call = bui.WeakCall(self._default_on_activate_call)
|
||||
self._on_activate_call = on_activate_call
|
||||
|
||||
self._button = bui.buttonwidget(
|
||||
parent=parent,
|
||||
size=size,
|
||||
label='' if show_tickets else bui.Lstr(resource='storeText'),
|
||||
scale=scale,
|
||||
autoselect=True,
|
||||
on_activate_call=self._on_activate,
|
||||
transition_delay=transition_delay,
|
||||
color=color,
|
||||
button_type=button_type,
|
||||
)
|
||||
|
||||
self._title_text: bui.Widget | None
|
||||
self._ticket_text: bui.Widget | None
|
||||
|
||||
if show_tickets:
|
||||
self._title_text = bui.textwidget(
|
||||
parent=parent,
|
||||
position=(
|
||||
position[0] + size[0] * 0.5 * scale,
|
||||
position[1] + size[1] * 0.65 * scale,
|
||||
),
|
||||
size=(0, 0),
|
||||
h_align='center',
|
||||
v_align='center',
|
||||
maxwidth=size[0] * scale * 0.65,
|
||||
text=bui.Lstr(resource='storeText'),
|
||||
draw_controller=self._button,
|
||||
scale=scale,
|
||||
transition_delay=transition_delay,
|
||||
color=textcolor,
|
||||
)
|
||||
self._ticket_text = bui.textwidget(
|
||||
parent=parent,
|
||||
size=(0, 0),
|
||||
h_align='center',
|
||||
v_align='center',
|
||||
maxwidth=size[0] * scale * 0.85,
|
||||
text='',
|
||||
color=(0.2, 1.0, 0.2),
|
||||
flatness=1.0,
|
||||
shadow=0.0,
|
||||
scale=scale * 0.6,
|
||||
transition_delay=transition_delay,
|
||||
)
|
||||
else:
|
||||
self._title_text = None
|
||||
self._ticket_text = None
|
||||
|
||||
self._circle_rad = 12 * scale
|
||||
self._circle_center = (0.0, 0.0)
|
||||
self._sale_circle_center = (0.0, 0.0)
|
||||
|
||||
self._available_purchase_backing = bui.imagewidget(
|
||||
parent=parent,
|
||||
color=(1, 0, 0),
|
||||
draw_controller=self._button,
|
||||
size=(2.2 * self._circle_rad, 2.2 * self._circle_rad),
|
||||
texture=bui.gettexture('circleShadow'),
|
||||
transition_delay=transition_delay,
|
||||
)
|
||||
self._available_purchase_text = bui.textwidget(
|
||||
parent=parent,
|
||||
size=(0, 0),
|
||||
h_align='center',
|
||||
v_align='center',
|
||||
text='',
|
||||
draw_controller=self._button,
|
||||
color=(1, 1, 1),
|
||||
flatness=1.0,
|
||||
shadow=1.0,
|
||||
scale=0.6 * scale,
|
||||
maxwidth=self._circle_rad * 1.4,
|
||||
transition_delay=transition_delay,
|
||||
)
|
||||
|
||||
self._sale_circle_rad = 18 * scale * sale_scale
|
||||
self._sale_backing = bui.imagewidget(
|
||||
parent=parent,
|
||||
color=(0.5, 0, 1.0),
|
||||
draw_controller=self._button,
|
||||
size=(2 * self._sale_circle_rad, 2 * self._sale_circle_rad),
|
||||
texture=bui.gettexture('circleZigZag'),
|
||||
transition_delay=transition_delay,
|
||||
)
|
||||
self._sale_title_text = bui.textwidget(
|
||||
parent=parent,
|
||||
size=(0, 0),
|
||||
h_align='center',
|
||||
v_align='center',
|
||||
draw_controller=self._button,
|
||||
color=(0, 1, 0),
|
||||
flatness=1.0,
|
||||
shadow=0.0,
|
||||
scale=0.5 * scale * sale_scale,
|
||||
maxwidth=self._sale_circle_rad * 1.5,
|
||||
transition_delay=transition_delay,
|
||||
)
|
||||
self._sale_time_text = bui.textwidget(
|
||||
parent=parent,
|
||||
size=(0, 0),
|
||||
h_align='center',
|
||||
v_align='center',
|
||||
draw_controller=self._button,
|
||||
color=(0, 1, 0),
|
||||
flatness=1.0,
|
||||
shadow=0.0,
|
||||
scale=0.4 * scale * sale_scale,
|
||||
maxwidth=self._sale_circle_rad * 1.5,
|
||||
transition_delay=transition_delay,
|
||||
)
|
||||
|
||||
self.set_position(position)
|
||||
self._update_timer = bui.AppTimer(
|
||||
1.0, bui.WeakCall(self._update), repeat=True
|
||||
)
|
||||
self._update()
|
||||
|
||||
def _on_activate(self) -> None:
|
||||
bui.increment_analytics_count('Store button press')
|
||||
self._on_activate_call()
|
||||
|
||||
def set_position(self, position: Sequence[float]) -> None:
|
||||
"""Set the button position."""
|
||||
self._position = position
|
||||
self._circle_center = (
|
||||
position[0] + 0.1 * self._size[0] * self._scale,
|
||||
position[1] + self._size[1] * self._scale * 0.8,
|
||||
)
|
||||
self._sale_circle_center = (
|
||||
position[0] + 0.07 * self._size[0] * self._scale,
|
||||
position[1] + self._size[1] * self._scale * 0.8,
|
||||
)
|
||||
|
||||
if not self._button:
|
||||
return
|
||||
bui.buttonwidget(edit=self._button, position=self._position)
|
||||
if self._title_text is not None:
|
||||
bui.textwidget(
|
||||
edit=self._title_text,
|
||||
position=(
|
||||
self._position[0] + self._size[0] * 0.5 * self._scale,
|
||||
self._position[1] + self._size[1] * 0.65 * self._scale,
|
||||
),
|
||||
)
|
||||
if self._ticket_text is not None:
|
||||
bui.textwidget(
|
||||
edit=self._ticket_text,
|
||||
position=(
|
||||
position[0] + self._size[0] * 0.5 * self._scale,
|
||||
position[1] + self._size[1] * 0.28 * self._scale,
|
||||
),
|
||||
size=(0, 0),
|
||||
)
|
||||
bui.imagewidget(
|
||||
edit=self._available_purchase_backing,
|
||||
position=(
|
||||
self._circle_center[0] - self._circle_rad * 1.02,
|
||||
self._circle_center[1] - self._circle_rad * 1.13,
|
||||
),
|
||||
)
|
||||
bui.textwidget(
|
||||
edit=self._available_purchase_text, position=self._circle_center
|
||||
)
|
||||
|
||||
bui.imagewidget(
|
||||
edit=self._sale_backing,
|
||||
position=(
|
||||
self._sale_circle_center[0] - self._sale_circle_rad,
|
||||
self._sale_circle_center[1] - self._sale_circle_rad,
|
||||
),
|
||||
)
|
||||
bui.textwidget(
|
||||
edit=self._sale_title_text,
|
||||
position=(
|
||||
self._sale_circle_center[0],
|
||||
self._sale_circle_center[1] + self._sale_circle_rad * 0.3,
|
||||
),
|
||||
)
|
||||
bui.textwidget(
|
||||
edit=self._sale_time_text,
|
||||
position=(
|
||||
self._sale_circle_center[0],
|
||||
self._sale_circle_center[1] - self._sale_circle_rad * 0.3,
|
||||
),
|
||||
)
|
||||
|
||||
def _default_on_activate_call(self) -> None:
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.account import show_sign_in_prompt
|
||||
|
||||
# from bauiv1lib.store.browser import StoreBrowserWindow
|
||||
|
||||
plus = bui.app.plus
|
||||
assert plus is not None
|
||||
if plus.get_v1_account_state() != 'signed_in':
|
||||
show_sign_in_prompt()
|
||||
return
|
||||
|
||||
raise RuntimeError('no longer wired up')
|
||||
|
||||
# StoreBrowserWindow(modal=True, origin_widget=self._button)
|
||||
|
||||
def get_button(self) -> bui.Widget:
|
||||
"""Return the underlying button widget."""
|
||||
return self._button
|
||||
|
||||
def _update(self) -> None:
|
||||
# pylint: disable=too-many-branches
|
||||
# pylint: disable=cyclic-import
|
||||
from babase import SpecialChar
|
||||
|
||||
plus = bui.app.plus
|
||||
assert plus is not None
|
||||
assert bui.app.classic is not None
|
||||
store = bui.app.classic.store
|
||||
|
||||
if not self._button:
|
||||
return # Our instance may outlive our UI objects.
|
||||
|
||||
if self._ticket_text is not None:
|
||||
if plus.get_v1_account_state() == 'signed_in':
|
||||
sval = bui.charstr(SpecialChar.TICKET) + str(
|
||||
plus.get_v1_account_ticket_count()
|
||||
)
|
||||
else:
|
||||
sval = '-'
|
||||
bui.textwidget(edit=self._ticket_text, text=sval)
|
||||
available_purchases = store.get_available_purchase_count()
|
||||
|
||||
# Old pro sale stuff..
|
||||
sale_time = store.get_available_sale_time('extras')
|
||||
|
||||
# ..also look for new style sales.
|
||||
if sale_time is None:
|
||||
import datetime
|
||||
|
||||
sales_raw = plus.get_v1_account_misc_read_val('sales', {})
|
||||
sale_times = []
|
||||
try:
|
||||
# Look at the current set of sales; filter any with time
|
||||
# remaining that we don't own.
|
||||
for sale_item, sale_info in list(sales_raw.items()):
|
||||
if not plus.get_v1_account_product_purchased(sale_item):
|
||||
to_end = (
|
||||
datetime.datetime.fromtimestamp(
|
||||
sale_info['e'], datetime.UTC
|
||||
)
|
||||
- utc_now()
|
||||
).total_seconds()
|
||||
if to_end > 0:
|
||||
sale_times.append(to_end)
|
||||
except Exception:
|
||||
logging.exception('Error parsing sales.')
|
||||
if sale_times:
|
||||
sale_time = int(min(sale_times) * 1000)
|
||||
|
||||
if sale_time is not None:
|
||||
bui.textwidget(
|
||||
edit=self._sale_title_text,
|
||||
text=bui.Lstr(resource='store.saleText'),
|
||||
)
|
||||
bui.textwidget(
|
||||
edit=self._sale_time_text,
|
||||
text=bui.timestring(sale_time / 1000.0, centi=False),
|
||||
)
|
||||
bui.imagewidget(edit=self._sale_backing, opacity=1.0)
|
||||
bui.imagewidget(edit=self._available_purchase_backing, opacity=1.0)
|
||||
bui.textwidget(edit=self._available_purchase_text, text='')
|
||||
bui.imagewidget(edit=self._available_purchase_backing, opacity=0.0)
|
||||
else:
|
||||
bui.imagewidget(edit=self._sale_backing, opacity=0.0)
|
||||
bui.textwidget(edit=self._sale_time_text, text='')
|
||||
bui.textwidget(edit=self._sale_title_text, text='')
|
||||
if available_purchases > 0:
|
||||
bui.textwidget(
|
||||
edit=self._available_purchase_text,
|
||||
text=str(available_purchases),
|
||||
)
|
||||
bui.imagewidget(
|
||||
edit=self._available_purchase_backing, opacity=1.0
|
||||
)
|
||||
else:
|
||||
bui.textwidget(edit=self._available_purchase_text, text='')
|
||||
bui.imagewidget(
|
||||
edit=self._available_purchase_backing, opacity=0.0
|
||||
)
|
||||
@ -206,7 +206,7 @@ void BaseFeatureSet::StartApp() {
|
||||
called_start_app_ = true;
|
||||
assert(!app_started_); // Shouldn't be possible.
|
||||
|
||||
LogVersionInfo_();
|
||||
LogStartupMessage_();
|
||||
|
||||
g_core->Log(LogName::kBaLifecycle, LogLevel::kInfo,
|
||||
"start-app begin (main thread)");
|
||||
@ -420,7 +420,7 @@ void BaseFeatureSet::OnAppShutdownComplete() {
|
||||
}
|
||||
}
|
||||
|
||||
void BaseFeatureSet::LogVersionInfo_() {
|
||||
void BaseFeatureSet::LogStartupMessage_() {
|
||||
char buffer[256];
|
||||
if (g_buildconfig.headless_build()) {
|
||||
snprintf(buffer, sizeof(buffer),
|
||||
|
||||
@ -828,7 +828,7 @@ class BaseFeatureSet : public FeatureSetNativeComponent,
|
||||
|
||||
private:
|
||||
BaseFeatureSet();
|
||||
void LogVersionInfo_();
|
||||
void LogStartupMessage_();
|
||||
void PrintContextNonLogicThread_();
|
||||
void PrintContextForCallableLabel_(const char* label);
|
||||
void PrintContextUnavailable_();
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
#include "ballistica/classic/python/methods/python_methods_classic.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@ -289,10 +290,10 @@ static PyMethodDef PyClassicAppModeDeactivateDef = {
|
||||
"(internal)\n",
|
||||
};
|
||||
|
||||
// -------------------------- set_root_ui_values -------------------------------
|
||||
// ---------------------- set_root_ui_account_values ---------------------------
|
||||
|
||||
static auto PySetRootUIValues(PyObject* self, PyObject* args, PyObject* keywds)
|
||||
-> PyObject* {
|
||||
static auto PySetRootUIAccountValues(PyObject* self, PyObject* args,
|
||||
PyObject* keywds) -> PyObject* {
|
||||
BA_PYTHON_TRY;
|
||||
|
||||
const char* tickets_text;
|
||||
@ -353,12 +354,12 @@ static auto PySetRootUIValues(PyObject* self, PyObject* args, PyObject* keywds)
|
||||
BA_PYTHON_CATCH;
|
||||
}
|
||||
|
||||
static PyMethodDef PySetRootUIValuesDef = {
|
||||
"set_root_ui_values", // name
|
||||
(PyCFunction)PySetRootUIValues, // method
|
||||
METH_VARARGS | METH_KEYWORDS, // flags
|
||||
static PyMethodDef PySetRootUIAccountValuesDef = {
|
||||
"set_root_ui_account_values", // name
|
||||
(PyCFunction)PySetRootUIAccountValues, // method
|
||||
METH_VARARGS | METH_KEYWORDS, // flags
|
||||
|
||||
"set_root_ui_values(*,\n"
|
||||
"set_root_ui_account_values(*,\n"
|
||||
" tickets_text: str,\n"
|
||||
" tokens_text: str,\n"
|
||||
" league_rank_text: str,\n"
|
||||
@ -377,6 +378,38 @@ static PyMethodDef PySetRootUIValuesDef = {
|
||||
"(internal)",
|
||||
};
|
||||
|
||||
// --------------------- set_root_ui_have_live_values --------------------------
|
||||
|
||||
static auto PySetRootUIHaveLiveValues(PyObject* self, PyObject* args,
|
||||
PyObject* keywds) -> PyObject* {
|
||||
BA_PYTHON_TRY;
|
||||
|
||||
int have_live_values{};
|
||||
|
||||
static const char* kwlist[] = {"have_live_values", nullptr};
|
||||
if (!PyArg_ParseTupleAndKeywords(
|
||||
args, keywds, "p", const_cast<char**>(kwlist), &have_live_values)) {
|
||||
return nullptr;
|
||||
}
|
||||
BA_PRECONDITION(g_base->InLogicThread());
|
||||
|
||||
auto* appmode = ClassicAppMode::GetActiveOrThrow();
|
||||
appmode->SetRootUIHaveLiveValues(have_live_values);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
BA_PYTHON_CATCH;
|
||||
}
|
||||
|
||||
static PyMethodDef PySetRootUIHaveLiveValuesDef = {
|
||||
"set_root_ui_have_live_values", // name
|
||||
(PyCFunction)PySetRootUIHaveLiveValues, // method
|
||||
METH_VARARGS | METH_KEYWORDS, // flags
|
||||
|
||||
"set_root_ui_have_live_values(have_live_values: bool) -> None\n"
|
||||
"\n"
|
||||
"(internal)",
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
auto PythonMethodsClassic::GetMethods() -> std::vector<PyMethodDef> {
|
||||
@ -387,7 +420,8 @@ auto PythonMethodsClassic::GetMethods() -> std::vector<PyMethodDef> {
|
||||
PyClassicAppModeHandleAppIntentDefaultDef,
|
||||
PyClassicAppModeActivateDef,
|
||||
PyClassicAppModeDeactivateDef,
|
||||
PySetRootUIValuesDef,
|
||||
PySetRootUIAccountValuesDef,
|
||||
PySetRootUIHaveLiveValuesDef,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -161,6 +161,7 @@ void ClassicAppMode::Reset_() {
|
||||
root_widget->SetChests(
|
||||
root_ui_chest_0_appearance_, root_ui_chest_1_appearance_,
|
||||
root_ui_chest_2_appearance_, root_ui_chest_3_appearance_);
|
||||
root_widget->SetHaveLiveValues(root_ui_have_live_values_);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1699,6 +1700,22 @@ void ClassicAppMode::SetRootUIGoldPass(bool enabled) {
|
||||
}
|
||||
}
|
||||
|
||||
void ClassicAppMode::SetRootUIHaveLiveValues(bool have_live_values) {
|
||||
if (have_live_values == root_ui_have_live_values_) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Store the value.
|
||||
root_ui_have_live_values_ = have_live_values;
|
||||
|
||||
// Apply it to any existing UI.
|
||||
if (uiv1_) {
|
||||
if (auto* root_widget = uiv1_->root_widget()) {
|
||||
root_widget->SetHaveLiveValues(root_ui_have_live_values_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ClassicAppMode::SetRootUIChests(const std::string& chest_0_appearance,
|
||||
const std::string& chest_1_appearance,
|
||||
const std::string& chest_2_appearance,
|
||||
|
||||
@ -228,6 +228,7 @@ class ClassicAppMode : public base::AppMode {
|
||||
const std::string& chest_1_appearance,
|
||||
const std::string& chest_2_appearance,
|
||||
const std::string& chest_3_appearance);
|
||||
void SetRootUIHaveLiveValues(bool val);
|
||||
|
||||
private:
|
||||
ClassicAppMode();
|
||||
@ -271,6 +272,7 @@ class ClassicAppMode : public base::AppMode {
|
||||
bool kick_voting_enabled_{true};
|
||||
bool replay_paused_{};
|
||||
bool root_ui_gold_pass_{};
|
||||
bool root_ui_have_live_values_{};
|
||||
|
||||
ui_v1::UIV1FeatureSet* uiv1_{};
|
||||
cJSON* game_roster_{};
|
||||
|
||||
@ -191,6 +191,17 @@ void ConnectionToClient::HandleGamePacket(const std::vector<uint8_t>& data) {
|
||||
string_buffer[string_buffer.size() - 1] = 0;
|
||||
set_peer_spec(PlayerSpec(&(string_buffer[0])));
|
||||
}
|
||||
|
||||
// If they sent us a garbage player-spec, kick them right out.
|
||||
if (!peer_spec().valid()) {
|
||||
g_core->Log(LogName::kBaNetworking, LogLevel::kDebug, [] {
|
||||
return std::string(
|
||||
"Rejecting client for submitting invalid player-spec.");
|
||||
});
|
||||
Error("");
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME: We should maybe set some sort of 'pending' peer-spec
|
||||
// and fetch their actual info from the master-server.
|
||||
// (or at least make that an option for internet servers)
|
||||
@ -198,6 +209,9 @@ void ConnectionToClient::HandleGamePacket(const std::vector<uint8_t>& data) {
|
||||
// Compare this against our blocked specs.. if there's a match, reject
|
||||
// them.
|
||||
if (appmode->IsPlayerBanned(peer_spec())) {
|
||||
g_core->Log(LogName::kBaNetworking, LogLevel::kDebug, [] {
|
||||
return std::string("Rejecting join attempt by banned player.");
|
||||
});
|
||||
Error("");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -18,32 +18,36 @@ PlayerSpec::PlayerSpec(const std::string& s) {
|
||||
cJSON* root_obj = cJSON_Parse(s.c_str());
|
||||
bool success = false;
|
||||
if (root_obj) {
|
||||
cJSON* name_obj = cJSON_GetObjectItem(root_obj, "n");
|
||||
cJSON* short_name_obj = cJSON_GetObjectItem(root_obj, "sn");
|
||||
cJSON* account_obj = cJSON_GetObjectItem(root_obj, "a");
|
||||
if (name_obj && short_name_obj && account_obj) {
|
||||
name_ = Utils::GetValidUTF8(name_obj->valuestring, "psps");
|
||||
short_name_ = Utils::GetValidUTF8(short_name_obj->valuestring, "psps2");
|
||||
if (cJSON_IsObject(root_obj)) {
|
||||
cJSON* name_obj = cJSON_GetObjectItem(root_obj, "n");
|
||||
cJSON* short_name_obj = cJSON_GetObjectItem(root_obj, "sn");
|
||||
cJSON* account_obj = cJSON_GetObjectItem(root_obj, "a");
|
||||
if (name_obj && short_name_obj && account_obj && cJSON_IsString(name_obj)
|
||||
&& cJSON_IsString(short_name_obj) && cJSON_IsString(account_obj)) {
|
||||
name_ = Utils::GetValidUTF8(name_obj->valuestring, "psps");
|
||||
short_name_ = Utils::GetValidUTF8(short_name_obj->valuestring, "psps2");
|
||||
|
||||
// Account type may technically be something we don't recognize,
|
||||
// but that's ok.. it'll just be 'invalid' to us in that case
|
||||
if (g_base->HaveClassic()) {
|
||||
v1_account_type_ = g_base->classic()->GetV1AccountTypeFromString(
|
||||
account_obj->valuestring);
|
||||
// classic::V1Account::AccountTypeFromString(account_obj->valuestring);
|
||||
} else {
|
||||
v1_account_type_ = 0; // kInvalid.
|
||||
// Account type may technically be something we don't recognize,
|
||||
// but that's ok.. it'll just be 'invalid' to us in that case
|
||||
if (g_base->HaveClassic()) {
|
||||
v1_account_type_ = g_base->classic()->GetV1AccountTypeFromString(
|
||||
account_obj->valuestring);
|
||||
} else {
|
||||
v1_account_type_ = 0; // kInvalid.
|
||||
}
|
||||
success = true;
|
||||
}
|
||||
success = true;
|
||||
}
|
||||
cJSON_Delete(root_obj);
|
||||
}
|
||||
if (!success) {
|
||||
g_core->Log(LogName::kBa, LogLevel::kError,
|
||||
valid_ = false;
|
||||
|
||||
// Only log this once in case it is used as an attack.
|
||||
BA_LOG_ONCE(LogName::kBa, LogLevel::kError,
|
||||
"Error creating PlayerSpec from string: '" + s + "'");
|
||||
name_ = "<error>";
|
||||
short_name_ = "";
|
||||
// account_type_ = classic::V1AccountType::kInvalid;
|
||||
short_name_ = "<error>";
|
||||
v1_account_type_ = 0; // kInvalid.
|
||||
}
|
||||
}
|
||||
@ -54,7 +58,6 @@ auto PlayerSpec::GetDisplayString() const -> std::string {
|
||||
+ name_;
|
||||
}
|
||||
return name_;
|
||||
// return classic::V1Account::AccountTypeToIconString(account_type_) + name_;
|
||||
}
|
||||
|
||||
auto PlayerSpec::GetShortName() const -> std::string {
|
||||
@ -76,7 +79,6 @@ auto PlayerSpec::GetSpecString() const -> std::string {
|
||||
cJSON_AddStringToObject(root, "n", name_.c_str());
|
||||
cJSON_AddStringToObject(
|
||||
root, "a",
|
||||
// classic::V1Account::AccountTypeToString(account_type_).c_str()
|
||||
g_base->HaveClassic()
|
||||
? g_base->classic()->V1AccountTypeToString(v1_account_type_).c_str()
|
||||
: "");
|
||||
|
||||
@ -46,10 +46,13 @@ class PlayerSpec {
|
||||
/// party hosts, etc.
|
||||
static auto GetDummyPlayerSpec(const std::string& name) -> PlayerSpec;
|
||||
|
||||
auto valid() const { return valid_; }
|
||||
|
||||
private:
|
||||
std::string name_;
|
||||
std::string short_name_;
|
||||
int v1_account_type_{};
|
||||
bool valid_{true};
|
||||
};
|
||||
|
||||
} // namespace ballistica::scene_v1
|
||||
|
||||
@ -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 = 22143;
|
||||
const int kEngineBuildNumber = 22150;
|
||||
const char* kEngineVersion = "1.7.37";
|
||||
const int kEngineApiVersion = 9;
|
||||
|
||||
|
||||
@ -1370,6 +1370,8 @@ static auto PyContainerWidget(PyObject* self, PyObject* args, PyObject* keywds)
|
||||
val = Widget::ToolbarVisibility::kInherit;
|
||||
} else if (sval == "get_tokens") {
|
||||
val = Widget::ToolbarVisibility::kGetTokens;
|
||||
} else if (sval == "no_menu_minimal") {
|
||||
val = Widget::ToolbarVisibility::kNoMenuMinimal;
|
||||
} else {
|
||||
throw Exception("Invalid toolbar_visibility: '" + sval + "'.",
|
||||
PyExcType::kValue);
|
||||
@ -1432,6 +1434,7 @@ static PyMethodDef PyContainerWidgetDef = {
|
||||
" 'menu_in_game',\n"
|
||||
" 'menu_tokens',\n"
|
||||
" 'get_tokens',\n"
|
||||
" 'no_menu_minimal',\n"
|
||||
" 'inherit',\n"
|
||||
" ] | None = None,\n"
|
||||
" on_select_call: Callable[[], None] | None = None,\n"
|
||||
|
||||
@ -336,6 +336,13 @@ void RootWidget::AddMeter_(MeterType_ type, float h_align, float r, float g,
|
||||
switch (type) {
|
||||
case MeterType_::kTrophy:
|
||||
trophy_icon_ = img;
|
||||
break;
|
||||
case MeterType_::kTickets:
|
||||
tickets_meter_icon_ = img;
|
||||
break;
|
||||
case MeterType_::kTokens:
|
||||
tokens_meter_icon_ = img;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -541,14 +548,11 @@ void RootWidget::Setup() {
|
||||
AddMeter_(MeterType_::kLevel, 0.0f, 1.0f, 1.0f, 1.0f, false, "");
|
||||
AddMeter_(MeterType_::kTrophy, 0.0f, 1.0f, 1.0f, 1.0f, false, "");
|
||||
|
||||
// Menu button (only shows up when we're not in a menu).
|
||||
// FIXME - this should never be visible on TV or VR UI modes
|
||||
{
|
||||
ButtonDef_ b;
|
||||
b.h_align = 1.0f;
|
||||
b.v_align = VAlign_::kTop;
|
||||
b.width = b.height = 65.0f;
|
||||
// b.x = -36.0f;
|
||||
b.y = b.height * -0.48f;
|
||||
b.img = "menuButton";
|
||||
b.call = UIV1Python::ObjID::kRootUIMenuButtonPressCall;
|
||||
@ -592,7 +596,8 @@ void RootWidget::Setup() {
|
||||
| static_cast<uint32_t>(Widget::ToolbarVisibility::kMenuFullNoBack)
|
||||
| static_cast<uint32_t>(Widget::ToolbarVisibility::kMenuFullRoot)
|
||||
| static_cast<uint32_t>(Widget::ToolbarVisibility::kGetTokens)
|
||||
| static_cast<uint32_t>(Widget::ToolbarVisibility::kMenuTokens));
|
||||
| static_cast<uint32_t>(Widget::ToolbarVisibility::kMenuTokens)
|
||||
| static_cast<uint32_t>(Widget::ToolbarVisibility::kNoMenuMinimal));
|
||||
b.pre_buffer = 5.0f;
|
||||
b.enable_sound = false;
|
||||
squad_button_ = AddButton_(b);
|
||||
@ -1448,6 +1453,64 @@ void RootWidget::SetXPText(const std::string& val) {
|
||||
xp_text_->widget->SetText(val);
|
||||
}
|
||||
|
||||
void RootWidget::SetHaveLiveValues(bool have_live_values) {
|
||||
// auto cval{have_live_values ? 1.0f : 0.4f};
|
||||
auto oval{have_live_values ? 1.0f : 0.4f};
|
||||
auto oval2{have_live_values ? 1.0f : 0.4f};
|
||||
|
||||
assert(tickets_meter_text_);
|
||||
assert(tickets_meter_icon_);
|
||||
tickets_meter_text_->widget->set_color(1.0f, 1.0f, 1.0f, oval);
|
||||
// tickets_meter_icon_->widget->set_color(cval, cval, cval);
|
||||
tickets_meter_icon_->widget->set_opacity(oval2);
|
||||
|
||||
assert(tokens_meter_text_);
|
||||
assert(tokens_meter_icon_);
|
||||
tokens_meter_text_->widget->set_color(1.0f, 1.0f, 1.0f, oval);
|
||||
// tokens_meter_icon_->widget->set_color(cval, cval, cval);
|
||||
tokens_meter_icon_->widget->set_opacity(oval2);
|
||||
|
||||
assert(inbox_button_);
|
||||
inbox_button_->widget->set_opacity(oval2);
|
||||
|
||||
assert(achievements_button_);
|
||||
achievements_button_->widget->set_opacity(oval2);
|
||||
assert(achievement_percent_text_);
|
||||
achievement_percent_text_->widget->set_color(1.0f, 1.0f, 1.0f, oval);
|
||||
|
||||
assert(store_button_);
|
||||
store_button_->widget->set_opacity(oval2);
|
||||
|
||||
assert(inventory_button_);
|
||||
inventory_button_->widget->set_opacity(oval2);
|
||||
|
||||
assert(get_tokens_button_);
|
||||
get_tokens_button_->widget->set_opacity(oval2);
|
||||
|
||||
assert(league_rank_text_);
|
||||
league_rank_text_->widget->set_color(1.0f, 1.0f, 1.0f, oval);
|
||||
|
||||
assert(tickets_meter_button_);
|
||||
tickets_meter_button_->widget->set_opacity(oval2);
|
||||
|
||||
assert(tokens_meter_button_);
|
||||
tokens_meter_button_->widget->set_opacity(oval2);
|
||||
|
||||
assert(trophy_meter_button_);
|
||||
trophy_meter_button_->widget->set_opacity(oval2);
|
||||
|
||||
assert(trophy_icon_);
|
||||
trophy_icon_->widget->set_opacity(oval2);
|
||||
|
||||
for (auto* button :
|
||||
{chest_0_button_, chest_1_button_, chest_2_button_, chest_3_button_}) {
|
||||
assert(button);
|
||||
button->widget->set_opacity(have_live_values ? 1.0f : 0.5f);
|
||||
}
|
||||
assert(chest_backing_);
|
||||
chest_backing_->widget->set_opacity(have_live_values ? 1.0f : 0.5f);
|
||||
}
|
||||
|
||||
void RootWidget::SetChests(const std::string& chest_0_appearance,
|
||||
const std::string& chest_1_appearance,
|
||||
const std::string& chest_2_appearance,
|
||||
@ -1470,9 +1533,8 @@ void RootWidget::SetChests(const std::string& chest_0_appearance,
|
||||
assert(b);
|
||||
if (appearance == "") {
|
||||
b->widget->set_color(0.473f, 0.44f, 0.583f);
|
||||
b->widget->set_opacity(have_chests ? 1.0f : 0.5f);
|
||||
b->width = b->height = 80.0f;
|
||||
b->y = have_chests ? 44.0f : 0.0f;
|
||||
b->y = have_chests ? 44.0f : -2.0f;
|
||||
{
|
||||
base::Assets::AssetListLock lock;
|
||||
b->widget->SetTexture(
|
||||
@ -1481,7 +1543,6 @@ void RootWidget::SetChests(const std::string& chest_0_appearance,
|
||||
} else {
|
||||
have_chests = true;
|
||||
b->widget->set_color(1.0f, 1.0f, 1.0f);
|
||||
b->widget->set_opacity(1.0f);
|
||||
b->width = b->height = 110.0f;
|
||||
b->y = 44.0f;
|
||||
{
|
||||
@ -1491,8 +1552,7 @@ void RootWidget::SetChests(const std::string& chest_0_appearance,
|
||||
}
|
||||
}
|
||||
assert(chest_backing_);
|
||||
chest_backing_->y = have_chests ? 41.0f : -10.0f;
|
||||
chest_backing_->widget->set_opacity(have_chests ? 1.0f : 0.5f);
|
||||
chest_backing_->y = have_chests ? 41.0f : -15.0f;
|
||||
|
||||
child_widgets_dirty_ = true;
|
||||
}
|
||||
|
||||
@ -51,6 +51,7 @@ class RootWidget : public ContainerWidget {
|
||||
const std::string& chest_1_appearance,
|
||||
const std::string& chest_2_appearance,
|
||||
const std::string& chest_3_appearance);
|
||||
void SetHaveLiveValues(bool have_live_values);
|
||||
|
||||
auto bottom_left_height() const { return bottom_left_height_; }
|
||||
|
||||
@ -102,6 +103,8 @@ class RootWidget : public ContainerWidget {
|
||||
Button_* chest_3_button_{};
|
||||
Button_* chest_backing_{};
|
||||
Image_* trophy_icon_{};
|
||||
Image_* tickets_meter_icon_{};
|
||||
Image_* tokens_meter_icon_{};
|
||||
Image_* inbox_count_backing_{};
|
||||
Text_* squad_size_text_{};
|
||||
Text_* account_name_text_{};
|
||||
|
||||
@ -21,19 +21,20 @@ class Widget : public Object {
|
||||
/// the entire set of visibilities they apply to.
|
||||
enum class ToolbarVisibility : uint16_t {
|
||||
kInherit = 0, // For popups and whatnot - leave toolbar as-is.
|
||||
kMenuMinimal = 1, // Squad and back buttons.
|
||||
kMenuMinimalNoBack = 2, // Squad button only.
|
||||
kMenuStore = 4, // Squad, level, and soft currency buttons.
|
||||
kMenuStoreNoBack = 8, // Squad, level, and soft currency buttons.
|
||||
kMenuReduced = 16, // Squad, account, inbox, settings, back.
|
||||
kMenuReducedNoBack = 32, // Squad, account, inbox, settings.
|
||||
kMenuMinimal = 1, // Menu, squad, back.
|
||||
kMenuMinimalNoBack = 2, // Menu, squad.
|
||||
kMenuStore = 4, // Menu, squad, level, and soft currency.
|
||||
kMenuStoreNoBack = 8, // Menu, squad, level, and soft currency.
|
||||
kMenuReduced = 16, // Menu, squad, account, inbox, settings, back.
|
||||
kMenuReducedNoBack = 32, // Menu, squad, account, inbox, settings.
|
||||
kMenuFull = 64, // Everything.
|
||||
kMenuFullNoBack = 128, // Everything.
|
||||
kMenuFullNoBack = 128, // Everything minus back.
|
||||
kMenuFullRoot = 256, // Obsolete.
|
||||
kInGame = 512, // Menu, squad.
|
||||
kGetTokens = 1024, // Squad, tokens without plus.
|
||||
kMenuInGame = 2048, // Squad, settings.
|
||||
kMenuTokens = 4096 // Squad, tokens.
|
||||
kMenuTokens = 4096, // Squad, tokens.
|
||||
kNoMenuMinimal = 8192, // Squad.
|
||||
};
|
||||
|
||||
Widget();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user