plugins and plugin-settings windows are now screen-size-responsive. Race minigame is now a cheap unlock for new accounts but already unlocked for existing ones.

This commit is contained in:
Eric Froemling 2025-01-25 15:42:24 -08:00
parent ee48a2fe12
commit 8fa95a9f24
No known key found for this signature in database
9 changed files with 209 additions and 142 deletions

60
.efrocachemap generated
View File

@ -432,12 +432,12 @@
"build/assets/ba_data/audio/zoeOw.ogg": "b2d705c31c9dcc1efdc71394764c3beb", "build/assets/ba_data/audio/zoeOw.ogg": "b2d705c31c9dcc1efdc71394764c3beb",
"build/assets/ba_data/audio/zoePickup01.ogg": "e9366dc2d2b8ab8b0c4e2c14c02d0789", "build/assets/ba_data/audio/zoePickup01.ogg": "e9366dc2d2b8ab8b0c4e2c14c02d0789",
"build/assets/ba_data/audio/zoeScream01.ogg": "903e0e45ee9b3373e9d9ce20c814374e", "build/assets/ba_data/audio/zoeScream01.ogg": "903e0e45ee9b3373e9d9ce20c814374e",
"build/assets/ba_data/data/langdata.json": "8d49fa91845f43ec8bff3ad3f4895e69", "build/assets/ba_data/data/langdata.json": "1155a380ac3dafb994b4f7438a6efa4f",
"build/assets/ba_data/data/languages/arabic.json": "32b9849fb8389b8c7798f0b744620318", "build/assets/ba_data/data/languages/arabic.json": "955758fcbd6ceaa19c8984ec04dc409b",
"build/assets/ba_data/data/languages/belarussian.json": "009b452aa308bf2b2f7e92d9b78ba5ff", "build/assets/ba_data/data/languages/belarussian.json": "009b452aa308bf2b2f7e92d9b78ba5ff",
"build/assets/ba_data/data/languages/chinese.json": "5363a79f843e6be7ef47a840f47cc17d", "build/assets/ba_data/data/languages/chinese.json": "5363a79f843e6be7ef47a840f47cc17d",
"build/assets/ba_data/data/languages/chinesetraditional.json": "bea0f9c17324591b8261015cbd80a265", "build/assets/ba_data/data/languages/chinesetraditional.json": "bea0f9c17324591b8261015cbd80a265",
"build/assets/ba_data/data/languages/croatian.json": "66be7ada024c5d5cf813a07b75217e48", "build/assets/ba_data/data/languages/croatian.json": "1ad9d43f30c6d7ed3b39e2a0e8bfae0b",
"build/assets/ba_data/data/languages/czech.json": "3418bee44e69be13b7f72996abe96921", "build/assets/ba_data/data/languages/czech.json": "3418bee44e69be13b7f72996abe96921",
"build/assets/ba_data/data/languages/danish.json": "8e57db30c5250df2abff14a822f83ea7", "build/assets/ba_data/data/languages/danish.json": "8e57db30c5250df2abff14a822f83ea7",
"build/assets/ba_data/data/languages/dutch.json": "4ba5bbcc0fecddd0aac6ee2c165d1e40", "build/assets/ba_data/data/languages/dutch.json": "4ba5bbcc0fecddd0aac6ee2c165d1e40",
@ -451,7 +451,7 @@
"build/assets/ba_data/data/languages/hindi.json": "567e6976b3c72f891431ad7fcc62ab16", "build/assets/ba_data/data/languages/hindi.json": "567e6976b3c72f891431ad7fcc62ab16",
"build/assets/ba_data/data/languages/hungarian.json": "af801baffb2c06460635dfb04c34bb3e", "build/assets/ba_data/data/languages/hungarian.json": "af801baffb2c06460635dfb04c34bb3e",
"build/assets/ba_data/data/languages/indonesian.json": "607ba358179185f032096ea1978e4448", "build/assets/ba_data/data/languages/indonesian.json": "607ba358179185f032096ea1978e4448",
"build/assets/ba_data/data/languages/italian.json": "8ed7e5b3a277ed1576af100fc944ef7e", "build/assets/ba_data/data/languages/italian.json": "656dbe1c77d0d4776f3b095384e8dda2",
"build/assets/ba_data/data/languages/korean.json": "360760d72832863e1a3451b0a514cb08", "build/assets/ba_data/data/languages/korean.json": "360760d72832863e1a3451b0a514cb08",
"build/assets/ba_data/data/languages/malay.json": "0212e18e54efa202c17505376e5b82fb", "build/assets/ba_data/data/languages/malay.json": "0212e18e54efa202c17505376e5b82fb",
"build/assets/ba_data/data/languages/persian.json": "517217e679c768fff4ffec7f8000ab77", "build/assets/ba_data/data/languages/persian.json": "517217e679c768fff4ffec7f8000ab77",
@ -459,10 +459,10 @@
"build/assets/ba_data/data/languages/polish.json": "993b612c5854fc42a78726ed09c65251", "build/assets/ba_data/data/languages/polish.json": "993b612c5854fc42a78726ed09c65251",
"build/assets/ba_data/data/languages/portuguese.json": "99eaba2900ab66b05f0e9f22da4792a2", "build/assets/ba_data/data/languages/portuguese.json": "99eaba2900ab66b05f0e9f22da4792a2",
"build/assets/ba_data/data/languages/romanian.json": "b04345d8c7631d657a69c73eb7be755a", "build/assets/ba_data/data/languages/romanian.json": "b04345d8c7631d657a69c73eb7be755a",
"build/assets/ba_data/data/languages/russian.json": "780d1857df77ef59104d5dac75415bd6", "build/assets/ba_data/data/languages/russian.json": "f2d5569c5924f21d02cfa45eabac758c",
"build/assets/ba_data/data/languages/serbian.json": "623fa4129a1154c2f32ed7867e56ff6a", "build/assets/ba_data/data/languages/serbian.json": "623fa4129a1154c2f32ed7867e56ff6a",
"build/assets/ba_data/data/languages/slovak.json": "c11c29708b3742cdc2a92b4fa0d6d29f", "build/assets/ba_data/data/languages/slovak.json": "c11c29708b3742cdc2a92b4fa0d6d29f",
"build/assets/ba_data/data/languages/spanish.json": "83baf596f8d29ebb965051bf280424c6", "build/assets/ba_data/data/languages/spanish.json": "1e429102b451ccb8c392a352624e8a6c",
"build/assets/ba_data/data/languages/swedish.json": "3b179e7333183c70adb0811246b09959", "build/assets/ba_data/data/languages/swedish.json": "3b179e7333183c70adb0811246b09959",
"build/assets/ba_data/data/languages/tamil.json": "ead39b864228696a9b0d19344bc4b5ec", "build/assets/ba_data/data/languages/tamil.json": "ead39b864228696a9b0d19344bc4b5ec",
"build/assets/ba_data/data/languages/thai.json": "383540a1e9c7c131ac579f51afc87471", "build/assets/ba_data/data/languages/thai.json": "383540a1e9c7c131ac579f51afc87471",
@ -4174,22 +4174,22 @@
"build/assets/windows/Win32/ucrtbased.dll": "bfd1180c269d3950b76f35a63655e9e1", "build/assets/windows/Win32/ucrtbased.dll": "bfd1180c269d3950b76f35a63655e9e1",
"build/assets/windows/Win32/vc_redist.x86.exe": "15a5f1f876503885adbdf5b3989b3718", "build/assets/windows/Win32/vc_redist.x86.exe": "15a5f1f876503885adbdf5b3989b3718",
"build/assets/windows/Win32/vcruntime140d.dll": "865b2af4d1e26a1a8073c89acb06e599", "build/assets/windows/Win32/vcruntime140d.dll": "865b2af4d1e26a1a8073c89acb06e599",
"build/prefab/full/linux_arm64_gui/debug/ballisticakit": "92bd286969030fdcc3f0b8ebad115c74", "build/prefab/full/linux_arm64_gui/debug/ballisticakit": "e24bb1259608c728a356750e81c536e6",
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "711ea2ab6d63356b03ea7a29899afcef", "build/prefab/full/linux_arm64_gui/release/ballisticakit": "2f34a75859192d79062ddcef8b53959a",
"build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "513f33f187e85b909793ddd2aa9732a5", "build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "188c2627da68618f1757d7c6264c631a",
"build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "8e9824b44c359d404e2ad8fcf0f448f8", "build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "a5e1677bee69b8cdc49a34009dd100c1",
"build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "3409817015ddf936d5fc293d9d71c706", "build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "48991687444dd243c6031e3cee4a1fa8",
"build/prefab/full/linux_x86_64_gui/release/ballisticakit": "cb054db65cddf26f667d1842b17f2b7d", "build/prefab/full/linux_x86_64_gui/release/ballisticakit": "6d664808b8c7644b32c41b25eeca0143",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "c97f0757f6282a46aad165ab2411818e", "build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "6c0ba3f4be9540c30fc09f13e8524fa4",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "8302bf0d0ee175ae28c8fd2e24c71628", "build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "378faae22a13d5ef4446fc3c6bb1ef90",
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "3f05bbbc8434cafe12b6692344cf5b6a", "build/prefab/full/mac_arm64_gui/debug/ballisticakit": "407e1f9508f9cfeb72bea651c231e66f",
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "e367580ee6a85f7baf70f13f3e1b4867", "build/prefab/full/mac_arm64_gui/release/ballisticakit": "b23c4ad8cc56228d58dab66e1387b615",
"build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "cdf23977be0849e7e944e0a44cdaf140", "build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "a6c73f1589742e19c0e9fd19a8bb4e52",
"build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "23e305bc449464c6eef46867cd8c5363", "build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "48fed8f481ad390c12b094a41a0d07ac",
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "0e5fc0e3f78ecc0a1cb382f00690829f", "build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "e5f1362531bab5b937545e149b6d4135",
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "afcd6f10eca27f891a3c000fbfe80231", "build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "8d83a22f2fedcebc2b6327a81e02e7c8",
"build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "1c75953b11ccd3efe5396f2c0add5c9e", "build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "fc2cf0933aaee17916c46cec57bff439",
"build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "8d6c40f72e7d1abc922a0567c858b14f", "build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "dc5927b762d684df558dfedf25e4fb70",
"build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "2ee184af9b80d60afea7f97aba29cb16", "build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "2ee184af9b80d60afea7f97aba29cb16",
"build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "37c5f795ccbaeffeb5d6ede4fe9f3c19", "build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "37c5f795ccbaeffeb5d6ede4fe9f3c19",
"build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "2ee184af9b80d60afea7f97aba29cb16", "build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "2ee184af9b80d60afea7f97aba29cb16",
@ -4202,14 +4202,14 @@
"build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "d258789fd7bdc5092aab87ccb4601921", "build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "d258789fd7bdc5092aab87ccb4601921",
"build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "314516411d5ac8e991a6f742f399d4de", "build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "314516411d5ac8e991a6f742f399d4de",
"build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "d258789fd7bdc5092aab87ccb4601921", "build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "d258789fd7bdc5092aab87ccb4601921",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "5393242850af9d26c0ee050765ee71cf", "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "728e7999dec1017d1e9f0e9e521885e4",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "887c497ef3462b454bd00891e10da33b", "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "6052cd644522f3e174f27db3d51396b1",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "37afc11cbb5bb3d8d5c617d285fd2ccf", "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "2fe7bf4e33e98872e9f52c71fc061134",
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "36ef12603f477db495d4be42ae8428dc", "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "692d2b8e0e68ded67a935fa72f17f8d2",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "9a935afd3302e43b8c8da97a901ee736", "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "a94e443fe7d96eb9d9e1684fc3a7af0b",
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "d02591202dd1201d6dd2c631344c2396", "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "e8323acf80bcc48b27beab167f4f70f9",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "b079ea523eceb09c63f9269e1f2096dd", "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "76645f01806e43775d376e45c84ef855",
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "15693ffb865e33c99820ee4d4962a39c", "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "594f4d538e9e910a21c73a8b71e8aaab",
"src/assets/ba_data/python/babase/_mgen/__init__.py": "f885fed7f2ed98ff2ba271f9dbe3391c", "src/assets/ba_data/python/babase/_mgen/__init__.py": "f885fed7f2ed98ff2ba271f9dbe3391c",
"src/assets/ba_data/python/babase/_mgen/enums.py": "794d258d59fd17a61752843a9a0551ad", "src/assets/ba_data/python/babase/_mgen/enums.py": "794d258d59fd17a61752843a9a0551ad",
"src/ballistica/base/mgen/pyembed/binding_base.inc": "06042d31df0ff9af96b99477162e2a91", "src/ballistica/base/mgen/pyembed/binding_base.inc": "06042d31df0ff9af96b99477162e2a91",

