Added a filter option to the public-party gather tab (and more general cleanup)

This commit is contained in:
Eric Froemling 2020-10-29 12:06:10 -05:00
parent e2126fa0f7
commit d3ec602329
16 changed files with 238 additions and 166 deletions

View File

@ -420,20 +420,20 @@
"assets/build/ba_data/audio/zoeOw.ogg": "https://files.ballistica.net/cache/ba1/04/0a/c4f7d2794b018593ab0b2bcb07f0", "assets/build/ba_data/audio/zoeOw.ogg": "https://files.ballistica.net/cache/ba1/04/0a/c4f7d2794b018593ab0b2bcb07f0",
"assets/build/ba_data/audio/zoePickup01.ogg": "https://files.ballistica.net/cache/ba1/06/4d/18777c9a2eb2207a2891a2837a70", "assets/build/ba_data/audio/zoePickup01.ogg": "https://files.ballistica.net/cache/ba1/06/4d/18777c9a2eb2207a2891a2837a70",
"assets/build/ba_data/audio/zoeScream01.ogg": "https://files.ballistica.net/cache/ba1/81/90/23ab1ecc8c55267bd904a9c05344", "assets/build/ba_data/audio/zoeScream01.ogg": "https://files.ballistica.net/cache/ba1/81/90/23ab1ecc8c55267bd904a9c05344",
"assets/build/ba_data/data/langdata.json": "https://files.ballistica.net/cache/ba1/48/27/1b912861ed8d8795add395df41b6", "assets/build/ba_data/data/langdata.json": "https://files.ballistica.net/cache/ba1/05/20/767f42e4f0f289389cbdfb61eb10",
"assets/build/ba_data/data/languages/arabic.json": "https://files.ballistica.net/cache/ba1/de/25/74be4875c2a0e22b813a4e1a103b", "assets/build/ba_data/data/languages/arabic.json": "https://files.ballistica.net/cache/ba1/de/25/74be4875c2a0e22b813a4e1a103b",
"assets/build/ba_data/data/languages/belarussian.json": "https://files.ballistica.net/cache/ba1/49/5f/b29bb65369040892fe6601801637", "assets/build/ba_data/data/languages/belarussian.json": "https://files.ballistica.net/cache/ba1/c1/2b/54aeb92c709c4af443f4a9013b3d",
"assets/build/ba_data/data/languages/chinese.json": "https://files.ballistica.net/cache/ba1/c3/3f/c37ac3c65ac65f171af9313a502a", "assets/build/ba_data/data/languages/chinese.json": "https://files.ballistica.net/cache/ba1/69/cc/f8bdd1e83162481c6bf2a78cb5e0",
"assets/build/ba_data/data/languages/chinesetraditional.json": "https://files.ballistica.net/cache/ba1/5a/9e/e8cad6f08b2b19803ab20fdc80d0", "assets/build/ba_data/data/languages/chinesetraditional.json": "https://files.ballistica.net/cache/ba1/7c/b5/ddf2aedf7a7821b134d3663ae320",
"assets/build/ba_data/data/languages/croatian.json": "https://files.ballistica.net/cache/ba1/56/02/c22deb7174aabdcbffe1da23e484", "assets/build/ba_data/data/languages/croatian.json": "https://files.ballistica.net/cache/ba1/56/02/c22deb7174aabdcbffe1da23e484",
"assets/build/ba_data/data/languages/czech.json": "https://files.ballistica.net/cache/ba1/97/6c/61de67c477c98b7c4fddd22e6ae0", "assets/build/ba_data/data/languages/czech.json": "https://files.ballistica.net/cache/ba1/97/6c/61de67c477c98b7c4fddd22e6ae0",
"assets/build/ba_data/data/languages/danish.json": "https://files.ballistica.net/cache/ba1/3f/46/e4da3c1d2b0ebf916df55c608b28", "assets/build/ba_data/data/languages/danish.json": "https://files.ballistica.net/cache/ba1/3f/46/e4da3c1d2b0ebf916df55c608b28",
"assets/build/ba_data/data/languages/dutch.json": "https://files.ballistica.net/cache/ba1/d1/07/37b7adc3dbec7328d26c5325f212", "assets/build/ba_data/data/languages/dutch.json": "https://files.ballistica.net/cache/ba1/d1/07/37b7adc3dbec7328d26c5325f212",
"assets/build/ba_data/data/languages/english.json": "https://files.ballistica.net/cache/ba1/d4/5b/fbd64cf1846340db0606824568c2", "assets/build/ba_data/data/languages/english.json": "https://files.ballistica.net/cache/ba1/3b/0d/eb66b073295cacde00e5d054dbae",
"assets/build/ba_data/data/languages/esperanto.json": "https://files.ballistica.net/cache/ba1/6e/fd/685a4e1da031474d47a1d9eb2731", "assets/build/ba_data/data/languages/esperanto.json": "https://files.ballistica.net/cache/ba1/6e/fd/685a4e1da031474d47a1d9eb2731",
"assets/build/ba_data/data/languages/french.json": "https://files.ballistica.net/cache/ba1/e3/6d/63e78b12ae9d99e0929671f52c12", "assets/build/ba_data/data/languages/french.json": "https://files.ballistica.net/cache/ba1/e3/6d/63e78b12ae9d99e0929671f52c12",
"assets/build/ba_data/data/languages/german.json": "https://files.ballistica.net/cache/ba1/3f/62/4eedf8cfad2e5f7ff2136ec19277", "assets/build/ba_data/data/languages/german.json": "https://files.ballistica.net/cache/ba1/3f/62/4eedf8cfad2e5f7ff2136ec19277",
"assets/build/ba_data/data/languages/gibberish.json": "https://files.ballistica.net/cache/ba1/97/3a/b6746b7f89af424584c794c4d11f", "assets/build/ba_data/data/languages/gibberish.json": "https://files.ballistica.net/cache/ba1/93/31/66df9784a606600b7933bcb8ffa1",
"assets/build/ba_data/data/languages/greek.json": "https://files.ballistica.net/cache/ba1/a4/2c/8c43b338be2367441b87378e19c1", "assets/build/ba_data/data/languages/greek.json": "https://files.ballistica.net/cache/ba1/a4/2c/8c43b338be2367441b87378e19c1",
"assets/build/ba_data/data/languages/hindi.json": "https://files.ballistica.net/cache/ba1/d5/19/5e450e35b83fe68722330d03b896", "assets/build/ba_data/data/languages/hindi.json": "https://files.ballistica.net/cache/ba1/d5/19/5e450e35b83fe68722330d03b896",
"assets/build/ba_data/data/languages/hungarian.json": "https://files.ballistica.net/cache/ba1/87/2d/027aa239eb66ea8f496562f4fd83", "assets/build/ba_data/data/languages/hungarian.json": "https://files.ballistica.net/cache/ba1/87/2d/027aa239eb66ea8f496562f4fd83",
@ -442,12 +442,12 @@
"assets/build/ba_data/data/languages/korean.json": "https://files.ballistica.net/cache/ba1/0a/84/bbb6ed2abf66509406f534cbbb52", "assets/build/ba_data/data/languages/korean.json": "https://files.ballistica.net/cache/ba1/0a/84/bbb6ed2abf66509406f534cbbb52",
"assets/build/ba_data/data/languages/persian.json": "https://files.ballistica.net/cache/ba1/1e/67/f8d1d6579698c10af9da2ecb62d9", "assets/build/ba_data/data/languages/persian.json": "https://files.ballistica.net/cache/ba1/1e/67/f8d1d6579698c10af9da2ecb62d9",
"assets/build/ba_data/data/languages/polish.json": "https://files.ballistica.net/cache/ba1/88/4b/6745a1a58220772e259f0de51196", "assets/build/ba_data/data/languages/polish.json": "https://files.ballistica.net/cache/ba1/88/4b/6745a1a58220772e259f0de51196",
"assets/build/ba_data/data/languages/portuguese.json": "https://files.ballistica.net/cache/ba1/42/b5/7612cce15fe4555889585108b3ef", "assets/build/ba_data/data/languages/portuguese.json": "https://files.ballistica.net/cache/ba1/59/49/d75f2b9916541bd2be8c49a5171f",
"assets/build/ba_data/data/languages/romanian.json": "https://files.ballistica.net/cache/ba1/44/3c/7cc06ca8d5475e1687d0ed05bdbf", "assets/build/ba_data/data/languages/romanian.json": "https://files.ballistica.net/cache/ba1/44/3c/7cc06ca8d5475e1687d0ed05bdbf",
"assets/build/ba_data/data/languages/russian.json": "https://files.ballistica.net/cache/ba1/d4/bb/69c09648f60e36f35bd38be20cf8", "assets/build/ba_data/data/languages/russian.json": "https://files.ballistica.net/cache/ba1/d4/bb/69c09648f60e36f35bd38be20cf8",
"assets/build/ba_data/data/languages/serbian.json": "https://files.ballistica.net/cache/ba1/e7/d8/ace32888249fc8b8cca0e2edb48b", "assets/build/ba_data/data/languages/serbian.json": "https://files.ballistica.net/cache/ba1/e7/d8/ace32888249fc8b8cca0e2edb48b",
"assets/build/ba_data/data/languages/slovak.json": "https://files.ballistica.net/cache/ba1/b7/0a/fab820b96e7aa587ee56427ecdc2", "assets/build/ba_data/data/languages/slovak.json": "https://files.ballistica.net/cache/ba1/b7/0a/fab820b96e7aa587ee56427ecdc2",
"assets/build/ba_data/data/languages/spanish.json": "https://files.ballistica.net/cache/ba1/32/0e/ac0b8dcef065d7934a6bc30d7560", "assets/build/ba_data/data/languages/spanish.json": "https://files.ballistica.net/cache/ba1/2a/35/eab086a95c41287fa0a856e59fb6",
"assets/build/ba_data/data/languages/swedish.json": "https://files.ballistica.net/cache/ba1/50/9f/be006ba19be6a69a57837eb6dca0", "assets/build/ba_data/data/languages/swedish.json": "https://files.ballistica.net/cache/ba1/50/9f/be006ba19be6a69a57837eb6dca0",
"assets/build/ba_data/data/languages/turkish.json": "https://files.ballistica.net/cache/ba1/9a/8a/f7b2521c1904ffc83262dff1e11b", "assets/build/ba_data/data/languages/turkish.json": "https://files.ballistica.net/cache/ba1/9a/8a/f7b2521c1904ffc83262dff1e11b",
"assets/build/ba_data/data/languages/ukrainian.json": "https://files.ballistica.net/cache/ba1/26/62/a072404c02c576a5c3f09059b582", "assets/build/ba_data/data/languages/ukrainian.json": "https://files.ballistica.net/cache/ba1/26/62/a072404c02c576a5c3f09059b582",
@ -3932,24 +3932,24 @@
"assets/build/windows/Win32/ucrtbased.dll": "https://files.ballistica.net/cache/ba1/b5/85/f8b6d0558ddb87267f34254b1450", "assets/build/windows/Win32/ucrtbased.dll": "https://files.ballistica.net/cache/ba1/b5/85/f8b6d0558ddb87267f34254b1450",
"assets/build/windows/Win32/vc_redist.x86.exe": "https://files.ballistica.net/cache/ba1/1c/e1/4a1a2eddda2f4aebd5f8b64ab08e", "assets/build/windows/Win32/vc_redist.x86.exe": "https://files.ballistica.net/cache/ba1/1c/e1/4a1a2eddda2f4aebd5f8b64ab08e",
"assets/build/windows/Win32/vcruntime140d.dll": "https://files.ballistica.net/cache/ba1/50/8d/bc2600ac9491f1b14d659709451f", "assets/build/windows/Win32/vcruntime140d.dll": "https://files.ballistica.net/cache/ba1/50/8d/bc2600ac9491f1b14d659709451f",
"build/prefab/full/linux_x86_64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/57/c4/65b46cdf99edf156347ed3381223", "build/prefab/full/linux_x86_64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/29/71/f22b8e8297b5fcda6a055501dd9a",
"build/prefab/full/linux_x86_64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/24/06/49b1e54e764a57c0c925cd48d71e", "build/prefab/full/linux_x86_64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/e3/4b/cd188b8da52207463fdeb6176704",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/b1/e9/12e2965cd54d3cbc02f795222441", "build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/10/52/435cd568d1690c023b8c1aebafff",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/94/e8/4b12bc80273d1527e2a8210cf090", "build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/6d/b7/57677f9d991f046f6b784bdd6321",
"build/prefab/full/mac_x86_64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/b3/f2/25d7adfb561752522269fe0677e7", "build/prefab/full/mac_x86_64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/0c/77/4c9ff1fa0630936563f0f7c9fee4",
"build/prefab/full/mac_x86_64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/e4/37/aa7613de3f87a18a5f981f73786a", "build/prefab/full/mac_x86_64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/80/25/43012563e7b0db4a76c431fcbb40",
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/de/7a/54548afb27b6027ff7c10c41838f", "build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/b3/e5/e2da42b1e5a6031487333c72da70",
"build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/6b/f3/8e66e8bfdc5ca8fbe5b61838c7b8", "build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/e1/89/0491751bb4d33a3d3ffc758a6b20",
"build/prefab/full/windows_x86/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/a0/af/d65a065f6da183970c71c2f479d7", "build/prefab/full/windows_x86/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/3a/c5/623f2be3c9f061941c0ed1543588",
"build/prefab/full/windows_x86/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/38/bc/dc1d1354f9a81f9e0120a3de63a7", "build/prefab/full/windows_x86/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/47/54/9f7775a0cdf008be499e80d62a8f",
"build/prefab/full/windows_x86_server/debug/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/da/9a/0c9617ad9d98f912db70b91dfd20", "build/prefab/full/windows_x86_server/debug/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/2b/29/060806ab4ed3b47f3890b14a36e9",
"build/prefab/full/windows_x86_server/release/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/53/28/8e4c340f2accc1cf3dfb600a2981", "build/prefab/full/windows_x86_server/release/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/e4/7e/943f5f6a581e4fccfe763b20fd30",
"build/prefab/lib/linux_x86_64/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/80/d2/d1c6766cf07f4c4828bbd5899f79", "build/prefab/lib/linux_x86_64/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/80/d2/d1c6766cf07f4c4828bbd5899f79",
"build/prefab/lib/linux_x86_64/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/de/49/2cfc34ac856737d903954db5571b", "build/prefab/lib/linux_x86_64/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/de/49/2cfc34ac856737d903954db5571b",
"build/prefab/lib/linux_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/17/9a/fa66aafdf31fc9bdd92ce382c619", "build/prefab/lib/linux_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/17/9a/fa66aafdf31fc9bdd92ce382c619",
"build/prefab/lib/linux_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/86/de/7d9c9a2b7bba34c630130ed759c9", "build/prefab/lib/linux_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/86/de/7d9c9a2b7bba34c630130ed759c9",
"build/prefab/lib/mac_x86_64/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/7c/71/59aa912540741d36fca95baf0cdf", "build/prefab/lib/mac_x86_64/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/11/ca/ef8dc06a074606894db186954d49",
"build/prefab/lib/mac_x86_64/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/f7/0e/da229849278095b6dcf26432909a", "build/prefab/lib/mac_x86_64/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/24/4d/f64b747dbea2588e6ffe8d3e24e8",
"build/prefab/lib/mac_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/58/e2/e78c6993c58dae2a94aa1c36139b", "build/prefab/lib/mac_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/35/5f/e588c08ae05abeb233bb803c695b",
"build/prefab/lib/mac_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/8b/28/742e9c33d64ceeb5e1ff87da5577" "build/prefab/lib/mac_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/c6/a4/77a6648afb78e6b452bcccad2c8e"
} }

