fixed in-game resume and removed continues code

This commit is contained in:
Eric 2024-09-28 10:47:44 -07:00
parent bbbea7bfa6
commit 3d3d3a4885
No known key found for this signature in database
GPG Key ID: 89C93F0F8D6D5A98
14 changed files with 38 additions and 429 deletions

40
.efrocachemap generated
View File

@ -4096,26 +4096,26 @@
"build/assets/windows/Win32/ucrtbased.dll": "2def5335207d41b21b9823f6805997f1",
"build/assets/windows/Win32/vc_redist.x86.exe": "b08a55e2e77623fe657bea24f223a3ae",
"build/assets/windows/Win32/vcruntime140d.dll": "865b2af4d1e26a1a8073c89acb06e599",
"build/prefab/full/linux_arm64_gui/debug/ballisticakit": "51d7c59e2e4f3900c86593a3b4bc58e4",
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "b06acdcb8eeaae7d2057d38c4bae1483",
"build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "0b1a09078dd8fb3355da26bcd712ccc5",
"build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "ae0c974ce5353900a6d4e271851e4e6e",
"build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "8ad0607d24d95c79230c74705ce77653",
"build/prefab/full/linux_x86_64_gui/release/ballisticakit": "24acf39e9aee8bf121c6805df19105c7",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "741106e84450bb7b77c639b217929806",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "e8ebc038d7d6358e68b7ccf7607d5dce",
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "5503f0b09868f947fadb0d1895e80ec8",
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "e4412a45302241e72046a2953e5b7941",
"build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "938ea19a8306d78e2a8729f26b545a76",
"build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "757ba15790a8d7d0dbaec3bcd8c19558",
"build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "65e48222cf0bdc9037c8cd612bd6e557",
"build/prefab/full/mac_x86_64_gui/release/ballisticakit": "47b7a2fdf91039955f9188732860cf40",
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "059d4a32feac25a0d3a33bc22cfe5fbd",
"build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "3e40ffa4d7a2adee533aa770e84294fb",
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "e8983bd5f1e0d93a20e1bd7bf31ad40e",
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "96a3b8424c5a8f544894bd8bd348fec9",
"build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "0ad6d8294e6b0d62c040137d2562ad42",
"build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "344e688d38f9d8340f96503d58622b6c",
"build/prefab/full/linux_arm64_gui/debug/ballisticakit": "934beaf11fef829457ce148c66b5e585",
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "3a781083e1bad04cec163e9d16d45271",
"build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "6b32c8bfea5a1b4fc6f32844e2edcc57",
"build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "eebd9448a29f673119df718380dc6dd8",
"build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "4c946813d6cc95bb1e8aed1881913ddf",
"build/prefab/full/linux_x86_64_gui/release/ballisticakit": "3b26ce105d259eaee3d964f82956224f",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "b8c814d170feb2faadb69164515842a9",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "0e310eb8656a9d953155bbb8d976b2f2",
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "ed7a41e344cf334b50c87b1cd8dc7816",
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "05977ff4eaa374af3d89c004e970568b",
"build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "91a820ff8a95d9e3bb95602990dca61d",
"build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "46854a9414fc4ed0ad06993e1588f7e1",
"build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "d5dfac17b713104c9c3eee62e5f9c66a",
"build/prefab/full/mac_x86_64_gui/release/ballisticakit": "1df8d35253f5165ff0c4c524abe629e9",
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "081ec14d53192678f499152848688504",
"build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "1e11c3a36ff8eb9ca6103eadf3d158ed",
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "73979876a4682fded2c39de4f37d67af",
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "7f66a8dba4b86e8a89d8d6f45cc84562",
"build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "58fa4a304e1a2d07046dc705cc409002",
"build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "fbbc1d2d5c6d1e693a51c15c4e9295da",
"build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "0b5f8bdbe8221c4642d9f7f8584645ce",
"build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "e94dd06863796f17b9cfcbb5f79edd32",
"build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "0b5f8bdbe8221c4642d9f7f8584645ce",

View File