View File

@ -1,4 +1,4 @@
### 1.7.37 (build 22248, api 9, 2025-01-23) ### 1.7.37 (build 22251, api 9, 2025-01-25)
- Bumping api version to 9. As you'll see below, there's some UI changes that - 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 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. touch UI stuff at all you can simply bump your api version and call it a day.

View File

@ -897,7 +897,7 @@ class ClassicAppSubsystem(babase.AppSubsystem):
return babase.Lstr(resource=rsrc) return babase.Lstr(resource=rsrc)
def required_purchase_for_game(self, game: str) -> str | None: def required_purchases_for_game(self, game: str) -> list[str]:
"""Return which purchase (if any) is required for a game.""" """Return which purchase (if any) is required for a game."""
# pylint: disable=too-many-return-statements # pylint: disable=too-many-return-statements
@ -907,9 +907,9 @@ class ClassicAppSubsystem(babase.AppSubsystem):
): ):
# Special case: Pro used to unlock this. # Special case: Pro used to unlock this.
return ( return (
None []
if self.accounts.have_pro() if self.accounts.have_pro()
else 'upgrades.infinite_runaround' else ['upgrades.infinite_runaround']
) )
if game in ( if game in (
'Challenges:Infinite Onslaught', 'Challenges:Infinite Onslaught',
@ -917,48 +917,52 @@ class ClassicAppSubsystem(babase.AppSubsystem):
): ):
# Special case: Pro used to unlock this. # Special case: Pro used to unlock this.
return ( return (
None []
if self.accounts.have_pro() if self.accounts.have_pro()
else 'upgrades.infinite_onslaught' else ['upgrades.infinite_onslaught']
) )
if game in ( if game in (
'Challenges:Meteor Shower', 'Challenges:Meteor Shower',
'Challenges:Epic Meteor Shower', 'Challenges:Epic Meteor Shower',
): ):
return 'games.meteor_shower' return ['games.meteor_shower']
if game in ( if game in (
'Challenges:Target Practice', 'Challenges:Target Practice',
'Challenges:Target Practice B', 'Challenges:Target Practice B',
): ):
return 'games.target_practice' return ['games.target_practice']
if game in ( if game in (
'Challenges:Ninja Fight', 'Challenges:Ninja Fight',
'Challenges:Pro Ninja Fight', 'Challenges:Pro Ninja Fight',
): ):
return 'games.ninja_fight' return ['games.ninja_fight']
if game in ('Challenges:Race', 'Challenges:Pro Race'):
return ['games.race']
if game in ('Challenges:Lake Frigid Race',): if game in ('Challenges:Lake Frigid Race',):
return 'maps.lake_frigid' return ['games.race', 'maps.lake_frigid']
if game in ( if game in (
'Challenges:Easter Egg Hunt', 'Challenges:Easter Egg Hunt',
'Challenges:Pro Easter Egg Hunt', 'Challenges:Pro Easter Egg Hunt',
): ):
return 'games.easter_egg_hunt' return ['games.easter_egg_hunt']
return None return []
def is_game_unlocked(self, game: str) -> bool: def is_game_unlocked(self, game: str) -> bool:
"""Is a particular game unlocked?""" """Is a particular game unlocked?"""
plus = babase.app.plus plus = babase.app.plus
assert plus is not None assert plus is not None
purchase = self.required_purchase_for_game(game) purchases = self.required_purchases_for_game(game)
if purchase is None: if not purchases:
return True return True
out = plus.get_v1_account_product_purchased(purchase) for purchase in purchases:
assert isinstance(out, bool) if not plus.get_v1_account_product_purchased(purchase):
return out return False
return True