View File

@ -30,8 +30,8 @@
<w>achname</w> <w>achname</w>
<w>achs</w> <w>achs</w>
<w>acinstance</w> <w>acinstance</w>
<w>ack</w>
<w>ack'ed</w> <w>ack'ed</w>
<w>ack</w>
<w>acked</w> <w>acked</w>
<w>acks</w> <w>acks</w>
<w>acnt</w> <w>acnt</w>
@ -153,8 +153,8 @@
<w>bacommon</w> <w>bacommon</w>
<w>badguy</w> <w>badguy</w>
<w>bafoundation</w> <w>bafoundation</w>
<w>ballistica</w>
<w>ballistica's</w> <w>ballistica's</w>
<w>ballistica</w>
<w>ballisticacore</w> <w>ballisticacore</w>
<w>ballisticacorecb</w> <w>ballisticacorecb</w>
<w>bamaster</w> <w>bamaster</w>
@ -703,6 +703,7 @@
<w>filterlines</w> <w>filterlines</w>
<w>filterpath</w> <w>filterpath</w>
<w>filterpaths</w> <w>filterpaths</w>
<w>filterval</w>
<w>finalhash</w> <w>finalhash</w>
<w>finalmaterials</w> <w>finalmaterials</w>
<w>finfo</w> <w>finfo</w>
@ -801,8 +802,8 @@
<w>gamedata</w> <w>gamedata</w>
<w>gameinstance</w> <w>gameinstance</w>
<w>gamemap</w> <w>gamemap</w>
<w>gamepad</w>
<w>gamepad's</w> <w>gamepad's</w>
<w>gamepad</w>
<w>gamepadadvanced</w> <w>gamepadadvanced</w>
<w>gamepads</w> <w>gamepads</w>
<w>gamepadselect</w> <w>gamepadselect</w>
@ -1191,8 +1192,8 @@
<w>lsqlite</w> <w>lsqlite</w>
<w>lssl</w> <w>lssl</w>
<w>lstart</w> <w>lstart</w>
<w>lstr</w>
<w>lstr's</w> <w>lstr's</w>
<w>lstr</w>
<w>lstrs</w> <w>lstrs</w>
<w>lsval</w> <w>lsval</w>
<w>ltex</w> <w>ltex</w>
@ -1828,8 +1829,8 @@
<w>sessionname</w> <w>sessionname</w>
<w>sessionplayer</w> <w>sessionplayer</w>
<w>sessionplayers</w> <w>sessionplayers</w>
<w>sessionteam</w>
<w>sessionteam's</w> <w>sessionteam's</w>
<w>sessionteam</w>
<w>sessionteams</w> <w>sessionteams</w>
<w>sessiontype</w> <w>sessiontype</w>
<w>setactivity</w> <w>setactivity</w>
@ -2164,8 +2165,8 @@
<w>txtw</w> <w>txtw</w>
<w>typeargs</w> <w>typeargs</w>
<w>typecheck</w> <w>typecheck</w>
<w>typechecker</w>
<w>typechecker's</w> <w>typechecker's</w>
<w>typechecker</w>
<w>typedval</w> <w>typedval</w>
<w>typeshed</w> <w>typeshed</w>
<w>typestr</w> <w>typestr</w>