@ -1,4 +1,4 @@
### 1.7.37 (build 22017, api 9, 2024-09-27)
### 1.7.37 (build 22018, api 9, 2024-09-28)
- Bumping api version to 9. As you'll see below, there's some UI changes that
will require a bit of work for any UI mods to adapt to. If your mods don't
touch UI stuff at all you can simply bump your api version and call it a day.
@ -17,6 +17,8 @@
- Campaign hard mode no longer requires pro.
- Full player profile color customization no longer requires pro.
- Removed nag screens for purchasing pro or bundle offers.
- Removed continue logic. Continues have been disabled server-side for a while
but now removing the client code to clean things up a bit.
- Switching over to the new 'toolbar mode' UI that has been in the works for
several years. This includes a number of handy things such as consistent
buttons and widgets for league status, currencies, inventory, and the store.

View File

@ -372,7 +372,6 @@
"ba_data/python/bauiv1lib/__pycache__/config.cpython-312.opt-1.pyc",
"ba_data/python/bauiv1lib/__pycache__/confirm.cpython-312.opt-1.pyc",
"ba_data/python/bauiv1lib/__pycache__/connectivity.cpython-312.opt-1.pyc",
"ba_data/python/bauiv1lib/__pycache__/continues.cpython-312.opt-1.pyc",
"ba_data/python/bauiv1lib/__pycache__/credits.cpython-312.opt-1.pyc",
"ba_data/python/bauiv1lib/__pycache__/discord.cpython-312.opt-1.pyc",
"ba_data/python/bauiv1lib/__pycache__/feedback.cpython-312.opt-1.pyc",
@ -425,7 +424,6 @@
"ba_data/python/bauiv1lib/config.py",
"ba_data/python/bauiv1lib/confirm.py",
"ba_data/python/bauiv1lib/connectivity.py",
"ba_data/python/bauiv1lib/continues.py",
"ba_data/python/bauiv1lib/coop/__init__.py",
"ba_data/python/bauiv1lib/coop/__pycache__/__init__.cpython-312.opt-1.pyc",
"ba_data/python/bauiv1lib/coop/__pycache__/browser.cpython-312.opt-1.pyc",

View File

@ -346,7 +346,6 @@ SCRIPT_TARGETS_PY_PUBLIC = \
$(BUILD_DIR)/ba_data/python/bauiv1lib/config.py \
$(BUILD_DIR)/ba_data/python/bauiv1lib/confirm.py \
$(BUILD_DIR)/ba_data/python/bauiv1lib/connectivity.py \
$(BUILD_DIR)/ba_data/python/bauiv1lib/continues.py \
$(BUILD_DIR)/ba_data/python/bauiv1lib/coop/__init__.py \
$(BUILD_DIR)/ba_data/python/bauiv1lib/coop/browser.py \
$(BUILD_DIR)/ba_data/python/bauiv1lib/coop/gamebutton.py \
@ -624,7 +623,6 @@ SCRIPT_TARGETS_PYC_PUBLIC = \
$(BUILD_DIR)/ba_data/python/bauiv1lib/__pycache__/config.cpython-312.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/bauiv1lib/__pycache__/confirm.cpython-312.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/bauiv1lib/__pycache__/connectivity.cpython-312.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/bauiv1lib/__pycache__/continues.cpython-312.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/bauiv1lib/coop/__pycache__/__init__.cpython-312.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/bauiv1lib/coop/__pycache__/browser.cpython-312.opt-1.pyc \
$(BUILD_DIR)/ba_data/python/bauiv1lib/coop/__pycache__/gamebutton.cpython-312.opt-1.pyc \

View File

@ -131,9 +131,15 @@ class ClassicAppMode(AppMode):
ui = app.ui_v1
# If *any* main-window is up, kill it.
# If *any* main-window is up, kill it and resume play.
old_window = ui.get_main_window()
if old_window is not None:
classic = app.classic
assert classic is not None
classic.resume()
ui.clear_main_window()
return

View File