View File

@ -90,10 +90,11 @@ class StoreSubsystem:
assert babase.app.classic is not None assert babase.app.classic is not None
if babase.app.classic.store_items is None: if babase.app.classic.store_items is None:
from bascenev1lib.game import ninjafight from bascenev1lib.game.race import RaceGame
from bascenev1lib.game import meteorshower from bascenev1lib.game.ninjafight import NinjaFightGame
from bascenev1lib.game import targetpractice from bascenev1lib.game.meteorshower import MeteorShowerGame
from bascenev1lib.game import easteregghunt from bascenev1lib.game.targetpractice import TargetPracticeGame
from bascenev1lib.game.easteregghunt import EasterEggHuntGame
# IMPORTANT - need to keep this synced with the master server. # IMPORTANT - need to keep this synced with the master server.
# (doing so manually for now) # (doing so manually for now)
@ -122,24 +123,28 @@ class StoreSubsystem:
'merch': {}, 'merch': {},
'pro': {}, 'pro': {},
'maps.lake_frigid': {'map_type': maps.LakeFrigid}, 'maps.lake_frigid': {'map_type': maps.LakeFrigid},
'games.race': {
'gametype': RaceGame,
'previewTex': 'bigGPreview',
},
'games.ninja_fight': { 'games.ninja_fight': {
'gametype': ninjafight.NinjaFightGame, 'gametype': NinjaFightGame,
'previewTex': 'courtyardPreview', 'previewTex': 'courtyardPreview',
}, },
'games.meteor_shower': { 'games.meteor_shower': {
'gametype': meteorshower.MeteorShowerGame, 'gametype': MeteorShowerGame,
'previewTex': 'rampagePreview', 'previewTex': 'rampagePreview',
}, },
'games.infinite_onslaught': { 'games.infinite_onslaught': {
'gametype': meteorshower.MeteorShowerGame, 'gametype': MeteorShowerGame,
'previewTex': 'rampagePreview', 'previewTex': 'rampagePreview',
}, },
'games.target_practice': { 'games.target_practice': {
'gametype': targetpractice.TargetPracticeGame, 'gametype': TargetPracticeGame,
'previewTex': 'doomShroomPreview', 'previewTex': 'doomShroomPreview',
}, },
'games.easter_egg_hunt': { 'games.easter_egg_hunt': {
'gametype': easteregghunt.EasterEggHuntGame, 'gametype': EasterEggHuntGame,
'previewTex': 'towerDPreview', 'previewTex': 'towerDPreview',
}, },
'icons.flag_us': { 'icons.flag_us': {
@ -380,6 +385,7 @@ class StoreSubsystem:
store_layout['minigames'] = [ store_layout['minigames'] = [
{ {
'items': [ 'items': [
'games.race',
'games.ninja_fight', 'games.ninja_fight',
'games.meteor_shower', 'games.meteor_shower',
'games.target_practice', 'games.target_practice',

View File

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

View File

@ -1022,19 +1022,33 @@ class CoopBrowserWindow(bui.MainWindow):
) )
return return
required_purchase = bui.app.classic.required_purchase_for_game(game) required_purchases = bui.app.classic.required_purchases_for_game(game)
if ( # Show pop-up to allow purchasing any required stuff we don't have.
required_purchase is not None for purchase in required_purchases:
and not plus.get_v1_account_product_purchased(required_purchase) if not plus.get_v1_account_product_purchased(purchase):
): if plus.get_v1_account_state() != 'signed_in':
if plus.get_v1_account_state() != 'signed_in': show_sign_in_prompt()
show_sign_in_prompt() else:
else: PurchaseWindow(
PurchaseWindow( items=[purchase], origin_widget=origin_widget
items=[required_purchase], origin_widget=origin_widget )
) return
return
# if required_purchases and not all(
# plus.get_v1_account_product_purchased(p)
# for p in required_purchases
# ):
# if plus.get_v1_account_state() != 'signed_in':
# show_sign_in_prompt()
# else:
# # Hmm just ask about the first I guess.. They can pop
# # this window back up to the next if they purchase the
# # first.
# PurchaseWindow(
# items=[required_purchases[0]], origin_widget=origin_widget
# )
# return
self._save_state() self._save_state()
@ -1107,18 +1121,34 @@ class CoopBrowserWindow(bui.MainWindow):
if tournament_button.game is not None and not classic.is_game_unlocked( if tournament_button.game is not None and not classic.is_game_unlocked(
tournament_button.game tournament_button.game
): ):
required_purchase = classic.required_purchase_for_game( required_purchases = classic.required_purchases_for_game(
tournament_button.game tournament_button.game
) )
assert required_purchase is not None # We gotta be missing *something* if its locked.
if plus.get_v1_account_state() != 'signed_in': assert required_purchases
show_sign_in_prompt()
else: for purchase in required_purchases:
PurchaseWindow( if not plus.get_v1_account_product_purchased(purchase):
items=[required_purchase], if plus.get_v1_account_state() != 'signed_in':
origin_widget=tournament_button.button, show_sign_in_prompt()
) else:
return PurchaseWindow(
items=[purchase],
origin_widget=tournament_button.button,
)
return
# assert required_purchases
# if plus.get_v1_account_state() != 'signed_in':
# show_sign_in_prompt()
# else:
# # Hmm; just show the first requirement. They can come
# # back to see more after they purchase the first.
# PurchaseWindow(
# items=[required_purchases[0]],
# origin_widget=tournament_button.button,
# )
# return
if tournament_button.time_remaining <= 0: if tournament_button.time_remaining <= 0:
bui.screenmessage( bui.screenmessage(

View File

@ -36,46 +36,58 @@ class PluginWindow(bui.MainWindow):
transition: str | None = 'in_right', transition: str | None = 'in_right',
origin_widget: bui.Widget | None = None, origin_widget: bui.Widget | None = None,
): ):
# pylint: disable=too-many-locals
app = bui.app app = bui.app
self._category = Category.ALL self._category = Category.ALL
assert bui.app.classic is not None assert bui.app.classic is not None
uiscale = bui.app.ui_v1.uiscale uiscale = bui.app.ui_v1.uiscale
self._width = 870.0 if uiscale is bui.UIScale.SMALL else 670.0 self._width = 1200.0 if uiscale is bui.UIScale.SMALL else 670.0
x_inset = 100 if uiscale is bui.UIScale.SMALL else 0
yoffs = -55.0 if uiscale is bui.UIScale.SMALL else 0
self._height = ( self._height = (
450.0 900.0
if uiscale is bui.UIScale.SMALL if uiscale is bui.UIScale.SMALL
else 450.0 if uiscale is bui.UIScale.MEDIUM else 520.0 else 450.0 if uiscale is bui.UIScale.MEDIUM else 520.0
) )
top_extra = 0 if uiscale is bui.UIScale.SMALL else 0
# Do some fancy math to fill all available screen area up to the
# size of our backing container. This lets us fit to the exact
# screen shape at small ui scale.
screensize = bui.get_virtual_screen_size()
scale = (
1.9
if uiscale is bui.UIScale.SMALL
else 1.4 if uiscale is bui.UIScale.MEDIUM else 1.0
)
# Calc screen size in our local container space and clamp to a
# bit smaller than our container size.
target_width = min(self._width - 80, screensize[0] / scale)
target_height = min(self._height - 80, screensize[1] / scale)
# To get top/left coords, go to the center of our window and
# offset by half the width/height of our target area.
yoffs = 0.5 * self._height + 0.5 * target_height + 20.0
self._scroll_width = target_width
self._scroll_height = target_height - 40
self._scroll_bottom = yoffs - 64 - self._scroll_height
super().__init__( super().__init__(
root_widget=bui.containerwidget( root_widget=bui.containerwidget(
size=(self._width, self._height + top_extra), size=(self._width, self._height),
toolbar_visibility=( toolbar_visibility=(
'menu_minimal' 'menu_minimal'
if uiscale is bui.UIScale.SMALL if uiscale is bui.UIScale.SMALL
else 'menu_full' else 'menu_full'
), ),
scale=( scale=scale,
1.9
if uiscale is bui.UIScale.SMALL
else 1.4 if uiscale is bui.UIScale.MEDIUM else 1.0
),
stack_offset=(
(0, 0) if uiscale is bui.UIScale.SMALL else (0, 0)
),
), ),
transition=transition, transition=transition,
origin_widget=origin_widget, origin_widget=origin_widget,
# We're affected by screen size only at small ui-scale.
refresh_on_screen_size_changes=uiscale is bui.UIScale.SMALL,
) )
self._scroll_width = self._width - (100 + 2 * x_inset)
self._scroll_height = self._height - (
200.0 if uiscale is bui.UIScale.SMALL else 115.0
)
self._sub_width = self._scroll_width * 0.95 self._sub_width = self._scroll_width * 0.95
self._sub_height = 724.0 self._sub_height = 724.0
@ -88,12 +100,12 @@ class PluginWindow(bui.MainWindow):
else: else:
self._back_button = bui.buttonwidget( self._back_button = bui.buttonwidget(
parent=self._root_widget, parent=self._root_widget,
position=(53 + x_inset, self._height - 60 + yoffs), position=(53, yoffs - 49),
size=(140, 60), size=(60, 60),
scale=0.8, scale=0.8,
autoselect=True, autoselect=True,
label=bui.Lstr(resource='backText'), label=bui.charstr(bui.SpecialChar.BACK),
button_type='back', button_type='backSmall',
on_activate_call=self.main_window_back, on_activate_call=self.main_window_back,
) )
bui.containerwidget( bui.containerwidget(
@ -102,7 +114,10 @@ class PluginWindow(bui.MainWindow):
self._title_text = bui.textwidget( self._title_text = bui.textwidget(
parent=self._root_widget, parent=self._root_widget,
position=(self._width * 0.5, self._height - 41 + yoffs), position=(
self._width * 0.5,
yoffs - (42 if uiscale is bui.UIScale.SMALL else 30),
),
size=(0, 0), size=(0, 0),
text=bui.Lstr(resource='pluginsText'), text=bui.Lstr(resource='pluginsText'),
color=app.ui_v1.title_color, color=app.ui_v1.title_color,
@ -111,19 +126,16 @@ class PluginWindow(bui.MainWindow):
v_align='center', v_align='center',
) )
if self._back_button is not None: settings_button_x = (
bui.buttonwidget( self._width * 0.5
edit=self._back_button, + self._scroll_width * 0.5
button_type='backSmall', - (100 if uiscale is bui.UIScale.SMALL else 40)
size=(60, 60), )
label=bui.charstr(bui.SpecialChar.BACK), button_row_yoffs = yoffs + (-2 if uiscale is bui.UIScale.SMALL else 10)
)
settings_button_x = 670 if uiscale is bui.UIScale.SMALL else 570
self._num_plugins_text = bui.textwidget( self._num_plugins_text = bui.textwidget(
parent=self._root_widget, parent=self._root_widget,
position=(settings_button_x - 130, self._height - 41 + yoffs), position=(settings_button_x - 130, button_row_yoffs - 41),
size=(0, 0), size=(0, 0),
text='', text='',
h_align='center', h_align='center',
@ -133,7 +145,7 @@ class PluginWindow(bui.MainWindow):
self._category_button = bui.buttonwidget( self._category_button = bui.buttonwidget(
parent=self._root_widget, parent=self._root_widget,
scale=0.7, scale=0.7,
position=(settings_button_x - 105, self._height - 60 + yoffs), position=(settings_button_x - 105, button_row_yoffs - 60),
size=(130, 60), size=(130, 60),
label=bui.Lstr(resource='allText'), label=bui.Lstr(resource='allText'),
autoselect=True, autoselect=True,
@ -144,7 +156,7 @@ class PluginWindow(bui.MainWindow):
self._settings_button = bui.buttonwidget( self._settings_button = bui.buttonwidget(
parent=self._root_widget, parent=self._root_widget,
position=(settings_button_x, self._height - 58 + yoffs), position=(settings_button_x, button_row_yoffs - 58),
size=(40, 40), size=(40, 40),
label='', label='',
on_activate_call=self._open_settings, on_activate_call=self._open_settings,
@ -152,7 +164,7 @@ class PluginWindow(bui.MainWindow):
bui.imagewidget( bui.imagewidget(
parent=self._root_widget, parent=self._root_widget,
position=(settings_button_x + 3, self._height - 57 + yoffs), position=(settings_button_x + 3, button_row_yoffs - 57),
draw_controller=self._settings_button, draw_controller=self._settings_button,
size=(35, 35), size=(35, 35),
texture=bui.gettexture('settingsIcon'), texture=bui.gettexture('settingsIcon'),
@ -166,15 +178,16 @@ class PluginWindow(bui.MainWindow):
self._scrollwidget = bui.scrollwidget( self._scrollwidget = bui.scrollwidget(
parent=self._root_widget, parent=self._root_widget,
size=(self._scroll_width, self._scroll_height),
position=( position=(
50 + x_inset, self._width * 0.5 - self._scroll_width * 0.5,
(135 if uiscale is bui.UIScale.SMALL else 50) + yoffs, self._scroll_bottom,
), ),
simple_culling_v=20.0, simple_culling_v=20.0,
highlight=False, highlight=False,
size=(self._scroll_width, self._scroll_height),
selection_loops_to_parent=True, selection_loops_to_parent=True,
claims_left_right=True, claims_left_right=True,
border_opacity=0.4,
) )
bui.widget(edit=self._scrollwidget, right_widget=self._scrollwidget) bui.widget(edit=self._scrollwidget, right_widget=self._scrollwidget)

View File

@ -21,42 +21,52 @@ class PluginSettingsWindow(bui.MainWindow):
assert bui.app.classic is not None assert bui.app.classic is not None
uiscale = bui.app.ui_v1.uiscale uiscale = bui.app.ui_v1.uiscale
width = 750.0 if uiscale is bui.UIScale.SMALL else 470.0 self._width = 1200.0 if uiscale is bui.UIScale.SMALL else 470.0
height = 400.0 if uiscale is bui.UIScale.SMALL else 300.0 self._height = 900.0 if uiscale is bui.UIScale.SMALL else 360.0
yoffs = -20 if uiscale is bui.UIScale.SMALL else 0
# Do some fancy math to fill all available screen area up to the
# size of our backing container. This lets us fit to the exact
# screen shape at small ui scale.
screensize = bui.get_virtual_screen_size()
scale = (
2.06
if uiscale is bui.UIScale.SMALL
else 1.4 if uiscale is bui.UIScale.MEDIUM else 1.0
)
# Calc screen size in our local container space and clamp to a
# bit smaller than our container size.
# target_width = min(self._width - 60, screensize[0] / scale)
target_height = min(self._height - 100, screensize[1] / scale)
# To get top/left coords, go to the center of our window and
# offset by half the width/height of our target area.
self._yoffs = 0.5 * self._height + 0.5 * target_height + 30.0
super().__init__( super().__init__(
root_widget=bui.containerwidget( root_widget=bui.containerwidget(
size=(width, height), size=(self._width, self._height),
toolbar_visibility=( toolbar_visibility=(
'menu_minimal' 'menu_minimal'
if uiscale is bui.UIScale.SMALL if uiscale is bui.UIScale.SMALL
else 'menu_full' else 'menu_full'
), ),
scale=( scale=scale,
2.06
if uiscale is bui.UIScale.SMALL
else 1.4 if uiscale is bui.UIScale.MEDIUM else 1.0
),
stack_offset=(
(0, 0) if uiscale is bui.UIScale.SMALL else (0, 0)
),
), ),
transition=transition, transition=transition,
origin_widget=origin_widget, origin_widget=origin_widget,
# We're affected by screen size only at small ui-scale.
refresh_on_screen_size_changes=uiscale is bui.UIScale.SMALL,
) )
if uiscale is bui.UIScale.SMALL: if uiscale is bui.UIScale.SMALL:
xoffs = 135
self._back_button = bui.get_special_widget('back_button') self._back_button = bui.get_special_widget('back_button')
bui.containerwidget( bui.containerwidget(
edit=self._root_widget, on_cancel_call=self.main_window_back edit=self._root_widget, on_cancel_call=self.main_window_back
) )
else: else:
xoffs = 0
self._back_button = bui.buttonwidget( self._back_button = bui.buttonwidget(
parent=self._root_widget, parent=self._root_widget,
position=(53, height - 60 + yoffs), position=(55, self._yoffs - 33),
size=(60, 60), size=(60, 60),
scale=0.8, scale=0.8,
autoselect=True, autoselect=True,
@ -71,20 +81,24 @@ class PluginSettingsWindow(bui.MainWindow):
self._title_text = bui.textwidget( self._title_text = bui.textwidget(
parent=self._root_widget, parent=self._root_widget,
position=( position=(
width * 0.5, self._width * 0.5,
height - (55 if uiscale is bui.UIScale.SMALL else 35) + yoffs, self._yoffs - (55 if uiscale is bui.UIScale.SMALL else 10),
), ),
size=(0, 0), size=(0, 0),
text=bui.Lstr(resource='pluginSettingsText'), text=bui.Lstr(resource='pluginSettingsText'),
maxwidth=230,
color=bui.app.ui_v1.title_color, color=bui.app.ui_v1.title_color,
h_align='center', h_align='center',
v_align='center', v_align='center',
) )
self._y_position = height - 140 + yoffs # Roughly center our few bits of content.
x = self._width * 0.5 - 175
y = self._height * 0.5 + 30
self._enable_plugins_button = bui.buttonwidget( self._enable_plugins_button = bui.buttonwidget(
parent=self._root_widget, parent=self._root_widget,
position=(xoffs + 65, self._y_position + yoffs), position=(x, y),
size=(350, 60), size=(350, 60),
autoselect=True, autoselect=True,
label=bui.Lstr(resource='pluginsEnableAllText'), label=bui.Lstr(resource='pluginsEnableAllText'),
@ -94,10 +108,10 @@ class PluginSettingsWindow(bui.MainWindow):
), ),
) )
self._y_position -= 70 y -= 70
self._disable_plugins_button = bui.buttonwidget( self._disable_plugins_button = bui.buttonwidget(
parent=self._root_widget, parent=self._root_widget,
position=(xoffs + 65, self._y_position + yoffs), position=(x, y),
size=(350, 60), size=(350, 60),
autoselect=True, autoselect=True,
label=bui.Lstr(resource='pluginsDisableAllText'), label=bui.Lstr(resource='pluginsDisableAllText'),
@ -107,10 +121,10 @@ class PluginSettingsWindow(bui.MainWindow):
), ),
) )
self._y_position -= 70 y -= 70
self._enable_new_plugins_check_box = bui.checkboxwidget( self._enable_new_plugins_check_box = bui.checkboxwidget(
parent=self._root_widget, parent=self._root_widget,
position=(xoffs + 65, self._y_position + yoffs), position=(x, y),
size=(350, 60), size=(350, 60),
value=bui.app.config.get( value=bui.app.config.get(
bui.app.plugins.AUTO_ENABLE_NEW_PLUGINS_CONFIG_KEY, bui.app.plugins.AUTO_ENABLE_NEW_PLUGINS_CONFIG_KEY,

View File

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