View File

@ -1,4 +1,4 @@
### 1.5.27 (20224) ### 1.5.27 (20231)
- Language functionality has been consolidated into a LanguageSubsystem object at ba.app.lang - Language functionality has been consolidated into a LanguageSubsystem object at ba.app.lang
- ba.get_valid_languages() is now an attr: ba.app.lang.available_languages - ba.get_valid_languages() is now an attr: ba.app.lang.available_languages
- Achievement functionality has been consolidated into an AchievementSubsystem object at ba.app.ach - Achievement functionality has been consolidated into an AchievementSubsystem object at ba.app.ach
@ -9,6 +9,8 @@
- Revamped tab-button functionality into a cleaner type-safe class (bastd.ui.tabs.TabRow) - Revamped tab-button functionality into a cleaner type-safe class (bastd.ui.tabs.TabRow)
- Split Gather-Window tabs out into individual classes for future improvements (bastd.ui.gather.*) - Split Gather-Window tabs out into individual classes for future improvements (bastd.ui.gather.*)
- Added the ability to disable ticket-purchasing UIs for builds (ba.app.allow_ticket_purchases) - Added the ability to disable ticket-purchasing UIs for builds (ba.app.allow_ticket_purchases)
- Reworked the public party gather section to perform better; it should no longer have to rebuild the list from scratch each time the UI is visited.
- Added a filter option to the public party list (sorry it has taken so long).
### 1.5.26 (20217) ### 1.5.26 (20217)
- Simplified licensing header on python scripts. - Simplified licensing header on python scripts.

View File

@ -27,7 +27,7 @@ The Ballistica project is the foundation for the next generation of [BombSquad](
* A: No, BombSquad is still BombSquad. 'Ballistica' is simply the new name for the engine/app-framework. This way it can also be used for other game/app projects without causing confusion (though that is mostly theoretical at this point). As a modder, the biggest changes you will notice is 'ba' prefixes in the API instead of 'bs' and naming that follows Python PEP8 standards (underscores and lowercase instead of camel-case). So `bs.playSound(mySound)` in the old system might look like `ba.playsound(my_sound)` in the new. You may also see the word 'BallisticaCore' show up various places, which in actual releases gets replaced by 'BombSquad'. * A: No, BombSquad is still BombSquad. 'Ballistica' is simply the new name for the engine/app-framework. This way it can also be used for other game/app projects without causing confusion (though that is mostly theoretical at this point). As a modder, the biggest changes you will notice is 'ba' prefixes in the API instead of 'bs' and naming that follows Python PEP8 standards (underscores and lowercase instead of camel-case). So `bs.playSound(mySound)` in the old system might look like `ba.playsound(my_sound)` in the new. You may also see the word 'BallisticaCore' show up various places, which in actual releases gets replaced by 'BombSquad'.
* **Q: Does this mean BombSquad is open source?** * **Q: Does this mean BombSquad is open source?**
* A: Yes and no. All code contained in this repo is MIT licensed and free for use anywhere. This includes game scripts, pipeline tools, etc. Over time I hope to expand this to include at least some of the binary engine sources. Anything not directly contained in this repository, however, even if automatically downloaded by build scripts, is still proprietary and cannot be redistributed without explicit consent. This includes assets and game binaries. So in a nutshell: create and share mods to your heart's content, but please don't distribute your own complete copies of the game without permission. Please email support@froemling.net if you have any questions about this. * A: Yes and no. All code contained in this repo is MIT licensed and free for use anywhere. This includes game scripts, pipeline tools, and most of the binary engine sources. Anything not directly contained in this repository, however, even if automatically downloaded by build scripts, is still proprietary and cannot be redistributed without explicit consent. This includes assets and game libraries/binaries. So in a nutshell: create and share mods to your heart's content, but please don't distribute your own complete copies of the game without permission. Please email support@froemling.net if you have any questions about this.
* **Q: Will my existing BombSquad 1.4.x mods still work?** * **Q: Will my existing BombSquad 1.4.x mods still work?**
* A: Not 'out of the box'. All mods will need to be explicitly updated to work with the new ballistica apis in 1.5+. This may or may not be a significant amount of work depending on the mod. I would highly suggest tinkering around with some of the new features in 1.5 such as type-safe Python and dynamic assets before attempting to port any old mods, as some things are done significantly differently now. You may also want to consider simply sticking with 1.4 builds for a while longer, especially for server duties, since they will remain fully compatible with clients running 1.5. The new ballistica APIs may be changing significantly for at least a while as the dust settles, but they will be worth switching to in the end, I promise! * A: Not 'out of the box'. All mods will need to be explicitly updated to work with the new ballistica apis in 1.5+. This may or may not be a significant amount of work depending on the mod. I would highly suggest tinkering around with some of the new features in 1.5 such as type-safe Python and dynamic assets before attempting to port any old mods, as some things are done significantly differently now. You may also want to consider simply sticking with 1.4 builds for a while longer, especially for server duties, since they will remain fully compatible with clients running 1.5. The new ballistica APIs may be changing significantly for at least a while as the dust settles, but they will be worth switching to in the end, I promise!

View File

@ -2622,6 +2622,7 @@ def hscrollwidget(edit: ba.Widget = None,
border_opacity: float = None, border_opacity: float = None,
simple_culling_h: float = None, simple_culling_h: float = None,
claims_left_right: bool = None, claims_left_right: bool = None,
claims_up_down: bool = None,
claims_tab: bool = None) -> ba.Widget: claims_tab: bool = None) -> ba.Widget:
"""hscrollwidget(edit: ba.Widget = None, parent: ba.Widget = None, """hscrollwidget(edit: ba.Widget = None, parent: ba.Widget = None,
size: Sequence[float] = None, position: Sequence[float] = None, size: Sequence[float] = None, position: Sequence[float] = None,
@ -2632,6 +2633,7 @@ def hscrollwidget(edit: ba.Widget = None,
highlight: bool = None, border_opacity: float = None, highlight: bool = None, border_opacity: float = None,
simple_culling_h: float = None, simple_culling_h: float = None,
claims_left_right: bool = None, claims_left_right: bool = None,
claims_up_down: bool = None,
claims_tab: bool = None) -> ba.Widget claims_tab: bool = None) -> ba.Widget
Create or edit a horizontal scroll widget. Create or edit a horizontal scroll widget.
@ -3388,6 +3390,7 @@ def scrollwidget(edit: ba.Widget = None,
simple_culling_v: float = None, simple_culling_v: float = None,
selection_loops_to_parent: bool = None, selection_loops_to_parent: bool = None,
claims_left_right: bool = None, claims_left_right: bool = None,
claims_up_down: bool = None,
claims_tab: bool = None, claims_tab: bool = None,
autoselect: bool = None) -> ba.Widget: autoselect: bool = None) -> ba.Widget:
"""scrollwidget(edit: ba.Widget = None, parent: ba.Widget = None, """scrollwidget(edit: ba.Widget = None, parent: ba.Widget = None,
@ -3399,6 +3402,7 @@ def scrollwidget(edit: ba.Widget = None,
simple_culling_v: float = None, simple_culling_v: float = None,
selection_loops_to_parent: bool = None, selection_loops_to_parent: bool = None,
claims_left_right: bool = None, claims_left_right: bool = None,
claims_up_down: bool = None,
claims_tab: bool = None, claims_tab: bool = None,
autoselect: bool = None) -> ba.Widget autoselect: bool = None) -> ba.Widget