@ -720,18 +720,6 @@ class ClassicAppSubsystem(babase.AppSubsystem):
return MainMenuSession
def continues_window(
self,
activity: bascenev1.Activity,
cost: int,
continue_call: Callable[[], Any],
cancel_call: Callable[[], Any],
) -> None:
"""(internal)"""
from bauiv1lib.continues import ContinuesWindow
ContinuesWindow(activity, cost, continue_call, cancel_call)
def profile_browser_window(
self,
transition: str = 'in_right',

View File

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

View File

@ -208,8 +208,6 @@ class GameActivity(Activity[PlayerT, TeamT]):
"""Instantiate the Activity."""
super().__init__(settings)
plus = babase.app.plus
# Holds some flattened info about the player set at the point
# when on_begin() is called.
self.initialplayerinfos: list[bascenev1.PlayerInfo] | None = None
@ -239,23 +237,6 @@ class GameActivity(Activity[PlayerT, TeamT]):
None
)
self._zoom_message_times: dict[int, float] = {}
self._is_waiting_for_continue = False
self._continue_cost = (
25
if plus is None
else plus.get_v1_account_misc_read_val('continueStartCost', 25)
)
self._continue_cost_mult = (
2
if plus is None
else plus.get_v1_account_misc_read_val('continuesMult', 2)
)
self._continue_cost_offset = (
0
if plus is None
else plus.get_v1_account_misc_read_val('continuesOffset', 0)
)
@property
def map(self) -> _map.Map:
@ -362,103 +343,6 @@ class GameActivity(Activity[PlayerT, TeamT]):
if music is not None:
_music.setmusic(music)
def on_continue(self) -> None:
"""
This is called if a game supports and offers a continue and the player
accepts. In this case the player should be given an extra life or
whatever is relevant to keep the game going.
"""
def _continue_choice(self, do_continue: bool) -> None:
plus = babase.app.plus
assert plus is not None
self._is_waiting_for_continue = False
if self.has_ended():
return
with self.context:
if do_continue:
_bascenev1.getsound('shieldUp').play()
_bascenev1.getsound('cashRegister').play()
plus.add_v1_account_transaction(
{'type': 'CONTINUE', 'cost': self._continue_cost}
)
if plus is not None:
plus.run_v1_account_transactions()
self._continue_cost = (
self._continue_cost * self._continue_cost_mult
+ self._continue_cost_offset
)
self.on_continue()
else:
self.end_game()
def is_waiting_for_continue(self) -> bool:
"""Returns whether or not this activity is currently waiting for the
player to continue (or timeout)"""
return self._is_waiting_for_continue
def continue_or_end_game(self) -> None:
"""If continues are allowed, prompts the player to purchase a continue
and calls either end_game or continue_game depending on the result
"""
# pylint: disable=too-many-nested-blocks
# pylint: disable=cyclic-import
from bascenev1._coopsession import CoopSession
classic = babase.app.classic
assert classic is not None
continues_window = classic.continues_window
# Turning these off. I want to migrate towards monetization that
# feels less pay-to-win-ish.
allow_continues = False
plus = babase.app.plus
try:
if (
plus is not None
and plus.get_v1_account_misc_read_val('enableContinues', False)
and allow_continues
):
session = self.session
# We only support continuing in non-tournament games.
tournament_id = session.tournament_id
if tournament_id is None:
# We currently only support continuing in sequential
# co-op campaigns.
if isinstance(session, CoopSession):
assert session.campaign is not None
if session.campaign.sequential:
gnode = self.globalsnode
# Only attempt this if we're not currently paused
# and there appears to be no UI.
assert babase.app.classic is not None
hmmw = babase.app.ui_v1.has_main_window()
if not gnode.paused and not hmmw:
self._is_waiting_for_continue = True
with babase.ContextRef.empty():
babase.apptimer(
0.5,
lambda: continues_window(
self,
self._continue_cost,
continue_call=babase.WeakCall(
self._continue_choice, True
),
cancel_call=babase.WeakCall(
self._continue_choice, False
),
),
)
return
except Exception:
logging.exception('Error handling continues.')
self.end_game()
@override
def on_begin(self) -> None:
super().on_begin()

View File

@ -1,6 +1,5 @@
# Released under the MIT License. See LICENSE for details.
#
# pylint: disable=too-many-lines
"""Implements football games (both co-op and teams varieties)."""
# ba_meta require api 9
@ -655,11 +654,6 @@ class FootballCoopGame(bs.CoopGameActivity[Player, Team]):
for bot in bots:
bot.target_flag = None
# If we're waiting on a continue, stop here so they don't keep scoring.
if self.is_waiting_for_continue():
self._bots.stop_moving()
return
# If we've got a flag and no player are holding it, find the closest
# bot to it, and make them the designated flag-bearer.
assert self._flag is not None
@ -816,14 +810,6 @@ class FootballCoopGame(bs.CoopGameActivity[Player, Team]):
self._bots.final_celebrate()
bs.timer(0.001, bs.Call(self.do_end, 'defeat'))
@override
def on_continue(self) -> None:
# Subtract one touchdown from the bots and get them moving again.
assert self._bot_team is not None
self._bot_team.score -= 7
self._bots.start_moving()
self.update_scores()
def update_scores(self) -> None:
"""update scoreboard and check for winners"""
# FIXME: tidy this up
@ -838,7 +824,7 @@ class FootballCoopGame(bs.CoopGameActivity[Player, Team]):
if not have_scoring_team:
self._scoring_team = team
if team is self._bot_team:
self.continue_or_end_game()
self.end_game()
else:
bs.setmusic(bs.MusicType.VICTORY)