View File

@ -45,6 +45,7 @@ class MainMenuActivity(ba.Activity[ba.Player, ba.Team]):
if not ba.app.toolbar_test: if not ba.app.toolbar_test:
color = ((1.0, 1.0, 1.0, 1.0) if vr_mode else (0.5, 0.6, 0.5, 0.6)) color = ((1.0, 1.0, 1.0, 1.0) if vr_mode else (0.5, 0.6, 0.5, 0.6))
# FIXME: Need a node attr for vr-specific-scale. # FIXME: Need a node attr for vr-specific-scale.
scale = (0.9 if scale = (0.9 if
(app.ui.uiscale is ba.UIScale.SMALL or vr_mode) else 0.7) (app.ui.uiscale is ba.UIScale.SMALL or vr_mode) else 0.7)

View File

@ -20,6 +20,7 @@ if TYPE_CHECKING:
from typing import Callable, Any, Optional, Dict, Union, Tuple, List from typing import Callable, Any, Optional, Dict, Union, Tuple, List
from bastd.ui.gather import GatherWindow from bastd.ui.gather import GatherWindow
# Print a bit of info about pings, queries, etc.
DEBUG_SERVER_COMMUNICATION = False DEBUG_SERVER_COMMUNICATION = False
@ -58,6 +59,7 @@ class State:
sub_tab: SubTabType = SubTabType.JOIN sub_tab: SubTabType = SubTabType.JOIN
parties: Optional[List[PartyEntry]] = None parties: Optional[List[PartyEntry]] = None
next_entry_index: int = 0 next_entry_index: int = 0
filter_value: str = ''
class SelectionComponent(Enum): class SelectionComponent(Enum):
@ -186,6 +188,7 @@ class PublicGatherTab(GatherTab):
self._container: Optional[ba.Widget] = None self._container: Optional[ba.Widget] = None
self._join_text: Optional[ba.Widget] = None self._join_text: Optional[ba.Widget] = None
self._host_text: Optional[ba.Widget] = None self._host_text: Optional[ba.Widget] = None
self._filter_text: Optional[ba.Widget] = None
self._local_address: Optional[str] = None self._local_address: Optional[str] = None
self._last_connect_attempt_time: Optional[float] = None self._last_connect_attempt_time: Optional[float] = None
self._sub_tab: SubTabType = SubTabType.JOIN self._sub_tab: SubTabType = SubTabType.JOIN
@ -207,7 +210,8 @@ class PublicGatherTab(GatherTab):
self._first_server_list_rebuild_time: Optional[float] = None self._first_server_list_rebuild_time: Optional[float] = None
self._next_entry_index = 0 self._next_entry_index = 0
self._have_valid_server_list = False self._have_valid_server_list = False
self._built_join_list = False self._server_list_dirty = True
self._filter_value = ''
def on_activate( def on_activate(
self, self,
@ -278,7 +282,6 @@ class PublicGatherTab(GatherTab):
if self._local_address is None: if self._local_address is None:
AddrFetchThread(ba.WeakCall(self._fetch_local_addr_cb)).start() AddrFetchThread(ba.WeakCall(self._fetch_local_addr_cb)).start()
assert self._sub_tab is not None
self._set_sub_tab(self._sub_tab, region_width, region_height) self._set_sub_tab(self._sub_tab, region_width, region_height)
self._update_timer = ba.Timer(0.2, self._update_timer = ba.Timer(0.2,
ba.WeakCall(self._update), ba.WeakCall(self._update),
@ -292,13 +295,15 @@ class PublicGatherTab(GatherTab):
def save_state(self) -> None: def save_state(self) -> None:
# Save off a small number of parties with the lowest ping; this # Save off a small number of parties with the lowest ping; we'll
# should be most of the ones that matter and will keep things # display these immediately when our UI comes back up which should
# a reasonable size. # be enough to make things feel nice and crisp while we do a full
# server re-query or whatnot.
ba.app.ui.window_states[self.__class__.__name__] = State( ba.app.ui.window_states[self.__class__.__name__] = State(
sub_tab=self._sub_tab, sub_tab=self._sub_tab,
parties=[copy.copy(p) for p in self._get_ordered_parties()[:20]], parties=[copy.copy(p) for p in self._get_ordered_parties()[:40]],
next_entry_index=self._next_entry_index) next_entry_index=self._next_entry_index,
filter_value=self._filter_value)
def restore_state(self) -> None: def restore_state(self) -> None:
state = ba.app.ui.window_states.get(self.__class__.__name__) state = ba.app.ui.window_states.get(self.__class__.__name__)
@ -307,7 +312,7 @@ class PublicGatherTab(GatherTab):
assert isinstance(state, State) assert isinstance(state, State)
self._sub_tab = state.sub_tab self._sub_tab = state.sub_tab
# Restore the parties we stored... # Restore the parties we stored.
if state.parties: if state.parties:
self._parties = { self._parties = {
f'{p.address}_{p.port}': copy.copy(p) f'{p.address}_{p.port}': copy.copy(p)
@ -315,6 +320,7 @@ class PublicGatherTab(GatherTab):
} }
self._next_entry_index = state.next_entry_index self._next_entry_index = state.next_entry_index
self._have_valid_server_list = True self._have_valid_server_list = True
self._filter_value = state.filter_value
def _set_sub_tab(self, def _set_sub_tab(self,
value: SubTabType, value: SubTabType,
@ -346,16 +352,9 @@ class PublicGatherTab(GatherTab):
if widget and widget not in {self._host_text, self._join_text}: if widget and widget not in {self._host_text, self._join_text}:
widget.delete() widget.delete()
c_width = region_width
c_height = region_height - 20
sub_scroll_height = c_height - 90
sub_scroll_width = 830
v = c_height - 35
v -= 25
if value is SubTabType.JOIN: if value is SubTabType.JOIN:
self._build_join_tab(v, sub_scroll_width, sub_scroll_height, self._build_join_tab(region_width, region_height)
c_width, c_height) self._server_list_dirty = True
self._built_join_list = False
# If we've not yet successfully fetched a server list, # If we've not yet successfully fetched a server list,
# force an attempt now and show the user a 'loading...' status. # force an attempt now and show the user a 'loading...' status.
@ -372,12 +371,36 @@ class PublicGatherTab(GatherTab):
ba.textwidget(edit=self._join_status_text, text=join_status_str) ba.textwidget(edit=self._join_status_text, text=join_status_str)
if value is SubTabType.HOST: if value is SubTabType.HOST:
self._build_host_tab(v, sub_scroll_width, sub_scroll_height, self._build_host_tab(region_width, region_height)
c_width, c_height)
def _build_join_tab(self, region_width: float,
region_height: float) -> None:
c_width = region_width
c_height = region_height - 20
sub_scroll_height = c_height - 125
sub_scroll_width = 830
v = c_height - 35
v -= 60
self._filter_text = ba.textwidget(parent=self._container,
text=self._filter_value,
size=(350, 45),
position=(290, v - 10),
h_align='left',
v_align='center',
editable=True)
ba.widget(edit=self._filter_text, up_widget=self._join_text)
ba.textwidget(text=ba.Lstr(resource='filterText'),
parent=self._container,
size=(0, 0),
position=(270, v + 15),
maxwidth=100,
scale=0.8,
color=(0.5, 0.5, 0.5),
flatness=1.0,
shadow=0.0,
h_align='right',
v_align='center')
def _build_join_tab(self, v: float, sub_scroll_width: float,
sub_scroll_height: float, c_width: float,
c_height: float) -> None:
ba.textwidget(text=ba.Lstr(resource='nameText'), ba.textwidget(text=ba.Lstr(resource='nameText'),
parent=self._container, parent=self._container,
size=(0, 0), size=(0, 0),
@ -412,19 +435,18 @@ class PublicGatherTab(GatherTab):
h_align='center', h_align='center',
v_align='center') v_align='center')
v -= sub_scroll_height + 23 v -= sub_scroll_height + 23
self._host_scrollwidget = scrollw = ba.scrollwidget( self._host_scrollwidget = scrollw = ba.scrollwidget(
parent=self._container, parent=self._container,
simple_culling_v=10, simple_culling_v=10,
position=((c_width - sub_scroll_width) * 0.5, v), position=((c_width - sub_scroll_width) * 0.5, v),
size=(sub_scroll_width, sub_scroll_height), size=(sub_scroll_width, sub_scroll_height),
claims_up_down=False,
claims_left_right=True, claims_left_right=True,
autoselect=True) autoselect=True)
self._join_list_column = ba.containerwidget(parent=scrollw, self._join_list_column = ba.containerwidget(parent=scrollw,
background=False, background=False,
size=(400, 400), size=(400, 400),
claims_left_right=True) claims_left_right=True)
self._join_status_text = ba.textwidget(parent=self._container, self._join_status_text = ba.textwidget(parent=self._container,
text='', text='',
size=(0, 0), size=(0, 0),
@ -438,10 +460,12 @@ class PublicGatherTab(GatherTab):
position=(c_width * 0.5, position=(c_width * 0.5,
c_height * 0.5)) c_height * 0.5))
def _build_host_tab(self, v: float, sub_scroll_width: float, def _build_host_tab(self, region_width: float,
sub_scroll_height: float, c_width: float, region_height: float) -> None:
c_height: float) -> None: c_width = region_width
del sub_scroll_width, sub_scroll_height, c_height # Unused. c_height = region_height - 20
v = c_height - 35
v -= 25
is_public_enabled = _ba.get_public_party_enabled() is_public_enabled = _ba.get_public_party_enabled()
v -= 30 v -= 30
party_name_text = ba.Lstr( party_name_text = ba.Lstr(
@ -584,7 +608,7 @@ class PublicGatherTab(GatherTab):
since_first = cur_time - self._first_server_list_rebuild_time since_first = cur_time - self._first_server_list_rebuild_time
wait_time = (1.0 if since_first < 2.0 else wait_time = (1.0 if since_first < 2.0 else
2.5 if since_first < 10.0 else 5.0) 2.5 if since_first < 10.0 else 5.0)
if (self._built_join_list if (not self._server_list_dirty
and self._last_server_list_update_time is not None and self._last_server_list_update_time is not None
and cur_time - self._last_server_list_update_time < wait_time): and cur_time - self._last_server_list_update_time < wait_time):
return return
@ -595,7 +619,7 @@ class PublicGatherTab(GatherTab):
return return
self._last_server_list_update_time = cur_time self._last_server_list_update_time = cur_time
self._built_join_list = True self._server_list_dirty = False
with ba.Context('ui'): with ba.Context('ui'):
@ -605,6 +629,14 @@ class PublicGatherTab(GatherTab):
ordered_parties = self._get_ordered_parties() ordered_parties = self._get_ordered_parties()
# If we've got a filter, filter them.
if self._filter_value:
# Let's do case-insensitive searching.
filterval = self._filter_value.lower()
ordered_parties = [
p for p in ordered_parties if filterval in p.name.lower()
]
sub_scroll_width = 830 sub_scroll_width = 830
lineheight = 42 lineheight = 42
sub_scroll_height = lineheight * len(ordered_parties) + 50 sub_scroll_height = lineheight * len(ordered_parties) + 50
@ -699,10 +731,10 @@ class PublicGatherTab(GatherTab):
if first: if first:
if party.stats_button: if party.stats_button:
ba.widget(edit=party.stats_button, ba.widget(edit=party.stats_button,
up_widget=self._join_text) up_widget=self._filter_text)
if party.name_widget: if party.name_widget:
ba.widget(edit=party.name_widget, ba.widget(edit=party.name_widget,
up_widget=self._join_text) up_widget=self._filter_text)
first = False first = False
party.size_widget = ba.textwidget( party.size_widget = ba.textwidget(
@ -820,54 +852,70 @@ class PublicGatherTab(GatherTab):
_ba.set_public_party_name(name) _ba.set_public_party_name(name)
if self._sub_tab is SubTabType.JOIN: if self._sub_tab is SubTabType.JOIN:
now = ba.time(ba.TimeType.REAL)
# Fire off a new public-party query periodically. # If our filter value has changed, refresh the list
if (self._last_server_list_query_time is None # using the new one.
or now - self._last_server_list_query_time > 0.001 * text = self._filter_text
_ba.get_account_misc_read_val('pubPartyRefreshMS', 10000)): if text:
self._last_server_list_query_time = now filter_value = cast(str, ba.textwidget(query=text))
if filter_value != self._filter_value:
self._filter_value = filter_value
self._server_list_dirty = True
self._update_server_list()
self._query_party_list_periodically()
self._ping_parties_periodically()
def _query_party_list_periodically(self) -> None:
now = ba.time(ba.TimeType.REAL)
# Fire off a new public-party query periodically.
if (self._last_server_list_query_time is None
or now - self._last_server_list_query_time > 0.001 *
_ba.get_account_misc_read_val('pubPartyRefreshMS', 10000)):
self._last_server_list_query_time = now
if DEBUG_SERVER_COMMUNICATION:
print('REQUESTING SERVER LIST')
_ba.add_transaction(
{
'type': 'PUBLIC_PARTY_QUERY',
'proto': ba.app.protocol_version,
'lang': ba.app.lang.language
},
callback=ba.WeakCall(self._on_public_party_query_result))
_ba.run_transactions()
def _ping_parties_periodically(self) -> None:
now = ba.time(ba.TimeType.REAL)
# Go through our existing public party entries firing off pings
# for any that have timed out.
for party in list(self._parties.values()):
if party.next_ping_time <= now and ba.app.ping_thread_count < 15:
# Crank the interval up for high-latency or non-responding
# parties to save us some useless work.
mult = 1
if party.ping_responses == 0:
if party.ping_attempts > 4:
mult = 10
elif party.ping_attempts > 2:
mult = 5
if party.ping is not None:
mult = (10 if party.ping > 300 else
5 if party.ping > 150 else 2)
interval = party.ping_interval * mult
if DEBUG_SERVER_COMMUNICATION: if DEBUG_SERVER_COMMUNICATION:
print('REQUESTING SERVER LIST') print(f'pinging #{party.index} cur={party.ping} '
_ba.add_transaction( f'interval={interval} '
{ f'({party.ping_responses}/{party.ping_attempts})')
'type': 'PUBLIC_PARTY_QUERY',
'proto': ba.app.protocol_version,
'lang': ba.app.lang.language
},
callback=ba.WeakCall(self._on_public_party_query_result))
_ba.run_transactions()
# Go through our existing public party entries firing off pings party.next_ping_time = now + party.ping_interval * mult
# for any that have timed out. party.ping_attempts += 1
for party in list(self._parties.values()):
if (party.next_ping_time <= now
and ba.app.ping_thread_count < 15):
# Crank the interval up for high-latency or non-responding PingThread(party.address, party.port,
# parties to save us some useless work. ba.WeakCall(self._ping_callback)).start()
mult = 1
if party.ping_responses == 0:
if party.ping_attempts > 4:
mult = 10
elif party.ping_attempts > 2:
mult = 5
if party.ping is not None:
mult = (10 if party.ping > 300 else
5 if party.ping > 150 else 2)
interval = party.ping_interval * mult
if DEBUG_SERVER_COMMUNICATION:
print(
f'pinging #{party.index} cur={party.ping} '
f'interval={interval} '
f'({party.ping_responses}/{party.ping_attempts})')
party.next_ping_time = now + party.ping_interval * mult
party.ping_attempts += 1
PingThread(party.address, party.port,
ba.WeakCall(self._ping_callback)).start()
def _ping_callback(self, address: str, port: Optional[int], def _ping_callback(self, address: str, port: Optional[int],
result: Optional[int]) -> None: result: Optional[int]) -> None:

View File

@ -38,8 +38,11 @@ class MainMenuWindow(ba.Window):
toolbar_visibility='menu_minimal_no_back' if self. toolbar_visibility='menu_minimal_no_back' if self.
_in_game else 'menu_minimal_no_back')) _in_game else 'menu_minimal_no_back'))
# Grab this stuff in case it changes.
self._is_demo = ba.app.demo_mode self._is_demo = ba.app.demo_mode
self._is_arcade = ba.app.arcade_mode self._is_arcade = ba.app.arcade_mode
self._is_iircade = ba.app.iircade_mode
self._tdelay = 0.0 self._tdelay = 0.0
self._t_delay_inc = 0.02 self._t_delay_inc = 0.02
self._t_delay_play = 1.7 self._t_delay_play = 1.7
@ -179,7 +182,7 @@ class MainMenuWindow(ba.Window):
self._have_settings_button = ( self._have_settings_button = (
(not self._in_game or not app.toolbar_test) (not self._in_game or not app.toolbar_test)
and not (self._is_demo or self._is_arcade)) and not (self._is_demo or self._is_arcade or self._is_iircade))
self._input_device = input_device = _ba.get_ui_input_device() self._input_device = input_device = _ba.get_ui_input_device()
self._input_player = input_device.player if input_device else None self._input_player = input_device.player if input_device else None
@ -436,7 +439,9 @@ class MainMenuWindow(ba.Window):
account_type_icon_color = (1.0, 1.0, 1.0) account_type_icon_color = (1.0, 1.0, 1.0)
account_type_call = self._show_account_window account_type_call = self._show_account_window
account_type_enable_button_sound = True account_type_enable_button_sound = True
b_count = 4 # play, help, credits, settings b_count = 3 # play, help, credits
if self._have_settings_button:
b_count += 1
if enable_account_button: if enable_account_button:
b_count += 1 b_count += 1
if self._have_quit_button: if self._have_quit_button:

View File

@ -209,15 +209,6 @@ class AllSettingsWindow(ba.Window):
# pylint: disable=cyclic-import # pylint: disable=cyclic-import
from bastd.ui.settings.controls import ControlsSettingsWindow from bastd.ui.settings.controls import ControlsSettingsWindow
self._save_state() self._save_state()
# Disallow this on iircade.
if ba.app.iircade_mode:
ba.playsound(ba.getsound('error'))
ba.screenmessage(
ba.Lstr(resource='getTicketsWindow.unavailableText'),
color=(1, 0, 0))
return
ba.containerwidget(edit=self._root_widget, transition='out_left') ba.containerwidget(edit=self._root_widget, transition='out_left')
ba.app.ui.set_main_menu_window( ba.app.ui.set_main_menu_window(
ControlsSettingsWindow( ControlsSettingsWindow(
@ -227,15 +218,6 @@ class AllSettingsWindow(ba.Window):
# pylint: disable=cyclic-import # pylint: disable=cyclic-import
from bastd.ui.settings.graphics import GraphicsSettingsWindow from bastd.ui.settings.graphics import GraphicsSettingsWindow
self._save_state() self._save_state()
# Disallow this on iircade.
if ba.app.iircade_mode:
ba.playsound(ba.getsound('error'))
ba.screenmessage(
ba.Lstr(resource='getTicketsWindow.unavailableText'),
color=(1, 0, 0))
return
ba.containerwidget(edit=self._root_widget, transition='out_left') ba.containerwidget(edit=self._root_widget, transition='out_left')
ba.app.ui.set_main_menu_window( ba.app.ui.set_main_menu_window(
GraphicsSettingsWindow( GraphicsSettingsWindow(

View File

@ -13,8 +13,8 @@
<w>ack'ed</w> <w>ack'ed</w>
<w>acked</w> <w>acked</w>
<w>acks</w> <w>acks</w>
<w>aclass</w>
<w>aclass's</w> <w>aclass's</w>
<w>aclass</w>
<w>activityplayer</w> <w>activityplayer</w>
<w>addrs</w> <w>addrs</w>
<w>adjoint</w> <w>adjoint</w>
@ -146,8 +146,8 @@
<w>cmath</w> <w>cmath</w>
<w>cmds</w> <w>cmds</w>
<w>cmdvals</w> <w>cmdvals</w>
<w>codewarrior</w>
<w>codewarrior's</w> <w>codewarrior's</w>
<w>codewarrior</w>
<w>cofnodes</w> <w>cofnodes</w>
<w>collapseable</w> <w>collapseable</w>
<w>collidable</w> <w>collidable</w>
@ -287,6 +287,7 @@
<w>fffffffffifff</w> <w>fffffffffifff</w>
<w>fgets</w> <w>fgets</w>
<w>fifteenbits</w> <w>fifteenbits</w>
<w>filterval</w>
<w>finishedptr</w> <w>finishedptr</w>
<w>fjco</w> <w>fjco</w>
<w>fjcoiwef</w> <w>fjcoiwef</w>

View File

@ -1,5 +1,5 @@
<!-- THIS FILE IS AUTO GENERATED; DO NOT EDIT BY HAND --> <!-- THIS FILE IS AUTO GENERATED; DO NOT EDIT BY HAND -->
<h4><em>last updated on 2020-10-27 for Ballistica version 1.5.27 build 20228</em></h4> <h4><em>last updated on 2020-10-29 for Ballistica version 1.5.27 build 20230</em></h4>
<p>This page documents the Python classes and functions in the 'ba' module, <p>This page documents the Python classes and functions in the 'ba' module,
which are the ones most relevant to modding in Ballistica. If you come across something you feel should be included here or could be better explained, please <a href="mailto:support@froemling.net">let me know</a>. Happy modding!</p> which are the ones most relevant to modding in Ballistica. If you come across something you feel should be included here or could be better explained, please <a href="mailto:support@froemling.net">let me know</a>. Happy modding!</p>
<hr> <hr>
@ -6760,6 +6760,7 @@ in the background if necessary.</p>
highlight: bool = None, border_opacity: float = None, highlight: bool = None, border_opacity: float = None,
simple_culling_h: float = None, simple_culling_h: float = None,
claims_left_right: bool = None, claims_left_right: bool = None,
claims_up_down: bool = None,
claims_tab: bool = None) -&gt; <a href="#class_ba_Widget">ba.Widget</a></span></p> claims_tab: bool = None) -&gt; <a href="#class_ba_Widget">ba.Widget</a></span></p>
<p>Create or edit a horizontal scroll widget.</p> <p>Create or edit a horizontal scroll widget.</p>
@ -7036,6 +7037,7 @@ Currently the 'clients' option only works for transient messages.</p>
simple_culling_v: float = None, simple_culling_v: float = None,
selection_loops_to_parent: bool = None, selection_loops_to_parent: bool = None,
claims_left_right: bool = None, claims_left_right: bool = None,
claims_up_down: bool = None,
claims_tab: bool = None, claims_tab: bool = None,
autoselect: bool = None) -&gt; <a href="#class_ba_Widget">ba.Widget</a></span></p> autoselect: bool = None) -&gt; <a href="#class_ba_Widget">ba.Widget</a></span></p>

View File

@ -21,7 +21,7 @@
namespace ballistica { namespace ballistica {
// These are set automatically via script; don't change here. // These are set automatically via script; don't change here.
const int kAppBuildNumber = 20229; const int kAppBuildNumber = 20232;
const char* kAppVersion = "1.5.27"; const char* kAppVersion = "1.5.27";
// Our standalone globals. // Our standalone globals.

View File

@ -1179,6 +1179,7 @@ auto PyScrollWidget(PyObject* self, PyObject* args, PyObject* keywds)
PyObject* simple_culling_v_obj{Py_None}; PyObject* simple_culling_v_obj{Py_None};
PyObject* selection_loops_to_parent_obj{Py_None}; PyObject* selection_loops_to_parent_obj{Py_None};
PyObject* claims_left_right_obj{Py_None}; PyObject* claims_left_right_obj{Py_None};
PyObject* claims_up_down_obj{Py_None};
PyObject* claims_tab_obj{Py_None}; PyObject* claims_tab_obj{Py_None};
PyObject* autoselect_obj{Py_None}; PyObject* autoselect_obj{Py_None};
@ -1197,18 +1198,19 @@ auto PyScrollWidget(PyObject* self, PyObject* args, PyObject* keywds)
"simple_culling_v", "simple_culling_v",
"selection_loops_to_parent", "selection_loops_to_parent",
"claims_left_right", "claims_left_right",
"claims_up_down",
"claims_tab", "claims_tab",
"autoselect", "autoselect",
nullptr}; nullptr};
if (!PyArg_ParseTupleAndKeywords( if (!PyArg_ParseTupleAndKeywords(
args, keywds, "|OOOOOOOOOOOOOOOOO", const_cast<char**>(kwlist), args, keywds, "|OOOOOOOOOOOOOOOOOO", const_cast<char**>(kwlist),
&edit_obj, &parent_obj, &size_obj, &pos_obj, &background_obj, &edit_obj, &parent_obj, &size_obj, &pos_obj, &background_obj,
&selected_child_obj, &capture_arrows_obj, &on_select_call_obj, &selected_child_obj, &capture_arrows_obj, &on_select_call_obj,
&center_small_content_obj, &color_obj, &highlight_obj, &center_small_content_obj, &color_obj, &highlight_obj,
&border_opacity_obj, &simple_culling_v_obj, &border_opacity_obj, &simple_culling_v_obj,
&selection_loops_to_parent_obj, &claims_left_right_obj, &selection_loops_to_parent_obj, &claims_left_right_obj,
&claims_tab_obj, &autoselect_obj)) &claims_up_down_obj, &claims_tab_obj, &autoselect_obj))
return nullptr; return nullptr;
if (!g_game->IsInUIContext()) { if (!g_game->IsInUIContext()) {
@ -1287,6 +1289,9 @@ auto PyScrollWidget(PyObject* self, PyObject* args, PyObject* keywds)
if (claims_left_right_obj != Py_None) { if (claims_left_right_obj != Py_None) {
widget->set_claims_left_right(Python::GetPyBool(claims_left_right_obj)); widget->set_claims_left_right(Python::GetPyBool(claims_left_right_obj));
} }
if (claims_up_down_obj != Py_None) {
widget->set_claims_up_down(Python::GetPyBool(claims_up_down_obj));
}
if (claims_tab_obj != Py_None) { if (claims_tab_obj != Py_None) {
widget->set_claims_tab(Python::GetPyBool(claims_tab_obj)); widget->set_claims_tab(Python::GetPyBool(claims_tab_obj));
} }
@ -1324,6 +1329,7 @@ auto PyHScrollWidget(PyObject* self, PyObject* args, PyObject* keywds)
PyObject* border_opacity_obj = Py_None; PyObject* border_opacity_obj = Py_None;
PyObject* simple_culling_h_obj = Py_None; PyObject* simple_culling_h_obj = Py_None;
PyObject* claims_left_right_obj = Py_None; PyObject* claims_left_right_obj = Py_None;
PyObject* claims_up_down_obj = Py_None;
PyObject* claims_tab_obj = Py_None; PyObject* claims_tab_obj = Py_None;
PyObject* autoselect_obj = Py_None; PyObject* autoselect_obj = Py_None;
@ -1341,6 +1347,7 @@ auto PyHScrollWidget(PyObject* self, PyObject* args, PyObject* keywds)
"border_opacity", "border_opacity",
"simple_culling_h", "simple_culling_h",
"claims_left_right", "claims_left_right",
"claims_up_down",
"claims_tab", "claims_tab",
"autoselect", "autoselect",
nullptr}; nullptr};
@ -1351,7 +1358,7 @@ auto PyHScrollWidget(PyObject* self, PyObject* args, PyObject* keywds)
&selected_child_obj, &capture_arrows_obj, &on_select_call_obj, &selected_child_obj, &capture_arrows_obj, &on_select_call_obj,
&center_small_content_obj, &color_obj, &highlight_obj, &center_small_content_obj, &color_obj, &highlight_obj,
&border_opacity_obj, &simple_culling_h_obj, &claims_left_right_obj, &border_opacity_obj, &simple_culling_h_obj, &claims_left_right_obj,
&claims_tab_obj, &autoselect_obj)) &claims_up_down_obj, &claims_tab_obj, &autoselect_obj))
return nullptr; return nullptr;
if (!g_game->IsInUIContext()) { if (!g_game->IsInUIContext()) {
@ -1425,6 +1432,9 @@ auto PyHScrollWidget(PyObject* self, PyObject* args, PyObject* keywds)
if (claims_left_right_obj != Py_None) { if (claims_left_right_obj != Py_None) {
widget->set_claims_left_right(Python::GetPyBool(claims_left_right_obj)); widget->set_claims_left_right(Python::GetPyBool(claims_left_right_obj));
} }
if (claims_up_down_obj != Py_None) {
widget->set_claims_up_down(Python::GetPyBool(claims_up_down_obj));
}
if (claims_tab_obj != Py_None) { if (claims_tab_obj != Py_None) {
widget->set_claims_tab(Python::GetPyBool(claims_tab_obj)); widget->set_claims_tab(Python::GetPyBool(claims_tab_obj));
} }
@ -2643,6 +2653,7 @@ PyMethodDef PythonMethodsUI::methods_def[] = {
" simple_culling_v: float = None,\n" " simple_culling_v: float = None,\n"
" selection_loops_to_parent: bool = None,\n" " selection_loops_to_parent: bool = None,\n"
" claims_left_right: bool = None,\n" " claims_left_right: bool = None,\n"
" claims_up_down: bool = None,\n"
" claims_tab: bool = None,\n" " claims_tab: bool = None,\n"
" autoselect: bool = None) -> ba.Widget\n" " autoselect: bool = None) -> ba.Widget\n"
"\n" "\n"
@ -2665,6 +2676,7 @@ PyMethodDef PythonMethodsUI::methods_def[] = {
" highlight: bool = None, border_opacity: float = None,\n" " highlight: bool = None, border_opacity: float = None,\n"
" simple_culling_h: float = None,\n" " simple_culling_h: float = None,\n"
" claims_left_right: bool = None,\n" " claims_left_right: bool = None,\n"
" claims_up_down: bool = None,\n"
" claims_tab: bool = None) -> ba.Widget\n" " claims_tab: bool = None) -> ba.Widget\n"
"\n" "\n"
"Create or edit a horizontal scroll widget.\n" "Create or edit a horizontal scroll widget.\n"

View File

@ -301,6 +301,7 @@ void TextWidget::Draw(RenderPass* pass, bool draw_transparent) {
if (text_group_dirty_) { if (text_group_dirty_) {
text_group_->SetText(text_translated_, align_h, align_v, big_, res_scale_); text_group_->SetText(text_translated_, align_h, align_v, big_, res_scale_);
text_width_ = g_text_graphics->GetStringWidth(text_translated_, big_); text_width_ = g_text_graphics->GetStringWidth(text_translated_, big_);
// FIXME: doesnt support big. // FIXME: doesnt support big.
text_height_ = g_text_graphics->GetStringHeight(text_translated_); text_height_ = g_text_graphics->GetStringHeight(text_translated_);
text_group_dirty_ = false; text_group_dirty_ = false;
@ -334,7 +335,9 @@ void TextWidget::Draw(RenderPass* pass, bool draw_transparent) {
for (int e = 0; e < elem_count; e++) { for (int e = 0; e < elem_count; e++) {
// Gracefully skip unloaded textures.. // Gracefully skip unloaded textures..
TextureData* t2 = text_group_->GetElementTexture(e); TextureData* t2 = text_group_->GetElementTexture(e);
if (!t2->preloaded()) continue; if (!t2->preloaded()) {
continue;
}
c.SetTexture(t2); c.SetTexture(t2);
c.SetMaskUV2Texture(text_group_->GetElementMaskUV2Texture(e)); c.SetMaskUV2Texture(text_group_->GetElementMaskUV2Texture(e));
c.SetShadow(-0.004f * text_group_->GetElementUScale(e), c.SetShadow(-0.004f * text_group_->GetElementUScale(e),
@ -607,23 +610,22 @@ auto TextWidget::HandleMessage(const WidgetMessage& m) -> bool {
return false; return false;
case SDLK_RETURN: case SDLK_RETURN:
case SDLK_KP_ENTER: case SDLK_KP_ENTER:
if (g_buildconfig.ostype_ios_tvos() || g_buildconfig.ostype_android()) {
#if BA_OSTYPE_IOS_TVOS || BA_OSTYPE_ANDROID // On mobile, return currently just deselects us.
// On iOS, return currently just deselects us. g_audio->PlaySound(g_media->GetSound(SystemSoundID::kSwish));
g_audio->PlaySound(g_media->GetSound(SystemSoundID::kSwish)); parent_widget()->SelectWidget(nullptr);
parent_widget()->SelectWidget(nullptr); return true;
return true; } else {
#else
if (on_return_press_call_.exists()) {
claimed = true;
if (on_return_press_call_.exists()) { if (on_return_press_call_.exists()) {
// Call this in the next cycle (don't wanna risk mucking with UI claimed = true;
// from within a UI loop) if (on_return_press_call_.exists()) {
g_game->PushPythonWeakCall( // Call this in the next cycle (don't wanna risk mucking with UI
Object::WeakRef<PythonContextCall>(on_return_press_call_)); // from within a UI loop)
g_game->PushPythonWeakCall(
Object::WeakRef<PythonContextCall>(on_return_press_call_));
}
} }
} }
#endif // BA_OSTYPE_IOS_TVOS
break; break;
case SDLK_LEFT: case SDLK_LEFT:
if (editable()) { if (editable()) {

View File

@ -33,7 +33,7 @@ PIP_REQUIREMENTS = [
PipRequirement(modulename='mypy', minversion=[0, 790]), PipRequirement(modulename='mypy', minversion=[0, 790]),
PipRequirement(modulename='yapf', minversion=[0, 30, 0]), PipRequirement(modulename='yapf', minversion=[0, 30, 0]),
PipRequirement(modulename='cpplint', minversion=[1, 5, 4]), PipRequirement(modulename='cpplint', minversion=[1, 5, 4]),
PipRequirement(modulename='pytest', minversion=[6, 1, 1]), PipRequirement(modulename='pytest', minversion=[6, 1, 2]),
PipRequirement(modulename='typing_extensions'), PipRequirement(modulename='typing_extensions'),
PipRequirement(modulename='pytz'), PipRequirement(modulename='pytz'),
PipRequirement(modulename='yaml', pipname='PyYAML'), PipRequirement(modulename='yaml', pipname='PyYAML'),
@ -739,41 +739,54 @@ def cmake_prep_dir(dirname: str) -> None:
else: else:
versions = {} versions = {}
# Get version of installed cmake. # Start fresh if cmake version changes.
cmake_ver_output = subprocess.run(['cmake', '--version'], cmake_ver_output = subprocess.run(['cmake', '--version'],
check=True, check=True,
capture_output=True).stdout.decode() capture_output=True).stdout.decode()
cmake_ver = cmake_ver_output.splitlines()[0].split('cmake version ')[1] cmake_ver = cmake_ver_output.splitlines()[0].split('cmake version ')[1]
cmake_ver_existing = versions.get('cmake_version')
cmake_ver_existing = versions.get('cmake')
assert isinstance(cmake_ver_existing, (str, type(None))) assert isinstance(cmake_ver_existing, (str, type(None)))
# Get specific version of our target python. # ...or if python's version changes.
python_ver_output = subprocess.run([f'python{PYVER}', '--version'], python_ver_output = subprocess.run([f'python{PYVER}', '--version'],
check=True, check=True,
capture_output=True).stdout.decode() capture_output=True).stdout.decode()
python_ver = python_ver_output.splitlines()[0].split('Python ')[1] python_ver = python_ver_output.splitlines()[0].split('Python ')[1]
python_ver_existing = versions.get('python_version')
python_ver_existing = versions.get('python')
assert isinstance(python_ver_existing, (str, type(None))) assert isinstance(python_ver_existing, (str, type(None)))
# If they don't match, blow away the dir and write the current version. # ...or if the actual location of python on disk changes.
if cmake_ver_existing != cmake_ver or python_ver_existing != python_ver: python_path = os.path.realpath(
if (cmake_ver_existing != cmake_ver subprocess.run(['which', f'python{PYVER}'],
and cmake_ver_existing is not None): check=True,
capture_output=True).stdout.decode())
python_path_existing = versions.get('python_path')
assert isinstance(python_path_existing, (str, type(None)))
# Blow away and start from scratch if any vals differ from existing.
if (cmake_ver_existing != cmake_ver or python_ver_existing != python_ver
or python_path != python_path_existing):
if (cmake_ver_existing is not None
and cmake_ver_existing != cmake_ver):
print(f'{Clr.BLU}CMake version changed from {cmake_ver_existing}' print(f'{Clr.BLU}CMake version changed from {cmake_ver_existing}'
f' to {cmake_ver}; clearing existing build at' f' to {cmake_ver}; clearing existing build at'
f' "{dirname}".{Clr.RST}') f' "{dirname}".{Clr.RST}')
if (python_ver_existing != python_ver if (python_ver_existing is not None
and python_ver_existing is not None): and python_ver_existing != python_ver):
print(f'{Clr.BLU}Python version changed from {python_ver_existing}' print(f'{Clr.BLU}Python version changed from {python_ver_existing}'
f' to {python_ver}; clearing existing build at' f' to {python_ver}; clearing existing build at'
f' "{dirname}".{Clr.RST}') f' "{dirname}".{Clr.RST}')
if (python_path_existing is not None
and python_path_existing != python_path):
print(f'{Clr.BLU}Python path changed from {python_path_existing}'
f' to {python_path}; clearing existing build at'
f' "{dirname}".{Clr.RST}')
subprocess.run(['rm', '-rf', dirname], check=True) subprocess.run(['rm', '-rf', dirname], check=True)
os.makedirs(dirname, exist_ok=True) os.makedirs(dirname, exist_ok=True)
with open(verfilename, 'w') as outfile: with open(verfilename, 'w') as outfile:
outfile.write( outfile.write(
json.dumps({ json.dumps({
'cmake': cmake_ver, 'cmake_version': cmake_ver,
'python': python_ver 'python_version': python_ver,
'python_path': python_path
})) }))

View File

@ -733,7 +733,6 @@ def cmake_prep_dir() -> None:
""" """
from efro.error import CleanError from efro.error import CleanError
import batools.build import batools.build
if len(sys.argv) != 3: if len(sys.argv) != 3:
raise CleanError('Expected 1 arg (dir name)') raise CleanError('Expected 1 arg (dir name)')
dirname = sys.argv[2] dirname = sys.argv[2]