View File

@ -1195,7 +1195,7 @@ class OnslaughtGame(bs.CoopGameActivity[Player, Team]):
def _respawn_players_for_wave(self) -> None:
# Respawn applicable players.
if self._wavenum > 1 and not self.is_waiting_for_continue():
if self._wavenum > 1:
for player in self.players:
if (
not player.is_alive()
@ -1642,19 +1642,9 @@ class OnslaughtGame(bs.CoopGameActivity[Player, Team]):
self.do_end('defeat', delay=2.0)
bs.setmusic(None)
@override
def on_continue(self) -> None:
for player in self.players:
if not player.is_alive():
self.spawn_player(player)
def _checkroundover(self) -> None:
"""Potentially end the round based on the state of the game."""
if self.has_ended():
return
if not any(player.is_alive() for player in self.teams[0].players):
# Allow continuing after wave 1.
if self._wavenum > 1:
self.continue_or_end_game()
else:
self.end_game()
self.end_game()

View File

@ -550,7 +550,7 @@ class RunaroundGame(bs.CoopGameActivity[Player, Team]):
self._lives -= 1
if self._lives == 0:
self._bots.stop_moving()
self.continue_or_end_game()
self.end_game()
# Heartbeat behavior
if self._lives < 5:
@ -613,14 +613,6 @@ class RunaroundGame(bs.CoopGameActivity[Player, Team]):
),
)
@override
def on_continue(self) -> None:
self._lives = 3
assert self._lives_text is not None
assert self._lives_text.node
self._lives_text.node.text = str(self._lives)
self._bots.start_moving()
@override
def spawn_player(self, player: Player) -> bs.Actor:
pos = (

View File

@ -1,235 +0,0 @@
# Released under the MIT License. See LICENSE for details.
#
"""Provides a popup window to continue a game."""
from __future__ import annotations
import weakref
from typing import TYPE_CHECKING
import bauiv1 as bui
if TYPE_CHECKING:
from typing import Any, Callable
import bascenev1 as bs
class ContinuesWindow(bui.Window):
"""A window to continue a game."""
def __init__(
self,
activity: bs.Activity,
cost: int,
continue_call: Callable[[], Any],
cancel_call: Callable[[], Any],
):
assert bui.app.classic is not None
self._activity = weakref.ref(activity)
self._cost = cost
self._continue_call = continue_call
self._cancel_call = cancel_call
self._start_count = self._count = 20
self._width = 300
self._height = 200
self._transitioning_out = False
super().__init__(
bui.containerwidget(
size=(self._width, self._height),
background=False,
toolbar_visibility='menu_store',
transition='in_scale',
scale=1.5,
)
)
txt = (
bui.Lstr(resource='continuePurchaseText')
.evaluate()
.split('${PRICE}')
)
t_left = txt[0]
t_left_width = bui.get_string_width(t_left, suppress_warning=True)
t_price = bui.charstr(bui.SpecialChar.TICKET) + str(self._cost)
t_price_width = bui.get_string_width(t_price, suppress_warning=True)
t_right = txt[-1]
t_right_width = bui.get_string_width(t_right, suppress_warning=True)
width_total_half = (t_left_width + t_price_width + t_right_width) * 0.5
bui.textwidget(
parent=self._root_widget,
text=t_left,
flatness=1.0,
shadow=1.0,
size=(0, 0),
h_align='left',
v_align='center',
position=(self._width * 0.5 - width_total_half, self._height - 30),
)
bui.textwidget(
parent=self._root_widget,
text=t_price,
flatness=1.0,
shadow=1.0,
color=(0.2, 1.0, 0.2),
size=(0, 0),
position=(
self._width * 0.5 - width_total_half + t_left_width,
self._height - 30,
),
h_align='left',
v_align='center',
)
bui.textwidget(
parent=self._root_widget,
text=t_right,
flatness=1.0,
shadow=1.0,
size=(0, 0),
h_align='left',
v_align='center',
position=(
self._width * 0.5
- width_total_half
+ t_left_width
+ t_price_width
+ 5,
self._height - 30,
),
)
self._tickets_text_base: str | None
self._tickets_text: bui.Widget | None
self._tickets_text_base = None
self._tickets_text = None
self._counter_text = bui.textwidget(
parent=self._root_widget,
text=str(self._count),
color=(0.7, 0.7, 0.7),
scale=1.2,
size=(0, 0),
big=True,
position=(self._width * 0.5, self._height - 80),
flatness=1.0,
shadow=1.0,
h_align='center',
v_align='center',
)
self._cancel_button = bui.buttonwidget(
parent=self._root_widget,
position=(30, 30),
size=(120, 50),
label=bui.Lstr(resource='endText', fallback_resource='cancelText'),
autoselect=True,
enable_sound=False,
on_activate_call=self._on_cancel_press,
)
self._continue_button = bui.buttonwidget(
parent=self._root_widget,
label=bui.Lstr(resource='continueText'),
autoselect=True,
position=(self._width - 130, 30),
size=(120, 50),
on_activate_call=self._on_continue_press,
)
bui.containerwidget(
edit=self._root_widget,
cancel_button=self._cancel_button,
start_button=self._continue_button,
selected_child=self._cancel_button,
)
self._counting_down = True
self._countdown_timer = bui.AppTimer(
1.0, bui.WeakCall(self._tick), repeat=True
)
# If there is foreground activity, suspend it.
bui.app.classic.pause()
self._tick()
def __del__(self) -> None:
# If there is suspended foreground activity, resume it.
assert bui.app.classic is not None
bui.app.classic.resume()
def _tick(self) -> None:
plus = bui.app.plus
assert plus is not None
# if our target activity is gone or has ended, go away
activity = self._activity()
if activity is None or activity.has_ended():
self._on_cancel()
return
if plus.get_v1_account_state() == 'signed_in':
sval = bui.charstr(bui.SpecialChar.TICKET) + str(
plus.get_v1_account_ticket_count()
)
else:
sval = '?'
if self._tickets_text is not None:
assert self._tickets_text_base is not None
bui.textwidget(
edit=self._tickets_text,
text=self._tickets_text_base.replace('${COUNT}', sval),
)
if self._counting_down:
self._count -= 1
bui.getsound('tick').play()
if self._count <= 0:
self._on_cancel()
else:
bui.textwidget(edit=self._counter_text, text=str(self._count))
def _on_cancel_press(self) -> None:
# disallow for first second
if self._start_count - self._count < 2:
bui.getsound('error').play()
else:
self._on_cancel()
def _on_continue_press(self) -> None:
# from bauiv1lib import gettickets
plus = bui.app.plus
assert plus is not None
# Disallow for first second.
if self._start_count - self._count < 2:
bui.getsound('error').play()
else:
# If somehow we got signed out...
if plus.get_v1_account_state() != 'signed_in':
bui.screenmessage(
bui.Lstr(resource='notSignedInText'), color=(1, 0, 0)
)
bui.getsound('error').play()
return
# If it appears we don't have enough tickets, offer to buy more.
tickets = plus.get_v1_account_ticket_count()
if tickets < self._cost:
# FIXME: Should we start the timer back up again after?
self._counting_down = False
bui.textwidget(edit=self._counter_text, text='')
bui.getsound('error').play()
# gettickets.show_get_tickets_prompt()
return
if not self._transitioning_out:
bui.getsound('swish').play()
self._transitioning_out = True
bui.containerwidget(
edit=self._root_widget, transition='out_scale'
)
self._continue_call()
def _on_cancel(self) -> None:
if not self._transitioning_out:
bui.getsound('swish').play()
self._transitioning_out = True
bui.containerwidget(edit=self._root_widget, transition='out_scale')
self._cancel_call()

View File

@ -593,5 +593,5 @@ class InGameMenuWindow(bui.MainWindow):
classic.main_menu_resume_callbacks.clear()
def __del__(self) -> None:
self._resume()
# def __del__(self) -> None:
# self._resume()

View File

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